Skip to main content

Overview

Orbit Inputs provide a powerful parameterization system that enables dynamic, reusable workflows. Instead of hardcoding values in task descriptions, you can use placeholders that are resolved at runtime with actual values provided through the OrbitInput structure.

Dynamic Values

Provide different values for each execution

Type Safety

Strongly-typed metadata structure

Interpolation

Automatic placeholder resolution

Reusability

Same workflow, different parameters

Validation

Built-in type checking and validation

Flexibility

Support for complex nested data

What are Orbit Inputs?

Orbit Inputs are runtime parameters passed to an orbit execution that:
  • Replace placeholders in task descriptions
  • Provide configuration values
  • Enable workflow customization
  • Support complex data structures
  • Maintain type safety
Think of Orbit Inputs like function parameters—they allow the same workflow to be executed with different values each time.

Key Benefits

Create generic workflows that can be reused with different parameters:
// One workflow definition
let task = ORTask(
    description: "Analyze {company} stock performance in {year}",
    expectedOutput: "Analysis report"
)

// Multiple executions with different inputs
let inputs1 = OrbitInput(Metadata([
    "company": .string("Apple"),
    "year": .int(2024)
]))

let inputs2 = OrbitInput(Metadata([
    "company": .string("Microsoft"),
    "year": .int(2023)
]))
Adjust workflow behavior without changing code:
let task = ORTask(
    description: """
    Generate report with {detail_level} detail.
    Include {sections} sections.
    Format: {output_format}
    """,
    expectedOutput: "Report"
)

// Different configurations
let detailedConfig = OrbitInput(Metadata([
    "detail_level": .string("comprehensive"),
    "sections": .int(10),
    "output_format": .string("PDF")
]))

let summaryConfig = OrbitInput(Metadata([
    "detail_level": .string("summary"),
    "sections": .int(3),
    "output_format": .string("markdown")
]))
Strongly-typed values prevent runtime errors:
// Compile-time type safety
let inputs = OrbitInput(Metadata([
    "count": .int(100),           // Integer
    "threshold": .double(0.85),   // Double
    "enabled": .bool(true),       // Boolean
    "name": .string("Analysis")   // String
]))

// Type mismatches caught early
Handle nested and complex data structures:
let inputs = OrbitInput(Metadata([
    "config": .dictionary([
        "api": .dictionary([
            "endpoint": .string("https://api.example.com"),
            "timeout": .int(30),
            "retries": .int(3)
        ]),
        "features": .array([
            .string("analytics"),
            .string("reporting"),
            .string("alerts")
        ])
    ])
]))

Placeholders

Placeholders are special markers in task descriptions that are replaced with actual values at runtime.

Placeholder Syntax

Format: {key}
// Simple placeholder
let task = ORTask(
    description: "Analyze data for {company}",
    expectedOutput: "Analysis report"
)

// Provide value
let inputs = OrbitInput(Metadata([
    "company": .string("TechCorp")
]))

// Result: "Analyze data for TechCorp"
Rules:
  • Placeholders are case-sensitive
  • Must match input keys exactly
  • Can appear anywhere in text
  • Multiple placeholders allowed
// Multiple placeholders
let task = ORTask(
    description: """
    Analyze {company} in {industry} sector.
    Focus on {metric} for {quarter} {year}.
    """,
    expectedOutput: "Report"
)

Placeholder Examples

// Define task with placeholders
let task = ORTask(
    description: """
    Write a {length}-word article about {topic}.
    Audience: {audience}
    Tone: {tone}
    """,
    expectedOutput: "Article"
)

// Provide inputs
let inputs = OrbitInput(Metadata([
    "length": .int(1500),
    "topic": .string("AI in healthcare"),
    "audience": .string("medical professionals"),
    "tone": .string("professional and informative")
]))

// Interpolated result:
// "Write a 1500-word article about AI in healthcare.
//  Audience: medical professionals
//  Tone: professional and informative"
// Define complex input structure
struct AnalysisConfig: Codable {
    let company: String
    let metrics: [String]
    let timeframe: TimeFrame
    let thresholds: Thresholds

    struct TimeFrame: Codable {
        let start: String
        let end: String
    }

    struct Thresholds: Codable {
        let minConfidence: Double
        let maxRisk: Double
    }
}

let config = AnalysisConfig(
    company: "TechCorp",
    metrics: ["revenue", "growth", "profit"],
    timeframe: .init(start: "2024-01-01", end: "2024-12-31"),
    thresholds: .init(minConfidence: 0.85, maxRisk: 0.3)
)

let inputs = try OrbitInput(from: config)

// Use in task
let task = ORTask(
    description: """
    Analyze {company} performance:
    - Metrics: {metrics}
    - Period: {timeframe.start} to {timeframe.end}
    - Min confidence: {thresholds.minConfidence}
    - Max risk: {thresholds.maxRisk}
    """,
    expectedOutput: "Analysis"
)
let inputs = OrbitInput(Metadata([
    "includeCharts": .bool(true),
    "includeData": .bool(false),
    "format": .string("PDF")
]))

let task = ORTask(
    description: """
    Generate report in {format} format.
    {if includeCharts}Include visual charts and graphs.{endif}
    {if includeData}Include raw data tables.{endif}
    {if format=PDF}Ensure proper pagination.{endif}
    """,
    expectedOutput: "Report"
)

// Interpolated result:
// "Generate report in PDF format.
//  Include visual charts and graphs.
//  Ensure proper pagination."
Conditional syntax: {if condition}content{endif} and {if key=value}content{endif}

Metadata Structure

The underlying data structure for orbit inputs.

Metadata Type

public enum Metadata: Codable, Sendable {
    case string(String)
    case int(Int)
    case double(Double)
    case bool(Bool)
    case array([Metadata])
    case dictionary([String: Metadata])
    case null

    // Type-safe accessors
    public var stringValue: String? { /* ... */ }
    public var intValue: Int? { /* ... */ }
    public var doubleValue: Double? { /* ... */ }
    public var boolValue: Bool? { /* ... */ }
    public var arrayValue: [Metadata]? { /* ... */ }
    public var dictionaryValue: [String: Metadata]? { /* ... */ }
}

Creating Metadata

// Simple values
let stringData = Metadata.string("Hello")
let intData = Metadata.int(42)
let doubleData = Metadata.double(3.14)
let boolData = Metadata.bool(true)

// Array
let arrayData = Metadata.array([
    .string("item1"),
    .string("item2"),
    .int(100)
])

// Dictionary
let dictData = Metadata.dictionary([
    "name": .string("John"),
    "age": .int(30),
    "active": .bool(true)
])

// Nested structures
let complexData = Metadata.dictionary([
    "user": .dictionary([
        "name": .string("Alice"),
        "email": .string("[email protected]"),
        "preferences": .dictionary([
            "theme": .string("dark"),
            "notifications": .bool(true)
        ])
    ]),
    "settings": .array([
        .string("feature1"),
        .string("feature2")
    ])
])

Type Accessors

let inputs = OrbitInput(Metadata([
    "name": .string("Alice"),
    "count": .int(42),
    "rate": .double(3.14),
    "enabled": .bool(true),
    "items": .array([.string("a"), .string("b")]),
    "config": .dictionary(["key": .string("value")])
]))

// Type-safe access
if let name = inputs.metadata["name"]?.stringValue {
    print("Name: \(name)")
}

if let count = inputs.metadata["count"]?.intValue {
    print("Count: \(count)")
}

if let rate = inputs.metadata["rate"]?.doubleValue {
    print("Rate: \(rate)")
}

if let enabled = inputs.metadata["enabled"]?.boolValue {
    print("Enabled: \(enabled)")
}

if let items = inputs.metadata["items"]?.arrayValue {
    for item in items {
        print("Item: \(item.stringValue ?? "")")
    }
}

if let config = inputs.metadata["config"]?.dictionaryValue {
    print("Config: \(config)")
}

Interpolation Process

How placeholders are resolved with actual values.

Interpolation Flow

Task Description with Placeholders

"Analyze {company} in {year}"

Interpolation Engine

1. Parse Placeholders
   - Identify: {company}, {year}

2. Lookup Values
   - company → "TechCorp"
   - year → 2024

3. Replace Placeholders
   - "{company}" → "TechCorp"
   - "{year}" → "2024"

4. Validate Result
   - Check for unresolved placeholders

Interpolated Description

"Analyze TechCorp in 2024"

Interpolation Engine

// Internal interpolation logic (simplified)
func interpolate(
    template: String,
    inputs: OrbitInput
) throws -> String {
    var result = template

    // Find all placeholders
    let pattern = "\\{([^}]+)\\}"
    let regex = try NSRegularExpression(pattern: pattern)
    let matches = regex.matches(
        in: template,
        range: NSRange(template.startIndex..., in: template)
    )

    // Replace each placeholder
    for match in matches.reversed() {
        guard let range = Range(match.range(at: 1), in: template) else {
            continue
        }

        let placeholder = String(template[range])

        // Parse placeholder (handle defaults, conditionals, etc.)
        let (key, defaultValue) = parsePlaceholder(placeholder)

        // Lookup value
        if let value = inputs.metadata[key] {
            let stringValue = convertToString(value)
            result.replaceSubrange(match.range, with: stringValue)
        } else if let defaultValue = defaultValue {
            result.replaceSubrange(match.range, with: defaultValue)
        } else {
            throw OrbitAIError.missingInput(key)
        }
    }

    return result
}

Interpolation Order

1

Parse Template

Identify all placeholders in the task description:
let description = """
Analyze {company} performance in {year}.
Focus on {metrics[0]} and {metrics[1]}.
Threshold: {config.threshold}
"""

// Identified placeholders:
// - {company}
// - {year}
// - {metrics[0]}
// - {metrics[1]}
// - {config.threshold}
2

Resolve Values

Look up each placeholder in the input metadata:
let inputs = OrbitInput(Metadata([
    "company": .string("TechCorp"),
    "year": .int(2024),
    "metrics": .array([
        .string("revenue"),
        .string("growth")
    ]),
    "config": .dictionary([
        "threshold": .double(0.85)
    ])
]))

// Resolution:
// {company} → "TechCorp"
// {year} → "2024"
// {metrics[0]} → "revenue"
// {metrics[1]} → "growth"
// {config.threshold} → "0.85"
3

Replace Placeholders

Substitute resolved values in the template:
// After interpolation:
let interpolated = """
Analyze TechCorp performance in 2024.
Focus on revenue and growth.
Threshold: 0.85
"""
4

Validate Result

Check for any unresolved placeholders:
// Check for remaining placeholders
let unresolvedPattern = "\\{[^}]+\\}"
if let range = interpolated.range(of: unresolvedPattern, options: .regularExpression) {
    let unresolved = String(interpolated[range])
    throw OrbitAIError.missingInput(
        "Unresolved placeholder: \(unresolved)"
    )
}

Error Handling

Common Input Errors

Missing Input

Error: Required placeholder has no value
let task = ORTask(
    description: "Analyze {company} data",
    expectedOutput: "Report"
)

// Error: No inputs provided
do {
    let result = try await orbit.start()
} catch OrbitAIError.missingInput(let key) {
    print("Missing input: \(key)")
    // "Missing input: company"
}
Solution: Provide all required inputs or use defaults

Type Mismatch

Error: Incorrect value type
let inputs = OrbitInput(Metadata([
    "count": .string("not a number")  // Should be int
]))

let task = ORTask(
    description: "Process {count} items",
    expectedOutput: "Result"
)

// May cause issues during processing
Solution: Use correct types in metadata

Invalid Path

Error: Nested key doesn’t exist
let inputs = OrbitInput(Metadata([
    "config": .dictionary([
        "api": .dictionary([
            "url": .string("https://api.example.com")
        ])
    ])
]))

// Error: Wrong path
let task = ORTask(
    description: "Connect to {config.api.endpoint}",
    // Should be: {config.api.url}
    expectedOutput: "Connection"
)
Solution: Verify nested paths match structure

Array Out of Bounds

Error: Array index doesn’t exist
let inputs = OrbitInput(Metadata([
    "items": .array([
        .string("first"),
        .string("second")
    ])
]))

// Error: Index 2 doesn't exist (only 0 and 1)
let task = ORTask(
    description: "Process {items[2]}",
    expectedOutput: "Result"
)
Solution: Ensure indices are within bounds

Error Recovery

func validateInputs(
    tasks: [ORTask],
    inputs: OrbitInput
) throws {
    // Extract all placeholders from tasks
    var requiredKeys: Set<String> = []

    for task in tasks {
        let pattern = "\\{([^}:]+)"
        let regex = try NSRegularExpression(pattern: pattern)
        let matches = regex.matches(
            in: task.description,
            range: NSRange(task.description.startIndex..., in: task.description)
        )

        for match in matches {
            if let range = Range(match.range(at: 1), in: task.description) {
                let key = String(task.description[range])
                requiredKeys.insert(key)
            }
        }
    }

    // Check if all required keys are provided
    for key in requiredKeys {
        if inputs.metadata[key] == nil {
            throw OrbitAIError.missingInput(
                "Required input '\(key)' not provided"
            )
        }
    }
}

// Usage
do {
    try validateInputs(tasks: orbit.tasks, inputs: inputs)
    let result = try await orbit.start(inputs: inputs)
} catch {
    print("Validation failed: \(error)")
}

Best Practices

Input Design

Descriptive Keys

Do: Use clear, descriptive key names
// Good
let inputs = OrbitInput(Metadata([
    "targetCompany": .string("TechCorp"),
    "analysisYear": .int(2024),
    "reportFormat": .string("PDF")
]))

// Bad
let inputs = OrbitInput(Metadata([
    "c": .string("TechCorp"),
    "y": .int(2024),
    "f": .string("PDF")
]))

Consistent Naming

Do: Follow naming conventions
// Good: camelCase
let inputs = OrbitInput(Metadata([
    "companyName": .string("TechCorp"),
    "yearStart": .int(2024),
    "includeCharts": .bool(true)
]))

// Avoid: Mixed conventions
let inputs = OrbitInput(Metadata([
    "company_name": .string("TechCorp"),
    "YearStart": .int(2024),
    "include-charts": .bool(true)
]))

Type Appropriateness

Do: Use appropriate types
// Good
let inputs = OrbitInput(Metadata([
    "count": .int(100),           // Numbers as int/double
    "threshold": .double(0.85),
    "enabled": .bool(true),       // Flags as bool
    "name": .string("Analysis")   // Text as string
]))

// Bad
let inputs = OrbitInput(Metadata([
    "count": .string("100"),      // Number as string
    "enabled": .string("true"),   // Bool as string
    "threshold": .int(1)          // Should be double
]))

Documentation

Do: Document expected inputs
/**
 Creates an analysis orbit.

 Required inputs:
 - company: String - Company name to analyze
 - year: Int - Analysis year (2020-2024)
 - metrics: Array<String> - Metrics to include

 Optional inputs:
 - format: String - Output format (default: "PDF")
 - detail: String - Detail level (default: "standard")
 */
func createAnalysisOrbit(
    agents: [Agent],
    tasks: [ORTask]
) async throws -> Orbit {
    // ...
}

Placeholder Usage

1

Use Meaningful Placeholders

// Good: Self-documenting
let task = ORTask(
    description: """
    Generate {reportType} report for {companyName}.
    Include data from {startDate} to {endDate}.
    Focus on {primaryMetric} and {secondaryMetric}.
    """,
    expectedOutput: "Report"
)

// Bad: Unclear
let task = ORTask(
    description: """
    Generate {type} report for {name}.
    Include data from {d1} to {d2}.
    Focus on {m1} and {m2}.
    """,
    expectedOutput: "Report"
)
2

Provide Sensible Defaults

// For optional parameters
let task = ORTask(
    description: """
    Analyze data with {detail:standard} detail.
    Format output as {format:markdown}.
    Include {sections:5} sections.
    """,
    expectedOutput: "Analysis"
)

// Users can override defaults or use as-is
3

Group Related Inputs

// Good: Structured grouping
let inputs = OrbitInput(Metadata([
    "company": .dictionary([
        "name": .string("TechCorp"),
        "industry": .string("Technology"),
        "founded": .int(2010)
    ]),
    "analysis": .dictionary([
        "startDate": .string("2024-01-01"),
        "endDate": .string("2024-12-31"),
        "metrics": .array([
            .string("revenue"),
            .string("growth")
        ])
    ])
]))

// Access: {company.name}, {analysis.startDate}, etc.
4

Validate Input Completeness

// Before execution, check all required inputs
func validateInputs(
    orbit: Orbit,
    inputs: OrbitInput
) throws {
    let requiredKeys = extractRequiredKeys(orbit.tasks)

    for key in requiredKeys {
        if inputs.metadata[key] == nil {
            throw OrbitAIError.missingInput(
                "Required input '\(key)' not provided. " +
                "Please provide this value before executing."
            )
        }
    }
}

Type Safety

// Define typed input structure
struct AnalysisInputs: Codable {
    let company: String
    let year: Int
    let metrics: [String]
    let thresholds: Thresholds

    struct Thresholds: Codable {
        let minConfidence: Double
        let maxRisk: Double
    }
}

// Create and validate at compile-time
let inputs = AnalysisInputs(
    company: "TechCorp",
    year: 2024,
    metrics: ["revenue", "growth"],
    thresholds: .init(minConfidence: 0.85, maxRisk: 0.3)
)

// Convert to OrbitInput
let orbitInputs = try OrbitInput(from: inputs)

// Type errors caught by compiler
// let badInputs = AnalysisInputs(
//     company: 123,  // Error: Cannot convert Int to String
//     year: "2024"   // Error: Cannot convert String to Int
// )
// Validate types at runtime
func validateInputTypes(
    inputs: OrbitInput,
    schema: [String: MetadataType]
) throws {
    for (key, expectedType) in schema {
        guard let value = inputs.metadata[key] else {
            continue  // Missing handled separately
        }

        let actualType = value.type
        if actualType != expectedType {
            throw OrbitAIError.invalidInput(
                "Input '\(key)' expected type \(expectedType), " +
                "got \(actualType)"
            )
        }
    }
}

enum MetadataType {
    case string, int, double, bool, array, dictionary
}

// Usage
let schema: [String: MetadataType] = [
    "company": .string,
    "year": .int,
    "threshold": .double,
    "enabled": .bool,
    "metrics": .array
]

try validateInputTypes(inputs: inputs, schema: schema)

Troubleshooting

Symptoms: {placeholder} appears literally in outputCommon Causes:
  1. Key mismatch (case-sensitive)
  2. Input not provided
  3. Wrong nested path
  4. Syntax error in placeholder
Solutions:
// Check exact key match
let task = ORTask(
    description: "Analyze {Company}",  // Capital C
    expectedOutput: "Report"
)

let inputs = OrbitInput(Metadata([
    "company": .string("TechCorp")  // Lowercase c - won't match!
]))

// Fix: Match case exactly
let inputs = OrbitInput(Metadata([
    "Company": .string("TechCorp")  // Now matches
]))

// Or fix task
let task = ORTask(
    description: "Analyze {company}",  // Lowercase to match
    expectedOutput: "Report"
)
Debug Steps:
// 1. Print available keys
print("Available keys:", inputs.metadata.keys)

// 2. Extract placeholders from task
let placeholders = extractPlaceholders(task.description)
print("Required placeholders:", placeholders)

// 3. Check for missing
for placeholder in placeholders {
    if inputs.metadata[placeholder] == nil {
        print("Missing: \(placeholder)")
    }
}
Symptoms: Error accessing nested valuesCommon Causes:
  1. Incorrect path syntax
  2. Missing intermediate keys
  3. Wrong structure
Solutions:
// Define structure
let inputs = OrbitInput(Metadata([
    "config": .dictionary([
        "api": .dictionary([
            "endpoint": .string("https://api.example.com"),
            "version": .string("v2")
        ])
    ])
]))

// Wrong: Missing level
// {config.endpoint} ❌

// Correct: Full path
// {config.api.endpoint} ✅

// Debug nested structure
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):")
            printStructure(value, indent: indent + 1)
        }
    default:
        print("\(spacing)\(metadata)")
    }
}

// Print to see structure
printStructure(inputs.metadata)
// Output:
// config:
//   api:
//     endpoint:
//       https://api.example.com
//     version:
//       v2
Symptoms: Error accessing array elementCommon Causes:
  1. Index too large
  2. Empty array
  3. Wrong array key
Solutions:
let inputs = OrbitInput(Metadata([
    "items": .array([
        .string("first"),
        .string("second")
    ])
]))

// Check array size first
if let array = inputs.metadata["items"]?.arrayValue {
    print("Array size: \(array.count)")  // 2 (indices 0, 1)

    // Safe access
    if array.count > 2 {
        // {items[2]} would be safe
    } else {
        // Use default or adjust
    }
}

// Use conditional access
let task = ORTask(
    description: """
    Primary: {items[0]}
    {if items[1]}Secondary: {items[1]}{endif}
    {if items[2]}Tertiary: {items[2]}{endif}
    """,
    expectedOutput: "List"
)
Symptoms: Unexpected string representationsCommon Causes:
  1. Implicit type conversion
  2. Array/dictionary to string
  3. Number formatting
Solutions:
// Control number formatting
let inputs = OrbitInput(Metadata([
    "price": .double(1234.56)
]))

// In task
let task = ORTask(
    description: "Price: ${price}",
    expectedOutput: "Info"
)
// Result: "Price: $1234.56"

// For custom formatting, pre-format
let formattedInputs = OrbitInput(Metadata([
    "price": .string("$1,234.56")  // Pre-formatted
]))

// Arrays convert to comma-separated
let arrayInputs = OrbitInput(Metadata([
    "tags": .array([
        .string("ai"),
        .string("ml"),
        .string("data")
    ])
]))

// In task: {tags}
// Result: "ai, ml, data"

// For custom format, pre-join
let tags = ["ai", "ml", "data"]
let customFormat = tags.joined(separator: " | ")
let formattedInputs = OrbitInput(Metadata([
    "tags": .string(customFormat)
]))
// Result: "ai | ml | data"
Symptoms: Execution fails with missing input errorSolutions:
// Create input validator
struct InputValidator {
    let requiredKeys: [String]
    let optionalKeys: [String]

    func validate(_ inputs: OrbitInput) throws {
        for key in requiredKeys {
            guard inputs.metadata[key] != nil else {
                throw OrbitAIError.missingInput(
                    "Required input '\(key)' not provided"
                )
            }
        }
    }

    func printMissing(_ inputs: OrbitInput) {
        let missing = requiredKeys.filter {
            inputs.metadata[$0] == nil
        }

        if !missing.isEmpty {
            print("Missing required inputs:")
            for key in missing {
                print("  - \(key)")
            }
        }
    }
}

// Usage
let validator = InputValidator(
    requiredKeys: ["company", "year", "metrics"],
    optionalKeys: ["format", "detail"]
)

// Validate before execution
do {
    try validator.validate(inputs)
    let result = try await orbit.start(inputs: inputs)
} catch {
    validator.printMissing(inputs)
    // Missing required inputs:
    //   - company
    //   - metrics
}
Symptoms: Slow interpolation with many placeholdersSolutions:
// Cache interpolation results
actor InterpolationCache {
    private var cache: [String: String] = [:]

    func get(_ key: String) -> String? {
        return cache[key]
    }

    func set(_ key: String, value: String) {
        cache[key] = value
    }

    func clear() {
        cache.removeAll()
    }
}

// Use cache for repeated interpolations
let cache = InterpolationCache()

func interpolateWithCache(
    template: String,
    inputs: OrbitInput,
    cache: InterpolationCache
) async throws -> String {
    // Check cache
    if let cached = await cache.get(template) {
        return cached
    }

    // Interpolate
    let result = try interpolate(template, inputs: inputs)

    // Cache result
    await cache.set(template, value: result)

    return result
}

// Or pre-interpolate static parts
// Only interpolate dynamic sections

Next Steps

For additional support, consult the GitHub Discussions or check the Issue Tracker.