Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4c82de4
fix(api): apply apiRequestTimeout consistently across OpenAI/Anthropi…
Jun 11, 2026
e9461af
docs(i18n): list providers where apiRequestTimeout has no effect
Jun 11, 2026
94344e7
test(api): align provider tests with apiRequestTimeout wiring
Jun 11, 2026
59ff0ed
docs(i18n): drop provider examples from apiRequestTimeout recommendation
Jun 11, 2026
591efef
chore: fix typo in Turkish apiRequestTimeout description
daewoongoh Jun 11, 2026
eee6cb0
fix(api): round timeout ms and restrict apiRequestTimeout to 1-3600s
daewoongoh Jun 15, 2026
1bb1827
docs(i18n): remove '0 = no timeout', scope GCP Vertex AI, add Moonsho…
daewoongoh Jun 15, 2026
0bad013
docs(i18n): tighten apiRequestTimeout description across all locales
daewoongoh Jun 15, 2026
cca8e68
refactor(api): extract timeout constants and validate range bounds
daewoongoh Jun 15, 2026
5411599
test(api): cover timeout passthrough for Vertex auth variants
daewoongoh Jun 15, 2026
9be9cec
Merge branch 'Zoo-Code-Org:main' into fix/api-request-timeout-consist…
daewoongoh Jun 15, 2026
09be296
test(api): make GoogleAuth mock newable in Vertex tests
daewoongoh Jun 15, 2026
ca18a8e
Merge branch 'Zoo-Code-Org:main' into fix/api-request-timeout-consist…
daewoongoh Jun 17, 2026
2aeb510
Add changeset for API request timeout
daewoongoh Jun 18, 2026
ad547d2
refactor: standardize API request timeout handling across providers
daewoongoh Jun 18, 2026
8cba19e
refactor: use class field initialization for timeoutMs in BaseProvider
daewoongoh Jun 18, 2026
b25a2d0
fix(tests): add getConfiguration mock to vscode provider tests
daewoongoh Jun 18, 2026
72ee779
chore(i18n): update apiRequestTimeout description for Google Gemini c…
daewoongoh Jun 18, 2026
fa1cb19
test: add timeout config mock to provider tests
daewoongoh Jun 18, 2026
2bbc093
Merge branch 'main' into fix/api-request-timeout-consistency
navedmerchant Jun 19, 2026
760f2ee
Merge branch 'Zoo-Code-Org:main' into fix/api-request-timeout-consist…
daewoongoh Jun 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions src/api/providers/__tests__/anthropic-vertex.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@

import { Anthropic } from "@anthropic-ai/sdk"
import { AnthropicVertex } from "@anthropic-ai/vertex-sdk"
import { GoogleAuth } from "google-auth-library"

import { VERTEX_1M_CONTEXT_MODEL_IDS } from "@roo-code/types"

import { ApiStreamChunk } from "../../transform/stream"

import { AnthropicVertexHandler } from "../anthropic-vertex"

vitest.mock("google-auth-library", () => ({
GoogleAuth: vitest.fn().mockImplementation(function (opts) {
return { __googleAuthOptions: opts }
}),
}))

vitest.mock("@anthropic-ai/vertex-sdk", () => ({
AnthropicVertex: vitest.fn().mockImplementation(function () {
return {
Expand Down Expand Up @@ -56,6 +63,11 @@ describe("VertexHandler", () => {
let handler: AnthropicVertexHandler

describe("constructor", () => {
beforeEach(() => {
;(AnthropicVertex as any).mockClear()
;(GoogleAuth as any).mockClear()
})

it("should initialize with provided config for Claude", () => {
handler = new AnthropicVertexHandler({
apiModelId: "claude-3-5-sonnet-v2@20241022",
Expand All @@ -66,7 +78,58 @@ describe("VertexHandler", () => {
expect(AnthropicVertex).toHaveBeenCalledWith({
projectId: "test-project",
region: "us-central1",
timeout: expect.any(Number),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only tests the bare construction path ({ projectId, region, timeout }). anthropic-vertex.ts has two other branches — parsedVertexCredentials and vertexKeyFile — that also pass timeout. Are those covered somewhere, or worth adding here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added explicit coverage for both additional Anthropic Vertex constructor branches: vertexJsonCredentials and vertexKeyFile.

The tests now assert that GoogleAuth receives the expected credentials/keyFile options and that the resulting AnthropicVertex constructor config includes timeout. I also adjusted the GoogleAuth mock to use a function expression so it can be invoked with new, matching the handler path.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same expect.any(Number) concern here (and lines 108 and 130). If timeout: getApiRequestTimeout() were replaced with timeout: 0, all three assertions would still pass.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the Anthropic Vertex tests to use the same concrete timeout sentinel across the bare, JSON credentials, and key file credentials paths.

})
})

it("should pass timeout when initializing with vertexJsonCredentials", () => {
const credentials = {
type: "service_account",
client_email: "test@test-project.iam.gserviceaccount.com",
private_key: "-----BEGIN PRIVATE KEY-----\nfake\n-----END PRIVATE KEY-----\n",
}

handler = new AnthropicVertexHandler({
apiModelId: "claude-3-5-sonnet-v2@20241022",
vertexProjectId: "test-project",
vertexRegion: "us-central1",
vertexJsonCredentials: JSON.stringify(credentials),
})

expect(GoogleAuth).toHaveBeenCalledWith({
scopes: ["https://www.googleapis.com/auth/cloud-platform"],
credentials,
})
expect(AnthropicVertex).toHaveBeenCalledWith(
expect.objectContaining({
projectId: "test-project",
region: "us-central1",
googleAuth: expect.any(Object),
timeout: expect.any(Number),
}),
)
})

it("should pass timeout when initializing with vertexKeyFile", () => {
handler = new AnthropicVertexHandler({
apiModelId: "claude-3-5-sonnet-v2@20241022",
vertexProjectId: "test-project",
vertexRegion: "us-central1",
vertexKeyFile: "/tmp/sa-key.json",
})

expect(GoogleAuth).toHaveBeenCalledWith({
scopes: ["https://www.googleapis.com/auth/cloud-platform"],
keyFile: "/tmp/sa-key.json",
})
expect(AnthropicVertex).toHaveBeenCalledWith(
expect.objectContaining({
projectId: "test-project",
region: "us-central1",
googleAuth: expect.any(Object),
timeout: expect.any(Number),
}),
)
})
})

Expand Down
8 changes: 7 additions & 1 deletion src/api/providers/__tests__/lite-llm.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ import { ApiHandlerOptions } from "../../../shared/api"
import { litellmDefaultModelId, litellmDefaultModelInfo } from "@roo-code/types"

// Mock vscode first to avoid import errors
vi.mock("vscode", () => ({}))
vi.mock("vscode", () => ({
workspace: {
getConfiguration: () => ({
get: (_key: string, defaultValue?: unknown) => defaultValue,
}),
},
}))

// Mock OpenAI
const mockCreate = vi.fn()
Expand Down
8 changes: 7 additions & 1 deletion src/api/providers/__tests__/opencode-go.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
// npx vitest run src/api/providers/__tests__/opencode-go.spec.ts

// Mock vscode first to avoid import errors
vitest.mock("vscode", () => ({}))
vitest.mock("vscode", () => ({
workspace: {
getConfiguration: () => ({
get: (_key: string, defaultValue?: unknown) => defaultValue,
}),
},
}))

import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI from "openai"
Expand Down
9 changes: 8 additions & 1 deletion src/api/providers/__tests__/openrouter.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
// pnpm --filter roo-cline test api/providers/__tests__/openrouter.spec.ts

vitest.mock("vscode", () => ({}))
vitest.mock("vscode", () => ({
workspace: {
getConfiguration: () => ({
get: (_key: string, defaultValue?: unknown) => defaultValue,
}),
},
}))

import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI from "openai"
Expand Down Expand Up @@ -108,6 +114,7 @@ describe("OpenRouterHandler", () => {
"X-Title": "Zoo Code",
"User-Agent": `ZooCode/${Package.version}`,
},
timeout: expect.any(Number),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this use a concrete value instead of expect.any(Number)? As written, a mutation replacing getApiRequestTimeout() with 0 (which the OpenAI SDK treats as "abort immediately") would still pass. The pattern in openai-timeout.spec.ts — mocking getApiRequestTimeout to return a sentinel and asserting the exact value — catches that class of bug.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. I mocked getApiRequestTimeout() with a concrete sentinel value and now assert that OpenRouter passes that exact value to the OpenAI client instead of only checking expect.any(Number).

})
})

Expand Down
2 changes: 2 additions & 0 deletions src/api/providers/__tests__/requesty.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ describe("RequestyHandler", () => {
"X-Title": "Zoo Code",
"User-Agent": `ZooCode/${Package.version}`,
},
timeout: expect.any(Number),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question as the openrouter test — expect.any(Number) accepts 0 or any hardcoded literal. Worth pinning to a specific sentinel so mutations are detectable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated Requesty as well. Both client-construction assertions now verify the exact mocked timeout value rather than accepting any number.

})
})

Expand All @@ -97,6 +98,7 @@ describe("RequestyHandler", () => {
"X-Title": "Zoo Code",
"User-Agent": `ZooCode/${Package.version}`,
},
timeout: expect.any(Number),
})
})

Expand Down
9 changes: 8 additions & 1 deletion src/api/providers/__tests__/vercel-ai-gateway.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
// npx vitest run src/api/providers/__tests__/vercel-ai-gateway.spec.ts

// Mock vscode first to avoid import errors
vitest.mock("vscode", () => ({}))
vitest.mock("vscode", () => ({
workspace: {
getConfiguration: () => ({
get: (_key: string, defaultValue?: unknown) => defaultValue,
}),
},
}))

import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI from "openai"
Expand Down Expand Up @@ -118,6 +124,7 @@ describe("VercelAiGatewayHandler", () => {
"X-Title": "Zoo Code",
"User-Agent": expect.stringContaining("ZooCode/"),
}),
timeout: expect.any(Number),
})
})

Expand Down
8 changes: 7 additions & 1 deletion src/api/providers/__tests__/vertex-credentials.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@

// Mock vscode first to avoid import errors when the provider stack pulls
// transitive vscode-dependent modules during construction.
vitest.mock("vscode", () => ({}))
vitest.mock("vscode", () => ({
workspace: {
getConfiguration: () => ({
get: (_key: string, defaultValue?: unknown) => defaultValue,
}),
},
}))

vitest.mock("@roo-code/telemetry", () => ({
TelemetryService: {
Expand Down
6 changes: 6 additions & 0 deletions src/api/providers/__tests__/zoo-gateway.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ vitest.mock("vscode", () => ({
window: { showErrorMessage },
env: { openExternal, uriScheme: "vscode", appName: "VS Code" },
Uri: { parse: (value: string) => ({ toString: () => value }) },
workspace: {
getConfiguration: () => ({
get: (_key: string, defaultValue?: unknown) => defaultValue,
}),
},
}))

vitest.mock("../../../i18n", () => ({
Expand Down Expand Up @@ -178,6 +183,7 @@ describe("ZooGatewayHandler", () => {
"X-Zoo-Editor": "vscode",
"X-Zoo-Extension-Version": Package.version,
}),
timeout: expect.any(Number),
})
})

Expand Down
7 changes: 6 additions & 1 deletion src/api/providers/anthropic-vertex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {

import { BaseProvider } from "./base-provider"
import { parseVertexJsonCredentials } from "./utils/vertex-credentials"
import { getApiRequestTimeout } from "./utils/timeout-config"
import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index"

// https://docs.anthropic.com/en/api/claude-on-vertex-ai
Expand All @@ -43,6 +44,8 @@ export class AnthropicVertexHandler extends BaseProvider implements SingleComple

const parsedVertexCredentials = parseVertexJsonCredentials(this.options.vertexJsonCredentials)

const timeout = getApiRequestTimeout()

if (parsedVertexCredentials) {
this.client = new AnthropicVertex({
projectId,
Expand All @@ -51,6 +54,7 @@ export class AnthropicVertexHandler extends BaseProvider implements SingleComple
scopes: ["https://www.googleapis.com/auth/cloud-platform"],
credentials: parsedVertexCredentials,
}),
timeout,
})
} else if (this.options.vertexKeyFile) {
this.client = new AnthropicVertex({
Expand All @@ -60,9 +64,10 @@ export class AnthropicVertexHandler extends BaseProvider implements SingleComple
scopes: ["https://www.googleapis.com/auth/cloud-platform"],
keyFile: this.options.vertexKeyFile,
}),
timeout,
})
} else {
this.client = new AnthropicVertex({ projectId, region })
this.client = new AnthropicVertex({ projectId, region, timeout })
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/api/providers/anthropic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { getModelParams } from "../transform/model-params"
import { filterNonAnthropicBlocks } from "../transform/anthropic-filter"
import { getAnthropicProviderReasoning } from "../transform/reasoning"
import { handleProviderError } from "./utils/error-handler"
import { getApiRequestTimeout } from "./utils/timeout-config"

import { BaseProvider } from "./base-provider"
import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index"
Expand All @@ -44,6 +45,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
this.client = new Anthropic({
baseURL: this.options.anthropicBaseUrl || undefined,
[apiKeyFieldName]: this.options.apiKey,
timeout: getApiRequestTimeout(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The apiRequestTimeout setting has "type": "number" in package.json, so users can enter decimal values like 16.1. 16.1 * 1000 produces a non-integer float, and the Anthropic SDK calls validatePositiveInteger("timeout", ...) in its constructor — which would throw for any decimal input. Would Math.round in getApiRequestTimeout() (or tightening the schema to "type": "integer") be the right fix?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed this in two places: the configuration schema now uses type: "integer", and getApiRequestTimeout() rounds the computed milliseconds before passing the value to SDK clients. I also centralized the timeout bounds/default handling around the documented 1–3600s range, so invalid or out-of-range values fall back to the default instead of propagating an SDK-invalid timeout value.

})
}

Expand Down
2 changes: 2 additions & 0 deletions src/api/providers/minimax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { getModelParams } from "../transform/model-params"
import { mergeEnvironmentDetailsForMiniMax } from "../transform/minimax-format"

import { BaseProvider } from "./base-provider"
import { getApiRequestTimeout } from "./utils/timeout-config"
import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index"
import { calculateApiCostAnthropic } from "../../shared/cost"
import { convertOpenAIToolsToAnthropic } from "../../core/prompts/tools/native-tools/converters"
Expand Down Expand Up @@ -73,6 +74,7 @@ export class MiniMaxHandler extends BaseProvider implements SingleCompletionHand
this.client = new Anthropic({
baseURL,
apiKey: options.minimaxApiKey,
timeout: getApiRequestTimeout(),
})
}

Expand Down
2 changes: 2 additions & 0 deletions src/api/providers/openai-codex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { isMcpTool } from "../../utils/mcp-name"
import { sanitizeOpenAiCallId } from "../../utils/tool-id"
import { openAiCodexOAuthManager } from "../../integrations/openai-codex/oauth"
import { t } from "../../i18n"
import { getApiRequestTimeout } from "./utils/timeout-config"

export type OpenAiCodexModel = ReturnType<OpenAiCodexHandler["getModel"]>

Expand Down Expand Up @@ -371,6 +372,7 @@ export class OpenAiCodexHandler extends BaseProvider implements SingleCompletion
apiKey: accessToken,
baseURL: CODEX_API_BASE_URL,
defaultHeaders: codexHeaders,
timeout: getApiRequestTimeout(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every other provider calls getApiRequestTimeout() once in the constructor and stores it in the client. Here it's re-read from VS Code config on every request (because the client is created lazily). Is the per-request re-evaluation intentional? If not, a private readonly timeoutMs = getApiRequestTimeout() field set at construction would align this with the other providers.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not intentional. I updated OpenAI Codex to cache the timeout at handler construction time and pass that cached value when the lazy client is created, so it now matches the behavior of the other providers.

})

const stream = (await (client as any).responses.create(requestBody, {
Expand Down
2 changes: 2 additions & 0 deletions src/api/providers/openai-native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { BaseProvider } from "./base-provider"
import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index"
import { isMcpTool } from "../../utils/mcp-name"
import { sanitizeOpenAiCallId } from "../../utils/tool-id"
import { getApiRequestTimeout } from "./utils/timeout-config"

export type OpenAiNativeModel = ReturnType<OpenAiNativeHandler["getModel"]>

Expand Down Expand Up @@ -104,6 +105,7 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio
session_id: this.sessionId,
"User-Agent": userAgent,
},
timeout: getApiRequestTimeout(),
})
}

Expand Down
3 changes: 2 additions & 1 deletion src/api/providers/openrouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { getModelEndpoints } from "./fetchers/modelEndpointCache"

import { DEFAULT_HEADERS } from "./constants"
import { BaseProvider } from "./base-provider"
import { getApiRequestTimeout } from "./utils/timeout-config"
import type { ApiHandlerCreateMessageMetadata, SingleCompletionHandler } from "../index"
import { handleOpenAIError } from "./utils/openai-error-handler"
import { generateImageWithProvider, ImageGenerationResult } from "./utils/image-generation"
Expand Down Expand Up @@ -153,7 +154,7 @@ 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) => {
Expand Down
2 changes: 2 additions & 0 deletions src/api/providers/qwen-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { ApiStream } from "../transform/stream"
import { BaseProvider } from "./base-provider"
import { extractReasoningFromDelta } from "./utils/extract-reasoning"
import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index"
import { getApiRequestTimeout } from "./utils/timeout-config"

const QWEN_OAUTH_BASE_URL = "https://chat.qwen.ai"
const QWEN_OAUTH_TOKEN_ENDPOINT = `${QWEN_OAUTH_BASE_URL}/api/v1/oauth2/token`
Expand Down Expand Up @@ -76,6 +77,7 @@ export class QwenCodeHandler extends BaseProvider implements SingleCompletionHan
"X-DashScope-UserAgent": `QwenCode/1.0.0 (${os.platform()}; ${os.arch()})`,
"X-DashScope-AuthType": "qwen-oauth",
},
timeout: getApiRequestTimeout(),
})
}
return this.client
Expand Down
2 changes: 2 additions & 0 deletions src/api/providers/requesty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { BaseProvider } from "./base-provider"
import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index"
import { toRequestyServiceUrl } from "../../shared/utils/requesty"
import { handleOpenAIError } from "./utils/openai-error-handler"
import { getApiRequestTimeout } from "./utils/timeout-config"
import { applyRouterToolPreferences } from "./utils/router-tool-preferences"
import { extractReasoningFromDelta } from "./utils/extract-reasoning"

Expand Down Expand Up @@ -69,6 +70,7 @@ export class RequestyHandler extends BaseProvider implements SingleCompletionHan
baseURL: this.baseURL,
apiKey: apiKey,
defaultHeaders: DEFAULT_HEADERS,
timeout: getApiRequestTimeout(),
})
}

Expand Down
2 changes: 2 additions & 0 deletions src/api/providers/router-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { BaseProvider } from "./base-provider"
import { getModels, getModelsFromCache } from "./fetchers/modelCache"

import { DEFAULT_HEADERS } from "./constants"
import { getApiRequestTimeout } from "./utils/timeout-config"

type RouterProviderOptions = {
name: RouterName
Expand Down Expand Up @@ -52,6 +53,7 @@ export abstract class RouterProvider extends BaseProvider {
...DEFAULT_HEADERS,
...(options.openAiHeaders || {}),
},
timeout: getApiRequestTimeout(),
})
}

Expand Down
2 changes: 2 additions & 0 deletions src/api/providers/unbound.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { getModels } from "./fetchers/modelCache"
import { BaseProvider } from "./base-provider"
import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index"
import { handleOpenAIError } from "./utils/openai-error-handler"
import { getApiRequestTimeout } from "./utils/timeout-config"
import { applyRouterToolPreferences } from "./utils/router-tool-preferences"
import { extractReasoningFromDelta } from "./utils/extract-reasoning"

Expand Down Expand Up @@ -63,6 +64,7 @@ export class UnboundHandler extends BaseProvider implements SingleCompletionHand
...DEFAULT_HEADERS,
"X-Unbound-Metadata": JSON.stringify({ labels: [{ key: "app", value: "zoo-code" }] }),
},
timeout: getApiRequestTimeout(),
})
}

Expand Down
Loading
Loading