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.cjs
CHANGED
|
@@ -238,8 +238,1660 @@ var init_audit_logger = __esm({
|
|
|
238
238
|
}
|
|
239
239
|
});
|
|
240
240
|
|
|
241
|
+
// src/core/router/pricing-data.json
|
|
242
|
+
var pricing_data_default = {
|
|
243
|
+
entries: [
|
|
244
|
+
{
|
|
245
|
+
model: "claude-opus-5",
|
|
246
|
+
provider: "anthropic",
|
|
247
|
+
modality: "text",
|
|
248
|
+
currency: "USD",
|
|
249
|
+
asOf: "2026-07-27",
|
|
250
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
251
|
+
provenance: "vendor-pricing-page",
|
|
252
|
+
tiers: [
|
|
253
|
+
{
|
|
254
|
+
inputPer1m: 5,
|
|
255
|
+
outputPer1m: 25,
|
|
256
|
+
cachedInputPer1m: 0.5,
|
|
257
|
+
cacheWritePer1m: 6.25,
|
|
258
|
+
reasoningOutputPer1m: 25
|
|
259
|
+
}
|
|
260
|
+
],
|
|
261
|
+
batchMultiplier: 0.5,
|
|
262
|
+
contextWindow: 1e6
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
model: "claude-opus-4-8",
|
|
266
|
+
provider: "anthropic",
|
|
267
|
+
modality: "text",
|
|
268
|
+
currency: "USD",
|
|
269
|
+
asOf: "2026-07-27",
|
|
270
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
271
|
+
provenance: "vendor-pricing-page",
|
|
272
|
+
tiers: [
|
|
273
|
+
{
|
|
274
|
+
inputPer1m: 5,
|
|
275
|
+
outputPer1m: 25,
|
|
276
|
+
cachedInputPer1m: 0.5,
|
|
277
|
+
cacheWritePer1m: 6.25,
|
|
278
|
+
reasoningOutputPer1m: 25
|
|
279
|
+
}
|
|
280
|
+
],
|
|
281
|
+
batchMultiplier: 0.5,
|
|
282
|
+
contextWindow: 1e6
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
model: "claude-opus-4-7",
|
|
286
|
+
provider: "anthropic",
|
|
287
|
+
modality: "text",
|
|
288
|
+
currency: "USD",
|
|
289
|
+
asOf: "2026-07-27",
|
|
290
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
291
|
+
provenance: "vendor-pricing-page",
|
|
292
|
+
tiers: [
|
|
293
|
+
{
|
|
294
|
+
inputPer1m: 5,
|
|
295
|
+
outputPer1m: 25,
|
|
296
|
+
cachedInputPer1m: 0.5,
|
|
297
|
+
cacheWritePer1m: 6.25,
|
|
298
|
+
reasoningOutputPer1m: 25
|
|
299
|
+
}
|
|
300
|
+
],
|
|
301
|
+
batchMultiplier: 0.5,
|
|
302
|
+
contextWindow: 1e6
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
model: "claude-opus-4-6",
|
|
306
|
+
provider: "anthropic",
|
|
307
|
+
modality: "text",
|
|
308
|
+
currency: "USD",
|
|
309
|
+
asOf: "2026-07-27",
|
|
310
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
311
|
+
provenance: "vendor-pricing-page",
|
|
312
|
+
tiers: [
|
|
313
|
+
{
|
|
314
|
+
inputPer1m: 5,
|
|
315
|
+
outputPer1m: 25,
|
|
316
|
+
cachedInputPer1m: 0.5,
|
|
317
|
+
cacheWritePer1m: 6.25,
|
|
318
|
+
reasoningOutputPer1m: 25
|
|
319
|
+
}
|
|
320
|
+
],
|
|
321
|
+
batchMultiplier: 0.5,
|
|
322
|
+
contextWindow: 1e6
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
model: "claude-opus-4-5",
|
|
326
|
+
provider: "anthropic",
|
|
327
|
+
modality: "text",
|
|
328
|
+
currency: "USD",
|
|
329
|
+
asOf: "2026-07-27",
|
|
330
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
331
|
+
provenance: "vendor-pricing-page",
|
|
332
|
+
tiers: [
|
|
333
|
+
{
|
|
334
|
+
inputPer1m: 5,
|
|
335
|
+
outputPer1m: 25,
|
|
336
|
+
cachedInputPer1m: 0.5,
|
|
337
|
+
cacheWritePer1m: 6.25,
|
|
338
|
+
reasoningOutputPer1m: 25
|
|
339
|
+
}
|
|
340
|
+
],
|
|
341
|
+
batchMultiplier: 0.5,
|
|
342
|
+
contextWindow: 2e5
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
model: "claude-opus-4-1",
|
|
346
|
+
provider: "anthropic",
|
|
347
|
+
modality: "text",
|
|
348
|
+
currency: "USD",
|
|
349
|
+
asOf: "2026-07-27",
|
|
350
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
351
|
+
provenance: "vendor-pricing-page",
|
|
352
|
+
tiers: [
|
|
353
|
+
{
|
|
354
|
+
inputPer1m: 15,
|
|
355
|
+
outputPer1m: 75,
|
|
356
|
+
cachedInputPer1m: 1.5,
|
|
357
|
+
cacheWritePer1m: 18.75,
|
|
358
|
+
reasoningOutputPer1m: 75
|
|
359
|
+
}
|
|
360
|
+
],
|
|
361
|
+
batchMultiplier: 0.5,
|
|
362
|
+
contextWindow: 2e5,
|
|
363
|
+
notes: "Deprecated by Anthropic; priced for historical/cost-reporting accuracy."
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
model: "claude-opus-4",
|
|
367
|
+
provider: "anthropic",
|
|
368
|
+
modality: "text",
|
|
369
|
+
currency: "USD",
|
|
370
|
+
asOf: "2026-07-27",
|
|
371
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
372
|
+
provenance: "vendor-pricing-page",
|
|
373
|
+
tiers: [
|
|
374
|
+
{
|
|
375
|
+
inputPer1m: 15,
|
|
376
|
+
outputPer1m: 75,
|
|
377
|
+
cachedInputPer1m: 1.5,
|
|
378
|
+
cacheWritePer1m: 18.75,
|
|
379
|
+
reasoningOutputPer1m: 75
|
|
380
|
+
}
|
|
381
|
+
],
|
|
382
|
+
batchMultiplier: 0.5,
|
|
383
|
+
contextWindow: 2e5,
|
|
384
|
+
notes: "Retired except on Google Cloud; priced for historical/cost-reporting accuracy."
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
model: "claude-sonnet-5",
|
|
388
|
+
provider: "anthropic",
|
|
389
|
+
modality: "text",
|
|
390
|
+
currency: "USD",
|
|
391
|
+
asOf: "2026-07-27",
|
|
392
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
393
|
+
provenance: "vendor-pricing-page",
|
|
394
|
+
tiers: [
|
|
395
|
+
{
|
|
396
|
+
inputPer1m: 2,
|
|
397
|
+
outputPer1m: 10,
|
|
398
|
+
cachedInputPer1m: 0.2,
|
|
399
|
+
cacheWritePer1m: 2.5,
|
|
400
|
+
reasoningOutputPer1m: 10
|
|
401
|
+
}
|
|
402
|
+
],
|
|
403
|
+
batchMultiplier: 0.5,
|
|
404
|
+
contextWindow: 1e6,
|
|
405
|
+
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."
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
model: "claude-sonnet-4-6",
|
|
409
|
+
provider: "anthropic",
|
|
410
|
+
modality: "text",
|
|
411
|
+
currency: "USD",
|
|
412
|
+
asOf: "2026-07-27",
|
|
413
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
414
|
+
provenance: "vendor-pricing-page",
|
|
415
|
+
tiers: [
|
|
416
|
+
{
|
|
417
|
+
inputPer1m: 3,
|
|
418
|
+
outputPer1m: 15,
|
|
419
|
+
cachedInputPer1m: 0.3,
|
|
420
|
+
cacheWritePer1m: 3.75,
|
|
421
|
+
reasoningOutputPer1m: 15
|
|
422
|
+
}
|
|
423
|
+
],
|
|
424
|
+
batchMultiplier: 0.5,
|
|
425
|
+
contextWindow: 1e6
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
model: "claude-sonnet-4-5",
|
|
429
|
+
provider: "anthropic",
|
|
430
|
+
modality: "text",
|
|
431
|
+
currency: "USD",
|
|
432
|
+
asOf: "2026-07-27",
|
|
433
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
434
|
+
provenance: "vendor-pricing-page",
|
|
435
|
+
tiers: [
|
|
436
|
+
{
|
|
437
|
+
maxInputTokens: 2e5,
|
|
438
|
+
inputPer1m: 3,
|
|
439
|
+
outputPer1m: 15,
|
|
440
|
+
cachedInputPer1m: 0.3,
|
|
441
|
+
cacheWritePer1m: 3.75,
|
|
442
|
+
reasoningOutputPer1m: 15
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
inputPer1m: 6,
|
|
446
|
+
outputPer1m: 22.5,
|
|
447
|
+
cachedInputPer1m: 0.6,
|
|
448
|
+
cacheWritePer1m: 7.5,
|
|
449
|
+
reasoningOutputPer1m: 22.5
|
|
450
|
+
}
|
|
451
|
+
],
|
|
452
|
+
batchMultiplier: 0.5,
|
|
453
|
+
contextWindow: 2e5,
|
|
454
|
+
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)."
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
model: "claude-sonnet-4",
|
|
458
|
+
provider: "anthropic",
|
|
459
|
+
modality: "text",
|
|
460
|
+
currency: "USD",
|
|
461
|
+
asOf: "2026-07-27",
|
|
462
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
463
|
+
provenance: "vendor-pricing-page",
|
|
464
|
+
tiers: [
|
|
465
|
+
{
|
|
466
|
+
inputPer1m: 3,
|
|
467
|
+
outputPer1m: 15,
|
|
468
|
+
cachedInputPer1m: 0.3,
|
|
469
|
+
cacheWritePer1m: 3.75,
|
|
470
|
+
reasoningOutputPer1m: 15
|
|
471
|
+
}
|
|
472
|
+
],
|
|
473
|
+
batchMultiplier: 0.5,
|
|
474
|
+
contextWindow: 2e5
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
model: "claude-haiku-4-5",
|
|
478
|
+
provider: "anthropic",
|
|
479
|
+
modality: "text",
|
|
480
|
+
currency: "USD",
|
|
481
|
+
asOf: "2026-07-27",
|
|
482
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
483
|
+
provenance: "vendor-pricing-page",
|
|
484
|
+
tiers: [
|
|
485
|
+
{
|
|
486
|
+
inputPer1m: 1,
|
|
487
|
+
outputPer1m: 5,
|
|
488
|
+
cachedInputPer1m: 0.1,
|
|
489
|
+
cacheWritePer1m: 1.25,
|
|
490
|
+
reasoningOutputPer1m: 5
|
|
491
|
+
}
|
|
492
|
+
],
|
|
493
|
+
batchMultiplier: 0.5,
|
|
494
|
+
contextWindow: 2e5
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
model: "claude-haiku-3-5",
|
|
498
|
+
provider: "anthropic",
|
|
499
|
+
modality: "text",
|
|
500
|
+
currency: "USD",
|
|
501
|
+
asOf: "2026-07-27",
|
|
502
|
+
source: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
503
|
+
provenance: "vendor-pricing-page",
|
|
504
|
+
tiers: [
|
|
505
|
+
{
|
|
506
|
+
inputPer1m: 0.8,
|
|
507
|
+
outputPer1m: 4,
|
|
508
|
+
cachedInputPer1m: 0.08,
|
|
509
|
+
cacheWritePer1m: 1,
|
|
510
|
+
reasoningOutputPer1m: 4
|
|
511
|
+
}
|
|
512
|
+
],
|
|
513
|
+
batchMultiplier: 0.5,
|
|
514
|
+
contextWindow: 2e5
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
model: "gpt-5.6-sol",
|
|
518
|
+
provider: "openai",
|
|
519
|
+
aliases: [
|
|
520
|
+
"gpt-5.6"
|
|
521
|
+
],
|
|
522
|
+
modality: "text",
|
|
523
|
+
currency: "USD",
|
|
524
|
+
asOf: "2026-07-27",
|
|
525
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
526
|
+
provenance: "litellm-catalog",
|
|
527
|
+
tiers: [
|
|
528
|
+
{
|
|
529
|
+
maxInputTokens: 272e3,
|
|
530
|
+
inputPer1m: 5,
|
|
531
|
+
outputPer1m: 30,
|
|
532
|
+
cachedInputPer1m: 0.5,
|
|
533
|
+
cacheWritePer1m: 6.25
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
inputPer1m: 10,
|
|
537
|
+
outputPer1m: 45,
|
|
538
|
+
cachedInputPer1m: 1,
|
|
539
|
+
cacheWritePer1m: 12.5
|
|
540
|
+
}
|
|
541
|
+
],
|
|
542
|
+
batchMultiplier: 0.5,
|
|
543
|
+
contextWindow: 105e4
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
model: "gpt-5.6-terra",
|
|
547
|
+
provider: "openai",
|
|
548
|
+
modality: "text",
|
|
549
|
+
currency: "USD",
|
|
550
|
+
asOf: "2026-07-27",
|
|
551
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
552
|
+
provenance: "litellm-catalog",
|
|
553
|
+
tiers: [
|
|
554
|
+
{
|
|
555
|
+
maxInputTokens: 272e3,
|
|
556
|
+
inputPer1m: 2.5,
|
|
557
|
+
outputPer1m: 15,
|
|
558
|
+
cachedInputPer1m: 0.25,
|
|
559
|
+
cacheWritePer1m: 3.125
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
inputPer1m: 5,
|
|
563
|
+
outputPer1m: 22.5,
|
|
564
|
+
cachedInputPer1m: 0.5,
|
|
565
|
+
cacheWritePer1m: 6.25
|
|
566
|
+
}
|
|
567
|
+
],
|
|
568
|
+
batchMultiplier: 0.5,
|
|
569
|
+
contextWindow: 105e4
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
model: "gpt-5.6-luna",
|
|
573
|
+
provider: "openai",
|
|
574
|
+
modality: "text",
|
|
575
|
+
currency: "USD",
|
|
576
|
+
asOf: "2026-07-27",
|
|
577
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
578
|
+
provenance: "litellm-catalog",
|
|
579
|
+
tiers: [
|
|
580
|
+
{
|
|
581
|
+
maxInputTokens: 272e3,
|
|
582
|
+
inputPer1m: 1,
|
|
583
|
+
outputPer1m: 6,
|
|
584
|
+
cachedInputPer1m: 0.1,
|
|
585
|
+
cacheWritePer1m: 1.25
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
inputPer1m: 2,
|
|
589
|
+
outputPer1m: 9,
|
|
590
|
+
cachedInputPer1m: 0.2,
|
|
591
|
+
cacheWritePer1m: 2.5
|
|
592
|
+
}
|
|
593
|
+
],
|
|
594
|
+
batchMultiplier: 0.5,
|
|
595
|
+
contextWindow: 105e4
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
model: "gpt-5.5",
|
|
599
|
+
provider: "openai",
|
|
600
|
+
modality: "text",
|
|
601
|
+
currency: "USD",
|
|
602
|
+
asOf: "2026-07-27",
|
|
603
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
604
|
+
provenance: "litellm-catalog",
|
|
605
|
+
tiers: [
|
|
606
|
+
{
|
|
607
|
+
maxInputTokens: 272e3,
|
|
608
|
+
inputPer1m: 5,
|
|
609
|
+
outputPer1m: 30,
|
|
610
|
+
cachedInputPer1m: 0.5
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
inputPer1m: 10,
|
|
614
|
+
outputPer1m: 45,
|
|
615
|
+
cachedInputPer1m: 1
|
|
616
|
+
}
|
|
617
|
+
],
|
|
618
|
+
batchMultiplier: 0.5,
|
|
619
|
+
contextWindow: 105e4
|
|
620
|
+
},
|
|
621
|
+
{
|
|
622
|
+
model: "gpt-5.4",
|
|
623
|
+
provider: "openai",
|
|
624
|
+
modality: "text",
|
|
625
|
+
currency: "USD",
|
|
626
|
+
asOf: "2026-07-27",
|
|
627
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
628
|
+
provenance: "litellm-catalog",
|
|
629
|
+
tiers: [
|
|
630
|
+
{
|
|
631
|
+
maxInputTokens: 272e3,
|
|
632
|
+
inputPer1m: 2.5,
|
|
633
|
+
outputPer1m: 15,
|
|
634
|
+
cachedInputPer1m: 0.25
|
|
635
|
+
},
|
|
636
|
+
{
|
|
637
|
+
inputPer1m: 5,
|
|
638
|
+
outputPer1m: 22.5,
|
|
639
|
+
cachedInputPer1m: 0.5
|
|
640
|
+
}
|
|
641
|
+
],
|
|
642
|
+
batchMultiplier: 0.5,
|
|
643
|
+
contextWindow: 105e4
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
model: "gpt-5.4-mini",
|
|
647
|
+
provider: "openai",
|
|
648
|
+
modality: "text",
|
|
649
|
+
currency: "USD",
|
|
650
|
+
asOf: "2026-07-27",
|
|
651
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
652
|
+
provenance: "litellm-catalog",
|
|
653
|
+
tiers: [
|
|
654
|
+
{
|
|
655
|
+
inputPer1m: 0.75,
|
|
656
|
+
outputPer1m: 4.5,
|
|
657
|
+
cachedInputPer1m: 0.075
|
|
658
|
+
}
|
|
659
|
+
],
|
|
660
|
+
batchMultiplier: 0.5,
|
|
661
|
+
contextWindow: 105e4
|
|
662
|
+
},
|
|
663
|
+
{
|
|
664
|
+
model: "gpt-5",
|
|
665
|
+
provider: "openai",
|
|
666
|
+
modality: "text",
|
|
667
|
+
currency: "USD",
|
|
668
|
+
asOf: "2026-07-27",
|
|
669
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
670
|
+
provenance: "litellm-catalog",
|
|
671
|
+
tiers: [
|
|
672
|
+
{
|
|
673
|
+
inputPer1m: 1.25,
|
|
674
|
+
outputPer1m: 10,
|
|
675
|
+
cachedInputPer1m: 0.125
|
|
676
|
+
}
|
|
677
|
+
],
|
|
678
|
+
contextWindow: 272e3
|
|
679
|
+
},
|
|
680
|
+
{
|
|
681
|
+
model: "gpt-5-mini",
|
|
682
|
+
provider: "openai",
|
|
683
|
+
modality: "text",
|
|
684
|
+
currency: "USD",
|
|
685
|
+
asOf: "2026-07-27",
|
|
686
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
687
|
+
provenance: "litellm-catalog",
|
|
688
|
+
tiers: [
|
|
689
|
+
{
|
|
690
|
+
inputPer1m: 0.25,
|
|
691
|
+
outputPer1m: 2,
|
|
692
|
+
cachedInputPer1m: 0.025
|
|
693
|
+
}
|
|
694
|
+
],
|
|
695
|
+
contextWindow: 272e3
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
model: "gpt-5-nano",
|
|
699
|
+
provider: "openai",
|
|
700
|
+
modality: "text",
|
|
701
|
+
currency: "USD",
|
|
702
|
+
asOf: "2026-07-27",
|
|
703
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
704
|
+
provenance: "litellm-catalog",
|
|
705
|
+
tiers: [
|
|
706
|
+
{
|
|
707
|
+
inputPer1m: 0.05,
|
|
708
|
+
outputPer1m: 0.4,
|
|
709
|
+
cachedInputPer1m: 5e-3
|
|
710
|
+
}
|
|
711
|
+
],
|
|
712
|
+
contextWindow: 272e3
|
|
713
|
+
},
|
|
714
|
+
{
|
|
715
|
+
model: "gpt-4.1",
|
|
716
|
+
provider: "openai",
|
|
717
|
+
modality: "text",
|
|
718
|
+
currency: "USD",
|
|
719
|
+
asOf: "2026-07-27",
|
|
720
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
721
|
+
provenance: "litellm-catalog",
|
|
722
|
+
tiers: [
|
|
723
|
+
{
|
|
724
|
+
inputPer1m: 2,
|
|
725
|
+
outputPer1m: 8,
|
|
726
|
+
cachedInputPer1m: 0.5
|
|
727
|
+
}
|
|
728
|
+
],
|
|
729
|
+
batchMultiplier: 0.5,
|
|
730
|
+
contextWindow: 1047576
|
|
731
|
+
},
|
|
732
|
+
{
|
|
733
|
+
model: "gpt-4.1-mini",
|
|
734
|
+
provider: "openai",
|
|
735
|
+
modality: "text",
|
|
736
|
+
currency: "USD",
|
|
737
|
+
asOf: "2026-07-27",
|
|
738
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
739
|
+
provenance: "litellm-catalog",
|
|
740
|
+
tiers: [
|
|
741
|
+
{
|
|
742
|
+
inputPer1m: 0.4,
|
|
743
|
+
outputPer1m: 1.6,
|
|
744
|
+
cachedInputPer1m: 0.1
|
|
745
|
+
}
|
|
746
|
+
],
|
|
747
|
+
batchMultiplier: 0.5,
|
|
748
|
+
contextWindow: 1047576
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
model: "gpt-4.1-nano",
|
|
752
|
+
provider: "openai",
|
|
753
|
+
modality: "text",
|
|
754
|
+
currency: "USD",
|
|
755
|
+
asOf: "2026-07-27",
|
|
756
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
757
|
+
provenance: "litellm-catalog",
|
|
758
|
+
tiers: [
|
|
759
|
+
{
|
|
760
|
+
inputPer1m: 0.1,
|
|
761
|
+
outputPer1m: 0.4,
|
|
762
|
+
cachedInputPer1m: 0.025
|
|
763
|
+
}
|
|
764
|
+
],
|
|
765
|
+
batchMultiplier: 0.5,
|
|
766
|
+
contextWindow: 1047576
|
|
767
|
+
},
|
|
768
|
+
{
|
|
769
|
+
model: "gpt-4o",
|
|
770
|
+
provider: "openai",
|
|
771
|
+
modality: "text",
|
|
772
|
+
currency: "USD",
|
|
773
|
+
asOf: "2026-07-27",
|
|
774
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
775
|
+
provenance: "litellm-catalog",
|
|
776
|
+
tiers: [
|
|
777
|
+
{
|
|
778
|
+
inputPer1m: 2.5,
|
|
779
|
+
outputPer1m: 10,
|
|
780
|
+
cachedInputPer1m: 1.25
|
|
781
|
+
}
|
|
782
|
+
],
|
|
783
|
+
batchMultiplier: 0.5,
|
|
784
|
+
contextWindow: 128e3
|
|
785
|
+
},
|
|
786
|
+
{
|
|
787
|
+
model: "gpt-4o-mini",
|
|
788
|
+
provider: "openai",
|
|
789
|
+
modality: "text",
|
|
790
|
+
currency: "USD",
|
|
791
|
+
asOf: "2026-07-27",
|
|
792
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
793
|
+
provenance: "litellm-catalog",
|
|
794
|
+
tiers: [
|
|
795
|
+
{
|
|
796
|
+
inputPer1m: 0.15,
|
|
797
|
+
outputPer1m: 0.6,
|
|
798
|
+
cachedInputPer1m: 0.075
|
|
799
|
+
}
|
|
800
|
+
],
|
|
801
|
+
batchMultiplier: 0.5,
|
|
802
|
+
contextWindow: 128e3
|
|
803
|
+
},
|
|
804
|
+
{
|
|
805
|
+
model: "gpt-5.5",
|
|
806
|
+
provider: "azure",
|
|
807
|
+
region: "global",
|
|
808
|
+
modality: "text",
|
|
809
|
+
currency: "USD",
|
|
810
|
+
asOf: "2026-07-27",
|
|
811
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
812
|
+
provenance: "litellm-catalog",
|
|
813
|
+
tiers: [
|
|
814
|
+
{
|
|
815
|
+
maxInputTokens: 272e3,
|
|
816
|
+
inputPer1m: 5,
|
|
817
|
+
outputPer1m: 30,
|
|
818
|
+
cachedInputPer1m: 0.5
|
|
819
|
+
},
|
|
820
|
+
{
|
|
821
|
+
inputPer1m: 10,
|
|
822
|
+
outputPer1m: 45,
|
|
823
|
+
cachedInputPer1m: 1
|
|
824
|
+
}
|
|
825
|
+
],
|
|
826
|
+
contextWindow: 105e4
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
model: "gpt-5.5",
|
|
830
|
+
provider: "azure",
|
|
831
|
+
region: "us",
|
|
832
|
+
modality: "text",
|
|
833
|
+
currency: "USD",
|
|
834
|
+
asOf: "2026-07-27",
|
|
835
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
836
|
+
provenance: "litellm-catalog",
|
|
837
|
+
tiers: [
|
|
838
|
+
{
|
|
839
|
+
maxInputTokens: 272e3,
|
|
840
|
+
inputPer1m: 5.5,
|
|
841
|
+
outputPer1m: 33,
|
|
842
|
+
cachedInputPer1m: 0.55
|
|
843
|
+
},
|
|
844
|
+
{
|
|
845
|
+
inputPer1m: 11,
|
|
846
|
+
outputPer1m: 49.5,
|
|
847
|
+
cachedInputPer1m: 1.1
|
|
848
|
+
}
|
|
849
|
+
],
|
|
850
|
+
contextWindow: 105e4
|
|
851
|
+
},
|
|
852
|
+
{
|
|
853
|
+
model: "gpt-5.5",
|
|
854
|
+
provider: "azure",
|
|
855
|
+
region: "eu",
|
|
856
|
+
modality: "text",
|
|
857
|
+
currency: "USD",
|
|
858
|
+
asOf: "2026-07-27",
|
|
859
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
860
|
+
provenance: "litellm-catalog",
|
|
861
|
+
tiers: [
|
|
862
|
+
{
|
|
863
|
+
maxInputTokens: 272e3,
|
|
864
|
+
inputPer1m: 5.5,
|
|
865
|
+
outputPer1m: 33,
|
|
866
|
+
cachedInputPer1m: 0.55
|
|
867
|
+
},
|
|
868
|
+
{
|
|
869
|
+
inputPer1m: 11,
|
|
870
|
+
outputPer1m: 49.5,
|
|
871
|
+
cachedInputPer1m: 1.1
|
|
872
|
+
}
|
|
873
|
+
],
|
|
874
|
+
contextWindow: 105e4
|
|
875
|
+
},
|
|
876
|
+
{
|
|
877
|
+
model: "gpt-5.4",
|
|
878
|
+
provider: "azure",
|
|
879
|
+
region: "global",
|
|
880
|
+
modality: "text",
|
|
881
|
+
currency: "USD",
|
|
882
|
+
asOf: "2026-07-27",
|
|
883
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
884
|
+
provenance: "litellm-catalog",
|
|
885
|
+
tiers: [
|
|
886
|
+
{
|
|
887
|
+
maxInputTokens: 272e3,
|
|
888
|
+
inputPer1m: 2.5,
|
|
889
|
+
outputPer1m: 15,
|
|
890
|
+
cachedInputPer1m: 0.25
|
|
891
|
+
},
|
|
892
|
+
{
|
|
893
|
+
inputPer1m: 5,
|
|
894
|
+
outputPer1m: 22.5,
|
|
895
|
+
cachedInputPer1m: 0.5
|
|
896
|
+
}
|
|
897
|
+
],
|
|
898
|
+
contextWindow: 105e4
|
|
899
|
+
},
|
|
900
|
+
{
|
|
901
|
+
model: "gpt-5.4",
|
|
902
|
+
provider: "azure",
|
|
903
|
+
region: "us",
|
|
904
|
+
modality: "text",
|
|
905
|
+
currency: "USD",
|
|
906
|
+
asOf: "2026-07-27",
|
|
907
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
908
|
+
provenance: "litellm-catalog",
|
|
909
|
+
tiers: [
|
|
910
|
+
{
|
|
911
|
+
inputPer1m: 2.75,
|
|
912
|
+
outputPer1m: 16.5,
|
|
913
|
+
cachedInputPer1m: 0.28
|
|
914
|
+
}
|
|
915
|
+
],
|
|
916
|
+
contextWindow: 105e4
|
|
917
|
+
},
|
|
918
|
+
{
|
|
919
|
+
model: "gpt-5.4",
|
|
920
|
+
provider: "azure",
|
|
921
|
+
region: "eu",
|
|
922
|
+
modality: "text",
|
|
923
|
+
currency: "USD",
|
|
924
|
+
asOf: "2026-07-27",
|
|
925
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
926
|
+
provenance: "litellm-catalog",
|
|
927
|
+
tiers: [
|
|
928
|
+
{
|
|
929
|
+
inputPer1m: 2.75,
|
|
930
|
+
outputPer1m: 16.5,
|
|
931
|
+
cachedInputPer1m: 0.28
|
|
932
|
+
}
|
|
933
|
+
],
|
|
934
|
+
contextWindow: 105e4
|
|
935
|
+
},
|
|
936
|
+
{
|
|
937
|
+
model: "gpt-5.6-terra",
|
|
938
|
+
provider: "azure",
|
|
939
|
+
region: "global",
|
|
940
|
+
modality: "text",
|
|
941
|
+
currency: "USD",
|
|
942
|
+
asOf: "2026-07-27",
|
|
943
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
944
|
+
provenance: "litellm-catalog",
|
|
945
|
+
tiers: [
|
|
946
|
+
{
|
|
947
|
+
maxInputTokens: 272e3,
|
|
948
|
+
inputPer1m: 2.5,
|
|
949
|
+
outputPer1m: 15,
|
|
950
|
+
cachedInputPer1m: 0.25
|
|
951
|
+
},
|
|
952
|
+
{
|
|
953
|
+
inputPer1m: 5,
|
|
954
|
+
outputPer1m: 22.5,
|
|
955
|
+
cachedInputPer1m: 0.5
|
|
956
|
+
}
|
|
957
|
+
],
|
|
958
|
+
contextWindow: 105e4
|
|
959
|
+
},
|
|
960
|
+
{
|
|
961
|
+
model: "gpt-5.6-terra",
|
|
962
|
+
provider: "azure",
|
|
963
|
+
region: "eu",
|
|
964
|
+
modality: "text",
|
|
965
|
+
currency: "USD",
|
|
966
|
+
asOf: "2026-07-27",
|
|
967
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
968
|
+
provenance: "litellm-catalog",
|
|
969
|
+
tiers: [
|
|
970
|
+
{
|
|
971
|
+
maxInputTokens: 272e3,
|
|
972
|
+
inputPer1m: 2.75,
|
|
973
|
+
outputPer1m: 16.5,
|
|
974
|
+
cachedInputPer1m: 0.275
|
|
975
|
+
},
|
|
976
|
+
{
|
|
977
|
+
inputPer1m: 5.5,
|
|
978
|
+
outputPer1m: 24.75,
|
|
979
|
+
cachedInputPer1m: 0.55
|
|
980
|
+
}
|
|
981
|
+
],
|
|
982
|
+
contextWindow: 105e4
|
|
983
|
+
},
|
|
984
|
+
{
|
|
985
|
+
model: "gpt-5",
|
|
986
|
+
provider: "azure",
|
|
987
|
+
region: "global",
|
|
988
|
+
modality: "text",
|
|
989
|
+
currency: "USD",
|
|
990
|
+
asOf: "2026-07-27",
|
|
991
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
992
|
+
provenance: "litellm-catalog",
|
|
993
|
+
tiers: [
|
|
994
|
+
{
|
|
995
|
+
inputPer1m: 1.25,
|
|
996
|
+
outputPer1m: 10,
|
|
997
|
+
cachedInputPer1m: 0.125
|
|
998
|
+
}
|
|
999
|
+
],
|
|
1000
|
+
contextWindow: 272e3
|
|
1001
|
+
},
|
|
1002
|
+
{
|
|
1003
|
+
model: "gpt-5-mini",
|
|
1004
|
+
provider: "azure",
|
|
1005
|
+
region: "global",
|
|
1006
|
+
modality: "text",
|
|
1007
|
+
currency: "USD",
|
|
1008
|
+
asOf: "2026-07-27",
|
|
1009
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1010
|
+
provenance: "litellm-catalog",
|
|
1011
|
+
tiers: [
|
|
1012
|
+
{
|
|
1013
|
+
inputPer1m: 0.25,
|
|
1014
|
+
outputPer1m: 2,
|
|
1015
|
+
cachedInputPer1m: 0.025
|
|
1016
|
+
}
|
|
1017
|
+
],
|
|
1018
|
+
contextWindow: 272e3
|
|
1019
|
+
},
|
|
1020
|
+
{
|
|
1021
|
+
model: "gpt-5-nano",
|
|
1022
|
+
provider: "azure",
|
|
1023
|
+
region: "global",
|
|
1024
|
+
modality: "text",
|
|
1025
|
+
currency: "USD",
|
|
1026
|
+
asOf: "2026-07-27",
|
|
1027
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1028
|
+
provenance: "litellm-catalog",
|
|
1029
|
+
tiers: [
|
|
1030
|
+
{
|
|
1031
|
+
inputPer1m: 0.05,
|
|
1032
|
+
outputPer1m: 0.4,
|
|
1033
|
+
cachedInputPer1m: 5e-3
|
|
1034
|
+
}
|
|
1035
|
+
],
|
|
1036
|
+
contextWindow: 272e3
|
|
1037
|
+
},
|
|
1038
|
+
{
|
|
1039
|
+
model: "gpt-4.1",
|
|
1040
|
+
provider: "azure",
|
|
1041
|
+
region: "global",
|
|
1042
|
+
modality: "text",
|
|
1043
|
+
currency: "USD",
|
|
1044
|
+
asOf: "2026-07-27",
|
|
1045
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1046
|
+
provenance: "litellm-catalog",
|
|
1047
|
+
tiers: [
|
|
1048
|
+
{
|
|
1049
|
+
inputPer1m: 2,
|
|
1050
|
+
outputPer1m: 8,
|
|
1051
|
+
cachedInputPer1m: 0.5
|
|
1052
|
+
}
|
|
1053
|
+
],
|
|
1054
|
+
batchMultiplier: 0.5,
|
|
1055
|
+
contextWindow: 1047576
|
|
1056
|
+
},
|
|
1057
|
+
{
|
|
1058
|
+
model: "gpt-4o",
|
|
1059
|
+
provider: "azure",
|
|
1060
|
+
region: "global",
|
|
1061
|
+
modality: "text",
|
|
1062
|
+
currency: "USD",
|
|
1063
|
+
asOf: "2026-07-27",
|
|
1064
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1065
|
+
provenance: "litellm-catalog",
|
|
1066
|
+
tiers: [
|
|
1067
|
+
{
|
|
1068
|
+
inputPer1m: 2.5,
|
|
1069
|
+
outputPer1m: 10,
|
|
1070
|
+
cachedInputPer1m: 1.25
|
|
1071
|
+
}
|
|
1072
|
+
],
|
|
1073
|
+
contextWindow: 128e3
|
|
1074
|
+
},
|
|
1075
|
+
{
|
|
1076
|
+
model: "gpt-4o-mini",
|
|
1077
|
+
provider: "azure",
|
|
1078
|
+
region: "global",
|
|
1079
|
+
modality: "text",
|
|
1080
|
+
currency: "USD",
|
|
1081
|
+
asOf: "2026-07-27",
|
|
1082
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1083
|
+
provenance: "litellm-catalog",
|
|
1084
|
+
tiers: [
|
|
1085
|
+
{
|
|
1086
|
+
inputPer1m: 0.165,
|
|
1087
|
+
outputPer1m: 0.66,
|
|
1088
|
+
cachedInputPer1m: 0.075
|
|
1089
|
+
}
|
|
1090
|
+
],
|
|
1091
|
+
contextWindow: 128e3,
|
|
1092
|
+
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."
|
|
1093
|
+
},
|
|
1094
|
+
{
|
|
1095
|
+
model: "gemini-3.1-pro",
|
|
1096
|
+
provider: "gemini",
|
|
1097
|
+
aliases: [
|
|
1098
|
+
"gemini-3.1-pro-preview"
|
|
1099
|
+
],
|
|
1100
|
+
modality: "text",
|
|
1101
|
+
currency: "USD",
|
|
1102
|
+
asOf: "2026-07-27",
|
|
1103
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1104
|
+
provenance: "vendor-pricing-page",
|
|
1105
|
+
tiers: [
|
|
1106
|
+
{
|
|
1107
|
+
maxInputTokens: 2e5,
|
|
1108
|
+
inputPer1m: 2,
|
|
1109
|
+
outputPer1m: 12,
|
|
1110
|
+
cachedInputPer1m: 0.2,
|
|
1111
|
+
reasoningOutputPer1m: 12
|
|
1112
|
+
},
|
|
1113
|
+
{
|
|
1114
|
+
inputPer1m: 4,
|
|
1115
|
+
outputPer1m: 18,
|
|
1116
|
+
cachedInputPer1m: 0.4,
|
|
1117
|
+
reasoningOutputPer1m: 18
|
|
1118
|
+
}
|
|
1119
|
+
],
|
|
1120
|
+
batchMultiplier: 0.5,
|
|
1121
|
+
contextWindow: 1048576,
|
|
1122
|
+
notes: "Long-context tier: $2/$12 per 1M up to 200K prompt tokens, $4/$18 above it."
|
|
1123
|
+
},
|
|
1124
|
+
{
|
|
1125
|
+
model: "gemini-3-pro",
|
|
1126
|
+
provider: "gemini",
|
|
1127
|
+
aliases: [
|
|
1128
|
+
"gemini-3-pro-preview"
|
|
1129
|
+
],
|
|
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
|
+
maxInputTokens: 2e5,
|
|
1138
|
+
inputPer1m: 2,
|
|
1139
|
+
outputPer1m: 12,
|
|
1140
|
+
cachedInputPer1m: 0.2,
|
|
1141
|
+
reasoningOutputPer1m: 12
|
|
1142
|
+
},
|
|
1143
|
+
{
|
|
1144
|
+
inputPer1m: 4,
|
|
1145
|
+
outputPer1m: 18,
|
|
1146
|
+
cachedInputPer1m: 0.4,
|
|
1147
|
+
reasoningOutputPer1m: 18
|
|
1148
|
+
}
|
|
1149
|
+
],
|
|
1150
|
+
batchMultiplier: 0.5,
|
|
1151
|
+
contextWindow: 1048576
|
|
1152
|
+
},
|
|
1153
|
+
{
|
|
1154
|
+
model: "gemini-3.6-flash",
|
|
1155
|
+
provider: "gemini",
|
|
1156
|
+
modality: "text",
|
|
1157
|
+
currency: "USD",
|
|
1158
|
+
asOf: "2026-07-27",
|
|
1159
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1160
|
+
provenance: "vendor-pricing-page",
|
|
1161
|
+
tiers: [
|
|
1162
|
+
{
|
|
1163
|
+
inputPer1m: 1.5,
|
|
1164
|
+
outputPer1m: 7.5,
|
|
1165
|
+
cachedInputPer1m: 0.15,
|
|
1166
|
+
reasoningOutputPer1m: 7.5
|
|
1167
|
+
}
|
|
1168
|
+
],
|
|
1169
|
+
batchMultiplier: 0.5,
|
|
1170
|
+
contextWindow: 1048576,
|
|
1171
|
+
notes: "Released 2026-07-21. Output pricing dropped ~17% from Gemini 3.5 Flash ($9.00 -> $7.50 per 1M); input unchanged."
|
|
1172
|
+
},
|
|
1173
|
+
{
|
|
1174
|
+
model: "gemini-3.5-flash",
|
|
1175
|
+
provider: "gemini",
|
|
1176
|
+
modality: "text",
|
|
1177
|
+
currency: "USD",
|
|
1178
|
+
asOf: "2026-07-27",
|
|
1179
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1180
|
+
provenance: "vendor-pricing-page",
|
|
1181
|
+
tiers: [
|
|
1182
|
+
{
|
|
1183
|
+
inputPer1m: 1.5,
|
|
1184
|
+
outputPer1m: 9,
|
|
1185
|
+
cachedInputPer1m: 0.15,
|
|
1186
|
+
reasoningOutputPer1m: 9
|
|
1187
|
+
}
|
|
1188
|
+
],
|
|
1189
|
+
batchMultiplier: 0.5,
|
|
1190
|
+
contextWindow: 1048576
|
|
1191
|
+
},
|
|
1192
|
+
{
|
|
1193
|
+
model: "gemini-3.5-flash-lite",
|
|
1194
|
+
provider: "gemini",
|
|
1195
|
+
modality: "text",
|
|
1196
|
+
currency: "USD",
|
|
1197
|
+
asOf: "2026-07-27",
|
|
1198
|
+
source: "https://ai.google.dev/gemini-api/docs/pricing",
|
|
1199
|
+
provenance: "litellm-catalog",
|
|
1200
|
+
tiers: [
|
|
1201
|
+
{
|
|
1202
|
+
inputPer1m: 0.3,
|
|
1203
|
+
outputPer1m: 2.5,
|
|
1204
|
+
cachedInputPer1m: 0.03,
|
|
1205
|
+
reasoningOutputPer1m: 2.5
|
|
1206
|
+
}
|
|
1207
|
+
],
|
|
1208
|
+
batchMultiplier: 0.5,
|
|
1209
|
+
contextWindow: 1048576
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
model: "gemini-3.1-flash-lite",
|
|
1213
|
+
provider: "gemini",
|
|
1214
|
+
modality: "text",
|
|
1215
|
+
currency: "USD",
|
|
1216
|
+
asOf: "2026-07-27",
|
|
1217
|
+
source: "https://ai.google.dev/gemini-api/docs/pricing#gemini-3.1-flash-lite",
|
|
1218
|
+
provenance: "litellm-catalog",
|
|
1219
|
+
tiers: [
|
|
1220
|
+
{
|
|
1221
|
+
inputPer1m: 0.25,
|
|
1222
|
+
outputPer1m: 1.5,
|
|
1223
|
+
cachedInputPer1m: 0.025,
|
|
1224
|
+
reasoningOutputPer1m: 1.5
|
|
1225
|
+
}
|
|
1226
|
+
],
|
|
1227
|
+
batchMultiplier: 0.5,
|
|
1228
|
+
contextWindow: 1048576
|
|
1229
|
+
},
|
|
1230
|
+
{
|
|
1231
|
+
model: "gemini-3-flash",
|
|
1232
|
+
provider: "gemini",
|
|
1233
|
+
aliases: [
|
|
1234
|
+
"gemini-3-flash-preview"
|
|
1235
|
+
],
|
|
1236
|
+
modality: "text",
|
|
1237
|
+
currency: "USD",
|
|
1238
|
+
asOf: "2026-07-27",
|
|
1239
|
+
source: "https://ai.google.dev/pricing/gemini-3",
|
|
1240
|
+
provenance: "litellm-catalog",
|
|
1241
|
+
tiers: [
|
|
1242
|
+
{
|
|
1243
|
+
inputPer1m: 0.5,
|
|
1244
|
+
outputPer1m: 3,
|
|
1245
|
+
cachedInputPer1m: 0.05,
|
|
1246
|
+
reasoningOutputPer1m: 3
|
|
1247
|
+
}
|
|
1248
|
+
],
|
|
1249
|
+
contextWindow: 1048576
|
|
1250
|
+
},
|
|
1251
|
+
{
|
|
1252
|
+
model: "gemini-2.5-pro",
|
|
1253
|
+
provider: "gemini",
|
|
1254
|
+
modality: "text",
|
|
1255
|
+
currency: "USD",
|
|
1256
|
+
asOf: "2026-07-27",
|
|
1257
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1258
|
+
provenance: "litellm-catalog",
|
|
1259
|
+
tiers: [
|
|
1260
|
+
{
|
|
1261
|
+
maxInputTokens: 2e5,
|
|
1262
|
+
inputPer1m: 1.25,
|
|
1263
|
+
outputPer1m: 10,
|
|
1264
|
+
cachedInputPer1m: 0.125
|
|
1265
|
+
},
|
|
1266
|
+
{
|
|
1267
|
+
inputPer1m: 2.5,
|
|
1268
|
+
outputPer1m: 15,
|
|
1269
|
+
cachedInputPer1m: 0.25
|
|
1270
|
+
}
|
|
1271
|
+
],
|
|
1272
|
+
contextWindow: 1048576
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
model: "gemini-2.5-flash",
|
|
1276
|
+
provider: "gemini",
|
|
1277
|
+
modality: "text",
|
|
1278
|
+
currency: "USD",
|
|
1279
|
+
asOf: "2026-07-27",
|
|
1280
|
+
source: "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
|
1281
|
+
provenance: "litellm-catalog",
|
|
1282
|
+
tiers: [
|
|
1283
|
+
{
|
|
1284
|
+
inputPer1m: 0.3,
|
|
1285
|
+
outputPer1m: 2.5,
|
|
1286
|
+
cachedInputPer1m: 0.03,
|
|
1287
|
+
reasoningOutputPer1m: 2.5
|
|
1288
|
+
}
|
|
1289
|
+
],
|
|
1290
|
+
contextWindow: 1048576
|
|
1291
|
+
},
|
|
1292
|
+
{
|
|
1293
|
+
model: "gemini-2.5-flash-lite",
|
|
1294
|
+
provider: "gemini",
|
|
1295
|
+
modality: "text",
|
|
1296
|
+
currency: "USD",
|
|
1297
|
+
asOf: "2026-07-27",
|
|
1298
|
+
source: "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-lite",
|
|
1299
|
+
provenance: "litellm-catalog",
|
|
1300
|
+
tiers: [
|
|
1301
|
+
{
|
|
1302
|
+
inputPer1m: 0.1,
|
|
1303
|
+
outputPer1m: 0.4,
|
|
1304
|
+
cachedInputPer1m: 0.01,
|
|
1305
|
+
reasoningOutputPer1m: 0.4
|
|
1306
|
+
}
|
|
1307
|
+
],
|
|
1308
|
+
contextWindow: 1048576
|
|
1309
|
+
},
|
|
1310
|
+
{
|
|
1311
|
+
model: "gemini-2.0-flash",
|
|
1312
|
+
provider: "gemini",
|
|
1313
|
+
modality: "text",
|
|
1314
|
+
currency: "USD",
|
|
1315
|
+
asOf: "2026-07-27",
|
|
1316
|
+
source: "https://ai.google.dev/pricing#2_0flash",
|
|
1317
|
+
provenance: "litellm-catalog",
|
|
1318
|
+
tiers: [
|
|
1319
|
+
{
|
|
1320
|
+
inputPer1m: 0.1,
|
|
1321
|
+
outputPer1m: 0.4,
|
|
1322
|
+
cachedInputPer1m: 0.025
|
|
1323
|
+
}
|
|
1324
|
+
],
|
|
1325
|
+
contextWindow: 1048576,
|
|
1326
|
+
notes: "Gemini Developer API price. Vertex AI lists $0.15/$0.60 per 1M for the same model \u2014 see the vertex entry."
|
|
1327
|
+
},
|
|
1328
|
+
{
|
|
1329
|
+
model: "gemini-2.0-flash-lite",
|
|
1330
|
+
provider: "gemini",
|
|
1331
|
+
modality: "text",
|
|
1332
|
+
currency: "USD",
|
|
1333
|
+
asOf: "2026-07-27",
|
|
1334
|
+
source: "https://ai.google.dev/gemini-api/docs/pricing#gemini-2.0-flash-lite",
|
|
1335
|
+
provenance: "litellm-catalog",
|
|
1336
|
+
tiers: [
|
|
1337
|
+
{
|
|
1338
|
+
inputPer1m: 0.075,
|
|
1339
|
+
outputPer1m: 0.3,
|
|
1340
|
+
cachedInputPer1m: 0.01875
|
|
1341
|
+
}
|
|
1342
|
+
],
|
|
1343
|
+
contextWindow: 1048576
|
|
1344
|
+
},
|
|
1345
|
+
{
|
|
1346
|
+
model: "gemini-2.0-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: 0.15,
|
|
1356
|
+
outputPer1m: 0.6
|
|
1357
|
+
}
|
|
1358
|
+
],
|
|
1359
|
+
batchMultiplier: 0.5,
|
|
1360
|
+
notes: "Vertex AI lists $0.15/$0.60 per 1M; the Gemini Developer API lists $0.10/$0.40 for the same model."
|
|
1361
|
+
},
|
|
1362
|
+
{
|
|
1363
|
+
model: "gemini-3.1-pro",
|
|
1364
|
+
provider: "vertex",
|
|
1365
|
+
aliases: [
|
|
1366
|
+
"gemini-3.1-pro-preview"
|
|
1367
|
+
],
|
|
1368
|
+
modality: "text",
|
|
1369
|
+
currency: "USD",
|
|
1370
|
+
asOf: "2026-07-27",
|
|
1371
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1372
|
+
provenance: "vendor-pricing-page",
|
|
1373
|
+
tiers: [
|
|
1374
|
+
{
|
|
1375
|
+
maxInputTokens: 2e5,
|
|
1376
|
+
inputPer1m: 2,
|
|
1377
|
+
outputPer1m: 12,
|
|
1378
|
+
cachedInputPer1m: 0.2,
|
|
1379
|
+
reasoningOutputPer1m: 12
|
|
1380
|
+
},
|
|
1381
|
+
{
|
|
1382
|
+
inputPer1m: 4,
|
|
1383
|
+
outputPer1m: 18,
|
|
1384
|
+
cachedInputPer1m: 0.4,
|
|
1385
|
+
reasoningOutputPer1m: 18
|
|
1386
|
+
}
|
|
1387
|
+
],
|
|
1388
|
+
batchMultiplier: 0.5,
|
|
1389
|
+
contextWindow: 1048576
|
|
1390
|
+
},
|
|
1391
|
+
{
|
|
1392
|
+
model: "gemini-3.6-flash",
|
|
1393
|
+
provider: "vertex",
|
|
1394
|
+
modality: "text",
|
|
1395
|
+
currency: "USD",
|
|
1396
|
+
asOf: "2026-07-27",
|
|
1397
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1398
|
+
provenance: "vendor-pricing-page",
|
|
1399
|
+
tiers: [
|
|
1400
|
+
{
|
|
1401
|
+
inputPer1m: 1.5,
|
|
1402
|
+
outputPer1m: 7.5,
|
|
1403
|
+
cachedInputPer1m: 0.15,
|
|
1404
|
+
reasoningOutputPer1m: 7.5
|
|
1405
|
+
}
|
|
1406
|
+
],
|
|
1407
|
+
batchMultiplier: 0.5,
|
|
1408
|
+
contextWindow: 1048576
|
|
1409
|
+
},
|
|
1410
|
+
{
|
|
1411
|
+
model: "gemini-2.5-pro",
|
|
1412
|
+
provider: "vertex",
|
|
1413
|
+
modality: "text",
|
|
1414
|
+
currency: "USD",
|
|
1415
|
+
asOf: "2026-07-27",
|
|
1416
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1417
|
+
provenance: "vendor-pricing-page",
|
|
1418
|
+
tiers: [
|
|
1419
|
+
{
|
|
1420
|
+
maxInputTokens: 2e5,
|
|
1421
|
+
inputPer1m: 1.25,
|
|
1422
|
+
outputPer1m: 10,
|
|
1423
|
+
cachedInputPer1m: 0.13
|
|
1424
|
+
},
|
|
1425
|
+
{
|
|
1426
|
+
inputPer1m: 2.5,
|
|
1427
|
+
outputPer1m: 15,
|
|
1428
|
+
cachedInputPer1m: 0.25
|
|
1429
|
+
}
|
|
1430
|
+
],
|
|
1431
|
+
batchMultiplier: 0.5,
|
|
1432
|
+
contextWindow: 1048576
|
|
1433
|
+
},
|
|
1434
|
+
{
|
|
1435
|
+
model: "gemini-2.5-flash",
|
|
1436
|
+
provider: "vertex",
|
|
1437
|
+
modality: "text",
|
|
1438
|
+
currency: "USD",
|
|
1439
|
+
asOf: "2026-07-27",
|
|
1440
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1441
|
+
provenance: "vendor-pricing-page",
|
|
1442
|
+
tiers: [
|
|
1443
|
+
{
|
|
1444
|
+
inputPer1m: 0.3,
|
|
1445
|
+
outputPer1m: 2.5,
|
|
1446
|
+
cachedInputPer1m: 0.03,
|
|
1447
|
+
reasoningOutputPer1m: 2.5
|
|
1448
|
+
}
|
|
1449
|
+
],
|
|
1450
|
+
batchMultiplier: 0.5,
|
|
1451
|
+
contextWindow: 1048576
|
|
1452
|
+
},
|
|
1453
|
+
{
|
|
1454
|
+
model: "claude-sonnet-4-6",
|
|
1455
|
+
provider: "openrouter",
|
|
1456
|
+
modality: "text",
|
|
1457
|
+
currency: "USD",
|
|
1458
|
+
asOf: "2026-07-27",
|
|
1459
|
+
source: "https://openrouter.ai/anthropic/claude-sonnet-4.6",
|
|
1460
|
+
provenance: "litellm-catalog",
|
|
1461
|
+
tiers: [
|
|
1462
|
+
{
|
|
1463
|
+
maxInputTokens: 2e5,
|
|
1464
|
+
inputPer1m: 3,
|
|
1465
|
+
outputPer1m: 15,
|
|
1466
|
+
cachedInputPer1m: 0.3,
|
|
1467
|
+
cacheWritePer1m: 3.75
|
|
1468
|
+
},
|
|
1469
|
+
{
|
|
1470
|
+
inputPer1m: 6,
|
|
1471
|
+
outputPer1m: 22.5,
|
|
1472
|
+
cachedInputPer1m: 0.6,
|
|
1473
|
+
cacheWritePer1m: 7.5
|
|
1474
|
+
}
|
|
1475
|
+
],
|
|
1476
|
+
contextWindow: 1e6
|
|
1477
|
+
},
|
|
1478
|
+
{
|
|
1479
|
+
model: "claude-haiku-4-5",
|
|
1480
|
+
provider: "openrouter",
|
|
1481
|
+
modality: "text",
|
|
1482
|
+
currency: "USD",
|
|
1483
|
+
asOf: "2026-07-27",
|
|
1484
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1485
|
+
provenance: "litellm-catalog",
|
|
1486
|
+
tiers: [
|
|
1487
|
+
{
|
|
1488
|
+
inputPer1m: 1,
|
|
1489
|
+
outputPer1m: 5,
|
|
1490
|
+
cachedInputPer1m: 0.1,
|
|
1491
|
+
cacheWritePer1m: 1.25
|
|
1492
|
+
}
|
|
1493
|
+
],
|
|
1494
|
+
contextWindow: 2e5
|
|
1495
|
+
},
|
|
1496
|
+
{
|
|
1497
|
+
model: "gpt-5",
|
|
1498
|
+
provider: "openrouter",
|
|
1499
|
+
modality: "text",
|
|
1500
|
+
currency: "USD",
|
|
1501
|
+
asOf: "2026-07-27",
|
|
1502
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1503
|
+
provenance: "litellm-catalog",
|
|
1504
|
+
tiers: [
|
|
1505
|
+
{
|
|
1506
|
+
inputPer1m: 1.25,
|
|
1507
|
+
outputPer1m: 10,
|
|
1508
|
+
cachedInputPer1m: 0.125
|
|
1509
|
+
}
|
|
1510
|
+
],
|
|
1511
|
+
contextWindow: 272e3
|
|
1512
|
+
},
|
|
1513
|
+
{
|
|
1514
|
+
model: "gpt-5-mini",
|
|
1515
|
+
provider: "openrouter",
|
|
1516
|
+
modality: "text",
|
|
1517
|
+
currency: "USD",
|
|
1518
|
+
asOf: "2026-07-27",
|
|
1519
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1520
|
+
provenance: "litellm-catalog",
|
|
1521
|
+
tiers: [
|
|
1522
|
+
{
|
|
1523
|
+
inputPer1m: 0.25,
|
|
1524
|
+
outputPer1m: 2,
|
|
1525
|
+
cachedInputPer1m: 0.025
|
|
1526
|
+
}
|
|
1527
|
+
],
|
|
1528
|
+
contextWindow: 272e3
|
|
1529
|
+
},
|
|
1530
|
+
{
|
|
1531
|
+
model: "gemini-3.1-pro",
|
|
1532
|
+
provider: "openrouter",
|
|
1533
|
+
aliases: [
|
|
1534
|
+
"gemini-3.1-pro-preview"
|
|
1535
|
+
],
|
|
1536
|
+
modality: "text",
|
|
1537
|
+
currency: "USD",
|
|
1538
|
+
asOf: "2026-07-27",
|
|
1539
|
+
source: "https://openrouter.ai/google/gemini-3.1-pro-preview",
|
|
1540
|
+
provenance: "litellm-catalog",
|
|
1541
|
+
tiers: [
|
|
1542
|
+
{
|
|
1543
|
+
maxInputTokens: 2e5,
|
|
1544
|
+
inputPer1m: 2,
|
|
1545
|
+
outputPer1m: 12,
|
|
1546
|
+
cachedInputPer1m: 0.2
|
|
1547
|
+
},
|
|
1548
|
+
{
|
|
1549
|
+
inputPer1m: 4,
|
|
1550
|
+
outputPer1m: 18,
|
|
1551
|
+
cachedInputPer1m: 0.4,
|
|
1552
|
+
cacheWritePer1m: 0.25
|
|
1553
|
+
}
|
|
1554
|
+
],
|
|
1555
|
+
contextWindow: 1048576
|
|
1556
|
+
},
|
|
1557
|
+
{
|
|
1558
|
+
model: "text-embedding-3-small",
|
|
1559
|
+
provider: "openai",
|
|
1560
|
+
modality: "embedding",
|
|
1561
|
+
currency: "USD",
|
|
1562
|
+
asOf: "2026-07-27",
|
|
1563
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1564
|
+
provenance: "litellm-catalog",
|
|
1565
|
+
tiers: [
|
|
1566
|
+
{
|
|
1567
|
+
inputPer1m: 0.02,
|
|
1568
|
+
outputPer1m: 0
|
|
1569
|
+
}
|
|
1570
|
+
],
|
|
1571
|
+
batchMultiplier: 0.5,
|
|
1572
|
+
contextWindow: 8191
|
|
1573
|
+
},
|
|
1574
|
+
{
|
|
1575
|
+
model: "text-embedding-3-large",
|
|
1576
|
+
provider: "openai",
|
|
1577
|
+
modality: "embedding",
|
|
1578
|
+
currency: "USD",
|
|
1579
|
+
asOf: "2026-07-27",
|
|
1580
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1581
|
+
provenance: "litellm-catalog",
|
|
1582
|
+
tiers: [
|
|
1583
|
+
{
|
|
1584
|
+
inputPer1m: 0.13,
|
|
1585
|
+
outputPer1m: 0
|
|
1586
|
+
}
|
|
1587
|
+
],
|
|
1588
|
+
batchMultiplier: 0.5,
|
|
1589
|
+
contextWindow: 8191
|
|
1590
|
+
},
|
|
1591
|
+
{
|
|
1592
|
+
model: "gemini-embedding-001",
|
|
1593
|
+
provider: "gemini",
|
|
1594
|
+
modality: "embedding",
|
|
1595
|
+
currency: "USD",
|
|
1596
|
+
asOf: "2026-07-27",
|
|
1597
|
+
source: "https://ai.google.dev/gemini-api/docs/embeddings#model-versions",
|
|
1598
|
+
provenance: "litellm-catalog",
|
|
1599
|
+
tiers: [
|
|
1600
|
+
{
|
|
1601
|
+
inputPer1m: 0.15,
|
|
1602
|
+
outputPer1m: 0
|
|
1603
|
+
}
|
|
1604
|
+
],
|
|
1605
|
+
contextWindow: 2048
|
|
1606
|
+
},
|
|
1607
|
+
{
|
|
1608
|
+
model: "dall-e-3",
|
|
1609
|
+
provider: "openai",
|
|
1610
|
+
modality: "image",
|
|
1611
|
+
currency: "USD",
|
|
1612
|
+
asOf: "2026-07-27",
|
|
1613
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1614
|
+
provenance: "litellm-catalog",
|
|
1615
|
+
rates: [
|
|
1616
|
+
{
|
|
1617
|
+
unit: "per_image",
|
|
1618
|
+
amount: 0.04,
|
|
1619
|
+
variant: "1024x1024 standard"
|
|
1620
|
+
}
|
|
1621
|
+
]
|
|
1622
|
+
},
|
|
1623
|
+
{
|
|
1624
|
+
model: "gemini-3-pro-image",
|
|
1625
|
+
provider: "gemini",
|
|
1626
|
+
modality: "image",
|
|
1627
|
+
currency: "USD",
|
|
1628
|
+
asOf: "2026-07-27",
|
|
1629
|
+
source: "https://ai.google.dev/gemini-api/docs/pricing",
|
|
1630
|
+
provenance: "litellm-catalog",
|
|
1631
|
+
rates: [
|
|
1632
|
+
{
|
|
1633
|
+
unit: "per_image",
|
|
1634
|
+
amount: 0.134,
|
|
1635
|
+
variant: "default output resolution"
|
|
1636
|
+
}
|
|
1637
|
+
],
|
|
1638
|
+
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."
|
|
1639
|
+
},
|
|
1640
|
+
{
|
|
1641
|
+
model: "imagen-4.0-generate-001",
|
|
1642
|
+
provider: "vertex",
|
|
1643
|
+
modality: "image",
|
|
1644
|
+
currency: "USD",
|
|
1645
|
+
asOf: "2026-07-27",
|
|
1646
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/pricing",
|
|
1647
|
+
provenance: "litellm-catalog",
|
|
1648
|
+
rates: [
|
|
1649
|
+
{
|
|
1650
|
+
unit: "per_image",
|
|
1651
|
+
amount: 0.04
|
|
1652
|
+
}
|
|
1653
|
+
]
|
|
1654
|
+
},
|
|
1655
|
+
{
|
|
1656
|
+
model: "veo-3.1-generate-001",
|
|
1657
|
+
provider: "gemini",
|
|
1658
|
+
modality: "video",
|
|
1659
|
+
currency: "USD",
|
|
1660
|
+
asOf: "2026-07-27",
|
|
1661
|
+
source: "https://ai.google.dev/gemini-api/docs/video",
|
|
1662
|
+
provenance: "litellm-catalog",
|
|
1663
|
+
rates: [
|
|
1664
|
+
{
|
|
1665
|
+
unit: "per_second",
|
|
1666
|
+
amount: 0.4
|
|
1667
|
+
}
|
|
1668
|
+
]
|
|
1669
|
+
},
|
|
1670
|
+
{
|
|
1671
|
+
model: "veo-3.1-generate-001",
|
|
1672
|
+
provider: "vertex",
|
|
1673
|
+
modality: "video",
|
|
1674
|
+
currency: "USD",
|
|
1675
|
+
asOf: "2026-07-27",
|
|
1676
|
+
source: "https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/veo",
|
|
1677
|
+
provenance: "litellm-catalog",
|
|
1678
|
+
rates: [
|
|
1679
|
+
{
|
|
1680
|
+
unit: "per_second",
|
|
1681
|
+
amount: 0.4
|
|
1682
|
+
}
|
|
1683
|
+
]
|
|
1684
|
+
},
|
|
1685
|
+
{
|
|
1686
|
+
model: "tts-1",
|
|
1687
|
+
provider: "openai",
|
|
1688
|
+
modality: "speech",
|
|
1689
|
+
currency: "USD",
|
|
1690
|
+
asOf: "2026-07-27",
|
|
1691
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1692
|
+
provenance: "litellm-catalog",
|
|
1693
|
+
rates: [
|
|
1694
|
+
{
|
|
1695
|
+
unit: "per_character",
|
|
1696
|
+
amount: 15e-6
|
|
1697
|
+
}
|
|
1698
|
+
],
|
|
1699
|
+
notes: "$15 per 1M characters."
|
|
1700
|
+
},
|
|
1701
|
+
{
|
|
1702
|
+
model: "tts-1-hd",
|
|
1703
|
+
provider: "openai",
|
|
1704
|
+
modality: "speech",
|
|
1705
|
+
currency: "USD",
|
|
1706
|
+
asOf: "2026-07-27",
|
|
1707
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1708
|
+
provenance: "litellm-catalog",
|
|
1709
|
+
rates: [
|
|
1710
|
+
{
|
|
1711
|
+
unit: "per_character",
|
|
1712
|
+
amount: 3e-5
|
|
1713
|
+
}
|
|
1714
|
+
],
|
|
1715
|
+
notes: "$30 per 1M characters."
|
|
1716
|
+
},
|
|
1717
|
+
{
|
|
1718
|
+
model: "whisper-1",
|
|
1719
|
+
provider: "openai",
|
|
1720
|
+
modality: "transcription",
|
|
1721
|
+
currency: "USD",
|
|
1722
|
+
asOf: "2026-07-27",
|
|
1723
|
+
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
1724
|
+
provenance: "litellm-catalog",
|
|
1725
|
+
rates: [
|
|
1726
|
+
{
|
|
1727
|
+
unit: "per_audio_minute",
|
|
1728
|
+
amount: 6e-3
|
|
1729
|
+
}
|
|
1730
|
+
],
|
|
1731
|
+
notes: "litellm records $0.0001 per audio second; $0.006/minute is that rate x 60."
|
|
1732
|
+
}
|
|
1733
|
+
]
|
|
1734
|
+
};
|
|
1735
|
+
|
|
1736
|
+
// src/core/router/pricing.ts
|
|
1737
|
+
var DATASET = pricing_data_default;
|
|
1738
|
+
function normalizeForPricing(id) {
|
|
1739
|
+
let s = id.trim().toLowerCase();
|
|
1740
|
+
s = s.replace(/^models\//, "");
|
|
1741
|
+
const slash = s.lastIndexOf("/");
|
|
1742
|
+
if (slash !== -1) s = s.slice(slash + 1);
|
|
1743
|
+
const tag = s.search(/[:@]/);
|
|
1744
|
+
if (tag !== -1) s = s.slice(0, tag);
|
|
1745
|
+
s = s.replace(/-latest$/, "");
|
|
1746
|
+
s = s.replace(/-preview(?:-\d{2}-\d{2})?$/, "");
|
|
1747
|
+
s = s.replace(/-\d{4}-\d{2}-\d{2}$/, "");
|
|
1748
|
+
s = s.replace(/-\d{8}$/, "");
|
|
1749
|
+
s = s.replace(/-preview$/, "");
|
|
1750
|
+
return s;
|
|
1751
|
+
}
|
|
1752
|
+
function indexKey(model, provider, region) {
|
|
1753
|
+
return `${provider}|${normalizeForPricing(model)}|${region ?? ""}`;
|
|
1754
|
+
}
|
|
1755
|
+
var INDEX = /* @__PURE__ */ new Map();
|
|
1756
|
+
for (const entry of DATASET.entries) {
|
|
1757
|
+
for (const name of [entry.model, ...entry.aliases ?? []]) {
|
|
1758
|
+
const withRegion = indexKey(name, entry.provider, entry.region);
|
|
1759
|
+
if (!INDEX.has(withRegion)) INDEX.set(withRegion, entry);
|
|
1760
|
+
const bare = indexKey(name, entry.provider);
|
|
1761
|
+
const existing = INDEX.get(bare);
|
|
1762
|
+
if (!existing || existing.region && existing.region !== "global" && entry.region === "global") {
|
|
1763
|
+
INDEX.set(bare, entry);
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
function findPricing(modelId, provider, opts = {}) {
|
|
1768
|
+
const ids = [modelId, opts.baseModelId].filter((x) => !!x);
|
|
1769
|
+
const providers = [provider, ...opts.fallbackProviders ?? []];
|
|
1770
|
+
for (const p of providers) {
|
|
1771
|
+
for (const id of ids) {
|
|
1772
|
+
const hit = (opts.region ? INDEX.get(indexKey(id, p, opts.region)) : void 0) ?? INDEX.get(indexKey(id, p));
|
|
1773
|
+
if (hit) return { entry: hit, matchedProvider: p, viaFallback: p !== provider };
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
return null;
|
|
1777
|
+
}
|
|
1778
|
+
function tierFor(entry, inputTokens = 0) {
|
|
1779
|
+
const tiers = entry.tiers;
|
|
1780
|
+
if (!tiers?.length) return null;
|
|
1781
|
+
for (const t of tiers) {
|
|
1782
|
+
if (t.maxInputTokens === void 0 || inputTokens <= t.maxInputTokens) return t;
|
|
1783
|
+
}
|
|
1784
|
+
return tiers[tiers.length - 1] ?? null;
|
|
1785
|
+
}
|
|
1786
|
+
var PER_1M_TO_PER_1K = 1 / 1e3;
|
|
1787
|
+
function tokenRatesPer1k(entry, opts = {}) {
|
|
1788
|
+
const tier = tierFor(entry, opts.inputTokens ?? 0);
|
|
1789
|
+
if (!tier) return null;
|
|
1790
|
+
const mult = (opts.batch ? entry.batchMultiplier ?? 1 : 1) * PER_1M_TO_PER_1K;
|
|
1791
|
+
const out = {
|
|
1792
|
+
input: tier.inputPer1m * mult,
|
|
1793
|
+
output: tier.outputPer1m * mult
|
|
1794
|
+
};
|
|
1795
|
+
if (tier.cachedInputPer1m !== void 0) out.cachedInput = tier.cachedInputPer1m * mult;
|
|
1796
|
+
if (tier.cacheWritePer1m !== void 0) out.cacheWrite = tier.cacheWritePer1m * mult;
|
|
1797
|
+
if (tier.reasoningOutputPer1m !== void 0) out.reasoningOutput = tier.reasoningOutputPer1m * mult;
|
|
1798
|
+
return out;
|
|
1799
|
+
}
|
|
1800
|
+
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]"]);
|
|
1801
|
+
function isLoopbackOrPrivateHost(url) {
|
|
1802
|
+
if (!url) return false;
|
|
1803
|
+
try {
|
|
1804
|
+
const host = new URL(url).hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
1805
|
+
if (LOOPBACK_HOSTS.has(host) || LOOPBACK_HOSTS.has(`[${host}]`)) return true;
|
|
1806
|
+
if (host.endsWith(".local")) return true;
|
|
1807
|
+
if (/^127\./.test(host)) return true;
|
|
1808
|
+
if (/^10\./.test(host)) return true;
|
|
1809
|
+
if (/^192\.168\./.test(host)) return true;
|
|
1810
|
+
if (/^172\.(1[6-9]|2\d|3[01])\./.test(host)) return true;
|
|
1811
|
+
return false;
|
|
1812
|
+
} catch {
|
|
1813
|
+
return false;
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
function isLocalEndpoint(cfg) {
|
|
1817
|
+
if (typeof cfg.local === "boolean") return cfg.local;
|
|
1818
|
+
if (cfg.type === "ollama") return true;
|
|
1819
|
+
if (cfg.type === "openai-compatible") return isLoopbackOrPrivateHost(cfg.baseUrl);
|
|
1820
|
+
return false;
|
|
1821
|
+
}
|
|
1822
|
+
var UNKNOWN = { input: 0, output: 0, unknown: true, free: false };
|
|
1823
|
+
var FREE = { input: 0, output: 0, unknown: false, free: true };
|
|
1824
|
+
function resolvePricing(model, opts = {}) {
|
|
1825
|
+
if (model.isLocal) return FREE;
|
|
1826
|
+
const fallbackProviders = model.provider === "azure" ? ["openai"] : model.provider === "gemini" ? ["vertex"] : [];
|
|
1827
|
+
const match = findPricing(model.id, model.provider, {
|
|
1828
|
+
region: opts.region,
|
|
1829
|
+
baseModelId: model.baseModelId,
|
|
1830
|
+
fallbackProviders
|
|
1831
|
+
});
|
|
1832
|
+
if (!match) return UNKNOWN;
|
|
1833
|
+
const rates = tokenRatesPer1k(match.entry, { inputTokens: opts.inputTokens, batch: opts.batch });
|
|
1834
|
+
if (!rates) return UNKNOWN;
|
|
1835
|
+
return {
|
|
1836
|
+
input: rates.input,
|
|
1837
|
+
output: rates.output,
|
|
1838
|
+
unknown: false,
|
|
1839
|
+
free: false,
|
|
1840
|
+
entry: match.entry,
|
|
1841
|
+
...match.viaFallback ? { estimatedFromProvider: match.matchedProvider } : {}
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1844
|
+
function withResolvedPricing(model, opts = {}) {
|
|
1845
|
+
const p = resolvePricing(model, opts);
|
|
1846
|
+
return {
|
|
1847
|
+
...model,
|
|
1848
|
+
inputCostPer1kTokens: p.input,
|
|
1849
|
+
outputCostPer1kTokens: p.output,
|
|
1850
|
+
...p.unknown ? { pricingUnknown: true } : { pricingUnknown: false }
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1853
|
+
function isPricingUnknown(model) {
|
|
1854
|
+
return model.pricingUnknown === true && !model.isLocal;
|
|
1855
|
+
}
|
|
1856
|
+
var BLENDED_COST_CEILING = 0.05;
|
|
1857
|
+
function blendedCostPer1k(model) {
|
|
1858
|
+
if (isPricingUnknown(model)) return BLENDED_COST_CEILING;
|
|
1859
|
+
return model.inputCostPer1kTokens + model.outputCostPer1kTokens * 2;
|
|
1860
|
+
}
|
|
1861
|
+
var DISAGREEMENT_THRESHOLD = 0.02;
|
|
1862
|
+
function reconcilePrice(model, live) {
|
|
1863
|
+
const baseline = resolvePricing(model);
|
|
1864
|
+
const liveIsZero = !(live.input > 0) && !(live.output > 0);
|
|
1865
|
+
if (liveIsZero && !model.isLocal) {
|
|
1866
|
+
return { input: baseline.input, output: baseline.output, accepted: false };
|
|
1867
|
+
}
|
|
1868
|
+
if (baseline.unknown || !baseline.entry) {
|
|
1869
|
+
return { input: live.input, output: live.output, accepted: true };
|
|
1870
|
+
}
|
|
1871
|
+
const dsBlend = baseline.input + baseline.output * 2;
|
|
1872
|
+
const liveBlend = live.input + live.output * 2;
|
|
1873
|
+
const ratio = dsBlend > 0 ? liveBlend / dsBlend : Number.POSITIVE_INFINITY;
|
|
1874
|
+
const material = dsBlend > 0 && Math.abs(ratio - 1) > DISAGREEMENT_THRESHOLD;
|
|
1875
|
+
return {
|
|
1876
|
+
input: live.input,
|
|
1877
|
+
output: live.output,
|
|
1878
|
+
accepted: true,
|
|
1879
|
+
...material ? {
|
|
1880
|
+
disagreement: {
|
|
1881
|
+
modelId: model.id,
|
|
1882
|
+
provider: model.provider,
|
|
1883
|
+
dataset: { input: baseline.input, output: baseline.output },
|
|
1884
|
+
live: { input: live.input, output: live.output },
|
|
1885
|
+
ratio: Math.round(ratio * 1e3) / 1e3,
|
|
1886
|
+
datasetAsOf: baseline.entry.asOf,
|
|
1887
|
+
datasetSource: baseline.entry.source
|
|
1888
|
+
}
|
|
1889
|
+
} : {}
|
|
1890
|
+
};
|
|
1891
|
+
}
|
|
1892
|
+
|
|
241
1893
|
// src/constants.ts
|
|
242
|
-
var CASCADE_VERSION = "0.
|
|
1894
|
+
var CASCADE_VERSION = "0.50.0";
|
|
243
1895
|
var CASCADE_CONFIG_DIR = ".cascade";
|
|
244
1896
|
var CASCADE_MD_FILE = "CASCADE.md";
|
|
245
1897
|
var CASCADE_IGNORE_FILE = ".cascadeignore";
|
|
@@ -588,6 +2240,13 @@ var MODELS = {
|
|
|
588
2240
|
for (const _m of Object.values(MODELS)) {
|
|
589
2241
|
if (_m.supportsToolUse === void 0) _m.supportsToolUse = !_m.isLocal;
|
|
590
2242
|
}
|
|
2243
|
+
for (const _m of Object.values(MODELS)) {
|
|
2244
|
+
const priced = resolvePricing(_m);
|
|
2245
|
+
if (priced.unknown || priced.free || priced.estimatedFromProvider) continue;
|
|
2246
|
+
_m.inputCostPer1kTokens = priced.input;
|
|
2247
|
+
_m.outputCostPer1kTokens = priced.output;
|
|
2248
|
+
_m.pricingUnknown = false;
|
|
2249
|
+
}
|
|
591
2250
|
var T1_MODEL_PRIORITY = [
|
|
592
2251
|
"gpt-5.5",
|
|
593
2252
|
"gpt-5.4",
|
|
@@ -678,21 +2337,49 @@ var PROVIDER_DISPLAY_NAMES = {
|
|
|
678
2337
|
|
|
679
2338
|
// src/utils/cost.ts
|
|
680
2339
|
function resolveModelPricing(model) {
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
2340
|
+
if (model.isLocal) return { input: 0, output: 0, unknown: false };
|
|
2341
|
+
if (model.inputCostPer1kTokens > 0 || model.outputCostPer1kTokens > 0) {
|
|
2342
|
+
return {
|
|
2343
|
+
input: model.inputCostPer1kTokens,
|
|
2344
|
+
output: model.outputCostPer1kTokens,
|
|
2345
|
+
unknown: false
|
|
2346
|
+
};
|
|
2347
|
+
}
|
|
2348
|
+
const fromDataset = resolvePricing(model);
|
|
2349
|
+
if (!fromDataset.unknown) {
|
|
2350
|
+
return {
|
|
2351
|
+
input: fromDataset.input,
|
|
2352
|
+
output: fromDataset.output,
|
|
2353
|
+
unknown: false,
|
|
2354
|
+
...fromDataset.estimatedFromProvider ? { estimatedFromProvider: fromDataset.estimatedFromProvider } : {}
|
|
2355
|
+
};
|
|
2356
|
+
}
|
|
2357
|
+
if (model.id) {
|
|
684
2358
|
const known = Object.values(MODELS).find((m) => m.id === model.id && !m.isLocal);
|
|
685
|
-
if (known) {
|
|
686
|
-
|
|
687
|
-
|
|
2359
|
+
if (known && (known.inputCostPer1kTokens > 0 || known.outputCostPer1kTokens > 0)) {
|
|
2360
|
+
return {
|
|
2361
|
+
input: known.inputCostPer1kTokens,
|
|
2362
|
+
output: known.outputCostPer1kTokens,
|
|
2363
|
+
unknown: false
|
|
2364
|
+
};
|
|
688
2365
|
}
|
|
689
2366
|
}
|
|
690
|
-
return { input, output };
|
|
2367
|
+
return { input: 0, output: 0, unknown: true };
|
|
691
2368
|
}
|
|
692
2369
|
function calculateCost(inputTokens, outputTokens, model) {
|
|
693
2370
|
const { input, output } = resolveModelPricing(model);
|
|
694
2371
|
return inputTokens / 1e3 * input + outputTokens / 1e3 * output;
|
|
695
2372
|
}
|
|
2373
|
+
function buildTokenUsage(inputTokens, outputTokens, model) {
|
|
2374
|
+
const { input, output, unknown } = resolveModelPricing(model);
|
|
2375
|
+
return {
|
|
2376
|
+
inputTokens,
|
|
2377
|
+
outputTokens,
|
|
2378
|
+
totalTokens: inputTokens + outputTokens,
|
|
2379
|
+
estimatedCostUsd: inputTokens / 1e3 * input + outputTokens / 1e3 * output,
|
|
2380
|
+
...unknown ? { costUnknown: true } : {}
|
|
2381
|
+
};
|
|
2382
|
+
}
|
|
696
2383
|
|
|
697
2384
|
// src/providers/base.ts
|
|
698
2385
|
var BaseProvider = class {
|
|
@@ -712,12 +2399,7 @@ var BaseProvider = class {
|
|
|
712
2399
|
return calculateCost(inputTokens, outputTokens, this.model);
|
|
713
2400
|
}
|
|
714
2401
|
makeUsage(inputTokens, outputTokens) {
|
|
715
|
-
return
|
|
716
|
-
inputTokens,
|
|
717
|
-
outputTokens,
|
|
718
|
-
totalTokens: inputTokens + outputTokens,
|
|
719
|
-
estimatedCostUsd: this.estimateCost(inputTokens, outputTokens)
|
|
720
|
-
};
|
|
2402
|
+
return buildTokenUsage(inputTokens, outputTokens, this.model);
|
|
721
2403
|
}
|
|
722
2404
|
};
|
|
723
2405
|
|
|
@@ -886,7 +2568,7 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
886
2568
|
return data.data.filter((m) => isChatModel(m.id)).map((m) => {
|
|
887
2569
|
const known = Object.values(MODELS).find((km) => km.id === m.id && km.provider === "anthropic");
|
|
888
2570
|
if (known) return known;
|
|
889
|
-
return {
|
|
2571
|
+
return withResolvedPricing({
|
|
890
2572
|
id: m.id,
|
|
891
2573
|
name: m.display_name || m.id,
|
|
892
2574
|
provider: "anthropic",
|
|
@@ -897,7 +2579,7 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
897
2579
|
maxOutputTokens: 8e3,
|
|
898
2580
|
supportsStreaming: true,
|
|
899
2581
|
isLocal: false
|
|
900
|
-
};
|
|
2582
|
+
});
|
|
901
2583
|
});
|
|
902
2584
|
} catch {
|
|
903
2585
|
return Object.values(MODELS).filter((m) => m.provider === "anthropic");
|
|
@@ -1101,7 +2783,7 @@ var OpenAIProvider = class extends BaseProvider {
|
|
|
1101
2783
|
return response.data.filter((m) => isChatModel(m.id)).map((m) => {
|
|
1102
2784
|
const known = Object.values(MODELS).find((km) => km.id === m.id && km.provider === "openai");
|
|
1103
2785
|
if (known) return known;
|
|
1104
|
-
return {
|
|
2786
|
+
return withResolvedPricing({
|
|
1105
2787
|
id: m.id,
|
|
1106
2788
|
name: m.id,
|
|
1107
2789
|
provider: "openai",
|
|
@@ -1112,7 +2794,7 @@ var OpenAIProvider = class extends BaseProvider {
|
|
|
1112
2794
|
maxOutputTokens: 4e3,
|
|
1113
2795
|
supportsStreaming: true,
|
|
1114
2796
|
isLocal: false
|
|
1115
|
-
};
|
|
2797
|
+
});
|
|
1116
2798
|
});
|
|
1117
2799
|
} catch {
|
|
1118
2800
|
return Object.values(MODELS).filter((m) => m.provider === "openai");
|
|
@@ -1208,6 +2890,10 @@ function azureModelForDeployment(cfg) {
|
|
|
1208
2890
|
const baseModelId = cfg.model?.trim() || inferAzureBaseModel(id) || void 0;
|
|
1209
2891
|
const base = baseModelId ? MODELS[baseModelId] : void 0;
|
|
1210
2892
|
if (base) {
|
|
2893
|
+
const azurePrice = resolvePricing(
|
|
2894
|
+
{ id, provider: "azure", isLocal: false, baseModelId },
|
|
2895
|
+
{ region: cfg.region }
|
|
2896
|
+
);
|
|
1211
2897
|
return {
|
|
1212
2898
|
...base,
|
|
1213
2899
|
id,
|
|
@@ -1216,7 +2902,12 @@ function azureModelForDeployment(cfg) {
|
|
|
1216
2902
|
provider: "azure",
|
|
1217
2903
|
baseModelId,
|
|
1218
2904
|
// real identity for benchmark + live pricing
|
|
1219
|
-
supportsToolUse: base.supportsToolUse ?? true
|
|
2905
|
+
supportsToolUse: base.supportsToolUse ?? true,
|
|
2906
|
+
...azurePrice.unknown ? {} : {
|
|
2907
|
+
inputCostPer1kTokens: azurePrice.input,
|
|
2908
|
+
outputCostPer1kTokens: azurePrice.output,
|
|
2909
|
+
pricingUnknown: false
|
|
2910
|
+
}
|
|
1220
2911
|
};
|
|
1221
2912
|
}
|
|
1222
2913
|
return {
|
|
@@ -1379,7 +3070,7 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
1379
3070
|
(km) => km.id === id && km.provider === "gemini"
|
|
1380
3071
|
);
|
|
1381
3072
|
if (known) return known;
|
|
1382
|
-
return {
|
|
3073
|
+
return withResolvedPricing({
|
|
1383
3074
|
id,
|
|
1384
3075
|
name: m.displayName || id,
|
|
1385
3076
|
provider: "gemini",
|
|
@@ -1390,7 +3081,7 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
1390
3081
|
maxOutputTokens: m.outputTokenLimit || 8e3,
|
|
1391
3082
|
supportsStreaming: true,
|
|
1392
3083
|
isLocal: false
|
|
1393
|
-
};
|
|
3084
|
+
});
|
|
1394
3085
|
});
|
|
1395
3086
|
} catch {
|
|
1396
3087
|
return Object.values(MODELS).filter((m) => m.provider === "gemini");
|
|
@@ -1737,9 +3428,10 @@ var OllamaProvider = class extends BaseProvider {
|
|
|
1737
3428
|
return supportedKeywords.some((k) => name.includes(k)) && isChatModel(name);
|
|
1738
3429
|
});
|
|
1739
3430
|
const shows = await Promise.all(entries.map((m) => this.showModel(m.name)));
|
|
3431
|
+
const local = isLocalEndpoint({ ...this.config, type: "ollama" });
|
|
1740
3432
|
return entries.map((m, i) => {
|
|
1741
3433
|
const show = shows[i];
|
|
1742
|
-
return {
|
|
3434
|
+
return withResolvedPricing({
|
|
1743
3435
|
id: m.name,
|
|
1744
3436
|
name: m.name,
|
|
1745
3437
|
provider: "ollama",
|
|
@@ -1749,10 +3441,10 @@ var OllamaProvider = class extends BaseProvider {
|
|
|
1749
3441
|
outputCostPer1kTokens: 0,
|
|
1750
3442
|
maxOutputTokens: 4e3,
|
|
1751
3443
|
supportsStreaming: true,
|
|
1752
|
-
isLocal:
|
|
3444
|
+
isLocal: local,
|
|
1753
3445
|
supportsToolUse: show?.tools ?? isToolCapable(m.name),
|
|
1754
3446
|
minSizeB: this.parseSizeB(m.details?.parameter_size)
|
|
1755
|
-
};
|
|
3447
|
+
});
|
|
1756
3448
|
});
|
|
1757
3449
|
} catch {
|
|
1758
3450
|
return [];
|
|
@@ -1855,7 +3547,8 @@ var OpenAICompatibleProvider = class extends OpenAIProvider {
|
|
|
1855
3547
|
}).filter((x) => typeof x === "string" && x.length > 0);
|
|
1856
3548
|
if (ids.length === 0) return [this.model];
|
|
1857
3549
|
const chatIds = ids.filter((id) => isChatModel(id));
|
|
1858
|
-
|
|
3550
|
+
const local = isLocalEndpoint(this.config);
|
|
3551
|
+
return (chatIds.length ? chatIds : ids).map((id) => withResolvedPricing({
|
|
1859
3552
|
id,
|
|
1860
3553
|
name: id,
|
|
1861
3554
|
provider: "openai-compatible",
|
|
@@ -1865,7 +3558,7 @@ var OpenAICompatibleProvider = class extends OpenAIProvider {
|
|
|
1865
3558
|
outputCostPer1kTokens: 0,
|
|
1866
3559
|
maxOutputTokens: 4e3,
|
|
1867
3560
|
supportsStreaming: true,
|
|
1868
|
-
isLocal:
|
|
3561
|
+
isLocal: local
|
|
1869
3562
|
}));
|
|
1870
3563
|
}
|
|
1871
3564
|
async isAvailable() {
|
|
@@ -2080,7 +3773,7 @@ var ModelSelector = class {
|
|
|
2080
3773
|
else if (this.availableProviders.size === 1) providerStr = Array.from(this.availableProviders)[0];
|
|
2081
3774
|
}
|
|
2082
3775
|
if (providerStr && this.availableProviders.has(providerStr)) {
|
|
2083
|
-
const dynamicModel = {
|
|
3776
|
+
const dynamicModel = withResolvedPricing({
|
|
2084
3777
|
id: actualId,
|
|
2085
3778
|
name: actualId,
|
|
2086
3779
|
provider: providerStr,
|
|
@@ -2091,7 +3784,7 @@ var ModelSelector = class {
|
|
|
2091
3784
|
maxOutputTokens: 8e3,
|
|
2092
3785
|
supportsStreaming: true,
|
|
2093
3786
|
isLocal: providerStr === "ollama"
|
|
2094
|
-
};
|
|
3787
|
+
});
|
|
2095
3788
|
this.addDynamicModel(dynamicModel);
|
|
2096
3789
|
return dynamicModel;
|
|
2097
3790
|
}
|
|
@@ -2483,6 +4176,8 @@ var LiveDataProvider = class {
|
|
|
2483
4176
|
snapshot = null;
|
|
2484
4177
|
prices = /* @__PURE__ */ new Map();
|
|
2485
4178
|
capabilities = /* @__PURE__ */ new Map();
|
|
4179
|
+
/** Dataset-vs-live price mismatches, deduped by `provider:modelId`. */
|
|
4180
|
+
disagreements = /* @__PURE__ */ new Map();
|
|
2486
4181
|
source = "bundled";
|
|
2487
4182
|
fetchedAt = 0;
|
|
2488
4183
|
loaded = false;
|
|
@@ -2636,14 +4331,45 @@ var LiveDataProvider = class {
|
|
|
2636
4331
|
/**
|
|
2637
4332
|
* Returns a price-corrected copy of each model when live pricing is known,
|
|
2638
4333
|
* leaving the original untouched (so the shared catalog is never mutated).
|
|
4334
|
+
*
|
|
4335
|
+
* Reconciliation policy (see reconcilePrice in pricing.ts): the bundled
|
|
4336
|
+
* dataset is the authoritative baseline, and a live quote overrides it
|
|
4337
|
+
* because it is fresher by construction — but the disagreement is RECORDED,
|
|
4338
|
+
* not swallowed. A dataset/live mismatch is the best signal available that
|
|
4339
|
+
* the committed prices have gone stale, so it must be visible rather than
|
|
4340
|
+
* silently resolved in either direction. A live quote of $0 for a non-local
|
|
4341
|
+
* model is rejected outright: that is the very failure mode this change
|
|
4342
|
+
* exists to prevent, and a marketplace's free-tier alias must never zero out
|
|
4343
|
+
* the price of the paid model Cascade is actually calling.
|
|
2639
4344
|
*/
|
|
2640
4345
|
applyLivePricing(models) {
|
|
2641
4346
|
return models.map((m) => {
|
|
2642
4347
|
const p = this.getLivePrice(m.baseModelId ?? m.id);
|
|
2643
4348
|
if (!p) return m;
|
|
2644
|
-
|
|
4349
|
+
const reconciled = reconcilePrice(m, p);
|
|
4350
|
+
if (reconciled.disagreement) this.recordDisagreement(reconciled.disagreement);
|
|
4351
|
+
if (!reconciled.accepted) return m;
|
|
4352
|
+
return {
|
|
4353
|
+
...m,
|
|
4354
|
+
inputCostPer1kTokens: reconciled.input,
|
|
4355
|
+
outputCostPer1kTokens: reconciled.output,
|
|
4356
|
+
pricingUnknown: false
|
|
4357
|
+
};
|
|
2645
4358
|
});
|
|
2646
4359
|
}
|
|
4360
|
+
recordDisagreement(d) {
|
|
4361
|
+
const key = `${d.provider}:${d.modelId}`;
|
|
4362
|
+
if (this.disagreements.has(key)) return;
|
|
4363
|
+
this.disagreements.set(key, d);
|
|
4364
|
+
}
|
|
4365
|
+
/**
|
|
4366
|
+
* Dataset-vs-live price mismatches seen this session, newest-model-first
|
|
4367
|
+
* insertion order. Surfaced by `cascade models --verbose`; a non-empty list
|
|
4368
|
+
* means src/core/router/pricing-data.json is probably due a refresh.
|
|
4369
|
+
*/
|
|
4370
|
+
getPriceDisagreements() {
|
|
4371
|
+
return [...this.disagreements.values()];
|
|
4372
|
+
}
|
|
2647
4373
|
/** Current capability facts for a model id, or null when unknown. */
|
|
2648
4374
|
getCapability(modelId) {
|
|
2649
4375
|
return this.capabilities.get(normalizeModelId2(modelId)) ?? null;
|
|
@@ -2840,7 +4566,9 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
|
|
|
2840
4566
|
tokensByTier: {},
|
|
2841
4567
|
inputTokensByTier: {},
|
|
2842
4568
|
outputTokensByTier: {},
|
|
2843
|
-
costByFeature: {}
|
|
4569
|
+
costByFeature: {},
|
|
4570
|
+
untrackedCostCalls: 0,
|
|
4571
|
+
untrackedCostModels: []
|
|
2844
4572
|
};
|
|
2845
4573
|
tierModels = /* @__PURE__ */ new Map();
|
|
2846
4574
|
/**
|
|
@@ -3190,7 +4918,7 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
|
|
|
3190
4918
|
`Model ${model.id} inference timed out after ${cloudTimeoutMs}ms`
|
|
3191
4919
|
);
|
|
3192
4920
|
}
|
|
3193
|
-
const
|
|
4921
|
+
const corrected = buildTokenUsage(
|
|
3194
4922
|
result.usage.inputTokens,
|
|
3195
4923
|
result.usage.outputTokens,
|
|
3196
4924
|
model
|
|
@@ -3199,7 +4927,8 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
|
|
|
3199
4927
|
...result,
|
|
3200
4928
|
usage: {
|
|
3201
4929
|
...result.usage,
|
|
3202
|
-
estimatedCostUsd:
|
|
4930
|
+
estimatedCostUsd: corrected.estimatedCostUsd,
|
|
4931
|
+
...corrected.costUnknown ? { costUnknown: true } : { costUnknown: void 0 }
|
|
3203
4932
|
}
|
|
3204
4933
|
};
|
|
3205
4934
|
if (!result || typeof result.content !== "string" || !result.usage) {
|
|
@@ -3386,7 +5115,9 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
|
|
|
3386
5115
|
tokensByTier: { ...this.stats.tokensByTier },
|
|
3387
5116
|
inputTokensByTier: { ...this.stats.inputTokensByTier },
|
|
3388
5117
|
outputTokensByTier: { ...this.stats.outputTokensByTier },
|
|
3389
|
-
costByFeature: { ...this.stats.costByFeature }
|
|
5118
|
+
costByFeature: { ...this.stats.costByFeature },
|
|
5119
|
+
untrackedCostCalls: this.stats.untrackedCostCalls,
|
|
5120
|
+
untrackedCostModels: [...this.stats.untrackedCostModels]
|
|
3390
5121
|
};
|
|
3391
5122
|
}
|
|
3392
5123
|
/**
|
|
@@ -3437,7 +5168,9 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
|
|
|
3437
5168
|
tokensByTier: {},
|
|
3438
5169
|
inputTokensByTier: {},
|
|
3439
5170
|
outputTokensByTier: {},
|
|
3440
|
-
costByFeature: {}
|
|
5171
|
+
costByFeature: {},
|
|
5172
|
+
untrackedCostCalls: 0,
|
|
5173
|
+
untrackedCostModels: []
|
|
3441
5174
|
};
|
|
3442
5175
|
this.sessionCostUsd = 0;
|
|
3443
5176
|
this.budgetState = "ok";
|
|
@@ -3622,6 +5355,18 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
|
|
|
3622
5355
|
if (featureTag) {
|
|
3623
5356
|
this.stats.costByFeature[featureTag] = (this.stats.costByFeature[featureTag] ?? 0) + usage.estimatedCostUsd;
|
|
3624
5357
|
}
|
|
5358
|
+
if (usage.costUnknown) {
|
|
5359
|
+
this.stats.untrackedCostCalls += 1;
|
|
5360
|
+
if (!this.stats.untrackedCostModels.includes(model.id)) {
|
|
5361
|
+
this.stats.untrackedCostModels.push(model.id);
|
|
5362
|
+
const capped = this.config?.budget?.maxCostPerRunUsd != null || this.config?.budget?.sessionBudgetUsd != null;
|
|
5363
|
+
this.emit("cost:untracked", {
|
|
5364
|
+
modelId: model.id,
|
|
5365
|
+
provider: model.provider,
|
|
5366
|
+
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.`
|
|
5367
|
+
});
|
|
5368
|
+
}
|
|
5369
|
+
}
|
|
3625
5370
|
this.runTokens += usage.totalTokens;
|
|
3626
5371
|
this.runCostUsd += usage.estimatedCostUsd;
|
|
3627
5372
|
this.updateBudgetState();
|
|
@@ -3762,6 +5507,14 @@ var BaseTier = class extends EventEmitter__default.default {
|
|
|
3762
5507
|
this.emit("tier:status", event);
|
|
3763
5508
|
}
|
|
3764
5509
|
/** Record the model serving this tier; future status events carry it. */
|
|
5510
|
+
/**
|
|
5511
|
+
* The model actually serving this tier ("provider:id"), once one has been
|
|
5512
|
+
* chosen. The run breaker reads it to attribute a failure to a model rather
|
|
5513
|
+
* than to the subtask that happened to hit it.
|
|
5514
|
+
*/
|
|
5515
|
+
getServingModel() {
|
|
5516
|
+
return this.servingModel;
|
|
5517
|
+
}
|
|
3765
5518
|
setServingModel(model) {
|
|
3766
5519
|
this.servingModel = model || void 0;
|
|
3767
5520
|
}
|
|
@@ -5497,6 +7250,7 @@ var T2Manager = class extends BaseTier {
|
|
|
5497
7250
|
escalations = [];
|
|
5498
7251
|
peerSyncBuffer = [];
|
|
5499
7252
|
store;
|
|
7253
|
+
runBreaker;
|
|
5500
7254
|
t3PeerBus = new PeerBus();
|
|
5501
7255
|
// ← T3↔T3 bus (local to this T2)
|
|
5502
7256
|
t2PeerBus;
|
|
@@ -5527,6 +7281,15 @@ var T2Manager = class extends BaseTier {
|
|
|
5527
7281
|
this.router = router;
|
|
5528
7282
|
this.toolRegistry = toolRegistry;
|
|
5529
7283
|
}
|
|
7284
|
+
/**
|
|
7285
|
+
* Share the run-wide circuit breaker. Every T2 in a run gets the SAME
|
|
7286
|
+
* instance: three sections each failing once against a dead model is the
|
|
7287
|
+
* same fact as one section failing three times, and a per-section breaker
|
|
7288
|
+
* would never see it.
|
|
7289
|
+
*/
|
|
7290
|
+
setRunBreaker(breaker) {
|
|
7291
|
+
this.runBreaker = breaker;
|
|
7292
|
+
}
|
|
5530
7293
|
setStore(store) {
|
|
5531
7294
|
this.store = store;
|
|
5532
7295
|
}
|
|
@@ -5650,7 +7413,8 @@ Guidance (must be followed): ${decision.note}`
|
|
|
5650
7413
|
const overallStatus = this.determineStatus(t3Results);
|
|
5651
7414
|
const isOk = overallStatus === "COMPLETED" || overallStatus === "PARTIAL";
|
|
5652
7415
|
this.setStatus(isOk ? "COMPLETED" : "FAILED", summary);
|
|
5653
|
-
|
|
7416
|
+
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";
|
|
7417
|
+
this.sendStatusUpdate({ progressPct: 100, currentAction: sectionAction, status: "IN_PROGRESS", output: summary });
|
|
5654
7418
|
const result = {
|
|
5655
7419
|
sectionId: assignment.sectionId,
|
|
5656
7420
|
sectionTitle: assignment.sectionTitle,
|
|
@@ -5820,6 +7584,24 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
|
5820
7584
|
const reinforceCfg = this.router.getReinforcementsConfig?.() ?? { enabled: false, maxPerSection: 4 };
|
|
5821
7585
|
let reinforcementsAdded = 0;
|
|
5822
7586
|
while (remaining.size > 0) {
|
|
7587
|
+
if (this.runBreaker?.isOpen()) {
|
|
7588
|
+
const why = this.runBreaker.skipMessage();
|
|
7589
|
+
this.log(`Run breaker open \u2014 skipping ${remaining.size} remaining subtask(s)`);
|
|
7590
|
+
for (const id of remaining) {
|
|
7591
|
+
this.t3PeerBus.publish(this.id, id, why, "FAILED");
|
|
7592
|
+
resultMap.set(id, {
|
|
7593
|
+
subtaskId: id,
|
|
7594
|
+
status: "FAILED",
|
|
7595
|
+
output: why,
|
|
7596
|
+
testResults: { checksRun: [], passed: [], failed: [] },
|
|
7597
|
+
issues: [why],
|
|
7598
|
+
peerSyncsUsed: [],
|
|
7599
|
+
correctionAttempts: 0
|
|
7600
|
+
});
|
|
7601
|
+
}
|
|
7602
|
+
remaining.clear();
|
|
7603
|
+
break;
|
|
7604
|
+
}
|
|
5823
7605
|
const runnableIds = [...remaining].filter((id) => (inDegree.get(id) ?? 0) === 0);
|
|
5824
7606
|
if (runnableIds.length === 0) {
|
|
5825
7607
|
const fallbackId = [...remaining].sort(
|
|
@@ -5922,8 +7704,32 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
|
5922
7704
|
remaining.delete(id);
|
|
5923
7705
|
const r = waveResults[i];
|
|
5924
7706
|
if (r.status === "rejected") {
|
|
5925
|
-
this.log(`T3 worker ${id} failed: ${r.reason instanceof Error ? r.reason.message : String(r.reason)} \u2014 retrying once`);
|
|
5926
7707
|
const assignment = sanitizedAssignments.find((a) => a.subtaskId === id);
|
|
7708
|
+
const failedModel = workerMap.get(id)?.getServingModel();
|
|
7709
|
+
const tripped = this.runBreaker?.record(r.reason, failedModel);
|
|
7710
|
+
if (tripped) {
|
|
7711
|
+
this.log(`Run breaker opened: ${tripped.failures} consecutive ${tripped.kind} failures on ${tripped.modelId} \u2014 stopping`);
|
|
7712
|
+
this.emit("run:breaker", tripped);
|
|
7713
|
+
}
|
|
7714
|
+
if (this.runBreaker?.isOpen()) {
|
|
7715
|
+
const why = this.runBreaker.skipMessage();
|
|
7716
|
+
this.log(`T3 worker ${id} failed and the run breaker is open \u2014 not retrying`);
|
|
7717
|
+
this.t3PeerBus.publish(this.id, id, why, "FAILED");
|
|
7718
|
+
resultMap.set(id, {
|
|
7719
|
+
subtaskId: id,
|
|
7720
|
+
status: "FAILED",
|
|
7721
|
+
output: why,
|
|
7722
|
+
testResults: { checksRun: [], passed: [], failed: [] },
|
|
7723
|
+
issues: [why],
|
|
7724
|
+
peerSyncsUsed: [],
|
|
7725
|
+
correctionAttempts: 0
|
|
7726
|
+
});
|
|
7727
|
+
for (const dependent of adj.get(id) ?? []) {
|
|
7728
|
+
inDegree.set(dependent, Math.max(0, (inDegree.get(dependent) ?? 0) - 1));
|
|
7729
|
+
}
|
|
7730
|
+
continue;
|
|
7731
|
+
}
|
|
7732
|
+
this.log(`T3 worker ${id} failed: ${r.reason instanceof Error ? r.reason.message : String(r.reason)} \u2014 retrying once`);
|
|
5927
7733
|
try {
|
|
5928
7734
|
const retried = await this.retryT3(assignment, taskId);
|
|
5929
7735
|
resultMap.set(id, retried);
|
|
@@ -6255,6 +8061,7 @@ var T1Administrator = class extends BaseTier {
|
|
|
6255
8061
|
t2Managers = /* @__PURE__ */ new Map();
|
|
6256
8062
|
escalations = [];
|
|
6257
8063
|
store;
|
|
8064
|
+
runBreaker;
|
|
6258
8065
|
t2PeerBus = new PeerBus();
|
|
6259
8066
|
permissionEscalator;
|
|
6260
8067
|
toolCreator;
|
|
@@ -6269,6 +8076,10 @@ var T1Administrator = class extends BaseTier {
|
|
|
6269
8076
|
this.toolRegistry = toolRegistry;
|
|
6270
8077
|
this.config = config;
|
|
6271
8078
|
}
|
|
8079
|
+
/** Share the run-wide circuit breaker with every section manager. */
|
|
8080
|
+
setRunBreaker(breaker) {
|
|
8081
|
+
this.runBreaker = breaker;
|
|
8082
|
+
}
|
|
6272
8083
|
setStore(store) {
|
|
6273
8084
|
this.store = store;
|
|
6274
8085
|
}
|
|
@@ -6631,6 +8442,7 @@ SPEC RULES \u2014 each subtask is a self-contained spec slice (workers execute f
|
|
|
6631
8442
|
};
|
|
6632
8443
|
const managers = sections.map((section) => {
|
|
6633
8444
|
const manager = new T2Manager(this.router, this.toolRegistry, this.id);
|
|
8445
|
+
if (this.runBreaker) manager.setRunBreaker(this.runBreaker);
|
|
6634
8446
|
manager.setHierarchyContext(`You are a T2 Manager for the section "${section.sectionTitle}". You are part of a COMPLEX task overseen by T1 Administrator.`);
|
|
6635
8447
|
if (this.store) {
|
|
6636
8448
|
manager.setStore(this.store);
|
|
@@ -6916,6 +8728,158 @@ Reply with exactly one word: YES, NO, or UNSURE.
|
|
|
6916
8728
|
}
|
|
6917
8729
|
};
|
|
6918
8730
|
|
|
8731
|
+
// src/core/router/provider-errors.ts
|
|
8732
|
+
var SYSTEMIC = /* @__PURE__ */ new Set([
|
|
8733
|
+
"rate_limit",
|
|
8734
|
+
"quota_exhausted",
|
|
8735
|
+
"auth",
|
|
8736
|
+
"model_unavailable"
|
|
8737
|
+
]);
|
|
8738
|
+
function isSystemicKind(kind) {
|
|
8739
|
+
return SYSTEMIC.has(kind);
|
|
8740
|
+
}
|
|
8741
|
+
function statusOf(err) {
|
|
8742
|
+
if (!err || typeof err !== "object") return void 0;
|
|
8743
|
+
const e = err;
|
|
8744
|
+
for (const key of ["status", "statusCode", "code"]) {
|
|
8745
|
+
const v = e[key];
|
|
8746
|
+
if (typeof v === "number" && v >= 100 && v < 600) return v;
|
|
8747
|
+
}
|
|
8748
|
+
const resp = e["response"];
|
|
8749
|
+
if (resp && typeof resp["status"] === "number") return resp["status"];
|
|
8750
|
+
const inner = e["error"];
|
|
8751
|
+
if (inner && typeof inner["code"] === "number") return inner["code"];
|
|
8752
|
+
return void 0;
|
|
8753
|
+
}
|
|
8754
|
+
function messageOf(err) {
|
|
8755
|
+
if (err instanceof Error) return err.message;
|
|
8756
|
+
if (typeof err === "string") return err;
|
|
8757
|
+
if (err && typeof err === "object") {
|
|
8758
|
+
const e = err;
|
|
8759
|
+
if (typeof e["message"] === "string") return e["message"];
|
|
8760
|
+
const inner = e["error"];
|
|
8761
|
+
if (inner && typeof inner["message"] === "string") return inner["message"];
|
|
8762
|
+
try {
|
|
8763
|
+
return JSON.stringify(err);
|
|
8764
|
+
} catch {
|
|
8765
|
+
}
|
|
8766
|
+
}
|
|
8767
|
+
return String(err);
|
|
8768
|
+
}
|
|
8769
|
+
function classifyProviderError(err) {
|
|
8770
|
+
const raw = messageOf(err).trim();
|
|
8771
|
+
const status = statusOf(err);
|
|
8772
|
+
const m = raw.toLowerCase();
|
|
8773
|
+
const kind = (() => {
|
|
8774
|
+
if (status === 429) {
|
|
8775
|
+
return /quota|billing|credit|exceeded your current quota|insufficient/.test(m) ? "quota_exhausted" : "rate_limit";
|
|
8776
|
+
}
|
|
8777
|
+
if (status === 401 || status === 403) return "auth";
|
|
8778
|
+
if (status === 404) return "model_unavailable";
|
|
8779
|
+
if (/rate limit|too many requests|resource_exhausted/.test(m)) {
|
|
8780
|
+
return /quota|billing|credit|insufficient/.test(m) ? "quota_exhausted" : "rate_limit";
|
|
8781
|
+
}
|
|
8782
|
+
if (/quota|billing|insufficient_quota|insufficient credit|payment required/.test(m)) return "quota_exhausted";
|
|
8783
|
+
if (/api key|unauthorized|authentication|permission denied|invalid[_ ]api/.test(m)) return "auth";
|
|
8784
|
+
if (/model .*(not found|does not exist|unavailable)|no such model|unknown model|not supported for/.test(m)) {
|
|
8785
|
+
return "model_unavailable";
|
|
8786
|
+
}
|
|
8787
|
+
if (/context length|too many tokens|maximum context|prompt is too long|input too long/.test(m)) return "context_length";
|
|
8788
|
+
if (/safety|content filter|blocked|recitation|prohibited/.test(m)) return "content_filter";
|
|
8789
|
+
if (/econnreset|etimedout|enotfound|socket hang up|network|fetch failed|aborted/.test(m)) return "network";
|
|
8790
|
+
return "unknown";
|
|
8791
|
+
})();
|
|
8792
|
+
return { kind, systemic: isSystemicKind(kind), status, raw };
|
|
8793
|
+
}
|
|
8794
|
+
function describeProviderError(c, modelId) {
|
|
8795
|
+
const on = ` on ${modelId}` ;
|
|
8796
|
+
const detail = c.raw ? ` Provider said: ${c.raw}` : "";
|
|
8797
|
+
switch (c.kind) {
|
|
8798
|
+
case "rate_limit":
|
|
8799
|
+
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}`;
|
|
8800
|
+
case "quota_exhausted":
|
|
8801
|
+
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}`;
|
|
8802
|
+
case "auth":
|
|
8803
|
+
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}`;
|
|
8804
|
+
case "model_unavailable":
|
|
8805
|
+
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}`;
|
|
8806
|
+
case "context_length":
|
|
8807
|
+
return `The prompt was too long${on}. Reduce the attached context, or use a model with a larger context window for this tier.${detail}`;
|
|
8808
|
+
case "content_filter":
|
|
8809
|
+
return `The provider's safety filter refused this request${on}.${detail}`;
|
|
8810
|
+
case "network":
|
|
8811
|
+
return `Network error reaching the provider${on}. This is usually transient.${detail}`;
|
|
8812
|
+
default:
|
|
8813
|
+
return `The model call failed${on}.${detail}`;
|
|
8814
|
+
}
|
|
8815
|
+
}
|
|
8816
|
+
|
|
8817
|
+
// src/core/run-breaker.ts
|
|
8818
|
+
var DEFAULT_FAILURE_THRESHOLD = 3;
|
|
8819
|
+
var RunBreaker = class {
|
|
8820
|
+
threshold;
|
|
8821
|
+
/** modelId → consecutive systemic failures of the SAME kind. */
|
|
8822
|
+
counts = /* @__PURE__ */ new Map();
|
|
8823
|
+
trip = null;
|
|
8824
|
+
constructor(threshold = DEFAULT_FAILURE_THRESHOLD) {
|
|
8825
|
+
this.threshold = Math.max(1, threshold);
|
|
8826
|
+
}
|
|
8827
|
+
/**
|
|
8828
|
+
* Record a failed model call. Returns the trip if this failure is the one
|
|
8829
|
+
* that opened the breaker, otherwise null.
|
|
8830
|
+
*
|
|
8831
|
+
* A non-systemic failure RESETS the model's counter: it is evidence the model
|
|
8832
|
+
* is reachable and answering, which is exactly what a systemic streak claims
|
|
8833
|
+
* it is not.
|
|
8834
|
+
*/
|
|
8835
|
+
record(err, modelId) {
|
|
8836
|
+
if (this.trip) return null;
|
|
8837
|
+
const id = modelId?.trim() || "unknown-model";
|
|
8838
|
+
const c = classifyProviderError(err);
|
|
8839
|
+
if (!c.systemic) {
|
|
8840
|
+
this.counts.delete(id);
|
|
8841
|
+
return null;
|
|
8842
|
+
}
|
|
8843
|
+
const prev = this.counts.get(id);
|
|
8844
|
+
const n = prev && prev.kind === c.kind ? prev.n + 1 : 1;
|
|
8845
|
+
this.counts.set(id, { kind: c.kind, n, last: c });
|
|
8846
|
+
if (n >= this.threshold) {
|
|
8847
|
+
this.trip = {
|
|
8848
|
+
modelId: id,
|
|
8849
|
+
kind: c.kind,
|
|
8850
|
+
failures: n,
|
|
8851
|
+
message: describeProviderError(c, id)
|
|
8852
|
+
};
|
|
8853
|
+
return this.trip;
|
|
8854
|
+
}
|
|
8855
|
+
return null;
|
|
8856
|
+
}
|
|
8857
|
+
/** True once the run should stop launching work that would fail identically. */
|
|
8858
|
+
isOpen() {
|
|
8859
|
+
return this.trip !== null;
|
|
8860
|
+
}
|
|
8861
|
+
/** Why the breaker opened, or null while it is closed. */
|
|
8862
|
+
reason() {
|
|
8863
|
+
return this.trip;
|
|
8864
|
+
}
|
|
8865
|
+
/**
|
|
8866
|
+
* One line for a worker/section that never ran because the breaker was
|
|
8867
|
+
* already open. Says the work was skipped deliberately — not that it failed
|
|
8868
|
+
* on its own merits, which is what an empty FAILED status would imply.
|
|
8869
|
+
*/
|
|
8870
|
+
skipMessage() {
|
|
8871
|
+
const t = this.trip;
|
|
8872
|
+
if (!t) return "Skipped.";
|
|
8873
|
+
return `Skipped \u2014 the run stopped after ${t.failures} consecutive failures on ${t.modelId}. ${t.message}`;
|
|
8874
|
+
}
|
|
8875
|
+
/** Test/diagnostic view of the per-model streaks. */
|
|
8876
|
+
streaks() {
|
|
8877
|
+
const out = {};
|
|
8878
|
+
for (const [k, v] of this.counts) out[k] = { kind: v.kind, n: v.n };
|
|
8879
|
+
return out;
|
|
8880
|
+
}
|
|
8881
|
+
};
|
|
8882
|
+
|
|
6919
8883
|
// src/tools/base.ts
|
|
6920
8884
|
var BaseTool = class {
|
|
6921
8885
|
workspaceRoot = process.cwd();
|
|
@@ -9129,7 +11093,18 @@ var ProviderConfigSchema = zod.z.object({
|
|
|
9129
11093
|
apiVersion: zod.z.string().optional(),
|
|
9130
11094
|
model: zod.z.string().optional(),
|
|
9131
11095
|
authToken: zod.z.string().optional(),
|
|
9132
|
-
credentialSource: zod.z.string().optional()
|
|
11096
|
+
credentialSource: zod.z.string().optional(),
|
|
11097
|
+
/**
|
|
11098
|
+
* "This endpoint is self-hosted, so inference genuinely costs $0."
|
|
11099
|
+
* Only meaningful for `ollama` and `openai-compatible`. Unset ⇒ inferred:
|
|
11100
|
+
* ollama is local, openai-compatible is local when its baseUrl is
|
|
11101
|
+
* loopback/LAN. Set it explicitly to override — e.g. `false` for an Ollama
|
|
11102
|
+
* instance you rent, `true` for a llama.cpp box on your own LAN reached by
|
|
11103
|
+
* hostname.
|
|
11104
|
+
*/
|
|
11105
|
+
local: zod.z.boolean().optional(),
|
|
11106
|
+
/** Region for providers whose price varies by region (Azure: global | us | eu | …). */
|
|
11107
|
+
region: zod.z.string().optional()
|
|
9133
11108
|
});
|
|
9134
11109
|
var ModelOverridesSchema = zod.z.object({
|
|
9135
11110
|
t1: zod.z.string().optional(),
|
|
@@ -9715,8 +11690,8 @@ var TaskAnalyzer = class {
|
|
|
9715
11690
|
}
|
|
9716
11691
|
costEfficiency(model, complexity) {
|
|
9717
11692
|
if (this.tracker) return this.tracker.costEfficiencyScore(model, complexity);
|
|
9718
|
-
const blended = model
|
|
9719
|
-
const normalised = Math.min(1, blended /
|
|
11693
|
+
const blended = blendedCostPer1k(model);
|
|
11694
|
+
const normalised = Math.min(1, blended / BLENDED_COST_CEILING);
|
|
9720
11695
|
const complexityWeight = (6 - complexity) / 5;
|
|
9721
11696
|
return Math.max(0.1, 1 - normalised * complexityWeight);
|
|
9722
11697
|
}
|
|
@@ -9851,8 +11826,8 @@ var ModelPerformanceTracker = class {
|
|
|
9851
11826
|
* normalised over $0.05 blended as the "expensive" ceiling.
|
|
9852
11827
|
*/
|
|
9853
11828
|
costEfficiencyScore(model, complexity) {
|
|
9854
|
-
const blended = model
|
|
9855
|
-
const normalised = Math.min(1, blended /
|
|
11829
|
+
const blended = blendedCostPer1k(model);
|
|
11830
|
+
const normalised = Math.min(1, blended / BLENDED_COST_CEILING);
|
|
9856
11831
|
const complexityWeight = (6 - complexity) / 5;
|
|
9857
11832
|
return Math.max(0.1, 1 - normalised * complexityWeight);
|
|
9858
11833
|
}
|
|
@@ -12477,6 +14452,7 @@ ${prompt}` : prompt;
|
|
|
12477
14452
|
if (toolCreator) toolCreator.setPermissionEscalator(escalator);
|
|
12478
14453
|
const rootPrompt = buildContextualPrompt(options.prompt, options.conversationHistory);
|
|
12479
14454
|
let finalOutput = "";
|
|
14455
|
+
const runBreaker = new RunBreaker();
|
|
12480
14456
|
let t2Results = [];
|
|
12481
14457
|
let runError = null;
|
|
12482
14458
|
let identityPrompt = "";
|
|
@@ -12545,6 +14521,7 @@ ${prompt}` : prompt;
|
|
|
12545
14521
|
this.emit("tier:status", { tierId: "t3-root", status: "COMPLETED", role: "T3" });
|
|
12546
14522
|
} else if (complexity === "Moderate") {
|
|
12547
14523
|
const t2 = new T2Manager(this.router, this.toolRegistry, "root");
|
|
14524
|
+
t2.setRunBreaker(runBreaker);
|
|
12548
14525
|
t2.setPresenter(true);
|
|
12549
14526
|
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.");
|
|
12550
14527
|
if (identityPrompt) {
|
|
@@ -12605,6 +14582,7 @@ _(incomplete: ${reasons.join("; ")})_` : partial;
|
|
|
12605
14582
|
}
|
|
12606
14583
|
} else {
|
|
12607
14584
|
const t1 = new T1Administrator(this.router, this.toolRegistry, this.config);
|
|
14585
|
+
t1.setRunBreaker(runBreaker);
|
|
12608
14586
|
t1.setPresenter(true);
|
|
12609
14587
|
t1.setHierarchyContext("You are the top-level Administrator. You are responsible for the overall plan and supervising multiple T2 Managers.");
|
|
12610
14588
|
if (identityPrompt) {
|
|
@@ -12629,6 +14607,20 @@ _(incomplete: ${reasons.join("; ")})_` : partial;
|
|
|
12629
14607
|
finalOutput = result.output;
|
|
12630
14608
|
t2Results = result.t2Results;
|
|
12631
14609
|
}
|
|
14610
|
+
const trip = runBreaker.reason();
|
|
14611
|
+
if (trip) {
|
|
14612
|
+
finalOutput = [
|
|
14613
|
+
`\u26A0 **Run stopped early \u2014 ${trip.modelId} was failing every request.**`,
|
|
14614
|
+
"",
|
|
14615
|
+
trip.message,
|
|
14616
|
+
"",
|
|
14617
|
+
`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.`,
|
|
14618
|
+
finalOutput ? `
|
|
14619
|
+
---
|
|
14620
|
+
|
|
14621
|
+
${finalOutput}` : ""
|
|
14622
|
+
].join("\n").trimEnd();
|
|
14623
|
+
}
|
|
12632
14624
|
} catch (err) {
|
|
12633
14625
|
if (err instanceof CascadeCancelledError || err instanceof Error && err.name === "AbortError" || options.signal?.aborted) {
|
|
12634
14626
|
this.emit("run:cancelled", {
|
|
@@ -12695,7 +14687,11 @@ _(incomplete: ${reasons.join("; ")})_` : partial;
|
|
|
12695
14687
|
inputTokens: 0,
|
|
12696
14688
|
outputTokens: 0,
|
|
12697
14689
|
totalTokens: stats.totalTokens,
|
|
12698
|
-
estimatedCostUsd: stats.totalCostUsd
|
|
14690
|
+
estimatedCostUsd: stats.totalCostUsd,
|
|
14691
|
+
// At least one call ran on a model with no known price, so its spend is
|
|
14692
|
+
// missing from the total above. Say so rather than letting the caller
|
|
14693
|
+
// present an undercount as the final figure.
|
|
14694
|
+
...stats.untrackedCostCalls > 0 ? { costUnknown: true } : {}
|
|
12699
14695
|
},
|
|
12700
14696
|
t2Results,
|
|
12701
14697
|
durationMs,
|
|
@@ -12765,7 +14761,8 @@ _(incomplete: ${reasons.join("; ")})_` : partial;
|
|
|
12765
14761
|
inputTokens: 0,
|
|
12766
14762
|
outputTokens: 0,
|
|
12767
14763
|
totalTokens: stats2.totalTokens,
|
|
12768
|
-
estimatedCostUsd: stats2.totalCostUsd
|
|
14764
|
+
estimatedCostUsd: stats2.totalCostUsd,
|
|
14765
|
+
...stats2.untrackedCostCalls > 0 ? { costUnknown: true } : {}
|
|
12769
14766
|
},
|
|
12770
14767
|
t2Results: [],
|
|
12771
14768
|
durationMs: Date.now() - startMs,
|
|
@@ -12789,7 +14786,8 @@ _(incomplete: ${reasons.join("; ")})_` : partial;
|
|
|
12789
14786
|
inputTokens: result.usage?.inputTokens ?? 0,
|
|
12790
14787
|
outputTokens: result.usage?.outputTokens ?? 0,
|
|
12791
14788
|
totalTokens: stats.totalTokens,
|
|
12792
|
-
estimatedCostUsd: stats.totalCostUsd
|
|
14789
|
+
estimatedCostUsd: stats.totalCostUsd,
|
|
14790
|
+
...stats.untrackedCostCalls > 0 ? { costUnknown: true } : {}
|
|
12793
14791
|
},
|
|
12794
14792
|
t2Results: [],
|
|
12795
14793
|
durationMs: Date.now() - startMs,
|
|
@@ -14573,7 +16571,10 @@ var DashboardServer = class {
|
|
|
14573
16571
|
this.socket.broadcast("cost:update", {
|
|
14574
16572
|
sessionId,
|
|
14575
16573
|
totalTokens: result.usage.totalTokens,
|
|
14576
|
-
totalCostUsd: result.usage.estimatedCostUsd
|
|
16574
|
+
totalCostUsd: result.usage.estimatedCostUsd,
|
|
16575
|
+
// True when a model with no known price was used: the total above is
|
|
16576
|
+
// an undercount, and the UI must not present it as the full spend.
|
|
16577
|
+
costUnknown: result.usage.costUnknown === true
|
|
14577
16578
|
});
|
|
14578
16579
|
this.throttledBroadcast("workspace");
|
|
14579
16580
|
} catch (err) {
|
|
@@ -14886,7 +16887,8 @@ var DashboardServer = class {
|
|
|
14886
16887
|
this.socket.broadcast("cost:update", {
|
|
14887
16888
|
sessionId,
|
|
14888
16889
|
totalTokens: result.usage.totalTokens,
|
|
14889
|
-
totalCostUsd: result.usage.estimatedCostUsd
|
|
16890
|
+
totalCostUsd: result.usage.estimatedCostUsd,
|
|
16891
|
+
costUnknown: result.usage.costUnknown === true
|
|
14890
16892
|
});
|
|
14891
16893
|
this.throttledBroadcast("workspace");
|
|
14892
16894
|
} catch (err) {
|
|
@@ -15428,7 +17430,8 @@ ${prompt}`;
|
|
|
15428
17430
|
this.socket.broadcast("cost:update", {
|
|
15429
17431
|
sessionId,
|
|
15430
17432
|
totalTokens: result.usage.totalTokens,
|
|
15431
|
-
totalCostUsd: result.usage.estimatedCostUsd
|
|
17433
|
+
totalCostUsd: result.usage.estimatedCostUsd,
|
|
17434
|
+
costUnknown: result.usage.costUnknown === true
|
|
15432
17435
|
});
|
|
15433
17436
|
this.socket.broadcastToRoom(`session:${sessionId}`, "session:complete", { sessionId, result });
|
|
15434
17437
|
this.throttledBroadcast("workspace");
|