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 in required, additionalProperties: false), so write the schema to satisfy them.
  • Anthropic: a single forced tool named kanros_structured_output is registered with the schema as its input_schema, and tool_choice is pinned to that tool. The model's reply lives in the tool's input payload, which kanros promotes back into the completion.content field.
  • 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-schema assertion, 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_object turns on JSON mode.
  • type: json_schema needs a schema, either at json_schema.schema or directly at schema; kanros validate fails without one.
  • type: text is 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.