diff --git a/packages/openai-adapters/src/apis/OpenAI.test.ts b/packages/openai-adapters/src/apis/OpenAI.test.ts new file mode 100644 index 00000000000..70fd3fa7081 --- /dev/null +++ b/packages/openai-adapters/src/apis/OpenAI.test.ts @@ -0,0 +1,38 @@ +import { describe, expect, it } from "vitest"; + +import { OpenAIApi } from "./OpenAI.js"; + +describe("OpenAIApi timeout conversion", () => { + const baseConfig = { + provider: "openai" as const, + apiKey: "test-key", + }; + + it("converts requestOptions.timeout from seconds to milliseconds", () => { + const api = new OpenAIApi({ + ...baseConfig, + requestOptions: { timeout: 300 }, + }); + + expect(api.openai.timeout).toBe(300_000); + }); + + it("preserves an explicit zero timeout instead of falling back to the SDK default", () => { + const api = new OpenAIApi({ + ...baseConfig, + requestOptions: { timeout: 0 }, + }); + + // The schema permits `timeout: 0`, and the SDK resolves its own default + // with `?? DEFAULT_TIMEOUT`, so 0 must survive as 0 rather than becoming + // undefined and silently turning into the 10-minute default. + expect(api.openai.timeout).toBe(0); + }); + + it("leaves the SDK default in place when no timeout is configured", () => { + const api = new OpenAIApi(baseConfig); + + // 10 minutes — the OpenAI SDK's DEFAULT_TIMEOUT. + expect(api.openai.timeout).toBe(600_000); + }); +}); diff --git a/packages/openai-adapters/src/apis/OpenAI.ts b/packages/openai-adapters/src/apis/OpenAI.ts index d0f8d30ca3a..3f750110c3d 100644 --- a/packages/openai-adapters/src/apis/OpenAI.ts +++ b/packages/openai-adapters/src/apis/OpenAI.ts @@ -45,7 +45,14 @@ export class OpenAIApi implements BaseLlmApi { apiKey: config.apiKey ?? "", baseURL: this.apiBase, fetch: customFetch(config.requestOptions), - timeout: config?.requestOptions?.timeout || undefined, + // requestOptions.timeout is in seconds; the OpenAI SDK expects ms. + // Nullish rather than truthy: the schema permits `timeout: 0`, and both + // the SDK (`options.timeout ?? DEFAULT_TIMEOUT`) and our own + // getAgentOptions treat 0 as a real value, not as "unset". + timeout: + config?.requestOptions?.timeout != null + ? config.requestOptions.timeout * 1000 + : undefined, }); } modifyChatBody(body: T): T {