| Modes |
|---|
| json mode | Output is valid JSON, but fields are up to the model. | response_format: { type: 'json_object' } |
| json_schema | Output conforms to your schema — every key, every type. | response_format: { type: 'json_schema', strict: true } |
| tool calling | Force shape via a function signature; arguments arrive parsed. | tools: [{ function: { parameters: schema } }] |
| constrained decoding | The mechanism underneath: tokens outside the grammar are masked. | logit_bias at decode time |
| Strict-mode schema rules |
|---|
| additionalProperties: false | Every object must be closed — no surprise keys. | "additionalProperties": false |
| all properties required | Optional keys are expressed as nullable, not omitted. | "required": ["name", "tags"] |
| root is an object | Strict mode cannot return a bare scalar or array. | { "type": "object", … } |
| unions via anyOf | oneOf is unsupported; allOf only as a single-element wrapper. | "anyOf": [{…}, {…}] |
| nullable arrays | Nullability uses the type-array form. | "type": ["string", "null"] |
| supported keywords | type/enum/const/items/properties/required + descriptions; minLength, pattern, etc. are ignored. | "enum": ["yes", "no"] |
| Failure modes & fixes |
|---|
| truncated output | Hit max_tokens mid-JSON — parse fails on a dangling object. | raise max_tokens or shrink the schema |
| refusals | The model can refuse instead of emitting the schema; check the refusal field. | if (resp.refusal) … |
| hallucinated enums | Non-strict modes can invent values; strict constrains them. | use strict: true for enums |
| first-token latency | Long schemas add prompt overhead before the first token. | keep schemas minimal |