@diogonzafe/tokenwatch 0.2.0 → 0.4.0
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/README.md +260 -15
- package/dist/adapters.cjs +81 -22
- package/dist/adapters.cjs.map +1 -1
- package/dist/adapters.d.cts +9 -3
- package/dist/adapters.d.ts +9 -3
- package/dist/adapters.js +81 -22
- package/dist/adapters.js.map +1 -1
- package/dist/cli.js +469 -50
- package/dist/cli.js.map +1 -1
- package/dist/{index-BQZaFcHQ.d.ts → index-CJKk1hHw.d.cts} +62 -2
- package/dist/{index-BQZaFcHQ.d.cts → index-CJKk1hHw.d.ts} +62 -2
- package/dist/index.cjs +559 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -3
- package/dist/index.d.ts +20 -3
- package/dist/index.js +558 -70
- package/dist/index.js.map +1 -1
- package/dist/langchain.cjs +62 -0
- package/dist/langchain.cjs.map +1 -0
- package/dist/langchain.d.cts +68 -0
- package/dist/langchain.d.ts +68 -0
- package/dist/langchain.js +35 -0
- package/dist/langchain.js.map +1 -0
- package/package.json +11 -3
- package/prices.json +167 -3
- package/dist/cli.cjs +0 -1639
- package/dist/cli.cjs.map +0 -1
- package/dist/cli.d.cts +0 -1
package/dist/cli.js
CHANGED
|
@@ -22,7 +22,7 @@ async function fetchRemotePrices(url = REMOTE_URL) {
|
|
|
22
22
|
const data = await res.json();
|
|
23
23
|
if (!data?.models) return null;
|
|
24
24
|
await persistCache(data);
|
|
25
|
-
return data.models;
|
|
25
|
+
return { models: data.models, updated_at: data.updated_at ?? "" };
|
|
26
26
|
} catch {
|
|
27
27
|
return null;
|
|
28
28
|
}
|
|
@@ -34,7 +34,8 @@ async function loadCachedPrices() {
|
|
|
34
34
|
const data = JSON.parse(raw);
|
|
35
35
|
const age = Date.now() - (data._cachedAt ?? 0);
|
|
36
36
|
if (age > CACHE_TTL_MS) return null;
|
|
37
|
-
|
|
37
|
+
if (!data.models) return null;
|
|
38
|
+
return { models: data.models, updated_at: data.updated_at ?? "" };
|
|
38
39
|
} catch {
|
|
39
40
|
return null;
|
|
40
41
|
}
|
|
@@ -98,29 +99,51 @@ var SqliteStorage = class {
|
|
|
98
99
|
migrate() {
|
|
99
100
|
this.db.exec(`
|
|
100
101
|
CREATE TABLE IF NOT EXISTS usage (
|
|
101
|
-
id
|
|
102
|
-
model
|
|
103
|
-
input_tokens
|
|
104
|
-
output_tokens
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
102
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
103
|
+
model TEXT NOT NULL,
|
|
104
|
+
input_tokens INTEGER NOT NULL,
|
|
105
|
+
output_tokens INTEGER NOT NULL,
|
|
106
|
+
reasoning_tokens INTEGER NOT NULL DEFAULT 0,
|
|
107
|
+
cached_tokens INTEGER NOT NULL DEFAULT 0,
|
|
108
|
+
cache_creation_tokens INTEGER NOT NULL DEFAULT 0,
|
|
109
|
+
cost_usd REAL NOT NULL,
|
|
110
|
+
session_id TEXT,
|
|
111
|
+
user_id TEXT,
|
|
112
|
+
feature TEXT,
|
|
113
|
+
timestamp TEXT NOT NULL
|
|
109
114
|
)
|
|
110
115
|
`);
|
|
116
|
+
const cols = this.db.prepare(`PRAGMA table_info(usage)`).all().map((c) => c.name);
|
|
117
|
+
if (!cols.includes("reasoning_tokens")) {
|
|
118
|
+
this.db.exec(`ALTER TABLE usage ADD COLUMN reasoning_tokens INTEGER NOT NULL DEFAULT 0`);
|
|
119
|
+
}
|
|
120
|
+
if (!cols.includes("feature")) {
|
|
121
|
+
this.db.exec(`ALTER TABLE usage ADD COLUMN feature TEXT`);
|
|
122
|
+
}
|
|
123
|
+
if (!cols.includes("cached_tokens")) {
|
|
124
|
+
this.db.exec(`ALTER TABLE usage ADD COLUMN cached_tokens INTEGER NOT NULL DEFAULT 0`);
|
|
125
|
+
}
|
|
126
|
+
if (!cols.includes("cache_creation_tokens")) {
|
|
127
|
+
this.db.exec(`ALTER TABLE usage ADD COLUMN cache_creation_tokens INTEGER NOT NULL DEFAULT 0`);
|
|
128
|
+
}
|
|
111
129
|
}
|
|
112
130
|
record(entry) {
|
|
113
131
|
this.db.prepare(
|
|
114
132
|
`INSERT INTO usage
|
|
115
|
-
(model, input_tokens, output_tokens,
|
|
116
|
-
|
|
133
|
+
(model, input_tokens, output_tokens, reasoning_tokens, cached_tokens, cache_creation_tokens,
|
|
134
|
+
cost_usd, session_id, user_id, feature, timestamp)
|
|
135
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
117
136
|
).run(
|
|
118
137
|
entry.model,
|
|
119
138
|
entry.inputTokens,
|
|
120
139
|
entry.outputTokens,
|
|
140
|
+
entry.reasoningTokens ?? 0,
|
|
141
|
+
entry.cachedTokens ?? 0,
|
|
142
|
+
entry.cacheCreationTokens ?? 0,
|
|
121
143
|
entry.costUSD,
|
|
122
144
|
entry.sessionId ?? null,
|
|
123
145
|
entry.userId ?? null,
|
|
146
|
+
entry.feature ?? null,
|
|
124
147
|
entry.timestamp
|
|
125
148
|
);
|
|
126
149
|
}
|
|
@@ -130,9 +153,13 @@ var SqliteStorage = class {
|
|
|
130
153
|
model: r.model,
|
|
131
154
|
inputTokens: r.input_tokens,
|
|
132
155
|
outputTokens: r.output_tokens,
|
|
156
|
+
...r.reasoning_tokens > 0 && { reasoningTokens: r.reasoning_tokens },
|
|
157
|
+
...r.cached_tokens > 0 && { cachedTokens: r.cached_tokens },
|
|
158
|
+
...r.cache_creation_tokens > 0 && { cacheCreationTokens: r.cache_creation_tokens },
|
|
133
159
|
costUSD: r.cost_usd,
|
|
134
160
|
...r.session_id != null && { sessionId: r.session_id },
|
|
135
161
|
...r.user_id != null && { userId: r.user_id },
|
|
162
|
+
...r.feature != null && { feature: r.feature },
|
|
136
163
|
timestamp: r.timestamp
|
|
137
164
|
}));
|
|
138
165
|
}
|
|
@@ -175,93 +202,153 @@ function lookupInMap(model, map) {
|
|
|
175
202
|
}
|
|
176
203
|
return void 0;
|
|
177
204
|
}
|
|
178
|
-
function calculateCost(inputTokens, outputTokens, price) {
|
|
179
|
-
|
|
205
|
+
function calculateCost(inputTokens, outputTokens, price, cachedTokens = 0, cacheCreationTokens = 0) {
|
|
206
|
+
const regularInputCost = inputTokens / 1e6 * price.input;
|
|
207
|
+
const cachedReadCost = cachedTokens / 1e6 * (price.cachedInput ?? price.input);
|
|
208
|
+
const cacheCreationCost = cacheCreationTokens / 1e6 * (price.cacheCreationInput ?? price.input * 1.25);
|
|
209
|
+
const outputCost = outputTokens / 1e6 * price.output;
|
|
210
|
+
return regularInputCost + cachedReadCost + cacheCreationCost + outputCost;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// src/core/suggestions.ts
|
|
214
|
+
var PROVIDER_PREFIXES = ["gpt-", "claude-", "gemini-", "deepseek-"];
|
|
215
|
+
function getProviderPrefix(model) {
|
|
216
|
+
return PROVIDER_PREFIXES.find((p) => model.startsWith(p));
|
|
217
|
+
}
|
|
218
|
+
function maybeSuggestCheaperModel(model, costUSD, inputTokens, outputTokens, layers) {
|
|
219
|
+
if (costUSD <= 0) return;
|
|
220
|
+
const prefix = getProviderPrefix(model);
|
|
221
|
+
if (!prefix) return;
|
|
222
|
+
const mergedMap = {
|
|
223
|
+
...layers.bundledPrices,
|
|
224
|
+
...layers.remotePrices ?? {},
|
|
225
|
+
...layers.customPrices ?? {}
|
|
226
|
+
};
|
|
227
|
+
let cheapestModel;
|
|
228
|
+
let cheapestCost = Infinity;
|
|
229
|
+
for (const key of Object.keys(mergedMap)) {
|
|
230
|
+
if (key === model || !key.startsWith(prefix)) continue;
|
|
231
|
+
const price = mergedMap[key];
|
|
232
|
+
if (!price) continue;
|
|
233
|
+
const candidateCost = calculateCost(inputTokens, outputTokens, price);
|
|
234
|
+
if (candidateCost < cheapestCost) {
|
|
235
|
+
cheapestCost = candidateCost;
|
|
236
|
+
cheapestModel = key;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (cheapestModel === void 0 || cheapestCost >= costUSD * 0.5) return;
|
|
240
|
+
const savingsPct = Math.round((1 - cheapestCost / costUSD) * 100);
|
|
241
|
+
console.log(
|
|
242
|
+
`[tokenwatch] Suggestion: ${cheapestModel} could handle this for ~$${cheapestCost.toFixed(4)} (${savingsPct}% cheaper than ${model})`
|
|
243
|
+
);
|
|
180
244
|
}
|
|
181
245
|
|
|
182
246
|
// prices.json
|
|
183
247
|
var prices_default = {
|
|
184
|
-
updated_at: "2026-04-
|
|
248
|
+
updated_at: "2026-04-22",
|
|
185
249
|
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
186
250
|
models: {
|
|
187
251
|
"gpt-4o": {
|
|
188
252
|
input: 2.5,
|
|
189
253
|
output: 10,
|
|
254
|
+
cachedInput: 1.25,
|
|
190
255
|
maxInputTokens: 128e3
|
|
191
256
|
},
|
|
192
257
|
"gpt-4o-mini": {
|
|
193
258
|
input: 0.15,
|
|
194
259
|
output: 0.6,
|
|
260
|
+
cachedInput: 0.075,
|
|
195
261
|
maxInputTokens: 128e3
|
|
196
262
|
},
|
|
197
263
|
"gpt-5": {
|
|
198
264
|
input: 1.25,
|
|
199
265
|
output: 10,
|
|
266
|
+
cachedInput: 0.125,
|
|
200
267
|
maxInputTokens: 272e3
|
|
201
268
|
},
|
|
202
269
|
"gpt-5-mini": {
|
|
203
270
|
input: 0.25,
|
|
204
271
|
output: 2,
|
|
272
|
+
cachedInput: 0.025,
|
|
205
273
|
maxInputTokens: 272e3
|
|
206
274
|
},
|
|
207
275
|
"gpt-5-nano": {
|
|
208
276
|
input: 0.05,
|
|
209
277
|
output: 0.4,
|
|
278
|
+
cachedInput: 5e-3,
|
|
210
279
|
maxInputTokens: 272e3
|
|
211
280
|
},
|
|
212
281
|
"claude-opus-4-6": {
|
|
213
282
|
input: 5,
|
|
214
283
|
output: 25,
|
|
284
|
+
cachedInput: 0.5,
|
|
285
|
+
cacheCreationInput: 6.25,
|
|
215
286
|
maxInputTokens: 1e6
|
|
216
287
|
},
|
|
217
288
|
"claude-sonnet-4-6": {
|
|
218
289
|
input: 3,
|
|
219
290
|
output: 15,
|
|
291
|
+
cachedInput: 0.3,
|
|
292
|
+
cacheCreationInput: 3.75,
|
|
220
293
|
maxInputTokens: 1e6
|
|
221
294
|
},
|
|
222
295
|
"claude-haiku-4-5": {
|
|
223
296
|
input: 1,
|
|
224
297
|
output: 5,
|
|
298
|
+
cachedInput: 0.1,
|
|
299
|
+
cacheCreationInput: 1.25,
|
|
225
300
|
maxInputTokens: 2e5
|
|
226
301
|
},
|
|
227
302
|
"gemini-2.5-pro": {
|
|
228
303
|
input: 1.25,
|
|
229
304
|
output: 10,
|
|
305
|
+
cachedInput: 0.125,
|
|
230
306
|
maxInputTokens: 1048576
|
|
231
307
|
},
|
|
232
308
|
"gemini-2.5-flash": {
|
|
233
309
|
input: 0.3,
|
|
234
310
|
output: 2.5,
|
|
311
|
+
cachedInput: 0.03,
|
|
235
312
|
maxInputTokens: 1048576
|
|
236
313
|
},
|
|
237
314
|
"deepseek-chat": {
|
|
238
315
|
input: 0.28,
|
|
239
316
|
output: 0.42,
|
|
317
|
+
cachedInput: 0.028,
|
|
240
318
|
maxInputTokens: 131072
|
|
241
319
|
},
|
|
242
320
|
"deepseek-reasoner": {
|
|
243
321
|
input: 0.28,
|
|
244
322
|
output: 0.42,
|
|
323
|
+
cachedInput: 0.028,
|
|
245
324
|
maxInputTokens: 131072
|
|
246
325
|
},
|
|
247
326
|
"claude-opus-4-5": {
|
|
248
327
|
input: 5,
|
|
249
328
|
output: 25,
|
|
329
|
+
cachedInput: 0.5,
|
|
330
|
+
cacheCreationInput: 6.25,
|
|
250
331
|
maxInputTokens: 2e5
|
|
251
332
|
},
|
|
252
333
|
"claude-opus-4-7": {
|
|
253
334
|
input: 5,
|
|
254
335
|
output: 25,
|
|
336
|
+
cachedInput: 0.5,
|
|
337
|
+
cacheCreationInput: 6.25,
|
|
255
338
|
maxInputTokens: 1e6
|
|
256
339
|
},
|
|
257
340
|
"claude-opus-4-1": {
|
|
258
341
|
input: 15,
|
|
259
342
|
output: 75,
|
|
343
|
+
cachedInput: 1.5,
|
|
344
|
+
cacheCreationInput: 18.75,
|
|
260
345
|
maxInputTokens: 2e5
|
|
261
346
|
},
|
|
262
347
|
"claude-sonnet-4-5": {
|
|
263
348
|
input: 3,
|
|
264
349
|
output: 15,
|
|
350
|
+
cachedInput: 0.3,
|
|
351
|
+
cacheCreationInput: 3.75,
|
|
265
352
|
maxInputTokens: 2e5
|
|
266
353
|
},
|
|
267
354
|
"gpt-oss-120b": {
|
|
@@ -352,36 +439,43 @@ var prices_default = {
|
|
|
352
439
|
"gpt-4.1": {
|
|
353
440
|
input: 2,
|
|
354
441
|
output: 8,
|
|
442
|
+
cachedInput: 0.5,
|
|
355
443
|
maxInputTokens: 1047576
|
|
356
444
|
},
|
|
357
445
|
"gpt-4.1-2025-04-14": {
|
|
358
446
|
input: 2,
|
|
359
447
|
output: 8,
|
|
448
|
+
cachedInput: 0.5,
|
|
360
449
|
maxInputTokens: 1047576
|
|
361
450
|
},
|
|
362
451
|
"gpt-4.1-mini": {
|
|
363
452
|
input: 0.4,
|
|
364
453
|
output: 1.6,
|
|
454
|
+
cachedInput: 0.1,
|
|
365
455
|
maxInputTokens: 1047576
|
|
366
456
|
},
|
|
367
457
|
"gpt-4.1-mini-2025-04-14": {
|
|
368
458
|
input: 0.4,
|
|
369
459
|
output: 1.6,
|
|
460
|
+
cachedInput: 0.1,
|
|
370
461
|
maxInputTokens: 1047576
|
|
371
462
|
},
|
|
372
463
|
"gpt-4.1-nano": {
|
|
373
464
|
input: 0.1,
|
|
374
465
|
output: 0.4,
|
|
466
|
+
cachedInput: 0.025,
|
|
375
467
|
maxInputTokens: 1047576
|
|
376
468
|
},
|
|
377
469
|
"gpt-4.1-nano-2025-04-14": {
|
|
378
470
|
input: 0.1,
|
|
379
471
|
output: 0.4,
|
|
472
|
+
cachedInput: 0.025,
|
|
380
473
|
maxInputTokens: 1047576
|
|
381
474
|
},
|
|
382
475
|
"gpt-4.5-preview": {
|
|
383
476
|
input: 75,
|
|
384
477
|
output: 150,
|
|
478
|
+
cachedInput: 37.5,
|
|
385
479
|
maxInputTokens: 128e3
|
|
386
480
|
},
|
|
387
481
|
"gpt-4o-2024-05-13": {
|
|
@@ -392,11 +486,13 @@ var prices_default = {
|
|
|
392
486
|
"gpt-4o-2024-08-06": {
|
|
393
487
|
input: 2.5,
|
|
394
488
|
output: 10,
|
|
489
|
+
cachedInput: 1.25,
|
|
395
490
|
maxInputTokens: 128e3
|
|
396
491
|
},
|
|
397
492
|
"gpt-4o-2024-11-20": {
|
|
398
493
|
input: 2.5,
|
|
399
494
|
output: 10,
|
|
495
|
+
cachedInput: 1.25,
|
|
400
496
|
maxInputTokens: 128e3
|
|
401
497
|
},
|
|
402
498
|
"gpt-audio-2025-08-28": {
|
|
@@ -422,6 +518,7 @@ var prices_default = {
|
|
|
422
518
|
"gpt-4o-mini-2024-07-18": {
|
|
423
519
|
input: 0.15,
|
|
424
520
|
output: 0.6,
|
|
521
|
+
cachedInput: 0.075,
|
|
425
522
|
maxInputTokens: 128e3
|
|
426
523
|
},
|
|
427
524
|
"gpt-4o-mini-audio-preview-2024-12-17": {
|
|
@@ -432,21 +529,25 @@ var prices_default = {
|
|
|
432
529
|
"gpt-4o-mini-realtime-preview-2024-12-17": {
|
|
433
530
|
input: 0.6,
|
|
434
531
|
output: 2.4,
|
|
532
|
+
cachedInput: 0.3,
|
|
435
533
|
maxInputTokens: 128e3
|
|
436
534
|
},
|
|
437
535
|
"gpt-realtime-2025-08-28": {
|
|
438
536
|
input: 4,
|
|
439
537
|
output: 16,
|
|
538
|
+
cachedInput: 0.4,
|
|
440
539
|
maxInputTokens: 32e3
|
|
441
540
|
},
|
|
442
541
|
"gpt-realtime-1.5-2026-02-23": {
|
|
443
542
|
input: 4,
|
|
444
543
|
output: 16,
|
|
544
|
+
cachedInput: 4,
|
|
445
545
|
maxInputTokens: 32e3
|
|
446
546
|
},
|
|
447
547
|
"gpt-realtime-mini-2025-10-06": {
|
|
448
548
|
input: 0.6,
|
|
449
549
|
output: 2.4,
|
|
550
|
+
cachedInput: 0.06,
|
|
450
551
|
maxInputTokens: 128e3
|
|
451
552
|
},
|
|
452
553
|
"gpt-4o-mini-transcribe": {
|
|
@@ -457,11 +558,13 @@ var prices_default = {
|
|
|
457
558
|
"gpt-4o-realtime-preview-2024-10-01": {
|
|
458
559
|
input: 5,
|
|
459
560
|
output: 20,
|
|
561
|
+
cachedInput: 2.5,
|
|
460
562
|
maxInputTokens: 128e3
|
|
461
563
|
},
|
|
462
564
|
"gpt-4o-realtime-preview-2024-12-17": {
|
|
463
565
|
input: 5,
|
|
464
566
|
output: 20,
|
|
567
|
+
cachedInput: 2.5,
|
|
465
568
|
maxInputTokens: 128e3
|
|
466
569
|
},
|
|
467
570
|
"gpt-4o-transcribe": {
|
|
@@ -477,51 +580,61 @@ var prices_default = {
|
|
|
477
580
|
"gpt-5.1-2025-11-13": {
|
|
478
581
|
input: 1.25,
|
|
479
582
|
output: 10,
|
|
583
|
+
cachedInput: 0.125,
|
|
480
584
|
maxInputTokens: 272e3
|
|
481
585
|
},
|
|
482
586
|
"gpt-5.1-chat-2025-11-13": {
|
|
483
587
|
input: 1.25,
|
|
484
588
|
output: 10,
|
|
589
|
+
cachedInput: 0.125,
|
|
485
590
|
maxInputTokens: 128e3
|
|
486
591
|
},
|
|
487
592
|
"gpt-5.1-codex-2025-11-13": {
|
|
488
593
|
input: 1.25,
|
|
489
594
|
output: 10,
|
|
595
|
+
cachedInput: 0.125,
|
|
490
596
|
maxInputTokens: 272e3
|
|
491
597
|
},
|
|
492
598
|
"gpt-5.1-codex-mini-2025-11-13": {
|
|
493
599
|
input: 0.25,
|
|
494
600
|
output: 2,
|
|
601
|
+
cachedInput: 0.025,
|
|
495
602
|
maxInputTokens: 272e3
|
|
496
603
|
},
|
|
497
604
|
"gpt-5-2025-08-07": {
|
|
498
605
|
input: 1.25,
|
|
499
606
|
output: 10,
|
|
607
|
+
cachedInput: 0.125,
|
|
500
608
|
maxInputTokens: 272e3
|
|
501
609
|
},
|
|
502
610
|
"gpt-5-chat": {
|
|
503
611
|
input: 1.25,
|
|
504
612
|
output: 10,
|
|
613
|
+
cachedInput: 0.125,
|
|
505
614
|
maxInputTokens: 128e3
|
|
506
615
|
},
|
|
507
616
|
"gpt-5-chat-latest": {
|
|
508
617
|
input: 1.25,
|
|
509
618
|
output: 10,
|
|
619
|
+
cachedInput: 0.125,
|
|
510
620
|
maxInputTokens: 128e3
|
|
511
621
|
},
|
|
512
622
|
"gpt-5-codex": {
|
|
513
623
|
input: 1.25,
|
|
514
624
|
output: 10,
|
|
625
|
+
cachedInput: 0.125,
|
|
515
626
|
maxInputTokens: 272e3
|
|
516
627
|
},
|
|
517
628
|
"gpt-5-mini-2025-08-07": {
|
|
518
629
|
input: 0.25,
|
|
519
630
|
output: 2,
|
|
631
|
+
cachedInput: 0.025,
|
|
520
632
|
maxInputTokens: 272e3
|
|
521
633
|
},
|
|
522
634
|
"gpt-5-nano-2025-08-07": {
|
|
523
635
|
input: 0.05,
|
|
524
636
|
output: 0.4,
|
|
637
|
+
cachedInput: 5e-3,
|
|
525
638
|
maxInputTokens: 272e3
|
|
526
639
|
},
|
|
527
640
|
"gpt-5-pro": {
|
|
@@ -532,61 +645,73 @@ var prices_default = {
|
|
|
532
645
|
"gpt-5.1": {
|
|
533
646
|
input: 1.25,
|
|
534
647
|
output: 10,
|
|
648
|
+
cachedInput: 0.125,
|
|
535
649
|
maxInputTokens: 272e3
|
|
536
650
|
},
|
|
537
651
|
"gpt-5.1-chat": {
|
|
538
652
|
input: 1.25,
|
|
539
653
|
output: 10,
|
|
654
|
+
cachedInput: 0.125,
|
|
540
655
|
maxInputTokens: 128e3
|
|
541
656
|
},
|
|
542
657
|
"gpt-5.1-codex": {
|
|
543
658
|
input: 1.25,
|
|
544
659
|
output: 10,
|
|
660
|
+
cachedInput: 0.125,
|
|
545
661
|
maxInputTokens: 272e3
|
|
546
662
|
},
|
|
547
663
|
"gpt-5.1-codex-max": {
|
|
548
664
|
input: 1.25,
|
|
549
665
|
output: 10,
|
|
666
|
+
cachedInput: 0.125,
|
|
550
667
|
maxInputTokens: 272e3
|
|
551
668
|
},
|
|
552
669
|
"gpt-5.1-codex-mini": {
|
|
553
670
|
input: 0.25,
|
|
554
671
|
output: 2,
|
|
672
|
+
cachedInput: 0.025,
|
|
555
673
|
maxInputTokens: 272e3
|
|
556
674
|
},
|
|
557
675
|
"gpt-5.2": {
|
|
558
676
|
input: 1.75,
|
|
559
677
|
output: 14,
|
|
678
|
+
cachedInput: 0.175,
|
|
560
679
|
maxInputTokens: 272e3
|
|
561
680
|
},
|
|
562
681
|
"gpt-5.2-2025-12-11": {
|
|
563
682
|
input: 1.75,
|
|
564
683
|
output: 14,
|
|
684
|
+
cachedInput: 0.175,
|
|
565
685
|
maxInputTokens: 272e3
|
|
566
686
|
},
|
|
567
687
|
"gpt-5.2-chat": {
|
|
568
688
|
input: 1.75,
|
|
569
689
|
output: 14,
|
|
690
|
+
cachedInput: 0.175,
|
|
570
691
|
maxInputTokens: 128e3
|
|
571
692
|
},
|
|
572
693
|
"gpt-5.2-chat-2025-12-11": {
|
|
573
694
|
input: 1.75,
|
|
574
695
|
output: 14,
|
|
696
|
+
cachedInput: 0.175,
|
|
575
697
|
maxInputTokens: 128e3
|
|
576
698
|
},
|
|
577
699
|
"gpt-5.2-codex": {
|
|
578
700
|
input: 1.75,
|
|
579
701
|
output: 14,
|
|
702
|
+
cachedInput: 0.175,
|
|
580
703
|
maxInputTokens: 272e3
|
|
581
704
|
},
|
|
582
705
|
"gpt-5.3-chat": {
|
|
583
706
|
input: 1.75,
|
|
584
707
|
output: 14,
|
|
708
|
+
cachedInput: 0.175,
|
|
585
709
|
maxInputTokens: 128e3
|
|
586
710
|
},
|
|
587
711
|
"gpt-5.3-codex": {
|
|
588
712
|
input: 1.75,
|
|
589
713
|
output: 14,
|
|
714
|
+
cachedInput: 0.175,
|
|
590
715
|
maxInputTokens: 272e3
|
|
591
716
|
},
|
|
592
717
|
"gpt-5.2-pro": {
|
|
@@ -602,71 +727,85 @@ var prices_default = {
|
|
|
602
727
|
"gpt-5.4": {
|
|
603
728
|
input: 2.5,
|
|
604
729
|
output: 15,
|
|
730
|
+
cachedInput: 0.25,
|
|
605
731
|
maxInputTokens: 105e4
|
|
606
732
|
},
|
|
607
733
|
"gpt-5.4-2026-03-05": {
|
|
608
734
|
input: 2.5,
|
|
609
735
|
output: 15,
|
|
736
|
+
cachedInput: 0.25,
|
|
610
737
|
maxInputTokens: 105e4
|
|
611
738
|
},
|
|
612
739
|
"gpt-5.4-pro": {
|
|
613
740
|
input: 30,
|
|
614
741
|
output: 180,
|
|
742
|
+
cachedInput: 3,
|
|
615
743
|
maxInputTokens: 105e4
|
|
616
744
|
},
|
|
617
745
|
"gpt-5.4-pro-2026-03-05": {
|
|
618
746
|
input: 30,
|
|
619
747
|
output: 180,
|
|
748
|
+
cachedInput: 3,
|
|
620
749
|
maxInputTokens: 105e4
|
|
621
750
|
},
|
|
622
751
|
"gpt-5.4-mini": {
|
|
623
752
|
input: 0.75,
|
|
624
753
|
output: 4.5,
|
|
754
|
+
cachedInput: 0.075,
|
|
625
755
|
maxInputTokens: 272e3
|
|
626
756
|
},
|
|
627
757
|
"gpt-5.4-nano": {
|
|
628
758
|
input: 0.2,
|
|
629
759
|
output: 1.25,
|
|
760
|
+
cachedInput: 0.02,
|
|
630
761
|
maxInputTokens: 272e3
|
|
631
762
|
},
|
|
632
763
|
"o1-2024-12-17": {
|
|
633
764
|
input: 15,
|
|
634
765
|
output: 60,
|
|
766
|
+
cachedInput: 7.5,
|
|
635
767
|
maxInputTokens: 2e5
|
|
636
768
|
},
|
|
637
769
|
"o1-mini": {
|
|
638
770
|
input: 1.21,
|
|
639
771
|
output: 4.84,
|
|
772
|
+
cachedInput: 0.605,
|
|
640
773
|
maxInputTokens: 128e3
|
|
641
774
|
},
|
|
642
775
|
"o1-mini-2024-09-12": {
|
|
643
776
|
input: 1.1,
|
|
644
777
|
output: 4.4,
|
|
778
|
+
cachedInput: 0.55,
|
|
645
779
|
maxInputTokens: 128e3
|
|
646
780
|
},
|
|
647
781
|
"o1-preview": {
|
|
648
782
|
input: 15,
|
|
649
783
|
output: 60,
|
|
784
|
+
cachedInput: 7.5,
|
|
650
785
|
maxInputTokens: 128e3
|
|
651
786
|
},
|
|
652
787
|
"o1-preview-2024-09-12": {
|
|
653
788
|
input: 15,
|
|
654
789
|
output: 60,
|
|
790
|
+
cachedInput: 7.5,
|
|
655
791
|
maxInputTokens: 128e3
|
|
656
792
|
},
|
|
657
793
|
"o3-2025-04-16": {
|
|
658
794
|
input: 2,
|
|
659
795
|
output: 8,
|
|
796
|
+
cachedInput: 0.5,
|
|
660
797
|
maxInputTokens: 2e5
|
|
661
798
|
},
|
|
662
799
|
"o3-mini": {
|
|
663
800
|
input: 1.1,
|
|
664
801
|
output: 4.4,
|
|
802
|
+
cachedInput: 0.55,
|
|
665
803
|
maxInputTokens: 2e5
|
|
666
804
|
},
|
|
667
805
|
"o3-mini-2025-01-31": {
|
|
668
806
|
input: 1.1,
|
|
669
807
|
output: 4.4,
|
|
808
|
+
cachedInput: 0.55,
|
|
670
809
|
maxInputTokens: 2e5
|
|
671
810
|
},
|
|
672
811
|
"o3-pro": {
|
|
@@ -682,11 +821,13 @@ var prices_default = {
|
|
|
682
821
|
"o4-mini": {
|
|
683
822
|
input: 1.1,
|
|
684
823
|
output: 4.4,
|
|
824
|
+
cachedInput: 0.275,
|
|
685
825
|
maxInputTokens: 2e5
|
|
686
826
|
},
|
|
687
827
|
"o4-mini-2025-04-16": {
|
|
688
828
|
input: 1.1,
|
|
689
829
|
output: 4.4,
|
|
830
|
+
cachedInput: 0.275,
|
|
690
831
|
maxInputTokens: 2e5
|
|
691
832
|
},
|
|
692
833
|
"deepseek-v3.2": {
|
|
@@ -707,6 +848,7 @@ var prices_default = {
|
|
|
707
848
|
"deepseek-v3": {
|
|
708
849
|
input: 0.27,
|
|
709
850
|
output: 1.1,
|
|
851
|
+
cachedInput: 0.07,
|
|
710
852
|
maxInputTokens: 65536
|
|
711
853
|
},
|
|
712
854
|
"deepseek-v3-0324": {
|
|
@@ -722,76 +864,105 @@ var prices_default = {
|
|
|
722
864
|
"claude-haiku-4-5-20251001": {
|
|
723
865
|
input: 1,
|
|
724
866
|
output: 5,
|
|
867
|
+
cachedInput: 0.1,
|
|
868
|
+
cacheCreationInput: 1.25,
|
|
725
869
|
maxInputTokens: 2e5
|
|
726
870
|
},
|
|
727
871
|
"claude-3-7-sonnet-20250219": {
|
|
728
872
|
input: 3,
|
|
729
873
|
output: 15,
|
|
874
|
+
cachedInput: 0.3,
|
|
875
|
+
cacheCreationInput: 3.75,
|
|
730
876
|
maxInputTokens: 2e5
|
|
731
877
|
},
|
|
732
878
|
"claude-3-haiku-20240307": {
|
|
733
879
|
input: 0.25,
|
|
734
880
|
output: 1.25,
|
|
881
|
+
cachedInput: 0.03,
|
|
882
|
+
cacheCreationInput: 0.3,
|
|
735
883
|
maxInputTokens: 2e5
|
|
736
884
|
},
|
|
737
885
|
"claude-3-opus-20240229": {
|
|
738
886
|
input: 15,
|
|
739
887
|
output: 75,
|
|
888
|
+
cachedInput: 1.5,
|
|
889
|
+
cacheCreationInput: 18.75,
|
|
740
890
|
maxInputTokens: 2e5
|
|
741
891
|
},
|
|
742
892
|
"claude-4-opus-20250514": {
|
|
743
893
|
input: 15,
|
|
744
894
|
output: 75,
|
|
895
|
+
cachedInput: 1.5,
|
|
896
|
+
cacheCreationInput: 18.75,
|
|
745
897
|
maxInputTokens: 2e5
|
|
746
898
|
},
|
|
747
899
|
"claude-4-sonnet-20250514": {
|
|
748
900
|
input: 3,
|
|
749
901
|
output: 15,
|
|
902
|
+
cachedInput: 0.3,
|
|
903
|
+
cacheCreationInput: 3.75,
|
|
750
904
|
maxInputTokens: 1e6
|
|
751
905
|
},
|
|
752
906
|
"claude-sonnet-4-5-20250929": {
|
|
753
907
|
input: 3,
|
|
754
908
|
output: 15,
|
|
909
|
+
cachedInput: 0.3,
|
|
910
|
+
cacheCreationInput: 3.75,
|
|
755
911
|
maxInputTokens: 2e5
|
|
756
912
|
},
|
|
757
913
|
"claude-sonnet-4-5-20250929-v1:0": {
|
|
758
914
|
input: 3,
|
|
759
915
|
output: 15,
|
|
916
|
+
cachedInput: 0.3,
|
|
917
|
+
cacheCreationInput: 3.75,
|
|
760
918
|
maxInputTokens: 2e5
|
|
761
919
|
},
|
|
762
920
|
"claude-opus-4-1-20250805": {
|
|
763
921
|
input: 15,
|
|
764
922
|
output: 75,
|
|
923
|
+
cachedInput: 1.5,
|
|
924
|
+
cacheCreationInput: 18.75,
|
|
765
925
|
maxInputTokens: 2e5
|
|
766
926
|
},
|
|
767
927
|
"claude-opus-4-20250514": {
|
|
768
928
|
input: 15,
|
|
769
929
|
output: 75,
|
|
930
|
+
cachedInput: 1.5,
|
|
931
|
+
cacheCreationInput: 18.75,
|
|
770
932
|
maxInputTokens: 2e5
|
|
771
933
|
},
|
|
772
934
|
"claude-opus-4-5-20251101": {
|
|
773
935
|
input: 5,
|
|
774
936
|
output: 25,
|
|
937
|
+
cachedInput: 0.5,
|
|
938
|
+
cacheCreationInput: 6.25,
|
|
775
939
|
maxInputTokens: 2e5
|
|
776
940
|
},
|
|
777
941
|
"claude-opus-4-6-20260205": {
|
|
778
942
|
input: 5,
|
|
779
943
|
output: 25,
|
|
944
|
+
cachedInput: 0.5,
|
|
945
|
+
cacheCreationInput: 6.25,
|
|
780
946
|
maxInputTokens: 1e6
|
|
781
947
|
},
|
|
782
948
|
"claude-opus-4-7-20260416": {
|
|
783
949
|
input: 5,
|
|
784
950
|
output: 25,
|
|
951
|
+
cachedInput: 0.5,
|
|
952
|
+
cacheCreationInput: 6.25,
|
|
785
953
|
maxInputTokens: 1e6
|
|
786
954
|
},
|
|
787
955
|
"claude-sonnet-4-20250514": {
|
|
788
956
|
input: 3,
|
|
789
957
|
output: 15,
|
|
958
|
+
cachedInput: 0.3,
|
|
959
|
+
cacheCreationInput: 3.75,
|
|
790
960
|
maxInputTokens: 1e6
|
|
791
961
|
},
|
|
792
962
|
"codex-mini-latest": {
|
|
793
963
|
input: 1.5,
|
|
794
964
|
output: 6,
|
|
965
|
+
cachedInput: 0.375,
|
|
795
966
|
maxInputTokens: 2e5
|
|
796
967
|
},
|
|
797
968
|
"deepseek-ai/deepseek-r1": {
|
|
@@ -841,6 +1012,7 @@ var prices_default = {
|
|
|
841
1012
|
"deepseek-ai/deepseek-v3.1-terminus": {
|
|
842
1013
|
input: 0.27,
|
|
843
1014
|
output: 1,
|
|
1015
|
+
cachedInput: 0.216,
|
|
844
1016
|
maxInputTokens: 163840
|
|
845
1017
|
},
|
|
846
1018
|
"deepseek-coder": {
|
|
@@ -851,26 +1023,31 @@ var prices_default = {
|
|
|
851
1023
|
"gemini-2.0-flash": {
|
|
852
1024
|
input: 0.1,
|
|
853
1025
|
output: 0.4,
|
|
1026
|
+
cachedInput: 0.025,
|
|
854
1027
|
maxInputTokens: 1048576
|
|
855
1028
|
},
|
|
856
1029
|
"gemini-2.0-flash-001": {
|
|
857
1030
|
input: 0.1,
|
|
858
1031
|
output: 0.4,
|
|
1032
|
+
cachedInput: 0.025,
|
|
859
1033
|
maxInputTokens: 1048576
|
|
860
1034
|
},
|
|
861
1035
|
"gemini-2.0-flash-lite": {
|
|
862
1036
|
input: 0.075,
|
|
863
1037
|
output: 0.3,
|
|
1038
|
+
cachedInput: 0.01875,
|
|
864
1039
|
maxInputTokens: 1048576
|
|
865
1040
|
},
|
|
866
1041
|
"gemini-2.0-flash-lite-001": {
|
|
867
1042
|
input: 0.075,
|
|
868
1043
|
output: 0.3,
|
|
1044
|
+
cachedInput: 0.01875,
|
|
869
1045
|
maxInputTokens: 1048576
|
|
870
1046
|
},
|
|
871
1047
|
"gemini-2.5-flash-image": {
|
|
872
1048
|
input: 0.3,
|
|
873
1049
|
output: 2.5,
|
|
1050
|
+
cachedInput: 0.03,
|
|
874
1051
|
maxInputTokens: 32768
|
|
875
1052
|
},
|
|
876
1053
|
"gemini-3-pro-image-preview": {
|
|
@@ -886,51 +1063,61 @@ var prices_default = {
|
|
|
886
1063
|
"gemini-3.1-flash-lite-preview": {
|
|
887
1064
|
input: 0.25,
|
|
888
1065
|
output: 1.5,
|
|
1066
|
+
cachedInput: 0.025,
|
|
889
1067
|
maxInputTokens: 1048576
|
|
890
1068
|
},
|
|
891
1069
|
"gemini-2.5-flash-lite": {
|
|
892
1070
|
input: 0.1,
|
|
893
1071
|
output: 0.4,
|
|
1072
|
+
cachedInput: 0.01,
|
|
894
1073
|
maxInputTokens: 1048576
|
|
895
1074
|
},
|
|
896
1075
|
"gemini-2.5-flash-lite-preview-09-2025": {
|
|
897
1076
|
input: 0.1,
|
|
898
1077
|
output: 0.4,
|
|
1078
|
+
cachedInput: 0.01,
|
|
899
1079
|
maxInputTokens: 1048576
|
|
900
1080
|
},
|
|
901
1081
|
"gemini-2.5-flash-preview-09-2025": {
|
|
902
1082
|
input: 0.3,
|
|
903
1083
|
output: 2.5,
|
|
1084
|
+
cachedInput: 0.075,
|
|
904
1085
|
maxInputTokens: 1048576
|
|
905
1086
|
},
|
|
906
1087
|
"gemini-live-2.5-flash-preview-native-audio-09-2025": {
|
|
907
1088
|
input: 0.3,
|
|
908
1089
|
output: 2,
|
|
1090
|
+
cachedInput: 0.075,
|
|
909
1091
|
maxInputTokens: 1048576
|
|
910
1092
|
},
|
|
911
1093
|
"gemini-2.5-flash-lite-preview-06-17": {
|
|
912
1094
|
input: 0.1,
|
|
913
1095
|
output: 0.4,
|
|
1096
|
+
cachedInput: 0.025,
|
|
914
1097
|
maxInputTokens: 1048576
|
|
915
1098
|
},
|
|
916
1099
|
"gemini-3-pro-preview": {
|
|
917
1100
|
input: 2,
|
|
918
1101
|
output: 12,
|
|
1102
|
+
cachedInput: 0.2,
|
|
919
1103
|
maxInputTokens: 1048576
|
|
920
1104
|
},
|
|
921
1105
|
"gemini-3.1-pro-preview": {
|
|
922
1106
|
input: 2,
|
|
923
1107
|
output: 12,
|
|
1108
|
+
cachedInput: 0.2,
|
|
924
1109
|
maxInputTokens: 1048576
|
|
925
1110
|
},
|
|
926
1111
|
"gemini-3.1-pro-preview-customtools": {
|
|
927
1112
|
input: 2,
|
|
928
1113
|
output: 12,
|
|
1114
|
+
cachedInput: 0.2,
|
|
929
1115
|
maxInputTokens: 1048576
|
|
930
1116
|
},
|
|
931
1117
|
"gemini-3-flash-preview": {
|
|
932
1118
|
input: 0.5,
|
|
933
1119
|
output: 3,
|
|
1120
|
+
cachedInput: 0.05,
|
|
934
1121
|
maxInputTokens: 1048576
|
|
935
1122
|
},
|
|
936
1123
|
"gemini-robotics-er-1.5-preview": {
|
|
@@ -946,11 +1133,13 @@ var prices_default = {
|
|
|
946
1133
|
"gemini-flash-latest": {
|
|
947
1134
|
input: 0.3,
|
|
948
1135
|
output: 2.5,
|
|
1136
|
+
cachedInput: 0.03,
|
|
949
1137
|
maxInputTokens: 1048576
|
|
950
1138
|
},
|
|
951
1139
|
"gemini-flash-lite-latest": {
|
|
952
1140
|
input: 0.1,
|
|
953
1141
|
output: 0.4,
|
|
1142
|
+
cachedInput: 0.01,
|
|
954
1143
|
maxInputTokens: 1048576
|
|
955
1144
|
},
|
|
956
1145
|
"gemini-gemma-2-27b-it": {
|
|
@@ -1026,39 +1215,47 @@ var prices_default = {
|
|
|
1026
1215
|
"gpt-4o-mini-realtime-preview": {
|
|
1027
1216
|
input: 0.6,
|
|
1028
1217
|
output: 2.4,
|
|
1218
|
+
cachedInput: 0.3,
|
|
1029
1219
|
maxInputTokens: 128e3
|
|
1030
1220
|
},
|
|
1031
1221
|
"gpt-4o-realtime-preview": {
|
|
1032
1222
|
input: 5,
|
|
1033
1223
|
output: 20,
|
|
1224
|
+
cachedInput: 2.5,
|
|
1034
1225
|
maxInputTokens: 128e3
|
|
1035
1226
|
},
|
|
1036
1227
|
"gpt-4o-realtime-preview-2025-06-03": {
|
|
1037
1228
|
input: 5,
|
|
1038
1229
|
output: 20,
|
|
1230
|
+
cachedInput: 2.5,
|
|
1039
1231
|
maxInputTokens: 128e3
|
|
1040
1232
|
},
|
|
1041
1233
|
"gpt-image-1.5": {
|
|
1042
1234
|
input: 5,
|
|
1043
|
-
output: 10
|
|
1235
|
+
output: 10,
|
|
1236
|
+
cachedInput: 1.25
|
|
1044
1237
|
},
|
|
1045
1238
|
"gpt-image-1.5-2025-12-16": {
|
|
1046
1239
|
input: 5,
|
|
1047
|
-
output: 10
|
|
1240
|
+
output: 10,
|
|
1241
|
+
cachedInput: 1.25
|
|
1048
1242
|
},
|
|
1049
1243
|
"gpt-5.1-chat-latest": {
|
|
1050
1244
|
input: 1.25,
|
|
1051
1245
|
output: 10,
|
|
1246
|
+
cachedInput: 0.125,
|
|
1052
1247
|
maxInputTokens: 128e3
|
|
1053
1248
|
},
|
|
1054
1249
|
"gpt-5.2-chat-latest": {
|
|
1055
1250
|
input: 1.75,
|
|
1056
1251
|
output: 14,
|
|
1252
|
+
cachedInput: 0.175,
|
|
1057
1253
|
maxInputTokens: 128e3
|
|
1058
1254
|
},
|
|
1059
1255
|
"gpt-5.3-chat-latest": {
|
|
1060
1256
|
input: 1.75,
|
|
1061
1257
|
output: 14,
|
|
1258
|
+
cachedInput: 0.175,
|
|
1062
1259
|
maxInputTokens: 128e3
|
|
1063
1260
|
},
|
|
1064
1261
|
"gpt-5-pro-2025-10-06": {
|
|
@@ -1069,11 +1266,13 @@ var prices_default = {
|
|
|
1069
1266
|
"gpt-realtime": {
|
|
1070
1267
|
input: 4,
|
|
1071
1268
|
output: 16,
|
|
1269
|
+
cachedInput: 0.4,
|
|
1072
1270
|
maxInputTokens: 32e3
|
|
1073
1271
|
},
|
|
1074
1272
|
"gpt-realtime-1.5": {
|
|
1075
1273
|
input: 4,
|
|
1076
1274
|
output: 16,
|
|
1275
|
+
cachedInput: 0.4,
|
|
1077
1276
|
maxInputTokens: 32e3
|
|
1078
1277
|
},
|
|
1079
1278
|
"gpt-realtime-mini": {
|
|
@@ -1120,6 +1319,7 @@ var prices_default = {
|
|
|
1120
1319
|
o1: {
|
|
1121
1320
|
input: 15,
|
|
1122
1321
|
output: 60,
|
|
1322
|
+
cachedInput: 7.5,
|
|
1123
1323
|
maxInputTokens: 2e5
|
|
1124
1324
|
},
|
|
1125
1325
|
"o1-pro": {
|
|
@@ -1135,6 +1335,7 @@ var prices_default = {
|
|
|
1135
1335
|
o3: {
|
|
1136
1336
|
input: 2,
|
|
1137
1337
|
output: 8,
|
|
1338
|
+
cachedInput: 0.5,
|
|
1138
1339
|
maxInputTokens: 2e5
|
|
1139
1340
|
},
|
|
1140
1341
|
"gpt-oss-20b": {
|
|
@@ -1159,6 +1360,8 @@ var prices_default = {
|
|
|
1159
1360
|
"claude-haiku-4-5@20251001": {
|
|
1160
1361
|
input: 1,
|
|
1161
1362
|
output: 5,
|
|
1363
|
+
cachedInput: 0.1,
|
|
1364
|
+
cacheCreationInput: 1.25,
|
|
1162
1365
|
maxInputTokens: 2e5
|
|
1163
1366
|
},
|
|
1164
1367
|
"claude-3-5-sonnet": {
|
|
@@ -1174,6 +1377,8 @@ var prices_default = {
|
|
|
1174
1377
|
"claude-3-7-sonnet@20250219": {
|
|
1175
1378
|
input: 3,
|
|
1176
1379
|
output: 15,
|
|
1380
|
+
cachedInput: 0.3,
|
|
1381
|
+
cacheCreationInput: 3.75,
|
|
1177
1382
|
maxInputTokens: 2e5
|
|
1178
1383
|
},
|
|
1179
1384
|
"claude-3-haiku": {
|
|
@@ -1209,46 +1414,64 @@ var prices_default = {
|
|
|
1209
1414
|
"claude-opus-4": {
|
|
1210
1415
|
input: 15,
|
|
1211
1416
|
output: 75,
|
|
1417
|
+
cachedInput: 1.5,
|
|
1418
|
+
cacheCreationInput: 18.75,
|
|
1212
1419
|
maxInputTokens: 2e5
|
|
1213
1420
|
},
|
|
1214
1421
|
"claude-opus-4-1@20250805": {
|
|
1215
1422
|
input: 15,
|
|
1216
1423
|
output: 75,
|
|
1424
|
+
cachedInput: 1.5,
|
|
1425
|
+
cacheCreationInput: 18.75,
|
|
1217
1426
|
maxInputTokens: 2e5
|
|
1218
1427
|
},
|
|
1219
1428
|
"claude-opus-4-5@20251101": {
|
|
1220
1429
|
input: 5,
|
|
1221
1430
|
output: 25,
|
|
1431
|
+
cachedInput: 0.5,
|
|
1432
|
+
cacheCreationInput: 6.25,
|
|
1222
1433
|
maxInputTokens: 2e5
|
|
1223
1434
|
},
|
|
1224
1435
|
"claude-opus-4-6@default": {
|
|
1225
1436
|
input: 5,
|
|
1226
1437
|
output: 25,
|
|
1438
|
+
cachedInput: 0.5,
|
|
1439
|
+
cacheCreationInput: 6.25,
|
|
1227
1440
|
maxInputTokens: 1e6
|
|
1228
1441
|
},
|
|
1229
1442
|
"claude-opus-4-7@default": {
|
|
1230
1443
|
input: 5,
|
|
1231
1444
|
output: 25,
|
|
1445
|
+
cachedInput: 0.5,
|
|
1446
|
+
cacheCreationInput: 6.25,
|
|
1232
1447
|
maxInputTokens: 1e6
|
|
1233
1448
|
},
|
|
1234
1449
|
"claude-sonnet-4-5@20250929": {
|
|
1235
1450
|
input: 3,
|
|
1236
1451
|
output: 15,
|
|
1452
|
+
cachedInput: 0.3,
|
|
1453
|
+
cacheCreationInput: 3.75,
|
|
1237
1454
|
maxInputTokens: 2e5
|
|
1238
1455
|
},
|
|
1239
1456
|
"claude-opus-4@20250514": {
|
|
1240
1457
|
input: 15,
|
|
1241
1458
|
output: 75,
|
|
1459
|
+
cachedInput: 1.5,
|
|
1460
|
+
cacheCreationInput: 18.75,
|
|
1242
1461
|
maxInputTokens: 2e5
|
|
1243
1462
|
},
|
|
1244
1463
|
"claude-sonnet-4": {
|
|
1245
1464
|
input: 3,
|
|
1246
1465
|
output: 15,
|
|
1466
|
+
cachedInput: 0.3,
|
|
1467
|
+
cacheCreationInput: 3.75,
|
|
1247
1468
|
maxInputTokens: 1e6
|
|
1248
1469
|
},
|
|
1249
1470
|
"claude-sonnet-4@20250514": {
|
|
1250
1471
|
input: 3,
|
|
1251
1472
|
output: 15,
|
|
1473
|
+
cachedInput: 0.3,
|
|
1474
|
+
cacheCreationInput: 3.75,
|
|
1252
1475
|
maxInputTokens: 1e6
|
|
1253
1476
|
},
|
|
1254
1477
|
"deepseek-ai/deepseek-v3.1-maas": {
|
|
@@ -1298,6 +1521,7 @@ var prices_default = {
|
|
|
1298
1521
|
"gpt-realtime-mini-2025-12-15": {
|
|
1299
1522
|
input: 0.6,
|
|
1300
1523
|
output: 2.4,
|
|
1524
|
+
cachedInput: 0.06,
|
|
1301
1525
|
maxInputTokens: 128e3
|
|
1302
1526
|
},
|
|
1303
1527
|
"gemini-2.5-flash-native-audio-latest": {
|
|
@@ -1323,16 +1547,20 @@ var prices_default = {
|
|
|
1323
1547
|
"gemini-pro-latest": {
|
|
1324
1548
|
input: 1.25,
|
|
1325
1549
|
output: 10,
|
|
1550
|
+
cachedInput: 0.125,
|
|
1326
1551
|
maxInputTokens: 1048576
|
|
1327
1552
|
},
|
|
1328
1553
|
"gemini-exp-1206": {
|
|
1329
1554
|
input: 0.3,
|
|
1330
1555
|
output: 2.5,
|
|
1556
|
+
cachedInput: 0.03,
|
|
1331
1557
|
maxInputTokens: 1048576
|
|
1332
1558
|
},
|
|
1333
1559
|
"claude-sonnet-4-6@default": {
|
|
1334
1560
|
input: 3,
|
|
1335
1561
|
output: 15,
|
|
1562
|
+
cachedInput: 0.3,
|
|
1563
|
+
cacheCreationInput: 3.75,
|
|
1336
1564
|
maxInputTokens: 1e6
|
|
1337
1565
|
}
|
|
1338
1566
|
}
|
|
@@ -1340,11 +1568,19 @@ var prices_default = {
|
|
|
1340
1568
|
|
|
1341
1569
|
// src/core/tracker.ts
|
|
1342
1570
|
var bundledPrices = prices_default.models;
|
|
1571
|
+
var bundledUpdatedAt = prices_default.updated_at ?? "";
|
|
1343
1572
|
var ModelPriceSchema = z.object({
|
|
1344
1573
|
input: z.number().nonnegative(),
|
|
1345
1574
|
output: z.number().nonnegative(),
|
|
1575
|
+
cachedInput: z.number().nonnegative().optional(),
|
|
1576
|
+
cacheCreationInput: z.number().nonnegative().optional(),
|
|
1346
1577
|
maxInputTokens: z.number().positive().optional()
|
|
1347
1578
|
});
|
|
1579
|
+
var BudgetConfigSchema = z.object({
|
|
1580
|
+
threshold: z.number().positive(),
|
|
1581
|
+
webhookUrl: z.string().url(),
|
|
1582
|
+
mode: z.enum(["once", "always"]).optional().default("once")
|
|
1583
|
+
});
|
|
1348
1584
|
var TrackerConfigSchema = z.object({
|
|
1349
1585
|
storage: z.union([z.enum(["memory", "sqlite"]), z.custom((v) => {
|
|
1350
1586
|
return v !== null && typeof v === "object" && typeof v.record === "function" && typeof v.getAll === "function" && typeof v.clearAll === "function" && typeof v.clearSession === "function";
|
|
@@ -1352,7 +1588,13 @@ var TrackerConfigSchema = z.object({
|
|
|
1352
1588
|
alertThreshold: z.number().positive().optional(),
|
|
1353
1589
|
webhookUrl: z.string().url().optional(),
|
|
1354
1590
|
syncPrices: z.boolean().optional().default(true),
|
|
1355
|
-
customPrices: z.record(z.string(), ModelPriceSchema).optional()
|
|
1591
|
+
customPrices: z.record(z.string(), ModelPriceSchema).optional(),
|
|
1592
|
+
warnIfStaleAfterHours: z.number().nonnegative().optional().default(72),
|
|
1593
|
+
budgets: z.object({
|
|
1594
|
+
perUser: BudgetConfigSchema.optional(),
|
|
1595
|
+
perSession: BudgetConfigSchema.optional()
|
|
1596
|
+
}).optional(),
|
|
1597
|
+
suggestions: z.boolean().optional().default(false)
|
|
1356
1598
|
});
|
|
1357
1599
|
function createTracker(config = {}) {
|
|
1358
1600
|
const parsed = TrackerConfigSchema.safeParse(config);
|
|
@@ -1366,19 +1608,45 @@ ${issues}`);
|
|
|
1366
1608
|
alertThreshold,
|
|
1367
1609
|
webhookUrl,
|
|
1368
1610
|
syncPrices,
|
|
1369
|
-
customPrices
|
|
1611
|
+
customPrices,
|
|
1612
|
+
warnIfStaleAfterHours,
|
|
1613
|
+
budgets,
|
|
1614
|
+
suggestions
|
|
1370
1615
|
} = parsed.data;
|
|
1371
1616
|
const storage = typeof storageOption === "object" ? storageOption : createStorage(storageOption);
|
|
1372
1617
|
let remotePrices;
|
|
1618
|
+
let pricesUpdatedAt = bundledUpdatedAt;
|
|
1373
1619
|
if (syncPrices) {
|
|
1374
1620
|
getRemotePrices().then((result) => {
|
|
1375
|
-
if (result)
|
|
1621
|
+
if (result) {
|
|
1622
|
+
remotePrices = result.models;
|
|
1623
|
+
pricesUpdatedAt = result.updated_at;
|
|
1624
|
+
}
|
|
1376
1625
|
}).catch(() => {
|
|
1377
1626
|
});
|
|
1378
1627
|
}
|
|
1628
|
+
let stalenessChecked = false;
|
|
1629
|
+
function maybeWarnStaleness() {
|
|
1630
|
+
if (stalenessChecked || !warnIfStaleAfterHours) return;
|
|
1631
|
+
stalenessChecked = true;
|
|
1632
|
+
if (!pricesUpdatedAt) return;
|
|
1633
|
+
try {
|
|
1634
|
+
const updatedMs = new Date(pricesUpdatedAt).getTime();
|
|
1635
|
+
const ageHours = (Date.now() - updatedMs) / (1e3 * 60 * 60);
|
|
1636
|
+
if (ageHours > warnIfStaleAfterHours) {
|
|
1637
|
+
console.warn(
|
|
1638
|
+
`[tokenwatch] Price data is ${Math.round(ageHours)}h old (updated_at: ${pricesUpdatedAt}). Run "tokenwatch sync" to refresh, or set warnIfStaleAfterHours: 0 to suppress.`
|
|
1639
|
+
);
|
|
1640
|
+
}
|
|
1641
|
+
} catch {
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1379
1644
|
let alertFired = false;
|
|
1645
|
+
const firedUserAlerts = /* @__PURE__ */ new Set();
|
|
1646
|
+
const firedSessionAlerts = /* @__PURE__ */ new Set();
|
|
1380
1647
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1381
1648
|
function resolveModelPrice(model) {
|
|
1649
|
+
maybeWarnStaleness();
|
|
1382
1650
|
return resolvePrice(model, {
|
|
1383
1651
|
bundledPrices,
|
|
1384
1652
|
...customPrices !== void 0 && { customPrices },
|
|
@@ -1389,8 +1657,10 @@ ${issues}`);
|
|
|
1389
1657
|
const price = resolveModelPrice(entry.model);
|
|
1390
1658
|
const costUSD = calculateCost(
|
|
1391
1659
|
entry.inputTokens,
|
|
1392
|
-
entry.outputTokens
|
|
1393
|
-
price
|
|
1660
|
+
entry.outputTokens,
|
|
1661
|
+
price,
|
|
1662
|
+
entry.cachedTokens,
|
|
1663
|
+
entry.cacheCreationTokens
|
|
1394
1664
|
);
|
|
1395
1665
|
const full = {
|
|
1396
1666
|
...entry,
|
|
@@ -1398,32 +1668,81 @@ ${issues}`);
|
|
|
1398
1668
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1399
1669
|
};
|
|
1400
1670
|
storage.record(full);
|
|
1401
|
-
|
|
1671
|
+
maybeFireAlerts(full);
|
|
1672
|
+
if (suggestions) {
|
|
1673
|
+
maybeSuggestCheaperModel(entry.model, costUSD, entry.inputTokens, entry.outputTokens, {
|
|
1674
|
+
bundledPrices,
|
|
1675
|
+
...customPrices !== void 0 && { customPrices },
|
|
1676
|
+
...remotePrices !== void 0 && { remotePrices }
|
|
1677
|
+
});
|
|
1678
|
+
}
|
|
1402
1679
|
}
|
|
1403
|
-
function
|
|
1404
|
-
if (
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
fetch(webhookUrl, {
|
|
1416
|
-
method: "POST",
|
|
1417
|
-
headers: { "Content-Type": "application/json" },
|
|
1418
|
-
body: JSON.stringify(payload)
|
|
1680
|
+
function maybeFireAlerts(entry) {
|
|
1681
|
+
if (alertThreshold && webhookUrl && !alertFired) {
|
|
1682
|
+
alertFired = true;
|
|
1683
|
+
Promise.resolve(storage.getAll()).then((entries) => {
|
|
1684
|
+
const total = computeTotal(entries);
|
|
1685
|
+
if (total < alertThreshold) {
|
|
1686
|
+
alertFired = false;
|
|
1687
|
+
return;
|
|
1688
|
+
}
|
|
1689
|
+
fireWebhook(webhookUrl, {
|
|
1690
|
+
text: `[tokenwatch] Alert: total cost reached $${total.toFixed(4)} USD (threshold: $${alertThreshold})`
|
|
1691
|
+
});
|
|
1419
1692
|
}).catch(() => {
|
|
1693
|
+
alertFired = false;
|
|
1420
1694
|
});
|
|
1695
|
+
}
|
|
1696
|
+
if (budgets?.perUser && entry.userId) {
|
|
1697
|
+
const cfg = budgets.perUser;
|
|
1698
|
+
const uid = entry.userId;
|
|
1699
|
+
if (cfg.mode === "always" || !firedUserAlerts.has(uid)) {
|
|
1700
|
+
if (cfg.mode !== "always") firedUserAlerts.add(uid);
|
|
1701
|
+
Promise.resolve(storage.getAll()).then((entries) => {
|
|
1702
|
+
const userCost = entries.filter((e) => e.userId === uid).reduce((s, e) => s + e.costUSD, 0);
|
|
1703
|
+
if (userCost >= cfg.threshold) {
|
|
1704
|
+
fireWebhook(cfg.webhookUrl, {
|
|
1705
|
+
text: `[tokenwatch] Budget alert: user "${uid}" reached $${userCost.toFixed(4)} USD (threshold: $${cfg.threshold})`
|
|
1706
|
+
});
|
|
1707
|
+
} else {
|
|
1708
|
+
if (cfg.mode !== "always") firedUserAlerts.delete(uid);
|
|
1709
|
+
}
|
|
1710
|
+
}).catch(() => {
|
|
1711
|
+
if (cfg.mode !== "always") firedUserAlerts.delete(uid);
|
|
1712
|
+
});
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
if (budgets?.perSession && entry.sessionId) {
|
|
1716
|
+
const cfg = budgets.perSession;
|
|
1717
|
+
const sid = entry.sessionId;
|
|
1718
|
+
if (cfg.mode === "always" || !firedSessionAlerts.has(sid)) {
|
|
1719
|
+
if (cfg.mode !== "always") firedSessionAlerts.add(sid);
|
|
1720
|
+
Promise.resolve(storage.getAll()).then((entries) => {
|
|
1721
|
+
const sessionCost = entries.filter((e) => e.sessionId === sid).reduce((s, e) => s + e.costUSD, 0);
|
|
1722
|
+
if (sessionCost >= cfg.threshold) {
|
|
1723
|
+
fireWebhook(cfg.webhookUrl, {
|
|
1724
|
+
text: `[tokenwatch] Budget alert: session "${sid}" reached $${sessionCost.toFixed(4)} USD (threshold: $${cfg.threshold})`
|
|
1725
|
+
});
|
|
1726
|
+
} else {
|
|
1727
|
+
if (cfg.mode !== "always") firedSessionAlerts.delete(sid);
|
|
1728
|
+
}
|
|
1729
|
+
}).catch(() => {
|
|
1730
|
+
if (cfg.mode !== "always") firedSessionAlerts.delete(sid);
|
|
1731
|
+
});
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
function fireWebhook(url, payload) {
|
|
1736
|
+
fetch(url, {
|
|
1737
|
+
method: "POST",
|
|
1738
|
+
headers: { "Content-Type": "application/json" },
|
|
1739
|
+
body: JSON.stringify(payload)
|
|
1421
1740
|
}).catch(() => {
|
|
1422
|
-
alertFired = false;
|
|
1423
1741
|
});
|
|
1424
1742
|
}
|
|
1425
|
-
async function getReport() {
|
|
1426
|
-
const
|
|
1743
|
+
async function getReport(options) {
|
|
1744
|
+
const allEntries = await Promise.resolve(storage.getAll());
|
|
1745
|
+
const entries = filterEntries(allEntries, options);
|
|
1427
1746
|
const byModel = {};
|
|
1428
1747
|
const bySession = {};
|
|
1429
1748
|
const byUser = {};
|
|
@@ -1431,18 +1750,24 @@ ${issues}`);
|
|
|
1431
1750
|
let totalInput = 0;
|
|
1432
1751
|
let totalOutput = 0;
|
|
1433
1752
|
let totalCost = 0;
|
|
1434
|
-
let
|
|
1753
|
+
let periodFrom = options ? entries[0]?.timestamp ?? startedAt : startedAt;
|
|
1754
|
+
let lastTimestamp = periodFrom;
|
|
1435
1755
|
for (const e of entries) {
|
|
1436
|
-
totalInput += e.inputTokens;
|
|
1756
|
+
totalInput += e.inputTokens + (e.cachedTokens ?? 0) + (e.cacheCreationTokens ?? 0);
|
|
1437
1757
|
totalOutput += e.outputTokens;
|
|
1438
1758
|
totalCost += e.costUSD;
|
|
1439
1759
|
if (e.timestamp > lastTimestamp) lastTimestamp = e.timestamp;
|
|
1440
|
-
const m = byModel[e.model] ??= {
|
|
1760
|
+
const m = byModel[e.model] ??= {
|
|
1761
|
+
costUSD: 0,
|
|
1762
|
+
calls: 0,
|
|
1763
|
+
tokens: { input: 0, output: 0, reasoning: 0, cached: 0 }
|
|
1764
|
+
};
|
|
1441
1765
|
m.costUSD += e.costUSD;
|
|
1442
1766
|
m.calls += 1;
|
|
1443
|
-
m.tokens.input += e.inputTokens;
|
|
1767
|
+
m.tokens.input += e.inputTokens + (e.cachedTokens ?? 0) + (e.cacheCreationTokens ?? 0);
|
|
1444
1768
|
m.tokens.output += e.outputTokens;
|
|
1445
1769
|
m.tokens.reasoning += e.reasoningTokens ?? 0;
|
|
1770
|
+
m.tokens.cached += e.cachedTokens ?? 0;
|
|
1446
1771
|
if (e.sessionId) {
|
|
1447
1772
|
const s = bySession[e.sessionId] ??= { costUSD: 0, calls: 0 };
|
|
1448
1773
|
s.costUSD += e.costUSD;
|
|
@@ -1459,6 +1784,9 @@ ${issues}`);
|
|
|
1459
1784
|
f.calls += 1;
|
|
1460
1785
|
}
|
|
1461
1786
|
}
|
|
1787
|
+
if (options && entries.length > 0) {
|
|
1788
|
+
periodFrom = entries[0]?.timestamp ?? periodFrom;
|
|
1789
|
+
}
|
|
1462
1790
|
return {
|
|
1463
1791
|
totalCostUSD: totalCost,
|
|
1464
1792
|
totalTokens: { input: totalInput, output: totalOutput },
|
|
@@ -1466,22 +1794,66 @@ ${issues}`);
|
|
|
1466
1794
|
bySession,
|
|
1467
1795
|
byUser,
|
|
1468
1796
|
byFeature,
|
|
1469
|
-
period: { from:
|
|
1797
|
+
period: { from: periodFrom, to: lastTimestamp },
|
|
1798
|
+
...pricesUpdatedAt ? { pricesUpdatedAt } : {}
|
|
1799
|
+
};
|
|
1800
|
+
}
|
|
1801
|
+
async function getCostForecast(options = {}) {
|
|
1802
|
+
const windowHours = options.windowHours ?? 24;
|
|
1803
|
+
const allEntries = await Promise.resolve(storage.getAll());
|
|
1804
|
+
const now = Date.now();
|
|
1805
|
+
const windowStart = now - windowHours * 60 * 60 * 1e3;
|
|
1806
|
+
const windowEntries = allEntries.filter(
|
|
1807
|
+
(e) => new Date(e.timestamp).getTime() >= windowStart
|
|
1808
|
+
);
|
|
1809
|
+
if (windowEntries.length < 2) {
|
|
1810
|
+
return {
|
|
1811
|
+
burnRatePerHour: 0,
|
|
1812
|
+
projectedDailyCostUSD: 0,
|
|
1813
|
+
projectedMonthlyCostUSD: 0,
|
|
1814
|
+
basedOnHours: 0,
|
|
1815
|
+
basedOnPeriod: null
|
|
1816
|
+
};
|
|
1817
|
+
}
|
|
1818
|
+
const first = windowEntries[0]?.timestamp ?? "";
|
|
1819
|
+
const last = windowEntries[windowEntries.length - 1]?.timestamp ?? "";
|
|
1820
|
+
const actualMs = new Date(last).getTime() - new Date(first).getTime();
|
|
1821
|
+
const actualHours = actualMs / (1e3 * 60 * 60);
|
|
1822
|
+
if (actualHours < 1e-3) {
|
|
1823
|
+
return {
|
|
1824
|
+
burnRatePerHour: 0,
|
|
1825
|
+
projectedDailyCostUSD: 0,
|
|
1826
|
+
projectedMonthlyCostUSD: 0,
|
|
1827
|
+
basedOnHours: 0,
|
|
1828
|
+
basedOnPeriod: { from: first, to: last }
|
|
1829
|
+
};
|
|
1830
|
+
}
|
|
1831
|
+
const totalCost = windowEntries.reduce((s, e) => s + e.costUSD, 0);
|
|
1832
|
+
const burnRatePerHour = totalCost / actualHours;
|
|
1833
|
+
return {
|
|
1834
|
+
burnRatePerHour,
|
|
1835
|
+
projectedDailyCostUSD: burnRatePerHour * 24,
|
|
1836
|
+
projectedMonthlyCostUSD: burnRatePerHour * 24 * 30,
|
|
1837
|
+
basedOnHours: Math.round(actualHours * 100) / 100,
|
|
1838
|
+
basedOnPeriod: { from: first, to: last }
|
|
1470
1839
|
};
|
|
1471
1840
|
}
|
|
1472
1841
|
async function reset() {
|
|
1473
1842
|
await Promise.resolve(storage.clearAll());
|
|
1474
1843
|
alertFired = false;
|
|
1844
|
+
firedUserAlerts.clear();
|
|
1845
|
+
firedSessionAlerts.clear();
|
|
1475
1846
|
}
|
|
1476
1847
|
async function resetSession(sessionId) {
|
|
1477
1848
|
await Promise.resolve(storage.clearSession(sessionId));
|
|
1849
|
+
firedSessionAlerts.delete(sessionId);
|
|
1478
1850
|
}
|
|
1479
1851
|
async function exportJSON() {
|
|
1480
1852
|
return JSON.stringify(await getReport(), null, 2);
|
|
1481
1853
|
}
|
|
1482
1854
|
async function exportCSV() {
|
|
1483
1855
|
const entries = await Promise.resolve(storage.getAll());
|
|
1484
|
-
const header = "timestamp,model,inputTokens,outputTokens,reasoningTokens,costUSD,sessionId,userId,feature";
|
|
1856
|
+
const header = "timestamp,model,inputTokens,outputTokens,reasoningTokens,cachedTokens,cacheCreationTokens,costUSD,sessionId,userId,feature";
|
|
1485
1857
|
const rows = entries.map(
|
|
1486
1858
|
(e) => [
|
|
1487
1859
|
csvEscape(e.timestamp),
|
|
@@ -1489,6 +1861,8 @@ ${issues}`);
|
|
|
1489
1861
|
e.inputTokens,
|
|
1490
1862
|
e.outputTokens,
|
|
1491
1863
|
e.reasoningTokens ?? 0,
|
|
1864
|
+
e.cachedTokens ?? 0,
|
|
1865
|
+
e.cacheCreationTokens ?? 0,
|
|
1492
1866
|
e.costUSD.toFixed(8),
|
|
1493
1867
|
csvEscape(e.sessionId ?? ""),
|
|
1494
1868
|
csvEscape(e.userId ?? ""),
|
|
@@ -1504,11 +1878,47 @@ ${issues}`);
|
|
|
1504
1878
|
...remotePrices !== void 0 && { remotePrices }
|
|
1505
1879
|
}) ?? null;
|
|
1506
1880
|
}
|
|
1507
|
-
return {
|
|
1881
|
+
return {
|
|
1882
|
+
track,
|
|
1883
|
+
getReport,
|
|
1884
|
+
getCostForecast,
|
|
1885
|
+
reset,
|
|
1886
|
+
resetSession,
|
|
1887
|
+
exportJSON,
|
|
1888
|
+
exportCSV,
|
|
1889
|
+
getModelInfo
|
|
1890
|
+
};
|
|
1508
1891
|
}
|
|
1509
1892
|
function computeTotal(entries) {
|
|
1510
1893
|
return entries.reduce((sum, e) => sum + e.costUSD, 0);
|
|
1511
1894
|
}
|
|
1895
|
+
function parseLastMs(last) {
|
|
1896
|
+
const match = /^(\d+(?:\.\d+)?)(h|d)$/.exec(last.trim());
|
|
1897
|
+
if (!match) throw new Error(`[tokenwatch] Invalid "last" value: "${last}". Use e.g. "24h", "7d".`);
|
|
1898
|
+
const value = parseFloat(match[1] ?? "0");
|
|
1899
|
+
const unit = match[2] ?? "h";
|
|
1900
|
+
return unit === "h" ? value * 60 * 60 * 1e3 : value * 24 * 60 * 60 * 1e3;
|
|
1901
|
+
}
|
|
1902
|
+
function filterEntries(entries, options) {
|
|
1903
|
+
if (!options) return entries;
|
|
1904
|
+
let sinceMs;
|
|
1905
|
+
let untilMs;
|
|
1906
|
+
if (options.last) {
|
|
1907
|
+
sinceMs = Date.now() - parseLastMs(options.last);
|
|
1908
|
+
} else if (options.since) {
|
|
1909
|
+
sinceMs = new Date(options.since).getTime();
|
|
1910
|
+
}
|
|
1911
|
+
if (options.until) {
|
|
1912
|
+
untilMs = new Date(options.until).getTime();
|
|
1913
|
+
}
|
|
1914
|
+
if (sinceMs === void 0 && untilMs === void 0) return entries;
|
|
1915
|
+
return entries.filter((e) => {
|
|
1916
|
+
const ts = new Date(e.timestamp).getTime();
|
|
1917
|
+
if (sinceMs !== void 0 && ts < sinceMs) return false;
|
|
1918
|
+
if (untilMs !== void 0 && ts > untilMs) return false;
|
|
1919
|
+
return true;
|
|
1920
|
+
});
|
|
1921
|
+
}
|
|
1512
1922
|
function csvEscape(value) {
|
|
1513
1923
|
if (value.includes(",") || value.includes('"') || value.includes("\n")) {
|
|
1514
1924
|
return `"${value.replace(/"/g, '""')}"`;
|
|
@@ -1529,7 +1939,7 @@ async function cmdSync() {
|
|
|
1529
1939
|
console.log("Fetching latest prices from remote...");
|
|
1530
1940
|
const result = await fetchRemotePrices();
|
|
1531
1941
|
if (result) {
|
|
1532
|
-
console.log(`\u2713 Prices updated. ${Object.keys(result).length} models cached.`);
|
|
1942
|
+
console.log(`\u2713 Prices updated. ${Object.keys(result.models).length} models cached (updated_at: ${result.updated_at}).`);
|
|
1533
1943
|
} else {
|
|
1534
1944
|
console.error("\u2717 Failed to fetch remote prices. Check your internet connection.");
|
|
1535
1945
|
process.exit(1);
|
|
@@ -1575,6 +1985,9 @@ async function cmdReport() {
|
|
|
1575
1985
|
console.log(` Total cost: $${report.totalCostUSD.toFixed(6)} USD`);
|
|
1576
1986
|
console.log(` Total tokens: ${report.totalTokens.input.toLocaleString()} in / ${report.totalTokens.output.toLocaleString()} out`);
|
|
1577
1987
|
console.log(` Period: ${report.period.from} \u2192 ${report.period.to}`);
|
|
1988
|
+
if (report.pricesUpdatedAt) {
|
|
1989
|
+
console.log(` Prices as of: ${report.pricesUpdatedAt}`);
|
|
1990
|
+
}
|
|
1578
1991
|
if (Object.keys(report.byModel).length > 0) {
|
|
1579
1992
|
console.log("\n By model:");
|
|
1580
1993
|
for (const [model, stats] of Object.entries(report.byModel)) {
|
|
@@ -1593,6 +2006,12 @@ async function cmdReport() {
|
|
|
1593
2006
|
console.log(` ${session.padEnd(30)} $${stats.costUSD.toFixed(6)} (${stats.calls} calls)`);
|
|
1594
2007
|
}
|
|
1595
2008
|
}
|
|
2009
|
+
if (Object.keys(report.byFeature).length > 0) {
|
|
2010
|
+
console.log("\n By feature:");
|
|
2011
|
+
for (const [feature, stats] of Object.entries(report.byFeature)) {
|
|
2012
|
+
console.log(` ${feature.padEnd(30)} $${stats.costUSD.toFixed(6)} (${stats.calls} calls)`);
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
1596
2015
|
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
|
|
1597
2016
|
}
|
|
1598
2017
|
function cmdHelp() {
|