console: 任务详情自动显示成本明细
打开详情就自动拉 GET /tasks/{id}/cost(不再只靠
“查成本”按钮出 JSON),详情顶部新一行:美元、总
token(实际/估算、缓存、无价单列)、轮数、fallback
次数;美元小于 1 美分时用 4 位小数,避免小任务
显示成 $0.00 误导成免费;加载中/失败有各自的
弱化文案,成本拉失败不影响详情其它内容;原
“查成本”按钮不动,仍可手动拉原始 JSON。
服务端不用改:成本接口与 _get_task 的 token 聚合
都已在 #150 / 详情 meta 那单里就位。
This commit is contained in:
1 parent
0f7162934b
commit
994c4ea5d1
7 files changed
+57
-31
No files matched your search
@@ -130,7 +130,7 @@ export function useConsoleStore(): ConsoleCtx {
|
||||
filteredTasks: taskList.filteredTasks,
|
||||
currentTask: detail.currentTask, detailBody: detail.detailBody, actionMsg: detail.actionMsg, actionReasons: detail.actionReasons,
|
||||
showDetail: (id: string) => void detail.showDetail(id),
|
||||
doAction: detail.doAction, taskCost: detail.taskCost, costBox: detail.costBox,
|
||||
doAction: detail.doAction, taskCost: detail.taskCost, costBox: detail.costBox, costSummary: detail.costSummary,
|
||||
forkVisible: detail.forkVisible, setForkVisible: detail.setForkVisible, forkTask: detail.forkTask, forkMsg: detail.forkMsg,
|
||||
coordChildren: detail.coordChildren, coordText: "", agents: detail.agents, agentsNote: detail.agentsNote,
|
||||
reviews: detail.reviews, reviewMsg: detail.reviewMsg, reviewError: detail.reviewError,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { api, getKey } from "../api/client";
|
||||
import { paths } from "../api/paths";
|
||||
import { TRACE_TERMINAL } from "../api/types";
|
||||
import type { Agent, Approval, ChatHistory, Followup, PeerMessage, Review, Task, ThreadInfo, TraceEntry } from "../api/types";
|
||||
import type { Agent, Approval, ChatHistory, Followup, PeerMessage, Review, Task, TaskCost, ThreadInfo, TraceEntry } from "../api/types";
|
||||
|
||||
// Task detail state: the selected task, its trace SSE/one-shot stream,
|
||||
// and every structured sub-list. Chat sending lives here too:
|
||||
@@ -13,6 +13,9 @@ export function useTaskDetail(getRole: (pid?: string) => string | null, onTasksR
|
||||
const [actionMsg, setActionMsg] = useState("");
|
||||
const [actionReasons, setActionReasons] = useState("");
|
||||
const [costBox, setCostBox] = useState("");
|
||||
// 成本明细:打开详情就自动加载(不再只靠“查成本”按钮出 JSON)。
|
||||
// undefined = 加载中,null = 加载失败,对象 = 已加载。
|
||||
const [costSummary, setCostSummary] = useState<TaskCost | null | undefined>(undefined);
|
||||
const [forkVisible, setForkVisible] = useState(false);
|
||||
const [forkMsg, setForkMsg] = useState("");
|
||||
const [coordChildren, setCoordChildren] = useState<Task[]>([]);
|
||||
@@ -68,6 +71,13 @@ export function useTaskDetail(getRole: (pid?: string) => string | null, onTasksR
|
||||
} catch { setCoordChildren([]); }
|
||||
}, []);
|
||||
|
||||
const loadCost = useCallback(async (taskId: string) => {
|
||||
try {
|
||||
const c = await api<TaskCost>(paths.taskCost(taskId));
|
||||
setCostSummary(c);
|
||||
} catch { setCostSummary(null); }
|
||||
}, []);
|
||||
|
||||
const loadFollowups = useCallback(async (taskId: string) => {
|
||||
try { const d = await api<{ followups: Followup[] }>(paths.taskFollowups(taskId)); setFollowups(d.followups || []); }
|
||||
catch { setFollowups([]); }
|
||||
@@ -215,6 +225,8 @@ export function useTaskDetail(getRole: (pid?: string) => string | null, onTasksR
|
||||
if (!body) body = "(暂无 result/error 输出)";
|
||||
setDetailBody(body);
|
||||
setCostBox("");
|
||||
setCostSummary(undefined);
|
||||
void loadCost(taskId);
|
||||
setForkVisible(false);
|
||||
setForkMsg("");
|
||||
setPeerMsg("");
|
||||
@@ -247,7 +259,7 @@ export function useTaskDetail(getRole: (pid?: string) => string | null, onTasksR
|
||||
setCurrentTask(null);
|
||||
setDetailBody("加载失败:" + e.message);
|
||||
}
|
||||
}, [stopTrace, startTrace, loadAgents, loadCoordChildren, loadChatHistory, loadFollowups, loadPeerMessages, loadThread, loadThreads, loadReviews, loadApprovals, getRole]);
|
||||
}, [stopTrace, startTrace, loadAgents, loadCoordChildren, loadChatHistory, loadFollowups, loadPeerMessages, loadThread, loadThreads, loadReviews, loadApprovals, loadCost, getRole]);
|
||||
|
||||
const refreshDetailLists = useCallback(async () => {
|
||||
const t = currentTaskRef.current;
|
||||
@@ -269,7 +281,8 @@ export function useTaskDetail(getRole: (pid?: string) => string | null, onTasksR
|
||||
void loadPeerMessages(t.id);
|
||||
void loadThread(t.id);
|
||||
void loadThreads(t.id);
|
||||
}, [loadFollowups, loadChatHistory, loadReviews, loadApprovals, loadCoordChildren, loadAgents, loadPeerMessages, loadThread, loadThreads]);
|
||||
void loadCost(t.id);
|
||||
}, [loadFollowups, loadChatHistory, loadReviews, loadApprovals, loadCoordChildren, loadAgents, loadPeerMessages, loadThread, loadThreads, loadCost]);
|
||||
|
||||
const doAction = useCallback(async (kind: string) => {
|
||||
const t = currentTaskRef.current;
|
||||
@@ -409,7 +422,7 @@ export function useTaskDetail(getRole: (pid?: string) => string | null, onTasksR
|
||||
useEffect(() => () => stopTrace(), [stopTrace]);
|
||||
|
||||
return {
|
||||
currentTask, detailBody, actionMsg, actionReasons, costBox, forkVisible, setForkVisible, forkMsg,
|
||||
currentTask, detailBody, actionMsg, actionReasons, costBox, costSummary, forkVisible, setForkVisible, forkMsg,
|
||||
coordChildren, agents, agentsNote, reviews, reviewMsg, reviewError, traceEntries, traceNote, traceDone,
|
||||
traceKind, setTraceKind, traceMsg, followups, approvals, approvalMsg, peerMessages, peerMsg,
|
||||
threadText, threadGateNote, threadMsg, threadList, chatMsg, chatHistory,
|
||||
|
||||
Reference in new issue
Block a user