Skip to content

fix(tools): interleave parallel tool calls with results to prevent upstream internal error - #261

Open
justhil wants to merge 1 commit into
dwgx:masterfrom
justhil:fix/interleave-parallel-tool-calls
Open

fix(tools): interleave parallel tool calls with results to prevent upstream internal error#261
justhil wants to merge 1 commit into
dwgx:masterfrom
justhil:fix/interleave-parallel-tool-calls

Conversation

@justhil

@justhil justhil commented Sep 12, 2026

Copy link
Copy Markdown

改了什么 / What changed

src/handlers/tool-emulation.js 中增加了 interleaveParallelToolMessages 工具函数,并在 DEVIN_CONNECTnativeStructured 链路以及直接消息处理流程中接入。

  • 之前:OpenAI / Responses 客户端在多轮对话中发来的并行工具调用历史(单条 assistant 消息携带多个 tool_calls,后跟多条 role: 'tool' 结果)会原样或直接批量压入底层消息流,导致上游 Devin 协议状态机无法解析多工具输出,直接报错 503 UPSTREAM_INTERNALan internal error occurred)。
  • 现在:对开启了 nativeStructured(或无工具直接历史)的多轮消息流自动按 tool_call_id 进行拆分与成对交错重排(assistant(tool_1) -> tool_1_result -> assistant(tool_2) -> tool_2_result),同时保留首个 tool call 上的助手正文与 reasoning 内容,使上游状态机能够正常推进。

为什么 / Why

修复客户端在使用支持并发工具调用的 Agent 框架调用 swe-2-maxswe-1-7 等模型时,第 2 轮带回多个工具结果时必然抛出 UPSTREAM_INTERNAL (an internal error occurred) 的问题。

验证过程说明:

  1. 报错重现与定位:构造标准 OpenAI 并发工具调用对话历史(1 条包含 2 个 tool_callsassistant 消息 + 2 条对应的 tool 结果消息),上游稳定复现 503 UPSTREAM_INTERNAL
  2. 消歧验证:保持相同 prompt 与工具参数,仅将历史改成单工具时完全正常;将多工具结果改成交错的 call -> result -> call -> result 单回合序列后,上游即可稳定返回 200 并流畅输出流式数据。
  3. 范围约束:确保该交织仅在 nativeStructured 模式及直接连接链路上生效,不影响 kimi_k2 等具有自身批处理 dialect 格式的纯文本仿真路径(test/tool-emulation.test.js 全部 65 个测试用例无回归且 100% pass)。

测试 / Testing

测试环境:Node.js

  1. 单元测试执行(相关套件全绿)
node --import ./test/setup-env.mjs --test test/tool-emulation.test.js

输出结果:

# tests 65
# suites 8
# pass 65
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 574.913653
  1. 真实上游 API 连通性测试(swe-2-max 实机验证)
  • 测试并发多工具历史(/v1/chat/completions 流式):
    Status: 200, Has UPSTREAM_INTERNAL: False,首 chunk 及推理正常输出。
  • 测试多工具多轮对话历史(/v1/responses 流式):
    Status: 200, Has response.failed: False,事件流正常生成。

Checklist

  • 代码风格和现有文件一致 / Code style matches existing files
  • 没有引入 npm 运行时依赖 / No new npm runtime dependencies (project is zero-dep)
  • 测试跑过了,贴了结果 / Tests run, output pasted above
  • 新行为有测试,且断言行为而不是 grep 源码文本 / New behaviour has tests that assert behaviour, not source text
  • 新开关默认关;若默认开,已加进 test/default-on-switch-registry.test.js 台账
  • 加了开关就同步了文档
  • 涉及 wire 协议改动时,注明字段号来源;未经实测的坐标默认关并在注释里直说不确定
  • 涉及 dashboard UI 用 App.confirm / App.prompt,不用浏览器原生 alert/confirm
  • commit message 没有任何 AI 署名尾注Co-Authored-ByGenerated with 等)

@justhil
justhil force-pushed the fix/interleave-parallel-tool-calls branch from d252adc to 181dda9 Compare September 12, 2026 13:07
@Rostopher

Copy link
Copy Markdown

独立复现确认:这个 PR 也修好了 Anthropic /v1/messages 路径上的同一问题。

复现路径(与你测试的 OpenAI/Responses 不同):pi coding agent 走 anthropic-messages 连 DEVIN_CONNECT(swe-2-max,23 tools),第二轮发出 2 个并行 bash 工具调用后,会话里永久带上"单 assistant 消息 2 个 tool_use"的历史,此后每一轮都稳定 UPSTREAM_INTERNAL (an internal error occurred)——历史中毒导致整个会话不可用,与上下文大小无关。

验证方式:抓取 pi 的真实请求体(58KB)后逐段二分回放——前 3 条(单 tool_use 往返)成功,加入并行调用对后确定性失败,内容全部替换为 "ok" 仍失败;合成最小用例(一轮 2 个 tool_use + 2 个结果)同样失败,1 个 tool_use 则成功。

对你补丁的实测:本地起源码实例做对照,未打补丁时上述捕获负载稳定 529,打上本 PR 的改动后同一负载返回 200 正常输出。src/devin-connect.js 的 native #6 tool_call 编码会把一轮多个 call 编成连续两条 role=2 消息,上游状态机不接受;你前置的 interleave 把历史拆成 call/result 交替后,wire 层生成的就是配对序列,所以这条路径无需额外改动。

另外提醒一个用户侧的连带现象:已经中毒的历史会话在修复前救不回来(并行调用记录留在上下文里),客户端只能 /clear 开新会话。

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