Skip to content

Add Qwen Image Edit 2509 pipeline support - #465

Open
justinchuby wants to merge 3 commits into
mainfrom
justinchuby-add-qwen-image-edit-2509
Open

Add Qwen Image Edit 2509 pipeline support#465
justinchuby wants to merge 3 commits into
mainfrom
justinchuby-add-qwen-image-edit-2509

Conversation

@justinchuby

@justinchuby justinchuby commented Aug 8, 2026

Copy link
Copy Markdown
Member

Summary

  • add faithful QwenImageEditPlusPipeline detection for Qwen/Qwen-Image-Edit-2509, including its Qwen2.5-VL image-aware prompt encoder, Qwen Image transformer, normalized 3D VAE, processor assets, and FlowMatch Euler scheduler configuration
  • align the denoiser with diffusers packed target/source latent tokens, non-contiguous text masks, text/image 3D RoPE, 256-wide timestep projection, target-token output slicing, and fp16 clipping
  • add dedicated prompt-encoding, denoising, and edit-VAE tasks while preserving direct diffusers weight names
  • emit onnx-genai image-edit runtime metadata for prompt templates, image sizing, latent packing, source conditioning, RoPE inputs, dynamic scheduler shifting, and component dataflow
  • add official-config graph coverage plus reduced-config numerical, dtype, CUDA, real-source-image, and deterministic end-to-end diffusion-loop coverage

Validation

  • lintrunner -a — full repository lint clean after the golden strengthening
  • full non-integration suite — 3,648 passed, 57 skipped
  • schema/model coverage sweep — 923 passed, 217 skipped
  • Qwen Image model module — 13 passed, including the genuine reduced VAE-to-denoiser-to-decoder golden chain and CUDA precision tests
  • Qwen Image transformer/edit integration — 6 passed
  • existing Qwen Image VAE integration — 2 passed
  • official Qwen/Qwen-Image-Edit-2509 fp16 CUDA component CLI export with --runtime onnx-genai — succeeded and emitted model.onnx, FlowMatch metadata, tokenizer, Qwen2-VL image/video processor configs, and chat template
  • RTX A1000, onnxruntime-gpu 1.28.0 CUDAExecutionProvider — profiled CUDA node execution on reduced configurations; fp32 CPU/CUDA parity passed; fp16 and bf16 outputs matched reduced diffusers references at rtol=atol=1e-2
  • CUDA verbose partitioning — zero inserted Memcpy nodes after hoisting the shared mask and removing the scalar shape transfer
  • official-config, no-weight optimized transformer graph — 10,369 nodes, 60 Attention nodes, 0 Identity nodes, 7 Cast nodes, 9 inputs, 1 output
  • independent review fixes — replaced Windows-only temporary paths with os.path.join and added strict latents_mean/latents_std length validation against z_dim

Golden provenance and semantics

The committed golden is a genuine independently generated reduced Qwen Image Edit diffusion pipeline reference, but it is not generated with the official 20.4B checkpoint weights and is not an official-checkpoint perceptual output.

Reference generation uses diffusers 0.39.0 with a seeded one-block QwenImageTransformer2DModel and seeded reduced AutoencoderKLQwenImage whose weights are copied into the separate Mobius ONNX graphs. The source is the real nonzero testdata/pipeline-cat-chonk.jpeg:

  1. diffusers resizes and encodes the JPEG through the reduced Qwen Image VAE
  2. it takes the posterior mode, applies Qwen latent mean/std normalization, and performs official 2x2 latent packing
  3. it concatenates those source-image tokens after independently seeded target latent tokens
  4. it invokes the diffusers transformer with deterministic prompt embeddings, a non-contiguous prompt mask, and image/text 3D RoPE
  5. it advances the target through three independent FlowMatchEulerDiscreteScheduler steps
  6. it unpacks, denormalizes, and decodes the final target through the reduced diffusers VAE

The test then independently runs the Mobius chain: ONNX VAE encoder -> separate NumPy 2x2 packing -> ONNX denoiser plus a separate FlowMatch scheduler -> NumPy unpacking -> ONNX VAE decoder.

  • L4 (l4_noise_pred): all 256 target-token flow-prediction values from the first actual diffusers transformer call. Both the diffusers reference and ONNX output are compared elementwise to the committed data.
  • L5 (l5_final_latents, l5_final_image): all 256 target latent-token values after three actual diffusers transformer/scheduler steps and all 768 clipped decoded image values from the final reduced diffusers VAE decode. Mobius final latent tokens and decoded image are compared elementwise.
  • Prompt scope: prompt embeddings are deterministic synthetic tensors so this reduced numerical golden isolates the image-edit diffusion chain. The official Qwen2.5-VL prompt-encoder split is covered separately by graph/config/runtime-contract tests; no claim is made that the golden exercises official prompt-encoder weights.

This proves real-image VAE conditioning, packing, transformer invocation, multi-step scheduling, and decode semantics without fake token-generation data. It does not claim official-weight image quality or official-checkpoint end-to-end parity.

Waivers

  • Official-weight L3/L4/L5 and perceptual E2E: the checkpoint contains 20.4B parameters and over 57 GB of weights, exceeding the RTX A1000's 8 GB VRAM and the available validation budget. Official component configs and the official-size graph/export path are validated without weights; numerical parity and committed goldens use the genuine reduced pipeline described above.
  • ORT GenAI load: onnxruntime-genai does not expose a diffusion/image-edit pipeline runtime. The supported onnx-genai iterative runtime sidecar is exported and tested instead.
  • Foundry Local / Olive quantization: no Qwen Image Edit diffusion runtime or applicable quantization path is available in this environment; these are not claimed as validated.

Implement the Qwen-Image-Edit-2509 packed-latent transformer contract, Qwen2.5-VL prompt encoder split, normalized Qwen VAE path, FlowMatch scheduler metadata, processor assets, and deterministic diffusion goldens.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Justin Chu <justinchu@microsoft.com>
@justinchuby
justinchuby requested review from a team and a lite review from Copilot August 8, 2026 01:38
Comment thread src/mobius/models/qwen_image_test.py Fixed
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

Performance Comparison

Comparing 78437cdba76f4e

Model Metric Baseline Current Delta
bert (feature-extraction) model_size_bytes 359 KB 359 KB +0.0%
bert (feature-extraction) num_nodes 60 60 +0.0%
falcon model_size_bytes 364 KB 364 KB +0.0%
falcon num_nodes 68 68 +0.0%
gemma2 model_size_bytes 428 KB 428 KB +0.0%
gemma2 num_nodes 107 107 +0.0%
gpt2 model_size_bytes 388 KB 388 KB +0.0%
gpt2 num_nodes 54 54 +0.0%
llama model_size_bytes 425 KB 425 KB +0.0%
llama num_nodes 62 62 +0.0%
llama (static-cache) model_size_bytes 425 KB 425 KB +0.0%
llama (static-cache) num_nodes 58 58 +0.0%
mamba (ssm-text-generation) model_size_bytes 296 KB 296 KB +0.0%
mamba (ssm-text-generation) num_nodes 98 98 +0.0%
phi3 model_size_bytes 421 KB 421 KB +0.0%
phi3 num_nodes 60 60 +0.0%
phi3 (static-cache) model_size_bytes 421 KB 421 KB +0.0%
phi3 (static-cache) num_nodes 56 56 +0.0%
qwen2 model_size_bytes 425 KB 425 KB +0.0%
qwen2 num_nodes 62 62 +0.0%
qwen2 (static-cache) model_size_bytes 425 KB 425 KB +0.0%
qwen2 (static-cache) num_nodes 58 58 +0.0%
qwen3_5_moe (hybrid-text-generation) model_size_bytes 506 KB 506 KB +0.0%
qwen3_5_moe (hybrid-text-generation) num_nodes 275 275 +0.0%
qwen3_5_text (hybrid-text-generation) model_size_bytes 458 KB 458 KB +0.0%
qwen3_5_text (hybrid-text-generation) num_nodes 129 129 +0.0%
qwen3_5_vl (hybrid-qwen-vl) model_size_bytes 977 KB 977 KB +0.0%
qwen3_5_vl (hybrid-qwen-vl) num_nodes 431 431 +0.0%
t5 (seq2seq) model_size_bytes 836 KB 836 KB +0.0%
t5 (seq2seq) num_nodes 166 166 +0.0%
whisper (speech-to-text) model_size_bytes 1008 KB 1008 KB +0.0%
whisper (speech-to-text) num_nodes 128 128 +0.0%

No performance regressions.

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

🏗️ Architecture Diff

Comparing 78437cdba76f4e

Model Sub-model Changes Status
bert (feature-extraction) model 0
falcon model 0
gemma2 model 0
gemma4 (gemma4) decoder 0
gemma4 (gemma4) embedding 0
gemma4 (gemma4) vision_encoder 0
gemma4_text model 0
gpt2 model 0
llama model 0
llama (static-cache) model 0
mamba (ssm-text-generation) model 0
phi3 model 0
phi3 (static-cache) model 0
qwen model 0
qwen (static-cache) model 0
qwen2 model 0
qwen2 (static-cache) model 0
qwen2_moe model 0
qwen2_moe (static-cache) model 0
qwen3 model 0
qwen3 (static-cache) model 0
qwen3_5_moe (hybrid-text-generation) model 0
qwen3_5_text (hybrid-text-generation) model 0
qwen3_5_vl (hybrid-qwen-vl) decoder 0
qwen3_5_vl (hybrid-qwen-vl) embedding 0
qwen3_5_vl (hybrid-qwen-vl) vision_encoder 0
qwen3_moe model 0
qwen3_moe (static-cache) model 0
qwen3_next (hybrid-text-generation) model 0
t5 (seq2seq) decoder 0
t5 (seq2seq) encoder 0
whisper (speech-to-text) decoder 0
whisper (speech-to-text) encoder 0

No architecture changes detected.


Legend: ⚪ No change · 🔵 Minor (attrs/inits) · 🟡 Moderate (nodes added/removed) · 🔴 Major (interface changed)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds end-to-end support for the Qwen/Qwen-Image-Edit-2509 diffusers pipeline in mobius, including model/task wiring for prompt encoding + denoising + edit-VAE, plus onnx-genai export metadata so the runtime can reproduce the pipeline’s non-neural contract (latent packing, RoPE inputs, scheduler shifting, etc.).

Changes:

  • Introduces Qwen Image Edit–specific tasks (prompt text-encoder split, packed-token denoiser, normalized-latent VAE) and updates Qwen Image/Qwen VL model codepaths to match diffusers behavior.
  • Extends diffusers pipeline building and onnx-genai auto-export to retain/emit pipeline metadata (scheduler + processor assets) and support filtered/single-component exports.
  • Adds unit/integration coverage and deterministic L4/L5 diffusion-stage goldens for the new pipeline.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/integration_test.py Uses TemporaryDirectory for ORT session model saving to improve portability/cleanup.
tests/build_graph_test.py Adds graph-build coverage for Qwen Image Edit text-encoder split and updates Qwen Image denoising task expectations.
testdata/golden/diffusion/qwen-image-edit-2509.json Adds deterministic diffusion-stage golden outputs for the Qwen Image Edit 2509 pipeline.
testdata/cases/diffusion/qwen-image-edit-2509.yaml Adds an L4/L5 golden-case descriptor for the official Qwen Image Edit 2509 pipeline.
src/mobius/tasks/_qwen_image.py New packed-token denoising task for Qwen Image with external RoPE + mask + target slicing.
src/mobius/tasks/_qwen_image_vae.py Adds dtype plumbing for VAE graphs and introduces an edit-VAE task that embeds latent normalization.
src/mobius/tasks/_qwen_image_text_encoder.py New task that builds the Qwen2.5-VL prompt encoder split needed by image edit.
src/mobius/tasks/init.py Exposes new Qwen Image tasks and registers their task names.
src/mobius/models/qwen_vl.py Adds return_hidden_states option to support prompt-encoder hidden-state extraction.
src/mobius/models/qwen_image.py Implements Qwen Image Edit denoiser changes: packed tokens, 3D RoPE inputs, masks, timestep embedding changes, output slicing, fp16 clipping.
src/mobius/models/qwen_image_vae.py Adjusts RMSNorm broadcasting for attention paths and preserves diffusers weight names via passthrough preprocess.
src/mobius/models/qwen_image_test.py Adds numerical parity tests vs diffusers, CUDA/precision coverage, and deterministic L4/L5 diffusion-stage golden replay.
src/mobius/integrations/onnx_genai/inference_metadata.py Extends scheduler metadata to support FlowMatch Euler fields and omits beta fields for flow-match configs.
src/mobius/integrations/onnx_genai/inference_metadata_test.py Adds coverage for FlowMatch Euler scheduler field mapping and metadata emission.
src/mobius/integrations/onnx_genai/auto_export.py Adds Qwen Image Edit–specific metadata augmentation, processor asset copying, and flat-path handling for single-component diffusion exports.
src/mobius/integrations/onnx_genai/auto_export_test.py Tests flat-path diffusion export and Qwen Image Edit metadata + processor assets emission.
src/mobius/_diffusers_configs.py Adds Qwen2.5-VL text-encoder adapter config and stores non-neural pipeline metadata on ModelPackage; adds dtype + latent stats to Qwen configs.
src/mobius/_diffusers_configs_test.py Adds L2-ish config mapping coverage for official Qwen Image Edit component configs (transformer + VAE latent stats).
src/mobius/_diffusers_builder.py Adds Qwen2.5-VL component support, component allowlisting, EP-aware build plumbing, normalized VAE task selection for Qwen Image Edit, and retains pipeline metadata.
src/mobius/_diffusers_builder_test.py Updates class/task expectations and adds tests for normalized VAE task selection + component allowlist behavior.
src/mobius/_builder.py Passes execution_provider through to diffusers pipeline building.
src/mobius/main.py Adds CLI component filtering behavior for diffusers pipelines and forwards EP selection through pipeline build.
README.md Documents diffusion support including Qwen-Image-Edit-2509.
docs/model-catalog.md Documents supported diffusers component classes including Qwen Image Edit prompt encoder + normalized VAE behavior.
Suppressed comments (5)

src/mobius/models/qwen_image_test.py:187

  • This path uses a Windows-style "\" separator. On POSIX it produces a filename containing a backslash and writes outside the temp directory, leaving artifacts behind.
        path = f"{directory}\\model.onnx"

src/mobius/models/qwen_image_test.py:192

  • Profiling output prefix uses a Windows-style "\" separator; on POSIX this writes profiling JSON outside the temp directory (and may leave it behind).
        options.profile_file_prefix = f"{directory}\\cuda_profile"

src/mobius/models/qwen_image_test.py:371

  • This path uses a Windows-style "\" separator. On POSIX it writes the model outside the temp directory (backslash becomes part of the filename), which can leak files between tests.
        path = f"{directory}\\model.onnx"

src/mobius/models/qwen_image_test.py:486

  • This path uses a Windows-style "\" separator. On POSIX it writes the model outside the temp directory, which can leak artifacts and break test isolation.
        path = f"{directory}\\model.onnx"

src/mobius/tasks/_qwen_image_vae.py:111

  • Same validation issue as the encoder: if latents_mean/latents_std lengths don’t match z_dim, the decoder graph can be built but will fail at runtime.
        if config.latents_mean is None or config.latents_std is None:
            raise ValueError(
                "Qwen Image Edit VAE config requires latents_mean and latents_std"
            )

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/mobius/models/qwen_image_test.py Outdated
).sample[:, :4]

with tempfile.TemporaryDirectory() as directory:
path = f"{directory}\\model.onnx"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 4cef061: all temporary model and profiling paths now use os.path.join, preserving TemporaryDirectory isolation on POSIX and Windows. Direct Ruff format/check and the Qwen Image test module pass.

Comment thread src/mobius/tasks/_qwen_image_vae.py Outdated
Comment on lines +83 to +86
if config.latents_mean is None or config.latents_std is None:
raise ValueError(
"Qwen Image Edit VAE config requires latents_mean and latents_std"
)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 4cef061: QwenImageEditVAETask now validates that both latent-stat arrays exist and have exactly z_dim entries before either graph is constructed, with a regression test for the mismatch case.

justinchuby and others added 2 commits August 7, 2026 19:02
Use portable temporary paths in diffusion tests and validate Qwen VAE latent statistics before graph construction.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Justin Chu <justinchu@microsoft.com>
Generate reduced L4 and L5 references from an independent diffusers VAE encode, packed transformer FlowMatch loop, and VAE decode, then replay the full Mobius ONNX chain against those outputs.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Justin Chu <justinchu@microsoft.com>

sub_pkg = build_from_module(model_module, config, task_name)
if (
pipeline_class == "QwenImageEditPlusPipeline"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't look general enough

else {}
),
model_type=(
"qwen_image_edit" if pipeline_class == "QwenImageEditPlusPipeline" else "diffusers"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also this

self.txt_norm = _RMSNorm(config.joint_attention_dim, eps=config.norm_eps)
self.txt_in = _Linear(config.joint_attention_dim, hidden_size)
self.time_text_embed = _TimestepEmbedding(hidden_size, hidden_size)
self.time_text_embed = _TimestepEmbedding(256, hidden_size)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants