@stll/ai-catalog 0.1.6 → 0.1.8
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 +8 -0
- package/dist/index.d.ts +1063 -959
- package/dist/index.js +980 -853
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -15,6 +15,7 @@ const MODEL_DOCUMENT_INPUT_OPTIONS = {
|
|
|
15
15
|
"gemini-3.1-flash-lite"
|
|
16
16
|
],
|
|
17
17
|
openrouter: [
|
|
18
|
+
"openai/gpt-6-astra",
|
|
18
19
|
"openai/gpt-5.6-luna",
|
|
19
20
|
"openai/gpt-5.6-terra",
|
|
20
21
|
"google/gemini-3.8-flash",
|
|
@@ -32,6 +33,7 @@ const MODEL_DOCUMENT_INPUT_OPTIONS = {
|
|
|
32
33
|
"openai/gpt-5.4-mini"
|
|
33
34
|
],
|
|
34
35
|
openai: [
|
|
36
|
+
"gpt-6-astra",
|
|
35
37
|
"gpt-5.6",
|
|
36
38
|
"gpt-5.6-terra",
|
|
37
39
|
"gpt-5.6-luna",
|
|
@@ -106,6 +108,13 @@ const MODEL_REASONING_EFFORTS = {
|
|
|
106
108
|
"medium",
|
|
107
109
|
"high"
|
|
108
110
|
],
|
|
111
|
+
"openai/gpt-6-astra": [
|
|
112
|
+
"low",
|
|
113
|
+
"medium",
|
|
114
|
+
"high",
|
|
115
|
+
"xhigh",
|
|
116
|
+
"max"
|
|
117
|
+
],
|
|
109
118
|
"openai/gpt-5.6-luna": [
|
|
110
119
|
"none",
|
|
111
120
|
"low",
|
|
@@ -207,6 +216,13 @@ const MODEL_REASONING_EFFORTS = {
|
|
|
207
216
|
"high",
|
|
208
217
|
"xhigh"
|
|
209
218
|
],
|
|
219
|
+
"gpt-6-astra": [
|
|
220
|
+
"low",
|
|
221
|
+
"medium",
|
|
222
|
+
"high",
|
|
223
|
+
"xhigh",
|
|
224
|
+
"max"
|
|
225
|
+
],
|
|
210
226
|
"gpt-5.6": [
|
|
211
227
|
"none",
|
|
212
228
|
"low",
|
|
@@ -358,6 +374,7 @@ const MODEL_DEFAULT_REASONING_EFFORTS = {
|
|
|
358
374
|
"gemini-3.1-pro-preview": null,
|
|
359
375
|
"gemini-3.5-flash": null,
|
|
360
376
|
"gemini-3.1-flash-lite": null,
|
|
377
|
+
"openai/gpt-6-astra": "medium",
|
|
361
378
|
"openai/gpt-5.6-luna": "medium",
|
|
362
379
|
"openai/gpt-5.6-terra": "medium",
|
|
363
380
|
"google/gemini-3.8-flash": "medium",
|
|
@@ -373,6 +390,7 @@ const MODEL_DEFAULT_REASONING_EFFORTS = {
|
|
|
373
390
|
"anthropic/claude-sonnet-4.6": "medium",
|
|
374
391
|
"openai/gpt-5.5": "medium",
|
|
375
392
|
"openai/gpt-5.4-mini": "medium",
|
|
393
|
+
"gpt-6-astra": null,
|
|
376
394
|
"gpt-5.6": null,
|
|
377
395
|
"gpt-5.6-terra": null,
|
|
378
396
|
"gpt-5.6-luna": null,
|
|
@@ -418,6 +436,7 @@ const MODEL_TEMPERATURE_POLICIES = {
|
|
|
418
436
|
"gemini-3.1-pro-preview": "emit",
|
|
419
437
|
"gemini-3.5-flash": "emit",
|
|
420
438
|
"gemini-3.1-flash-lite": "emit",
|
|
439
|
+
"openai/gpt-6-astra": "omit",
|
|
421
440
|
"openai/gpt-5.6-luna": "omit",
|
|
422
441
|
"openai/gpt-5.6-terra": "omit",
|
|
423
442
|
"google/gemini-3.8-flash": "omit",
|
|
@@ -433,6 +452,7 @@ const MODEL_TEMPERATURE_POLICIES = {
|
|
|
433
452
|
"anthropic/claude-sonnet-4.6": "emit",
|
|
434
453
|
"openai/gpt-5.5": "omit",
|
|
435
454
|
"openai/gpt-5.4-mini": "omit",
|
|
455
|
+
"gpt-6-astra": "omit",
|
|
436
456
|
"gpt-5.6": "omit",
|
|
437
457
|
"gpt-5.6-terra": "omit",
|
|
438
458
|
"gpt-5.6-luna": "omit",
|
|
@@ -466,997 +486,1102 @@ const MODEL_TEMPERATURE_POLICIES = {
|
|
|
466
486
|
"pixtral-large-latest": "emit"
|
|
467
487
|
};
|
|
468
488
|
//#endregion
|
|
469
|
-
//#region src/
|
|
470
|
-
const
|
|
471
|
-
"
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
reason: "2026-07-20: absent from the models.dev amazon-bedrock catalog; Bedrock Converse accepts temperature and Nova has no reasoning dial"
|
|
489
|
+
//#region src/model-rates.gen.ts
|
|
490
|
+
const MODEL_RATES = {
|
|
491
|
+
"gemini-3.8-flash": {
|
|
492
|
+
kind: "flat",
|
|
493
|
+
inputPerMTok: 75e3,
|
|
494
|
+
outputPerMTok: 375e3,
|
|
495
|
+
cachedInputPerMTok: 7500
|
|
477
496
|
},
|
|
478
|
-
"
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
reason: "2026-07-20: absent from the models.dev amazon-bedrock catalog; Bedrock Converse accepts temperature and Nova has no reasoning dial"
|
|
497
|
+
"gemini-3.7-flash": {
|
|
498
|
+
kind: "flat",
|
|
499
|
+
inputPerMTok: 75e3,
|
|
500
|
+
outputPerMTok: 375e3,
|
|
501
|
+
cachedInputPerMTok: 7500
|
|
484
502
|
},
|
|
485
|
-
"
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
reason: "2026-07-20: absent from the models.dev amazon-bedrock catalog; Bedrock Converse accepts temperature and Nova has no reasoning dial"
|
|
491
|
-
}
|
|
492
|
-
};
|
|
493
|
-
//#endregion
|
|
494
|
-
//#region src/document-input-overrides.ts
|
|
495
|
-
/**
|
|
496
|
-
* Reviewed document-input corrections for models.dev records that exist but
|
|
497
|
-
* under-report the provider API's file-input support. The generator rejects an
|
|
498
|
-
* override once upstream agrees, so sourced data takes ownership automatically.
|
|
499
|
-
*/
|
|
500
|
-
const DOCUMENT_INPUT_OVERRIDES = {
|
|
501
|
-
"gpt-5.4-mini": {
|
|
502
|
-
supported: true,
|
|
503
|
-
reason: "2026-08-20: OpenAI Responses accepts input_file PDF content for this vision-capable model; models.dev currently omits the pdf modality"
|
|
503
|
+
"gemini-3.6-flash": {
|
|
504
|
+
kind: "flat",
|
|
505
|
+
inputPerMTok: 75e3,
|
|
506
|
+
outputPerMTok: 375e3,
|
|
507
|
+
cachedInputPerMTok: 7500
|
|
504
508
|
},
|
|
505
|
-
"
|
|
506
|
-
|
|
507
|
-
|
|
509
|
+
"gemini-3.5-flash-lite": {
|
|
510
|
+
kind: "flat",
|
|
511
|
+
inputPerMTok: 3e4,
|
|
512
|
+
outputPerMTok: 25e4,
|
|
513
|
+
cachedInputPerMTok: 3e3
|
|
508
514
|
},
|
|
509
|
-
"
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
* and the BYOK settings UI (`apps/web`), so the picker can never offer
|
|
523
|
-
* a model the backend rejects, and vice versa.
|
|
524
|
-
*
|
|
525
|
-
* Pure data: no provider SDKs, no env access, no side effects. This is
|
|
526
|
-
* deliberate so scripts (the nightly upstream-validation check) and
|
|
527
|
-
* both apps can import it cheaply. Keep it that way. The only runtime
|
|
528
|
-
* dependency is valibot, used solely to construct branded types the
|
|
529
|
-
* same way the rest of the repo does.
|
|
530
|
-
*
|
|
531
|
-
* Model IDs go stale when providers rename or retire models. The
|
|
532
|
-
* nightly `model-catalog-upstream` check
|
|
533
|
-
* (`packages/scripts/src/model-catalog-upstream.ts`) validates every
|
|
534
|
-
* ID here against live provider/aggregator listings so a retired model
|
|
535
|
-
* fails CI instead of 400-ing a user at runtime.
|
|
536
|
-
*/
|
|
537
|
-
/**
|
|
538
|
-
* Logical model roles. Call sites declare *what* they need, not
|
|
539
|
-
* *which* model to use.
|
|
540
|
-
*
|
|
541
|
-
* - fast: classification, extraction, short generation
|
|
542
|
-
* - chat: conversational with tool use and streaming
|
|
543
|
-
* - reasoning: complex multi-step legal analysis
|
|
544
|
-
* - pdf: native PDF/image understanding
|
|
545
|
-
*/
|
|
546
|
-
const MODEL_ROLES = [
|
|
547
|
-
"fast",
|
|
548
|
-
"chat",
|
|
549
|
-
"reasoning",
|
|
550
|
-
"pdf"
|
|
551
|
-
];
|
|
552
|
-
const TEMPERATURE_POLICIES = ["emit", "omit"];
|
|
553
|
-
const AI_PROVIDERS = [
|
|
554
|
-
"google",
|
|
555
|
-
"openrouter",
|
|
556
|
-
"openai",
|
|
557
|
-
"azure_foundry",
|
|
558
|
-
"anthropic",
|
|
559
|
-
"bedrock",
|
|
560
|
-
"mistral",
|
|
561
|
-
"openai_compatible",
|
|
562
|
-
"huggingface"
|
|
563
|
-
];
|
|
564
|
-
const TANSTACK_AI_PROVIDERS = [
|
|
565
|
-
"google",
|
|
566
|
-
"openrouter",
|
|
567
|
-
"openai",
|
|
568
|
-
"anthropic",
|
|
569
|
-
"bedrock",
|
|
570
|
-
"mistral"
|
|
571
|
-
];
|
|
572
|
-
const MODEL_CATALOG_PROVIDER_KIND = {
|
|
573
|
-
google: "first-party",
|
|
574
|
-
openrouter: "aggregator",
|
|
575
|
-
openai: "first-party",
|
|
576
|
-
anthropic: "first-party",
|
|
577
|
-
bedrock: "platform",
|
|
578
|
-
mistral: "first-party"
|
|
579
|
-
};
|
|
580
|
-
/**
|
|
581
|
-
* Runtime form of the first-party provider union. The conditional annotation
|
|
582
|
-
* makes adding a first-party policy without adding its discovery implementation
|
|
583
|
-
* a compile error.
|
|
584
|
-
*/
|
|
585
|
-
const FIRST_PARTY_MODEL_PROVIDERS = [
|
|
586
|
-
"google",
|
|
587
|
-
"openai",
|
|
588
|
-
"anthropic",
|
|
589
|
-
"mistral"
|
|
590
|
-
];
|
|
591
|
-
/**
|
|
592
|
-
* Per-role default model IDs for the BYOK-capable cloud providers.
|
|
593
|
-
* Shared between the instance default table (`DEFAULT_MODELS`) and the
|
|
594
|
-
* settings-UI default selection, so a default is defined exactly once.
|
|
595
|
-
*/
|
|
596
|
-
const BYOK_DEFAULT_MODELS = {
|
|
597
|
-
google: {
|
|
598
|
-
fast: "gemini-3.7-flash",
|
|
599
|
-
chat: "gemini-3.7-flash",
|
|
600
|
-
reasoning: "gemini-3.7-flash",
|
|
601
|
-
pdf: "gemini-3.7-flash"
|
|
515
|
+
"gemini-3.1-pro-preview": {
|
|
516
|
+
kind: "input-token-tiered",
|
|
517
|
+
inputTokenThreshold: 2e5,
|
|
518
|
+
standard: {
|
|
519
|
+
inputPerMTok: 2e5,
|
|
520
|
+
outputPerMTok: 12e5,
|
|
521
|
+
cachedInputPerMTok: 2e4
|
|
522
|
+
},
|
|
523
|
+
aboveThreshold: {
|
|
524
|
+
inputPerMTok: 4e5,
|
|
525
|
+
outputPerMTok: 18e5,
|
|
526
|
+
cachedInputPerMTok: 4e4
|
|
527
|
+
}
|
|
602
528
|
},
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
529
|
+
"gemini-3.5-flash": {
|
|
530
|
+
kind: "flat",
|
|
531
|
+
inputPerMTok: 15e4,
|
|
532
|
+
outputPerMTok: 9e5,
|
|
533
|
+
cachedInputPerMTok: 15e3
|
|
608
534
|
},
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
535
|
+
"gemini-3.1-flash-lite": {
|
|
536
|
+
kind: "flat",
|
|
537
|
+
inputPerMTok: 25e3,
|
|
538
|
+
outputPerMTok: 15e4,
|
|
539
|
+
cachedInputPerMTok: 2500
|
|
614
540
|
},
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
541
|
+
"gpt-6-astra": {
|
|
542
|
+
kind: "input-token-tiered",
|
|
543
|
+
inputTokenThreshold: 272e3,
|
|
544
|
+
standard: {
|
|
545
|
+
inputPerMTok: 1e6,
|
|
546
|
+
outputPerMTok: 5e6,
|
|
547
|
+
cachedInputPerMTok: 1e5,
|
|
548
|
+
cachedWriteInputPerMTok: 125e4
|
|
549
|
+
},
|
|
550
|
+
aboveThreshold: {
|
|
551
|
+
inputPerMTok: 2e6,
|
|
552
|
+
outputPerMTok: 75e5,
|
|
553
|
+
cachedInputPerMTok: 2e5,
|
|
554
|
+
cachedWriteInputPerMTok: 25e5
|
|
555
|
+
}
|
|
620
556
|
},
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
557
|
+
"gpt-5.6": {
|
|
558
|
+
kind: "input-token-tiered",
|
|
559
|
+
inputTokenThreshold: 272e3,
|
|
560
|
+
standard: {
|
|
561
|
+
inputPerMTok: 4e5,
|
|
562
|
+
outputPerMTok: 2e6,
|
|
563
|
+
cachedInputPerMTok: 4e4,
|
|
564
|
+
cachedWriteInputPerMTok: 5e5
|
|
565
|
+
},
|
|
566
|
+
aboveThreshold: {
|
|
567
|
+
inputPerMTok: 8e5,
|
|
568
|
+
outputPerMTok: 3e6,
|
|
569
|
+
cachedInputPerMTok: 8e4,
|
|
570
|
+
cachedWriteInputPerMTok: 1e6
|
|
571
|
+
}
|
|
626
572
|
},
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
fast: "gpt-5.4-nano",
|
|
643
|
-
chat: "gpt-5.4-mini",
|
|
644
|
-
reasoning: "gpt-5.4",
|
|
645
|
-
pdf: "gpt-5.4"
|
|
573
|
+
"gpt-5.6-terra": {
|
|
574
|
+
kind: "input-token-tiered",
|
|
575
|
+
inputTokenThreshold: 272e3,
|
|
576
|
+
standard: {
|
|
577
|
+
inputPerMTok: 2e5,
|
|
578
|
+
outputPerMTok: 12e5,
|
|
579
|
+
cachedInputPerMTok: 2e4,
|
|
580
|
+
cachedWriteInputPerMTok: 25e4
|
|
581
|
+
},
|
|
582
|
+
aboveThreshold: {
|
|
583
|
+
inputPerMTok: 4e5,
|
|
584
|
+
outputPerMTok: 18e5,
|
|
585
|
+
cachedInputPerMTok: 4e4,
|
|
586
|
+
cachedWriteInputPerMTok: 5e5
|
|
587
|
+
}
|
|
646
588
|
},
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
* from in org settings, and the server-side allowlist the API enforces.
|
|
663
|
-
* The frontend list is not a security boundary; this is what the API
|
|
664
|
-
* accepts.
|
|
665
|
-
*
|
|
666
|
-
* Limited to providers the TanStack AI integration supports for BYOK.
|
|
667
|
-
* Providers without a first-class TanStack adapter path are intentionally
|
|
668
|
-
* not accepted at this boundary.
|
|
669
|
-
*/
|
|
670
|
-
const BYOK_MODEL_OPTIONS = {
|
|
671
|
-
google: [
|
|
672
|
-
"gemini-3.8-flash",
|
|
673
|
-
"gemini-3.7-flash",
|
|
674
|
-
"gemini-3.6-flash",
|
|
675
|
-
"gemini-3.5-flash-lite",
|
|
676
|
-
"gemini-3.1-pro-preview",
|
|
677
|
-
"gemini-3.5-flash",
|
|
678
|
-
"gemini-3.1-flash-lite"
|
|
679
|
-
],
|
|
680
|
-
anthropic: [
|
|
681
|
-
"claude-sonnet-5",
|
|
682
|
-
"claude-fable-5-1",
|
|
683
|
-
"claude-fable-5",
|
|
684
|
-
"claude-opus-5",
|
|
685
|
-
"claude-opus-4-8",
|
|
686
|
-
"claude-opus-4-7",
|
|
687
|
-
"claude-sonnet-4-6",
|
|
688
|
-
"claude-opus-4-6",
|
|
689
|
-
"claude-haiku-4-5-20251001"
|
|
690
|
-
],
|
|
691
|
-
openai: [
|
|
692
|
-
"gpt-5.6",
|
|
693
|
-
"gpt-5.6-terra",
|
|
694
|
-
"gpt-5.6-luna",
|
|
695
|
-
"gpt-5.5",
|
|
696
|
-
"gpt-5.4",
|
|
697
|
-
"gpt-5.4-mini",
|
|
698
|
-
"gpt-5.4-nano",
|
|
699
|
-
"gpt-5.2"
|
|
700
|
-
],
|
|
701
|
-
openrouter: [
|
|
702
|
-
"openai/gpt-5.6-luna",
|
|
703
|
-
"openai/gpt-5.6-terra",
|
|
704
|
-
"google/gemini-3.8-flash",
|
|
705
|
-
"google/gemini-3.7-flash",
|
|
706
|
-
"google/gemini-3.6-flash",
|
|
707
|
-
"google/gemini-3.5-flash-lite",
|
|
708
|
-
"google/gemini-3.1-pro-preview",
|
|
709
|
-
"google/gemini-3.5-flash",
|
|
710
|
-
"google/gemini-3.1-flash-lite",
|
|
711
|
-
"anthropic/claude-sonnet-5",
|
|
712
|
-
"anthropic/claude-opus-5",
|
|
713
|
-
"anthropic/claude-opus-4.8",
|
|
714
|
-
"anthropic/claude-sonnet-4.6",
|
|
715
|
-
"openai/gpt-5.5",
|
|
716
|
-
"openai/gpt-5.4-mini"
|
|
717
|
-
],
|
|
718
|
-
bedrock: [
|
|
719
|
-
"us.anthropic.claude-sonnet-4-5-20250929-v1:0",
|
|
720
|
-
"us.anthropic.claude-haiku-4-5-20251001-v1:0",
|
|
721
|
-
"us.amazon.nova-pro-v1:0",
|
|
722
|
-
"us.amazon.nova-lite-v1:0",
|
|
723
|
-
"us.amazon.nova-micro-v1:0",
|
|
724
|
-
"openai.gpt-oss-120b-1:0",
|
|
725
|
-
"openai.gpt-oss-20b-1:0",
|
|
726
|
-
"us.deepseek.r1-v1:0"
|
|
727
|
-
],
|
|
728
|
-
mistral: [
|
|
729
|
-
"mistral-large-latest",
|
|
730
|
-
"mistral-medium-latest",
|
|
731
|
-
"mistral-small-latest",
|
|
732
|
-
"magistral-medium-latest",
|
|
733
|
-
"magistral-small",
|
|
734
|
-
"pixtral-large-latest"
|
|
735
|
-
]
|
|
736
|
-
};
|
|
737
|
-
/**
|
|
738
|
-
* Presentation metadata for every offered model. Keeping this exhaustive beside
|
|
739
|
-
* the allowlist prevents a newly offered model from leaking a raw provider ID
|
|
740
|
-
* into user-facing pickers. OpenRouter entries deliberately carry their model
|
|
741
|
-
* creator's icon while their routing provider remains available separately.
|
|
742
|
-
*/
|
|
743
|
-
const MODEL_DISPLAY_METADATA = {
|
|
744
|
-
"gemini-3.8-flash": {
|
|
745
|
-
displayName: "Gemini 3.8 Flash",
|
|
746
|
-
iconProvider: "google"
|
|
747
|
-
},
|
|
748
|
-
"gemini-3.7-flash": {
|
|
749
|
-
displayName: "Gemini 3.7 Flash",
|
|
750
|
-
iconProvider: "google"
|
|
589
|
+
"gpt-5.6-luna": {
|
|
590
|
+
kind: "input-token-tiered",
|
|
591
|
+
inputTokenThreshold: 272e3,
|
|
592
|
+
standard: {
|
|
593
|
+
inputPerMTok: 2e4,
|
|
594
|
+
outputPerMTok: 12e4,
|
|
595
|
+
cachedInputPerMTok: 2e3,
|
|
596
|
+
cachedWriteInputPerMTok: 25e3
|
|
597
|
+
},
|
|
598
|
+
aboveThreshold: {
|
|
599
|
+
inputPerMTok: 4e4,
|
|
600
|
+
outputPerMTok: 18e4,
|
|
601
|
+
cachedInputPerMTok: 4e3,
|
|
602
|
+
cachedWriteInputPerMTok: 5e4
|
|
603
|
+
}
|
|
751
604
|
},
|
|
752
|
-
"
|
|
753
|
-
|
|
754
|
-
|
|
605
|
+
"gpt-5.5": {
|
|
606
|
+
kind: "input-token-tiered",
|
|
607
|
+
inputTokenThreshold: 272e3,
|
|
608
|
+
standard: {
|
|
609
|
+
inputPerMTok: 5e5,
|
|
610
|
+
outputPerMTok: 3e6,
|
|
611
|
+
cachedInputPerMTok: 5e4
|
|
612
|
+
},
|
|
613
|
+
aboveThreshold: {
|
|
614
|
+
inputPerMTok: 1e6,
|
|
615
|
+
outputPerMTok: 45e5,
|
|
616
|
+
cachedInputPerMTok: 1e5
|
|
617
|
+
}
|
|
755
618
|
},
|
|
756
|
-
"
|
|
757
|
-
|
|
758
|
-
|
|
619
|
+
"gpt-5.4": {
|
|
620
|
+
kind: "input-token-tiered",
|
|
621
|
+
inputTokenThreshold: 272e3,
|
|
622
|
+
standard: {
|
|
623
|
+
inputPerMTok: 25e4,
|
|
624
|
+
outputPerMTok: 15e5,
|
|
625
|
+
cachedInputPerMTok: 25e3
|
|
626
|
+
},
|
|
627
|
+
aboveThreshold: {
|
|
628
|
+
inputPerMTok: 5e5,
|
|
629
|
+
outputPerMTok: 225e4,
|
|
630
|
+
cachedInputPerMTok: 5e4
|
|
631
|
+
}
|
|
759
632
|
},
|
|
760
|
-
"
|
|
761
|
-
|
|
762
|
-
|
|
633
|
+
"gpt-5.4-mini": {
|
|
634
|
+
kind: "flat",
|
|
635
|
+
inputPerMTok: 75e3,
|
|
636
|
+
outputPerMTok: 45e4,
|
|
637
|
+
cachedInputPerMTok: 7500
|
|
763
638
|
},
|
|
764
|
-
"
|
|
765
|
-
|
|
766
|
-
|
|
639
|
+
"gpt-5.4-nano": {
|
|
640
|
+
kind: "flat",
|
|
641
|
+
inputPerMTok: 2e4,
|
|
642
|
+
outputPerMTok: 125e3,
|
|
643
|
+
cachedInputPerMTok: 2e3
|
|
767
644
|
},
|
|
768
|
-
"
|
|
769
|
-
|
|
770
|
-
|
|
645
|
+
"gpt-5.2": {
|
|
646
|
+
kind: "flat",
|
|
647
|
+
inputPerMTok: 175e3,
|
|
648
|
+
outputPerMTok: 14e5,
|
|
649
|
+
cachedInputPerMTok: 17500
|
|
771
650
|
},
|
|
772
651
|
"claude-sonnet-5": {
|
|
773
|
-
|
|
774
|
-
|
|
652
|
+
kind: "flat",
|
|
653
|
+
inputPerMTok: 2e5,
|
|
654
|
+
outputPerMTok: 1e6,
|
|
655
|
+
cachedInputPerMTok: 2e4,
|
|
656
|
+
cachedWriteInputPerMTok: 25e4
|
|
775
657
|
},
|
|
776
658
|
"claude-fable-5-1": {
|
|
777
|
-
|
|
778
|
-
|
|
659
|
+
kind: "flat",
|
|
660
|
+
inputPerMTok: 1e6,
|
|
661
|
+
outputPerMTok: 5e6,
|
|
662
|
+
cachedInputPerMTok: 25e3,
|
|
663
|
+
cachedWriteInputPerMTok: 125e4
|
|
779
664
|
},
|
|
780
665
|
"claude-fable-5": {
|
|
781
|
-
|
|
782
|
-
|
|
666
|
+
kind: "flat",
|
|
667
|
+
inputPerMTok: 1e6,
|
|
668
|
+
outputPerMTok: 5e6,
|
|
669
|
+
cachedInputPerMTok: 1e5,
|
|
670
|
+
cachedWriteInputPerMTok: 125e4
|
|
783
671
|
},
|
|
784
672
|
"claude-opus-5": {
|
|
785
|
-
|
|
786
|
-
|
|
673
|
+
kind: "flat",
|
|
674
|
+
inputPerMTok: 5e5,
|
|
675
|
+
outputPerMTok: 25e5,
|
|
676
|
+
cachedInputPerMTok: 5e4,
|
|
677
|
+
cachedWriteInputPerMTok: 625e3
|
|
787
678
|
},
|
|
788
679
|
"claude-opus-4-8": {
|
|
789
|
-
|
|
790
|
-
|
|
680
|
+
kind: "flat",
|
|
681
|
+
inputPerMTok: 5e5,
|
|
682
|
+
outputPerMTok: 25e5,
|
|
683
|
+
cachedInputPerMTok: 5e4,
|
|
684
|
+
cachedWriteInputPerMTok: 625e3
|
|
791
685
|
},
|
|
792
686
|
"claude-opus-4-7": {
|
|
793
|
-
|
|
794
|
-
|
|
687
|
+
kind: "flat",
|
|
688
|
+
inputPerMTok: 5e5,
|
|
689
|
+
outputPerMTok: 25e5,
|
|
690
|
+
cachedInputPerMTok: 5e4,
|
|
691
|
+
cachedWriteInputPerMTok: 625e3
|
|
795
692
|
},
|
|
796
693
|
"claude-sonnet-4-6": {
|
|
797
|
-
|
|
798
|
-
|
|
694
|
+
kind: "flat",
|
|
695
|
+
inputPerMTok: 3e5,
|
|
696
|
+
outputPerMTok: 15e5,
|
|
697
|
+
cachedInputPerMTok: 3e4,
|
|
698
|
+
cachedWriteInputPerMTok: 375e3
|
|
799
699
|
},
|
|
800
700
|
"claude-opus-4-6": {
|
|
801
|
-
|
|
802
|
-
|
|
701
|
+
kind: "flat",
|
|
702
|
+
inputPerMTok: 5e5,
|
|
703
|
+
outputPerMTok: 25e5,
|
|
704
|
+
cachedInputPerMTok: 5e4,
|
|
705
|
+
cachedWriteInputPerMTok: 625e3
|
|
803
706
|
},
|
|
804
707
|
"claude-haiku-4-5-20251001": {
|
|
805
|
-
|
|
806
|
-
|
|
708
|
+
kind: "flat",
|
|
709
|
+
inputPerMTok: 1e5,
|
|
710
|
+
outputPerMTok: 5e5,
|
|
711
|
+
cachedInputPerMTok: 1e4,
|
|
712
|
+
cachedWriteInputPerMTok: 125e3
|
|
807
713
|
},
|
|
808
|
-
"
|
|
809
|
-
|
|
810
|
-
|
|
714
|
+
"us.anthropic.claude-sonnet-4-5-20250929-v1:0": {
|
|
715
|
+
kind: "flat",
|
|
716
|
+
inputPerMTok: 3e5,
|
|
717
|
+
outputPerMTok: 15e5,
|
|
718
|
+
cachedInputPerMTok: 3e4,
|
|
719
|
+
cachedWriteInputPerMTok: 375e3
|
|
811
720
|
},
|
|
812
|
-
"
|
|
813
|
-
|
|
814
|
-
|
|
721
|
+
"us.anthropic.claude-haiku-4-5-20251001-v1:0": {
|
|
722
|
+
kind: "flat",
|
|
723
|
+
inputPerMTok: 1e5,
|
|
724
|
+
outputPerMTok: 5e5,
|
|
725
|
+
cachedInputPerMTok: 1e4,
|
|
726
|
+
cachedWriteInputPerMTok: 125e3
|
|
815
727
|
},
|
|
816
|
-
"
|
|
817
|
-
|
|
818
|
-
|
|
728
|
+
"us.amazon.nova-pro-v1:0": {
|
|
729
|
+
kind: "flat",
|
|
730
|
+
inputPerMTok: 8e4,
|
|
731
|
+
outputPerMTok: 32e4,
|
|
732
|
+
cachedInputPerMTok: 2e4
|
|
819
733
|
},
|
|
820
|
-
"
|
|
821
|
-
|
|
822
|
-
|
|
734
|
+
"us.amazon.nova-lite-v1:0": {
|
|
735
|
+
kind: "flat",
|
|
736
|
+
inputPerMTok: 6e3,
|
|
737
|
+
outputPerMTok: 24e3,
|
|
738
|
+
cachedInputPerMTok: 1500
|
|
823
739
|
},
|
|
824
|
-
"
|
|
825
|
-
|
|
826
|
-
|
|
740
|
+
"us.amazon.nova-micro-v1:0": {
|
|
741
|
+
kind: "flat",
|
|
742
|
+
inputPerMTok: 3500,
|
|
743
|
+
outputPerMTok: 14e3,
|
|
744
|
+
cachedInputPerMTok: 875
|
|
827
745
|
},
|
|
828
|
-
"gpt-
|
|
829
|
-
|
|
830
|
-
|
|
746
|
+
"openai.gpt-oss-120b-1:0": {
|
|
747
|
+
kind: "flat",
|
|
748
|
+
inputPerMTok: 15e3,
|
|
749
|
+
outputPerMTok: 6e4
|
|
831
750
|
},
|
|
832
|
-
"gpt-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
"gpt-5.2": {
|
|
837
|
-
displayName: "GPT-5.2",
|
|
838
|
-
iconProvider: "openai"
|
|
839
|
-
},
|
|
840
|
-
"openai/gpt-5.6-luna": {
|
|
841
|
-
displayName: "GPT-5.6 Luna",
|
|
842
|
-
iconProvider: "openai"
|
|
843
|
-
},
|
|
844
|
-
"openai/gpt-5.6-terra": {
|
|
845
|
-
displayName: "GPT-5.6 Terra",
|
|
846
|
-
iconProvider: "openai"
|
|
847
|
-
},
|
|
848
|
-
"google/gemini-3.8-flash": {
|
|
849
|
-
displayName: "Gemini 3.8 Flash",
|
|
850
|
-
iconProvider: "google"
|
|
851
|
-
},
|
|
852
|
-
"google/gemini-3.7-flash": {
|
|
853
|
-
displayName: "Gemini 3.7 Flash",
|
|
854
|
-
iconProvider: "google"
|
|
751
|
+
"openai.gpt-oss-20b-1:0": {
|
|
752
|
+
kind: "flat",
|
|
753
|
+
inputPerMTok: 7e3,
|
|
754
|
+
outputPerMTok: 3e4
|
|
855
755
|
},
|
|
856
|
-
"
|
|
857
|
-
|
|
858
|
-
|
|
756
|
+
"us.deepseek.r1-v1:0": {
|
|
757
|
+
kind: "flat",
|
|
758
|
+
inputPerMTok: 135e3,
|
|
759
|
+
outputPerMTok: 54e4
|
|
859
760
|
},
|
|
860
|
-
"
|
|
861
|
-
|
|
862
|
-
|
|
761
|
+
"mistral-large-latest": {
|
|
762
|
+
kind: "flat",
|
|
763
|
+
inputPerMTok: 5e4,
|
|
764
|
+
outputPerMTok: 15e4
|
|
863
765
|
},
|
|
864
|
-
"
|
|
865
|
-
|
|
866
|
-
|
|
766
|
+
"mistral-medium-latest": {
|
|
767
|
+
kind: "flat",
|
|
768
|
+
inputPerMTok: 15e4,
|
|
769
|
+
outputPerMTok: 75e4
|
|
867
770
|
},
|
|
868
|
-
"
|
|
869
|
-
|
|
870
|
-
|
|
771
|
+
"mistral-small-latest": {
|
|
772
|
+
kind: "flat",
|
|
773
|
+
inputPerMTok: 15e3,
|
|
774
|
+
outputPerMTok: 6e4
|
|
871
775
|
},
|
|
872
|
-
"
|
|
873
|
-
|
|
874
|
-
|
|
776
|
+
"magistral-medium-latest": {
|
|
777
|
+
kind: "flat",
|
|
778
|
+
inputPerMTok: 2e5,
|
|
779
|
+
outputPerMTok: 5e5
|
|
875
780
|
},
|
|
876
|
-
"
|
|
877
|
-
|
|
878
|
-
|
|
781
|
+
"magistral-small": {
|
|
782
|
+
kind: "flat",
|
|
783
|
+
inputPerMTok: 5e4,
|
|
784
|
+
outputPerMTok: 15e4
|
|
879
785
|
},
|
|
880
|
-
"
|
|
881
|
-
|
|
882
|
-
|
|
786
|
+
"pixtral-large-latest": {
|
|
787
|
+
kind: "flat",
|
|
788
|
+
inputPerMTok: 2e5,
|
|
789
|
+
outputPerMTok: 6e5
|
|
883
790
|
},
|
|
884
|
-
"
|
|
885
|
-
|
|
886
|
-
|
|
791
|
+
"gemini-2.5-flash": {
|
|
792
|
+
kind: "flat",
|
|
793
|
+
inputPerMTok: 3e4,
|
|
794
|
+
outputPerMTok: 25e4,
|
|
795
|
+
cachedInputPerMTok: 3e3
|
|
887
796
|
},
|
|
888
|
-
"
|
|
889
|
-
|
|
890
|
-
|
|
797
|
+
"gemini-2.5-pro": {
|
|
798
|
+
kind: "input-token-tiered",
|
|
799
|
+
inputTokenThreshold: 2e5,
|
|
800
|
+
standard: {
|
|
801
|
+
inputPerMTok: 125e3,
|
|
802
|
+
outputPerMTok: 1e6,
|
|
803
|
+
cachedInputPerMTok: 12500
|
|
804
|
+
},
|
|
805
|
+
aboveThreshold: {
|
|
806
|
+
inputPerMTok: 25e4,
|
|
807
|
+
outputPerMTok: 15e5,
|
|
808
|
+
cachedInputPerMTok: 25e3
|
|
809
|
+
}
|
|
891
810
|
},
|
|
892
|
-
"
|
|
893
|
-
|
|
894
|
-
|
|
811
|
+
"gpt-4o-mini": {
|
|
812
|
+
kind: "flat",
|
|
813
|
+
inputPerMTok: 15e3,
|
|
814
|
+
outputPerMTok: 6e4,
|
|
815
|
+
cachedInputPerMTok: 7500
|
|
895
816
|
},
|
|
896
|
-
"
|
|
897
|
-
|
|
898
|
-
|
|
817
|
+
"gpt-4o": {
|
|
818
|
+
kind: "flat",
|
|
819
|
+
inputPerMTok: 25e4,
|
|
820
|
+
outputPerMTok: 1e6,
|
|
821
|
+
cachedInputPerMTok: 125e3
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
//#endregion
|
|
825
|
+
//#region src/model-rate.ts
|
|
826
|
+
/**
|
|
827
|
+
* models.dev publishes USD prices per one million tokens. Stella stores the
|
|
828
|
+
* same rates as integer ledger units so usage arithmetic never depends on
|
|
829
|
+
* floating point values.
|
|
830
|
+
*/
|
|
831
|
+
const MODEL_RATE_UNITS_PER_USD = 1e5;
|
|
832
|
+
const getStandardModelRate = (rate) => rate.kind === "flat" ? rate : rate.standard;
|
|
833
|
+
const resolveModelRate = (rate, inputTokens) => rate.kind === "input-token-tiered" && inputTokens > rate.inputTokenThreshold ? rate.aboveThreshold : getStandardModelRate(rate);
|
|
834
|
+
//#endregion
|
|
835
|
+
//#region src/model-rate-policy.ts
|
|
836
|
+
const MODELS_DEV_RATE_PROVIDER_BY_CATALOG_PROVIDER = {
|
|
837
|
+
google: "google",
|
|
838
|
+
openai: "openai",
|
|
839
|
+
anthropic: "anthropic",
|
|
840
|
+
bedrock: "amazon-bedrock",
|
|
841
|
+
mistral: "mistral"
|
|
842
|
+
};
|
|
843
|
+
const defineRateSourceAliases = (aliases) => aliases;
|
|
844
|
+
/**
|
|
845
|
+
* Explicit source coordinates for non-offered model IDs that remain valid in
|
|
846
|
+
* deployment overrides and usage attribution. Their numeric rates still come
|
|
847
|
+
* exclusively from models.dev.
|
|
848
|
+
*/
|
|
849
|
+
const RETAINED_MODELS_DEV_RATE_ENTRIES = {
|
|
850
|
+
"gemini-2.5-flash": {
|
|
851
|
+
modelId: "gemini-2.5-flash",
|
|
852
|
+
provider: "google"
|
|
899
853
|
},
|
|
900
|
-
"
|
|
901
|
-
|
|
902
|
-
|
|
854
|
+
"gemini-2.5-pro": {
|
|
855
|
+
modelId: "gemini-2.5-pro",
|
|
856
|
+
provider: "google"
|
|
903
857
|
},
|
|
904
|
-
"
|
|
905
|
-
|
|
906
|
-
|
|
858
|
+
"gpt-4o-mini": {
|
|
859
|
+
modelId: "gpt-4o-mini",
|
|
860
|
+
provider: "openai"
|
|
907
861
|
},
|
|
862
|
+
"gpt-4o": {
|
|
863
|
+
modelId: "gpt-4o",
|
|
864
|
+
provider: "openai"
|
|
865
|
+
}
|
|
866
|
+
};
|
|
867
|
+
/**
|
|
868
|
+
* Exact mappings where Stella uses a provider routing ID while models.dev
|
|
869
|
+
* publishes pricing under the corresponding base model ID. Generation rejects
|
|
870
|
+
* a mapping once models.dev begins publishing the Stella ID directly.
|
|
871
|
+
*/
|
|
872
|
+
const MODELS_DEV_RATE_SOURCE_ALIASES = defineRateSourceAliases({
|
|
908
873
|
"us.amazon.nova-pro-v1:0": {
|
|
909
|
-
|
|
910
|
-
|
|
874
|
+
modelId: "amazon.nova-pro-v1:0",
|
|
875
|
+
provider: "amazon-bedrock",
|
|
876
|
+
reason: "2026-09-03: stella uses the Bedrock US inference-profile ID; models.dev prices its corresponding base model ID",
|
|
877
|
+
sourceUrl: "https://docs.aws.amazon.com/bedrock/latest/userguide/cross-region-inference.html"
|
|
911
878
|
},
|
|
912
879
|
"us.amazon.nova-lite-v1:0": {
|
|
913
|
-
|
|
914
|
-
|
|
880
|
+
modelId: "amazon.nova-lite-v1:0",
|
|
881
|
+
provider: "amazon-bedrock",
|
|
882
|
+
reason: "2026-09-03: stella uses the Bedrock US inference-profile ID; models.dev prices its corresponding base model ID",
|
|
883
|
+
sourceUrl: "https://docs.aws.amazon.com/bedrock/latest/userguide/cross-region-inference.html"
|
|
915
884
|
},
|
|
916
885
|
"us.amazon.nova-micro-v1:0": {
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
"mistral-large-latest": {
|
|
933
|
-
displayName: "Mistral Large",
|
|
934
|
-
iconProvider: "mistral"
|
|
935
|
-
},
|
|
936
|
-
"mistral-medium-latest": {
|
|
937
|
-
displayName: "Mistral Medium",
|
|
938
|
-
iconProvider: "mistral"
|
|
886
|
+
modelId: "amazon.nova-micro-v1:0",
|
|
887
|
+
provider: "amazon-bedrock",
|
|
888
|
+
reason: "2026-09-03: stella uses the Bedrock US inference-profile ID; models.dev prices its corresponding base model ID",
|
|
889
|
+
sourceUrl: "https://docs.aws.amazon.com/bedrock/latest/userguide/cross-region-inference.html"
|
|
890
|
+
}
|
|
891
|
+
});
|
|
892
|
+
//#endregion
|
|
893
|
+
//#region src/capabilities-overrides.ts
|
|
894
|
+
const CAPABILITY_OVERRIDES = {
|
|
895
|
+
"us.amazon.nova-pro-v1:0": {
|
|
896
|
+
documentInput: true,
|
|
897
|
+
documentInputReason: "2026-08-20: AWS documents PDF input for Nova Pro through Bedrock Converse",
|
|
898
|
+
reasoningEfforts: null,
|
|
899
|
+
temperatureSupported: true,
|
|
900
|
+
reason: "2026-07-20: absent from the models.dev amazon-bedrock catalog; Bedrock Converse accepts temperature and Nova has no reasoning dial"
|
|
939
901
|
},
|
|
940
|
-
"
|
|
941
|
-
|
|
942
|
-
|
|
902
|
+
"us.amazon.nova-lite-v1:0": {
|
|
903
|
+
documentInput: true,
|
|
904
|
+
documentInputReason: "2026-08-20: AWS documents PDF input for Nova Lite through Bedrock Converse",
|
|
905
|
+
reasoningEfforts: null,
|
|
906
|
+
temperatureSupported: true,
|
|
907
|
+
reason: "2026-07-20: absent from the models.dev amazon-bedrock catalog; Bedrock Converse accepts temperature and Nova has no reasoning dial"
|
|
943
908
|
},
|
|
944
|
-
"
|
|
945
|
-
|
|
946
|
-
|
|
909
|
+
"us.amazon.nova-micro-v1:0": {
|
|
910
|
+
documentInput: false,
|
|
911
|
+
documentInputReason: "2026-08-20: AWS documents Nova Micro as text-only",
|
|
912
|
+
reasoningEfforts: null,
|
|
913
|
+
temperatureSupported: true,
|
|
914
|
+
reason: "2026-07-20: absent from the models.dev amazon-bedrock catalog; Bedrock Converse accepts temperature and Nova has no reasoning dial"
|
|
915
|
+
}
|
|
916
|
+
};
|
|
917
|
+
//#endregion
|
|
918
|
+
//#region src/document-input-overrides.ts
|
|
919
|
+
/**
|
|
920
|
+
* Reviewed document-input corrections for models.dev records that exist but
|
|
921
|
+
* under-report the provider API's file-input support. The generator rejects an
|
|
922
|
+
* override once upstream agrees, so sourced data takes ownership automatically.
|
|
923
|
+
*/
|
|
924
|
+
const DOCUMENT_INPUT_OVERRIDES = {
|
|
925
|
+
"gpt-5.4-mini": {
|
|
926
|
+
supported: true,
|
|
927
|
+
reason: "2026-08-20: OpenAI Responses accepts input_file PDF content for this vision-capable model; models.dev currently omits the pdf modality"
|
|
947
928
|
},
|
|
948
|
-
"
|
|
949
|
-
|
|
950
|
-
|
|
929
|
+
"gpt-5.4-nano": {
|
|
930
|
+
supported: true,
|
|
931
|
+
reason: "2026-08-20: OpenAI Responses accepts input_file PDF content for this vision-capable model; models.dev currently omits the pdf modality"
|
|
951
932
|
},
|
|
952
|
-
"
|
|
953
|
-
|
|
954
|
-
|
|
933
|
+
"gpt-5.2": {
|
|
934
|
+
supported: true,
|
|
935
|
+
reason: "2026-08-20: OpenAI Responses accepts input_file PDF content for this vision-capable model; models.dev currently omits the pdf modality"
|
|
955
936
|
}
|
|
956
937
|
};
|
|
957
|
-
|
|
958
|
-
|
|
938
|
+
//#endregion
|
|
939
|
+
//#region src/index.ts
|
|
959
940
|
/**
|
|
960
|
-
*
|
|
961
|
-
*
|
|
962
|
-
*
|
|
941
|
+
* Canonical AI provider and model catalog.
|
|
942
|
+
*
|
|
943
|
+
* Single source of truth for every provider/model identifier stella
|
|
944
|
+
* offers. Imported by the API runtime
|
|
945
|
+
* (`apps/api/src/lib/tanstack-ai-models.ts`)
|
|
946
|
+
* and the BYOK settings UI (`apps/web`), so the picker can never offer
|
|
947
|
+
* a model the backend rejects, and vice versa.
|
|
948
|
+
*
|
|
949
|
+
* Pure data: no provider SDKs, no env access, no side effects. This is
|
|
950
|
+
* deliberate so scripts (the nightly upstream-validation check) and
|
|
951
|
+
* both apps can import it cheaply. Keep it that way. The only runtime
|
|
952
|
+
* dependency is valibot, used solely to construct branded types the
|
|
953
|
+
* same way the rest of the repo does.
|
|
954
|
+
*
|
|
955
|
+
* Model IDs go stale when providers rename or retire models. The
|
|
956
|
+
* nightly `model-catalog-upstream` check
|
|
957
|
+
* (`packages/scripts/src/model-catalog-upstream.ts`) validates every
|
|
958
|
+
* ID here against live provider/aggregator listings so a retired model
|
|
959
|
+
* fails CI instead of 400-ing a user at runtime.
|
|
963
960
|
*/
|
|
964
|
-
const BYOK_DOCUMENT_INPUT_MODEL_OPTIONS = MODEL_DOCUMENT_INPUT_OPTIONS;
|
|
965
|
-
const isBYOKProviderRoleSupported = ({ provider, role }) => role !== "pdf" || BYOK_DOCUMENT_INPUT_MODEL_OPTIONS[provider].length > 0;
|
|
966
|
-
const isBYOKModelRoleSupported = ({ provider, modelId, role }) => {
|
|
967
|
-
if (role !== "pdf") return true;
|
|
968
|
-
return BYOK_DOCUMENT_INPUT_MODEL_OPTIONS[provider].includes(modelId);
|
|
969
|
-
};
|
|
970
961
|
/**
|
|
971
|
-
*
|
|
972
|
-
*
|
|
973
|
-
* pdf ROLE (`BYOK_DOCUMENT_INPUT_MODEL_OPTIONS`, which curates model selection
|
|
974
|
-
* for PDF-processing flows): it only asks "will the chat stream survive this
|
|
975
|
-
* attachment." The Mistral text adapter maps PDF `document` parts to
|
|
976
|
-
* `document_url`, so its
|
|
977
|
-
* vision models accept PDF attachments even though Mistral is deliberately not
|
|
978
|
-
* offered as a pdf-role provider. Mistral's `document_url` takes PDF but NOT
|
|
979
|
-
* textual documents (docx/txt/csv/md), so those still gate on
|
|
980
|
-
* `BYOK_DOCUMENT_INPUT_MODEL_OPTIONS` (which excludes Mistral). Superset
|
|
981
|
-
* invariant: every pdf-role document model also accepts a PDF chat attachment.
|
|
982
|
-
*/
|
|
983
|
-
const CHAT_PDF_ATTACHMENT_MODEL_OPTIONS = {
|
|
984
|
-
...BYOK_DOCUMENT_INPUT_MODEL_OPTIONS,
|
|
985
|
-
mistral: [
|
|
986
|
-
"mistral-medium-latest",
|
|
987
|
-
"mistral-small-latest",
|
|
988
|
-
"pixtral-large-latest"
|
|
989
|
-
]
|
|
990
|
-
};
|
|
991
|
-
const isChatPdfAttachmentModelSupported = ({ provider, modelId }) => {
|
|
992
|
-
return CHAT_PDF_ATTACHMENT_MODEL_OPTIONS[provider].includes(modelId);
|
|
993
|
-
};
|
|
994
|
-
/**
|
|
995
|
-
* Whether a model id is currently offered for this provider+role: it
|
|
996
|
-
* must be in the curated catalog for the provider AND satisfy the
|
|
997
|
-
* role's input-modality constraint (PDF needs a document-capable
|
|
998
|
-
* model). This is the runtime allowlist, so a model dropped by a
|
|
999
|
-
* catalog bump stops being valid here even though it is still a
|
|
1000
|
-
* non-empty string in a stored org config.
|
|
1001
|
-
*/
|
|
1002
|
-
const isOfferedBYOKModelForRole = ({ provider, modelId, role }) => {
|
|
1003
|
-
return BYOK_MODEL_OPTIONS[provider].includes(modelId) && isBYOKModelRoleSupported({
|
|
1004
|
-
provider,
|
|
1005
|
-
modelId,
|
|
1006
|
-
role
|
|
1007
|
-
});
|
|
1008
|
-
};
|
|
1009
|
-
/**
|
|
1010
|
-
* Resolve a model id that will actually work for this provider+role,
|
|
1011
|
-
* keeping the SAME provider. Returns the caller's model unchanged when
|
|
1012
|
-
* it is still offered; otherwise falls back to the provider's per-role
|
|
1013
|
-
* default (`BYOK_DEFAULT_MODELS`). Returns `null` only when the
|
|
1014
|
-
* provider has no valid model for the role at all — the sole case
|
|
1015
|
-
* today is `mistral` + `pdf`, because the TanStack Mistral adapter
|
|
1016
|
-
* exposes no `document` input modality, so no Mistral model (not even
|
|
1017
|
-
* the default) can serve the PDF role.
|
|
962
|
+
* Logical model roles. Call sites declare *what* they need, not
|
|
963
|
+
* *which* model to use.
|
|
1018
964
|
*
|
|
1019
|
-
*
|
|
1020
|
-
*
|
|
1021
|
-
*
|
|
1022
|
-
|
|
1023
|
-
const resolveWorkingBYOKModelForRole = ({ provider, modelId, role }) => {
|
|
1024
|
-
if (isOfferedBYOKModelForRole({
|
|
1025
|
-
provider,
|
|
1026
|
-
modelId,
|
|
1027
|
-
role
|
|
1028
|
-
})) return modelId;
|
|
1029
|
-
const fallback = BYOK_DEFAULT_MODELS[provider][role];
|
|
1030
|
-
return isOfferedBYOKModelForRole({
|
|
1031
|
-
provider,
|
|
1032
|
-
modelId: fallback,
|
|
1033
|
-
role
|
|
1034
|
-
}) ? fallback : null;
|
|
1035
|
-
};
|
|
1036
|
-
/**
|
|
1037
|
-
* Anthropic models that use the adaptive-thinking request shape
|
|
1038
|
-
* (`thinking: { type: "adaptive" }`). Newer Claude models reject the
|
|
1039
|
-
* legacy budget-based form, so every Opus 4.6+/Sonnet 4.6/Fable entry
|
|
1040
|
-
* offered above must appear here or it will 400 on the reasoning role.
|
|
965
|
+
* - fast: classification, extraction, short generation
|
|
966
|
+
* - chat: conversational with tool use and streaming
|
|
967
|
+
* - reasoning: complex multi-step legal analysis
|
|
968
|
+
* - pdf: native PDF/image understanding
|
|
1041
969
|
*/
|
|
1042
|
-
const
|
|
1043
|
-
"
|
|
1044
|
-
"
|
|
1045
|
-
"
|
|
1046
|
-
"
|
|
1047
|
-
"claude-opus-4-7",
|
|
1048
|
-
"claude-opus-4-8",
|
|
1049
|
-
"claude-fable-5",
|
|
1050
|
-
"claude-fable-5-1"
|
|
970
|
+
const MODEL_ROLES = [
|
|
971
|
+
"fast",
|
|
972
|
+
"chat",
|
|
973
|
+
"reasoning",
|
|
974
|
+
"pdf"
|
|
1051
975
|
];
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
"
|
|
1060
|
-
"
|
|
1061
|
-
"
|
|
1062
|
-
"
|
|
1063
|
-
"high",
|
|
1064
|
-
"xhigh",
|
|
1065
|
-
"max"
|
|
976
|
+
const TEMPERATURE_POLICIES = ["emit", "omit"];
|
|
977
|
+
const AI_PROVIDERS = [
|
|
978
|
+
"google",
|
|
979
|
+
"openrouter",
|
|
980
|
+
"openai",
|
|
981
|
+
"azure_foundry",
|
|
982
|
+
"anthropic",
|
|
983
|
+
"bedrock",
|
|
984
|
+
"mistral",
|
|
985
|
+
"openai_compatible",
|
|
986
|
+
"huggingface"
|
|
1066
987
|
];
|
|
1067
|
-
const
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
openrouter: "openai/gpt-5.6-luna",
|
|
1083
|
-
openai: "gpt-5.6-luna",
|
|
1084
|
-
anthropic: DEFAULT_MODELS.anthropic.fast,
|
|
1085
|
-
bedrock: DEFAULT_MODELS.bedrock.fast,
|
|
1086
|
-
mistral: DEFAULT_MODELS.mistral.fast,
|
|
1087
|
-
azure_foundry: null,
|
|
1088
|
-
openai_compatible: null,
|
|
1089
|
-
huggingface: null
|
|
1090
|
-
};
|
|
1091
|
-
const MODEL_CATALOG_ID_ALIASES = {
|
|
1092
|
-
"gpt-5.6-sol": "gpt-5.6",
|
|
1093
|
-
"claude-opus-4.8": "claude-opus-4-8",
|
|
1094
|
-
"claude-sonnet-4.6": "claude-sonnet-4-6"
|
|
988
|
+
const TANSTACK_AI_PROVIDERS = [
|
|
989
|
+
"google",
|
|
990
|
+
"openrouter",
|
|
991
|
+
"openai",
|
|
992
|
+
"anthropic",
|
|
993
|
+
"bedrock",
|
|
994
|
+
"mistral"
|
|
995
|
+
];
|
|
996
|
+
const MODEL_CATALOG_PROVIDER_KIND = {
|
|
997
|
+
google: "first-party",
|
|
998
|
+
openrouter: "aggregator",
|
|
999
|
+
openai: "first-party",
|
|
1000
|
+
anthropic: "first-party",
|
|
1001
|
+
bedrock: "platform",
|
|
1002
|
+
mistral: "first-party"
|
|
1095
1003
|
};
|
|
1096
|
-
const MODEL_CATALOG_ID_ALIAS_TARGET_BY_ID = MODEL_CATALOG_ID_ALIASES;
|
|
1097
|
-
const normalizeModelCatalogId = (modelId) => MODEL_CATALOG_ID_ALIAS_TARGET_BY_ID[modelId] ?? modelId;
|
|
1098
|
-
const MODEL_TEMPERATURE_POLICY_BY_ID = MODEL_TEMPERATURE_POLICIES;
|
|
1099
1004
|
/**
|
|
1100
|
-
*
|
|
1101
|
-
*
|
|
1102
|
-
*
|
|
1103
|
-
* also resolve to `false` (send nothing; provider default). Callers must
|
|
1104
|
-
* never index `MODEL_TEMPERATURE_POLICIES` directly with
|
|
1105
|
-
* a runtime string.
|
|
1005
|
+
* Runtime form of the first-party provider union. The conditional annotation
|
|
1006
|
+
* makes adding a first-party policy without adding its discovery implementation
|
|
1007
|
+
* a compile error.
|
|
1106
1008
|
*/
|
|
1107
|
-
const
|
|
1108
|
-
|
|
1009
|
+
const FIRST_PARTY_MODEL_PROVIDERS = [
|
|
1010
|
+
"google",
|
|
1011
|
+
"openai",
|
|
1012
|
+
"anthropic",
|
|
1013
|
+
"mistral"
|
|
1014
|
+
];
|
|
1109
1015
|
/**
|
|
1110
|
-
*
|
|
1111
|
-
*
|
|
1112
|
-
*
|
|
1113
|
-
* directly with a runtime string.
|
|
1016
|
+
* Per-role default model IDs for the BYOK-capable cloud providers.
|
|
1017
|
+
* Shared between the instance default table (`DEFAULT_MODELS`) and the
|
|
1018
|
+
* settings-UI default selection, so a default is defined exactly once.
|
|
1114
1019
|
*/
|
|
1115
|
-
const
|
|
1116
|
-
|
|
1020
|
+
const BYOK_DEFAULT_MODELS = {
|
|
1021
|
+
google: {
|
|
1022
|
+
fast: "gemini-3.7-flash",
|
|
1023
|
+
chat: "gemini-3.7-flash",
|
|
1024
|
+
reasoning: "gemini-3.7-flash",
|
|
1025
|
+
pdf: "gemini-3.7-flash"
|
|
1026
|
+
},
|
|
1027
|
+
openrouter: {
|
|
1028
|
+
fast: "openai/gpt-5.6-luna",
|
|
1029
|
+
chat: "openai/gpt-5.6-terra",
|
|
1030
|
+
reasoning: "openai/gpt-5.6-terra",
|
|
1031
|
+
pdf: "openai/gpt-5.6-terra"
|
|
1032
|
+
},
|
|
1033
|
+
openai: {
|
|
1034
|
+
fast: "gpt-5.4-nano",
|
|
1035
|
+
chat: "gpt-5.4-mini",
|
|
1036
|
+
reasoning: "gpt-5.4",
|
|
1037
|
+
pdf: "gpt-5.4"
|
|
1038
|
+
},
|
|
1039
|
+
anthropic: {
|
|
1040
|
+
fast: "claude-opus-5",
|
|
1041
|
+
chat: "claude-opus-5",
|
|
1042
|
+
reasoning: "claude-opus-5",
|
|
1043
|
+
pdf: "claude-opus-5"
|
|
1044
|
+
},
|
|
1045
|
+
bedrock: {
|
|
1046
|
+
fast: "us.amazon.nova-micro-v1:0",
|
|
1047
|
+
chat: "us.anthropic.claude-sonnet-4-5-20250929-v1:0",
|
|
1048
|
+
reasoning: "us.anthropic.claude-sonnet-4-5-20250929-v1:0",
|
|
1049
|
+
pdf: "us.anthropic.claude-sonnet-4-5-20250929-v1:0"
|
|
1050
|
+
},
|
|
1051
|
+
mistral: {
|
|
1052
|
+
fast: "mistral-small-latest",
|
|
1053
|
+
chat: "mistral-medium-latest",
|
|
1054
|
+
reasoning: "magistral-medium-latest",
|
|
1055
|
+
pdf: "mistral-large-latest"
|
|
1056
|
+
}
|
|
1057
|
+
};
|
|
1117
1058
|
/**
|
|
1118
|
-
*
|
|
1119
|
-
*
|
|
1059
|
+
* Instance-level default model IDs per provider. Extends the BYOK
|
|
1060
|
+
* defaults with the providers that only the instance path uses
|
|
1061
|
+
* (custom deployments and OpenAI-compatible endpoints).
|
|
1120
1062
|
*/
|
|
1121
|
-
const
|
|
1122
|
-
|
|
1063
|
+
const DEFAULT_MODELS = {
|
|
1064
|
+
...BYOK_DEFAULT_MODELS,
|
|
1065
|
+
azure_foundry: {
|
|
1066
|
+
fast: "gpt-5.4-nano",
|
|
1067
|
+
chat: "gpt-5.4-mini",
|
|
1068
|
+
reasoning: "gpt-5.4",
|
|
1069
|
+
pdf: "gpt-5.4"
|
|
1070
|
+
},
|
|
1071
|
+
openai_compatible: {
|
|
1072
|
+
fast: "default",
|
|
1073
|
+
chat: "default",
|
|
1074
|
+
reasoning: "default",
|
|
1075
|
+
pdf: "default"
|
|
1076
|
+
},
|
|
1077
|
+
huggingface: {
|
|
1078
|
+
fast: "speakleash/Bielik-1.5B-v3.0-Instruct",
|
|
1079
|
+
chat: "speakleash/Bielik-11B-v2.3-Instruct",
|
|
1080
|
+
reasoning: "speakleash/Bielik-11B-v2.3-Instruct",
|
|
1081
|
+
pdf: "speakleash/Bielik-11B-v2.3-Instruct"
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1123
1084
|
/**
|
|
1124
|
-
*
|
|
1125
|
-
*
|
|
1085
|
+
* BYOK-offered model IDs per provider — the curated catalog users pick
|
|
1086
|
+
* from in org settings, and the server-side allowlist the API enforces.
|
|
1087
|
+
* The frontend list is not a security boundary; this is what the API
|
|
1088
|
+
* accepts.
|
|
1126
1089
|
*
|
|
1127
|
-
*
|
|
1128
|
-
*
|
|
1129
|
-
*
|
|
1130
|
-
* - Otherwise → the supported value nearest on `REASONING_EFFORTS`,
|
|
1131
|
-
* preferring the weaker side on ties, so a "none" request against a
|
|
1132
|
-
* reasoning-mandatory model degrades to its weakest tier instead of
|
|
1133
|
-
* erroring, and requests above the model's ceiling clamp down to it.
|
|
1090
|
+
* Limited to providers the TanStack AI integration supports for BYOK.
|
|
1091
|
+
* Providers without a first-class TanStack adapter path are intentionally
|
|
1092
|
+
* not accepted at this boundary.
|
|
1134
1093
|
*/
|
|
1135
|
-
const
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1094
|
+
const BYOK_MODEL_OPTIONS = {
|
|
1095
|
+
google: [
|
|
1096
|
+
"gemini-3.8-flash",
|
|
1097
|
+
"gemini-3.7-flash",
|
|
1098
|
+
"gemini-3.6-flash",
|
|
1099
|
+
"gemini-3.5-flash-lite",
|
|
1100
|
+
"gemini-3.1-pro-preview",
|
|
1101
|
+
"gemini-3.5-flash",
|
|
1102
|
+
"gemini-3.1-flash-lite"
|
|
1103
|
+
],
|
|
1104
|
+
anthropic: [
|
|
1105
|
+
"claude-sonnet-5",
|
|
1106
|
+
"claude-fable-5-1",
|
|
1107
|
+
"claude-fable-5",
|
|
1108
|
+
"claude-opus-5",
|
|
1109
|
+
"claude-opus-4-8",
|
|
1110
|
+
"claude-opus-4-7",
|
|
1111
|
+
"claude-sonnet-4-6",
|
|
1112
|
+
"claude-opus-4-6",
|
|
1113
|
+
"claude-haiku-4-5-20251001"
|
|
1114
|
+
],
|
|
1115
|
+
openai: [
|
|
1116
|
+
"gpt-6-astra",
|
|
1117
|
+
"gpt-5.6",
|
|
1118
|
+
"gpt-5.6-terra",
|
|
1119
|
+
"gpt-5.6-luna",
|
|
1120
|
+
"gpt-5.5",
|
|
1121
|
+
"gpt-5.4",
|
|
1122
|
+
"gpt-5.4-mini",
|
|
1123
|
+
"gpt-5.4-nano",
|
|
1124
|
+
"gpt-5.2"
|
|
1125
|
+
],
|
|
1126
|
+
openrouter: [
|
|
1127
|
+
"openai/gpt-6-astra",
|
|
1128
|
+
"openai/gpt-5.6-luna",
|
|
1129
|
+
"openai/gpt-5.6-terra",
|
|
1130
|
+
"google/gemini-3.8-flash",
|
|
1131
|
+
"google/gemini-3.7-flash",
|
|
1132
|
+
"google/gemini-3.6-flash",
|
|
1133
|
+
"google/gemini-3.5-flash-lite",
|
|
1134
|
+
"google/gemini-3.1-pro-preview",
|
|
1135
|
+
"google/gemini-3.5-flash",
|
|
1136
|
+
"google/gemini-3.1-flash-lite",
|
|
1137
|
+
"anthropic/claude-sonnet-5",
|
|
1138
|
+
"anthropic/claude-opus-5",
|
|
1139
|
+
"anthropic/claude-opus-4.8",
|
|
1140
|
+
"anthropic/claude-sonnet-4.6",
|
|
1141
|
+
"openai/gpt-5.5",
|
|
1142
|
+
"openai/gpt-5.4-mini"
|
|
1143
|
+
],
|
|
1144
|
+
bedrock: [
|
|
1145
|
+
"us.anthropic.claude-sonnet-4-5-20250929-v1:0",
|
|
1146
|
+
"us.anthropic.claude-haiku-4-5-20251001-v1:0",
|
|
1147
|
+
"us.amazon.nova-pro-v1:0",
|
|
1148
|
+
"us.amazon.nova-lite-v1:0",
|
|
1149
|
+
"us.amazon.nova-micro-v1:0",
|
|
1150
|
+
"openai.gpt-oss-120b-1:0",
|
|
1151
|
+
"openai.gpt-oss-20b-1:0",
|
|
1152
|
+
"us.deepseek.r1-v1:0"
|
|
1153
|
+
],
|
|
1154
|
+
mistral: [
|
|
1155
|
+
"mistral-large-latest",
|
|
1156
|
+
"mistral-medium-latest",
|
|
1157
|
+
"mistral-small-latest",
|
|
1158
|
+
"magistral-medium-latest",
|
|
1159
|
+
"magistral-small",
|
|
1160
|
+
"pixtral-large-latest"
|
|
1161
|
+
]
|
|
1151
1162
|
};
|
|
1152
|
-
const getStandardModelRate = (rate) => rate.kind === "flat" ? rate : rate.standard;
|
|
1153
|
-
const resolveModelRate = (rate, inputTokens) => rate.kind === "input-token-tiered" && inputTokens > rate.inputTokenThreshold ? rate.aboveThreshold : getStandardModelRate(rate);
|
|
1154
1163
|
/**
|
|
1155
|
-
*
|
|
1156
|
-
*
|
|
1157
|
-
*
|
|
1158
|
-
*
|
|
1159
|
-
* model IDs or the fallback rate.
|
|
1164
|
+
* Presentation metadata for every offered model. Keeping this exhaustive beside
|
|
1165
|
+
* the allowlist prevents a newly offered model from leaking a raw provider ID
|
|
1166
|
+
* into user-facing pickers. OpenRouter entries deliberately carry their model
|
|
1167
|
+
* creator's icon while their routing provider remains available separately.
|
|
1160
1168
|
*/
|
|
1161
|
-
const
|
|
1162
|
-
"gemini-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
outputPerMTok: 25e4,
|
|
1166
|
-
cachedInputPerMTok: 7500
|
|
1169
|
+
const MODEL_DISPLAY_METADATA = {
|
|
1170
|
+
"gemini-3.8-flash": {
|
|
1171
|
+
displayName: "Gemini 3.8 Flash",
|
|
1172
|
+
iconProvider: "google"
|
|
1167
1173
|
},
|
|
1168
|
-
"gemini-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
outputPerMTok: 1e6,
|
|
1172
|
-
cachedInputPerMTok: 31250
|
|
1174
|
+
"gemini-3.7-flash": {
|
|
1175
|
+
displayName: "Gemini 3.7 Flash",
|
|
1176
|
+
iconProvider: "google"
|
|
1173
1177
|
},
|
|
1174
|
-
"gemini-3.
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1178
|
+
"gemini-3.6-flash": {
|
|
1179
|
+
displayName: "Gemini 3.6 Flash",
|
|
1180
|
+
iconProvider: "google"
|
|
1181
|
+
},
|
|
1182
|
+
"gemini-3.5-flash-lite": {
|
|
1183
|
+
displayName: "Gemini 3.5 Flash Lite",
|
|
1184
|
+
iconProvider: "google"
|
|
1185
|
+
},
|
|
1186
|
+
"gemini-3.1-pro-preview": {
|
|
1187
|
+
displayName: "Gemini 3.1 Pro Preview",
|
|
1188
|
+
iconProvider: "google"
|
|
1179
1189
|
},
|
|
1180
1190
|
"gemini-3.5-flash": {
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
outputPerMTok: 9e5,
|
|
1184
|
-
cachedInputPerMTok: 15e3
|
|
1191
|
+
displayName: "Gemini 3.5 Flash",
|
|
1192
|
+
iconProvider: "google"
|
|
1185
1193
|
},
|
|
1186
|
-
"gemini-3.
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
outputPerMTok: 25e4,
|
|
1190
|
-
cachedInputPerMTok: 3e3
|
|
1194
|
+
"gemini-3.1-flash-lite": {
|
|
1195
|
+
displayName: "Gemini 3.1 Flash Lite",
|
|
1196
|
+
iconProvider: "google"
|
|
1191
1197
|
},
|
|
1192
|
-
"
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
outputPerMTok: 375e3,
|
|
1196
|
-
cachedInputPerMTok: 7500
|
|
1198
|
+
"claude-sonnet-5": {
|
|
1199
|
+
displayName: "Claude Sonnet 5",
|
|
1200
|
+
iconProvider: "anthropic"
|
|
1197
1201
|
},
|
|
1198
|
-
"
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1202
|
+
"claude-fable-5-1": {
|
|
1203
|
+
displayName: "Claude Fable 5.1",
|
|
1204
|
+
iconProvider: "anthropic"
|
|
1205
|
+
},
|
|
1206
|
+
"claude-fable-5": {
|
|
1207
|
+
displayName: "Claude Fable 5",
|
|
1208
|
+
iconProvider: "anthropic"
|
|
1209
|
+
},
|
|
1210
|
+
"claude-opus-5": {
|
|
1211
|
+
displayName: "Claude Opus 5",
|
|
1212
|
+
iconProvider: "anthropic"
|
|
1213
|
+
},
|
|
1214
|
+
"claude-opus-4-8": {
|
|
1215
|
+
displayName: "Claude Opus 4.8",
|
|
1216
|
+
iconProvider: "anthropic"
|
|
1203
1217
|
},
|
|
1204
|
-
"
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
outputPerMTok: 375e3,
|
|
1208
|
-
cachedInputPerMTok: 7500
|
|
1218
|
+
"claude-opus-4-7": {
|
|
1219
|
+
displayName: "Claude Opus 4.7",
|
|
1220
|
+
iconProvider: "anthropic"
|
|
1209
1221
|
},
|
|
1210
|
-
"
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
standard: {
|
|
1214
|
-
inputPerMTok: 2e5,
|
|
1215
|
-
outputPerMTok: 12e5,
|
|
1216
|
-
cachedInputPerMTok: 2e4
|
|
1217
|
-
},
|
|
1218
|
-
aboveThreshold: {
|
|
1219
|
-
inputPerMTok: 4e5,
|
|
1220
|
-
outputPerMTok: 18e5,
|
|
1221
|
-
cachedInputPerMTok: 4e4
|
|
1222
|
-
}
|
|
1222
|
+
"claude-sonnet-4-6": {
|
|
1223
|
+
displayName: "Claude Sonnet 4.6",
|
|
1224
|
+
iconProvider: "anthropic"
|
|
1223
1225
|
},
|
|
1224
|
-
"
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
outputPerMTok: 6e4,
|
|
1228
|
-
cachedInputPerMTok: 7500
|
|
1226
|
+
"claude-opus-4-6": {
|
|
1227
|
+
displayName: "Claude Opus 4.6",
|
|
1228
|
+
iconProvider: "anthropic"
|
|
1229
1229
|
},
|
|
1230
|
-
"
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
outputPerMTok: 1e6,
|
|
1234
|
-
cachedInputPerMTok: 125e3
|
|
1230
|
+
"claude-haiku-4-5-20251001": {
|
|
1231
|
+
displayName: "Claude Haiku 4.5",
|
|
1232
|
+
iconProvider: "anthropic"
|
|
1235
1233
|
},
|
|
1236
|
-
"gpt-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
outputPerMTok: 14e5,
|
|
1240
|
-
cachedInputPerMTok: 17500
|
|
1234
|
+
"gpt-6-astra": {
|
|
1235
|
+
displayName: "GPT-6 Astra",
|
|
1236
|
+
iconProvider: "openai"
|
|
1241
1237
|
},
|
|
1242
|
-
"gpt-5.
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
outputPerMTok: 125e3,
|
|
1246
|
-
cachedInputPerMTok: 2e3
|
|
1238
|
+
"gpt-5.6": {
|
|
1239
|
+
displayName: "GPT-5.6 Sol",
|
|
1240
|
+
iconProvider: "openai"
|
|
1247
1241
|
},
|
|
1248
|
-
"gpt-5.
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
outputPerMTok: 45e4,
|
|
1252
|
-
cachedInputPerMTok: 7500
|
|
1242
|
+
"gpt-5.6-terra": {
|
|
1243
|
+
displayName: "GPT-5.6 Terra",
|
|
1244
|
+
iconProvider: "openai"
|
|
1253
1245
|
},
|
|
1254
|
-
"gpt-5.
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
standard: {
|
|
1258
|
-
inputPerMTok: 25e4,
|
|
1259
|
-
outputPerMTok: 15e5,
|
|
1260
|
-
cachedInputPerMTok: 25e3
|
|
1261
|
-
},
|
|
1262
|
-
aboveThreshold: {
|
|
1263
|
-
inputPerMTok: 5e5,
|
|
1264
|
-
outputPerMTok: 225e4,
|
|
1265
|
-
cachedInputPerMTok: 5e4
|
|
1266
|
-
}
|
|
1246
|
+
"gpt-5.6-luna": {
|
|
1247
|
+
displayName: "GPT-5.6 Luna",
|
|
1248
|
+
iconProvider: "openai"
|
|
1267
1249
|
},
|
|
1268
1250
|
"gpt-5.5": {
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
standard: {
|
|
1272
|
-
inputPerMTok: 5e5,
|
|
1273
|
-
outputPerMTok: 3e6,
|
|
1274
|
-
cachedInputPerMTok: 5e4
|
|
1275
|
-
},
|
|
1276
|
-
aboveThreshold: {
|
|
1277
|
-
inputPerMTok: 1e6,
|
|
1278
|
-
outputPerMTok: 45e5,
|
|
1279
|
-
cachedInputPerMTok: 1e5
|
|
1280
|
-
}
|
|
1251
|
+
displayName: "GPT-5.5",
|
|
1252
|
+
iconProvider: "openai"
|
|
1281
1253
|
},
|
|
1282
|
-
"gpt-5.
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
standard: {
|
|
1286
|
-
inputPerMTok: 4e5,
|
|
1287
|
-
outputPerMTok: 2e6,
|
|
1288
|
-
cachedInputPerMTok: 4e4
|
|
1289
|
-
},
|
|
1290
|
-
aboveThreshold: {
|
|
1291
|
-
inputPerMTok: 8e5,
|
|
1292
|
-
outputPerMTok: 3e6,
|
|
1293
|
-
cachedInputPerMTok: 8e4
|
|
1294
|
-
}
|
|
1254
|
+
"gpt-5.4": {
|
|
1255
|
+
displayName: "GPT-5.4",
|
|
1256
|
+
iconProvider: "openai"
|
|
1295
1257
|
},
|
|
1296
|
-
"gpt-5.
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
standard: {
|
|
1300
|
-
inputPerMTok: 2e4,
|
|
1301
|
-
outputPerMTok: 12e4,
|
|
1302
|
-
cachedInputPerMTok: 2e3
|
|
1303
|
-
},
|
|
1304
|
-
aboveThreshold: {
|
|
1305
|
-
inputPerMTok: 4e4,
|
|
1306
|
-
outputPerMTok: 18e4,
|
|
1307
|
-
cachedInputPerMTok: 4e3
|
|
1308
|
-
}
|
|
1258
|
+
"gpt-5.4-mini": {
|
|
1259
|
+
displayName: "GPT-5.4 Mini",
|
|
1260
|
+
iconProvider: "openai"
|
|
1309
1261
|
},
|
|
1310
|
-
"gpt-5.
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
standard: {
|
|
1314
|
-
inputPerMTok: 2e5,
|
|
1315
|
-
outputPerMTok: 12e5,
|
|
1316
|
-
cachedInputPerMTok: 2e4
|
|
1317
|
-
},
|
|
1318
|
-
aboveThreshold: {
|
|
1319
|
-
inputPerMTok: 4e5,
|
|
1320
|
-
outputPerMTok: 18e5,
|
|
1321
|
-
cachedInputPerMTok: 4e4
|
|
1322
|
-
}
|
|
1262
|
+
"gpt-5.4-nano": {
|
|
1263
|
+
displayName: "GPT-5.4 Nano",
|
|
1264
|
+
iconProvider: "openai"
|
|
1323
1265
|
},
|
|
1324
|
-
"
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
outputPerMTok: 5e5,
|
|
1328
|
-
cachedInputPerMTok: 1e4
|
|
1266
|
+
"gpt-5.2": {
|
|
1267
|
+
displayName: "GPT-5.2",
|
|
1268
|
+
iconProvider: "openai"
|
|
1329
1269
|
},
|
|
1330
|
-
"
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
outputPerMTok: 15e5,
|
|
1334
|
-
cachedInputPerMTok: 3e4
|
|
1270
|
+
"openai/gpt-6-astra": {
|
|
1271
|
+
displayName: "GPT-6 Astra",
|
|
1272
|
+
iconProvider: "openai"
|
|
1335
1273
|
},
|
|
1336
|
-
"
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
outputPerMTok: 1e6,
|
|
1340
|
-
cachedInputPerMTok: 2e4
|
|
1274
|
+
"openai/gpt-5.6-luna": {
|
|
1275
|
+
displayName: "GPT-5.6 Luna",
|
|
1276
|
+
iconProvider: "openai"
|
|
1341
1277
|
},
|
|
1342
|
-
"
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
outputPerMTok: 25e5,
|
|
1346
|
-
cachedInputPerMTok: 5e4
|
|
1278
|
+
"openai/gpt-5.6-terra": {
|
|
1279
|
+
displayName: "GPT-5.6 Terra",
|
|
1280
|
+
iconProvider: "openai"
|
|
1347
1281
|
},
|
|
1348
|
-
"
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
outputPerMTok: 25e5,
|
|
1352
|
-
cachedInputPerMTok: 5e4
|
|
1282
|
+
"google/gemini-3.8-flash": {
|
|
1283
|
+
displayName: "Gemini 3.8 Flash",
|
|
1284
|
+
iconProvider: "google"
|
|
1353
1285
|
},
|
|
1354
|
-
"
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
outputPerMTok: 25e5,
|
|
1358
|
-
cachedInputPerMTok: 5e4
|
|
1286
|
+
"google/gemini-3.7-flash": {
|
|
1287
|
+
displayName: "Gemini 3.7 Flash",
|
|
1288
|
+
iconProvider: "google"
|
|
1359
1289
|
},
|
|
1360
|
-
"
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
outputPerMTok: 25e5,
|
|
1364
|
-
cachedInputPerMTok: 5e4
|
|
1290
|
+
"google/gemini-3.6-flash": {
|
|
1291
|
+
displayName: "Gemini 3.6 Flash",
|
|
1292
|
+
iconProvider: "google"
|
|
1365
1293
|
},
|
|
1366
|
-
"
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
outputPerMTok: 5e6,
|
|
1370
|
-
cachedInputPerMTok: 1e5
|
|
1294
|
+
"google/gemini-3.5-flash-lite": {
|
|
1295
|
+
displayName: "Gemini 3.5 Flash Lite",
|
|
1296
|
+
iconProvider: "google"
|
|
1371
1297
|
},
|
|
1372
|
-
"
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
outputPerMTok: 5e6,
|
|
1376
|
-
cachedInputPerMTok: 25e3
|
|
1298
|
+
"google/gemini-3.1-pro-preview": {
|
|
1299
|
+
displayName: "Gemini 3.1 Pro Preview",
|
|
1300
|
+
iconProvider: "google"
|
|
1377
1301
|
},
|
|
1378
|
-
"
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
outputPerMTok: 6e4
|
|
1302
|
+
"google/gemini-3.5-flash": {
|
|
1303
|
+
displayName: "Gemini 3.5 Flash",
|
|
1304
|
+
iconProvider: "google"
|
|
1382
1305
|
},
|
|
1383
|
-
"
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
outputPerMTok: 15e4
|
|
1306
|
+
"google/gemini-3.1-flash-lite": {
|
|
1307
|
+
displayName: "Gemini 3.1 Flash Lite",
|
|
1308
|
+
iconProvider: "google"
|
|
1387
1309
|
},
|
|
1388
|
-
"
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
outputPerMTok: 75e4
|
|
1310
|
+
"anthropic/claude-sonnet-5": {
|
|
1311
|
+
displayName: "Claude Sonnet 5",
|
|
1312
|
+
iconProvider: "anthropic"
|
|
1392
1313
|
},
|
|
1393
|
-
"
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
outputPerMTok: 75e4
|
|
1314
|
+
"anthropic/claude-opus-5": {
|
|
1315
|
+
displayName: "Claude Opus 5",
|
|
1316
|
+
iconProvider: "anthropic"
|
|
1397
1317
|
},
|
|
1398
|
-
"
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
outputPerMTok: 5e5
|
|
1318
|
+
"anthropic/claude-opus-4.8": {
|
|
1319
|
+
displayName: "Claude Opus 4.8",
|
|
1320
|
+
iconProvider: "anthropic"
|
|
1402
1321
|
},
|
|
1403
|
-
"
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
outputPerMTok: 15e4
|
|
1322
|
+
"anthropic/claude-sonnet-4.6": {
|
|
1323
|
+
displayName: "Claude Sonnet 4.6",
|
|
1324
|
+
iconProvider: "anthropic"
|
|
1407
1325
|
},
|
|
1408
|
-
"
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
outputPerMTok: 15e4
|
|
1326
|
+
"openai/gpt-5.5": {
|
|
1327
|
+
displayName: "GPT-5.5",
|
|
1328
|
+
iconProvider: "openai"
|
|
1412
1329
|
},
|
|
1413
|
-
"
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
outputPerMTok: 6e5
|
|
1330
|
+
"openai/gpt-5.4-mini": {
|
|
1331
|
+
displayName: "GPT-5.4 Mini",
|
|
1332
|
+
iconProvider: "openai"
|
|
1417
1333
|
},
|
|
1418
1334
|
"us.anthropic.claude-sonnet-4-5-20250929-v1:0": {
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
outputPerMTok: 15e5,
|
|
1422
|
-
cachedInputPerMTok: 3e4
|
|
1335
|
+
displayName: "Claude Sonnet 4.5",
|
|
1336
|
+
iconProvider: "anthropic"
|
|
1423
1337
|
},
|
|
1424
1338
|
"us.anthropic.claude-haiku-4-5-20251001-v1:0": {
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
outputPerMTok: 5e5,
|
|
1428
|
-
cachedInputPerMTok: 1e4
|
|
1339
|
+
displayName: "Claude Haiku 4.5",
|
|
1340
|
+
iconProvider: "anthropic"
|
|
1429
1341
|
},
|
|
1430
1342
|
"us.amazon.nova-pro-v1:0": {
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
outputPerMTok: 32e4
|
|
1343
|
+
displayName: "Amazon Nova Pro",
|
|
1344
|
+
iconProvider: "bedrock"
|
|
1434
1345
|
},
|
|
1435
1346
|
"us.amazon.nova-lite-v1:0": {
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
outputPerMTok: 24e3
|
|
1347
|
+
displayName: "Amazon Nova Lite",
|
|
1348
|
+
iconProvider: "bedrock"
|
|
1439
1349
|
},
|
|
1440
1350
|
"us.amazon.nova-micro-v1:0": {
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
outputPerMTok: 14e3
|
|
1351
|
+
displayName: "Amazon Nova Micro",
|
|
1352
|
+
iconProvider: "bedrock"
|
|
1444
1353
|
},
|
|
1445
1354
|
"openai.gpt-oss-120b-1:0": {
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
outputPerMTok: 6e4
|
|
1355
|
+
displayName: "GPT OSS 120B",
|
|
1356
|
+
iconProvider: "openai"
|
|
1449
1357
|
},
|
|
1450
1358
|
"openai.gpt-oss-20b-1:0": {
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
outputPerMTok: 3e4
|
|
1359
|
+
displayName: "GPT OSS 20B",
|
|
1360
|
+
iconProvider: "openai"
|
|
1454
1361
|
},
|
|
1455
1362
|
"us.deepseek.r1-v1:0": {
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1363
|
+
displayName: "DeepSeek R1",
|
|
1364
|
+
iconProvider: "bedrock"
|
|
1365
|
+
},
|
|
1366
|
+
"mistral-large-latest": {
|
|
1367
|
+
displayName: "Mistral Large",
|
|
1368
|
+
iconProvider: "mistral"
|
|
1369
|
+
},
|
|
1370
|
+
"mistral-medium-latest": {
|
|
1371
|
+
displayName: "Mistral Medium",
|
|
1372
|
+
iconProvider: "mistral"
|
|
1373
|
+
},
|
|
1374
|
+
"mistral-small-latest": {
|
|
1375
|
+
displayName: "Mistral Small",
|
|
1376
|
+
iconProvider: "mistral"
|
|
1377
|
+
},
|
|
1378
|
+
"magistral-medium-latest": {
|
|
1379
|
+
displayName: "Magistral Medium",
|
|
1380
|
+
iconProvider: "mistral"
|
|
1381
|
+
},
|
|
1382
|
+
"magistral-small": {
|
|
1383
|
+
displayName: "Magistral Small",
|
|
1384
|
+
iconProvider: "mistral"
|
|
1385
|
+
},
|
|
1386
|
+
"pixtral-large-latest": {
|
|
1387
|
+
displayName: "Pixtral Large",
|
|
1388
|
+
iconProvider: "mistral"
|
|
1389
|
+
}
|
|
1390
|
+
};
|
|
1391
|
+
const MODEL_DISPLAY_METADATA_BY_ID = MODEL_DISPLAY_METADATA;
|
|
1392
|
+
const getModelDisplayMetadata = (modelId) => MODEL_DISPLAY_METADATA_BY_ID[normalizeModelCatalogId(modelId)] ?? null;
|
|
1393
|
+
/**
|
|
1394
|
+
* Offered models whose provider API accepts PDF/file content. Generated from
|
|
1395
|
+
* live models.dev input modalities plus dated, reviewed source corrections;
|
|
1396
|
+
* the nightly upstream check rejects drift.
|
|
1397
|
+
*/
|
|
1398
|
+
const BYOK_DOCUMENT_INPUT_MODEL_OPTIONS = MODEL_DOCUMENT_INPUT_OPTIONS;
|
|
1399
|
+
const isBYOKProviderRoleSupported = ({ provider, role }) => role !== "pdf" || BYOK_DOCUMENT_INPUT_MODEL_OPTIONS[provider].length > 0;
|
|
1400
|
+
const isBYOKModelRoleSupported = ({ provider, modelId, role }) => {
|
|
1401
|
+
if (role !== "pdf") return true;
|
|
1402
|
+
return BYOK_DOCUMENT_INPUT_MODEL_OPTIONS[provider].includes(modelId);
|
|
1403
|
+
};
|
|
1404
|
+
/**
|
|
1405
|
+
* Models whose provider adapter can receive a PDF `document` content part in a
|
|
1406
|
+
* chat turn without throwing. This is a narrower, runtime question than the
|
|
1407
|
+
* pdf ROLE (`BYOK_DOCUMENT_INPUT_MODEL_OPTIONS`, which curates model selection
|
|
1408
|
+
* for PDF-processing flows): it only asks "will the chat stream survive this
|
|
1409
|
+
* attachment." The Mistral text adapter maps PDF `document` parts to
|
|
1410
|
+
* `document_url`, so its
|
|
1411
|
+
* vision models accept PDF attachments even though Mistral is deliberately not
|
|
1412
|
+
* offered as a pdf-role provider. Mistral's `document_url` takes PDF but NOT
|
|
1413
|
+
* textual documents (docx/txt/csv/md), so those still gate on
|
|
1414
|
+
* `BYOK_DOCUMENT_INPUT_MODEL_OPTIONS` (which excludes Mistral). Superset
|
|
1415
|
+
* invariant: every pdf-role document model also accepts a PDF chat attachment.
|
|
1416
|
+
*/
|
|
1417
|
+
const CHAT_PDF_ATTACHMENT_MODEL_OPTIONS = {
|
|
1418
|
+
...BYOK_DOCUMENT_INPUT_MODEL_OPTIONS,
|
|
1419
|
+
mistral: [
|
|
1420
|
+
"mistral-medium-latest",
|
|
1421
|
+
"mistral-small-latest",
|
|
1422
|
+
"pixtral-large-latest"
|
|
1423
|
+
]
|
|
1424
|
+
};
|
|
1425
|
+
const isChatPdfAttachmentModelSupported = ({ provider, modelId }) => {
|
|
1426
|
+
return CHAT_PDF_ATTACHMENT_MODEL_OPTIONS[provider].includes(modelId);
|
|
1427
|
+
};
|
|
1428
|
+
/**
|
|
1429
|
+
* Whether a model id is currently offered for this provider+role: it
|
|
1430
|
+
* must be in the curated catalog for the provider AND satisfy the
|
|
1431
|
+
* role's input-modality constraint (PDF needs a document-capable
|
|
1432
|
+
* model). This is the runtime allowlist, so a model dropped by a
|
|
1433
|
+
* catalog bump stops being valid here even though it is still a
|
|
1434
|
+
* non-empty string in a stored org config.
|
|
1435
|
+
*/
|
|
1436
|
+
const isOfferedBYOKModelForRole = ({ provider, modelId, role }) => {
|
|
1437
|
+
return BYOK_MODEL_OPTIONS[provider].includes(modelId) && isBYOKModelRoleSupported({
|
|
1438
|
+
provider,
|
|
1439
|
+
modelId,
|
|
1440
|
+
role
|
|
1441
|
+
});
|
|
1442
|
+
};
|
|
1443
|
+
/**
|
|
1444
|
+
* Resolve a model id that will actually work for this provider+role,
|
|
1445
|
+
* keeping the SAME provider. Returns the caller's model unchanged when
|
|
1446
|
+
* it is still offered; otherwise falls back to the provider's per-role
|
|
1447
|
+
* default (`BYOK_DEFAULT_MODELS`). Returns `null` only when the
|
|
1448
|
+
* provider has no valid model for the role at all — the sole case
|
|
1449
|
+
* today is `mistral` + `pdf`, because the TanStack Mistral adapter
|
|
1450
|
+
* exposes no `document` input modality, so no Mistral model (not even
|
|
1451
|
+
* the default) can serve the PDF role.
|
|
1452
|
+
*
|
|
1453
|
+
* Used to auto-heal org AI configs whose pinned model was removed by a
|
|
1454
|
+
* catalog bump, so generation resolves to a supported model instead of
|
|
1455
|
+
* 400-ing (or forwarding a retired id to the provider).
|
|
1456
|
+
*/
|
|
1457
|
+
const resolveWorkingBYOKModelForRole = ({ provider, modelId, role }) => {
|
|
1458
|
+
if (isOfferedBYOKModelForRole({
|
|
1459
|
+
provider,
|
|
1460
|
+
modelId,
|
|
1461
|
+
role
|
|
1462
|
+
})) return modelId;
|
|
1463
|
+
const fallback = BYOK_DEFAULT_MODELS[provider][role];
|
|
1464
|
+
return isOfferedBYOKModelForRole({
|
|
1465
|
+
provider,
|
|
1466
|
+
modelId: fallback,
|
|
1467
|
+
role
|
|
1468
|
+
}) ? fallback : null;
|
|
1469
|
+
};
|
|
1470
|
+
/**
|
|
1471
|
+
* Anthropic models that use the adaptive-thinking request shape
|
|
1472
|
+
* (`thinking: { type: "adaptive" }`). Newer Claude models reject the
|
|
1473
|
+
* legacy budget-based form, so every Opus 4.6+/Sonnet 4.6/Fable entry
|
|
1474
|
+
* offered above must appear here or it will 400 on the reasoning role.
|
|
1475
|
+
*/
|
|
1476
|
+
const ANTHROPIC_ADAPTIVE_THINKING_MODELS = [
|
|
1477
|
+
"claude-sonnet-5",
|
|
1478
|
+
"claude-opus-5",
|
|
1479
|
+
"claude-sonnet-4-6",
|
|
1480
|
+
"claude-opus-4-6",
|
|
1481
|
+
"claude-opus-4-7",
|
|
1482
|
+
"claude-opus-4-8",
|
|
1483
|
+
"claude-fable-5",
|
|
1484
|
+
"claude-fable-5-1"
|
|
1485
|
+
];
|
|
1486
|
+
/**
|
|
1487
|
+
* Canonical reasoning-effort ladder, ordered weakest to strongest.
|
|
1488
|
+
* Union of every effort keyword the offered providers accept; no
|
|
1489
|
+
* single model accepts all of them, which is exactly why the
|
|
1490
|
+
* generated per-model `MODEL_REASONING_EFFORTS` exists.
|
|
1491
|
+
*/
|
|
1492
|
+
const REASONING_EFFORTS = [
|
|
1493
|
+
"none",
|
|
1494
|
+
"minimal",
|
|
1495
|
+
"low",
|
|
1496
|
+
"medium",
|
|
1497
|
+
"high",
|
|
1498
|
+
"xhigh",
|
|
1499
|
+
"max"
|
|
1500
|
+
];
|
|
1501
|
+
const resolvedReasoningEffortSchema = v.pipe(v.picklist(REASONING_EFFORTS), v.brand("ResolvedReasoningEffort"));
|
|
1502
|
+
/**
|
|
1503
|
+
* Per-provider model served when a user's included budget is exhausted
|
|
1504
|
+
* and no explicit selection overrides it. Falls back to each
|
|
1505
|
+
* provider's fast-role default; providers without a first-class
|
|
1506
|
+
* adapter path cannot serve the lane. Total over providers so a new
|
|
1507
|
+
* provider lands with an explicit decision.
|
|
1508
|
+
*/
|
|
1509
|
+
const FALLBACK_CHAT_MODEL_BY_PROVIDER = {
|
|
1510
|
+
google: DEFAULT_MODELS.google.fast,
|
|
1511
|
+
openrouter: "openai/gpt-5.6-luna",
|
|
1512
|
+
openai: "gpt-5.6-luna",
|
|
1513
|
+
anthropic: DEFAULT_MODELS.anthropic.fast,
|
|
1514
|
+
bedrock: DEFAULT_MODELS.bedrock.fast,
|
|
1515
|
+
mistral: DEFAULT_MODELS.mistral.fast,
|
|
1516
|
+
azure_foundry: null,
|
|
1517
|
+
openai_compatible: null,
|
|
1518
|
+
huggingface: null
|
|
1519
|
+
};
|
|
1520
|
+
/**
|
|
1521
|
+
* Provider-native IDs that are exact aliases of an offered catalog ID.
|
|
1522
|
+
* Every metadata lookup normalizes here, so instance/dev overrides cannot
|
|
1523
|
+
* bypass rates or capabilities and duplicated alias rows cannot drift apart.
|
|
1524
|
+
*/
|
|
1525
|
+
const MODEL_CATALOG_ID_ALIASES = {
|
|
1526
|
+
"gpt-5.6-sol": "gpt-5.6",
|
|
1527
|
+
"claude-opus-4.8": "claude-opus-4-8",
|
|
1528
|
+
"claude-sonnet-4.6": "claude-sonnet-4-6"
|
|
1529
|
+
};
|
|
1530
|
+
const MODEL_CATALOG_ID_ALIAS_TARGET_BY_ID = MODEL_CATALOG_ID_ALIASES;
|
|
1531
|
+
const normalizeModelCatalogId = (modelId) => MODEL_CATALOG_ID_ALIAS_TARGET_BY_ID[modelId] ?? modelId;
|
|
1532
|
+
const MODEL_TEMPERATURE_POLICY_BY_ID = MODEL_TEMPERATURE_POLICIES;
|
|
1533
|
+
/**
|
|
1534
|
+
* Whether stella should emit a `temperature` override for this model.
|
|
1535
|
+
* This is deliberately stricter than provider acceptance: deprecated
|
|
1536
|
+
* parameters that are accepted but ignored resolve to `false`. Unknown ids
|
|
1537
|
+
* also resolve to `false` (send nothing; provider default). Callers must
|
|
1538
|
+
* never index `MODEL_TEMPERATURE_POLICIES` directly with
|
|
1539
|
+
* a runtime string.
|
|
1540
|
+
*/
|
|
1541
|
+
const shouldEmitTemperature = (modelId) => MODEL_TEMPERATURE_POLICY_BY_ID[normalizeModelCatalogId(modelId)] === "emit";
|
|
1542
|
+
const MODEL_REASONING_EFFORTS_BY_ID = MODEL_REASONING_EFFORTS;
|
|
1543
|
+
/**
|
|
1544
|
+
* Declared reasoning-effort values for a model ID, or `null` when the
|
|
1545
|
+
* model has no effort control or is not catalogued (custom deployments,
|
|
1546
|
+
* env overrides). Callers must never index `MODEL_REASONING_EFFORTS`
|
|
1547
|
+
* directly with a runtime string.
|
|
1548
|
+
*/
|
|
1549
|
+
const getModelReasoningEfforts = (modelId) => MODEL_REASONING_EFFORTS_BY_ID[normalizeModelCatalogId(modelId)] ?? null;
|
|
1550
|
+
const MODEL_DEFAULT_REASONING_EFFORTS_BY_ID = MODEL_DEFAULT_REASONING_EFFORTS;
|
|
1551
|
+
/**
|
|
1552
|
+
* Concrete provider-default effort published for a model, or null when the
|
|
1553
|
+
* provider exposes no named default. Unknown ids also resolve to null.
|
|
1554
|
+
*/
|
|
1555
|
+
const getModelDefaultReasoningEffort = (modelId) => MODEL_DEFAULT_REASONING_EFFORTS_BY_ID[normalizeModelCatalogId(modelId)] ?? null;
|
|
1556
|
+
const asResolvedReasoningEffort = (effort) => v.parse(resolvedReasoningEffortSchema, effort);
|
|
1557
|
+
/**
|
|
1558
|
+
* Clamp a requested reasoning effort into the target model's declared
|
|
1559
|
+
* capability.
|
|
1560
|
+
*
|
|
1561
|
+
* - Unknown model or no effort control → `null` (send no effort at
|
|
1562
|
+
* all; the provider default is the only universally safe choice).
|
|
1563
|
+
* - Requested value supported → returned unchanged.
|
|
1564
|
+
* - Otherwise → the supported value nearest on `REASONING_EFFORTS`,
|
|
1565
|
+
* preferring the weaker side on ties, so a "none" request against a
|
|
1566
|
+
* reasoning-mandatory model degrades to its weakest tier instead of
|
|
1567
|
+
* erroring, and requests above the model's ceiling clamp down to it.
|
|
1568
|
+
*/
|
|
1569
|
+
const resolveReasoningEffort = ({ modelId, requested }) => {
|
|
1570
|
+
const efforts = getModelReasoningEfforts(modelId);
|
|
1571
|
+
const weakest = efforts?.at(0);
|
|
1572
|
+
if (efforts === null || weakest === void 0) return null;
|
|
1573
|
+
if (efforts.includes(requested)) return asResolvedReasoningEffort(requested);
|
|
1574
|
+
const requestedRank = REASONING_EFFORTS.indexOf(requested);
|
|
1575
|
+
let nearest = weakest;
|
|
1576
|
+
let nearestDistance = Number.POSITIVE_INFINITY;
|
|
1577
|
+
for (const effort of efforts) {
|
|
1578
|
+
const distance = Math.abs(REASONING_EFFORTS.indexOf(effort) - requestedRank);
|
|
1579
|
+
if (distance < nearestDistance || distance === nearestDistance && REASONING_EFFORTS.indexOf(effort) < REASONING_EFFORTS.indexOf(nearest)) {
|
|
1580
|
+
nearest = effort;
|
|
1581
|
+
nearestDistance = distance;
|
|
1582
|
+
}
|
|
1459
1583
|
}
|
|
1584
|
+
return asResolvedReasoningEffort(nearest);
|
|
1460
1585
|
};
|
|
1461
1586
|
const MODEL_RATES_BY_ID = MODEL_RATES;
|
|
1462
1587
|
const OPENROUTER_UNDERLYING_MODEL_ID_BY_ID = Object.fromEntries(BYOK_MODEL_OPTIONS.openrouter.map((modelId) => [modelId, modelId.slice(modelId.indexOf("/") + 1)]));
|
|
@@ -1501,6 +1626,7 @@ const CONTEXT_WINDOW_TOKENS = {
|
|
|
1501
1626
|
"gpt-5.4-mini": 4e5,
|
|
1502
1627
|
"gpt-5.4": 4e5,
|
|
1503
1628
|
"gpt-5.5": 4e5,
|
|
1629
|
+
"gpt-6-astra": 922e3,
|
|
1504
1630
|
"gpt-5.6": 922e3,
|
|
1505
1631
|
"gpt-5.6-luna": 922e3,
|
|
1506
1632
|
"gpt-5.6-terra": 922e3,
|
|
@@ -1532,6 +1658,7 @@ const CONTEXT_WINDOW_TOKENS = {
|
|
|
1532
1658
|
"anthropic/claude-opus-5": 1e6,
|
|
1533
1659
|
"anthropic/claude-opus-4.8": 2e5,
|
|
1534
1660
|
"anthropic/claude-sonnet-4.6": 2e5,
|
|
1661
|
+
"openai/gpt-6-astra": 922e3,
|
|
1535
1662
|
"openai/gpt-5.6-luna": 922e3,
|
|
1536
1663
|
"openai/gpt-5.6-terra": 922e3,
|
|
1537
1664
|
"openai/gpt-5.5": 4e5,
|
|
@@ -1559,4 +1686,4 @@ const DEFAULT_CONTEXT_WINDOW_TOKENS = 128e3;
|
|
|
1559
1686
|
*/
|
|
1560
1687
|
const getContextWindowTokens = (modelId) => CONTEXT_WINDOW_TOKENS_BY_ID[normalizeModelCatalogId(modelId)] ?? 128e3;
|
|
1561
1688
|
//#endregion
|
|
1562
|
-
export { AI_PROVIDERS, ANTHROPIC_ADAPTIVE_THINKING_MODELS, BYOK_DEFAULT_MODELS, BYOK_DOCUMENT_INPUT_MODEL_OPTIONS, BYOK_MODEL_OPTIONS, CAPABILITY_OVERRIDES, CHAT_PDF_ATTACHMENT_MODEL_OPTIONS, CONTEXT_WINDOW_TOKENS, DEFAULT_CONTEXT_WINDOW_TOKENS, DEFAULT_MODELS, DOCUMENT_INPUT_OVERRIDES, FALLBACK_CHAT_MODEL_BY_PROVIDER, FIRST_PARTY_MODEL_PROVIDERS, MODEL_CATALOG_ID_ALIASES, MODEL_CATALOG_PROVIDER_KIND, MODEL_DEFAULT_REASONING_EFFORTS, MODEL_DISPLAY_METADATA, MODEL_RATES, MODEL_REASONING_EFFORTS, MODEL_ROLES, MODEL_TEMPERATURE_POLICIES, REASONING_EFFORTS, TANSTACK_AI_PROVIDERS, TEMPERATURE_POLICIES, getContextWindowTokens, getModelDefaultReasoningEffort, getModelDisplayMetadata, getModelRate, getModelReasoningEfforts, getStandardModelRate, isBYOKModelRoleSupported, isBYOKProviderRoleSupported, isChatPdfAttachmentModelSupported, normalizeModelCatalogId, resolveModelRate, resolveReasoningEffort, resolveWorkingBYOKModelForRole, shouldEmitTemperature };
|
|
1689
|
+
export { AI_PROVIDERS, ANTHROPIC_ADAPTIVE_THINKING_MODELS, BYOK_DEFAULT_MODELS, BYOK_DOCUMENT_INPUT_MODEL_OPTIONS, BYOK_MODEL_OPTIONS, CAPABILITY_OVERRIDES, CHAT_PDF_ATTACHMENT_MODEL_OPTIONS, CONTEXT_WINDOW_TOKENS, DEFAULT_CONTEXT_WINDOW_TOKENS, DEFAULT_MODELS, DOCUMENT_INPUT_OVERRIDES, FALLBACK_CHAT_MODEL_BY_PROVIDER, FIRST_PARTY_MODEL_PROVIDERS, MODELS_DEV_RATE_PROVIDER_BY_CATALOG_PROVIDER, MODELS_DEV_RATE_SOURCE_ALIASES, MODEL_CATALOG_ID_ALIASES, MODEL_CATALOG_PROVIDER_KIND, MODEL_DEFAULT_REASONING_EFFORTS, MODEL_DISPLAY_METADATA, MODEL_RATES, MODEL_RATE_UNITS_PER_USD, MODEL_REASONING_EFFORTS, MODEL_ROLES, MODEL_TEMPERATURE_POLICIES, REASONING_EFFORTS, RETAINED_MODELS_DEV_RATE_ENTRIES, TANSTACK_AI_PROVIDERS, TEMPERATURE_POLICIES, getContextWindowTokens, getModelDefaultReasoningEffort, getModelDisplayMetadata, getModelRate, getModelReasoningEfforts, getStandardModelRate, isBYOKModelRoleSupported, isBYOKProviderRoleSupported, isChatPdfAttachmentModelSupported, normalizeModelCatalogId, resolveModelRate, resolveReasoningEffort, resolveWorkingBYOKModelForRole, shouldEmitTemperature };
|