Skip to content

fix(openai): filter empty-name tools from Codex + namespace input handling#76

Open
fmosca wants to merge 4 commits into
ZhiYi-R:mainfrom
fmosca:fix/namespaced-history
Open

fix(openai): filter empty-name tools from Codex + namespace input handling#76
fmosca wants to merge 4 commits into
ZhiYi-R:mainfrom
fmosca:fix/namespaced-history

Conversation

@fmosca

@fmosca fmosca commented Jun 4, 2026

Copy link
Copy Markdown

背景 / Background

在复杂的文件操作和多轮对话场景中,Codex 会将已经执行过的 Namespaced 工具调用及结果存入历史记录并传回。由于这些历史项中包含 namespace 信息(例如 namespace: "mcp__filesystem"),而 Moon Bridge 的入参结构体缺少该字段解析,导致历史工具调用丢失了 namespace 前缀,最终在上游重构时因工具名称不匹配触发 400 Bad Request 验证失败。

此外,由于 Codex 偶尔会传递一些不带有效名称的空工具定义,这会导致上游推理模型(例如 DeepSeek V4 / GLM)在严格验证阶段抛出 "Invalid tools[N].function.name: empty string" 的 schema 错误。

In complex file CRUD and multi-turn conversation loops, Codex serializes past namespaced tool calls into the conversation history with explicit "namespace" attributes. Due to the absence of namespace metadata parsing during conversion, historical calls were stripped of their namespace prefix, causing subsequent requests to fail with upstream 400 Bad Request validation errors. Furthermore, certain empty-named tools occasionally generated by Codex caused strict schemas on DeepSeek V4/GLM models to reject the payload.

修改内容 / Changes

1. 多轮对话历史还原 / Inbound Namespace Ingestion

  • 结构体扩展: 在 internal/protocol/openai/adapter.goinputItem 结构体中引入 Namespace 字段,以还原来自 Codex 传入的 "namespace" JSON 标识。
  • 前缀重构: 在 convertInput 过程中,如果输入项具有命名空间,则对其进行就地嵌套重构。将工具名称指向 namespace(如 mcp__filesystem),并把原调用封装为 {"action": Name, "params": Arguments} 的嵌套结构。这使得 upstream 模型能够完美识别多轮对话历史。

2. 规避空工具定义 / Empty Tool Filter

  • flattenToolsWithNamespace 阶段,自动过滤掉函数名为 ""(空字符串)的 CoreTools 定义,从而彻底规避 DeepSeek / GLM 等模型因 schema 检验严格而发生的 400 报错。

此 PR 基于 #75 堆叠。建议在合并 #75 后合入。
This PR is stacked on top of #75. It should be merged after #75.

测试计划 / Test Plan

  • 编写了 TestToCoreRequest_NamespacedToolCallReconstruction 验证多轮历史重构路径的正确性。
  • 运行 go test ./... 确保全量测试无回归通过。

fmosca added 4 commits June 4, 2026 21:14
…coding

Introduces the ToolNestedNamespace ToolKind to identify tools grouped under
a shared namespace. Implements decodeNestedNamespaceArguments to reverse-map
clean nested parameters back to Codex.
…ation

Converts namespaced tool structures into a single top-level schema using
unified action and anyOf-based parameters fields. Implements a stream
buffering layer to reconstruct streamed action and parameter deltas into
clean namespaced tool calls for the downstream interpreter.
…tion

Adds TestOutputItemFromBlockForToolNestedNamespace to verify the extraction
of clean action, namespace, and parameters when reconstructing output items.
…dling

- Filter CoreTools with empty names in flattenToolsWithNamespace before
  dispatching to upstream providers (fixes DeepSeek 400 error on
  'Invalid tools[N].function.name: empty string')
- Add namespace field to inputItem struct for proper namespace tool
  result reconstruction in convertInput

Closes upstream issue ZhiYi-R#51 / ZhiYi-R#65
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.

1 participant