Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

[Bug]: 流式工具调用续片带空串 id/name 时累加器丢弃参数片段,导致 "Arguments are invalid JSON"

Open
#3,225 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
72/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
rust
Domain
backend

Research direction

Start with the openbitfun_agent_stream::tool_call_accumulator mentioned in the issue and inspect how streaming tool-call continuations are matched to existing calls. Reproduce with chunks whose continuation has empty id and name, then verify that arguments are accumulated by index and form valid JSON; compare with continuations where those fields are omitted. Done when both forms work without invalid-JSON retries.

Written by the indexing model from the issue text.

Description

简述

部分按 OpenAI 流式规范分片发送工具调用参数、且续片把 id/name 写成空字符串 "" 的模型(如 glm-5.2),其工具调用会被 tool_call_accumulator 判定为 Arguments are invalid JSON. 而整轮失败重试。根因不在模型:模型实际发出了合法 JSON,但累加器只保留了每个工具调用的第一片 {,后续参数片段被丢弃。

环境

  • OpenBitFun Desktop: 1.0.1(Windows)
  • 模型: glm-5.2(provider: sensenova,OpenAI 兼容流式)
  • 出现会话: 20260923T213930、20260924T163057(前者参数解析失败 9 次,后者 8 次)

复现

让 glm-5.2 在一轮里并行发起两个工具调用(如 ExecCommand + Glob)。app.log 出现:

[ERROR][openbitfun_agent_stream::tool_call_accumulator] Tool call arguments parsing failed at boundary=finish_reason: tool_id=call_9072c7daa3af466782cee2ea, tool_name=Glob, raw_len=1, error=EOF while parsing an object at line 1 column 1, parse_error_category=eof, parse_error_is_eof=true, completion=Unknown
[ERROR][openbitfun_agent_stream] SSE Error: Has invalid tool calls
[ERROR][openbitfun_core::agentic::tools::pipeline::tool_pipeline] Tool execution failed: error=Validation error: Arguments are invalid JSON.
[WARN][openbitfun_core::agentic::execution::round_executor] Retrying stream because provider returned only invalid tool arguments: ... local_retry=1/10

关键:raw_len=1 —— 累加器里每个工具调用只剩 1 个字符 {。

根因(SSE 证据)

模型把同一工具调用的 arguments 拆成多个 SSE chunk 发送,首片带 id/name,续片把它们写成空串:

17: tool_calls[0] id="call_b45..." name="ExecCommand" arguments="{"
18: tool_calls[0] id=""            name=""            arguments="\"cmd\": "
19: tool_calls[0] id=""            name=""            arguments="\"Get-"
...(续片累积命令字符串)
28: tool_calls[0] id=""            name=""            arguments="\"}"
29: tool_calls[1] id="call_907..." name="Glob"       arguments="{"
30: tool_calls[1] id=""            name=""            arguments="\"pattern\": "
33: tool_calls[1] id=""            name=""            arguments="}"
34: finish_reason="tool_calls"

把续片 arguments 顺序拼接,得到的是合法 JSON:

  • ExecCommand → {"cmd": "Get-Date; Write-Output \"PowerShell 工具运行正常"}
  • Glob → {"pattern": "*.md"}

但累加器只保留了首片的 {,续片全部丢失 → 解析到 EOF while parsing an object at line 1 column 1。

为什么"有些模型"出问题(对比)

同一台机器上 qwen3.8-27b-uncensored 基本正常。其续片是把 id/name 字段省略(而非空串):

"tool_calls":[{"index":0,"type":"function","function":{"arguments":"{"}}]
"tool_calls":[{"index":0,"type":"function","function":{"arguments":"\"file_path\":"}}]

glm-5.2 的续片则是空串:

"tool_calls":[{"index":0,"id":"","type":"function","function":{"name":"","arguments":"\"cmd\": "}}]

两种写法都符合 OpenAI 流式语义,但实现细节不同:省略字段 → serde 反序列化为 None,累加器走续片追加分支(正常);空串 "" → 累加器按 id 查归属工具调用,查不到(真 id 是 call_xxx),于是丢弃该参数片段,缓冲区永远停在 {。

(注:9/21 那次 qwen 的唯一失败是另一类:boundary=graceful_shutdown、raw_len=67、completion=Interrupted,是流被中途打断导致参数真的截断,且已累积到 67 字符,反证 qwen 的累加本身是工作的。)

期望行为

续片无论 id/name 是省略还是空串,都应视为"无 id/无 name"的续片,按 index 追加到对应工具调用的参数缓冲区。两种写法都应正确合并出完整 JSON。

修复建议(openbitfun_agent_stream::tool_call_accumulator)

  1. 续片归属应按 index 匹配,而不是按 id;或
  2. 当 id 为空串 / name 为空串时,等价于 None,走续片追加分支,而非当成新 id 去查表。

影响范围

凡按 OpenAI 规范分片发送 arguments、且续片带空串 id/name 的模型都会 100% 失败并触发整轮重试(local_retry 最多 10 次)。续片字段被省略的模型不受影响。已用本机真实 SSE 流量验证(上述日志片段为原始截取,仅脱敏用户路径)。

Dominant language
Rust
Stars
2.3k
Forks
236
Avg merge
2h 53m
Merged PRs (30d)
604

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from GCWing/OpenBitFun

All issues in GCWing/OpenBitFun

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.