OnlyFunc

Exploring structured generation with FoundationModels

In the last post, we learned that FoundationModels excel at generating text. However, we can also use FoundationModels to generate structured data. Rather than parsing unstructured strings or json output from FoundationModels, we can use Swift structs directly as the output type.

Getting started with structured generation

To start generating structured data, we need to define a struct that represents the data we want to generate.

For example, if we want to generate a person's name, age, and gender, we can define a struct like this and add the @Generable macro conformance:

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

Then we can simply call the respond(to:generating:includeSchemaInPrompt:options:) method from LanguageModelSession:

let session = LanguageModelSession(model: .default)
let response = try await session.respond(to: prompt, generating: Person.self)

You can also generate basic Swift types like String, Float, and Int from FoundationModels.

In the example above, we use String for the gender field. However, if you want to generate an enum instead, you can use the @Generable macro conformance as well:

@Generable
enum Gender: String {
    case male
    case female
    case other
}

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

Using @Guide to guide FoundationModel Generation

We can provide additional information to guide FoundationModels using the @Guide macro. Take a look at the example below:

@Generable(description: "Store data")
struct Store {
    @Guide(description: "Store name")
    var name: String
    
    @Guide(description: "Rating", .minimumCount(1), .maximumCount(5))
    var rating: Int
    
    @Guide(description: "Tier", .anyOf(["Gold", "Silver", "Bronze"]))
    var tier: String
    
    @Guide(description: "Total number of products")
    var totalProducts: Int
}

We can add additional information to the struct so that when FoundationModels generate the rating, they won't generate a rating greater than 5 or less than 1.

Structured generation with streaming calls

We can also use the streamResponse(generating:includeSchemaInPrompt:options:prompt:) method with structured generation. This allows you to stream the response as it's generated. However, the stream will produce the T.PartiallyGenerated type as the output. The partially generated object mirrors the structure of the struct, with the difference being that all fields are optional.

Typically, you'll check for nil values in your views. This provides a better experience since users don't have to wait until generation is complete before seeing results.

You can animate the UI as FoundationModels output the results.

Conclusion

In this post, we explored the power of structured generation with FoundationModels, moving beyond simple text generation to create well-defined Swift structs and enums directly from model responses. We learned how to leverage @Generable for basic types and custom structs, and how @Guide can be used to impose constraints and provide context, ensuring more accurate and controlled outputs. We also touched on streaming structured responses, which allows for dynamic UI updates as data is generated.

This capability opens up exciting possibilities for building more robust and predictable AI-powered applications. In our next post, we'll explore another advanced topic: Tool Calling, where we'll discover how FoundationModels can interact with external functions and APIs to perform complex tasks. Stay tuned!

#AI #FoundationModels #swift