server: 任务列表一次聚合回 total_tokens
_list_tasks 在取附件的同时用 _task_tokens_map 一次查 cost_records 按 task_id 求和(GROUP BY),一屏只多一条 查询,不按任务逐个调成本接口。无记录计 0、表不存在时 全计 0 且列表不失败;_task_to_dict 新增 total_tokens 参数,默认 None(非列表调用方不把“未算”写成 0)。 前端 TaskListSection 的 token 栏位此前已接好 total_tokens, 补上后列表行直接显示真数,不再显示 token —;前端注释 与占位文案同步更新。 新测试 test_task_list_tokens.py 3 项:HTTP 列表聚合 (actual+estimated 加总、无记录为 0)、缺表不炸、 _task_to_dict 默认 None。
This commit is contained in:
1 parent
65a875ada9
commit
9eedee5a3d
4 files changed
+168
-14
No files matched your search
+38
-2
@@ -1233,6 +1233,37 @@ def _attachments_map(conn, task_ids):
|
||||
return out
|
||||
|
||||
|
||||
def _task_tokens_map(conn, task_ids):
|
||||
"""一批任务的 cost_records token 合计:{task_id: total_tokens}。
|
||||
|
||||
任务列表接口用一次查询取全量,不按任务逐条调成本接口
|
||||
(一屏 N 个任务就是 N 个请求,会拖慢列表刷新、还容易撞
|
||||
限流)。无成本记录的任务计 0(不是缺失);cost_records
|
||||
表还不存在时(全新库、CostStore 未建过表)同样全计 0,
|
||||
列表绝不因此失败——0 在这里就是“未记录到任何 token”,
|
||||
与 _metrics 的缺表按空统计口径一致。
|
||||
"""
|
||||
out = {tid: 0 for tid in task_ids}
|
||||
if not task_ids:
|
||||
return out
|
||||
placeholders = ", ".join("?" * len(task_ids))
|
||||
try:
|
||||
rows = conn.execute(
|
||||
"SELECT task_id, COALESCE(SUM(total_tokens),0) AS t"
|
||||
" FROM cost_records WHERE task_id IN (%s)"
|
||||
" GROUP BY task_id" % placeholders,
|
||||
tuple(task_ids),
|
||||
).fetchall()
|
||||
except (sqlite3.OperationalError, _PgOperationalError):
|
||||
return out
|
||||
for row in rows:
|
||||
tid = row[0] if isinstance(row, tuple) else row["task_id"]
|
||||
if tid in out:
|
||||
out[tid] = int(row[1] if isinstance(row, tuple)
|
||||
else row["t"] or 0)
|
||||
return out
|
||||
|
||||
|
||||
def _ensure_task_checkpoints_table(conn):
|
||||
"""幂等建断点表(P0 三件其一):server init_db(PG 与 SQLite
|
||||
两支)与 worker 迁移兜底共用。DOUBLE PRECISION 在两后端都可用。
|
||||
@@ -1663,7 +1694,7 @@ def _row_get(row, name):
|
||||
return None
|
||||
|
||||
|
||||
def _task_to_dict(row, attachments=None):
|
||||
def _task_to_dict(row, attachments=None, total_tokens=None):
|
||||
depends_on = row["depends_on"]
|
||||
try:
|
||||
depends_on = json.loads(depends_on) if depends_on else []
|
||||
@@ -1719,6 +1750,9 @@ def _task_to_dict(row, attachments=None):
|
||||
"interrupted_reason": _row_get(row, "interrupted_reason"),
|
||||
"auto_resume_count": _row_get(row, "auto_resume_count") or 0,
|
||||
"no_time_limit": _row_get(row, "no_time_limit") or 0,
|
||||
# 列表接口由 _task_tokens_map 一次聚合传入;其它调用方
|
||||
# 不传时为 None(前端显示占位),绝不把“未算”写成 0。
|
||||
"total_tokens": total_tokens,
|
||||
}
|
||||
|
||||
|
||||
@@ -4390,10 +4424,12 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
|
||||
(project_id,),
|
||||
).fetchall()
|
||||
amap = _attachments_map(conn, [r["id"] for r in rows])
|
||||
tmap = _task_tokens_map(conn, [r["id"] for r in rows])
|
||||
finally:
|
||||
conn.close()
|
||||
self._send_json(200, {"tasks": [
|
||||
_task_to_dict(r, amap[r["id"]]) for r in rows]})
|
||||
_task_to_dict(r, amap[r["id"]], tmap.get(r["id"], 0))
|
||||
for r in rows]})
|
||||
|
||||
def _fetch_task(self, task_id):
|
||||
conn = self._db()
|
||||
|
||||
Reference in new issue
Block a user