@mbeato/contextscope 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/context/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/items/page.js +3 -2
- package/.next/standalone/.next/server/app/items/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/items/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/page.js +3 -2
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/sessions/page.js +3 -2
- package/.next/standalone/.next/server/app/sessions/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__08l_yt3._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0j40w4k._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lbywin._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p2sxww._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0wj0exw._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0j0avc7._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/app/page.tsx +8 -2
- package/.next/standalone/app/sessions/page.tsx +10 -2
- package/.next/standalone/lib/model-prices.json +147 -0
- package/.next/standalone/lib/pricing.ts +80 -0
- package/.next/standalone/lib/sessions.ts +33 -6
- package/.next/standalone/lib/transcripts.ts +218 -54
- package/.next/standalone/package.json +2 -1
- package/.next/standalone/scripts/refresh-prices.mjs +58 -0
- package/.next/standalone/tsconfig.tsbuildinfo +1 -1
- package/.next/static/chunks/118uk9v3812u1.css +1 -0
- package/package.json +2 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__08jw6yr._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0duau-w._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ubqc9u._.js +0 -3
- package/.next/static/chunks/13525lf.7uo9s.css +0 -1
- /package/.next/static/{BT6H4g8OlEYi9snYU0PI- → I-W1iV4XdkTRyjis8Ros1}/_buildManifest.js +0 -0
- /package/.next/static/{BT6H4g8OlEYi9snYU0PI- → I-W1iV4XdkTRyjis8Ros1}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{BT6H4g8OlEYi9snYU0PI- → I-W1iV4XdkTRyjis8Ros1}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
3
|
+
"_refreshedAt": "2026-05-27T17:24:45.345Z",
|
|
4
|
+
"_description": "Anthropic model prices in USD per token (input, output, cache_read, cache_creation 5min default). Refresh with `npm run refresh-prices`.",
|
|
5
|
+
"models": {
|
|
6
|
+
"claude-3-7-sonnet-20250219": {
|
|
7
|
+
"input": 0.000003,
|
|
8
|
+
"output": 0.000015,
|
|
9
|
+
"cache_read": 3e-7,
|
|
10
|
+
"cache_creation_5m": 0.00000375,
|
|
11
|
+
"cache_creation_1h": 0.000006
|
|
12
|
+
},
|
|
13
|
+
"claude-3-haiku-20240307": {
|
|
14
|
+
"input": 2.5e-7,
|
|
15
|
+
"output": 0.00000125,
|
|
16
|
+
"cache_read": 3e-8,
|
|
17
|
+
"cache_creation_5m": 3e-7,
|
|
18
|
+
"cache_creation_1h": 0.000006
|
|
19
|
+
},
|
|
20
|
+
"claude-3-opus-20240229": {
|
|
21
|
+
"input": 0.000015,
|
|
22
|
+
"output": 0.000075,
|
|
23
|
+
"cache_read": 0.0000015,
|
|
24
|
+
"cache_creation_5m": 0.00001875,
|
|
25
|
+
"cache_creation_1h": 0.000006
|
|
26
|
+
},
|
|
27
|
+
"claude-4-opus-20250514": {
|
|
28
|
+
"input": 0.000015,
|
|
29
|
+
"output": 0.000075,
|
|
30
|
+
"cache_read": 0.0000015,
|
|
31
|
+
"cache_creation_5m": 0.00001875,
|
|
32
|
+
"cache_creation_1h": 0.0000375
|
|
33
|
+
},
|
|
34
|
+
"claude-4-sonnet-20250514": {
|
|
35
|
+
"input": 0.000003,
|
|
36
|
+
"output": 0.000015,
|
|
37
|
+
"cache_read": 3e-7,
|
|
38
|
+
"cache_creation_5m": 0.00000375,
|
|
39
|
+
"cache_creation_1h": 0.0000075
|
|
40
|
+
},
|
|
41
|
+
"claude-haiku-4-5": {
|
|
42
|
+
"input": 0.000001,
|
|
43
|
+
"output": 0.000005,
|
|
44
|
+
"cache_read": 1e-7,
|
|
45
|
+
"cache_creation_5m": 0.00000125,
|
|
46
|
+
"cache_creation_1h": 0.000002
|
|
47
|
+
},
|
|
48
|
+
"claude-haiku-4-5-20251001": {
|
|
49
|
+
"input": 0.000001,
|
|
50
|
+
"output": 0.000005,
|
|
51
|
+
"cache_read": 1e-7,
|
|
52
|
+
"cache_creation_5m": 0.00000125,
|
|
53
|
+
"cache_creation_1h": 0.000002
|
|
54
|
+
},
|
|
55
|
+
"claude-opus-4-1": {
|
|
56
|
+
"input": 0.000015,
|
|
57
|
+
"output": 0.000075,
|
|
58
|
+
"cache_read": 0.0000015,
|
|
59
|
+
"cache_creation_5m": 0.00001875,
|
|
60
|
+
"cache_creation_1h": 0.00003
|
|
61
|
+
},
|
|
62
|
+
"claude-opus-4-1-20250805": {
|
|
63
|
+
"input": 0.000015,
|
|
64
|
+
"output": 0.000075,
|
|
65
|
+
"cache_read": 0.0000015,
|
|
66
|
+
"cache_creation_5m": 0.00001875,
|
|
67
|
+
"cache_creation_1h": 0.00003
|
|
68
|
+
},
|
|
69
|
+
"claude-opus-4-20250514": {
|
|
70
|
+
"input": 0.000015,
|
|
71
|
+
"output": 0.000075,
|
|
72
|
+
"cache_read": 0.0000015,
|
|
73
|
+
"cache_creation_5m": 0.00001875,
|
|
74
|
+
"cache_creation_1h": 0.00003
|
|
75
|
+
},
|
|
76
|
+
"claude-opus-4-5": {
|
|
77
|
+
"input": 0.000005,
|
|
78
|
+
"output": 0.000025,
|
|
79
|
+
"cache_read": 5e-7,
|
|
80
|
+
"cache_creation_5m": 0.00000625,
|
|
81
|
+
"cache_creation_1h": 0.00001
|
|
82
|
+
},
|
|
83
|
+
"claude-opus-4-5-20251101": {
|
|
84
|
+
"input": 0.000005,
|
|
85
|
+
"output": 0.000025,
|
|
86
|
+
"cache_read": 5e-7,
|
|
87
|
+
"cache_creation_5m": 0.00000625,
|
|
88
|
+
"cache_creation_1h": 0.00001
|
|
89
|
+
},
|
|
90
|
+
"claude-opus-4-6": {
|
|
91
|
+
"input": 0.000005,
|
|
92
|
+
"output": 0.000025,
|
|
93
|
+
"cache_read": 5e-7,
|
|
94
|
+
"cache_creation_5m": 0.00000625,
|
|
95
|
+
"cache_creation_1h": 0.00001
|
|
96
|
+
},
|
|
97
|
+
"claude-opus-4-6-20260205": {
|
|
98
|
+
"input": 0.000005,
|
|
99
|
+
"output": 0.000025,
|
|
100
|
+
"cache_read": 5e-7,
|
|
101
|
+
"cache_creation_5m": 0.00000625,
|
|
102
|
+
"cache_creation_1h": 0.00001
|
|
103
|
+
},
|
|
104
|
+
"claude-opus-4-7": {
|
|
105
|
+
"input": 0.000005,
|
|
106
|
+
"output": 0.000025,
|
|
107
|
+
"cache_read": 5e-7,
|
|
108
|
+
"cache_creation_5m": 0.00000625,
|
|
109
|
+
"cache_creation_1h": 0.00001
|
|
110
|
+
},
|
|
111
|
+
"claude-opus-4-7-20260416": {
|
|
112
|
+
"input": 0.000005,
|
|
113
|
+
"output": 0.000025,
|
|
114
|
+
"cache_read": 5e-7,
|
|
115
|
+
"cache_creation_5m": 0.00000625,
|
|
116
|
+
"cache_creation_1h": 0.00001
|
|
117
|
+
},
|
|
118
|
+
"claude-sonnet-4-20250514": {
|
|
119
|
+
"input": 0.000003,
|
|
120
|
+
"output": 0.000015,
|
|
121
|
+
"cache_read": 3e-7,
|
|
122
|
+
"cache_creation_5m": 0.00000375,
|
|
123
|
+
"cache_creation_1h": 0.000006
|
|
124
|
+
},
|
|
125
|
+
"claude-sonnet-4-5": {
|
|
126
|
+
"input": 0.000003,
|
|
127
|
+
"output": 0.000015,
|
|
128
|
+
"cache_read": 3e-7,
|
|
129
|
+
"cache_creation_5m": 0.00000375,
|
|
130
|
+
"cache_creation_1h": 0.0000075
|
|
131
|
+
},
|
|
132
|
+
"claude-sonnet-4-5-20250929": {
|
|
133
|
+
"input": 0.000003,
|
|
134
|
+
"output": 0.000015,
|
|
135
|
+
"cache_read": 3e-7,
|
|
136
|
+
"cache_creation_5m": 0.00000375,
|
|
137
|
+
"cache_creation_1h": 0.0000075
|
|
138
|
+
},
|
|
139
|
+
"claude-sonnet-4-6": {
|
|
140
|
+
"input": 0.000003,
|
|
141
|
+
"output": 0.000015,
|
|
142
|
+
"cache_read": 3e-7,
|
|
143
|
+
"cache_creation_5m": 0.00000375,
|
|
144
|
+
"cache_creation_1h": 0.0000075
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-message API cost calculator.
|
|
3
|
+
*
|
|
4
|
+
* Prices come from lib/model-prices.json (refreshed from LiteLLM — see
|
|
5
|
+
* scripts/refresh-prices.mjs). They're Anthropic's PUBLIC API rates — the
|
|
6
|
+
* "you'd pay this on the API" number, not what CC subscribers actually pay.
|
|
7
|
+
*
|
|
8
|
+
* Cache-creation cost uses the 5-minute (default) tier. CC sometimes uses the
|
|
9
|
+
* 1-hour tier; transcripts don't tell us which, so we use the more conservative
|
|
10
|
+
* lower price (5min). Real cost may be marginally higher.
|
|
11
|
+
*
|
|
12
|
+
* Unknown models log once and return 0 — never silently fudge.
|
|
13
|
+
*/
|
|
14
|
+
import prices from "./model-prices.json";
|
|
15
|
+
|
|
16
|
+
type ModelPrice = {
|
|
17
|
+
input: number;
|
|
18
|
+
output: number;
|
|
19
|
+
cache_read: number;
|
|
20
|
+
cache_creation_5m: number;
|
|
21
|
+
cache_creation_1h: number;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const MODELS = prices.models as Record<string, ModelPrice>;
|
|
25
|
+
|
|
26
|
+
// Aliases for short model names that appear in some CC transcripts.
|
|
27
|
+
// Map each to the latest released model of that tier.
|
|
28
|
+
const ALIASES: Record<string, string> = {
|
|
29
|
+
opus: "claude-opus-4-7",
|
|
30
|
+
sonnet: "claude-sonnet-4-6",
|
|
31
|
+
haiku: "claude-haiku-4-5",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const warned = new Set<string>();
|
|
35
|
+
|
|
36
|
+
export type Usage = {
|
|
37
|
+
input: number;
|
|
38
|
+
output: number;
|
|
39
|
+
cacheRead: number;
|
|
40
|
+
cacheCreation5m: number;
|
|
41
|
+
cacheCreation1h: number;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function resolveModel(model: string): ModelPrice | null {
|
|
45
|
+
if (!model || model === "<synthetic>") return null;
|
|
46
|
+
if (MODELS[model]) return MODELS[model];
|
|
47
|
+
if (ALIASES[model] && MODELS[ALIASES[model]]) return MODELS[ALIASES[model]];
|
|
48
|
+
// Strip [variant] suffix (e.g. claude-opus-4-7[1m] → claude-opus-4-7)
|
|
49
|
+
const noSuffix = model.replace(/\[[^\]]+\]$/, "");
|
|
50
|
+
if (noSuffix !== model && MODELS[noSuffix]) return MODELS[noSuffix];
|
|
51
|
+
// Strip date suffix (-YYYYMMDD) and retry
|
|
52
|
+
const noDate = noSuffix.replace(/-\d{8}$/, "");
|
|
53
|
+
if (MODELS[noDate]) return MODELS[noDate];
|
|
54
|
+
if (!warned.has(model)) {
|
|
55
|
+
warned.add(model);
|
|
56
|
+
console.warn(`[contextscope] no price found for model "${model}" — treating as $0. Add to lib/model-prices.json or alias.`);
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function costForUsage(model: string, u: Usage): number {
|
|
62
|
+
const p = resolveModel(model);
|
|
63
|
+
if (!p) return 0;
|
|
64
|
+
return (
|
|
65
|
+
u.input * p.input +
|
|
66
|
+
u.output * p.output +
|
|
67
|
+
u.cacheRead * p.cache_read +
|
|
68
|
+
u.cacheCreation5m * p.cache_creation_5m +
|
|
69
|
+
u.cacheCreation1h * p.cache_creation_1h
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function formatUsd(n: number): string {
|
|
74
|
+
if (n >= 100) return `$${n.toFixed(0)}`;
|
|
75
|
+
if (n >= 10) return `$${n.toFixed(1)}`;
|
|
76
|
+
if (n >= 1) return `$${n.toFixed(2)}`;
|
|
77
|
+
if (n >= 0.01) return `$${n.toFixed(2)}`;
|
|
78
|
+
if (n > 0) return `<$0.01`;
|
|
79
|
+
return `$0`;
|
|
80
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getAllTranscripts, type TranscriptResult } from "./transcripts";
|
|
2
|
+
import { costForUsage } from "./pricing";
|
|
2
3
|
|
|
3
4
|
export type Session = {
|
|
4
5
|
sessionId: string;
|
|
@@ -14,11 +15,26 @@ export type Session = {
|
|
|
14
15
|
cacheCreationTokens: number;
|
|
15
16
|
outputTokens: number;
|
|
16
17
|
totalTokens: number;
|
|
18
|
+
costUsd: number;
|
|
17
19
|
toolCalls: Record<string, number>;
|
|
18
20
|
toolErrors: number;
|
|
19
21
|
sidechainTurns: number;
|
|
20
22
|
};
|
|
21
23
|
|
|
24
|
+
function computeCost(t: TranscriptResult): number {
|
|
25
|
+
let cost = 0;
|
|
26
|
+
for (const [model, u] of Object.entries(t.byModel)) {
|
|
27
|
+
cost += costForUsage(model, {
|
|
28
|
+
input: u.inputTokens,
|
|
29
|
+
output: u.outputTokens,
|
|
30
|
+
cacheRead: u.cacheReadTokens,
|
|
31
|
+
cacheCreation5m: u.cacheCreation5mTokens,
|
|
32
|
+
cacheCreation1h: u.cacheCreation1hTokens,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return cost;
|
|
36
|
+
}
|
|
37
|
+
|
|
22
38
|
function toSession(t: TranscriptResult): Session {
|
|
23
39
|
const totalTokens =
|
|
24
40
|
t.inputTokens + t.cacheReadTokens + t.cacheCreationTokens + t.outputTokens;
|
|
@@ -36,6 +52,7 @@ function toSession(t: TranscriptResult): Session {
|
|
|
36
52
|
cacheCreationTokens: t.cacheCreationTokens,
|
|
37
53
|
outputTokens: t.outputTokens,
|
|
38
54
|
totalTokens,
|
|
55
|
+
costUsd: computeCost(t),
|
|
39
56
|
toolCalls: t.toolCalls,
|
|
40
57
|
toolErrors: t.toolErrors,
|
|
41
58
|
sidechainTurns: t.sidechainTurns,
|
|
@@ -55,14 +72,15 @@ export type SessionsSummary = {
|
|
|
55
72
|
totalTokens: number;
|
|
56
73
|
totalOutputTokens: number;
|
|
57
74
|
totalInputPlusCache: number;
|
|
75
|
+
totalCostUsd: number;
|
|
58
76
|
cacheHitRatio: number;
|
|
59
77
|
outputInputRatio: number;
|
|
60
78
|
averageSessionTokens: number;
|
|
61
79
|
medianSessionTokens: number;
|
|
62
80
|
p95SessionTokens: number;
|
|
63
81
|
longSessions: Session[];
|
|
64
|
-
dailyBurn: { date: string; tokens: number }[];
|
|
65
|
-
byProject: { project: string; projectPath: string; count: number; tokens: number; turns: number }[];
|
|
82
|
+
dailyBurn: { date: string; tokens: number; cost: number }[];
|
|
83
|
+
byProject: { project: string; projectPath: string; count: number; tokens: number; cost: number; turns: number }[];
|
|
66
84
|
totalToolCalls: Record<string, number>;
|
|
67
85
|
totalToolErrors: number;
|
|
68
86
|
totalSidechainTurns: number;
|
|
@@ -78,6 +96,7 @@ export function summarizeSessions(sessions: Session[]): SessionsSummary {
|
|
|
78
96
|
totalTokens: 0,
|
|
79
97
|
totalOutputTokens: 0,
|
|
80
98
|
totalInputPlusCache: 0,
|
|
99
|
+
totalCostUsd: 0,
|
|
81
100
|
cacheHitRatio: 0,
|
|
82
101
|
outputInputRatio: 0,
|
|
83
102
|
averageSessionTokens: 0,
|
|
@@ -110,7 +129,7 @@ export function summarizeSessions(sessions: Session[]): SessionsSummary {
|
|
|
110
129
|
|
|
111
130
|
const longSessions = sessions.filter((s) => s.totalTokens >= LONG_SESSION_THRESHOLD);
|
|
112
131
|
|
|
113
|
-
const byDay = new Map<string, number>();
|
|
132
|
+
const byDay = new Map<string, { tokens: number; cost: number }>();
|
|
114
133
|
for (const s of sessions) {
|
|
115
134
|
const ts = s.endTime || s.startTime;
|
|
116
135
|
if (!ts) continue;
|
|
@@ -118,27 +137,34 @@ export function summarizeSessions(sessions: Session[]): SessionsSummary {
|
|
|
118
137
|
const key = `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, "0")}-${String(
|
|
119
138
|
d.getUTCDate()
|
|
120
139
|
).padStart(2, "0")}`;
|
|
121
|
-
|
|
140
|
+
const cur = byDay.get(key) ?? { tokens: 0, cost: 0 };
|
|
141
|
+
cur.tokens += s.totalTokens;
|
|
142
|
+
cur.cost += s.costUsd;
|
|
143
|
+
byDay.set(key, cur);
|
|
122
144
|
}
|
|
123
145
|
const dailyBurn = [...byDay.entries()]
|
|
124
|
-
.map(([date,
|
|
146
|
+
.map(([date, v]) => ({ date, tokens: v.tokens, cost: v.cost }))
|
|
125
147
|
.sort((a, b) => a.date.localeCompare(b.date));
|
|
126
148
|
|
|
127
149
|
// By project
|
|
128
150
|
const projectMap = new Map<
|
|
129
151
|
string,
|
|
130
|
-
{ project: string; projectPath: string; count: number; tokens: number; turns: number }
|
|
152
|
+
{ project: string; projectPath: string; count: number; tokens: number; cost: number; turns: number }
|
|
131
153
|
>();
|
|
154
|
+
let totalCostUsd = 0;
|
|
132
155
|
for (const s of sessions) {
|
|
156
|
+
totalCostUsd += s.costUsd;
|
|
133
157
|
const cur = projectMap.get(s.project) ?? {
|
|
134
158
|
project: s.project,
|
|
135
159
|
projectPath: s.projectPath,
|
|
136
160
|
count: 0,
|
|
137
161
|
tokens: 0,
|
|
162
|
+
cost: 0,
|
|
138
163
|
turns: 0,
|
|
139
164
|
};
|
|
140
165
|
cur.count += 1;
|
|
141
166
|
cur.tokens += s.totalTokens;
|
|
167
|
+
cur.cost += s.costUsd;
|
|
142
168
|
cur.turns += s.turnCount;
|
|
143
169
|
projectMap.set(s.project, cur);
|
|
144
170
|
}
|
|
@@ -163,6 +189,7 @@ export function summarizeSessions(sessions: Session[]): SessionsSummary {
|
|
|
163
189
|
totalTokens: total,
|
|
164
190
|
totalOutputTokens: outSum,
|
|
165
191
|
totalInputPlusCache: inputPlusCache,
|
|
192
|
+
totalCostUsd,
|
|
166
193
|
cacheHitRatio,
|
|
167
194
|
outputInputRatio,
|
|
168
195
|
averageSessionTokens: Math.round(total / sessions.length),
|