token统计

This commit is contained in:
Sirius
2026-08-06 12:01:17 +08:00
parent 64469f9c1d
commit d5eb882dfe
3 changed files with 47 additions and 2 deletions
+44 -1
View File
@@ -1,4 +1,5 @@
import { inspect } from "node:util";
import type { CommandUsage } from "./openai.js";
const tty = process.stdout.isTTY;
const color = (code: number, text: string) => tty ? `\u001b[${code}m${text}\u001b[0m` : text;
@@ -6,6 +7,40 @@ const cyan = (text: string) => color(36, text);
const green = (text: string) => color(32, text);
const yellow = (text: string) => color(33, text);
const dim = (text: string) => color(2, text);
const tokenNumber = new Intl.NumberFormat("zh-CN");
const cumulativeUsage: Required<CommandUsage> = { inputTokens: 0, outputTokens: 0 };
let cumulativeUsagePrinted = false;
function tokenCount(value: unknown): number | undefined {
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0
? value
: undefined;
}
function readUsage(value: unknown): Required<CommandUsage> | undefined {
if (typeof value !== "object" || value === null) return undefined;
const usage = value as Record<string, unknown>;
const inputTokens = tokenCount(usage.inputTokens);
const outputTokens = tokenCount(usage.outputTokens);
if (inputTokens === undefined && outputTokens === undefined) return undefined;
return {
inputTokens: inputTokens ?? 0,
outputTokens: outputTokens ?? 0,
};
}
function formatUsage(usage: Required<CommandUsage>): string {
const totalTokens = usage.inputTokens + usage.outputTokens;
return `输入 ${tokenNumber.format(usage.inputTokens)} · 输出 ${tokenNumber.format(usage.outputTokens)} · 合计 ${tokenNumber.format(totalTokens)}`;
}
export function printCumulativeUsage(): void {
if (cumulativeUsagePrinted) return;
cumulativeUsagePrinted = true;
process.stdout.write(`\n${cyan("━━━━━━━━ Token 累计 ━━━━━━━━")}\n`);
process.stdout.write(`${formatUsage(cumulativeUsage)}\n`);
process.stdout.write(`${cyan("━━━━━━━━━━━━━━━━━━━━━━━━━━")}\n`);
}
function details(event: Record<string, unknown>): string {
const copy = { ...event };
@@ -61,9 +96,17 @@ export class TerminalRenderer {
case "model_request_end":
process.stdout.write(`\n${dim(`模型请求结束 · ${String(event.stopReason ?? "unknown")}`)}\n`);
return;
case "turn_end":
case "turn_end": {
const usage = readUsage(event.usage);
if (usage) {
cumulativeUsage.inputTokens += usage.inputTokens;
cumulativeUsage.outputTokens += usage.outputTokens;
}
process.stdout.write(`${dim(`Turn ${String(event.turnNumber ?? "?")} 结束`)}\n`);
process.stdout.write(`${dim("Token 本轮")} · ${usage ? formatUsage(usage) : "不可用"}\n`);
process.stdout.write(`${dim("Token 累计")} · ${formatUsage(cumulativeUsage)}\n`);
return;
}
default:
if (type.includes("tool") || type.includes("permission") || type.includes("question")) {
process.stdout.write(`${yellow(`\n[${type}]`)}\n${details(event)}\n`);