From cf31b8fe1d57684dc9eef28e1d8268b5a7ef0590 Mon Sep 17 00:00:00 2001 From: memosr Date: Thu, 20 Aug 2026 23:23:35 +0300 Subject: [PATCH 1/3] fix(image): stop advertising dall-e-3 and flux in the OpenClaw picker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The image UI sends the picked id from provider.models straight to /v1/images/generations, which forwards the body verbatim with no alias resolution. Two ids there have been dead since v0.12.227: - openai/dall-e-3 — gateway 400s (delisted upstream 2026-05-25) - black-forest/flux-1.1-pro — no gateway entry at all Both were dropped from IMAGE_PRICING and MODEL_ALIASES in that release, but buildImageGenerationProvider was missed, so picking either one is a guaranteed upstream failure. The three live models added since (gpt-image-2, nano-banana-2, seedream-5-pro) were never advertised. Same drift class the blockrun_image_generation tool description carried, which pointed agents at the same two dead ids. - picker list resynced to IMAGE_PRICING's 9 gateway-served ids - IMAGE_MODEL_IDS exported from proxy.ts as the pinning source of truth - partner tool description and model-param example refreshed - stale --model dall-e-3 hints in user-facing copy updated src/index.image-provider.test.ts pins the picker against IMAGE_PRICING so the two cannot drift apart again; it fails on the pre-fix list. --- src/index.image-provider.test.ts | 50 ++++++++++++++++++++++++++++++++ src/index.ts | 13 +++++++-- src/partners/registry.ts | 11 +++---- src/proxy.ts | 15 ++++++++-- 4 files changed, 79 insertions(+), 10 deletions(-) create mode 100644 src/index.image-provider.test.ts diff --git a/src/index.image-provider.test.ts b/src/index.image-provider.test.ts new file mode 100644 index 00000000..eba5b3d1 --- /dev/null +++ b/src/index.image-provider.test.ts @@ -0,0 +1,50 @@ +import { describe, expect, it } from "vitest"; + +import { buildImageGenerationProvider } from "./index.js"; +import { IMAGE_MODEL_IDS } from "./proxy.js"; + +/** + * The OpenClaw image UI sends the picked entry from `provider.models` straight + * through to `POST /v1/images/generations`, and that handler forwards the body + * to the gateway verbatim — no `resolveModelAlias()` pass. So every id we + * advertise here has to be a live gateway model id, not an alias and not a + * retired one, or the user's pick 400s upstream. + * + * `IMAGE_PRICING` in proxy.ts is the list that v0.12.227 kept in sync with + * blockrun's IMAGE_MODELS, so it is the local source of truth for "the gateway + * can serve this". Anything advertised but unpriced is drift. + */ +describe("image generation provider model list", () => { + const provider = buildImageGenerationProvider(); + + // `models` is optional on ImageGenerationProviderPlugin, so pin that we + // actually advertise something before asserting on its contents. + const models = provider.models ?? []; + + it("advertises a model list", () => { + expect(models.length).toBeGreaterThan(0); + }); + + it("only advertises models the gateway can still serve", () => { + const unservable = models.filter((id) => !IMAGE_MODEL_IDS.includes(id)); + expect(unservable).toEqual([]); + }); + + it("does not advertise models delisted upstream", () => { + // dall-e-3: gateway 400s ("Delisted 2026-05-25: OpenAI removed dall-e-3 + // from the API"). flux-1.1-pro: no gateway entry at all. Both were dropped + // from IMAGE_PRICING and MODEL_ALIASES in v0.12.227. + expect(models).not.toContain("openai/dall-e-3"); + expect(models).not.toContain("black-forest/flux-1.1-pro"); + }); + + it("advertises the live successors", () => { + expect(models).toContain("openai/gpt-image-2"); + expect(models).toContain("google/nano-banana-2"); + expect(models).toContain("bytedance/seedream-5-pro"); + }); + + it("advertises a default model that is itself advertised", () => { + expect(models).toContain(provider.defaultModel); + }); +}); diff --git a/src/index.ts b/src/index.ts index 2031fa1c..c9c9a51f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1172,17 +1172,24 @@ function parseGenArgs(raw: string): { * with OpenClaw's native image generation UI. * Delegates to the local proxy (which handles x402 payment). */ -function buildImageGenerationProvider(): ImageGenerationProviderPlugin { +export function buildImageGenerationProvider(): ImageGenerationProviderPlugin { return { id: "blockrun", label: "BlockRun", defaultModel: "google/nano-banana", + // Must stay in sync with IMAGE_PRICING (proxy.ts). OpenClaw sends the + // picked id straight to /v1/images/generations, which forwards the body + // verbatim with no alias resolution, so a retired id here is a guaranteed + // upstream 400. dall-e-3 (delisted 2026-05-25) and flux-1.1-pro (no + // gateway entry) were dropped in v0.12.227 and had lingered here. + // src/index.image-provider.test.ts pins the two lists together. models: [ "google/nano-banana", + "google/nano-banana-2", "google/nano-banana-pro", "openai/gpt-image-1", - "openai/dall-e-3", - "black-forest/flux-1.1-pro", + "openai/gpt-image-2", + "bytedance/seedream-5-pro", "xai/grok-imagine-image", "xai/grok-imagine-image-pro", "zai/cogview-4", diff --git a/src/partners/registry.ts b/src/partners/registry.ts index f43b3834..9d7d23de 100644 --- a/src/partners/registry.ts +++ b/src/partners/registry.ts @@ -636,12 +636,13 @@ export const PARTNER_SERVICES: PartnerServiceDefinition[] = [ name: "Image Generation", partner: "BlockRun", category: "Image & Video", - shortDescription: "8 image models (DALL-E, Flux, Grok, ...)", + shortDescription: "9 image models (Nano Banana, GPT Image, ...)", description: "Generate an image from a text prompt. Models available: google/nano-banana (default), " + - "google/nano-banana-pro (up to 4K), openai/gpt-image-1, openai/dall-e-3, " + - "black-forest/flux-1.1-pro, xai/grok-imagine-image, xai/grok-imagine-image-pro, " + - "zai/cogview-4. Returns a local http://localhost:8402/images/.png URL.", + "google/nano-banana-2, google/nano-banana-pro (up to 4K), openai/gpt-image-1, " + + "openai/gpt-image-2, bytedance/seedream-5-pro, xai/grok-imagine-image, " + + "xai/grok-imagine-image-pro, zai/cogview-4. " + + "Returns a local http://localhost:8402/images/.png URL.", proxyPath: "/images/generations", method: "POST", params: [ @@ -655,7 +656,7 @@ export const PARTNER_SERVICES: PartnerServiceDefinition[] = [ name: "model", type: "string", description: - "Full model ID (e.g. 'google/nano-banana', 'openai/dall-e-3'). Default: google/nano-banana.", + "Full model ID (e.g. 'google/nano-banana', 'openai/gpt-image-2'). Default: google/nano-banana.", required: false, }, { diff --git a/src/proxy.ts b/src/proxy.ts index 72f52f5c..0d8d9bea 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -1609,6 +1609,17 @@ const IMAGE_PRICING: Record Date: Fri, 21 Aug 2026 19:58:56 +0300 Subject: [PATCH 2/3] fix(image): add nano-banana-2 alias and pin the catalogs both ways Review feedback on #254. - IMAGE_MODEL_ALIASES gained banana-2 / nano-banana-2; the shorthand was otherwise passed through raw and 400'd, while the full id worked. Also listed in the /cr-imagegen help block. - The picker test now asserts set equality against IMAGE_MODEL_IDS rather than one-way inclusion, so a model added to the pricing table but not advertised fails too. The reviewer's suggestion to build the picker list from IMAGE_MODEL_IDS is left out: importing it into index.ts breaks index.lifecycle.test.ts, which fully mocks ./proxy.js. --- src/index.image-provider.test.ts | 8 +++++--- src/proxy.ts | 3 +++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/index.image-provider.test.ts b/src/index.image-provider.test.ts index eba5b3d1..fa47a2e0 100644 --- a/src/index.image-provider.test.ts +++ b/src/index.image-provider.test.ts @@ -25,9 +25,11 @@ describe("image generation provider model list", () => { expect(models.length).toBeGreaterThan(0); }); - it("only advertises models the gateway can still serve", () => { - const unservable = models.filter((id) => !IMAGE_MODEL_IDS.includes(id)); - expect(unservable).toEqual([]); + it("matches the gateway's image catalog exactly", () => { + // Both directions: an advertised id the gateway cannot serve is a + // guaranteed 400, and a servable id we never advertise is unreachable + // from the picker. The 2026-05 sweep left drift in both directions. + expect([...models].sort()).toEqual([...IMAGE_MODEL_IDS].sort()); }); it("does not advertise models delisted upstream", () => { diff --git a/src/proxy.ts b/src/proxy.ts index 0d8d9bea..7d3a97d2 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -3902,6 +3902,8 @@ async function proxyRequest( seedream: "bytedance/seedream-5-pro", banana: "google/nano-banana", "nano-banana": "google/nano-banana", + "banana-2": "google/nano-banana-2", + "nano-banana-2": "google/nano-banana-2", "banana-pro": "google/nano-banana-pro", "nano-banana-pro": "google/nano-banana-pro", "grok-imagine": "xai/grok-imagine-image", @@ -3929,6 +3931,7 @@ async function proxyRequest( "", "Models:", " nano-banana Google Gemini Flash — $0.05/image", + " banana-2 Google Nano Banana 2 — $0.09/image", " banana-pro Google Gemini Pro — $0.10/image (up to 4K)", " gpt-image OpenAI GPT Image 1 — $0.02/image", " gpt-image-2 OpenAI GPT Image 2 — $0.06/image", From 065646425591ffc8fd08b2619b839b42d8da5e20 Mon Sep 17 00:00:00 2001 From: memosr Date: Fri, 21 Aug 2026 20:19:17 +0300 Subject: [PATCH 3/3] fix(image): list grok-imagine-pro in the /cr-imagegen help block Review feedback on #254. The help block listed 8 of the 9 models the picker advertises; xai/grok-imagine-image-pro was missing even though IMAGE_MODEL_ALIASES has mapped grok-imagine-pro to it all along, so the model was reachable but undiscoverable. Price matches IMAGE_PRICING. --- src/proxy.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/proxy.ts b/src/proxy.ts index 7d3a97d2..48af6040 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -3937,6 +3937,7 @@ async function proxyRequest( " gpt-image-2 OpenAI GPT Image 2 — $0.06/image", " seedream ByteDance Seedream 5 Pro — $0.045/image", " grok-imagine xAI Grok Imagine — $0.02/image", + " grok-imagine-pro xAI Grok Imagine Pro — $0.07/image", " cogview Zhipu CogView-4 — $0.015/image", "", "Examples:",