Skip to content

feat(imagegen): add shared image generation runtime and APIs - #2497

Merged
inureyes merged 4 commits into
lablup:mainfrom
hephaex:feat/image-generation-runtime-2341
Oct 20, 2026
Merged

inureyes merged 4 commits into
lablup:mainfrom
hephaex:feat/image-generation-runtime-2341

Conversation

@hephaex

@hephaex hephaex commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the shared image generation runtime: the generate-image CLI, /v1/images/generations and /v1/images/edits routes behind --image-model, the family probe, a PNG writer, the FLUX.2 VAE, the flow-match schedulers, Z-Image DiT blocks, and a Qwen3 hidden-state conditioner. The family registry ships empty; model-family issues register their constructors on top of this.

Related issues

Closes #2341

Part of #2348. Unblocks #2424, #2425, #2426, #2427 and #2428.

Type of change

  • feat — new user-visible feature
  • docs — documentation (docs/image-generation.md, README, MLXCEL_IMAGE_MODEL)

Test plan

Everything below ran on the CPU MLX backend (MLXCEL_BUILD_METAL=OFF, --features accelerate), because the Metal toolchain is not installed on the author's machine.

  • cargo fmt --all -- --check: clean
  • cargo clippy --workspace --all-targets -D warnings (CPU backend): exit 0
  • imagegen unit tests: 134 passed, 1 ignored (the real-checkpoint conditioner check)
  • Workspace tests: 61 failures, all Metal/GPU-dependent (mlxcel-core cache/paged_v2/mla/turbo/ffi/hardware, plus 11 root-crate tests such as florence2, webui catalog, mllama). The same 11 root-crate tests fail on an unmodified upstream/main worktree; mlxcel-core is untouched.
  • Upstream parity (Blaizzy/mlx-vlm @ 9527f2da, mlx 0.32.4 fixtures; tests/fixtures/generate_imagegen_fixtures.py reproduces the committed fixtures exactly):
    • schedulers at 1/4/12/30 steps: match
    • real ERNIE-Image-Turbo VAE vs upstream f32: decode 3.4e-6, packed decode 1.2e-6, encode 3.0e-6 (relative RMS); default f16 decode 1.2e-3 (asserted < 5e-3)
    • DiT blocks and Qwen3 conditioner on tiny models: match
  • Real checkpoint mlx-community/Qwen3-0.6B-4bit: conditioner last hidden state reproduces the generation-path logits exactly (rel-RMS 0)
  • Numbers traced: examples/logit_trace, Qwen3-0.6B-4bit, 96 positions, 12 chunks of 8 tokens. Baseline and branch traces are byte-identical, 0 disagreements, perplexity 80.1327 for both. The only change to Qwen3Model is one added method, forward_all_hidden.
  • Manual: generate-image on an empty dir and on an unported family prints clear errors; generate/serve/mlxcel-server on an image checkpoint print "use mlxcel generate-image"; a real SmolLM2 server without --image-model returns 501 naming the flag on both routes and the /images/* aliases.
  • cargo test --workspace --profile test-fast --features metal,accelerate: not run (no Metal toolchain)
  • make verify / Metal numerics: not run
  • cargo deny check: not run
  • Real image-family checkpoint end to end: not possible yet, since no family is registered (follow-up issues)

Notes for reviewers

  • Labels from the linked issue: please apply as appropriate (I do not have triage permission).
  • Upstream defect found while porting: _match_conv_layout in mlx-vlm leaves the encoder's RGB conv_in (128,3,3,3) in PyTorch layout; encode-decode reconstruction rel-RMS is 0.96 there versus 0.096 when transposed. This port decides the conv layout once per checkpoint (detect_conv_layout) and is not affected. Not reported upstream.
  • Deviations from upstream: attention mask fill is -30000 instead of -inf so a fully blocked row averages instead of producing NaN; scheduler sigmas are computed on the CPU in f32.
  • image_model_path lives only in ServerStartupConfig, not ServerConfig, to avoid the runtime-settings schema and webui label cascade.
  • Known gaps: mlxcel run <image dir> fails earlier with "No tokenizer found"; mlxcel arch --json has no ImageGenerate row until a family registers, so only the serialization is pinned by tests.

Checklist

  • PR title uses a Conventional Commits prefix
  • One logical change per PR
  • Updated docs/ (new docs/image-generation.md, README, env-var table)
  • Updated // Used by: ... comments on any shared function I modified (only an added method on Qwen3Model)
  • No secrets, credentials, or .env files committed

hephaex and others added 4 commits October 20, 2026 15:58
Add `src/imagegen/` with the model-independent pieces shared by every
text-to-image and image-editing family: request and result types, the
checkpoint family probe, the flow-match Euler scheduler (dynamic and
static shift), latent patching and packing, the FLUX.2 VAE, the Z-Image
DiT blocks, the Qwen3 hidden-state conditioner, PNG output, and the
family registry (empty until a model issue registers a row).

Surfaces:
- `mlxcel generate-image` (generate and edit, -n, --seed, --gen-kwargs)
- `mlxcel-server --image-model` with `POST /v1/images/generations` and
  `POST /v1/images/edits`, served by a dedicated worker; without the flag
  both routes answer 501 naming it
- `Runtime::ImageGenerate` and `OutputKind::Image` in the registry
- `generate`/`serve` on an image checkpoint now point at
  `mlxcel generate-image` instead of failing with `model_type not found`

`Qwen3Model` gains `forward_all_hidden`, one added method that reuses the
existing blocks. Teacher-forced logit traces on Qwen3-0.6B-4bit before and
after are byte-identical.

Tests compare against the upstream Python (Blaizzy/mlx-vlm) through
committed fixtures; `tests/fixtures/generate_imagegen_fixtures.py`
regenerates them exactly. The real ERNIE-Image-Turbo VAE matches upstream
f32 to a relative RMS of 3e-6 (decode), 1.2e-6 (packed decode) and 3e-6
(encode). Upstream's `_match_conv_layout` leaves the encoder's RGB
`conv_in` in PyTorch layout, which breaks its encoder; this port decides
the layout once per checkpoint.

Refs lablup#2341
Add docs/image-generation.md (CLI, server routes, probe, shared building
blocks, precision decision, family registration, verification) and point to
it from the README and the environment variable reference.

Refs lablup#2341
Discard expired queued requests before GPU execution, cap output pixel counts and explicit diffusion steps, and replace the predictable output probe with atomic exclusive creation. Add regression coverage for deadline handling, request limits, existing files and symlinks.

Declare the four reproducible numerical fixtures with individual size ceilings and an explicit aggregate binary budget. Document the limits and the remaining inability to interrupt an already-running generation.

Refs lablup#2341

Refs lablup#2497
Document the test-only checkpoint directory, default lookup path, skip behavior and ignored-test invocation. Validate the current main merge tree with the environment-variable documentation guard and all 15 guard tests.

Refs lablup#2341

Refs lablup#2497
@inureyes

Copy link
Copy Markdown
Member

Reviewed PR #2497 against issue #2341 and its accepted scope amendments, including the Qwen3 conditioner, static scheduler, model probing, CLI, HTTP aliases and authentication.

Fixed four findings in 0de942c: expired queued requests could still start GPU work after the caller timed out; output dimensions and explicit diffusion steps had no shared resource ceiling; the predictable directory probe could truncate an existing file or follow a symlink; and the four numerical fixtures were undeclared in the binary-asset gate. Requests now cap each output at 16,777,216 pixels and explicit steps at 1–1,000, queued expiration is checked before execution, and probes use atomic exclusive creation. Fixture declarations have individual size limits and a bounded aggregate budget. Added focused regressions and documented the behavior. Commit 2fea6a6 documents the new Qwen3 checkpoint override, resolving the environment-documentation and Python-tooling CI failures.

Validation on Linux/aarch64 NVIDIA GB10: CUDA library selection imagegen:: server::image_worker:: server::routes::images:: server::api_prefix_tests:: server::auth_route_tests:: passed 165 tests (two checkpoint-dependent tests ignored); cargo test --profile test-fast --features cuda --bin mlxcel -- generate_image --test-threads=1 passed 14. The separately selected ignored Qwen3 test passed against the local Qwen3-0.6B-4bit checkpoint, with logits relative RMS 0 and matching greedy tokens. cargo clippy -j4 -p mlxcel --lib --tests -- -D warnings, formatting, diff checks and all six binary-gate self-tests passed. The current-main merge tree passed the environment-variable guard and all 15 associated tests.

Authentication and route-prefix regressions passed. Already-running generation remains non-interruptible through the current model trait; expired queued work is now discarded. The real ERNIE VAE checkpoint was unavailable locally. As agreed in #2341, this infrastructure PR has no registered production image family; model-family wiring remains follow-up work. CI completion is still required before merge.

Refs #2341

@inureyes inureyes added type:enhancement New features, capabilities, or significant additions status:review Under review priority:medium Medium priority area:architecture Architecture and code structure changes modeltype:vlm Vision-language model labels Oct 20, 2026
@inureyes inureyes changed the title feat: add image generation runtime (generate-image CLI, /v1/images routes, shared VAE and scheduler) feat(imagegen): add shared image generation runtime and APIs Oct 20, 2026
@inureyes
inureyes merged commit cf1354f into lablup:main Oct 20, 2026
28 checks passed
@inureyes inureyes added status:done Completed and removed status:review Under review labels Oct 20, 2026
@hephaex
hephaex deleted the feat/image-generation-runtime-2341 branch October 23, 2026 06:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:architecture Architecture and code structure changes modeltype:vlm Vision-language model priority:medium Medium priority status:done Completed type:enhancement New features, capabilities, or significant additions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Add image generation runtime: generate-image CLI, /v1/images routes, shared VAE and scheduler

2 participants