Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | 12x 12x 12x 12x 12x 12x 12x 12x 12x 12x 12x 207x 268x 177x 23x 9x 9x 16x 16x 5x 11x 9x 62x 8x 4x 3x 3x 3x 3x 3x 3x 26x | import type { UsageStats, AuxiliaryUsageMap } from '../types/index.js';
import type { AuxiliaryUsageEntry } from './types.js';
export interface RuntimeUsageResult {
usage?: {
input_tokens?: number | null;
output_tokens?: number | null;
cache_read_input_tokens?: number | null;
cache_creation_input_tokens?: number | null;
cache_creation?: {
ephemeral_1h_input_tokens?: number | null;
ephemeral_5m_input_tokens?: number | null;
} | null;
server_tool_use?: {
web_search_requests?: number | null;
} | null;
} | null;
total_cost_usd?: number | null;
}
/**
* Extract usage stats from a runtime result message.
*
* The Anthropic API reports `input_tokens` as only the non-cached portion.
* We normalize so that `inputTokens` is the total input token count
* (non-cached + cache_read + cache_creation), with cache fields reported
* separately as subsets of that total.
*/
export function extractUsage(result: RuntimeUsageResult): UsageStats {
const usage = result.usage;
const rawInput = usage?.input_tokens ?? 0;
const cacheRead = usage?.cache_read_input_tokens ?? 0;
const rawCacheCreation = usage?.cache_creation_input_tokens ?? 0;
const cacheCreation1h = usage?.cache_creation?.ephemeral_1h_input_tokens ?? 0;
const tieredCacheCreation5m = usage?.cache_creation?.ephemeral_5m_input_tokens ?? 0;
const hasTieredCacheCreation = usage?.cache_creation !== undefined && usage.cache_creation !== null;
const tieredCacheCreation = tieredCacheCreation5m + cacheCreation1h;
const cacheCreation = Math.max(rawCacheCreation, tieredCacheCreation);
const cacheCreation5m = hasTieredCacheCreation ? tieredCacheCreation5m : rawCacheCreation;
return {
inputTokens: rawInput + cacheRead + cacheCreation,
outputTokens: usage?.output_tokens ?? 0,
cacheReadInputTokens: cacheRead,
cacheCreationInputTokens: cacheCreation,
cacheCreation5mInputTokens: cacheCreation5m,
cacheCreation1hInputTokens: cacheCreation1h,
webSearchRequests: usage?.server_tool_use?.web_search_requests ?? 0,
costUSD: result.total_cost_usd ?? 0,
};
}
/**
* Create empty usage stats.
*/
export function emptyUsage(): UsageStats {
return {
inputTokens: 0,
outputTokens: 0,
cacheReadInputTokens: 0,
cacheCreationInputTokens: 0,
cacheCreation5mInputTokens: 0,
cacheCreation1hInputTokens: 0,
webSearchRequests: 0,
costUSD: 0,
};
}
function addUsage(a: UsageStats, b: UsageStats): UsageStats {
return {
inputTokens: a.inputTokens + b.inputTokens,
outputTokens: a.outputTokens + b.outputTokens,
cacheReadInputTokens: (a.cacheReadInputTokens ?? 0) + (b.cacheReadInputTokens ?? 0),
cacheCreationInputTokens: (a.cacheCreationInputTokens ?? 0) + (b.cacheCreationInputTokens ?? 0),
cacheCreation5mInputTokens: (a.cacheCreation5mInputTokens ?? 0) + (b.cacheCreation5mInputTokens ?? 0),
cacheCreation1hInputTokens: (a.cacheCreation1hInputTokens ?? 0) + (b.cacheCreation1hInputTokens ?? 0),
webSearchRequests: (a.webSearchRequests ?? 0) + (b.webSearchRequests ?? 0),
costUSD: a.costUSD + b.costUSD,
};
}
/**
* Aggregate multiple usage stats into one.
*/
export function aggregateUsage(usages: UsageStats[]): UsageStats {
return usages.reduce(addUsage, emptyUsage());
}
/**
* Aggregate auxiliary usage entries by agent name.
* Merges multiple entries for the same agent into a single UsageStats.
* Returns undefined if no entries are provided.
*/
export function aggregateAuxiliaryUsage(
entries: AuxiliaryUsageEntry[]
): AuxiliaryUsageMap | undefined {
if (entries.length === 0) return undefined;
const map: AuxiliaryUsageMap = {};
for (const { agent, usage } of entries) {
const existing = map[agent];
if (existing) {
map[agent] = addUsage(existing, usage);
} else {
map[agent] = { ...usage };
}
}
return map;
}
/**
* Merge two AuxiliaryUsageMaps together.
* Entries for the same agent are summed.
*/
export function mergeAuxiliaryUsage(
a: AuxiliaryUsageMap | undefined,
b: AuxiliaryUsageMap | undefined
): AuxiliaryUsageMap | undefined {
if (!a && !b) return undefined;
if (!a) return b;
if (!b) return a;
const entries: { agent: string; usage: UsageStats }[] = [];
for (const [agent, usage] of Object.entries(a)) {
entries.push({ agent, usage });
}
for (const [agent, usage] of Object.entries(b)) {
entries.push({ agent, usage });
}
return aggregateAuxiliaryUsage(entries);
}
/**
* Estimate token count from character count.
* Uses chars/4 as a rough approximation for English text.
*/
export function estimateTokens(chars: number): number {
return Math.ceil(chars / 4);
}
|