bug(sdk-python): LangchainCallbackHandler loses cache token metrics and inflates input costs

使用 langchain_openai.ChatOpenAI 配合 Langfuse 的 LangchainCallbackHandler ,通过 LiteLLM 代理调用 Anthropic 模型(如 claude-sonnet-4-20250514 ),并采用 非流式 模式( llm.ainvo

bug(sdk-python): LangchainCallbackHandler loses cache token metrics and inflates input costs

bug(sdk-python): LangchainCallbackHandler loses cache token metrics and inflates input costs

快速结论:在非流式(non‑streaming)调用 Anthropic 模型(通过 LiteLLM 代理)时,Langfuse Python SDK 的 _parse_usage_model 方法会丢弃令牌缓存指标(prompt_tokens_details 以字典格式传入时被忽略),并因未从 input 中减去缓存令牌而导致输入成本虚高。优先检查 llm_output["token_usage"]prompt_tokens_details 是否为字典格式。

问题场景

使用 langchain_openai.ChatOpenAI 配合 Langfuse 的 LangchainCallbackHandler,通过 LiteLLM 代理调用 Anthropic 模型(如 claude-sonnet-4-20250514),并采用非流式模式(llm.ainvoke)。

报错原文

# 实际表现并非抛出异常,而是指标缺失和成本异常:
# - cache_read 令牌(prompt_tokens_details 中的 cached_tokens)完全丢失
# - 输入成本按全量 prompt_tokens 计费,远高于实际使用

# 核心代码路径:_parse_usage_model 中对 prompt_tokens_details 的处理缺失
# line 1220-1236 仅处理了 list 格式(Vertex AI),未处理 dict 格式
# line 1278 的 isinstance(v, int) 过滤器将 dict 格式丢弃

原因分析

LangchainCallbackHandler 中的 _parse_usage_model 方法在解析令牌使用数据时,对 prompt_tokens_details 字段的处理存在两个缺陷:

  1. 字典格式被忽略:LiteLLM 代理返回的 prompt_tokens_details 是字典 {"cached_tokens": 12000},但 SDK 仅处理列表格式(Vertex AI 风格)。字典格式在 line 1220 的类型检查中被跳过,最终在 line 1278 的整数过滤器中被静默丢弃。
  2. 未调整 input 令牌数:由于缓存令牌被丢弃,input 字段(即 prompt_tokens)未减去 cached_tokens,导致 Langfuse 按全价计算输入成本。缓存命中率越高,虚增倍数越大。

注意:cache_creation_input_tokens 作为顶层整数字段会被保留,但由于没有减去 cached_tokens,成本计算仍然错误。

环境排查

  • langfuse(Python SDK)版本:需确认是否包含对 prompt_tokens_details 字典格式的支持(截至 Issue 关闭时,官方尚未合并修复)
  • LiteLLM 版本:不同版本返回的字段名称可能不同,需验证 llm_output["token_usage"]prompt_tokens_details 的实际格式
  • Anthropic 模型版本:本问题在 claude-sonnet-4-20250514 上复现,但理论上影响所有支持缓存提示的模型
  • 调用模式:仅非流式(non‑streaming)路径受影响;流式路径(streaming)使用 message.usage_metadata 且工作正常

解决步骤

  1. 验证问题来源:在代码中添加临时日志,打印 llm_output["token_usage"] 的内容,确认 prompt_tokens_details 是否为字典格式,以及 cache_creation_input_tokens 是否存在。
  2. 临时修复(Monkey Patch):如果等不及官方修复,可以在初始化 LangchainCallbackHandler 前,对 _parse_usage_model 方法进行猴子补丁,增加对 prompt_tokens_details 字典格式的处理分支:将 {"cached_tokens": N} 映射为 input_cached_tokens = N,并从 input 中减去。
  3. 关注官方 PR:跟踪相关 PR langfuse-python#1549 的进展(该 PR 针对另一同类问题,但触及同一方法)。
  4. 提交新 Issue 或 PR:如果暂无修复,可向 langfuse/langfuse-python 提交 PR,参考 Issue 中分析——在 _parse_usage_modelprompt_tokens_details 处理分支中增加 dict 格式支持。
  5. 切换到流式模式(临时降级方案):如果业务允许,暂时改用流式调用(llm.astream)规避问题,因为流式路径缓存指标正常。

验证方法

修复后,在 Langfuse 的 trace 详情页中检查对应调用的 usage 指标:

  • 确认 input_cached_tokens 字段存在,值应等于 prompt_tokens_details.cached_tokens
  • 确认 input 值已减去缓存令牌数:即 input = prompt_tokens - cached_tokens
  • 成本对比:修复前后对同一请求的成本数应下降(下降幅度取决于缓存命中率)

参考来源

langfuse/langfuse #13024

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

celebrityanime
celebrityanime
文章: 9293

发表回复

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