fix(responses): preserve polymorphic reasoning and Codex metadata - #2178
Conversation
Chat-compatible upstreams (e.g. CC-Switch bridge) return
reasoning_content as a plain string on function_call items. The
native Responses API uses an array of {type, text} objects.
Add PolymorphicReasoningContent (same pattern as Input) so both
shapes unmarshal without errors.
📝 WalkthroughWalkthroughThe change adds allowlisted Codex Responses metadata header forwarding for eligible pass-through requests. It also changes ChangesPass-through request headers
Polymorphic reasoning content
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: 🚥 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 |
There was a problem hiding this comment.
Pull request overview
This PR updates the OpenAI Responses transformer and orchestrator pass-through behavior to better preserve upstream semantics: it supports polymorphic reasoning_content shapes in Responses items and forwards a curated set of Codex protocol metadata headers when the original request body is replayed.
Changes:
- Update Responses
Item.reasoning_contentto accept either a string (Chat-compatible function-call items) or an array (native reasoning items), and preserve the original representation during JSON round trips. - Add transformer tests covering string/array/null/invalid
reasoning_contentcases. - Forward an explicit allowlist of Codex Responses metadata headers when request body pass-through is active, while keeping sensitive headers excluded (tested).
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| llm/transformer/openai/responses/model.go | Introduces a polymorphic JSON type for reasoning_content to accept string-or-array and round-trip the same representation. |
| llm/transformer/openai/responses/model_test.go | Adds coverage validating polymorphic reasoning_content unmarshalling, round-tripping, and invalid-type rejection. |
| internal/server/orchestrator/pass_through.go | Adds an allowlist and middleware to forward Codex Responses metadata headers when pass-through body replay is applied. |
| internal/server/orchestrator/pass_through_test.go | Adds tests ensuring allowlisted metadata is forwarded and sensitive/untrusted headers are not. |
| internal/server/orchestrator/orchestrator.go | Wires the new header pass-through middleware into the outbound middleware chain. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if !outbound.state.PassThroughApplied || outbound.state.LlmRequest == nil || | ||
| outbound.state.LlmRequest.APIFormat != llm.APIFormatOpenAIResponse || | ||
| outbound.state.LlmRequest.RawRequest == nil { | ||
| return request, nil |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@llm/transformer/openai/responses/model.go`:
- Line 580: Preserve explicit null for Item.ReasoningContent by tracking whether
the field was present and null during deserialization, then emitting
reasoning_content:null during serialization instead of omitting it; retain
normal behavior for non-null and absent values. Update
llm/transformer/openai/responses/model.go#L580-L580 accordingly, and change
llm/transformer/openai/responses/model_test.go#L118-L123 to expect
reasoning_content:null after the JSON round trip.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 8cf9feef-18c3-4887-9021-b9ce22323aa3
📒 Files selected for processing (5)
internal/server/orchestrator/orchestrator.gointernal/server/orchestrator/pass_through.gointernal/server/orchestrator/pass_through_test.gollm/transformer/openai/responses/model.gollm/transformer/openai/responses/model_test.go
| Summary []ReasoningSummary `json:"summary,omitempty"` | ||
| // Reasoning text content - array of reasoning text items. | ||
| ReasoningContent []ReasoningContent `json:"reasoning_content,omitempty"` | ||
| ReasoningContent *PolymorphicReasoningContent `json:"reasoning_content,omitempty"` |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Preserve explicit null during the JSON round trip.
On Line 580, JSON null sets Item.ReasoningContent to nil without calling PolymorphicReasoningContent.UnmarshalJSON. omitempty then removes the field during marshal. This converts a supported reasoning_content:null representation into an absent property.
Track field presence and explicit null separately in Item serialization. Update the test to require reasoning_content:null after the round trip.
llm/transformer/openai/responses/model.go#L580-L580: retain explicit-null state when decoding and emitnullwhen that state is set.llm/transformer/openai/responses/model_test.go#L118-L123: expect explicitnullinstead of an omitted property.
📍 Affects 2 files
llm/transformer/openai/responses/model.go#L580-L580(this comment)llm/transformer/openai/responses/model_test.go#L118-L123
🤖 Prompt for 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.
In `@llm/transformer/openai/responses/model.go` at line 580, Preserve explicit
null for Item.ReasoningContent by tracking whether the field was present and
null during deserialization, then emitting reasoning_content:null during
serialization instead of omitting it; retain normal behavior for non-null and
absent values. Update llm/transformer/openai/responses/model.go#L580-L580
accordingly, and change llm/transformer/openai/responses/model_test.go#L118-L123
to expect reasoning_content:null after the JSON round trip.
Summary
reasoning_contentWhy
Some Chat-compatible Responses upstreams attach a plain string
reasoning_contentto function-call items, while native Responses reasoning items use an array. Rejecting the string form breaks otherwise valid responses. In addition, dropping Codex protocol metadata while replaying the original body can change upstream behavior.Verification
Summary by CodeRabbit