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
This commit is contained in:
Roo Code 2025-12-02 21:00:38 +00:00
parent 9a1d7a673b
commit 2caf9aeb6f
4 changed files with 18 additions and 2 deletions

View file

@ -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(),
})
}

View file

@ -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"

View file

@ -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<OpenAiNativeHandler["getModel"]>
@ -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 {

View file

@ -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) => {