Documentation Index
Fetch the complete documentation index at: https://docs.dartantic.ai/llms.txt
Use this file to discover all available pages before exploring further.
Typed output allows constraining LLM responses to specific JSON schemas.
Basic JSON
You can pass a JSON schema to the outputSchema parameter of the Agent.send
method. The schema is used to constrain the LLM’s response to a specific JSON
object shape.
final result = await agent.send(
'The windy city in the US of A',
outputSchema: Schema.fromMap({
'type': 'object',
'properties': {
'city': {'type': 'string'},
},
'required': ['city'],
}),
);
print(result.output); // {"city":"Chicago"}
Parsed Map
You can also pass a type to the sendFor method. By default, dartantic already
knows how to parse the LLM’s response to a Map<String, dynamic>.
final result = await agent.sendFor<Map<String, dynamic>>(
'Name 3 colors that make you happy',
outputSchema: Schema.fromMap({
'type': 'object',
'properties': {
'colors': {
'type': 'array',
'items': {'type': 'string'},
},
},
'required': ['colors'],
}),
);
print(result.output['colors']); // ['sunshine yellow', 'ocean blue', 'grass green']
Custom Types
If you’d like to parse the JSON output to a custom type, you can pass a type to
the sendFor method along with the outputFromJson parameter.
class Weather {
final String city;
final int temp;
Weather.fromJson(Map<String, dynamic> json)
: city = json['city'],
temp = json['temp'];
}
final result = await agent.sendFor<Weather>(
'How hot is it in the Big Apple right now?',
outputSchema: Schema.fromMap({
'type': 'object',
'properties': {
'city': {'type': 'string'},
'temp': {'type': 'integer'},
},
'required': ['city', 'temp'],
}),
outputFromJson: Weather.fromJson,
);
print('${result.output.city}: ${result.output.temp}°F');
Schema Generation
Hand-writing schemas and JSON serialization is a lot of boilerplate. Consider
using the json_serializable and soti_schema_plus packages to automate this:
// let's use the `soti_schema_plus` package to generate the schema and JSON serialization
@SotiSchema()
@JsonSerializable()
class TownAndCountry {
final String town;
final String country;
TownAndCountry({required this.town, required this.country});
factory TownAndCountry.fromJson(Map<String, dynamic> json) =>
_$TownAndCountryFromJson(json);
@jsonSchema
static Map<String, dynamic> get schemaMap =>
_$TownAndCountrySchemaMap;
}
// now we pass the schema and JSON serialization to the agent and magic happens
final result = await agent.sendFor<TownAndCountry>(
'What is the capital of France?',
outputSchema: TownAndCountry.schema,
outputFromJson: TownAndCountry.fromJson,
);
print('${result.output.town}, ${result.output.country}');
Streaming
You can stream structured JSON from the provider by passing the outputSchema
parameter to the sendStream method.
// Stream structured JSON
await for (final chunk in agent.sendStream(
'List 3 facts',
outputSchema: factsSchema,
)) {
stdout.write(chunk.output); // Streams JSON chunks
}
This is useful if you’d like to render the JSON progressively as it’s streamed.
Most providers support both tools and typed output in the same request, allowing
you to gather information via tools and return structured results:
final weatherTool = Tool(
name: 'get_weather',
description: 'Get current weather',
inputSchema: weatherInputSchema,
onCall: (input) => getWeather(input['city']),
);
final agent = Agent('openai', tools: [weatherTool]);
final result = await agent.sendFor<WeatherReport>(
'Get weather for Seattle and format as a report',
outputSchema: WeatherReport.schema,
outputFromJson: WeatherReport.fromJson,
);
print(result.output); // WeatherReport with structured data
Provider Support
| Provider | Tools + Typed Output | Method |
|---|
| OpenAI | ✅ | Native response_format |
| Anthropic | ✅ | return_result tool |
| Google | ✅ | Double agent orchestrator |
| Together | ✅ | OpenAI-compatible |
| OpenRouter | ✅ | OpenAI-compatible |
| Ollama | ❌ | Coming soon |
| Cohere | ❌ | API limitation |
Google’s Double Agent Pattern: Google’s API doesn’t support tools and typed
output in a single call, but dartantic handles this transparently with a
two-phase approach: first executing tools, then requesting structured output
with the tool results.
See the typed output
example
for working code.
Examples
Next Steps