Skip to content

[modular diffusers] more refactor #11235

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 56 commits into from
Jun 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
ee84283
add componentspec and configspec
yiyixuxu Feb 27, 2025
bf99ab2
up
yiyixuxu Apr 9, 2025
9ad1470
up
yiyixuxu Apr 11, 2025
d143851
move methods to blocks
yiyixuxu Apr 12, 2025
b863bdd
Modular Diffusers Guiders (#11311)
a-r-r-o-w Apr 25, 2025
6d5beef
[modular diffusers] introducing ModularLoader (#11462)
yiyixuxu Apr 30, 2025
ce642e9
Merge branch 'modular-diffusers' into modular-refactor
yiyixuxu May 1, 2025
c8b5d56
make loader optional
yiyixuxu May 1, 2025
7b86fce
remove lora step and ip-adapter step -> no longer needed
yiyixuxu May 2, 2025
7ca860c
rename pipeline -> components, data -> block_state
yiyixuxu May 2, 2025
efd70b7
seperate controlnet step into input + denoise
yiyixuxu May 3, 2025
43ac1ff
refactor controlnet union
yiyixuxu May 4, 2025
dc4dbfe
reefactor pipeline/block states so that it can dynamically accept kwargs
yiyixuxu May 6, 2025
f552773
remove controlnet union denoise step, refactor & reuse controlnet den…
yiyixuxu May 6, 2025
16b6583
allow input_fields as input & update message
yiyixuxu May 8, 2025
d89631f
update input formating, consider kwarggs_type inputs with no name, e/…
yiyixuxu May 8, 2025
0f0618f
refactor the denoiseestep using LoopSequential! also add a new file f…
yiyixuxu May 8, 2025
c677d52
change warning to debug
yiyixuxu May 9, 2025
2b361a2
fix get_execusion blocks with loopsequential
yiyixuxu May 9, 2025
2017ae5
fix auto denoise so all tests pass
yiyixuxu May 9, 2025
cf01aae
update imports on guiders
yiyixuxu May 10, 2025
462429b
remove modular reelated change from pipelines folder
yiyixuxu May 10, 2025
0acb5e1
made a modular_pipelines folder!
yiyixuxu May 10, 2025
153ae34
update __init__
yiyixuxu May 10, 2025
796453c
add notes
yiyixuxu May 11, 2025
144eae4
add block state will also make sure modifed intermediates_inputs will…
yiyixuxu May 11, 2025
522e827
move block mappings to its own file
yiyixuxu May 11, 2025
5cde77f
make inputs truly immutable, remove the output logic in sequential pi…
yiyixuxu May 12, 2025
58358c2
decode block, if skip decoding do not need to update latent
yiyixuxu May 12, 2025
506a8ea
fix imports
yiyixuxu May 13, 2025
e2491af
fix import
yiyixuxu May 13, 2025
a0deefb
fix more
yiyixuxu May 13, 2025
a7fb2d2
remove the output step
yiyixuxu May 13, 2025
8ad14a5
make generator intermediates (it is mutable)
yiyixuxu May 13, 2025
96ce674
after_denoise -> decoders
yiyixuxu May 14, 2025
27c1158
add a to-do for guider cconfig mixin
yiyixuxu May 18, 2025
d0fbf74
refactor component spec: replace create/create_from_pretrained/create…
yiyixuxu May 18, 2025
163341d
refactor modular loader: 1. load only load (pretrained components onl…
yiyixuxu May 18, 2025
73ab572
update components manager
yiyixuxu May 18, 2025
61dac3b
up
yiyixuxu May 19, 2025
808dff0
[WIP] Modular Diffusers support custom code/pipeline blocks (#11539)
DN6 May 20, 2025
4968edc
remove the duplicated components_manager file I forgot to deletee
yiyixuxu May 20, 2025
de6ab6b
fix import in block mapping
yiyixuxu May 20, 2025
eb94150
add a to-do for modular loader
yiyixuxu May 20, 2025
1b89ac1
prepare_latents_img2img pipeline method -> function, maybe do the sam…
yiyixuxu May 20, 2025
d136ae3
update input for loop blocks, do not need to include intermediate
yiyixuxu May 20, 2025
3471f2f
merge part1
yiyixuxu May 20, 2025
72e1b74
solve merge conflict: manually add back the remote code change to mod…
yiyixuxu May 20, 2025
29de29f
add node_utils
yiyixuxu May 21, 2025
87f63d4
modular node!
yiyixuxu May 22, 2025
f16e9c7
add
yiyixuxu Jun 10, 2025
cb6d5fe
refator based on dhruv's feedbacks
yiyixuxu Jun 18, 2025
58e9565
update doc format for kwargs_type
yiyixuxu Jun 19, 2025
de63194
up
yiyixuxu Jun 19, 2025
8423652
updatee modular_pipeline.from_pretrained, modular_repo ->pretrained_m…
yiyixuxu Jun 19, 2025
79be5a1
save_pretrained for serializing config. (#11603)
sayakpaul Jun 19, 2025
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
79 changes: 72 additions & 7 deletions src/diffusers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@

_import_structure = {
"configuration_utils": ["ConfigMixin"],
"guiders": [],
"hooks": [],
"loaders": ["FromOriginalModelMixin"],
"models": [],
"pipelines": [],
"modular_pipelines": [],
"quantizers.quantization_config": [],
"schedulers": [],
"utils": [
Expand Down Expand Up @@ -130,12 +132,26 @@
_import_structure["utils.dummy_pt_objects"] = [name for name in dir(dummy_pt_objects) if not name.startswith("_")]

else:
_import_structure["guiders"].extend(
[
"AdaptiveProjectedGuidance",
"AutoGuidance",
"ClassifierFreeGuidance",
"ClassifierFreeZeroStarGuidance",
"SkipLayerGuidance",
"SmoothedEnergyGuidance",
"TangentialClassifierFreeGuidance",
]
)
_import_structure["hooks"].extend(
[
"FasterCacheConfig",
"HookRegistry",
"PyramidAttentionBroadcastConfig",
"LayerSkipConfig",
"SmoothedEnergyGuidanceConfig",
"apply_faster_cache",
"apply_layer_skip",
"apply_pyramid_attention_broadcast",
]
)
Expand Down Expand Up @@ -239,13 +255,21 @@
"KarrasVePipeline",
"LDMPipeline",
"LDMSuperResolutionPipeline",
"ModularPipeline",
"PNDMPipeline",
"RePaintPipeline",
"ScoreSdeVePipeline",
"StableDiffusionMixin",
]
)
_import_structure["modular_pipelines"].extend(
[
"ModularLoader",
"ModularPipeline",
"ModularPipelineBlocks",
"ComponentSpec",
"ComponentsManager",
]
)
_import_structure["quantizers"] = ["DiffusersQuantizer"]
_import_structure["schedulers"].extend(
[
Expand Down Expand Up @@ -494,12 +518,10 @@
"StableDiffusionXLImg2ImgPipeline",
"StableDiffusionXLInpaintPipeline",
"StableDiffusionXLInstructPix2PixPipeline",
"StableDiffusionXLModularPipeline",
"StableDiffusionXLPAGImg2ImgPipeline",
"StableDiffusionXLPAGInpaintPipeline",
"StableDiffusionXLPAGPipeline",
"StableDiffusionXLPipeline",
"StableDiffusionXLAutoPipeline",
"StableUnCLIPImg2ImgPipeline",
"StableUnCLIPPipeline",
"StableVideoDiffusionPipeline",
Expand All @@ -526,6 +548,24 @@
]
)


try:
if not (is_torch_available() and is_transformers_available()):
raise OptionalDependencyNotAvailable()
except OptionalDependencyNotAvailable:
from .utils import dummy_torch_and_transformers_objects # noqa F403

_import_structure["utils.dummy_torch_and_transformers_objects"] = [
name for name in dir(dummy_torch_and_transformers_objects) if not name.startswith("_")
]

else:
_import_structure["modular_pipelines"].extend(
[
"StableDiffusionXLAutoPipeline",
"StableDiffusionXLModularLoader",
]
)
try:
if not (is_torch_available() and is_transformers_available() and is_opencv_available()):
raise OptionalDependencyNotAvailable()
Expand Down Expand Up @@ -731,10 +771,22 @@
except OptionalDependencyNotAvailable:
from .utils.dummy_pt_objects import * # noqa F403
else:
from .guiders import (
AdaptiveProjectedGuidance,
AutoGuidance,
ClassifierFreeGuidance,
ClassifierFreeZeroStarGuidance,
SkipLayerGuidance,
SmoothedEnergyGuidance,
TangentialClassifierFreeGuidance,
)
from .hooks import (
FasterCacheConfig,
HookRegistry,
LayerSkipConfig,
PyramidAttentionBroadcastConfig,
SmoothedEnergyGuidanceConfig,
apply_layer_skip,
apply_faster_cache,
apply_pyramid_attention_broadcast,
)
Expand Down Expand Up @@ -837,12 +889,18 @@
KarrasVePipeline,
LDMPipeline,
LDMSuperResolutionPipeline,
ModularPipeline,
PNDMPipeline,
RePaintPipeline,
ScoreSdeVePipeline,
StableDiffusionMixin,
)
from .modular_pipelines import (
ModularLoader,
ModularPipeline,
ModularPipelineBlocks,
ComponentSpec,
ComponentsManager,
)
from .quantizers import DiffusersQuantizer
from .schedulers import (
AmusedScheduler,
Expand Down Expand Up @@ -1070,12 +1128,10 @@
StableDiffusionXLImg2ImgPipeline,
StableDiffusionXLInpaintPipeline,
StableDiffusionXLInstructPix2PixPipeline,
StableDiffusionXLModularPipeline,
StableDiffusionXLPAGImg2ImgPipeline,
StableDiffusionXLPAGInpaintPipeline,
StableDiffusionXLPAGPipeline,
StableDiffusionXLPipeline,
StableDiffusionXLAutoPipeline,
StableUnCLIPImg2ImgPipeline,
StableUnCLIPPipeline,
StableVideoDiffusionPipeline,
Expand All @@ -1100,7 +1156,16 @@
WuerstchenDecoderPipeline,
WuerstchenPriorPipeline,
)

try:
if not (is_torch_available() and is_transformers_available()):
raise OptionalDependencyNotAvailable()
except OptionalDependencyNotAvailable:
from .utils.dummy_torch_and_transformers_objects import * # noqa F403
else:
from .modular_pipelines import (
StableDiffusionXLAutoPipeline,
StableDiffusionXLModularLoader,
)
try:
if not (is_torch_available() and is_transformers_available() and is_k_diffusion_available()):
raise OptionalDependencyNotAvailable()
Expand Down
133 changes: 133 additions & 0 deletions src/diffusers/commands/custom_blocks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Copyright 2025 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.

"""
Usage example:
TODO
"""

import ast
from argparse import ArgumentParser, Namespace
from pathlib import Path
import importlib.util
import os
from ..utils import logging
from . import BaseDiffusersCLICommand


EXPECTED_PARENT_CLASSES = ["PipelineBlock"]
CONFIG = "config.json"

def conversion_command_factory(args: Namespace):
return CustomBlocksCommand(args.block_module_name, args.block_class_name)


class CustomBlocksCommand(BaseDiffusersCLICommand):
@staticmethod
def register_subcommand(parser: ArgumentParser):
conversion_parser = parser.add_parser("custom_blocks")
conversion_parser.add_argument(
"--block_module_name",
type=str,
default="block.py",
help="Module filename in which the custom block will be implemented.",
)
conversion_parser.add_argument(
"--block_class_name", type=str, default=None, help="Name of the custom block. If provided None, we will try to infer it."
)
conversion_parser.set_defaults(func=conversion_command_factory)

def __init__(self, block_module_name: str = "block.py", block_class_name: str = None):
self.logger = logging.get_logger("diffusers-cli/custom_blocks")
self.block_module_name = Path(block_module_name)
self.block_class_name = block_class_name

def run(self):
# determine the block to be saved.
out = self._get_class_names(self.block_module_name)
classes_found = list({cls for cls, _ in out})

if self.block_class_name is not None:
child_class, parent_class = self._choose_block(out, self.block_class_name)
if child_class is None and parent_class is None:
raise ValueError(
"`block_class_name` could not be retrieved. Available classes from "
f"{self.block_module_name}:\n{classes_found}"
)
else:
self.logger.info(
f"Found classes: {classes_found} will be using {classes_found[0]}. "
"If this needs to be changed, re-run the command specifying `block_class_name`."
)
child_class, parent_class = out[0][0], out[0][1]

# dynamically get the custom block and initialize it to call `save_pretrained` in the current directory.
# the user is responsible for running it, so I guess that is safe?
module_name = f"__dynamic__{self.block_module_name.stem}"
spec = importlib.util.spec_from_file_location(module_name, str(self.block_module_name))
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
getattr(module, child_class)().save_pretrained(os.getcwd())

# or, we could create it manually.
# automap = self._create_automap(parent_class=parent_class, child_class=child_class)
# with open(CONFIG, "w") as f:
# json.dump(automap, f)
with open("requirements.txt", "w") as f:
f.write("")

def _choose_block(self, candidates, chosen=None):
for cls, base in candidates:
if cls == chosen:
return cls, base
return None, None

def _get_class_names(self, file_path):
source = file_path.read_text(encoding="utf-8")
try:
tree = ast.parse(source, filename=file_path)
except SyntaxError as e:
raise ValueError(f"Could not parse {file_path!r}: {e}") from e

results: list[tuple[str, str]] = []
for node in tree.body:
if not isinstance(node, ast.ClassDef):
continue

# extract all base names for this class
base_names = [
bname for b in node.bases
if (bname := self._get_base_name(b)) is not None
]

# for each allowed base that appears in the class's bases, emit a tuple
for allowed in EXPECTED_PARENT_CLASSES:
if allowed in base_names:
results.append((node.name, allowed))

return results

def _get_base_name(self, node: ast.expr):
if isinstance(node, ast.Name):
return node.id
elif isinstance(node, ast.Attribute):
val = self._get_base_name(node.value)
return f"{val}.{node.attr}" if val else node.attr
return None

def _create_automap(self, parent_class, child_class):
module = str(self.block_module_name).replace(".py", "").rsplit(".", 1)[-1]
auto_map = {f"{parent_class}": f"{module}.{child_class}"}
return {"auto_map": auto_map}

2 changes: 2 additions & 0 deletions src/diffusers/commands/diffusers_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from .env import EnvironmentCommand
from .fp16_safetensors import FP16SafetensorsCommand
from .custom_blocks import CustomBlocksCommand


def main():
Expand All @@ -26,6 +27,7 @@ def main():
# Register commands
EnvironmentCommand.register_subcommand(commands_parser)
FP16SafetensorsCommand.register_subcommand(commands_parser)
CustomBlocksCommand.register_subcommand(commands_parser)

# Let's go
args = parser.parse_args()
Expand Down
Loading