Skip to content

fix(gateway): preserve tool calls in non-streaming Chat responses - #505

Open
tom062119 wants to merge 1 commit into
qxcnm:mainfrom
tom062119:fix/nonstream-chat-tool-calls
Open

tom062119 wants to merge 1 commit into
qxcnm:mainfrom
tom062119:fix/nonstream-chat-tool-calls

Conversation

@tom062119

Copy link
Copy Markdown

变更摘要

修复非流式 /v1/chat/completions 经 Responses 兼容转换时丢失函数调用的问题。v0.6.4 的上游即使返回 function_call,客户端仍会收到空的 assistant 文本、没有 tool_calls,且 finish_reason 为 stop,导致工具执行循环提前结束。

转换现在保留全部函数调用的 ID、名称和参数,并使用 finish_reason: "tool_calls"。仅有工具调用时,message.content 为 null;同时存在文本时保留文本。沿用现有工具名称还原映射。

复现方式

在启用了 Responses-to-Chat 兼容转换的网关上,发送非流式 Chat 请求;模型名称替换为网关中可用的模型:

{
  "model": "<available-model>",
  "stream": false,
  "messages": [{"role": "user", "content": "Call echo_probe with value ok."}],
  "tools": [{
    "type": "function",
    "function": {
      "name": "echo_probe",
      "description": "Echo a harmless test value.",
      "parameters": {
        "type": "object",
        "properties": {"value": {"type": "string"}},
        "required": ["value"],
        "additionalProperties": false
      }
    }
  }],
  "tool_choice": {"type": "function", "function": {"name": "echo_probe"}}
}

当上游 Responses 输出包含如下条目时:

{"type":"function_call","call_id":"call_echo","name":"echo_probe","arguments":"{\"value\":\"ok\"}"}

修复前没有 message.tool_calls,且结束原因为 stop;修复后返回对应的 tool_calls[0],id 为 call_echo,结束原因为 tool_calls。本补丁也覆盖上游 SSE 汇总为非流式 JSON 的路径。

改动范围

  • Frontend
  • Desktop / Tauri
  • Service
  • Gateway / Protocol Adapter
  • Docs / Governance
  • Workflow / Release

主要文件

  • crates/service/src/gateway/observability/http_bridge/body_conversion.rs:传递工具名称还原映射,将 Responses function_call 转为 Chat tool_calls。
  • crates/service/src/gateway/observability/http_bridge/delivery_tests.rs:新增并行工具调用、名称还原、文本与工具共存、SSE 汇总后纯工具调用的回归测试;调整现有调用点。

验证

  • pnpm -C apps run test
  • pnpm -C apps run build
  • pnpm -C apps run test:ui
  • cargo test --workspace
  • 其他本地验证已说明

已执行的实际验证(Windows x86_64 GNU,v0.6.4 + 本补丁):

cargo test --locked --release -p codexmanager-service --lib http_bridge -- --test-threads=2
177 passed; 0 failed; 1683 filtered out

cargo build --locked --release -p codexmanager-service --bin codexmanager-service
passed

git diff --check
passed

已用编译后的服务完成无业务副作用的 echo 工具往返验证:非流式 Chat 返回工具调用,提交对应工具结果后模型正常完成回答。

未执行前端和完整 workspace 测试:改动仅涉及 HTTP bridge 的两个 Rust 文件,已运行相关回归集和服务 release 构建。

风险与影响面

  • 影响范围为 Responses-to-Chat 的非流式响应转换。
  • 沿用现有文本、reasoning、图片、usage 和工具名称还原逻辑;没有新增配置项、数据库迁移或认证变更。
  • 优先使用 call_id,缺失时兼容 id;参数保留原始字符串。
  • 分支基于 v0.6.4。提交前已检查 upstream main f14bd735dca9203d7430958480b82ba7713641a9,这两个文件仍与 v0.6.4 相同。

备注

复现数据为合成测试数据,不包含部署凭据或业务数据。

@qxcnm

qxcnm commented Oct 26, 2026

Copy link
Copy Markdown
Owner

已复核当前提交 2fdb3be4 与最新 main 430eddb0,目前暂不合并。

这项核心修复已由主线提交 bb40540c 实现,当前 main 已保留非流式 Chat 的函数调用、纯工具消息的 content: null 和 finish_reason: "tool_calls",并包含 JSON/SSE、原生 Responses/Chat、流式工具调用的回归覆盖。

当前分支虽然没有文本冲突,但与最新 main 合成后执行 cargo test --locked -p codexmanager-service --lib http_bridge --no-run 实际失败:转换函数新增第二参数后,stream_readers/chat_completions_tool_tests.rs:504 的调用未同步(E0061);新增 text.is_empty() 又借用了主线已移动的 text(E0382)。合成版本无法编译,因此未执行后续流式/非流式运行测试。

此外,当前实现叠加到主线会重复执行工具调用转换,并回退主线已支持的对象类型 arguments 和空 call_id 回退行为。请先基于最新 main 更新,去掉已覆盖的转换实现,仅保留确有增量的改进/测试,并保留现有兼容行为后重新验证。感谢提供复现与回归用例。

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