From e4bfc8066ecf90699ec4b8bc1b15c6be40c1965d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 25 Apr 2026 23:34:18 +0100 Subject: [PATCH] fix(openai): extend Azure image timeout Closes #71705 --- CHANGELOG.md | 3 ++ docs/providers/openai.md | 2 ++ .../openai/image-generation-provider.test.ts | 29 +++++++++++++++++++ .../openai/image-generation-provider.ts | 13 +++++++-- 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43095970d12..3354d012954 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,9 @@ Docs: https://docs.openclaw.ai internal calls no longer hit `scope-upgrade` pairing prompts while remote, browser, node, device-token, and explicit-device paths still require normal pairing approval. Fixes #63548. +- Providers/Azure OpenAI: give deployment-scoped image generation requests a + longer 600s default timeout so slow `gpt-image-2` generations can complete + without a per-call `timeoutMs`. Fixes #71705. Thanks @voytas75. - CLI/gateway: keep diagnostic probes from creating first-time read-only device pairings, while still reusing cached device tokens for detailed read probes. Fixes #71766. Thanks @SunboZ. diff --git a/docs/providers/openai.md b/docs/providers/openai.md index b6a0bc283a0..84aab90120a 100644 --- a/docs/providers/openai.md +++ b/docs/providers/openai.md @@ -543,6 +543,8 @@ For image-generation requests on a recognized Azure host, OpenClaw: - Sends the `api-key` header instead of `Authorization: Bearer` - Uses deployment-scoped paths (`/openai/deployments/{deployment}/...`) - Appends `?api-version=...` to each request +- Uses a 600s default request timeout for Azure image-generation calls. + Per-call `timeoutMs` values still override this default. Other base URLs (public OpenAI, OpenAI-compatible proxies) keep the standard OpenAI image request shape. diff --git a/extensions/openai/image-generation-provider.test.ts b/extensions/openai/image-generation-provider.test.ts index 8ae786df990..aa137b99589 100644 --- a/extensions/openai/image-generation-provider.test.ts +++ b/extensions/openai/image-generation-provider.test.ts @@ -1316,6 +1316,35 @@ describe("openai image generation provider", () => { n: 1, size: "1024x1024", }, + timeoutMs: 600_000, + }), + ); + }); + + it("lets explicit timeoutMs override the Azure image default", async () => { + mockGeneratedPngResponse(); + + const provider = buildOpenAIImageGenerationProvider(); + await provider.generateImage({ + provider: "openai", + model: "gpt-image-2-1", + prompt: "Azure cat", + cfg: { + models: { + providers: { + openai: { + baseUrl: "https://myresource.openai.azure.com/openai/v1", + models: [], + }, + }, + }, + }, + timeoutMs: 123_456, + }); + + expect(postJsonRequestMock).toHaveBeenCalledWith( + expect.objectContaining({ + timeoutMs: 123_456, }), ); }); diff --git a/extensions/openai/image-generation-provider.ts b/extensions/openai/image-generation-provider.ts index 86bcbddac6d..eb22549bdc7 100644 --- a/extensions/openai/image-generation-provider.ts +++ b/extensions/openai/image-generation-provider.ts @@ -32,6 +32,7 @@ const DEFAULT_OPENAI_CODEX_IMAGE_RESPONSES_MODEL = "gpt-5.5"; const OPENAI_CODEX_IMAGE_INSTRUCTIONS = "You are an image generation assistant."; const OPENAI_TRANSPARENT_BACKGROUND_IMAGE_MODEL = "gpt-image-1.5"; const DEFAULT_OPENAI_IMAGE_TIMEOUT_MS = 180_000; +const DEFAULT_AZURE_OPENAI_IMAGE_TIMEOUT_MS = 600_000; const DEFAULT_OUTPUT_MIME = "image/png"; const DEFAULT_OUTPUT_EXTENSION = "png"; const DEFAULT_SIZE = "1024x1024"; @@ -91,8 +92,14 @@ function sanitizeLogValue(value: unknown): string { : cleaned; } -function resolveOpenAIImageTimeoutMs(timeoutMs: number | undefined): number { - return timeoutMs ?? DEFAULT_OPENAI_IMAGE_TIMEOUT_MS; +function resolveOpenAIImageTimeoutMs( + timeoutMs: number | undefined, + options?: { isAzure?: boolean }, +): number { + return ( + timeoutMs ?? + (options?.isAzure ? DEFAULT_AZURE_OPENAI_IMAGE_TIMEOUT_MS : DEFAULT_OPENAI_IMAGE_TIMEOUT_MS) + ); } function resolveOpenAIImageCount(count: number | undefined): number { @@ -746,7 +753,7 @@ export function buildOpenAIImageGenerationProvider(): ImageGenerationProvider { }); const count = resolveOpenAIImageCount(req.count); const size = req.size ?? DEFAULT_SIZE; - const timeoutMs = resolveOpenAIImageTimeoutMs(req.timeoutMs); + const timeoutMs = resolveOpenAIImageTimeoutMs(req.timeoutMs, { isAzure }); const url = isAzure ? buildAzureImageUrl(rawBaseUrl, model, isEdit ? "edits" : "generations") : `${baseUrl}/images/${isEdit ? "edits" : "generations"}`;