逐任务真实成本:上游用量事件 + 价目表计价

此前每任务只记一条 estimated(字符数/4,金额为空),注释称
host 协议不暴露真实 token 数。本轮实测推翻了该前提:
- account/usage/read 在中继上是死的(要求 ChatGPT 账号登录),
  不是来源,已在 cost.py docstring 写明死胡同
- app-server 每上游补全发 thread/tokenUsage/updated,last 是该次
  补全的用量;中继 5 调用回合实测 last 之和等于最后 total
  (input/output/cached/reasoning 四路都对得上)

实现:host.py run_turn 从事件窗口按 threadId+turnId 汇总用量,
重试/goal 各轮累加,dispatch/run JSON 与 goal 用量行透传;
sandbox 三路解析(dispatch JSON 解析改用完整 stdout,顺带修
了长 text 下 failed 被误判 completed 的潜 bug);worker 每任务
记一条 actual(重试/fallback 用量累加进同一条),无用量时回落
estimated;cost.py 价目表(默认 OpenAI 挂牌价,env 可覆盖,
无价模型 usd 记 None、报表进 unpriced_actual_tokens 而非 0 美元),
cost_records 加 cached_input_tokens 列(SQLite/PG 两支迁移);
报表与按天行新增 cached_input_tokens/unpriced_actual_tokens。
测试:host 32、cloud 483 全过。
This commit is contained in:
host committed 2026-10-26 18:41:27 +08:00
1 parent 353d6c1aa8
commit 63823fc2e3
11 files changed
+709 -67

No files matched your search

+17 -4
View File
@@ -1092,14 +1092,20 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
row = conn.execute(
"SELECT day, COALESCE(SUM(total_tokens),0) AS t,"
" COALESCE(SUM(usd_micros),0) AS u, COUNT(*) AS c,"
" COALESCE(SUM(fallback_count),0) AS f"
" COALESCE(SUM(fallback_count),0) AS f,"
" COALESCE(SUM(cached_input_tokens),0) AS cached,"
" COALESCE(SUM(CASE WHEN source = 'actual'"
" AND usd_micros IS NULL THEN total_tokens"
" ELSE 0 END),0) AS unpriced"
" FROM cost_records WHERE project_id = ? AND day = ?"
" GROUP BY day",
(project_id, day)).fetchone()
if row:
daily = [{"day": row["day"], "total_tokens": row["t"],
"usd_micros": row["u"], "turns": row["c"],
"fallback_count": row["f"]}]
"fallback_count": row["f"],
"cached_input_tokens": row["cached"],
"unpriced_actual_tokens": row["unpriced"]}]
elif days is not None:
try:
n = int(days)
@@ -1112,13 +1118,20 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
rows = conn.execute(
"SELECT day, COALESCE(SUM(total_tokens),0) AS t,"
" COALESCE(SUM(usd_micros),0) AS u, COUNT(*) AS c,"
" COALESCE(SUM(fallback_count),0) AS f"
" COALESCE(SUM(fallback_count),0) AS f,"
" COALESCE(SUM(cached_input_tokens),0) AS cached,"
" COALESCE(SUM(CASE WHEN source = 'actual'"
" AND usd_micros IS NULL THEN total_tokens"
" ELSE 0 END),0) AS unpriced"
" FROM cost_records WHERE project_id = ? AND day >= ?"
" GROUP BY day ORDER BY day",
(project_id, cutoff)).fetchall()
daily = [{"day": r["day"], "total_tokens": r["t"],
"usd_micros": r["u"], "turns": r["c"],
"fallback_count": r["f"]} for r in rows]
"fallback_count": r["f"],
"cached_input_tokens": r["cached"],
"unpriced_actual_tokens": r["unpriced"]}
for r in rows]
finally:
try:
store._conn.close()