fix: 修复 AI 回复 token 统计信息显示

- 直接 API 路径:增强 finalStats 捕获,从任意包含统计的 chunk 兜底收集
- Agent Loop 路径:新增 total_duration / eval_count 统计回传
- chat-area.ts: 修复 falsy 判断(eval_count=0 也应显示)
- agent-engine.ts: onDone 回调新增 stats 参数
This commit is contained in:
thzxx
2026-04-07 01:56:17 +08:00
parent 9735a1ed38
commit a80baf363c
3 changed files with 37 additions and 17 deletions
+16 -9
View File
@@ -32,7 +32,7 @@ export interface AgentCallbacks {
onToolCallStart: (call: ToolCall) => void;
onToolCallResult: (name: string, result: ToolResult, call: ToolCall) => void;
onToolCallError: (name: string, error: string, call: ToolCall) => void;
onDone: (finalContent: string, toolRecords?: ToolCallRecord[]) => void;
onDone: (finalContent: string, toolRecords?: ToolCallRecord[], stats?: { eval_count?: number; total_duration?: number }) => void;
onConfirmTool: (call: ToolCall) => Promise<boolean>;
}
@@ -101,6 +101,12 @@ export async function runAgentLoop(
const allToolRecords: ToolCallRecord[] = [];
const loopStartTime = Date.now();
let content = '';
let totalEvalCount = 0;
const makeStats = () => {
const totalDuration = (Date.now() - loopStartTime) * 1e6; // 转为纳秒
return { eval_count: totalEvalCount || undefined, total_duration: totalDuration };
};
while (loopCount < MAX_LOOPS) {
loopCount++;
@@ -108,14 +114,14 @@ export async function runAgentLoop(
// 全局超时检查
if (Date.now() - loopStartTime > MAX_LOOP_TIME) {
logWarn('Agent Loop 达到最大运行时间(5分钟),自动停止');
callbacks.onDone(content || '(达到最大运行时间限制)', allToolRecords);
callbacks.onDone(content || '(达到最大运行时间限制)', allToolRecords, makeStats());
return;
}
// 检查是否已中止
if (state.get<AbortController | null>(KEYS.ABORT_CONTROLLER)?.signal.aborted) {
logInfo('Agent Loop 已中止');
callbacks.onDone(content, allToolRecords.length > 0 ? allToolRecords : undefined);
callbacks.onDone(content, allToolRecords.length > 0 ? allToolRecords : undefined, makeStats());
return;
}
@@ -156,6 +162,7 @@ export async function runAgentLoop(
content += chunk.message.content;
callbacks.onContent(content);
}
if (chunk.eval_count) totalEvalCount = chunk.eval_count;
if (chunk.message?.tool_calls?.length) {
for (const tc of chunk.message.tool_calls) {
if (tc.function?.name) {
@@ -193,7 +200,7 @@ export async function runAgentLoop(
...(thinking && { thinking })
});
}
callbacks.onDone(content, allToolRecords.length > 0 ? allToolRecords : undefined);
callbacks.onDone(content, allToolRecords.length > 0 ? allToolRecords : undefined, makeStats());
return;
}
// 超时错误 — 保留已生成的内容
@@ -206,7 +213,7 @@ export async function runAgentLoop(
...(thinking && { thinking })
});
}
callbacks.onDone(content || '(模型响应超时,已自动中断)', allToolRecords.length > 0 ? allToolRecords : undefined);
callbacks.onDone(content || '(模型响应超时,已自动中断)', allToolRecords.length > 0 ? allToolRecords : undefined, makeStats());
return;
}
throw err;
@@ -228,14 +235,14 @@ export async function runAgentLoop(
if (toolCalls.length === 0) {
logInfo('无工具调用,Agent Loop 结束');
callbacks.onDone(content, allToolRecords.length > 0 ? allToolRecords : undefined);
callbacks.onDone(content, allToolRecords.length > 0 ? allToolRecords : undefined, makeStats());
return;
}
for (const call of toolCalls) {
// 检查是否已中止
if (abortController.signal.aborted) {
callbacks.onDone(content, allToolRecords.length > 0 ? allToolRecords : undefined);
callbacks.onDone(content, allToolRecords.length > 0 ? allToolRecords : undefined, makeStats());
return;
}
@@ -246,7 +253,7 @@ export async function runAgentLoop(
const confirmed = await callbacks.onConfirmTool(call);
// 确认后再次检查是否已中止
if (abortController.signal.aborted) {
callbacks.onDone(content, allToolRecords.length > 0 ? allToolRecords : undefined);
callbacks.onDone(content, allToolRecords.length > 0 ? allToolRecords : undefined, makeStats());
return;
}
if (!confirmed) {
@@ -320,5 +327,5 @@ export async function runAgentLoop(
}
logWarn('Agent Loop 达到最大工具调用次数限制');
callbacks.onDone(content || '(达到最大工具调用次数限制)', allToolRecords);
callbacks.onDone(content || '(达到最大工具调用次数限制)', allToolRecords, makeStats());
}