Generating Structured Data
This page adapts the original AI SDK documentation: Generating Structured Data.
While text generation can be useful, your use case will likely call for generating structured data. For example, you might want to extract information from text, classify data, or generate synthetic data.
Many language models are capable of generating structured data, often defined as using “JSON modes” or “tools”. However, you need to manually provide schemas and then validate the generated data as LLMs can produce incorrect or incomplete structured data.
The AI SDK standardises structured object generation across model providers
with the generateObject
and streamObject functions.
You can use both functions with different output strategies, e.g. array, object, enum, or no-schema,
and with different generation modes, e.g. auto, tool, or json.
You can describe the shape using JSON Schema,
and the AI model will generate data that conforms to that structure.
Note In the Swift port, the most ergonomic option is
generateObject(schema: MyCodableType.self). For advanced scenarios you can still fall back toFlexibleSchema<MyType>.jsonSchema(...)orSchema.codable(...).
Generate Object
Section titled “Generate Object”The generateObject generates structured data from a prompt.
The schema is also used to validate the generated data, ensuring type safety and correctness.
import SwiftAISDKimport OpenAIProvider
struct Ingredient: Codable, Sendable { let name: String let amount: String}
struct Recipe: Codable, Sendable { let name: String let ingredients: [Ingredient] let steps: [String]}
let result = try await generateObject( model: openai("gpt-4o"), schema: Recipe.self, prompt: "Generate a lasagna recipe.", schemaName: "recipe", schemaDescription: "A recipe for lasagna.").object
print(result.name)print(result.ingredients)Note See
generateObjectusage examples below.
Accessing response headers & body
Section titled “Accessing response headers & body”Sometimes you need access to the full response from the model provider, e.g. to access some provider-specific headers or body content.
You can access the raw response headers and body using the response property:
let result = try await generateObject( model: openai("gpt-4o"), schema: Recipe.self, prompt: "Generate a lasagna recipe.", schemaName: "recipe", schemaDescription: "A recipe for lasagna.")print(String(describing: result.response.headers))print(String(describing: result.response.body))Inspecting the full result
Section titled “Inspecting the full result”When you need the full payload (same shape as JSON.stringify in TypeScript), call jsonString() or jsonValue() on the result.
let fullResult = try await generateObject( model: openai("gpt-4o"), schema: Recipe.self, prompt: "Generate a lasagna recipe.")
let json = try fullResult.jsonString()print(json)Using a manual JSON Schema
Section titled “Using a manual JSON Schema”Some projects maintain JSON Schema definitions separately (for example in TypeScript or via an OpenAPI registry). Swift exposes a helper that mirrors the TypeScript DX:
let recipeSchemaJSON: JSONValue = [ "type": "object", "properties": [ "recipe": [ "type": "object", "properties": [ "name": ["type": "string"], "ingredients": [ "type": "array", "items": ["type": "object"] ] ] ] ]]
let result = try await generateObject( model: openai("gpt-4o"), schema: FlexibleSchema<Response>.jsonSchema(recipeSchemaJSON), prompt: "Generate a lasagna recipe.", mode: .json)FlexibleSchema<Response>.jsonSchema wraps Schema.codable internally, so developers can copy the JSON Schema from the TypeScript docs and still receive a strongly typed Response.
Stream Object
Section titled “Stream Object”Given the added complexity of returning structured data, model response time can be unacceptable for your interactive use case.
With the streamObject function, you can stream the model’s response as it is generated.
import SwiftAISDKimport OpenAIProvider
let stream = try streamObject( model: openai("gpt-4o"), schema: Recipe.self, prompt: "Generate a lasagna recipe.", schemaName: "recipe", schemaDescription: "A recipe for lasagna.")
for try await partial in stream.partialObjectStream { print(partial)}You can use streamObject to stream generated UIs in combination with React Server Components (see Generative UI)) or the useObject hook.
Note See
streamObjectusage in Swift examples below.
onError callback
Section titled “onError callback”streamObject immediately starts streaming.
Errors become part of the stream and are not thrown to prevent e.g. servers from crashing.
To log errors, you can provide an onError callback that is triggered when an error occurs.
let _ = try streamObject( model: openai("gpt-4o"), schema: Recipe.self, prompt: "Generate a lasagna recipe.", schemaName: "recipe", schemaDescription: "A recipe for lasagna.", onError: { error in print("Stream error: \(error)") })Output Strategy
Section titled “Output Strategy”You can use both functions with different output strategies, e.g. array, object, enum, or no-schema.
Object
Section titled “Object”The default output strategy is object, which returns the generated data as an object.
You don’t need to specify the output strategy if you want to use the default.
If you want to generate an array of objects, you can set the output strategy to array.
When you use the array output strategy, the schema specifies the shape of an array element.
With streamObject, you can also stream the generated array elements using elementStream.
struct Hero: Codable, Sendable { let name: String let `class`: String let description: String}
let arrayStream = try streamObject( model: openai("gpt-4o"), output: .array(schema: FlexibleSchema.auto(Hero.self)), prompt: "Generate 3 hero descriptions for a fantasy role playing game.", schemaName: "hero", schemaDescription: "A hero character for a fantasy RPG.")
for try await hero in arrayStream.elementStream { print(hero)}If you want to generate a specific enum value, e.g. for classification tasks,
you can set the output strategy to enum
and provide a list of possible values in the enum parameter.
Note Enum output is only available with
generateObject.
let genres = ["action","comedy","drama","horror","sci-fi"]let enumResult = try await generateObject( model: openai("gpt-4o"), output: .enum(cases: genres), prompt: "Classify the genre of this movie plot: 'A group of astronauts travel through a wormhole in search of a new habitable planet for humanity.'")print(enumResult.object)No Schema
Section titled “No Schema”In some cases, you might not want to use a schema,
for example when the data is a dynamic user request.
You can use the output setting to set the output format to no-schema in those cases
and omit the schema parameter.
let noSchema = try await generateObject( model: openai("gpt-4o"), output: .noSchema, prompt: "Generate a lasagna recipe.")print(noSchema.object)Schema Name and Description
Section titled “Schema Name and Description”You can optionally specify a name and description for the schema. These are used by some providers for additional LLM guidance, e.g. via tool or schema name.
let named = try await generateObject( model: openai("gpt-4o"), schema: Recipe.self, prompt: "Generate a lasagna recipe.", schemaName: "recipe", schemaDescription: "A recipe for a dish.")print(named.object)Accessing Reasoning
Section titled “Accessing Reasoning”You can access the reasoning used by the language model to generate the object via the reasoning property on the result. This property contains a string with the model’s thought process, if available.
let reasoning = try await generateObject( model: openai("gpt-4o"), schema: Recipe.self, prompt: "Generate a lasagna recipe.", schemaName: "recipe", schemaDescription: "A recipe for lasagna.", providerOptions: [ "openai": [ "strictJsonSchema": .bool(true), "reasoningSummary": .string("detailed") ] ])print(reasoning.reasoning)Error Handling
Section titled “Error Handling”When generateObject cannot generate a valid object, it throws a NoObjectGeneratedError.
This error occurs when the AI provider fails to generate a parsable object that conforms to the schema. It can arise due to the following reasons:
- The model failed to generate a response.
- The model generated a response that could not be parsed.
- The model generated a response that could not be validated against the schema.
The error preserves the following information to help you log the issue:
text: The text that was generated by the model. This can be the raw text or the tool call text, depending on the object generation mode.response: Metadata about the language model response, including response id, timestamp, and model.usage: Request token usage.cause: The cause of the error (e.g. a JSON parsing error). You can use this for more detailed error handling.
import SwiftAISDK
do { _ = try await generateObject( model: openai("gpt-4o"), schema: Recipe.self, prompt: "...", schemaName: "recipe" )} catch let error as NoObjectGeneratedError { print("NoObjectGeneratedError") print("Cause:", error.cause as Any) print("Text:", error.text as Any) print("Response:", error.response as Any) print("Usage:", error.usage as Any)}Repairing Invalid or Malformed JSON
Section titled “Repairing Invalid or Malformed JSON”Warning The
repairTextfunction is experimental and may change.
Sometimes the model will generate invalid or malformed JSON.
You can use the repairText function to attempt to repair the JSON.
It receives the error, either a JSONParseError or a TypeValidationError,
and the text that was generated by the model.
You can then attempt to repair the text and return the repaired text.
let repaired = try await generateObject( model: openai("gpt-4o"), schema: Recipe.self, prompt: "...", schemaName: "recipe", experimentalRepairText: { text, error in return text + "}" })Structured outputs with generateText and streamText
Section titled “Structured outputs with generateText and streamText”You can generate structured data with generateText and streamText by using the experimental_output setting.
Note Some models support structured outputs and tool calling simultaneously with
generateText/streamText.
Warning Structured output generation with
generateTextandstreamTextis experimental and may change.
generateText
Section titled “generateText”struct Person: Codable, Sendable { struct Contact: Codable, Sendable { let type: String let value: String }
struct Occupation: Codable, Sendable { let type: String let company: String let position: String }
let name: String let age: Double? let contact: Contact let occupation: Occupation}
let gt = try await generateText( model: openai("gpt-4o"), experimentalOutput: Output.object(Person.self), prompt: "Generate an example person for testing.")let person = try gt.experimentalOutputstreamText
Section titled “streamText”let st = try streamText( model: openai("gpt-4o"), experimentalOutput: Output.object(Person.self), prompt: "Generate an example person for testing.")for try await partial in st.experimentalPartialOutputStream { print(partial)}More Examples
Section titled “More Examples”You can see generateObject and streamObject in action using various frameworks in the following examples:
generateObject
Section titled “generateObject”Examples for Swift will be added in Quickstarts; Node/Next links are omitted in the Swift port.
streamObject
Section titled “streamObject”Examples for Swift will be added in Quickstarts; Node/Next links are omitted in the Swift port.