skills/foundation-models/references/structured-output.md

2.5 KiB

Structured Output with @Generable

Why @Generable Over JSON

Manual JSON parsing fails unpredictably:

// BAD - Model might output wrong keys or invalid JSON
let response = try await session.respond(to: "Generate person as JSON")
let person = try JSONDecoder().decode(Person.self, from: data) // CRASHES!

@Generable uses constrained decoding - model can only generate valid structure:

@Generable
struct Person {
    let name: String
    let age: Int
}

let response = try await session.respond(
    to: "Generate a person",
    generating: Person.self
)
let person = response.content // Guaranteed valid Person

Supported Types

@Generable
struct Example {
    let text: String           // Primitives
    let count: Int
    let isActive: Bool
    let items: [String]        // Arrays
    let plan: DayPlan          // Nested @Generable types
}

@Generable
enum Encounter {               // Enums with associated values
    case order(item: String)
    case complaint(reason: String)
}

@Guide Constraints

@Generable
struct Character {
    @Guide(description: "A full name")
    let name: String

    @Guide(.range(1...10))
    let level: Int

    @Guide(.count(4))
    var searchTerms: [String]   // Exactly 4 items

    @Guide(.maximumCount(3))
    let topics: [String]        // Up to 3 items
}

Regex patterns:

@Guide(Regex {
    ChoiceOf { "Mr"; "Mrs" }
    ". "
    OneOrMore(.word)
})
let name: String  // Output: "Mrs. Brewster"

Property Order Matters

Properties generate in declaration order. Put summaries last:

@Generable
struct Itinerary {
    var destination: String  // Generated first
    var days: [DayPlan]      // Generated second
    var summary: String      // Generated last (references days)
}

Skip Schema on Subsequent Requests

// First request - schema inserted automatically
let first = try await session.respond(to: "Generate person", generating: Person.self)

// Subsequent - skip schema for 10-20% speedup
let second = try await session.respond(
    to: "Generate another",
    generating: Person.self,
    options: GenerationOptions(includeSchemaInPrompt: false)
)

Content Tagging Adapter

let session = LanguageModelSession(
    model: SystemLanguageModel(useCase: .contentTagging)
)

@Generable
struct TagResult {
    @Guide(.maximumCount(5))
    let topics: [String]
}

let result = try await session.respond(to: article, generating: TagResult.self)