@trazum/core 2.4.2 → 2.5.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/dist/advisories.d.ts.map +1 -1
- package/dist/advisories.js +17 -10
- package/dist/advisories.js.map +1 -1
- package/dist/answer.d.ts.map +1 -1
- package/dist/answer.js +2 -1
- package/dist/answer.js.map +1 -1
- package/dist/anthropic-usage.d.ts +6 -0
- package/dist/anthropic-usage.d.ts.map +1 -1
- package/dist/anthropic-usage.js +1 -0
- package/dist/anthropic-usage.js.map +1 -1
- package/dist/billing.d.ts +20 -0
- package/dist/billing.d.ts.map +1 -0
- package/dist/billing.js +32 -0
- package/dist/billing.js.map +1 -0
- package/dist/budget.d.ts.map +1 -1
- package/dist/budget.js +18 -8
- package/dist/budget.js.map +1 -1
- package/dist/connector.d.ts +20 -1
- package/dist/connector.d.ts.map +1 -1
- package/dist/connector.js +42 -11
- package/dist/connector.js.map +1 -1
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +4 -2
- package/dist/conversation.js.map +1 -1
- package/dist/gateway.d.ts.map +1 -1
- package/dist/gateway.js +5 -3
- package/dist/gateway.js.map +1 -1
- package/dist/guard.d.ts.map +1 -1
- package/dist/guard.js +7 -5
- package/dist/guard.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/input-shape.d.ts.map +1 -1
- package/dist/input-shape.js +4 -2
- package/dist/input-shape.js.map +1 -1
- package/dist/ladder.d.ts.map +1 -1
- package/dist/ladder.js +2 -1
- package/dist/ladder.js.map +1 -1
- package/dist/levers.d.ts.map +1 -1
- package/dist/levers.js +5 -2
- package/dist/levers.js.map +1 -1
- package/dist/openai-usage.d.ts +18 -0
- package/dist/openai-usage.d.ts.map +1 -1
- package/dist/openai-usage.js +26 -1
- package/dist/openai-usage.js.map +1 -1
- package/dist/openrouter-activity.d.ts +6 -0
- package/dist/openrouter-activity.d.ts.map +1 -1
- package/dist/openrouter-activity.js +7 -0
- package/dist/openrouter-activity.js.map +1 -1
- package/dist/output-shape.d.ts.map +1 -1
- package/dist/output-shape.js +2 -1
- package/dist/output-shape.js.map +1 -1
- package/dist/pricing.d.ts +36 -4
- package/dist/pricing.d.ts.map +1 -1
- package/dist/pricing.js +804 -91
- package/dist/pricing.js.map +1 -1
- package/dist/receipt.d.ts +17 -0
- package/dist/receipt.d.ts.map +1 -1
- package/dist/receipt.js +10 -0
- package/dist/receipt.js.map +1 -1
- package/dist/repeats.d.ts.map +1 -1
- package/dist/repeats.js +4 -2
- package/dist/repeats.js.map +1 -1
- package/dist/reprice.d.ts +18 -2
- package/dist/reprice.d.ts.map +1 -1
- package/dist/reprice.js +34 -12
- package/dist/reprice.js.map +1 -1
- package/dist/rollup.d.ts.map +1 -1
- package/dist/rollup.js +3 -0
- package/dist/rollup.js.map +1 -1
- package/dist/savings.d.ts.map +1 -1
- package/dist/savings.js +10 -3
- package/dist/savings.js.map +1 -1
- package/dist/session-cost.d.ts.map +1 -1
- package/dist/session-cost.js +5 -2
- package/dist/session-cost.js.map +1 -1
- package/dist/session-ledger.d.ts.map +1 -1
- package/dist/session-ledger.js +12 -7
- package/dist/session-ledger.js.map +1 -1
- package/dist/truncation-retry.d.ts.map +1 -1
- package/dist/truncation-retry.js +4 -2
- package/dist/truncation-retry.js.map +1 -1
- package/dist/ttl-fit.d.ts +8 -0
- package/dist/ttl-fit.d.ts.map +1 -1
- package/dist/ttl-fit.js +25 -14
- package/dist/ttl-fit.js.map +1 -1
- package/dist/types.d.ts +51 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/usage.d.ts +36 -1
- package/dist/usage.d.ts.map +1 -1
- package/dist/usage.js +91 -10
- package/dist/usage.js.map +1 -1
- package/package.json +1 -1
- package/src/advisories.ts +18 -10
- package/src/answer.ts +6 -1
- package/src/anthropic-usage.ts +7 -0
- package/src/billing.ts +37 -0
- package/src/budget.ts +18 -8
- package/src/connector.ts +58 -10
- package/src/conversation.ts +4 -2
- package/src/gateway.ts +5 -3
- package/src/guard.ts +7 -5
- package/src/index.ts +1 -1
- package/src/input-shape.ts +4 -2
- package/src/ladder.ts +2 -1
- package/src/levers.ts +5 -2
- package/src/openai-usage.ts +44 -2
- package/src/openrouter-activity.ts +13 -0
- package/src/output-shape.ts +2 -1
- package/src/pricing.ts +854 -96
- package/src/receipt.ts +23 -1
- package/src/repeats.ts +4 -2
- package/src/reprice.ts +57 -16
- package/src/rollup.ts +3 -0
- package/src/savings.ts +12 -5
- package/src/session-cost.ts +5 -2
- package/src/session-ledger.ts +18 -9
- package/src/truncation-retry.ts +4 -2
- package/src/ttl-fit.ts +39 -15
- package/src/types.ts +46 -0
- package/src/usage.ts +129 -11
package/src/pricing.ts
CHANGED
|
@@ -32,9 +32,9 @@ import type { ModelPricing, PricingTier, TierCondition } from './types.js';
|
|
|
32
32
|
export const PROVIDER_REVIEWED: Readonly<Record<string, string>> = Object.freeze({
|
|
33
33
|
/*
|
|
34
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
|
|
36
|
-
* models were added
|
|
37
|
-
*
|
|
35
|
+
* each model's own page, read 2026-10-10. Every existing row matched and
|
|
36
|
+
* five models were added. Claude Haiku 5.5 waited for per-request pricing,
|
|
37
|
+
* because it bills by prompt length, and was added with it on 2026-10-11.
|
|
38
38
|
*/
|
|
39
39
|
anthropic: '2026-10-10',
|
|
40
40
|
/*
|
|
@@ -48,34 +48,56 @@ 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) waited for
|
|
58
|
+
* per-request pricing, since each doubles its rates for prompts over 272K
|
|
59
|
+
* tokens, and was added with it the same day, from each model's own page.
|
|
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, and Gemini 3.1
|
|
70
|
+
* Pro Preview on 2026-10-11 with per-request pricing, which its prompts
|
|
71
|
+
* over 200k need. 2.5 Pro's own long-context rate is named in its note.
|
|
51
72
|
*/
|
|
52
|
-
|
|
53
|
-
/* ai.google.dev/gemini-api/docs/pricing, read 2026-08-28. */
|
|
54
|
-
google: '2026-08-28',
|
|
73
|
+
google: '2026-10-10',
|
|
55
74
|
/*
|
|
56
|
-
* platform.kimi.ai/docs/pricing/chat
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* repository does not hold. The newer models are not added: the page states
|
|
63
|
-
* no context window for any of them, and a context window this table did
|
|
64
|
-
* not read is a number it would be inventing.
|
|
75
|
+
* platform.kimi.ai/docs/pricing/chat, models and pricing/batch, read
|
|
76
|
+
* 2026-10-11 in their Markdown form; the HTML renders its tables in the
|
|
77
|
+
* browser, which is why the review of 2026-09-12 found no context window
|
|
78
|
+
* and added nothing. Kimi K3, K2.7 Code (and its high-speed variant) and
|
|
79
|
+
* K2.6 are priced now. `kimi-k2` was discontinued on 2026-05-25 by the
|
|
80
|
+
* provider's own notice, so it stays priced and is no longer recommended.
|
|
65
81
|
*/
|
|
66
|
-
moonshot: '2026-
|
|
67
|
-
/* api-docs.deepseek.com/quick_start/pricing, read 2026-08-28. */
|
|
68
|
-
deepseek: '2026-08-28',
|
|
82
|
+
moonshot: '2026-10-11',
|
|
69
83
|
/*
|
|
70
|
-
* docs.
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* would understate it by half while looking exact.
|
|
84
|
+
* api-docs.deepseek.com/quick_start/pricing read 2026-10-10, and the
|
|
85
|
+
* 2026-09-10 announcement (api-docs.deepseek.com/news/news260910) read
|
|
86
|
+
* 2026-10-11 for the instant: "New pricing takes effect at 04:00 UTC on
|
|
87
|
+
* Sept 10, 2026." V4 Flash was retired that day and its id now bills at
|
|
88
|
+
* the new deepseek-flash price, so the old price lives in its `history`.
|
|
89
|
+
* V4 Pro matched the page unchanged.
|
|
77
90
|
*/
|
|
78
|
-
|
|
91
|
+
deepseek: '2026-10-10',
|
|
92
|
+
/*
|
|
93
|
+
* docs.x.ai/developers/models, each model's page and the 2026-05-15
|
|
94
|
+
* retirement notice, read 2026-10-11 in their Markdown form. Grok 4.7, 4.6,
|
|
95
|
+
* 4.5, 4.3, the 4.20 family and Build 0.1 are priced, each with the rate a
|
|
96
|
+
* request pays once its prompt reaches 200k tokens, which per-request
|
|
97
|
+
* pricing can now express. `grok-4` was retired on 2026-05-15 and its calls
|
|
98
|
+
* have billed at Grok 4.3's prices since, so it carries both periods.
|
|
99
|
+
*/
|
|
100
|
+
xai: '2026-10-11',
|
|
79
101
|
/*
|
|
80
102
|
* `mistral.ai/pricing`, read 2026-08-28 — by a human, because the page
|
|
81
103
|
* renders its table in the browser and serves a fetch only an FAQ example
|
|
@@ -86,7 +108,18 @@ export const PROVIDER_REVIEWED: Readonly<Record<string, string>> = Object.freeze
|
|
|
86
108
|
* servable is not the same as checking, and moving the date then would have
|
|
87
109
|
* said it was.
|
|
88
110
|
*/
|
|
89
|
-
|
|
111
|
+
/*
|
|
112
|
+
* Re-read 2026-10-11 from mistral.ai/cloud/inference/ and the model cards
|
|
113
|
+
* at mistral.ai/news/mistral-3/, /mistral-small-4/ and
|
|
114
|
+
* /vibe-remote-agents-mistral-medium-3-5/, which carry the prices in their
|
|
115
|
+
* markup (`data-prices`) and bind the `-latest` ids. Every price matched.
|
|
116
|
+
* What changed is caching: the inference page offers "Cached input tokens
|
|
117
|
+
* -90% on input token", and every one of the three cards marks its input
|
|
118
|
+
* price as carrying the cache discount. This table said Mistral had no
|
|
119
|
+
* prompt caching at all. docs.mistral.ai, where a minimum or a mechanism
|
|
120
|
+
* would be, is not reachable from here, so neither is claimed.
|
|
121
|
+
*/
|
|
122
|
+
mistral: '2026-10-11',
|
|
90
123
|
});
|
|
91
124
|
|
|
92
125
|
/**
|
|
@@ -132,12 +165,17 @@ const multipliersMemo = new WeakMap<
|
|
|
132
165
|
{ cacheWrite5m: number; cacheWrite1h: number; cacheRead: number; batch: number | null }
|
|
133
166
|
>();
|
|
134
167
|
|
|
135
|
-
export function multipliersFor(model: ModelPricing): {
|
|
168
|
+
export function multipliersFor(model: ModelPricing, on?: Date): {
|
|
136
169
|
cacheWrite5m: number;
|
|
137
170
|
cacheWrite1h: number;
|
|
138
171
|
cacheRead: number;
|
|
139
172
|
batch: number | null;
|
|
140
173
|
} {
|
|
174
|
+
// A dated question about a repriced model asks the period in force then.
|
|
175
|
+
if (on !== undefined && model.history !== undefined) {
|
|
176
|
+
const period = pricedAt(model, on);
|
|
177
|
+
if (period !== model) return multipliersFor(period);
|
|
178
|
+
}
|
|
141
179
|
const cached = multipliersMemo.get(model);
|
|
142
180
|
if (cached !== undefined) return cached;
|
|
143
181
|
const m = model.multipliers ?? {};
|
|
@@ -151,6 +189,57 @@ export function multipliersFor(model: ModelPricing): {
|
|
|
151
189
|
return result;
|
|
152
190
|
}
|
|
153
191
|
|
|
192
|
+
/** Five minutes and an hour: Anthropic's two cache entries, and the default. */
|
|
193
|
+
const ANTHROPIC_TTL = Object.freeze({ writeMs: 5 * 60_000, longerMs: 60 * 60_000 });
|
|
194
|
+
|
|
195
|
+
/** How long a model's cache entries live. See `ModelPricing.cacheTtl`. */
|
|
196
|
+
export function cacheTtlOf(model: ModelPricing): { writeMs: number; longerMs: number | null } {
|
|
197
|
+
return model.cacheTtl ?? ANTHROPIC_TTL;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Mistral discounts cached input by 90% and publishes nothing else about it
|
|
202
|
+
* that can be read from here: not how caching starts, not a minimum, not a
|
|
203
|
+
* write price. Its usage reports no write tokens, so the write ratio of 1 is
|
|
204
|
+
* never multiplied by anything; it is there so the default (Anthropic's 1.25)
|
|
205
|
+
* cannot be.
|
|
206
|
+
*/
|
|
207
|
+
/** What the GPT-5 family's three rows share, from OpenAI's guide and deprecations page, read 2026-10-11. */
|
|
208
|
+
/** xAI's long-context band: a request whose prompt reaches 200,000 tokens, at the published higher rates. */
|
|
209
|
+
const GROK_LONG = (inputPerMTok: number, outputPerMTok: number): PricingTier => ({
|
|
210
|
+
id: 'from-200k',
|
|
211
|
+
when: { kind: 'input-tokens-at-least', tokens: 200_000 },
|
|
212
|
+
inputPerMTok,
|
|
213
|
+
outputPerMTok,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Said on every row that caches but whose provider publishes no minimum
|
|
218
|
+
* prefix. The bill still prices its cache reads and writes, which are
|
|
219
|
+
* published; what goes quiet is the advice that needs the minimum to promise
|
|
220
|
+
* a saving, and the reader is told why rather than handed a number from
|
|
221
|
+
* nowhere. The catalogue tests hold a null minimum on a caching model to
|
|
222
|
+
* carrying this sentence, so a forgotten minimum cannot pass as a deliberate one.
|
|
223
|
+
*/
|
|
224
|
+
const NO_PUBLISHED_CACHE_MINIMUM =
|
|
225
|
+
'Caches automatically, but no minimum prefix length is published, so no caching advice is given for it.';
|
|
226
|
+
|
|
227
|
+
/** OpenAI's GPT-5.6 and later: one cache entry, living at least 30 minutes. */
|
|
228
|
+
const OPENAI_CACHE_TTL = Object.freeze({ writeMs: 30 * 60_000, longerMs: null });
|
|
229
|
+
|
|
230
|
+
const OPENAI_CURRENT_NOTE =
|
|
231
|
+
'Priced per request by prompt length: over 272,000 input tokens, 2x input and cache and 1.5x output for'
|
|
232
|
+
+ ' the whole request. Cache writes cost 1.25x input and an entry lives at least 30 minutes.';
|
|
233
|
+
|
|
234
|
+
const OPENAI_GPT5_NOTE =
|
|
235
|
+
'Caching is automatic, with no cache_control to set. OpenAI says the minimum for models before GPT-5.6 varies by'
|
|
236
|
+
+ ' request settings, so 1,024 here is the nearest published floor. The 2025-08-07 snapshot shuts down on'
|
|
237
|
+
+ ' 2026-12-11; OpenAI names the gpt-5.6 models as replacements.';
|
|
238
|
+
|
|
239
|
+
const MISTRAL_CACHE_NOTE =
|
|
240
|
+
'Cached input costs 10% of the input price (mistral.ai/cloud/inference, read 2026-10-11). How caching starts and'
|
|
241
|
+
+ ' its minimum are not published anywhere Trazum can read, so no caching advice is given for it.';
|
|
242
|
+
|
|
154
243
|
export const MODELS: ModelPricing[] = [
|
|
155
244
|
{
|
|
156
245
|
/*
|
|
@@ -348,18 +437,45 @@ export const MODELS: ModelPricing[] = [
|
|
|
348
437
|
capability: 'mid',
|
|
349
438
|
tier: 'sonnet',
|
|
350
439
|
},
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
440
|
+
{
|
|
441
|
+
/*
|
|
442
|
+
* The pricing page, the models overview and the prompt-caching page, read
|
|
443
|
+
* 2026-10-10. Priced by prompt length, per request: $0.10/$0.50 for a
|
|
444
|
+
* prompt up to 100,000 tokens and $0.50/$2.50 over it, where the length
|
|
445
|
+
* counts every input token the request carried, cache reads and writes
|
|
446
|
+
* included, and "each request is priced on its own". Caching is the same
|
|
447
|
+
* ratio in both bands (hits $0.01 and $0.05, writes $0.125/$0.20 and
|
|
448
|
+
* $0.625/$1), and so is batch ($0.05/$0.25, $0.25/$1.25). 1M context,
|
|
449
|
+
* 128K output, a 512-token cache minimum.
|
|
450
|
+
*
|
|
451
|
+
* It waited for the change that prices a log per request. A record that
|
|
452
|
+
* is one call carries its own prompt; a row out of a usage report sums
|
|
453
|
+
* requests, and is billed at the over-100,000 rate unless its whole
|
|
454
|
+
* prompt total is under the line, with the cheaper figure printed beside
|
|
455
|
+
* it. See `PromptSize`.
|
|
456
|
+
*/
|
|
457
|
+
id: 'claude-haiku-5-5',
|
|
458
|
+
provider: 'anthropic',
|
|
459
|
+
displayName: 'Claude Haiku 5.5',
|
|
460
|
+
inputPerMTok: 0.1,
|
|
461
|
+
outputPerMTok: 0.5,
|
|
462
|
+
contextWindow: 1_000_000,
|
|
463
|
+
cacheMinTokens: 512,
|
|
464
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1.25, cacheWrite1h: 2, batch: 0.5 },
|
|
465
|
+
tiers: [
|
|
466
|
+
{
|
|
467
|
+
id: 'over-100k',
|
|
468
|
+
when: { kind: 'input-tokens-above', tokens: 100_000 },
|
|
469
|
+
inputPerMTok: 0.5,
|
|
470
|
+
outputPerMTok: 2.5,
|
|
471
|
+
},
|
|
472
|
+
],
|
|
473
|
+
capability: 'small',
|
|
474
|
+
tier: 'haiku',
|
|
475
|
+
notes:
|
|
476
|
+
'Priced per request by prompt length: $0.10/$0.50 up to 100,000 input tokens, cache reads'
|
|
477
|
+
+ ' and writes included, and $0.50/$2.50 over it.',
|
|
478
|
+
},
|
|
363
479
|
{
|
|
364
480
|
id: 'claude-haiku-4-5',
|
|
365
481
|
// The dated form is the canonical API id and is what a real usage log
|
|
@@ -380,13 +496,200 @@ export const MODELS: ModelPricing[] = [
|
|
|
380
496
|
// ------------------------------------------------------------------------
|
|
381
497
|
// OpenAI
|
|
382
498
|
//
|
|
383
|
-
// Caching is AUTOMATIC
|
|
384
|
-
//
|
|
385
|
-
//
|
|
386
|
-
//
|
|
499
|
+
// Caching is AUTOMATIC, with no cache_control to set, and a cached read costs
|
|
500
|
+
// a tenth of input on these three ($0.125 against $1.25). The reordering
|
|
501
|
+
// advice is identical (a prefix is a prefix), the marker advice is not, which
|
|
502
|
+
// is why `caching` and `multipliers` exist at all. The 1,024 minimum is the
|
|
503
|
+
// one OpenAI publishes for GPT-5.6 and later; for these older models its
|
|
504
|
+
// guide says the minimum "varies by request settings", so it is the nearest
|
|
505
|
+
// published floor, not a promise, and each row's note says so.
|
|
506
|
+
// ------------------------------------------------------------------------
|
|
507
|
+
// GPT-5.6 and later: OpenAI's current generation.
|
|
508
|
+
//
|
|
509
|
+
// The pricing page and each model's page, read 2026-10-10 and 2026-10-11,
|
|
510
|
+
// and the prompt-caching guide. Three things set them apart from GPT-5:
|
|
511
|
+
//
|
|
512
|
+
// - **A long-context band, per request.** "Prompts with more than 272K input
|
|
513
|
+
// tokens are priced at 2x input and cache rates and 1.5x output for the
|
|
514
|
+
// full request." 272,000, the GPT-5 era's own input limit; a tier.
|
|
515
|
+
// - **Cache writes are billed**, at 1.25x the uncached input rate, and the
|
|
516
|
+
// usage object reports them (`cache_write_tokens`). Reads are 10% of input,
|
|
517
|
+
// 5% on GPT-6.1 Sol.
|
|
518
|
+
// - **An entry lives at least 30 minutes** after its last write or read, and
|
|
519
|
+
// there is no longer one to buy: `cacheTtl`.
|
|
520
|
+
//
|
|
521
|
+
// Caching is implicit by default (a breakpoint at the end of the latest
|
|
522
|
+
// eligible message) from a 1,024-token minimum. Batch and Flex are half
|
|
523
|
+
// price; Fast, Ultrafast and regional processing are other service tiers
|
|
524
|
+
// and are not priced here. Every model: 1,050,000 context, 922,000 input at
|
|
525
|
+
// most, 128,000 output.
|
|
526
|
+
// ------------------------------------------------------------------------
|
|
527
|
+
{
|
|
528
|
+
id: 'gpt-6-astra',
|
|
529
|
+
provider: 'openai',
|
|
530
|
+
displayName: 'GPT-6 Astra',
|
|
531
|
+
inputPerMTok: 10,
|
|
532
|
+
outputPerMTok: 50,
|
|
533
|
+
contextWindow: 1_050_000,
|
|
534
|
+
cacheMinTokens: 1024,
|
|
535
|
+
caching: 'automatic',
|
|
536
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1.25, cacheWrite1h: 1.25, batch: 0.5 },
|
|
537
|
+
cacheTtl: OPENAI_CACHE_TTL,
|
|
538
|
+
tiers: [
|
|
539
|
+
{
|
|
540
|
+
id: 'over-272k',
|
|
541
|
+
when: { kind: 'input-tokens-above', tokens: 272_000 },
|
|
542
|
+
inputPerMTok: 20,
|
|
543
|
+
outputPerMTok: 75,
|
|
544
|
+
},
|
|
545
|
+
],
|
|
546
|
+
capability: 'frontier',
|
|
547
|
+
tier: 'frontier',
|
|
548
|
+
notes: OPENAI_CURRENT_NOTE,
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
id: 'gpt-6.1-sol',
|
|
552
|
+
provider: 'openai',
|
|
553
|
+
displayName: 'GPT-6.1 Sol',
|
|
554
|
+
inputPerMTok: 2,
|
|
555
|
+
outputPerMTok: 10,
|
|
556
|
+
contextWindow: 1_050_000,
|
|
557
|
+
cacheMinTokens: 1024,
|
|
558
|
+
caching: 'automatic',
|
|
559
|
+
multipliers: { cacheRead: 0.05, cacheWrite5m: 1.25, cacheWrite1h: 1.25, batch: 0.5 },
|
|
560
|
+
cacheTtl: OPENAI_CACHE_TTL,
|
|
561
|
+
tiers: [
|
|
562
|
+
{
|
|
563
|
+
id: 'over-272k',
|
|
564
|
+
when: { kind: 'input-tokens-above', tokens: 272_000 },
|
|
565
|
+
inputPerMTok: 4,
|
|
566
|
+
outputPerMTok: 15,
|
|
567
|
+
},
|
|
568
|
+
],
|
|
569
|
+
capability: 'large',
|
|
570
|
+
tier: 'opus',
|
|
571
|
+
notes: OPENAI_CURRENT_NOTE,
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
id: 'gpt-6-sol',
|
|
575
|
+
provider: 'openai',
|
|
576
|
+
displayName: 'GPT-6 Sol',
|
|
577
|
+
inputPerMTok: 2,
|
|
578
|
+
outputPerMTok: 10,
|
|
579
|
+
contextWindow: 1_050_000,
|
|
580
|
+
cacheMinTokens: 1024,
|
|
581
|
+
caching: 'automatic',
|
|
582
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1.25, cacheWrite1h: 1.25, batch: 0.5 },
|
|
583
|
+
cacheTtl: OPENAI_CACHE_TTL,
|
|
584
|
+
tiers: [
|
|
585
|
+
{
|
|
586
|
+
id: 'over-272k',
|
|
587
|
+
when: { kind: 'input-tokens-above', tokens: 272_000 },
|
|
588
|
+
inputPerMTok: 4,
|
|
589
|
+
outputPerMTok: 15,
|
|
590
|
+
},
|
|
591
|
+
],
|
|
592
|
+
capability: 'large',
|
|
593
|
+
tier: 'opus',
|
|
594
|
+
notes: OPENAI_CURRENT_NOTE,
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
id: 'gpt-6-luna',
|
|
598
|
+
provider: 'openai',
|
|
599
|
+
displayName: 'GPT-6 Luna',
|
|
600
|
+
inputPerMTok: 0.1,
|
|
601
|
+
outputPerMTok: 0.5,
|
|
602
|
+
contextWindow: 1_050_000,
|
|
603
|
+
cacheMinTokens: 1024,
|
|
604
|
+
caching: 'automatic',
|
|
605
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1.25, cacheWrite1h: 1.25, batch: 0.5 },
|
|
606
|
+
cacheTtl: OPENAI_CACHE_TTL,
|
|
607
|
+
tiers: [
|
|
608
|
+
{
|
|
609
|
+
id: 'over-272k',
|
|
610
|
+
when: { kind: 'input-tokens-above', tokens: 272_000 },
|
|
611
|
+
inputPerMTok: 0.2,
|
|
612
|
+
outputPerMTok: 0.75,
|
|
613
|
+
},
|
|
614
|
+
],
|
|
615
|
+
capability: 'small',
|
|
616
|
+
tier: 'haiku',
|
|
617
|
+
notes: OPENAI_CURRENT_NOTE,
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
id: 'gpt-5.6-sol',
|
|
621
|
+
provider: 'openai',
|
|
622
|
+
displayName: 'GPT-5.6 Sol',
|
|
623
|
+
inputPerMTok: 4,
|
|
624
|
+
outputPerMTok: 20,
|
|
625
|
+
contextWindow: 1_050_000,
|
|
626
|
+
cacheMinTokens: 1024,
|
|
627
|
+
caching: 'automatic',
|
|
628
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1.25, cacheWrite1h: 1.25, batch: 0.5 },
|
|
629
|
+
cacheTtl: OPENAI_CACHE_TTL,
|
|
630
|
+
tiers: [
|
|
631
|
+
{
|
|
632
|
+
id: 'over-272k',
|
|
633
|
+
when: { kind: 'input-tokens-above', tokens: 272_000 },
|
|
634
|
+
inputPerMTok: 8,
|
|
635
|
+
outputPerMTok: 30,
|
|
636
|
+
},
|
|
637
|
+
],
|
|
638
|
+
capability: 'large',
|
|
639
|
+
tier: 'opus',
|
|
640
|
+
notes: OPENAI_CURRENT_NOTE,
|
|
641
|
+
},
|
|
642
|
+
{
|
|
643
|
+
id: 'gpt-5.6-terra',
|
|
644
|
+
provider: 'openai',
|
|
645
|
+
displayName: 'GPT-5.6 Terra',
|
|
646
|
+
inputPerMTok: 2,
|
|
647
|
+
outputPerMTok: 12,
|
|
648
|
+
contextWindow: 1_050_000,
|
|
649
|
+
cacheMinTokens: 1024,
|
|
650
|
+
caching: 'automatic',
|
|
651
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1.25, cacheWrite1h: 1.25, batch: 0.5 },
|
|
652
|
+
cacheTtl: OPENAI_CACHE_TTL,
|
|
653
|
+
tiers: [
|
|
654
|
+
{
|
|
655
|
+
id: 'over-272k',
|
|
656
|
+
when: { kind: 'input-tokens-above', tokens: 272_000 },
|
|
657
|
+
inputPerMTok: 4,
|
|
658
|
+
outputPerMTok: 18,
|
|
659
|
+
},
|
|
660
|
+
],
|
|
661
|
+
capability: 'mid',
|
|
662
|
+
tier: 'sonnet',
|
|
663
|
+
notes: OPENAI_CURRENT_NOTE,
|
|
664
|
+
},
|
|
665
|
+
{
|
|
666
|
+
id: 'gpt-5.6-luna',
|
|
667
|
+
provider: 'openai',
|
|
668
|
+
displayName: 'GPT-5.6 Luna',
|
|
669
|
+
inputPerMTok: 0.2,
|
|
670
|
+
outputPerMTok: 1.2,
|
|
671
|
+
contextWindow: 1_050_000,
|
|
672
|
+
cacheMinTokens: 1024,
|
|
673
|
+
caching: 'automatic',
|
|
674
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1.25, cacheWrite1h: 1.25, batch: 0.5 },
|
|
675
|
+
cacheTtl: OPENAI_CACHE_TTL,
|
|
676
|
+
tiers: [
|
|
677
|
+
{
|
|
678
|
+
id: 'over-272k',
|
|
679
|
+
when: { kind: 'input-tokens-above', tokens: 272_000 },
|
|
680
|
+
inputPerMTok: 0.4,
|
|
681
|
+
outputPerMTok: 1.8,
|
|
682
|
+
},
|
|
683
|
+
],
|
|
684
|
+
capability: 'small',
|
|
685
|
+
tier: 'haiku',
|
|
686
|
+
notes: OPENAI_CURRENT_NOTE,
|
|
687
|
+
},
|
|
688
|
+
|
|
387
689
|
// ------------------------------------------------------------------------
|
|
388
690
|
{
|
|
389
691
|
id: 'gpt-5',
|
|
692
|
+
aliases: ['gpt-5-2025-08-07'],
|
|
390
693
|
provider: 'openai',
|
|
391
694
|
displayName: 'GPT-5',
|
|
392
695
|
inputPerMTok: 1.25,
|
|
@@ -397,10 +700,11 @@ export const MODELS: ModelPricing[] = [
|
|
|
397
700
|
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
398
701
|
capability: 'frontier',
|
|
399
702
|
tier: 'frontier',
|
|
400
|
-
notes:
|
|
703
|
+
notes: OPENAI_GPT5_NOTE,
|
|
401
704
|
},
|
|
402
705
|
{
|
|
403
706
|
id: 'gpt-5-mini',
|
|
707
|
+
aliases: ['gpt-5-mini-2025-08-07'],
|
|
404
708
|
provider: 'openai',
|
|
405
709
|
displayName: 'GPT-5 mini',
|
|
406
710
|
inputPerMTok: 0.25,
|
|
@@ -411,9 +715,11 @@ export const MODELS: ModelPricing[] = [
|
|
|
411
715
|
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
412
716
|
capability: 'mid',
|
|
413
717
|
tier: 'sonnet',
|
|
718
|
+
notes: OPENAI_GPT5_NOTE,
|
|
414
719
|
},
|
|
415
720
|
{
|
|
416
721
|
id: 'gpt-5-nano',
|
|
722
|
+
aliases: ['gpt-5-nano-2025-08-07'],
|
|
417
723
|
provider: 'openai',
|
|
418
724
|
displayName: 'GPT-5 nano',
|
|
419
725
|
inputPerMTok: 0.05,
|
|
@@ -424,6 +730,7 @@ export const MODELS: ModelPricing[] = [
|
|
|
424
730
|
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
425
731
|
capability: 'small',
|
|
426
732
|
tier: 'haiku',
|
|
733
|
+
notes: OPENAI_GPT5_NOTE,
|
|
427
734
|
},
|
|
428
735
|
|
|
429
736
|
// ------------------------------------------------------------------------
|
|
@@ -437,8 +744,9 @@ export const MODELS: ModelPricing[] = [
|
|
|
437
744
|
outputPerMTok: 10,
|
|
438
745
|
contextWindow: 1_048_576,
|
|
439
746
|
cacheMinTokens: 2048,
|
|
440
|
-
caching: '
|
|
441
|
-
|
|
747
|
+
caching: 'automatic',
|
|
748
|
+
/* $0.125 cached against $1.25 input for prompts up to 200k, read 2026-10-10. */
|
|
749
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
442
750
|
/* Refused on 2026-08-28: still in `GET /v1beta/models`, and a 404 to a request. */
|
|
443
751
|
retired: {
|
|
444
752
|
on: '2026-08-28',
|
|
@@ -447,7 +755,10 @@ export const MODELS: ModelPricing[] = [
|
|
|
447
755
|
},
|
|
448
756
|
capability: 'large',
|
|
449
757
|
tier: 'opus',
|
|
450
|
-
notes:
|
|
758
|
+
notes:
|
|
759
|
+
'Prompts over 200k tokens are billed $2.50 in and $15.00 out; this row prices every prompt at the up-to-200k rate,'
|
|
760
|
+
+ ' because no log Trazum prices says how long each prompt was. Implicit caching is automatic; explicit caching is'
|
|
761
|
+
+ ' also billed for storage per hour.',
|
|
451
762
|
},
|
|
452
763
|
{
|
|
453
764
|
id: 'gemini-2.5-flash',
|
|
@@ -456,9 +767,10 @@ export const MODELS: ModelPricing[] = [
|
|
|
456
767
|
inputPerMTok: 0.3,
|
|
457
768
|
outputPerMTok: 2.5,
|
|
458
769
|
contextWindow: 1_048_576,
|
|
459
|
-
cacheMinTokens:
|
|
460
|
-
caching: '
|
|
461
|
-
|
|
770
|
+
cacheMinTokens: 2048,
|
|
771
|
+
caching: 'automatic',
|
|
772
|
+
/* $0.03 cached against $0.30 input (text), read 2026-10-10. */
|
|
773
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
462
774
|
/* Refused on 2026-08-28: still in `GET /v1beta/models`, and a 404 to a request. */
|
|
463
775
|
retired: {
|
|
464
776
|
on: '2026-08-28',
|
|
@@ -471,9 +783,10 @@ export const MODELS: ModelPricing[] = [
|
|
|
471
783
|
|
|
472
784
|
{
|
|
473
785
|
/*
|
|
474
|
-
* Google's own error message names this as the replacement for the
|
|
475
|
-
*
|
|
476
|
-
* rates, read 2026-08-28
|
|
786
|
+
* Google's own error message names this as the replacement for the retired
|
|
787
|
+
* 2.5 Flash (the 2.5 Pro refusal names 3.1 Pro Preview), and
|
|
788
|
+
* ai.google.dev/gemini-api/docs/pricing carries the rates, read 2026-08-28
|
|
789
|
+
* and again on 2026-10-10.
|
|
477
790
|
*
|
|
478
791
|
* The introductory price has a published end date and a published successor,
|
|
479
792
|
* which is exactly what `promo` is for and is the shape the catalogue nearly
|
|
@@ -487,8 +800,8 @@ export const MODELS: ModelPricing[] = [
|
|
|
487
800
|
outputPerMTok: 7.5,
|
|
488
801
|
promo: { inputPerMTok: 0.75, outputPerMTok: 3.75, until: '2026-12-31' },
|
|
489
802
|
contextWindow: 1_048_576,
|
|
490
|
-
cacheMinTokens:
|
|
491
|
-
caching: '
|
|
803
|
+
cacheMinTokens: 4096,
|
|
804
|
+
caching: 'automatic',
|
|
492
805
|
/*
|
|
493
806
|
* $0.075 against $0.75 inside the introductory window, and $0.15 against
|
|
494
807
|
* $1.50 after it: Google scales the cache price with the headline one, so
|
|
@@ -499,13 +812,147 @@ export const MODELS: ModelPricing[] = [
|
|
|
499
812
|
capability: 'mid',
|
|
500
813
|
tier: 'sonnet',
|
|
501
814
|
notes:
|
|
502
|
-
'
|
|
815
|
+
'Implicit caching is automatic above 4,096 tokens. Explicit caching is also billed for storage:'
|
|
816
|
+
+ ' $0.50 per 1M tokens per hour through 2026-12-31, $1.00 after.',
|
|
817
|
+
},
|
|
818
|
+
{
|
|
819
|
+
/*
|
|
820
|
+
* Gemini 3.8 Flash, from ai.google.dev/gemini-api/docs/pricing, its model
|
|
821
|
+
* page and docs/caching, read 2026-10-10. The same rates and the same
|
|
822
|
+
* introductory window as 3.6 Flash, published as one table each: $0.75
|
|
823
|
+
* and $3.75 through 2026-12-31, $1.50 and $7.50 from 2027-01-01, and the
|
|
824
|
+
* cache price scales with them ($0.075, then $0.15).
|
|
825
|
+
*/
|
|
826
|
+
id: 'gemini-3.8-flash',
|
|
827
|
+
provider: 'google',
|
|
828
|
+
displayName: 'Gemini 3.8 Flash',
|
|
829
|
+
inputPerMTok: 1.5,
|
|
830
|
+
outputPerMTok: 7.5,
|
|
831
|
+
promo: { inputPerMTok: 0.75, outputPerMTok: 3.75, until: '2026-12-31' },
|
|
832
|
+
contextWindow: 1_048_576,
|
|
833
|
+
cacheMinTokens: 4096,
|
|
834
|
+
caching: 'automatic',
|
|
835
|
+
/* $0.075 / $0.75 = 0.1 (and $0.15 / $1.50); batch $0.375 / $0.75 = 0.5 (and $1.875 / $3.75). */
|
|
836
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
837
|
+
capability: 'mid',
|
|
838
|
+
tier: 'sonnet',
|
|
839
|
+
notes:
|
|
840
|
+
'Implicit caching is automatic above 4,096 tokens. Explicit caching is also billed for storage:'
|
|
503
841
|
+ ' $0.50 per 1M tokens per hour through 2026-12-31, $1.00 after.',
|
|
504
842
|
},
|
|
843
|
+
{
|
|
844
|
+
/*
|
|
845
|
+
* The pricing page, the model page and the caching page, read 2026-10-10
|
|
846
|
+
* and 2026-10-11. The model Google's own refusal of 2.5 Pro names as its
|
|
847
|
+
* replacement, and the second model here priced by prompt length: $2/$12
|
|
848
|
+
* for a prompt up to 200,000 tokens and $4/$18 over it, the cache at
|
|
849
|
+
* $0.20 and $0.40 (10% in both bands), batch $1/$6 and $2/$9 (half in
|
|
850
|
+
* both). 1,048,576 tokens in, 65,536 out, implicit caching from 4,096.
|
|
851
|
+
* The `-customtools` endpoint is "same as Gemini 3.1 Pro Preview pricing".
|
|
852
|
+
*
|
|
853
|
+
* A preview, and priced because a log carries it: Google's 2.5 Pro
|
|
854
|
+
* refusal sends every caller here.
|
|
855
|
+
*/
|
|
856
|
+
id: 'gemini-3.1-pro-preview',
|
|
857
|
+
aliases: ['gemini-3.1-pro-preview-customtools'],
|
|
858
|
+
provider: 'google',
|
|
859
|
+
displayName: 'Gemini 3.1 Pro Preview',
|
|
860
|
+
inputPerMTok: 2,
|
|
861
|
+
outputPerMTok: 12,
|
|
862
|
+
contextWindow: 1_048_576,
|
|
863
|
+
cacheMinTokens: 4096,
|
|
864
|
+
caching: 'automatic',
|
|
865
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
866
|
+
tiers: [
|
|
867
|
+
{
|
|
868
|
+
id: 'over-200k',
|
|
869
|
+
when: { kind: 'input-tokens-above', tokens: 200_000 },
|
|
870
|
+
inputPerMTok: 4,
|
|
871
|
+
outputPerMTok: 18,
|
|
872
|
+
},
|
|
873
|
+
],
|
|
874
|
+
capability: 'large',
|
|
875
|
+
tier: 'opus',
|
|
876
|
+
notes:
|
|
877
|
+
'Priced per request by prompt length: $2/$12 up to 200,000 input tokens and $4/$18 over it.'
|
|
878
|
+
+ ' Implicit caching is automatic above 4,096 tokens; explicit caching is also billed for storage,'
|
|
879
|
+
+ ' $4.50 per 1M tokens per hour.',
|
|
880
|
+
},
|
|
505
881
|
|
|
506
882
|
// ------------------------------------------------------------------------
|
|
507
883
|
// Moonshot
|
|
508
884
|
// ------------------------------------------------------------------------
|
|
885
|
+
/*
|
|
886
|
+
* platform.kimi.ai/docs/pricing/chat, models and pricing/batch, read in
|
|
887
|
+
* their Markdown form on 2026-10-11 (the HTML renders its tables in the
|
|
888
|
+
* browser, which is why the September review saw no context windows). K3
|
|
889
|
+
* bills cache writes as their own line, by TTL, like Anthropic: $3.00 for a
|
|
890
|
+
* 5-minute entry and $6.00 for an hour against a $3.00 input, and reads at
|
|
891
|
+
* $0.30. The K2 series bills no write, and reads at its own hit price. Batch
|
|
892
|
+
* is 60% of standard on K2.7 Code and K2.6 and not offered on K3 or the
|
|
893
|
+
* high-speed K2.7. No cache minimum is published for any of them.
|
|
894
|
+
*/
|
|
895
|
+
{
|
|
896
|
+
id: 'kimi-k3',
|
|
897
|
+
provider: 'moonshot',
|
|
898
|
+
displayName: 'Kimi K3',
|
|
899
|
+
inputPerMTok: 3,
|
|
900
|
+
outputPerMTok: 15,
|
|
901
|
+
contextWindow: 1_048_576,
|
|
902
|
+
cacheMinTokens: null,
|
|
903
|
+
caching: 'automatic',
|
|
904
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 2, batch: null },
|
|
905
|
+
capability: 'frontier',
|
|
906
|
+
tier: 'frontier',
|
|
907
|
+
notes:
|
|
908
|
+
'Cache writes are billed as their own line: 1x input for a 5-minute entry, 2x for an hour.'
|
|
909
|
+
+ ` No batch API for this model. ${NO_PUBLISHED_CACHE_MINIMUM}`,
|
|
910
|
+
},
|
|
911
|
+
{
|
|
912
|
+
id: 'kimi-k2.7-code',
|
|
913
|
+
provider: 'moonshot',
|
|
914
|
+
displayName: 'Kimi K2.7 Code',
|
|
915
|
+
inputPerMTok: 0.95,
|
|
916
|
+
outputPerMTok: 4,
|
|
917
|
+
contextWindow: 262_144,
|
|
918
|
+
cacheMinTokens: null,
|
|
919
|
+
caching: 'automatic',
|
|
920
|
+
/* $0.19 on a hit against $0.95; batch $0.57 / $2.40, 60%. */
|
|
921
|
+
multipliers: { cacheRead: 0.2, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.6 },
|
|
922
|
+
capability: 'mid',
|
|
923
|
+
tier: 'sonnet',
|
|
924
|
+
notes: NO_PUBLISHED_CACHE_MINIMUM,
|
|
925
|
+
},
|
|
926
|
+
{
|
|
927
|
+
id: 'kimi-k2.7-code-highspeed',
|
|
928
|
+
provider: 'moonshot',
|
|
929
|
+
displayName: 'Kimi K2.7 Code (high speed)',
|
|
930
|
+
inputPerMTok: 1.9,
|
|
931
|
+
outputPerMTok: 8,
|
|
932
|
+
contextWindow: 262_144,
|
|
933
|
+
cacheMinTokens: null,
|
|
934
|
+
caching: 'automatic',
|
|
935
|
+
/* $0.38 on a hit against $1.90; no batch API for this variant. */
|
|
936
|
+
multipliers: { cacheRead: 0.2, cacheWrite5m: 1, cacheWrite1h: 1, batch: null },
|
|
937
|
+
capability: 'mid',
|
|
938
|
+
tier: 'sonnet',
|
|
939
|
+
notes: NO_PUBLISHED_CACHE_MINIMUM,
|
|
940
|
+
},
|
|
941
|
+
{
|
|
942
|
+
id: 'kimi-k2.6',
|
|
943
|
+
provider: 'moonshot',
|
|
944
|
+
displayName: 'Kimi K2.6',
|
|
945
|
+
inputPerMTok: 0.95,
|
|
946
|
+
outputPerMTok: 4,
|
|
947
|
+
contextWindow: 262_144,
|
|
948
|
+
cacheMinTokens: null,
|
|
949
|
+
caching: 'automatic',
|
|
950
|
+
/* $0.16 on a hit against $0.95, written as the ratio so the product is the page's figure. */
|
|
951
|
+
multipliers: { cacheRead: 0.16 / 0.95, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.6 },
|
|
952
|
+
capability: 'mid',
|
|
953
|
+
tier: 'sonnet',
|
|
954
|
+
notes: NO_PUBLISHED_CACHE_MINIMUM,
|
|
955
|
+
},
|
|
509
956
|
{
|
|
510
957
|
id: 'kimi-k2',
|
|
511
958
|
provider: 'moonshot',
|
|
@@ -513,14 +960,23 @@ export const MODELS: ModelPricing[] = [
|
|
|
513
960
|
inputPerMTok: 0.6,
|
|
514
961
|
outputPerMTok: 2.5,
|
|
515
962
|
contextWindow: 256_000,
|
|
516
|
-
cacheMinTokens:
|
|
963
|
+
cacheMinTokens: null,
|
|
517
964
|
caching: 'automatic',
|
|
518
965
|
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: null },
|
|
966
|
+
/*
|
|
967
|
+
* Discontinued: "The kimi-k2 series models were officially discontinued
|
|
968
|
+
* on May 25, 2026" (the provider's model list, read 2026-10-11). Kept at
|
|
969
|
+
* its last published price, because calls in somebody's log really
|
|
970
|
+
* happened at it, and never offered as somewhere to move. Not `retired`,
|
|
971
|
+
* which records the provider refusing a request, and no key was here to
|
|
972
|
+
* send one.
|
|
973
|
+
*/
|
|
974
|
+
recommendable: false,
|
|
519
975
|
capability: 'mid',
|
|
520
976
|
tier: 'sonnet',
|
|
521
977
|
notes:
|
|
522
|
-
'
|
|
523
|
-
+
|
|
978
|
+
'Discontinued by Moonshot on 2026-05-25; priced at the last rate it published. No batch API.'
|
|
979
|
+
+ ` ${NO_PUBLISHED_CACHE_MINIMUM}`,
|
|
524
980
|
},
|
|
525
981
|
|
|
526
982
|
// ------------------------------------------------------------------------
|
|
@@ -547,34 +1003,97 @@ export const MODELS: ModelPricing[] = [
|
|
|
547
1003
|
},
|
|
548
1004
|
{
|
|
549
1005
|
/*
|
|
550
|
-
* DeepSeek V4 Flash,
|
|
551
|
-
* 2026-
|
|
1006
|
+
* DeepSeek V4 Flash, retired on 2026-09-10. The pricing page (read
|
|
1007
|
+
* 2026-10-10) says the id "is still accepted ... served by the
|
|
1008
|
+
* DeepSeek-V4.1-Flash model and billed at the Flash price", so from
|
|
1009
|
+
* 04:00 UTC that day it carries deepseek-flash's rates. Before that
|
|
1010
|
+
* instant it billed at its own, read off the same page on 2026-08-28:
|
|
1011
|
+
* $0.22/$0.66 off-peak, double at peak, $0.007 a cache hit.
|
|
552
1012
|
*
|
|
553
1013
|
* The base rate is off-peak because off-peak is the common case: peak is
|
|
554
1014
|
* 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.
|
|
1015
|
+
* The peak tier is exactly double, published as such. Clock pricing began
|
|
1016
|
+
* at 16:00 UTC on 2026-08-16; the flat price before it is on no page this
|
|
1017
|
+
* table can read, so calls before then are priced as the first period.
|
|
1018
|
+
*
|
|
1019
|
+
* Not recommendable: a legacy id DeepSeek routes "temporarily". Without
|
|
1020
|
+
* this, a tie on price would offer it ahead of deepseek-flash.
|
|
556
1021
|
*/
|
|
557
1022
|
id: 'deepseek-v4-flash',
|
|
558
1023
|
provider: 'deepseek',
|
|
559
1024
|
displayName: 'DeepSeek V4 Flash',
|
|
560
|
-
|
|
561
|
-
|
|
1025
|
+
recommendable: false,
|
|
1026
|
+
inputPerMTok: 0.15,
|
|
1027
|
+
outputPerMTok: 0.6,
|
|
562
1028
|
tiers: [
|
|
563
1029
|
{
|
|
564
1030
|
id: 'peak',
|
|
565
1031
|
when: { kind: 'utc-hours', hours: [1, 2, 3, 6, 7, 8, 9], weekdaysOnly: true },
|
|
566
|
-
inputPerMTok: 0.
|
|
567
|
-
outputPerMTok: 1.
|
|
1032
|
+
inputPerMTok: 0.3,
|
|
1033
|
+
outputPerMTok: 1.2,
|
|
1034
|
+
},
|
|
1035
|
+
],
|
|
1036
|
+
history: [
|
|
1037
|
+
{
|
|
1038
|
+
until: '2026-09-10T04:00:00Z',
|
|
1039
|
+
inputPerMTok: 0.22,
|
|
1040
|
+
outputPerMTok: 0.66,
|
|
1041
|
+
tiers: [
|
|
1042
|
+
{
|
|
1043
|
+
id: 'peak',
|
|
1044
|
+
when: { kind: 'utc-hours', hours: [1, 2, 3, 6, 7, 8, 9], weekdaysOnly: true },
|
|
1045
|
+
inputPerMTok: 0.44,
|
|
1046
|
+
outputPerMTok: 1.32,
|
|
1047
|
+
},
|
|
1048
|
+
],
|
|
1049
|
+
/* $0.007 cache hit against $0.22 input off-peak. */
|
|
1050
|
+
multipliers: { cacheRead: 0.0318, cacheWrite5m: 1, cacheWrite1h: 1, batch: null },
|
|
568
1051
|
},
|
|
569
1052
|
],
|
|
570
1053
|
contextWindow: 1_000_000,
|
|
571
1054
|
cacheMinTokens: 1024,
|
|
572
1055
|
caching: 'automatic',
|
|
573
|
-
/* $0.
|
|
574
|
-
multipliers: { cacheRead: 0.
|
|
1056
|
+
/* $0.003 cache hit against $0.15 input off-peak, $0.006 against $0.30 at peak. */
|
|
1057
|
+
multipliers: { cacheRead: 0.02, cacheWrite5m: 1, cacheWrite1h: 1, batch: null },
|
|
575
1058
|
capability: 'mid',
|
|
576
1059
|
tier: 'sonnet',
|
|
577
|
-
notes:
|
|
1060
|
+
notes:
|
|
1061
|
+
'Retired 2026-09-10: the id is still accepted, served by V4.1 Flash and billed at the deepseek-flash price.'
|
|
1062
|
+
+ ' Maximum output 384K tokens. Peak excludes Chinese public holidays, which this table cannot express.',
|
|
1063
|
+
},
|
|
1064
|
+
{
|
|
1065
|
+
/*
|
|
1066
|
+
* DeepSeek V4.1 Flash, from api-docs.deepseek.com/quick_start/pricing,
|
|
1067
|
+
* read 2026-10-10. Released 2026-09-10; its price took effect at 04:00
|
|
1068
|
+
* UTC that day. Off-peak is the base and peak is exactly double, as
|
|
1069
|
+
* published; the cache hit is 2% of input at both ($0.003 and $0.006).
|
|
1070
|
+
*
|
|
1071
|
+
* The cache minimum is carried from deepseek-v4-flash: DeepSeek's pages
|
|
1072
|
+
* publish none for this generation, and 1,024 is the figure the earlier
|
|
1073
|
+
* row was reviewed with.
|
|
1074
|
+
*/
|
|
1075
|
+
id: 'deepseek-flash',
|
|
1076
|
+
provider: 'deepseek',
|
|
1077
|
+
displayName: 'DeepSeek V4.1 Flash',
|
|
1078
|
+
inputPerMTok: 0.15,
|
|
1079
|
+
outputPerMTok: 0.6,
|
|
1080
|
+
tiers: [
|
|
1081
|
+
{
|
|
1082
|
+
id: 'peak',
|
|
1083
|
+
when: { kind: 'utc-hours', hours: [1, 2, 3, 6, 7, 8, 9], weekdaysOnly: true },
|
|
1084
|
+
inputPerMTok: 0.3,
|
|
1085
|
+
outputPerMTok: 1.2,
|
|
1086
|
+
},
|
|
1087
|
+
],
|
|
1088
|
+
contextWindow: 1_000_000,
|
|
1089
|
+
cacheMinTokens: 1024,
|
|
1090
|
+
caching: 'automatic',
|
|
1091
|
+
multipliers: { cacheRead: 0.02, cacheWrite5m: 1, cacheWrite1h: 1, batch: null },
|
|
1092
|
+
capability: 'mid',
|
|
1093
|
+
tier: 'sonnet',
|
|
1094
|
+
notes:
|
|
1095
|
+
'Maximum output 384K tokens. Peak excludes Chinese public holidays, which this table cannot express:'
|
|
1096
|
+
+ ' on a weekday holiday it charges peak where DeepSeek charges off-peak.',
|
|
578
1097
|
},
|
|
579
1098
|
{
|
|
580
1099
|
/* Same page, same clock. Pro is three times Flash at both ends. */
|
|
@@ -604,20 +1123,175 @@ export const MODELS: ModelPricing[] = [
|
|
|
604
1123
|
// ------------------------------------------------------------------------
|
|
605
1124
|
// xAI
|
|
606
1125
|
// ------------------------------------------------------------------------
|
|
1126
|
+
/*
|
|
1127
|
+
* docs.x.ai/developers/models and each model's page, read in their
|
|
1128
|
+
* Markdown form on 2026-10-11. Every text model has two rates: "requests
|
|
1129
|
+
* whose prompt reaches 200k tokens are billed at the higher rate for all
|
|
1130
|
+
* tokens in the request", which is a size tier at 200,000 inclusive and is
|
|
1131
|
+
* priced per request. Cached input scales with the band; writes are not
|
|
1132
|
+
* billed; batch is a 20% discount where the model's page offers it at all.
|
|
1133
|
+
* No cache minimum is published. Aliases are the stable family names the
|
|
1134
|
+
* pages list, never the `-latest` names, which move.
|
|
1135
|
+
*/
|
|
1136
|
+
{
|
|
1137
|
+
id: 'grok-4.7',
|
|
1138
|
+
provider: 'xai',
|
|
1139
|
+
displayName: 'Grok 4.7',
|
|
1140
|
+
inputPerMTok: 2,
|
|
1141
|
+
outputPerMTok: 6,
|
|
1142
|
+
contextWindow: 500_000,
|
|
1143
|
+
cacheMinTokens: null,
|
|
1144
|
+
caching: 'automatic',
|
|
1145
|
+
multipliers: { cacheRead: 0.25, cacheWrite5m: 1, cacheWrite1h: 1, batch: null },
|
|
1146
|
+
tiers: [GROK_LONG(4, 12)],
|
|
1147
|
+
capability: 'frontier',
|
|
1148
|
+
tier: 'frontier',
|
|
1149
|
+
notes: NO_PUBLISHED_CACHE_MINIMUM,
|
|
1150
|
+
},
|
|
1151
|
+
{
|
|
1152
|
+
id: 'grok-4.6',
|
|
1153
|
+
provider: 'xai',
|
|
1154
|
+
displayName: 'Grok 4.6',
|
|
1155
|
+
inputPerMTok: 2,
|
|
1156
|
+
outputPerMTok: 6,
|
|
1157
|
+
contextWindow: 500_000,
|
|
1158
|
+
cacheMinTokens: null,
|
|
1159
|
+
caching: 'automatic',
|
|
1160
|
+
multipliers: { cacheRead: 0.25, cacheWrite5m: 1, cacheWrite1h: 1, batch: null },
|
|
1161
|
+
tiers: [GROK_LONG(4, 12)],
|
|
1162
|
+
capability: 'large',
|
|
1163
|
+
tier: 'opus',
|
|
1164
|
+
notes: NO_PUBLISHED_CACHE_MINIMUM,
|
|
1165
|
+
},
|
|
1166
|
+
{
|
|
1167
|
+
id: 'grok-4.5',
|
|
1168
|
+
provider: 'xai',
|
|
1169
|
+
displayName: 'Grok 4.5',
|
|
1170
|
+
inputPerMTok: 2,
|
|
1171
|
+
outputPerMTok: 6,
|
|
1172
|
+
contextWindow: 500_000,
|
|
1173
|
+
cacheMinTokens: null,
|
|
1174
|
+
caching: 'automatic',
|
|
1175
|
+
/* $0.30 cached against $2.00. */
|
|
1176
|
+
multipliers: { cacheRead: 0.15, cacheWrite5m: 1, cacheWrite1h: 1, batch: null },
|
|
1177
|
+
tiers: [GROK_LONG(4, 12)],
|
|
1178
|
+
capability: 'large',
|
|
1179
|
+
tier: 'opus',
|
|
1180
|
+
notes: NO_PUBLISHED_CACHE_MINIMUM,
|
|
1181
|
+
},
|
|
1182
|
+
{
|
|
1183
|
+
id: 'grok-4.3',
|
|
1184
|
+
provider: 'xai',
|
|
1185
|
+
displayName: 'Grok 4.3',
|
|
1186
|
+
inputPerMTok: 1.25,
|
|
1187
|
+
outputPerMTok: 2.5,
|
|
1188
|
+
contextWindow: 1_000_000,
|
|
1189
|
+
cacheMinTokens: null,
|
|
1190
|
+
caching: 'automatic',
|
|
1191
|
+
/* $0.20 cached against $1.25; batch 20% off. */
|
|
1192
|
+
multipliers: { cacheRead: 0.16, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.8 },
|
|
1193
|
+
tiers: [GROK_LONG(2.5, 5)],
|
|
1194
|
+
capability: 'mid',
|
|
1195
|
+
tier: 'sonnet',
|
|
1196
|
+
notes: NO_PUBLISHED_CACHE_MINIMUM,
|
|
1197
|
+
},
|
|
607
1198
|
{
|
|
1199
|
+
id: 'grok-4.20-0309-reasoning',
|
|
1200
|
+
aliases: ['grok-4.20', 'grok-4.20-reasoning', 'grok-4.20-0309'],
|
|
1201
|
+
provider: 'xai',
|
|
1202
|
+
displayName: 'Grok 4.20 (reasoning)',
|
|
1203
|
+
inputPerMTok: 1.25,
|
|
1204
|
+
outputPerMTok: 2.5,
|
|
1205
|
+
contextWindow: 1_000_000,
|
|
1206
|
+
cacheMinTokens: null,
|
|
1207
|
+
caching: 'automatic',
|
|
1208
|
+
multipliers: { cacheRead: 0.16, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.8 },
|
|
1209
|
+
tiers: [GROK_LONG(2.5, 5)],
|
|
1210
|
+
capability: 'mid',
|
|
1211
|
+
tier: 'sonnet',
|
|
1212
|
+
notes: NO_PUBLISHED_CACHE_MINIMUM,
|
|
1213
|
+
},
|
|
1214
|
+
{
|
|
1215
|
+
id: 'grok-4.20-0309-non-reasoning',
|
|
1216
|
+
aliases: ['grok-4.20-non-reasoning'],
|
|
1217
|
+
provider: 'xai',
|
|
1218
|
+
displayName: 'Grok 4.20 (non-reasoning)',
|
|
1219
|
+
inputPerMTok: 1.25,
|
|
1220
|
+
outputPerMTok: 2.5,
|
|
1221
|
+
contextWindow: 1_000_000,
|
|
1222
|
+
cacheMinTokens: null,
|
|
1223
|
+
caching: 'automatic',
|
|
1224
|
+
multipliers: { cacheRead: 0.16, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.8 },
|
|
1225
|
+
tiers: [GROK_LONG(2.5, 5)],
|
|
1226
|
+
capability: 'mid',
|
|
1227
|
+
tier: 'sonnet',
|
|
1228
|
+
notes: NO_PUBLISHED_CACHE_MINIMUM,
|
|
1229
|
+
},
|
|
1230
|
+
{
|
|
1231
|
+
id: 'grok-4.20-multi-agent-0309',
|
|
1232
|
+
aliases: ['grok-4.20-multi-agent'],
|
|
1233
|
+
provider: 'xai',
|
|
1234
|
+
displayName: 'Grok 4.20 Multi-Agent',
|
|
1235
|
+
inputPerMTok: 1.25,
|
|
1236
|
+
outputPerMTok: 2.5,
|
|
1237
|
+
contextWindow: 1_000_000,
|
|
1238
|
+
cacheMinTokens: null,
|
|
1239
|
+
caching: 'automatic',
|
|
1240
|
+
multipliers: { cacheRead: 0.16, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.8 },
|
|
1241
|
+
tiers: [GROK_LONG(2.5, 5)],
|
|
1242
|
+
capability: 'mid',
|
|
1243
|
+
tier: 'sonnet',
|
|
1244
|
+
notes: NO_PUBLISHED_CACHE_MINIMUM,
|
|
1245
|
+
},
|
|
1246
|
+
{
|
|
1247
|
+
id: 'grok-build-0.1',
|
|
1248
|
+
provider: 'xai',
|
|
1249
|
+
displayName: 'Grok Build 0.1',
|
|
1250
|
+
inputPerMTok: 1,
|
|
1251
|
+
outputPerMTok: 2,
|
|
1252
|
+
contextWindow: 256_000,
|
|
1253
|
+
cacheMinTokens: null,
|
|
1254
|
+
caching: 'automatic',
|
|
1255
|
+
multipliers: { cacheRead: 0.2, cacheWrite5m: 1, cacheWrite1h: 1, batch: null },
|
|
1256
|
+
tiers: [GROK_LONG(2, 4)],
|
|
1257
|
+
capability: 'mid',
|
|
1258
|
+
tier: 'sonnet',
|
|
1259
|
+
notes: NO_PUBLISHED_CACHE_MINIMUM,
|
|
1260
|
+
},
|
|
1261
|
+
{
|
|
1262
|
+
/*
|
|
1263
|
+
* Retired on 2026-05-15 at 12:00 PT (19:00 UTC), and billed since at
|
|
1264
|
+
* Grok 4.3's prices: "requests to the retired model slugs above will
|
|
1265
|
+
* automatically redirect to grok-4.3 ... you will be billed at grok-4.3
|
|
1266
|
+
* pricing" (xAI's retirement notice, read 2026-10-11). So the id has two
|
|
1267
|
+
* periods, and a log that spans the date is billed both ways, at the
|
|
1268
|
+
* instant xAI gave. `grok-4-0709` is the dated id the notice names.
|
|
1269
|
+
*/
|
|
608
1270
|
id: 'grok-4',
|
|
1271
|
+
aliases: ['grok-4-0709'],
|
|
609
1272
|
provider: 'xai',
|
|
610
1273
|
displayName: 'Grok 4',
|
|
611
|
-
inputPerMTok:
|
|
612
|
-
outputPerMTok:
|
|
1274
|
+
inputPerMTok: 1.25,
|
|
1275
|
+
outputPerMTok: 2.5,
|
|
613
1276
|
contextWindow: 256_000,
|
|
614
|
-
cacheMinTokens:
|
|
1277
|
+
cacheMinTokens: null,
|
|
615
1278
|
caching: 'automatic',
|
|
616
|
-
multipliers: { cacheRead: 0.
|
|
1279
|
+
multipliers: { cacheRead: 0.16, cacheWrite5m: 1, cacheWrite1h: 1, batch: null },
|
|
1280
|
+
tiers: [GROK_LONG(2.5, 5)],
|
|
1281
|
+
history: [
|
|
1282
|
+
{
|
|
1283
|
+
until: '2026-05-15T19:00:00Z',
|
|
1284
|
+
inputPerMTok: 3,
|
|
1285
|
+
outputPerMTok: 15,
|
|
1286
|
+
multipliers: { cacheRead: 0.25, cacheWrite5m: 1, cacheWrite1h: 1, batch: null },
|
|
1287
|
+
},
|
|
1288
|
+
],
|
|
1289
|
+
recommendable: false,
|
|
617
1290
|
capability: 'large',
|
|
618
1291
|
tier: 'opus',
|
|
619
1292
|
notes:
|
|
620
|
-
'
|
|
1293
|
+
'Retired by xAI on 2026-05-15 and redirected to Grok 4.3, whose price it bills at from then;'
|
|
1294
|
+
+ ` $3/$15 before. ${NO_PUBLISHED_CACHE_MINIMUM}`,
|
|
621
1295
|
},
|
|
622
1296
|
|
|
623
1297
|
// ------------------------------------------------------------------------
|
|
@@ -667,12 +1341,12 @@ export const MODELS: ModelPricing[] = [
|
|
|
667
1341
|
inputPerMTok: 0.5,
|
|
668
1342
|
outputPerMTok: 1.5,
|
|
669
1343
|
contextWindow: 262_144,
|
|
670
|
-
cacheMinTokens:
|
|
671
|
-
caching: '
|
|
672
|
-
multipliers: { batch: 0.5 },
|
|
1344
|
+
cacheMinTokens: null,
|
|
1345
|
+
caching: 'unknown',
|
|
1346
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
673
1347
|
capability: 'large',
|
|
674
1348
|
tier: 'opus',
|
|
675
|
-
notes:
|
|
1349
|
+
notes: MISTRAL_CACHE_NOTE,
|
|
676
1350
|
},
|
|
677
1351
|
{
|
|
678
1352
|
/*
|
|
@@ -695,11 +1369,12 @@ export const MODELS: ModelPricing[] = [
|
|
|
695
1369
|
inputPerMTok: 1.5,
|
|
696
1370
|
outputPerMTok: 7.5,
|
|
697
1371
|
contextWindow: 262_144,
|
|
698
|
-
cacheMinTokens:
|
|
699
|
-
caching: '
|
|
700
|
-
multipliers: { batch: 0.5 },
|
|
1372
|
+
cacheMinTokens: null,
|
|
1373
|
+
caching: 'unknown',
|
|
1374
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
701
1375
|
capability: 'mid',
|
|
702
1376
|
tier: 'sonnet',
|
|
1377
|
+
notes: MISTRAL_CACHE_NOTE,
|
|
703
1378
|
},
|
|
704
1379
|
{
|
|
705
1380
|
/* Same page, same day. `mistral-small-2603` is what the API reports. */
|
|
@@ -710,11 +1385,12 @@ export const MODELS: ModelPricing[] = [
|
|
|
710
1385
|
inputPerMTok: 0.15,
|
|
711
1386
|
outputPerMTok: 0.6,
|
|
712
1387
|
contextWindow: 262_144,
|
|
713
|
-
cacheMinTokens:
|
|
714
|
-
caching: '
|
|
715
|
-
multipliers: { batch: 0.5 },
|
|
1388
|
+
cacheMinTokens: null,
|
|
1389
|
+
caching: 'unknown',
|
|
1390
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
716
1391
|
capability: 'small',
|
|
717
1392
|
tier: 'haiku',
|
|
1393
|
+
notes: MISTRAL_CACHE_NOTE,
|
|
718
1394
|
},
|
|
719
1395
|
];
|
|
720
1396
|
|
|
@@ -871,7 +1547,38 @@ interface Rates {
|
|
|
871
1547
|
inputPerMTok: number;
|
|
872
1548
|
outputPerMTok: number;
|
|
873
1549
|
promoApplied: boolean;
|
|
874
|
-
tier: {
|
|
1550
|
+
tier: {
|
|
1551
|
+
applied: string | null;
|
|
1552
|
+
decided: boolean;
|
|
1553
|
+
because: string;
|
|
1554
|
+
/**
|
|
1555
|
+
* The cheapest rate the undecided condition allows, present only when it
|
|
1556
|
+
* was not decided. The figure above is the ceiling and stays the one
|
|
1557
|
+
* billed; this is the other end, so a report can say how wide the
|
|
1558
|
+
* uncertainty is instead of only that there is some.
|
|
1559
|
+
*/
|
|
1560
|
+
floor?: { inputPerMTok: number; outputPerMTok: number };
|
|
1561
|
+
} | null;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
/**
|
|
1565
|
+
* What is known about the size of the prompt being priced.
|
|
1566
|
+
*
|
|
1567
|
+
* `inputTokens` is one request's prompt, every input token it carried, cache
|
|
1568
|
+
* reads and writes included, and decides a size tier outright.
|
|
1569
|
+
*
|
|
1570
|
+
* `inputTokensAtMost` is a bound, for a row that sums requests: a usage
|
|
1571
|
+
* report's hourly or daily bucket, an export's per-day line. No single
|
|
1572
|
+
* request in it can be larger than the whole row, so a row whose total is at
|
|
1573
|
+
* or under a threshold proves every request in it was under, and that decides
|
|
1574
|
+
* the tier. A row over the threshold proves nothing about any one request,
|
|
1575
|
+
* and the tier stays undecided. The bound is never treated as a size: a day of
|
|
1576
|
+
* 2,000 short prompts sums past 100,000 tokens without a single request
|
|
1577
|
+
* getting near it.
|
|
1578
|
+
*/
|
|
1579
|
+
export interface PromptSize {
|
|
1580
|
+
inputTokens?: number;
|
|
1581
|
+
inputTokensAtMost?: number;
|
|
875
1582
|
}
|
|
876
1583
|
|
|
877
1584
|
const pricingMemo = new WeakMap<
|
|
@@ -879,6 +1586,40 @@ const pricingMemo = new WeakMap<
|
|
|
879
1586
|
{ untilMs: number | null; promo: Rates | null; base: Rates }
|
|
880
1587
|
>();
|
|
881
1588
|
|
|
1589
|
+
/**
|
|
1590
|
+
* The row as it stood at `on`: itself when the current price applies, or a
|
|
1591
|
+
* frozen copy carrying the period's rates, tiers and ratios. Built once per
|
|
1592
|
+
* period, so a log of a million calls does not build a million rows.
|
|
1593
|
+
*/
|
|
1594
|
+
const periodMemo = new WeakMap<ModelPricing, { untilMs: number; row: ModelPricing }[]>();
|
|
1595
|
+
|
|
1596
|
+
function pricedAt(model: ModelPricing, on: Date): ModelPricing {
|
|
1597
|
+
const history = model.history;
|
|
1598
|
+
if (history === undefined || history.length === 0) return model;
|
|
1599
|
+
let periods = periodMemo.get(model);
|
|
1600
|
+
if (periods === undefined) {
|
|
1601
|
+
periods = history.map((period) => {
|
|
1602
|
+
const { history: _history, promo: _promo, tiers: _tiers, multipliers: _multipliers, ...rest } = model;
|
|
1603
|
+
const row: ModelPricing = Object.freeze({
|
|
1604
|
+
...rest,
|
|
1605
|
+
inputPerMTok: period.inputPerMTok,
|
|
1606
|
+
outputPerMTok: period.outputPerMTok,
|
|
1607
|
+
...(period.tiers !== undefined ? { tiers: period.tiers } : {}),
|
|
1608
|
+
...(period.multipliers !== undefined
|
|
1609
|
+
? { multipliers: period.multipliers }
|
|
1610
|
+
: model.multipliers !== undefined
|
|
1611
|
+
? { multipliers: model.multipliers }
|
|
1612
|
+
: {}),
|
|
1613
|
+
});
|
|
1614
|
+
return { untilMs: new Date(period.until).getTime(), row };
|
|
1615
|
+
});
|
|
1616
|
+
periodMemo.set(model, periods);
|
|
1617
|
+
}
|
|
1618
|
+
const at = on.getTime();
|
|
1619
|
+
for (const period of periods) if (at < period.untilMs) return period.row;
|
|
1620
|
+
return model;
|
|
1621
|
+
}
|
|
1622
|
+
|
|
882
1623
|
/** The two unconditional answers for one model, parsed once. */
|
|
883
1624
|
function cache(model: ModelPricing): {
|
|
884
1625
|
untilMs: number | null;
|
|
@@ -913,18 +1654,18 @@ function cache(model: ModelPricing): {
|
|
|
913
1654
|
* because "this does not apply" and "nobody said" lead to different reports and
|
|
914
1655
|
* collapsing them is the oldest mistake in this repository.
|
|
915
1656
|
*/
|
|
916
|
-
function holds(
|
|
917
|
-
when: TierCondition,
|
|
918
|
-
on: Date,
|
|
919
|
-
inputTokens: number | undefined,
|
|
920
|
-
): boolean | null {
|
|
1657
|
+
function holds(when: TierCondition, on: Date, size: PromptSize): boolean | null {
|
|
921
1658
|
if (when.kind === 'utc-hours') {
|
|
922
1659
|
const day = on.getUTCDay();
|
|
923
1660
|
if (when.weekdaysOnly && (day === 0 || day === 6)) return false;
|
|
924
1661
|
return when.hours.includes(on.getUTCHours());
|
|
925
1662
|
}
|
|
926
|
-
|
|
927
|
-
|
|
1663
|
+
const over = (tokens: number): boolean =>
|
|
1664
|
+
when.kind === 'input-tokens-at-least' ? tokens >= when.tokens : tokens > when.tokens;
|
|
1665
|
+
if (size.inputTokens !== undefined) return over(size.inputTokens);
|
|
1666
|
+
// A whole row under the line puts every request in it under the line.
|
|
1667
|
+
if (size.inputTokensAtMost !== undefined && !over(size.inputTokensAtMost)) return false;
|
|
1668
|
+
return null;
|
|
928
1669
|
}
|
|
929
1670
|
|
|
930
1671
|
/**
|
|
@@ -946,8 +1687,13 @@ function holds(
|
|
|
946
1687
|
export function effectivePricing(
|
|
947
1688
|
model: ModelPricing,
|
|
948
1689
|
on: Date = new Date(),
|
|
949
|
-
context:
|
|
1690
|
+
context: PromptSize = {},
|
|
950
1691
|
): Rates {
|
|
1692
|
+
/* A call before a published price change is priced as it was billed then. */
|
|
1693
|
+
if (model.history !== undefined) {
|
|
1694
|
+
const period = pricedAt(model, on);
|
|
1695
|
+
if (period !== model) return effectivePricing(period, on, context);
|
|
1696
|
+
}
|
|
951
1697
|
const memo = pricingMemo.get(model) ?? cache(model);
|
|
952
1698
|
|
|
953
1699
|
/**
|
|
@@ -961,7 +1707,7 @@ export function effectivePricing(
|
|
|
961
1707
|
if (model.tiers !== undefined && model.tiers.length > 0) {
|
|
962
1708
|
const undecided: PricingTier[] = [];
|
|
963
1709
|
for (const tier of model.tiers) {
|
|
964
|
-
const verdict = holds(tier.when, on, context
|
|
1710
|
+
const verdict = holds(tier.when, on, context);
|
|
965
1711
|
if (verdict === true) {
|
|
966
1712
|
return {
|
|
967
1713
|
inputPerMTok: tier.inputPerMTok,
|
|
@@ -984,6 +1730,11 @@ export function effectivePricing(
|
|
|
984
1730
|
const dearest = undecided.reduce((worst, tier) =>
|
|
985
1731
|
tier.inputPerMTok > worst.inputPerMTok ? tier : worst,
|
|
986
1732
|
);
|
|
1733
|
+
/*
|
|
1734
|
+
* And the cheapest, which is the base: every undecided condition might not
|
|
1735
|
+
* hold, and a tier is never below the base (`pricing-tiers.test.js`).
|
|
1736
|
+
*/
|
|
1737
|
+
const floor = { inputPerMTok: model.inputPerMTok, outputPerMTok: model.outputPerMTok };
|
|
987
1738
|
const ceiling =
|
|
988
1739
|
dearest.inputPerMTok > model.inputPerMTok
|
|
989
1740
|
? {
|
|
@@ -1003,7 +1754,11 @@ export function effectivePricing(
|
|
|
1003
1754
|
tier: {
|
|
1004
1755
|
applied: ceiling.applied,
|
|
1005
1756
|
decided: false,
|
|
1006
|
-
because:
|
|
1757
|
+
because:
|
|
1758
|
+
context.inputTokensAtMost !== undefined
|
|
1759
|
+
? `${describe(dearest.when)}, and this row sums requests without giving any one prompt's length`
|
|
1760
|
+
: `${describe(dearest.when)}, and the token count was not given`,
|
|
1761
|
+
floor,
|
|
1007
1762
|
},
|
|
1008
1763
|
};
|
|
1009
1764
|
}
|
|
@@ -1017,6 +1772,9 @@ function describe(when: TierCondition): string {
|
|
|
1017
1772
|
if (when.kind === 'input-tokens-above') {
|
|
1018
1773
|
return `prompts over ${when.tokens.toLocaleString('en-US')} input tokens`;
|
|
1019
1774
|
}
|
|
1775
|
+
if (when.kind === 'input-tokens-at-least') {
|
|
1776
|
+
return `prompts of ${when.tokens.toLocaleString('en-US')} input tokens or more`;
|
|
1777
|
+
}
|
|
1020
1778
|
const windows = [];
|
|
1021
1779
|
let start = null;
|
|
1022
1780
|
const sorted = [...when.hours].sort((a, b) => a - b);
|