> ## 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

> Get more than a string back from your AI.

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.

```dart theme={null}
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>`.

```dart theme={null}
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.

```dart theme={null}
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:

```dart theme={null}
// 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.

```dart theme={null}
// 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.

## With Tools

Most providers support both tools and typed output in the same request, allowing
you to gather information via tools and return structured results:

```dart theme={null}
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](https://github.com/csells/dartantic_ai/blob/main/packages/dartantic_ai/example/bin/typed_output.dart)
for working code.

## Examples

* [Typed output
  basics](https://github.com/csells/dartantic_ai/blob/main/packages/dartantic_ai/example/bin/typed_output.dart)
* [Chat with typed
  output](https://github.com/csells/dartantic_ai/blob/main/packages/dartantic_ai/example/bin/chat.dart)

## Next Steps

* [Tool Calling](/tool-calling) - Extend with functions
* [Multi-turn Chat](/multi-turn-chat) - Structured conversations
