Skip to main content
Glama
tokens.ts6.21 kB
/** * Token 计数和成本估算模块 */ /** 模型定价(每 1M tokens,美元) */ interface ModelPricing { input: number; output: number; } /** 各模型定价表(截至 2024 年) */ const MODEL_PRICING: Record<string, ModelPricing> = { // OpenAI 'gpt-4o': { input: 2.5, output: 10 }, 'gpt-4o-mini': { input: 0.15, output: 0.6 }, 'gpt-4-turbo': { input: 10, output: 30 }, 'gpt-3.5-turbo': { input: 0.5, output: 1.5 }, // Anthropic 'claude-3-opus': { input: 15, output: 75 }, 'claude-3-sonnet': { input: 3, output: 15 }, 'claude-sonnet-4-20250514': { input: 3, output: 15 }, 'claude-3-haiku': { input: 0.25, output: 1.25 }, // Gemini 'gemini-pro': { input: 0.5, output: 1.5 }, 'gemini-2.0-flash-exp': { input: 0, output: 0 }, // 免费 'gemini-1.5-pro': { input: 1.25, output: 5 }, 'gemini-1.5-flash': { input: 0.075, output: 0.3 }, }; /** 默认定价(未知模型) */ const DEFAULT_PRICING: ModelPricing = { input: 1, output: 3 }; /** * Token 使用记录 */ export interface TokenUsage { /** 模型名称 */ model: string; /** 输入 token 数 */ inputTokens: number; /** 输出 token 数 */ outputTokens: number; /** 时间戳 */ timestamp: number; } /** * 成本统计 */ export interface CostSummary { /** 总输入 token */ totalInputTokens: number; /** 总输出 token */ totalOutputTokens: number; /** 总 token */ totalTokens: number; /** 估算总成本(美元) */ estimatedCostUSD: number; /** 按模型分类的使用量 */ byModel: Record<string, { inputTokens: number; outputTokens: number; costUSD: number; calls: number; }>; } /** * Token 追踪器 * 统计 token 使用量和估算成本 */ export class TokenTracker { private usages: TokenUsage[] = []; private readonly maxHistorySize: number; constructor(maxHistorySize = 1000) { this.maxHistorySize = maxHistorySize; } /** * 记录 token 使用 */ record(model: string, inputTokens: number, outputTokens: number): void { this.usages.push({ model: this.normalizeModelName(model), inputTokens, outputTokens, timestamp: Date.now(), }); // 限制历史记录大小 if (this.usages.length > this.maxHistorySize) { this.usages = this.usages.slice(-this.maxHistorySize); } } /** * 估算文本的 token 数(简单估算) * 实际应使用 tiktoken 等库进行精确计算 */ static estimateTokens(text: string): number { // 简单估算:平均每 4 个字符 = 1 token(英文) // 中文大约每个字 = 2 tokens const chineseChars = (text.match(/[\u4e00-\u9fa5]/g) || []).length; const otherChars = text.length - chineseChars; return Math.ceil(chineseChars * 2 + otherChars / 4); } /** * 获取成本统计 */ getSummary(since?: number): CostSummary { const filteredUsages = since ? this.usages.filter(u => u.timestamp >= since) : this.usages; const byModel: CostSummary['byModel'] = {}; let totalInputTokens = 0; let totalOutputTokens = 0; let estimatedCostUSD = 0; for (const usage of filteredUsages) { const pricing = this.getPricing(usage.model); const inputCost = (usage.inputTokens / 1_000_000) * pricing.input; const outputCost = (usage.outputTokens / 1_000_000) * pricing.output; const totalCost = inputCost + outputCost; totalInputTokens += usage.inputTokens; totalOutputTokens += usage.outputTokens; estimatedCostUSD += totalCost; if (!byModel[usage.model]) { byModel[usage.model] = { inputTokens: 0, outputTokens: 0, costUSD: 0, calls: 0, }; } byModel[usage.model].inputTokens += usage.inputTokens; byModel[usage.model].outputTokens += usage.outputTokens; byModel[usage.model].costUSD += totalCost; byModel[usage.model].calls += 1; } return { totalInputTokens, totalOutputTokens, totalTokens: totalInputTokens + totalOutputTokens, estimatedCostUSD: Math.round(estimatedCostUSD * 10000) / 10000, byModel, }; } /** * 格式化成本统计为可读文本 */ formatSummary(since?: number): string { const summary = this.getSummary(since); const lines: string[] = ['## 📊 Token 使用统计\n']; lines.push(`**总计**: ${this.formatNumber(summary.totalTokens)} tokens`); lines.push(` - 输入: ${this.formatNumber(summary.totalInputTokens)}`); lines.push(` - 输出: ${this.formatNumber(summary.totalOutputTokens)}`); lines.push(`**估算成本**: $${summary.estimatedCostUSD.toFixed(4)}\n`); if (Object.keys(summary.byModel).length > 0) { lines.push('### 按模型分类\n'); lines.push('| 模型 | 调用次数 | 输入 | 输出 | 成本 |'); lines.push('|------|---------|------|------|------|'); for (const [model, stats] of Object.entries(summary.byModel)) { lines.push( `| ${model} | ${stats.calls} | ${this.formatNumber(stats.inputTokens)} | ${this.formatNumber(stats.outputTokens)} | $${stats.costUSD.toFixed(4)} |` ); } } return lines.join('\n'); } /** * 清空统计 */ clear(): void { this.usages = []; } /** * 获取模型定价 */ private getPricing(model: string): ModelPricing { // 尝试精确匹配 if (MODEL_PRICING[model]) { return MODEL_PRICING[model]; } // 尝试前缀匹配 for (const [key, pricing] of Object.entries(MODEL_PRICING)) { if (model.startsWith(key) || model.includes(key)) { return pricing; } } return DEFAULT_PRICING; } /** * 标准化模型名称 */ private normalizeModelName(model: string): string { return model.toLowerCase().trim(); } /** * 格式化数字 */ private formatNumber(num: number): string { if (num >= 1_000_000) { return `${(num / 1_000_000).toFixed(2)}M`; } if (num >= 1_000) { return `${(num / 1_000).toFixed(1)}K`; } return num.toString(); } } /** 全局 Token 追踪器 */ export const globalTokenTracker = new TokenTracker();

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/7836246/claude-team-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server