Skip to content

fix(openai): 工具调用与结果按 call_id 配对,Responses 请求发出前再检查一次 - #190

Merged
Slacker-LLC merged 1 commit into
mainfrom
fix/responses-tool-pairing
Oct 10, 2026
Merged

Slacker-LLC merged 1 commit into
mainfrom
fix/responses-tool-pairing

Conversation

@Slacker-LLC

@Slacker-LLC Slacker-LLC commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner

问题

OpenAI(Responses 路径)的对话一旦出现 400 No tool output found for function call …,这个对话就再也发不出去,新开对话正常。用户在 GPT-6 Luna 上遇到过这条报错;之后同一个对话里又出现了 400 Unknown parameter: '…'(参数名被横幅截断),新对话正常,旧对话无法继续。

原因

Responses 路径给工具调用存的是组合 id call_x|fc_y,结果里可能只存 call_x(换模型、回退、从数据库重载、中转改写 id 都会出现)。我们的两处历史修复(OutgoingHistory.dropOrphanedToolParts、ToolBatchRepair)按整串 id 比较,把配对正确的两条当成两个孤儿:删掉真实结果,再塞一个"中断"占位。请求每次都从同一份历史重建,所以每次重试都带着同样的缺口。

对照上游

OpenMinis 1.14 有两样东西,我们没有:

  • ToolPairing.key:取 | 之前的部分作为配对键,上游的历史修复都用它。
  • ResponsesToolPairing:Responses 请求写入 input 前的最后一道检查。

这个 PR 把它们移植过来,并把两处历史修复改成按配对键比较。

改动

  • 新增 provider/ToolPairing.kt、provider/openai/ResponsesToolPairing.kt(取自上游,原样)。
  • OpenAIProviderResponses.kt:input 写入前过 ResponsesToolPairing.sanitize:没有对应调用的输出丢弃、重复输出丢弃(保留第一个)、没有输出的调用补占位(结尾那一串调用不补)。修复发生时写 warning 日志。
  • OutgoingHistory.dropOrphanedToolParts、ToolBatchRepair:按 ToolPairing.key 比较。
  • ToolResultImageSerializationTest:夹具里有一条没有调用的结果,新的检查会丢弃它;按上游的方式补上对应调用。
  • PROVENANCE.md 登记移植,CHANGELOG.md 记录。

测试

  • 新增 ResponsesToolPairingTest(16 个):线上请求形状(孤儿输出不发出、重复输出只发第一个、中途缺失补占位、结尾调用不补、组合 id 与裸 id 仍配对、完好历史原样发出)、整理器本身、配对键,以及两处历史修复的混合 id 用例。
  • 否定用例:真正的孤儿结果仍被删,真正缺失的结果仍被补;混合 id 的配对对象不被修改。
  • 全量单测 3551 个全部通过(1 个跳过);lintDebug、运行时包边界、文档溯源、构建清理三个检查通过。

未验证

  • 没有在真机上复现原来的卡死对话。已经坏掉的对话需要升级后再发一次才能验证能否恢复。
  • Unknown parameter 这条 400 不能确定这个 PR 能修好。 它和"No tool output"是两种报错,只是同样表现为旧对话卡死、新对话正常。需要完整报错文本(参数名)才能判断;fix(chat): 模型 400 错误按原因分类,横幅可点开 #188 发版后点开横幅可以看到。上游对照里,我们独有而上游没有的请求改动是回放 reasoning 原始条目和托管网页搜索,是下一步的首要怀疑对象,但没有证据。

🤖 Generated with Claude Code

https://claude.ai/code/session_01PV4ECd3Yzu9k81sw7vHjS3


Generated by Claude Code

…e Responses request before sending

A chat whose history held a tool call stored as "call_x|fc_y" and its result stored as
"call_x" kept failing with 400 "No tool output found for function call" on every send.
The history repairs compared raw ids, called the pair two orphans, deleted the real result
and left the request unpaired. They now compare ToolPairing.key (the part before "|").

Responses requests also pass through ResponsesToolPairing.sanitize just before sending:
results with no call and repeated results are dropped, calls with no result get a
placeholder (the trailing call run is exempt).

Both files and the call-site logic are ported from OpenMinis 1.14.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01PV4ECd3Yzu9k81sw7vHjS3
@Slacker-LLC
Slacker-LLC merged commit ea06b0e into main Oct 10, 2026
14 checks passed
@Slacker-LLC
Slacker-LLC deleted the fix/responses-tool-pairing branch October 10, 2026 19:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants