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