@@ -1231,53 +1231,60 @@ export class AIChatPanel extends UI.Panel.Panel {
12311231 }
12321232
12331233 /**
1234- * Refreshes Groq models from the API
1234+ * Generic method to refresh models for any provider
1235+ * @param providerId The provider to refresh models for (e.g., 'groq', 'anthropic', 'googleai')
12351236 */
1236- async #refreshGroqModels ( ) : Promise < void > {
1237+ async #refreshProviderModels ( providerId : ProviderType ) : Promise < void > {
12371238 try {
1238- const groqApiKey = localStorage . getItem ( 'ai_chat_groq_api_key' ) ;
1239-
1240- if ( ! groqApiKey ) {
1241- logger . info ( 'No Groq API key configured, skipping model refresh' ) ;
1239+ // Import provider configs dynamically to avoid circular dependencies
1240+ const { GENERIC_PROVIDERS } = await import ( './settings/providerConfigs.js' ) ;
1241+
1242+ // Get provider config
1243+ const config = GENERIC_PROVIDERS . find ( p => p . id === providerId ) ;
1244+ if ( ! config || ! config . hasFetchButton ) {
1245+ logger . debug ( `Provider ${ providerId } does not support model fetching` ) ;
1246+ this . #updateModelOptions( [ ] , false ) ;
1247+ this . performUpdate ( ) ;
12421248 return ;
12431249 }
1244-
1245- const { models : groqModels } = await this . #fetchGroqModels( groqApiKey ) ;
1246- this . #updateModelOptions( groqModels , false ) ;
1247-
1248- // Update MODEL_OPTIONS to reflect the fetched models
1249- this . performUpdate ( ) ;
1250- } catch ( error ) {
1251- logger . error ( 'Failed to refresh Groq models:' , error ) ;
1252- // Clear Groq models on error
1253- AIChatPanel . updateModelOptions ( [ ] , false ) ;
1254- this . performUpdate ( ) ;
1255- }
1256- }
12571250
1258- /**
1259- * Fetches Groq models from the API
1260- * @param apiKey API key to use for the request
1261- * @returns Object containing models
1262- */
1263- async #fetchGroqModels( apiKey : string ) : Promise < { models : ModelOption [ ] } > {
1264- try {
1265- // Fetch models from Groq
1266- const models = await LLMClient . fetchGroqModels ( apiKey ) ;
1251+ // Get API key
1252+ const apiKey = localStorage . getItem ( config . apiKeyStorageKey ) ;
1253+ if ( ! apiKey && ! config . apiKeyOptional ) {
1254+ logger . info ( `No API key configured for ${ providerId } , skipping model refresh` ) ;
1255+ this . #updateModelOptions( [ ] , false ) ;
1256+ this . performUpdate ( ) ;
1257+ return ;
1258+ }
12671259
1268- // Transform the models to the format we need
1269- const groqModels = models . map ( model => ( {
1260+ // Get endpoint (only relevant for some providers like LiteLLM)
1261+ const endpoint = LLMProviderRegistry . getProviderEndpoint ( providerId as LLMProvider ) ;
1262+
1263+ // Fetch models using the generic registry method
1264+ const models = await LLMProviderRegistry . fetchProviderModels (
1265+ providerId as LLMProvider ,
1266+ apiKey ?? '' ,
1267+ endpoint ?? undefined
1268+ ) ;
1269+
1270+ // Convert to ModelOption format
1271+ const modelOptions : ModelOption [ ] = models . map ( model => ( {
12701272 value : model . id ,
1271- label : `Groq: ${ model . id } ` ,
1272- type : 'groq' as const
1273+ label : config . useNameAsLabel ? ( model . name || model . id ) : model . id ,
1274+ type : providerId
12731275 } ) ) ;
12741276
1275- logger . info ( `Fetched ${ groqModels . length } Groq models` ) ;
1276- return { models : groqModels } ;
1277+ logger . info ( `Fetched ${ modelOptions . length } ${ providerId } models` ) ;
1278+
1279+ // Update model options
1280+ this . #updateModelOptions( modelOptions , false ) ;
1281+ this . performUpdate ( ) ;
1282+
12771283 } catch ( error ) {
1278- logger . error ( 'Failed to fetch Groq models:' , error ) ;
1279- // Return empty array on error
1280- return { models : [ ] } ;
1284+ logger . error ( `Failed to refresh ${ providerId } models:` , error ) ;
1285+ // Clear models on error
1286+ this . #updateModelOptions( [ ] , false ) ;
1287+ this . performUpdate ( ) ;
12811288 }
12821289 }
12831290
@@ -2506,39 +2513,35 @@ export class AIChatPanel extends UI.Panel.Panel {
25062513 // Get the selected provider
25072514 const prevProvider = localStorage . getItem ( PROVIDER_SELECTION_KEY ) || 'openai' ;
25082515 const newProvider = localStorage . getItem ( PROVIDER_SELECTION_KEY ) || 'openai' ;
2509-
2516+
25102517 logger . info ( `Provider changing from ${ prevProvider } to ${ newProvider } ` ) ;
2511-
2518+
25122519 // Load saved settings
25132520 this . #apiKey = localStorage . getItem ( 'ai_chat_api_key' ) ;
25142521 this . #liteLLMApiKey = localStorage . getItem ( LITELLM_API_KEY_STORAGE_KEY ) ;
25152522 this . #liteLLMEndpoint = localStorage . getItem ( LITELLM_ENDPOINT_KEY ) ;
2516-
2517- // Reset model options based on the new provider
2523+
2524+ // Import provider configs to check if provider supports model fetching
2525+ const { GENERIC_PROVIDERS } = await import ( './settings/providerConfigs.js' ) ;
2526+ const config = GENERIC_PROVIDERS . find ( p => p . id === newProvider ) ;
2527+
2528+ // Reset model options
2529+ this . #updateModelOptions( [ ] , false ) ;
2530+
2531+ // Refresh models based on provider type
25182532 if ( newProvider === 'litellm' ) {
2519- // First update model options with empty models
2520- this . #updateModelOptions( [ ] , false ) ;
2521-
2522- // Then refresh LiteLLM models
2533+ // LiteLLM has special handling for wildcard flag
25232534 await this . #refreshLiteLLMModels( ) ;
2524- } else if ( newProvider === 'groq' ) {
2525- // For Groq, update model options and refresh models if API key exists
2526- this . #updateModelOptions( [ ] , false ) ;
2527-
2528- const groqApiKey = localStorage . getItem ( 'ai_chat_groq_api_key' ) ;
2529- if ( groqApiKey ) {
2530- await this . #refreshGroqModels( ) ;
2531- }
2532- } else {
2533- // For OpenAI, just update model options with empty LiteLLM models
2534- this . #updateModelOptions( [ ] , false ) ;
2535+ } else if ( config ?. hasFetchButton ) {
2536+ // For all other providers with fetch capability, use generic method
2537+ await this . #refreshProviderModels( newProvider as ProviderType ) ;
25352538 }
2536-
2539+
25372540 this . #updateModelSelections( ) ;
2538-
2541+
25392542 // Validate models after updating selections
25402543 this . #validateAndFixModelSelections( ) ;
2541-
2544+
25422545 this . #initializeAgentService( ) ;
25432546 // Re-initialize MCP based on latest settings
25442547 try {
@@ -2588,15 +2591,21 @@ export class AIChatPanel extends UI.Panel.Panel {
25882591
25892592 // Check if the current selected model is valid for the new provider
25902593 const selectedModelOption = MODEL_OPTIONS . find ( opt => opt . value === this . #selectedModel) ;
2591- if ( ! selectedModelOption || selectedModelOption . type !== currentProvider ) {
2592- logger . info ( `Selected model ${ this . #selectedModel} is not valid for provider ${ currentProvider } ` ) ;
2593-
2594+ if ( ! this . #selectedModel || ! selectedModelOption || selectedModelOption . type !== currentProvider ) {
2595+ logger . info ( `Selected model ${ this . #selectedModel} is not valid for provider ${ currentProvider } , selecting default ` ) ;
2596+
25942597 // Try to use provider default main model first
25952598 if ( providerDefaults . main && MODEL_OPTIONS . some ( option => option . value === providerDefaults . main ) ) {
25962599 this . #selectedModel = providerDefaults . main ;
2600+ logger . info ( `Set main model to provider default: ${ providerDefaults . main } ` ) ;
25972601 } else if ( MODEL_OPTIONS . length > 0 ) {
25982602 // Otherwise, use the first available model
25992603 this . #selectedModel = MODEL_OPTIONS [ 0 ] . value ;
2604+ logger . info ( `Set main model to first available: ${ this . #selectedModel} ` ) ;
2605+ } else {
2606+ // No models available
2607+ this . #selectedModel = '' ;
2608+ logger . warn ( `No models available for provider ${ currentProvider } ` ) ;
26002609 }
26012610 localStorage . setItem ( MODEL_SELECTION_KEY , this . #selectedModel) ;
26022611 }
0 commit comments