多用户生产级缺口批:sweep 鉴权、默认配额、默认 executor、 无价 token 上限
- POST /admin/sweep 改走任一项目 maintainer+ 门槛(与备份管理同口径),否则 403,记 admin_sweep 审计;threshold 下限钳 60 秒 - OAuth 建号 created=True 时写默认配额行(默认 $10/月,CLOUD_DEFAULT_QUOTA_USD_MICROS 可调,INSERT OR IGNORE 幂等);老用户无配额行不受影响 - 建任务未指定且无环境继承时默认 executor=docker(分支继承为空时同理);CLOUD_DEFAULT_EXECUTOR 可覆盖,空字符串恢复旧行为;命令台下拉默认改为 docker - 无价模型 token 上限:价目表外模型(默认 terra)成本 usd 为 NULL,USD 配额永不生效;有配额行的用户当月无价 token 达 CLOUD_QUOTA_UNPRICED_TOKEN_CAP(默认 2000 万/月)即停,服务端 402、worker 认领/重试/后续消息同口径;报表仍记 unpriced,不折假美元 - 测试:新增 test_multiuser_gaps 11 个 + test_worker_quota 3 个;全量 cloud 668、host 64、deploy 19 全过
This commit is contained in:
1 parent
c1f9313c10
commit
7e081211db
7 files changed
+580
-13
No files matched your search
@@ -80,6 +80,26 @@ def _insert_donor_cost(db_path, micros):
|
||||
pass
|
||||
|
||||
|
||||
def _insert_unpriced_cost(db_path, tokens):
|
||||
"""给 tester 记一笔无价成本:已完成任务 + usd_micros NULL 的行。
|
||||
|
||||
tokens 拆成 input/output 两半记(input + output = tokens,
|
||||
与服务端/ worker 的无价 token 口径一致)。
|
||||
"""
|
||||
_insert_task(db_path, "donor-unpriced", status="completed")
|
||||
store = CostStore(db_path)
|
||||
try:
|
||||
store.record("donor-unpriced", "p1", "gpt-5.6-terra",
|
||||
input_tokens=tokens // 2,
|
||||
output_tokens=tokens - tokens // 2,
|
||||
usd_micros=None, source="actual")
|
||||
finally:
|
||||
try:
|
||||
store._conn.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _insert_followup(db_path, fu_id, task_id, body="follow up"):
|
||||
now = time.time()
|
||||
conn = sqlite3.connect(db_path)
|
||||
@@ -229,6 +249,73 @@ class WorkerQuotaTestCase(unittest.TestCase):
|
||||
result = worker.run_one()
|
||||
self.assertEqual(result["status"], "completed")
|
||||
|
||||
# -- 无价 token 上限 -------------------------------------------------
|
||||
def test_unpriced_token_cap_blocks_claim(self):
|
||||
_insert_quota(self.db_path, "tester", 10 ** 12) # USD 很大,不挡
|
||||
_insert_unpriced_cost(self.db_path, 150)
|
||||
_insert_task(self.db_path, "T1")
|
||||
worker = self._worker(_usage_runner(
|
||||
{"input_tokens": 10, "output_tokens": 1, "total_tokens": 11},
|
||||
self.calls))
|
||||
old = os.environ.get("CLOUD_QUOTA_UNPRICED_TOKEN_CAP")
|
||||
os.environ["CLOUD_QUOTA_UNPRICED_TOKEN_CAP"] = "100"
|
||||
try:
|
||||
result = worker.run_one()
|
||||
finally:
|
||||
if old is None:
|
||||
os.environ.pop("CLOUD_QUOTA_UNPRICED_TOKEN_CAP", None)
|
||||
else:
|
||||
os.environ["CLOUD_QUOTA_UNPRICED_TOKEN_CAP"] = old
|
||||
self.assertEqual(result["status"], "interrupted")
|
||||
self.assertIn("无价模型 token", result["error"])
|
||||
self.assertEqual(self.calls, [])
|
||||
|
||||
def test_unpriced_round_usage_counts_toward_cap(self):
|
||||
_insert_quota(self.db_path, "tester", 10 ** 12)
|
||||
_insert_unpriced_cost(self.db_path, 90)
|
||||
_insert_task(self.db_path, "T1")
|
||||
_insert_followup(self.db_path, "fu1", "T1")
|
||||
worker = Worker(self.db_path, "/fake/host.py", runner=_usage_runner(
|
||||
{"input_tokens": 50, "cached_input_tokens": 0,
|
||||
"output_tokens": 0, "total_tokens": 50},
|
||||
self.calls), env=self.env, model="gpt-5.6-terra",
|
||||
retry_backoff_seconds=0)
|
||||
old = os.environ.get("CLOUD_QUOTA_UNPRICED_TOKEN_CAP")
|
||||
os.environ["CLOUD_QUOTA_UNPRICED_TOKEN_CAP"] = "100"
|
||||
try:
|
||||
result = worker.run_one()
|
||||
finally:
|
||||
if old is None:
|
||||
os.environ.pop("CLOUD_QUOTA_UNPRICED_TOKEN_CAP", None)
|
||||
else:
|
||||
os.environ["CLOUD_QUOTA_UNPRICED_TOKEN_CAP"] = old
|
||||
# 首轮无价 50 token:90 + 50 = 140 >= 100,排空时停。
|
||||
self.assertEqual(result["status"], "interrupted")
|
||||
self.assertIn("quota_exceeded", result["error"])
|
||||
self.assertEqual(_followup_status(self.db_path, "fu1"), "pending")
|
||||
|
||||
def test_priced_usage_ignores_token_cap(self):
|
||||
_insert_quota(self.db_path, "tester", 10 ** 12)
|
||||
_insert_unpriced_cost(self.db_path, 90)
|
||||
_insert_task(self.db_path, "T1")
|
||||
_insert_followup(self.db_path, "fu1", "T1")
|
||||
worker = self._worker(_usage_runner(
|
||||
{"input_tokens": 100, "cached_input_tokens": 0,
|
||||
"output_tokens": 10, "total_tokens": 110},
|
||||
self.calls))
|
||||
old = os.environ.get("CLOUD_QUOTA_UNPRICED_TOKEN_CAP")
|
||||
os.environ["CLOUD_QUOTA_UNPRICED_TOKEN_CAP"] = "100"
|
||||
try:
|
||||
result = worker.run_one()
|
||||
finally:
|
||||
if old is None:
|
||||
os.environ.pop("CLOUD_QUOTA_UNPRICED_TOKEN_CAP", None)
|
||||
else:
|
||||
os.environ["CLOUD_QUOTA_UNPRICED_TOKEN_CAP"] = old
|
||||
# 有价模型用量不计入无价上限:90 < 100,正常跑完。
|
||||
self.assertEqual(result["status"], "completed")
|
||||
self.assertEqual(_followup_status(self.db_path, "fu1"), "done")
|
||||
|
||||
# -- 轮询掐断 ------------------------------------------------------
|
||||
def test_poll_loop_quota_kill(self):
|
||||
_insert_task(self.db_path, "T1")
|
||||
|
||||
Reference in new issue
Block a user