v0.20.2: three silent failures in the request path
Highlights
Constrained decoding dropped every non-ASCII character (#1197). With response_format: json_schema or json_object, "Die Bären hören" came back as "Die Baren horen", so German and every other non-English language was unusable. The state machine was never at fault: classify_token() reads through signed char, so every byte of a multi-byte UTF-8 sequence was negative and lost CAT_STRING_CHAR, and the category mask dropped those tokens before the machine was consulted. GBNF and regex constraining were never affected.
An image sent to a model that cannot see it is refused, not ignored (#1198). A multimodal SafeTensors checkpoint whose tower imp does not understand loads text-only and says so in the load log, but image_url parts were still accepted and answered from the text alone, so the caller got a confident description of a picture the model never received. Now a 400 with code vision_unavailable, in every dialect.
Generation could stop with the JSON document still open (#1199), returning a 200 that does not parse. <|im_end|> decodes to plain printable ASCII, so the classifier gave it CAT_STRING_CHAR and the free-string shortcut allowed it without simulating. The clear that exists for exactly this hazard was gated on the XML dialect and is unconditional now.
Also in here
docs/supported-models.mdnow states which checkpoints can see and which cannot- CI can be run against a ref on demand (
gh workflow run CI --ref main). A squash merge performed by auto-merge starts no workflow run, somain's reported CI state can be many commits old: it was ten commits stale while the warnings below were read off it - The build is warning-free again, seven warnings from five causes, including a
tmpnamin a test fixture whose guessed path then went throughsystem("rm -rf …")
Gate
Not measured. This release was cut while the GPU was busy with unrelated work, and a reading taken against a fully utilised card is not a measurement. Nothing here is in the unconstrained decode path: the constrainer changes execute only when a request carries response_format, and the rest is tests, CI config and docs. Last measured gate on this lineage is v0.20.1, Qwen3-8B-Q8_0 on one RTX 5090, decode 288.52 tok/s.
No breaking changes. Full detail: CHANGELOG · 5 PRs since v0.20.1.