@trazum/core 2.4.2 → 2.4.3
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/dist/pricing.d.ts +1 -1
- package/dist/pricing.d.ts.map +1 -1
- package/dist/pricing.js +242 -44
- package/dist/pricing.js.map +1 -1
- package/dist/types.d.ts +27 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/usage.d.ts +5 -1
- package/dist/usage.d.ts.map +1 -1
- package/dist/usage.js +14 -2
- package/dist/usage.js.map +1 -1
- package/package.json +1 -1
- package/src/pricing.ts +247 -44
- package/src/types.ts +28 -0
- package/src/usage.ts +19 -3
package/src/pricing.ts
CHANGED
|
@@ -48,10 +48,30 @@ export const PROVIDER_REVIEWED: Readonly<Record<string, string>> = Object.freeze
|
|
|
48
48
|
* and priority. The standard one is the catalogue's, identified by the other
|
|
49
49
|
* three being its multiples (0.5x, 0.5x, 2x) rather than by trusting the
|
|
50
50
|
* order they appear in.
|
|
51
|
+
*
|
|
52
|
+
* Re-read 2026-10-11 with the prompt-caching guide, the GPT-5 model page and
|
|
53
|
+
* the deprecations page. GPT-5, mini and nano matched to the cent. Their
|
|
54
|
+
* dated snapshots (`gpt-5-2025-08-07` and the mini and nano ones), which is
|
|
55
|
+
* the id a usage log usually carries, shut down on 2026-12-11 and are now
|
|
56
|
+
* declared as aliases. The current generation (gpt-6-astra, gpt-6.1-sol,
|
|
57
|
+
* gpt-6-sol, gpt-6-luna and gpt-5.6 sol, terra and luna) is on the same
|
|
58
|
+
* page and not added: every one of them doubles its rates for prompts over
|
|
59
|
+
* 272K tokens, a per-request condition no log Trazum prices passes yet.
|
|
60
|
+
*/
|
|
61
|
+
openai: '2026-10-11',
|
|
62
|
+
/*
|
|
63
|
+
* ai.google.dev/gemini-api/docs/pricing, docs/caching and the model pages,
|
|
64
|
+
* read 2026-10-10. Every price matched; what changed is how caching starts
|
|
65
|
+
* and what it costs on the old models. Implicit caching is on by default
|
|
66
|
+
* for every Gemini 2.5 and newer model, so `caching` is 'automatic' across
|
|
67
|
+
* the board; the minimums moved to 4,096 on 3.6 and 3.8 Flash and 2,048 on
|
|
68
|
+
* 2.5 Flash; and the 2.5 models read the cache at 10% of input today, not
|
|
69
|
+
* the 25% this table carried. Gemini 3.8 Flash was added. Gemini 3.1 Pro
|
|
70
|
+
* Preview and 2.5 Pro's prompts over 200k are priced by prompt length per
|
|
71
|
+
* request, which nothing that prices a log passes yet, so the first is left
|
|
72
|
+
* out and the second's tier is named in its note rather than guessed.
|
|
51
73
|
*/
|
|
52
|
-
|
|
53
|
-
/* ai.google.dev/gemini-api/docs/pricing, read 2026-08-28. */
|
|
54
|
-
google: '2026-08-28',
|
|
74
|
+
google: '2026-10-10',
|
|
55
75
|
/*
|
|
56
76
|
* platform.kimi.ai/docs/pricing/chat (platform.moonshot.ai 301s there),
|
|
57
77
|
* read 2026-09-12. The page prices kimi-k3, kimi-k2.6, kimi-k2.7-code and
|
|
@@ -64,8 +84,15 @@ export const PROVIDER_REVIEWED: Readonly<Record<string, string>> = Object.freeze
|
|
|
64
84
|
* not read is a number it would be inventing.
|
|
65
85
|
*/
|
|
66
86
|
moonshot: '2026-09-12',
|
|
67
|
-
/*
|
|
68
|
-
|
|
87
|
+
/*
|
|
88
|
+
* api-docs.deepseek.com/quick_start/pricing read 2026-10-10, and the
|
|
89
|
+
* 2026-09-10 announcement (api-docs.deepseek.com/news/news260910) read
|
|
90
|
+
* 2026-10-11 for the instant: "New pricing takes effect at 04:00 UTC on
|
|
91
|
+
* Sept 10, 2026." V4 Flash was retired that day and its id now bills at
|
|
92
|
+
* the new deepseek-flash price, so the old price lives in its `history`.
|
|
93
|
+
* V4 Pro matched the page unchanged.
|
|
94
|
+
*/
|
|
95
|
+
deepseek: '2026-10-10',
|
|
69
96
|
/*
|
|
70
97
|
* docs.x.ai/docs/models, read 2026-09-12. The page prices grok-4.6, 4.5,
|
|
71
98
|
* 4.3 and the 4.20 family, each at two rates split at 200k prompt tokens;
|
|
@@ -86,7 +113,18 @@ export const PROVIDER_REVIEWED: Readonly<Record<string, string>> = Object.freeze
|
|
|
86
113
|
* servable is not the same as checking, and moving the date then would have
|
|
87
114
|
* said it was.
|
|
88
115
|
*/
|
|
89
|
-
|
|
116
|
+
/*
|
|
117
|
+
* Re-read 2026-10-11 from mistral.ai/cloud/inference/ and the model cards
|
|
118
|
+
* at mistral.ai/news/mistral-3/, /mistral-small-4/ and
|
|
119
|
+
* /vibe-remote-agents-mistral-medium-3-5/, which carry the prices in their
|
|
120
|
+
* markup (`data-prices`) and bind the `-latest` ids. Every price matched.
|
|
121
|
+
* What changed is caching: the inference page offers "Cached input tokens
|
|
122
|
+
* -90% on input token", and every one of the three cards marks its input
|
|
123
|
+
* price as carrying the cache discount. This table said Mistral had no
|
|
124
|
+
* prompt caching at all. docs.mistral.ai, where a minimum or a mechanism
|
|
125
|
+
* would be, is not reachable from here, so neither is claimed.
|
|
126
|
+
*/
|
|
127
|
+
mistral: '2026-10-11',
|
|
90
128
|
});
|
|
91
129
|
|
|
92
130
|
/**
|
|
@@ -132,12 +170,17 @@ const multipliersMemo = new WeakMap<
|
|
|
132
170
|
{ cacheWrite5m: number; cacheWrite1h: number; cacheRead: number; batch: number | null }
|
|
133
171
|
>();
|
|
134
172
|
|
|
135
|
-
export function multipliersFor(model: ModelPricing): {
|
|
173
|
+
export function multipliersFor(model: ModelPricing, on?: Date): {
|
|
136
174
|
cacheWrite5m: number;
|
|
137
175
|
cacheWrite1h: number;
|
|
138
176
|
cacheRead: number;
|
|
139
177
|
batch: number | null;
|
|
140
178
|
} {
|
|
179
|
+
// A dated question about a repriced model asks the period in force then.
|
|
180
|
+
if (on !== undefined && model.history !== undefined) {
|
|
181
|
+
const period = pricedAt(model, on);
|
|
182
|
+
if (period !== model) return multipliersFor(period);
|
|
183
|
+
}
|
|
141
184
|
const cached = multipliersMemo.get(model);
|
|
142
185
|
if (cached !== undefined) return cached;
|
|
143
186
|
const m = model.multipliers ?? {};
|
|
@@ -151,6 +194,23 @@ export function multipliersFor(model: ModelPricing): {
|
|
|
151
194
|
return result;
|
|
152
195
|
}
|
|
153
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Mistral discounts cached input by 90% and publishes nothing else about it
|
|
199
|
+
* that can be read from here: not how caching starts, not a minimum, not a
|
|
200
|
+
* write price. Its usage reports no write tokens, so the write ratio of 1 is
|
|
201
|
+
* never multiplied by anything; it is there so the default (Anthropic's 1.25)
|
|
202
|
+
* cannot be.
|
|
203
|
+
*/
|
|
204
|
+
/** What the GPT-5 family's three rows share, from OpenAI's guide and deprecations page, read 2026-10-11. */
|
|
205
|
+
const OPENAI_GPT5_NOTE =
|
|
206
|
+
'Caching is automatic, with no cache_control to set. OpenAI says the minimum for models before GPT-5.6 varies by'
|
|
207
|
+
+ ' request settings, so 1,024 here is the nearest published floor. The 2025-08-07 snapshot shuts down on'
|
|
208
|
+
+ ' 2026-12-11; OpenAI names the gpt-5.6 models as replacements.';
|
|
209
|
+
|
|
210
|
+
const MISTRAL_CACHE_NOTE =
|
|
211
|
+
'Cached input costs 10% of the input price (mistral.ai/cloud/inference, read 2026-10-11). How caching starts and'
|
|
212
|
+
+ ' its minimum are not published anywhere Trazum can read, so no caching advice is given for it.';
|
|
213
|
+
|
|
154
214
|
export const MODELS: ModelPricing[] = [
|
|
155
215
|
{
|
|
156
216
|
/*
|
|
@@ -380,13 +440,17 @@ export const MODELS: ModelPricing[] = [
|
|
|
380
440
|
// ------------------------------------------------------------------------
|
|
381
441
|
// OpenAI
|
|
382
442
|
//
|
|
383
|
-
// Caching is AUTOMATIC
|
|
384
|
-
//
|
|
385
|
-
//
|
|
386
|
-
//
|
|
443
|
+
// Caching is AUTOMATIC, with no cache_control to set, and a cached read costs
|
|
444
|
+
// a tenth of input on these three ($0.125 against $1.25). The reordering
|
|
445
|
+
// advice is identical (a prefix is a prefix), the marker advice is not, which
|
|
446
|
+
// is why `caching` and `multipliers` exist at all. The 1,024 minimum is the
|
|
447
|
+
// one OpenAI publishes for GPT-5.6 and later; for these older models its
|
|
448
|
+
// guide says the minimum "varies by request settings", so it is the nearest
|
|
449
|
+
// published floor, not a promise, and each row's note says so.
|
|
387
450
|
// ------------------------------------------------------------------------
|
|
388
451
|
{
|
|
389
452
|
id: 'gpt-5',
|
|
453
|
+
aliases: ['gpt-5-2025-08-07'],
|
|
390
454
|
provider: 'openai',
|
|
391
455
|
displayName: 'GPT-5',
|
|
392
456
|
inputPerMTok: 1.25,
|
|
@@ -397,10 +461,11 @@ export const MODELS: ModelPricing[] = [
|
|
|
397
461
|
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
398
462
|
capability: 'frontier',
|
|
399
463
|
tier: 'frontier',
|
|
400
|
-
notes:
|
|
464
|
+
notes: OPENAI_GPT5_NOTE,
|
|
401
465
|
},
|
|
402
466
|
{
|
|
403
467
|
id: 'gpt-5-mini',
|
|
468
|
+
aliases: ['gpt-5-mini-2025-08-07'],
|
|
404
469
|
provider: 'openai',
|
|
405
470
|
displayName: 'GPT-5 mini',
|
|
406
471
|
inputPerMTok: 0.25,
|
|
@@ -411,9 +476,11 @@ export const MODELS: ModelPricing[] = [
|
|
|
411
476
|
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
412
477
|
capability: 'mid',
|
|
413
478
|
tier: 'sonnet',
|
|
479
|
+
notes: OPENAI_GPT5_NOTE,
|
|
414
480
|
},
|
|
415
481
|
{
|
|
416
482
|
id: 'gpt-5-nano',
|
|
483
|
+
aliases: ['gpt-5-nano-2025-08-07'],
|
|
417
484
|
provider: 'openai',
|
|
418
485
|
displayName: 'GPT-5 nano',
|
|
419
486
|
inputPerMTok: 0.05,
|
|
@@ -424,6 +491,7 @@ export const MODELS: ModelPricing[] = [
|
|
|
424
491
|
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
425
492
|
capability: 'small',
|
|
426
493
|
tier: 'haiku',
|
|
494
|
+
notes: OPENAI_GPT5_NOTE,
|
|
427
495
|
},
|
|
428
496
|
|
|
429
497
|
// ------------------------------------------------------------------------
|
|
@@ -437,8 +505,9 @@ export const MODELS: ModelPricing[] = [
|
|
|
437
505
|
outputPerMTok: 10,
|
|
438
506
|
contextWindow: 1_048_576,
|
|
439
507
|
cacheMinTokens: 2048,
|
|
440
|
-
caching: '
|
|
441
|
-
|
|
508
|
+
caching: 'automatic',
|
|
509
|
+
/* $0.125 cached against $1.25 input for prompts up to 200k, read 2026-10-10. */
|
|
510
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
442
511
|
/* Refused on 2026-08-28: still in `GET /v1beta/models`, and a 404 to a request. */
|
|
443
512
|
retired: {
|
|
444
513
|
on: '2026-08-28',
|
|
@@ -447,7 +516,10 @@ export const MODELS: ModelPricing[] = [
|
|
|
447
516
|
},
|
|
448
517
|
capability: 'large',
|
|
449
518
|
tier: 'opus',
|
|
450
|
-
notes:
|
|
519
|
+
notes:
|
|
520
|
+
'Prompts over 200k tokens are billed $2.50 in and $15.00 out; this row prices every prompt at the up-to-200k rate,'
|
|
521
|
+
+ ' because no log Trazum prices says how long each prompt was. Implicit caching is automatic; explicit caching is'
|
|
522
|
+
+ ' also billed for storage per hour.',
|
|
451
523
|
},
|
|
452
524
|
{
|
|
453
525
|
id: 'gemini-2.5-flash',
|
|
@@ -456,9 +528,10 @@ export const MODELS: ModelPricing[] = [
|
|
|
456
528
|
inputPerMTok: 0.3,
|
|
457
529
|
outputPerMTok: 2.5,
|
|
458
530
|
contextWindow: 1_048_576,
|
|
459
|
-
cacheMinTokens:
|
|
460
|
-
caching: '
|
|
461
|
-
|
|
531
|
+
cacheMinTokens: 2048,
|
|
532
|
+
caching: 'automatic',
|
|
533
|
+
/* $0.03 cached against $0.30 input (text), read 2026-10-10. */
|
|
534
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
462
535
|
/* Refused on 2026-08-28: still in `GET /v1beta/models`, and a 404 to a request. */
|
|
463
536
|
retired: {
|
|
464
537
|
on: '2026-08-28',
|
|
@@ -471,9 +544,10 @@ export const MODELS: ModelPricing[] = [
|
|
|
471
544
|
|
|
472
545
|
{
|
|
473
546
|
/*
|
|
474
|
-
* Google's own error message names this as the replacement for the
|
|
475
|
-
*
|
|
476
|
-
* rates, read 2026-08-28
|
|
547
|
+
* Google's own error message names this as the replacement for the retired
|
|
548
|
+
* 2.5 Flash (the 2.5 Pro refusal names 3.1 Pro Preview), and
|
|
549
|
+
* ai.google.dev/gemini-api/docs/pricing carries the rates, read 2026-08-28
|
|
550
|
+
* and again on 2026-10-10.
|
|
477
551
|
*
|
|
478
552
|
* The introductory price has a published end date and a published successor,
|
|
479
553
|
* which is exactly what `promo` is for and is the shape the catalogue nearly
|
|
@@ -487,8 +561,8 @@ export const MODELS: ModelPricing[] = [
|
|
|
487
561
|
outputPerMTok: 7.5,
|
|
488
562
|
promo: { inputPerMTok: 0.75, outputPerMTok: 3.75, until: '2026-12-31' },
|
|
489
563
|
contextWindow: 1_048_576,
|
|
490
|
-
cacheMinTokens:
|
|
491
|
-
caching: '
|
|
564
|
+
cacheMinTokens: 4096,
|
|
565
|
+
caching: 'automatic',
|
|
492
566
|
/*
|
|
493
567
|
* $0.075 against $0.75 inside the introductory window, and $0.15 against
|
|
494
568
|
* $1.50 after it: Google scales the cache price with the headline one, so
|
|
@@ -499,7 +573,32 @@ export const MODELS: ModelPricing[] = [
|
|
|
499
573
|
capability: 'mid',
|
|
500
574
|
tier: 'sonnet',
|
|
501
575
|
notes:
|
|
502
|
-
'
|
|
576
|
+
'Implicit caching is automatic above 4,096 tokens. Explicit caching is also billed for storage:'
|
|
577
|
+
+ ' $0.50 per 1M tokens per hour through 2026-12-31, $1.00 after.',
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
/*
|
|
581
|
+
* Gemini 3.8 Flash, from ai.google.dev/gemini-api/docs/pricing, its model
|
|
582
|
+
* page and docs/caching, read 2026-10-10. The same rates and the same
|
|
583
|
+
* introductory window as 3.6 Flash, published as one table each: $0.75
|
|
584
|
+
* and $3.75 through 2026-12-31, $1.50 and $7.50 from 2027-01-01, and the
|
|
585
|
+
* cache price scales with them ($0.075, then $0.15).
|
|
586
|
+
*/
|
|
587
|
+
id: 'gemini-3.8-flash',
|
|
588
|
+
provider: 'google',
|
|
589
|
+
displayName: 'Gemini 3.8 Flash',
|
|
590
|
+
inputPerMTok: 1.5,
|
|
591
|
+
outputPerMTok: 7.5,
|
|
592
|
+
promo: { inputPerMTok: 0.75, outputPerMTok: 3.75, until: '2026-12-31' },
|
|
593
|
+
contextWindow: 1_048_576,
|
|
594
|
+
cacheMinTokens: 4096,
|
|
595
|
+
caching: 'automatic',
|
|
596
|
+
/* $0.075 / $0.75 = 0.1 (and $0.15 / $1.50); batch $0.375 / $0.75 = 0.5 (and $1.875 / $3.75). */
|
|
597
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
598
|
+
capability: 'mid',
|
|
599
|
+
tier: 'sonnet',
|
|
600
|
+
notes:
|
|
601
|
+
'Implicit caching is automatic above 4,096 tokens. Explicit caching is also billed for storage:'
|
|
503
602
|
+ ' $0.50 per 1M tokens per hour through 2026-12-31, $1.00 after.',
|
|
504
603
|
},
|
|
505
604
|
|
|
@@ -547,34 +646,97 @@ export const MODELS: ModelPricing[] = [
|
|
|
547
646
|
},
|
|
548
647
|
{
|
|
549
648
|
/*
|
|
550
|
-
* DeepSeek V4 Flash,
|
|
551
|
-
* 2026-
|
|
649
|
+
* DeepSeek V4 Flash, retired on 2026-09-10. The pricing page (read
|
|
650
|
+
* 2026-10-10) says the id "is still accepted ... served by the
|
|
651
|
+
* DeepSeek-V4.1-Flash model and billed at the Flash price", so from
|
|
652
|
+
* 04:00 UTC that day it carries deepseek-flash's rates. Before that
|
|
653
|
+
* instant it billed at its own, read off the same page on 2026-08-28:
|
|
654
|
+
* $0.22/$0.66 off-peak, double at peak, $0.007 a cache hit.
|
|
552
655
|
*
|
|
553
656
|
* The base rate is off-peak because off-peak is the common case: peak is
|
|
554
657
|
* 01:00-04:00 and 06:00-10:00 UTC on weekdays, which is 35 hours of 168.
|
|
555
|
-
* The peak tier is exactly double, published as such.
|
|
658
|
+
* The peak tier is exactly double, published as such. Clock pricing began
|
|
659
|
+
* at 16:00 UTC on 2026-08-16; the flat price before it is on no page this
|
|
660
|
+
* table can read, so calls before then are priced as the first period.
|
|
661
|
+
*
|
|
662
|
+
* Not recommendable: a legacy id DeepSeek routes "temporarily". Without
|
|
663
|
+
* this, a tie on price would offer it ahead of deepseek-flash.
|
|
556
664
|
*/
|
|
557
665
|
id: 'deepseek-v4-flash',
|
|
558
666
|
provider: 'deepseek',
|
|
559
667
|
displayName: 'DeepSeek V4 Flash',
|
|
560
|
-
|
|
561
|
-
|
|
668
|
+
recommendable: false,
|
|
669
|
+
inputPerMTok: 0.15,
|
|
670
|
+
outputPerMTok: 0.6,
|
|
671
|
+
tiers: [
|
|
672
|
+
{
|
|
673
|
+
id: 'peak',
|
|
674
|
+
when: { kind: 'utc-hours', hours: [1, 2, 3, 6, 7, 8, 9], weekdaysOnly: true },
|
|
675
|
+
inputPerMTok: 0.3,
|
|
676
|
+
outputPerMTok: 1.2,
|
|
677
|
+
},
|
|
678
|
+
],
|
|
679
|
+
history: [
|
|
680
|
+
{
|
|
681
|
+
until: '2026-09-10T04:00:00Z',
|
|
682
|
+
inputPerMTok: 0.22,
|
|
683
|
+
outputPerMTok: 0.66,
|
|
684
|
+
tiers: [
|
|
685
|
+
{
|
|
686
|
+
id: 'peak',
|
|
687
|
+
when: { kind: 'utc-hours', hours: [1, 2, 3, 6, 7, 8, 9], weekdaysOnly: true },
|
|
688
|
+
inputPerMTok: 0.44,
|
|
689
|
+
outputPerMTok: 1.32,
|
|
690
|
+
},
|
|
691
|
+
],
|
|
692
|
+
/* $0.007 cache hit against $0.22 input off-peak. */
|
|
693
|
+
multipliers: { cacheRead: 0.0318, cacheWrite5m: 1, cacheWrite1h: 1, batch: null },
|
|
694
|
+
},
|
|
695
|
+
],
|
|
696
|
+
contextWindow: 1_000_000,
|
|
697
|
+
cacheMinTokens: 1024,
|
|
698
|
+
caching: 'automatic',
|
|
699
|
+
/* $0.003 cache hit against $0.15 input off-peak, $0.006 against $0.30 at peak. */
|
|
700
|
+
multipliers: { cacheRead: 0.02, cacheWrite5m: 1, cacheWrite1h: 1, batch: null },
|
|
701
|
+
capability: 'mid',
|
|
702
|
+
tier: 'sonnet',
|
|
703
|
+
notes:
|
|
704
|
+
'Retired 2026-09-10: the id is still accepted, served by V4.1 Flash and billed at the deepseek-flash price.'
|
|
705
|
+
+ ' Maximum output 384K tokens. Peak excludes Chinese public holidays, which this table cannot express.',
|
|
706
|
+
},
|
|
707
|
+
{
|
|
708
|
+
/*
|
|
709
|
+
* DeepSeek V4.1 Flash, from api-docs.deepseek.com/quick_start/pricing,
|
|
710
|
+
* read 2026-10-10. Released 2026-09-10; its price took effect at 04:00
|
|
711
|
+
* UTC that day. Off-peak is the base and peak is exactly double, as
|
|
712
|
+
* published; the cache hit is 2% of input at both ($0.003 and $0.006).
|
|
713
|
+
*
|
|
714
|
+
* The cache minimum is carried from deepseek-v4-flash: DeepSeek's pages
|
|
715
|
+
* publish none for this generation, and 1,024 is the figure the earlier
|
|
716
|
+
* row was reviewed with.
|
|
717
|
+
*/
|
|
718
|
+
id: 'deepseek-flash',
|
|
719
|
+
provider: 'deepseek',
|
|
720
|
+
displayName: 'DeepSeek V4.1 Flash',
|
|
721
|
+
inputPerMTok: 0.15,
|
|
722
|
+
outputPerMTok: 0.6,
|
|
562
723
|
tiers: [
|
|
563
724
|
{
|
|
564
725
|
id: 'peak',
|
|
565
726
|
when: { kind: 'utc-hours', hours: [1, 2, 3, 6, 7, 8, 9], weekdaysOnly: true },
|
|
566
|
-
inputPerMTok: 0.
|
|
567
|
-
outputPerMTok: 1.
|
|
727
|
+
inputPerMTok: 0.3,
|
|
728
|
+
outputPerMTok: 1.2,
|
|
568
729
|
},
|
|
569
730
|
],
|
|
570
731
|
contextWindow: 1_000_000,
|
|
571
732
|
cacheMinTokens: 1024,
|
|
572
733
|
caching: 'automatic',
|
|
573
|
-
|
|
574
|
-
multipliers: { cacheRead: 0.0318, cacheWrite5m: 1, cacheWrite1h: 1, batch: null },
|
|
734
|
+
multipliers: { cacheRead: 0.02, cacheWrite5m: 1, cacheWrite1h: 1, batch: null },
|
|
575
735
|
capability: 'mid',
|
|
576
736
|
tier: 'sonnet',
|
|
577
|
-
notes:
|
|
737
|
+
notes:
|
|
738
|
+
'Maximum output 384K tokens. Peak excludes Chinese public holidays, which this table cannot express:'
|
|
739
|
+
+ ' on a weekday holiday it charges peak where DeepSeek charges off-peak.',
|
|
578
740
|
},
|
|
579
741
|
{
|
|
580
742
|
/* Same page, same clock. Pro is three times Flash at both ends. */
|
|
@@ -667,12 +829,12 @@ export const MODELS: ModelPricing[] = [
|
|
|
667
829
|
inputPerMTok: 0.5,
|
|
668
830
|
outputPerMTok: 1.5,
|
|
669
831
|
contextWindow: 262_144,
|
|
670
|
-
cacheMinTokens:
|
|
671
|
-
caching: '
|
|
672
|
-
multipliers: { batch: 0.5 },
|
|
832
|
+
cacheMinTokens: null,
|
|
833
|
+
caching: 'unknown',
|
|
834
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
673
835
|
capability: 'large',
|
|
674
836
|
tier: 'opus',
|
|
675
|
-
notes:
|
|
837
|
+
notes: MISTRAL_CACHE_NOTE,
|
|
676
838
|
},
|
|
677
839
|
{
|
|
678
840
|
/*
|
|
@@ -695,11 +857,12 @@ export const MODELS: ModelPricing[] = [
|
|
|
695
857
|
inputPerMTok: 1.5,
|
|
696
858
|
outputPerMTok: 7.5,
|
|
697
859
|
contextWindow: 262_144,
|
|
698
|
-
cacheMinTokens:
|
|
699
|
-
caching: '
|
|
700
|
-
multipliers: { batch: 0.5 },
|
|
860
|
+
cacheMinTokens: null,
|
|
861
|
+
caching: 'unknown',
|
|
862
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
701
863
|
capability: 'mid',
|
|
702
864
|
tier: 'sonnet',
|
|
865
|
+
notes: MISTRAL_CACHE_NOTE,
|
|
703
866
|
},
|
|
704
867
|
{
|
|
705
868
|
/* Same page, same day. `mistral-small-2603` is what the API reports. */
|
|
@@ -710,11 +873,12 @@ export const MODELS: ModelPricing[] = [
|
|
|
710
873
|
inputPerMTok: 0.15,
|
|
711
874
|
outputPerMTok: 0.6,
|
|
712
875
|
contextWindow: 262_144,
|
|
713
|
-
cacheMinTokens:
|
|
714
|
-
caching: '
|
|
715
|
-
multipliers: { batch: 0.5 },
|
|
876
|
+
cacheMinTokens: null,
|
|
877
|
+
caching: 'unknown',
|
|
878
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
716
879
|
capability: 'small',
|
|
717
880
|
tier: 'haiku',
|
|
881
|
+
notes: MISTRAL_CACHE_NOTE,
|
|
718
882
|
},
|
|
719
883
|
];
|
|
720
884
|
|
|
@@ -879,6 +1043,40 @@ const pricingMemo = new WeakMap<
|
|
|
879
1043
|
{ untilMs: number | null; promo: Rates | null; base: Rates }
|
|
880
1044
|
>();
|
|
881
1045
|
|
|
1046
|
+
/**
|
|
1047
|
+
* The row as it stood at `on`: itself when the current price applies, or a
|
|
1048
|
+
* frozen copy carrying the period's rates, tiers and ratios. Built once per
|
|
1049
|
+
* period, so a log of a million calls does not build a million rows.
|
|
1050
|
+
*/
|
|
1051
|
+
const periodMemo = new WeakMap<ModelPricing, { untilMs: number; row: ModelPricing }[]>();
|
|
1052
|
+
|
|
1053
|
+
function pricedAt(model: ModelPricing, on: Date): ModelPricing {
|
|
1054
|
+
const history = model.history;
|
|
1055
|
+
if (history === undefined || history.length === 0) return model;
|
|
1056
|
+
let periods = periodMemo.get(model);
|
|
1057
|
+
if (periods === undefined) {
|
|
1058
|
+
periods = history.map((period) => {
|
|
1059
|
+
const { history: _history, promo: _promo, tiers: _tiers, multipliers: _multipliers, ...rest } = model;
|
|
1060
|
+
const row: ModelPricing = Object.freeze({
|
|
1061
|
+
...rest,
|
|
1062
|
+
inputPerMTok: period.inputPerMTok,
|
|
1063
|
+
outputPerMTok: period.outputPerMTok,
|
|
1064
|
+
...(period.tiers !== undefined ? { tiers: period.tiers } : {}),
|
|
1065
|
+
...(period.multipliers !== undefined
|
|
1066
|
+
? { multipliers: period.multipliers }
|
|
1067
|
+
: model.multipliers !== undefined
|
|
1068
|
+
? { multipliers: model.multipliers }
|
|
1069
|
+
: {}),
|
|
1070
|
+
});
|
|
1071
|
+
return { untilMs: new Date(period.until).getTime(), row };
|
|
1072
|
+
});
|
|
1073
|
+
periodMemo.set(model, periods);
|
|
1074
|
+
}
|
|
1075
|
+
const at = on.getTime();
|
|
1076
|
+
for (const period of periods) if (at < period.untilMs) return period.row;
|
|
1077
|
+
return model;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
882
1080
|
/** The two unconditional answers for one model, parsed once. */
|
|
883
1081
|
function cache(model: ModelPricing): {
|
|
884
1082
|
untilMs: number | null;
|
|
@@ -948,6 +1146,11 @@ export function effectivePricing(
|
|
|
948
1146
|
on: Date = new Date(),
|
|
949
1147
|
context: { inputTokens?: number } = {},
|
|
950
1148
|
): Rates {
|
|
1149
|
+
/* A call before a published price change is priced as it was billed then. */
|
|
1150
|
+
if (model.history !== undefined) {
|
|
1151
|
+
const period = pricedAt(model, on);
|
|
1152
|
+
if (period !== model) return effectivePricing(period, on, context);
|
|
1153
|
+
}
|
|
951
1154
|
const memo = pricingMemo.get(model) ?? cache(model);
|
|
952
1155
|
|
|
953
1156
|
/**
|
package/src/types.ts
CHANGED
|
@@ -203,6 +203,18 @@ export interface PricingTier {
|
|
|
203
203
|
outputPerMTok: number;
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
+
/** One published price this model carried until an instant. See `ModelPricing.history`. */
|
|
207
|
+
export interface PricePeriod {
|
|
208
|
+
/** ISO 8601 instant, exclusive: a call at exactly this instant is priced by what came after. */
|
|
209
|
+
until: string;
|
|
210
|
+
inputPerMTok: number;
|
|
211
|
+
outputPerMTok: number;
|
|
212
|
+
/** The conditional rates in force during the period; none means none applied. */
|
|
213
|
+
tiers?: PricingTier[];
|
|
214
|
+
/** The cache and batch ratios during the period; absent means the row's own. */
|
|
215
|
+
multipliers?: CostMultipliers;
|
|
216
|
+
}
|
|
217
|
+
|
|
206
218
|
export interface ModelPricing {
|
|
207
219
|
id: string;
|
|
208
220
|
displayName: string;
|
|
@@ -298,6 +310,22 @@ export interface ModelPricing {
|
|
|
298
310
|
* and keeps this field optional in effect as well as in the type.
|
|
299
311
|
*/
|
|
300
312
|
tiers?: PricingTier[];
|
|
313
|
+
/**
|
|
314
|
+
* What this id cost before its current price, earliest period first.
|
|
315
|
+
*
|
|
316
|
+
* A usage log is history, and a provider that cuts a price at 04:00 UTC on
|
|
317
|
+
* a Thursday has billed the same id two ways in one month. One price for
|
|
318
|
+
* all time priced the older calls at the newer rate, or the newer at the
|
|
319
|
+
* older, and nothing on screen said which. A call at `on` is priced by the
|
|
320
|
+
* first period whose `until` is after it; a call at or after the last
|
|
321
|
+
* `until` takes the fields above. Each period carries its own tiers and
|
|
322
|
+
* cache ratio, because a provider that reprices usually reprices both.
|
|
323
|
+
*
|
|
324
|
+
* Only periods a provider published, with the instant it published. A
|
|
325
|
+
* period before the first one this table can source is not guessed at: the
|
|
326
|
+
* earliest period stands for it, and the row's note says so.
|
|
327
|
+
*/
|
|
328
|
+
history?: PricePeriod[];
|
|
301
329
|
/**
|
|
302
330
|
* Other ids that bill at exactly this rate.
|
|
303
331
|
*
|
package/src/usage.ts
CHANGED
|
@@ -944,8 +944,20 @@ function add(into: UsageBreakdown, record: UsageRecord, catalogue: PricingCatalo
|
|
|
944
944
|
if (!model) return false;
|
|
945
945
|
|
|
946
946
|
countInto(into, record);
|
|
947
|
-
|
|
948
|
-
|
|
947
|
+
/**
|
|
948
|
+
* **Each call at the price of its own moment.** A bill is history: a call
|
|
949
|
+
* made inside a promotion was billed at the promotional rate, a DeepSeek
|
|
950
|
+
* call at 03:00 UTC on a weekday was billed at peak, and a call before a
|
|
951
|
+
* published price change was billed at the old price. Priced at the report's
|
|
952
|
+
* date instead, every one of those came out at whatever applied the day the
|
|
953
|
+
* report ran: a clock-priced log read during peak hours was all peak, and an
|
|
954
|
+
* August log read in October lost its August promotion. `on` is the date for
|
|
955
|
+
* a record that carries no clock, which is the only thing it can mean here.
|
|
956
|
+
*/
|
|
957
|
+
const at = record.ts !== null ? new Date(record.ts) : on;
|
|
958
|
+
const { inputPerMTok, outputPerMTok } = effectivePricing(model, at);
|
|
959
|
+
// Dated, like the rate above: a repriced cache read is part of the reprice.
|
|
960
|
+
const rates = multipliersFor(model, at);
|
|
949
961
|
const per = (tokens: number, rate: number): number => (tokens / 1_000_000) * rate;
|
|
950
962
|
|
|
951
963
|
into.inputUsd += per(record.inputTokens, inputPerMTok);
|
|
@@ -991,7 +1003,11 @@ function add(into: UsageBreakdown, record: UsageRecord, catalogue: PricingCatalo
|
|
|
991
1003
|
|
|
992
1004
|
export interface UsageProfileOptions {
|
|
993
1005
|
catalogue: PricingCatalogue;
|
|
994
|
-
/**
|
|
1006
|
+
/**
|
|
1007
|
+
* The date a record with no timestamp is priced at, and the date the
|
|
1008
|
+
* forward-looking findings (levers, what-ifs) are priced at. A record that
|
|
1009
|
+
* carries its own `ts` is billed at its own moment: see `add`.
|
|
1010
|
+
*/
|
|
995
1011
|
on?: Date;
|
|
996
1012
|
/**
|
|
997
1013
|
* Profile only the records carrying this label — the drill-down, once the
|