1
0
mirror of https://github.com/huggingface/diffusers.git synced 2026-10-23 09:09:01 +03:00
Files
diffusers/docs/source/en/modular_diffusers/loop_sequential_pipeline_blocks.md
YiYi Xu b800f828f3 [docs] FLUX.3: modular-only notes, lazy loading, callbacks (#15069)
* [docs] FLUX.3: modular-only notes, lazy loading, callbacks

Add a tips callout on what differs from DiffusionPipeline (lazy loading,
outputs, guider, offloading, editing blocks), move the prompting advice
into the upsampling section with the pop-the-step example, and add a
per-step callbacks section with a block that takes callback_on_step_end.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* [docs] FLUX.3: callback block walks all denoising loops

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* [docs] modular: add "Coming from DiffusionPipeline" to the quickstart and move per-step callbacks to the callbacks guide

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* [docs] FLUX.3: show the upsampled caption; clarify the index note in the quickstart

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* [docs] modular: make "Coming from DiffusionPipeline" its own page

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-16 13:34:11 -10:00

4.4 KiB

LoopSequentialPipelineBlocks

[~modular_pipelines.LoopSequentialPipelineBlocks] are a multi-block type that composes other [~modular_pipelines.ModularPipelineBlocks] together in a loop. Data flows circularly, using inputs and intermediate_outputs, and each block is run iteratively. This is typically used to create a denoising loop which is iterative by default.

This guide shows you how to create [~modular_pipelines.LoopSequentialPipelineBlocks].

Loop wrapper

[~modular_pipelines.LoopSequentialPipelineBlocks], is also known as the loop wrapper because it defines the loop structure, iteration variables, and configuration. Within the loop wrapper, you need the following variables.

  • loop_inputs are user provided values and equivalent to [~modular_pipelines.ModularPipelineBlocks.inputs].
  • loop_intermediate_outputs are new intermediate variables created by the block and added to the [~modular_pipelines.PipelineState]. It is equivalent to [~modular_pipelines.ModularPipelineBlocks.intermediate_outputs].
  • __call__ method defines the loop structure and iteration logic.
import torch
from diffusers.modular_pipelines import LoopSequentialPipelineBlocks, ModularPipelineBlocks, InputParam, OutputParam

class LoopWrapper(LoopSequentialPipelineBlocks):
    model_name = "test"
    @property
    def description(self):
        return "I'm a loop!!"
    @property
    def loop_inputs(self):
        return [InputParam(name="num_steps")]
    @torch.no_grad()
    def __call__(self, components, state):
        block_state = self.get_block_state(state)
        # Loop structure - can be customized to your needs
        for i in range(block_state.num_steps):
            # loop_step executes all registered blocks in sequence
            components, block_state = self.loop_step(components, block_state, i=i)
        self.set_block_state(state, block_state)
        return components, state

The loop wrapper can pass additional arguments, like current iteration index, to the loop blocks.

Loop blocks

A loop block is a [~modular_pipelines.ModularPipelineBlocks], but the __call__ method behaves differently.

  • It receives the iteration variable from the loop wrapper.
  • It works directly with the [~modular_pipelines.BlockState] instead of the [~modular_pipelines.PipelineState].
  • It doesn't require retrieving or updating the [~modular_pipelines.BlockState].

Loop blocks share the same [~modular_pipelines.BlockState] to allow values to accumulate and change for each iteration in the loop.

class LoopBlock(ModularPipelineBlocks):
    model_name = "test"
    @property
    def inputs(self):
        return [InputParam(name="x")]
    @property
    def intermediate_outputs(self):
        # outputs produced by this block
        return [OutputParam(name="x")]
    @property
    def description(self):
        return "I'm a block used inside the `LoopWrapper` class"
    def __call__(self, components, block_state, i: int):
        block_state.x += 1
        return components, block_state

LoopSequentialPipelineBlocks

Use the [~modular_pipelines.LoopSequentialPipelineBlocks.from_blocks_dict] method to add the loop block to the loop wrapper to create [~modular_pipelines.LoopSequentialPipelineBlocks].

loop = LoopWrapper.from_blocks_dict({"block1": LoopBlock})

Add more loop blocks to run within each iteration with [~modular_pipelines.LoopSequentialPipelineBlocks.from_blocks_dict]. This allows you to modify the blocks without changing the loop logic itself.

loop = LoopWrapper.from_blocks_dict({"block1": LoopBlock(), "block2": LoopBlock})

To add a loop block to an existing pipeline's denoising loop, for example one that runs a callback_on_step_end function at every step, see Modular pipelines in the callbacks guide.