@Generable and @Guide Macros in FoundationModels: Automating Model Generation
The @Generable and @Guide macros in the FoundationModels framework enable the generation of Swift structure instances based on language model outputs. The compiler synthesizes protocols and methods for streaming responses from LanguageModelSession. This reduces boilerplate code and ensures type safety when working with structured data from LLMs.
Applying @Generable to a structure makes it compatible with generation mechanisms. The structure must conform to Identifiable for proper object identification.
@Generable
struct ShoppingItem: Identifiable {
let id: String
let value: String
}
The optional description parameter clarifies the generation context, guiding the LLM toward relevant results.
@Generable(description: "Generate items for a shopping list")
struct ShoppingItem: Identifiable {
let id: String
let value: String
}
Detailing Properties with @Guide
The @Guide macro is applied to individual properties to provide instructions to the LLM. This offers precise control over field content without altering the model generation logic.
@Generable(description: "Generate items for a shopping list")
struct ShoppingItem: Identifiable {
let id: String
@Guide(description: "The name of the product to purchase")
let value: String
}
@Guide annotations are passed to the language model session as directives, influencing the parsing and validation of outputs. This is useful for complex structures with nested types or specific data formats.
Integration with LanguageModelSession
Generated models integrate into asynchronous tasks via streamResponse. The method accepts a result type and prompt, returning a stream of chunks.
private func generateShoppingList() {
let prompt = "Create 15 shopping list items"
Task {
do {
let session = LanguageModelSession()
let response = session.streamResponse(
generating: [ShoppingItem].self
) {
prompt
}
isGenerating = true
for try await chunk in response {
self.shoppingList = chunk.compactMap {
guard let id = $0.id, let task = $0.value else {
return nil
}
return ShoppingItem(id: id, value: task)
}
}
isGenerating = false
} catch {
print(error.localizedDescription)
isGenerating = false
}
}
}
Chunk processing includes filtering nil values and updating the UI state. This ensures interface responsiveness during generation.
- Streaming Processing: Chunks arrive asynchronously, minimizing delays.
- Type Safety: The compiler verifies the structure's protocol conformance.
- Validation: Guard expressions filter out incomplete objects.
- Error Handling: Try-catch captures session failures.
SwiftUI Integration for Streamed Generation
In SwiftUI views, models are bound to @State. A button triggers generation, becoming disabled during execution.
struct ContentView: View {
@State private var shoppingList: [ShoppingItem] = []
@State private var isGenerating: Bool = false
var body: some View {
List(shoppingList) { item in
Text(item.value)
}
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button(
"Generate Shopping List",
systemImage: "cart.fill.badge.plus"
) {
generateShoppingList()
}
.disabled(isGenerating)
}
}
}
private func generateShoppingList() {
// ...
}
}
The UI updates reactively as data arrives. This demonstrates real-world application for dynamic content in apps.
Key Takeaways
@Generablesynthesizes protocols for streaming generation from LLMs into Swift structures.@Guideprovides property-level instructions for precise parsing of outputs.- Integration with
LanguageModelSessionsupports asynchronous streaming with type safety. - Suitable for generating lists, forms, and dynamic data in SwiftUI applications.
- Reduces boilerplate code while maintaining control over generated object structure.
— Editorial Team
No comments yet.