Structured output

Getting JSON back reliably, and what happens when you don't give it room to think.

This was tested against the live gateway rather than assumed, because response_format is one of those parameters every OpenAI-compatible proxy claims to support and not all of them actually enforce. Here is what was observed, model by model.

`json_schema` is enforced

Send response_format: {"type": "json_schema", "json_schema": {"name": …, "schema": {…}, "strict": true}} and both chat models — larsa-general and larsa-general-fast — decode under a grammar built from your schema. The result is not "usually valid JSON"; it cannot be anything else. Sent the same schema against both engines and got back the identical, directly-parseable object each time, no markdown fence, no leading prose.

Give it room to think first
Both chat models are reasoning models: by default they emit a chain of thought (returned as reasoning_content alongside content) before the constrained answer. The grammar only applies to the final answer — the thinking that comes before it is free text and can run long. Asked for a two-field object with max_tokens: 300, the model spent the entire budget thinking and finish_reason came back length with an empty content. The same request with max_tokens: 1500 finished normally, reasoning_content around 1,000 tokens, content the exact two fields asked for. Budget for the thinking, not just the answer — 800 to 1,500 tokens is enough for a small object; scale it up with how much the model has to reason about.

`json_object` is weaker — prefer `json_schema`

response_format: {"type": "json_object"} is accepted but not grammar-constrained the way json_schema is. Asked for the same two fields with only json_object set, the model answered correctly but wrapped it in a markdown fence — ``json\n{...}\n` — rather than a bare object. That is easy to strip, but it means json_object alone is a prompting aid, not a guarantee. Use json_schema with strict: true whenever the shape matters; reach for json_object` only for "some JSON, format unspecified" and strip fences before parsing.

A structured request

curl https://api.console.larsa.larsima.com/v1/chat/completions \
  -H "Authorization: Bearer $LARSA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "larsa-general",
    "messages": [{"role": "user", "content": "A person named Ana, age 30."}],
    "max_tokens": 1500,
    "response_format": {
      "type": "json_schema",
      "json_schema": {
        "name": "person",
        "strict": true,
        "schema": {
          "type": "object",
          "properties": {
            "name": {"type": "string"},
            "age": {"type": "integer"}
          },
          "required": ["name", "age"],
          "additionalProperties": false
        }
      }
    }
  }'

Validate anyway

Grammar-constrained decoding guarantees the shape matches your schema; it cannot guarantee an integer field is the *right* integer. Parse and validate client-side regardless — jsonschema in Python, ajv in JavaScript/TypeScript, encoding/json with struct tags in Go — the same way you would validate a webhook payload you did not generate yourself.

Navigate Open esc Close