feat(core): 实现 Token 消耗统计系统
- 扩展 SessionStats schema 添加 token 统计字段 - 添加 TokenUsageInfo 类型和 ChatResult.usage 字段 - AgentMessageHandler 从 AI SDK response 提取 usage - AgentExecutor 返回 usage 到执行结果 - 新增 TokenStatsManager 管理统计: - updateSessionStats: 更新会话 token 统计 - mergeChildSessionStats: 合并子会话统计到父会话 - getSessionStats/getProjectStats: 查询统计 - Agent.chat() 完成后自动更新统计 - Task 工具完成后合并子会话统计 - 新增 REST API: /api/stats/sessions/:id, /api/stats/projects/:id - 添加 TokenStatsManager 单元测试 (12 tests)
This commit is contained in:
@@ -6,7 +6,7 @@ import {
|
||||
type Tool as AITool,
|
||||
type LanguageModel,
|
||||
} from 'ai';
|
||||
import type { Tool, ToolResult, AgentConfig, ContentBlock } from '../types/index.js';
|
||||
import type { Tool, ToolResult, AgentConfig, ContentBlock, TokenUsageInfo } from '../types/index.js';
|
||||
import { buildZodSchema } from '../types/index.js';
|
||||
import { ToolRegistry } from '../tools/registry.js';
|
||||
import type {
|
||||
@@ -95,6 +95,7 @@ export class AgentExecutor {
|
||||
|
||||
let fullResponse = '';
|
||||
let steps = 0;
|
||||
let usage: TokenUsageInfo | undefined;
|
||||
|
||||
// 工具调用时间追踪(用于计算持续时间)
|
||||
const toolStartTimes = new Map<string, number>();
|
||||
@@ -200,7 +201,9 @@ export class AgentExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
await result.response;
|
||||
const response = await result.response;
|
||||
// 提取 usage 信息
|
||||
usage = this.extractUsage(response);
|
||||
} else {
|
||||
// 非流式模式
|
||||
const result = await generateText({
|
||||
@@ -214,6 +217,8 @@ export class AgentExecutor {
|
||||
|
||||
fullResponse = result.text;
|
||||
steps = result.steps.length;
|
||||
// 提取 usage 信息
|
||||
usage = this.extractUsage(result.response);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -221,6 +226,7 @@ export class AgentExecutor {
|
||||
text: fullResponse,
|
||||
steps,
|
||||
sessionId: context.parentSessionId ?? 'standalone',
|
||||
usage,
|
||||
};
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
@@ -416,4 +422,27 @@ export class AgentExecutor {
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 AI SDK 响应中提取 usage 信息
|
||||
*/
|
||||
private extractUsage(response: unknown): TokenUsageInfo | undefined {
|
||||
// AI SDK 的 response 对象包含 usage 字段
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const resp = response as any;
|
||||
const usage = resp?.usage;
|
||||
|
||||
if (!usage) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
promptTokens: usage.promptTokens ?? 0,
|
||||
completionTokens: usage.completionTokens ?? 0,
|
||||
totalTokens: usage.totalTokens ?? (usage.promptTokens ?? 0) + (usage.completionTokens ?? 0),
|
||||
// Anthropic API 特有的缓存字段
|
||||
cacheReadInputTokens: usage.cacheReadInputTokens,
|
||||
cacheCreationInputTokens: usage.cacheCreationInputTokens,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user