Provider proposal: OpenRouter image generation (/api/v1/images)
docs/gen.md says Gemini/OpenRouter/fal/BytePlus are intentionally not included. I searched the issue tracker and found no discussion recording the reason, so this is a proposal to revisit OpenRouter specifically — the landscape changed enough that I think the exclusion may no longer hold, and I'd like to hear whether it conflicts with a reason I can't see.
What exists today (verified 2026-09-15 against the live API)
OpenRouter now ships a first-class images endpoint that sits surprisingly close to sprite-gen's one-call-one-verified-PNG contract:
POST /api/v1/images — body {model, prompt, input_references[], aspect_ratio, output_format, ...}; response {data: [{b64_json, media_type}], usage} (raw base64, not data URLs).
GET /api/v1/models?output_modalities=image → 54 models today, including google/gemini-3-pro-image, openai/gpt-image-1 / gpt-image-2, bytedance-seed/seedream-4.5, black-forest-labs/flux.2-*. All advertise image input modality (i.e. refs/editing).
- Refs go through
input_references (HTTP URLs or base64 data URLs); per-provider reference-count limits vary by routed backend.
- Billing is all-or-nothing: failed generations return 502 and are not charged. Auth is a plain Bearer API key (
OPENROUTER_API_KEY) — no OAuth/login probe, no subscription-login-vs-API-key precedence question like grok's.
Why it fits sprite-gen's shape
- One credential unlocks every routed image backend, which is useful exactly where sprite-gen is strong: row generation with an identity base + layout guide attached is a multi-image edit call, and every model above supports image input.
- The row pipeline's transparency path is already chroma (
raw/<state>.png generated on a key, keyed downstream), so nothing in prepare/gen-set needs to change.
Proposed design (respecting the declared-once Provider.transparency SSoT)
The architectural wrinkle with an aggregator is that transparency capability varies per routed model, not per router (gpt-image-* can return real alpha; Gemini-class backends paint backgrounds). The design below takes the honest router-level claim:
OpenRouterProvider declares transparency = TRANSPARENCY_CHROMA unconditionally — that is the only statement true for every model it can route to. No change to resolve_transparency_strategy; chroma is also exactly what the sprite-row pipeline already uses.
native_alpha: true refused fail-loud before any upload (grok precedent). A measured per-model native-alpha path (e.g. background: transparent on gpt-image backends) would require its own measurement and a per-model declaration — explicitly out of scope for v1. Same for the endpoint's background/output_format params: not relied on in v1, since per-model support varies.
--model is required, fail-loud when absent — a router has no honest hard-default backend. Optional OPENROUTER_IMAGE_MODEL env override, mirroring SPRITE_GEN_DEFAULT_PROVIDER's role. openrouter is not added to resolve_default_provider — the default policy stays codex→grok; OpenRouter participates only via explicit --provider openrouter.
- Transport: decode
data[0].b64_json (exactly one image expected, otherwise fail), re-encode to a verified PNG without resizing, atomic tempfile + os.replace publish, verify_png — all mirroring GrokProvider. Refs validated as PNG/JPEG/WebP like grok's _reference.
- Report
extra: auth_source: "env", transport: "openrouter-api", endpoint, model, and usage.cost when present. Error bodies are not echoed (can contain prompts).
- Registry:
PROVIDERS += "openrouter", _make_provider, --provider/--model help, and the doc surfaces (gen.md provider + transparency tables, SKILL.md routes, user-workflow access check — GET /api/v1/key is the cheap credential probe).
- Tests mirror
tests/gen/test_gen.py's fake-transport pattern: native_alpha refusal before upload, missing model fails loud, non-200 fails loud with no retry/fallback, one-image response contract, invalid ref. test_unknown_provider_fails_loud (gemini) keeps passing untouched.
Measurement plan before any PR
Capability claims here are measured, so I'd run the real row pipeline end-to-end with a credit key and attach per-model evidence before opening the PR — e.g. a 4-frame idle row and a walk row on google/gemini-3-pro-image, openai/gpt-image-1, and bytedance-seed/seedream-4.5, reporting: output media_type, alpha_zero_pct after keying, chroma edge quality on a pixel-art subject, refs adherence (identity lock against base + layout guide), and elapsed_seconds against the codex/grok baselines already in docs/gen.md. Happy to narrow the model set to whatever is worth the maintainer's review time.
Ask
- Would a PR along these lines be considered — and is there a reason OpenRouter was originally excluded that this design fails to address?
- Any preference on the
--model requirement vs. an env default, and on which models the measurements should cover?
I can open the adapter + tests + docs PR as soon as the approach is confirmed, measurements attached.
Provider proposal: OpenRouter image generation (
/api/v1/images)docs/gen.mdsays Gemini/OpenRouter/fal/BytePlus are intentionally not included. I searched the issue tracker and found no discussion recording the reason, so this is a proposal to revisit OpenRouter specifically — the landscape changed enough that I think the exclusion may no longer hold, and I'd like to hear whether it conflicts with a reason I can't see.What exists today (verified 2026-09-15 against the live API)
OpenRouter now ships a first-class images endpoint that sits surprisingly close to sprite-gen's one-call-one-verified-PNG contract:
POST /api/v1/images— body{model, prompt, input_references[], aspect_ratio, output_format, ...}; response{data: [{b64_json, media_type}], usage}(raw base64, not data URLs).GET /api/v1/models?output_modalities=image→ 54 models today, includinggoogle/gemini-3-pro-image,openai/gpt-image-1/gpt-image-2,bytedance-seed/seedream-4.5,black-forest-labs/flux.2-*. All advertise image input modality (i.e. refs/editing).input_references(HTTP URLs or base64 data URLs); per-provider reference-count limits vary by routed backend.OPENROUTER_API_KEY) — no OAuth/login probe, no subscription-login-vs-API-key precedence question like grok's.Why it fits sprite-gen's shape
raw/<state>.pnggenerated on a key, keyed downstream), so nothing inprepare/gen-setneeds to change.Proposed design (respecting the declared-once
Provider.transparencySSoT)The architectural wrinkle with an aggregator is that transparency capability varies per routed model, not per router (
gpt-image-*can return real alpha; Gemini-class backends paint backgrounds). The design below takes the honest router-level claim:OpenRouterProviderdeclarestransparency = TRANSPARENCY_CHROMAunconditionally — that is the only statement true for every model it can route to. No change toresolve_transparency_strategy; chroma is also exactly what the sprite-row pipeline already uses.native_alpha: truerefused fail-loud before any upload (grok precedent). A measured per-model native-alpha path (e.g.background: transparenton gpt-image backends) would require its own measurement and a per-model declaration — explicitly out of scope for v1. Same for the endpoint'sbackground/output_formatparams: not relied on in v1, since per-model support varies.--modelis required, fail-loud when absent — a router has no honest hard-default backend. OptionalOPENROUTER_IMAGE_MODELenv override, mirroringSPRITE_GEN_DEFAULT_PROVIDER's role.openrouteris not added toresolve_default_provider— the default policy stays codex→grok; OpenRouter participates only via explicit--provider openrouter.data[0].b64_json(exactly one image expected, otherwise fail), re-encode to a verified PNG without resizing, atomictempfile+os.replacepublish,verify_png— all mirroringGrokProvider. Refs validated as PNG/JPEG/WebP like grok's_reference.extra:auth_source: "env",transport: "openrouter-api", endpoint, model, andusage.costwhen present. Error bodies are not echoed (can contain prompts).PROVIDERS += "openrouter",_make_provider,--provider/--modelhelp, and the doc surfaces (gen.mdprovider + transparency tables, SKILL.md routes, user-workflow access check —GET /api/v1/keyis the cheap credential probe).tests/gen/test_gen.py's fake-transport pattern: native_alpha refusal before upload, missing model fails loud, non-200 fails loud with no retry/fallback, one-image response contract, invalid ref.test_unknown_provider_fails_loud(gemini) keeps passing untouched.Measurement plan before any PR
Capability claims here are measured, so I'd run the real row pipeline end-to-end with a credit key and attach per-model evidence before opening the PR — e.g. a 4-frame idle row and a walk row on
google/gemini-3-pro-image,openai/gpt-image-1, andbytedance-seed/seedream-4.5, reporting: outputmedia_type,alpha_zero_pctafter keying, chroma edge quality on a pixel-art subject, refs adherence (identity lock against base + layout guide), andelapsed_secondsagainst the codex/grok baselines already indocs/gen.md. Happy to narrow the model set to whatever is worth the maintainer's review time.Ask
--modelrequirement vs. an env default, and on which models the measurements should cover?I can open the adapter + tests + docs PR as soon as the approach is confirmed, measurements attached.