Constrained decoding

/v1/chat/completions honors response_format {"type":"json_object"} and {"type":"json_schema", ...} as real constrained decoding, not post-hoc validation: the schema compiles to an llguidance grammar, and each step's token bitset masks disallowed tokens on device before sampling. Constrained sessions ride the same device-sample, CUDA-graph, and speculative paths as unconstrained ones — no path is lost to being constrained.

r = client.chat.completions.create(
    model="qwen3.6-27b",
    messages=[{"role": "user", "content": "reply as JSON"}],
    response_format={"type": "json_object"},
)
r = client.chat.completions.create(
    model="qwen3.6-27b",
    messages=[{"role": "user", "content": "extract the invoice fields"}],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "invoice",
            "schema": {
                "type": "object",
                "properties": {
                    "vendor":   {"type": "string", "maxLength": 120},
                    "total":    {"type": "number"},
                    "currency": {"type": "string", "enum": ["USD", "EUR", "ILS"]},
                },
                "required": ["vendor", "total", "currency"],
                "additionalProperties": False,
            },
        },
    },
)

Cost

Plain constrained-greedy runs at 99.4% of unconstrained speed (123.7 vs 124.4 tok/s, N=3 same-session); per-step grammar compute is 0.006–0.007 ms. The spec lane pays more under tight grammars, and the cost there is draft acceptance, not mask overhead — draft-side masking recovers a measured part of it.

Draft-side masking

The speculative drafter is masked too: a constrained session clones its grammar matcher once per spec round and bans illegal tokens in the draft head's own logits, so proposals are legal by construction. The verify-side truncation backstop stays as the correctness backstop but stops firing — grammar cuts went to 0/N on every measured cell. On a bounded tight schema this is +5.0% throughput (216.6 → 227.5 tok/s, N=3 interleaved medians). Unconstrained traffic is unaffected.

Bound your schema

Prefer bounded schemas: maxLength on strings, enum where the value set is known, additionalProperties: false, required listing every field. Two reasons:

  • Tighter grammars give the drafter less room to propose tokens the grammar rejects, which is where constrained throughput is won.
  • An unbounded schema that lets the model degenerate into arbitrary whitespace against a token cap has a measured draft-chain-shape-dependent tail (floating-point summation order flips argmax at near-ties in that tail). It predates constrained decoding and is not a masking property. Bound the schema and it goes away.

Interaction with reasoning ("no-think")

Constrained requests force the chat template's no-think switch: a grammar that masks from token 0 can never close an open <think> tail. If a model's template opens a think tail and has no enable_thinking switch, a constrained request is a loud 400 — never silently wrong output.

Exactness

Device masking is gated byte-identical to a host reference oracle; spec-constrained is byte-identical to plain-constrained; graphed is byte-identical to eager; draft-masking on is byte-identical to off (greedy and seeded-sampled, 7 cells). Unknown response_format types are loud 400s. /v1/completions (non-chat) carries no response_format.