[Bug]: decisions (typesafe): missing usage becomes zero cost, a 200 with an invalid body becomes a 500, and the error body is only in the me

在 LiteLLM v1.106.2 通过 litellm.adecisions(custom_llm_provider="typesafe", ...) 调用 TypeSafe System One 时,响应缺少 usage 会被记为 0 token、$0 成本;200 响应校验失败会被误报为 5

快速结论:在 LiteLLM v1.106.2 通过 litellm.adecisions(custom_llm_provider="typesafe", ...) 调用 TypeSafe System One 时,响应缺少 usage 会被记为 0 token、$0 成本;200 响应校验失败会被误报为 500;上游 400 的 JSON 错误体只出现在异常消息字符串里。优先排查响应中是否真的带 usage,以及是否依赖 exc.body 获取错误详情。

适用环境:Issue 中确认的环境:LiteLLM v1.106.2,SDK(litellm Python 包),调用 custom_llm_provider="typesafe" 的 decisions 路径(TypeSafe System One,/v1/systemone),测试针对 127.0.0.1 上的本地 stub。Issue 未给出 Python、操作系统、CUDA、显卡等版本信息。

最快修复方案:升级到包含 PR #48261 的 LiteLLM 版本(该 PR 修复“缺少原生 usage 被报成零成本”),升级后重新验证 r.usage 与 _hidden_params["response_cost"]。Issue 中其他三项问题未在讨论链中确认已有修复。

注意事项:PR #48261 只覆盖缺失 usage 被记为 0 成本这一项;无效 200 响应被抛成 500、错误体仅在消息文本中、decisions POST 无响应大小上限,在 Issue 正文中属于期望/建议,未确认已合并。升级前请勿仅凭 response_cost == 0.0 判断调用免费。

问题场景

用户在自有 httpx 客户端调用 TypeSafe System One 后,迁移到 litellm.adecisions(custom_llm_provider="typesafe", model="jev-1.13.0", ...),并把 api_base 指向本地 /v1/systemone stub 进行测试。当 stub 返回不带 usage 的正常响应、返回 400 带 JSON 错误体、或返回 200 但 choice 答案缺少 probabilities 时,LiteLLM 返回的 usage、成本、异常类型和错误体都达不到按用量计费与路由所需的可辨识度。

报错原文

[Bug]: decisions (typesafe): missing usage becomes zero cost, a 200 with an invalid body becomes a 500, and the error body is only in the message
InternalServerError (status 500, "returned an unexpected response")
APIConnectionError (status 500)
TypesafeException - {"detail": {"error_type": "api_usage_error", "message": "Unknown model: ..."}}

原因分析

根据 Issue 描述,最可能的原因集中在 decisions 的 typesafe 适配层:

  • systemone_response_to_ir 使用 response.usage or DecisionsUsage(),导致“供应商未报告 usage”与“供应商报告 0 token”无法区分,返回的 usage 恒为 {"input_tokens": 0, "output_tokens": 0},_hidden_params["response_cost"] 为 0.0。
  • 200 响应体校验失败(如缺少 probabilities、非 JSON 响应体)被重新包装为 InternalServerError 或 APIConnectionError,上游真实状态码 200 丢失,响应体中可能携带的 usage 也随之丢弃。
  • 400 场景下 exc.body 为 None、exc.response.text 为空,上游错误 JSON 只拼接进了 str(exc) 的消息文本,调用方只能解析字符串来区分“未知模型”等错误类型。
  • decisions 的 POST 请求未复用 AsyncHTTPHandler 已有的 max_response_bytes 限制(该机制目前用于 GET 的 _get_with_response_limit),因此响应体大小不受约束。

环境排查

  • 确认 LiteLLM 版本:Issue 报告于 v1.106.2,确认当前版本是否包含 PR #48261 的修复。
  • 确认调用入口:使用 litellm.adecisions 且 custom_llm_provider="typesafe",api_base 指向目标服务。
  • 确认上游 stub/服务在 POST /v1/systemone 的响应中是否包含 usage 字段,以及 choice 答案是否带 probabilities。
  • 确认错误路径下 exc.body 与 exc.response.text 的实际取值,判断是否只能从 str(exc) 中解析上游错误 JSON。
  • Issue 未提供 Python、CUDA、PyTorch、显卡、节点等版本信息,无需据此排查。

解决步骤

  1. 记录当前现象:对不带 usage 的 stub 响应运行 litellm.adecisions,保存 r.usage 与 r._hidden_params["response_cost"] 的值,确认是否分别为零 usage 与 0.0。
  2. 升级 LiteLLM 到已合并 PR #48261 的版本,重新运行同一 stub 场景,确认缺失 usage 不再被当作零成本。
  3. 针对无效 200 响应(缺少 probabilities 的 choice 答案、非 JSON 响应体),单独记录抛出的异常类型与状态码,确认是否仍为 InternalServerError(500) / APIConnectionError(500);讨论中建议将上游状态(200)与本地校验失败分开保留。
  4. 针对 400 场景,检查 exc.body 与 exc.response.text 是否为空;若仍需区分 api_usage_error 等错误类型,暂只能从 str(exc) 解析,并在代码中注明这一临时做法。
  5. 在重试策略下对无效 200 响应用例记录实际向上游发出的 POST 次数,避免校验失败被重分类为传输错误而触发第二次可能计费的请求(讨论中提出的回归矩阵建议)。

验证方法

用同一本地 stub 分别构造三种响应——缺失 usage、显式 0 usage、非零 usage——并各自搭配有效与无效的 200 响应体,断言两处结果:调用方可见的 usage / response_cost,以及日志载荷。修复生效的标志是:缺失 usage 不再表现为零成本,显式 0 与非零 usage 仍按原值透传;无效 200 响应不会丢失上游状态码,且默认重试策略下不会额外发出可计费的 POST。

参考来源

BerriAI/litellm #48147 PR #48261(缺失 usage 被报成零成本的修复)

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 29794

发表回复

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