[Bug]: SpendLogs rows silently dropped when the provider reuses response ids (Ollama: `chatcmpl- `)

当 LiteLLM 代理把 Ollama 的响应 ID(形如 chatcmpl- )当作 LiteLLM_SpendLogs.request_id 主键写入时,重复 ID 会因 skip_duplicates=True 被静默丢弃,表现为请求返回 200 但消费日志缺失。优先排查上游

快速结论:当 LiteLLM 代理把 Ollama 的响应 ID(形如 chatcmpl-<0..999>)当作 LiteLLM_SpendLogs.request_id 主键写入时,重复 ID 会因 skip_duplicates=True 被静默丢弃,表现为请求返回 200 但消费日志缺失。优先排查上游 provider 是否复用了短小的响应 ID,并确认 LiteLLM 版本是否已包含对应修复。

适用环境:LiteLLM 代理 v1.105.0、v1.104.2、v1.104.0(镜像 ghcr.io/berriai/litellm:v1.104.0),Postgres 作为数据库,Ollama 0.40.3,使用 openai/ provider 指向 api_base: http://<ollama>:11434/v1。Issue 中未提供 Python、CUDA、显卡等环境信息。

最快修复方案:升级到包含 PR #39227 的 LiteLLM 版本,使行记录改用 litellm_call_id 作为键、provider 响应 ID 保留为元数据。Issue 已在维护者回复中确认该 PR 修复了此问题,Ollama 响应 ID 现在无需额外配置即可唯一。

注意事项:Issue 中的临时绕行方案(BEFORE INSERT 触发器给匹配 ^chatcmpl-[0-9]{1,4}$ 的 ID 追加 uuid)仅是权宜之计,升级后应移除,避免重复改写。此外,Issue 中的独立复现是用 mock 上游模拟 Ollama 短 ID 完成的,验证的是碰撞机制,而不是精确复现 46% 的损失率。

问题场景

用户在 LiteLLM 代理后端接入一个 Ollama 部署,通过 openai/ 风格调用聊天补全接口。请求本身返回 200 成功,但对应记录没有出现在 LiteLLM_SpendLogs 表中,代理日志也没有任何错误或警告。随着表中已存在行数增加,丢失比例会从约 10% 上升到 46%。按请求计的消费日志是客户计费和结算依据,丢失静默发生因而不会触发告警;每日聚合表 LiteLLM_DailyUserSpend.api_requests 不受影响。

报错原文

[Bug]: SpendLogs rows silently dropped when the provider reuses response ids (Ollama: `chatcmpl-<0..999>`)

Successful requests to an Ollama backend leave no row in `LiteLLM_SpendLogs`, with no error logged.

`LiteLLM_SpendLogs.request_id` is the primary key, and `get_spend_logs_id()` prefers the provider's response id over `litellm_call_id`.

candidate_ids = (
    response_obj.get("id"),
    standard_logging_payload.get("id") ...,
    kwargs.get("litellm_call_id"),
)

Ollama's OpenAI-compatible API answers with `id: chatcmpl-<rand.Intn(999)>`, i.e. 1000 possible ids.

Spend logs are inserted with skip-duplicates, so every request whose id collides with any row still in the table (30 days with our retention) is dropped silently.

On `main` (b5aa827, 2026-10-14), `get_spend_logs_id()` still prefers `response_obj["id"]`, and `_create_spend_logs_with_poison_isolation()` writes with `create_many(..., skip_duplicates=True)`.

原因分析

最可能的原因是 LiteLLM_SpendLogs.request_id 被设为主键,而 get_spend_logs_id()(位于 litellm/proxy/spend_tracking/spend_tracking_utils.py)在候选 ID 中优先取 provider 返回的 response_obj["id"],而非 LiteLLM 自己生成的 litellm_call_id。Ollama 的 OpenAI 兼容接口返回形如 chatcmpl-<0..999> 的 ID,总共只有 1000 种取值。写入时使用 create_many(..., skip_duplicates=True),因此只要新请求的 ID 与保留期内任意一行碰撞,该行就被静默跳过。表中已有行越多,碰撞概率越高,这也解释了损失率随天数上升的现象。对照后端(vLLM 的 chatcmpl-<uuid>、OpenRouter 的 gen-<hex>)ID 唯一,未出现丢失。缓存命中路径已经通过追加时间戳绕开了同一约束,说明问题出在键的选择上。

环境排查

  • LiteLLM 代理版本:是否处于 v1.104.0、v1.104.2、v1.105.0 等受影响区间;是否已包含 PR #39227。
  • 数据库:是否使用 Postgres,以及 LiteLLM_SpendLogs 的实际保留期(Issue 中为 30 天)。
  • 上游 provider 类型与配置:是否为 Ollama 的 OpenAI 兼容接口(api_base: http://<ollama>:11434/v1)。
  • Ollama 版本:Issue 中确认为 0.40.3。
  • 响应 ID 形态:抓取上游返回,确认是否为 chatcmpl-<0..999> 这类短且可复用的 ID。
  • 对比基线:切换到 ID 唯一的后端(如 vLLM、OpenRouter)观察是否仍有丢失。

解决步骤

  1. 先确认症状:对同一 model_group 发送一批聊天补全(Issue 中为 60 次,4 并发,max_tokens: 16),断言全部返回 200。
  2. 查询 select count(*) from "LiteLLM_SpendLogs" where model_group = '<model>',与成功请求数对比;再用 select request_id ... 查看是否为 chatcmpl-833、chatcmpl-36 这类短 ID。
  3. 核对每日聚合表,确认 LiteLLM_DailyUserSpend.api_requests 计数完整,而按请求的行缺失,以排除请求本身失败的可能。
  4. 将 LiteLLM 升级到包含 PR #39227 的版本。该 PR 使行记录以 litellm_call_id 为主键,provider 响应 ID 转为元数据,从根源消除碰撞。维护者已确认此修复,Ollama 响应 ID 无需额外配置即唯一。
  5. 如果暂时无法升级,可优先尝试 Issue 中的临时绕行:添加 BEFORE INSERT 触发器,对匹配 ^chatcmpl-[0-9]{1,4}$ 的 ID 追加 uuid。注意这只是权宜手段。
  6. 升级或移除绕行后,重复第 1–2 步的验证。

验证方法

对同一个 Ollama 后端模型再次发送相同批次请求,确认全部返回 200 后,select count(*) from "LiteLLM_SpendLogs" where model_group = '<model>' 的计数与成功请求数一致;同时检查被记录的 request_id 不再是可碰撞的短 chatcmpl-<0..999>,并且此前被丢弃请求的 x-litellm-call-id 能在消费日志中查到。Issue 中的独立复现显示,使用唯一 ID 时 300/300 行全部保留,可作为对照。

参考来源

BerriAI/litellm #46403

修复 PR:BerriAI/litellm #39227

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 29248

发表回复

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