huggingface / huggingface/diffusers

`pipeline_infrastructure` model/pipeline review

Aberta
#13,653 0 comentários 0 reações 0 responsáveis Ver no GitHub
Linguagem predominante
Python
Estrelas
34.5k
Forks
7.3k
Merge médio
3d 3h
PRs com merge (30d)
91

Descrição

# `pipeline_infrastructure` model/pipeline review

Commit tested: `0f1abc4ae8b0eb2a3b40e82a310507281144c423`

Review performed against the repository review rules.

Files reviewed: `src/diffusers/pipelines/__init__.py`, `src/diffusers/pipelines/auto_pipeline.py`, `src/diffusers/pipelines/free_init_utils.py`, `src/diffusers/pipelines/free_noise_utils.py`, `src/diffusers/pipelines/onnx_utils.py`, `src/diffusers/pipelines/pipeline_flax_utils.py`, `src/diffusers/pipelines/pipeline_loading_utils.py`, `src/diffusers/pipelines/pipeline_utils.py`, `src/diffusers/pipelines/transformers_loading_utils.py`.

Duplicate search: checked GitHub Issues and PRs for `pipeline_infrastructure`, FreeNoise failures, `OnnxRuntimeModel provider_options`, AutoPipeline ControlNetUnion, and DDUF tokenizer loading. No duplicates found. Related but not duplicate: https://github.com/huggingface/diffusers/pull/10661 added `provider_options`.

## Issue 1: Direct ONNX model loading requires an optional kwarg

Affected code:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/onnx_utils.py#L179-L205

Problem:
`OnnxRuntimeModel._from_pretrained()` calls `kwargs.pop("provider_options")` in both local and Hub paths. Pipeline loading passes `provider_options=None`, but direct public `OnnxRuntimeModel.from_pretrained(...)` does not, so it raises `KeyError` before loading the model.

Impact:
The exported ONNX helper cannot be used directly with default arguments.

Reproduction:
```python
import tempfile
from diffusers.pipelines.onnx_utils import OnnxRuntimeModel

with tempfile.TemporaryDirectory() as d:
OnnxRuntimeModel.from_pretrained(d)
# KeyError: 'provider_options'
```

Relevant precedent:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/onnx_utils.py#L78-L81

Suggested fix:
```python
provider_options = kwargs.pop("provider_options", None)
model = OnnxRuntimeModel.load_model(..., provider_options=provider_options)
```

## Issue 2: `enable_free_noise(context_length=None)` crashes before applying the documented default

Affected code:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/free_noise_utils.py#L446-L506

Problem:
The docstring says `context_length=None` should use `motion_adapter.config.motion_max_seq_length`, but the code compares `None > int` before resolving the default.

Impact:
The documented API path is unusable.

Reproduction:
```python
from types import SimpleNamespace
from diffusers.pipelines.free_noise_utils import AnimateDiffFreeNoiseMixin

class Pipe(AnimateDiffFreeNoiseMixin):
pass

pipe = Pipe()
pipe.motion_adapter = SimpleNamespace(config=SimpleNamespace(motion_max_seq_length=16))
pipe.enable_free_noise(context_length=None)
# TypeError: '>' not supported between instances of 'NoneType' and 'int'
```

Relevant precedent:
The same function already resolves the fallback later:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/free_noise_utils.py#L504-L504

Suggested fix:
```python
context_length = context_length or self.motion_adapter.config.motion_max_seq_length
if context_length > self.motion_adapter.config.motion_max_seq_length:
logger.warning(...)
self._free_noise_context_length = context_length
```

## Issue 3: FreeNoise latent preparation ignores `context_length` for `repeat_context`

Affected code:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/free_noise_utils.py#L374-L426

Problem:
`context_num_frames` compares `self._free_noise_context_length` to the string `"repeat_context"`. Since the left side is an integer, `repeat_context` starts from `num_frames` random frames, repeats those, and slices back to `num_frames`, so no context repetition actually happens.

Impact:
The documented `noise_type="repeat_context"` behavior is silently wrong.

Reproduction:
```python
import torch
from diffusers.pipelines.free_noise_utils import AnimateDiffFreeNoiseMixin

class Pipe(AnimateDiffFreeNoiseMixin):
pass

pipe = Pipe()
pipe.vae_scale_factor = 1
pipe._free_noise_context_length = 4
pipe._free_noise_context_stride = 4
pipe._free_noise_noise_type = "repeat_context"

latents = pipe._prepare_latents_free_noise(
batch_size=1, num_channels_latents=1, num_frames=10, height=2, width=2,
dtype=torch.float32, device=torch.device("cpu"),
generator=torch.Generator(device="cpu").manual_seed(0),
)
print((latents[:, :, 0] - latents[:, :, 4]).abs().max().item())
# non-zero; expected 0 for repeated context
```

Relevant precedent:
The docstring defines `repeat_context` as repeating a fixed context window:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/free_noise_utils.py#L479-L488

Suggested fix:
```python
context_num_frames = (
num_frames if self._free_noise_noise_type == "random" else self._free_noise_context_length
)
```

## Issue 4: FreeNoise list generators fail in `shuffle_context`

Affected code:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/free_noise_utils.py#L368-L410

Problem:
The helper validates list generator length, and `randn_tensor` supports list generators, but `torch.randperm(..., generator=generator)` receives the full list.

Impact:
Batched deterministic generation with per-sample generators fails only when FreeNoise uses `shuffle_context`.

Reproduction:
```python
import torch
from diffusers.pipelines.free_noise_utils import AnimateDiffFreeNoiseMixin

class Pipe(AnimateDiffFreeNoiseMixin):
pass

pipe = Pipe()
pipe.vae_scale_factor = 1
pipe._free_noise_context_length = 4
pipe._free_noise_context_stride = 2
pipe._free_noise_noise_type = "shuffle_context"

pipe._prepare_latents_free_noise(
1, 1, 8, 2, 2, torch.float32, torch.device("cpu"),
generator=[torch.Generator(device="cpu").manual_seed(0)],
)
# TypeError: randperm() ... generator=list
```

Relevant precedent:
`randn_tensor` handles list generators for batched reproducibility.

Suggested fix:
```python
perm_generator = generator[0] if isinstance(generator, list) else generator
shuffled_indices = indices[torch.randperm(window_length, generator=perm_generator)]
```

## Issue 5: `disable_free_noise()` always visits `mid_block`

Affected code:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/free_noise_utils.py#L519-L530

Problem:
`disable_free_noise()` computes a block list that skips `mid_block` when it lacks `motion_modules`, then immediately overwrites it with a list that always includes `mid_block`.

Impact:
Pipelines whose mid block has no motion modules fail while disabling FreeNoise.

Reproduction:
```python
from types import SimpleNamespace
from diffusers.pipelines.free_noise_utils import AnimateDiffFreeNoiseMixin

class Pipe(AnimateDiffFreeNoiseMixin):
pass

pipe = Pipe()
pipe.unet = SimpleNamespace(down_blocks=[], mid_block=object(), up_blocks=[])
pipe.disable_free_noise()
# AttributeError: 'object' object has no attribute 'motion_modules'
```

Relevant precedent:
`enable_free_noise()` already uses the correct conditional block selection:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/free_noise_utils.py#L510-L514

Suggested fix:
```python
# remove the unconditional reassignment at line 528
```

## Issue 6: AutoPipeline `from_pipe()` drops ControlNet-Union routing

Affected code:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/auto_pipeline.py#L551-L558
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/auto_pipeline.py#L854-L864
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/auto_pipeline.py#L1157-L1166

Problem:
`from_pretrained()` special-cases `ControlNetUnionModel`, but all three `from_pipe()` methods only insert `ControlNet`, selecting non-union SDXL ControlNet pipelines.

Impact:
Converting an existing SDXL pipeline with a `ControlNetUnionModel` returns the wrong pipeline class.

Reproduction:
```python
from diffusers import AutoPipelineForText2Image, ControlNetUnionModel

pipe = AutoPipelineForText2Image.from_pretrained("hf-internal-testing/tiny-stable-diffusion-xl-pipe")
controlnet = ControlNetUnionModel(
in_channels=4, conditioning_channels=3, down_block_types=("DownBlock2D",),
block_out_channels=(4,), layers_per_block=1, norm_num_groups=1,
cross_attention_dim=32, attention_head_dim=1,
conditioning_embedding_out_channels=(4,), addition_time_embed_dim=4,
num_control_type=1, num_trans_channel=4, num_trans_head=1,
num_trans_layer=1, num_proj_channel=4,
)
converted = AutoPipelineForText2Image.from_pipe(pipe, controlnet=controlnet)
print(converted.__class__.__name__)
# StableDiffusionXLControlNetPipeline, expected StableDiffusionXLControlNetUnionPipeline
```

Relevant precedent:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/auto_pipeline.py#L501-L504

Suggested fix:
Mirror the `from_pretrained()` union branch in every `from_pipe()` controlnet branch, including list/tuple union models.

## Issue 7: DDUF tokenizer loading raises `UnboundLocalError` for missing tokenizer entries

Affected code:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/transformers_loading_utils.py#L36-L56

Problem:
`tmp_entry_path` is only assigned inside the matching-entry loop, then used unconditionally. Missing tokenizer entries therefore produce `UnboundLocalError` instead of a useful loading error.

Impact:
Invalid or incomplete DDUF archives fail with an internal Python error, unlike transformer model DDUF loading which raises clear `EnvironmentError`s.

Reproduction:
```python
from diffusers.pipelines.transformers_loading_utils import _load_tokenizer_from_dduf

class DummyTokenizer:
@classmethod
def from_pretrained(cls, path, **kwargs):
return path

_load_tokenizer_from_dduf(DummyTokenizer, "tokenizer", {})
# UnboundLocalError: cannot access local variable 'tmp_entry_path'
```

Relevant precedent:
https://github.com/huggingface/diffusers/blob/0f1abc4ae8b0eb2a3b40e82a310507281144c423/src/diffusers/pipelines/transformers_loading_utils.py#L68-L83

Suggested fix:
```python
matched = False
with tempfile.TemporaryDirectory() as tmp_dir:
for entry_name, entry in dduf_entries.items():
if entry_name.startswith(name + "/"):
matched = True
...
if not matched:
raise EnvironmentError(f"Could not find tokenizer files for component {name} in DDUF file.")
return cls.from_pretrained(os.path.join(tmp_dir, name), **kwargs)
```

## Test coverage status

Fast tests exist for AutoPipeline, pipeline utilities, ONNX pipelines, and FreeNoise basics. Slow tests exist for AutoPipeline, ONNX Stable Diffusion, and AnimateDiff, but the failing paths above are not covered: FreeNoise `context_length=None`, `repeat_context`, list generators, mid-block-less disable, direct `OnnxRuntimeModel.from_pretrained()`, DDUF tokenizer missing-entry errors, and AutoPipeline ControlNet-Union `from_pipe()`.

Guia de contribuição

Abrir o guia de contribuição

Direção de pesquisa

Start with the affected entry points in src/diffusers/pipelines/onnx_utils.py, free_noise_utils.py, auto_pipeline.py, and transformers_loading_utils.py, then inspect the existing fast tests for these components. Reproduce each listed failure before changing behavior. Done means all seven paths work as documented and regression coverage exists for the missing cases, including direct ONNX loading, FreeNoise modes, ControlNet-Union conversion, and missing DDUF tokenizers.

Escrita pelo modelo de indexação a partir do texto da issue.

Avaliação

Stack de tecnologia
python, pytorch
Domínio
machine-learning, testing-qa
Tipo de issue
Bug
Dificuldade
5/5
Tempo estimado
Mais de uma semana
Status de atividade
Pouca atividade
Clareza
Claramente especificada
Facilidade para iniciantes
25/100

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.