Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
7f3fa8a
feat: add Qwen-Image 2.1 pipeline with block-causal attention and KV …
naykun Sep 14, 2026
35b34cd
fix style
naykun Sep 14, 2026
b25d7e2
refactor: address PR review feedback for Qwen-Image 2.1
naykun Sep 15, 2026
c17116b
fix: KV cache pinned the whole prefill sequence at batch size 1
naykun Sep 16, 2026
9a1a603
move the block-causal segmentation into the SDPA processor
yiyixuxu Sep 16, 2026
de43a12
refactor: let the processor pick the block-causal path
naykun Sep 16, 2026
b3cb5d7
fix: VAE class defaults did not describe the released model
naykun Sep 16, 2026
f374bef
address review feedback: copies, pipeline, docs
naykun Sep 16, 2026
c245e42
address review feedback: validate before doing work
naykun Sep 16, 2026
711fe5e
fix: num_images_per_prompt > 1 raised in the pipeline
naykun Sep 16, 2026
e18afdc
Use the recommended sampling defaults: 40 steps, no guidance
naykun Sep 16, 2026
d6dfd67
docs: complete the forward and __call__ docstrings
naykun Sep 17, 2026
b027736
add pipeline tests for qwenimage 2.1 (#6)
sayakpaul Sep 17, 2026
3e52c4f
add more copied froms (#5)
sayakpaul Sep 17, 2026
9827e15
docs: describe multiple condition images, and inline the flex warning
naykun Sep 17, 2026
450abe2
fix-copies: drop the AvgDown3D marker
naykun Sep 17, 2026
0a33bcb
fix: feed the transformer the pre-norm text hidden state
naykun Sep 17, 2026
21b3a41
fix: match the checkpoint's text conditioning, and repair the unexerc…
naykun Sep 17, 2026
24c36a6
fix callback test (#2)
sayakpaul Sep 18, 2026
8d3c30b
Add a TODO at the text encoder hook
naykun Sep 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/source/en/_toctree.yml
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,8 @@
title: PixArtTransformer2DModel
- local: api/models/prior_transformer
title: PriorTransformer
- local: api/models/qwenimage21_transformer2d
title: QwenImage21Transformer2DModel
- local: api/models/qwenimage_transformer2d
title: QwenImageTransformer2DModel
- local: api/models/sana_transformer2d
Expand Down Expand Up @@ -489,6 +491,8 @@
title: AutoencoderKLMochi
- local: api/models/autoencoderkl_qwenimage
title: AutoencoderKLQwenImage
- local: api/models/autoencoderkl_qwenimage21
title: AutoencoderKLQwenImage21
- local: api/models/autoencoder_kl_wan
title: AutoencoderKLWan
- local: api/models/autoencoder_rae
Expand Down Expand Up @@ -637,6 +641,8 @@
title: PRX
- local: api/pipelines/prx_pixel
title: PRX Pixel
- local: api/pipelines/qwenimage21
title: Qwen-Image 2.1
- local: api/pipelines/qwenimage
title: QwenImage
- local: api/pipelines/sana
Expand Down
37 changes: 37 additions & 0 deletions docs/source/en/api/models/autoencoderkl_qwenimage21.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<!-- Copyright 2026 The HuggingFace Team. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License. -->

# AutoencoderKLQwenImage21

The 64-channel variational auto-encoder used by Qwen-Image 2.1. It compresses 16x spatially, and its per-channel
`latents_mean` / `latents_std` are part of the config rather than a single scaling factor.

```python
import torch
from diffusers import AutoencoderKLQwenImage21

vae = AutoencoderKLQwenImage21.from_pretrained("Qwen/Qwen-Image-2.1", subfolder="vae", dtype=torch.bfloat16)
```

## AutoencoderKLQwenImage21

[[autodoc]] AutoencoderKLQwenImage21
- decode
- encode
- all

## AutoencoderKLOutput

[[autodoc]] models.autoencoders.autoencoder_kl.AutoencoderKLOutput

## DecoderOutput

[[autodoc]] models.autoencoders.vae.DecoderOutput
45 changes: 45 additions & 0 deletions docs/source/en/api/models/qwenimage21_transformer2d.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!-- Copyright 2026 The HuggingFace Team. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License. -->

# QwenImage21Transformer2DModel

The single-stream transformer used by Qwen-Image 2.1. Text and image latents share one sequence, and a single shared
`modulation` projection feeds every block.

Two behaviours distinguish 2.1 from earlier QwenImage transformers:

- **Block-causal attention** — attention follows `(q_idx >= kv_idx) or same_image_block`, so the joint sequence is
causal while each image block stays internally bidirectional. `QwenImage21AttnProcessor` implements it as one
attention call per prefix segment and is the default. `QwenImage21FlexAttnProcessor` implements it as a single
`flex_attention` call driven by a `BlockMask`, which is faster once the model is compiled. Both produce the same
results.
- `causal_condition` — text and condition-image tokens are modulated from `t = 0` rather than the sampled timestep.
Their activations are independent of the denoising step, so the keys and values of that prefix are cacheable
across steps via the `kv_cache` argument.

Load it with:

```python
import torch
from diffusers import QwenImage21Transformer2DModel

transformer = QwenImage21Transformer2DModel.from_pretrained(
"Qwen/Qwen-Image-2.1", subfolder="transformer", dtype=torch.bfloat16
)
```

## QwenImage21Transformer2DModel

[[autodoc]] QwenImage21Transformer2DModel

## Transformer2DModelOutput

[[autodoc]] models.modeling_outputs.Transformer2DModelOutput
73 changes: 73 additions & 0 deletions docs/source/en/api/pipelines/qwenimage21.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<!-- Copyright 2026 The HuggingFace Team. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License. -->

# Qwen-Image 2.1

Qwen-Image 2.1 encodes the prompt and any condition images together with a Qwen3-VL model, then denoises the target
image with a single-stream block-causal transformer. See
[`QwenImage21Transformer2DModel`](../models/qwenimage21_transformer2d) for block-causal attention, the attention
processors, and `causal_condition`.

The defaults are the values Qwen recommends: 40 steps and no guidance. Pass a `negative_prompt` together with
`true_cfg_scale > 1` to turn classifier-free guidance on, which doubles the work per step.

```python
import torch
from diffusers import QwenImage21Pipeline

pipe = QwenImage21Pipeline.from_pretrained("Qwen/Qwen-Image-2.1", dtype=torch.bfloat16).to("cuda")

# Text-to-image
image = pipe("A capybara wearing a wizard hat, oil painting").images[0]
image.save("t2i.png")

# Image-conditioned editing
edited = pipe("Move it to a snowy mountain top", image=image).images[0]
edited.save("edit.png")
```

## Multiple condition images

Pass a list to `image` and every entry becomes its own block in the joint sequence: the Qwen3-VL encoder sees them as
vision context and the VAE contributes their latent tokens. Block-causal attention keeps each block internally
bidirectional while letting later blocks and the target image attend to the earlier ones, so the order you pass them
in is the order the model reads them.

```python
edited = pipe("Put the flowers from the first image into the second scene", image=[flowers, scene]).images[0]
```

## Faster attention with flex_attention

The default `QwenImage21AttnProcessor` runs the block-causal prefill as one attention call per prefix segment. It
needs no compilation and works on any PyTorch build. `QwenImage21FlexAttnProcessor` expresses the same mask as a
single `flex_attention` call, which is faster once the model is **_compiled_**.

> [!TIP]
> Compile the model when you switch to the flex processor. An uncompiled `flex_attention` materializes the full
> attention score matrix in fp32, which is much slower and runs out of memory at high resolution.

```python
from diffusers.models.transformers.transformer_qwenimage21 import QwenImage21FlexAttnProcessor

pipe.transformer.set_attn_processor(QwenImage21FlexAttnProcessor())
pipe.transformer.compile()
```

## QwenImage21Pipeline

[[autodoc]] QwenImage21Pipeline
- all
- __call__

## QwenImagePipelineOutput

[[autodoc]] pipelines.qwenimage.pipeline_output.QwenImagePipelineOutput
6 changes: 6 additions & 0 deletions src/diffusers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@
"AutoencoderKLMiniMaxH3Audio",
"AutoencoderKLMochi",
"AutoencoderKLQwenImage",
"AutoencoderKLQwenImage21",
"AutoencoderKLTemporalDecoder",
"AutoencoderKLWan",
"AutoencoderOobleck",
Expand Down Expand Up @@ -332,6 +333,7 @@
"PixArtTransformer2DModel",
"PriorTransformer",
"PRXTransformer2DModel",
"QwenImage21Transformer2DModel",
"QwenImageControlNetModel",
"QwenImageMultiControlNetModel",
"QwenImageTransformer2DModel",
Expand Down Expand Up @@ -764,6 +766,7 @@
"PixArtSigmaPipeline",
"PRXPipeline",
"PRXPixelPipeline",
"QwenImage21Pipeline",
"QwenImageControlNetInpaintPipeline",
"QwenImageControlNetPipeline",
"QwenImageEditInpaintPipeline",
Expand Down Expand Up @@ -1130,6 +1133,7 @@
AutoencoderKLMiniMaxH3Audio,
AutoencoderKLMochi,
AutoencoderKLQwenImage,
AutoencoderKLQwenImage21,
AutoencoderKLTemporalDecoder,
AutoencoderKLWan,
AutoencoderOobleck,
Expand Down Expand Up @@ -1209,6 +1213,7 @@
PixArtTransformer2DModel,
PriorTransformer,
PRXTransformer2DModel,
QwenImage21Transformer2DModel,
QwenImageControlNetModel,
QwenImageMultiControlNetModel,
QwenImageTransformer2DModel,
Expand Down Expand Up @@ -1616,6 +1621,7 @@
PixArtSigmaPipeline,
PRXPipeline,
PRXPixelPipeline,
QwenImage21Pipeline,
QwenImageControlNetInpaintPipeline,
QwenImageControlNetPipeline,
QwenImageEditInpaintPipeline,
Expand Down
4 changes: 4 additions & 0 deletions src/diffusers/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
_import_structure["autoencoders.autoencoder_kl_minimax_h3_audio"] = ["AutoencoderKLMiniMaxH3Audio"]
_import_structure["autoencoders.autoencoder_kl_mochi"] = ["AutoencoderKLMochi"]
_import_structure["autoencoders.autoencoder_kl_qwenimage"] = ["AutoencoderKLQwenImage"]
_import_structure["autoencoders.autoencoder_kl_qwenimage21"] = ["AutoencoderKLQwenImage21"]
_import_structure["autoencoders.autoencoder_kl_temporal_decoder"] = ["AutoencoderKLTemporalDecoder"]
_import_structure["autoencoders.autoencoder_kl_wan"] = ["AutoencoderKLWan"]
_import_structure["autoencoders.autoencoder_longcat_audio_dit"] = ["LongCatAudioDiTVae"]
Expand Down Expand Up @@ -144,6 +145,7 @@
_import_structure["transformers.transformer_ovis_image"] = ["OvisImageTransformer2DModel"]
_import_structure["transformers.transformer_prx"] = ["PRXTransformer2DModel"]
_import_structure["transformers.transformer_qwenimage"] = ["QwenImageTransformer2DModel"]
_import_structure["transformers.transformer_qwenimage21"] = ["QwenImage21Transformer2DModel"]
_import_structure["transformers.transformer_sana_video"] = ["SanaVideoTransformer3DModel"]
_import_structure["transformers.transformer_sd3"] = ["SD3Transformer2DModel"]
_import_structure["transformers.transformer_skyreels_v2"] = ["SkyReelsV2Transformer3DModel"]
Expand Down Expand Up @@ -195,6 +197,7 @@
AutoencoderKLMiniMaxH3Audio,
AutoencoderKLMochi,
AutoencoderKLQwenImage,
AutoencoderKLQwenImage21,
AutoencoderKLTemporalDecoder,
AutoencoderKLWan,
AutoencoderOobleck,
Expand Down Expand Up @@ -287,6 +290,7 @@
PixArtTransformer2DModel,
PriorTransformer,
PRXTransformer2DModel,
QwenImage21Transformer2DModel,
QwenImageTransformer2DModel,
SanaTransformer2DModel,
SanaVideoTransformer3DModel,
Expand Down
1 change: 1 addition & 0 deletions src/diffusers/models/autoencoders/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from .autoencoder_kl_minimax_h3_audio import AutoencoderKLMiniMaxH3Audio
from .autoencoder_kl_mochi import AutoencoderKLMochi
from .autoencoder_kl_qwenimage import AutoencoderKLQwenImage
from .autoencoder_kl_qwenimage21 import AutoencoderKLQwenImage21
from .autoencoder_kl_temporal_decoder import AutoencoderKLTemporalDecoder
from .autoencoder_kl_wan import AutoencoderKLWan
from .autoencoder_longcat_audio_dit import LongCatAudioDiTVae
Expand Down
Loading
Loading