Structured output (JSON Schema)
kanros lets you constrain a model's output to a JSON Schema. Set the schema on the provider entry and kanros asks the API to enforce it on every request made through that provider — eval cells, model-graded assertion graders, red-team probes and agent loops alike.
Configuration
Put a JSON Schema object under response_schema on the provider entry
(or inside its config: block — a provider-level key wins when both are
set):
providers:
- id: openai:gpt-4o-mini
temperature: 0
response_schema:
type: object
properties:
name: { type: string }
age: { type: number }
email: { type: string }
required: [name, age, email]
additionalProperties: false
prompts:
- "Extract the person described here as JSON: {{ bio }}"
tests:
- vars:
bio: "Ada Lovelace, 36, ada@example.com"
assert:
- type: is-json
- type: json-schema
schema:
type: object
required: [name, age]
kanros validate rejects a response_schema that is not a JSON object.
How each provider enforces it
- OpenAI:
response_format = { "type": "json_schema", "json_schema": { "schema": ..., "strict": true } }— OpenAI's native structured outputs. OpenAI's strict mode has its own rules (every property listed inrequired,additionalProperties: false), so write the schema to satisfy them. - Anthropic: a single forced tool named
kanros_structured_outputis registered with the schema as itsinput_schema, andtool_choiceis pinned to that tool. The model's reply lives in the tool'sinputpayload, which kanros promotes back into thecompletion.contentfield. - Other JSON-capable providers (Groq, DeepSeek, Fireworks, Cohere, …)
receive JSON mode: the model is told to return a JSON object, but the
schema itself is not enforced on the wire. Pair the provider with a
json-schemaassertion, as in the example above, to check the shape.
response_format
If you already have OpenAI-style response_format blocks, kanros accepts
them too:
providers:
- id: openai:gpt-4o-mini
response_format:
type: json_schema # text | json_object | json_schema
json_schema:
name: person
schema:
type: object
properties: { name: { type: string } }
required: [name]
additionalProperties: false
prompts:
- "Who wrote the first published algorithm? Answer as JSON."
tests:
- vars: {}
assert:
- type: is-json
type: json_objectturns on JSON mode.type: json_schemaneeds a schema, either atjson_schema.schemaor directly atschema;kanros validatefails without one.type: textis the provider default (no constraint).
Validation
For OpenAI and Anthropic, kanros also validates the response against the
schema itself. A failure produces
ProviderError::SchemaViolation with a details field listing
up to five failures:
schema violation from `openai:gpt-4o-mini`: 42 is not of type "string" at /name
SchemaViolation is not a retryable error, because retrying a
deterministic schema mismatch wastes tokens.
Programmatic API
When you drive a provider from Rust, set response_schema on the
CompletionRequest directly; a value set on the request takes
precedence over the one from the provider's config.