import {EventEmitter} from 'events'; import {TokenUsage} from '../types'; import {LanguageModelUsage} from "ai"; export class TokenTracker extends EventEmitter { private usages: TokenUsage[] = []; private budget?: number; constructor(budget?: number) { super(); this.budget = budget; if ('asyncLocalContext' in process) { const asyncLocalContext = process.asyncLocalContext as any; this.on('usage', () => { if (asyncLocalContext.available()) { asyncLocalContext.ctx.chargeAmount = this.getTotalUsage().totalTokens; } }); } } trackUsage(tool: string, usage: LanguageModelUsage) { const u = {tool, usage}; this.usages.push(u); this.emit('usage', usage); } getTotalUsage(): LanguageModelUsage { return this.usages.reduce((acc, {usage}) => { acc.promptTokens += usage.promptTokens; acc.completionTokens += usage.completionTokens; acc.totalTokens += usage.totalTokens; return acc; }, {promptTokens: 0, completionTokens: 0, totalTokens: 0}); } getTotalUsageSnakeCase(): {prompt_tokens: number, completion_tokens: number, total_tokens: number} { return this.usages.reduce((acc, {usage}) => { acc.prompt_tokens += usage.promptTokens; acc.completion_tokens += usage.completionTokens; acc.total_tokens += usage.totalTokens; return acc; }, {prompt_tokens: 0, completion_tokens: 0, total_tokens: 0}); } getUsageBreakdown(): Record { return this.usages.reduce((acc, {tool, usage}) => { acc[tool] = (acc[tool] || 0) + usage.totalTokens; return acc; }, {} as Record); } printSummary() { const breakdown = this.getUsageBreakdown(); console.log('Token Usage Summary:', { budget: this.budget, total: this.getTotalUsage(), breakdown }); } reset() { this.usages = []; } }