cascade-ai 0.48.1 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -2
- package/dist/cli.cjs +2113 -51
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2113 -51
- package/dist/cli.js.map +1 -1
- package/dist/desktop-core.cjs +2050 -47
- package/dist/index.cjs +2050 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +159 -0
- package/dist/index.d.ts +159 -0
- package/dist/index.js +2050 -47
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -59,11 +59,1688 @@ var __export = (target, all) => {
|
|
|
59
59
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
+
// src/core/router/pricing-data.json
|
|
63
|
+
var pricing_data_default;
|
|
64
|
+
var init_pricing_data = __esm({
|
|
65
|
+
"src/core/router/pricing-data.json"() {
|
|
66
|
+
pricing_data_default = {
|
|
67
|
+
schemaVersion: 1,
|
|
68
|
+
generatedAt: "2026-07-27",
|
|
69
|
+
defaultCurrency: "USD",
|
|
70
|
+
note: 'Authoritative offline baseline for model pricing, keyed by model x provider (x region). All token rates are per 1,000,000 tokens in the entry currency. A live source (see live-data.ts) may override an entry when it is fresher; disagreements are recorded rather than silently accepted. Every entry must carry currency, asOf, source and provenance \u2014 a model with NO entry is priced "unknown", never $0. Regenerate with scripts/refresh-pricing.mjs.',
|
|
71
|
+
provenanceLegend: {
|
|
72
|
+
"vendor-pricing-page": "Read directly from the vendor's own published pricing page (see `source`).",
|
|
73
|
+
"litellm-catalog": "From BerriAI/litellm model_prices_and_context_window.json, which cites the vendor page per entry. Used where the vendor page refused automated access.",
|
|
74
|
+
"seed-approximation": "NOT sourced \u2014 an approximation. No entry in this file currently uses this value; it exists so an approximation can never masquerade as a measured price."
|
|
75
|
+
},
|
|
76
|
+
entries: [
|
|
77
|
+
{
|
|
78
|
+
model: "claude-opus-5",
|
|
79
|
+
provider: "anthropic",
|
|
80
|
+
modality: "text",
|
|
81
|
+
currency: "USD",
|
|
82
|
+
asOf: "2026-07-27",
|
|
83
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
84
|
+
provenance: "vendor-pricing-page",
|
|
85
|
+
tiers: [
|
|
86
|
+
{
|
|
87
|
+
inputPer1m: 5,
|
|
88
|
+
outputPer1m: 25,
|
|
89
|
+
cachedInputPer1m: 0.5,
|
|
90
|
+
cacheWritePer1m: 6.25,
|
|
91
|
+
reasoningOutputPer1m: 25
|
|
92
|
+
}
|
|
93
|
+
],
|
|
94
|
+
batchMultiplier: 0.5,
|
|
95
|
+
contextWindow: 1e6
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
model: "claude-opus-4-8",
|
|
99
|
+
provider: "anthropic",
|
|
100
|
+
modality: "text",
|
|
101
|
+
currency: "USD",
|
|
102
|
+
asOf: "2026-07-27",
|
|
103
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
104
|
+
provenance: "vendor-pricing-page",
|
|
105
|
+
tiers: [
|
|
106
|
+
{
|
|
107
|
+
inputPer1m: 5,
|
|
108
|
+
outputPer1m: 25,
|
|
109
|
+
cachedInputPer1m: 0.5,
|
|
110
|
+
cacheWritePer1m: 6.25,
|
|
111
|
+
reasoningOutputPer1m: 25
|
|
112
|
+
}
|
|
113
|
+
],
|
|
114
|
+
batchMultiplier: 0.5,
|
|
115
|
+
contextWindow: 1e6
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
model: "claude-opus-4-7",
|
|
119
|
+
provider: "anthropic",
|
|
120
|
+
modality: "text",
|
|
121
|
+
currency: "USD",
|
|
122
|
+
asOf: "2026-07-27",
|
|
123
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
124
|
+
provenance: "vendor-pricing-page",
|
|
125
|
+
tiers: [
|
|
126
|
+
{
|
|
127
|
+
inputPer1m: 5,
|
|
128
|
+
outputPer1m: 25,
|
|
129
|
+
cachedInputPer1m: 0.5,
|
|
130
|
+
cacheWritePer1m: 6.25,
|
|
131
|
+
reasoningOutputPer1m: 25
|
|
132
|
+
}
|
|
133
|
+
],
|
|
134
|
+
batchMultiplier: 0.5,
|
|
135
|
+
contextWindow: 1e6
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
model: "claude-opus-4-6",
|
|
139
|
+
provider: "anthropic",
|
|
140
|
+
modality: "text",
|
|
141
|
+
currency: "USD",
|
|
142
|
+
asOf: "2026-07-27",
|
|
143
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
144
|
+
provenance: "vendor-pricing-page",
|
|
145
|
+
tiers: [
|
|
146
|
+
{
|
|
147
|
+
inputPer1m: 5,
|
|
148
|
+
outputPer1m: 25,
|
|
149
|
+
cachedInputPer1m: 0.5,
|
|
150
|
+
cacheWritePer1m: 6.25,
|
|
151
|
+
reasoningOutputPer1m: 25
|
|
152
|
+
}
|
|
153
|
+
],
|
|
154
|
+
batchMultiplier: 0.5,
|
|
155
|
+
contextWindow: 1e6
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
model: "claude-opus-4-5",
|
|
159
|
+
provider: "anthropic",
|
|
160
|
+
modality: "text",
|
|
161
|
+
currency: "USD",
|
|
162
|
+
asOf: "2026-07-27",
|
|
163
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
164
|
+
provenance: "vendor-pricing-page",
|
|
165
|
+
tiers: [
|
|
166
|
+
{
|
|
167
|
+
inputPer1m: 5,
|
|
168
|
+
outputPer1m: 25,
|
|
169
|
+
cachedInputPer1m: 0.5,
|
|
170
|
+
cacheWritePer1m: 6.25,
|
|
171
|
+
reasoningOutputPer1m: 25
|
|
172
|
+
}
|
|
173
|
+
],
|
|
174
|
+
batchMultiplier: 0.5,
|
|
175
|
+
contextWindow: 2e5
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
model: "claude-opus-4-1",
|
|
179
|
+
provider: "anthropic",
|
|
180
|
+
modality: "text",
|
|
181
|
+
currency: "USD",
|
|
182
|
+
asOf: "2026-07-27",
|
|
183
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
184
|
+
provenance: "vendor-pricing-page",
|
|
185
|
+
tiers: [
|
|
186
|
+
{
|
|
187
|
+
inputPer1m: 15,
|
|
188
|
+
outputPer1m: 75,
|
|
189
|
+
cachedInputPer1m: 1.5,
|
|
190
|
+
cacheWritePer1m: 18.75,
|
|
191
|
+
reasoningOutputPer1m: 75
|
|
192
|
+
}
|
|
193
|
+
],
|
|
194
|
+
batchMultiplier: 0.5,
|
|
195
|
+
contextWindow: 2e5,
|
|
196
|
+
notes: "Deprecated by Anthropic; priced for historical/cost-reporting accuracy."
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
model: "claude-opus-4",
|
|
200
|
+
provider: "anthropic",
|
|
201
|
+
modality: "text",
|
|
202
|
+
currency: "USD",
|
|
203
|
+
asOf: "2026-07-27",
|
|
204
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
205
|
+
provenance: "vendor-pricing-page",
|
|
206
|
+
tiers: [
|
|
207
|
+
{
|
|
208
|
+
inputPer1m: 15,
|
|
209
|
+
outputPer1m: 75,
|
|
210
|
+
cachedInputPer1m: 1.5,
|
|
211
|
+
cacheWritePer1m: 18.75,
|
|
212
|
+
reasoningOutputPer1m: 75
|
|
213
|
+
}
|
|
214
|
+
],
|
|
215
|
+
batchMultiplier: 0.5,
|
|
216
|
+
contextWindow: 2e5,
|
|
217
|
+
notes: "Retired except on Google Cloud; priced for historical/cost-reporting accuracy."
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
model: "claude-sonnet-5",
|
|
221
|
+
provider: "anthropic",
|
|
222
|
+
modality: "text",
|
|
223
|
+
currency: "USD",
|
|
224
|
+
asOf: "2026-07-27",
|
|
225
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
226
|
+
provenance: "vendor-pricing-page",
|
|
227
|
+
tiers: [
|
|
228
|
+
{
|
|
229
|
+
inputPer1m: 2,
|
|
230
|
+
outputPer1m: 10,
|
|
231
|
+
cachedInputPer1m: 0.2,
|
|
232
|
+
cacheWritePer1m: 2.5,
|
|
233
|
+
reasoningOutputPer1m: 10
|
|
234
|
+
}
|
|
235
|
+
],
|
|
236
|
+
batchMultiplier: 0.5,
|
|
237
|
+
contextWindow: 1e6,
|
|
238
|
+
notes: "Introductory pricing through 2026-08-31. Standard pricing of $3/$15 per 1M takes effect 2026-09-01 \u2014 re-check this entry then."
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
model: "claude-sonnet-4-6",
|
|
242
|
+
provider: "anthropic",
|
|
243
|
+
modality: "text",
|
|
244
|
+
currency: "USD",
|
|
245
|
+
asOf: "2026-07-27",
|
|
246
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
247
|
+
provenance: "vendor-pricing-page",
|
|
248
|
+
tiers: [
|
|
249
|
+
{
|
|
250
|
+
inputPer1m: 3,
|
|
251
|
+
outputPer1m: 15,
|
|
252
|
+
cachedInputPer1m: 0.3,
|
|
253
|
+
cacheWritePer1m: 3.75,
|
|
254
|
+
reasoningOutputPer1m: 15
|
|
255
|
+
}
|
|
256
|
+
],
|
|
257
|
+
batchMultiplier: 0.5,
|
|
258
|
+
contextWindow: 1e6
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
model: "claude-sonnet-4-5",
|
|
262
|
+
provider: "anthropic",
|
|
263
|
+
modality: "text",
|
|
264
|
+
currency: "USD",
|
|
265
|
+
asOf: "2026-07-27",
|
|
266
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
267
|
+
provenance: "vendor-pricing-page",
|
|
268
|
+
tiers: [
|
|
269
|
+
{
|
|
270
|
+
maxInputTokens: 2e5,
|
|
271
|
+
inputPer1m: 3,
|
|
272
|
+
outputPer1m: 15,
|
|
273
|
+
cachedInputPer1m: 0.3,
|
|
274
|
+
cacheWritePer1m: 3.75,
|
|
275
|
+
reasoningOutputPer1m: 15
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
inputPer1m: 6,
|
|
279
|
+
outputPer1m: 22.5,
|
|
280
|
+
cachedInputPer1m: 0.6,
|
|
281
|
+
cacheWritePer1m: 7.5,
|
|
282
|
+
reasoningOutputPer1m: 22.5
|
|
283
|
+
}
|
|
284
|
+
],
|
|
285
|
+
batchMultiplier: 0.5,
|
|
286
|
+
contextWindow: 2e5,
|
|
287
|
+
notes: "Long-context (>200K) premium applies to the 1M-context beta. Base rates from the Anthropic pricing page; the >200K tier from the litellm catalog (Anthropic does not tabulate it on the pricing page)."
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
model: "claude-sonnet-4",
|
|
291
|
+
provider: "anthropic",
|
|
292
|
+
modality: "text",
|
|
293
|
+
currency: "USD",
|
|
294
|
+
asOf: "2026-07-27",
|
|
295
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
296
|
+
provenance: "vendor-pricing-page",
|
|
297
|
+
tiers: [
|
|
298
|
+
{
|
|
299
|
+
inputPer1m: 3,
|
|
300
|
+
outputPer1m: 15,
|
|
301
|
+
cachedInputPer1m: 0.3,
|
|
302
|
+
cacheWritePer1m: 3.75,
|
|
303
|
+
reasoningOutputPer1m: 15
|
|
304
|
+
}
|
|
305
|
+
],
|
|
306
|
+
batchMultiplier: 0.5,
|
|
307
|
+
contextWindow: 2e5
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
model: "claude-haiku-4-5",
|
|
311
|
+
provider: "anthropic",
|
|
312
|
+
modality: "text",
|
|
313
|
+
currency: "USD",
|
|
314
|
+
asOf: "2026-07-27",
|
|
315
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
316
|
+
provenance: "vendor-pricing-page",
|
|
317
|
+
tiers: [
|
|
318
|
+
{
|
|
319
|
+
inputPer1m: 1,
|
|
320
|
+
outputPer1m: 5,
|
|
321
|
+
cachedInputPer1m: 0.1,
|
|
322
|
+
cacheWritePer1m: 1.25,
|
|
323
|
+
reasoningOutputPer1m: 5
|
|
324
|
+
}
|
|
325
|
+
],
|
|
326
|
+
batchMultiplier: 0.5,
|
|
327
|
+
contextWindow: 2e5
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
model: "claude-haiku-3-5",
|
|
331
|
+
provider: "anthropic",
|
|
332
|
+
modality: "text",
|
|
333
|
+
currency: "USD",
|
|
334
|
+
asOf: "2026-07-27",
|
|
335
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
336
|
+
provenance: "vendor-pricing-page",
|
|
337
|
+
tiers: [
|
|
338
|
+
{
|
|
339
|
+
inputPer1m: 0.8,
|
|
340
|
+
outputPer1m: 4,
|
|
341
|
+
cachedInputPer1m: 0.08,
|
|
342
|
+
cacheWritePer1m: 1,
|
|
343
|
+
reasoningOutputPer1m: 4
|
|
344
|
+
}
|
|
345
|
+
],
|
|
346
|
+
batchMultiplier: 0.5,
|
|
347
|
+
contextWindow: 2e5
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
model: "gpt-5.6-sol",
|
|
351
|
+
provider: "openai",
|
|
352
|
+
aliases: [
|
|
353
|
+
"gpt-5.6"
|
|
354
|
+
],
|
|
355
|
+
modality: "text",
|
|
356
|
+
currency: "USD",
|
|
357
|
+
asOf: "2026-07-27",
|
|
358
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
359
|
+
provenance: "litellm-catalog",
|
|
360
|
+
tiers: [
|
|
361
|
+
{
|
|
362
|
+
maxInputTokens: 272e3,
|
|
363
|
+
inputPer1m: 5,
|
|
364
|
+
outputPer1m: 30,
|
|
365
|
+
cachedInputPer1m: 0.5,
|
|
366
|
+
cacheWritePer1m: 6.25
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
inputPer1m: 10,
|
|
370
|
+
outputPer1m: 45,
|
|
371
|
+
cachedInputPer1m: 1,
|
|
372
|
+
cacheWritePer1m: 12.5
|
|
373
|
+
}
|
|
374
|
+
],
|
|
375
|
+
batchMultiplier: 0.5,
|
|
376
|
+
contextWindow: 105e4
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
model: "gpt-5.6-terra",
|
|
380
|
+
provider: "openai",
|
|
381
|
+
modality: "text",
|
|
382
|
+
currency: "USD",
|
|
383
|
+
asOf: "2026-07-27",
|
|
384
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
385
|
+
provenance: "litellm-catalog",
|
|
386
|
+
tiers: [
|
|
387
|
+
{
|
|
388
|
+
maxInputTokens: 272e3,
|
|
389
|
+
inputPer1m: 2.5,
|
|
390
|
+
outputPer1m: 15,
|
|
391
|
+
cachedInputPer1m: 0.25,
|
|
392
|
+
cacheWritePer1m: 3.125
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
inputPer1m: 5,
|
|
396
|
+
outputPer1m: 22.5,
|
|
397
|
+
cachedInputPer1m: 0.5,
|
|
398
|
+
cacheWritePer1m: 6.25
|
|
399
|
+
}
|
|
400
|
+
],
|
|
401
|
+
batchMultiplier: 0.5,
|
|
402
|
+
contextWindow: 105e4
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
model: "gpt-5.6-luna",
|
|
406
|
+
provider: "openai",
|
|
407
|
+
modality: "text",
|
|
408
|
+
currency: "USD",
|
|
409
|
+
asOf: "2026-07-27",
|
|
410
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
411
|
+
provenance: "litellm-catalog",
|
|
412
|
+
tiers: [
|
|
413
|
+
{
|
|
414
|
+
maxInputTokens: 272e3,
|
|
415
|
+
inputPer1m: 1,
|
|
416
|
+
outputPer1m: 6,
|
|
417
|
+
cachedInputPer1m: 0.1,
|
|
418
|
+
cacheWritePer1m: 1.25
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
inputPer1m: 2,
|
|
422
|
+
outputPer1m: 9,
|
|
423
|
+
cachedInputPer1m: 0.2,
|
|
424
|
+
cacheWritePer1m: 2.5
|
|
425
|
+
}
|
|
426
|
+
],
|
|
427
|
+
batchMultiplier: 0.5,
|
|
428
|
+
contextWindow: 105e4
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
model: "gpt-5.5",
|
|
432
|
+
provider: "openai",
|
|
433
|
+
modality: "text",
|
|
434
|
+
currency: "USD",
|
|
435
|
+
asOf: "2026-07-27",
|
|
436
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
437
|
+
provenance: "litellm-catalog",
|
|
438
|
+
tiers: [
|
|
439
|
+
{
|
|
440
|
+
maxInputTokens: 272e3,
|
|
441
|
+
inputPer1m: 5,
|
|
442
|
+
outputPer1m: 30,
|
|
443
|
+
cachedInputPer1m: 0.5
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
inputPer1m: 10,
|
|
447
|
+
outputPer1m: 45,
|
|
448
|
+
cachedInputPer1m: 1
|
|
449
|
+
}
|
|
450
|
+
],
|
|
451
|
+
batchMultiplier: 0.5,
|
|
452
|
+
contextWindow: 105e4
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
model: "gpt-5.4",
|
|
456
|
+
provider: "openai",
|
|
457
|
+
modality: "text",
|
|
458
|
+
currency: "USD",
|
|
459
|
+
asOf: "2026-07-27",
|
|
460
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
461
|
+
provenance: "litellm-catalog",
|
|
462
|
+
tiers: [
|
|
463
|
+
{
|
|
464
|
+
maxInputTokens: 272e3,
|
|
465
|
+
inputPer1m: 2.5,
|
|
466
|
+
outputPer1m: 15,
|
|
467
|
+
cachedInputPer1m: 0.25
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
inputPer1m: 5,
|
|
471
|
+
outputPer1m: 22.5,
|
|
472
|
+
cachedInputPer1m: 0.5
|
|
473
|
+
}
|
|
474
|
+
],
|
|
475
|
+
batchMultiplier: 0.5,
|
|
476
|
+
contextWindow: 105e4
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
model: "gpt-5.4-mini",
|
|
480
|
+
provider: "openai",
|
|
481
|
+
modality: "text",
|
|
482
|
+
currency: "USD",
|
|
483
|
+
asOf: "2026-07-27",
|
|
484
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
485
|
+
provenance: "litellm-catalog",
|
|
486
|
+
tiers: [
|
|
487
|
+
{
|
|
488
|
+
inputPer1m: 0.75,
|
|
489
|
+
outputPer1m: 4.5,
|
|
490
|
+
cachedInputPer1m: 0.075
|
|
491
|
+
}
|
|
492
|
+
],
|
|
493
|
+
batchMultiplier: 0.5,
|
|
494
|
+
contextWindow: 105e4
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
model: "gpt-5",
|
|
498
|
+
provider: "openai",
|
|
499
|
+
modality: "text",
|
|
500
|
+
currency: "USD",
|
|
501
|
+
asOf: "2026-07-27",
|
|
502
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
503
|
+
provenance: "litellm-catalog",
|
|
504
|
+
tiers: [
|
|
505
|
+
{
|
|
506
|
+
inputPer1m: 1.25,
|
|
507
|
+
outputPer1m: 10,
|
|
508
|
+
cachedInputPer1m: 0.125
|
|
509
|
+
}
|
|
510
|
+
],
|
|
511
|
+
contextWindow: 272e3
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
model: "gpt-5-mini",
|
|
515
|
+
provider: "openai",
|
|
516
|
+
modality: "text",
|
|
517
|
+
currency: "USD",
|
|
518
|
+
asOf: "2026-07-27",
|
|
519
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
520
|
+
provenance: "litellm-catalog",
|
|
521
|
+
tiers: [
|
|
522
|
+
{
|
|
523
|
+
inputPer1m: 0.25,
|
|
524
|
+
outputPer1m: 2,
|
|
525
|
+
cachedInputPer1m: 0.025
|
|
526
|
+
}
|
|
527
|
+
],
|
|
528
|
+
contextWindow: 272e3
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
model: "gpt-5-nano",
|
|
532
|
+
provider: "openai",
|
|
533
|
+
modality: "text",
|
|
534
|
+
currency: "USD",
|
|
535
|
+
asOf: "2026-07-27",
|
|
536
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
537
|
+
provenance: "litellm-catalog",
|
|
538
|
+
tiers: [
|
|
539
|
+
{
|
|
540
|
+
inputPer1m: 0.05,
|
|
541
|
+
outputPer1m: 0.4,
|
|
542
|
+
cachedInputPer1m: 5e-3
|
|
543
|
+
}
|
|
544
|
+
],
|
|
545
|
+
contextWindow: 272e3
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
model: "gpt-4.1",
|
|
549
|
+
provider: "openai",
|
|
550
|
+
modality: "text",
|
|
551
|
+
currency: "USD",
|
|
552
|
+
asOf: "2026-07-27",
|
|
553
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
554
|
+
provenance: "litellm-catalog",
|
|
555
|
+
tiers: [
|
|
556
|
+
{
|
|
557
|
+
inputPer1m: 2,
|
|
558
|
+
outputPer1m: 8,
|
|
559
|
+
cachedInputPer1m: 0.5
|
|
560
|
+
}
|
|
561
|
+
],
|
|
562
|
+
batchMultiplier: 0.5,
|
|
563
|
+
contextWindow: 1047576
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
model: "gpt-4.1-mini",
|
|
567
|
+
provider: "openai",
|
|
568
|
+
modality: "text",
|
|
569
|
+
currency: "USD",
|
|
570
|
+
asOf: "2026-07-27",
|
|
571
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
572
|
+
provenance: "litellm-catalog",
|
|
573
|
+
tiers: [
|
|
574
|
+
{
|
|
575
|
+
inputPer1m: 0.4,
|
|
576
|
+
outputPer1m: 1.6,
|
|
577
|
+
cachedInputPer1m: 0.1
|
|
578
|
+
}
|
|
579
|
+
],
|
|
580
|
+
batchMultiplier: 0.5,
|
|
581
|
+
contextWindow: 1047576
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
model: "gpt-4.1-nano",
|
|
585
|
+
provider: "openai",
|
|
586
|
+
modality: "text",
|
|
587
|
+
currency: "USD",
|
|
588
|
+
asOf: "2026-07-27",
|
|
589
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
590
|
+
provenance: "litellm-catalog",
|
|
591
|
+
tiers: [
|
|
592
|
+
{
|
|
593
|
+
inputPer1m: 0.1,
|
|
594
|
+
outputPer1m: 0.4,
|
|
595
|
+
cachedInputPer1m: 0.025
|
|
596
|
+
}
|
|
597
|
+
],
|
|
598
|
+
batchMultiplier: 0.5,
|
|
599
|
+
contextWindow: 1047576
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
model: "gpt-4o",
|
|
603
|
+
provider: "openai",
|
|
604
|
+
modality: "text",
|
|
605
|
+
currency: "USD",
|
|
606
|
+
asOf: "2026-07-27",
|
|
607
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
608
|
+
provenance: "litellm-catalog",
|
|
609
|
+
tiers: [
|
|
610
|
+
{
|
|
611
|
+
inputPer1m: 2.5,
|
|
612
|
+
outputPer1m: 10,
|
|
613
|
+
cachedInputPer1m: 1.25
|
|
614
|
+
}
|
|
615
|
+
],
|
|
616
|
+
batchMultiplier: 0.5,
|
|
617
|
+
contextWindow: 128e3
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
model: "gpt-4o-mini",
|
|
621
|
+
provider: "openai",
|
|
622
|
+
modality: "text",
|
|
623
|
+
currency: "USD",
|
|
624
|
+
asOf: "2026-07-27",
|
|
625
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
626
|
+
provenance: "litellm-catalog",
|
|
627
|
+
tiers: [
|
|
628
|
+
{
|
|
629
|
+
inputPer1m: 0.15,
|
|
630
|
+
outputPer1m: 0.6,
|
|
631
|
+
cachedInputPer1m: 0.075
|
|
632
|
+
}
|
|
633
|
+
],
|
|
634
|
+
batchMultiplier: 0.5,
|
|
635
|
+
contextWindow: 128e3
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
model: "gpt-5.5",
|
|
639
|
+
provider: "azure",
|
|
640
|
+
region: "global",
|
|
641
|
+
modality: "text",
|
|
642
|
+
currency: "USD",
|
|
643
|
+
asOf: "2026-07-27",
|
|
644
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
645
|
+
provenance: "litellm-catalog",
|
|
646
|
+
tiers: [
|
|
647
|
+
{
|
|
648
|
+
maxInputTokens: 272e3,
|
|
649
|
+
inputPer1m: 5,
|
|
650
|
+
outputPer1m: 30,
|
|
651
|
+
cachedInputPer1m: 0.5
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
inputPer1m: 10,
|
|
655
|
+
outputPer1m: 45,
|
|
656
|
+
cachedInputPer1m: 1
|
|
657
|
+
}
|
|
658
|
+
],
|
|
659
|
+
contextWindow: 105e4
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
model: "gpt-5.5",
|
|
663
|
+
provider: "azure",
|
|
664
|
+
region: "us",
|
|
665
|
+
modality: "text",
|
|
666
|
+
currency: "USD",
|
|
667
|
+
asOf: "2026-07-27",
|
|
668
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
669
|
+
provenance: "litellm-catalog",
|
|
670
|
+
tiers: [
|
|
671
|
+
{
|
|
672
|
+
maxInputTokens: 272e3,
|
|
673
|
+
inputPer1m: 5.5,
|
|
674
|
+
outputPer1m: 33,
|
|
675
|
+
cachedInputPer1m: 0.55
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
inputPer1m: 11,
|
|
679
|
+
outputPer1m: 49.5,
|
|
680
|
+
cachedInputPer1m: 1.1
|
|
681
|
+
}
|
|
682
|
+
],
|
|
683
|
+
contextWindow: 105e4
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
model: "gpt-5.5",
|
|
687
|
+
provider: "azure",
|
|
688
|
+
region: "eu",
|
|
689
|
+
modality: "text",
|
|
690
|
+
currency: "USD",
|
|
691
|
+
asOf: "2026-07-27",
|
|
692
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
693
|
+
provenance: "litellm-catalog",
|
|
694
|
+
tiers: [
|
|
695
|
+
{
|
|
696
|
+
maxInputTokens: 272e3,
|
|
697
|
+
inputPer1m: 5.5,
|
|
698
|
+
outputPer1m: 33,
|
|
699
|
+
cachedInputPer1m: 0.55
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
inputPer1m: 11,
|
|
703
|
+
outputPer1m: 49.5,
|
|
704
|
+
cachedInputPer1m: 1.1
|
|
705
|
+
}
|
|
706
|
+
],
|
|
707
|
+
contextWindow: 105e4
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
model: "gpt-5.4",
|
|
711
|
+
provider: "azure",
|
|
712
|
+
region: "global",
|
|
713
|
+
modality: "text",
|
|
714
|
+
currency: "USD",
|
|
715
|
+
asOf: "2026-07-27",
|
|
716
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
717
|
+
provenance: "litellm-catalog",
|
|
718
|
+
tiers: [
|
|
719
|
+
{
|
|
720
|
+
maxInputTokens: 272e3,
|
|
721
|
+
inputPer1m: 2.5,
|
|
722
|
+
outputPer1m: 15,
|
|
723
|
+
cachedInputPer1m: 0.25
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
inputPer1m: 5,
|
|
727
|
+
outputPer1m: 22.5,
|
|
728
|
+
cachedInputPer1m: 0.5
|
|
729
|
+
}
|
|
730
|
+
],
|
|
731
|
+
contextWindow: 105e4
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
model: "gpt-5.4",
|
|
735
|
+
provider: "azure",
|
|
736
|
+
region: "us",
|
|
737
|
+
modality: "text",
|
|
738
|
+
currency: "USD",
|
|
739
|
+
asOf: "2026-07-27",
|
|
740
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
741
|
+
provenance: "litellm-catalog",
|
|
742
|
+
tiers: [
|
|
743
|
+
{
|
|
744
|
+
inputPer1m: 2.75,
|
|
745
|
+
outputPer1m: 16.5,
|
|
746
|
+
cachedInputPer1m: 0.28
|
|
747
|
+
}
|
|
748
|
+
],
|
|
749
|
+
contextWindow: 105e4
|
|
750
|
+
},
|
|
751
|
+
{
|
|
752
|
+
model: "gpt-5.4",
|
|
753
|
+
provider: "azure",
|
|
754
|
+
region: "eu",
|
|
755
|
+
modality: "text",
|
|
756
|
+
currency: "USD",
|
|
757
|
+
asOf: "2026-07-27",
|
|
758
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
759
|
+
provenance: "litellm-catalog",
|
|
760
|
+
tiers: [
|
|
761
|
+
{
|
|
762
|
+
inputPer1m: 2.75,
|
|
763
|
+
outputPer1m: 16.5,
|
|
764
|
+
cachedInputPer1m: 0.28
|
|
765
|
+
}
|
|
766
|
+
],
|
|
767
|
+
contextWindow: 105e4
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
model: "gpt-5.6-terra",
|
|
771
|
+
provider: "azure",
|
|
772
|
+
region: "global",
|
|
773
|
+
modality: "text",
|
|
774
|
+
currency: "USD",
|
|
775
|
+
asOf: "2026-07-27",
|
|
776
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
777
|
+
provenance: "litellm-catalog",
|
|
778
|
+
tiers: [
|
|
779
|
+
{
|
|
780
|
+
maxInputTokens: 272e3,
|
|
781
|
+
inputPer1m: 2.5,
|
|
782
|
+
outputPer1m: 15,
|
|
783
|
+
cachedInputPer1m: 0.25
|
|
784
|
+
},
|
|
785
|
+
{
|
|
786
|
+
inputPer1m: 5,
|
|
787
|
+
outputPer1m: 22.5,
|
|
788
|
+
cachedInputPer1m: 0.5
|
|
789
|
+
}
|
|
790
|
+
],
|
|
791
|
+
contextWindow: 105e4
|
|
792
|
+
},
|
|
793
|
+
{
|
|
794
|
+
model: "gpt-5.6-terra",
|
|
795
|
+
provider: "azure",
|
|
796
|
+
region: "eu",
|
|
797
|
+
modality: "text",
|
|
798
|
+
currency: "USD",
|
|
799
|
+
asOf: "2026-07-27",
|
|
800
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
801
|
+
provenance: "litellm-catalog",
|
|
802
|
+
tiers: [
|
|
803
|
+
{
|
|
804
|
+
maxInputTokens: 272e3,
|
|
805
|
+
inputPer1m: 2.75,
|
|
806
|
+
outputPer1m: 16.5,
|
|
807
|
+
cachedInputPer1m: 0.275
|
|
808
|
+
},
|
|
809
|
+
{
|
|
810
|
+
inputPer1m: 5.5,
|
|
811
|
+
outputPer1m: 24.75,
|
|
812
|
+
cachedInputPer1m: 0.55
|
|
813
|
+
}
|
|
814
|
+
],
|
|
815
|
+
contextWindow: 105e4
|
|
816
|
+
},
|
|
817
|
+
{
|
|
818
|
+
model: "gpt-5",
|
|
819
|
+
provider: "azure",
|
|
820
|
+
region: "global",
|
|
821
|
+
modality: "text",
|
|
822
|
+
currency: "USD",
|
|
823
|
+
asOf: "2026-07-27",
|
|
824
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
825
|
+
provenance: "litellm-catalog",
|
|
826
|
+
tiers: [
|
|
827
|
+
{
|
|
828
|
+
inputPer1m: 1.25,
|
|
829
|
+
outputPer1m: 10,
|
|
830
|
+
cachedInputPer1m: 0.125
|
|
831
|
+
}
|
|
832
|
+
],
|
|
833
|
+
contextWindow: 272e3
|
|
834
|
+
},
|
|
835
|
+
{
|
|
836
|
+
model: "gpt-5-mini",
|
|
837
|
+
provider: "azure",
|
|
838
|
+
region: "global",
|
|
839
|
+
modality: "text",
|
|
840
|
+
currency: "USD",
|
|
841
|
+
asOf: "2026-07-27",
|
|
842
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
843
|
+
provenance: "litellm-catalog",
|
|
844
|
+
tiers: [
|
|
845
|
+
{
|
|
846
|
+
inputPer1m: 0.25,
|
|
847
|
+
outputPer1m: 2,
|
|
848
|
+
cachedInputPer1m: 0.025
|
|
849
|
+
}
|
|
850
|
+
],
|
|
851
|
+
contextWindow: 272e3
|
|
852
|
+
},
|
|
853
|
+
{
|
|
854
|
+
model: "gpt-5-nano",
|
|
855
|
+
provider: "azure",
|
|
856
|
+
region: "global",
|
|
857
|
+
modality: "text",
|
|
858
|
+
currency: "USD",
|
|
859
|
+
asOf: "2026-07-27",
|
|
860
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
861
|
+
provenance: "litellm-catalog",
|
|
862
|
+
tiers: [
|
|
863
|
+
{
|
|
864
|
+
inputPer1m: 0.05,
|
|
865
|
+
outputPer1m: 0.4,
|
|
866
|
+
cachedInputPer1m: 5e-3
|
|
867
|
+
}
|
|
868
|
+
],
|
|
869
|
+
contextWindow: 272e3
|
|
870
|
+
},
|
|
871
|
+
{
|
|
872
|
+
model: "gpt-4.1",
|
|
873
|
+
provider: "azure",
|
|
874
|
+
region: "global",
|
|
875
|
+
modality: "text",
|
|
876
|
+
currency: "USD",
|
|
877
|
+
asOf: "2026-07-27",
|
|
878
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
879
|
+
provenance: "litellm-catalog",
|
|
880
|
+
tiers: [
|
|
881
|
+
{
|
|
882
|
+
inputPer1m: 2,
|
|
883
|
+
outputPer1m: 8,
|
|
884
|
+
cachedInputPer1m: 0.5
|
|
885
|
+
}
|
|
886
|
+
],
|
|
887
|
+
batchMultiplier: 0.5,
|
|
888
|
+
contextWindow: 1047576
|
|
889
|
+
},
|
|
890
|
+
{
|
|
891
|
+
model: "gpt-4o",
|
|
892
|
+
provider: "azure",
|
|
893
|
+
region: "global",
|
|
894
|
+
modality: "text",
|
|
895
|
+
currency: "USD",
|
|
896
|
+
asOf: "2026-07-27",
|
|
897
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
898
|
+
provenance: "litellm-catalog",
|
|
899
|
+
tiers: [
|
|
900
|
+
{
|
|
901
|
+
inputPer1m: 2.5,
|
|
902
|
+
outputPer1m: 10,
|
|
903
|
+
cachedInputPer1m: 1.25
|
|
904
|
+
}
|
|
905
|
+
],
|
|
906
|
+
contextWindow: 128e3
|
|
907
|
+
},
|
|
908
|
+
{
|
|
909
|
+
model: "gpt-4o-mini",
|
|
910
|
+
provider: "azure",
|
|
911
|
+
region: "global",
|
|
912
|
+
modality: "text",
|
|
913
|
+
currency: "USD",
|
|
914
|
+
asOf: "2026-07-27",
|
|
915
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
916
|
+
provenance: "litellm-catalog",
|
|
917
|
+
tiers: [
|
|
918
|
+
{
|
|
919
|
+
inputPer1m: 0.165,
|
|
920
|
+
outputPer1m: 0.66,
|
|
921
|
+
cachedInputPer1m: 0.075
|
|
922
|
+
}
|
|
923
|
+
],
|
|
924
|
+
contextWindow: 128e3,
|
|
925
|
+
notes: "Azure charges $0.165/$0.66 per 1M for gpt-4o-mini vs $0.15/$0.60 on OpenAI \u2014 same model, different price."
|
|
926
|
+
},
|
|
927
|
+
{
|
|
928
|
+
model: "gemini-3.1-pro",
|
|
929
|
+
provider: "gemini",
|
|
930
|
+
aliases: [
|
|
931
|
+
"gemini-3.1-pro-preview"
|
|
932
|
+
],
|
|
933
|
+
modality: "text",
|
|
934
|
+
currency: "USD",
|
|
935
|
+
asOf: "2026-07-27",
|
|
936
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
937
|
+
provenance: "vendor-pricing-page",
|
|
938
|
+
tiers: [
|
|
939
|
+
{
|
|
940
|
+
maxInputTokens: 2e5,
|
|
941
|
+
inputPer1m: 2,
|
|
942
|
+
outputPer1m: 12,
|
|
943
|
+
cachedInputPer1m: 0.2,
|
|
944
|
+
reasoningOutputPer1m: 12
|
|
945
|
+
},
|
|
946
|
+
{
|
|
947
|
+
inputPer1m: 4,
|
|
948
|
+
outputPer1m: 18,
|
|
949
|
+
cachedInputPer1m: 0.4,
|
|
950
|
+
reasoningOutputPer1m: 18
|
|
951
|
+
}
|
|
952
|
+
],
|
|
953
|
+
batchMultiplier: 0.5,
|
|
954
|
+
contextWindow: 1048576,
|
|
955
|
+
notes: "Long-context tier: $2/$12 per 1M up to 200K prompt tokens, $4/$18 above it."
|
|
956
|
+
},
|
|
957
|
+
{
|
|
958
|
+
model: "gemini-3-pro",
|
|
959
|
+
provider: "gemini",
|
|
960
|
+
aliases: [
|
|
961
|
+
"gemini-3-pro-preview"
|
|
962
|
+
],
|
|
963
|
+
modality: "text",
|
|
964
|
+
currency: "USD",
|
|
965
|
+
asOf: "2026-07-27",
|
|
966
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
967
|
+
provenance: "vendor-pricing-page",
|
|
968
|
+
tiers: [
|
|
969
|
+
{
|
|
970
|
+
maxInputTokens: 2e5,
|
|
971
|
+
inputPer1m: 2,
|
|
972
|
+
outputPer1m: 12,
|
|
973
|
+
cachedInputPer1m: 0.2,
|
|
974
|
+
reasoningOutputPer1m: 12
|
|
975
|
+
},
|
|
976
|
+
{
|
|
977
|
+
inputPer1m: 4,
|
|
978
|
+
outputPer1m: 18,
|
|
979
|
+
cachedInputPer1m: 0.4,
|
|
980
|
+
reasoningOutputPer1m: 18
|
|
981
|
+
}
|
|
982
|
+
],
|
|
983
|
+
batchMultiplier: 0.5,
|
|
984
|
+
contextWindow: 1048576
|
|
985
|
+
},
|
|
986
|
+
{
|
|
987
|
+
model: "gemini-3.6-flash",
|
|
988
|
+
provider: "gemini",
|
|
989
|
+
modality: "text",
|
|
990
|
+
currency: "USD",
|
|
991
|
+
asOf: "2026-07-27",
|
|
992
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
993
|
+
provenance: "vendor-pricing-page",
|
|
994
|
+
tiers: [
|
|
995
|
+
{
|
|
996
|
+
inputPer1m: 1.5,
|
|
997
|
+
outputPer1m: 7.5,
|
|
998
|
+
cachedInputPer1m: 0.15,
|
|
999
|
+
reasoningOutputPer1m: 7.5
|
|
1000
|
+
}
|
|
1001
|
+
],
|
|
1002
|
+
batchMultiplier: 0.5,
|
|
1003
|
+
contextWindow: 1048576,
|
|
1004
|
+
notes: "Released 2026-07-21. Output pricing dropped ~17% from Gemini 3.5 Flash ($9.00 -> $7.50 per 1M); input unchanged."
|
|
1005
|
+
},
|
|
1006
|
+
{
|
|
1007
|
+
model: "gemini-3.5-flash",
|
|
1008
|
+
provider: "gemini",
|
|
1009
|
+
modality: "text",
|
|
1010
|
+
currency: "USD",
|
|
1011
|
+
asOf: "2026-07-27",
|
|
1012
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1013
|
+
provenance: "vendor-pricing-page",
|
|
1014
|
+
tiers: [
|
|
1015
|
+
{
|
|
1016
|
+
inputPer1m: 1.5,
|
|
1017
|
+
outputPer1m: 9,
|
|
1018
|
+
cachedInputPer1m: 0.15,
|
|
1019
|
+
reasoningOutputPer1m: 9
|
|
1020
|
+
}
|
|
1021
|
+
],
|
|
1022
|
+
batchMultiplier: 0.5,
|
|
1023
|
+
contextWindow: 1048576
|
|
1024
|
+
},
|
|
1025
|
+
{
|
|
1026
|
+
model: "gemini-3.5-flash-lite",
|
|
1027
|
+
provider: "gemini",
|
|
1028
|
+
modality: "text",
|
|
1029
|
+
currency: "USD",
|
|
1030
|
+
asOf: "2026-07-27",
|
|
1031
|
+
source: "https://ai.google.dev/gemini-api/docs/pricing",
|
|
1032
|
+
provenance: "litellm-catalog",
|
|
1033
|
+
tiers: [
|
|
1034
|
+
{
|
|
1035
|
+
inputPer1m: 0.3,
|
|
1036
|
+
outputPer1m: 2.5,
|
|
1037
|
+
cachedInputPer1m: 0.03,
|
|
1038
|
+
reasoningOutputPer1m: 2.5
|
|
1039
|
+
}
|
|
1040
|
+
],
|
|
1041
|
+
batchMultiplier: 0.5,
|
|
1042
|
+
contextWindow: 1048576
|
|
1043
|
+
},
|
|
1044
|
+
{
|
|
1045
|
+
model: "gemini-3.1-flash-lite",
|
|
1046
|
+
provider: "gemini",
|
|
1047
|
+
modality: "text",
|
|
1048
|
+
currency: "USD",
|
|
1049
|
+
asOf: "2026-07-27",
|
|
1050
|
+
source: "https://ai.google.dev/gemini-api/docs/pricing#gemini-3.1-flash-lite",
|
|
1051
|
+
provenance: "litellm-catalog",
|
|
1052
|
+
tiers: [
|
|
1053
|
+
{
|
|
1054
|
+
inputPer1m: 0.25,
|
|
1055
|
+
outputPer1m: 1.5,
|
|
1056
|
+
cachedInputPer1m: 0.025,
|
|
1057
|
+
reasoningOutputPer1m: 1.5
|
|
1058
|
+
}
|
|
1059
|
+
],
|
|
1060
|
+
batchMultiplier: 0.5,
|
|
1061
|
+
contextWindow: 1048576
|
|
1062
|
+
},
|
|
1063
|
+
{
|
|
1064
|
+
model: "gemini-3-flash",
|
|
1065
|
+
provider: "gemini",
|
|
1066
|
+
aliases: [
|
|
1067
|
+
"gemini-3-flash-preview"
|
|
1068
|
+
],
|
|
1069
|
+
modality: "text",
|
|
1070
|
+
currency: "USD",
|
|
1071
|
+
asOf: "2026-07-27",
|
|
1072
|
+
source: "https://ai.google.dev/pricing/gemini-3",
|
|
1073
|
+
provenance: "litellm-catalog",
|
|
1074
|
+
tiers: [
|
|
1075
|
+
{
|
|
1076
|
+
inputPer1m: 0.5,
|
|
1077
|
+
outputPer1m: 3,
|
|
1078
|
+
cachedInputPer1m: 0.05,
|
|
1079
|
+
reasoningOutputPer1m: 3
|
|
1080
|
+
}
|
|
1081
|
+
],
|
|
1082
|
+
contextWindow: 1048576
|
|
1083
|
+
},
|
|
1084
|
+
{
|
|
1085
|
+
model: "gemini-2.5-pro",
|
|
1086
|
+
provider: "gemini",
|
|
1087
|
+
modality: "text",
|
|
1088
|
+
currency: "USD",
|
|
1089
|
+
asOf: "2026-07-27",
|
|
1090
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1091
|
+
provenance: "litellm-catalog",
|
|
1092
|
+
tiers: [
|
|
1093
|
+
{
|
|
1094
|
+
maxInputTokens: 2e5,
|
|
1095
|
+
inputPer1m: 1.25,
|
|
1096
|
+
outputPer1m: 10,
|
|
1097
|
+
cachedInputPer1m: 0.125
|
|
1098
|
+
},
|
|
1099
|
+
{
|
|
1100
|
+
inputPer1m: 2.5,
|
|
1101
|
+
outputPer1m: 15,
|
|
1102
|
+
cachedInputPer1m: 0.25
|
|
1103
|
+
}
|
|
1104
|
+
],
|
|
1105
|
+
contextWindow: 1048576
|
|
1106
|
+
},
|
|
1107
|
+
{
|
|
1108
|
+
model: "gemini-2.5-flash",
|
|
1109
|
+
provider: "gemini",
|
|
1110
|
+
modality: "text",
|
|
1111
|
+
currency: "USD",
|
|
1112
|
+
asOf: "2026-07-27",
|
|
1113
|
+
source: "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
|
1114
|
+
provenance: "litellm-catalog",
|
|
1115
|
+
tiers: [
|
|
1116
|
+
{
|
|
1117
|
+
inputPer1m: 0.3,
|
|
1118
|
+
outputPer1m: 2.5,
|
|
1119
|
+
cachedInputPer1m: 0.03,
|
|
1120
|
+
reasoningOutputPer1m: 2.5
|
|
1121
|
+
}
|
|
1122
|
+
],
|
|
1123
|
+
contextWindow: 1048576
|
|
1124
|
+
},
|
|
1125
|
+
{
|
|
1126
|
+
model: "gemini-2.5-flash-lite",
|
|
1127
|
+
provider: "gemini",
|
|
1128
|
+
modality: "text",
|
|
1129
|
+
currency: "USD",
|
|
1130
|
+
asOf: "2026-07-27",
|
|
1131
|
+
source: "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-lite",
|
|
1132
|
+
provenance: "litellm-catalog",
|
|
1133
|
+
tiers: [
|
|
1134
|
+
{
|
|
1135
|
+
inputPer1m: 0.1,
|
|
1136
|
+
outputPer1m: 0.4,
|
|
1137
|
+
cachedInputPer1m: 0.01,
|
|
1138
|
+
reasoningOutputPer1m: 0.4
|
|
1139
|
+
}
|
|
1140
|
+
],
|
|
1141
|
+
contextWindow: 1048576
|
|
1142
|
+
},
|
|
1143
|
+
{
|
|
1144
|
+
model: "gemini-2.0-flash",
|
|
1145
|
+
provider: "gemini",
|
|
1146
|
+
modality: "text",
|
|
1147
|
+
currency: "USD",
|
|
1148
|
+
asOf: "2026-07-27",
|
|
1149
|
+
source: "https://ai.google.dev/pricing#2_0flash",
|
|
1150
|
+
provenance: "litellm-catalog",
|
|
1151
|
+
tiers: [
|
|
1152
|
+
{
|
|
1153
|
+
inputPer1m: 0.1,
|
|
1154
|
+
outputPer1m: 0.4,
|
|
1155
|
+
cachedInputPer1m: 0.025
|
|
1156
|
+
}
|
|
1157
|
+
],
|
|
1158
|
+
contextWindow: 1048576,
|
|
1159
|
+
notes: "Gemini Developer API price. Vertex AI lists $0.15/$0.60 per 1M for the same model \u2014 see the vertex entry."
|
|
1160
|
+
},
|
|
1161
|
+
{
|
|
1162
|
+
model: "gemini-2.0-flash-lite",
|
|
1163
|
+
provider: "gemini",
|
|
1164
|
+
modality: "text",
|
|
1165
|
+
currency: "USD",
|
|
1166
|
+
asOf: "2026-07-27",
|
|
1167
|
+
source: "https://ai.google.dev/gemini-api/docs/pricing#gemini-2.0-flash-lite",
|
|
1168
|
+
provenance: "litellm-catalog",
|
|
1169
|
+
tiers: [
|
|
1170
|
+
{
|
|
1171
|
+
inputPer1m: 0.075,
|
|
1172
|
+
outputPer1m: 0.3,
|
|
1173
|
+
cachedInputPer1m: 0.01875
|
|
1174
|
+
}
|
|
1175
|
+
],
|
|
1176
|
+
contextWindow: 1048576
|
|
1177
|
+
},
|
|
1178
|
+
{
|
|
1179
|
+
model: "gemini-2.0-flash",
|
|
1180
|
+
provider: "vertex",
|
|
1181
|
+
modality: "text",
|
|
1182
|
+
currency: "USD",
|
|
1183
|
+
asOf: "2026-07-27",
|
|
1184
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1185
|
+
provenance: "vendor-pricing-page",
|
|
1186
|
+
tiers: [
|
|
1187
|
+
{
|
|
1188
|
+
inputPer1m: 0.15,
|
|
1189
|
+
outputPer1m: 0.6
|
|
1190
|
+
}
|
|
1191
|
+
],
|
|
1192
|
+
batchMultiplier: 0.5,
|
|
1193
|
+
notes: "Vertex AI lists $0.15/$0.60 per 1M; the Gemini Developer API lists $0.10/$0.40 for the same model."
|
|
1194
|
+
},
|
|
1195
|
+
{
|
|
1196
|
+
model: "gemini-3.1-pro",
|
|
1197
|
+
provider: "vertex",
|
|
1198
|
+
aliases: [
|
|
1199
|
+
"gemini-3.1-pro-preview"
|
|
1200
|
+
],
|
|
1201
|
+
modality: "text",
|
|
1202
|
+
currency: "USD",
|
|
1203
|
+
asOf: "2026-07-27",
|
|
1204
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1205
|
+
provenance: "vendor-pricing-page",
|
|
1206
|
+
tiers: [
|
|
1207
|
+
{
|
|
1208
|
+
maxInputTokens: 2e5,
|
|
1209
|
+
inputPer1m: 2,
|
|
1210
|
+
outputPer1m: 12,
|
|
1211
|
+
cachedInputPer1m: 0.2,
|
|
1212
|
+
reasoningOutputPer1m: 12
|
|
1213
|
+
},
|
|
1214
|
+
{
|
|
1215
|
+
inputPer1m: 4,
|
|
1216
|
+
outputPer1m: 18,
|
|
1217
|
+
cachedInputPer1m: 0.4,
|
|
1218
|
+
reasoningOutputPer1m: 18
|
|
1219
|
+
}
|
|
1220
|
+
],
|
|
1221
|
+
batchMultiplier: 0.5,
|
|
1222
|
+
contextWindow: 1048576
|
|
1223
|
+
},
|
|
1224
|
+
{
|
|
1225
|
+
model: "gemini-3.6-flash",
|
|
1226
|
+
provider: "vertex",
|
|
1227
|
+
modality: "text",
|
|
1228
|
+
currency: "USD",
|
|
1229
|
+
asOf: "2026-07-27",
|
|
1230
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1231
|
+
provenance: "vendor-pricing-page",
|
|
1232
|
+
tiers: [
|
|
1233
|
+
{
|
|
1234
|
+
inputPer1m: 1.5,
|
|
1235
|
+
outputPer1m: 7.5,
|
|
1236
|
+
cachedInputPer1m: 0.15,
|
|
1237
|
+
reasoningOutputPer1m: 7.5
|
|
1238
|
+
}
|
|
1239
|
+
],
|
|
1240
|
+
batchMultiplier: 0.5,
|
|
1241
|
+
contextWindow: 1048576
|
|
1242
|
+
},
|
|
1243
|
+
{
|
|
1244
|
+
model: "gemini-2.5-pro",
|
|
1245
|
+
provider: "vertex",
|
|
1246
|
+
modality: "text",
|
|
1247
|
+
currency: "USD",
|
|
1248
|
+
asOf: "2026-07-27",
|
|
1249
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1250
|
+
provenance: "vendor-pricing-page",
|
|
1251
|
+
tiers: [
|
|
1252
|
+
{
|
|
1253
|
+
maxInputTokens: 2e5,
|
|
1254
|
+
inputPer1m: 1.25,
|
|
1255
|
+
outputPer1m: 10,
|
|
1256
|
+
cachedInputPer1m: 0.13
|
|
1257
|
+
},
|
|
1258
|
+
{
|
|
1259
|
+
inputPer1m: 2.5,
|
|
1260
|
+
outputPer1m: 15,
|
|
1261
|
+
cachedInputPer1m: 0.25
|
|
1262
|
+
}
|
|
1263
|
+
],
|
|
1264
|
+
batchMultiplier: 0.5,
|
|
1265
|
+
contextWindow: 1048576
|
|
1266
|
+
},
|
|
1267
|
+
{
|
|
1268
|
+
model: "gemini-2.5-flash",
|
|
1269
|
+
provider: "vertex",
|
|
1270
|
+
modality: "text",
|
|
1271
|
+
currency: "USD",
|
|
1272
|
+
asOf: "2026-07-27",
|
|
1273
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1274
|
+
provenance: "vendor-pricing-page",
|
|
1275
|
+
tiers: [
|
|
1276
|
+
{
|
|
1277
|
+
inputPer1m: 0.3,
|
|
1278
|
+
outputPer1m: 2.5,
|
|
1279
|
+
cachedInputPer1m: 0.03,
|
|
1280
|
+
reasoningOutputPer1m: 2.5
|
|
1281
|
+
}
|
|
1282
|
+
],
|
|
1283
|
+
batchMultiplier: 0.5,
|
|
1284
|
+
contextWindow: 1048576
|
|
1285
|
+
},
|
|
1286
|
+
{
|
|
1287
|
+
model: "claude-sonnet-4-6",
|
|
1288
|
+
provider: "openrouter",
|
|
1289
|
+
modality: "text",
|
|
1290
|
+
currency: "USD",
|
|
1291
|
+
asOf: "2026-07-27",
|
|
1292
|
+
source: "https://openrouter.ai/anthropic/claude-sonnet-4.6",
|
|
1293
|
+
provenance: "litellm-catalog",
|
|
1294
|
+
tiers: [
|
|
1295
|
+
{
|
|
1296
|
+
maxInputTokens: 2e5,
|
|
1297
|
+
inputPer1m: 3,
|
|
1298
|
+
outputPer1m: 15,
|
|
1299
|
+
cachedInputPer1m: 0.3,
|
|
1300
|
+
cacheWritePer1m: 3.75
|
|
1301
|
+
},
|
|
1302
|
+
{
|
|
1303
|
+
inputPer1m: 6,
|
|
1304
|
+
outputPer1m: 22.5,
|
|
1305
|
+
cachedInputPer1m: 0.6,
|
|
1306
|
+
cacheWritePer1m: 7.5
|
|
1307
|
+
}
|
|
1308
|
+
],
|
|
1309
|
+
contextWindow: 1e6
|
|
1310
|
+
},
|
|
1311
|
+
{
|
|
1312
|
+
model: "claude-haiku-4-5",
|
|
1313
|
+
provider: "openrouter",
|
|
1314
|
+
modality: "text",
|
|
1315
|
+
currency: "USD",
|
|
1316
|
+
asOf: "2026-07-27",
|
|
1317
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1318
|
+
provenance: "litellm-catalog",
|
|
1319
|
+
tiers: [
|
|
1320
|
+
{
|
|
1321
|
+
inputPer1m: 1,
|
|
1322
|
+
outputPer1m: 5,
|
|
1323
|
+
cachedInputPer1m: 0.1,
|
|
1324
|
+
cacheWritePer1m: 1.25
|
|
1325
|
+
}
|
|
1326
|
+
],
|
|
1327
|
+
contextWindow: 2e5
|
|
1328
|
+
},
|
|
1329
|
+
{
|
|
1330
|
+
model: "gpt-5",
|
|
1331
|
+
provider: "openrouter",
|
|
1332
|
+
modality: "text",
|
|
1333
|
+
currency: "USD",
|
|
1334
|
+
asOf: "2026-07-27",
|
|
1335
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1336
|
+
provenance: "litellm-catalog",
|
|
1337
|
+
tiers: [
|
|
1338
|
+
{
|
|
1339
|
+
inputPer1m: 1.25,
|
|
1340
|
+
outputPer1m: 10,
|
|
1341
|
+
cachedInputPer1m: 0.125
|
|
1342
|
+
}
|
|
1343
|
+
],
|
|
1344
|
+
contextWindow: 272e3
|
|
1345
|
+
},
|
|
1346
|
+
{
|
|
1347
|
+
model: "gpt-5-mini",
|
|
1348
|
+
provider: "openrouter",
|
|
1349
|
+
modality: "text",
|
|
1350
|
+
currency: "USD",
|
|
1351
|
+
asOf: "2026-07-27",
|
|
1352
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1353
|
+
provenance: "litellm-catalog",
|
|
1354
|
+
tiers: [
|
|
1355
|
+
{
|
|
1356
|
+
inputPer1m: 0.25,
|
|
1357
|
+
outputPer1m: 2,
|
|
1358
|
+
cachedInputPer1m: 0.025
|
|
1359
|
+
}
|
|
1360
|
+
],
|
|
1361
|
+
contextWindow: 272e3
|
|
1362
|
+
},
|
|
1363
|
+
{
|
|
1364
|
+
model: "gemini-3.1-pro",
|
|
1365
|
+
provider: "openrouter",
|
|
1366
|
+
aliases: [
|
|
1367
|
+
"gemini-3.1-pro-preview"
|
|
1368
|
+
],
|
|
1369
|
+
modality: "text",
|
|
1370
|
+
currency: "USD",
|
|
1371
|
+
asOf: "2026-07-27",
|
|
1372
|
+
source: "https://openrouter.ai/google/gemini-3.1-pro-preview",
|
|
1373
|
+
provenance: "litellm-catalog",
|
|
1374
|
+
tiers: [
|
|
1375
|
+
{
|
|
1376
|
+
maxInputTokens: 2e5,
|
|
1377
|
+
inputPer1m: 2,
|
|
1378
|
+
outputPer1m: 12,
|
|
1379
|
+
cachedInputPer1m: 0.2
|
|
1380
|
+
},
|
|
1381
|
+
{
|
|
1382
|
+
inputPer1m: 4,
|
|
1383
|
+
outputPer1m: 18,
|
|
1384
|
+
cachedInputPer1m: 0.4,
|
|
1385
|
+
cacheWritePer1m: 0.25
|
|
1386
|
+
}
|
|
1387
|
+
],
|
|
1388
|
+
contextWindow: 1048576
|
|
1389
|
+
},
|
|
1390
|
+
{
|
|
1391
|
+
model: "text-embedding-3-small",
|
|
1392
|
+
provider: "openai",
|
|
1393
|
+
modality: "embedding",
|
|
1394
|
+
currency: "USD",
|
|
1395
|
+
asOf: "2026-07-27",
|
|
1396
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1397
|
+
provenance: "litellm-catalog",
|
|
1398
|
+
tiers: [
|
|
1399
|
+
{
|
|
1400
|
+
inputPer1m: 0.02,
|
|
1401
|
+
outputPer1m: 0
|
|
1402
|
+
}
|
|
1403
|
+
],
|
|
1404
|
+
batchMultiplier: 0.5,
|
|
1405
|
+
contextWindow: 8191
|
|
1406
|
+
},
|
|
1407
|
+
{
|
|
1408
|
+
model: "text-embedding-3-large",
|
|
1409
|
+
provider: "openai",
|
|
1410
|
+
modality: "embedding",
|
|
1411
|
+
currency: "USD",
|
|
1412
|
+
asOf: "2026-07-27",
|
|
1413
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1414
|
+
provenance: "litellm-catalog",
|
|
1415
|
+
tiers: [
|
|
1416
|
+
{
|
|
1417
|
+
inputPer1m: 0.13,
|
|
1418
|
+
outputPer1m: 0
|
|
1419
|
+
}
|
|
1420
|
+
],
|
|
1421
|
+
batchMultiplier: 0.5,
|
|
1422
|
+
contextWindow: 8191
|
|
1423
|
+
},
|
|
1424
|
+
{
|
|
1425
|
+
model: "gemini-embedding-001",
|
|
1426
|
+
provider: "gemini",
|
|
1427
|
+
modality: "embedding",
|
|
1428
|
+
currency: "USD",
|
|
1429
|
+
asOf: "2026-07-27",
|
|
1430
|
+
source: "https://ai.google.dev/gemini-api/docs/embeddings#model-versions",
|
|
1431
|
+
provenance: "litellm-catalog",
|
|
1432
|
+
tiers: [
|
|
1433
|
+
{
|
|
1434
|
+
inputPer1m: 0.15,
|
|
1435
|
+
outputPer1m: 0
|
|
1436
|
+
}
|
|
1437
|
+
],
|
|
1438
|
+
contextWindow: 2048
|
|
1439
|
+
},
|
|
1440
|
+
{
|
|
1441
|
+
model: "dall-e-3",
|
|
1442
|
+
provider: "openai",
|
|
1443
|
+
modality: "image",
|
|
1444
|
+
currency: "USD",
|
|
1445
|
+
asOf: "2026-07-27",
|
|
1446
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1447
|
+
provenance: "litellm-catalog",
|
|
1448
|
+
rates: [
|
|
1449
|
+
{
|
|
1450
|
+
unit: "per_image",
|
|
1451
|
+
amount: 0.04,
|
|
1452
|
+
variant: "1024x1024 standard"
|
|
1453
|
+
}
|
|
1454
|
+
]
|
|
1455
|
+
},
|
|
1456
|
+
{
|
|
1457
|
+
model: "gemini-3-pro-image",
|
|
1458
|
+
provider: "gemini",
|
|
1459
|
+
modality: "image",
|
|
1460
|
+
currency: "USD",
|
|
1461
|
+
asOf: "2026-07-27",
|
|
1462
|
+
source: "https://ai.google.dev/gemini-api/docs/pricing",
|
|
1463
|
+
provenance: "litellm-catalog",
|
|
1464
|
+
rates: [
|
|
1465
|
+
{
|
|
1466
|
+
unit: "per_image",
|
|
1467
|
+
amount: 0.134,
|
|
1468
|
+
variant: "default output resolution"
|
|
1469
|
+
}
|
|
1470
|
+
],
|
|
1471
|
+
notes: "Google bills image output per output-token ($120 per 1M image output tokens); $0.134/image is that rate applied to one default-resolution image. Cost per image therefore varies with resolution."
|
|
1472
|
+
},
|
|
1473
|
+
{
|
|
1474
|
+
model: "imagen-4.0-generate-001",
|
|
1475
|
+
provider: "vertex",
|
|
1476
|
+
modality: "image",
|
|
1477
|
+
currency: "USD",
|
|
1478
|
+
asOf: "2026-07-27",
|
|
1479
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1480
|
+
provenance: "litellm-catalog",
|
|
1481
|
+
rates: [
|
|
1482
|
+
{
|
|
1483
|
+
unit: "per_image",
|
|
1484
|
+
amount: 0.04
|
|
1485
|
+
}
|
|
1486
|
+
]
|
|
1487
|
+
},
|
|
1488
|
+
{
|
|
1489
|
+
model: "veo-3.1-generate-001",
|
|
1490
|
+
provider: "gemini",
|
|
1491
|
+
modality: "video",
|
|
1492
|
+
currency: "USD",
|
|
1493
|
+
asOf: "2026-07-27",
|
|
1494
|
+
source: "https://ai.google.dev/gemini-api/docs/video",
|
|
1495
|
+
provenance: "litellm-catalog",
|
|
1496
|
+
rates: [
|
|
1497
|
+
{
|
|
1498
|
+
unit: "per_second",
|
|
1499
|
+
amount: 0.4
|
|
1500
|
+
}
|
|
1501
|
+
]
|
|
1502
|
+
},
|
|
1503
|
+
{
|
|
1504
|
+
model: "veo-3.1-generate-001",
|
|
1505
|
+
provider: "vertex",
|
|
1506
|
+
modality: "video",
|
|
1507
|
+
currency: "USD",
|
|
1508
|
+
asOf: "2026-07-27",
|
|
1509
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/veo",
|
|
1510
|
+
provenance: "litellm-catalog",
|
|
1511
|
+
rates: [
|
|
1512
|
+
{
|
|
1513
|
+
unit: "per_second",
|
|
1514
|
+
amount: 0.4
|
|
1515
|
+
}
|
|
1516
|
+
]
|
|
1517
|
+
},
|
|
1518
|
+
{
|
|
1519
|
+
model: "tts-1",
|
|
1520
|
+
provider: "openai",
|
|
1521
|
+
modality: "speech",
|
|
1522
|
+
currency: "USD",
|
|
1523
|
+
asOf: "2026-07-27",
|
|
1524
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1525
|
+
provenance: "litellm-catalog",
|
|
1526
|
+
rates: [
|
|
1527
|
+
{
|
|
1528
|
+
unit: "per_character",
|
|
1529
|
+
amount: 15e-6
|
|
1530
|
+
}
|
|
1531
|
+
],
|
|
1532
|
+
notes: "$15 per 1M characters."
|
|
1533
|
+
},
|
|
1534
|
+
{
|
|
1535
|
+
model: "tts-1-hd",
|
|
1536
|
+
provider: "openai",
|
|
1537
|
+
modality: "speech",
|
|
1538
|
+
currency: "USD",
|
|
1539
|
+
asOf: "2026-07-27",
|
|
1540
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1541
|
+
provenance: "litellm-catalog",
|
|
1542
|
+
rates: [
|
|
1543
|
+
{
|
|
1544
|
+
unit: "per_character",
|
|
1545
|
+
amount: 3e-5
|
|
1546
|
+
}
|
|
1547
|
+
],
|
|
1548
|
+
notes: "$30 per 1M characters."
|
|
1549
|
+
},
|
|
1550
|
+
{
|
|
1551
|
+
model: "whisper-1",
|
|
1552
|
+
provider: "openai",
|
|
1553
|
+
modality: "transcription",
|
|
1554
|
+
currency: "USD",
|
|
1555
|
+
asOf: "2026-07-27",
|
|
1556
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1557
|
+
provenance: "litellm-catalog",
|
|
1558
|
+
rates: [
|
|
1559
|
+
{
|
|
1560
|
+
unit: "per_audio_minute",
|
|
1561
|
+
amount: 6e-3
|
|
1562
|
+
}
|
|
1563
|
+
],
|
|
1564
|
+
notes: "litellm records $0.0001 per audio second; $0.006/minute is that rate x 60."
|
|
1565
|
+
}
|
|
1566
|
+
]
|
|
1567
|
+
};
|
|
1568
|
+
}
|
|
1569
|
+
});
|
|
1570
|
+
|
|
1571
|
+
// src/core/router/pricing.ts
|
|
1572
|
+
function normalizeForPricing(id) {
|
|
1573
|
+
let s = id.trim().toLowerCase();
|
|
1574
|
+
s = s.replace(/^models\//, "");
|
|
1575
|
+
const slash = s.lastIndexOf("/");
|
|
1576
|
+
if (slash !== -1) s = s.slice(slash + 1);
|
|
1577
|
+
const tag = s.search(/[:@]/);
|
|
1578
|
+
if (tag !== -1) s = s.slice(0, tag);
|
|
1579
|
+
s = s.replace(/-latest$/, "");
|
|
1580
|
+
s = s.replace(/-preview(?:-\d{2}-\d{2})?$/, "");
|
|
1581
|
+
s = s.replace(/-\d{4}-\d{2}-\d{2}$/, "");
|
|
1582
|
+
s = s.replace(/-\d{8}$/, "");
|
|
1583
|
+
s = s.replace(/-preview$/, "");
|
|
1584
|
+
return s;
|
|
1585
|
+
}
|
|
1586
|
+
function indexKey(model, provider, region) {
|
|
1587
|
+
return `${provider}|${normalizeForPricing(model)}|${region ?? ""}`;
|
|
1588
|
+
}
|
|
1589
|
+
function findPricing(modelId, provider, opts = {}) {
|
|
1590
|
+
const ids = [modelId, opts.baseModelId].filter((x) => !!x);
|
|
1591
|
+
const providers = [provider, ...opts.fallbackProviders ?? []];
|
|
1592
|
+
for (const p of providers) {
|
|
1593
|
+
for (const id of ids) {
|
|
1594
|
+
const hit = (opts.region ? INDEX.get(indexKey(id, p, opts.region)) : void 0) ?? INDEX.get(indexKey(id, p));
|
|
1595
|
+
if (hit) return { entry: hit, matchedProvider: p, viaFallback: p !== provider };
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
return null;
|
|
1599
|
+
}
|
|
1600
|
+
function pricingDatasetMeta() {
|
|
1601
|
+
const { schemaVersion, generatedAt, defaultCurrency } = DATASET;
|
|
1602
|
+
return { schemaVersion, generatedAt, defaultCurrency };
|
|
1603
|
+
}
|
|
1604
|
+
function tierFor(entry, inputTokens = 0) {
|
|
1605
|
+
const tiers = entry.tiers;
|
|
1606
|
+
if (!tiers?.length) return null;
|
|
1607
|
+
for (const t of tiers) {
|
|
1608
|
+
if (t.maxInputTokens === void 0 || inputTokens <= t.maxInputTokens) return t;
|
|
1609
|
+
}
|
|
1610
|
+
return tiers[tiers.length - 1] ?? null;
|
|
1611
|
+
}
|
|
1612
|
+
function tokenRatesPer1k(entry, opts = {}) {
|
|
1613
|
+
const tier = tierFor(entry, opts.inputTokens ?? 0);
|
|
1614
|
+
if (!tier) return null;
|
|
1615
|
+
const mult = (opts.batch ? entry.batchMultiplier ?? 1 : 1) * PER_1M_TO_PER_1K;
|
|
1616
|
+
const out = {
|
|
1617
|
+
input: tier.inputPer1m * mult,
|
|
1618
|
+
output: tier.outputPer1m * mult
|
|
1619
|
+
};
|
|
1620
|
+
if (tier.cachedInputPer1m !== void 0) out.cachedInput = tier.cachedInputPer1m * mult;
|
|
1621
|
+
if (tier.cacheWritePer1m !== void 0) out.cacheWrite = tier.cacheWritePer1m * mult;
|
|
1622
|
+
if (tier.reasoningOutputPer1m !== void 0) out.reasoningOutput = tier.reasoningOutputPer1m * mult;
|
|
1623
|
+
return out;
|
|
1624
|
+
}
|
|
1625
|
+
function isLoopbackOrPrivateHost(url) {
|
|
1626
|
+
if (!url) return false;
|
|
1627
|
+
try {
|
|
1628
|
+
const host = new URL(url).hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
1629
|
+
if (LOOPBACK_HOSTS.has(host) || LOOPBACK_HOSTS.has(`[${host}]`)) return true;
|
|
1630
|
+
if (host.endsWith(".local")) return true;
|
|
1631
|
+
if (/^127\./.test(host)) return true;
|
|
1632
|
+
if (/^10\./.test(host)) return true;
|
|
1633
|
+
if (/^192\.168\./.test(host)) return true;
|
|
1634
|
+
if (/^172\.(1[6-9]|2\d|3[01])\./.test(host)) return true;
|
|
1635
|
+
return false;
|
|
1636
|
+
} catch {
|
|
1637
|
+
return false;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
function isLocalEndpoint(cfg) {
|
|
1641
|
+
if (typeof cfg.local === "boolean") return cfg.local;
|
|
1642
|
+
if (cfg.type === "ollama") return true;
|
|
1643
|
+
if (cfg.type === "openai-compatible") return isLoopbackOrPrivateHost(cfg.baseUrl);
|
|
1644
|
+
return false;
|
|
1645
|
+
}
|
|
1646
|
+
function resolvePricing(model, opts = {}) {
|
|
1647
|
+
if (model.isLocal) return FREE;
|
|
1648
|
+
const fallbackProviders = model.provider === "azure" ? ["openai"] : model.provider === "gemini" ? ["vertex"] : [];
|
|
1649
|
+
const match = findPricing(model.id, model.provider, {
|
|
1650
|
+
region: opts.region,
|
|
1651
|
+
baseModelId: model.baseModelId,
|
|
1652
|
+
fallbackProviders
|
|
1653
|
+
});
|
|
1654
|
+
if (!match) return UNKNOWN;
|
|
1655
|
+
const rates = tokenRatesPer1k(match.entry, { inputTokens: opts.inputTokens, batch: opts.batch });
|
|
1656
|
+
if (!rates) return UNKNOWN;
|
|
1657
|
+
return {
|
|
1658
|
+
input: rates.input,
|
|
1659
|
+
output: rates.output,
|
|
1660
|
+
unknown: false,
|
|
1661
|
+
free: false,
|
|
1662
|
+
entry: match.entry,
|
|
1663
|
+
...match.viaFallback ? { estimatedFromProvider: match.matchedProvider } : {}
|
|
1664
|
+
};
|
|
1665
|
+
}
|
|
1666
|
+
function withResolvedPricing(model, opts = {}) {
|
|
1667
|
+
const p = resolvePricing(model, opts);
|
|
1668
|
+
return {
|
|
1669
|
+
...model,
|
|
1670
|
+
inputCostPer1kTokens: p.input,
|
|
1671
|
+
outputCostPer1kTokens: p.output,
|
|
1672
|
+
...p.unknown ? { pricingUnknown: true } : { pricingUnknown: false }
|
|
1673
|
+
};
|
|
1674
|
+
}
|
|
1675
|
+
function isPricingUnknown(model) {
|
|
1676
|
+
return model.pricingUnknown === true && !model.isLocal;
|
|
1677
|
+
}
|
|
1678
|
+
function blendedCostPer1k(model) {
|
|
1679
|
+
if (isPricingUnknown(model)) return BLENDED_COST_CEILING;
|
|
1680
|
+
return model.inputCostPer1kTokens + model.outputCostPer1kTokens * 2;
|
|
1681
|
+
}
|
|
1682
|
+
function reconcilePrice(model, live) {
|
|
1683
|
+
const baseline = resolvePricing(model);
|
|
1684
|
+
const liveIsZero = !(live.input > 0) && !(live.output > 0);
|
|
1685
|
+
if (liveIsZero && !model.isLocal) {
|
|
1686
|
+
return { input: baseline.input, output: baseline.output, accepted: false };
|
|
1687
|
+
}
|
|
1688
|
+
if (baseline.unknown || !baseline.entry) {
|
|
1689
|
+
return { input: live.input, output: live.output, accepted: true };
|
|
1690
|
+
}
|
|
1691
|
+
const dsBlend = baseline.input + baseline.output * 2;
|
|
1692
|
+
const liveBlend = live.input + live.output * 2;
|
|
1693
|
+
const ratio = dsBlend > 0 ? liveBlend / dsBlend : Number.POSITIVE_INFINITY;
|
|
1694
|
+
const material = dsBlend > 0 && Math.abs(ratio - 1) > DISAGREEMENT_THRESHOLD;
|
|
1695
|
+
return {
|
|
1696
|
+
input: live.input,
|
|
1697
|
+
output: live.output,
|
|
1698
|
+
accepted: true,
|
|
1699
|
+
...material ? {
|
|
1700
|
+
disagreement: {
|
|
1701
|
+
modelId: model.id,
|
|
1702
|
+
provider: model.provider,
|
|
1703
|
+
dataset: { input: baseline.input, output: baseline.output },
|
|
1704
|
+
live: { input: live.input, output: live.output },
|
|
1705
|
+
ratio: Math.round(ratio * 1e3) / 1e3,
|
|
1706
|
+
datasetAsOf: baseline.entry.asOf,
|
|
1707
|
+
datasetSource: baseline.entry.source
|
|
1708
|
+
}
|
|
1709
|
+
} : {}
|
|
1710
|
+
};
|
|
1711
|
+
}
|
|
1712
|
+
var DATASET, INDEX, PER_1M_TO_PER_1K, LOOPBACK_HOSTS, UNKNOWN, FREE, BLENDED_COST_CEILING, DISAGREEMENT_THRESHOLD;
|
|
1713
|
+
var init_pricing = __esm({
|
|
1714
|
+
"src/core/router/pricing.ts"() {
|
|
1715
|
+
init_pricing_data();
|
|
1716
|
+
DATASET = pricing_data_default;
|
|
1717
|
+
INDEX = /* @__PURE__ */ new Map();
|
|
1718
|
+
for (const entry of DATASET.entries) {
|
|
1719
|
+
for (const name of [entry.model, ...entry.aliases ?? []]) {
|
|
1720
|
+
const withRegion = indexKey(name, entry.provider, entry.region);
|
|
1721
|
+
if (!INDEX.has(withRegion)) INDEX.set(withRegion, entry);
|
|
1722
|
+
const bare = indexKey(name, entry.provider);
|
|
1723
|
+
const existing = INDEX.get(bare);
|
|
1724
|
+
if (!existing || existing.region && existing.region !== "global" && entry.region === "global") {
|
|
1725
|
+
INDEX.set(bare, entry);
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
PER_1M_TO_PER_1K = 1 / 1e3;
|
|
1730
|
+
LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]"]);
|
|
1731
|
+
UNKNOWN = { input: 0, output: 0, unknown: true, free: false };
|
|
1732
|
+
FREE = { input: 0, output: 0, unknown: false, free: true };
|
|
1733
|
+
BLENDED_COST_CEILING = 0.05;
|
|
1734
|
+
DISAGREEMENT_THRESHOLD = 0.02;
|
|
1735
|
+
}
|
|
1736
|
+
});
|
|
1737
|
+
|
|
62
1738
|
// src/constants.ts
|
|
63
1739
|
var CASCADE_VERSION, CASCADE_CONFIG_FILE, CASCADE_DB_FILE, CASCADE_DASHBOARD_SECRET_FILE, GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE, GLOBAL_KEYSTORE_FILE, GLOBAL_CREDENTIALS_FILE, GLOBAL_RUNTIME_DB_FILE, DEFAULT_DASHBOARD_PORT, DEFAULT_CONTEXT_LIMIT, DEFAULT_AUTO_SUMMARIZE_AT, MODELS, T1_MODEL_PRIORITY, T2_MODEL_PRIORITY, T3_MODEL_PRIORITY, VISION_MODEL_PRIORITY, COMPLEXITY_T2_COUNT, THEME_NAMES, DEFAULT_THEME, OLLAMA_BASE_URL, LM_STUDIO_BASE_URL, AZURE_BASE_URL_TEMPLATE, TOOL_NAMES, DEFAULT_APPROVAL_REQUIRED;
|
|
64
1740
|
var init_constants = __esm({
|
|
65
1741
|
"src/constants.ts"() {
|
|
66
|
-
|
|
1742
|
+
init_pricing();
|
|
1743
|
+
CASCADE_VERSION = "0.50.0";
|
|
67
1744
|
CASCADE_CONFIG_FILE = ".cascade/config.json";
|
|
68
1745
|
CASCADE_DB_FILE = ".cascade/memory.db";
|
|
69
1746
|
CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
|
|
@@ -404,6 +2081,13 @@ var init_constants = __esm({
|
|
|
404
2081
|
for (const _m of Object.values(MODELS)) {
|
|
405
2082
|
if (_m.supportsToolUse === void 0) _m.supportsToolUse = !_m.isLocal;
|
|
406
2083
|
}
|
|
2084
|
+
for (const _m of Object.values(MODELS)) {
|
|
2085
|
+
const priced = resolvePricing(_m);
|
|
2086
|
+
if (priced.unknown || priced.free || priced.estimatedFromProvider) continue;
|
|
2087
|
+
_m.inputCostPer1kTokens = priced.input;
|
|
2088
|
+
_m.outputCostPer1kTokens = priced.output;
|
|
2089
|
+
_m.pricingUnknown = false;
|
|
2090
|
+
}
|
|
407
2091
|
T1_MODEL_PRIORITY = [
|
|
408
2092
|
"gpt-5.5",
|
|
409
2093
|
"gpt-5.4",
|
|
@@ -488,24 +2172,53 @@ var init_constants = __esm({
|
|
|
488
2172
|
|
|
489
2173
|
// src/utils/cost.ts
|
|
490
2174
|
function resolveModelPricing(model) {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
2175
|
+
if (model.isLocal) return { input: 0, output: 0, unknown: false };
|
|
2176
|
+
if (model.inputCostPer1kTokens > 0 || model.outputCostPer1kTokens > 0) {
|
|
2177
|
+
return {
|
|
2178
|
+
input: model.inputCostPer1kTokens,
|
|
2179
|
+
output: model.outputCostPer1kTokens,
|
|
2180
|
+
unknown: false
|
|
2181
|
+
};
|
|
2182
|
+
}
|
|
2183
|
+
const fromDataset = resolvePricing(model);
|
|
2184
|
+
if (!fromDataset.unknown) {
|
|
2185
|
+
return {
|
|
2186
|
+
input: fromDataset.input,
|
|
2187
|
+
output: fromDataset.output,
|
|
2188
|
+
unknown: false,
|
|
2189
|
+
...fromDataset.estimatedFromProvider ? { estimatedFromProvider: fromDataset.estimatedFromProvider } : {}
|
|
2190
|
+
};
|
|
2191
|
+
}
|
|
2192
|
+
if (model.id) {
|
|
494
2193
|
const known = Object.values(MODELS).find((m) => m.id === model.id && !m.isLocal);
|
|
495
|
-
if (known) {
|
|
496
|
-
|
|
497
|
-
|
|
2194
|
+
if (known && (known.inputCostPer1kTokens > 0 || known.outputCostPer1kTokens > 0)) {
|
|
2195
|
+
return {
|
|
2196
|
+
input: known.inputCostPer1kTokens,
|
|
2197
|
+
output: known.outputCostPer1kTokens,
|
|
2198
|
+
unknown: false
|
|
2199
|
+
};
|
|
498
2200
|
}
|
|
499
2201
|
}
|
|
500
|
-
return { input, output };
|
|
2202
|
+
return { input: 0, output: 0, unknown: true };
|
|
501
2203
|
}
|
|
502
2204
|
function calculateCost(inputTokens, outputTokens, model) {
|
|
503
2205
|
const { input, output } = resolveModelPricing(model);
|
|
504
2206
|
return inputTokens / 1e3 * input + outputTokens / 1e3 * output;
|
|
505
2207
|
}
|
|
2208
|
+
function buildTokenUsage(inputTokens, outputTokens, model) {
|
|
2209
|
+
const { input, output, unknown } = resolveModelPricing(model);
|
|
2210
|
+
return {
|
|
2211
|
+
inputTokens,
|
|
2212
|
+
outputTokens,
|
|
2213
|
+
totalTokens: inputTokens + outputTokens,
|
|
2214
|
+
estimatedCostUsd: inputTokens / 1e3 * input + outputTokens / 1e3 * output,
|
|
2215
|
+
...unknown ? { costUnknown: true } : {}
|
|
2216
|
+
};
|
|
2217
|
+
}
|
|
506
2218
|
var init_cost = __esm({
|
|
507
2219
|
"src/utils/cost.ts"() {
|
|
508
2220
|
init_constants();
|
|
2221
|
+
init_pricing();
|
|
509
2222
|
}
|
|
510
2223
|
});
|
|
511
2224
|
|
|
@@ -531,12 +2244,7 @@ var init_base = __esm({
|
|
|
531
2244
|
return calculateCost(inputTokens, outputTokens, this.model);
|
|
532
2245
|
}
|
|
533
2246
|
makeUsage(inputTokens, outputTokens) {
|
|
534
|
-
return
|
|
535
|
-
inputTokens,
|
|
536
|
-
outputTokens,
|
|
537
|
-
totalTokens: inputTokens + outputTokens,
|
|
538
|
-
estimatedCostUsd: this.estimateCost(inputTokens, outputTokens)
|
|
539
|
-
};
|
|
2247
|
+
return buildTokenUsage(inputTokens, outputTokens, this.model);
|
|
540
2248
|
}
|
|
541
2249
|
};
|
|
542
2250
|
}
|
|
@@ -612,6 +2320,7 @@ var init_anthropic = __esm({
|
|
|
612
2320
|
"src/providers/anthropic.ts"() {
|
|
613
2321
|
init_constants();
|
|
614
2322
|
init_base();
|
|
2323
|
+
init_pricing();
|
|
615
2324
|
init_model_filter();
|
|
616
2325
|
AnthropicProvider = class extends BaseProvider {
|
|
617
2326
|
client;
|
|
@@ -722,7 +2431,7 @@ var init_anthropic = __esm({
|
|
|
722
2431
|
return data.data.filter((m) => isChatModel(m.id)).map((m) => {
|
|
723
2432
|
const known = Object.values(MODELS).find((km) => km.id === m.id && km.provider === "anthropic");
|
|
724
2433
|
if (known) return known;
|
|
725
|
-
return {
|
|
2434
|
+
return withResolvedPricing({
|
|
726
2435
|
id: m.id,
|
|
727
2436
|
name: m.display_name || m.id,
|
|
728
2437
|
provider: "anthropic",
|
|
@@ -733,7 +2442,7 @@ var init_anthropic = __esm({
|
|
|
733
2442
|
maxOutputTokens: 8e3,
|
|
734
2443
|
supportsStreaming: true,
|
|
735
2444
|
isLocal: false
|
|
736
|
-
};
|
|
2445
|
+
});
|
|
737
2446
|
});
|
|
738
2447
|
} catch {
|
|
739
2448
|
return Object.values(MODELS).filter((m) => m.provider === "anthropic");
|
|
@@ -828,6 +2537,7 @@ var init_openai = __esm({
|
|
|
828
2537
|
"src/providers/openai.ts"() {
|
|
829
2538
|
init_constants();
|
|
830
2539
|
init_base();
|
|
2540
|
+
init_pricing();
|
|
831
2541
|
init_model_filter();
|
|
832
2542
|
OpenAIProvider = class extends BaseProvider {
|
|
833
2543
|
client;
|
|
@@ -953,7 +2663,7 @@ var init_openai = __esm({
|
|
|
953
2663
|
return response.data.filter((m) => isChatModel(m.id)).map((m) => {
|
|
954
2664
|
const known = Object.values(MODELS).find((km) => km.id === m.id && km.provider === "openai");
|
|
955
2665
|
if (known) return known;
|
|
956
|
-
return {
|
|
2666
|
+
return withResolvedPricing({
|
|
957
2667
|
id: m.id,
|
|
958
2668
|
name: m.id,
|
|
959
2669
|
provider: "openai",
|
|
@@ -964,7 +2674,7 @@ var init_openai = __esm({
|
|
|
964
2674
|
maxOutputTokens: 4e3,
|
|
965
2675
|
supportsStreaming: true,
|
|
966
2676
|
isLocal: false
|
|
967
|
-
};
|
|
2677
|
+
});
|
|
968
2678
|
});
|
|
969
2679
|
} catch {
|
|
970
2680
|
return Object.values(MODELS).filter((m) => m.provider === "openai");
|
|
@@ -1067,6 +2777,10 @@ function azureModelForDeployment(cfg) {
|
|
|
1067
2777
|
const baseModelId = cfg.model?.trim() || inferAzureBaseModel(id) || void 0;
|
|
1068
2778
|
const base = baseModelId ? MODELS[baseModelId] : void 0;
|
|
1069
2779
|
if (base) {
|
|
2780
|
+
const azurePrice = resolvePricing(
|
|
2781
|
+
{ id, provider: "azure", isLocal: false, baseModelId },
|
|
2782
|
+
{ region: cfg.region }
|
|
2783
|
+
);
|
|
1070
2784
|
return {
|
|
1071
2785
|
...base,
|
|
1072
2786
|
id,
|
|
@@ -1075,7 +2789,12 @@ function azureModelForDeployment(cfg) {
|
|
|
1075
2789
|
provider: "azure",
|
|
1076
2790
|
baseModelId,
|
|
1077
2791
|
// real identity for benchmark + live pricing
|
|
1078
|
-
supportsToolUse: base.supportsToolUse ?? true
|
|
2792
|
+
supportsToolUse: base.supportsToolUse ?? true,
|
|
2793
|
+
...azurePrice.unknown ? {} : {
|
|
2794
|
+
inputCostPer1kTokens: azurePrice.input,
|
|
2795
|
+
outputCostPer1kTokens: azurePrice.output,
|
|
2796
|
+
pricingUnknown: false
|
|
2797
|
+
}
|
|
1079
2798
|
};
|
|
1080
2799
|
}
|
|
1081
2800
|
return {
|
|
@@ -1098,6 +2817,7 @@ var init_azure = __esm({
|
|
|
1098
2817
|
"src/providers/azure.ts"() {
|
|
1099
2818
|
init_constants();
|
|
1100
2819
|
init_openai();
|
|
2820
|
+
init_pricing();
|
|
1101
2821
|
DEFAULT_AZURE_API_VERSION = "2024-12-01-preview";
|
|
1102
2822
|
AzureOpenAIProvider = class extends OpenAIProvider {
|
|
1103
2823
|
constructor(config, model) {
|
|
@@ -1158,6 +2878,7 @@ var init_gemini = __esm({
|
|
|
1158
2878
|
"src/providers/gemini.ts"() {
|
|
1159
2879
|
init_constants();
|
|
1160
2880
|
init_base();
|
|
2881
|
+
init_pricing();
|
|
1161
2882
|
init_model_filter();
|
|
1162
2883
|
GeminiProvider = class extends BaseProvider {
|
|
1163
2884
|
client;
|
|
@@ -1258,7 +2979,7 @@ var init_gemini = __esm({
|
|
|
1258
2979
|
(km) => km.id === id && km.provider === "gemini"
|
|
1259
2980
|
);
|
|
1260
2981
|
if (known) return known;
|
|
1261
|
-
return {
|
|
2982
|
+
return withResolvedPricing({
|
|
1262
2983
|
id,
|
|
1263
2984
|
name: m.displayName || id,
|
|
1264
2985
|
provider: "gemini",
|
|
@@ -1269,7 +2990,7 @@ var init_gemini = __esm({
|
|
|
1269
2990
|
maxOutputTokens: m.outputTokenLimit || 8e3,
|
|
1270
2991
|
supportsStreaming: true,
|
|
1271
2992
|
isLocal: false
|
|
1272
|
-
};
|
|
2993
|
+
});
|
|
1273
2994
|
});
|
|
1274
2995
|
} catch {
|
|
1275
2996
|
return Object.values(MODELS).filter((m) => m.provider === "gemini");
|
|
@@ -1475,6 +3196,7 @@ var init_ollama = __esm({
|
|
|
1475
3196
|
init_constants();
|
|
1476
3197
|
init_net();
|
|
1477
3198
|
init_base();
|
|
3199
|
+
init_pricing();
|
|
1478
3200
|
init_model_filter();
|
|
1479
3201
|
TOOL_CAPABLE_FAMILIES = [
|
|
1480
3202
|
"llama3.1",
|
|
@@ -1634,9 +3356,10 @@ var init_ollama = __esm({
|
|
|
1634
3356
|
return supportedKeywords.some((k) => name.includes(k)) && isChatModel(name);
|
|
1635
3357
|
});
|
|
1636
3358
|
const shows = await Promise.all(entries.map((m) => this.showModel(m.name)));
|
|
3359
|
+
const local = isLocalEndpoint({ ...this.config, type: "ollama" });
|
|
1637
3360
|
return entries.map((m, i) => {
|
|
1638
3361
|
const show = shows[i];
|
|
1639
|
-
return {
|
|
3362
|
+
return withResolvedPricing({
|
|
1640
3363
|
id: m.name,
|
|
1641
3364
|
name: m.name,
|
|
1642
3365
|
provider: "ollama",
|
|
@@ -1646,10 +3369,10 @@ var init_ollama = __esm({
|
|
|
1646
3369
|
outputCostPer1kTokens: 0,
|
|
1647
3370
|
maxOutputTokens: 4e3,
|
|
1648
3371
|
supportsStreaming: true,
|
|
1649
|
-
isLocal:
|
|
3372
|
+
isLocal: local,
|
|
1650
3373
|
supportsToolUse: show?.tools ?? isToolCapable(m.name),
|
|
1651
3374
|
minSizeB: this.parseSizeB(m.details?.parameter_size)
|
|
1652
|
-
};
|
|
3375
|
+
});
|
|
1653
3376
|
});
|
|
1654
3377
|
} catch {
|
|
1655
3378
|
return [];
|
|
@@ -1733,6 +3456,7 @@ var init_openai_compatible = __esm({
|
|
|
1733
3456
|
init_openai();
|
|
1734
3457
|
init_model_filter();
|
|
1735
3458
|
init_net();
|
|
3459
|
+
init_pricing();
|
|
1736
3460
|
OpenAICompatibleProvider = class extends OpenAIProvider {
|
|
1737
3461
|
constructor(config, model) {
|
|
1738
3462
|
super({ ...config, apiKey: config.apiKey ?? "not-required" }, model);
|
|
@@ -1766,7 +3490,8 @@ var init_openai_compatible = __esm({
|
|
|
1766
3490
|
}).filter((x) => typeof x === "string" && x.length > 0);
|
|
1767
3491
|
if (ids.length === 0) return [this.model];
|
|
1768
3492
|
const chatIds = ids.filter((id) => isChatModel(id));
|
|
1769
|
-
|
|
3493
|
+
const local = isLocalEndpoint(this.config);
|
|
3494
|
+
return (chatIds.length ? chatIds : ids).map((id) => withResolvedPricing({
|
|
1770
3495
|
id,
|
|
1771
3496
|
name: id,
|
|
1772
3497
|
provider: "openai-compatible",
|
|
@@ -1776,7 +3501,7 @@ var init_openai_compatible = __esm({
|
|
|
1776
3501
|
outputCostPer1kTokens: 0,
|
|
1777
3502
|
maxOutputTokens: 4e3,
|
|
1778
3503
|
supportsStreaming: true,
|
|
1779
|
-
isLocal:
|
|
3504
|
+
isLocal: local
|
|
1780
3505
|
}));
|
|
1781
3506
|
}
|
|
1782
3507
|
async isAvailable() {
|
|
@@ -3098,7 +4823,18 @@ var ProviderConfigSchema = z.object({
|
|
|
3098
4823
|
apiVersion: z.string().optional(),
|
|
3099
4824
|
model: z.string().optional(),
|
|
3100
4825
|
authToken: z.string().optional(),
|
|
3101
|
-
credentialSource: z.string().optional()
|
|
4826
|
+
credentialSource: z.string().optional(),
|
|
4827
|
+
/**
|
|
4828
|
+
* "This endpoint is self-hosted, so inference genuinely costs $0."
|
|
4829
|
+
* Only meaningful for `ollama` and `openai-compatible`. Unset ⇒ inferred:
|
|
4830
|
+
* ollama is local, openai-compatible is local when its baseUrl is
|
|
4831
|
+
* loopback/LAN. Set it explicitly to override — e.g. `false` for an Ollama
|
|
4832
|
+
* instance you rent, `true` for a llama.cpp box on your own LAN reached by
|
|
4833
|
+
* hostname.
|
|
4834
|
+
*/
|
|
4835
|
+
local: z.boolean().optional(),
|
|
4836
|
+
/** Region for providers whose price varies by region (Azure: global | us | eu | …). */
|
|
4837
|
+
region: z.string().optional()
|
|
3102
4838
|
});
|
|
3103
4839
|
var ModelOverridesSchema = z.object({
|
|
3104
4840
|
t1: z.string().optional(),
|
|
@@ -3947,6 +5683,7 @@ init_openai();
|
|
|
3947
5683
|
|
|
3948
5684
|
// src/core/router/selector.ts
|
|
3949
5685
|
init_constants();
|
|
5686
|
+
init_pricing();
|
|
3950
5687
|
function normalizeModelId(id) {
|
|
3951
5688
|
return id.replace(/^models\//, "").toLowerCase();
|
|
3952
5689
|
}
|
|
@@ -4148,7 +5885,7 @@ var ModelSelector = class {
|
|
|
4148
5885
|
else if (this.availableProviders.size === 1) providerStr = Array.from(this.availableProviders)[0];
|
|
4149
5886
|
}
|
|
4150
5887
|
if (providerStr && this.availableProviders.has(providerStr)) {
|
|
4151
|
-
const dynamicModel = {
|
|
5888
|
+
const dynamicModel = withResolvedPricing({
|
|
4152
5889
|
id: actualId,
|
|
4153
5890
|
name: actualId,
|
|
4154
5891
|
provider: providerStr,
|
|
@@ -4159,7 +5896,7 @@ var ModelSelector = class {
|
|
|
4159
5896
|
maxOutputTokens: 8e3,
|
|
4160
5897
|
supportsStreaming: true,
|
|
4161
5898
|
isLocal: providerStr === "ollama"
|
|
4162
|
-
};
|
|
5899
|
+
});
|
|
4163
5900
|
this.addDynamicModel(dynamicModel);
|
|
4164
5901
|
return dynamicModel;
|
|
4165
5902
|
}
|
|
@@ -4529,6 +6266,7 @@ function computeDelegationSavings(stats, t1Model) {
|
|
|
4529
6266
|
|
|
4530
6267
|
// src/core/router/live-data.ts
|
|
4531
6268
|
init_constants();
|
|
6269
|
+
init_pricing();
|
|
4532
6270
|
var DEFAULT_SNAPSHOT_URL = "https://raw.githubusercontent.com/Varun-SV/Cascade-AI/main/src/core/router/benchmark-data.json";
|
|
4533
6271
|
var OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models";
|
|
4534
6272
|
var FETCH_TIMEOUT_MS = 8e3;
|
|
@@ -4546,6 +6284,8 @@ var LiveDataProvider = class {
|
|
|
4546
6284
|
snapshot = null;
|
|
4547
6285
|
prices = /* @__PURE__ */ new Map();
|
|
4548
6286
|
capabilities = /* @__PURE__ */ new Map();
|
|
6287
|
+
/** Dataset-vs-live price mismatches, deduped by `provider:modelId`. */
|
|
6288
|
+
disagreements = /* @__PURE__ */ new Map();
|
|
4549
6289
|
source = "bundled";
|
|
4550
6290
|
fetchedAt = 0;
|
|
4551
6291
|
loaded = false;
|
|
@@ -4699,14 +6439,45 @@ var LiveDataProvider = class {
|
|
|
4699
6439
|
/**
|
|
4700
6440
|
* Returns a price-corrected copy of each model when live pricing is known,
|
|
4701
6441
|
* leaving the original untouched (so the shared catalog is never mutated).
|
|
6442
|
+
*
|
|
6443
|
+
* Reconciliation policy (see reconcilePrice in pricing.ts): the bundled
|
|
6444
|
+
* dataset is the authoritative baseline, and a live quote overrides it
|
|
6445
|
+
* because it is fresher by construction — but the disagreement is RECORDED,
|
|
6446
|
+
* not swallowed. A dataset/live mismatch is the best signal available that
|
|
6447
|
+
* the committed prices have gone stale, so it must be visible rather than
|
|
6448
|
+
* silently resolved in either direction. A live quote of $0 for a non-local
|
|
6449
|
+
* model is rejected outright: that is the very failure mode this change
|
|
6450
|
+
* exists to prevent, and a marketplace's free-tier alias must never zero out
|
|
6451
|
+
* the price of the paid model Cascade is actually calling.
|
|
4702
6452
|
*/
|
|
4703
6453
|
applyLivePricing(models) {
|
|
4704
6454
|
return models.map((m) => {
|
|
4705
6455
|
const p = this.getLivePrice(m.baseModelId ?? m.id);
|
|
4706
6456
|
if (!p) return m;
|
|
4707
|
-
|
|
6457
|
+
const reconciled = reconcilePrice(m, p);
|
|
6458
|
+
if (reconciled.disagreement) this.recordDisagreement(reconciled.disagreement);
|
|
6459
|
+
if (!reconciled.accepted) return m;
|
|
6460
|
+
return {
|
|
6461
|
+
...m,
|
|
6462
|
+
inputCostPer1kTokens: reconciled.input,
|
|
6463
|
+
outputCostPer1kTokens: reconciled.output,
|
|
6464
|
+
pricingUnknown: false
|
|
6465
|
+
};
|
|
4708
6466
|
});
|
|
4709
6467
|
}
|
|
6468
|
+
recordDisagreement(d) {
|
|
6469
|
+
const key = `${d.provider}:${d.modelId}`;
|
|
6470
|
+
if (this.disagreements.has(key)) return;
|
|
6471
|
+
this.disagreements.set(key, d);
|
|
6472
|
+
}
|
|
6473
|
+
/**
|
|
6474
|
+
* Dataset-vs-live price mismatches seen this session, newest-model-first
|
|
6475
|
+
* insertion order. Surfaced by `cascade models --verbose`; a non-empty list
|
|
6476
|
+
* means src/core/router/pricing-data.json is probably due a refresh.
|
|
6477
|
+
*/
|
|
6478
|
+
getPriceDisagreements() {
|
|
6479
|
+
return [...this.disagreements.values()];
|
|
6480
|
+
}
|
|
4710
6481
|
/** Current capability facts for a model id, or null when unknown. */
|
|
4711
6482
|
getCapability(modelId) {
|
|
4712
6483
|
return this.capabilities.get(normalizeModelId2(modelId)) ?? null;
|
|
@@ -4903,7 +6674,9 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
|
|
|
4903
6674
|
tokensByTier: {},
|
|
4904
6675
|
inputTokensByTier: {},
|
|
4905
6676
|
outputTokensByTier: {},
|
|
4906
|
-
costByFeature: {}
|
|
6677
|
+
costByFeature: {},
|
|
6678
|
+
untrackedCostCalls: 0,
|
|
6679
|
+
untrackedCostModels: []
|
|
4907
6680
|
};
|
|
4908
6681
|
tierModels = /* @__PURE__ */ new Map();
|
|
4909
6682
|
/**
|
|
@@ -5253,7 +7026,7 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
|
|
|
5253
7026
|
`Model ${model.id} inference timed out after ${cloudTimeoutMs}ms`
|
|
5254
7027
|
);
|
|
5255
7028
|
}
|
|
5256
|
-
const
|
|
7029
|
+
const corrected = buildTokenUsage(
|
|
5257
7030
|
result.usage.inputTokens,
|
|
5258
7031
|
result.usage.outputTokens,
|
|
5259
7032
|
model
|
|
@@ -5262,7 +7035,8 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
|
|
|
5262
7035
|
...result,
|
|
5263
7036
|
usage: {
|
|
5264
7037
|
...result.usage,
|
|
5265
|
-
estimatedCostUsd:
|
|
7038
|
+
estimatedCostUsd: corrected.estimatedCostUsd,
|
|
7039
|
+
...corrected.costUnknown ? { costUnknown: true } : { costUnknown: void 0 }
|
|
5266
7040
|
}
|
|
5267
7041
|
};
|
|
5268
7042
|
if (!result || typeof result.content !== "string" || !result.usage) {
|
|
@@ -5449,7 +7223,9 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
|
|
|
5449
7223
|
tokensByTier: { ...this.stats.tokensByTier },
|
|
5450
7224
|
inputTokensByTier: { ...this.stats.inputTokensByTier },
|
|
5451
7225
|
outputTokensByTier: { ...this.stats.outputTokensByTier },
|
|
5452
|
-
costByFeature: { ...this.stats.costByFeature }
|
|
7226
|
+
costByFeature: { ...this.stats.costByFeature },
|
|
7227
|
+
untrackedCostCalls: this.stats.untrackedCostCalls,
|
|
7228
|
+
untrackedCostModels: [...this.stats.untrackedCostModels]
|
|
5453
7229
|
};
|
|
5454
7230
|
}
|
|
5455
7231
|
/**
|
|
@@ -5500,7 +7276,9 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
|
|
|
5500
7276
|
tokensByTier: {},
|
|
5501
7277
|
inputTokensByTier: {},
|
|
5502
7278
|
outputTokensByTier: {},
|
|
5503
|
-
costByFeature: {}
|
|
7279
|
+
costByFeature: {},
|
|
7280
|
+
untrackedCostCalls: 0,
|
|
7281
|
+
untrackedCostModels: []
|
|
5504
7282
|
};
|
|
5505
7283
|
this.sessionCostUsd = 0;
|
|
5506
7284
|
this.budgetState = "ok";
|
|
@@ -5685,6 +7463,18 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
|
|
|
5685
7463
|
if (featureTag) {
|
|
5686
7464
|
this.stats.costByFeature[featureTag] = (this.stats.costByFeature[featureTag] ?? 0) + usage.estimatedCostUsd;
|
|
5687
7465
|
}
|
|
7466
|
+
if (usage.costUnknown) {
|
|
7467
|
+
this.stats.untrackedCostCalls += 1;
|
|
7468
|
+
if (!this.stats.untrackedCostModels.includes(model.id)) {
|
|
7469
|
+
this.stats.untrackedCostModels.push(model.id);
|
|
7470
|
+
const capped = this.config?.budget?.maxCostPerRunUsd != null || this.config?.budget?.sessionBudgetUsd != null;
|
|
7471
|
+
this.emit("cost:untracked", {
|
|
7472
|
+
modelId: model.id,
|
|
7473
|
+
provider: model.provider,
|
|
7474
|
+
reason: capped ? `No published price for ${model.provider}:${model.id}. Its spend is not counted toward your cost budget \u2014 the token cap still applies. Add it to the pricing dataset, or set the provider's \`local: true\` if this endpoint really is free.` : `No published price for ${model.provider}:${model.id}. Cost for this model is reported as "not tracked" rather than $0.00.`
|
|
7475
|
+
});
|
|
7476
|
+
}
|
|
7477
|
+
}
|
|
5688
7478
|
this.runTokens += usage.totalTokens;
|
|
5689
7479
|
this.runCostUsd += usage.estimatedCostUsd;
|
|
5690
7480
|
this.updateBudgetState();
|
|
@@ -5825,6 +7615,14 @@ var BaseTier = class extends EventEmitter {
|
|
|
5825
7615
|
this.emit("tier:status", event);
|
|
5826
7616
|
}
|
|
5827
7617
|
/** Record the model serving this tier; future status events carry it. */
|
|
7618
|
+
/**
|
|
7619
|
+
* The model actually serving this tier ("provider:id"), once one has been
|
|
7620
|
+
* chosen. The run breaker reads it to attribute a failure to a model rather
|
|
7621
|
+
* than to the subtask that happened to hit it.
|
|
7622
|
+
*/
|
|
7623
|
+
getServingModel() {
|
|
7624
|
+
return this.servingModel;
|
|
7625
|
+
}
|
|
5828
7626
|
setServingModel(model) {
|
|
5829
7627
|
this.servingModel = model || void 0;
|
|
5830
7628
|
}
|
|
@@ -7561,6 +9359,7 @@ var T2Manager = class extends BaseTier {
|
|
|
7561
9359
|
escalations = [];
|
|
7562
9360
|
peerSyncBuffer = [];
|
|
7563
9361
|
store;
|
|
9362
|
+
runBreaker;
|
|
7564
9363
|
t3PeerBus = new PeerBus();
|
|
7565
9364
|
// ← T3↔T3 bus (local to this T2)
|
|
7566
9365
|
t2PeerBus;
|
|
@@ -7591,6 +9390,15 @@ var T2Manager = class extends BaseTier {
|
|
|
7591
9390
|
this.router = router;
|
|
7592
9391
|
this.toolRegistry = toolRegistry;
|
|
7593
9392
|
}
|
|
9393
|
+
/**
|
|
9394
|
+
* Share the run-wide circuit breaker. Every T2 in a run gets the SAME
|
|
9395
|
+
* instance: three sections each failing once against a dead model is the
|
|
9396
|
+
* same fact as one section failing three times, and a per-section breaker
|
|
9397
|
+
* would never see it.
|
|
9398
|
+
*/
|
|
9399
|
+
setRunBreaker(breaker) {
|
|
9400
|
+
this.runBreaker = breaker;
|
|
9401
|
+
}
|
|
7594
9402
|
setStore(store) {
|
|
7595
9403
|
this.store = store;
|
|
7596
9404
|
}
|
|
@@ -7714,7 +9522,8 @@ Guidance (must be followed): ${decision.note}`
|
|
|
7714
9522
|
const overallStatus = this.determineStatus(t3Results);
|
|
7715
9523
|
const isOk = overallStatus === "COMPLETED" || overallStatus === "PARTIAL";
|
|
7716
9524
|
this.setStatus(isOk ? "COMPLETED" : "FAILED", summary);
|
|
7717
|
-
|
|
9525
|
+
const sectionAction = overallStatus === "COMPLETED" ? "Section complete" : overallStatus === "PARTIAL" ? "Section partially complete \u2014 some subtasks failed" : overallStatus === "ESCALATED" ? "Section escalated \u2014 needs a decision" : "Section failed \u2014 no subtask completed";
|
|
9526
|
+
this.sendStatusUpdate({ progressPct: 100, currentAction: sectionAction, status: "IN_PROGRESS", output: summary });
|
|
7718
9527
|
const result = {
|
|
7719
9528
|
sectionId: assignment.sectionId,
|
|
7720
9529
|
sectionTitle: assignment.sectionTitle,
|
|
@@ -7884,6 +9693,24 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
|
7884
9693
|
const reinforceCfg = this.router.getReinforcementsConfig?.() ?? { enabled: false, maxPerSection: 4 };
|
|
7885
9694
|
let reinforcementsAdded = 0;
|
|
7886
9695
|
while (remaining.size > 0) {
|
|
9696
|
+
if (this.runBreaker?.isOpen()) {
|
|
9697
|
+
const why = this.runBreaker.skipMessage();
|
|
9698
|
+
this.log(`Run breaker open \u2014 skipping ${remaining.size} remaining subtask(s)`);
|
|
9699
|
+
for (const id of remaining) {
|
|
9700
|
+
this.t3PeerBus.publish(this.id, id, why, "FAILED");
|
|
9701
|
+
resultMap.set(id, {
|
|
9702
|
+
subtaskId: id,
|
|
9703
|
+
status: "FAILED",
|
|
9704
|
+
output: why,
|
|
9705
|
+
testResults: { checksRun: [], passed: [], failed: [] },
|
|
9706
|
+
issues: [why],
|
|
9707
|
+
peerSyncsUsed: [],
|
|
9708
|
+
correctionAttempts: 0
|
|
9709
|
+
});
|
|
9710
|
+
}
|
|
9711
|
+
remaining.clear();
|
|
9712
|
+
break;
|
|
9713
|
+
}
|
|
7887
9714
|
const runnableIds = [...remaining].filter((id) => (inDegree.get(id) ?? 0) === 0);
|
|
7888
9715
|
if (runnableIds.length === 0) {
|
|
7889
9716
|
const fallbackId = [...remaining].sort(
|
|
@@ -7986,8 +9813,32 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
|
7986
9813
|
remaining.delete(id);
|
|
7987
9814
|
const r = waveResults[i];
|
|
7988
9815
|
if (r.status === "rejected") {
|
|
7989
|
-
this.log(`T3 worker ${id} failed: ${r.reason instanceof Error ? r.reason.message : String(r.reason)} \u2014 retrying once`);
|
|
7990
9816
|
const assignment = sanitizedAssignments.find((a) => a.subtaskId === id);
|
|
9817
|
+
const failedModel = workerMap.get(id)?.getServingModel();
|
|
9818
|
+
const tripped = this.runBreaker?.record(r.reason, failedModel);
|
|
9819
|
+
if (tripped) {
|
|
9820
|
+
this.log(`Run breaker opened: ${tripped.failures} consecutive ${tripped.kind} failures on ${tripped.modelId} \u2014 stopping`);
|
|
9821
|
+
this.emit("run:breaker", tripped);
|
|
9822
|
+
}
|
|
9823
|
+
if (this.runBreaker?.isOpen()) {
|
|
9824
|
+
const why = this.runBreaker.skipMessage();
|
|
9825
|
+
this.log(`T3 worker ${id} failed and the run breaker is open \u2014 not retrying`);
|
|
9826
|
+
this.t3PeerBus.publish(this.id, id, why, "FAILED");
|
|
9827
|
+
resultMap.set(id, {
|
|
9828
|
+
subtaskId: id,
|
|
9829
|
+
status: "FAILED",
|
|
9830
|
+
output: why,
|
|
9831
|
+
testResults: { checksRun: [], passed: [], failed: [] },
|
|
9832
|
+
issues: [why],
|
|
9833
|
+
peerSyncsUsed: [],
|
|
9834
|
+
correctionAttempts: 0
|
|
9835
|
+
});
|
|
9836
|
+
for (const dependent of adj.get(id) ?? []) {
|
|
9837
|
+
inDegree.set(dependent, Math.max(0, (inDegree.get(dependent) ?? 0) - 1));
|
|
9838
|
+
}
|
|
9839
|
+
continue;
|
|
9840
|
+
}
|
|
9841
|
+
this.log(`T3 worker ${id} failed: ${r.reason instanceof Error ? r.reason.message : String(r.reason)} \u2014 retrying once`);
|
|
7991
9842
|
try {
|
|
7992
9843
|
const retried = await this.retryT3(assignment, taskId);
|
|
7993
9844
|
resultMap.set(id, retried);
|
|
@@ -8322,6 +10173,7 @@ var T1Administrator = class extends BaseTier {
|
|
|
8322
10173
|
t2Managers = /* @__PURE__ */ new Map();
|
|
8323
10174
|
escalations = [];
|
|
8324
10175
|
store;
|
|
10176
|
+
runBreaker;
|
|
8325
10177
|
t2PeerBus = new PeerBus();
|
|
8326
10178
|
permissionEscalator;
|
|
8327
10179
|
toolCreator;
|
|
@@ -8336,6 +10188,10 @@ var T1Administrator = class extends BaseTier {
|
|
|
8336
10188
|
this.toolRegistry = toolRegistry;
|
|
8337
10189
|
this.config = config;
|
|
8338
10190
|
}
|
|
10191
|
+
/** Share the run-wide circuit breaker with every section manager. */
|
|
10192
|
+
setRunBreaker(breaker) {
|
|
10193
|
+
this.runBreaker = breaker;
|
|
10194
|
+
}
|
|
8339
10195
|
setStore(store) {
|
|
8340
10196
|
this.store = store;
|
|
8341
10197
|
}
|
|
@@ -8698,6 +10554,7 @@ SPEC RULES \u2014 each subtask is a self-contained spec slice (workers execute f
|
|
|
8698
10554
|
};
|
|
8699
10555
|
const managers = sections.map((section) => {
|
|
8700
10556
|
const manager = new T2Manager(this.router, this.toolRegistry, this.id);
|
|
10557
|
+
if (this.runBreaker) manager.setRunBreaker(this.runBreaker);
|
|
8701
10558
|
manager.setHierarchyContext(`You are a T2 Manager for the section "${section.sectionTitle}". You are part of a COMPLEX task overseen by T1 Administrator.`);
|
|
8702
10559
|
if (this.store) {
|
|
8703
10560
|
manager.setStore(this.store);
|
|
@@ -8986,6 +10843,158 @@ Reply with exactly one word: YES, NO, or UNSURE.
|
|
|
8986
10843
|
// src/core/cascade.ts
|
|
8987
10844
|
init_cost();
|
|
8988
10845
|
|
|
10846
|
+
// src/core/router/provider-errors.ts
|
|
10847
|
+
var SYSTEMIC = /* @__PURE__ */ new Set([
|
|
10848
|
+
"rate_limit",
|
|
10849
|
+
"quota_exhausted",
|
|
10850
|
+
"auth",
|
|
10851
|
+
"model_unavailable"
|
|
10852
|
+
]);
|
|
10853
|
+
function isSystemicKind(kind) {
|
|
10854
|
+
return SYSTEMIC.has(kind);
|
|
10855
|
+
}
|
|
10856
|
+
function statusOf(err) {
|
|
10857
|
+
if (!err || typeof err !== "object") return void 0;
|
|
10858
|
+
const e = err;
|
|
10859
|
+
for (const key of ["status", "statusCode", "code"]) {
|
|
10860
|
+
const v = e[key];
|
|
10861
|
+
if (typeof v === "number" && v >= 100 && v < 600) return v;
|
|
10862
|
+
}
|
|
10863
|
+
const resp = e["response"];
|
|
10864
|
+
if (resp && typeof resp["status"] === "number") return resp["status"];
|
|
10865
|
+
const inner = e["error"];
|
|
10866
|
+
if (inner && typeof inner["code"] === "number") return inner["code"];
|
|
10867
|
+
return void 0;
|
|
10868
|
+
}
|
|
10869
|
+
function messageOf(err) {
|
|
10870
|
+
if (err instanceof Error) return err.message;
|
|
10871
|
+
if (typeof err === "string") return err;
|
|
10872
|
+
if (err && typeof err === "object") {
|
|
10873
|
+
const e = err;
|
|
10874
|
+
if (typeof e["message"] === "string") return e["message"];
|
|
10875
|
+
const inner = e["error"];
|
|
10876
|
+
if (inner && typeof inner["message"] === "string") return inner["message"];
|
|
10877
|
+
try {
|
|
10878
|
+
return JSON.stringify(err);
|
|
10879
|
+
} catch {
|
|
10880
|
+
}
|
|
10881
|
+
}
|
|
10882
|
+
return String(err);
|
|
10883
|
+
}
|
|
10884
|
+
function classifyProviderError(err) {
|
|
10885
|
+
const raw = messageOf(err).trim();
|
|
10886
|
+
const status = statusOf(err);
|
|
10887
|
+
const m = raw.toLowerCase();
|
|
10888
|
+
const kind = (() => {
|
|
10889
|
+
if (status === 429) {
|
|
10890
|
+
return /quota|billing|credit|exceeded your current quota|insufficient/.test(m) ? "quota_exhausted" : "rate_limit";
|
|
10891
|
+
}
|
|
10892
|
+
if (status === 401 || status === 403) return "auth";
|
|
10893
|
+
if (status === 404) return "model_unavailable";
|
|
10894
|
+
if (/rate limit|too many requests|resource_exhausted/.test(m)) {
|
|
10895
|
+
return /quota|billing|credit|insufficient/.test(m) ? "quota_exhausted" : "rate_limit";
|
|
10896
|
+
}
|
|
10897
|
+
if (/quota|billing|insufficient_quota|insufficient credit|payment required/.test(m)) return "quota_exhausted";
|
|
10898
|
+
if (/api key|unauthorized|authentication|permission denied|invalid[_ ]api/.test(m)) return "auth";
|
|
10899
|
+
if (/model .*(not found|does not exist|unavailable)|no such model|unknown model|not supported for/.test(m)) {
|
|
10900
|
+
return "model_unavailable";
|
|
10901
|
+
}
|
|
10902
|
+
if (/context length|too many tokens|maximum context|prompt is too long|input too long/.test(m)) return "context_length";
|
|
10903
|
+
if (/safety|content filter|blocked|recitation|prohibited/.test(m)) return "content_filter";
|
|
10904
|
+
if (/econnreset|etimedout|enotfound|socket hang up|network|fetch failed|aborted/.test(m)) return "network";
|
|
10905
|
+
return "unknown";
|
|
10906
|
+
})();
|
|
10907
|
+
return { kind, systemic: isSystemicKind(kind), status, raw };
|
|
10908
|
+
}
|
|
10909
|
+
function describeProviderError(c, modelId) {
|
|
10910
|
+
const on = ` on ${modelId}` ;
|
|
10911
|
+
const detail = c.raw ? ` Provider said: ${c.raw}` : "";
|
|
10912
|
+
switch (c.kind) {
|
|
10913
|
+
case "rate_limit":
|
|
10914
|
+
return `Rate limit hit${on}. The provider is throttling requests \u2014 lower the number of parallel workers, or use a model with a higher requests-per-minute allowance.${detail}`;
|
|
10915
|
+
case "quota_exhausted":
|
|
10916
|
+
return `Quota or billing limit reached${on}. This will not recover on its own \u2014 check the provider's billing page, or switch this tier to a different provider.${detail}`;
|
|
10917
|
+
case "auth":
|
|
10918
|
+
return `Authentication failed${on}. The API key is missing, expired, or lacks access to this model \u2014 re-check the key in Settings > Providers.${detail}`;
|
|
10919
|
+
case "model_unavailable":
|
|
10920
|
+
return `Model unavailable${on}. The id may be wrong, retired, or not enabled for this API key \u2014 pick a different model for this tier.${detail}`;
|
|
10921
|
+
case "context_length":
|
|
10922
|
+
return `The prompt was too long${on}. Reduce the attached context, or use a model with a larger context window for this tier.${detail}`;
|
|
10923
|
+
case "content_filter":
|
|
10924
|
+
return `The provider's safety filter refused this request${on}.${detail}`;
|
|
10925
|
+
case "network":
|
|
10926
|
+
return `Network error reaching the provider${on}. This is usually transient.${detail}`;
|
|
10927
|
+
default:
|
|
10928
|
+
return `The model call failed${on}.${detail}`;
|
|
10929
|
+
}
|
|
10930
|
+
}
|
|
10931
|
+
|
|
10932
|
+
// src/core/run-breaker.ts
|
|
10933
|
+
var DEFAULT_FAILURE_THRESHOLD = 3;
|
|
10934
|
+
var RunBreaker = class {
|
|
10935
|
+
threshold;
|
|
10936
|
+
/** modelId → consecutive systemic failures of the SAME kind. */
|
|
10937
|
+
counts = /* @__PURE__ */ new Map();
|
|
10938
|
+
trip = null;
|
|
10939
|
+
constructor(threshold = DEFAULT_FAILURE_THRESHOLD) {
|
|
10940
|
+
this.threshold = Math.max(1, threshold);
|
|
10941
|
+
}
|
|
10942
|
+
/**
|
|
10943
|
+
* Record a failed model call. Returns the trip if this failure is the one
|
|
10944
|
+
* that opened the breaker, otherwise null.
|
|
10945
|
+
*
|
|
10946
|
+
* A non-systemic failure RESETS the model's counter: it is evidence the model
|
|
10947
|
+
* is reachable and answering, which is exactly what a systemic streak claims
|
|
10948
|
+
* it is not.
|
|
10949
|
+
*/
|
|
10950
|
+
record(err, modelId) {
|
|
10951
|
+
if (this.trip) return null;
|
|
10952
|
+
const id = modelId?.trim() || "unknown-model";
|
|
10953
|
+
const c = classifyProviderError(err);
|
|
10954
|
+
if (!c.systemic) {
|
|
10955
|
+
this.counts.delete(id);
|
|
10956
|
+
return null;
|
|
10957
|
+
}
|
|
10958
|
+
const prev = this.counts.get(id);
|
|
10959
|
+
const n = prev && prev.kind === c.kind ? prev.n + 1 : 1;
|
|
10960
|
+
this.counts.set(id, { kind: c.kind, n, last: c });
|
|
10961
|
+
if (n >= this.threshold) {
|
|
10962
|
+
this.trip = {
|
|
10963
|
+
modelId: id,
|
|
10964
|
+
kind: c.kind,
|
|
10965
|
+
failures: n,
|
|
10966
|
+
message: describeProviderError(c, id)
|
|
10967
|
+
};
|
|
10968
|
+
return this.trip;
|
|
10969
|
+
}
|
|
10970
|
+
return null;
|
|
10971
|
+
}
|
|
10972
|
+
/** True once the run should stop launching work that would fail identically. */
|
|
10973
|
+
isOpen() {
|
|
10974
|
+
return this.trip !== null;
|
|
10975
|
+
}
|
|
10976
|
+
/** Why the breaker opened, or null while it is closed. */
|
|
10977
|
+
reason() {
|
|
10978
|
+
return this.trip;
|
|
10979
|
+
}
|
|
10980
|
+
/**
|
|
10981
|
+
* One line for a worker/section that never ran because the breaker was
|
|
10982
|
+
* already open. Says the work was skipped deliberately — not that it failed
|
|
10983
|
+
* on its own merits, which is what an empty FAILED status would imply.
|
|
10984
|
+
*/
|
|
10985
|
+
skipMessage() {
|
|
10986
|
+
const t = this.trip;
|
|
10987
|
+
if (!t) return "Skipped.";
|
|
10988
|
+
return `Skipped \u2014 the run stopped after ${t.failures} consecutive failures on ${t.modelId}. ${t.message}`;
|
|
10989
|
+
}
|
|
10990
|
+
/** Test/diagnostic view of the per-model streaks. */
|
|
10991
|
+
streaks() {
|
|
10992
|
+
const out = {};
|
|
10993
|
+
for (const [k, v] of this.counts) out[k] = { kind: v.kind, n: v.n };
|
|
10994
|
+
return out;
|
|
10995
|
+
}
|
|
10996
|
+
};
|
|
10997
|
+
|
|
8989
10998
|
// src/tools/registry.ts
|
|
8990
10999
|
init_constants();
|
|
8991
11000
|
|
|
@@ -11215,6 +13224,7 @@ var noopTelemetry = {
|
|
|
11215
13224
|
};
|
|
11216
13225
|
|
|
11217
13226
|
// src/core/router/task-analyzer.ts
|
|
13227
|
+
init_pricing();
|
|
11218
13228
|
var CODE_SIGNALS = [
|
|
11219
13229
|
/\b(?:function|class|interface|async|await|import|export|const|let|var|def|return|if|else|for|while|try|catch)\b/,
|
|
11220
13230
|
/\b(?:typescript|javascript|python|rust|go|java|c\+\+|sql|bash|shell|dockerfile|kubernetes|terraform)\b/i,
|
|
@@ -11391,8 +13401,8 @@ var TaskAnalyzer = class {
|
|
|
11391
13401
|
}
|
|
11392
13402
|
costEfficiency(model, complexity) {
|
|
11393
13403
|
if (this.tracker) return this.tracker.costEfficiencyScore(model, complexity);
|
|
11394
|
-
const blended = model
|
|
11395
|
-
const normalised = Math.min(1, blended /
|
|
13404
|
+
const blended = blendedCostPer1k(model);
|
|
13405
|
+
const normalised = Math.min(1, blended / BLENDED_COST_CEILING);
|
|
11396
13406
|
const complexityWeight = (6 - complexity) / 5;
|
|
11397
13407
|
return Math.max(0.1, 1 - normalised * complexityWeight);
|
|
11398
13408
|
}
|
|
@@ -11407,6 +13417,9 @@ var TaskAnalyzer = class {
|
|
|
11407
13417
|
analysisCache.clear();
|
|
11408
13418
|
}
|
|
11409
13419
|
};
|
|
13420
|
+
|
|
13421
|
+
// src/core/router/model-performance-tracker.ts
|
|
13422
|
+
init_pricing();
|
|
11410
13423
|
var DEFAULT_STATS_FILE = path32.join(os10.homedir(), ".cascade", "model-perf.json");
|
|
11411
13424
|
var ModelPerformanceTracker = class {
|
|
11412
13425
|
stats = /* @__PURE__ */ new Map();
|
|
@@ -11527,8 +13540,8 @@ var ModelPerformanceTracker = class {
|
|
|
11527
13540
|
* normalised over $0.05 blended as the "expensive" ceiling.
|
|
11528
13541
|
*/
|
|
11529
13542
|
costEfficiencyScore(model, complexity) {
|
|
11530
|
-
const blended = model
|
|
11531
|
-
const normalised = Math.min(1, blended /
|
|
13543
|
+
const blended = blendedCostPer1k(model);
|
|
13544
|
+
const normalised = Math.min(1, blended / BLENDED_COST_CEILING);
|
|
11532
13545
|
const complexityWeight = (6 - complexity) / 5;
|
|
11533
13546
|
return Math.max(0.1, 1 - normalised * complexityWeight);
|
|
11534
13547
|
}
|
|
@@ -14114,6 +16127,7 @@ ${prompt}` : prompt;
|
|
|
14114
16127
|
if (toolCreator) toolCreator.setPermissionEscalator(escalator);
|
|
14115
16128
|
const rootPrompt = buildContextualPrompt(options.prompt, options.conversationHistory);
|
|
14116
16129
|
let finalOutput = "";
|
|
16130
|
+
const runBreaker = new RunBreaker();
|
|
14117
16131
|
let t2Results = [];
|
|
14118
16132
|
let runError = null;
|
|
14119
16133
|
let identityPrompt = "";
|
|
@@ -14182,6 +16196,7 @@ ${prompt}` : prompt;
|
|
|
14182
16196
|
this.emit("tier:status", { tierId: "t3-root", status: "COMPLETED", role: "T3" });
|
|
14183
16197
|
} else if (complexity === "Moderate") {
|
|
14184
16198
|
const t2 = new T2Manager(this.router, this.toolRegistry, "root");
|
|
16199
|
+
t2.setRunBreaker(runBreaker);
|
|
14185
16200
|
t2.setPresenter(true);
|
|
14186
16201
|
t2.setHierarchyContext("You are the ROOT Manager for this task. There is no T1 Administrator involved in this run. You are responsible for decomposing the task and managing T3 workers directly.");
|
|
14187
16202
|
if (identityPrompt) {
|
|
@@ -14242,6 +16257,7 @@ _(incomplete: ${reasons.join("; ")})_` : partial;
|
|
|
14242
16257
|
}
|
|
14243
16258
|
} else {
|
|
14244
16259
|
const t1 = new T1Administrator(this.router, this.toolRegistry, this.config);
|
|
16260
|
+
t1.setRunBreaker(runBreaker);
|
|
14245
16261
|
t1.setPresenter(true);
|
|
14246
16262
|
t1.setHierarchyContext("You are the top-level Administrator. You are responsible for the overall plan and supervising multiple T2 Managers.");
|
|
14247
16263
|
if (identityPrompt) {
|
|
@@ -14266,6 +16282,20 @@ _(incomplete: ${reasons.join("; ")})_` : partial;
|
|
|
14266
16282
|
finalOutput = result.output;
|
|
14267
16283
|
t2Results = result.t2Results;
|
|
14268
16284
|
}
|
|
16285
|
+
const trip = runBreaker.reason();
|
|
16286
|
+
if (trip) {
|
|
16287
|
+
finalOutput = [
|
|
16288
|
+
`\u26A0 **Run stopped early \u2014 ${trip.modelId} was failing every request.**`,
|
|
16289
|
+
"",
|
|
16290
|
+
trip.message,
|
|
16291
|
+
"",
|
|
16292
|
+
`Cascade stopped after ${trip.failures} consecutive failures rather than retrying every remaining subtask against the same model, which would have cost more and produced the same result.`,
|
|
16293
|
+
finalOutput ? `
|
|
16294
|
+
---
|
|
16295
|
+
|
|
16296
|
+
${finalOutput}` : ""
|
|
16297
|
+
].join("\n").trimEnd();
|
|
16298
|
+
}
|
|
14269
16299
|
} catch (err) {
|
|
14270
16300
|
if (err instanceof CascadeCancelledError || err instanceof Error && err.name === "AbortError" || options.signal?.aborted) {
|
|
14271
16301
|
this.emit("run:cancelled", {
|
|
@@ -14332,7 +16362,11 @@ _(incomplete: ${reasons.join("; ")})_` : partial;
|
|
|
14332
16362
|
inputTokens: 0,
|
|
14333
16363
|
outputTokens: 0,
|
|
14334
16364
|
totalTokens: stats.totalTokens,
|
|
14335
|
-
estimatedCostUsd: stats.totalCostUsd
|
|
16365
|
+
estimatedCostUsd: stats.totalCostUsd,
|
|
16366
|
+
// At least one call ran on a model with no known price, so its spend is
|
|
16367
|
+
// missing from the total above. Say so rather than letting the caller
|
|
16368
|
+
// present an undercount as the final figure.
|
|
16369
|
+
...stats.untrackedCostCalls > 0 ? { costUnknown: true } : {}
|
|
14336
16370
|
},
|
|
14337
16371
|
t2Results,
|
|
14338
16372
|
durationMs,
|
|
@@ -14402,7 +16436,8 @@ _(incomplete: ${reasons.join("; ")})_` : partial;
|
|
|
14402
16436
|
inputTokens: 0,
|
|
14403
16437
|
outputTokens: 0,
|
|
14404
16438
|
totalTokens: stats2.totalTokens,
|
|
14405
|
-
estimatedCostUsd: stats2.totalCostUsd
|
|
16439
|
+
estimatedCostUsd: stats2.totalCostUsd,
|
|
16440
|
+
...stats2.untrackedCostCalls > 0 ? { costUnknown: true } : {}
|
|
14406
16441
|
},
|
|
14407
16442
|
t2Results: [],
|
|
14408
16443
|
durationMs: Date.now() - startMs,
|
|
@@ -14426,7 +16461,8 @@ _(incomplete: ${reasons.join("; ")})_` : partial;
|
|
|
14426
16461
|
inputTokens: result.usage?.inputTokens ?? 0,
|
|
14427
16462
|
outputTokens: result.usage?.outputTokens ?? 0,
|
|
14428
16463
|
totalTokens: stats.totalTokens,
|
|
14429
|
-
estimatedCostUsd: stats.totalCostUsd
|
|
16464
|
+
estimatedCostUsd: stats.totalCostUsd,
|
|
16465
|
+
...stats.untrackedCostCalls > 0 ? { costUnknown: true } : {}
|
|
14430
16466
|
},
|
|
14431
16467
|
t2Results: [],
|
|
14432
16468
|
durationMs: Date.now() - startMs,
|
|
@@ -19643,7 +21679,10 @@ var DashboardServer = class {
|
|
|
19643
21679
|
this.socket.broadcast("cost:update", {
|
|
19644
21680
|
sessionId,
|
|
19645
21681
|
totalTokens: result.usage.totalTokens,
|
|
19646
|
-
totalCostUsd: result.usage.estimatedCostUsd
|
|
21682
|
+
totalCostUsd: result.usage.estimatedCostUsd,
|
|
21683
|
+
// True when a model with no known price was used: the total above is
|
|
21684
|
+
// an undercount, and the UI must not present it as the full spend.
|
|
21685
|
+
costUnknown: result.usage.costUnknown === true
|
|
19647
21686
|
});
|
|
19648
21687
|
this.throttledBroadcast("workspace");
|
|
19649
21688
|
} catch (err) {
|
|
@@ -19956,7 +21995,8 @@ var DashboardServer = class {
|
|
|
19956
21995
|
this.socket.broadcast("cost:update", {
|
|
19957
21996
|
sessionId,
|
|
19958
21997
|
totalTokens: result.usage.totalTokens,
|
|
19959
|
-
totalCostUsd: result.usage.estimatedCostUsd
|
|
21998
|
+
totalCostUsd: result.usage.estimatedCostUsd,
|
|
21999
|
+
costUnknown: result.usage.costUnknown === true
|
|
19960
22000
|
});
|
|
19961
22001
|
this.throttledBroadcast("workspace");
|
|
19962
22002
|
} catch (err) {
|
|
@@ -20498,7 +22538,8 @@ ${prompt}`;
|
|
|
20498
22538
|
this.socket.broadcast("cost:update", {
|
|
20499
22539
|
sessionId,
|
|
20500
22540
|
totalTokens: result.usage.totalTokens,
|
|
20501
|
-
totalCostUsd: result.usage.estimatedCostUsd
|
|
22541
|
+
totalCostUsd: result.usage.estimatedCostUsd,
|
|
22542
|
+
costUnknown: result.usage.costUnknown === true
|
|
20502
22543
|
});
|
|
20503
22544
|
this.socket.broadcastToRoom(`session:${sessionId}`, "session:complete", { sessionId, result });
|
|
20504
22545
|
this.throttledBroadcast("workspace");
|
|
@@ -20894,6 +22935,7 @@ function makeIdentityCommand(workspacePath = process.cwd()) {
|
|
|
20894
22935
|
});
|
|
20895
22936
|
return identity;
|
|
20896
22937
|
}
|
|
22938
|
+
init_pricing();
|
|
20897
22939
|
async function modelsCommand(options = {}) {
|
|
20898
22940
|
console.log(chalk7.magenta("\n \u25C8 Cascade Models\n"));
|
|
20899
22941
|
const cm = new ConfigManager(process.cwd());
|
|
@@ -20915,17 +22957,20 @@ async function modelsCommand(options = {}) {
|
|
|
20915
22957
|
{ tier: "T3", label: "T3 Worker", color: chalk7.hex("#2DD4BF") }
|
|
20916
22958
|
];
|
|
20917
22959
|
let anyMissing = false;
|
|
22960
|
+
let anyUnpriced = false;
|
|
20918
22961
|
for (const { tier, label, color } of tiers) {
|
|
20919
22962
|
const model = router.getTierModel(tier);
|
|
20920
22963
|
if (model) {
|
|
20921
|
-
const
|
|
20922
|
-
|
|
22964
|
+
const unpriced = isPricingUnknown(model);
|
|
22965
|
+
if (unpriced) anyUnpriced = true;
|
|
22966
|
+
const costIn = unpriced ? "cost not tracked" : model.inputCostPer1kTokens === 0 ? "free" : `$${model.inputCostPer1kTokens.toFixed(4)}/1K in`;
|
|
22967
|
+
const costOut = unpriced ? "" : model.outputCostPer1kTokens === 0 ? "free" : `, $${model.outputCostPer1kTokens.toFixed(4)}/1K out`;
|
|
20923
22968
|
const ctx = model.contextWindow >= 1e6 ? `${(model.contextWindow / 1e6).toFixed(1)}M ctx` : `${(model.contextWindow / 1e3).toFixed(0)}K ctx`;
|
|
20924
22969
|
const local = model.isLocal ? chalk7.gray(" [local]") : "";
|
|
20925
22970
|
const vision = model.isVisionCapable ? chalk7.gray(" \u{1F441}") : "";
|
|
20926
22971
|
const bench = Math.round(benchmarkScore01(model, "mixed") * 100);
|
|
20927
22972
|
console.log(
|
|
20928
|
-
` ${color.bold(tier)} ${chalk7.white(col(model.name, 24))}${chalk7.gray(col(model.provider, 16))}` + (options.verbose ? `${chalk7.gray(col(ctx, 12))}${chalk7.gray(col(`bench ${bench}/100`, 14))}${chalk7.gray(`${costIn}
|
|
22973
|
+
` ${color.bold(tier)} ${chalk7.white(col(model.name, 24))}${chalk7.gray(col(model.provider, 16))}` + (options.verbose ? `${chalk7.gray(col(ctx, 12))}${chalk7.gray(col(`bench ${bench}/100`, 14))}${chalk7.gray(`${costIn}${costOut}`)}` : `${chalk7.gray(col(ctx, 10))}${chalk7.gray(`bench ${bench}/100`)}`) + local + vision
|
|
20929
22974
|
);
|
|
20930
22975
|
} else {
|
|
20931
22976
|
console.log(` ${color.bold(tier)} ${chalk7.red("No model available")} ${chalk7.gray(`(check provider config for ${label})`)}`);
|
|
@@ -20939,8 +22984,25 @@ async function modelsCommand(options = {}) {
|
|
|
20939
22984
|
const src = liveData.getDataSource();
|
|
20940
22985
|
const gen = liveData.getGeneratedAt();
|
|
20941
22986
|
const srcLabel = src === "live" ? "live (just fetched)" : src === "cache" ? "cached" : "bundled";
|
|
22987
|
+
const pricingMeta = pricingDatasetMeta();
|
|
20942
22988
|
console.log(chalk7.gray(
|
|
20943
|
-
` Benchmark data: ${srcLabel}` + (gen ? ` \xB7 updated ${gen.slice(0, 10)}` : "") + ` \xB7 pricing: ${liveData.hasLivePricing() ? "live (OpenRouter)" : "
|
|
22989
|
+
` Benchmark data: ${srcLabel}` + (gen ? ` \xB7 updated ${gen.slice(0, 10)}` : "") + ` \xB7 pricing: ${liveData.hasLivePricing() ? "live (OpenRouter)" : "dataset"} (baseline ${pricingMeta.generatedAt})`
|
|
22990
|
+
));
|
|
22991
|
+
const drift = liveData.getPriceDisagreements();
|
|
22992
|
+
if (drift.length) {
|
|
22993
|
+
console.log(chalk7.yellow(`
|
|
22994
|
+
${drift.length} model${drift.length === 1 ? "" : "s"} priced differently by the live source than by the bundled dataset:`));
|
|
22995
|
+
for (const d of drift.slice(0, 10)) {
|
|
22996
|
+
console.log(chalk7.gray(
|
|
22997
|
+
` ${d.provider}:${d.modelId} dataset $${d.dataset.input.toFixed(5)}/$${d.dataset.output.toFixed(5)} per 1K (as of ${d.datasetAsOf}) \u2192 live $${d.live.input.toFixed(5)}/$${d.live.output.toFixed(5)} (${d.ratio}\xD7)`
|
|
22998
|
+
));
|
|
22999
|
+
}
|
|
23000
|
+
console.log(chalk7.gray(" Live pricing is used. Refresh src/core/router/pricing-data.json to clear this."));
|
|
23001
|
+
}
|
|
23002
|
+
}
|
|
23003
|
+
if (anyUnpriced) {
|
|
23004
|
+
console.log(chalk7.yellow(
|
|
23005
|
+
'\n Some tiers use a model with no published price \u2014 their spend is reported as "cost not tracked", never $0.00,\n and is not counted toward a cost budget. Add the model to the pricing dataset, or set `local: true` on the\n provider if the endpoint genuinely costs nothing to run.'
|
|
20944
23006
|
));
|
|
20945
23007
|
}
|
|
20946
23008
|
if (options.verbose) {
|