[Bug]: `litellm_settings.max_budget` ignores `budget_duration`; global proxy budget is a hardcoded trailing-30-day cap

在 LiteLLM 代理中设置 litellm_settings.max_budget 并指定 budget_duration 后, budget_duration 被忽略,全局预算实际按硬编码的过去 30 天滚动总花费生效,导致配置的每日/每周上限不重置。优先检查是否将 max_budget 与

快速结论:在 LiteLLM 代理中设置 litellm_settings.max_budget 并指定 budget_duration 后,budget_duration 被忽略,全局预算实际按硬编码的过去 30 天滚动总花费生效,导致配置的每日/每周上限不重置。优先检查是否将 max_budgetbudget_duration 配合使用,并确认实际行为是否为 30 天窗口。

适用环境:LiteLLM 代理(proxy)配置了 litellm_settings.max_budgetbudget_duration 的场景。Issue 不涉及特定操作系统、Python、CUDA 版本。

最快修复方案:暂无确认的一步修复方案。该问题在代码层面尚未合并修复补丁,当前需要等待 LiteLLM 官方修复或手动修改源码。若有紧急需求,可优先尝试自行修改 litellm/proxy/auth/user_api_key_auth.py 中的 _load_global_spend 函数,将查询窗口从硬编码的 MonthlyGlobalSpend 视图(30 天)改为根据 budget_duration 动态计算窗口(例如使用 NOW() - budget_duration 条件),但此改动未经官方验证且可能影响其他功能。

注意事项:Issue 中提到的方案(窗口化基线查询)仅是开发者计划,尚未合入主分支,自行修改需谨慎测试,并注意保留 MonthlyGlobalSpend 视图(用于管理界面)不破坏。另外,default_user_id 行的 spend 仅记录管理员用户自己的花费,不可作为代理总花费直接替代。

问题场景

用户在 LiteLLM 代理配置文件中设置全局预算和预算周期(如 max_budget: 100budget_duration: 1d),预期每日花费不超过 100 单位,实际却变成过去 30 天滚动总花费限制,且一旦触发 429 响应后无法按预期周期恢复。

报错原文

# 配置示例
litellm_settings:
  max_budget: 100
  budget_duration: 1d

# 实际行为:budget_duration 被忽略,全局预算基于 trailing-30-day 窗口
# 超出后所有请求返回 429,直到 30 天前花费过期才恢复
# 内部错误:ResetBudgetJob 重置了 default_user_id 行的 spend,但强制执行从不读取该计数器
# 相关查询:SELECT SUM(spend) AS total_spend FROM "MonthlyGlobalSpend";
# MonthlyGlobalSpend 视图定义:WHERE "startTime" >= (CURRENT_DATE - INTERVAL '30 days')

原因分析

可能原因:LiteLLM 代理内部存在两个相互矛盾的计数器:

  • 重置计数器default_user_id(代理管理员用户行)的 spendbudget_durationResetBudgetJob 定期清零,但这个计数器仅记录管理员自身花费,不是代理总花费。
  • 强制执行计数器_global_proxy_budget_check 读取的是全局花费累加器,其基线来自 MonthlyGlobalSpend 视图,该视图硬编码为过去 30 天窗口。
  • budget_duration 被存储并用于重置,但强制执行从未读取这个窗口,导致配置的周期(1d、7d、30d)全部表现为 30 天滚动上限。这是设计上的不一致:全局预算作为唯一不读取自身重置计数器的预算类型。

环境排查

  • 确认 LiteLLM 版本(建议至少 1.0.0+,Issue 在 2026 年提出,版本可能较新)
  • 检查代理配置中 litellm_settings.max_budgetbudget_duration 是否同时设置
  • 使用数据库客户端查询 MonthlyGlobalSpend 视图内容,确认窗口为 30 天
  • 检查 ResetBudgetJob 是否正确执行,并能看到 default_user_id 行的 spend 被重置(但不影响全局限制)

解决步骤

  1. 确认问题影响范围:通过以下步骤验证 budget_duration 被忽略:
    • 设置 max_budget: 100budget_duration: 1d,触发超过 100 花费后所有请求返回 429。
    • 等待超过 1 天(或预期重置时间),观察 429 是否解除。如果仍然阻塞(需等到 30 天前的花费过期),则确认问题存在。
  2. 临时工作绕行:如果无法等待官方修复,可考虑手动修改源码(风险自负):
    • 定位到 litellm/proxy/auth/user_api_key_auth.py 中的 _load_global_spend 函数。
    • 将原来查询 MonthlyGlobalSpend 视图的逻辑改为直接查询 LiteLLM_SpendLogs 表,窗口条件设置为 NOW() - budget_duration(需要从代理设置中获取 budget_duration 值)。
    • 注意保留对未设置 budget_duration 时的回退行为(默认 30 天)。
    • 添加单元测试确保窗口正确。
  3. 等待官方修复:关注 Issue #31292 的进展,或提交 Pull Request 以合并修复。

验证方法

修改后重新启动代理,用测试用户反复产生花费,确认:

  • 设置 budget_duration: 1dmax_budget: 100,第一天达到 100 后应返回 429。
  • 第二天(超过 1 天窗口)请求应恢复正常。
  • 观察数据库日志确认查询窗口不再是固定的 30 天。
  • 检查 ResetBudgetJob 是否仍正常重置但不再影响全局限制。

参考来源

BerriAI/litellm #31292

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 15139

发表回复

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