Repository navigation
feat(models): add LoRA support for Qwen-Image-2.1 - #9793
Open
Pfannkuchensack wants to merge 2 commits into
Open
Pfannkuchensack wants to merge 2 commits into
Pfannkuchensack wants to merge 2 commits into
Conversation
Pfannkuchensack
force-pushed
the
feat/qwen-image-2-1-lora
branch
from
October 10, 2026 23:56
57cb054 to
2184ae7
Compare
This was referenced Oct 10, 2026
Pfannkuchensack
marked this pull request as draft
October 11, 2026 00:20
Pfannkuchensack
force-pushed
the
feat/qwen-image-2-1-lora
branch
from
October 11, 2026 00:27
2184ae7 to
e23bbbc
Compare
Pfannkuchensack
marked this pull request as ready for review
October 11, 2026 01:01
lstein
added a commit
that referenced
this pull request
Oct 16, 2026
) ## Summary Adds Qwen-Image-2.1 (Base and the 8-step Turbo) as a new `qwen-image-2-1` base. This covers text-to-image plus canvas img2img, inpaint and outpaint. Before this, the released files were misidentified: the GGUF as FLUX schnell, the VAE as SD1. **Model support** - **Formats:** Diffusers folders (`QwenImage21Pipeline`); single-file transformers in bf16, ComfyUI scaled fp8 or `int8_convrot`; GGUF transformers; standalone VAEs in Diffusers or ComfyUI layout. - **Nodes:** model loader, Qwen3-VL 8B text encoder, denoise, i2l and l2i. - **Parity:** the denoise and the decode reproduce `QwenImage21Pipeline` bit for bit. That covers the prompt template and pre-norm hidden states, the Base shift and Turbo sigma table, CPU noise, the scheduler's bf16 step, the prefix KV cache, and the VAE's denormalization and pixel mapping, both done in the VAE's dtype as the pipeline does. - **Strength:** on Base, img2img and inpaint pick the start step by strength (step index), as Qwen-Image, Z-Image and diffusers' img2img pipelines do, so a strength changes the image about as much as on those models (0.75 starts at sigma ~0.85 at 40 steps). Turbo picks by noise level (`clip_timestep_schedule_fractional`): its table is front-loaded, so by index any strength from 0.5 to 0.85 would start above sigma 0.89. - **RGBA VAE:** a decode is saved as RGB when at most 0.1% of pixels have alpha < 224, otherwise as RGBA. Encodes treat the input as opaque. The VAE, bundled or standalone, runs at the configured precision, except float16, which is unstable for it. - **Memory:** encodes and decodes tile up front when the untiled frame would not fit (1024/768/512 px tiles, via the shared Qwen-Image tiling helper at 16x). The working-memory estimates cover the KV cache, CFG, SDPA scores and the GGUF dequant transient, and scale with the VAE and compute dtypes. - **Refused at install** (`InvalidMatchError`): nvfp4 files, torchao-quantized files (such as unsloth's FP8 build; loading them is #9798), int8 files without the convrot marker, and Q8_CR GGUFs. The check is shared with Ideogram 4. - **Identification:** Qwen-Image LoRA configs no longer claim 2.1 LoRAs; attention-only ones are told apart by their 4096 width. The FLUX GGUF config now requires `double_blocks`, so the 2.1 GGUF is no longer taken for FLUX schnell. **Starter models** - Diffusers Base and Turbo, the Q4_K_M GGUF and the ComfyUI VAE. - The Qwen3-VL 8B encoder starter moves to `common.py`, shared with Ideogram 4. `previous_names` keeps existing installs matched. **Frontend** - webv2: base identity, generation policy (VAE and Qwen3-VL 8B encoder are required for non-Diffusers models), graph builder, canvas mapping and metadata modes. - webv1 enums and labels for shared-schema compatibility. **Docs:** a new model page, plus the requirements, model-families, generate-panel, parameters, low-VRAM and FP8 pages. Reference images (Edit) and LoRA support follow in separate PRs. ## Related Issues / Discussions Part of #9594. Builds on #9774 (diffusers 0.41.0). ## QA Instructions - **Python:** the affected suites pass locally, ruff clean; the full suite runs in CI. - **webv2:** `pnpm lint` passes. vitest: 10073 passed; 3 failures are locale-dependent tests that fail on a German-locale machine, unrelated to this change. - **webv1:** `tsc` shows the same 16 errors as on the base commit `9d30e6a148`; none are new. - **Docs:** the build passes and all internal links are valid. - **Parity (RTX 4090):** final latents are bit-identical to `QwenImage21Pipeline` at 1024² and 2048² (40 steps). The decoded 1024² image is RGB bit-identical to the pipeline's (it was off by up to 12/255 on a quarter of the pixels before the dtype fix); a tiled decode differs from the pipeline's untiled one by the tiling. - **Browser (webv2, GGUF Q4_K_M + fp8 encoder + ComfyUI VAE):** - txt2img, about 47 s for 40 steps at 1024². - CFG 4 with a negative prompt. - Recall All, which restores model, VAE, encoder, CFG, seed and prompts. - Canvas img2img, inpaint and outpaint. - A 2048² img2img, where the encode tiles. - **Real files (RTX 4090, 1024², 40 steps, same seed, against the Diffusers transformer):** ComfyUI bf16 is bit-identical; `int8_convrot` 37 dB; GGUF Q4_K_M 21 dB (visibly the same image). The ComfyUI Turbo bf16 file is identified as Turbo and gives clean 8-step images. unsloth's FP8 (torchao) file is refused at install. No ComfyUI scaled-fp8 2.1 build is published; that path is covered by fixtures. ## Review All material findings were resolved, with regression tests: - the Turbo strength mapping; - NaN sigmas at one Base step; - missing encode auto-tiling; - the dtype-blind VAE estimate; - install-time refusal of nvfp4 and unmarked int8; - attention-only LoRA misidentification; - the webv1 enum. From the maintainer review: the decode now matches the pipeline's dtype order (bit-identical image), the bundled VAE honours the configured precision and never runs in float16, the denoise estimate scales with the compute dtype, the Turbo fixture carries the real Turbo scheduler config, and the webv2 builder is tested at CFG > 1 and for the GGUF component requirements. From the second maintainer review: Base image-to-image starts by step index (above), Qwen-Image-2.1 is listed on the Model Format Support page, the builder's unreachable component checks are gone, and tests pin the dtype width at the call site, the strength rounding, the guidance slice and the encoder edge's source field. Remaining limitation: LoRAs that target only `to_out` or the MLP halves, and attention-only LoKr, are told apart from Qwen-Image LoRAs only with #9793. ## Compatibility / Rollout - **Models already installed:** Qwen-Image-2.1 files installed before this PR keep their wrong records (FLUX/SD1) until they are removed and reinstalled. There is no migration. - **Starter rename:** the 8B encoder starter is renamed "Qwen3-VL 8B Encoder" and keeps its old name in `previous_names`. - **Generated API files:** `openapi.json` and `schema.ts` are regenerated; the changes are additive. ## Checklist - [x] _The PR has a short but descriptive title, suitable for a changelog_ - [x] _Meaningful regression coverage added / updated where needed; obsolete tests/code removed_ - [x] _Persisted-state and API changes include required migrations / compatibility validation_ - [x] _Relevant performance/efficiency opportunities considered; material claims have evidence_ - [x] _Material review findings resolved and relevant checks rerun_ - [x] _Documentation added / updated (if applicable)_ - [ ] _Updated `What's New` copy (if doing a release after this PR)_
The prompt encoder reads up to four references through the vision tower; their latents fill the image slots. The encoder reserves memory for the references' attention; the denoise checks slot grids before loading. Reference images are offered only on Diffusers models, which keep the vision tower.
Load ComfyUI, diffusers/PEFT and Kohya LoRAs, split ComfyUI's fused gate_up, and merge norm diffs on every format. Identify 2.1 LoRAs by their gated MLP or 4096-wide attention; refuse any that would apply only in part. Apply them to the denoise transformer through a collection loader, as sidecars on GGUF, int8 and fp8 weights.
Pfannkuchensack
force-pushed
the
feat/qwen-image-2-1-lora
branch
from
October 19, 2026 04:51
e04ef43 to
56fe808
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds LoRA support to Qwen-Image-2.1.
Stacked on #9787. This PR's own change is the last commit; the one before it is #9787 (#9784 is merged).
Layouts. The published LoRAs target the diffusers module names under a
diffusion_model.(ComfyUI),transformer./base_model.model.(diffusers/PEFT) orlora_unet_(Kohya) prefix. One exception: ComfyUI fuses each block's gated MLP input intoimg_mlp.gate_up. It is split by its output rows intogate_layer(first half) andproj(second half), as the base checkpoint converter does. The split covers low-rank layers, PEFT/ai-toolkit DoRA (output-dim magnitude) and full (diff) layers. Fulldiffs on the RMSNorm weights (attn.norm_q/norm_k,txt_in.text_norm), which ComfyUI's official Turbo LoRA carries, are merged into those weights on every format, since no sidecar can patch a norm.All or nothing. Identification and conversion share one key table. A LoRA with a layer this transformer lacks, or with a
gate_uplayer that cannot be split, is not installed as a Qwen-Image-2.1 LoRA. That covers LoKR, LoHA and LyCORIS DoRA, which mix both halves. Applying only the part that fits would pass a partial LoRA off as a whole one.Backend
LoRA_LyCORIS_QwenImage21_Config: the gated MLP's names (gate_up,gate_layer,proj,out) or the 4096 width of the block attention (to_q/k/v,to_out.0, low-rank or LoKR), excluding FLUX.2 LoRAs and anything with unsupported keys. Qwen-Image LoRA configs no longer claim such files, attention-only 2.1 LoKr included.double_blocks,single_blocks); a Kohya Qwen LoRA whose every key is on the MLP no longer reads as FLUX.qwen_image_2_1_lora_loaderandqwen_image_2_1_lora_collection_loadernodes. They refuse non-LoRAs and LoRAs of other bases by name, with a hint that Qwen Image LoRAs do not fit 2.1, and skip LoRAs already applied.qwen_image_2_1_denoisepatches the transformer for the whole sampling run, before the prefix KV cache is built:webv2:
qwen-image-2-1joins the LoRA-supported bases. The graph routes the transformer through the collection loader.Docs: a LoRA section on the Qwen-Image-2.1 page, plus the generate-panel and model-families rows.
Related Issues / Discussions
Part of #9594. Depends on #9787.
QA Instructions
pnpm lintpasses; the generation suites pass.gate_upsplit, including DoRA magnitudes and full layers;gate_up, foreign layers;qwen-image-2-1.Review
All material findings were resolved, with regression tests:
gate_upsplit now splits every row-indexed value (low-rankup, DoRA magnitude, fulldiff), not just the low-rank pair;to_out.0or the MLP halves, and attention-only LoKr, were filed as Qwen-Image LoRAs.Remaining limitation: a LoRA on only
to_q/to_k/to_vof FLUX.2 Klein 9B's first blocks has exactly the keys and shapes of a Qwen-Image-2.1 one and is identified as the latter. This is pinned in a test and noted in the docs.Compatibility / Rollout
openapi.jsonandschema.tsare regenerated; the changes are additive: two new nodes and a new LoRA config.qwen_image_2_1_denoisemoves to 1.2.0 without new fields.Checklist
What's Newcopy (if doing a release after this PR)