@trazum/core 2.4.1 → 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/src/pricing.ts CHANGED
@@ -30,8 +30,13 @@ import type { ModelPricing, PricingTier, TierCondition } from './types.js';
30
30
  * catalogue's headline date derived as the oldest of them rather than typed.
31
31
  */
32
32
  export const PROVIDER_REVIEWED: Readonly<Record<string, string>> = Object.freeze({
33
- /* platform.claude.com/docs/en/about-claude/pricing, read 2026-08-27. */
34
- anthropic: '2026-08-27',
33
+ /*
34
+ * platform.claude.com/docs/en/about-claude/pricing, the models overview and
35
+ * each model's own page, read 2026-10-10. Every existing row matched; five
36
+ * models were added and one (Claude Haiku 5.5) deliberately was not, see the
37
+ * comment above its would-be place in MODELS.
38
+ */
39
+ anthropic: '2026-10-10',
35
40
  /*
36
41
  * developers.openai.com/api/docs/pricing, read 2026-08-31.
37
42
  *
@@ -43,10 +48,30 @@ export const PROVIDER_REVIEWED: Readonly<Record<string, string>> = Object.freeze
43
48
  * and priority. The standard one is the catalogue's, identified by the other
44
49
  * three being its multiples (0.5x, 0.5x, 2x) rather than by trusting the
45
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.
46
73
  */
47
- openai: '2026-08-31',
48
- /* ai.google.dev/gemini-api/docs/pricing, read 2026-08-28. */
49
- google: '2026-08-28',
74
+ google: '2026-10-10',
50
75
  /*
51
76
  * platform.kimi.ai/docs/pricing/chat (platform.moonshot.ai 301s there),
52
77
  * read 2026-09-12. The page prices kimi-k3, kimi-k2.6, kimi-k2.7-code and
@@ -59,8 +84,15 @@ export const PROVIDER_REVIEWED: Readonly<Record<string, string>> = Object.freeze
59
84
  * not read is a number it would be inventing.
60
85
  */
61
86
  moonshot: '2026-09-12',
62
- /* api-docs.deepseek.com/quick_start/pricing, read 2026-08-28. */
63
- deepseek: '2026-08-28',
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',
64
96
  /*
65
97
  * docs.x.ai/docs/models, read 2026-09-12. The page prices grok-4.6, 4.5,
66
98
  * 4.3 and the 4.20 family, each at two rates split at 200k prompt tokens;
@@ -81,7 +113,18 @@ export const PROVIDER_REVIEWED: Readonly<Record<string, string>> = Object.freeze
81
113
  * servable is not the same as checking, and moving the date then would have
82
114
  * said it was.
83
115
  */
84
- mistral: '2026-08-28',
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',
85
128
  });
86
129
 
87
130
  /**
@@ -96,11 +139,12 @@ export const PRICING_LAST_REVIEWED: string = Object.values(PROVIDER_REVIEWED).re
96
139
  /**
97
140
  * The defaults, which are Anthropic's numbers.
98
141
  *
99
- * Still exported and still correct for every Anthropic model, but no longer the
100
- * whole story: a model can override any of these through `multipliers`, and
101
- * anything computing a cost should go through `multipliersFor` rather than
102
- * reading these directly. A cache read is ~10% of input on Anthropic and ~50%
103
- * on OpenAI, and using one number for both invents a saving.
142
+ * Still exported, and still Anthropic's numbers for every model up to Opus 5
143
+ * and Sonnet 5, but no longer the whole story even there: Claude Fable 5.1 and
144
+ * Mythos 5.1 read the cache at 0.025x and Opus 5.5 and Sonnet 5.5 at 0.05x, so
145
+ * a model overrides any of these through `multipliers`, and anything computing
146
+ * a cost should go through `multipliersFor` rather than reading these
147
+ * directly. Using one cache-read ratio for every model invents a saving.
104
148
  */
105
149
  export const COST_MULTIPLIERS = {
106
150
  /** Cache write with a 5-minute TTL. */
@@ -126,12 +170,17 @@ const multipliersMemo = new WeakMap<
126
170
  { cacheWrite5m: number; cacheWrite1h: number; cacheRead: number; batch: number | null }
127
171
  >();
128
172
 
129
- export function multipliersFor(model: ModelPricing): {
173
+ export function multipliersFor(model: ModelPricing, on?: Date): {
130
174
  cacheWrite5m: number;
131
175
  cacheWrite1h: number;
132
176
  cacheRead: number;
133
177
  batch: number | null;
134
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
+ }
135
184
  const cached = multipliersMemo.get(model);
136
185
  if (cached !== undefined) return cached;
137
186
  const m = model.multipliers ?? {};
@@ -145,7 +194,65 @@ export function multipliersFor(model: ModelPricing): {
145
194
  return result;
146
195
  }
147
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
+
148
214
  export const MODELS: ModelPricing[] = [
215
+ {
216
+ /*
217
+ * platform.claude.com/docs/en/about-claude/pricing and the Fable 5.1 model
218
+ * page, read 2026-10-10. The same $10/$50 as Fable 5; what moved is the
219
+ * cache read, $0.25 against $10 of input, a quarter of the 0.1x every
220
+ * earlier Claude model pays. Writes stay at 1.25x and 2x ($12.50, $20),
221
+ * and the Batch API halves both sides ($5/$25).
222
+ */
223
+ id: 'claude-fable-5-1',
224
+ provider: 'anthropic',
225
+ displayName: 'Claude Fable 5.1',
226
+ inputPerMTok: 10,
227
+ outputPerMTok: 50,
228
+ contextWindow: 1_000_000,
229
+ cacheMinTokens: 512,
230
+ multipliers: { cacheRead: 0.025, cacheWrite5m: 1.25, cacheWrite1h: 2, batch: 0.5 },
231
+ capability: 'frontier',
232
+ tier: 'frontier',
233
+ notes: 'Cache hits cost 2.5% of the input price ($0.25). Requires 30-day data retention.',
234
+ },
235
+ {
236
+ /*
237
+ * The same page and prices as Fable 5.1, cache read included ($0.25,
238
+ * 0.025x). Sold only to verified organisations, so priced for the logs that
239
+ * carry it and never offered as somebody's cheaper alternative.
240
+ */
241
+ id: 'claude-mythos-5-1',
242
+ provider: 'anthropic',
243
+ displayName: 'Claude Mythos 5.1',
244
+ recommendable: false,
245
+ inputPerMTok: 10,
246
+ outputPerMTok: 50,
247
+ contextWindow: 1_000_000,
248
+ cacheMinTokens: 512,
249
+ multipliers: { cacheRead: 0.025, cacheWrite5m: 1.25, cacheWrite1h: 2, batch: 0.5 },
250
+ capability: 'frontier',
251
+ tier: 'frontier',
252
+ notes:
253
+ 'Available only to organizations verified through Anthropic\'s verification programs.'
254
+ + ' Requires 30-day data retention.',
255
+ },
149
256
  {
150
257
  id: 'claude-fable-5',
151
258
  provider: 'anthropic',
@@ -156,7 +263,7 @@ export const MODELS: ModelPricing[] = [
156
263
  cacheMinTokens: 512,
157
264
  capability: 'frontier',
158
265
  tier: 'frontier',
159
- notes: 'Highest capability. Requires 30-day data retention.',
266
+ notes: 'Legacy: superseded by Claude Fable 5.1 at the same prices. Requires 30-day data retention.',
160
267
  },
161
268
  {
162
269
  id: 'claude-mythos-5',
@@ -169,7 +276,28 @@ export const MODELS: ModelPricing[] = [
169
276
  cacheMinTokens: 512,
170
277
  capability: 'frontier',
171
278
  tier: 'frontier',
172
- notes: 'Available only through Project Glasswing.',
279
+ notes:
280
+ 'Available only to organizations verified through Anthropic\'s verification programs.'
281
+ + ' Requires 30-day data retention.',
282
+ },
283
+ {
284
+ /*
285
+ * The pricing page and the Opus 5.5 model page, read 2026-10-10. Cheaper
286
+ * than Opus 5 at both ends, $4/$20, and the overview now says to start
287
+ * there for most workloads. Cache read $0.20 is 0.05x, half the ratio of
288
+ * every earlier Opus; writes $5 and $8 are 1.25x and 2x; batch $2/$10.
289
+ */
290
+ id: 'claude-opus-5-5',
291
+ provider: 'anthropic',
292
+ displayName: 'Claude Opus 5.5',
293
+ inputPerMTok: 4,
294
+ outputPerMTok: 20,
295
+ contextWindow: 1_000_000,
296
+ cacheMinTokens: 512,
297
+ multipliers: { cacheRead: 0.05, cacheWrite5m: 1.25, cacheWrite1h: 2, batch: 0.5 },
298
+ capability: 'large',
299
+ tier: 'opus',
300
+ notes: 'Cache hits cost 5% of the input price ($0.20), against 10% on Opus 5 and earlier.',
173
301
  },
174
302
  {
175
303
  id: 'claude-opus-5',
@@ -216,6 +344,45 @@ export const MODELS: ModelPricing[] = [
216
344
  capability: 'large',
217
345
  tier: 'opus',
218
346
  },
347
+ {
348
+ /*
349
+ * Legacy and still served, and never carried here before, so every
350
+ * Claude Code log from late 2025 had it as an unpriced gap. The Opus 4.5
351
+ * model page, read 2026-10-10: model id claude-opus-4-5-20251101, alias
352
+ * claude-opus-4-5, 200K context, $5/$25, 4,096-token cache minimum (the
353
+ * prompt caching page). Like Haiku 4.5, the short alias is the id and the
354
+ * dated canonical form is declared, not derived.
355
+ */
356
+ id: 'claude-opus-4-5',
357
+ aliases: ['claude-opus-4-5-20251101'],
358
+ provider: 'anthropic',
359
+ displayName: 'Claude Opus 4.5',
360
+ inputPerMTok: 5,
361
+ outputPerMTok: 25,
362
+ contextWindow: 200_000,
363
+ cacheMinTokens: 4096,
364
+ capability: 'large',
365
+ tier: 'opus',
366
+ notes: '200K context window and a 64K output cap.',
367
+ },
368
+ {
369
+ /*
370
+ * The pricing page and the Sonnet 5.5 model page, read 2026-10-10. The
371
+ * same $2/$10 as Sonnet 5, with the cache read at $0.10, 0.05x, where
372
+ * Sonnet 5 reads at $0.20. Writes $2.50 and $4; batch $1/$5.
373
+ */
374
+ id: 'claude-sonnet-5-5',
375
+ provider: 'anthropic',
376
+ displayName: 'Claude Sonnet 5.5',
377
+ inputPerMTok: 2,
378
+ outputPerMTok: 10,
379
+ contextWindow: 1_000_000,
380
+ cacheMinTokens: 512,
381
+ multipliers: { cacheRead: 0.05, cacheWrite5m: 1.25, cacheWrite1h: 2, batch: 0.5 },
382
+ capability: 'mid',
383
+ tier: 'sonnet',
384
+ notes: 'Cache hits cost 5% of the input price ($0.10), against 10% ($0.20) on Sonnet 5.',
385
+ },
219
386
  {
220
387
  id: 'claude-sonnet-5',
221
388
  provider: 'anthropic',
@@ -241,6 +408,18 @@ export const MODELS: ModelPricing[] = [
241
408
  capability: 'mid',
242
409
  tier: 'sonnet',
243
410
  },
411
+ /*
412
+ * Claude Haiku 5.5 is not here, on purpose, and its calls are an unpriced
413
+ * gap that names it. The pricing page (read 2026-10-10) prices it by prompt
414
+ * length: $0.10/$0.50 up to 100,000 tokens and $0.50/$2.50 above, where the
415
+ * length counts cache reads and writes and each request is priced on its
416
+ * own. A tier can say that, but nothing that prices a usage log passes one
417
+ * request's prompt length to `effectivePricing`: records are summed per
418
+ * workload, model and day before they are priced. Added today, every Haiku
419
+ * 5.5 call would fall to the undecided ceiling, five times the price most of
420
+ * them paid. It goes in with the change that prices tiered models per
421
+ * request, not before.
422
+ */
244
423
  {
245
424
  id: 'claude-haiku-4-5',
246
425
  // The dated form is the canonical API id and is what a real usage log
@@ -261,13 +440,17 @@ export const MODELS: ModelPricing[] = [
261
440
  // ------------------------------------------------------------------------
262
441
  // OpenAI
263
442
  //
264
- // Caching is AUTOMATIC above 1,024 tokens — there is no cache_control to set,
265
- // and a cached read costs about half the input price rather than a tenth. The
266
- // reordering advice is identical (a prefix is a prefix), the marker advice is
267
- // not, which is why `caching` and `multipliers` exist at all.
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.
268
450
  // ------------------------------------------------------------------------
269
451
  {
270
452
  id: 'gpt-5',
453
+ aliases: ['gpt-5-2025-08-07'],
271
454
  provider: 'openai',
272
455
  displayName: 'GPT-5',
273
456
  inputPerMTok: 1.25,
@@ -278,10 +461,11 @@ export const MODELS: ModelPricing[] = [
278
461
  multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
279
462
  capability: 'frontier',
280
463
  tier: 'frontier',
281
- notes: 'Caching is automatic above 1,024 tokens; there is no cache_control to set.',
464
+ notes: OPENAI_GPT5_NOTE,
282
465
  },
283
466
  {
284
467
  id: 'gpt-5-mini',
468
+ aliases: ['gpt-5-mini-2025-08-07'],
285
469
  provider: 'openai',
286
470
  displayName: 'GPT-5 mini',
287
471
  inputPerMTok: 0.25,
@@ -292,9 +476,11 @@ export const MODELS: ModelPricing[] = [
292
476
  multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
293
477
  capability: 'mid',
294
478
  tier: 'sonnet',
479
+ notes: OPENAI_GPT5_NOTE,
295
480
  },
296
481
  {
297
482
  id: 'gpt-5-nano',
483
+ aliases: ['gpt-5-nano-2025-08-07'],
298
484
  provider: 'openai',
299
485
  displayName: 'GPT-5 nano',
300
486
  inputPerMTok: 0.05,
@@ -305,6 +491,7 @@ export const MODELS: ModelPricing[] = [
305
491
  multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
306
492
  capability: 'small',
307
493
  tier: 'haiku',
494
+ notes: OPENAI_GPT5_NOTE,
308
495
  },
309
496
 
310
497
  // ------------------------------------------------------------------------
@@ -318,8 +505,9 @@ export const MODELS: ModelPricing[] = [
318
505
  outputPerMTok: 10,
319
506
  contextWindow: 1_048_576,
320
507
  cacheMinTokens: 2048,
321
- caching: 'explicit',
322
- multipliers: { cacheRead: 0.25, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
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 },
323
511
  /* Refused on 2026-08-28: still in `GET /v1beta/models`, and a 404 to a request. */
324
512
  retired: {
325
513
  on: '2026-08-28',
@@ -328,7 +516,10 @@ export const MODELS: ModelPricing[] = [
328
516
  },
329
517
  capability: 'large',
330
518
  tier: 'opus',
331
- notes: 'Context caching is billed for storage per hour as well as per read.',
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.',
332
523
  },
333
524
  {
334
525
  id: 'gemini-2.5-flash',
@@ -337,9 +528,10 @@ export const MODELS: ModelPricing[] = [
337
528
  inputPerMTok: 0.3,
338
529
  outputPerMTok: 2.5,
339
530
  contextWindow: 1_048_576,
340
- cacheMinTokens: 1024,
341
- caching: 'explicit',
342
- multipliers: { cacheRead: 0.25, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
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 },
343
535
  /* Refused on 2026-08-28: still in `GET /v1beta/models`, and a 404 to a request. */
344
536
  retired: {
345
537
  on: '2026-08-28',
@@ -352,9 +544,10 @@ export const MODELS: ModelPricing[] = [
352
544
 
353
545
  {
354
546
  /*
355
- * Google's own error message names this as the replacement for the two
356
- * retired 2.5 models, and ai.google.dev/gemini-api/docs/pricing carries the
357
- * 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.
358
551
  *
359
552
  * The introductory price has a published end date and a published successor,
360
553
  * which is exactly what `promo` is for and is the shape the catalogue nearly
@@ -368,8 +561,8 @@ export const MODELS: ModelPricing[] = [
368
561
  outputPerMTok: 7.5,
369
562
  promo: { inputPerMTok: 0.75, outputPerMTok: 3.75, until: '2026-12-31' },
370
563
  contextWindow: 1_048_576,
371
- cacheMinTokens: 1024,
372
- caching: 'explicit',
564
+ cacheMinTokens: 4096,
565
+ caching: 'automatic',
373
566
  /*
374
567
  * $0.075 against $0.75 inside the introductory window, and $0.15 against
375
568
  * $1.50 after it: Google scales the cache price with the headline one, so
@@ -380,7 +573,32 @@ export const MODELS: ModelPricing[] = [
380
573
  capability: 'mid',
381
574
  tier: 'sonnet',
382
575
  notes:
383
- 'Context caching is billed for storage per hour as well as per read:'
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:'
384
602
  + ' $0.50 per 1M tokens per hour through 2026-12-31, $1.00 after.',
385
603
  },
386
604
 
@@ -428,34 +646,97 @@ export const MODELS: ModelPricing[] = [
428
646
  },
429
647
  {
430
648
  /*
431
- * DeepSeek V4 Flash, from api-docs.deepseek.com/quick_start/pricing, read
432
- * 2026-08-28. It replaced `deepseek-v3`, which the API now refuses.
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.
433
655
  *
434
656
  * The base rate is off-peak because off-peak is the common case: peak is
435
657
  * 01:00-04:00 and 06:00-10:00 UTC on weekdays, which is 35 hours of 168.
436
- * 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.
437
664
  */
438
665
  id: 'deepseek-v4-flash',
439
666
  provider: 'deepseek',
440
667
  displayName: 'DeepSeek V4 Flash',
441
- inputPerMTok: 0.22,
442
- outputPerMTok: 0.66,
668
+ recommendable: false,
669
+ inputPerMTok: 0.15,
670
+ outputPerMTok: 0.6,
443
671
  tiers: [
444
672
  {
445
673
  id: 'peak',
446
674
  when: { kind: 'utc-hours', hours: [1, 2, 3, 6, 7, 8, 9], weekdaysOnly: true },
447
- inputPerMTok: 0.44,
448
- outputPerMTok: 1.32,
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 },
449
694
  },
450
695
  ],
451
696
  contextWindow: 1_000_000,
452
697
  cacheMinTokens: 1024,
453
698
  caching: 'automatic',
454
- /* $0.007 cache hit against $0.22 input off-peak. */
455
- multipliers: { cacheRead: 0.0318, cacheWrite5m: 1, cacheWrite1h: 1, batch: null },
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 },
456
701
  capability: 'mid',
457
702
  tier: 'sonnet',
458
- notes: 'Maximum output 384K tokens.',
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,
723
+ tiers: [
724
+ {
725
+ id: 'peak',
726
+ when: { kind: 'utc-hours', hours: [1, 2, 3, 6, 7, 8, 9], weekdaysOnly: true },
727
+ inputPerMTok: 0.3,
728
+ outputPerMTok: 1.2,
729
+ },
730
+ ],
731
+ contextWindow: 1_000_000,
732
+ cacheMinTokens: 1024,
733
+ caching: 'automatic',
734
+ multipliers: { cacheRead: 0.02, cacheWrite5m: 1, cacheWrite1h: 1, batch: null },
735
+ capability: 'mid',
736
+ tier: 'sonnet',
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.',
459
740
  },
460
741
  {
461
742
  /* Same page, same clock. Pro is three times Flash at both ends. */
@@ -548,12 +829,12 @@ export const MODELS: ModelPricing[] = [
548
829
  inputPerMTok: 0.5,
549
830
  outputPerMTok: 1.5,
550
831
  contextWindow: 262_144,
551
- cacheMinTokens: 0,
552
- caching: 'none',
553
- multipliers: { batch: 0.5 },
832
+ cacheMinTokens: null,
833
+ caching: 'unknown',
834
+ multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
554
835
  capability: 'large',
555
836
  tier: 'opus',
556
- notes: 'No prompt caching: reordering still helps readability but saves nothing here.',
837
+ notes: MISTRAL_CACHE_NOTE,
557
838
  },
558
839
  {
559
840
  /*
@@ -576,11 +857,12 @@ export const MODELS: ModelPricing[] = [
576
857
  inputPerMTok: 1.5,
577
858
  outputPerMTok: 7.5,
578
859
  contextWindow: 262_144,
579
- cacheMinTokens: 0,
580
- caching: 'none',
581
- multipliers: { batch: 0.5 },
860
+ cacheMinTokens: null,
861
+ caching: 'unknown',
862
+ multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
582
863
  capability: 'mid',
583
864
  tier: 'sonnet',
865
+ notes: MISTRAL_CACHE_NOTE,
584
866
  },
585
867
  {
586
868
  /* Same page, same day. `mistral-small-2603` is what the API reports. */
@@ -591,11 +873,12 @@ export const MODELS: ModelPricing[] = [
591
873
  inputPerMTok: 0.15,
592
874
  outputPerMTok: 0.6,
593
875
  contextWindow: 262_144,
594
- cacheMinTokens: 0,
595
- caching: 'none',
596
- multipliers: { batch: 0.5 },
876
+ cacheMinTokens: null,
877
+ caching: 'unknown',
878
+ multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
597
879
  capability: 'small',
598
880
  tier: 'haiku',
881
+ notes: MISTRAL_CACHE_NOTE,
599
882
  },
600
883
  ];
601
884
 
@@ -760,6 +1043,40 @@ const pricingMemo = new WeakMap<
760
1043
  { untilMs: number | null; promo: Rates | null; base: Rates }
761
1044
  >();
762
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
+
763
1080
  /** The two unconditional answers for one model, parsed once. */
764
1081
  function cache(model: ModelPricing): {
765
1082
  untilMs: number | null;
@@ -829,6 +1146,11 @@ export function effectivePricing(
829
1146
  on: Date = new Date(),
830
1147
  context: { inputTokens?: number } = {},
831
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
+ }
832
1154
  const memo = pricingMemo.get(model) ?? cache(model);
833
1155
 
834
1156
  /**