Structured Output

Force the model to return a typed Swift struct instead of free-form text. Guaranteed to decode with no parsing and no guard chains.

How It Works

Under the hood, runStructured() registers a hidden tool whose input schema is your type's jsonSchema. The model must call that tool to respond and cannot return free text. The SDK then decodes the tool's input directly into your Swift type.

Your prompt Model Hidden tool call Swift struct

Conforming a Struct

Conform your struct to StructuredOutput and Codable, then provide a jsonSchema that describes its properties:

Recipe exampleSwift
struct Recipe: Codable, StructuredOutput {
    let name: String
    let ingredients: [String]
    let steps: [String]
    let prepTimeMinutes: Int
    let note: String?

    static var jsonSchema: JSONSchema {
        [
            "type": "object",
            "properties": [
                "name":             ["type": "string"],
                "ingredients":      ["type": "array", "items": ["type": "string"]],
                "steps":            ["type": "array", "items": ["type": "string"]],
                "prepTimeMinutes":  ["type": "integer"],
                "note":             ["type": "string"],
            ],
            "required": ["name", "ingredients", "steps", "prepTimeMinutes"]
        ]
    }
}

let agent = Agent(model: provider)
let recipe: Recipe = try await agent.runStructured("Give me a pasta recipe")

print(recipe.name)               // "Spaghetti Carbonara"
print(recipe.ingredients[0])     // "200g spaghetti"
print(recipe.prepTimeMinutes)    // 20
print(recipe.note ?? "")         // may be nil

Type Mapping Reference

Use these mappings when writing your jsonSchema:

Swift TypeJSON SchemaIn required?
String{"type":"string"}Yes
Int, Int64, …{"type":"integer"}Yes
Double, Float{"type":"number"}Yes
Bool{"type":"boolean"}Yes
[T] / Array<T>{"type":"array","items":{...T's schema...}}Yes
T? / Optional<T>Same as TNo
Other types{"type":"object"}Yes

Array types

Arrays of primitives work automatically. Each element's schema is derived recursively:

Swift
struct Report: Codable, StructuredOutput {
    let title: String
    let tags: [String]       // → {"type":"array","items":{"type":"string"}}
    let scores: [Double]     // → {"type":"array","items":{"type":"number"}}
    let flags: [Bool]        // → {"type":"array","items":{"type":"boolean"}}
    // ...jsonSchema omitted for brevity
}

Optional fields

Optional properties are included in the schema but excluded from required, so the model can choose to omit them:

Swift
struct ProductReview: Codable, StructuredOutput {
    let productName: String
    let rating: Int
    let summary: String
    let pros: [String]
    let cons: [String]
    let verdict: String?      // optional -- omit from "required" in jsonSchema

    static var jsonSchema: JSONSchema {
        [
            "type": "object",
            "properties": [
                "productName": ["type": "string"],
                "rating":      ["type": "integer"],
                "summary":     ["type": "string"],
                "pros":        ["type": "array", "items": ["type": "string"]],
                "cons":        ["type": "array", "items": ["type": "string"]],
                "verdict":     ["type": "string"],
            ],
            "required": ["productName", "rating", "summary", "pros", "cons"]
        ]
    }
}

Manual Conformance

For nested types, polymorphic schemas, or any schema the macro cannot express, conform to StructuredOutput manually:

Manual jsonSchemaSwift
struct WeatherReport: StructuredOutput {
    let city: String
    let temperature: Double
    let condition: String
    let humidity: Int

    static var jsonSchema: JSONSchema {
        [
            "type": "object",
            "properties": [
                "city":        ["type": "string"],
                "temperature": ["type": "number",  "description": "Celsius"],
                "condition":   ["type": "string",  "enum": ["sunny","cloudy","rainy","snowy"]],
                "humidity":    ["type": "integer", "minimum": 0, "maximum": 100],
            ],
            "required": ["city", "temperature", "condition", "humidity"],
        ]
    }
}

let report: WeatherReport = try await agent.runStructured("What's the weather in London?")
print("\(report.city): \(report.temperature)°C, \(report.condition)")
💡

Use enum constraints in manual schemas to restrict the values the model can produce. This is especially useful for status fields, categories, and ratings.

Nested Types

The macro maps unknown types to {"type":"object"} without a nested schema. For structs with nested types, define jsonSchema manually to give the model the full schema:

Swift
// Nested type: define manually so the schema is complete
struct OrderSummary: StructuredOutput {
    struct LineItem: Codable {
        let product: String
        let quantity: Int
        let price: Double
    }
    let orderId: String
    let items: [LineItem]
    let total: Double

    static var jsonSchema: JSONSchema {
        [
            "type": "object",
            "properties": [
                "orderId": ["type": "string"],
                "items": [
                    "type": "array",
                    "items": [
                        "type": "object",
                        "properties": [
                            "product":  ["type": "string"],
                            "quantity": ["type": "integer"],
                            "price":    ["type": "number"],
                        ],
                        "required": ["product", "quantity", "price"],
                    ]
                ],
                "total": ["type": "number"],
            ],
            "required": ["orderId", "items", "total"],
        ]
    }
}