huggingface / huggingface/diffusers

longcat_image model/pipeline review

Offen
#13,636 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
Vorherrschende Sprache
Python
Sterne
34.5k
Forks
7.3k
Ø Merge
3 T. 3 Std.
Gemergte PRs (30 T.)
91

Beschreibung

# `longcat_image` model/pipeline review

Commit tested: `0f1abc4ae8b0eb2a3b40e82a310507281144c423`

Review performed against the repository review rules.

Duplicate search checked GitHub issues/PRs for `longcat_image`, `LongCatImagePipeline`, `LongCatImageEditPipeline`, `LongCatImageTransformer2DModel`, `prompt_embeds`, `negative_prompt`, `randn_tensor`, `AutoPipeline`, and LoRA failures. Existing overlaps are noted below.

## Issue 1: `prompt_embeds`-only calls crash in both pipelines

Affected code:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/longcat_image/pipeline_longcat_image.py#L341-L345
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/longcat_image/pipeline_longcat_image_edit.py#L351-L355

Problem:
`check_inputs()` allows `prompt=None` with `prompt_embeds`, but `encode_prompt()` immediately does `len(prompt)`. The text-to-image pipeline also calls prompt rewriting before encoding unless users manually set `enable_prompt_rewrite=False`.

Impact:
Users cannot use precomputed prompt embeddings, despite the public `prompt_embeds` parameters.

Reproduction:
```python
import torch
from diffusers import LongCatImagePipeline

pipe = LongCatImagePipeline.__new__(LongCatImagePipeline)
pipe.encode_prompt(prompt=None, prompt_embeds=torch.zeros(1, 2, 8))
# TypeError: object of type 'NoneType' has no len()
```

Relevant precedent:
`QwenImagePipeline.encode_prompt` derives batch size from `prompt_embeds` when prompt is absent.

Suggested fix:
```python
if prompt_embeds is not None and prompt is None:
batch_size = prompt_embeds.shape[0]
else:
prompt = [prompt] if isinstance(prompt, str) else prompt
batch_size = len(prompt)
```

## Issue 2: Batched prompts break CFG when `negative_prompt` is omitted

Affected code:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/longcat_image/pipeline_longcat_image.py#L542-L551

Problem:
For `prompt=["a", "b"]`, the pipeline sets `negative_prompt = ""`, so unconditional embeddings have batch size 1 while latents and conditional embeddings have batch size 2.

Impact:
Batched text-to-image generation with default CFG can fail or produce invalid conditioning.

Reproduction:
```python
prompt = ["a", "b"]
negative_prompt = ""
print(len(prompt), 1 if isinstance(negative_prompt, str) else len(negative_prompt))
# 2 1
```

Relevant precedent:
Flux/Qwen-style pipelines normalize a scalar negative prompt to match prompt batch size.

Suggested fix:
```python
if negative_prompt is None:
negative_prompt = [""] * batch_size if batch_size > 1 else ""
elif isinstance(negative_prompt, str) and batch_size > 1:
negative_prompt = [negative_prompt] * batch_size
```

## Issue 3: Text-to-image noise is generated in default float32, then cast

Affected code:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/longcat_image/pipeline_longcat_image.py#L419-L420

Problem:
`randn_tensor()` is called without `dtype=dtype`, then cast afterward. The edit pipeline already passes `dtype`.

Impact:
bf16/fp16 inference gets different initial noise than a direct low-precision draw, which can hurt parity and wastes memory.

Reproduction:
```python
import torch
import diffusers.pipelines.longcat_image.pipeline_longcat_image as mod
from diffusers import LongCatImagePipeline

seen = {}
def spy(shape, **kwargs):
seen["dtype"] = kwargs.get("dtype")
return torch.zeros(shape)

mod.randn_tensor = spy
pipe = LongCatImagePipeline.__new__(LongCatImagePipeline)
pipe.vae_scale_factor = 8
pipe.tokenizer_max_length = 512
pipe.prepare_latents(1, 16, 32, 32, torch.bfloat16, "cpu", None)
print(seen["dtype"])
# None
```

Relevant precedent:
`FluxPipeline.prepare_latents` passes `dtype=dtype`.

Suggested fix:
```python
latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
```

## Issue 4: Edit prompt truncation warning crashes

Affected code:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/longcat_image/pipeline_longcat_image_edit.py#L284-L289

Problem:
The warning formats `len(len(all_tokens))`, which raises when the truncation branch is reached.

Impact:
Long edit prompts crash instead of being truncated with a warning.

Reproduction:
```python
all_tokens = [1] * 513
print(len(len(all_tokens)))
# TypeError: object of type 'int' has no len()
```

Relevant precedent:
The sibling text-to-image pipeline uses `len(all_tokens)` correctly. Duplicate already exists: https://github.com/huggingface/diffusers/pull/13526

Suggested fix:
```python
f" {self.tokenizer_max_length} input token nums : {len(all_tokens)}"
```

## Issue 5: Edit image position IDs are forced to float64

Affected code:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/longcat_image/pipeline_longcat_image_edit.py#L447-L453

Problem:
`image_latents_ids` is created with `dtype=torch.float64`. The text pipeline does not do this, and the model immediately casts position IDs to float32 internally.

Impact:
This can fail or fall back on backends with poor/no float64 support, including MPS/NPU, and creates unnecessary dtype divergence.

Reproduction:
```python
import torch
from diffusers.pipelines.longcat_image.pipeline_longcat_image_edit import prepare_pos_ids

ids = prepare_pos_ids(modality_id=2, type="image", height=2, width=2).to("cpu", dtype=torch.float64)
print(ids.dtype)
# torch.float64
```

Relevant precedent:
`LongCatImagePipeline.prepare_latents` keeps position IDs at the default dtype.

Suggested fix:
```python
).to(device)
```

## Issue 6: LoRA/joint attention integration is incomplete

Affected code:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/longcat_image/pipeline_longcat_image.py#L205-L213
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/models/transformers/transformer_longcat_image.py#L466-L475

Problem:
The docs show a LoRA badge, but both pipelines lack a LoRA loader mixin, and the transformer forward does not accept or forward `joint_attention_kwargs` even though blocks/processors support it.

Impact:
`pipe.load_lora_weights()` is unavailable and runtime attention kwargs cannot reach processors.

Reproduction:
```python
from diffusers import LongCatImagePipeline, LongCatImageTransformer2DModel
import inspect

print(hasattr(LongCatImagePipeline, "load_lora_weights"))
print("joint_attention_kwargs" in inspect.signature(LongCatImageTransformer2DModel.forward).parameters)
# False
# False
```

Relevant precedent:
`QwenImagePipeline` inherits `QwenImageLoraLoaderMixin`; `FluxTransformer2DModel.forward` accepts `joint_attention_kwargs`. Duplicate LoRA issue/PR: https://github.com/huggingface/diffusers/issues/12859 and https://github.com/huggingface/diffusers/pull/12867

Suggested fix:
Add a LongCat-specific LoRA loader mixin, inherit it in both pipelines, add `joint_attention_kwargs` to transformer `forward`, and pass it through each block.

## Issue 7: Transformer is missing `_no_split_modules`

Affected code:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/models/transformers/transformer_longcat_image.py#L397-L410

Problem:
The model declares repeated block classes but not `_no_split_modules`.

Impact:
`device_map`/offload placement can split residual attention blocks across devices, which is both slower and riskier for correctness/memory.

Reproduction:
```python
from diffusers import LongCatImageTransformer2DModel
print(getattr(LongCatImageTransformer2DModel, "_no_split_modules", None))
# None
```

Relevant precedent:
`FluxTransformer2DModel` and `QwenImageTransformer2DModel` set `_no_split_modules`.

Suggested fix:
```python
_no_split_modules = ["LongCatImageTransformerBlock", "LongCatImageSingleTransformerBlock"]
```

## Issue 8: Pipeline latent channels are hardcoded to 16

Affected code:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/longcat_image/pipeline_longcat_image.py#L553-L564
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/longcat_image/pipeline_longcat_image_edit.py#L607-L620

Problem:
Both pipelines use `num_channels_latents = 16` instead of deriving it from `self.transformer.config.in_channels // 4`.

Impact:
Tiny/custom configs and future compatible checkpoints with different packed channel sizes fail with shape mismatches.

Reproduction:
```python
from diffusers import LongCatImageTransformer2DModel

model = LongCatImageTransformer2DModel(in_channels=8, num_layers=1, num_single_layers=1,
attention_head_dim=6, num_attention_heads=1, joint_attention_dim=8, axes_dims_rope=[2, 2, 2])
print(model.config.in_channels // 4)
# 2, but the pipelines always use 16
```

Relevant precedent:
Flux and QwenImage derive latent channels from `self.transformer.config.in_channels // 4`.

Suggested fix:
```python
num_channels_latents = self.transformer.config.in_channels // 4
```

## Issue 9: AutoPipeline mappings omit LongCat image pipelines

Affected code:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/auto_pipeline.py#L139-L191
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/auto_pipeline.py#L195-L222

Problem:
`LongCatImagePipeline` and `LongCatImageEditPipeline` are public top-level imports but are absent from `AUTO_TEXT2IMAGE_PIPELINES_MAPPING` and `AUTO_IMAGE2IMAGE_PIPELINES_MAPPING`.

Impact:
`AutoPipelineForText2Image`/`AutoPipelineForImage2Image` cannot resolve LongCat checkpoints by class name.

Reproduction:
```python
from diffusers import LongCatImagePipeline
from diffusers.pipelines.auto_pipeline import AUTO_TEXT2IMAGE_PIPELINES_MAPPING

print(LongCatImagePipeline in AUTO_TEXT2IMAGE_PIPELINES_MAPPING.values())
# False
```

Relevant precedent:
Flux and QwenImage families are registered in the corresponding AutoPipeline maps.

Suggested fix:
Import LongCat pipelines in `auto_pipeline.py` and add:
```python
("longcat-image", LongCatImagePipeline)
("longcat-image-edit", LongCatImageEditPipeline)
```

## Issue 10: No LongCat image fast or slow tests exist

Affected code:
`tests/` has no `longcat_image` model or pipeline test file at this commit.

Problem:
There are no fast tests for top-level imports, save/load, tiny transformer forward, prompt-embed paths, CFG batching, dtype handling, or pipeline serialization. There are also no slow tests for the real LongCat-Image or LongCat-Image-Edit checkpoints.

Impact:
The current regressions are not covered, and the required slow coverage is missing.

Reproduction:
```python
from pathlib import Path
print(list(Path("tests").rglob("*longcat_image*")))
# []
```

Relevant precedent:
`tests/pipelines/longcat_audio_dit/test_longcat_audio_dit.py` includes fast and slow LongCat audio coverage.

Suggested fix:
Add `tests/models/transformers/test_models_transformer_longcat_image.py` and `tests/pipelines/longcat_image/test_longcat_image.py`, including slow tests guarded by env/model availability for both text-to-image and edit checkpoints.

Beitragsleitfaden

Beitragsleitfaden öffnen

Rechercherichtung

Beginne mit den betroffenen Dateien in src/diffusers/pipelines/longcat_image/, src/diffusers/models/transformers/transformer_longcat_image.py und src/diffusers/pipelines/auto_pipeline.py und vergleiche die zitierten Flux- und Qwen-Implementierungen. Füge die fehlende Abdeckung für schnelle und langsame Tests unter tests/models/transformers/ und tests/pipelines/longcat_image/ hinzu; als erledigt gilt die Aufgabe, wenn die aufgeführten Lücken bei Prompt, CFG, dtype, LoRA, Geräteplatzierung, Kanälen, AutoPipeline und Tests ohne Regressionen abgedeckt sind.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
python, pytorch
Bereich
machine-learning, testing-qa
Issue-Typ
Bug
Schwierigkeit
5/5
Geschätzter Aufwand
Über eine Woche
Aktivitätsstatus
Ruhig
Klarheit
Klar beschrieben
Anfängerfreundlichkeit
35/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.