dd349b64cf
v2 的 stable_limit = usage + balance - BUFFER 依赖余额 API 随消耗实时递减, 实测滞后(08-12 快照 1,558,307 在 usage 涨 45 万期间不动)导致池膨胀、限额 虚高,放行到扣费区(当日超余额 13.8 万已扣费)。 扣费口径(用户确认):是否扣费只取决于当下免费额度,超出即扣费,采集量只 决定明日发放。v4:stable_limit = balance - BUFFER 冻结快照,本地 usage 实时 撞线即拦,等价于本地实时扣减余额;effective = min(2M 配额, stable_limit)。 新增 docs/volc-quota-notes.md 额度机制速查;merge-auto-consume.md 补 §4.5。
8.2 KiB
8.2 KiB
火山引擎免费额度单日限制合入 new-api 的可行性分析
日期:2026-08-05 背景:litellm 目前唯一实质用途是
volc_limit_sync.py(火山引擎免费资源包的单日用量限制)。考虑将该功能合入 new-api(另一台服务器的~/workspace/new-api,deploy 分支 fork),从而退役 litellm。 证据来源:volc_limit_sync.py(本仓库)与/root/workspace/new-api源码(HEADe4324841,2026-08-01,upstream QuantumNous/new-api 活跃)。
一、现状:volc_limit_sync.py 的四个核心能力
单文件 1393 行,挂在 LiteLLM 回调上,由后台守护线程驱动:
| 能力 | 实现 | 位置 |
|---|---|---|
| 火山余额同步 | SigV4 签名调 ListResourcePackages,分页拉取资源包;normalize_volc_name 去后缀归一化;按监控模型聚合余额 |
fetch_all_packages / process_packages_breakdown |
| 动态限额(Peak-Buffer) | PeakPool = 当日用量 + 余额;峰池跟踪(余额跳升 >5% 立即信任、骤降接受、初始化/漂移取当前值);stable_limit = PeakPool − 15% margin(下限 25w);与静态 HARD_DAILY_LIMITS(含 api_base hash 精确匹配、模糊匹配、__DEFAULT__ 兜底)取小得 effective_limit |
get_effective_limit + active_monitor_loop |
| 当日用量计数 | 成功回调 INCRBY total_tokens → Redis daily_usage:{北京日期}:{clean_model}:{api_base_hash};北京时间 00:00 滚动,TTL 2 天 |
_record_usage / get_usage_key |
| 超限停用/恢复 | soft_evict_from_router 把 router 中 model_name 重命名为 xxx-limit-reached 软踢;北京时间早 7–13 点每 30 分钟、其余 4 小时检查恢复;启动时 startup_audit 预踢超额模型 |
soft_evict_from_router / restore_blocked_models / startup_audit |
监控模型自动发现:从 config.yaml 解析 volcengine/ 前缀 + HUOSHAN_BASE_URL/volcengineapi 的条目,构建 MONITORED_MODEL_PAIRS(模型名 + api_base hash);常规版本号模型自动推导别名,特殊别名走 MANUAL_MONITORED_MODEL_ALIASES。
要点:按「模型 + 端点」粒度限制;成功后被动踢(滞后一个请求);靠直接改 LiteLLM router 内部结构实现,较为 hacky。
二、new-api 现状调查结论
✅ 可直接复用的机制
- volcengine 是原生渠道:
relay/channel/volcengine/完整适配器(constant.ChannelTypeVolcEngine,adaptor.go:242),支持 chat/embedding/images/TTS,GetRequestURL走/api/v3/...。 - 渠道状态机:
Status:1=启用 / 2=手动禁用 / 3=自动禁用(common/constants.go:252-254);service.DisableChannel/EnableChannel(service/channel.go:19-36);自动禁用链路processChannelError→ShouldDisableChannel+AutoBan(controller/relay.go:399-404)。 - 定时任务基建:通用
SystemTaskRunner(DB 租约去重、多 master 互斥、ScheduledSystemTaskHandler接口 Type/Run/Enabled/Interval/NewPayload,service/system_task.go:29-44;main.go:151-152 启动)。另有订阅配额每日重置任务StartSubscriptionQuotaResetTask(ticker 1min 批量重置,含 daily 对齐次日 0 点的calcNextResetTime,model/subscription.go:352-381,main.go:129)。 - 用量数据:
quota_data表按 模型 × 渠道 × 小时 聚合(LogQuotaData截断到小时,model/usedata.go:78-81);logs表有每次请求 token 明细(RecordConsumeLog,model/log.go:343)。按模型查当日用量现成。 - 渠道级限流范式:
service/channel_rate_limit.go(Redis 固定窗口 RPM 检查,relay 重试循环内排除被限渠道并 429)——与「每日限额」几乎同构,是最佳参考实现。 - 渠道额外设置 JSON 扩展范式:
ChannelOtherSettings(relaykit/dto/channel_settings.go:68-91,含 RateLimitEnabled/RateLimitRPM/RateLimitTPM)——每日限额字段可照此扩展。 - 程序化渠道管理 API:
POST /api/channel/:id/status(启停)、PUT /api/channel/(改配置)、GET /api/channel/(列表)(router/channel-router.go:39-90)——旁路方案 B 的基础。
❌ 完全缺失的(全仓库 grep DailyLimit/每日限额 0 命中)
- 渠道/模型的每日额度限额字段与执行逻辑(Channel 表只有累计
UsedQuota,只增不减)。 - 令牌配额每日重置(一次性扣减;subscription 每日重置是唯一先例)。
- 火山余额同步(适配器只转发、不查账)。
三、可行性结论:可行,且 new-api 架构上更合适
- 可请求前拦截:渠道选择在
middleware.Distribute/controller.Relay重试循环(controller/relay.go:204-210,与 RPM 限流同款位置)完成,可在请求发出前跳过超限渠道——优于现状「成功后才踢」,杜绝超限瞬间的漏网请求。 - 停用/恢复有现成状态机:无需 hack router 内部结构;自动恢复可借鉴周期渠道测试的 PassiveRecovery 模式(controller/channel-test.go:1024)。
- 同步任务有框架:火山余额同步做成一个
ScheduledSystemTaskHandler,天然多 master 安全。 - litellm 可退役:合并后 litellm + redis + postgres 三个容器下线,消除一个攻击面(docker-compose 注释已提示 litellm 1.82.7/1.82.8 被投毒)。
四、两条合入路径对比
| A. 改 fork 源码(Go 实现) | B. 不改源码:独立守护进程 + 管理 API | |
|---|---|---|
| 做法 | 在 deploy fork 上加每日限额功能(字段 + 检查点 + 同步任务 + 前端) | 改造 volc_limit_sync.py 去掉 litellm 依赖:查 new-api DB/Redis 统计当日用量 → 调 POST /api/channel/:id/status 停/启渠道;余额同步逻辑原样保留 |
| 拦截时机 | 请求前(跳过渠道 / 429) | 成功后停渠道(与现状一致,滞后一个请求) |
| 成本 | Go 开发 + 前端表单 + fork 维护 | Python 改造 + 一个常驻进程 |
| 优点 | 彻底、架构最干净、UI 可配置 | 不碰 Go、可快速上线、可作 PoC |
| 缺点 | 工作量大;upstream 更新极频繁需持续同步 | 无法请求前拦截;多一个系统要运维 |
倾向推荐 A:已在用带定制功能的 deploy fork(RPM 限流、订阅重置、系统任务均为 fork 自加),fork 维护成本已存在;A 方案下「超限恢复」可与渠道自动测试/恢复机制统一。
五、路径 A 改造点清单
ChannelOtherSettings扩展每日限额配置:DailyLimitEnabled/DailyLimitTokens/ 火山余额同步开关等。- 新增
service/channel_daily_limit.go:当日用量计数(Redis INCR,北京时间滚动,仿service/channel_rate_limit.go固定窗口)。 - 检查点:
controller/relay.go:204-210重试循环内跳过超限渠道(或直接 429)。 - 新增
SystemTaskHandler:火山余额同步(SigV4ListResourcePackages+ 归一化 + Peak-Buffer 动态限额),结果写 Redis/DB。 - 超限处理:渠道级用
DisableChannel;粒度注意——litellm 按「模型」限,new-api 需按「渠道 + 模型」统计(quota_data有 ChannelID+ModelName 双维度,正合适)。 - 前端渠道编辑表单加每日限额字段。
- 迁移 config.yaml:别名 1:N 映射(如
doubao-seed-nothink→ 3 个上游模型)在 new-api 中 model_mapping 是 1:1,需用「多个渠道声明同一对外模型名 + 权重轮询」实现。
六、风险提示
- Peak-Buffer 算法是多年调出来的业务资产(跳升信任 / 骤降接受 / 15% 边际 / 30w 缓冲 / 空闲感知 + 突发检测的动态同步间隔),移植时须完整保留,勿简化。
- fork 分支特殊性:系统任务 / RPM 限流 / 订阅重置等基建上游 main 没有;若目标是反哺上游,需在纯上游上重新评估。
- 密钥暴露面:迁移后 new-api 直接持有火山 AK/SK(目前只存在于 litellm 服务器 env),需评估。
- 北京时间语义:每日重置必须按 UTC+8 对齐(现有
get_usage_key与订阅重置calcNextResetTime均有先例)。
七、后续可选动作
- 路径 A 详细技术设计(数据模型 + 关键函数签名 + 前端字段)。
- 路径 B 快速 PoC(可运行的旁路守护进程验证效果)。
- 仅迁移规划(config.yaml 别名 → new-api 渠道配置映射表)。