Bug: A Responses API tool call announced but never completed is dropped, and the turn mislabelled no_response_body
维护者通常 1 天内回复
已经有一个关联 PR 被合并了。
- #1419 来自 @evaline-ju —— 已合并
评估
调研方向
Start in core/plugins/inferenceparser/plugin.go, where InferenceParser.OnResponseFrame handles the Responses API frames and the terminal arm records pctx.Skip("no_response_body"). Read the existing Anthropic test TestInferenceParser_AnthropicMessages_StreamToolUseOnlyIsNotASkip for the expected handling of truncated arguments. Done means the four-shape table in the issue holds, with the added-only case returning one call with empty arguments and the duplicate-added case still returning one call.
由索引模型根据 Issue 内容生成。
描述
What happens
When a Responses API stream emits response.output_item.added for a tool call but the stream ends before the matching response.output_item.done — a turn cancelled in that window — the call is lost entirely. The turn then also gets mislabelled as having carried no response body.
Measured against main (c8415388) by feeding frames through InferenceParser.OnResponseFrame:
| Frame shape | Result on main |
|---|---|
added → done(incomplete) → terminal |
1 call, partial args "const r = await" |
added → done(in_progress), no terminal |
1 call, partial args |
added only, stream cut |
0 calls ← this bug |
Two of the three shapes are fine and should stay as they are: keeping truncated arguments is what the Anthropic dialect in this same package does deliberately — TestInferenceParser_AnthropicMessages_StreamToolUseOnlyIsNotASkip pins "Arguments stay as the model left them — truncated, not discarded".
Why the mislabelling matters more than the missing call
InferenceParser's terminal arm records pctx.Skip("no_response_body") when Completion, FinishReason, TotalTokens and ToolCalls are all empty (core/plugins/inferenceparser/plugin.go, the "Tool calls count as a body" guard). A turn cancelled mid-tool-call has no completion text, no finish reason and no usage block — so with the call dropped too, all four are empty and the row is labelled as carrying nothing, when it demonstrably started a tool call. The guard's own comment says that outcome is what it exists to prevent; it just cannot see a call that was never recorded.
Fix
Track a tool call from response.output_item.added (call_id + name, no arguments yet), and have response.output_item.done fill in the arguments on that same entry rather than appending a second one. An id-keyed lookup is needed so a call seen on both events is not double-counted.
Add no status-based exclusion. This is the part worth stating explicitly, because it was tried and was wrong — see #1331, which excluded items whose status was incomplete/in_progress and thereby dropped calls main keeps, regressing two shapes to fix one. The concern behind it (truncated arguments presented as complete) is already answered: shape 1 sets FinishReason: "incomplete", which is the signal a consumer should read.
Expected result — strictly additive, nothing else changes:
| Shape | Before | After |
|---|---|---|
| added → done(incomplete) | 1, partial | 1, partial (unchanged) |
| added → done(in_progress) | 1, partial | 1, partial (unchanged) |
| added only | 0 | 1, empty args |
| normal complete call | 1, full | 1, full (unchanged) |
Tests worth having
- One per row of that table, asserting the count AND the arguments, so a future change that blanks the partial-argument rows fails loudly.
- A duplicate-
addedguard case: twoaddedevents for onecall_idmust not yield two calls. - A
done-with-no-addedcase (not expected on live traffic, but the fallback should not drop the call). - A
function_call-typed case alongsidecustom_tool_call, since #1331's review found both arms could be individually deleted with the suite staying green.
Severity
Low. It needs a cancellation landing in the window between added and done, which is narrow — Codex emits custom_tool_call_input.delta events in between, so the window is real but small. Filed because it is now a well-understood small fix rather than an open question.
Context: #1331 (closed, has the full analysis), #942, #1239.
Assisted-By: Claude (Anthropic AI) [email protected]
- 主要语言
- Go
- 星标
- 21
- 派生
- 43
- 平均合并
- 14 小时 49 分钟
- 30 天内合并 PR
- 236
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 没有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
rossoctl/cortex 的其他 Issue
-
UI shows costs for all sessions even when drilled into a session可能已有人在做 @esnible 于 13 天前认领。 未关闭
难度 2/5 1-3 小时 新手友好度 70/100
rossoctl/cortex#1116 · 2 条评论 · 已指派 1 人 ·
维护者通常 1 天内回复
-
难度 5/5 一周以上 新手友好度 35/100
维护者通常 1 天内回复
-
Lineage plugin records no payload for plain-HTTP exchanges可能已有人在做 @JoshSag 于 1 天前认领。 未关闭enhancement in-progress
rossoctl/cortex#1505 · 已指派 1 人 ·
维护者通常 1 天内回复
-
enhancement laptop
难度 5/5 一周以上 新手友好度 35/100
维护者通常 1 天内回复
-
enhancement laptop
难度 5/5 一周以上 新手友好度 25/100
维护者通常 1 天内回复
相似的 Issue
-
[doc-healer] DDUw improvement: retry integrity-filtered issue reads with direct single-item lookups未关闭automation documentation
难度 2/5 1-3 小时 新手友好度 68/100
维护者通常 1 天内回复
-
bug needs-triage
难度 2/5 1-3 小时 新手友好度 80/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 78/100
containerd/containerd#14476 · 1 条评论 ·
维护者通常 1 天内回复
-
难度 1/5 1-3 小时 新手友好度 82/100
Infisical/infisical#8987 · 1 条评论 ·
维护者通常 1 天内回复
-
scan framework and scan control accept empty and whitespace-only policy names in Args validation可能已有人在做 @WKr1shna 今天认领。 未关闭
难度 2/5 1-3 小时 新手友好度 78/100
维护者通常 1 天内回复