openclaw - ✅(Solved) Fix OpenAI image generation provider doesn't support Azure OpenAI endpoints [3 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#70487Fetched 2026-04-24 05:57:26
View on GitHub
Comments
0
Participants
1
Timeline
8
Reactions
0
Author
Participants
Timeline (top)
referenced ×4cross-referenced ×3closed ×1

OpenClaw recently upgraded the default OpenAI image model to gpt-image-2, which is available both on api.openai.com and on Azure OpenAI deployments (GA'd 2026‑04‑21, doc: https://learn.microsoft.com/azure/ai-foundry/openai/how-to/dall-e ). A lot of enterprise users route OpenAI traffic through an Azure OpenAI endpoint by overriding models.providers.openai.baseUrl to https://<resource>.openai.azure.com.

For chat/Responses traffic, OpenClaw already handles this correctly — src/agents/openai-transport-stream.ts detects Azure hostnames, switches to AzureOpenAI, and injects api-version as a query string (see resolveAzureOpenAIApiVersion, isAzureOpenAICompatibleHost, buildOpenAICompletionsClientConfig).

The image generation provider in extensions/openai/image-generation-provider.ts was never taught any of this, so pointing openai.baseUrl at an Azure OpenAI host breaks image generation entirely.

Root Cause

  1. Onboard the openai provider with a custom base URL pointing at an Azure deployment:
    {
      "models": {
        "providers": {
          "openai": {
            "baseUrl": "https://<resource>.openai.azure.com",
            "apiKey": "<azure-key>"
          }
        }
      }
    }
  2. Run any image-generation flow that resolves to the openai provider (default gpt-image-2).
  3. Observed: 401/404 from Azure — depending on the api-version default, either the Authorization: Bearer header is rejected or the /images/generations path 404s because Azure expects /openai/deployments/<name>/images/generations.

Fix Action

Fixed

PR fix notes

PR #70488: fix(openai): route image generation through Azure OpenAI when base URL targets Azure

Description (problem / solution / changelog)

Closes #70487.

Summary

OpenAI image generation (default model: gpt-image-2) was hardcoded for public api.openai.com and silently broken whenever models.providers.openai.baseUrl pointed at an Azure OpenAI endpoint (https://<resource>.openai.azure.com).

Three concrete gaps, all of which are already solved for chat / Responses in src/agents/openai-transport-stream.ts but were never replicated in the image provider:

ConcernPublic OpenAIAzure OpenAI
Auth headerAuthorization: Bearer <key>api-key: <key>
URL{base}/images/{generations,edits}{base}/openai/deployments/{deployment}/images/{generations,edits}
api-versionnot neededrequired query string

This PR plugs that hole without changing public-OpenAI behavior.

Changes

  • extensions/openai/azure.ts (new) — small helper with

    • isAzureOpenAIBaseUrl() covering .openai.azure.com, .services.ai.azure.com, .cognitiveservices.azure.com (same suffix set the chat path's isAzureOpenAICompatibleHost uses)
    • resolveAzureOpenAIApiVersion() reading AZURE_OPENAI_API_VERSION with the same default as src/agents/openai-transport-stream.ts (2024-12-01-preview)
    • buildAzureOpenAIImageRoute() assembling the deployment-scoped URL + api-key header override

    The helper is duplicated from src/ because extensions can't import from src/ under lint:extensions:no-src-outside-plugin-sdk; both constants are tiny and documented as "keep in sync with the canonical chat-path version."

  • extensions/openai/image-generation-provider.ts — detects Azure at request time, switches auth header + URL accordingly, and (incidentally) flattens the duplicated IIFE for edit vs generate into a single postJsonRequest call since both branches now differ only in the body shape.

  • extensions/openai/image-generation-provider.test.ts — 5 new Azure routing tests plus 1 explicit "public OpenAI path stays on Bearer" regression guard:

    • Azure base URL → deployment-scoped URL + api-key header (no Authorization)
    • AZURE_OPENAI_API_VERSION env var honored
    • All three Azure host suffixes (.openai.azure.com, .services.ai.azure.com, .cognitiveservices.azure.com) detected
    • Azure edit path routes to /images/edits with same header swap
    • Non-Azure openai path keeps Authorization: Bearer + api.openai.com/v1/...

Out of scope (mentioned in #70487 for a follow-up)

gpt-image-2 also supports quality, output_format, background, moderation that the current ImageGenerationRequest type doesn't expose. Plumbing those cleanly means extending the type in src/image-generation/types.ts, which I kept out of this PR to stay focused on the actual breakage — happy to send a follow-up.

Test plan

  • pnpm test:extension openai — 146 passing (image-generation-provider.test.ts: 6 → 11 tests). The one failing suite (openai-codex-cli-bridge.test.ts) is a pre-existing typebox module-resolution error that is red on main today and unrelated to this change — per CONTRIBUTING.md I am not touching it.
  • npx oxlint + npx oxfmt --check on the three changed files — clean.
  • Plugin-SDK boundary lints: lint:extensions:no-src-outside-plugin-sdk, lint:extensions:no-plugin-sdk-internal, lint:extensions:no-relative-outside-package — all pass.
  • Live Azure verification: ran the patched URL + header pattern against a real Azure gpt-image-2 deployment (api-version=2024-10-21, quality=low, n=1, size=1024x1024) — HTTP 200 with a valid b64_json image. End-to-end path is real, not just mocked.

AI-assisted

Written with Claude Opus 4.6. Fully tested (unit tests + manual Azure live probe). Reviewed and understood the diff before submitting.

Changed files

  • extensions/openai/azure.test.ts (added, +148/-0)
  • extensions/openai/azure.ts (added, +97/-0)
  • extensions/openai/image-generation-provider.test.ts (modified, +172/-0)
  • extensions/openai/image-generation-provider.ts (modified, +55/-47)

PR #70503: feat(openai): expose quality / output_format / background / moderation for gpt-image-2

Description (problem / solution / changelog)

Summary

Follow-up to #70488. Extends ImageGenerationRequest with four optional provider-hint fields so callers can reach the knobs that make gpt-image-2 actually usable in production (chiefly quality: "low" for cheap / fast preview renders), without widening the blast radius on other providers.

#70488 intentionally scoped this piece out:

Out of scope ... extending ImageGenerationRequest in src/image-generation/types.ts ... has wider blast radius because every image-generation provider consumes that shape. Happy to send a follow-up that threads those through.

This is that follow-up. Partially addresses #70487 item 4 ("Azure-flavored features not considered").

What

FieldType (string literal union)Effect when suppliedDefault when omitted
quality"low" | "medium" | "high" | "auto"Emitted as quality in the OpenAI / Azure OpenAI body. low renders dramatically faster/cheaper.Field absent from the request body; server-side default applies.
outputFormat"png" | "jpeg" | "webp"Emitted as output_format. Azure currently accepts only png / jpeg; the SDK layer does not gatekeep.Field absent; server returns PNG.
background"opaque" | "auto" | "transparent"Emitted as background. Some gpt-image variants reject transparent at request time; again not gatekept here.Field absent; server-side default applies.
moderation"auto" | "low"Emitted as moderation.Field absent; server-side default applies.

All four are optional provider hints: when the caller does not supply them, the request body is byte-identical to what shipped in #70488. Other providers (Google, Anthropic, xAI, fal, comfy, minimax, vydra) receive the extended ImageGenerationRequest type but do not read the new keys, so their runtime behaviour is unchanged.

Changed files

  • src/image-generation/types.ts — four exported literal unions plus optional fields on ImageGenerationRequest, with JSDoc that makes the "unrecognised values are ignored" contract explicit.
  • src/image-generation/runtime-types.ts — matching optional fields on GenerateImageParams.
  • src/image-generation/runtime.ts — forwards the four fields verbatim into each provider's generateImage call.
  • extensions/openai/image-generation-provider.ts — conditionally attaches quality, output_format, background, moderation to the request body, same logic for images/generations and images/edits, same logic for public OpenAI and the Azure deployment-scoped route added in #70488.
  • extensions/openai/image-generation-provider.test.ts — extends the existing suite (no new describe block) with:
    • generate / edit + Azure / public OpenAI coverage for each hint.
    • explicit guard that omitting the hints keeps the body clean (expect(call.body).not.toHaveProperty(...)).

Diff vs main (only new code):

extensions/openai/image-generation-provider.test.ts | +137
extensions/openai/image-generation-provider.ts      |  +18 −0
src/image-generation/runtime-types.ts               |  +12 −0
src/image-generation/runtime.ts                     |   +4 −0
src/image-generation/types.ts                       |  +48 −0

Stacked on top of #70488; once that lands the diff collapses to just these five files.

Testing

  • pnpm test:extension openai21 files / 153 tests passing (was 149; +4 from this change).
  • pnpm run-vitest ... src/image-generation/10/10 passing.
  • npx oxlint extensions/openai/ src/image-generation/ — 0 warnings, 0 errors.
  • npx oxfmt --check extensions/openai/ src/image-generation/ — clean.
  • npx tsc -p tsconfig.core.json --noEmit — clean.
  • npx tsc -p tsconfig.extensions.json --noEmit — clean.
  • Real Azure gpt-image-2 smoke via the deployment-scoped route:
    • quality=low → HTTP 200 in ~61 s, b64 payload 41,184 chars (~30 kB PNG).
    • default quality (omitted) hit the same Azure backend flake observed in #70488 (HTTP 500 after ~93 s); retries of quality=low continued to succeed during the same window, which was the whole reason we want this knob exposed.

Backward compatibility

  • ImageGenerationRequest and GenerateImageParams only gain optional fields; every existing caller compiles unchanged.
  • Non-OpenAI providers accept the wider type but do not destructure the new keys, so their HTTP traffic is byte-identical.
  • When the new fields are omitted, the OpenAI / Azure request body is byte-identical to what #70488 produces (covered by the explicit "no hints" test).

AI-assisted disclosure

This PR was drafted with AI assistance. The endpoint plumbing, tests, and Azure live check were authored and reviewed by a human before submission.

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • docs/.generated/plugin-sdk-api-baseline.sha256 (modified, +2/-2)
  • docs/plugins/sdk-subpaths.md (modified, +1/-1)
  • docs/tools/image-generation.md (modified, +36/-4)
  • extensions/openai/image-generation-provider.test.ts (modified, +100/-2)
  • extensions/openai/image-generation-provider.ts (modified, +79/-13)
  • scripts/check-no-raw-channel-fetch.mjs (modified, +3/-3)
  • src/agents/tools/image-generate-tool.test.ts (modified, +56/-0)
  • src/agents/tools/image-generate-tool.ts (modified, +126/-0)
  • src/image-generation/normalization.ts (modified, +22/-0)
  • src/image-generation/runtime-types.ts (modified, +6/-0)
  • src/image-generation/runtime.test.ts (modified, +122/-0)
  • src/image-generation/runtime.ts (modified, +5/-0)
  • src/image-generation/types.ts (modified, +34/-1)
  • src/infra/exec-safe-bin-policy.test.ts (modified, +3/-2)
  • src/plugin-sdk/image-generation.ts (modified, +6/-0)

PR #70570: fix #70487: OpenAI image generation provider does not support Azure OpenAI endpoints

Description (problem / solution / changelog)

Summary

Fixes #70487

Issue

OpenAI image generation provider does not support Azure OpenAI endpoints — when using an Azure OpenAI base URL, the provider incorrectly used standard OpenAI auth headers and URL paths.

Solution

  • Added Azure endpoint detection via hostname suffixes (.openai.azure.com, .cognitiveservices.azure.com, .services.ai.azure.com)
  • For Azure endpoints: use api-key header instead of Bearer auth
  • For Azure endpoints: build deployment-scoped URLs (/openai/deployments/{model}/images/generations?api-version=...)
  • Support AZURE_OPENAI_API_VERSION environment variable override
  • Strip trailing /v1 from Azure base URLs
  • Added 7 comprehensive test cases covering all Azure scenarios

Testing

  • Unit tests added for all Azure hostname patterns
  • Test for AZURE_OPENAI_API_VERSION env override
  • Test for Azure edit URL generation
  • Test for trailing /v1 stripping
  • Test confirming standard OpenAI auth is unchanged

This PR was automatically generated by a daily automation task.

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • extensions/openai/image-generation-provider.test.ts (modified, +237/-0)
  • extensions/openai/image-generation-provider.ts (modified, +46/-6)

Code Example

defaultHeaders: {
     Authorization: `Bearer ${auth.apiKey}`,
   },

---

url: `${baseUrl}/images/generations`
   url: `${baseUrl}/images/edits`

---

{
     "models": {
       "providers": {
         "openai": {
           "baseUrl": "https://<resource>.openai.azure.com",
           "apiKey": "<azure-key>"
         }
       }
     }
   }
RAW_BUFFERClick to expand / collapse

Context

OpenClaw recently upgraded the default OpenAI image model to gpt-image-2, which is available both on api.openai.com and on Azure OpenAI deployments (GA'd 2026‑04‑21, doc: https://learn.microsoft.com/azure/ai-foundry/openai/how-to/dall-e ). A lot of enterprise users route OpenAI traffic through an Azure OpenAI endpoint by overriding models.providers.openai.baseUrl to https://<resource>.openai.azure.com.

For chat/Responses traffic, OpenClaw already handles this correctly — src/agents/openai-transport-stream.ts detects Azure hostnames, switches to AzureOpenAI, and injects api-version as a query string (see resolveAzureOpenAIApiVersion, isAzureOpenAICompatibleHost, buildOpenAICompletionsClientConfig).

The image generation provider in extensions/openai/image-generation-provider.ts was never taught any of this, so pointing openai.baseUrl at an Azure OpenAI host breaks image generation entirely.

Problem

Today's image path (extensions/openai/image-generation-provider.ts):

  1. Auth header hard-coded for public OpenAI

    defaultHeaders: {
      Authorization: `Bearer ${auth.apiKey}`,
    },

    Azure OpenAI rejects this; it requires the api-key: <key> header (or a bearer AAD token, which is a different story).

  2. URL shape hard-coded for public OpenAI

    url: `${baseUrl}/images/generations`
    url: `${baseUrl}/images/edits`

    Azure's image routes are {endpoint}/openai/deployments/{deployment}/images/generations and .../images/edits.

  3. Missing api-version query parameter — required on every Azure OpenAI request (e.g. ?api-version=2024-10-21).

  4. Azure-flavored features not consideredgpt-image-2 supports useful cost/behavior knobs (quality, output_format, background, moderation) that the current request body never carries. Users stuck on "default" quality pay the high-quality price even when quality="low" would be fine. (This is secondary — raising it here for visibility; the first three are the actual breakage.)

  5. Existing Azure plumbing not reusedextensions/openai/transport-policy.ts already has a private isAzureOpenAIBaseUrl helper, and src/agents/openai-transport-stream.ts already resolves api-version from env. The image provider simply never learned about any of it.

Repro

  1. Onboard the openai provider with a custom base URL pointing at an Azure deployment:
    {
      "models": {
        "providers": {
          "openai": {
            "baseUrl": "https://<resource>.openai.azure.com",
            "apiKey": "<azure-key>"
          }
        }
      }
    }
  2. Run any image-generation flow that resolves to the openai provider (default gpt-image-2).
  3. Observed: 401/404 from Azure — depending on the api-version default, either the Authorization: Bearer header is rejected or the /images/generations path 404s because Azure expects /openai/deployments/<name>/images/generations.

Expected: the provider should detect the Azure OpenAI host and route the request the same way openai-transport-stream.ts does for chat completions — i.e. api-key header, deployment-scoped URL, and api-version query parameter.

Proposed fix

In buildOpenAIImageGenerationProvider():

  • Detect Azure OpenAI hostnames (.openai.azure.com / .services.ai.azure.com / .cognitiveservices.azure.com) the same way buildOpenAICompletionsClientConfig does.
  • On Azure: set api-key instead of Authorization: Bearer, build the URL as {base}/openai/deployments/{model}/images/{generations|edits}?api-version={ver}, and resolve api-version from AZURE_OPENAI_API_VERSION with the same default the rest of the provider uses (2024-12-01-preview, with 2024-10-21 also valid for images).
  • Deployment name comes from req.model (same convention as resolveAzureDeploymentName); optional AZURE_OPENAI_DEPLOYMENT_NAME_MAP support can be added later if someone needs it here.
  • Keep the current public-OpenAI path exactly as-is.

I'd like to open a PR scoped narrowly to these three fixes (auth header / URL / api-version). The param-passthrough item (#4) is worth a separate, smaller follow-up because it requires extending ImageGenerationRequest in src/image-generation/types.ts, which has wider blast radius.

Reference

  • Azure image docs: https://learn.microsoft.com/azure/ai-foundry/openai/how-to/dall-e
  • Existing Azure detection in extensions/openai/transport-policy.ts (private isAzureOpenAIBaseUrl)
  • Existing Azure api-version / url rewrite in src/agents/openai-transport-stream.ts (resolveAzureOpenAIApiVersion, buildOpenAICompletionsClientConfig)

Happy to submit the PR if this triage direction looks right.

extent analysis

TL;DR

Update the image generation provider to detect Azure OpenAI hosts and adjust the auth header, URL, and API version accordingly.

Guidance

  • Detect Azure OpenAI hostnames in buildOpenAIImageGenerationProvider() using the same logic as buildOpenAICompletionsClientConfig.
  • For Azure hosts, set the api-key header instead of Authorization: Bearer and construct the URL with the deployment scope and API version.
  • Resolve the api-version from the AZURE_OPENAI_API_VERSION environment variable with a default value of 2024-12-01-preview.
  • Keep the current public OpenAI path unchanged for non-Azure hosts.

Example

const isAzureOpenAIHost = (baseUrl: string) => {
  // implement Azure OpenAI hostname detection logic
};

const buildOpenAIImageGenerationProvider = (baseUrl: string, apiKey: string) => {
  if (isAzureOpenAIHost(baseUrl)) {
    const apiVersion = process.env.AZURE_OPENAI_API_VERSION || '2024-12-01-preview';
    const deploymentName = req.model;
    const url = `${baseUrl}/openai/deployments/${deploymentName}/images/generations?api-version=${apiVersion}`;
    const headers = {
      'api-key': apiKey,
    };
    // ...
  } else {
    // current public OpenAI logic
  }
};

Notes

The proposed fix only addresses the immediate breakage and does not include the param-passthrough item (#4), which requires a separate follow-up PR.

Recommendation

Apply the proposed workaround to update the image generation provider for Azure OpenAI hosts, as it addresses the immediate breakage and allows for a separate follow-up PR to handle the param-passthrough item.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING