diff --git a/src/api/providers/fetchers/openrouter.ts b/src/api/providers/fetchers/openrouter.ts index 0cf65fb09c..274a3e28de 100644 --- a/src/api/providers/fetchers/openrouter.ts +++ b/src/api/providers/fetchers/openrouter.ts @@ -99,7 +99,7 @@ export async function getOpenRouterModels(options?: ApiHandlerOptions): Promise< const baseURL = options?.openRouterBaseUrl || "https://openrouter.ai/api/v1" try { - const response = await axios.get(`${baseURL}/models`) + const response = await axios.get(`${baseURL}/models`, { timeout: 10_000 }) const result = openRouterModelsResponseSchema.safeParse(response.data) const data = result.success ? result.data.data : response.data.data @@ -147,7 +147,9 @@ export async function getOpenRouterModelEndpoints( const baseURL = options?.openRouterBaseUrl || "https://openrouter.ai/api/v1" try { - const response = await axios.get(`${baseURL}/models/${modelId}/endpoints`) + const response = await axios.get(`${baseURL}/models/${modelId}/endpoints`, { + timeout: 10_000, + }) const result = openRouterModelEndpointsResponseSchema.safeParse(response.data) const data = result.success ? result.data.data : response.data.data diff --git a/src/api/providers/fetchers/requesty.ts b/src/api/providers/fetchers/requesty.ts index 64c7de6689..49e5cde4e1 100644 --- a/src/api/providers/fetchers/requesty.ts +++ b/src/api/providers/fetchers/requesty.ts @@ -18,7 +18,7 @@ export async function getRequestyModels(baseUrl?: string, apiKey?: string): Prom const resolvedBaseUrl = toRequestyServiceUrl(baseUrl) const modelsUrl = new URL("v1/models", resolvedBaseUrl) - const response = await axios.get(modelsUrl.toString(), { headers }) + const response = await axios.get(modelsUrl.toString(), { headers, timeout: 10_000 }) const rawModels = response.data.data for (const rawModel of rawModels) { diff --git a/src/api/providers/fetchers/unbound.ts b/src/api/providers/fetchers/unbound.ts index 3841011809..24702a9925 100644 --- a/src/api/providers/fetchers/unbound.ts +++ b/src/api/providers/fetchers/unbound.ts @@ -14,7 +14,7 @@ export async function getUnboundModels(apiKey?: string | null): Promise(`${baseURL}/models`) + const response = await axios.get(`${baseURL}/models`, { timeout: 10_000 }) const result = vercelAiGatewayModelsResponseSchema.safeParse(response.data) const data = result.success ? result.data.data : response.data.data diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 4ec715cf10..dd19ba05e1 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -956,10 +956,23 @@ export const webviewMessageHandler = async ( await flushModels(targetCandidate.options, true) } + // Fetch models incrementally: send each provider's models to the webview + // as soon as they resolve, rather than waiting for all providers to finish. + // This ensures providers with working DNS are available immediately while + // blocked providers fail gracefully in the background (see #11747). const results = await Promise.allSettled( modelFetchPromises.map(async ({ key, options }) => { const models = await safeGetModels(options) - return { key, models } // The key is `ProviderName` here. + + // Send this provider's models to the webview immediately. + routerModels[key] = models + provider.postMessageToWebview({ + type: "routerModels", + routerModels: { ...routerModels }, + values: providerFilter ? { provider: requestedProvider } : undefined, + }) + + return { key, models } }), ) @@ -968,8 +981,6 @@ export const webviewMessageHandler = async ( if (result.status === "fulfilled") { routerModels[routerName] = result.value.models - - // Ollama and LM Studio settings pages still need these events. They are not fetched here. } else { // Handle rejection: Post a specific error message for this provider. const errorMessage = result.reason instanceof Error ? result.reason.message : String(result.reason) @@ -986,6 +997,7 @@ export const webviewMessageHandler = async ( } }) + // Send final aggregated message for backward compatibility. provider.postMessageToWebview({ type: "routerModels", routerModels,