@diogonzafe/tokenwatch 0.2.1 → 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 +224 -13
- package/dist/adapters.cjs +61 -36
- package/dist/adapters.cjs.map +1 -1
- package/dist/adapters.d.cts +5 -3
- package/dist/adapters.d.ts +5 -3
- package/dist/adapters.js +61 -36
- package/dist/adapters.js.map +1 -1
- package/dist/cli.js +455 -51
- package/dist/cli.js.map +1 -1
- package/dist/{index-B_EmA3K7.d.cts → index-CJKk1hHw.d.cts} +62 -2
- package/dist/{index-B_EmA3K7.d.ts → index-CJKk1hHw.d.ts} +62 -2
- package/dist/index.cjs +548 -64
- 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 +547 -64
- 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 -2
- package/prices.json +167 -3
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,16 +99,18 @@ 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
|
-
reasoning_tokens
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
|
111
114
|
)
|
|
112
115
|
`);
|
|
113
116
|
const cols = this.db.prepare(`PRAGMA table_info(usage)`).all().map((c) => c.name);
|
|
@@ -117,17 +120,26 @@ var SqliteStorage = class {
|
|
|
117
120
|
if (!cols.includes("feature")) {
|
|
118
121
|
this.db.exec(`ALTER TABLE usage ADD COLUMN feature TEXT`);
|
|
119
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
|
+
}
|
|
120
129
|
}
|
|
121
130
|
record(entry) {
|
|
122
131
|
this.db.prepare(
|
|
123
132
|
`INSERT INTO usage
|
|
124
|
-
(model, input_tokens, output_tokens, reasoning_tokens,
|
|
125
|
-
|
|
133
|
+
(model, input_tokens, output_tokens, reasoning_tokens, cached_tokens, cache_creation_tokens,
|
|
134
|
+
cost_usd, session_id, user_id, feature, timestamp)
|
|
135
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
126
136
|
).run(
|
|
127
137
|
entry.model,
|
|
128
138
|
entry.inputTokens,
|
|
129
139
|
entry.outputTokens,
|
|
130
140
|
entry.reasoningTokens ?? 0,
|
|
141
|
+
entry.cachedTokens ?? 0,
|
|
142
|
+
entry.cacheCreationTokens ?? 0,
|
|
131
143
|
entry.costUSD,
|
|
132
144
|
entry.sessionId ?? null,
|
|
133
145
|
entry.userId ?? null,
|
|
@@ -142,6 +154,8 @@ var SqliteStorage = class {
|
|
|
142
154
|
inputTokens: r.input_tokens,
|
|
143
155
|
outputTokens: r.output_tokens,
|
|
144
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 },
|
|
145
159
|
costUSD: r.cost_usd,
|
|
146
160
|
...r.session_id != null && { sessionId: r.session_id },
|
|
147
161
|
...r.user_id != null && { userId: r.user_id },
|
|
@@ -188,93 +202,153 @@ function lookupInMap(model, map) {
|
|
|
188
202
|
}
|
|
189
203
|
return void 0;
|
|
190
204
|
}
|
|
191
|
-
function calculateCost(inputTokens, outputTokens, price) {
|
|
192
|
-
|
|
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
|
+
);
|
|
193
244
|
}
|
|
194
245
|
|
|
195
246
|
// prices.json
|
|
196
247
|
var prices_default = {
|
|
197
|
-
updated_at: "2026-04-
|
|
248
|
+
updated_at: "2026-04-22",
|
|
198
249
|
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
199
250
|
models: {
|
|
200
251
|
"gpt-4o": {
|
|
201
252
|
input: 2.5,
|
|
202
253
|
output: 10,
|
|
254
|
+
cachedInput: 1.25,
|
|
203
255
|
maxInputTokens: 128e3
|
|
204
256
|
},
|
|
205
257
|
"gpt-4o-mini": {
|
|
206
258
|
input: 0.15,
|
|
207
259
|
output: 0.6,
|
|
260
|
+
cachedInput: 0.075,
|
|
208
261
|
maxInputTokens: 128e3
|
|
209
262
|
},
|
|
210
263
|
"gpt-5": {
|
|
211
264
|
input: 1.25,
|
|
212
265
|
output: 10,
|
|
266
|
+
cachedInput: 0.125,
|
|
213
267
|
maxInputTokens: 272e3
|
|
214
268
|
},
|
|
215
269
|
"gpt-5-mini": {
|
|
216
270
|
input: 0.25,
|
|
217
271
|
output: 2,
|
|
272
|
+
cachedInput: 0.025,
|
|
218
273
|
maxInputTokens: 272e3
|
|
219
274
|
},
|
|
220
275
|
"gpt-5-nano": {
|
|
221
276
|
input: 0.05,
|
|
222
277
|
output: 0.4,
|
|
278
|
+
cachedInput: 5e-3,
|
|
223
279
|
maxInputTokens: 272e3
|
|
224
280
|
},
|
|
225
281
|
"claude-opus-4-6": {
|
|
226
282
|
input: 5,
|
|
227
283
|
output: 25,
|
|
284
|
+
cachedInput: 0.5,
|
|
285
|
+
cacheCreationInput: 6.25,
|
|
228
286
|
maxInputTokens: 1e6
|
|
229
287
|
},
|
|
230
288
|
"claude-sonnet-4-6": {
|
|
231
289
|
input: 3,
|
|
232
290
|
output: 15,
|
|
291
|
+
cachedInput: 0.3,
|
|
292
|
+
cacheCreationInput: 3.75,
|
|
233
293
|
maxInputTokens: 1e6
|
|
234
294
|
},
|
|
235
295
|
"claude-haiku-4-5": {
|
|
236
296
|
input: 1,
|
|
237
297
|
output: 5,
|
|
298
|
+
cachedInput: 0.1,
|
|
299
|
+
cacheCreationInput: 1.25,
|
|
238
300
|
maxInputTokens: 2e5
|
|
239
301
|
},
|
|
240
302
|
"gemini-2.5-pro": {
|
|
241
303
|
input: 1.25,
|
|
242
304
|
output: 10,
|
|
305
|
+
cachedInput: 0.125,
|
|
243
306
|
maxInputTokens: 1048576
|
|
244
307
|
},
|
|
245
308
|
"gemini-2.5-flash": {
|
|
246
309
|
input: 0.3,
|
|
247
310
|
output: 2.5,
|
|
311
|
+
cachedInput: 0.03,
|
|
248
312
|
maxInputTokens: 1048576
|
|
249
313
|
},
|
|
250
314
|
"deepseek-chat": {
|
|
251
315
|
input: 0.28,
|
|
252
316
|
output: 0.42,
|
|
317
|
+
cachedInput: 0.028,
|
|
253
318
|
maxInputTokens: 131072
|
|
254
319
|
},
|
|
255
320
|
"deepseek-reasoner": {
|
|
256
321
|
input: 0.28,
|
|
257
322
|
output: 0.42,
|
|
323
|
+
cachedInput: 0.028,
|
|
258
324
|
maxInputTokens: 131072
|
|
259
325
|
},
|
|
260
326
|
"claude-opus-4-5": {
|
|
261
327
|
input: 5,
|
|
262
328
|
output: 25,
|
|
329
|
+
cachedInput: 0.5,
|
|
330
|
+
cacheCreationInput: 6.25,
|
|
263
331
|
maxInputTokens: 2e5
|
|
264
332
|
},
|
|
265
333
|
"claude-opus-4-7": {
|
|
266
334
|
input: 5,
|
|
267
335
|
output: 25,
|
|
336
|
+
cachedInput: 0.5,
|
|
337
|
+
cacheCreationInput: 6.25,
|
|
268
338
|
maxInputTokens: 1e6
|
|
269
339
|
},
|
|
270
340
|
"claude-opus-4-1": {
|
|
271
341
|
input: 15,
|
|
272
342
|
output: 75,
|
|
343
|
+
cachedInput: 1.5,
|
|
344
|
+
cacheCreationInput: 18.75,
|
|
273
345
|
maxInputTokens: 2e5
|
|
274
346
|
},
|
|
275
347
|
"claude-sonnet-4-5": {
|
|
276
348
|
input: 3,
|
|
277
349
|
output: 15,
|
|
350
|
+
cachedInput: 0.3,
|
|
351
|
+
cacheCreationInput: 3.75,
|
|
278
352
|
maxInputTokens: 2e5
|
|
279
353
|
},
|
|
280
354
|
"gpt-oss-120b": {
|
|
@@ -365,36 +439,43 @@ var prices_default = {
|
|
|
365
439
|
"gpt-4.1": {
|
|
366
440
|
input: 2,
|
|
367
441
|
output: 8,
|
|
442
|
+
cachedInput: 0.5,
|
|
368
443
|
maxInputTokens: 1047576
|
|
369
444
|
},
|
|
370
445
|
"gpt-4.1-2025-04-14": {
|
|
371
446
|
input: 2,
|
|
372
447
|
output: 8,
|
|
448
|
+
cachedInput: 0.5,
|
|
373
449
|
maxInputTokens: 1047576
|
|
374
450
|
},
|
|
375
451
|
"gpt-4.1-mini": {
|
|
376
452
|
input: 0.4,
|
|
377
453
|
output: 1.6,
|
|
454
|
+
cachedInput: 0.1,
|
|
378
455
|
maxInputTokens: 1047576
|
|
379
456
|
},
|
|
380
457
|
"gpt-4.1-mini-2025-04-14": {
|
|
381
458
|
input: 0.4,
|
|
382
459
|
output: 1.6,
|
|
460
|
+
cachedInput: 0.1,
|
|
383
461
|
maxInputTokens: 1047576
|
|
384
462
|
},
|
|
385
463
|
"gpt-4.1-nano": {
|
|
386
464
|
input: 0.1,
|
|
387
465
|
output: 0.4,
|
|
466
|
+
cachedInput: 0.025,
|
|
388
467
|
maxInputTokens: 1047576
|
|
389
468
|
},
|
|
390
469
|
"gpt-4.1-nano-2025-04-14": {
|
|
391
470
|
input: 0.1,
|
|
392
471
|
output: 0.4,
|
|
472
|
+
cachedInput: 0.025,
|
|
393
473
|
maxInputTokens: 1047576
|
|
394
474
|
},
|
|
395
475
|
"gpt-4.5-preview": {
|
|
396
476
|
input: 75,
|
|
397
477
|
output: 150,
|
|
478
|
+
cachedInput: 37.5,
|
|
398
479
|
maxInputTokens: 128e3
|
|
399
480
|
},
|
|
400
481
|
"gpt-4o-2024-05-13": {
|
|
@@ -405,11 +486,13 @@ var prices_default = {
|
|
|
405
486
|
"gpt-4o-2024-08-06": {
|
|
406
487
|
input: 2.5,
|
|
407
488
|
output: 10,
|
|
489
|
+
cachedInput: 1.25,
|
|
408
490
|
maxInputTokens: 128e3
|
|
409
491
|
},
|
|
410
492
|
"gpt-4o-2024-11-20": {
|
|
411
493
|
input: 2.5,
|
|
412
494
|
output: 10,
|
|
495
|
+
cachedInput: 1.25,
|
|
413
496
|
maxInputTokens: 128e3
|
|
414
497
|
},
|
|
415
498
|
"gpt-audio-2025-08-28": {
|
|
@@ -435,6 +518,7 @@ var prices_default = {
|
|
|
435
518
|
"gpt-4o-mini-2024-07-18": {
|
|
436
519
|
input: 0.15,
|
|
437
520
|
output: 0.6,
|
|
521
|
+
cachedInput: 0.075,
|
|
438
522
|
maxInputTokens: 128e3
|
|
439
523
|
},
|
|
440
524
|
"gpt-4o-mini-audio-preview-2024-12-17": {
|
|
@@ -445,21 +529,25 @@ var prices_default = {
|
|
|
445
529
|
"gpt-4o-mini-realtime-preview-2024-12-17": {
|
|
446
530
|
input: 0.6,
|
|
447
531
|
output: 2.4,
|
|
532
|
+
cachedInput: 0.3,
|
|
448
533
|
maxInputTokens: 128e3
|
|
449
534
|
},
|
|
450
535
|
"gpt-realtime-2025-08-28": {
|
|
451
536
|
input: 4,
|
|
452
537
|
output: 16,
|
|
538
|
+
cachedInput: 0.4,
|
|
453
539
|
maxInputTokens: 32e3
|
|
454
540
|
},
|
|
455
541
|
"gpt-realtime-1.5-2026-02-23": {
|
|
456
542
|
input: 4,
|
|
457
543
|
output: 16,
|
|
544
|
+
cachedInput: 4,
|
|
458
545
|
maxInputTokens: 32e3
|
|
459
546
|
},
|
|
460
547
|
"gpt-realtime-mini-2025-10-06": {
|
|
461
548
|
input: 0.6,
|
|
462
549
|
output: 2.4,
|
|
550
|
+
cachedInput: 0.06,
|
|
463
551
|
maxInputTokens: 128e3
|
|
464
552
|
},
|
|
465
553
|
"gpt-4o-mini-transcribe": {
|
|
@@ -470,11 +558,13 @@ var prices_default = {
|
|
|
470
558
|
"gpt-4o-realtime-preview-2024-10-01": {
|
|
471
559
|
input: 5,
|
|
472
560
|
output: 20,
|
|
561
|
+
cachedInput: 2.5,
|
|
473
562
|
maxInputTokens: 128e3
|
|
474
563
|
},
|
|
475
564
|
"gpt-4o-realtime-preview-2024-12-17": {
|
|
476
565
|
input: 5,
|
|
477
566
|
output: 20,
|
|
567
|
+
cachedInput: 2.5,
|
|
478
568
|
maxInputTokens: 128e3
|
|
479
569
|
},
|
|
480
570
|
"gpt-4o-transcribe": {
|
|
@@ -490,51 +580,61 @@ var prices_default = {
|
|
|
490
580
|
"gpt-5.1-2025-11-13": {
|
|
491
581
|
input: 1.25,
|
|
492
582
|
output: 10,
|
|
583
|
+
cachedInput: 0.125,
|
|
493
584
|
maxInputTokens: 272e3
|
|
494
585
|
},
|
|
495
586
|
"gpt-5.1-chat-2025-11-13": {
|
|
496
587
|
input: 1.25,
|
|
497
588
|
output: 10,
|
|
589
|
+
cachedInput: 0.125,
|
|
498
590
|
maxInputTokens: 128e3
|
|
499
591
|
},
|
|
500
592
|
"gpt-5.1-codex-2025-11-13": {
|
|
501
593
|
input: 1.25,
|
|
502
594
|
output: 10,
|
|
595
|
+
cachedInput: 0.125,
|
|
503
596
|
maxInputTokens: 272e3
|
|
504
597
|
},
|
|
505
598
|
"gpt-5.1-codex-mini-2025-11-13": {
|
|
506
599
|
input: 0.25,
|
|
507
600
|
output: 2,
|
|
601
|
+
cachedInput: 0.025,
|
|
508
602
|
maxInputTokens: 272e3
|
|
509
603
|
},
|
|
510
604
|
"gpt-5-2025-08-07": {
|
|
511
605
|
input: 1.25,
|
|
512
606
|
output: 10,
|
|
607
|
+
cachedInput: 0.125,
|
|
513
608
|
maxInputTokens: 272e3
|
|
514
609
|
},
|
|
515
610
|
"gpt-5-chat": {
|
|
516
611
|
input: 1.25,
|
|
517
612
|
output: 10,
|
|
613
|
+
cachedInput: 0.125,
|
|
518
614
|
maxInputTokens: 128e3
|
|
519
615
|
},
|
|
520
616
|
"gpt-5-chat-latest": {
|
|
521
617
|
input: 1.25,
|
|
522
618
|
output: 10,
|
|
619
|
+
cachedInput: 0.125,
|
|
523
620
|
maxInputTokens: 128e3
|
|
524
621
|
},
|
|
525
622
|
"gpt-5-codex": {
|
|
526
623
|
input: 1.25,
|
|
527
624
|
output: 10,
|
|
625
|
+
cachedInput: 0.125,
|
|
528
626
|
maxInputTokens: 272e3
|
|
529
627
|
},
|
|
530
628
|
"gpt-5-mini-2025-08-07": {
|
|
531
629
|
input: 0.25,
|
|
532
630
|
output: 2,
|
|
631
|
+
cachedInput: 0.025,
|
|
533
632
|
maxInputTokens: 272e3
|
|
534
633
|
},
|
|
535
634
|
"gpt-5-nano-2025-08-07": {
|
|
536
635
|
input: 0.05,
|
|
537
636
|
output: 0.4,
|
|
637
|
+
cachedInput: 5e-3,
|
|
538
638
|
maxInputTokens: 272e3
|
|
539
639
|
},
|
|
540
640
|
"gpt-5-pro": {
|
|
@@ -545,61 +645,73 @@ var prices_default = {
|
|
|
545
645
|
"gpt-5.1": {
|
|
546
646
|
input: 1.25,
|
|
547
647
|
output: 10,
|
|
648
|
+
cachedInput: 0.125,
|
|
548
649
|
maxInputTokens: 272e3
|
|
549
650
|
},
|
|
550
651
|
"gpt-5.1-chat": {
|
|
551
652
|
input: 1.25,
|
|
552
653
|
output: 10,
|
|
654
|
+
cachedInput: 0.125,
|
|
553
655
|
maxInputTokens: 128e3
|
|
554
656
|
},
|
|
555
657
|
"gpt-5.1-codex": {
|
|
556
658
|
input: 1.25,
|
|
557
659
|
output: 10,
|
|
660
|
+
cachedInput: 0.125,
|
|
558
661
|
maxInputTokens: 272e3
|
|
559
662
|
},
|
|
560
663
|
"gpt-5.1-codex-max": {
|
|
561
664
|
input: 1.25,
|
|
562
665
|
output: 10,
|
|
666
|
+
cachedInput: 0.125,
|
|
563
667
|
maxInputTokens: 272e3
|
|
564
668
|
},
|
|
565
669
|
"gpt-5.1-codex-mini": {
|
|
566
670
|
input: 0.25,
|
|
567
671
|
output: 2,
|
|
672
|
+
cachedInput: 0.025,
|
|
568
673
|
maxInputTokens: 272e3
|
|
569
674
|
},
|
|
570
675
|
"gpt-5.2": {
|
|
571
676
|
input: 1.75,
|
|
572
677
|
output: 14,
|
|
678
|
+
cachedInput: 0.175,
|
|
573
679
|
maxInputTokens: 272e3
|
|
574
680
|
},
|
|
575
681
|
"gpt-5.2-2025-12-11": {
|
|
576
682
|
input: 1.75,
|
|
577
683
|
output: 14,
|
|
684
|
+
cachedInput: 0.175,
|
|
578
685
|
maxInputTokens: 272e3
|
|
579
686
|
},
|
|
580
687
|
"gpt-5.2-chat": {
|
|
581
688
|
input: 1.75,
|
|
582
689
|
output: 14,
|
|
690
|
+
cachedInput: 0.175,
|
|
583
691
|
maxInputTokens: 128e3
|
|
584
692
|
},
|
|
585
693
|
"gpt-5.2-chat-2025-12-11": {
|
|
586
694
|
input: 1.75,
|
|
587
695
|
output: 14,
|
|
696
|
+
cachedInput: 0.175,
|
|
588
697
|
maxInputTokens: 128e3
|
|
589
698
|
},
|
|
590
699
|
"gpt-5.2-codex": {
|
|
591
700
|
input: 1.75,
|
|
592
701
|
output: 14,
|
|
702
|
+
cachedInput: 0.175,
|
|
593
703
|
maxInputTokens: 272e3
|
|
594
704
|
},
|
|
595
705
|
"gpt-5.3-chat": {
|
|
596
706
|
input: 1.75,
|
|
597
707
|
output: 14,
|
|
708
|
+
cachedInput: 0.175,
|
|
598
709
|
maxInputTokens: 128e3
|
|
599
710
|
},
|
|
600
711
|
"gpt-5.3-codex": {
|
|
601
712
|
input: 1.75,
|
|
602
713
|
output: 14,
|
|
714
|
+
cachedInput: 0.175,
|
|
603
715
|
maxInputTokens: 272e3
|
|
604
716
|
},
|
|
605
717
|
"gpt-5.2-pro": {
|
|
@@ -615,71 +727,85 @@ var prices_default = {
|
|
|
615
727
|
"gpt-5.4": {
|
|
616
728
|
input: 2.5,
|
|
617
729
|
output: 15,
|
|
730
|
+
cachedInput: 0.25,
|
|
618
731
|
maxInputTokens: 105e4
|
|
619
732
|
},
|
|
620
733
|
"gpt-5.4-2026-03-05": {
|
|
621
734
|
input: 2.5,
|
|
622
735
|
output: 15,
|
|
736
|
+
cachedInput: 0.25,
|
|
623
737
|
maxInputTokens: 105e4
|
|
624
738
|
},
|
|
625
739
|
"gpt-5.4-pro": {
|
|
626
740
|
input: 30,
|
|
627
741
|
output: 180,
|
|
742
|
+
cachedInput: 3,
|
|
628
743
|
maxInputTokens: 105e4
|
|
629
744
|
},
|
|
630
745
|
"gpt-5.4-pro-2026-03-05": {
|
|
631
746
|
input: 30,
|
|
632
747
|
output: 180,
|
|
748
|
+
cachedInput: 3,
|
|
633
749
|
maxInputTokens: 105e4
|
|
634
750
|
},
|
|
635
751
|
"gpt-5.4-mini": {
|
|
636
752
|
input: 0.75,
|
|
637
753
|
output: 4.5,
|
|
754
|
+
cachedInput: 0.075,
|
|
638
755
|
maxInputTokens: 272e3
|
|
639
756
|
},
|
|
640
757
|
"gpt-5.4-nano": {
|
|
641
758
|
input: 0.2,
|
|
642
759
|
output: 1.25,
|
|
760
|
+
cachedInput: 0.02,
|
|
643
761
|
maxInputTokens: 272e3
|
|
644
762
|
},
|
|
645
763
|
"o1-2024-12-17": {
|
|
646
764
|
input: 15,
|
|
647
765
|
output: 60,
|
|
766
|
+
cachedInput: 7.5,
|
|
648
767
|
maxInputTokens: 2e5
|
|
649
768
|
},
|
|
650
769
|
"o1-mini": {
|
|
651
770
|
input: 1.21,
|
|
652
771
|
output: 4.84,
|
|
772
|
+
cachedInput: 0.605,
|
|
653
773
|
maxInputTokens: 128e3
|
|
654
774
|
},
|
|
655
775
|
"o1-mini-2024-09-12": {
|
|
656
776
|
input: 1.1,
|
|
657
777
|
output: 4.4,
|
|
778
|
+
cachedInput: 0.55,
|
|
658
779
|
maxInputTokens: 128e3
|
|
659
780
|
},
|
|
660
781
|
"o1-preview": {
|
|
661
782
|
input: 15,
|
|
662
783
|
output: 60,
|
|
784
|
+
cachedInput: 7.5,
|
|
663
785
|
maxInputTokens: 128e3
|
|
664
786
|
},
|
|
665
787
|
"o1-preview-2024-09-12": {
|
|
666
788
|
input: 15,
|
|
667
789
|
output: 60,
|
|
790
|
+
cachedInput: 7.5,
|
|
668
791
|
maxInputTokens: 128e3
|
|
669
792
|
},
|
|
670
793
|
"o3-2025-04-16": {
|
|
671
794
|
input: 2,
|
|
672
795
|
output: 8,
|
|
796
|
+
cachedInput: 0.5,
|
|
673
797
|
maxInputTokens: 2e5
|
|
674
798
|
},
|
|
675
799
|
"o3-mini": {
|
|
676
800
|
input: 1.1,
|
|
677
801
|
output: 4.4,
|
|
802
|
+
cachedInput: 0.55,
|
|
678
803
|
maxInputTokens: 2e5
|
|
679
804
|
},
|
|
680
805
|
"o3-mini-2025-01-31": {
|
|
681
806
|
input: 1.1,
|
|
682
807
|
output: 4.4,
|
|
808
|
+
cachedInput: 0.55,
|
|
683
809
|
maxInputTokens: 2e5
|
|
684
810
|
},
|
|
685
811
|
"o3-pro": {
|
|
@@ -695,11 +821,13 @@ var prices_default = {
|
|
|
695
821
|
"o4-mini": {
|
|
696
822
|
input: 1.1,
|
|
697
823
|
output: 4.4,
|
|
824
|
+
cachedInput: 0.275,
|
|
698
825
|
maxInputTokens: 2e5
|
|
699
826
|
},
|
|
700
827
|
"o4-mini-2025-04-16": {
|
|
701
828
|
input: 1.1,
|
|
702
829
|
output: 4.4,
|
|
830
|
+
cachedInput: 0.275,
|
|
703
831
|
maxInputTokens: 2e5
|
|
704
832
|
},
|
|
705
833
|
"deepseek-v3.2": {
|
|
@@ -720,6 +848,7 @@ var prices_default = {
|
|
|
720
848
|
"deepseek-v3": {
|
|
721
849
|
input: 0.27,
|
|
722
850
|
output: 1.1,
|
|
851
|
+
cachedInput: 0.07,
|
|
723
852
|
maxInputTokens: 65536
|
|
724
853
|
},
|
|
725
854
|
"deepseek-v3-0324": {
|
|
@@ -735,76 +864,105 @@ var prices_default = {
|
|
|
735
864
|
"claude-haiku-4-5-20251001": {
|
|
736
865
|
input: 1,
|
|
737
866
|
output: 5,
|
|
867
|
+
cachedInput: 0.1,
|
|
868
|
+
cacheCreationInput: 1.25,
|
|
738
869
|
maxInputTokens: 2e5
|
|
739
870
|
},
|
|
740
871
|
"claude-3-7-sonnet-20250219": {
|
|
741
872
|
input: 3,
|
|
742
873
|
output: 15,
|
|
874
|
+
cachedInput: 0.3,
|
|
875
|
+
cacheCreationInput: 3.75,
|
|
743
876
|
maxInputTokens: 2e5
|
|
744
877
|
},
|
|
745
878
|
"claude-3-haiku-20240307": {
|
|
746
879
|
input: 0.25,
|
|
747
880
|
output: 1.25,
|
|
881
|
+
cachedInput: 0.03,
|
|
882
|
+
cacheCreationInput: 0.3,
|
|
748
883
|
maxInputTokens: 2e5
|
|
749
884
|
},
|
|
750
885
|
"claude-3-opus-20240229": {
|
|
751
886
|
input: 15,
|
|
752
887
|
output: 75,
|
|
888
|
+
cachedInput: 1.5,
|
|
889
|
+
cacheCreationInput: 18.75,
|
|
753
890
|
maxInputTokens: 2e5
|
|
754
891
|
},
|
|
755
892
|
"claude-4-opus-20250514": {
|
|
756
893
|
input: 15,
|
|
757
894
|
output: 75,
|
|
895
|
+
cachedInput: 1.5,
|
|
896
|
+
cacheCreationInput: 18.75,
|
|
758
897
|
maxInputTokens: 2e5
|
|
759
898
|
},
|
|
760
899
|
"claude-4-sonnet-20250514": {
|
|
761
900
|
input: 3,
|
|
762
901
|
output: 15,
|
|
902
|
+
cachedInput: 0.3,
|
|
903
|
+
cacheCreationInput: 3.75,
|
|
763
904
|
maxInputTokens: 1e6
|
|
764
905
|
},
|
|
765
906
|
"claude-sonnet-4-5-20250929": {
|
|
766
907
|
input: 3,
|
|
767
908
|
output: 15,
|
|
909
|
+
cachedInput: 0.3,
|
|
910
|
+
cacheCreationInput: 3.75,
|
|
768
911
|
maxInputTokens: 2e5
|
|
769
912
|
},
|
|
770
913
|
"claude-sonnet-4-5-20250929-v1:0": {
|
|
771
914
|
input: 3,
|
|
772
915
|
output: 15,
|
|
916
|
+
cachedInput: 0.3,
|
|
917
|
+
cacheCreationInput: 3.75,
|
|
773
918
|
maxInputTokens: 2e5
|
|
774
919
|
},
|
|
775
920
|
"claude-opus-4-1-20250805": {
|
|
776
921
|
input: 15,
|
|
777
922
|
output: 75,
|
|
923
|
+
cachedInput: 1.5,
|
|
924
|
+
cacheCreationInput: 18.75,
|
|
778
925
|
maxInputTokens: 2e5
|
|
779
926
|
},
|
|
780
927
|
"claude-opus-4-20250514": {
|
|
781
928
|
input: 15,
|
|
782
929
|
output: 75,
|
|
930
|
+
cachedInput: 1.5,
|
|
931
|
+
cacheCreationInput: 18.75,
|
|
783
932
|
maxInputTokens: 2e5
|
|
784
933
|
},
|
|
785
934
|
"claude-opus-4-5-20251101": {
|
|
786
935
|
input: 5,
|
|
787
936
|
output: 25,
|
|
937
|
+
cachedInput: 0.5,
|
|
938
|
+
cacheCreationInput: 6.25,
|
|
788
939
|
maxInputTokens: 2e5
|
|
789
940
|
},
|
|
790
941
|
"claude-opus-4-6-20260205": {
|
|
791
942
|
input: 5,
|
|
792
943
|
output: 25,
|
|
944
|
+
cachedInput: 0.5,
|
|
945
|
+
cacheCreationInput: 6.25,
|
|
793
946
|
maxInputTokens: 1e6
|
|
794
947
|
},
|
|
795
948
|
"claude-opus-4-7-20260416": {
|
|
796
949
|
input: 5,
|
|
797
950
|
output: 25,
|
|
951
|
+
cachedInput: 0.5,
|
|
952
|
+
cacheCreationInput: 6.25,
|
|
798
953
|
maxInputTokens: 1e6
|
|
799
954
|
},
|
|
800
955
|
"claude-sonnet-4-20250514": {
|
|
801
956
|
input: 3,
|
|
802
957
|
output: 15,
|
|
958
|
+
cachedInput: 0.3,
|
|
959
|
+
cacheCreationInput: 3.75,
|
|
803
960
|
maxInputTokens: 1e6
|
|
804
961
|
},
|
|
805
962
|
"codex-mini-latest": {
|
|
806
963
|
input: 1.5,
|
|
807
964
|
output: 6,
|
|
965
|
+
cachedInput: 0.375,
|
|
808
966
|
maxInputTokens: 2e5
|
|
809
967
|
},
|
|
810
968
|
"deepseek-ai/deepseek-r1": {
|
|
@@ -854,6 +1012,7 @@ var prices_default = {
|
|
|
854
1012
|
"deepseek-ai/deepseek-v3.1-terminus": {
|
|
855
1013
|
input: 0.27,
|
|
856
1014
|
output: 1,
|
|
1015
|
+
cachedInput: 0.216,
|
|
857
1016
|
maxInputTokens: 163840
|
|
858
1017
|
},
|
|
859
1018
|
"deepseek-coder": {
|
|
@@ -864,26 +1023,31 @@ var prices_default = {
|
|
|
864
1023
|
"gemini-2.0-flash": {
|
|
865
1024
|
input: 0.1,
|
|
866
1025
|
output: 0.4,
|
|
1026
|
+
cachedInput: 0.025,
|
|
867
1027
|
maxInputTokens: 1048576
|
|
868
1028
|
},
|
|
869
1029
|
"gemini-2.0-flash-001": {
|
|
870
1030
|
input: 0.1,
|
|
871
1031
|
output: 0.4,
|
|
1032
|
+
cachedInput: 0.025,
|
|
872
1033
|
maxInputTokens: 1048576
|
|
873
1034
|
},
|
|
874
1035
|
"gemini-2.0-flash-lite": {
|
|
875
1036
|
input: 0.075,
|
|
876
1037
|
output: 0.3,
|
|
1038
|
+
cachedInput: 0.01875,
|
|
877
1039
|
maxInputTokens: 1048576
|
|
878
1040
|
},
|
|
879
1041
|
"gemini-2.0-flash-lite-001": {
|
|
880
1042
|
input: 0.075,
|
|
881
1043
|
output: 0.3,
|
|
1044
|
+
cachedInput: 0.01875,
|
|
882
1045
|
maxInputTokens: 1048576
|
|
883
1046
|
},
|
|
884
1047
|
"gemini-2.5-flash-image": {
|
|
885
1048
|
input: 0.3,
|
|
886
1049
|
output: 2.5,
|
|
1050
|
+
cachedInput: 0.03,
|
|
887
1051
|
maxInputTokens: 32768
|
|
888
1052
|
},
|
|
889
1053
|
"gemini-3-pro-image-preview": {
|
|
@@ -899,51 +1063,61 @@ var prices_default = {
|
|
|
899
1063
|
"gemini-3.1-flash-lite-preview": {
|
|
900
1064
|
input: 0.25,
|
|
901
1065
|
output: 1.5,
|
|
1066
|
+
cachedInput: 0.025,
|
|
902
1067
|
maxInputTokens: 1048576
|
|
903
1068
|
},
|
|
904
1069
|
"gemini-2.5-flash-lite": {
|
|
905
1070
|
input: 0.1,
|
|
906
1071
|
output: 0.4,
|
|
1072
|
+
cachedInput: 0.01,
|
|
907
1073
|
maxInputTokens: 1048576
|
|
908
1074
|
},
|
|
909
1075
|
"gemini-2.5-flash-lite-preview-09-2025": {
|
|
910
1076
|
input: 0.1,
|
|
911
1077
|
output: 0.4,
|
|
1078
|
+
cachedInput: 0.01,
|
|
912
1079
|
maxInputTokens: 1048576
|
|
913
1080
|
},
|
|
914
1081
|
"gemini-2.5-flash-preview-09-2025": {
|
|
915
1082
|
input: 0.3,
|
|
916
1083
|
output: 2.5,
|
|
1084
|
+
cachedInput: 0.075,
|
|
917
1085
|
maxInputTokens: 1048576
|
|
918
1086
|
},
|
|
919
1087
|
"gemini-live-2.5-flash-preview-native-audio-09-2025": {
|
|
920
1088
|
input: 0.3,
|
|
921
1089
|
output: 2,
|
|
1090
|
+
cachedInput: 0.075,
|
|
922
1091
|
maxInputTokens: 1048576
|
|
923
1092
|
},
|
|
924
1093
|
"gemini-2.5-flash-lite-preview-06-17": {
|
|
925
1094
|
input: 0.1,
|
|
926
1095
|
output: 0.4,
|
|
1096
|
+
cachedInput: 0.025,
|
|
927
1097
|
maxInputTokens: 1048576
|
|
928
1098
|
},
|
|
929
1099
|
"gemini-3-pro-preview": {
|
|
930
1100
|
input: 2,
|
|
931
1101
|
output: 12,
|
|
1102
|
+
cachedInput: 0.2,
|
|
932
1103
|
maxInputTokens: 1048576
|
|
933
1104
|
},
|
|
934
1105
|
"gemini-3.1-pro-preview": {
|
|
935
1106
|
input: 2,
|
|
936
1107
|
output: 12,
|
|
1108
|
+
cachedInput: 0.2,
|
|
937
1109
|
maxInputTokens: 1048576
|
|
938
1110
|
},
|
|
939
1111
|
"gemini-3.1-pro-preview-customtools": {
|
|
940
1112
|
input: 2,
|
|
941
1113
|
output: 12,
|
|
1114
|
+
cachedInput: 0.2,
|
|
942
1115
|
maxInputTokens: 1048576
|
|
943
1116
|
},
|
|
944
1117
|
"gemini-3-flash-preview": {
|
|
945
1118
|
input: 0.5,
|
|
946
1119
|
output: 3,
|
|
1120
|
+
cachedInput: 0.05,
|
|
947
1121
|
maxInputTokens: 1048576
|
|
948
1122
|
},
|
|
949
1123
|
"gemini-robotics-er-1.5-preview": {
|
|
@@ -959,11 +1133,13 @@ var prices_default = {
|
|
|
959
1133
|
"gemini-flash-latest": {
|
|
960
1134
|
input: 0.3,
|
|
961
1135
|
output: 2.5,
|
|
1136
|
+
cachedInput: 0.03,
|
|
962
1137
|
maxInputTokens: 1048576
|
|
963
1138
|
},
|
|
964
1139
|
"gemini-flash-lite-latest": {
|
|
965
1140
|
input: 0.1,
|
|
966
1141
|
output: 0.4,
|
|
1142
|
+
cachedInput: 0.01,
|
|
967
1143
|
maxInputTokens: 1048576
|
|
968
1144
|
},
|
|
969
1145
|
"gemini-gemma-2-27b-it": {
|
|
@@ -1039,39 +1215,47 @@ var prices_default = {
|
|
|
1039
1215
|
"gpt-4o-mini-realtime-preview": {
|
|
1040
1216
|
input: 0.6,
|
|
1041
1217
|
output: 2.4,
|
|
1218
|
+
cachedInput: 0.3,
|
|
1042
1219
|
maxInputTokens: 128e3
|
|
1043
1220
|
},
|
|
1044
1221
|
"gpt-4o-realtime-preview": {
|
|
1045
1222
|
input: 5,
|
|
1046
1223
|
output: 20,
|
|
1224
|
+
cachedInput: 2.5,
|
|
1047
1225
|
maxInputTokens: 128e3
|
|
1048
1226
|
},
|
|
1049
1227
|
"gpt-4o-realtime-preview-2025-06-03": {
|
|
1050
1228
|
input: 5,
|
|
1051
1229
|
output: 20,
|
|
1230
|
+
cachedInput: 2.5,
|
|
1052
1231
|
maxInputTokens: 128e3
|
|
1053
1232
|
},
|
|
1054
1233
|
"gpt-image-1.5": {
|
|
1055
1234
|
input: 5,
|
|
1056
|
-
output: 10
|
|
1235
|
+
output: 10,
|
|
1236
|
+
cachedInput: 1.25
|
|
1057
1237
|
},
|
|
1058
1238
|
"gpt-image-1.5-2025-12-16": {
|
|
1059
1239
|
input: 5,
|
|
1060
|
-
output: 10
|
|
1240
|
+
output: 10,
|
|
1241
|
+
cachedInput: 1.25
|
|
1061
1242
|
},
|
|
1062
1243
|
"gpt-5.1-chat-latest": {
|
|
1063
1244
|
input: 1.25,
|
|
1064
1245
|
output: 10,
|
|
1246
|
+
cachedInput: 0.125,
|
|
1065
1247
|
maxInputTokens: 128e3
|
|
1066
1248
|
},
|
|
1067
1249
|
"gpt-5.2-chat-latest": {
|
|
1068
1250
|
input: 1.75,
|
|
1069
1251
|
output: 14,
|
|
1252
|
+
cachedInput: 0.175,
|
|
1070
1253
|
maxInputTokens: 128e3
|
|
1071
1254
|
},
|
|
1072
1255
|
"gpt-5.3-chat-latest": {
|
|
1073
1256
|
input: 1.75,
|
|
1074
1257
|
output: 14,
|
|
1258
|
+
cachedInput: 0.175,
|
|
1075
1259
|
maxInputTokens: 128e3
|
|
1076
1260
|
},
|
|
1077
1261
|
"gpt-5-pro-2025-10-06": {
|
|
@@ -1082,11 +1266,13 @@ var prices_default = {
|
|
|
1082
1266
|
"gpt-realtime": {
|
|
1083
1267
|
input: 4,
|
|
1084
1268
|
output: 16,
|
|
1269
|
+
cachedInput: 0.4,
|
|
1085
1270
|
maxInputTokens: 32e3
|
|
1086
1271
|
},
|
|
1087
1272
|
"gpt-realtime-1.5": {
|
|
1088
1273
|
input: 4,
|
|
1089
1274
|
output: 16,
|
|
1275
|
+
cachedInput: 0.4,
|
|
1090
1276
|
maxInputTokens: 32e3
|
|
1091
1277
|
},
|
|
1092
1278
|
"gpt-realtime-mini": {
|
|
@@ -1133,6 +1319,7 @@ var prices_default = {
|
|
|
1133
1319
|
o1: {
|
|
1134
1320
|
input: 15,
|
|
1135
1321
|
output: 60,
|
|
1322
|
+
cachedInput: 7.5,
|
|
1136
1323
|
maxInputTokens: 2e5
|
|
1137
1324
|
},
|
|
1138
1325
|
"o1-pro": {
|
|
@@ -1148,6 +1335,7 @@ var prices_default = {
|
|
|
1148
1335
|
o3: {
|
|
1149
1336
|
input: 2,
|
|
1150
1337
|
output: 8,
|
|
1338
|
+
cachedInput: 0.5,
|
|
1151
1339
|
maxInputTokens: 2e5
|
|
1152
1340
|
},
|
|
1153
1341
|
"gpt-oss-20b": {
|
|
@@ -1172,6 +1360,8 @@ var prices_default = {
|
|
|
1172
1360
|
"claude-haiku-4-5@20251001": {
|
|
1173
1361
|
input: 1,
|
|
1174
1362
|
output: 5,
|
|
1363
|
+
cachedInput: 0.1,
|
|
1364
|
+
cacheCreationInput: 1.25,
|
|
1175
1365
|
maxInputTokens: 2e5
|
|
1176
1366
|
},
|
|
1177
1367
|
"claude-3-5-sonnet": {
|
|
@@ -1187,6 +1377,8 @@ var prices_default = {
|
|
|
1187
1377
|
"claude-3-7-sonnet@20250219": {
|
|
1188
1378
|
input: 3,
|
|
1189
1379
|
output: 15,
|
|
1380
|
+
cachedInput: 0.3,
|
|
1381
|
+
cacheCreationInput: 3.75,
|
|
1190
1382
|
maxInputTokens: 2e5
|
|
1191
1383
|
},
|
|
1192
1384
|
"claude-3-haiku": {
|
|
@@ -1222,46 +1414,64 @@ var prices_default = {
|
|
|
1222
1414
|
"claude-opus-4": {
|
|
1223
1415
|
input: 15,
|
|
1224
1416
|
output: 75,
|
|
1417
|
+
cachedInput: 1.5,
|
|
1418
|
+
cacheCreationInput: 18.75,
|
|
1225
1419
|
maxInputTokens: 2e5
|
|
1226
1420
|
},
|
|
1227
1421
|
"claude-opus-4-1@20250805": {
|
|
1228
1422
|
input: 15,
|
|
1229
1423
|
output: 75,
|
|
1424
|
+
cachedInput: 1.5,
|
|
1425
|
+
cacheCreationInput: 18.75,
|
|
1230
1426
|
maxInputTokens: 2e5
|
|
1231
1427
|
},
|
|
1232
1428
|
"claude-opus-4-5@20251101": {
|
|
1233
1429
|
input: 5,
|
|
1234
1430
|
output: 25,
|
|
1431
|
+
cachedInput: 0.5,
|
|
1432
|
+
cacheCreationInput: 6.25,
|
|
1235
1433
|
maxInputTokens: 2e5
|
|
1236
1434
|
},
|
|
1237
1435
|
"claude-opus-4-6@default": {
|
|
1238
1436
|
input: 5,
|
|
1239
1437
|
output: 25,
|
|
1438
|
+
cachedInput: 0.5,
|
|
1439
|
+
cacheCreationInput: 6.25,
|
|
1240
1440
|
maxInputTokens: 1e6
|
|
1241
1441
|
},
|
|
1242
1442
|
"claude-opus-4-7@default": {
|
|
1243
1443
|
input: 5,
|
|
1244
1444
|
output: 25,
|
|
1445
|
+
cachedInput: 0.5,
|
|
1446
|
+
cacheCreationInput: 6.25,
|
|
1245
1447
|
maxInputTokens: 1e6
|
|
1246
1448
|
},
|
|
1247
1449
|
"claude-sonnet-4-5@20250929": {
|
|
1248
1450
|
input: 3,
|
|
1249
1451
|
output: 15,
|
|
1452
|
+
cachedInput: 0.3,
|
|
1453
|
+
cacheCreationInput: 3.75,
|
|
1250
1454
|
maxInputTokens: 2e5
|
|
1251
1455
|
},
|
|
1252
1456
|
"claude-opus-4@20250514": {
|
|
1253
1457
|
input: 15,
|
|
1254
1458
|
output: 75,
|
|
1459
|
+
cachedInput: 1.5,
|
|
1460
|
+
cacheCreationInput: 18.75,
|
|
1255
1461
|
maxInputTokens: 2e5
|
|
1256
1462
|
},
|
|
1257
1463
|
"claude-sonnet-4": {
|
|
1258
1464
|
input: 3,
|
|
1259
1465
|
output: 15,
|
|
1466
|
+
cachedInput: 0.3,
|
|
1467
|
+
cacheCreationInput: 3.75,
|
|
1260
1468
|
maxInputTokens: 1e6
|
|
1261
1469
|
},
|
|
1262
1470
|
"claude-sonnet-4@20250514": {
|
|
1263
1471
|
input: 3,
|
|
1264
1472
|
output: 15,
|
|
1473
|
+
cachedInput: 0.3,
|
|
1474
|
+
cacheCreationInput: 3.75,
|
|
1265
1475
|
maxInputTokens: 1e6
|
|
1266
1476
|
},
|
|
1267
1477
|
"deepseek-ai/deepseek-v3.1-maas": {
|
|
@@ -1311,6 +1521,7 @@ var prices_default = {
|
|
|
1311
1521
|
"gpt-realtime-mini-2025-12-15": {
|
|
1312
1522
|
input: 0.6,
|
|
1313
1523
|
output: 2.4,
|
|
1524
|
+
cachedInput: 0.06,
|
|
1314
1525
|
maxInputTokens: 128e3
|
|
1315
1526
|
},
|
|
1316
1527
|
"gemini-2.5-flash-native-audio-latest": {
|
|
@@ -1336,16 +1547,20 @@ var prices_default = {
|
|
|
1336
1547
|
"gemini-pro-latest": {
|
|
1337
1548
|
input: 1.25,
|
|
1338
1549
|
output: 10,
|
|
1550
|
+
cachedInput: 0.125,
|
|
1339
1551
|
maxInputTokens: 1048576
|
|
1340
1552
|
},
|
|
1341
1553
|
"gemini-exp-1206": {
|
|
1342
1554
|
input: 0.3,
|
|
1343
1555
|
output: 2.5,
|
|
1556
|
+
cachedInput: 0.03,
|
|
1344
1557
|
maxInputTokens: 1048576
|
|
1345
1558
|
},
|
|
1346
1559
|
"claude-sonnet-4-6@default": {
|
|
1347
1560
|
input: 3,
|
|
1348
1561
|
output: 15,
|
|
1562
|
+
cachedInput: 0.3,
|
|
1563
|
+
cacheCreationInput: 3.75,
|
|
1349
1564
|
maxInputTokens: 1e6
|
|
1350
1565
|
}
|
|
1351
1566
|
}
|
|
@@ -1353,11 +1568,19 @@ var prices_default = {
|
|
|
1353
1568
|
|
|
1354
1569
|
// src/core/tracker.ts
|
|
1355
1570
|
var bundledPrices = prices_default.models;
|
|
1571
|
+
var bundledUpdatedAt = prices_default.updated_at ?? "";
|
|
1356
1572
|
var ModelPriceSchema = z.object({
|
|
1357
1573
|
input: z.number().nonnegative(),
|
|
1358
1574
|
output: z.number().nonnegative(),
|
|
1575
|
+
cachedInput: z.number().nonnegative().optional(),
|
|
1576
|
+
cacheCreationInput: z.number().nonnegative().optional(),
|
|
1359
1577
|
maxInputTokens: z.number().positive().optional()
|
|
1360
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
|
+
});
|
|
1361
1584
|
var TrackerConfigSchema = z.object({
|
|
1362
1585
|
storage: z.union([z.enum(["memory", "sqlite"]), z.custom((v) => {
|
|
1363
1586
|
return v !== null && typeof v === "object" && typeof v.record === "function" && typeof v.getAll === "function" && typeof v.clearAll === "function" && typeof v.clearSession === "function";
|
|
@@ -1365,7 +1588,13 @@ var TrackerConfigSchema = z.object({
|
|
|
1365
1588
|
alertThreshold: z.number().positive().optional(),
|
|
1366
1589
|
webhookUrl: z.string().url().optional(),
|
|
1367
1590
|
syncPrices: z.boolean().optional().default(true),
|
|
1368
|
-
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)
|
|
1369
1598
|
});
|
|
1370
1599
|
function createTracker(config = {}) {
|
|
1371
1600
|
const parsed = TrackerConfigSchema.safeParse(config);
|
|
@@ -1379,19 +1608,45 @@ ${issues}`);
|
|
|
1379
1608
|
alertThreshold,
|
|
1380
1609
|
webhookUrl,
|
|
1381
1610
|
syncPrices,
|
|
1382
|
-
customPrices
|
|
1611
|
+
customPrices,
|
|
1612
|
+
warnIfStaleAfterHours,
|
|
1613
|
+
budgets,
|
|
1614
|
+
suggestions
|
|
1383
1615
|
} = parsed.data;
|
|
1384
1616
|
const storage = typeof storageOption === "object" ? storageOption : createStorage(storageOption);
|
|
1385
1617
|
let remotePrices;
|
|
1618
|
+
let pricesUpdatedAt = bundledUpdatedAt;
|
|
1386
1619
|
if (syncPrices) {
|
|
1387
1620
|
getRemotePrices().then((result) => {
|
|
1388
|
-
if (result)
|
|
1621
|
+
if (result) {
|
|
1622
|
+
remotePrices = result.models;
|
|
1623
|
+
pricesUpdatedAt = result.updated_at;
|
|
1624
|
+
}
|
|
1389
1625
|
}).catch(() => {
|
|
1390
1626
|
});
|
|
1391
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
|
+
}
|
|
1392
1644
|
let alertFired = false;
|
|
1645
|
+
const firedUserAlerts = /* @__PURE__ */ new Set();
|
|
1646
|
+
const firedSessionAlerts = /* @__PURE__ */ new Set();
|
|
1393
1647
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1394
1648
|
function resolveModelPrice(model) {
|
|
1649
|
+
maybeWarnStaleness();
|
|
1395
1650
|
return resolvePrice(model, {
|
|
1396
1651
|
bundledPrices,
|
|
1397
1652
|
...customPrices !== void 0 && { customPrices },
|
|
@@ -1400,39 +1655,94 @@ ${issues}`);
|
|
|
1400
1655
|
}
|
|
1401
1656
|
function track(entry) {
|
|
1402
1657
|
const price = resolveModelPrice(entry.model);
|
|
1403
|
-
const costUSD = calculateCost(
|
|
1658
|
+
const costUSD = calculateCost(
|
|
1659
|
+
entry.inputTokens,
|
|
1660
|
+
entry.outputTokens,
|
|
1661
|
+
price,
|
|
1662
|
+
entry.cachedTokens,
|
|
1663
|
+
entry.cacheCreationTokens
|
|
1664
|
+
);
|
|
1404
1665
|
const full = {
|
|
1405
1666
|
...entry,
|
|
1406
1667
|
costUSD,
|
|
1407
1668
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1408
1669
|
};
|
|
1409
1670
|
storage.record(full);
|
|
1410
|
-
|
|
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
|
+
}
|
|
1411
1679
|
}
|
|
1412
|
-
function
|
|
1413
|
-
if (
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
fetch(webhookUrl, {
|
|
1425
|
-
method: "POST",
|
|
1426
|
-
headers: { "Content-Type": "application/json" },
|
|
1427
|
-
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
|
+
});
|
|
1428
1692
|
}).catch(() => {
|
|
1693
|
+
alertFired = false;
|
|
1429
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)
|
|
1430
1740
|
}).catch(() => {
|
|
1431
|
-
alertFired = false;
|
|
1432
1741
|
});
|
|
1433
1742
|
}
|
|
1434
|
-
async function getReport() {
|
|
1435
|
-
const
|
|
1743
|
+
async function getReport(options) {
|
|
1744
|
+
const allEntries = await Promise.resolve(storage.getAll());
|
|
1745
|
+
const entries = filterEntries(allEntries, options);
|
|
1436
1746
|
const byModel = {};
|
|
1437
1747
|
const bySession = {};
|
|
1438
1748
|
const byUser = {};
|
|
@@ -1440,18 +1750,24 @@ ${issues}`);
|
|
|
1440
1750
|
let totalInput = 0;
|
|
1441
1751
|
let totalOutput = 0;
|
|
1442
1752
|
let totalCost = 0;
|
|
1443
|
-
let
|
|
1753
|
+
let periodFrom = options ? entries[0]?.timestamp ?? startedAt : startedAt;
|
|
1754
|
+
let lastTimestamp = periodFrom;
|
|
1444
1755
|
for (const e of entries) {
|
|
1445
|
-
totalInput += e.inputTokens;
|
|
1756
|
+
totalInput += e.inputTokens + (e.cachedTokens ?? 0) + (e.cacheCreationTokens ?? 0);
|
|
1446
1757
|
totalOutput += e.outputTokens;
|
|
1447
1758
|
totalCost += e.costUSD;
|
|
1448
1759
|
if (e.timestamp > lastTimestamp) lastTimestamp = e.timestamp;
|
|
1449
|
-
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
|
+
};
|
|
1450
1765
|
m.costUSD += e.costUSD;
|
|
1451
1766
|
m.calls += 1;
|
|
1452
|
-
m.tokens.input += e.inputTokens;
|
|
1767
|
+
m.tokens.input += e.inputTokens + (e.cachedTokens ?? 0) + (e.cacheCreationTokens ?? 0);
|
|
1453
1768
|
m.tokens.output += e.outputTokens;
|
|
1454
1769
|
m.tokens.reasoning += e.reasoningTokens ?? 0;
|
|
1770
|
+
m.tokens.cached += e.cachedTokens ?? 0;
|
|
1455
1771
|
if (e.sessionId) {
|
|
1456
1772
|
const s = bySession[e.sessionId] ??= { costUSD: 0, calls: 0 };
|
|
1457
1773
|
s.costUSD += e.costUSD;
|
|
@@ -1468,6 +1784,9 @@ ${issues}`);
|
|
|
1468
1784
|
f.calls += 1;
|
|
1469
1785
|
}
|
|
1470
1786
|
}
|
|
1787
|
+
if (options && entries.length > 0) {
|
|
1788
|
+
periodFrom = entries[0]?.timestamp ?? periodFrom;
|
|
1789
|
+
}
|
|
1471
1790
|
return {
|
|
1472
1791
|
totalCostUSD: totalCost,
|
|
1473
1792
|
totalTokens: { input: totalInput, output: totalOutput },
|
|
@@ -1475,22 +1794,66 @@ ${issues}`);
|
|
|
1475
1794
|
bySession,
|
|
1476
1795
|
byUser,
|
|
1477
1796
|
byFeature,
|
|
1478
|
-
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 }
|
|
1479
1839
|
};
|
|
1480
1840
|
}
|
|
1481
1841
|
async function reset() {
|
|
1482
1842
|
await Promise.resolve(storage.clearAll());
|
|
1483
1843
|
alertFired = false;
|
|
1844
|
+
firedUserAlerts.clear();
|
|
1845
|
+
firedSessionAlerts.clear();
|
|
1484
1846
|
}
|
|
1485
1847
|
async function resetSession(sessionId) {
|
|
1486
1848
|
await Promise.resolve(storage.clearSession(sessionId));
|
|
1849
|
+
firedSessionAlerts.delete(sessionId);
|
|
1487
1850
|
}
|
|
1488
1851
|
async function exportJSON() {
|
|
1489
1852
|
return JSON.stringify(await getReport(), null, 2);
|
|
1490
1853
|
}
|
|
1491
1854
|
async function exportCSV() {
|
|
1492
1855
|
const entries = await Promise.resolve(storage.getAll());
|
|
1493
|
-
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";
|
|
1494
1857
|
const rows = entries.map(
|
|
1495
1858
|
(e) => [
|
|
1496
1859
|
csvEscape(e.timestamp),
|
|
@@ -1498,6 +1861,8 @@ ${issues}`);
|
|
|
1498
1861
|
e.inputTokens,
|
|
1499
1862
|
e.outputTokens,
|
|
1500
1863
|
e.reasoningTokens ?? 0,
|
|
1864
|
+
e.cachedTokens ?? 0,
|
|
1865
|
+
e.cacheCreationTokens ?? 0,
|
|
1501
1866
|
e.costUSD.toFixed(8),
|
|
1502
1867
|
csvEscape(e.sessionId ?? ""),
|
|
1503
1868
|
csvEscape(e.userId ?? ""),
|
|
@@ -1513,11 +1878,47 @@ ${issues}`);
|
|
|
1513
1878
|
...remotePrices !== void 0 && { remotePrices }
|
|
1514
1879
|
}) ?? null;
|
|
1515
1880
|
}
|
|
1516
|
-
return {
|
|
1881
|
+
return {
|
|
1882
|
+
track,
|
|
1883
|
+
getReport,
|
|
1884
|
+
getCostForecast,
|
|
1885
|
+
reset,
|
|
1886
|
+
resetSession,
|
|
1887
|
+
exportJSON,
|
|
1888
|
+
exportCSV,
|
|
1889
|
+
getModelInfo
|
|
1890
|
+
};
|
|
1517
1891
|
}
|
|
1518
1892
|
function computeTotal(entries) {
|
|
1519
1893
|
return entries.reduce((sum, e) => sum + e.costUSD, 0);
|
|
1520
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
|
+
}
|
|
1521
1922
|
function csvEscape(value) {
|
|
1522
1923
|
if (value.includes(",") || value.includes('"') || value.includes("\n")) {
|
|
1523
1924
|
return `"${value.replace(/"/g, '""')}"`;
|
|
@@ -1538,7 +1939,7 @@ async function cmdSync() {
|
|
|
1538
1939
|
console.log("Fetching latest prices from remote...");
|
|
1539
1940
|
const result = await fetchRemotePrices();
|
|
1540
1941
|
if (result) {
|
|
1541
|
-
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}).`);
|
|
1542
1943
|
} else {
|
|
1543
1944
|
console.error("\u2717 Failed to fetch remote prices. Check your internet connection.");
|
|
1544
1945
|
process.exit(1);
|
|
@@ -1584,6 +1985,9 @@ async function cmdReport() {
|
|
|
1584
1985
|
console.log(` Total cost: $${report.totalCostUSD.toFixed(6)} USD`);
|
|
1585
1986
|
console.log(` Total tokens: ${report.totalTokens.input.toLocaleString()} in / ${report.totalTokens.output.toLocaleString()} out`);
|
|
1586
1987
|
console.log(` Period: ${report.period.from} \u2192 ${report.period.to}`);
|
|
1988
|
+
if (report.pricesUpdatedAt) {
|
|
1989
|
+
console.log(` Prices as of: ${report.pricesUpdatedAt}`);
|
|
1990
|
+
}
|
|
1587
1991
|
if (Object.keys(report.byModel).length > 0) {
|
|
1588
1992
|
console.log("\n By model:");
|
|
1589
1993
|
for (const [model, stats] of Object.entries(report.byModel)) {
|