
bug(python-sdk): Streaming tool calls are missing `id` and `type` fields in generation output, causing UI to incorrectly show “not called”
快速结论:该报错发生在使用 Langfuse 的 OpenAI 包装器进行流式 ChatCompletion 调用时。由于 Python SDK 的流式积累逻辑未正确保留工具调用(tool_calls)中的 `id` 和 `type` 字段,导致 Langfuse UI 将已成功调用的工具错误显示为 “not called”。优先检查 `langfuse-python` 版本并应用 Issue 中提出的补丁。
问题场景
用户通过 langfuse.openai.AsyncOpenAI 或同步客户端包装器执行流式(stream=True)ChatCompletion 请求,且模型触发工具调用时。Langfuse UI 中 Tools 选项卡下所有工具均显示 “not called”,尽管工具已被成功调用。
报错原文
# 原始 trace payload 中缺少 id 和 type 字段
"output": {
"role": "assistant",
"tool_calls": [
{
"function": {
"name": "ReadFile",
"arguments": "{\"path\": \"test.txt\"}"
}
}
]
}
# 非流式场景的正确输出
"output": {
"role": "assistant",
"tool_calls": [
{
"id": "call_123456789",
"type": "function",
"function": {
"name": "ReadFile",
"arguments": "{\"path\": \"test.txt\"}"
}
}
]
}
原因分析
根本原因位于 langfuse/openai.py 中的 _extract_streamed_openai_response 方法(langfuse-python v3.14.5)。在流式工具调用 delta 的积累逻辑中(约第 663-700 行),代码仅提取了 function 属性:
tool_call_chunk = getattr(delta.get("tool_calls", None)[0], "function", None)
该行完全忽略了原始 delta 对象中的 id 和 type 字段,导致最终 Generation 输出缺失这两个关键字段。
此外,Issue #12490 报告了另一个相关流式 bug:当非空的 content 块在 tool_calls 块之前到达时,get_response_for_chat() 中的短路逻辑会直接丢弃 tool_calls。后端 PR #10847 已修复归一化逻辑以正确保留 id 和 type,但前提是 Python SDK 正确发送这些字段。
环境排查
- 确认
langfuse-python版本(报错时为 v3.14.5,建议升级到最新版或确认当前版本是否存在该问题)。 - 确认 Langfuse 服务版本(自托管时为 v3.155.1)。
- 确认使用的 OpenAI 客户端类型(Async/Sync)及流式标志(
stream=True)。 - 检查 Langfuse trace payload 中 tool_calls 是否缺少
id和type字段。
解决步骤
- 定位源代码:找到
langfuse/openai.py中的_extract_streamed_openai_response函数。 - 修改积累逻辑:在函数内部,将原始 delta 对象中的
id和type字段提取出来并附加到累积的 tool_calls 数组中。参考代码:
raw_tc = delta.get("tool_calls", None)[0]
tool_call_chunk = getattr(raw_tc, "function", None)
tc_id = getattr(raw_tc, "id", None)
tc_type = getattr(raw_tc, "type", None)
然后将tc_id和tc_type合并到累积的 tool_calls 对象中。 - 可优先尝试:由于 Issue 提交者已本地验证该修改有效,可直接应用上述补丁。
- 等待官方修复:该 Issue 已被标记为未确认 bug 且已僵化。如果不想手动补丁,建议持续关注 langfuse-python 仓库的更新。
验证方法
修改后,执行一次流式 ChatCompletion 请求并触发工具调用。通过 Langfuse API 或 Trace 列表检查 Generation 的 raw payload,确认 output 中的 tool_calls 数组包含 id 和 type 字段。然后在 Langfuse UI 中打开该 Trace 的 Tools 选项卡,确认工具不再显示 “not called”,而是显示正确的调用状态。

![[程序员] 让 ChatGPT review Claude 写的代码,它挑了 35 条,没一条是错的](https://www.chat-gpts.plus/wp-content/uploads/2026/06/ai_cover_5-783-768x403.jpg)

