Typed Output
By default, an LLM will return a string. You can use Dartantic to get typed output instead. To do so, you have to at least provide a schema that defines the output you're looking for.
JSON String with JSON Schema
By initializing an Agent
with an outputSchema
, you'll get a JSON-formatted
string returned to you, which you can then parse into a Map
object.
The following example provides JSON output using a hand-written schemaMap
property, which configures the underlying LLM to response in JSON. You can also
use the toSchema
method on a Map<String, dynamic>
to get a JsonSchema
object.
import 'dart:convert';
import 'package:dartantic_ai/dartantic_ai.dart';
void main() async {
// Define a JSON schema for structured output
final townCountrySchema = {
'type': 'object',
'properties': {
'town': {'type': 'string'},
'country': {'type': 'string'},
},
'required': ['town', 'country'],
};
// Create an agent with the schema
final agent = Agent('openai', outputSchema: townCountrySchema.toSchema());
// Get structured output as a JSON string
final result = await agent.run('The windy city in the US of A.');
print(result.output); // Output: {"town":"Chicago","country":"United States"}
// Convert the JSON string to a Map
final json = jsonDecode(result.output);
print(json['town']); // Output: Chicago
print(json['country']); // Output: United States
}
JSON Map Result
Using the Agent.runFor<T>
method, you can ask Dartantic to convert the JSON
string into a Dart object of type T
. The easiest thing to do is to convert to
a Map<String, dynamic>
:
import 'package:dartantic_ai/dartantic_ai.dart';
void main() async {
// Define a JSON schema for structured output
final townCountrySchema = {
'type': 'object',
'properties': {
'town': {'type': 'string'},
'country': {'type': 'string'},
},
'required': ['town', 'country'],
};
// Create an agent with the schema
final agent = Agent('openai', outputSchema: townCountrySchema.toSchema());
// Get structured output as a JSON Map
final result = await agent.runFor<Map<String, dynamic>>(
'The windy city in the US of A.',
);
print(result.output['town']); // Output: Chicago
print(result.output['country']); // Output: United States
}
Typed Dart Object Result
In additonal to mapping JSON to a Map<String, dynamic>
, you can also map JSON
to a Dart object. This example provides typed output using automatic json
decoding from a hand-written fromJson
method and a hand-written schemaMap
property.
// Create a data class in your code
class TownAndCountry {
final String town;
final String country;
TownAndCountry({required this.town, required this.country});
factory TownAndCountry.fromJson(Map<String, dynamic> json) => TownAndCountry(
town: json['town'],
country: json['country'],
);
static Map<String, dynamic> get schemaMap => {
'type': 'object',
'properties': {
'town': {'type': 'string'},
'country': {'type': 'string'},
},
'required': ['town', 'country'],
};
@override
String toString() => 'TownAndCountry(town: $town, country: $country)';
}
void main() async {
// Use runFor with a type parameter for automatic conversion
final agent = Agent(
'openai',
outputSchema: TownAndCountry.schemaMap.toSchema(),
outputFromJson: TownAndCountry.fromJson,
);
final result = await agent.runFor<TownAndCountry>(
'The windy city in the US of A.',
);
print(result.output); // Output: TownAndCountry(town: Chicago, country: US)
}
This is where things get a little dicey. Hand-writing the fromJson
method and
schemaMap
property is a lot of boilerplate and it's easy to make mistakes.
Typed Dart Object with json_serializable and soti_schema
If you'd like to automatically generate the fromJson
method and schemaMap
property, you can use the
json_serializable and
soti_schema packages.
json_serializable
is a code generator that creates the fromJson
method and
toJson
method for a Dart class. soti_schema
is a code generator that creates
the schemaMap
property for a Dart class.
Put them together with the associated builders, and you've got an automated
system for generating the fromJson
method and schemaMap
property.
// Create a data class in your code
@SotiSchema()
@JsonSerializable()
class TownAndCountry {
TownAndCountry({required this.town, required this.country});
factory TownAndCountry.fromJson(Map<String, dynamic> json) =>
_$TownAndCountryFromJson(json);
final String town;
final String country;
Map<String, dynamic> toJson() => _$TownAndCountryToJson(this);
@jsonSchema
static Map<String, dynamic> get schemaMap => _$TownAndCountrySchemaMap;
@override
String toString() => 'TownAndCountry(town: $town, country: $country)';
}
void main() async {
// Use runFor with a type parameter for automatic conversion
final agent = Agent(
'openai',
outputSchema: TownAndCountry.schemaMap.toSchema(),
outputFromJson: TownAndCountry.fromJson,
);
final result = await agent.runFor<TownAndCountry>(
'The windy city in the US of A.',
);
print(result.output); // Output: TownAndCountry(town: Chicago, country: US)
}
If you want to try this out yourself, check out the output_types.dart example.