Skip to content

fix: 归一化非法 tool_use id,避免请求在本地校验阶段被拒 - #127

Merged
voocel merged 1 commit into
voocel:mainfrom
Smith-106:fix/tool-use-id-repair
Sep 26, 2026
Merged

voocel merged 1 commit into
voocel:mainfrom
Smith-106:fix/tool-use-id-repair

Conversation

@Smith-106

Copy link
Copy Markdown
Contributor

问题

部分上游返回的 tool call id 带有 #、: 等字符(例:call_3543acedc27c4404bfe7317c#235532d85de245bda8ff64f6a683623a)。这类 id 进入下一轮请求时会直接被拒:

messages[2]: tool use id "call_3543acedc27c4404bfe7317c#235532d85de245bda8ff64f6a683623a" is invalid

失败发生在本地(ErrorTypeValidation):请求根本没有发往 provider,上游日志里查不到该请求,因此表现为同一轮会话在同一个位置反复失败、无法推进。

根因

  1. litellm 的请求校验要求 tool_use id 满足 [A-Za-z0-9_-]{1,64}(repair.go);
  2. 客户端修复策略 c.repair 默认是 RepairNone,而 createModelFromConfig 未注入任何策略;
  3. validateRequest 在 repairRequest 之后执行,因此只要开启策略即可在发送前救回。

⇒ 含 # 的 id 在本地校验阶段被判死,provider 侧完全无感。

修复

internal/bootstrap/models.go:在统一的模型工厂里注入修复策略

llm.WithClientOptions(litellm.WithMessageRepair(litellm.RepairToolUseIDs)),
  • RepairToolUseIDs = 归一化非法字符 + 为缺失 id 的 tool call 补生成合法 id;
  • tool_result 侧由同一映射成对改写,不会破坏 tool_use / tool_result 配对;
  • 所有角色模型与 fallback 共用该工厂,一处生效。

验证

新增 internal/bootstrap/models_toolid_test.go:以 stub provider 断言真正到达 provider 的 id 合法,且 tool_use 与 tool_result 的 id 一致(成对改写)。

场景 结果
撤下该策略(对照) 测试失败并复现原报错:messages[1]: tool use id "call_...c#..." is invalid
注入策略 请求成功发出,provider 收到归一化后的成对 id

本机 CI 等价检查(Linux 语义一致,CGO_ENABLED=0):

  • gofmt -l . → 空
  • go vet ./... → 通过
  • go test -buildvcs=false -count=1 ./... → 全部包通过(0 失败)

兼容性

仅新增客户端「发送前修复」策略:不改协议、不改请求字段、无 API 变化。历史会话中已存在的非法 id 会在下一次请求时自动归一化,无需清理数据。

部分上游返回的 tool_use id 含 '#'、':' 等字符,而 litellm 的请求校验只接受 [A-Za-z0-9_-];默认策略 RepairNone 会在请求发出前直接判非法,使整轮会话反复失败(messages[i]: tool use id "..." is invalid)。

createModelFromConfig 为所有角色模型统一开启 litellm 的消息修复策略:id 在发送前归一化,tool_use / tool_result 由同一映射成对改写,缺失 id 的 tool call 也会补生成合法 id。所有角色与 fallback 共用该工厂,一处生效。

新增 internal/bootstrap/models_toolid_test.go:以 stub provider 断言真正到达 provider 的 id 合法且成对;撤下该策略时测试失败并复现原报错。
@Smith-106

Copy link
Copy Markdown
Contributor Author

补充串行证据(本机等价环境,CGO_ENABLED=0):

对照:撤下该策略时,同一测试失败并复现原报错

--- FAIL: TestCreateModelNormalizesInvalidToolUseIDs
    models_toolid_test.go:66: 请求未发出(若为 tool use id ... is invalid 则说明归一化未生效):
    messages[1]: tool use id "call_3543acedc27c4404bfe7317c#235532d85de245bda8ff64f6a683623a" is invalid

注入策略后: 同一请求成功发出,stub provider 收到归一化后的 id
(call_3543acedc27c4404bfe7317c_235532d85de245bda8ff64f6a683623a),且 tool_use 与 tool_result 成对一致。

CI 等价检查: gofmt -l . 输出为空;go vet ./... 通过;go test -buildvcs=false -count=1 ./... 全部包通过(33 包,0 失败)。

@voocel
voocel merged commit a4e3c45 into voocel:main Sep 26, 2026
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