Skip to content

Commit 3eee3a5

Browse files
committed
feat: support usage
1 parent c9f47b9 commit 3eee3a5

File tree

3 files changed

+79
-1
lines changed

3 files changed

+79
-1
lines changed

packages/components/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@astack-tech/components",
3-
"version": "0.1.1-beta.3",
3+
"version": "0.1.1-beta.4",
44
"description": "Components for the Astack AI Framework.",
55
"main": "dist/index.cjs",
66
"module": "dist/index.js",

packages/components/src/agents/StreamingAgent.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,17 @@ export interface StreamingChunk {
7979
* 错误信息(当 type 为 error 时)
8080
*/
8181
error?: string;
82+
83+
/**
84+
* Token 使用统计信息(当 type 为 completed 时)
85+
*/
86+
usage?: {
87+
completion_tokens: number;
88+
prompt_tokens: number;
89+
prompt_cache_hit_tokens?: number;
90+
prompt_cache_miss_tokens?: number;
91+
total_tokens: number;
92+
};
8293
}
8394

8495
/**
@@ -217,6 +228,7 @@ export class StreamingAgent extends Component {
217228
let iteration = 0;
218229
let lastAssistantMessage: MessageWithToolCalls | null = null;
219230
let hasMoreToolsToCall = true;
231+
let capturedUsage: StreamingChunk['usage'] | undefined;
220232

221233
// 获取 Agent 的配置(通过反射访问私有属性)
222234
const maxIterations =
@@ -373,6 +385,23 @@ export class StreamingAgent extends Component {
373385
}
374386
modelResponse.tool_calls = finalToolCalls;
375387
}
388+
389+
// 捕获 token 使用统计信息
390+
const chunkWithUsage = chunk as Partial<MessageWithToolCalls> & {
391+
usage?: StreamingChunk['usage'];
392+
};
393+
if (chunkWithUsage.usage) {
394+
capturedUsage = {
395+
completion_tokens: chunkWithUsage.usage.completion_tokens || 0,
396+
prompt_tokens: chunkWithUsage.usage.prompt_tokens || 0,
397+
prompt_cache_hit_tokens: chunkWithUsage.usage.prompt_cache_hit_tokens,
398+
prompt_cache_miss_tokens: chunkWithUsage.usage.prompt_cache_miss_tokens,
399+
total_tokens: chunkWithUsage.usage.total_tokens || 0,
400+
};
401+
if (verbose) {
402+
console.log('[StreamingAgent Debug] 捕获到 usage 信息:', capturedUsage);
403+
}
404+
}
376405
}
377406

378407
if (verbose) {
@@ -551,6 +580,7 @@ export class StreamingAgent extends Component {
551580
finalMessage: lastAssistantMessage?.content || '',
552581
history: currentMessages,
553582
allToolCalls: toolCalls,
583+
usage: capturedUsage,
554584
};
555585
} catch (error) {
556586
const errorMessage = error instanceof Error ? error.message : String(error);

packages/components/src/agents/index.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ export interface Message {
3535
* 消息的附加元数据
3636
*/
3737
metadata?: Record<string, unknown>;
38+
39+
/**
40+
* Token 使用统计(可选)
41+
*/
42+
usage?: {
43+
completion_tokens: number;
44+
prompt_tokens: number;
45+
prompt_cache_hit_tokens?: number;
46+
prompt_cache_miss_tokens?: number;
47+
total_tokens: number;
48+
};
3849
}
3950

4051
/**
@@ -225,6 +236,17 @@ export interface AgentOutput {
225236
* 完整的消息历史
226237
*/
227238
history: Message[];
239+
240+
/**
241+
* Token 使用统计(可选)
242+
*/
243+
usage?: {
244+
completion_tokens: number;
245+
prompt_tokens: number;
246+
prompt_cache_hit_tokens?: number;
247+
prompt_cache_miss_tokens?: number;
248+
total_tokens: number;
249+
};
228250
}
229251

230252
/**
@@ -406,6 +428,7 @@ export class Agent extends Component {
406428
let iteration = 0;
407429
let lastAssistantMessage: MessageWithToolCalls | null = null;
408430
let hasMoreToolsToCall = true;
431+
let accumulatedUsage: AgentOutput['usage'] | undefined;
409432

410433
// 2. 开始迭代循环,直到达到最大迭代次数或模型没有更多工具调用
411434
while (hasMoreToolsToCall && iteration < this.maxIterations) {
@@ -422,6 +445,30 @@ export class Agent extends Component {
422445
// 保存模型回复作为最后的助手消息
423446
lastAssistantMessage = modelResponse;
424447

448+
// 累积 token 使用统计
449+
if (modelResponse.usage) {
450+
if (!accumulatedUsage) {
451+
accumulatedUsage = {
452+
completion_tokens: 0,
453+
prompt_tokens: 0,
454+
total_tokens: 0,
455+
};
456+
}
457+
accumulatedUsage.completion_tokens += modelResponse.usage.completion_tokens || 0;
458+
accumulatedUsage.prompt_tokens += modelResponse.usage.prompt_tokens || 0;
459+
accumulatedUsage.total_tokens += modelResponse.usage.total_tokens || 0;
460+
if (modelResponse.usage.prompt_cache_hit_tokens) {
461+
accumulatedUsage.prompt_cache_hit_tokens =
462+
(accumulatedUsage.prompt_cache_hit_tokens || 0) +
463+
modelResponse.usage.prompt_cache_hit_tokens;
464+
}
465+
if (modelResponse.usage.prompt_cache_miss_tokens) {
466+
accumulatedUsage.prompt_cache_miss_tokens =
467+
(accumulatedUsage.prompt_cache_miss_tokens || 0) +
468+
modelResponse.usage.prompt_cache_miss_tokens;
469+
}
470+
}
471+
425472
// 4. 检查是否有工具调用
426473
if (!modelResponse.tool_calls || modelResponse.tool_calls.length === 0) {
427474
// 没有工具调用,完成迭代
@@ -538,6 +585,7 @@ export class Agent extends Component {
538585
message: lastAssistantMessage?.content || '',
539586
history: currentMessages,
540587
toolCalls,
588+
usage: accumulatedUsage,
541589
};
542590
} catch (error) {
543591
console.error(

0 commit comments

Comments
 (0)