llm_schema 0.1.0
llm_schema: ^0.1.0 copied to clipboard
Zod-style schema validation for LLM structured outputs. Define a schema once to validate AI-generated JSON, emit JSON Schema for tool calling, and extract JSON from messy model responses. Pure Dart, n [...]
example/llm_schema_example.dart
// ignore_for_file: avoid_print
import 'package:llm_schema/llm_schema.dart';
enum Difficulty { easy, medium, hard }
void main() {
// Define the shape of the data you want from the model, once.
final recipe = S.object({
'title': S.string().nonEmpty().describe('Recipe name'),
'servings': S.integer().min(1).describe('How many people it feeds'),
'difficulty': S.enumOf(Difficulty.values, caseInsensitive: true),
'ingredients': S
.list(S.object({
'name': S.string(),
'quantity': S.string(),
}))
.nonEmpty(),
'notes': S.string().optional(),
});
// 1. Ship the schema to the model as a tool / structured-output schema.
print('--- JSON Schema for the model ---');
print(recipe.toJsonSchemaString(pretty: true));
// 2. Parse what the model actually sends back — fences, prose, trailing
// commas and all.
const modelResponse = '''
Sure! Here's a simple recipe for you:
```json
{
"title": "Garlic butter pasta",
"servings": 2.0,
"difficulty": "Easy",
"ingredients": [
{"name": "spaghetti", "quantity": "200 g"},
{"name": "garlic", "quantity": "4 cloves"},
{"name": "butter", "quantity": "50 g"},
],
"extra_commentary": "Enjoy!"
}
```
Let me know if you'd like a variation.
''';
final value = recipe.parseText(modelResponse);
print('\n--- Parsed ---');
print(value);
// servings arrived as 2.0 → parsed as int 2
// "Easy" matched Difficulty.easy (case-insensitive)
// extra_commentary was stripped
// 3. When the model gets it wrong, get errors you can send back to it.
final bad = recipe.safeParseText(
'{"title": "", "servings": 0, "difficulty": "impossible", '
'"ingredients": []}',
);
if (bad case SchemaFailure(issues: _) && final failure) {
print('\n--- Repair prompt ---');
print(failure.toPromptString());
}
}