[Bug]: GLM tool-call streaming final chunks repeat metadata and combine arguments with finish_reason

用户在 vLLM main 分支(commit 6bdabbad5 / 023808c23 )上,通过 OpenAIServingChat 服务层,使用 GLM 工具解析器(如 glm45 / glm47 )进行工具调用的流式输出时触发。问题不依赖具体 GLM 模型权重加载,可通过 unit tes

快速结论:在使用 vLLM 的 GLM(如 glm45/glm47)工具调用流式输出时,最终分片会重复发射 idtypefunction.name 等元数据,并将最后一段 function.argumentsfinish_reason="tool_calls" 合并到同一个分片中,导致 OpenAI 兼容客户端解析异常。优先排查 vllm/entrypoints/openai/chat_completion/serving.py_create_remaining_args_delta() 的实现是否已修复。

问题场景

用户在 vLLM main 分支(commit 6bdabbad5 / 023808c23)上,通过 OpenAIServingChat 服务层,使用 GLM 工具解析器(如 glm45 / glm47)进行工具调用的流式输出时触发。问题不依赖具体 GLM 模型权重加载,可通过 unit test 在无模型环境下复现。

报错原文

# 问题1:剩余参数字块重复发射元数据
remaining_delta.model_dump=
{'id': 'call_current', 'type': 'function', 'index': 0,
 'function': {'name': 'current_name', 'arguments': ']}'}}

# 问题2:终端参数字块与 finish_reason 合并
{
  "index": 0,
  "delta": {
    "tool_calls": [
      {"index": 0, "function": {"arguments": "\"pong.py\"}"}}
    ]
  },
  "finish_reason": "tool_calls"
}

预期行为:

  • 对于同一个 tool-call index,只有首个分片应包含 id, type, function.name;后续分片只应包含 function.arguments
  • 终端非空 function.arguments 应与 finish_reason: null 一起发射,随后单独发射一个空 delta 的 finish 分片(finish_reason: "tool_calls")。

原因分析

问题出在 vllm/entrypoints/openai/chat_completion/serving.py_create_remaining_args_delta() 的实现:在构造剩余参数字块时,该方法保留了原始 DeltaToolCall 中的 idtypefunction.name 字段,导致这些元数据在后续分片中重复发射。同时,当流式生成器同时遇到工具参数 delta 和 output.finish_reason is not None 时,会将两者合并到同一个 ChatCompletionResponseStreamChoice 中,而不是拆分成分步发射。

该问题与以下 Issue/PR 相关但不完全覆盖:

  • #38603 / PR #39598:处理 null/空字段和 MTP 工具调用最终分片行为,但未覆盖此 metadata 重复问题。
  • #36857 / PR #37845:后缀对齐/全参数重复发射问题,已修复,但未涉及这两个协议行为。
  • PR #39253:修复了 MTP/stream interval 下的 GLM 解析器流式输出,但服务层最终分片行为仍有问题。
  • vllm-ascend#8327:报告了参数 delta 与 finish_reason="tool_calls" 合并在一个最终分片中的问题。

环境排查

  • vLLM 版本:当前 main 分支(commit 6bdabbad5023808c23),非 released wheel 或 Docker 镜像
  • 平台:Linux 5.10.0-216.0.0.115.oe2203sp4.aarch64,aarch64 架构
  • 无模型权重依赖:可通过 unit test 复现

解决步骤

  1. 确认是否已应用修复 PR #45915:该 PR 重写了 GLM 解析器引擎,并在 follow-up #44099 中验证了 GLM 路径上不再重现该问题。注意:#44099 已关闭(被 supersede),并未合并,但 #45915 已合并。
  2. 运行单元测试验证:
    python -m pytest -q tests/entrypoints/openai/chat_completion/test_serving_chat.py::TestCreateRemainingArgsDelta

    预期输出 6 passed,覆盖:

    • 连续/最终剩余参数字块不应重复发射 idtypefunction.name(除非是该 tool-call index 的首个分片)
    • 终端非空 function.arguments 应与 finish_reason: null 一起发射,随后单独发射空 delta finish 分片(finish_reason="tool_calls"
  3. 如果测试未通过,请检查是否使用了最新 main 分支,或手动检查 _create_remaining_args_delta() 的实现是否已更新。
  4. 注意:如果问题出现在非 GLM 工具解析器路径上,需要提供非 GLM 的复现示例,该修复仅针对 GLM 路径。

验证方法

运行上述 unit test 确认 6 passed。如果使用实际 GLM 模型进行流式工具调用,检查最终分片的 JSON 内容:确认 idtypefunction.name 仅在首个分片中出现;确认最后一段 function.arguments 不与 finish_reason="tool_calls" 出现在同一个分片中(应分两个分片:先参数 + finish_reason: null,再空 delta + finish_reason="tool_calls")。

参考来源

vllm-project/vllm #44098

关联 PR/Issue:vllm-project/vllm #45915(已合并的修复),vllm-project/vllm #44099(被 supersede 的 follow-up),vllm-project/vllm-ascend #8327(下游服务器端症状报告),vllm-project/vllm-ascend #8178(下游避免重复 function metadata 的补丁)

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 16126

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注