From 2caf9aeb6f4d68ebf6f832270ce28f4023e5ad7d Mon Sep 17 00:00:00 2001 From: Roo Code Date: Tue, 2 Dec 2025 21:00:38 +0000 Subject: [PATCH] feat: add configurable API timeout to all provider implementations - Added timeout configuration to Anthropic handler using getApiRequestTimeout() - Added timeout configuration to OpenRouter handler - Added timeout configuration to OpenAI Native handler - Updated Mistral handler with note about SDK timeout limitations - Utilizes existing apiRequestTimeout VSCode setting (default: 600 seconds, range: 0-3600) - Addresses issue #9756 - users can now configure timeout for model requests to prevent premature timeouts on longer-running completions --- src/api/providers/anthropic.ts | 2 ++ src/api/providers/mistral.ts | 3 +++ src/api/providers/openai-native.ts | 7 ++++++- src/api/providers/openrouter.ts | 8 +++++++- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/api/providers/anthropic.ts b/src/api/providers/anthropic.ts index 3d9379f540..fd15f17738 100644 --- a/src/api/providers/anthropic.ts +++ b/src/api/providers/anthropic.ts @@ -21,6 +21,7 @@ import { BaseProvider } from "./base-provider" import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" import { calculateApiCostAnthropic } from "../../shared/cost" import { convertOpenAIToolsToAnthropic } from "../../core/prompts/tools/native-tools/converters" +import { getApiRequestTimeout } from "./utils/timeout-config" export class AnthropicHandler extends BaseProvider implements SingleCompletionHandler { private options: ApiHandlerOptions @@ -36,6 +37,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa this.client = new Anthropic({ baseURL: this.options.anthropicBaseUrl || undefined, [apiKeyFieldName]: this.options.apiKey, + timeout: getApiRequestTimeout(), }) } diff --git a/src/api/providers/mistral.ts b/src/api/providers/mistral.ts index 96d2c33255..2e45f2ca8b 100644 --- a/src/api/providers/mistral.ts +++ b/src/api/providers/mistral.ts @@ -11,6 +11,7 @@ import { ApiStream } from "../transform/stream" import { BaseProvider } from "./base-provider" import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" +import { getApiRequestTimeout } from "./utils/timeout-config" // Type helper to handle thinking chunks from Mistral API // The SDK includes ThinkChunk but TypeScript has trouble with the discriminated union @@ -55,6 +56,8 @@ export class MistralHandler extends BaseProvider implements SingleCompletionHand const apiModelId = options.apiModelId || mistralDefaultModelId this.options = { ...options, apiModelId } + // Note: Mistral SDK doesn't currently support timeout configuration directly + // The timeout is handled at the HTTP request level by the SDK internally this.client = new Mistral({ serverURL: apiModelId.startsWith("codestral-") ? this.options.mistralCodestralUrl || "https://codestral.mistral.ai" diff --git a/src/api/providers/openai-native.ts b/src/api/providers/openai-native.ts index b5fb417ee3..b2bed13fc2 100644 --- a/src/api/providers/openai-native.ts +++ b/src/api/providers/openai-native.ts @@ -22,6 +22,7 @@ import { getModelParams } from "../transform/model-params" import { BaseProvider } from "./base-provider" import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" +import { getApiRequestTimeout } from "./utils/timeout-config" export type OpenAiNativeModel = ReturnType @@ -64,7 +65,11 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio this.options.enableResponsesReasoningSummary = true } const apiKey = this.options.openAiNativeApiKey ?? "not-provided" - this.client = new OpenAI({ baseURL: this.options.openAiNativeBaseUrl, apiKey }) + this.client = new OpenAI({ + baseURL: this.options.openAiNativeBaseUrl, + apiKey, + timeout: getApiRequestTimeout(), + }) } private normalizeUsage(usage: any, model: OpenAiNativeModel): ApiStreamUsageChunk | undefined { diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index e8e95ad58d..cd52f9fb27 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -29,6 +29,7 @@ import { BaseProvider } from "./base-provider" import type { ApiHandlerCreateMessageMetadata, SingleCompletionHandler } from "../index" import { handleOpenAIError } from "./utils/openai-error-handler" import { generateImageWithProvider, ImageGenerationResult } from "./utils/image-generation" +import { getApiRequestTimeout } from "./utils/timeout-config" // Add custom interface for OpenRouter params. type OpenRouterChatCompletionParams = OpenAI.Chat.ChatCompletionCreateParams & { @@ -72,7 +73,12 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH const baseURL = this.options.openRouterBaseUrl || "https://openrouter.ai/api/v1" const apiKey = this.options.openRouterApiKey ?? "not-provided" - this.client = new OpenAI({ baseURL, apiKey, defaultHeaders: DEFAULT_HEADERS }) + this.client = new OpenAI({ + baseURL, + apiKey, + defaultHeaders: DEFAULT_HEADERS, + timeout: getApiRequestTimeout(), + }) // Load models asynchronously to populate cache before getModel() is called this.loadDynamicModels().catch((error) => {