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/index.js
CHANGED
|
@@ -192,8 +192,1660 @@ var init_audit_logger = __esm({
|
|
|
192
192
|
}
|
|
193
193
|
});
|
|
194
194
|
|
|
195
|
+
// src/core/router/pricing-data.json
|
|
196
|
+
var pricing_data_default = {
|
|
197
|
+
entries: [
|
|
198
|
+
{
|
|
199
|
+
model: "claude-opus-5",
|
|
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: 5,
|
|
209
|
+
outputPer1m: 25,
|
|
210
|
+
cachedInputPer1m: 0.5,
|
|
211
|
+
cacheWritePer1m: 6.25,
|
|
212
|
+
reasoningOutputPer1m: 25
|
|
213
|
+
}
|
|
214
|
+
],
|
|
215
|
+
batchMultiplier: 0.5,
|
|
216
|
+
contextWindow: 1e6
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
model: "claude-opus-4-8",
|
|
220
|
+
provider: "anthropic",
|
|
221
|
+
modality: "text",
|
|
222
|
+
currency: "USD",
|
|
223
|
+
asOf: "2026-07-27",
|
|
224
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
225
|
+
provenance: "vendor-pricing-page",
|
|
226
|
+
tiers: [
|
|
227
|
+
{
|
|
228
|
+
inputPer1m: 5,
|
|
229
|
+
outputPer1m: 25,
|
|
230
|
+
cachedInputPer1m: 0.5,
|
|
231
|
+
cacheWritePer1m: 6.25,
|
|
232
|
+
reasoningOutputPer1m: 25
|
|
233
|
+
}
|
|
234
|
+
],
|
|
235
|
+
batchMultiplier: 0.5,
|
|
236
|
+
contextWindow: 1e6
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
model: "claude-opus-4-7",
|
|
240
|
+
provider: "anthropic",
|
|
241
|
+
modality: "text",
|
|
242
|
+
currency: "USD",
|
|
243
|
+
asOf: "2026-07-27",
|
|
244
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
245
|
+
provenance: "vendor-pricing-page",
|
|
246
|
+
tiers: [
|
|
247
|
+
{
|
|
248
|
+
inputPer1m: 5,
|
|
249
|
+
outputPer1m: 25,
|
|
250
|
+
cachedInputPer1m: 0.5,
|
|
251
|
+
cacheWritePer1m: 6.25,
|
|
252
|
+
reasoningOutputPer1m: 25
|
|
253
|
+
}
|
|
254
|
+
],
|
|
255
|
+
batchMultiplier: 0.5,
|
|
256
|
+
contextWindow: 1e6
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
model: "claude-opus-4-6",
|
|
260
|
+
provider: "anthropic",
|
|
261
|
+
modality: "text",
|
|
262
|
+
currency: "USD",
|
|
263
|
+
asOf: "2026-07-27",
|
|
264
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
265
|
+
provenance: "vendor-pricing-page",
|
|
266
|
+
tiers: [
|
|
267
|
+
{
|
|
268
|
+
inputPer1m: 5,
|
|
269
|
+
outputPer1m: 25,
|
|
270
|
+
cachedInputPer1m: 0.5,
|
|
271
|
+
cacheWritePer1m: 6.25,
|
|
272
|
+
reasoningOutputPer1m: 25
|
|
273
|
+
}
|
|
274
|
+
],
|
|
275
|
+
batchMultiplier: 0.5,
|
|
276
|
+
contextWindow: 1e6
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
model: "claude-opus-4-5",
|
|
280
|
+
provider: "anthropic",
|
|
281
|
+
modality: "text",
|
|
282
|
+
currency: "USD",
|
|
283
|
+
asOf: "2026-07-27",
|
|
284
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
285
|
+
provenance: "vendor-pricing-page",
|
|
286
|
+
tiers: [
|
|
287
|
+
{
|
|
288
|
+
inputPer1m: 5,
|
|
289
|
+
outputPer1m: 25,
|
|
290
|
+
cachedInputPer1m: 0.5,
|
|
291
|
+
cacheWritePer1m: 6.25,
|
|
292
|
+
reasoningOutputPer1m: 25
|
|
293
|
+
}
|
|
294
|
+
],
|
|
295
|
+
batchMultiplier: 0.5,
|
|
296
|
+
contextWindow: 2e5
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
model: "claude-opus-4-1",
|
|
300
|
+
provider: "anthropic",
|
|
301
|
+
modality: "text",
|
|
302
|
+
currency: "USD",
|
|
303
|
+
asOf: "2026-07-27",
|
|
304
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
305
|
+
provenance: "vendor-pricing-page",
|
|
306
|
+
tiers: [
|
|
307
|
+
{
|
|
308
|
+
inputPer1m: 15,
|
|
309
|
+
outputPer1m: 75,
|
|
310
|
+
cachedInputPer1m: 1.5,
|
|
311
|
+
cacheWritePer1m: 18.75,
|
|
312
|
+
reasoningOutputPer1m: 75
|
|
313
|
+
}
|
|
314
|
+
],
|
|
315
|
+
batchMultiplier: 0.5,
|
|
316
|
+
contextWindow: 2e5,
|
|
317
|
+
notes: "Deprecated by Anthropic; priced for historical/cost-reporting accuracy."
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
model: "claude-opus-4",
|
|
321
|
+
provider: "anthropic",
|
|
322
|
+
modality: "text",
|
|
323
|
+
currency: "USD",
|
|
324
|
+
asOf: "2026-07-27",
|
|
325
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
326
|
+
provenance: "vendor-pricing-page",
|
|
327
|
+
tiers: [
|
|
328
|
+
{
|
|
329
|
+
inputPer1m: 15,
|
|
330
|
+
outputPer1m: 75,
|
|
331
|
+
cachedInputPer1m: 1.5,
|
|
332
|
+
cacheWritePer1m: 18.75,
|
|
333
|
+
reasoningOutputPer1m: 75
|
|
334
|
+
}
|
|
335
|
+
],
|
|
336
|
+
batchMultiplier: 0.5,
|
|
337
|
+
contextWindow: 2e5,
|
|
338
|
+
notes: "Retired except on Google Cloud; priced for historical/cost-reporting accuracy."
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
model: "claude-sonnet-5",
|
|
342
|
+
provider: "anthropic",
|
|
343
|
+
modality: "text",
|
|
344
|
+
currency: "USD",
|
|
345
|
+
asOf: "2026-07-27",
|
|
346
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
347
|
+
provenance: "vendor-pricing-page",
|
|
348
|
+
tiers: [
|
|
349
|
+
{
|
|
350
|
+
inputPer1m: 2,
|
|
351
|
+
outputPer1m: 10,
|
|
352
|
+
cachedInputPer1m: 0.2,
|
|
353
|
+
cacheWritePer1m: 2.5,
|
|
354
|
+
reasoningOutputPer1m: 10
|
|
355
|
+
}
|
|
356
|
+
],
|
|
357
|
+
batchMultiplier: 0.5,
|
|
358
|
+
contextWindow: 1e6,
|
|
359
|
+
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."
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
model: "claude-sonnet-4-6",
|
|
363
|
+
provider: "anthropic",
|
|
364
|
+
modality: "text",
|
|
365
|
+
currency: "USD",
|
|
366
|
+
asOf: "2026-07-27",
|
|
367
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
368
|
+
provenance: "vendor-pricing-page",
|
|
369
|
+
tiers: [
|
|
370
|
+
{
|
|
371
|
+
inputPer1m: 3,
|
|
372
|
+
outputPer1m: 15,
|
|
373
|
+
cachedInputPer1m: 0.3,
|
|
374
|
+
cacheWritePer1m: 3.75,
|
|
375
|
+
reasoningOutputPer1m: 15
|
|
376
|
+
}
|
|
377
|
+
],
|
|
378
|
+
batchMultiplier: 0.5,
|
|
379
|
+
contextWindow: 1e6
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
model: "claude-sonnet-4-5",
|
|
383
|
+
provider: "anthropic",
|
|
384
|
+
modality: "text",
|
|
385
|
+
currency: "USD",
|
|
386
|
+
asOf: "2026-07-27",
|
|
387
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
388
|
+
provenance: "vendor-pricing-page",
|
|
389
|
+
tiers: [
|
|
390
|
+
{
|
|
391
|
+
maxInputTokens: 2e5,
|
|
392
|
+
inputPer1m: 3,
|
|
393
|
+
outputPer1m: 15,
|
|
394
|
+
cachedInputPer1m: 0.3,
|
|
395
|
+
cacheWritePer1m: 3.75,
|
|
396
|
+
reasoningOutputPer1m: 15
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
inputPer1m: 6,
|
|
400
|
+
outputPer1m: 22.5,
|
|
401
|
+
cachedInputPer1m: 0.6,
|
|
402
|
+
cacheWritePer1m: 7.5,
|
|
403
|
+
reasoningOutputPer1m: 22.5
|
|
404
|
+
}
|
|
405
|
+
],
|
|
406
|
+
batchMultiplier: 0.5,
|
|
407
|
+
contextWindow: 2e5,
|
|
408
|
+
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)."
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
model: "claude-sonnet-4",
|
|
412
|
+
provider: "anthropic",
|
|
413
|
+
modality: "text",
|
|
414
|
+
currency: "USD",
|
|
415
|
+
asOf: "2026-07-27",
|
|
416
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
417
|
+
provenance: "vendor-pricing-page",
|
|
418
|
+
tiers: [
|
|
419
|
+
{
|
|
420
|
+
inputPer1m: 3,
|
|
421
|
+
outputPer1m: 15,
|
|
422
|
+
cachedInputPer1m: 0.3,
|
|
423
|
+
cacheWritePer1m: 3.75,
|
|
424
|
+
reasoningOutputPer1m: 15
|
|
425
|
+
}
|
|
426
|
+
],
|
|
427
|
+
batchMultiplier: 0.5,
|
|
428
|
+
contextWindow: 2e5
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
model: "claude-haiku-4-5",
|
|
432
|
+
provider: "anthropic",
|
|
433
|
+
modality: "text",
|
|
434
|
+
currency: "USD",
|
|
435
|
+
asOf: "2026-07-27",
|
|
436
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
437
|
+
provenance: "vendor-pricing-page",
|
|
438
|
+
tiers: [
|
|
439
|
+
{
|
|
440
|
+
inputPer1m: 1,
|
|
441
|
+
outputPer1m: 5,
|
|
442
|
+
cachedInputPer1m: 0.1,
|
|
443
|
+
cacheWritePer1m: 1.25,
|
|
444
|
+
reasoningOutputPer1m: 5
|
|
445
|
+
}
|
|
446
|
+
],
|
|
447
|
+
batchMultiplier: 0.5,
|
|
448
|
+
contextWindow: 2e5
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
model: "claude-haiku-3-5",
|
|
452
|
+
provider: "anthropic",
|
|
453
|
+
modality: "text",
|
|
454
|
+
currency: "USD",
|
|
455
|
+
asOf: "2026-07-27",
|
|
456
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
457
|
+
provenance: "vendor-pricing-page",
|
|
458
|
+
tiers: [
|
|
459
|
+
{
|
|
460
|
+
inputPer1m: 0.8,
|
|
461
|
+
outputPer1m: 4,
|
|
462
|
+
cachedInputPer1m: 0.08,
|
|
463
|
+
cacheWritePer1m: 1,
|
|
464
|
+
reasoningOutputPer1m: 4
|
|
465
|
+
}
|
|
466
|
+
],
|
|
467
|
+
batchMultiplier: 0.5,
|
|
468
|
+
contextWindow: 2e5
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
model: "gpt-5.6-sol",
|
|
472
|
+
provider: "openai",
|
|
473
|
+
aliases: [
|
|
474
|
+
"gpt-5.6"
|
|
475
|
+
],
|
|
476
|
+
modality: "text",
|
|
477
|
+
currency: "USD",
|
|
478
|
+
asOf: "2026-07-27",
|
|
479
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
480
|
+
provenance: "litellm-catalog",
|
|
481
|
+
tiers: [
|
|
482
|
+
{
|
|
483
|
+
maxInputTokens: 272e3,
|
|
484
|
+
inputPer1m: 5,
|
|
485
|
+
outputPer1m: 30,
|
|
486
|
+
cachedInputPer1m: 0.5,
|
|
487
|
+
cacheWritePer1m: 6.25
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
inputPer1m: 10,
|
|
491
|
+
outputPer1m: 45,
|
|
492
|
+
cachedInputPer1m: 1,
|
|
493
|
+
cacheWritePer1m: 12.5
|
|
494
|
+
}
|
|
495
|
+
],
|
|
496
|
+
batchMultiplier: 0.5,
|
|
497
|
+
contextWindow: 105e4
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
model: "gpt-5.6-terra",
|
|
501
|
+
provider: "openai",
|
|
502
|
+
modality: "text",
|
|
503
|
+
currency: "USD",
|
|
504
|
+
asOf: "2026-07-27",
|
|
505
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
506
|
+
provenance: "litellm-catalog",
|
|
507
|
+
tiers: [
|
|
508
|
+
{
|
|
509
|
+
maxInputTokens: 272e3,
|
|
510
|
+
inputPer1m: 2.5,
|
|
511
|
+
outputPer1m: 15,
|
|
512
|
+
cachedInputPer1m: 0.25,
|
|
513
|
+
cacheWritePer1m: 3.125
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
inputPer1m: 5,
|
|
517
|
+
outputPer1m: 22.5,
|
|
518
|
+
cachedInputPer1m: 0.5,
|
|
519
|
+
cacheWritePer1m: 6.25
|
|
520
|
+
}
|
|
521
|
+
],
|
|
522
|
+
batchMultiplier: 0.5,
|
|
523
|
+
contextWindow: 105e4
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
model: "gpt-5.6-luna",
|
|
527
|
+
provider: "openai",
|
|
528
|
+
modality: "text",
|
|
529
|
+
currency: "USD",
|
|
530
|
+
asOf: "2026-07-27",
|
|
531
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
532
|
+
provenance: "litellm-catalog",
|
|
533
|
+
tiers: [
|
|
534
|
+
{
|
|
535
|
+
maxInputTokens: 272e3,
|
|
536
|
+
inputPer1m: 1,
|
|
537
|
+
outputPer1m: 6,
|
|
538
|
+
cachedInputPer1m: 0.1,
|
|
539
|
+
cacheWritePer1m: 1.25
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
inputPer1m: 2,
|
|
543
|
+
outputPer1m: 9,
|
|
544
|
+
cachedInputPer1m: 0.2,
|
|
545
|
+
cacheWritePer1m: 2.5
|
|
546
|
+
}
|
|
547
|
+
],
|
|
548
|
+
batchMultiplier: 0.5,
|
|
549
|
+
contextWindow: 105e4
|
|
550
|
+
},
|
|
551
|
+
{
|
|
552
|
+
model: "gpt-5.5",
|
|
553
|
+
provider: "openai",
|
|
554
|
+
modality: "text",
|
|
555
|
+
currency: "USD",
|
|
556
|
+
asOf: "2026-07-27",
|
|
557
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
558
|
+
provenance: "litellm-catalog",
|
|
559
|
+
tiers: [
|
|
560
|
+
{
|
|
561
|
+
maxInputTokens: 272e3,
|
|
562
|
+
inputPer1m: 5,
|
|
563
|
+
outputPer1m: 30,
|
|
564
|
+
cachedInputPer1m: 0.5
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
inputPer1m: 10,
|
|
568
|
+
outputPer1m: 45,
|
|
569
|
+
cachedInputPer1m: 1
|
|
570
|
+
}
|
|
571
|
+
],
|
|
572
|
+
batchMultiplier: 0.5,
|
|
573
|
+
contextWindow: 105e4
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
model: "gpt-5.4",
|
|
577
|
+
provider: "openai",
|
|
578
|
+
modality: "text",
|
|
579
|
+
currency: "USD",
|
|
580
|
+
asOf: "2026-07-27",
|
|
581
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
582
|
+
provenance: "litellm-catalog",
|
|
583
|
+
tiers: [
|
|
584
|
+
{
|
|
585
|
+
maxInputTokens: 272e3,
|
|
586
|
+
inputPer1m: 2.5,
|
|
587
|
+
outputPer1m: 15,
|
|
588
|
+
cachedInputPer1m: 0.25
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
inputPer1m: 5,
|
|
592
|
+
outputPer1m: 22.5,
|
|
593
|
+
cachedInputPer1m: 0.5
|
|
594
|
+
}
|
|
595
|
+
],
|
|
596
|
+
batchMultiplier: 0.5,
|
|
597
|
+
contextWindow: 105e4
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
model: "gpt-5.4-mini",
|
|
601
|
+
provider: "openai",
|
|
602
|
+
modality: "text",
|
|
603
|
+
currency: "USD",
|
|
604
|
+
asOf: "2026-07-27",
|
|
605
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
606
|
+
provenance: "litellm-catalog",
|
|
607
|
+
tiers: [
|
|
608
|
+
{
|
|
609
|
+
inputPer1m: 0.75,
|
|
610
|
+
outputPer1m: 4.5,
|
|
611
|
+
cachedInputPer1m: 0.075
|
|
612
|
+
}
|
|
613
|
+
],
|
|
614
|
+
batchMultiplier: 0.5,
|
|
615
|
+
contextWindow: 105e4
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
model: "gpt-5",
|
|
619
|
+
provider: "openai",
|
|
620
|
+
modality: "text",
|
|
621
|
+
currency: "USD",
|
|
622
|
+
asOf: "2026-07-27",
|
|
623
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
624
|
+
provenance: "litellm-catalog",
|
|
625
|
+
tiers: [
|
|
626
|
+
{
|
|
627
|
+
inputPer1m: 1.25,
|
|
628
|
+
outputPer1m: 10,
|
|
629
|
+
cachedInputPer1m: 0.125
|
|
630
|
+
}
|
|
631
|
+
],
|
|
632
|
+
contextWindow: 272e3
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
model: "gpt-5-mini",
|
|
636
|
+
provider: "openai",
|
|
637
|
+
modality: "text",
|
|
638
|
+
currency: "USD",
|
|
639
|
+
asOf: "2026-07-27",
|
|
640
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
641
|
+
provenance: "litellm-catalog",
|
|
642
|
+
tiers: [
|
|
643
|
+
{
|
|
644
|
+
inputPer1m: 0.25,
|
|
645
|
+
outputPer1m: 2,
|
|
646
|
+
cachedInputPer1m: 0.025
|
|
647
|
+
}
|
|
648
|
+
],
|
|
649
|
+
contextWindow: 272e3
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
model: "gpt-5-nano",
|
|
653
|
+
provider: "openai",
|
|
654
|
+
modality: "text",
|
|
655
|
+
currency: "USD",
|
|
656
|
+
asOf: "2026-07-27",
|
|
657
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
658
|
+
provenance: "litellm-catalog",
|
|
659
|
+
tiers: [
|
|
660
|
+
{
|
|
661
|
+
inputPer1m: 0.05,
|
|
662
|
+
outputPer1m: 0.4,
|
|
663
|
+
cachedInputPer1m: 5e-3
|
|
664
|
+
}
|
|
665
|
+
],
|
|
666
|
+
contextWindow: 272e3
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
model: "gpt-4.1",
|
|
670
|
+
provider: "openai",
|
|
671
|
+
modality: "text",
|
|
672
|
+
currency: "USD",
|
|
673
|
+
asOf: "2026-07-27",
|
|
674
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
675
|
+
provenance: "litellm-catalog",
|
|
676
|
+
tiers: [
|
|
677
|
+
{
|
|
678
|
+
inputPer1m: 2,
|
|
679
|
+
outputPer1m: 8,
|
|
680
|
+
cachedInputPer1m: 0.5
|
|
681
|
+
}
|
|
682
|
+
],
|
|
683
|
+
batchMultiplier: 0.5,
|
|
684
|
+
contextWindow: 1047576
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
model: "gpt-4.1-mini",
|
|
688
|
+
provider: "openai",
|
|
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
|
+
inputPer1m: 0.4,
|
|
697
|
+
outputPer1m: 1.6,
|
|
698
|
+
cachedInputPer1m: 0.1
|
|
699
|
+
}
|
|
700
|
+
],
|
|
701
|
+
batchMultiplier: 0.5,
|
|
702
|
+
contextWindow: 1047576
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
model: "gpt-4.1-nano",
|
|
706
|
+
provider: "openai",
|
|
707
|
+
modality: "text",
|
|
708
|
+
currency: "USD",
|
|
709
|
+
asOf: "2026-07-27",
|
|
710
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
711
|
+
provenance: "litellm-catalog",
|
|
712
|
+
tiers: [
|
|
713
|
+
{
|
|
714
|
+
inputPer1m: 0.1,
|
|
715
|
+
outputPer1m: 0.4,
|
|
716
|
+
cachedInputPer1m: 0.025
|
|
717
|
+
}
|
|
718
|
+
],
|
|
719
|
+
batchMultiplier: 0.5,
|
|
720
|
+
contextWindow: 1047576
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
model: "gpt-4o",
|
|
724
|
+
provider: "openai",
|
|
725
|
+
modality: "text",
|
|
726
|
+
currency: "USD",
|
|
727
|
+
asOf: "2026-07-27",
|
|
728
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
729
|
+
provenance: "litellm-catalog",
|
|
730
|
+
tiers: [
|
|
731
|
+
{
|
|
732
|
+
inputPer1m: 2.5,
|
|
733
|
+
outputPer1m: 10,
|
|
734
|
+
cachedInputPer1m: 1.25
|
|
735
|
+
}
|
|
736
|
+
],
|
|
737
|
+
batchMultiplier: 0.5,
|
|
738
|
+
contextWindow: 128e3
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
model: "gpt-4o-mini",
|
|
742
|
+
provider: "openai",
|
|
743
|
+
modality: "text",
|
|
744
|
+
currency: "USD",
|
|
745
|
+
asOf: "2026-07-27",
|
|
746
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
747
|
+
provenance: "litellm-catalog",
|
|
748
|
+
tiers: [
|
|
749
|
+
{
|
|
750
|
+
inputPer1m: 0.15,
|
|
751
|
+
outputPer1m: 0.6,
|
|
752
|
+
cachedInputPer1m: 0.075
|
|
753
|
+
}
|
|
754
|
+
],
|
|
755
|
+
batchMultiplier: 0.5,
|
|
756
|
+
contextWindow: 128e3
|
|
757
|
+
},
|
|
758
|
+
{
|
|
759
|
+
model: "gpt-5.5",
|
|
760
|
+
provider: "azure",
|
|
761
|
+
region: "global",
|
|
762
|
+
modality: "text",
|
|
763
|
+
currency: "USD",
|
|
764
|
+
asOf: "2026-07-27",
|
|
765
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
766
|
+
provenance: "litellm-catalog",
|
|
767
|
+
tiers: [
|
|
768
|
+
{
|
|
769
|
+
maxInputTokens: 272e3,
|
|
770
|
+
inputPer1m: 5,
|
|
771
|
+
outputPer1m: 30,
|
|
772
|
+
cachedInputPer1m: 0.5
|
|
773
|
+
},
|
|
774
|
+
{
|
|
775
|
+
inputPer1m: 10,
|
|
776
|
+
outputPer1m: 45,
|
|
777
|
+
cachedInputPer1m: 1
|
|
778
|
+
}
|
|
779
|
+
],
|
|
780
|
+
contextWindow: 105e4
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
model: "gpt-5.5",
|
|
784
|
+
provider: "azure",
|
|
785
|
+
region: "us",
|
|
786
|
+
modality: "text",
|
|
787
|
+
currency: "USD",
|
|
788
|
+
asOf: "2026-07-27",
|
|
789
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
790
|
+
provenance: "litellm-catalog",
|
|
791
|
+
tiers: [
|
|
792
|
+
{
|
|
793
|
+
maxInputTokens: 272e3,
|
|
794
|
+
inputPer1m: 5.5,
|
|
795
|
+
outputPer1m: 33,
|
|
796
|
+
cachedInputPer1m: 0.55
|
|
797
|
+
},
|
|
798
|
+
{
|
|
799
|
+
inputPer1m: 11,
|
|
800
|
+
outputPer1m: 49.5,
|
|
801
|
+
cachedInputPer1m: 1.1
|
|
802
|
+
}
|
|
803
|
+
],
|
|
804
|
+
contextWindow: 105e4
|
|
805
|
+
},
|
|
806
|
+
{
|
|
807
|
+
model: "gpt-5.5",
|
|
808
|
+
provider: "azure",
|
|
809
|
+
region: "eu",
|
|
810
|
+
modality: "text",
|
|
811
|
+
currency: "USD",
|
|
812
|
+
asOf: "2026-07-27",
|
|
813
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
814
|
+
provenance: "litellm-catalog",
|
|
815
|
+
tiers: [
|
|
816
|
+
{
|
|
817
|
+
maxInputTokens: 272e3,
|
|
818
|
+
inputPer1m: 5.5,
|
|
819
|
+
outputPer1m: 33,
|
|
820
|
+
cachedInputPer1m: 0.55
|
|
821
|
+
},
|
|
822
|
+
{
|
|
823
|
+
inputPer1m: 11,
|
|
824
|
+
outputPer1m: 49.5,
|
|
825
|
+
cachedInputPer1m: 1.1
|
|
826
|
+
}
|
|
827
|
+
],
|
|
828
|
+
contextWindow: 105e4
|
|
829
|
+
},
|
|
830
|
+
{
|
|
831
|
+
model: "gpt-5.4",
|
|
832
|
+
provider: "azure",
|
|
833
|
+
region: "global",
|
|
834
|
+
modality: "text",
|
|
835
|
+
currency: "USD",
|
|
836
|
+
asOf: "2026-07-27",
|
|
837
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
838
|
+
provenance: "litellm-catalog",
|
|
839
|
+
tiers: [
|
|
840
|
+
{
|
|
841
|
+
maxInputTokens: 272e3,
|
|
842
|
+
inputPer1m: 2.5,
|
|
843
|
+
outputPer1m: 15,
|
|
844
|
+
cachedInputPer1m: 0.25
|
|
845
|
+
},
|
|
846
|
+
{
|
|
847
|
+
inputPer1m: 5,
|
|
848
|
+
outputPer1m: 22.5,
|
|
849
|
+
cachedInputPer1m: 0.5
|
|
850
|
+
}
|
|
851
|
+
],
|
|
852
|
+
contextWindow: 105e4
|
|
853
|
+
},
|
|
854
|
+
{
|
|
855
|
+
model: "gpt-5.4",
|
|
856
|
+
provider: "azure",
|
|
857
|
+
region: "us",
|
|
858
|
+
modality: "text",
|
|
859
|
+
currency: "USD",
|
|
860
|
+
asOf: "2026-07-27",
|
|
861
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
862
|
+
provenance: "litellm-catalog",
|
|
863
|
+
tiers: [
|
|
864
|
+
{
|
|
865
|
+
inputPer1m: 2.75,
|
|
866
|
+
outputPer1m: 16.5,
|
|
867
|
+
cachedInputPer1m: 0.28
|
|
868
|
+
}
|
|
869
|
+
],
|
|
870
|
+
contextWindow: 105e4
|
|
871
|
+
},
|
|
872
|
+
{
|
|
873
|
+
model: "gpt-5.4",
|
|
874
|
+
provider: "azure",
|
|
875
|
+
region: "eu",
|
|
876
|
+
modality: "text",
|
|
877
|
+
currency: "USD",
|
|
878
|
+
asOf: "2026-07-27",
|
|
879
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
880
|
+
provenance: "litellm-catalog",
|
|
881
|
+
tiers: [
|
|
882
|
+
{
|
|
883
|
+
inputPer1m: 2.75,
|
|
884
|
+
outputPer1m: 16.5,
|
|
885
|
+
cachedInputPer1m: 0.28
|
|
886
|
+
}
|
|
887
|
+
],
|
|
888
|
+
contextWindow: 105e4
|
|
889
|
+
},
|
|
890
|
+
{
|
|
891
|
+
model: "gpt-5.6-terra",
|
|
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
|
+
maxInputTokens: 272e3,
|
|
902
|
+
inputPer1m: 2.5,
|
|
903
|
+
outputPer1m: 15,
|
|
904
|
+
cachedInputPer1m: 0.25
|
|
905
|
+
},
|
|
906
|
+
{
|
|
907
|
+
inputPer1m: 5,
|
|
908
|
+
outputPer1m: 22.5,
|
|
909
|
+
cachedInputPer1m: 0.5
|
|
910
|
+
}
|
|
911
|
+
],
|
|
912
|
+
contextWindow: 105e4
|
|
913
|
+
},
|
|
914
|
+
{
|
|
915
|
+
model: "gpt-5.6-terra",
|
|
916
|
+
provider: "azure",
|
|
917
|
+
region: "eu",
|
|
918
|
+
modality: "text",
|
|
919
|
+
currency: "USD",
|
|
920
|
+
asOf: "2026-07-27",
|
|
921
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
922
|
+
provenance: "litellm-catalog",
|
|
923
|
+
tiers: [
|
|
924
|
+
{
|
|
925
|
+
maxInputTokens: 272e3,
|
|
926
|
+
inputPer1m: 2.75,
|
|
927
|
+
outputPer1m: 16.5,
|
|
928
|
+
cachedInputPer1m: 0.275
|
|
929
|
+
},
|
|
930
|
+
{
|
|
931
|
+
inputPer1m: 5.5,
|
|
932
|
+
outputPer1m: 24.75,
|
|
933
|
+
cachedInputPer1m: 0.55
|
|
934
|
+
}
|
|
935
|
+
],
|
|
936
|
+
contextWindow: 105e4
|
|
937
|
+
},
|
|
938
|
+
{
|
|
939
|
+
model: "gpt-5",
|
|
940
|
+
provider: "azure",
|
|
941
|
+
region: "global",
|
|
942
|
+
modality: "text",
|
|
943
|
+
currency: "USD",
|
|
944
|
+
asOf: "2026-07-27",
|
|
945
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
946
|
+
provenance: "litellm-catalog",
|
|
947
|
+
tiers: [
|
|
948
|
+
{
|
|
949
|
+
inputPer1m: 1.25,
|
|
950
|
+
outputPer1m: 10,
|
|
951
|
+
cachedInputPer1m: 0.125
|
|
952
|
+
}
|
|
953
|
+
],
|
|
954
|
+
contextWindow: 272e3
|
|
955
|
+
},
|
|
956
|
+
{
|
|
957
|
+
model: "gpt-5-mini",
|
|
958
|
+
provider: "azure",
|
|
959
|
+
region: "global",
|
|
960
|
+
modality: "text",
|
|
961
|
+
currency: "USD",
|
|
962
|
+
asOf: "2026-07-27",
|
|
963
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
964
|
+
provenance: "litellm-catalog",
|
|
965
|
+
tiers: [
|
|
966
|
+
{
|
|
967
|
+
inputPer1m: 0.25,
|
|
968
|
+
outputPer1m: 2,
|
|
969
|
+
cachedInputPer1m: 0.025
|
|
970
|
+
}
|
|
971
|
+
],
|
|
972
|
+
contextWindow: 272e3
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
model: "gpt-5-nano",
|
|
976
|
+
provider: "azure",
|
|
977
|
+
region: "global",
|
|
978
|
+
modality: "text",
|
|
979
|
+
currency: "USD",
|
|
980
|
+
asOf: "2026-07-27",
|
|
981
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
982
|
+
provenance: "litellm-catalog",
|
|
983
|
+
tiers: [
|
|
984
|
+
{
|
|
985
|
+
inputPer1m: 0.05,
|
|
986
|
+
outputPer1m: 0.4,
|
|
987
|
+
cachedInputPer1m: 5e-3
|
|
988
|
+
}
|
|
989
|
+
],
|
|
990
|
+
contextWindow: 272e3
|
|
991
|
+
},
|
|
992
|
+
{
|
|
993
|
+
model: "gpt-4.1",
|
|
994
|
+
provider: "azure",
|
|
995
|
+
region: "global",
|
|
996
|
+
modality: "text",
|
|
997
|
+
currency: "USD",
|
|
998
|
+
asOf: "2026-07-27",
|
|
999
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1000
|
+
provenance: "litellm-catalog",
|
|
1001
|
+
tiers: [
|
|
1002
|
+
{
|
|
1003
|
+
inputPer1m: 2,
|
|
1004
|
+
outputPer1m: 8,
|
|
1005
|
+
cachedInputPer1m: 0.5
|
|
1006
|
+
}
|
|
1007
|
+
],
|
|
1008
|
+
batchMultiplier: 0.5,
|
|
1009
|
+
contextWindow: 1047576
|
|
1010
|
+
},
|
|
1011
|
+
{
|
|
1012
|
+
model: "gpt-4o",
|
|
1013
|
+
provider: "azure",
|
|
1014
|
+
region: "global",
|
|
1015
|
+
modality: "text",
|
|
1016
|
+
currency: "USD",
|
|
1017
|
+
asOf: "2026-07-27",
|
|
1018
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1019
|
+
provenance: "litellm-catalog",
|
|
1020
|
+
tiers: [
|
|
1021
|
+
{
|
|
1022
|
+
inputPer1m: 2.5,
|
|
1023
|
+
outputPer1m: 10,
|
|
1024
|
+
cachedInputPer1m: 1.25
|
|
1025
|
+
}
|
|
1026
|
+
],
|
|
1027
|
+
contextWindow: 128e3
|
|
1028
|
+
},
|
|
1029
|
+
{
|
|
1030
|
+
model: "gpt-4o-mini",
|
|
1031
|
+
provider: "azure",
|
|
1032
|
+
region: "global",
|
|
1033
|
+
modality: "text",
|
|
1034
|
+
currency: "USD",
|
|
1035
|
+
asOf: "2026-07-27",
|
|
1036
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1037
|
+
provenance: "litellm-catalog",
|
|
1038
|
+
tiers: [
|
|
1039
|
+
{
|
|
1040
|
+
inputPer1m: 0.165,
|
|
1041
|
+
outputPer1m: 0.66,
|
|
1042
|
+
cachedInputPer1m: 0.075
|
|
1043
|
+
}
|
|
1044
|
+
],
|
|
1045
|
+
contextWindow: 128e3,
|
|
1046
|
+
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."
|
|
1047
|
+
},
|
|
1048
|
+
{
|
|
1049
|
+
model: "gemini-3.1-pro",
|
|
1050
|
+
provider: "gemini",
|
|
1051
|
+
aliases: [
|
|
1052
|
+
"gemini-3.1-pro-preview"
|
|
1053
|
+
],
|
|
1054
|
+
modality: "text",
|
|
1055
|
+
currency: "USD",
|
|
1056
|
+
asOf: "2026-07-27",
|
|
1057
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1058
|
+
provenance: "vendor-pricing-page",
|
|
1059
|
+
tiers: [
|
|
1060
|
+
{
|
|
1061
|
+
maxInputTokens: 2e5,
|
|
1062
|
+
inputPer1m: 2,
|
|
1063
|
+
outputPer1m: 12,
|
|
1064
|
+
cachedInputPer1m: 0.2,
|
|
1065
|
+
reasoningOutputPer1m: 12
|
|
1066
|
+
},
|
|
1067
|
+
{
|
|
1068
|
+
inputPer1m: 4,
|
|
1069
|
+
outputPer1m: 18,
|
|
1070
|
+
cachedInputPer1m: 0.4,
|
|
1071
|
+
reasoningOutputPer1m: 18
|
|
1072
|
+
}
|
|
1073
|
+
],
|
|
1074
|
+
batchMultiplier: 0.5,
|
|
1075
|
+
contextWindow: 1048576,
|
|
1076
|
+
notes: "Long-context tier: $2/$12 per 1M up to 200K prompt tokens, $4/$18 above it."
|
|
1077
|
+
},
|
|
1078
|
+
{
|
|
1079
|
+
model: "gemini-3-pro",
|
|
1080
|
+
provider: "gemini",
|
|
1081
|
+
aliases: [
|
|
1082
|
+
"gemini-3-pro-preview"
|
|
1083
|
+
],
|
|
1084
|
+
modality: "text",
|
|
1085
|
+
currency: "USD",
|
|
1086
|
+
asOf: "2026-07-27",
|
|
1087
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1088
|
+
provenance: "vendor-pricing-page",
|
|
1089
|
+
tiers: [
|
|
1090
|
+
{
|
|
1091
|
+
maxInputTokens: 2e5,
|
|
1092
|
+
inputPer1m: 2,
|
|
1093
|
+
outputPer1m: 12,
|
|
1094
|
+
cachedInputPer1m: 0.2,
|
|
1095
|
+
reasoningOutputPer1m: 12
|
|
1096
|
+
},
|
|
1097
|
+
{
|
|
1098
|
+
inputPer1m: 4,
|
|
1099
|
+
outputPer1m: 18,
|
|
1100
|
+
cachedInputPer1m: 0.4,
|
|
1101
|
+
reasoningOutputPer1m: 18
|
|
1102
|
+
}
|
|
1103
|
+
],
|
|
1104
|
+
batchMultiplier: 0.5,
|
|
1105
|
+
contextWindow: 1048576
|
|
1106
|
+
},
|
|
1107
|
+
{
|
|
1108
|
+
model: "gemini-3.6-flash",
|
|
1109
|
+
provider: "gemini",
|
|
1110
|
+
modality: "text",
|
|
1111
|
+
currency: "USD",
|
|
1112
|
+
asOf: "2026-07-27",
|
|
1113
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1114
|
+
provenance: "vendor-pricing-page",
|
|
1115
|
+
tiers: [
|
|
1116
|
+
{
|
|
1117
|
+
inputPer1m: 1.5,
|
|
1118
|
+
outputPer1m: 7.5,
|
|
1119
|
+
cachedInputPer1m: 0.15,
|
|
1120
|
+
reasoningOutputPer1m: 7.5
|
|
1121
|
+
}
|
|
1122
|
+
],
|
|
1123
|
+
batchMultiplier: 0.5,
|
|
1124
|
+
contextWindow: 1048576,
|
|
1125
|
+
notes: "Released 2026-07-21. Output pricing dropped ~17% from Gemini 3.5 Flash ($9.00 -> $7.50 per 1M); input unchanged."
|
|
1126
|
+
},
|
|
1127
|
+
{
|
|
1128
|
+
model: "gemini-3.5-flash",
|
|
1129
|
+
provider: "gemini",
|
|
1130
|
+
modality: "text",
|
|
1131
|
+
currency: "USD",
|
|
1132
|
+
asOf: "2026-07-27",
|
|
1133
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1134
|
+
provenance: "vendor-pricing-page",
|
|
1135
|
+
tiers: [
|
|
1136
|
+
{
|
|
1137
|
+
inputPer1m: 1.5,
|
|
1138
|
+
outputPer1m: 9,
|
|
1139
|
+
cachedInputPer1m: 0.15,
|
|
1140
|
+
reasoningOutputPer1m: 9
|
|
1141
|
+
}
|
|
1142
|
+
],
|
|
1143
|
+
batchMultiplier: 0.5,
|
|
1144
|
+
contextWindow: 1048576
|
|
1145
|
+
},
|
|
1146
|
+
{
|
|
1147
|
+
model: "gemini-3.5-flash-lite",
|
|
1148
|
+
provider: "gemini",
|
|
1149
|
+
modality: "text",
|
|
1150
|
+
currency: "USD",
|
|
1151
|
+
asOf: "2026-07-27",
|
|
1152
|
+
source: "https://ai.google.dev/gemini-api/docs/pricing",
|
|
1153
|
+
provenance: "litellm-catalog",
|
|
1154
|
+
tiers: [
|
|
1155
|
+
{
|
|
1156
|
+
inputPer1m: 0.3,
|
|
1157
|
+
outputPer1m: 2.5,
|
|
1158
|
+
cachedInputPer1m: 0.03,
|
|
1159
|
+
reasoningOutputPer1m: 2.5
|
|
1160
|
+
}
|
|
1161
|
+
],
|
|
1162
|
+
batchMultiplier: 0.5,
|
|
1163
|
+
contextWindow: 1048576
|
|
1164
|
+
},
|
|
1165
|
+
{
|
|
1166
|
+
model: "gemini-3.1-flash-lite",
|
|
1167
|
+
provider: "gemini",
|
|
1168
|
+
modality: "text",
|
|
1169
|
+
currency: "USD",
|
|
1170
|
+
asOf: "2026-07-27",
|
|
1171
|
+
source: "https://ai.google.dev/gemini-api/docs/pricing#gemini-3.1-flash-lite",
|
|
1172
|
+
provenance: "litellm-catalog",
|
|
1173
|
+
tiers: [
|
|
1174
|
+
{
|
|
1175
|
+
inputPer1m: 0.25,
|
|
1176
|
+
outputPer1m: 1.5,
|
|
1177
|
+
cachedInputPer1m: 0.025,
|
|
1178
|
+
reasoningOutputPer1m: 1.5
|
|
1179
|
+
}
|
|
1180
|
+
],
|
|
1181
|
+
batchMultiplier: 0.5,
|
|
1182
|
+
contextWindow: 1048576
|
|
1183
|
+
},
|
|
1184
|
+
{
|
|
1185
|
+
model: "gemini-3-flash",
|
|
1186
|
+
provider: "gemini",
|
|
1187
|
+
aliases: [
|
|
1188
|
+
"gemini-3-flash-preview"
|
|
1189
|
+
],
|
|
1190
|
+
modality: "text",
|
|
1191
|
+
currency: "USD",
|
|
1192
|
+
asOf: "2026-07-27",
|
|
1193
|
+
source: "https://ai.google.dev/pricing/gemini-3",
|
|
1194
|
+
provenance: "litellm-catalog",
|
|
1195
|
+
tiers: [
|
|
1196
|
+
{
|
|
1197
|
+
inputPer1m: 0.5,
|
|
1198
|
+
outputPer1m: 3,
|
|
1199
|
+
cachedInputPer1m: 0.05,
|
|
1200
|
+
reasoningOutputPer1m: 3
|
|
1201
|
+
}
|
|
1202
|
+
],
|
|
1203
|
+
contextWindow: 1048576
|
|
1204
|
+
},
|
|
1205
|
+
{
|
|
1206
|
+
model: "gemini-2.5-pro",
|
|
1207
|
+
provider: "gemini",
|
|
1208
|
+
modality: "text",
|
|
1209
|
+
currency: "USD",
|
|
1210
|
+
asOf: "2026-07-27",
|
|
1211
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1212
|
+
provenance: "litellm-catalog",
|
|
1213
|
+
tiers: [
|
|
1214
|
+
{
|
|
1215
|
+
maxInputTokens: 2e5,
|
|
1216
|
+
inputPer1m: 1.25,
|
|
1217
|
+
outputPer1m: 10,
|
|
1218
|
+
cachedInputPer1m: 0.125
|
|
1219
|
+
},
|
|
1220
|
+
{
|
|
1221
|
+
inputPer1m: 2.5,
|
|
1222
|
+
outputPer1m: 15,
|
|
1223
|
+
cachedInputPer1m: 0.25
|
|
1224
|
+
}
|
|
1225
|
+
],
|
|
1226
|
+
contextWindow: 1048576
|
|
1227
|
+
},
|
|
1228
|
+
{
|
|
1229
|
+
model: "gemini-2.5-flash",
|
|
1230
|
+
provider: "gemini",
|
|
1231
|
+
modality: "text",
|
|
1232
|
+
currency: "USD",
|
|
1233
|
+
asOf: "2026-07-27",
|
|
1234
|
+
source: "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
|
1235
|
+
provenance: "litellm-catalog",
|
|
1236
|
+
tiers: [
|
|
1237
|
+
{
|
|
1238
|
+
inputPer1m: 0.3,
|
|
1239
|
+
outputPer1m: 2.5,
|
|
1240
|
+
cachedInputPer1m: 0.03,
|
|
1241
|
+
reasoningOutputPer1m: 2.5
|
|
1242
|
+
}
|
|
1243
|
+
],
|
|
1244
|
+
contextWindow: 1048576
|
|
1245
|
+
},
|
|
1246
|
+
{
|
|
1247
|
+
model: "gemini-2.5-flash-lite",
|
|
1248
|
+
provider: "gemini",
|
|
1249
|
+
modality: "text",
|
|
1250
|
+
currency: "USD",
|
|
1251
|
+
asOf: "2026-07-27",
|
|
1252
|
+
source: "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-lite",
|
|
1253
|
+
provenance: "litellm-catalog",
|
|
1254
|
+
tiers: [
|
|
1255
|
+
{
|
|
1256
|
+
inputPer1m: 0.1,
|
|
1257
|
+
outputPer1m: 0.4,
|
|
1258
|
+
cachedInputPer1m: 0.01,
|
|
1259
|
+
reasoningOutputPer1m: 0.4
|
|
1260
|
+
}
|
|
1261
|
+
],
|
|
1262
|
+
contextWindow: 1048576
|
|
1263
|
+
},
|
|
1264
|
+
{
|
|
1265
|
+
model: "gemini-2.0-flash",
|
|
1266
|
+
provider: "gemini",
|
|
1267
|
+
modality: "text",
|
|
1268
|
+
currency: "USD",
|
|
1269
|
+
asOf: "2026-07-27",
|
|
1270
|
+
source: "https://ai.google.dev/pricing#2_0flash",
|
|
1271
|
+
provenance: "litellm-catalog",
|
|
1272
|
+
tiers: [
|
|
1273
|
+
{
|
|
1274
|
+
inputPer1m: 0.1,
|
|
1275
|
+
outputPer1m: 0.4,
|
|
1276
|
+
cachedInputPer1m: 0.025
|
|
1277
|
+
}
|
|
1278
|
+
],
|
|
1279
|
+
contextWindow: 1048576,
|
|
1280
|
+
notes: "Gemini Developer API price. Vertex AI lists $0.15/$0.60 per 1M for the same model \u2014 see the vertex entry."
|
|
1281
|
+
},
|
|
1282
|
+
{
|
|
1283
|
+
model: "gemini-2.0-flash-lite",
|
|
1284
|
+
provider: "gemini",
|
|
1285
|
+
modality: "text",
|
|
1286
|
+
currency: "USD",
|
|
1287
|
+
asOf: "2026-07-27",
|
|
1288
|
+
source: "https://ai.google.dev/gemini-api/docs/pricing#gemini-2.0-flash-lite",
|
|
1289
|
+
provenance: "litellm-catalog",
|
|
1290
|
+
tiers: [
|
|
1291
|
+
{
|
|
1292
|
+
inputPer1m: 0.075,
|
|
1293
|
+
outputPer1m: 0.3,
|
|
1294
|
+
cachedInputPer1m: 0.01875
|
|
1295
|
+
}
|
|
1296
|
+
],
|
|
1297
|
+
contextWindow: 1048576
|
|
1298
|
+
},
|
|
1299
|
+
{
|
|
1300
|
+
model: "gemini-2.0-flash",
|
|
1301
|
+
provider: "vertex",
|
|
1302
|
+
modality: "text",
|
|
1303
|
+
currency: "USD",
|
|
1304
|
+
asOf: "2026-07-27",
|
|
1305
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1306
|
+
provenance: "vendor-pricing-page",
|
|
1307
|
+
tiers: [
|
|
1308
|
+
{
|
|
1309
|
+
inputPer1m: 0.15,
|
|
1310
|
+
outputPer1m: 0.6
|
|
1311
|
+
}
|
|
1312
|
+
],
|
|
1313
|
+
batchMultiplier: 0.5,
|
|
1314
|
+
notes: "Vertex AI lists $0.15/$0.60 per 1M; the Gemini Developer API lists $0.10/$0.40 for the same model."
|
|
1315
|
+
},
|
|
1316
|
+
{
|
|
1317
|
+
model: "gemini-3.1-pro",
|
|
1318
|
+
provider: "vertex",
|
|
1319
|
+
aliases: [
|
|
1320
|
+
"gemini-3.1-pro-preview"
|
|
1321
|
+
],
|
|
1322
|
+
modality: "text",
|
|
1323
|
+
currency: "USD",
|
|
1324
|
+
asOf: "2026-07-27",
|
|
1325
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1326
|
+
provenance: "vendor-pricing-page",
|
|
1327
|
+
tiers: [
|
|
1328
|
+
{
|
|
1329
|
+
maxInputTokens: 2e5,
|
|
1330
|
+
inputPer1m: 2,
|
|
1331
|
+
outputPer1m: 12,
|
|
1332
|
+
cachedInputPer1m: 0.2,
|
|
1333
|
+
reasoningOutputPer1m: 12
|
|
1334
|
+
},
|
|
1335
|
+
{
|
|
1336
|
+
inputPer1m: 4,
|
|
1337
|
+
outputPer1m: 18,
|
|
1338
|
+
cachedInputPer1m: 0.4,
|
|
1339
|
+
reasoningOutputPer1m: 18
|
|
1340
|
+
}
|
|
1341
|
+
],
|
|
1342
|
+
batchMultiplier: 0.5,
|
|
1343
|
+
contextWindow: 1048576
|
|
1344
|
+
},
|
|
1345
|
+
{
|
|
1346
|
+
model: "gemini-3.6-flash",
|
|
1347
|
+
provider: "vertex",
|
|
1348
|
+
modality: "text",
|
|
1349
|
+
currency: "USD",
|
|
1350
|
+
asOf: "2026-07-27",
|
|
1351
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1352
|
+
provenance: "vendor-pricing-page",
|
|
1353
|
+
tiers: [
|
|
1354
|
+
{
|
|
1355
|
+
inputPer1m: 1.5,
|
|
1356
|
+
outputPer1m: 7.5,
|
|
1357
|
+
cachedInputPer1m: 0.15,
|
|
1358
|
+
reasoningOutputPer1m: 7.5
|
|
1359
|
+
}
|
|
1360
|
+
],
|
|
1361
|
+
batchMultiplier: 0.5,
|
|
1362
|
+
contextWindow: 1048576
|
|
1363
|
+
},
|
|
1364
|
+
{
|
|
1365
|
+
model: "gemini-2.5-pro",
|
|
1366
|
+
provider: "vertex",
|
|
1367
|
+
modality: "text",
|
|
1368
|
+
currency: "USD",
|
|
1369
|
+
asOf: "2026-07-27",
|
|
1370
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1371
|
+
provenance: "vendor-pricing-page",
|
|
1372
|
+
tiers: [
|
|
1373
|
+
{
|
|
1374
|
+
maxInputTokens: 2e5,
|
|
1375
|
+
inputPer1m: 1.25,
|
|
1376
|
+
outputPer1m: 10,
|
|
1377
|
+
cachedInputPer1m: 0.13
|
|
1378
|
+
},
|
|
1379
|
+
{
|
|
1380
|
+
inputPer1m: 2.5,
|
|
1381
|
+
outputPer1m: 15,
|
|
1382
|
+
cachedInputPer1m: 0.25
|
|
1383
|
+
}
|
|
1384
|
+
],
|
|
1385
|
+
batchMultiplier: 0.5,
|
|
1386
|
+
contextWindow: 1048576
|
|
1387
|
+
},
|
|
1388
|
+
{
|
|
1389
|
+
model: "gemini-2.5-flash",
|
|
1390
|
+
provider: "vertex",
|
|
1391
|
+
modality: "text",
|
|
1392
|
+
currency: "USD",
|
|
1393
|
+
asOf: "2026-07-27",
|
|
1394
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1395
|
+
provenance: "vendor-pricing-page",
|
|
1396
|
+
tiers: [
|
|
1397
|
+
{
|
|
1398
|
+
inputPer1m: 0.3,
|
|
1399
|
+
outputPer1m: 2.5,
|
|
1400
|
+
cachedInputPer1m: 0.03,
|
|
1401
|
+
reasoningOutputPer1m: 2.5
|
|
1402
|
+
}
|
|
1403
|
+
],
|
|
1404
|
+
batchMultiplier: 0.5,
|
|
1405
|
+
contextWindow: 1048576
|
|
1406
|
+
},
|
|
1407
|
+
{
|
|
1408
|
+
model: "claude-sonnet-4-6",
|
|
1409
|
+
provider: "openrouter",
|
|
1410
|
+
modality: "text",
|
|
1411
|
+
currency: "USD",
|
|
1412
|
+
asOf: "2026-07-27",
|
|
1413
|
+
source: "https://openrouter.ai/anthropic/claude-sonnet-4.6",
|
|
1414
|
+
provenance: "litellm-catalog",
|
|
1415
|
+
tiers: [
|
|
1416
|
+
{
|
|
1417
|
+
maxInputTokens: 2e5,
|
|
1418
|
+
inputPer1m: 3,
|
|
1419
|
+
outputPer1m: 15,
|
|
1420
|
+
cachedInputPer1m: 0.3,
|
|
1421
|
+
cacheWritePer1m: 3.75
|
|
1422
|
+
},
|
|
1423
|
+
{
|
|
1424
|
+
inputPer1m: 6,
|
|
1425
|
+
outputPer1m: 22.5,
|
|
1426
|
+
cachedInputPer1m: 0.6,
|
|
1427
|
+
cacheWritePer1m: 7.5
|
|
1428
|
+
}
|
|
1429
|
+
],
|
|
1430
|
+
contextWindow: 1e6
|
|
1431
|
+
},
|
|
1432
|
+
{
|
|
1433
|
+
model: "claude-haiku-4-5",
|
|
1434
|
+
provider: "openrouter",
|
|
1435
|
+
modality: "text",
|
|
1436
|
+
currency: "USD",
|
|
1437
|
+
asOf: "2026-07-27",
|
|
1438
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1439
|
+
provenance: "litellm-catalog",
|
|
1440
|
+
tiers: [
|
|
1441
|
+
{
|
|
1442
|
+
inputPer1m: 1,
|
|
1443
|
+
outputPer1m: 5,
|
|
1444
|
+
cachedInputPer1m: 0.1,
|
|
1445
|
+
cacheWritePer1m: 1.25
|
|
1446
|
+
}
|
|
1447
|
+
],
|
|
1448
|
+
contextWindow: 2e5
|
|
1449
|
+
},
|
|
1450
|
+
{
|
|
1451
|
+
model: "gpt-5",
|
|
1452
|
+
provider: "openrouter",
|
|
1453
|
+
modality: "text",
|
|
1454
|
+
currency: "USD",
|
|
1455
|
+
asOf: "2026-07-27",
|
|
1456
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1457
|
+
provenance: "litellm-catalog",
|
|
1458
|
+
tiers: [
|
|
1459
|
+
{
|
|
1460
|
+
inputPer1m: 1.25,
|
|
1461
|
+
outputPer1m: 10,
|
|
1462
|
+
cachedInputPer1m: 0.125
|
|
1463
|
+
}
|
|
1464
|
+
],
|
|
1465
|
+
contextWindow: 272e3
|
|
1466
|
+
},
|
|
1467
|
+
{
|
|
1468
|
+
model: "gpt-5-mini",
|
|
1469
|
+
provider: "openrouter",
|
|
1470
|
+
modality: "text",
|
|
1471
|
+
currency: "USD",
|
|
1472
|
+
asOf: "2026-07-27",
|
|
1473
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1474
|
+
provenance: "litellm-catalog",
|
|
1475
|
+
tiers: [
|
|
1476
|
+
{
|
|
1477
|
+
inputPer1m: 0.25,
|
|
1478
|
+
outputPer1m: 2,
|
|
1479
|
+
cachedInputPer1m: 0.025
|
|
1480
|
+
}
|
|
1481
|
+
],
|
|
1482
|
+
contextWindow: 272e3
|
|
1483
|
+
},
|
|
1484
|
+
{
|
|
1485
|
+
model: "gemini-3.1-pro",
|
|
1486
|
+
provider: "openrouter",
|
|
1487
|
+
aliases: [
|
|
1488
|
+
"gemini-3.1-pro-preview"
|
|
1489
|
+
],
|
|
1490
|
+
modality: "text",
|
|
1491
|
+
currency: "USD",
|
|
1492
|
+
asOf: "2026-07-27",
|
|
1493
|
+
source: "https://openrouter.ai/google/gemini-3.1-pro-preview",
|
|
1494
|
+
provenance: "litellm-catalog",
|
|
1495
|
+
tiers: [
|
|
1496
|
+
{
|
|
1497
|
+
maxInputTokens: 2e5,
|
|
1498
|
+
inputPer1m: 2,
|
|
1499
|
+
outputPer1m: 12,
|
|
1500
|
+
cachedInputPer1m: 0.2
|
|
1501
|
+
},
|
|
1502
|
+
{
|
|
1503
|
+
inputPer1m: 4,
|
|
1504
|
+
outputPer1m: 18,
|
|
1505
|
+
cachedInputPer1m: 0.4,
|
|
1506
|
+
cacheWritePer1m: 0.25
|
|
1507
|
+
}
|
|
1508
|
+
],
|
|
1509
|
+
contextWindow: 1048576
|
|
1510
|
+
},
|
|
1511
|
+
{
|
|
1512
|
+
model: "text-embedding-3-small",
|
|
1513
|
+
provider: "openai",
|
|
1514
|
+
modality: "embedding",
|
|
1515
|
+
currency: "USD",
|
|
1516
|
+
asOf: "2026-07-27",
|
|
1517
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1518
|
+
provenance: "litellm-catalog",
|
|
1519
|
+
tiers: [
|
|
1520
|
+
{
|
|
1521
|
+
inputPer1m: 0.02,
|
|
1522
|
+
outputPer1m: 0
|
|
1523
|
+
}
|
|
1524
|
+
],
|
|
1525
|
+
batchMultiplier: 0.5,
|
|
1526
|
+
contextWindow: 8191
|
|
1527
|
+
},
|
|
1528
|
+
{
|
|
1529
|
+
model: "text-embedding-3-large",
|
|
1530
|
+
provider: "openai",
|
|
1531
|
+
modality: "embedding",
|
|
1532
|
+
currency: "USD",
|
|
1533
|
+
asOf: "2026-07-27",
|
|
1534
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1535
|
+
provenance: "litellm-catalog",
|
|
1536
|
+
tiers: [
|
|
1537
|
+
{
|
|
1538
|
+
inputPer1m: 0.13,
|
|
1539
|
+
outputPer1m: 0
|
|
1540
|
+
}
|
|
1541
|
+
],
|
|
1542
|
+
batchMultiplier: 0.5,
|
|
1543
|
+
contextWindow: 8191
|
|
1544
|
+
},
|
|
1545
|
+
{
|
|
1546
|
+
model: "gemini-embedding-001",
|
|
1547
|
+
provider: "gemini",
|
|
1548
|
+
modality: "embedding",
|
|
1549
|
+
currency: "USD",
|
|
1550
|
+
asOf: "2026-07-27",
|
|
1551
|
+
source: "https://ai.google.dev/gemini-api/docs/embeddings#model-versions",
|
|
1552
|
+
provenance: "litellm-catalog",
|
|
1553
|
+
tiers: [
|
|
1554
|
+
{
|
|
1555
|
+
inputPer1m: 0.15,
|
|
1556
|
+
outputPer1m: 0
|
|
1557
|
+
}
|
|
1558
|
+
],
|
|
1559
|
+
contextWindow: 2048
|
|
1560
|
+
},
|
|
1561
|
+
{
|
|
1562
|
+
model: "dall-e-3",
|
|
1563
|
+
provider: "openai",
|
|
1564
|
+
modality: "image",
|
|
1565
|
+
currency: "USD",
|
|
1566
|
+
asOf: "2026-07-27",
|
|
1567
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1568
|
+
provenance: "litellm-catalog",
|
|
1569
|
+
rates: [
|
|
1570
|
+
{
|
|
1571
|
+
unit: "per_image",
|
|
1572
|
+
amount: 0.04,
|
|
1573
|
+
variant: "1024x1024 standard"
|
|
1574
|
+
}
|
|
1575
|
+
]
|
|
1576
|
+
},
|
|
1577
|
+
{
|
|
1578
|
+
model: "gemini-3-pro-image",
|
|
1579
|
+
provider: "gemini",
|
|
1580
|
+
modality: "image",
|
|
1581
|
+
currency: "USD",
|
|
1582
|
+
asOf: "2026-07-27",
|
|
1583
|
+
source: "https://ai.google.dev/gemini-api/docs/pricing",
|
|
1584
|
+
provenance: "litellm-catalog",
|
|
1585
|
+
rates: [
|
|
1586
|
+
{
|
|
1587
|
+
unit: "per_image",
|
|
1588
|
+
amount: 0.134,
|
|
1589
|
+
variant: "default output resolution"
|
|
1590
|
+
}
|
|
1591
|
+
],
|
|
1592
|
+
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."
|
|
1593
|
+
},
|
|
1594
|
+
{
|
|
1595
|
+
model: "imagen-4.0-generate-001",
|
|
1596
|
+
provider: "vertex",
|
|
1597
|
+
modality: "image",
|
|
1598
|
+
currency: "USD",
|
|
1599
|
+
asOf: "2026-07-27",
|
|
1600
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1601
|
+
provenance: "litellm-catalog",
|
|
1602
|
+
rates: [
|
|
1603
|
+
{
|
|
1604
|
+
unit: "per_image",
|
|
1605
|
+
amount: 0.04
|
|
1606
|
+
}
|
|
1607
|
+
]
|
|
1608
|
+
},
|
|
1609
|
+
{
|
|
1610
|
+
model: "veo-3.1-generate-001",
|
|
1611
|
+
provider: "gemini",
|
|
1612
|
+
modality: "video",
|
|
1613
|
+
currency: "USD",
|
|
1614
|
+
asOf: "2026-07-27",
|
|
1615
|
+
source: "https://ai.google.dev/gemini-api/docs/video",
|
|
1616
|
+
provenance: "litellm-catalog",
|
|
1617
|
+
rates: [
|
|
1618
|
+
{
|
|
1619
|
+
unit: "per_second",
|
|
1620
|
+
amount: 0.4
|
|
1621
|
+
}
|
|
1622
|
+
]
|
|
1623
|
+
},
|
|
1624
|
+
{
|
|
1625
|
+
model: "veo-3.1-generate-001",
|
|
1626
|
+
provider: "vertex",
|
|
1627
|
+
modality: "video",
|
|
1628
|
+
currency: "USD",
|
|
1629
|
+
asOf: "2026-07-27",
|
|
1630
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/veo",
|
|
1631
|
+
provenance: "litellm-catalog",
|
|
1632
|
+
rates: [
|
|
1633
|
+
{
|
|
1634
|
+
unit: "per_second",
|
|
1635
|
+
amount: 0.4
|
|
1636
|
+
}
|
|
1637
|
+
]
|
|
1638
|
+
},
|
|
1639
|
+
{
|
|
1640
|
+
model: "tts-1",
|
|
1641
|
+
provider: "openai",
|
|
1642
|
+
modality: "speech",
|
|
1643
|
+
currency: "USD",
|
|
1644
|
+
asOf: "2026-07-27",
|
|
1645
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1646
|
+
provenance: "litellm-catalog",
|
|
1647
|
+
rates: [
|
|
1648
|
+
{
|
|
1649
|
+
unit: "per_character",
|
|
1650
|
+
amount: 15e-6
|
|
1651
|
+
}
|
|
1652
|
+
],
|
|
1653
|
+
notes: "$15 per 1M characters."
|
|
1654
|
+
},
|
|
1655
|
+
{
|
|
1656
|
+
model: "tts-1-hd",
|
|
1657
|
+
provider: "openai",
|
|
1658
|
+
modality: "speech",
|
|
1659
|
+
currency: "USD",
|
|
1660
|
+
asOf: "2026-07-27",
|
|
1661
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1662
|
+
provenance: "litellm-catalog",
|
|
1663
|
+
rates: [
|
|
1664
|
+
{
|
|
1665
|
+
unit: "per_character",
|
|
1666
|
+
amount: 3e-5
|
|
1667
|
+
}
|
|
1668
|
+
],
|
|
1669
|
+
notes: "$30 per 1M characters."
|
|
1670
|
+
},
|
|
1671
|
+
{
|
|
1672
|
+
model: "whisper-1",
|
|
1673
|
+
provider: "openai",
|
|
1674
|
+
modality: "transcription",
|
|
1675
|
+
currency: "USD",
|
|
1676
|
+
asOf: "2026-07-27",
|
|
1677
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1678
|
+
provenance: "litellm-catalog",
|
|
1679
|
+
rates: [
|
|
1680
|
+
{
|
|
1681
|
+
unit: "per_audio_minute",
|
|
1682
|
+
amount: 6e-3
|
|
1683
|
+
}
|
|
1684
|
+
],
|
|
1685
|
+
notes: "litellm records $0.0001 per audio second; $0.006/minute is that rate x 60."
|
|
1686
|
+
}
|
|
1687
|
+
]
|
|
1688
|
+
};
|
|
1689
|
+
|
|
1690
|
+
// src/core/router/pricing.ts
|
|
1691
|
+
var DATASET = pricing_data_default;
|
|
1692
|
+
function normalizeForPricing(id) {
|
|
1693
|
+
let s = id.trim().toLowerCase();
|
|
1694
|
+
s = s.replace(/^models\//, "");
|
|
1695
|
+
const slash = s.lastIndexOf("/");
|
|
1696
|
+
if (slash !== -1) s = s.slice(slash + 1);
|
|
1697
|
+
const tag = s.search(/[:@]/);
|
|
1698
|
+
if (tag !== -1) s = s.slice(0, tag);
|
|
1699
|
+
s = s.replace(/-latest$/, "");
|
|
1700
|
+
s = s.replace(/-preview(?:-\d{2}-\d{2})?$/, "");
|
|
1701
|
+
s = s.replace(/-\d{4}-\d{2}-\d{2}$/, "");
|
|
1702
|
+
s = s.replace(/-\d{8}$/, "");
|
|
1703
|
+
s = s.replace(/-preview$/, "");
|
|
1704
|
+
return s;
|
|
1705
|
+
}
|
|
1706
|
+
function indexKey(model, provider, region) {
|
|
1707
|
+
return `${provider}|${normalizeForPricing(model)}|${region ?? ""}`;
|
|
1708
|
+
}
|
|
1709
|
+
var INDEX = /* @__PURE__ */ new Map();
|
|
1710
|
+
for (const entry of DATASET.entries) {
|
|
1711
|
+
for (const name of [entry.model, ...entry.aliases ?? []]) {
|
|
1712
|
+
const withRegion = indexKey(name, entry.provider, entry.region);
|
|
1713
|
+
if (!INDEX.has(withRegion)) INDEX.set(withRegion, entry);
|
|
1714
|
+
const bare = indexKey(name, entry.provider);
|
|
1715
|
+
const existing = INDEX.get(bare);
|
|
1716
|
+
if (!existing || existing.region && existing.region !== "global" && entry.region === "global") {
|
|
1717
|
+
INDEX.set(bare, entry);
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
function findPricing(modelId, provider, opts = {}) {
|
|
1722
|
+
const ids = [modelId, opts.baseModelId].filter((x) => !!x);
|
|
1723
|
+
const providers = [provider, ...opts.fallbackProviders ?? []];
|
|
1724
|
+
for (const p of providers) {
|
|
1725
|
+
for (const id of ids) {
|
|
1726
|
+
const hit = (opts.region ? INDEX.get(indexKey(id, p, opts.region)) : void 0) ?? INDEX.get(indexKey(id, p));
|
|
1727
|
+
if (hit) return { entry: hit, matchedProvider: p, viaFallback: p !== provider };
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
return null;
|
|
1731
|
+
}
|
|
1732
|
+
function tierFor(entry, inputTokens = 0) {
|
|
1733
|
+
const tiers = entry.tiers;
|
|
1734
|
+
if (!tiers?.length) return null;
|
|
1735
|
+
for (const t of tiers) {
|
|
1736
|
+
if (t.maxInputTokens === void 0 || inputTokens <= t.maxInputTokens) return t;
|
|
1737
|
+
}
|
|
1738
|
+
return tiers[tiers.length - 1] ?? null;
|
|
1739
|
+
}
|
|
1740
|
+
var PER_1M_TO_PER_1K = 1 / 1e3;
|
|
1741
|
+
function tokenRatesPer1k(entry, opts = {}) {
|
|
1742
|
+
const tier = tierFor(entry, opts.inputTokens ?? 0);
|
|
1743
|
+
if (!tier) return null;
|
|
1744
|
+
const mult = (opts.batch ? entry.batchMultiplier ?? 1 : 1) * PER_1M_TO_PER_1K;
|
|
1745
|
+
const out = {
|
|
1746
|
+
input: tier.inputPer1m * mult,
|
|
1747
|
+
output: tier.outputPer1m * mult
|
|
1748
|
+
};
|
|
1749
|
+
if (tier.cachedInputPer1m !== void 0) out.cachedInput = tier.cachedInputPer1m * mult;
|
|
1750
|
+
if (tier.cacheWritePer1m !== void 0) out.cacheWrite = tier.cacheWritePer1m * mult;
|
|
1751
|
+
if (tier.reasoningOutputPer1m !== void 0) out.reasoningOutput = tier.reasoningOutputPer1m * mult;
|
|
1752
|
+
return out;
|
|
1753
|
+
}
|
|
1754
|
+
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]"]);
|
|
1755
|
+
function isLoopbackOrPrivateHost(url) {
|
|
1756
|
+
if (!url) return false;
|
|
1757
|
+
try {
|
|
1758
|
+
const host = new URL(url).hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
1759
|
+
if (LOOPBACK_HOSTS.has(host) || LOOPBACK_HOSTS.has(`[${host}]`)) return true;
|
|
1760
|
+
if (host.endsWith(".local")) return true;
|
|
1761
|
+
if (/^127\./.test(host)) return true;
|
|
1762
|
+
if (/^10\./.test(host)) return true;
|
|
1763
|
+
if (/^192\.168\./.test(host)) return true;
|
|
1764
|
+
if (/^172\.(1[6-9]|2\d|3[01])\./.test(host)) return true;
|
|
1765
|
+
return false;
|
|
1766
|
+
} catch {
|
|
1767
|
+
return false;
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
function isLocalEndpoint(cfg) {
|
|
1771
|
+
if (typeof cfg.local === "boolean") return cfg.local;
|
|
1772
|
+
if (cfg.type === "ollama") return true;
|
|
1773
|
+
if (cfg.type === "openai-compatible") return isLoopbackOrPrivateHost(cfg.baseUrl);
|
|
1774
|
+
return false;
|
|
1775
|
+
}
|
|
1776
|
+
var UNKNOWN = { input: 0, output: 0, unknown: true, free: false };
|
|
1777
|
+
var FREE = { input: 0, output: 0, unknown: false, free: true };
|
|
1778
|
+
function resolvePricing(model, opts = {}) {
|
|
1779
|
+
if (model.isLocal) return FREE;
|
|
1780
|
+
const fallbackProviders = model.provider === "azure" ? ["openai"] : model.provider === "gemini" ? ["vertex"] : [];
|
|
1781
|
+
const match = findPricing(model.id, model.provider, {
|
|
1782
|
+
region: opts.region,
|
|
1783
|
+
baseModelId: model.baseModelId,
|
|
1784
|
+
fallbackProviders
|
|
1785
|
+
});
|
|
1786
|
+
if (!match) return UNKNOWN;
|
|
1787
|
+
const rates = tokenRatesPer1k(match.entry, { inputTokens: opts.inputTokens, batch: opts.batch });
|
|
1788
|
+
if (!rates) return UNKNOWN;
|
|
1789
|
+
return {
|
|
1790
|
+
input: rates.input,
|
|
1791
|
+
output: rates.output,
|
|
1792
|
+
unknown: false,
|
|
1793
|
+
free: false,
|
|
1794
|
+
entry: match.entry,
|
|
1795
|
+
...match.viaFallback ? { estimatedFromProvider: match.matchedProvider } : {}
|
|
1796
|
+
};
|
|
1797
|
+
}
|
|
1798
|
+
function withResolvedPricing(model, opts = {}) {
|
|
1799
|
+
const p = resolvePricing(model, opts);
|
|
1800
|
+
return {
|
|
1801
|
+
...model,
|
|
1802
|
+
inputCostPer1kTokens: p.input,
|
|
1803
|
+
outputCostPer1kTokens: p.output,
|
|
1804
|
+
...p.unknown ? { pricingUnknown: true } : { pricingUnknown: false }
|
|
1805
|
+
};
|
|
1806
|
+
}
|
|
1807
|
+
function isPricingUnknown(model) {
|
|
1808
|
+
return model.pricingUnknown === true && !model.isLocal;
|
|
1809
|
+
}
|
|
1810
|
+
var BLENDED_COST_CEILING = 0.05;
|
|
1811
|
+
function blendedCostPer1k(model) {
|
|
1812
|
+
if (isPricingUnknown(model)) return BLENDED_COST_CEILING;
|
|
1813
|
+
return model.inputCostPer1kTokens + model.outputCostPer1kTokens * 2;
|
|
1814
|
+
}
|
|
1815
|
+
var DISAGREEMENT_THRESHOLD = 0.02;
|
|
1816
|
+
function reconcilePrice(model, live) {
|
|
1817
|
+
const baseline = resolvePricing(model);
|
|
1818
|
+
const liveIsZero = !(live.input > 0) && !(live.output > 0);
|
|
1819
|
+
if (liveIsZero && !model.isLocal) {
|
|
1820
|
+
return { input: baseline.input, output: baseline.output, accepted: false };
|
|
1821
|
+
}
|
|
1822
|
+
if (baseline.unknown || !baseline.entry) {
|
|
1823
|
+
return { input: live.input, output: live.output, accepted: true };
|
|
1824
|
+
}
|
|
1825
|
+
const dsBlend = baseline.input + baseline.output * 2;
|
|
1826
|
+
const liveBlend = live.input + live.output * 2;
|
|
1827
|
+
const ratio = dsBlend > 0 ? liveBlend / dsBlend : Number.POSITIVE_INFINITY;
|
|
1828
|
+
const material = dsBlend > 0 && Math.abs(ratio - 1) > DISAGREEMENT_THRESHOLD;
|
|
1829
|
+
return {
|
|
1830
|
+
input: live.input,
|
|
1831
|
+
output: live.output,
|
|
1832
|
+
accepted: true,
|
|
1833
|
+
...material ? {
|
|
1834
|
+
disagreement: {
|
|
1835
|
+
modelId: model.id,
|
|
1836
|
+
provider: model.provider,
|
|
1837
|
+
dataset: { input: baseline.input, output: baseline.output },
|
|
1838
|
+
live: { input: live.input, output: live.output },
|
|
1839
|
+
ratio: Math.round(ratio * 1e3) / 1e3,
|
|
1840
|
+
datasetAsOf: baseline.entry.asOf,
|
|
1841
|
+
datasetSource: baseline.entry.source
|
|
1842
|
+
}
|
|
1843
|
+
} : {}
|
|
1844
|
+
};
|
|
1845
|
+
}
|
|
1846
|
+
|
|
195
1847
|
// src/constants.ts
|
|
196
|
-
var CASCADE_VERSION = "0.
|
|
1848
|
+
var CASCADE_VERSION = "0.50.0";
|
|
197
1849
|
var CASCADE_CONFIG_DIR = ".cascade";
|
|
198
1850
|
var CASCADE_MD_FILE = "CASCADE.md";
|
|
199
1851
|
var CASCADE_IGNORE_FILE = ".cascadeignore";
|
|
@@ -542,6 +2194,13 @@ var MODELS = {
|
|
|
542
2194
|
for (const _m of Object.values(MODELS)) {
|
|
543
2195
|
if (_m.supportsToolUse === void 0) _m.supportsToolUse = !_m.isLocal;
|
|
544
2196
|
}
|
|
2197
|
+
for (const _m of Object.values(MODELS)) {
|
|
2198
|
+
const priced = resolvePricing(_m);
|
|
2199
|
+
if (priced.unknown || priced.free || priced.estimatedFromProvider) continue;
|
|
2200
|
+
_m.inputCostPer1kTokens = priced.input;
|
|
2201
|
+
_m.outputCostPer1kTokens = priced.output;
|
|
2202
|
+
_m.pricingUnknown = false;
|
|
2203
|
+
}
|
|
545
2204
|
var T1_MODEL_PRIORITY = [
|
|
546
2205
|
"gpt-5.5",
|
|
547
2206
|
"gpt-5.4",
|
|
@@ -632,21 +2291,49 @@ var PROVIDER_DISPLAY_NAMES = {
|
|
|
632
2291
|
|
|
633
2292
|
// src/utils/cost.ts
|
|
634
2293
|
function resolveModelPricing(model) {
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
2294
|
+
if (model.isLocal) return { input: 0, output: 0, unknown: false };
|
|
2295
|
+
if (model.inputCostPer1kTokens > 0 || model.outputCostPer1kTokens > 0) {
|
|
2296
|
+
return {
|
|
2297
|
+
input: model.inputCostPer1kTokens,
|
|
2298
|
+
output: model.outputCostPer1kTokens,
|
|
2299
|
+
unknown: false
|
|
2300
|
+
};
|
|
2301
|
+
}
|
|
2302
|
+
const fromDataset = resolvePricing(model);
|
|
2303
|
+
if (!fromDataset.unknown) {
|
|
2304
|
+
return {
|
|
2305
|
+
input: fromDataset.input,
|
|
2306
|
+
output: fromDataset.output,
|
|
2307
|
+
unknown: false,
|
|
2308
|
+
...fromDataset.estimatedFromProvider ? { estimatedFromProvider: fromDataset.estimatedFromProvider } : {}
|
|
2309
|
+
};
|
|
2310
|
+
}
|
|
2311
|
+
if (model.id) {
|
|
638
2312
|
const known = Object.values(MODELS).find((m) => m.id === model.id && !m.isLocal);
|
|
639
|
-
if (known) {
|
|
640
|
-
|
|
641
|
-
|
|
2313
|
+
if (known && (known.inputCostPer1kTokens > 0 || known.outputCostPer1kTokens > 0)) {
|
|
2314
|
+
return {
|
|
2315
|
+
input: known.inputCostPer1kTokens,
|
|
2316
|
+
output: known.outputCostPer1kTokens,
|
|
2317
|
+
unknown: false
|
|
2318
|
+
};
|
|
642
2319
|
}
|
|
643
2320
|
}
|
|
644
|
-
return { input, output };
|
|
2321
|
+
return { input: 0, output: 0, unknown: true };
|
|
645
2322
|
}
|
|
646
2323
|
function calculateCost(inputTokens, outputTokens, model) {
|
|
647
2324
|
const { input, output } = resolveModelPricing(model);
|
|
648
2325
|
return inputTokens / 1e3 * input + outputTokens / 1e3 * output;
|
|
649
2326
|
}
|
|
2327
|
+
function buildTokenUsage(inputTokens, outputTokens, model) {
|
|
2328
|
+
const { input, output, unknown } = resolveModelPricing(model);
|
|
2329
|
+
return {
|
|
2330
|
+
inputTokens,
|
|
2331
|
+
outputTokens,
|
|
2332
|
+
totalTokens: inputTokens + outputTokens,
|
|
2333
|
+
estimatedCostUsd: inputTokens / 1e3 * input + outputTokens / 1e3 * output,
|
|
2334
|
+
...unknown ? { costUnknown: true } : {}
|
|
2335
|
+
};
|
|
2336
|
+
}
|
|
650
2337
|
|
|
651
2338
|
// src/providers/base.ts
|
|
652
2339
|
var BaseProvider = class {
|
|
@@ -666,12 +2353,7 @@ var BaseProvider = class {
|
|
|
666
2353
|
return calculateCost(inputTokens, outputTokens, this.model);
|
|
667
2354
|
}
|
|
668
2355
|
makeUsage(inputTokens, outputTokens) {
|
|
669
|
-
return
|
|
670
|
-
inputTokens,
|
|
671
|
-
outputTokens,
|
|
672
|
-
totalTokens: inputTokens + outputTokens,
|
|
673
|
-
estimatedCostUsd: this.estimateCost(inputTokens, outputTokens)
|
|
674
|
-
};
|
|
2356
|
+
return buildTokenUsage(inputTokens, outputTokens, this.model);
|
|
675
2357
|
}
|
|
676
2358
|
};
|
|
677
2359
|
|
|
@@ -840,7 +2522,7 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
840
2522
|
return data.data.filter((m) => isChatModel(m.id)).map((m) => {
|
|
841
2523
|
const known = Object.values(MODELS).find((km) => km.id === m.id && km.provider === "anthropic");
|
|
842
2524
|
if (known) return known;
|
|
843
|
-
return {
|
|
2525
|
+
return withResolvedPricing({
|
|
844
2526
|
id: m.id,
|
|
845
2527
|
name: m.display_name || m.id,
|
|
846
2528
|
provider: "anthropic",
|
|
@@ -851,7 +2533,7 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
851
2533
|
maxOutputTokens: 8e3,
|
|
852
2534
|
supportsStreaming: true,
|
|
853
2535
|
isLocal: false
|
|
854
|
-
};
|
|
2536
|
+
});
|
|
855
2537
|
});
|
|
856
2538
|
} catch {
|
|
857
2539
|
return Object.values(MODELS).filter((m) => m.provider === "anthropic");
|
|
@@ -1055,7 +2737,7 @@ var OpenAIProvider = class extends BaseProvider {
|
|
|
1055
2737
|
return response.data.filter((m) => isChatModel(m.id)).map((m) => {
|
|
1056
2738
|
const known = Object.values(MODELS).find((km) => km.id === m.id && km.provider === "openai");
|
|
1057
2739
|
if (known) return known;
|
|
1058
|
-
return {
|
|
2740
|
+
return withResolvedPricing({
|
|
1059
2741
|
id: m.id,
|
|
1060
2742
|
name: m.id,
|
|
1061
2743
|
provider: "openai",
|
|
@@ -1066,7 +2748,7 @@ var OpenAIProvider = class extends BaseProvider {
|
|
|
1066
2748
|
maxOutputTokens: 4e3,
|
|
1067
2749
|
supportsStreaming: true,
|
|
1068
2750
|
isLocal: false
|
|
1069
|
-
};
|
|
2751
|
+
});
|
|
1070
2752
|
});
|
|
1071
2753
|
} catch {
|
|
1072
2754
|
return Object.values(MODELS).filter((m) => m.provider === "openai");
|
|
@@ -1162,6 +2844,10 @@ function azureModelForDeployment(cfg) {
|
|
|
1162
2844
|
const baseModelId = cfg.model?.trim() || inferAzureBaseModel(id) || void 0;
|
|
1163
2845
|
const base = baseModelId ? MODELS[baseModelId] : void 0;
|
|
1164
2846
|
if (base) {
|
|
2847
|
+
const azurePrice = resolvePricing(
|
|
2848
|
+
{ id, provider: "azure", isLocal: false, baseModelId },
|
|
2849
|
+
{ region: cfg.region }
|
|
2850
|
+
);
|
|
1165
2851
|
return {
|
|
1166
2852
|
...base,
|
|
1167
2853
|
id,
|
|
@@ -1170,7 +2856,12 @@ function azureModelForDeployment(cfg) {
|
|
|
1170
2856
|
provider: "azure",
|
|
1171
2857
|
baseModelId,
|
|
1172
2858
|
// real identity for benchmark + live pricing
|
|
1173
|
-
supportsToolUse: base.supportsToolUse ?? true
|
|
2859
|
+
supportsToolUse: base.supportsToolUse ?? true,
|
|
2860
|
+
...azurePrice.unknown ? {} : {
|
|
2861
|
+
inputCostPer1kTokens: azurePrice.input,
|
|
2862
|
+
outputCostPer1kTokens: azurePrice.output,
|
|
2863
|
+
pricingUnknown: false
|
|
2864
|
+
}
|
|
1174
2865
|
};
|
|
1175
2866
|
}
|
|
1176
2867
|
return {
|
|
@@ -1333,7 +3024,7 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
1333
3024
|
(km) => km.id === id && km.provider === "gemini"
|
|
1334
3025
|
);
|
|
1335
3026
|
if (known) return known;
|
|
1336
|
-
return {
|
|
3027
|
+
return withResolvedPricing({
|
|
1337
3028
|
id,
|
|
1338
3029
|
name: m.displayName || id,
|
|
1339
3030
|
provider: "gemini",
|
|
@@ -1344,7 +3035,7 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
1344
3035
|
maxOutputTokens: m.outputTokenLimit || 8e3,
|
|
1345
3036
|
supportsStreaming: true,
|
|
1346
3037
|
isLocal: false
|
|
1347
|
-
};
|
|
3038
|
+
});
|
|
1348
3039
|
});
|
|
1349
3040
|
} catch {
|
|
1350
3041
|
return Object.values(MODELS).filter((m) => m.provider === "gemini");
|
|
@@ -1691,9 +3382,10 @@ var OllamaProvider = class extends BaseProvider {
|
|
|
1691
3382
|
return supportedKeywords.some((k) => name.includes(k)) && isChatModel(name);
|
|
1692
3383
|
});
|
|
1693
3384
|
const shows = await Promise.all(entries.map((m) => this.showModel(m.name)));
|
|
3385
|
+
const local = isLocalEndpoint({ ...this.config, type: "ollama" });
|
|
1694
3386
|
return entries.map((m, i) => {
|
|
1695
3387
|
const show = shows[i];
|
|
1696
|
-
return {
|
|
3388
|
+
return withResolvedPricing({
|
|
1697
3389
|
id: m.name,
|
|
1698
3390
|
name: m.name,
|
|
1699
3391
|
provider: "ollama",
|
|
@@ -1703,10 +3395,10 @@ var OllamaProvider = class extends BaseProvider {
|
|
|
1703
3395
|
outputCostPer1kTokens: 0,
|
|
1704
3396
|
maxOutputTokens: 4e3,
|
|
1705
3397
|
supportsStreaming: true,
|
|
1706
|
-
isLocal:
|
|
3398
|
+
isLocal: local,
|
|
1707
3399
|
supportsToolUse: show?.tools ?? isToolCapable(m.name),
|
|
1708
3400
|
minSizeB: this.parseSizeB(m.details?.parameter_size)
|
|
1709
|
-
};
|
|
3401
|
+
});
|
|
1710
3402
|
});
|
|
1711
3403
|
} catch {
|
|
1712
3404
|
return [];
|
|
@@ -1809,7 +3501,8 @@ var OpenAICompatibleProvider = class extends OpenAIProvider {
|
|
|
1809
3501
|
}).filter((x) => typeof x === "string" && x.length > 0);
|
|
1810
3502
|
if (ids.length === 0) return [this.model];
|
|
1811
3503
|
const chatIds = ids.filter((id) => isChatModel(id));
|
|
1812
|
-
|
|
3504
|
+
const local = isLocalEndpoint(this.config);
|
|
3505
|
+
return (chatIds.length ? chatIds : ids).map((id) => withResolvedPricing({
|
|
1813
3506
|
id,
|
|
1814
3507
|
name: id,
|
|
1815
3508
|
provider: "openai-compatible",
|
|
@@ -1819,7 +3512,7 @@ var OpenAICompatibleProvider = class extends OpenAIProvider {
|
|
|
1819
3512
|
outputCostPer1kTokens: 0,
|
|
1820
3513
|
maxOutputTokens: 4e3,
|
|
1821
3514
|
supportsStreaming: true,
|
|
1822
|
-
isLocal:
|
|
3515
|
+
isLocal: local
|
|
1823
3516
|
}));
|
|
1824
3517
|
}
|
|
1825
3518
|
async isAvailable() {
|
|
@@ -2034,7 +3727,7 @@ var ModelSelector = class {
|
|
|
2034
3727
|
else if (this.availableProviders.size === 1) providerStr = Array.from(this.availableProviders)[0];
|
|
2035
3728
|
}
|
|
2036
3729
|
if (providerStr && this.availableProviders.has(providerStr)) {
|
|
2037
|
-
const dynamicModel = {
|
|
3730
|
+
const dynamicModel = withResolvedPricing({
|
|
2038
3731
|
id: actualId,
|
|
2039
3732
|
name: actualId,
|
|
2040
3733
|
provider: providerStr,
|
|
@@ -2045,7 +3738,7 @@ var ModelSelector = class {
|
|
|
2045
3738
|
maxOutputTokens: 8e3,
|
|
2046
3739
|
supportsStreaming: true,
|
|
2047
3740
|
isLocal: providerStr === "ollama"
|
|
2048
|
-
};
|
|
3741
|
+
});
|
|
2049
3742
|
this.addDynamicModel(dynamicModel);
|
|
2050
3743
|
return dynamicModel;
|
|
2051
3744
|
}
|
|
@@ -2437,6 +4130,8 @@ var LiveDataProvider = class {
|
|
|
2437
4130
|
snapshot = null;
|
|
2438
4131
|
prices = /* @__PURE__ */ new Map();
|
|
2439
4132
|
capabilities = /* @__PURE__ */ new Map();
|
|
4133
|
+
/** Dataset-vs-live price mismatches, deduped by `provider:modelId`. */
|
|
4134
|
+
disagreements = /* @__PURE__ */ new Map();
|
|
2440
4135
|
source = "bundled";
|
|
2441
4136
|
fetchedAt = 0;
|
|
2442
4137
|
loaded = false;
|
|
@@ -2590,14 +4285,45 @@ var LiveDataProvider = class {
|
|
|
2590
4285
|
/**
|
|
2591
4286
|
* Returns a price-corrected copy of each model when live pricing is known,
|
|
2592
4287
|
* leaving the original untouched (so the shared catalog is never mutated).
|
|
4288
|
+
*
|
|
4289
|
+
* Reconciliation policy (see reconcilePrice in pricing.ts): the bundled
|
|
4290
|
+
* dataset is the authoritative baseline, and a live quote overrides it
|
|
4291
|
+
* because it is fresher by construction — but the disagreement is RECORDED,
|
|
4292
|
+
* not swallowed. A dataset/live mismatch is the best signal available that
|
|
4293
|
+
* the committed prices have gone stale, so it must be visible rather than
|
|
4294
|
+
* silently resolved in either direction. A live quote of $0 for a non-local
|
|
4295
|
+
* model is rejected outright: that is the very failure mode this change
|
|
4296
|
+
* exists to prevent, and a marketplace's free-tier alias must never zero out
|
|
4297
|
+
* the price of the paid model Cascade is actually calling.
|
|
2593
4298
|
*/
|
|
2594
4299
|
applyLivePricing(models) {
|
|
2595
4300
|
return models.map((m) => {
|
|
2596
4301
|
const p = this.getLivePrice(m.baseModelId ?? m.id);
|
|
2597
4302
|
if (!p) return m;
|
|
2598
|
-
|
|
4303
|
+
const reconciled = reconcilePrice(m, p);
|
|
4304
|
+
if (reconciled.disagreement) this.recordDisagreement(reconciled.disagreement);
|
|
4305
|
+
if (!reconciled.accepted) return m;
|
|
4306
|
+
return {
|
|
4307
|
+
...m,
|
|
4308
|
+
inputCostPer1kTokens: reconciled.input,
|
|
4309
|
+
outputCostPer1kTokens: reconciled.output,
|
|
4310
|
+
pricingUnknown: false
|
|
4311
|
+
};
|
|
2599
4312
|
});
|
|
2600
4313
|
}
|
|
4314
|
+
recordDisagreement(d) {
|
|
4315
|
+
const key = `${d.provider}:${d.modelId}`;
|
|
4316
|
+
if (this.disagreements.has(key)) return;
|
|
4317
|
+
this.disagreements.set(key, d);
|
|
4318
|
+
}
|
|
4319
|
+
/**
|
|
4320
|
+
* Dataset-vs-live price mismatches seen this session, newest-model-first
|
|
4321
|
+
* insertion order. Surfaced by `cascade models --verbose`; a non-empty list
|
|
4322
|
+
* means src/core/router/pricing-data.json is probably due a refresh.
|
|
4323
|
+
*/
|
|
4324
|
+
getPriceDisagreements() {
|
|
4325
|
+
return [...this.disagreements.values()];
|
|
4326
|
+
}
|
|
2601
4327
|
/** Current capability facts for a model id, or null when unknown. */
|
|
2602
4328
|
getCapability(modelId) {
|
|
2603
4329
|
return this.capabilities.get(normalizeModelId2(modelId)) ?? null;
|
|
@@ -2794,7 +4520,9 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
|
|
|
2794
4520
|
tokensByTier: {},
|
|
2795
4521
|
inputTokensByTier: {},
|
|
2796
4522
|
outputTokensByTier: {},
|
|
2797
|
-
costByFeature: {}
|
|
4523
|
+
costByFeature: {},
|
|
4524
|
+
untrackedCostCalls: 0,
|
|
4525
|
+
untrackedCostModels: []
|
|
2798
4526
|
};
|
|
2799
4527
|
tierModels = /* @__PURE__ */ new Map();
|
|
2800
4528
|
/**
|
|
@@ -3144,7 +4872,7 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
|
|
|
3144
4872
|
`Model ${model.id} inference timed out after ${cloudTimeoutMs}ms`
|
|
3145
4873
|
);
|
|
3146
4874
|
}
|
|
3147
|
-
const
|
|
4875
|
+
const corrected = buildTokenUsage(
|
|
3148
4876
|
result.usage.inputTokens,
|
|
3149
4877
|
result.usage.outputTokens,
|
|
3150
4878
|
model
|
|
@@ -3153,7 +4881,8 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
|
|
|
3153
4881
|
...result,
|
|
3154
4882
|
usage: {
|
|
3155
4883
|
...result.usage,
|
|
3156
|
-
estimatedCostUsd:
|
|
4884
|
+
estimatedCostUsd: corrected.estimatedCostUsd,
|
|
4885
|
+
...corrected.costUnknown ? { costUnknown: true } : { costUnknown: void 0 }
|
|
3157
4886
|
}
|
|
3158
4887
|
};
|
|
3159
4888
|
if (!result || typeof result.content !== "string" || !result.usage) {
|
|
@@ -3340,7 +5069,9 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
|
|
|
3340
5069
|
tokensByTier: { ...this.stats.tokensByTier },
|
|
3341
5070
|
inputTokensByTier: { ...this.stats.inputTokensByTier },
|
|
3342
5071
|
outputTokensByTier: { ...this.stats.outputTokensByTier },
|
|
3343
|
-
costByFeature: { ...this.stats.costByFeature }
|
|
5072
|
+
costByFeature: { ...this.stats.costByFeature },
|
|
5073
|
+
untrackedCostCalls: this.stats.untrackedCostCalls,
|
|
5074
|
+
untrackedCostModels: [...this.stats.untrackedCostModels]
|
|
3344
5075
|
};
|
|
3345
5076
|
}
|
|
3346
5077
|
/**
|
|
@@ -3391,7 +5122,9 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
|
|
|
3391
5122
|
tokensByTier: {},
|
|
3392
5123
|
inputTokensByTier: {},
|
|
3393
5124
|
outputTokensByTier: {},
|
|
3394
|
-
costByFeature: {}
|
|
5125
|
+
costByFeature: {},
|
|
5126
|
+
untrackedCostCalls: 0,
|
|
5127
|
+
untrackedCostModels: []
|
|
3395
5128
|
};
|
|
3396
5129
|
this.sessionCostUsd = 0;
|
|
3397
5130
|
this.budgetState = "ok";
|
|
@@ -3576,6 +5309,18 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
|
|
|
3576
5309
|
if (featureTag) {
|
|
3577
5310
|
this.stats.costByFeature[featureTag] = (this.stats.costByFeature[featureTag] ?? 0) + usage.estimatedCostUsd;
|
|
3578
5311
|
}
|
|
5312
|
+
if (usage.costUnknown) {
|
|
5313
|
+
this.stats.untrackedCostCalls += 1;
|
|
5314
|
+
if (!this.stats.untrackedCostModels.includes(model.id)) {
|
|
5315
|
+
this.stats.untrackedCostModels.push(model.id);
|
|
5316
|
+
const capped = this.config?.budget?.maxCostPerRunUsd != null || this.config?.budget?.sessionBudgetUsd != null;
|
|
5317
|
+
this.emit("cost:untracked", {
|
|
5318
|
+
modelId: model.id,
|
|
5319
|
+
provider: model.provider,
|
|
5320
|
+
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.`
|
|
5321
|
+
});
|
|
5322
|
+
}
|
|
5323
|
+
}
|
|
3579
5324
|
this.runTokens += usage.totalTokens;
|
|
3580
5325
|
this.runCostUsd += usage.estimatedCostUsd;
|
|
3581
5326
|
this.updateBudgetState();
|
|
@@ -3716,6 +5461,14 @@ var BaseTier = class extends EventEmitter {
|
|
|
3716
5461
|
this.emit("tier:status", event);
|
|
3717
5462
|
}
|
|
3718
5463
|
/** Record the model serving this tier; future status events carry it. */
|
|
5464
|
+
/**
|
|
5465
|
+
* The model actually serving this tier ("provider:id"), once one has been
|
|
5466
|
+
* chosen. The run breaker reads it to attribute a failure to a model rather
|
|
5467
|
+
* than to the subtask that happened to hit it.
|
|
5468
|
+
*/
|
|
5469
|
+
getServingModel() {
|
|
5470
|
+
return this.servingModel;
|
|
5471
|
+
}
|
|
3719
5472
|
setServingModel(model) {
|
|
3720
5473
|
this.servingModel = model || void 0;
|
|
3721
5474
|
}
|
|
@@ -5451,6 +7204,7 @@ var T2Manager = class extends BaseTier {
|
|
|
5451
7204
|
escalations = [];
|
|
5452
7205
|
peerSyncBuffer = [];
|
|
5453
7206
|
store;
|
|
7207
|
+
runBreaker;
|
|
5454
7208
|
t3PeerBus = new PeerBus();
|
|
5455
7209
|
// ← T3↔T3 bus (local to this T2)
|
|
5456
7210
|
t2PeerBus;
|
|
@@ -5481,6 +7235,15 @@ var T2Manager = class extends BaseTier {
|
|
|
5481
7235
|
this.router = router;
|
|
5482
7236
|
this.toolRegistry = toolRegistry;
|
|
5483
7237
|
}
|
|
7238
|
+
/**
|
|
7239
|
+
* Share the run-wide circuit breaker. Every T2 in a run gets the SAME
|
|
7240
|
+
* instance: three sections each failing once against a dead model is the
|
|
7241
|
+
* same fact as one section failing three times, and a per-section breaker
|
|
7242
|
+
* would never see it.
|
|
7243
|
+
*/
|
|
7244
|
+
setRunBreaker(breaker) {
|
|
7245
|
+
this.runBreaker = breaker;
|
|
7246
|
+
}
|
|
5484
7247
|
setStore(store) {
|
|
5485
7248
|
this.store = store;
|
|
5486
7249
|
}
|
|
@@ -5604,7 +7367,8 @@ Guidance (must be followed): ${decision.note}`
|
|
|
5604
7367
|
const overallStatus = this.determineStatus(t3Results);
|
|
5605
7368
|
const isOk = overallStatus === "COMPLETED" || overallStatus === "PARTIAL";
|
|
5606
7369
|
this.setStatus(isOk ? "COMPLETED" : "FAILED", summary);
|
|
5607
|
-
|
|
7370
|
+
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";
|
|
7371
|
+
this.sendStatusUpdate({ progressPct: 100, currentAction: sectionAction, status: "IN_PROGRESS", output: summary });
|
|
5608
7372
|
const result = {
|
|
5609
7373
|
sectionId: assignment.sectionId,
|
|
5610
7374
|
sectionTitle: assignment.sectionTitle,
|
|
@@ -5774,6 +7538,24 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
|
5774
7538
|
const reinforceCfg = this.router.getReinforcementsConfig?.() ?? { enabled: false, maxPerSection: 4 };
|
|
5775
7539
|
let reinforcementsAdded = 0;
|
|
5776
7540
|
while (remaining.size > 0) {
|
|
7541
|
+
if (this.runBreaker?.isOpen()) {
|
|
7542
|
+
const why = this.runBreaker.skipMessage();
|
|
7543
|
+
this.log(`Run breaker open \u2014 skipping ${remaining.size} remaining subtask(s)`);
|
|
7544
|
+
for (const id of remaining) {
|
|
7545
|
+
this.t3PeerBus.publish(this.id, id, why, "FAILED");
|
|
7546
|
+
resultMap.set(id, {
|
|
7547
|
+
subtaskId: id,
|
|
7548
|
+
status: "FAILED",
|
|
7549
|
+
output: why,
|
|
7550
|
+
testResults: { checksRun: [], passed: [], failed: [] },
|
|
7551
|
+
issues: [why],
|
|
7552
|
+
peerSyncsUsed: [],
|
|
7553
|
+
correctionAttempts: 0
|
|
7554
|
+
});
|
|
7555
|
+
}
|
|
7556
|
+
remaining.clear();
|
|
7557
|
+
break;
|
|
7558
|
+
}
|
|
5777
7559
|
const runnableIds = [...remaining].filter((id) => (inDegree.get(id) ?? 0) === 0);
|
|
5778
7560
|
if (runnableIds.length === 0) {
|
|
5779
7561
|
const fallbackId = [...remaining].sort(
|
|
@@ -5876,8 +7658,32 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
|
5876
7658
|
remaining.delete(id);
|
|
5877
7659
|
const r = waveResults[i];
|
|
5878
7660
|
if (r.status === "rejected") {
|
|
5879
|
-
this.log(`T3 worker ${id} failed: ${r.reason instanceof Error ? r.reason.message : String(r.reason)} \u2014 retrying once`);
|
|
5880
7661
|
const assignment = sanitizedAssignments.find((a) => a.subtaskId === id);
|
|
7662
|
+
const failedModel = workerMap.get(id)?.getServingModel();
|
|
7663
|
+
const tripped = this.runBreaker?.record(r.reason, failedModel);
|
|
7664
|
+
if (tripped) {
|
|
7665
|
+
this.log(`Run breaker opened: ${tripped.failures} consecutive ${tripped.kind} failures on ${tripped.modelId} \u2014 stopping`);
|
|
7666
|
+
this.emit("run:breaker", tripped);
|
|
7667
|
+
}
|
|
7668
|
+
if (this.runBreaker?.isOpen()) {
|
|
7669
|
+
const why = this.runBreaker.skipMessage();
|
|
7670
|
+
this.log(`T3 worker ${id} failed and the run breaker is open \u2014 not retrying`);
|
|
7671
|
+
this.t3PeerBus.publish(this.id, id, why, "FAILED");
|
|
7672
|
+
resultMap.set(id, {
|
|
7673
|
+
subtaskId: id,
|
|
7674
|
+
status: "FAILED",
|
|
7675
|
+
output: why,
|
|
7676
|
+
testResults: { checksRun: [], passed: [], failed: [] },
|
|
7677
|
+
issues: [why],
|
|
7678
|
+
peerSyncsUsed: [],
|
|
7679
|
+
correctionAttempts: 0
|
|
7680
|
+
});
|
|
7681
|
+
for (const dependent of adj.get(id) ?? []) {
|
|
7682
|
+
inDegree.set(dependent, Math.max(0, (inDegree.get(dependent) ?? 0) - 1));
|
|
7683
|
+
}
|
|
7684
|
+
continue;
|
|
7685
|
+
}
|
|
7686
|
+
this.log(`T3 worker ${id} failed: ${r.reason instanceof Error ? r.reason.message : String(r.reason)} \u2014 retrying once`);
|
|
5881
7687
|
try {
|
|
5882
7688
|
const retried = await this.retryT3(assignment, taskId);
|
|
5883
7689
|
resultMap.set(id, retried);
|
|
@@ -6209,6 +8015,7 @@ var T1Administrator = class extends BaseTier {
|
|
|
6209
8015
|
t2Managers = /* @__PURE__ */ new Map();
|
|
6210
8016
|
escalations = [];
|
|
6211
8017
|
store;
|
|
8018
|
+
runBreaker;
|
|
6212
8019
|
t2PeerBus = new PeerBus();
|
|
6213
8020
|
permissionEscalator;
|
|
6214
8021
|
toolCreator;
|
|
@@ -6223,6 +8030,10 @@ var T1Administrator = class extends BaseTier {
|
|
|
6223
8030
|
this.toolRegistry = toolRegistry;
|
|
6224
8031
|
this.config = config;
|
|
6225
8032
|
}
|
|
8033
|
+
/** Share the run-wide circuit breaker with every section manager. */
|
|
8034
|
+
setRunBreaker(breaker) {
|
|
8035
|
+
this.runBreaker = breaker;
|
|
8036
|
+
}
|
|
6226
8037
|
setStore(store) {
|
|
6227
8038
|
this.store = store;
|
|
6228
8039
|
}
|
|
@@ -6585,6 +8396,7 @@ SPEC RULES \u2014 each subtask is a self-contained spec slice (workers execute f
|
|
|
6585
8396
|
};
|
|
6586
8397
|
const managers = sections.map((section) => {
|
|
6587
8398
|
const manager = new T2Manager(this.router, this.toolRegistry, this.id);
|
|
8399
|
+
if (this.runBreaker) manager.setRunBreaker(this.runBreaker);
|
|
6588
8400
|
manager.setHierarchyContext(`You are a T2 Manager for the section "${section.sectionTitle}". You are part of a COMPLEX task overseen by T1 Administrator.`);
|
|
6589
8401
|
if (this.store) {
|
|
6590
8402
|
manager.setStore(this.store);
|
|
@@ -6870,6 +8682,158 @@ Reply with exactly one word: YES, NO, or UNSURE.
|
|
|
6870
8682
|
}
|
|
6871
8683
|
};
|
|
6872
8684
|
|
|
8685
|
+
// src/core/router/provider-errors.ts
|
|
8686
|
+
var SYSTEMIC = /* @__PURE__ */ new Set([
|
|
8687
|
+
"rate_limit",
|
|
8688
|
+
"quota_exhausted",
|
|
8689
|
+
"auth",
|
|
8690
|
+
"model_unavailable"
|
|
8691
|
+
]);
|
|
8692
|
+
function isSystemicKind(kind) {
|
|
8693
|
+
return SYSTEMIC.has(kind);
|
|
8694
|
+
}
|
|
8695
|
+
function statusOf(err) {
|
|
8696
|
+
if (!err || typeof err !== "object") return void 0;
|
|
8697
|
+
const e = err;
|
|
8698
|
+
for (const key of ["status", "statusCode", "code"]) {
|
|
8699
|
+
const v = e[key];
|
|
8700
|
+
if (typeof v === "number" && v >= 100 && v < 600) return v;
|
|
8701
|
+
}
|
|
8702
|
+
const resp = e["response"];
|
|
8703
|
+
if (resp && typeof resp["status"] === "number") return resp["status"];
|
|
8704
|
+
const inner = e["error"];
|
|
8705
|
+
if (inner && typeof inner["code"] === "number") return inner["code"];
|
|
8706
|
+
return void 0;
|
|
8707
|
+
}
|
|
8708
|
+
function messageOf(err) {
|
|
8709
|
+
if (err instanceof Error) return err.message;
|
|
8710
|
+
if (typeof err === "string") return err;
|
|
8711
|
+
if (err && typeof err === "object") {
|
|
8712
|
+
const e = err;
|
|
8713
|
+
if (typeof e["message"] === "string") return e["message"];
|
|
8714
|
+
const inner = e["error"];
|
|
8715
|
+
if (inner && typeof inner["message"] === "string") return inner["message"];
|
|
8716
|
+
try {
|
|
8717
|
+
return JSON.stringify(err);
|
|
8718
|
+
} catch {
|
|
8719
|
+
}
|
|
8720
|
+
}
|
|
8721
|
+
return String(err);
|
|
8722
|
+
}
|
|
8723
|
+
function classifyProviderError(err) {
|
|
8724
|
+
const raw = messageOf(err).trim();
|
|
8725
|
+
const status = statusOf(err);
|
|
8726
|
+
const m = raw.toLowerCase();
|
|
8727
|
+
const kind = (() => {
|
|
8728
|
+
if (status === 429) {
|
|
8729
|
+
return /quota|billing|credit|exceeded your current quota|insufficient/.test(m) ? "quota_exhausted" : "rate_limit";
|
|
8730
|
+
}
|
|
8731
|
+
if (status === 401 || status === 403) return "auth";
|
|
8732
|
+
if (status === 404) return "model_unavailable";
|
|
8733
|
+
if (/rate limit|too many requests|resource_exhausted/.test(m)) {
|
|
8734
|
+
return /quota|billing|credit|insufficient/.test(m) ? "quota_exhausted" : "rate_limit";
|
|
8735
|
+
}
|
|
8736
|
+
if (/quota|billing|insufficient_quota|insufficient credit|payment required/.test(m)) return "quota_exhausted";
|
|
8737
|
+
if (/api key|unauthorized|authentication|permission denied|invalid[_ ]api/.test(m)) return "auth";
|
|
8738
|
+
if (/model .*(not found|does not exist|unavailable)|no such model|unknown model|not supported for/.test(m)) {
|
|
8739
|
+
return "model_unavailable";
|
|
8740
|
+
}
|
|
8741
|
+
if (/context length|too many tokens|maximum context|prompt is too long|input too long/.test(m)) return "context_length";
|
|
8742
|
+
if (/safety|content filter|blocked|recitation|prohibited/.test(m)) return "content_filter";
|
|
8743
|
+
if (/econnreset|etimedout|enotfound|socket hang up|network|fetch failed|aborted/.test(m)) return "network";
|
|
8744
|
+
return "unknown";
|
|
8745
|
+
})();
|
|
8746
|
+
return { kind, systemic: isSystemicKind(kind), status, raw };
|
|
8747
|
+
}
|
|
8748
|
+
function describeProviderError(c, modelId) {
|
|
8749
|
+
const on = ` on ${modelId}` ;
|
|
8750
|
+
const detail = c.raw ? ` Provider said: ${c.raw}` : "";
|
|
8751
|
+
switch (c.kind) {
|
|
8752
|
+
case "rate_limit":
|
|
8753
|
+
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}`;
|
|
8754
|
+
case "quota_exhausted":
|
|
8755
|
+
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}`;
|
|
8756
|
+
case "auth":
|
|
8757
|
+
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}`;
|
|
8758
|
+
case "model_unavailable":
|
|
8759
|
+
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}`;
|
|
8760
|
+
case "context_length":
|
|
8761
|
+
return `The prompt was too long${on}. Reduce the attached context, or use a model with a larger context window for this tier.${detail}`;
|
|
8762
|
+
case "content_filter":
|
|
8763
|
+
return `The provider's safety filter refused this request${on}.${detail}`;
|
|
8764
|
+
case "network":
|
|
8765
|
+
return `Network error reaching the provider${on}. This is usually transient.${detail}`;
|
|
8766
|
+
default:
|
|
8767
|
+
return `The model call failed${on}.${detail}`;
|
|
8768
|
+
}
|
|
8769
|
+
}
|
|
8770
|
+
|
|
8771
|
+
// src/core/run-breaker.ts
|
|
8772
|
+
var DEFAULT_FAILURE_THRESHOLD = 3;
|
|
8773
|
+
var RunBreaker = class {
|
|
8774
|
+
threshold;
|
|
8775
|
+
/** modelId → consecutive systemic failures of the SAME kind. */
|
|
8776
|
+
counts = /* @__PURE__ */ new Map();
|
|
8777
|
+
trip = null;
|
|
8778
|
+
constructor(threshold = DEFAULT_FAILURE_THRESHOLD) {
|
|
8779
|
+
this.threshold = Math.max(1, threshold);
|
|
8780
|
+
}
|
|
8781
|
+
/**
|
|
8782
|
+
* Record a failed model call. Returns the trip if this failure is the one
|
|
8783
|
+
* that opened the breaker, otherwise null.
|
|
8784
|
+
*
|
|
8785
|
+
* A non-systemic failure RESETS the model's counter: it is evidence the model
|
|
8786
|
+
* is reachable and answering, which is exactly what a systemic streak claims
|
|
8787
|
+
* it is not.
|
|
8788
|
+
*/
|
|
8789
|
+
record(err, modelId) {
|
|
8790
|
+
if (this.trip) return null;
|
|
8791
|
+
const id = modelId?.trim() || "unknown-model";
|
|
8792
|
+
const c = classifyProviderError(err);
|
|
8793
|
+
if (!c.systemic) {
|
|
8794
|
+
this.counts.delete(id);
|
|
8795
|
+
return null;
|
|
8796
|
+
}
|
|
8797
|
+
const prev = this.counts.get(id);
|
|
8798
|
+
const n = prev && prev.kind === c.kind ? prev.n + 1 : 1;
|
|
8799
|
+
this.counts.set(id, { kind: c.kind, n, last: c });
|
|
8800
|
+
if (n >= this.threshold) {
|
|
8801
|
+
this.trip = {
|
|
8802
|
+
modelId: id,
|
|
8803
|
+
kind: c.kind,
|
|
8804
|
+
failures: n,
|
|
8805
|
+
message: describeProviderError(c, id)
|
|
8806
|
+
};
|
|
8807
|
+
return this.trip;
|
|
8808
|
+
}
|
|
8809
|
+
return null;
|
|
8810
|
+
}
|
|
8811
|
+
/** True once the run should stop launching work that would fail identically. */
|
|
8812
|
+
isOpen() {
|
|
8813
|
+
return this.trip !== null;
|
|
8814
|
+
}
|
|
8815
|
+
/** Why the breaker opened, or null while it is closed. */
|
|
8816
|
+
reason() {
|
|
8817
|
+
return this.trip;
|
|
8818
|
+
}
|
|
8819
|
+
/**
|
|
8820
|
+
* One line for a worker/section that never ran because the breaker was
|
|
8821
|
+
* already open. Says the work was skipped deliberately — not that it failed
|
|
8822
|
+
* on its own merits, which is what an empty FAILED status would imply.
|
|
8823
|
+
*/
|
|
8824
|
+
skipMessage() {
|
|
8825
|
+
const t = this.trip;
|
|
8826
|
+
if (!t) return "Skipped.";
|
|
8827
|
+
return `Skipped \u2014 the run stopped after ${t.failures} consecutive failures on ${t.modelId}. ${t.message}`;
|
|
8828
|
+
}
|
|
8829
|
+
/** Test/diagnostic view of the per-model streaks. */
|
|
8830
|
+
streaks() {
|
|
8831
|
+
const out = {};
|
|
8832
|
+
for (const [k, v] of this.counts) out[k] = { kind: v.kind, n: v.n };
|
|
8833
|
+
return out;
|
|
8834
|
+
}
|
|
8835
|
+
};
|
|
8836
|
+
|
|
6873
8837
|
// src/tools/base.ts
|
|
6874
8838
|
var BaseTool = class {
|
|
6875
8839
|
workspaceRoot = process.cwd();
|
|
@@ -9083,7 +11047,18 @@ var ProviderConfigSchema = z.object({
|
|
|
9083
11047
|
apiVersion: z.string().optional(),
|
|
9084
11048
|
model: z.string().optional(),
|
|
9085
11049
|
authToken: z.string().optional(),
|
|
9086
|
-
credentialSource: z.string().optional()
|
|
11050
|
+
credentialSource: z.string().optional(),
|
|
11051
|
+
/**
|
|
11052
|
+
* "This endpoint is self-hosted, so inference genuinely costs $0."
|
|
11053
|
+
* Only meaningful for `ollama` and `openai-compatible`. Unset ⇒ inferred:
|
|
11054
|
+
* ollama is local, openai-compatible is local when its baseUrl is
|
|
11055
|
+
* loopback/LAN. Set it explicitly to override — e.g. `false` for an Ollama
|
|
11056
|
+
* instance you rent, `true` for a llama.cpp box on your own LAN reached by
|
|
11057
|
+
* hostname.
|
|
11058
|
+
*/
|
|
11059
|
+
local: z.boolean().optional(),
|
|
11060
|
+
/** Region for providers whose price varies by region (Azure: global | us | eu | …). */
|
|
11061
|
+
region: z.string().optional()
|
|
9087
11062
|
});
|
|
9088
11063
|
var ModelOverridesSchema = z.object({
|
|
9089
11064
|
t1: z.string().optional(),
|
|
@@ -9669,8 +11644,8 @@ var TaskAnalyzer = class {
|
|
|
9669
11644
|
}
|
|
9670
11645
|
costEfficiency(model, complexity) {
|
|
9671
11646
|
if (this.tracker) return this.tracker.costEfficiencyScore(model, complexity);
|
|
9672
|
-
const blended = model
|
|
9673
|
-
const normalised = Math.min(1, blended /
|
|
11647
|
+
const blended = blendedCostPer1k(model);
|
|
11648
|
+
const normalised = Math.min(1, blended / BLENDED_COST_CEILING);
|
|
9674
11649
|
const complexityWeight = (6 - complexity) / 5;
|
|
9675
11650
|
return Math.max(0.1, 1 - normalised * complexityWeight);
|
|
9676
11651
|
}
|
|
@@ -9805,8 +11780,8 @@ var ModelPerformanceTracker = class {
|
|
|
9805
11780
|
* normalised over $0.05 blended as the "expensive" ceiling.
|
|
9806
11781
|
*/
|
|
9807
11782
|
costEfficiencyScore(model, complexity) {
|
|
9808
|
-
const blended = model
|
|
9809
|
-
const normalised = Math.min(1, blended /
|
|
11783
|
+
const blended = blendedCostPer1k(model);
|
|
11784
|
+
const normalised = Math.min(1, blended / BLENDED_COST_CEILING);
|
|
9810
11785
|
const complexityWeight = (6 - complexity) / 5;
|
|
9811
11786
|
return Math.max(0.1, 1 - normalised * complexityWeight);
|
|
9812
11787
|
}
|
|
@@ -12431,6 +14406,7 @@ ${prompt}` : prompt;
|
|
|
12431
14406
|
if (toolCreator) toolCreator.setPermissionEscalator(escalator);
|
|
12432
14407
|
const rootPrompt = buildContextualPrompt(options.prompt, options.conversationHistory);
|
|
12433
14408
|
let finalOutput = "";
|
|
14409
|
+
const runBreaker = new RunBreaker();
|
|
12434
14410
|
let t2Results = [];
|
|
12435
14411
|
let runError = null;
|
|
12436
14412
|
let identityPrompt = "";
|
|
@@ -12499,6 +14475,7 @@ ${prompt}` : prompt;
|
|
|
12499
14475
|
this.emit("tier:status", { tierId: "t3-root", status: "COMPLETED", role: "T3" });
|
|
12500
14476
|
} else if (complexity === "Moderate") {
|
|
12501
14477
|
const t2 = new T2Manager(this.router, this.toolRegistry, "root");
|
|
14478
|
+
t2.setRunBreaker(runBreaker);
|
|
12502
14479
|
t2.setPresenter(true);
|
|
12503
14480
|
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.");
|
|
12504
14481
|
if (identityPrompt) {
|
|
@@ -12559,6 +14536,7 @@ _(incomplete: ${reasons.join("; ")})_` : partial;
|
|
|
12559
14536
|
}
|
|
12560
14537
|
} else {
|
|
12561
14538
|
const t1 = new T1Administrator(this.router, this.toolRegistry, this.config);
|
|
14539
|
+
t1.setRunBreaker(runBreaker);
|
|
12562
14540
|
t1.setPresenter(true);
|
|
12563
14541
|
t1.setHierarchyContext("You are the top-level Administrator. You are responsible for the overall plan and supervising multiple T2 Managers.");
|
|
12564
14542
|
if (identityPrompt) {
|
|
@@ -12583,6 +14561,20 @@ _(incomplete: ${reasons.join("; ")})_` : partial;
|
|
|
12583
14561
|
finalOutput = result.output;
|
|
12584
14562
|
t2Results = result.t2Results;
|
|
12585
14563
|
}
|
|
14564
|
+
const trip = runBreaker.reason();
|
|
14565
|
+
if (trip) {
|
|
14566
|
+
finalOutput = [
|
|
14567
|
+
`\u26A0 **Run stopped early \u2014 ${trip.modelId} was failing every request.**`,
|
|
14568
|
+
"",
|
|
14569
|
+
trip.message,
|
|
14570
|
+
"",
|
|
14571
|
+
`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.`,
|
|
14572
|
+
finalOutput ? `
|
|
14573
|
+
---
|
|
14574
|
+
|
|
14575
|
+
${finalOutput}` : ""
|
|
14576
|
+
].join("\n").trimEnd();
|
|
14577
|
+
}
|
|
12586
14578
|
} catch (err) {
|
|
12587
14579
|
if (err instanceof CascadeCancelledError || err instanceof Error && err.name === "AbortError" || options.signal?.aborted) {
|
|
12588
14580
|
this.emit("run:cancelled", {
|
|
@@ -12649,7 +14641,11 @@ _(incomplete: ${reasons.join("; ")})_` : partial;
|
|
|
12649
14641
|
inputTokens: 0,
|
|
12650
14642
|
outputTokens: 0,
|
|
12651
14643
|
totalTokens: stats.totalTokens,
|
|
12652
|
-
estimatedCostUsd: stats.totalCostUsd
|
|
14644
|
+
estimatedCostUsd: stats.totalCostUsd,
|
|
14645
|
+
// At least one call ran on a model with no known price, so its spend is
|
|
14646
|
+
// missing from the total above. Say so rather than letting the caller
|
|
14647
|
+
// present an undercount as the final figure.
|
|
14648
|
+
...stats.untrackedCostCalls > 0 ? { costUnknown: true } : {}
|
|
12653
14649
|
},
|
|
12654
14650
|
t2Results,
|
|
12655
14651
|
durationMs,
|
|
@@ -12719,7 +14715,8 @@ _(incomplete: ${reasons.join("; ")})_` : partial;
|
|
|
12719
14715
|
inputTokens: 0,
|
|
12720
14716
|
outputTokens: 0,
|
|
12721
14717
|
totalTokens: stats2.totalTokens,
|
|
12722
|
-
estimatedCostUsd: stats2.totalCostUsd
|
|
14718
|
+
estimatedCostUsd: stats2.totalCostUsd,
|
|
14719
|
+
...stats2.untrackedCostCalls > 0 ? { costUnknown: true } : {}
|
|
12723
14720
|
},
|
|
12724
14721
|
t2Results: [],
|
|
12725
14722
|
durationMs: Date.now() - startMs,
|
|
@@ -12743,7 +14740,8 @@ _(incomplete: ${reasons.join("; ")})_` : partial;
|
|
|
12743
14740
|
inputTokens: result.usage?.inputTokens ?? 0,
|
|
12744
14741
|
outputTokens: result.usage?.outputTokens ?? 0,
|
|
12745
14742
|
totalTokens: stats.totalTokens,
|
|
12746
|
-
estimatedCostUsd: stats.totalCostUsd
|
|
14743
|
+
estimatedCostUsd: stats.totalCostUsd,
|
|
14744
|
+
...stats.untrackedCostCalls > 0 ? { costUnknown: true } : {}
|
|
12747
14745
|
},
|
|
12748
14746
|
t2Results: [],
|
|
12749
14747
|
durationMs: Date.now() - startMs,
|
|
@@ -14527,7 +16525,10 @@ var DashboardServer = class {
|
|
|
14527
16525
|
this.socket.broadcast("cost:update", {
|
|
14528
16526
|
sessionId,
|
|
14529
16527
|
totalTokens: result.usage.totalTokens,
|
|
14530
|
-
totalCostUsd: result.usage.estimatedCostUsd
|
|
16528
|
+
totalCostUsd: result.usage.estimatedCostUsd,
|
|
16529
|
+
// True when a model with no known price was used: the total above is
|
|
16530
|
+
// an undercount, and the UI must not present it as the full spend.
|
|
16531
|
+
costUnknown: result.usage.costUnknown === true
|
|
14531
16532
|
});
|
|
14532
16533
|
this.throttledBroadcast("workspace");
|
|
14533
16534
|
} catch (err) {
|
|
@@ -14840,7 +16841,8 @@ var DashboardServer = class {
|
|
|
14840
16841
|
this.socket.broadcast("cost:update", {
|
|
14841
16842
|
sessionId,
|
|
14842
16843
|
totalTokens: result.usage.totalTokens,
|
|
14843
|
-
totalCostUsd: result.usage.estimatedCostUsd
|
|
16844
|
+
totalCostUsd: result.usage.estimatedCostUsd,
|
|
16845
|
+
costUnknown: result.usage.costUnknown === true
|
|
14844
16846
|
});
|
|
14845
16847
|
this.throttledBroadcast("workspace");
|
|
14846
16848
|
} catch (err) {
|
|
@@ -15382,7 +17384,8 @@ ${prompt}`;
|
|
|
15382
17384
|
this.socket.broadcast("cost:update", {
|
|
15383
17385
|
sessionId,
|
|
15384
17386
|
totalTokens: result.usage.totalTokens,
|
|
15385
|
-
totalCostUsd: result.usage.estimatedCostUsd
|
|
17387
|
+
totalCostUsd: result.usage.estimatedCostUsd,
|
|
17388
|
+
costUnknown: result.usage.costUnknown === true
|
|
15386
17389
|
});
|
|
15387
17390
|
this.socket.broadcastToRoom(`session:${sessionId}`, "session:complete", { sessionId, result });
|
|
15388
17391
|
this.throttledBroadcast("workspace");
|