fix: 避免 Responses 流重复输出同一工具调用 - #6279
Conversation
WalkthroughThe stream converter reuses existing tool mappings before generating keys, preventing duplicate tool calls when terminal output repeats an earlier streamed function call. A regression test covers the complete event sequence. ChangesResponses tool-call deduplication
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related issues
Possibly related PRs
Suggested reviewers: Poem
🚥 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 |
da6f19a to
3efbbab
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
service/relayconvert/internal/oai_responses/to_oai_chat_stream_resp.go (1)
272-288: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winFlatten the nested lookups to improve readability.
As per coding guidelines, prefer clear branches and well-named local variables over deep nesting or layered control flow. Flattening the fallback lookups with an
else ifbranch and short-circuit evaluation makes the code much more direct and easier to read.♻️ Proposed refactor
if tool == nil { - if itemID := responseStreamEventItemID(event); itemID != "" { - if existingKey := s.itemIDToKey[itemID]; existingKey != "" { - tool = s.toolByKey[existingKey] - } - } - if tool == nil { - if callID := strings.TrimSpace(event.Item.CallId); callID != "" { - if existingKey := s.callIDToKey[callID]; existingKey != "" { - tool = s.toolByKey[existingKey] - } - } - } + itemID := responseStreamEventItemID(event) + callID := strings.TrimSpace(event.Item.CallId) + + if itemID != "" && s.itemIDToKey[itemID] != "" { + tool = s.toolByKey[s.itemIDToKey[itemID]] + } else if callID != "" && s.callIDToKey[callID] != "" { + tool = s.toolByKey[s.callIDToKey[callID]] + } + if tool != nil { s.toolByKey[key] = tool } }🤖 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 `@service/relayconvert/internal/oai_responses/to_oai_chat_stream_resp.go` around lines 272 - 288, Refactor the fallback lookup in the tool-resolution block to use clear short-circuit branches: resolve by item ID first, then use an else-if fallback for the trimmed call ID lookup only when no tool was found. Preserve the existing mappings and final s.toolByKey[key] assignment, while reducing nested conditionals and retaining the current lookup behavior.Source: Coding guidelines
🤖 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.
Nitpick comments:
In `@service/relayconvert/internal/oai_responses/to_oai_chat_stream_resp.go`:
- Around line 272-288: Refactor the fallback lookup in the tool-resolution block
to use clear short-circuit branches: resolve by item ID first, then use an
else-if fallback for the trimmed call ID lookup only when no tool was found.
Preserve the existing mappings and final s.toolByKey[key] assignment, while
reducing nested conditionals and retaining the current lookup behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: e91b774e-1bc0-48d6-bc98-8489dab3ef3a
📒 Files selected for processing (2)
service/relayconvert/internal/oai_responses/to_oai_chat_resp_test.goservice/relayconvert/internal/oai_responses/to_oai_chat_stream_resp.go
🚧 Files skipped from review as they are similar to previous changes (1)
- service/relayconvert/internal/oai_responses/to_oai_chat_resp_test.go
|
Closing this duplicate submission; the fix remains available on our fork branch for internal use. |
Important
📝 变更描述 / Description
Responses 流在增量事件中已经输出工具调用后,终止事件的完整 response.output 可能再次携带同一 function_call。此前状态机在终止阶段因缺少 output_index,会按 item_id 创建第二个工具状态。
本 PR 在创建工具状态前先查询已有的 output_index、item_id 和 call_id 映射,命中时复用原状态;只有均未命中时才沿用原逻辑创建新 key。同时加入完整事件序列的回归测试。
本 PR 的代码与测试由 AI 辅助生成,提交者已对改动范围、状态机逻辑和测试结果进行审查。
🚀 变更类型 / Type of change
🔗 关联任务 / Related Issue
✅ 提交前检查项 / Checklist
📸 运行证明 / Proof of Work
修复前,真实事件序列生成的工具 index 为:
修复后回归测试结果为:
已通过:
Summary by CodeRabbit