Skip to content

Commit 6753e95

Browse files
committed
fix the provider fetch issue
1 parent 9f01e22 commit 6753e95

File tree

3 files changed

+99
-81
lines changed

3 files changed

+99
-81
lines changed

front_end/panels/ai_chat/LLM/LLMProviderRegistry.ts

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -116,25 +116,29 @@ export class LLMProviderRegistry {
116116
* Create a temporary provider instance for utility operations
117117
* Used when provider isn't registered yet (e.g., during setup/validation)
118118
*/
119-
private static createTemporaryProvider(providerType: LLMProvider): LLMProviderInterface | null {
119+
private static createTemporaryProvider(
120+
providerType: LLMProvider,
121+
apiKey: string = '',
122+
endpoint?: string
123+
): LLMProviderInterface | null {
120124
try {
121125
switch (providerType) {
122126
case 'openai':
123-
return new OpenAIProvider('');
127+
return new OpenAIProvider(apiKey);
124128
case 'litellm':
125-
return new LiteLLMProvider('', '');
129+
return new LiteLLMProvider(apiKey, endpoint || '');
126130
case 'groq':
127-
return new GroqProvider('');
131+
return new GroqProvider(apiKey);
128132
case 'openrouter':
129-
return new OpenRouterProvider('');
133+
return new OpenRouterProvider(apiKey);
130134
case 'browseroperator':
131-
return new BrowserOperatorProvider(null, '');
135+
return new BrowserOperatorProvider(null, apiKey);
132136
case 'cerebras':
133-
return new CerebrasProvider('');
137+
return new CerebrasProvider(apiKey);
134138
case 'anthropic':
135-
return new AnthropicProvider('');
139+
return new AnthropicProvider(apiKey);
136140
case 'googleai':
137-
return new GoogleAIProvider('');
141+
return new GoogleAIProvider(apiKey);
138142
default:
139143
logger.warn(`Unknown provider type: ${providerType}`);
140144
return null;
@@ -148,16 +152,23 @@ export class LLMProviderRegistry {
148152
/**
149153
* Get or create a provider instance for utility operations
150154
* Prefers registered instance, falls back to temporary instance
155+
* @param providerType The type of provider to get/create
156+
* @param apiKey Optional API key for temporary provider creation
157+
* @param endpoint Optional endpoint for temporary provider creation
151158
*/
152-
private static getOrCreateProvider(providerType: LLMProvider): LLMProviderInterface | null {
159+
private static getOrCreateProvider(
160+
providerType: LLMProvider,
161+
apiKey?: string,
162+
endpoint?: string
163+
): LLMProviderInterface | null {
153164
// Try to get registered provider first
154165
const registered = this.getProvider(providerType);
155166
if (registered) {
156167
return registered;
157168
}
158169

159-
// Fall back to creating temporary instance
160-
return this.createTemporaryProvider(providerType);
170+
// Fall back to creating temporary instance with provided credentials
171+
return this.createTemporaryProvider(providerType, apiKey || '', endpoint);
161172
}
162173

163174
/**
@@ -296,19 +307,16 @@ export class LLMProviderRegistry {
296307
apiKey: string,
297308
endpoint?: string
298309
): Promise<ModelInfo[]> {
299-
const provider = this.getOrCreateProvider(providerType);
310+
// Get or create provider with provided credentials
311+
const provider = this.getOrCreateProvider(providerType, apiKey, endpoint);
300312
if (!provider) {
301313
logger.warn(`Provider ${providerType} not available`);
302314
return [];
303315
}
304316

305317
try {
306-
// Use the provider's fetchModels method if available
307-
if ('fetchModels' in provider && typeof provider.fetchModels === 'function') {
308-
return await provider.fetchModels(apiKey, endpoint);
309-
}
310-
311-
// Fallback to getModels
318+
// Use getModels() which returns standardized ModelInfo[] with 'id' property
319+
// The provider was created with the provided apiKey, so getModels() will use it
312320
return await provider.getModels();
313321
} catch (error) {
314322
logger.error(`Failed to fetch models for ${providerType}:`, error);

front_end/panels/ai_chat/core/LLMConfigurationManager.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export class LLMConfigurationManager {
7777

7878
/**
7979
* Get the main model with override fallback
80+
* Note: For default fallback, ensure models are fetched and selected in the UI
8081
*/
8182
getMainModel(): string {
8283
if (this.overrideConfig?.mainModel) {

front_end/panels/ai_chat/ui/AIChatPanel.ts

Lines changed: 71 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)