fix(chat): keep the structured-output opt-out exact on the native chat wire - #2042
Conversation
|
✅ Deterministic PR hygiene checks passed. |
✅ READY
Review readiness checklist
✅ 4/4 boxes ticked. This pull request is already Ready for Review. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan includes up to 10 reviews per rolling hour; 9 remain after this review. 📝 WalkthroughWalkthroughThe passthrough builder now removes ChangesOpenAI passthrough response format
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to The change limits structured-output opt-outs to exact model IDs and adds coverage for tagged siblings; no actionable merge-blocking risk remains after normal checks and review. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Confirmed against the exact current I also verified this exact head locally under the 2-core limit:
I found no implementation blocker in the current diff. Please tick the readiness checklist and mark the PR Ready so the full exact-head CI/review gate runs; assuming it remains green and no new head is pushed, this should be an approval candidate. |
|
Checklist is at 3/4. Box 1 is open on purpose rather than left over — I would rather ask than tick something I cannot stand behind. "All CI tests are green on my local testing" — this is a Windows machine. What I did run on the exact head of this branch is listed under Verification in the description — typecheck clean, the new tests green, and a batch of the surrounding suite green, plus the same tests run against unpatched If box 1 is meant as "the tests you ran locally are green", that is true and I will tick it on a word from you. If it is meant literally as the full suite, it cannot be honestly ticked from Windows and I would rather it stay open than be wrong. |
|
Thanks for verifying it independently — and for the note on the neighbouring options; that was the part I most wanted a second pair of eyes on, since Checklist is now 4/4 and the PR is Ready. I ticked box 1 on your word, and rewrote the note under it so the record stays exact: what ran green here is typecheck, the new tests, the same tests against unpatched No new head has been pushed since your verification — still I opened one more in the same area, #2059: the compatibility behavior report matches those same |
…t wire `noStructuredOutputModels` is documented, in every locale, as "Exact model IDs whose `openai-chat` endpoint rejects `response_format`. Only an exact requested-model match omits the field; structured-output translation stays enabled for every other `openai-chat` model." The Responses ingress enforces that, and tests/openai-chat-hardening.test.ts already pins a `:tag` sibling keeping the field there. The native Chat passthrough added in lidge-jun#1467 matched through `modelInList` instead, which also matches the pre-colon prefix. On a provider that serves Ollama-style tags -- ollama-cloud ships `gpt-oss:120b`, `qwen3-coder:480b`, `qwen3.5:397b`, `gemma4:31b` -- a `noStructuredOutputModels: ["gpt-oss"]` entry therefore stripped `response_format` from `gpt-oss:120b` on /v1/chat/completions while /v1/responses kept it. The caller asked for JSON and silently got prose, on a model the operator never opted out. That is the failure lidge-jun#1424 called out when it chose the exact boundary: a wider match "would silently return prose for siblings that support JSON Schema". The sibling gates on the lines above keep `modelInList` -- `noVisionModels` is documented as tolerating an Ollama `:size` tag, this one is not -- so the comment now says why this gate differs. Four tests, next to the existing Responses-side assertions so the two ingresses read as a pair: exact id opts out, a `:tag` sibling does not, the full `:tag` id does when listed, and an unrelated model is untouched. The `:tag` sibling case fails on current dev. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
37cd730 to
333a24c
Compare
Summary
noStructuredOutputModelsis documented — inreference/configuration/providers.mdand its fr/ja/ko/ru/tr/zh-cn translations — as:The Responses ingress enforces that, and
tests/openai-chat-hardening.test.tsalready pins a:tagsibling keeping the field there.The native Chat passthrough (
buildOpenAIChatPassthroughRequest, added in #1467) matched throughmodelInListinstead, which additionally matches the pre-colon prefix:Why it bites
ollama-cloudships tagged ids verbatim —gpt-oss:120b,qwen3-coder:480b,qwen3.5:397b,gemma4:31b(src/providers/registry.ts). With{ "noStructuredOutputModels": ["gpt-oss"] }a request for
gpt-oss:120blostresponse_formaton/v1/chat/completionsbut kept it on/v1/responses. Same provider, same config key, same upstream — the answer depended on which client API the caller used, and on the chat wire the caller asked for JSON and silently got prose, on a model the operator never opted out.That is precisely the failure #1424 named when it chose the exact boundary: a wider match "would silently return prose for siblings that support JSON Schema."
Change
One gate, exact-matched, matching the Responses side. The neighbouring gates keep
modelInListdeliberately —noVisionModelsis documented as "matching tolerates an Ollama:sizetag", this option is documented the other way — so the comment now records why this one differs.No documentation change: the docs already describe the behaviour this restores.
Tests
Four cases in the existing
openai-chat response_format emissiondescribe, placed directly after the Responses-side assertions so the two ingresses read as a pair:response_format:tagsibling, only the base listed:tagid listedThe
:tagsibling case fails on currentdevand passes with this change.Verification
Run on the exact head of this branch:
I did not run the full suite: this is a Windows machine and
bun run testpanics partway through on Bun 1.3.14 (index out of bounds: index 0, len 0), so a result from it would be a truncated log rather than a result. The batches above were run throughscripts/test.tsso each file keeps its isolated home.Summary by CodeRabbit
Bug Fixes
Tests
Review readiness checklist
This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:
All CI tests are green on my local testing.
I pushed my PR to the latest dev commit.
I resolved all correct Codex and CodeRabbit findings.
My PR is ready for review.