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.
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
Use Swift’s type system for compile-time safety:
Copy
struct Report: Codable, Sendable { let title: String let summary: String let findings: [Finding]}// Type-safe accesslet report: Report = try output.decode(as: Report.self)// Compiler ensures correct types
Error Recovery
Automatic fallback strategies for parsing failures:
Copy
// Attempts multiple parsing strategieslet 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
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?}
The aggregated result from an entire orbit execution.
Structure
Accessing
Filtering Results
Exporting
Copy
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?}
Parsed structured data with optional schema validation.
Structure
Creating
Validation
Copy
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}
OrbitAI supports multiple output formats to suit different use cases.
Text
JSON
Markdown
CSV
XML
Structured
Format: Plain text output
Copy
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 stringprint(output.rawOutput)// "The article discusses AI trends in healthcare..."
Create a Codable struct representing your desired output:
Copy
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 withStructuredOutput factory method:
Copy
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])
The system automatically:
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:
Copy
let orbit = try await Orbit.create( name: "Business Analysis", agents: [analystAgent], tasks: [task])let result = try await orbit.start()// Type-safe decodingif 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:
Copy
// Try with automatic fallbacklet report = try output.decodeWithFallback(as: AnalysisReport.self)// Or handle explicitlydo { 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}
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 datalet 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 databasetry await database.insert(customer)
API Responses
Copy
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 responselet 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 responselet encoder = JSONEncoder()encoder.dateEncodingStrategy = .iso8601let jsonData = try encoder.encode(response)return Response(body: jsonData, contentType: .json)
UI Rendering
Copy
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 configurationlet 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 UIawait renderDashboard(dashboard)
Workflow Chaining
Copy
// Task 1: Generate structured datastruct 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 1struct 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 workflowlet result = try await orbit.start()// Access both structured outputslet 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)")
// If some fields missing, use defaultsstruct 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:
Copy
let cleaned = jsonString .replacingOccurrences(of: "```json", with: "") .replacingOccurrences(of: "```", with: "") .trimmingCharacters(in: .whitespacesAndNewlines)// Try parsing cleaned version
do { let result = try await orbit.start() guard let output = result.taskOutputs.first else { throw OrbitAIError.taskExecutionFailed( "No output generated" ) }} catch { print("Error: \(error)")}
Causes:
Task failed silently
Agent produced empty response
Output filtering removed content
Malformed JSON
Error: Cannot parse JSON output
Copy
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)}
Causes:
LLM generated invalid JSON
Extra text before/after JSON
Unclosed brackets/quotes
Schema Mismatch
Error: Output doesn’t match expected structure
Copy
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)")}
Causes:
LLM misunderstood schema
Field name variations
Type differences
Empty Content
Error: Output exists but content is empty
Copy
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)") }}
// Good: Logical organizationstruct 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, disorganizedstruct Report: Codable { let title: String let thing1: String let data: [String] let misc: String}
Appropriate Types
Do: Use specific types
Copy
// Goodstruct Product: Codable { let id: UUID let price: Decimal let inStock: Bool let category: Category enum Category: String, Codable { case electronics, clothing, books }}// Badstruct 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
Copy
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
Copy
/// 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]}
// Good: Concise outputsstruct Summary: Codable { let keyPoints: [String] // Top 5 only let metrics: [String: Double] // Essential metrics}// Avoid: Unnecessarily large outputsstruct VerboseSummary: Codable { let entireDocument: String // Don't include full text let everyMetric: [String: Any] // Don't include everything}
2
Use Streaming for Large Outputs
Copy
// For large content generationlet 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
Copy
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 querieslet cacheKey = "\(task.description)-\(inputs.hashValue)"if let cached = await cache.get(cacheKey) { return cached}
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 accessif analysis.priority == .critical { // Handle critical priority}
Use Nested Types
Copy
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 } }}
// Expected Int, got Stringstruct Data: Codable { let count: Int}// LLM output: {"count": "42"}// Expected Array, got single valuestruct Data: Codable { let tags: [String]}// LLM output: {"tags": "tag1"}
Solutions:
Copy
// Use flexible typesstruct FlexibleData: Codable { let count: FlexibleInt let tags: FlexibleArray<String>}// Custom decodingstruct 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
Symptoms: Output missing expected fieldsDebug:
Copy
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)") }}
Solutions:
Copy
// Make fields optionalstruct Report: Codable { let title: String let summary: String let findings: [Finding]? // Optional let recommendations: [Recommendation]? // Optional}// Or provide defaultsstruct Report: Codable { let title: String let summary: String let findings: [Finding] = [] let recommendations: [Recommendation] = []}// Or decode partiallylet partial = try output.decode(as: PartialReport.self)let complete = partial.fillDefaults()