01 — 四类限流器全景¶
LiteLLM 代理同时启用四类限流器,每一类有独立的检查变量、累加来源与作用域。它们之间互不冗余——一个请求要被允许通过,必须同时满足所有限流器的检查。
1. _PROXY_MaxBudgetLimiter(用户花费上限)¶
文件:litellm/proxy/hooks/max_budget_limiter.py
# max_budget_limiter.py:15-41
async def async_pre_call_hook(self, user_api_key_dict, cache, data, call_type):
cache_key = f"{user_api_key_dict.user_id}_user_api_key_user_id"
user_row = await cache.async_get_cache(cache_key, ...)
if user_row is None:
return
max_budget = user_row["max_budget"]
curr_spend = user_row["spend"]
...
if curr_spend >= max_budget:
raise HTTPException(status_code=429, detail="Max budget limit reached.")
| 维度 | 值 |
|---|---|
| 比较对象 | user.spend ≥ user.max_budget(单位:USD) |
| 数据来源 | DualCache 中的 {user_id}_user_api_key_user_id,由 update_cache() 在每次请求结束后刷新 |
| 累加来源 | response_cost → db_spend_update_writer.update_database() → DB LiteLLM_UserTable.spend 列 |
| 作用域 | 用户级(不是 key 级) |
| 失效条件 | response_cost 长期为 0 / 显著低估 |
⚠️ 这里查的是 user 级别 spend,不是 key 级别。Key 级别的
max_budget在另一个位置(见下条)。
2. _virtual_key_max_budget_check(API Key 花费上限)¶
文件:litellm/proxy/auth/auth_checks.py:2669-2717
# auth_checks.py:2680-2717
if valid_token.spend is not None and valid_token.max_budget is not None:
...
if valid_token.spend >= valid_token.max_budget:
raise litellm.BudgetExceededError(
current_cost=valid_token.spend,
max_budget=valid_token.max_budget,
)
| 维度 | 值 |
|---|---|
| 比较对象 | valid_token.spend ≥ valid_token.max_budget |
| 数据来源 | UserAPIKeyAuth 对象,从 LiteLLM_VerificationToken 表加载 |
| 累加来源 | 同 _PROXY_MaxBudgetLimiter:response_cost 累加到 token 行的 spend 列 |
| 作用域 | Key 级别 |
| 失效条件 | (1) response_cost 失真使 spend 涨不到限额;(2) skip_budget_checks=True 直接整段跳过本检查(见 05-skip-budget-checks-bug.md) |
auth_checks.py 还按相同模板对 user / end_user / team_member / team / project / organization 各级别都做了同样的 spend ≥ max_budget 检查(约六处,分别在 line 343、367、2847、2890、3020、3202),全部依赖 spend 列的正确累加。
3. _PROXY_MaxParallelRequestsHandler_v3(TPM / RPM / 并发)¶
文件:litellm/proxy/hooks/parallel_request_limiter_v3.py(1646 行,已替换 v1)
注册位置:litellm/proxy/hooks/init.py:22,30
检查的字段¶
V3 一次性检查多个层级(pre-call hook,约 line 181-446):
key 级别: user_api_key_dict.tpm_limit / rpm_limit / max_parallel_requests
user 级别: user_tpm_limit / user_rpm_limit
team 级别: team_tpm_limit / team_rpm_limit
org 级别: organization_tpm_limit / organization_rpm_limit
per-model: key.model_tpm_limit / key.model_rpm_limit (e.g. {"glm-4-7": 1000})
end_user: end_user_tpm_limit / end_user_rpm_limit
累加来源(关键点)¶
TPM 计数采用"剔除缓存"语义:
# parallel_request_limiter_v3.py:1238-1290
def _get_total_tokens_from_usage(self, usage, rate_limit_type):
"""
For 'input' and 'total' rate limit types, cached tokens are excluded
because providers like AWS Bedrock don't count cached tokens toward
rate limits. This aligns LiteLLM's TPM calculation with provider behavior.
"""
...
if rate_limit_type in ("input", "total"):
cached_tokens = getattr(usage.prompt_tokens_details, "cached_tokens", 0) or 0
if cached_tokens > 0:
total_tokens = max(0, total_tokens - cached_tokens)
return total_tokens
| 维度 | 值 |
|---|---|
| 比较对象 | current_tpm ≥ tpm_limit、current_rpm ≥ rpm_limit、current_parallel_requests ≥ max_parallel_requests |
| 数据来源 | Redis 计数器(窗口大小 = self.window_size,默认 60s) |
| 累加来源 | usage.total_tokens − cached_tokens(input/total 类型);RPM 每请求 +1 |
| 作用域 | key / user / team / org / per-model(互相独立累加) |
| 失效条件 | 与 response_cost 无关——即使 cost=0 仍正确累加;唯一失效场景是 Redis 不可用或被错误重置 |
关键差异:TPM 限流不依赖价格表,因此即使 cache 价格未配置,TPM 仍能正确限流。这也是诊断"限流为什么不生效"的关键分水岭——如果 TPM 限住了但 max_budget 没限住,几乎可确认是 cache 价格漏配。
4. max_parallel_requests(并发限)¶
集成在同一个 V3 限流器内。语义:
- 进入
pre_call_hook时,对应 key 的 Redis 计数器 +1,并立即比较是否超过max_parallel_requests post_call_success_hook或失败钩子里 -1
不依赖 cost、不依赖 token 数,纯并发计数。与价格配置完全无关。
限流器与配置的对照速查¶
| 限流器 | 配置在哪 | DB 表/字段 |
|---|---|---|
| Key max_budget | UI Keys 页或 /key/generate |
LiteLLM_VerificationToken.max_budget |
| User max_budget | UI Users 页或 /user/new |
LiteLLM_UserTable.max_budget |
| Team max_budget | UI Teams 页或 /team/new |
LiteLLM_TeamTable.max_budget |
| TPM/RPM (key) | /key/generate 或 UI |
LiteLLM_VerificationToken.tpm_limit / rpm_limit |
| Per-model TPM/RPM (key) | UI Keys → Model Limits | LiteLLM_VerificationToken.metadata.model_tpm_limit |
| max_parallel_requests | /key/generate |
LiteLLM_VerificationToken.max_parallel_requests |
项目自定义:所有限流字段在 PG 表中存储,本仓库的 PG schema 不允许变更(见
MEMORY.md→pgsql 表结构禁止修改)。
限流器全部依赖一个共同前提¶
无论是 Budget 还是 TPM 类,都依赖 DualCache 中的字段被及时刷新。如果 Redis 与 PG 之间不同步,或者 update_cache() 被异常吞掉,会出现"DB 中 spend 已超但缓存里还是旧值"的滞后窗口(默认更新间隔 ~1s)。这不在本次事故的范围内,但排查时需要排除。