feat(commands): 工具执行页新增调用次数统计

「工具执行」页工具栏加「统计」按钮,弹窗按工具展示调用次数、占比与
失败次数,按次数降序。统计沿用列表的 task/q 筛选,算的是整个结果集
而非当前页;弹窗打开时才请求,翻页不会多付一次聚合查询。

- db: 抽出 commandFilter,列表与统计共用同一份 WHERE;新增 ToolStats
- server: GET /api/commands/stats
- web: 统计弹窗 + demo 模式 mock
This commit is contained in:
Autumn-27 committed 2026-08-27 10:56:11 -04:00
1 parent a380eb4195
commit e10f8413dc
7 files changed
+207 -22

No files matched your search

+8
View File
@@ -71,6 +71,7 @@ import type {
TokenTotal,
TokenUsage,
Tool,
ToolStat,
TrafficDetail,
TrafficHost,
TrafficResp,
@@ -941,6 +942,13 @@ export const api = {
sp.set("size", String(params?.size ?? 50));
return get<{ commands: CommandRecord[]; total: number }>(`/commands?${sp}`);
},
// 各工具调用次数;沿用列表的 task/q 筛选,统计的是整个结果集而非当前页。
commandStats: (params?: { task?: string; q?: string }) => {
const sp = new URLSearchParams();
if (params?.task) sp.set("task", params.task);
if (params?.q) sp.set("q", params.q);
return get<{ stats: ToolStat[] }>(`/commands/stats?${sp}`);
},
// ---- LLM records ----
llmRecords: (params?: { model?: string; session?: string; task?: string; page?: number; size?: number }) => {
+11
View File
@@ -1128,6 +1128,17 @@ function route(m: string, path: string, seg: string[], q: URLSearchParams, b: Re
// ── 工具执行历史 ──
if (path === "/commands" && m === "GET") return { commands: D.commandRecords, total: D.commandRecords.length };
if (path === "/commands/stats" && m === "GET") {
const tally = new Map<string, { tool: string; total: number; errors: number }>();
for (const c of D.commandRecords) {
const tool = c.tool || "-";
const s = tally.get(tool) ?? { tool, total: 0, errors: 0 };
s.total++;
if (c.is_error) s.errors++;
tally.set(tool, s);
}
return { stats: [...tally.values()].sort((a, b) => b.total - a.total || a.tool.localeCompare(b.tool)) };
}
// ── LLM ──
if (path === "/llm/records" && m === "GET") return { records: mockLLMRecords, total: mockLLMRecords.length };
+7
View File
@@ -1065,6 +1065,13 @@ export interface CommandRecord {
created_at: string;
}
// 单个工具的调用统计(/commands/stats);errors 为其中失败的次数。
export interface ToolStat {
tool: string;
total: number;
errors: number;
}
// ---- LLM recording ----
export interface LLMRecordItem {
id: number;