Skip to content

Provider proposal: OpenRouter image generation (/api/v1/images) — design + measurement offer #37

Description

@gabrielrf01

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=image54 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

  1. Would a PR along these lines be considered — and is there a reason OpenRouter was originally excluded that this design fails to address?
  2. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions