@@ -1033,5 +1033,219 @@ function objectToYaml(obj: unknown, indent: number = 0): string {
10331033 return String ( obj ) ;
10341034}
10351035
1036+ // ===========================
1037+ // Multi-Model Pool Management
1038+ // ===========================
1039+
1040+ /**
1041+ * Migrate legacy embeddingPoolConfig to new modelPools array
1042+ * This function ensures backward compatibility with existing configurations
1043+ */
1044+ function migrateEmbeddingPoolToModelPools ( config : LiteLLMApiConfig ) : void {
1045+ // Skip if already has modelPools or no legacy config
1046+ if ( config . modelPools && config . modelPools . length > 0 ) return ;
1047+ if ( ! config . embeddingPoolConfig ) return ;
1048+
1049+ // Convert legacy embeddingPoolConfig to ModelPoolConfig
1050+ const legacyPool = config . embeddingPoolConfig ;
1051+ const modelPool : import ( '../types/litellm-api-config.js' ) . ModelPoolConfig = {
1052+ id : `pool-embedding-${ Date . now ( ) } ` ,
1053+ modelType : 'embedding' ,
1054+ enabled : legacyPool . enabled ,
1055+ targetModel : legacyPool . targetModel ,
1056+ strategy : legacyPool . strategy ,
1057+ autoDiscover : legacyPool . autoDiscover ,
1058+ excludedProviderIds : legacyPool . excludedProviderIds || [ ] ,
1059+ defaultCooldown : legacyPool . defaultCooldown ,
1060+ defaultMaxConcurrentPerKey : legacyPool . defaultMaxConcurrentPerKey ,
1061+ name : `Embedding Pool - ${ legacyPool . targetModel } ` ,
1062+ description : 'Migrated from legacy embeddingPoolConfig' ,
1063+ } ;
1064+
1065+ config . modelPools = [ modelPool ] ;
1066+ // Keep legacy config for backward compatibility with old CodexLens versions
1067+ }
1068+
1069+ /**
1070+ * Get all model pool configurations
1071+ * Returns empty array if no pools configured
1072+ */
1073+ export function getModelPools ( baseDir : string ) : import ( '../types/litellm-api-config.js' ) . ModelPoolConfig [ ] {
1074+ const config = loadLiteLLMApiConfig ( baseDir ) ;
1075+
1076+ // Auto-migrate if needed
1077+ migrateEmbeddingPoolToModelPools ( config ) ;
1078+
1079+ return config . modelPools || [ ] ;
1080+ }
1081+
1082+ /**
1083+ * Get a specific model pool by ID
1084+ */
1085+ export function getModelPool (
1086+ baseDir : string ,
1087+ poolId : string
1088+ ) : import ( '../types/litellm-api-config.js' ) . ModelPoolConfig | undefined {
1089+ const pools = getModelPools ( baseDir ) ;
1090+ return pools . find ( p => p . id === poolId ) ;
1091+ }
1092+
1093+ /**
1094+ * Add a new model pool configuration
1095+ */
1096+ export function addModelPool (
1097+ baseDir : string ,
1098+ poolConfig : Omit < import ( '../types/litellm-api-config.js' ) . ModelPoolConfig , 'id' >
1099+ ) : { poolId : string ; syncResult ?: { success : boolean ; message : string ; endpointCount ?: number } } {
1100+ const config = loadLiteLLMApiConfig ( baseDir ) ;
1101+
1102+ // Auto-migrate if needed
1103+ migrateEmbeddingPoolToModelPools ( config ) ;
1104+
1105+ // Ensure modelPools array exists
1106+ if ( ! config . modelPools ) {
1107+ config . modelPools = [ ] ;
1108+ }
1109+
1110+ // Generate unique ID
1111+ const poolId = `pool-${ poolConfig . modelType } -${ Date . now ( ) } ` ;
1112+
1113+ const newPool : import ( '../types/litellm-api-config.js' ) . ModelPoolConfig = {
1114+ ...poolConfig ,
1115+ id : poolId ,
1116+ } ;
1117+
1118+ config . modelPools . push ( newPool ) ;
1119+ saveConfig ( baseDir , config ) ;
1120+
1121+ // Sync to CodexLens if this is an embedding pool
1122+ const syncResult = poolConfig . modelType === 'embedding' && poolConfig . enabled
1123+ ? syncCodexLensConfig ( baseDir )
1124+ : undefined ;
1125+
1126+ return { poolId, syncResult } ;
1127+ }
1128+
1129+ /**
1130+ * Update an existing model pool configuration
1131+ */
1132+ export function updateModelPool (
1133+ baseDir : string ,
1134+ poolId : string ,
1135+ updates : Partial < Omit < import ( '../types/litellm-api-config.js' ) . ModelPoolConfig , 'id' > >
1136+ ) : { success : boolean ; syncResult ?: { success : boolean ; message : string ; endpointCount ?: number } } {
1137+ const config = loadLiteLLMApiConfig ( baseDir ) ;
1138+
1139+ // Auto-migrate if needed
1140+ migrateEmbeddingPoolToModelPools ( config ) ;
1141+
1142+ if ( ! config . modelPools ) {
1143+ return { success : false } ;
1144+ }
1145+
1146+ const poolIndex = config . modelPools . findIndex ( p => p . id === poolId ) ;
1147+ if ( poolIndex === - 1 ) {
1148+ return { success : false } ;
1149+ }
1150+
1151+ // Apply updates
1152+ config . modelPools [ poolIndex ] = {
1153+ ...config . modelPools [ poolIndex ] ,
1154+ ...updates ,
1155+ } ;
1156+
1157+ saveConfig ( baseDir , config ) ;
1158+
1159+ // Sync to CodexLens if this is an enabled embedding pool
1160+ const pool = config . modelPools [ poolIndex ] ;
1161+ const syncResult = pool . modelType === 'embedding' && pool . enabled
1162+ ? syncCodexLensConfig ( baseDir )
1163+ : undefined ;
1164+
1165+ return { success : true , syncResult } ;
1166+ }
1167+
1168+ /**
1169+ * Delete a model pool configuration
1170+ */
1171+ export function deleteModelPool (
1172+ baseDir : string ,
1173+ poolId : string
1174+ ) : { success : boolean ; syncResult ?: { success : boolean ; message : string ; endpointCount ?: number } } {
1175+ const config = loadLiteLLMApiConfig ( baseDir ) ;
1176+
1177+ if ( ! config . modelPools ) {
1178+ return { success : false } ;
1179+ }
1180+
1181+ const poolIndex = config . modelPools . findIndex ( p => p . id === poolId ) ;
1182+ if ( poolIndex === - 1 ) {
1183+ return { success : false } ;
1184+ }
1185+
1186+ const deletedPool = config . modelPools [ poolIndex ] ;
1187+ config . modelPools . splice ( poolIndex , 1 ) ;
1188+
1189+ saveConfig ( baseDir , config ) ;
1190+
1191+ // Sync to CodexLens if we deleted an embedding pool
1192+ const syncResult = deletedPool . modelType === 'embedding'
1193+ ? syncCodexLensConfig ( baseDir )
1194+ : undefined ;
1195+
1196+ return { success : true , syncResult } ;
1197+ }
1198+
1199+ /**
1200+ * Get available models for a specific model type
1201+ * Used for pool configuration UI
1202+ */
1203+ export function getAvailableModelsForType (
1204+ baseDir : string ,
1205+ modelType : import ( '../types/litellm-api-config.js' ) . ModelPoolType
1206+ ) : Array < { modelId : string ; modelName : string ; providers : string [ ] } > {
1207+ const config = loadLiteLLMApiConfig ( baseDir ) ;
1208+ const availableModels : Array < { modelId : string ; modelName : string ; providers : string [ ] } > = [ ] ;
1209+ const modelMap = new Map < string , { modelId : string ; modelName : string ; providers : string [ ] } > ( ) ;
1210+
1211+ for ( const provider of config . providers ) {
1212+ if ( ! provider . enabled ) continue ;
1213+
1214+ let models : typeof provider . embeddingModels | undefined ;
1215+
1216+ switch ( modelType ) {
1217+ case 'embedding' :
1218+ models = provider . embeddingModels ;
1219+ break ;
1220+ case 'llm' :
1221+ models = provider . llmModels ;
1222+ break ;
1223+ case 'reranker' :
1224+ models = provider . rerankerModels ;
1225+ break ;
1226+ }
1227+
1228+ if ( ! models ) continue ;
1229+
1230+ for ( const model of models ) {
1231+ if ( ! model . enabled ) continue ;
1232+
1233+ const key = model . id ;
1234+ if ( modelMap . has ( key ) ) {
1235+ modelMap . get ( key ) ! . providers . push ( provider . name ) ;
1236+ } else {
1237+ modelMap . set ( key , {
1238+ modelId : model . id ,
1239+ modelName : model . name ,
1240+ providers : [ provider . name ] ,
1241+ } ) ;
1242+ }
1243+ }
1244+ }
1245+
1246+ availableModels . push ( ...Array . from ( modelMap . values ( ) ) ) ;
1247+ return availableModels ;
1248+ }
1249+
10361250// Re-export types
10371251export type { ProviderCredential , CustomEndpoint , ProviderType , CacheStrategy , CodexLensEmbeddingRotation , CodexLensEmbeddingProvider , EmbeddingPoolConfig } ;
0 commit comments