Overview
Outputs are the results generated by agents executing tasks within orbits. OrbitAI provides a comprehensive output system that supports multiple formats, type-safe structured data, and rich metadata about execution.Multiple Formats
Text, JSON, Markdown, CSV, XML, and structured
Type Safety
Strongly-typed structured outputs with Codable
Rich Metadata
Usage metrics, tool usage, timestamps
Fallback Parsing
Automatic error recovery for malformed outputs
Validation
Schema validation for structured data
Traceability
Track which agent produced which output
What are Outputs?
Outputs are the structured results from task execution that contain:- The actual generated content (raw or structured)
- Metadata about execution (agent, task, timestamps)
- Usage metrics (tokens, API calls)
- Tool execution information
- Validation results
Outputs provide complete traceability and observability into what agents produced and how they accomplished their tasks.
Key Characteristics
Multi-Format Support
Multi-Format Support
Outputs can be in various formats to suit different use cases:
// Plain text
let textOutput = TaskOutput(rawOutput: "Analysis complete")
// Structured JSON
let jsonOutput = TaskOutput(
rawOutput: #"{"status": "success", "count": 42}"#,
structuredOutput: parsedJSON
)
// Type-safe structure
let typedOutput = StructuredTaskOutput(
data: AnalysisResult(findings: [...]),
rawOutput: jsonString
)
Metadata Enrichment
Metadata Enrichment
Every output includes comprehensive metadata:
public struct TaskOutput {
let rawOutput: String // The actual content
let usageMetrics: UsageMetrics // Token usage
let toolsUsed: [ToolUsage] // Tool execution data
let agentId: OrbitAIID // Which agent produced this
let taskId: OrbitAIID // For which task
let timestamp: Date // When it was generated
}
Type Safety
Type Safety
Use Swift’s type system for compile-time safety:
struct Report: Codable, Sendable {
let title: String
let summary: String
let findings: [Finding]
}
// Type-safe access
let report: Report = try output.decode(as: Report.self)
// Compiler ensures correct types
Error Recovery
Error Recovery
Automatic fallback strategies for parsing failures:
// Attempts multiple parsing strategies
let data = try output.decodeWithFallback(as: Report.self)
// Fallback order:
// 1. Direct decode
// 2. Unwrap common wrappers
// 3. Normalize field names
// 4. Partial extraction with defaults
// 5. Clean markdown artifacts
Output Types
TaskOutput
The result from a single task execution.- Structure
- Creating
- Accessing
- Type-Safe Decoding
public struct TaskOutput: Codable, Sendable {
// Core content
public let rawOutput: String
public let structuredOutput: StructuredOutput?
// Execution metadata
public let usageMetrics: UsageMetrics
public let toolsUsed: [ToolUsage]
// Traceability
public let agentId: OrbitAIID
public let taskId: OrbitAIID
public let timestamp: Date
// Validation (if applicable)
public let validationResult: TaskValidationResult?
}
| Property | Type | Description |
|---|---|---|
rawOutput | String | Raw text output from agent |
structuredOutput | StructuredOutput? | Parsed structured data |
usageMetrics | UsageMetrics | Token and API usage stats |
toolsUsed | [ToolUsage] | Tools executed during task |
agentId | OrbitAIID | Agent that produced output |
taskId | OrbitAIID | Task that was executed |
timestamp | Date | When output was generated |
validationResult | TaskValidationResult? | Manager validation result |
// TaskOutput is typically created by the system
// But you can create manually if needed
let output = TaskOutput(
rawOutput: "Analysis complete. Found 42 anomalies.",
structuredOutput: nil,
usageMetrics: UsageMetrics(
promptTokens: 150,
completionTokens: 50,
totalTokens: 200,
successfulRequests: 1,
totalRequests: 1
),
toolsUsed: [
ToolUsage(
toolName: "data_analyzer",
executionTime: 2.5,
success: true,
inputSize: 1024,
outputSize: 512
)
],
agentId: analyst.id,
taskId: task.id,
timestamp: Date()
)
// Basic access
print("Output: \(output.rawOutput)")
print("Agent: \(output.agentId)")
print("Tokens used: \(output.usageMetrics.totalTokens)")
// Check tools used
for toolUsage in output.toolsUsed {
print("Tool: \(toolUsage.toolName)")
print(" Time: \(toolUsage.executionTime)s")
print(" Success: \(toolUsage.success)")
}
// Access structured output
if let structured = output.structuredOutput {
print("Structured data available")
}
// Define expected structure
struct AnalysisResult: Codable, Sendable {
let summary: String
let anomalies: Int
let severity: String
let recommendations: [String]
}
// Decode with type safety
do {
let result = try output.decode(as: AnalysisResult.self)
print("Summary: \(result.summary)")
print("Anomalies: \(result.anomalies)")
print("Severity: \(result.severity)")
for rec in result.recommendations {
print("- \(rec)")
}
} catch {
print("Failed to decode: \(error)")
// Fall back to raw output
print("Raw: \(output.rawOutput)")
}
OrbitOutput
The aggregated result from an entire orbit execution.- Structure
- Accessing
- Filtering Results
- Exporting
public struct OrbitOutput: Codable, Sendable {
// Task results
public let taskOutputs: [TaskOutput]
// Aggregated metrics
public let usageMetrics: UsageMetrics
// Execution timing
public let executionTime: TimeInterval
// Orbit metadata
public let orbitId: OrbitAIID
public let orbitName: String
public let completedAt: Date
// Process information
public let processType: Process?
}
| Property | Type | Description |
|---|---|---|
taskOutputs | [TaskOutput] | All task results in order |
usageMetrics | UsageMetrics | Total usage across all tasks |
executionTime | TimeInterval | Total execution duration |
orbitId | OrbitAIID | Orbit identifier |
orbitName | String | Human-readable orbit name |
completedAt | Date | Completion timestamp |
processType | Process? | Sequential/Hierarchical/etc |
let result = try await orbit.start()
// Access all outputs
print("Total tasks: \(result.taskOutputs.count)")
for (index, output) in result.taskOutputs.enumerated() {
print("\n=== Task \(index) ===")
print("Output: \(output.rawOutput)")
print("Agent: \(output.agentId)")
print("Tokens: \(output.usageMetrics.totalTokens)")
}
// Aggregated metrics
print("\n=== Total Metrics ===")
print("Tokens: \(result.usageMetrics.totalTokens)")
print(" Prompt: \(result.usageMetrics.promptTokens)")
print(" Completion: \(result.usageMetrics.completionTokens)")
print("API calls: \(result.usageMetrics.totalRequests)")
print("Success rate: \(result.usageMetrics.successfulRequests)/\(result.usageMetrics.totalRequests)")
// Timing
print("\nExecution time: \(result.executionTime)s")
print("Completed: \(result.completedAt)")
let result = try await orbit.start()
// Get outputs from specific agent
let analystOutputs = result.taskOutputs.filter {
$0.agentId == analystAgent.id
}
// Get outputs that used specific tool
let searchOutputs = result.taskOutputs.filter {
$0.toolsUsed.contains { $0.toolName == "web_search" }
}
// Find longest execution
let longest = result.taskOutputs.max {
($0.usageMetrics.totalTokens) < ($1.usageMetrics.totalTokens)
}
// Calculate average execution time per task
let avgTime = result.executionTime / Double(result.taskOutputs.count)
print("Average time per task: \(avgTime)s")
let result = try await orbit.start()
// Export to JSON
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
encoder.dateEncodingStrategy = .iso8601
let jsonData = try encoder.encode(result)
try jsonData.write(to: URL(fileURLWithPath: "./orbit-result.json"))
// Export summary
let summary = """
Orbit: \(result.orbitName)
Completed: \(result.completedAt)
Tasks: \(result.taskOutputs.count)
Tokens: \(result.usageMetrics.totalTokens)
Time: \(result.executionTime)s
"""
try summary.write(to: URL(fileURLWithPath: "./summary.txt"))
// Export individual task outputs
for (index, output) in result.taskOutputs.enumerated() {
let filename = "./task_\(index)_output.txt"
try output.rawOutput.write(
toFile: filename,
atomically: true,
encoding: .utf8
)
}
StructuredOutput
Parsed structured data with optional schema validation.- Structure
- Creating
- Validation
public struct StructuredOutput: Codable, Sendable {
// Parsed data
public let data: Metadata
// Schema (if validated)
public let schema: JSONSchema?
// Validation result
public let isValid: Bool
public let validationErrors: [String]?
// Original raw output
public let rawJSON: String
}
| Property | Type | Description |
|---|---|---|
data | Metadata | Parsed structured data |
schema | JSONSchema? | Validation schema used |
isValid | Bool | Schema validation passed |
validationErrors | [String]? | Validation error messages |
rawJSON | String | Original JSON string |
// Define schema
let schema = JSONSchema(
type: .object,
properties: [
"title": JSONSchema(type: .string),
"count": JSONSchema(type: .integer),
"confidence": JSONSchema(type: .number)
],
required: ["title", "count"]
)
// Parse and validate
let jsonString = #"{"title": "Analysis", "count": 42, "confidence": 0.95}"#
do {
let structured = try StructuredOutput.parse(
jsonString: jsonString,
schema: schema
)
print("Valid: \(structured.isValid)")
print("Title: \(structured.data["title"]?.stringValue ?? "")")
print("Count: \(structured.data["count"]?.intValue ?? 0)")
} catch {
print("Parsing failed: \(error)")
}
let structured = try StructuredOutput.parse(
jsonString: jsonString,
schema: schema
)
if structured.isValid {
print("✅ Valid output")
// Process data
} else {
print("❌ Invalid output")
if let errors = structured.validationErrors {
for error in errors {
print(" - \(error)")
}
}
}
// Access data regardless of validation
let data = structured.data
Output Formats
OrbitAI supports multiple output formats to suit different use cases.- Text
- JSON
- Markdown
- CSV
- XML
- Structured
Format: Plain text outputUse Cases:
let task = ORTask(
description: "Summarize the article",
expectedOutput: "Brief summary in plain text",
outputFormat: .text // Default
)
let result = try await orbit.start()
let output = result.taskOutputs.first!
// Access as string
print(output.rawOutput)
// "The article discusses AI trends in healthcare..."
- Human-readable reports
- Summaries
- Descriptions
- General text generation
Format: Generic JSON outputUse Cases:
let task = ORTask(
description: "Generate product data in JSON format",
expectedOutput: "JSON object with product details",
outputFormat: .json
)
let result = try await orbit.start()
let output = result.taskOutputs.first!
// Parse JSON
if let jsonData = output.rawOutput.data(using: .utf8),
let json = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any] {
print("Product name: \(json["name"] ?? "")")
print("Price: \(json["price"] ?? 0)")
}
// Or use structured output
if let structured = output.structuredOutput {
print("Name: \(structured.data["name"]?.stringValue ?? "")")
}
- API responses
- Data interchange
- Configuration files
- Flexible structures
Format: Markdown formatted textUse Cases:
let task = ORTask(
description: "Write documentation",
expectedOutput: "Documentation in Markdown format",
outputFormat: .markdown
)
let result = try await orbit.start()
let output = result.taskOutputs.first!
// Output is markdown-formatted
print(output.rawOutput)
// # Documentation
// ## Section 1
// Content here...
// Save as .md file
try output.rawOutput.write(
to: URL(fileURLWithPath: "./docs.md"),
atomically: true,
encoding: .utf8
)
- Documentation
- Blog posts
- README files
- Technical writing
Format: Comma-separated valuesUse Cases:
let task = ORTask(
description: "Generate sales report",
expectedOutput: "CSV with sales data",
outputFormat: .csv
)
let result = try await orbit.start()
let output = result.taskOutputs.first!
// Output is CSV format
print(output.rawOutput)
// Date,Product,Quantity,Revenue
// 2024-01-01,Widget A,100,1000.00
// 2024-01-02,Widget B,150,1500.00
// Save as CSV
try output.rawOutput.write(
to: URL(fileURLWithPath: "./sales.csv"),
atomically: true,
encoding: .utf8
)
// Parse CSV
let rows = output.rawOutput.components(separatedBy: "\n")
for row in rows {
let columns = row.components(separatedBy: ",")
print(columns)
}
- Data exports
- Spreadsheet imports
- Tabular data
- Reports
Format: XML structured outputUse Cases:
let task = ORTask(
description: "Generate configuration",
expectedOutput: "Configuration in XML format",
outputFormat: .xml
)
let result = try await orbit.start()
let output = result.taskOutputs.first!
// Output is XML format
print(output.rawOutput)
// <?xml version="1.0"?>
// <config>
// <setting name="timeout">30</setting>
// <setting name="retries">3</setting>
// </config>
// Parse XML
let xmlData = output.rawOutput.data(using: .utf8)!
let parser = XMLParser(data: xmlData)
// ... XML parsing logic
- Legacy system integration
- Configuration files
- SOAP APIs
- Structured documents
Format: Type-safe structured output with schemaUse Cases:
// Define schema
let schema = JSONSchema(
type: .object,
properties: [
"name": JSONSchema(type: .string),
"age": JSONSchema(type: .integer),
"email": JSONSchema(type: .string)
],
required: ["name", "email"]
)
let task = ORTask(
description: "Generate user profile",
expectedOutput: "User profile data",
outputFormat: .structured(schema)
)
let result = try await orbit.start()
let output = result.taskOutputs.first!
// Access validated structured output
if let structured = output.structuredOutput {
if structured.isValid {
let name = structured.data["name"]?.stringValue
let age = structured.data["age"]?.intValue
let email = structured.data["email"]?.stringValue
print("User: \(name ?? "Unknown")")
print("Age: \(age ?? 0)")
print("Email: \(email ?? "N/A")")
}
}
- Type-safe data
- Validated outputs
- API contracts
- Database inserts
Structured Outputs
Comprehensive guide to creating and using type-safe structured outputs.Creating Structured Outputs
1
Define Data Structure
Create a Codable struct representing your desired output:
struct AnalysisReport: Codable, Sendable {
let title: String
let executiveSummary: String
let findings: [Finding]
let recommendations: [Recommendation]
let metadata: Metadata
struct Finding: Codable, Sendable {
let category: String
let description: String
let severity: Severity
let evidence: [String]
}
struct Recommendation: Codable, Sendable {
let title: String
let description: String
let priority: Priority
let estimatedImpact: String
}
struct Metadata: Codable, Sendable {
let analysisDate: Date
let analyst: String
let confidence: Double
let version: String
}
enum Severity: String, Codable {
case critical, high, medium, low
}
enum Priority: String, Codable {
case urgent, high, medium, low
}
}
Use nested types to organize complex data structures logically.
2
Create Task with Structured Output
Use the The system automatically:
withStructuredOutput factory method:let task = ORTask.withStructuredOutput(
description: """
Analyze the Q4 2024 business performance data and generate
a comprehensive report with findings and recommendations.
Focus on:
- Revenue trends
- Cost analysis
- Market position
- Growth opportunities
""",
expectedType: AnalysisReport.self,
agent: analystAgent.id,
context: [dataTask.id]
)
- Generates appropriate JSON schema from the type
- Instructs the LLM to return structured JSON
- Validates the output against the schema
- Provides type-safe decoding
3
Execute and Access
Execute the orbit and decode the structured output:
let orbit = try await Orbit.create(
name: "Business Analysis",
agents: [analystAgent],
tasks: [task]
)
let result = try await orbit.start()
// Type-safe decoding
if let output = result.taskOutputs.first {
do {
let report = try output.decode(as: AnalysisReport.self)
// Access with full type safety
print("Title: \(report.title)")
print("Summary: \(report.executiveSummary)")
print("\nFindings (\(report.findings.count)):")
for finding in report.findings {
print("- [\(finding.severity)] \(finding.category)")
print(" \(finding.description)")
}
print("\nRecommendations:")
for rec in report.recommendations {
print("- [\(rec.priority)] \(rec.title)")
}
print("\nConfidence: \(report.metadata.confidence)")
} catch {
print("Decoding failed: \(error)")
// Fall back to raw output
print("Raw output: \(output.rawOutput)")
}
}
4
Handle Decoding Errors
Implement fallback strategies:
// Try with automatic fallback
let report = try output.decodeWithFallback(as: AnalysisReport.self)
// Or handle explicitly
do {
let report = try output.decode(as: AnalysisReport.self)
processReport(report)
} catch DecodingError.keyNotFound(let key, _) {
print("Missing key: \(key.stringValue)")
// Try partial decode or use defaults
} catch DecodingError.typeMismatch(let type, let context) {
print("Type mismatch for \(type) at \(context.codingPath)")
// Try type coercion
} catch {
print("Unexpected error: \(error)")
// Fall back to raw output processing
}
Assigning Structured Outputs
- Method 1: Factory
- Method 2: Schema
- Method 3: TypedJSON
// Using factory method (recommended)
let task = ORTask.withStructuredOutput(
description: "Generate user profile data",
expectedType: UserProfile.self,
agent: agent.id
)
- Automatic schema generation
- Type-safe at compile time
- Clean, readable API
// Using explicit schema
let schema = JSONSchema(
type: .object,
properties: [
"name": JSONSchema(type: .string),
"age": JSONSchema(type: .integer),
"email": JSONSchema(
type: .string,
description: "Valid email address"
),
"tags": JSONSchema(
type: .array,
items: .init(value: JSONSchema(type: .string))
)
],
required: ["name", "email"]
)
let task = ORTask(
description: "Generate user profile",
expectedOutput: "User profile in JSON",
outputFormat: .structured(schema)
)
- Full control over schema
- Custom descriptions
- Validation rules
// Using typedJSON format
struct Product: Codable, Sendable {
let id: String
let name: String
let price: Double
let inStock: Bool
}
let task = ORTask(
description: "Generate product data",
expectedOutput: "Product details",
outputFormat: .typedJSON("Product")
)
// Later decode
let product: Product = try output.decode(as: Product.self)
- Named type reference
- Flexible decoding
- Runtime type resolution
Using Structured Outputs
Database Integration
Database Integration
struct Customer: Codable, Sendable {
let id: UUID
let name: String
let email: String
let phone: String
let address: Address
struct Address: Codable, Sendable {
let street: String
let city: String
let state: String
let zip: String
}
}
// Generate customer data
let task = ORTask.withStructuredOutput(
description: "Generate customer record from form data",
expectedType: Customer.self,
agent: dataAgent.id
)
let result = try await orbit.start()
let customer = try result.taskOutputs.first!.decode(as: Customer.self)
// Insert into database
try await database.insert(customer)
API Responses
API Responses
struct APIResponse: Codable, Sendable {
let status: String
let data: ResponseData
let metadata: ResponseMetadata
struct ResponseData: Codable, Sendable {
let items: [Item]
let total: Int
let page: Int
}
struct ResponseMetadata: Codable, Sendable {
let requestId: String
let timestamp: Date
let version: String
}
}
// Generate API response
let task = ORTask.withStructuredOutput(
description: "Format query results as API response",
expectedType: APIResponse.self,
agent: apiAgent.id
)
let result = try await orbit.start()
let response = try result.taskOutputs.first!.decode(as: APIResponse.self)
// Return as HTTP response
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
let jsonData = try encoder.encode(response)
return Response(body: jsonData, contentType: .json)
UI Rendering
UI Rendering
struct DashboardData: Codable, Sendable {
let title: String
let widgets: [Widget]
let refreshInterval: Int
struct Widget: Codable, Sendable {
let id: String
let type: WidgetType
let title: String
let data: Metadata
let position: Position
enum WidgetType: String, Codable {
case chart, table, metric, text
}
struct Position: Codable, Sendable {
let x: Int
let y: Int
let width: Int
let height: Int
}
}
}
// Generate dashboard configuration
let task = ORTask.withStructuredOutput(
description: "Create dashboard layout for sales metrics",
expectedType: DashboardData.self,
agent: uiAgent.id
)
let result = try await orbit.start()
let dashboard = try result.taskOutputs.first!.decode(as: DashboardData.self)
// Render UI
await renderDashboard(dashboard)
Workflow Chaining
Workflow Chaining
// Task 1: Generate structured data
struct Analysis: Codable, Sendable {
let insights: [String]
let metrics: [String: Double]
}
let analysisTask = ORTask.withStructuredOutput(
description: "Analyze data and generate insights",
expectedType: Analysis.self,
agent: analystAgent.id
)
// Task 2: Use structured output from Task 1
struct Report: Codable, Sendable {
let summary: String
let details: [Detail]
}
let reportTask = ORTask.withStructuredOutput(
description: """
Create report based on analysis: {task_0_output}
""",
expectedType: Report.self,
agent: reportAgent.id,
context: [analysisTask.id]
)
// Execute workflow
let result = try await orbit.start()
// Access both structured outputs
let analysis = try result.taskOutputs[0].decode(as: Analysis.self)
let report = try result.taskOutputs[1].decode(as: Report.self)
print("Insights: \(analysis.insights.count)")
print("Report sections: \(report.details.count)")
Fallback Parsing
OrbitAI includes sophisticated fallback strategies for handling malformed outputs:1
Direct Decoding
First attempt: Standard JSONDecoder
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: jsonData)
2
Wrapper Unwrapping
Handle common wrapper patterns:
// LLM might wrap in: {"data": {...}}
let wrapperKeys = ["data", "result", "response", "content"]
for key in wrapperKeys {
if let wrapped = json[key] as? [String: Any] {
// Try decoding wrapped content
}
}
3
Field Normalization
Map alternative field names:
// Handle snake_case vs camelCase
decoder.keyDecodingStrategy = .convertFromSnakeCase
// Custom key mapping
let normalized = normalizeFieldNames(json, for: T.self)
4
Partial Extraction
Extract available fields with defaults:
// If some fields missing, use defaults
struct PartialUser: Codable {
let name: String
let email: String
let age: Int = 0 // Default
let phone: String? = nil // Optional
}
5
Markdown Cleaning
Remove markdown formatting artifacts:
let cleaned = jsonString
.replacingOccurrences(of: "```json", with: "")
.replacingOccurrences(of: "```", with: "")
.trimmingCharacters(in: .whitespacesAndNewlines)
// Try parsing cleaned version
Error Handling
Common Output Errors
Missing Output
Error: Task completes but produces no outputCauses:
do {
let result = try await orbit.start()
guard let output = result.taskOutputs.first else {
throw OrbitAIError.taskExecutionFailed(
"No output generated"
)
}
} catch {
print("Error: \(error)")
}
- Task failed silently
- Agent produced empty response
- Output filtering removed content
Malformed JSON
Error: Cannot parse JSON outputCauses:
do {
let data = try output.decode(as: Report.self)
} catch DecodingError.dataCorrupted {
print("Invalid JSON format")
// Use fallback parsing
let data = try output.decodeWithFallback(as: Report.self)
}
- LLM generated invalid JSON
- Extra text before/after JSON
- Unclosed brackets/quotes
Schema Mismatch
Error: Output doesn’t match expected structureCauses:
do {
let data = try output.decode(as: Report.self)
} catch DecodingError.keyNotFound(let key, _) {
print("Missing field: \(key.stringValue)")
} catch DecodingError.typeMismatch(let type, _) {
print("Wrong type for field: \(type)")
}
- LLM misunderstood schema
- Field name variations
- Type differences
Empty Content
Error: Output exists but content is emptyCauses:
let output = result.taskOutputs.first!
if output.rawOutput.isEmpty {
print("Warning: Empty output")
// Check if task failed
if let task = orbit.tasks.first {
print("Task status: \(task.status)")
}
}
- Task execution issue
- Agent configuration problem
- LLM returned empty response
Error Recovery Strategies
- Graceful Fallback
- Retry with Clarification
- Partial Success
- Validation & Repair
func getReportData(output: TaskOutput) -> ReportData {
// Try structured decode
if let structured = try? output.decode(as: ReportData.self) {
return structured
}
// Try fallback parsing
if let fallback = try? output.decodeWithFallback(as: ReportData.self) {
return fallback
}
// Parse raw output manually
if let parsed = parseRawOutput(output.rawOutput) {
return parsed
}
// Return minimal valid data
return ReportData(
title: "Error: Unable to parse report",
summary: output.rawOutput,
findings: [],
recommendations: []
)
}
func executeWithRetry(
task: ORTask,
expectedType: Report.Type,
maxRetries: Int = 2
) async throws -> Report {
var lastError: Error?
for attempt in 1...maxRetries {
let result = try await orbit.start()
let output = result.taskOutputs.first!
do {
return try output.decode(as: Report.self)
} catch {
lastError = error
if attempt < maxRetries {
// Create clarification task
let clarificationTask = ORTask(
description: """
Previous output had parsing errors: \(error)
Please generate the report again with valid JSON.
Ensure all required fields are present.
""",
expectedOutput: "Valid JSON report",
outputFormat: .structured(reportSchema)
)
// Retry with clarification
// ... recreate orbit with clarification task
}
}
}
throw lastError ?? OrbitAIError.taskExecutionFailed("Max retries exceeded")
}
// Handle partial outputs
struct PartialReport: Codable, Sendable {
let title: String?
let summary: String?
let findings: [Finding]?
let recommendations: [Recommendation]?
// Convert to complete report with defaults
func toComplete() -> Report {
return Report(
title: title ?? "Untitled Report",
summary: summary ?? "No summary available",
findings: findings ?? [],
recommendations: recommendations ?? []
)
}
}
// Try parsing as partial
if let partial = try? output.decode(as: PartialReport.self) {
let complete = partial.toComplete()
print("Warning: Partial data, filled with defaults")
return complete
}
func validateAndRepair(_ output: TaskOutput) -> TaskOutput {
// Try to decode
guard let data = try? output.decode(as: Report.self) else {
// Attempt repair
let repairedJSON = repairJSON(output.rawOutput)
// Create new output with repaired JSON
return TaskOutput(
rawOutput: repairedJSON,
structuredOutput: nil,
usageMetrics: output.usageMetrics,
toolsUsed: output.toolsUsed,
agentId: output.agentId,
taskId: output.taskId,
timestamp: output.timestamp
)
}
return output
}
func repairJSON(_ json: String) -> String {
var repaired = json
// Remove markdown code blocks
repaired = repaired
.replacingOccurrences(of: "```json", with: "")
.replacingOccurrences(of: "```", with: "")
// Fix common issues
repaired = repaired
.replacingOccurrences(of: "\\n", with: " ")
.replacingOccurrences(of: "'", with: "\"")
// Balance brackets
repaired = balanceBrackets(repaired)
return repaired
}
Best Practices
Output Design
Clear Structure
Do: Define clear, well-organized structures
// Good: Logical organization
struct Report: Codable {
let metadata: Metadata
let content: Content
let appendices: [Appendix]
struct Metadata: Codable {
let title: String
let author: String
let date: Date
}
}
// Bad: Flat, disorganized
struct Report: Codable {
let title: String
let thing1: String
let data: [String]
let misc: String
}
Appropriate Types
Do: Use specific types
// Good
struct Product: Codable {
let id: UUID
let price: Decimal
let inStock: Bool
let category: Category
enum Category: String, Codable {
case electronics, clothing, books
}
}
// Bad
struct Product: Codable {
let id: String // Should be UUID
let price: String // Should be number
let inStock: String // Should be Bool
let category: String // Should be enum
}
Optional vs Required
Do: Make intentional choices
struct User: Codable {
// Required fields
let id: UUID
let email: String
let name: String
// Optional fields
let phone: String?
let bio: String?
let avatar: URL?
// With defaults
let role: String = "user"
let active: Bool = true
}
Documentation
Do: Document expected structures
/// Represents an analysis report with findings
/// and recommendations.
struct AnalysisReport: Codable {
/// Report title (max 100 chars)
let title: String
/// Executive summary (200-500 words)
let summary: String
/// Detailed findings (3-10 items)
let findings: [Finding]
/// Actionable recommendations (min 2)
let recommendations: [Recommendation]
}
Performance
1
Minimize Output Size
// Good: Concise outputs
struct Summary: Codable {
let keyPoints: [String] // Top 5 only
let metrics: [String: Double] // Essential metrics
}
// Avoid: Unnecessarily large outputs
struct VerboseSummary: Codable {
let entireDocument: String // Don't include full text
let everyMetric: [String: Any] // Don't include everything
}
2
Use Streaming for Large Outputs
// For large content generation
let stream = try await manager.generateStreamingCompletion(
request: request
)
var fullOutput = ""
for try await chunk in stream {
fullOutput += chunk.content ?? ""
// Process incrementally
await updateUI(chunk.content)
}
3
Cache Common Outputs
actor OutputCache {
private var cache: [String: TaskOutput] = [:]
func get(_ key: String) -> TaskOutput? {
return cache[key]
}
func set(_ key: String, output: TaskOutput) {
cache[key] = output
}
}
// Use for repeated queries
let cacheKey = "\(task.description)-\(inputs.hashValue)"
if let cached = await cache.get(cacheKey) {
return cached
}
Type Safety
Leverage Enums
Leverage Enums
struct Analysis: Codable {
let status: Status
let priority: Priority
let category: Category
enum Status: String, Codable {
case pending, inProgress, completed, failed
}
enum Priority: String, Codable {
case low, medium, high, critical
}
enum Category: String, Codable {
case bug, feature, improvement, documentation
}
}
// Type-safe access
if analysis.priority == .critical {
// Handle critical priority
}
Use Nested Types
Use Nested Types
struct Order: Codable {
let id: UUID
let customer: Customer
let items: [Item]
let payment: Payment
let shipping: Shipping
struct Customer: Codable {
let id: UUID
let name: String
let email: String
}
struct Item: Codable {
let productId: UUID
let quantity: Int
let price: Decimal
}
struct Payment: Codable {
let method: PaymentMethod
let amount: Decimal
let status: PaymentStatus
enum PaymentMethod: String, Codable {
case card, paypal, bankTransfer
}
enum PaymentStatus: String, Codable {
case pending, completed, failed
}
}
struct Shipping: Codable {
let address: Address
let method: ShippingMethod
let tracking: String?
struct Address: Codable {
let street: String
let city: String
let state: String
let zip: String
let country: String
}
enum ShippingMethod: String, Codable {
case standard, express, overnight
}
}
}
Troubleshooting
Output Not Generated
Output Not Generated
Symptoms: Task completes but no outputDebug Steps:Solutions:
// Check task status
let tasks = await orbit.getTasks()
for task in tasks {
print("Task: \(task.description)")
print("Status: \(task.status)")
if let result = task.result {
switch result {
case .success(let output):
print("Output: \(output.rawOutput)")
case .failure(let error):
print("Error: \(error)")
}
}
}
// Check agent configuration
let agents = await orbit.getAgents()
for agent in agents {
print("Agent: \(agent.role)")
print("Tools: \(await agent.getToolNames())")
}
- Verify agent has proper configuration
- Check task description is clear
- Enable verbose logging
- Verify LLM provider is working
JSON Parsing Fails
JSON Parsing Fails
Symptoms: DecodingError when parsing outputDebug Steps:Solutions:
// Print raw output
print("Raw output:")
print(output.rawOutput)
// Try parsing manually
if let jsonData = output.rawOutput.data(using: .utf8) {
do {
let json = try JSONSerialization.jsonObject(with: jsonData)
print("Valid JSON:")
print(json)
} catch {
print("Invalid JSON: \(error)")
// Check for common issues
if output.rawOutput.contains("```") {
print("Contains markdown code blocks")
}
if !output.rawOutput.hasPrefix("{") && !output.rawOutput.hasPrefix("[") {
print("Extra text before JSON")
}
}
}
// Use fallback parsing
let data = try output.decodeWithFallback(as: Report.self)
// Or clean manually
let cleaned = output.rawOutput
.replacingOccurrences(of: "```json", with: "")
.replacingOccurrences(of: "```", with: "")
.trimmingCharacters(in: .whitespacesAndNewlines)
// Extract JSON from text
if let range = cleaned.range(of: #"\{[\s\S]*\}"#, options: .regularExpression) {
let jsonOnly = String(cleaned[range])
// Parse jsonOnly
}
Schema Validation Fails
Schema Validation Fails
Symptoms: Valid JSON but doesn’t match schemaDebug:Solutions:
if let structured = output.structuredOutput {
print("Valid: \(structured.isValid)")
if !structured.isValid {
print("Validation errors:")
for error in structured.validationErrors ?? [] {
print(" - \(error)")
}
}
// Inspect actual structure
print("Actual data:")
printStructure(structured.data)
}
func printStructure(_ metadata: Metadata, indent: Int = 0) {
let spacing = String(repeating: " ", count: indent)
switch metadata {
case .dictionary(let dict):
for (key, value) in dict {
print("\(spacing)\(key): \(type(of: value))")
printStructure(value, indent: indent + 1)
}
case .array(let items):
print("\(spacing)Array(\(items.count) items)")
default:
print("\(spacing)\(metadata)")
}
}
- Adjust schema to match actual output
- Provide clearer instructions to LLM
- Use examples in task description
- Lower validation strictness
Type Mismatches
Type Mismatches
Symptoms: DecodingError.typeMismatchCommon Causes:Solutions:
// Expected Int, got String
struct Data: Codable {
let count: Int
}
// LLM output: {"count": "42"}
// Expected Array, got single value
struct Data: Codable {
let tags: [String]
}
// LLM output: {"tags": "tag1"}
// Use flexible types
struct FlexibleData: Codable {
let count: FlexibleInt
let tags: FlexibleArray<String>
}
// Custom decoding
struct Data: Codable {
let count: Int
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Try Int first, then String
if let intValue = try? container.decode(Int.self, forKey: .count) {
count = intValue
} else if let stringValue = try? container.decode(String.self, forKey: .count),
let intValue = Int(stringValue) {
count = intValue
} else {
count = 0 // Default
}
}
}
Incomplete Outputs
Incomplete Outputs
Symptoms: Output missing expected fieldsDebug:Solutions:
do {
let report = try output.decode(as: Report.self)
} catch DecodingError.keyNotFound(let key, let context) {
print("Missing key: \(key.stringValue)")
print("Context: \(context.codingPath)")
print("Debug description: \(context.debugDescription)")
// Check what's actually present
if let json = try? JSONSerialization.jsonObject(
with: output.rawOutput.data(using: .utf8)!
) as? [String: Any] {
print("Available keys: \(json.keys)")
}
}
// Make fields optional
struct Report: Codable {
let title: String
let summary: String
let findings: [Finding]? // Optional
let recommendations: [Recommendation]? // Optional
}
// Or provide defaults
struct Report: Codable {
let title: String
let summary: String
let findings: [Finding] = []
let recommendations: [Recommendation] = []
}
// Or decode partially
let partial = try output.decode(as: PartialReport.self)
let complete = partial.fillDefaults()
Next Steps
Inputs
Learn about orbit inputs
Tasks
Configure task output formats
Agents
Understand agent output generation
Orbits
Access orbit-level outputs
For additional support, consult the GitHub Discussions or check the Issue Tracker.