1+ import { toVendorSlug } from "@/lib/modelVendor" ;
12import { logger } from "@/server/logger" ;
23import { getModelById , getModelsIndex } from "./modelsDev" ;
34
@@ -8,6 +9,59 @@ export interface ModelCapabilities {
89 contextLimit : number ;
910}
1011
12+ const MODEL_VENDOR_PATTERNS : Array < { pattern : RegExp ; vendor : string } > = [
13+ { pattern : / ^ g p t [ - _ ] / , vendor : "openai" } ,
14+ { pattern : / ^ c l a u d e [ - _ ] / , vendor : "anthropic" } ,
15+ { pattern : / ^ g e m i n i [ - _ ] / , vendor : "google" } ,
16+ { pattern : / ^ g r o k [ - _ ] / , vendor : "xai" } ,
17+ { pattern : / ( ^ | [ - _ ] ) k i m i ( [ - _ . ] | $ ) / , vendor : "moonshot" } ,
18+ { pattern : / ^ g l m [ - _ ] / , vendor : "z-ai" } ,
19+ ] ;
20+
21+ const MULTI_VENDOR_PROVIDERS = new Set ( [ "openrouter" , "opencode" ] ) ;
22+
23+ function extractVendorFromModelId ( modelId : string ) : string | null {
24+ const separatorIndex = modelId . indexOf ( "/" ) ;
25+ if ( separatorIndex <= 0 ) {
26+ return null ;
27+ }
28+
29+ const vendorPart = modelId . slice ( 0 , separatorIndex ) ;
30+ if ( ! vendorPart ) {
31+ return null ;
32+ }
33+
34+ return toVendorSlug ( vendorPart ) ;
35+ }
36+
37+ function inferVendorFromModelId ( modelId : string ) : string | null {
38+ const normalizedModelId = modelId . trim ( ) . toLowerCase ( ) ;
39+
40+ for ( const { pattern, vendor } of MODEL_VENDOR_PATTERNS ) {
41+ if ( pattern . test ( normalizedModelId ) ) {
42+ return vendor ;
43+ }
44+ }
45+
46+ return null ;
47+ }
48+
49+ export function resolveModelVendor (
50+ providerId : string ,
51+ modelId : string ,
52+ ) : string {
53+ const normalizedProvider = toVendorSlug ( providerId ) ;
54+ const vendorFromModelId = extractVendorFromModelId ( modelId ) ;
55+
56+ if ( ! MULTI_VENDOR_PROVIDERS . has ( normalizedProvider ) ) {
57+ return normalizedProvider ;
58+ }
59+
60+ return (
61+ vendorFromModelId ?? inferVendorFromModelId ( modelId ) ?? normalizedProvider
62+ ) ;
63+ }
64+
1165/**
1266 * Normalize model IDs from models.dev for each provider.
1367 * Returns the model ID as-is (matching models.dev format) and infers vendor for display.
@@ -24,55 +78,7 @@ function normalizeModelId(
2478 modelId : string ,
2579 providerId : string ,
2680) : { id : string ; vendor : string } {
27- // For openrouter, models are already in vendor/model format
28- // Return as-is and extract vendor from the ID
29- if ( providerId === "openrouter" ) {
30- // Extract vendor from the model ID (first part before /)
31- const parts = modelId . split ( "/" ) ;
32- const vendor = parts [ 0 ] ?? modelId ;
33- return { id : modelId , vendor } ;
34- }
35-
36- // If already in vendor/model format, return as-is (for other providers with vendor prefix)
37- if ( modelId . includes ( "/" ) ) {
38- const parts = modelId . split ( "/" ) ;
39- const vendor = parts [ 0 ] ;
40- if ( ! vendor ) {
41- throw new Error ( `Invalid model ID format: ${ modelId } ` ) ;
42- }
43- return { id : modelId , vendor } ;
44- }
45-
46- // For opencode provider, models don't have vendor prefix in their ID
47- // Infer vendor from model name for display/grouping purposes only
48- if ( providerId === "opencode" ) {
49- if ( modelId . startsWith ( "gpt-" ) || modelId . startsWith ( "gpt_" ) ) {
50- return { id : modelId , vendor : "openai" } ;
51- }
52- if ( modelId . startsWith ( "claude-" ) ) {
53- return { id : modelId , vendor : "anthropic" } ;
54- }
55- if ( modelId . startsWith ( "gemini-" ) ) {
56- return { id : modelId , vendor : "google" } ;
57- }
58- if ( modelId . startsWith ( "grok-" ) ) {
59- return { id : modelId , vendor : "xai" } ;
60- }
61- if ( modelId . includes ( "kimi" ) || modelId . startsWith ( "kimi-" ) ) {
62- return { id : modelId , vendor : "moonshot" } ;
63- }
64- if ( modelId . startsWith ( "glm-" ) || modelId . startsWith ( "glm_" ) ) {
65- return { id : modelId , vendor : "z-ai" } ;
66- }
67- // Default: use the model ID as vendor if no match
68- return { id : modelId , vendor : modelId } ;
69- }
70-
71- // For other providers, use the first part of the model ID as vendor if available
72- const parts = modelId . split ( "/" ) ;
73- const vendor = parts [ 0 ] ?? modelId ;
74-
75- return { id : modelId , vendor } ;
81+ return { id : modelId , vendor : resolveModelVendor ( providerId , modelId ) } ;
7682}
7783
7884/**
0 commit comments