fix: handle zero timeout correctly for OpenAI-compatible providers

- Pass undefined instead of 0 to OpenAI SDK when apiRequestTimeout is set to 0
- OpenAI SDK interprets 0 as immediate timeout rather than no timeout
- Fixes timeout issues with LM Studio and other OpenAI-compatible providers
- Updates tests to verify the correct behavior

Fixes #7366
This commit is contained in:
Roo Code 2025-08-24 06:58:17 +00:00
parent fc70012f54
commit 3f9846773d
6 changed files with 17 additions and 11 deletions

View file

@ -72,7 +72,7 @@ describe("LmStudioHandler timeout configuration", () => {
)
})
it("should handle zero timeout (no timeout)", () => {
it("should handle zero timeout (no timeout) by passing undefined to OpenAI client", () => {
;(getApiRequestTimeout as any).mockReturnValue(0)
const options: ApiHandlerOptions = {
@ -84,7 +84,7 @@ describe("LmStudioHandler timeout configuration", () => {
expect(mockOpenAIConstructor).toHaveBeenCalledWith(
expect.objectContaining({
timeout: 0, // No timeout
timeout: undefined, // OpenAI SDK expects undefined for no timeout, not 0
}),
)
})

View file

@ -71,7 +71,7 @@ describe("OllamaHandler timeout configuration", () => {
)
})
it("should handle zero timeout (no timeout)", () => {
it("should handle zero timeout (no timeout) by passing undefined to OpenAI client", () => {
;(getApiRequestTimeout as any).mockReturnValue(0)
const options: ApiHandlerOptions = {
@ -84,7 +84,7 @@ describe("OllamaHandler timeout configuration", () => {
expect(mockOpenAIConstructor).toHaveBeenCalledWith(
expect.objectContaining({
timeout: 0, // No timeout
timeout: undefined, // OpenAI SDK expects undefined for no timeout, not 0
}),
)
})

View file

@ -125,7 +125,7 @@ describe("OpenAiHandler timeout configuration", () => {
)
})
it("should handle zero timeout (no timeout)", () => {
it("should handle zero timeout (no timeout) by passing undefined to OpenAI client", () => {
;(getApiRequestTimeout as any).mockReturnValue(0)
const options: ApiHandlerOptions = {
@ -137,7 +137,7 @@ describe("OpenAiHandler timeout configuration", () => {
expect(mockOpenAIConstructor).toHaveBeenCalledWith(
expect.objectContaining({
timeout: 0, // No timeout
timeout: undefined, // OpenAI SDK expects undefined for no timeout, not 0
}),
)
})

View file

@ -24,10 +24,12 @@ export class LmStudioHandler extends BaseProvider implements SingleCompletionHan
super()
this.options = options
const timeout = getApiRequestTimeout()
this.client = new OpenAI({
baseURL: (this.options.lmStudioBaseUrl || "http://localhost:1234") + "/v1",
apiKey: "noop",
timeout: getApiRequestTimeout(),
// OpenAI SDK expects undefined for no timeout, not 0
timeout: timeout === 0 ? undefined : timeout,
})
}

View file

@ -25,10 +25,12 @@ export class OllamaHandler extends BaseProvider implements SingleCompletionHandl
super()
this.options = options
const timeout = getApiRequestTimeout()
this.client = new OpenAI({
baseURL: (this.options.ollamaBaseUrl || "http://localhost:11434") + "/v1",
apiKey: "ollama",
timeout: getApiRequestTimeout(),
// OpenAI SDK expects undefined for no timeout, not 0
timeout: timeout === 0 ? undefined : timeout,
})
}

View file

@ -48,6 +48,8 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
}
const timeout = getApiRequestTimeout()
// OpenAI SDK expects undefined for no timeout, not 0
const clientTimeout = timeout === 0 ? undefined : timeout
if (isAzureAiInference) {
// Azure AI Inference Service (e.g., for DeepSeek) uses a different path structure
@ -56,7 +58,7 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
apiKey,
defaultHeaders: headers,
defaultQuery: { "api-version": this.options.azureApiVersion || "2024-05-01-preview" },
timeout,
timeout: clientTimeout,
})
} else if (isAzureOpenAi) {
// Azure API shape slightly differs from the core API shape:
@ -66,14 +68,14 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
apiKey,
apiVersion: this.options.azureApiVersion || azureOpenAiDefaultApiVersion,
defaultHeaders: headers,
timeout,
timeout: clientTimeout,
})
} else {
this.client = new OpenAI({
baseURL,
apiKey,
defaultHeaders: headers,
timeout,
timeout: clientTimeout,
})
}
}