fix: add per-model structured output opt-out - #1424
Conversation
📝 WalkthroughWalkthroughThe provider configuration adds ChangesStructured-output compatibility
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant ChatClient
participant OpenAIChatAdapter
participant RoutedProvider
ChatClient->>OpenAIChatAdapter: Send structured-output request
OpenAIChatAdapter->>OpenAIChatAdapter: Check noStructuredOutputModels
OpenAIChatAdapter->>RoutedProvider: Forward request without response_format for matching model
RoutedProvider-->>ChatClient: Return response
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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 |
|
✅ Deterministic PR hygiene checks passed. |
Wibias
left a comment
There was a problem hiding this comment.
Request changes based on a full review of the current head.
Merge blockers / required fixes:
-
noStructuredOutputModelsis documented and implemented as an exact per-model escape hatch, but the adapter usesmodelInList(). That helper intentionally family-matches colon-tagged variants (fooalso matchesfoo:variant). This can disableresponse_formatfor sibling/variant models that were not explicitly listed, which breaks the mixed-capability-gateway contract this PR is introducing. Use exact equality for this field (for examplelist.includes(parsed.modelId)) or a dedicated exact-match helper, and add a negative regression provingfoodoes not matchfoo:structured. -
Persisted
config.jsonand Management API PATCH normalize this field differently. PATCH trims and deduplicates entries, but schema/config loading only verifies thatentry.trim()is nonblank and preserves the original string. A manually persisted value such as" deepseek-v4-flash "is accepted but will not match the runtime model id. Normalize trim/dedupe at the config boundary so every management surface has identical semantics.
I did not find a security vulnerability in this PR, but the first issue is a direct contract violation and should be fixed before merge. After fixes, refresh onto current dev and rerun exact-head CI.
9c88233 to
1a4524d
Compare
|
Addressed both blockers on the current head
Verification on the rebased head:
@Wibias please re-review the exact head after CI completes. |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs-site/src/content/docs/reference/configuration/providers.md`:
- Line 106: Update the noStructuredOutputModels? reference row in
docs-site/src/content/docs/reference/configuration/providers.md:106-106 to state
that entries are exact model IDs, response_format is omitted only for exact
matches, and structured-output translation remains enabled for other openai-chat
models. Apply the same clarification in
docs-site/src/content/docs/ja/reference/configuration/providers.md:96-96,
docs-site/src/content/docs/ko/reference/configuration/providers.md:96-96,
docs-site/src/content/docs/ru/reference/configuration/providers.md:109-109, and
docs-site/src/content/docs/zh-cn/reference/configuration/providers.md:96-96
using each document’s language.
In `@tests/management-provider-validation.test.ts`:
- Around line 147-152: Remove the duplicated type-assertion fragments in
tests/management-provider-validation.test.ts at lines 147-152 and 1166-1169:
keep only one closing `} as OcxConfig) as ...` assertion at the first site and
one `providers: Record<string, { noStructuredOutputModels?: string[] }>;`
declaration at the second, restoring valid TypeScript syntax.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 003f6489-ec7f-49b5-97ab-2a328cbdbc35
📒 Files selected for processing (15)
docs-site/src/content/docs/ja/reference/configuration/providers.mddocs-site/src/content/docs/ko/reference/configuration/providers.mddocs-site/src/content/docs/reference/configuration/providers.mddocs-site/src/content/docs/reference/proxy-formats.mddocs-site/src/content/docs/ru/reference/configuration/providers.mddocs-site/src/content/docs/zh-cn/reference/configuration/providers.mdsrc/adapters/openai-chat.tssrc/config.tssrc/server/auth-cors.tssrc/server/management/provider-routes.tssrc/types.tsstructure/04_transports-and-sidecars.mdtests/chat-completions-endpoint.test.tstests/management-provider-validation.test.tstests/openai-chat-hardening.test.ts
1a4524d to
a607411
Compare
|
Updated #1424 on latest
Verification on exact head
The branch was rebased onto current |
Wibias
left a comment
There was a problem hiding this comment.
Approving. Earlier blockers (exact includes match instead of modelInList, trim/dedupe at the config boundary) are fixed on this head, docs now state the exact-match contract, and the remaining CodeRabbit duplicate-cast note is a false positive against the current test file.
|
Thanks @Ingwannu — this was useful because it adds a precise per-model escape hatch for gateways that reject Merging now. |
Summary
response_formattranslation as the default for routedopenai-chatmodels.noStructuredOutputModels, so only exact listed model IDs omit the field.Closes #1415
Verification
taskset -c 0,1 nice -n 10 bun test tests/chat-completions-endpoint.test.ts tests/management-provider-validation.test.ts tests/openai-chat-hardening.test.ts— 139 pass, 0 fail on exact current head.taskset -c 0,1 nice -n 10 bun run typecheck— passed on exact current head.taskset -c 0,1 nice -n 10 bun run privacy:scan— passed on exact current head.cd docs-site && taskset -c 0,1 nice -n 10 bun install --frozen-lockfile && taskset -c 0,1 nice -n 10 bun run build— passed, 221 pages built on exact current head.git diff --check— passed.codex-shimfixture-isolation baseline failure that reproduced unchanged on its base. Refreshed exact-head platform CI is running after the rebase.a607411453d1a568e6cff93e6bb3c55520b11260, rebased ontodevdd0078416e1ac1efb70fcf088f49f11af6517db0.Checklist