fix(relay): preserve presence/frequency penalty in Responses conversion - #6654
Conversation
`dto.OpenAIResponsesRequest` never declared `presence_penalty` / `frequency_penalty`. The non-passthrough path parses the request into that struct and re-marshals it, so both fields were silently dropped before reaching the upstream, while `temperature` / `top_p` survived because they are declared. Passthrough mode forwards the raw body and was unaffected. - Declare both fields as `*float64` with `omitempty` so an explicit `0` is preserved and an absent field stays omitted. - Carry them across the Responses <-> Chat Completions converters, which rebuild the request field by field. - Clear them in the Codex adaptor next to `temperature`, since the Codex backend rejects sampling parameters it does not accept. Note that the official OpenAI Responses API does not accept these two parameters; OpenAI-compatible upstreams such as vLLM do, and forwarding them is what the reporter's setup needs. Fixes QuantumNous#6614
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
🚧 Files skipped from review as they are similar to previous changes (5)
WalkthroughThe Responses request DTO now includes frequency and presence penalties. Chat Completions and Responses conversions preserve these fields, including explicit zero values. Codex conversion removes them with other unsupported parameters. ChangesPenalty parameter handling
Estimated code review effort: 2 (Simple) | ~10 minutes Sequence Diagram(s)sequenceDiagram
participant ChatCompletionsRequest
participant ChatToResponses
participant OpenAIResponsesRequest
participant ResponsesToChat
ChatCompletionsRequest->>ChatToResponses: marshal penalty values
ChatToResponses->>OpenAIResponsesRequest: set frequency_penalty and presence_penalty
OpenAIResponsesRequest->>ResponsesToChat: provide raw JSON penalty values
ResponsesToChat->>ChatCompletionsRequest: parse penalty values or return labeled errors
Possibly related PRs
Suggested reviewers: Poem
🚥 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 |
|
新增的参数改成 json.RawMessage 类型吧 |
Change the two new fields on `dto.OpenAIResponsesRequest` from `*float64` to
`json.RawMessage`, matching the struct's convention for passthrough extension
parameters that the official OpenAI Responses API does not declare. Explicit
zero values still round-trip (a `RawMessage("0")` is never dropped by
`omitempty`), and the Codex adaptor's nil-clearing is unaffected.
The Responses->Chat converter now parses the raw scalar into `*float64` for
the chat request, returning an error on malformed input; the Chat->Responses
converter marshals the typed field back to raw bytes.
已修正,两个字段已改为 json.RawMessage,转换器里做了对应的 raw↔float64 转换,测试已通过。 顺带一提:AGENTS.md 里写的是 optional scalar fields MUST use pointer types with omitempty,而 json.RawMessage 底层是 []byte,不是指针类型,字面上和这条稍微有点出入。 |
上游 253a74d(QuantumNous#6654) 与 7d09c69(QuantumNous#6861) 新增的测试用例调用 ResponsesRequestToChatCompletionsRequest / ChatCompletionsRequestToResponsesRequest 时使用的是旧签名,而本地 PR QuantumNous#6655 补丁已为这两个函数引入 convmeta.Meta 首参(reasoning_content 跨转换保留的核心机制)。 合并后函数定义取本地版本、测试取上游版本,导致 relaykit 模块 vet 失败。为 5 处新增用例补上 nil meta。 受影响用例: - to_oai_chat_req_test.go: penalty 转换 2 处 - to_oai_responses_req_test.go: prompt_cache_key 2 处 / penalty 1 处 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014AZbTUCaSkfaTLkrfzKGSP
dto.OpenAIResponsesRequestnever declaredpresence_penalty/frequency_penalty. The non-passthrough path parses the request into that struct and re-marshals it, so both fields were silently dropped before reaching the upstream, whiletemperature/top_psurvived because they are declared. Passthrough mode forwards the raw body and was unaffected.*float64withomitemptyso an explicit0is preserved and an absent field stays omitted.temperature, since the Codex backend rejects sampling parameters it does not accept.Note that the official OpenAI Responses API does not accept these two parameters; OpenAI-compatible upstreams such as vLLM do, and forwarding them is what the reporter's setup needs.
Fixes #6614
Important
📝 变更描述 / Description
/v1/responses在非透传模式下会丢掉presence_penalty和frequency_penalty。根因是
dto.OpenAIResponsesRequest从来没声明过这两个字段。非透传路径的流程是「按结构体解析请求 → 重新序列化发给上游」,结构体里没有的键在序列化那一步就没了;temperature、top_p因为声明了所以能正常到达。透传模式直接转发原始 body,所以不受影响 —— 这也解释了 issue 里三组对比的结果差异。改动分三处:
OpenAIResponsesRequest加上这两个字段,类型用*float64+omitempty。用指针是为了区分「没传」和「显式传 0」—— 不传是nil不下发,传0是非nil照常下发;非指针配omitempty会把0静默吃掉。temperature一起置空。Codex 后端会拒绝它不认的采样参数,字段加进 DTO 后会开始转发出去,这里补上剥离以保持原有行为。测试覆盖了显式零值不被
omitempty吞掉、两个转换器方向的正值/零值/未设置三种情况,以及 Codex 的剥离。🚀 变更类型 / Type of change
🔗 关联任务 / Related Issue
✅ 提交前检查项 / Checklist
Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。Summary by CodeRabbit
Bug Fixes
Tests