feat(proxy): T-X1 预算四档渐进干预(采纳 ai-model-router budgets 设计)

- billing.BillingMixin 新增 budget_mode(student, est, ts):整数基点判定
  normal(<80%) / optimize(>=80%) / cheap(>=95%) / block(>100%);
  cap<=0 不限额恒 normal;学生缺失 normal(扣费与拒绝权威仍在 try_hold)
- routes._run_chat:optimize/cheap 档自动降档池条目(_downgrade_entry,
  TIERS 排名制降档、杜绝反向升档、跳过停用与 mock),重算 model/est;
  非正常档位经 X-Budget-Mode 响应头如实上报(缓存命中路径同样携带)
- 黄金用例锁死边界:7999/8000/9499/9500/10000/10001bp 九组参数化断言,
  全整数运算无浮点漂移;跨日重置与不限额口径与 try_hold 一致

pytest 443 passed(基线 425 + 18)
This commit is contained in:
tzt
2026-09-18 22:28:50 +08:00
parent ebb3cbb41d
commit aa7cb0704c
4 changed files with 237 additions and 8 deletions
+39
View File
@@ -21,6 +21,10 @@ _SQL_HOLD = """UPDATE students SET
spent_today_milli = ?, spent_date = ?
WHERE id = ? AND balance_milli >= ?"""
# 预算四档阈值(T-X1,基点 bp:1% = 100bp;整数比较,杜绝浮点边界漂移)
BUDGET_OPTIMIZE_BP = 8000 # >= 80% 建议降一档
BUDGET_CHEAP_BP = 9500 # >= 95% 强制最低档
_SQL_HOLD_MARK = """INSERT INTO usage_ledger
(request_id, ts, key_id, model, bucket, charged_milli, status)
VALUES (?, ?, ?, ?, ?, ?, 'holding')"""
@@ -132,6 +136,41 @@ class BillingMixin:
" WHERE request_id = ?", (status, request_id))
return True
# ---------- 预算四档(T-X1 ----------
def budget_mode(self, student_id: int, est_milli: int, ts: float) -> str:
"""预算档位判定:normal / optimize / cheap / block。
以「当日已 spent + 本次预估」占日上限的比例判定(基点 bp,全整数运算,
无浮点边界漂移;黄金用例锁死):
< 8000bp80% -> normal(现行行为)
>= 8000bp80% -> optimize(调用方应降一档模型)
>= 9500bp95% -> cheap(调用方应强制最低档)
> 10000bp100% -> blocktry_hold 的硬拒绝语义兜底)
daily_cap_milli <= 0 视为不限额 -> 恒 normal;学生不存在 -> normal
(try_hold 才是扣费与拒绝的唯一权威,本方法只做档位建议)。
"""
today = _today(ts)
with self._lock, self._connect() as conn:
stu = conn.execute(
"SELECT daily_cap_milli, spent_today_milli, spent_date"
" FROM students WHERE id = ?", (student_id,)).fetchone()
if stu is None:
return "normal"
cap = int(stu["daily_cap_milli"] or 0)
if cap <= 0:
return "normal"
spent = int(stu["spent_today_milli"] or 0) \
if stu["spent_date"] == today else 0
projected = spent + max(0, int(est_milli))
projected_bp = projected * 10000 // cap
if projected_bp > 10000:
return "block"
if projected_bp >= BUDGET_CHEAP_BP:
return "cheap"
if projected_bp >= BUDGET_OPTIMIZE_BP:
return "optimize"
return "normal"
# ---------- 流水查询 ----------
def get_usage(self, request_id: str) -> Optional[Dict[str, Any]]:
with self._lock, self._connect() as conn: