新镜像 main-latest 自 2026-09-20 起默认启用 v2 migration resolver (上游 fac518dbf7),v2 启动时调用 _warn_if_db_ahead_of_head() 读 _prisma_migrations。本库 litellm 为 SQL_ASCII(server/client_encoding 均 SQL_ASCII,datcollate=C),psycopg 的 TextLoader 对此直接返回 bytes 而非 str(psycopg/types/string.py:110-120),导致 utils.py:174 的 _MIGRATION_TS_RE.match(bytes) 抛 TypeError,容器启动即崩。 同一缺陷还潜伏在 repair_invalid_indexes/_index_repair (utils.py:760,_TRANSIENT_INDEX_SUFFIX_RE.search(index.name))与 _failed_migration_logs(utils.py:1023,str in bytes),二者 v1/v2 双路径 均会经过,只在对应数据非空时触发(CREATE INDEX CONCURRENTLY 中断或 迁移死锁)。 根因修复:DATABASE_URL 追加 client_encoding=UTF8。libpq 将其作为 启动参数下发(fe-protocol3.c:2503),psycopg 按服务端确认值决定解码 (pq_ctypes.py:229-232),恢复返回 str。Prisma 侧无影响:quaint 丢弃 未知参数(url.rs:459),其驱动 tokio-postgres 本就固定发 client_encoding=UTF8。已全库校验 450 个文本列/43548 行/14.5MB 全部 为合法 UTF-8,无解码失败风险。 另保留 USE_V2_MIGRATION_RESOLVER=false 作独立兜底,沿用更新前的 v1 路径;根因已除后该项可移除以启用 v2。
VolcSync - LiteLLM 火山模型监控与限额说明
当前生效入口
当前 config.yaml 使用的回调入口是:
callbacks: ["volc_limit_sync.proxy_handler_instance"]
这意味着当前 Docker 容器里真正生效的是 volc_limit_sync.py。
旧脚本 archive/daily_limit.py.deprecated 目前 未启用,只保留作历史参考。除非你把 callbacks 改回旧模块,否则改它里面的 MODEL_QUOTAS 不会影响当前运行行为。
术语约定
为避免再把“白名单”这种不准确的说法带进来,这个仓库统一使用下面几组名称:
-
纳入监控模型
- 指
config.yaml中启用了火山api_base的上游模型。 - 这些模型会被
volc_limit_sync.py自动发现,并参与资源包同步、用量统计和限额控制。
- 指
-
监控别名
- 指火山资源包返回的名字,经过归一化后,用来匹配“纳入监控模型”的名字。
- 例如
doubao-seed-2-0-pro-260215会自动推导出监控别名doubao-seed-2-0-pro。
-
手工别名覆盖
- 指
volc_limit_sync.py中MANUAL_MONITORED_MODEL_ALIASES里的特殊映射。 - 只用于两类场景:
- 资源包名和上游模型名不一致。
- 一个资源包要映射到多个上游模型。
- 指
-
硬限额
- 指
volc_limit_sync.py中的HARD_DAILY_LIMITS。 - 支持三种常见粒度:
model:hashmodel__DEFAULT__
- 指
维护入口
主入口:config.yaml
新增、删除“纳入监控模型”时,第一步永远是改 config.yaml 里的 model_list。
对大多数正常的版本号模型,做到这一步就已经足够让运行时识别它们进入监控链路了。现在的逻辑会自动把:
deepseek-v3-2-251201 -> deepseek-v3-2
doubao-seed-2-0-pro-260215 -> doubao-seed-2-0-pro
glm-4-7-251222 -> glm-4-7
这类常规版本号模型推导成监控别名,不需要你再手工去补映射。
审计入口:generate_keys.py
每次改完 config.yaml 后,执行:
python scripts/generate_keys.py
这个脚本现在不是单纯“生成 key”,而是正式的 监控模型审计入口。它会输出:
- 当前从
config.yaml发现了哪些“纳入监控模型” - 每个上游模型对应哪些“监控别名”
- 每个模型当前命中的限额来源
- 自定义键
- 模型级规则
- 模糊规则
__DEFAULT__
- 哪些模型当前仍在使用
__DEFAULT__ - 如果你想把默认限额改成单模型硬限额,应复制哪些 key 到
HARD_DAILY_LIMITS
特殊入口:volc_limit_sync.py
只有下面两种情况,才需要改 volc_limit_sync.py:
- 资源包名和上游模型名不一致
示例:
"doubao-seed-code": "doubao-seed-code-preview-251028"
- 一个资源包要映射到多个上游模型
示例:
"doubao-seed-1-6": ["doubao-seed-1-6-250615", "doubao-seed-1-6-251015"]
如果只是新增一个普通的版本号模型,例如 xxx-260215,通常不需要改这里。
推荐维护流程
新增监控模型
- 在
config.yaml的model_list里加入新的火山模型条目。 - 运行
python scripts/generate_keys.py。 - 先确认脚本输出中:
- 该模型已经出现在“监控模型审计”里。
- “监控别名”正确。
- “限额来源”符合预期。
- 如果只是走默认限额,到这一步通常已经可以结束。
- 如果需要单模型硬限额,把脚本输出的 key 复制到
volc_limit_sync.py的HARD_DAILY_LIMITS。 - 只有当脚本显示资源包名无法靠常规版本号自动推导时,再去改
MANUAL_MONITORED_MODEL_ALIASES。 - 重启 LiteLLM 容器,让新配置生效。
删除监控模型
- 从
config.yaml里删掉对应模型条目。 - 运行
python scripts/generate_keys.py,确认它已经不在“监控模型审计”里。 - 如果该模型曾有单独的
HARD_DAILY_LIMITS项,可以按需清理。 - 如果该模型对应的是特殊手工别名,且已经没有任何上游模型再用它,可以再清理
MANUAL_MONITORED_MODEL_ALIASES。 - 重启 LiteLLM 容器。
运行时行为概览
1. 自动发现纳入监控模型
volc_limit_sync.py 启动后会扫描 config.yaml,找出使用火山 api_base 的上游模型,并记录:
- 监控的上游模型名
api_base对应的 hash- 由上游模型自动推导出来的监控别名
2. 资源包名归一化
火山接口返回资源包名后,脚本会先做归一化:
- 去掉资源包后缀
- 统一小写
_和.归一化成-
之后再尝试通过:
- 自动版本号推导
- 手工别名覆盖
把资源包归到实际纳入监控的上游模型。
3. 限额来源优先级
HARD_DAILY_LIMITS 的命中顺序是:
model:hashmodel- 模糊规则
__DEFAULT__
例如(当前值,2026-08):
HARD_DAILY_LIMITS = {
# 火山免费额度规则:每模型每日采集上限 2M,次日按采集量发放资源包。
# 采集上限封顶;真正决定今日可采的是 v5 动态层:
# effective = min(2M, stable_limit),stable_limit = 余额快照 − BUFFER(见 docs/volc-quota-notes.md)
"__DEFAULT__": 2_000_000,
}
常见坑
1. 改了旧脚本(archive/daily_limit.py.deprecated)但不生效
这是最容易踩的坑。
当前 config.yaml 没有启用旧模块 daily_limit,所以:
- 改
archive/daily_limit.py.deprecated - 改
MODEL_QUOTAS
都不会影响当前 LiteLLM 容器的启动和监控行为。
2. 只补了硬限额,但没有纳入监控
HARD_DAILY_LIMITS 只负责限额,不负责把模型纳入监控。
模型是否被监控,首先取决于:
config.yaml是否有该火山模型条目- 资源包名是否能被自动推导或手工别名覆盖正确归到该模型
3. 普通版本号模型不需要每次都手改映射
现在的运行逻辑已经支持从上游模型自动推导:
xxx-251201 -> xxx
xxx-260215 -> xxx
所以普通版本升级时,通常只需要改 config.yaml,再跑一遍 python scripts/generate_keys.py 审计确认。
常用命令
审计当前纳入监控模型
python scripts/generate_keys.py
直接运行同步监控
python volc_limit_sync.py
LiteLLM 调试模式
DAILY_LIMIT_LOG_LEVEL=DEBUG python volc_limit_sync.py
手动软踢某个模型(运维应急,立即 429)
模型踩采集上限/异常时,无需等脚本同步阈值,直接往 Redis 写一个 manual_evict key 即可将该模型立即、持续地软踢(恒 429,直到显式解除)。
key 格式:manual_evict:<config.yaml 中该条目的完整 model 字段>(含渠道前缀,全匹配,如 volcengine/deepseek-v4-flash-ga-260731;同一 clean 名但不同渠道的模型不会被误伤)。
# 软踢:立即对该模型返回 429(key 落 Redis 持久卷,重启 litellm 后仍生效)
docker exec litellm_redis redis-cli SET manual_evict:volcengine/<model> 1
# 解除软踢
docker exec litellm_redis redis-cli DEL manual_evict:volcengine/<model>
# 查看当前手动软踢的模型
docker exec litellm_redis redis-cli KEYS manual_evict:*
示例(软踢 deepseek-v4-flash-ga-260731):
docker exec litellm_redis redis-cli SET manual_evict:volcengine/deepseek-v4-flash-ga-260731 1
注意:
- 软踢只作用于纳入监控的火山模型(
MONITORED_MODEL_PAIRS内);非监控渠道不受影响。 - 幂等:key 在期间每个请求都 429,重复 SET 无副作用。
- 解除后立即恢复按正常限额判定。
- 自动解除:当脚本检测到当前
balance_at_sync比踢除时记录的余额快照上升 ≥ 50 万(AUTO_RELEASE_BALANCE_MARGIN,可调),即视为免费额度已返还,自动 DEL 该 key 并恢复请求。日常运维无需手动 DEL;仅当模型已被自动解除但你想立即恢复软踢时,重新 SET 即可。 - 自动解除阈值默认 50 万,可在
.env中覆盖:AUTO_RELEASE_BALANCE_MARGIN=1000000。
文件职责
litellm-config/
├── config.yaml # 纳入监控模型的主入口
├── docker-compose.yml # 容器编排(挂载 config.yaml / volc_limit_sync.py / volc_consume.py)
├── volc_limit_sync.py # 当前生效的监控、同步、限额脚本(v4 冻结快照限额)
├── volc_consume.py # 每日 23:20 自动消耗任务(防免费资源包过期蒸发)
├── AGENTS.md # 仓库 Agent 协议(代码风格/运行/提交规范)
├── README.md
├── docs/
│ ├── merge-auto-consume.md # 消耗任务 + 限额设计(v2 历史 / v4 修订)
│ ├── volc-quota-notes.md # 火山额度机制速查(扣费口径/方案演进/排障)
│ └── merge-volc-limit-into-newapi.md # 未实施的迁移方案(历史)
├── scripts/
│ └── generate_keys.py # 监控模型审计入口
├── debug/
│ ├── debug_volc_sync.py # 资源包调试脚本
│ └── debug_volc_sync_manual.py # 手动调试脚本
└── archive/
├── daily_limit.py.deprecated # 旧脚本,当前 config 未启用(历史参考)
└── recover.py # 一次性恢复工具(历史)
维护原则
- 先改
config.yaml,不要先改旧脚本archive/daily_limit.py.deprecated - 先跑
python scripts/generate_keys.py审计,再决定要不要补HARD_DAILY_LIMITS - 普通版本号模型优先依赖自动推导,不要手工补一堆重复映射
- 只有特殊别名或一对多场景,才改
MANUAL_MONITORED_MODEL_ALIASES