linoyts HF Staff commited on
Commit
7f70ff0
·
verified ·
1 Parent(s): 7471beb

Canvas: switch to step-distilled model + AOTI (PR#29 revendor, aokit.load + eager fallback, 16 steps no guider)

Browse files
README.md CHANGED
@@ -14,7 +14,7 @@ short_description: FLUX.3 image-small bounding-box layout canvas (ZeroGPU)
14
  # FLUX.3 image-small — layout-control canvas (private)
15
 
16
  A custom-HTML canvas on top of the **diffusers-native FLUX.3 modular pipeline**
17
- ([`diffusers-new-model-addition-f3` PR #23](https://github.com/huggingface/diffusers-new-model-addition-f3/pull/23)),
18
  on ZeroGPU.
19
 
20
  Drag a bounding box for every element that matters, give each box an id and a
@@ -23,11 +23,11 @@ every element placed inside its box. Toggle the **Bounding boxes** overlay on th
23
  result, or **Replay** to regenerate.
24
 
25
  > **Private.** Weights are read at runtime from the private repo
26
- > `diffusers-internal-dev/f3-small-img` (never copied here). Keep this Space private.
27
 
28
  Layout control uses FLUX.3's region format: the final prompt is the scene caption
29
  followed by a JSON list of regions, each `{"id", "bbox": [y0, x0, y1, x1] (0-1000), "desc"}`.
30
 
31
  ## Setup
32
 
33
- Needs an `HF_TOKEN` secret with read access to `diffusers-internal-dev/f3-small-img`.
 
14
  # FLUX.3 image-small — layout-control canvas (private)
15
 
16
  A custom-HTML canvas on top of the **diffusers-native FLUX.3 modular pipeline**
17
+ ([`diffusers-new-model-addition-f3` PR #29](https://github.com/huggingface/diffusers-new-model-addition-f3/pull/29)), running the **step-distilled** model AOT-compiled with aokit,
18
  on ZeroGPU.
19
 
20
  Drag a bounding box for every element that matters, give each box an id and a
 
23
  result, or **Replay** to regenerate.
24
 
25
  > **Private.** Weights are read at runtime from the private repo
26
+ > `diffusers-internal-dev/f3-small-image-distilled` (never copied here). Keep this Space private.
27
 
28
  Layout control uses FLUX.3's region format: the final prompt is the scene caption
29
  followed by a JSON list of regions, each `{"id", "bbox": [y0, x0, y1, x1] (0-1000), "desc"}`.
30
 
31
  ## Setup
32
 
33
+ Needs an `HF_TOKEN` secret with read access to `diffusers-internal-dev/f3-small-image-distilled` and the private AOTI artifact `diffusers-modular/f3-small-image-distilled-aoti-sm120`.
app.py CHANGED
@@ -1,9 +1,14 @@
1
  """FLUX.3 image-small — layout-control canvas (private ZeroGPU demo, diffusers modular).
2
 
3
  A custom HTML/JS canvas on top of the diffusers-native FLUX.3 modular pipeline
4
- (PR #23, vendored `diffusers/`). You drag bounding boxes on an aspect-ratio
5
- artboard, give each box an id + a description, write a one-line scene prompt, and
6
- FLUX.3 renders the scene with every element placed inside its box.
 
 
 
 
 
7
 
8
  Layout control uses FLUX.3's region format (see `integration-notes/flux3_examples.py`):
9
  the final prompt is the scene caption followed by a JSON list of regions, each
@@ -23,18 +28,22 @@ import base64
23
  import io
24
  import json
25
  import random
 
26
 
27
  import gradio as gr
28
  import torch
 
29
 
30
  from diffusers import ModularPipeline
31
 
32
- REPO = os.environ.get("F3_REPO", "diffusers-internal-dev/f3-small-img")
 
 
 
33
  HF_TOKEN = os.environ.get("HF_TOKEN")
34
 
35
  MAX_SEED = 2**63 - 1
36
- DEFAULT_STEPS = int(os.environ.get("F3_STEPS", "50"))
37
- DEFAULT_GUIDANCE = 3.5
38
 
39
  torch.backends.cuda.matmul.allow_tf32 = True
40
  torch.backends.cudnn.allow_tf32 = True
@@ -50,14 +59,66 @@ RATIOS = {
50
  "9:16": (768, 1344),
51
  }
52
 
53
- # ---- load the pipeline on CPU at import; place on cuda under ZeroGPU ----
54
- print(f"[app] loading modular pipeline from {REPO} ...", flush=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  PIPE = ModularPipeline.from_pretrained(REPO, token=HF_TOKEN)
56
  PIPE.load_components(dtype={"vae": torch.float32, "default": torch.bfloat16}, token=HF_TOKEN)
57
  print("[app] components loaded on CPU.", flush=True)
 
58
  if spaces is not None:
59
  PIPE.to("cuda")
60
- print("[app] pipeline on cuda; ready.", flush=True)
 
 
 
 
 
 
61
 
62
 
63
  def _build_prompt(scene: str, elements: list) -> str:
@@ -88,7 +149,8 @@ def _duration(state_json, *_):
88
  except Exception:
89
  w, h = 1024, 1024
90
  px = w * h
91
- return int(min(300, 45 + DEFAULT_STEPS * 2.6 * max(1.0, px / (1024 * 1024))))
 
92
 
93
 
94
  def _generate(state_json):
@@ -109,7 +171,7 @@ def _generate(state_json):
109
  seed = st.get("seed")
110
  seed = int(seed) if isinstance(seed, (int, float)) and seed >= 0 else random.randint(0, MAX_SEED)
111
 
112
- PIPE.guider = PIPE.guider.new(guidance_scale=DEFAULT_GUIDANCE)
113
  image = PIPE(
114
  prompt=full_prompt,
115
  height=h,
 
1
  """FLUX.3 image-small — layout-control canvas (private ZeroGPU demo, diffusers modular).
2
 
3
  A custom HTML/JS canvas on top of the diffusers-native FLUX.3 modular pipeline
4
+ (PR #29, vendored `diffusers/`), running the **step-distilled** flow-map model
5
+ (`Flux3DistilledAutoBlocks`, 16 steps, no guider / negative prompt), with its
6
+ transformer block stacks **AOT-compiled via aokit** for ZeroGPU (`aokit.load` +
7
+ a per-block eager fallback; set `F3_AOTI=0` to run eager).
8
+
9
+ You drag bounding boxes on an aspect-ratio artboard, give each box an id + a
10
+ description, write a one-line scene prompt, and FLUX.3 renders the scene with
11
+ every element placed inside its box.
12
 
13
  Layout control uses FLUX.3's region format (see `integration-notes/flux3_examples.py`):
14
  the final prompt is the scene caption followed by a JSON list of regions, each
 
28
  import io
29
  import json
30
  import random
31
+ from pathlib import Path
32
 
33
  import gradio as gr
34
  import torch
35
+ from huggingface_hub import hf_hub_download
36
 
37
  from diffusers import ModularPipeline
38
 
39
+ # Step-distilled FLUX.3 image model + its private AOTI artifact.
40
+ REPO = os.environ.get("F3_REPO", "diffusers-internal-dev/f3-small-image-distilled")
41
+ AOTI_REPO = os.environ.get("F3_AOTI_REPO", "diffusers-modular/f3-small-image-distilled-aoti-sm120")
42
+ AOTI_ENABLED = os.environ.get("F3_AOTI", "1") != "0"
43
  HF_TOKEN = os.environ.get("HF_TOKEN")
44
 
45
  MAX_SEED = 2**63 - 1
46
+ DEFAULT_STEPS = int(os.environ.get("F3_STEPS", "16")) # distilled default
 
47
 
48
  torch.backends.cuda.matmul.allow_tf32 = True
49
  torch.backends.cudnn.allow_tf32 = True
 
59
  "9:16": (768, 1344),
60
  }
61
 
62
+
63
+ def _eager_fallback(compiled, eager):
64
+ """Call the AOTI graph; fall back to the eager forward on any shape it can't
65
+ handle (keeps odd resolutions / layouts working instead of crashing)."""
66
+ def forward(*args, **kwargs):
67
+ try:
68
+ return compiled(*args, **kwargs)
69
+ except Exception:
70
+ return eager(*args, **kwargs)
71
+ return forward
72
+
73
+
74
+ def _load_aoti(transformer, repo, token):
75
+ """aokit.load the compiled stacks, then wrap each block with an eager fallback.
76
+ The artifact's torch version is asserted against the runtime."""
77
+ import aokit
78
+
79
+ cfg = json.loads(Path(hf_hub_download(repo, "config.json", token=token)).read_text())
80
+ if cfg.get("torch") != torch.__version__:
81
+ raise RuntimeError(
82
+ f"AOTI artifact built for torch {cfg.get('torch')}, runtime has {torch.__version__}"
83
+ )
84
+ stack_names = list(cfg.get("stacks", {})) or [
85
+ "mode_blocks.image", "mode_blocks.image_cond", "txt_mode_blocks", "single_transformer_blocks",
86
+ ]
87
+
88
+ def _blocks(name):
89
+ try:
90
+ stack = transformer.get_submodule(name)
91
+ except AttributeError:
92
+ return []
93
+ return list(stack) if isinstance(stack, torch.nn.ModuleList) else [stack]
94
+
95
+ eager = {name: [blk.forward for blk in _blocks(name)] for name in stack_names}
96
+ aokit.load(transformer, repo) # patches block.forward -> compiled (weights stay live)
97
+ wrapped = 0
98
+ for name in stack_names:
99
+ for blk, eager_fwd in zip(_blocks(name), eager[name]):
100
+ blk.forward = _eager_fallback(blk.forward, eager_fwd)
101
+ wrapped += 1
102
+ print(f"[app] AOTI loaded from {repo}: {len(stack_names)} stacks, {wrapped} blocks wrapped "
103
+ f"(torch {cfg.get('torch')}, {cfg.get('arch')})", flush=True)
104
+ return cfg
105
+
106
+
107
+ # ---- load the pipeline on CPU at import; place on cuda + AOTI under ZeroGPU ----
108
+ print(f"[app] loading distilled modular pipeline from {REPO} ...", flush=True)
109
  PIPE = ModularPipeline.from_pretrained(REPO, token=HF_TOKEN)
110
  PIPE.load_components(dtype={"vae": torch.float32, "default": torch.bfloat16}, token=HF_TOKEN)
111
  print("[app] components loaded on CPU.", flush=True)
112
+ AOTI_LOADED = False
113
  if spaces is not None:
114
  PIPE.to("cuda")
115
+ if AOTI_ENABLED:
116
+ try:
117
+ _load_aoti(PIPE.transformer, AOTI_REPO, HF_TOKEN)
118
+ AOTI_LOADED = True
119
+ except Exception as exc: # fall back to eager if the artifact is unavailable
120
+ print(f"[app] AOTI unavailable, running eager: {exc!r}", flush=True)
121
+ print(f"[app] pipeline on cuda; ready (aoti={AOTI_LOADED}).", flush=True)
122
 
123
 
124
  def _build_prompt(scene: str, elements: list) -> str:
 
149
  except Exception:
150
  w, h = 1024, 1024
151
  px = w * h
152
+ # Distilled flow-map model: single forward per step (no CFG), so cheaper/step.
153
+ return int(min(300, 40 + DEFAULT_STEPS * 1.6 * max(1.0, px / (1024 * 1024))))
154
 
155
 
156
  def _generate(state_json):
 
171
  seed = st.get("seed")
172
  seed = int(seed) if isinstance(seed, (int, float)) and seed >= 0 else random.randint(0, MAX_SEED)
173
 
174
+ # Distilled flow-map model has no guider / guidance.
175
  image = PIPE(
176
  prompt=full_prompt,
177
  height=h,
diffusers/__init__.py CHANGED
@@ -527,6 +527,8 @@ else:
527
  "Flux2KleinModularPipeline",
528
  "Flux2ModularPipeline",
529
  "Flux3AutoBlocks",
 
 
530
  "Flux3ModularPipeline",
531
  "FluxAutoBlocks",
532
  "FluxKontextAutoBlocks",
@@ -1386,6 +1388,8 @@ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
1386
  Flux2KleinModularPipeline,
1387
  Flux2ModularPipeline,
1388
  Flux3AutoBlocks,
 
 
1389
  Flux3ModularPipeline,
1390
  FluxAutoBlocks,
1391
  FluxKontextAutoBlocks,
 
527
  "Flux2KleinModularPipeline",
528
  "Flux2ModularPipeline",
529
  "Flux3AutoBlocks",
530
+ "Flux3DistilledAutoBlocks",
531
+ "Flux3DistilledModularPipeline",
532
  "Flux3ModularPipeline",
533
  "FluxAutoBlocks",
534
  "FluxKontextAutoBlocks",
 
1388
  Flux2KleinModularPipeline,
1389
  Flux2ModularPipeline,
1390
  Flux3AutoBlocks,
1391
+ Flux3DistilledAutoBlocks,
1392
+ Flux3DistilledModularPipeline,
1393
  Flux3ModularPipeline,
1394
  FluxAutoBlocks,
1395
  FluxKontextAutoBlocks,
diffusers/experimental/README.md ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ # 🧨 Diffusers Experimental
2
+
3
+ We are adding experimental code to support novel applications and usages of the Diffusers library.
4
+ Currently, the following experiments are supported:
5
+ * Reinforcement learning via an implementation of the [Diffuser](https://huggingface.co/papers/2205.09991) model.
diffusers/models/README.md ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # Models
2
+
3
+ For more detail on the models, please refer to the [docs](https://huggingface.co/docs/diffusers/api/models/overview).
diffusers/models/transformers/transformer_flux3.py CHANGED
@@ -346,7 +346,9 @@ class Flux3Transformer2DModel(ModelMixin, ConfigMixin, PeftAdapterMixin, Attenti
346
  passes through its own per-mode transformer blocks (`mode_blocks` for the image modes, `txt_mode_blocks` for the
347
  text context), then the text context and the active image modes are concatenated for the joint single blocks, and
348
  only the image tokens pass through the output layers. The modulation is per mode: the image tokens are modulated by
349
- the flow timestep, the reference image tokens and the text context by timestep `0`.
 
 
350
 
351
  Args:
352
  in_channels (`int`, defaults to `128`):
@@ -373,6 +375,9 @@ class Flux3Transformer2DModel(ModelMixin, ConfigMixin, PeftAdapterMixin, Attenti
373
  The base frequency of the rotary positional embeddings.
374
  eps (`float`, defaults to `1e-6`):
375
  The epsilon of the layer norms and the QK norms.
 
 
 
376
  """
377
 
378
  _supports_gradient_checkpointing = True
@@ -395,6 +400,7 @@ class Flux3Transformer2DModel(ModelMixin, ConfigMixin, PeftAdapterMixin, Attenti
395
  axes_dims_rope: tuple[int, ...] = (32, 32, 32, 32),
396
  rope_theta: int = 10000,
397
  eps: float = 1e-6,
 
398
  ):
399
  super().__init__()
400
 
@@ -410,6 +416,12 @@ class Flux3Transformer2DModel(ModelMixin, ConfigMixin, PeftAdapterMixin, Attenti
410
  self.timestep_embedder = TimestepEmbedding(
411
  in_channels=timestep_channels, time_embed_dim=self.inner_dim, sample_proj_bias=False
412
  )
 
 
 
 
 
 
413
 
414
  # 3. Modulation: one head per mode (+ text) for the per-mode blocks and for the joint blocks
415
  self.early_stream_modulations = nn.ModuleDict(
@@ -486,6 +498,7 @@ class Flux3Transformer2DModel(ModelMixin, ConfigMixin, PeftAdapterMixin, Attenti
486
  txt_ids: torch.Tensor,
487
  image_cond_hidden_states: torch.Tensor | None = None,
488
  image_cond_ids: torch.Tensor | None = None,
 
489
  attention_kwargs: dict[str, Any] | None = None,
490
  return_dict: bool = True,
491
  ) -> Transformer2DModelOutput | tuple[torch.Tensor]:
@@ -509,6 +522,9 @@ class Flux3Transformer2DModel(ModelMixin, ConfigMixin, PeftAdapterMixin, Attenti
509
  VAE-encoded reference image tokens (clean conditioning content, for image-to-image generation).
510
  image_cond_ids (`torch.Tensor` of shape `(cond_seq_len, 4)`, *optional*):
511
  Position ids of the reference image tokens. Required with `image_cond_hidden_states`.
 
 
 
512
  attention_kwargs (`dict`, *optional*):
513
  A kwargs dictionary whose `scale` entry, if specified, scales the LoRA layers for this forward pass.
514
  return_dict (`bool`, *optional*, defaults to `True`):
@@ -524,6 +540,10 @@ class Flux3Transformer2DModel(ModelMixin, ConfigMixin, PeftAdapterMixin, Attenti
524
  raise ValueError("`image_cond_hidden_states` requires `image_cond_ids`.")
525
  if image_cond_hidden_states is None and image_cond_ids is not None:
526
  raise ValueError("`image_cond_ids` was passed without `image_cond_hidden_states`.")
 
 
 
 
527
 
528
  batch_size, image_seq_len = hidden_states.shape[:2]
529
  dtype = hidden_states.dtype
@@ -531,9 +551,17 @@ class Flux3Transformer2DModel(ModelMixin, ConfigMixin, PeftAdapterMixin, Attenti
531
  # 1. Timestep embeddings: the image tokens at `timestep`, the text context and the reference tokens at 0.
532
  # Each is a per-token embedding (batch_size, seq_len, dim) since the modulation is per token.
533
  image_temb = self.time_proj(timestep.to(dtype) * 1000)
534
- image_temb = self.timestep_embedder(image_temb.to(dtype))[:, None, :].expand(batch_size, image_seq_len, -1)
535
  zero_temb = self.time_proj(torch.zeros(batch_size, device=hidden_states.device, dtype=dtype))
536
  zero_temb = self.timestep_embedder(zero_temb.to(dtype))[:, None, :]
 
 
 
 
 
 
 
 
537
  zero_temb_act = F.silu(zero_temb)
538
 
539
  # 2. Text context: the two modulation sets, input projection and RoPE
 
346
  passes through its own per-mode transformer blocks (`mode_blocks` for the image modes, `txt_mode_blocks` for the
347
  text context), then the text context and the active image modes are concatenated for the joint single blocks, and
348
  only the image tokens pass through the output layers. The modulation is per mode: the image tokens are modulated by
349
+ the flow timestep, the reference image tokens and the text context by timestep `0`. A flow-map checkpoint
350
+ (`flow_map=True`, the step-distilled models) additionally embeds the timestep each denoising step jumps to and adds
351
+ it to every mode's modulation.
352
 
353
  Args:
354
  in_channels (`int`, defaults to `128`):
 
375
  The base frequency of the rotary positional embeddings.
376
  eps (`float`, defaults to `1e-6`):
377
  The epsilon of the layer norms and the QK norms.
378
+ flow_map (`bool`, defaults to `False`):
379
+ Whether the model is a flow map (step-distilled): adds the `target_timestep_embedder` and requires
380
+ `target_timestep` in the forward pass.
381
  """
382
 
383
  _supports_gradient_checkpointing = True
 
400
  axes_dims_rope: tuple[int, ...] = (32, 32, 32, 32),
401
  rope_theta: int = 10000,
402
  eps: float = 1e-6,
403
+ flow_map: bool = False,
404
  ):
405
  super().__init__()
406
 
 
416
  self.timestep_embedder = TimestepEmbedding(
417
  in_channels=timestep_channels, time_embed_dim=self.inner_dim, sample_proj_bias=False
418
  )
419
+ # flow map (step-distilled checkpoints): a second embedder for the timestep each step jumps to
420
+ self.target_timestep_embedder = (
421
+ TimestepEmbedding(in_channels=timestep_channels, time_embed_dim=self.inner_dim, sample_proj_bias=False)
422
+ if flow_map
423
+ else None
424
+ )
425
 
426
  # 3. Modulation: one head per mode (+ text) for the per-mode blocks and for the joint blocks
427
  self.early_stream_modulations = nn.ModuleDict(
 
498
  txt_ids: torch.Tensor,
499
  image_cond_hidden_states: torch.Tensor | None = None,
500
  image_cond_ids: torch.Tensor | None = None,
501
+ target_timestep: torch.Tensor | None = None,
502
  attention_kwargs: dict[str, Any] | None = None,
503
  return_dict: bool = True,
504
  ) -> Transformer2DModelOutput | tuple[torch.Tensor]:
 
522
  VAE-encoded reference image tokens (clean conditioning content, for image-to-image generation).
523
  image_cond_ids (`torch.Tensor` of shape `(cond_seq_len, 4)`, *optional*):
524
  Position ids of the reference image tokens. Required with `image_cond_hidden_states`.
525
+ target_timestep (`torch.Tensor` of shape `(batch_size,)`, *optional*):
526
+ Flow timestep in `[0, 1]` that the current denoising step jumps to. Required by, and only by, a flow-map
527
+ model (`flow_map=True`).
528
  attention_kwargs (`dict`, *optional*):
529
  A kwargs dictionary whose `scale` entry, if specified, scales the LoRA layers for this forward pass.
530
  return_dict (`bool`, *optional*, defaults to `True`):
 
540
  raise ValueError("`image_cond_hidden_states` requires `image_cond_ids`.")
541
  if image_cond_hidden_states is None and image_cond_ids is not None:
542
  raise ValueError("`image_cond_ids` was passed without `image_cond_hidden_states`.")
543
+ if (target_timestep is not None) != bool(self.config.flow_map):
544
+ raise ValueError(
545
+ "`target_timestep` is required by, and only by, a flow-map transformer (`flow_map=True`)."
546
+ )
547
 
548
  batch_size, image_seq_len = hidden_states.shape[:2]
549
  dtype = hidden_states.dtype
 
551
  # 1. Timestep embeddings: the image tokens at `timestep`, the text context and the reference tokens at 0.
552
  # Each is a per-token embedding (batch_size, seq_len, dim) since the modulation is per token.
553
  image_temb = self.time_proj(timestep.to(dtype) * 1000)
554
+ image_temb = self.timestep_embedder(image_temb.to(dtype))[:, None, :]
555
  zero_temb = self.time_proj(torch.zeros(batch_size, device=hidden_states.device, dtype=dtype))
556
  zero_temb = self.timestep_embedder(zero_temb.to(dtype))[:, None, :]
557
+ if target_timestep is not None:
558
+ # flow map: the embedding of the timestep this step jumps to is added to every mode's embedding, so it
559
+ # reaches the per-mode blocks, the joint blocks and the output layer
560
+ target_temb = self.time_proj(target_timestep.to(dtype) * 1000)
561
+ target_temb = self.target_timestep_embedder(target_temb.to(dtype))[:, None, :]
562
+ image_temb = image_temb + target_temb
563
+ zero_temb = zero_temb + target_temb
564
+ image_temb = image_temb.expand(batch_size, image_seq_len, -1)
565
  zero_temb_act = F.silu(zero_temb)
566
 
567
  # 2. Text context: the two modulation sets, input projection and RoPE
diffusers/modular_pipelines/__init__.py CHANGED
@@ -83,6 +83,8 @@ else:
83
  ]
84
  _import_structure["flux3"] = [
85
  "Flux3AutoBlocks",
 
 
86
  "Flux3ModularPipeline",
87
  ]
88
  _import_structure["ideogram4"] = [
@@ -172,7 +174,12 @@ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
172
  Flux2KleinModularPipeline,
173
  Flux2ModularPipeline,
174
  )
175
- from .flux3 import Flux3AutoBlocks, Flux3ModularPipeline
 
 
 
 
 
176
  from .helios import (
177
  HeliosAutoBlocks,
178
  HeliosModularPipeline,
 
83
  ]
84
  _import_structure["flux3"] = [
85
  "Flux3AutoBlocks",
86
+ "Flux3DistilledAutoBlocks",
87
+ "Flux3DistilledModularPipeline",
88
  "Flux3ModularPipeline",
89
  ]
90
  _import_structure["ideogram4"] = [
 
174
  Flux2KleinModularPipeline,
175
  Flux2ModularPipeline,
176
  )
177
+ from .flux3 import (
178
+ Flux3AutoBlocks,
179
+ Flux3DistilledAutoBlocks,
180
+ Flux3DistilledModularPipeline,
181
+ Flux3ModularPipeline,
182
+ )
183
  from .helios import (
184
  HeliosAutoBlocks,
185
  HeliosModularPipeline,
diffusers/modular_pipelines/flux3/__init__.py CHANGED
@@ -22,7 +22,8 @@ except OptionalDependencyNotAvailable:
22
  _dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
23
  else:
24
  _import_structure["modular_blocks_flux3"] = ["Flux3AutoBlocks"]
25
- _import_structure["modular_pipeline"] = ["Flux3ModularPipeline"]
 
26
 
27
  if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
28
  try:
@@ -32,7 +33,8 @@ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
32
  from ...utils.dummy_torch_and_transformers_objects import * # noqa F403
33
  else:
34
  from .modular_blocks_flux3 import Flux3AutoBlocks
35
- from .modular_pipeline import Flux3ModularPipeline
 
36
  else:
37
  import sys
38
 
 
22
  _dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
23
  else:
24
  _import_structure["modular_blocks_flux3"] = ["Flux3AutoBlocks"]
25
+ _import_structure["modular_blocks_flux3_distilled"] = ["Flux3DistilledAutoBlocks"]
26
+ _import_structure["modular_pipeline"] = ["Flux3DistilledModularPipeline", "Flux3ModularPipeline"]
27
 
28
  if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
29
  try:
 
33
  from ...utils.dummy_torch_and_transformers_objects import * # noqa F403
34
  else:
35
  from .modular_blocks_flux3 import Flux3AutoBlocks
36
+ from .modular_blocks_flux3_distilled import Flux3DistilledAutoBlocks
37
+ from .modular_pipeline import Flux3DistilledModularPipeline, Flux3ModularPipeline
38
  else:
39
  import sys
40
 
diffusers/modular_pipelines/flux3/before_denoise.py CHANGED
@@ -92,9 +92,11 @@ def retrieve_timesteps(
92
  class Flux3SetTimestepsStep(ModularPipelineBlocks):
93
  model_name = "flux3"
94
 
95
- def __init__(self, shift: float = 6.93):
96
- # the constant timestep shift ("alpha") of the FLUX.3 sampling recipe, configured on the scheduler
 
97
  self.shift = shift
 
98
  super().__init__()
99
 
100
  @property
@@ -118,7 +120,7 @@ class Flux3SetTimestepsStep(ModularPipelineBlocks):
118
  @property
119
  def inputs(self) -> list[InputParam]:
120
  return [
121
- InputParam.template("num_inference_steps"),
122
  InputParam.template("timesteps", note="overrides `num_inference_steps` when provided"),
123
  InputParam.template("sigmas", note="overrides `num_inference_steps` when provided"),
124
  ]
@@ -395,3 +397,40 @@ class Flux3TextRoPEInputsStep(ModularPipelineBlocks):
395
 
396
  self.set_block_state(state, block_state)
397
  return components, state
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  class Flux3SetTimestepsStep(ModularPipelineBlocks):
93
  model_name = "flux3"
94
 
95
+ def __init__(self, shift: float = 6.93, num_inference_steps: int = 50):
96
+ # the constant timestep shift ("alpha") of the FLUX.3 sampling recipe, configured on the scheduler, and the
97
+ # default number of steps (50; 16 for the step-distilled checkpoints)
98
  self.shift = shift
99
+ self.num_inference_steps = num_inference_steps
100
  super().__init__()
101
 
102
  @property
 
120
  @property
121
  def inputs(self) -> list[InputParam]:
122
  return [
123
+ InputParam.template("num_inference_steps", default=self.num_inference_steps),
124
  InputParam.template("timesteps", note="overrides `num_inference_steps` when provided"),
125
  InputParam.template("sigmas", note="overrides `num_inference_steps` when provided"),
126
  ]
 
397
 
398
  self.set_block_state(state, block_state)
399
  return components, state
400
+
401
+
402
+ class Flux3DistilledTextRoPEInputsStep(Flux3TextRoPEInputsStep):
403
+ model_name = "flux3-distilled"
404
+
405
+ @property
406
+ def description(self) -> str:
407
+ return (
408
+ "Step that prepares the 4D RoPE position IDs of the text tokens for the step-distilled FLUX.3 model (no "
409
+ "negative prompt). Place it after the text encoder step."
410
+ )
411
+
412
+ @property
413
+ def inputs(self) -> list[InputParam]:
414
+ return [
415
+ InputParam.template(
416
+ "prompt_embeds",
417
+ description="Text embeddings used to guide the generation. Can be generated from text_encoder step.",
418
+ ),
419
+ ]
420
+
421
+ @property
422
+ def intermediate_outputs(self) -> list[OutputParam]:
423
+ return [
424
+ OutputParam(
425
+ name="txt_ids",
426
+ kwargs_type="denoiser_input_fields",
427
+ type_hint=torch.Tensor,
428
+ description="4D position IDs (T, H, W, L) for text tokens, used for RoPE calculation.",
429
+ ),
430
+ ]
431
+
432
+ def __call__(self, components: Flux3ModularPipeline, state: PipelineState) -> PipelineState:
433
+ block_state = self.get_block_state(state)
434
+ block_state.txt_ids = self._prepare_text_ids(block_state.prompt_embeds).to(block_state.prompt_embeds.device)
435
+ self.set_block_state(state, block_state)
436
+ return components, state
diffusers/modular_pipelines/flux3/denoise.py CHANGED
@@ -138,6 +138,76 @@ class Flux3LoopDenoiser(ModularPipelineBlocks):
138
  return components, block_state
139
 
140
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  class Flux3LoopAfterDenoiser(ModularPipelineBlocks):
142
  model_name = "flux3"
143
 
@@ -276,3 +346,73 @@ class Flux3DenoiseStep(Flux3DenoiseLoopWrapper):
276
  " - `Flux3LoopAfterDenoiser`\n"
277
  "This block supports text-to-image and image-to-image generation."
278
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  return components, block_state
139
 
140
 
141
+ class Flux3DistilledLoopDenoiser(ModularPipelineBlocks):
142
+ model_name = "flux3-distilled"
143
+
144
+ @property
145
+ def expected_components(self) -> list[ComponentSpec]:
146
+ return [ComponentSpec("transformer", Flux3Transformer2DModel)]
147
+
148
+ @property
149
+ def description(self) -> str:
150
+ return (
151
+ "Step within the denoising loop that predicts the velocity of the image tokens with a step-distilled "
152
+ "(flow-map) FLUX.3 transformer: no classifier-free guidance, and each step also passes the transformer the "
153
+ "timestep it jumps to. The static conditioning fields (`prompt_embeds`, `txt_ids`, `img_ids`, the "
154
+ "reference image tokens and their ids) arrive via `denoiser_input_fields`. "
155
+ "This block should be used to compose the `sub_blocks` attribute of a `LoopSequentialPipelineBlocks` "
156
+ "object (e.g. `Flux3DistilledDenoiseStep`)"
157
+ )
158
+
159
+ @property
160
+ def inputs(self) -> list[tuple[str, Any]]:
161
+ return [
162
+ InputParam(
163
+ "latents",
164
+ required=True,
165
+ type_hint=torch.Tensor,
166
+ description="The image latent tokens to denoise. Shape: (B, seq_len, C)",
167
+ ),
168
+ InputParam.template("denoiser_input_fields"),
169
+ ]
170
+
171
+ @property
172
+ def intermediate_outputs(self) -> list[OutputParam]:
173
+ return [OutputParam("noise_pred", type_hint=torch.Tensor, description="The predicted velocity for this step")]
174
+
175
+ @torch.no_grad()
176
+ def __call__(
177
+ self, components: Flux3ModularPipeline, block_state: BlockState, i: int, t: torch.Tensor
178
+ ) -> PipelineState:
179
+ denoiser_input_fields = block_state.denoiser_input_fields
180
+
181
+ # transformer kwargs = every collected static field matching the forward signature (`img_ids`, `txt_ids`, and
182
+ # the `image_cond_*` fields of image-to-image generation)
183
+ transformer_args = set(inspect.signature(components.transformer.forward).parameters.keys())
184
+ transformer_kwargs = {
185
+ field_name: field_value
186
+ for field_name, field_value in denoiser_input_fields.items()
187
+ if field_name in transformer_args
188
+ }
189
+
190
+ latents = block_state.latents.to(components.transformer.dtype)
191
+ batch_size = latents.shape[0]
192
+ # the flow timestep in [0, 1] (scheduler timesteps are sigmas * 1000), and the one this step jumps to: the next
193
+ # scheduler timestep, 0 after the last
194
+ timesteps = block_state.timesteps
195
+ next_t = timesteps[i + 1] if i + 1 < len(timesteps) else torch.zeros_like(t)
196
+ timestep = (t / 1000).expand(batch_size).to(latents.dtype)
197
+ target_timestep = (next_t / 1000).expand(batch_size).to(latents.dtype)
198
+
199
+ block_state.noise_pred = components.transformer(
200
+ hidden_states=latents,
201
+ encoder_hidden_states=denoiser_input_fields["prompt_embeds"],
202
+ timestep=timestep,
203
+ target_timestep=target_timestep,
204
+ return_dict=False,
205
+ **transformer_kwargs,
206
+ )[0]
207
+
208
+ return components, block_state
209
+
210
+
211
  class Flux3LoopAfterDenoiser(ModularPipelineBlocks):
212
  model_name = "flux3"
213
 
 
346
  " - `Flux3LoopAfterDenoiser`\n"
347
  "This block supports text-to-image and image-to-image generation."
348
  )
349
+
350
+
351
+ # auto_docstring
352
+ class Flux3DistilledDenoiseStep(Flux3DenoiseLoopWrapper):
353
+ """
354
+ Denoise step that iteratively denoises the latents for the step-distilled (flow-map) FLUX.3 model.
355
+ Its loop logic is defined in `Flux3DenoiseLoopWrapper.__call__` method At each iteration, it runs blocks defined
356
+ in `sub_blocks` sequentially:
357
+ - `Flux3DistilledLoopDenoiser`
358
+ - `Flux3LoopAfterDenoiser`
359
+ This block supports text-to-image and image-to-image generation, without classifier-free guidance.
360
+
361
+ Components:
362
+ transformer (`Flux3Transformer2DModel`) scheduler (`FlowMatchEulerDiscreteScheduler`)
363
+
364
+ Inputs:
365
+ timesteps (`Tensor`):
366
+ The timesteps to use for the denoising process.
367
+ num_inference_steps (`int`):
368
+ The number of inference steps to use for the denoising process.
369
+ latents (`Tensor`):
370
+ The image latent tokens to denoise. Shape: (B, seq_len, C)
371
+ **denoiser_input_fields (`None`, *optional*):
372
+ conditional model inputs for the denoiser: e.g. prompt_embeds, negative_prompt_embeds, etc.
373
+
374
+ Outputs:
375
+ latents (`Tensor`):
376
+ The denoised latents
377
+ """
378
+
379
+ model_name = "flux3-distilled"
380
+ """
381
+ Denoise step that iteratively denoises the latents for the step-distilled (flow-map) FLUX.3 model.
382
+ Its loop logic is defined in `Flux3DenoiseLoopWrapper.__call__` method At each iteration, it runs blocks defined
383
+ in `sub_blocks` sequentially:
384
+ - `Flux3DistilledLoopDenoiser`
385
+ - `Flux3LoopAfterDenoiser`
386
+ This block supports text-to-image and image-to-image generation, without classifier-free guidance.
387
+
388
+ Components:
389
+ transformer (`Flux3Transformer2DModel`) scheduler (`FlowMatchEulerDiscreteScheduler`)
390
+
391
+ Inputs:
392
+ timesteps (`Tensor`):
393
+ The timesteps to use for the denoising process.
394
+ num_inference_steps (`int`):
395
+ The number of inference steps to use for the denoising process.
396
+ latents (`Tensor`):
397
+ The image latent tokens to denoise. Shape: (B, seq_len, C)
398
+ **denoiser_input_fields (`None`, *optional*):
399
+ conditional model inputs for the denoiser: e.g. prompt_embeds, negative_prompt_embeds, etc.
400
+
401
+ Outputs:
402
+ latents (`Tensor`):
403
+ The denoised latents
404
+ """
405
+
406
+ block_classes = [Flux3DistilledLoopDenoiser(), Flux3LoopAfterDenoiser()]
407
+ block_names = ["denoiser", "after_denoiser"]
408
+
409
+ @property
410
+ def description(self) -> str:
411
+ return (
412
+ "Denoise step that iteratively denoises the latents for the step-distilled (flow-map) FLUX.3 model. \n"
413
+ "Its loop logic is defined in `Flux3DenoiseLoopWrapper.__call__` method \n"
414
+ "At each iteration, it runs blocks defined in `sub_blocks` sequentially:\n"
415
+ " - `Flux3DistilledLoopDenoiser`\n"
416
+ " - `Flux3LoopAfterDenoiser`\n"
417
+ "This block supports text-to-image and image-to-image generation, without classifier-free guidance."
418
+ )
diffusers/modular_pipelines/flux3/encoders.py CHANGED
@@ -326,3 +326,70 @@ class Flux3TextEncoderStep(ModularPipelineBlocks):
326
 
327
  self.set_block_state(state, block_state)
328
  return components, state
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
 
327
  self.set_block_state(state, block_state)
328
  return components, state
329
+
330
+
331
+ class Flux3DistilledTextEncoderStep(Flux3TextEncoderStep):
332
+ model_name = "flux3-distilled"
333
+
334
+ @property
335
+ def description(self) -> str:
336
+ return (
337
+ "Text Encoder step that generates text embeddings using Qwen3-VL to guide the generation with the "
338
+ "step-distilled FLUX.3 model, which samples without classifier-free guidance (no negative prompt). Takes "
339
+ f"one prompt per call; it goes through the chat template and is right-padded to a multiple of "
340
+ f"{self.pad_multiple} tokens."
341
+ )
342
+
343
+ @property
344
+ def inputs(self) -> list[InputParam]:
345
+ return [
346
+ InputParam.template("prompt", description="The prompt to guide the generation (one prompt per call)"),
347
+ InputParam.template(
348
+ "max_sequence_length",
349
+ default=8192,
350
+ description="Maximum number of prompt tokens; longer prompts are truncated.",
351
+ ),
352
+ InputParam(
353
+ "text_encoder_out_layers",
354
+ type_hint=tuple[int],
355
+ default=(4, 8, 12, 16, 20, 24, 28, 32),
356
+ required=False,
357
+ description="Indices of the text-encoder hidden layers whose states are stacked to form the text embeddings",
358
+ ),
359
+ ]
360
+
361
+ @property
362
+ def intermediate_outputs(self) -> list[OutputParam]:
363
+ return [
364
+ OutputParam(
365
+ "prompt_embeds",
366
+ kwargs_type="denoiser_input_fields",
367
+ type_hint=torch.Tensor,
368
+ description="Text embeddings (stacked Qwen3-VL hidden states) used to guide the image generation",
369
+ ),
370
+ ]
371
+
372
+ @staticmethod
373
+ def check_inputs(block_state):
374
+ if not isinstance(block_state.prompt, str):
375
+ raise ValueError(
376
+ f"`prompt` must be a str; batched prompts are not supported, got {type(block_state.prompt).__name__}."
377
+ )
378
+
379
+ @torch.no_grad()
380
+ def __call__(self, components: Flux3ModularPipeline, state: PipelineState) -> PipelineState:
381
+ block_state = self.get_block_state(state)
382
+ self.check_inputs(block_state)
383
+
384
+ block_state.prompt_embeds = self._get_qwen3_vl_prompt_embeds(
385
+ text_encoder=components.text_encoder,
386
+ tokenizer=components.tokenizer,
387
+ prompt=block_state.prompt,
388
+ device=components._execution_device,
389
+ max_sequence_length=block_state.max_sequence_length,
390
+ pad_multiple=self.pad_multiple,
391
+ hidden_states_layers=block_state.text_encoder_out_layers,
392
+ )
393
+
394
+ self.set_block_state(state, block_state)
395
+ return components, state
diffusers/modular_pipelines/flux3/inputs.py CHANGED
@@ -173,6 +173,73 @@ class Flux3TextInputStep(ModularPipelineBlocks):
173
  return components, state
174
 
175
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  class Flux3ImageCondInputStep(ModularPipelineBlocks):
177
  model_name = "flux3"
178
 
 
173
  return components, state
174
 
175
 
176
+ class Flux3DistilledTextInputStep(ModularPipelineBlocks):
177
+ model_name = "flux3-distilled"
178
+
179
+ @property
180
+ def description(self) -> str:
181
+ return (
182
+ "This step:\n"
183
+ " 1. Determines `batch_size` from `prompt_embeds` and the compute `dtype` from the transformer\n"
184
+ " 2. Repeats the text embeddings to the batch size (batch_size * num_images_per_prompt)\n"
185
+ "For the step-distilled FLUX.3 model, which samples without classifier-free guidance (no negative "
186
+ "embeddings)."
187
+ )
188
+
189
+ @property
190
+ def expected_components(self) -> list[ComponentSpec]:
191
+ return [ComponentSpec("transformer", Flux3Transformer2DModel)]
192
+
193
+ @property
194
+ def inputs(self) -> list[InputParam]:
195
+ return [
196
+ InputParam.template("num_images_per_prompt"),
197
+ InputParam(
198
+ "prompt_embeds",
199
+ required=True,
200
+ kwargs_type="denoiser_input_fields",
201
+ type_hint=torch.Tensor,
202
+ description="Pre-generated text embeddings. Can be generated from text_encoder step.",
203
+ ),
204
+ ]
205
+
206
+ @property
207
+ def intermediate_outputs(self) -> list[OutputParam]:
208
+ return [
209
+ OutputParam(
210
+ "batch_size",
211
+ type_hint=int,
212
+ description="Number of prompts, the final batch size of model inputs should be batch_size * num_images_per_prompt",
213
+ ),
214
+ OutputParam(
215
+ "dtype",
216
+ type_hint=torch.dtype,
217
+ description="Data type of model tensor inputs (the transformer's dtype)",
218
+ ),
219
+ OutputParam(
220
+ "prompt_embeds",
221
+ type_hint=torch.Tensor,
222
+ kwargs_type="denoiser_input_fields",
223
+ description="Text embeddings used to guide the image generation",
224
+ ),
225
+ ]
226
+
227
+ @torch.no_grad()
228
+ def __call__(self, components: Flux3ModularPipeline, state: PipelineState) -> PipelineState:
229
+ block_state = self.get_block_state(state)
230
+
231
+ block_state.batch_size = block_state.prompt_embeds.shape[0]
232
+ # the denoiser consumes every tensor this chain prepares, so its dtype is the compute dtype
233
+ block_state.dtype = components.transformer.dtype
234
+
235
+ block_state.prompt_embeds = repeat_tensor_to_batch_size(
236
+ "prompt_embeds", block_state.prompt_embeds, block_state.batch_size, block_state.num_images_per_prompt
237
+ ).to(block_state.dtype)
238
+
239
+ self.set_block_state(state, block_state)
240
+ return components, state
241
+
242
+
243
  class Flux3ImageCondInputStep(ModularPipelineBlocks):
244
  model_name = "flux3"
245
 
diffusers/modular_pipelines/flux3/modular_blocks_flux3_distilled.py ADDED
@@ -0,0 +1,333 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2026 Black Forest Labs and The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ from ...utils import logging
17
+ from ..modular_pipeline import AutoPipelineBlocks, SequentialPipelineBlocks
18
+ from ..modular_pipeline_utils import InsertableDict, OutputParam
19
+ from .before_denoise import (
20
+ Flux3DistilledTextRoPEInputsStep,
21
+ Flux3PrepareImageCondLatentsStep,
22
+ Flux3PrepareLatentsStep,
23
+ Flux3SetTimestepsStep,
24
+ )
25
+ from .decoders import Flux3DecodeStep, Flux3UnpackLatentsStep
26
+ from .denoise import Flux3DistilledDenoiseStep
27
+ from .encoders import Flux3DistilledTextEncoderStep
28
+ from .inputs import Flux3DistilledTextInputStep, Flux3ImageCondInputStep
29
+ from .modular_blocks_flux3 import Flux3AutoVaeEncoderStep
30
+
31
+
32
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
33
+
34
+
35
+ # The step-distilled (flow-map) FLUX.3 checkpoints: 16 steps and no classifier-free guidance. The text encoder,
36
+ # text input, text RoPE and denoise steps have distilled versions without the negative prompt; the denoise loop
37
+ # passes the transformer the timestep each step jumps to and runs a single pass. Everything else is shared with the
38
+ # base model.
39
+
40
+ ###
41
+ ### Core denoise
42
+ ###
43
+
44
+ Flux3DistilledCoreDenoiseBlocks = InsertableDict(
45
+ [
46
+ ("input", Flux3DistilledTextInputStep()),
47
+ ("prepare_latents", Flux3PrepareLatentsStep()),
48
+ ("set_timesteps", Flux3SetTimestepsStep(num_inference_steps=16)),
49
+ ("prepare_rope_inputs", Flux3DistilledTextRoPEInputsStep()),
50
+ ("denoise", Flux3DistilledDenoiseStep()),
51
+ ("after_denoise", Flux3UnpackLatentsStep()),
52
+ ]
53
+ )
54
+
55
+
56
+ # auto_docstring
57
+ class Flux3DistilledCoreDenoiseStep(SequentialPipelineBlocks):
58
+ """
59
+ Core denoise step that performs the denoising process for the step-distilled FLUX.3 model, for text-to-image
60
+ generation.
61
+
62
+ Components:
63
+ transformer (`Flux3Transformer2DModel`) scheduler (`FlowMatchEulerDiscreteScheduler`)
64
+
65
+ Inputs:
66
+ num_images_per_prompt (`int`, *optional*, defaults to 1):
67
+ The number of images to generate per prompt.
68
+ prompt_embeds (`Tensor`):
69
+ Pre-generated text embeddings. Can be generated from text_encoder step.
70
+ height (`int`, *optional*, defaults to 1024):
71
+ The height in pixels of the generated image.
72
+ width (`int`, *optional*, defaults to 1024):
73
+ The width in pixels of the generated image.
74
+ latents (`Tensor`, *optional*):
75
+ Pre-generated noisy latents for image generation.
76
+ generator (`Generator`, *optional*):
77
+ Torch generator for deterministic generation.
78
+ num_inference_steps (`int`, *optional*, defaults to 16):
79
+ The number of denoising steps.
80
+ timesteps (`Tensor`, *optional*):
81
+ Timesteps for the denoising process. (overrides `num_inference_steps` when provided)
82
+ sigmas (`list`, *optional*):
83
+ Custom sigmas for the denoising process. (overrides `num_inference_steps` when provided)
84
+ **denoiser_input_fields (`None`, *optional*):
85
+ conditional model inputs for the denoiser: e.g. prompt_embeds, negative_prompt_embeds, etc.
86
+
87
+ Outputs:
88
+ latents (`Tensor`):
89
+ Denoised latents.
90
+ """
91
+
92
+ model_name = "flux3-distilled"
93
+ block_classes = Flux3DistilledCoreDenoiseBlocks.values()
94
+ block_names = Flux3DistilledCoreDenoiseBlocks.keys()
95
+
96
+ @property
97
+ def description(self):
98
+ return (
99
+ "Core denoise step that performs the denoising process for the step-distilled FLUX.3 model, for "
100
+ "text-to-image generation."
101
+ )
102
+
103
+ @property
104
+ def outputs(self):
105
+ return [
106
+ OutputParam.template("latents"),
107
+ ]
108
+
109
+
110
+ Flux3DistilledImageConditionedCoreDenoiseBlocks = InsertableDict(
111
+ [
112
+ ("input", Flux3DistilledTextInputStep()),
113
+ ("cond_input", Flux3ImageCondInputStep()),
114
+ ("prepare_latents", Flux3PrepareLatentsStep()),
115
+ ("prepare_image_cond_latents", Flux3PrepareImageCondLatentsStep()),
116
+ ("set_timesteps", Flux3SetTimestepsStep(num_inference_steps=16)),
117
+ ("prepare_rope_inputs", Flux3DistilledTextRoPEInputsStep()),
118
+ ("denoise", Flux3DistilledDenoiseStep()),
119
+ ("after_denoise", Flux3UnpackLatentsStep()),
120
+ ]
121
+ )
122
+
123
+
124
+ # auto_docstring
125
+ class Flux3DistilledImageConditionedCoreDenoiseStep(SequentialPipelineBlocks):
126
+ """
127
+ Core denoise step that performs the denoising process for the step-distilled FLUX.3 model with reference images
128
+ (image-to-image).
129
+
130
+ Components:
131
+ transformer (`Flux3Transformer2DModel`) scheduler (`FlowMatchEulerDiscreteScheduler`)
132
+
133
+ Inputs:
134
+ num_images_per_prompt (`int`, *optional*, defaults to 1):
135
+ The number of images to generate per prompt.
136
+ prompt_embeds (`Tensor`):
137
+ Pre-generated text embeddings. Can be generated from text_encoder step.
138
+ image_cond_latents (`list`):
139
+ Normalized packed latents of the reference images, one tensor of shape (B, C, height_i//16, width_i//16)
140
+ per image
141
+ height (`int`, *optional*):
142
+ The height in pixels of the generated image; defaults to the first reference image's aspect ratio at the
143
+ default area
144
+ width (`int`, *optional*):
145
+ The width in pixels of the generated image; defaults to the first reference image's aspect ratio at the
146
+ default area
147
+ latents (`Tensor`, *optional*):
148
+ Pre-generated noisy latents for image generation.
149
+ generator (`Generator`, *optional*):
150
+ Torch generator for deterministic generation.
151
+ num_inference_steps (`int`, *optional*, defaults to 16):
152
+ The number of denoising steps.
153
+ timesteps (`Tensor`, *optional*):
154
+ Timesteps for the denoising process. (overrides `num_inference_steps` when provided)
155
+ sigmas (`list`, *optional*):
156
+ Custom sigmas for the denoising process. (overrides `num_inference_steps` when provided)
157
+ **denoiser_input_fields (`None`, *optional*):
158
+ conditional model inputs for the denoiser: e.g. prompt_embeds, negative_prompt_embeds, etc.
159
+
160
+ Outputs:
161
+ latents (`Tensor`):
162
+ Denoised latents.
163
+ """
164
+
165
+ model_name = "flux3-distilled"
166
+ block_classes = Flux3DistilledImageConditionedCoreDenoiseBlocks.values()
167
+ block_names = Flux3DistilledImageConditionedCoreDenoiseBlocks.keys()
168
+
169
+ @property
170
+ def description(self):
171
+ return (
172
+ "Core denoise step that performs the denoising process for the step-distilled FLUX.3 model with reference "
173
+ "images (image-to-image)."
174
+ )
175
+
176
+ @property
177
+ def outputs(self):
178
+ return [
179
+ OutputParam.template("latents"),
180
+ ]
181
+
182
+
183
+ # auto_docstring
184
+ class Flux3DistilledAutoCoreDenoiseStep(AutoPipelineBlocks):
185
+ """
186
+ Core denoise step that selects the step-distilled FLUX.3 denoising workflow by the conditioning inputs.
187
+ - `Flux3DistilledImageConditionedCoreDenoiseStep` is used when `image_cond_latents` is provided
188
+ (image-to-image).
189
+ - `Flux3DistilledCoreDenoiseStep` is used otherwise (text-to-image).
190
+
191
+ Components:
192
+ transformer (`Flux3Transformer2DModel`) scheduler (`FlowMatchEulerDiscreteScheduler`)
193
+
194
+ Inputs:
195
+ num_images_per_prompt (`int`, *optional*, defaults to 1):
196
+ The number of images to generate per prompt.
197
+ prompt_embeds (`Tensor`):
198
+ Pre-generated text embeddings. Can be generated from text_encoder step.
199
+ image_cond_latents (`list`, *optional*):
200
+ Normalized packed latents of the reference images, one tensor of shape (B, C, height_i//16, width_i//16)
201
+ per image
202
+ height (`int`, *optional*, defaults to None or 1024, depending on the workflow):
203
+ The height in pixels of the generated image; defaults to the first reference image's aspect ratio at the
204
+ default area
205
+ width (`int`, *optional*, defaults to None or 1024, depending on the workflow):
206
+ The width in pixels of the generated image; defaults to the first reference image's aspect ratio at the
207
+ default area
208
+ latents (`Tensor`):
209
+ Pre-generated noisy latents for image generation.
210
+ generator (`Generator`, *optional*):
211
+ Torch generator for deterministic generation.
212
+ num_inference_steps (`int`):
213
+ The number of denoising steps.
214
+ timesteps (`Tensor`):
215
+ Timesteps for the denoising process. (overrides `num_inference_steps` when provided)
216
+ sigmas (`list`, *optional*):
217
+ Custom sigmas for the denoising process. (overrides `num_inference_steps` when provided)
218
+ **denoiser_input_fields (`None`, *optional*):
219
+ conditional model inputs for the denoiser: e.g. prompt_embeds, negative_prompt_embeds, etc.
220
+
221
+ Outputs:
222
+ latents (`Tensor`):
223
+ Denoised latents.
224
+ """
225
+
226
+ model_name = "flux3-distilled"
227
+ block_classes = [Flux3DistilledImageConditionedCoreDenoiseStep, Flux3DistilledCoreDenoiseStep]
228
+ block_names = ["image_conditioned", "text2image"]
229
+ block_trigger_inputs = ["image_cond_latents", None]
230
+
231
+ @property
232
+ def description(self):
233
+ return (
234
+ "Core denoise step that selects the step-distilled FLUX.3 denoising workflow by the conditioning inputs.\n"
235
+ " - `Flux3DistilledImageConditionedCoreDenoiseStep` is used when `image_cond_latents` is provided "
236
+ "(image-to-image).\n"
237
+ " - `Flux3DistilledCoreDenoiseStep` is used otherwise (text-to-image)."
238
+ )
239
+
240
+
241
+ ###
242
+ ### Auto blocks
243
+ ###
244
+
245
+ DISTILLED_AUTO_BLOCKS = InsertableDict(
246
+ [
247
+ ("text_encoder", Flux3DistilledTextEncoderStep()),
248
+ ("vae_encoder", Flux3AutoVaeEncoderStep()),
249
+ ("denoise", Flux3DistilledAutoCoreDenoiseStep()),
250
+ ("decode", Flux3DecodeStep()),
251
+ ]
252
+ )
253
+
254
+
255
+ # auto_docstring
256
+ class Flux3DistilledAutoBlocks(SequentialPipelineBlocks):
257
+ """
258
+ Auto blocks that perform text-to-image and image-to-image generation using the step-distilled FLUX.3 model (16
259
+ steps, no classifier-free guidance).
260
+
261
+ Supported workflows:
262
+ - `text2image`: requires `prompt`
263
+ - `image_conditioned`: requires `prompt`, `image`
264
+
265
+ Components:
266
+ text_encoder (`Qwen3VLForConditionalGeneration`) tokenizer (`AutoProcessor`) image_processor
267
+ (`Flux3ImageProcessor`) vae (`AutoencoderKLFlux2`) transformer (`Flux3Transformer2DModel`) scheduler
268
+ (`FlowMatchEulerDiscreteScheduler`)
269
+
270
+ Inputs:
271
+ prompt (`str`):
272
+ The prompt to guide the generation (one prompt per call)
273
+ max_sequence_length (`int`, *optional*, defaults to 8192):
274
+ Maximum number of prompt tokens; longer prompts are truncated.
275
+ text_encoder_out_layers (`tuple`, *optional*, defaults to (4, 8, 12, 16, 20, 24, 28, 32)):
276
+ Indices of the text-encoder hidden layers whose states are stacked to form the text embeddings
277
+ image (`Image | list`, *optional*):
278
+ The reference image or images to condition the generation on; each keeps its own resolution
279
+ (center-cropped to the 16-pixel grid)
280
+ num_images_per_prompt (`int`, *optional*, defaults to 1):
281
+ The number of images to generate per prompt.
282
+ image_cond_latents (`list`, *optional*):
283
+ Normalized packed latents of the reference images, one tensor of shape (B, C, height_i//16, width_i//16)
284
+ per image
285
+ height (`int`, *optional*, defaults to None or 1024, depending on the workflow):
286
+ The height in pixels of the generated image; defaults to the first reference image's aspect ratio at the
287
+ default area
288
+ width (`int`, *optional*, defaults to None or 1024, depending on the workflow):
289
+ The width in pixels of the generated image; defaults to the first reference image's aspect ratio at the
290
+ default area
291
+ latents (`Tensor`):
292
+ Pre-generated noisy latents for image generation.
293
+ generator (`Generator`, *optional*):
294
+ Torch generator for deterministic generation.
295
+ num_inference_steps (`int`):
296
+ The number of denoising steps.
297
+ timesteps (`Tensor`):
298
+ Timesteps for the denoising process. (overrides `num_inference_steps` when provided)
299
+ sigmas (`list`, *optional*):
300
+ Custom sigmas for the denoising process. (overrides `num_inference_steps` when provided)
301
+ **denoiser_input_fields (`None`, *optional*):
302
+ conditional model inputs for the denoiser: e.g. prompt_embeds, negative_prompt_embeds, etc.
303
+ output_type (`str`, *optional*, defaults to pil):
304
+ Output format: 'pil', 'np', 'pt'.
305
+
306
+ Outputs:
307
+ images (`list`):
308
+ Generated images.
309
+ latents (`Tensor`):
310
+ Denoised latents.
311
+ """
312
+
313
+ model_name = "flux3-distilled"
314
+ block_classes = DISTILLED_AUTO_BLOCKS.values()
315
+ block_names = DISTILLED_AUTO_BLOCKS.keys()
316
+ _workflow_map = {
317
+ "text2image": {"prompt": True},
318
+ "image_conditioned": {"prompt": True, "image": True},
319
+ }
320
+
321
+ @property
322
+ def description(self):
323
+ return (
324
+ "Auto blocks that perform text-to-image and image-to-image generation using the step-distilled FLUX.3 "
325
+ "model (16 steps, no classifier-free guidance)."
326
+ )
327
+
328
+ @property
329
+ def outputs(self):
330
+ return [
331
+ OutputParam.template("images"),
332
+ OutputParam.template("latents"),
333
+ ]
diffusers/modular_pipelines/flux3/modular_pipeline.py CHANGED
@@ -46,3 +46,11 @@ class Flux3ModularPipeline(ModularPipeline):
46
  if getattr(self, "guider", None) is None:
47
  return False
48
  return self.guider._enabled and self.guider.num_conditions > 1
 
 
 
 
 
 
 
 
 
46
  if getattr(self, "guider", None) is None:
47
  return False
48
  return self.guider._enabled and self.guider.num_conditions > 1
49
+
50
+
51
+ class Flux3DistilledModularPipeline(Flux3ModularPipeline):
52
+ """
53
+ A ModularPipeline for the step-distilled FLUX.3 model (16 steps, no classifier-free guidance).
54
+ """
55
+
56
+ default_blocks_name = "Flux3DistilledAutoBlocks"
diffusers/modular_pipelines/modular_pipeline.py CHANGED
@@ -141,6 +141,7 @@ MODULAR_PIPELINE_MAPPING = OrderedDict(
141
  ("flux2", _create_default_map_fn("Flux2ModularPipeline")),
142
  ("flux2-klein", _flux2_klein_map_fn),
143
  ("flux3", _create_default_map_fn("Flux3ModularPipeline")),
 
144
  ("ideogram4", _create_default_map_fn("Ideogram4ModularPipeline")),
145
  ("krea2", _krea2_map_fn),
146
  ("qwenimage", _create_default_map_fn("QwenImageModularPipeline")),
 
141
  ("flux2", _create_default_map_fn("Flux2ModularPipeline")),
142
  ("flux2-klein", _flux2_klein_map_fn),
143
  ("flux3", _create_default_map_fn("Flux3ModularPipeline")),
144
+ ("flux3-distilled", _create_default_map_fn("Flux3DistilledModularPipeline")),
145
  ("ideogram4", _create_default_map_fn("Ideogram4ModularPipeline")),
146
  ("krea2", _krea2_map_fn),
147
  ("qwenimage", _create_default_map_fn("QwenImageModularPipeline")),
diffusers/pipelines/README.md ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🧨 Diffusers Pipelines
2
+
3
+ Pipelines provide a simple way to run state-of-the-art diffusion models in inference.
4
+ Most diffusion systems consist of multiple independently-trained models and highly adaptable scheduler
5
+ components - all of which are needed to have a functioning end-to-end diffusion system.
6
+
7
+ As an example, [Stable Diffusion](https://huggingface.co/blog/stable_diffusion) has three independently trained models:
8
+ - [Autoencoder](https://github.com/huggingface/diffusers/blob/5cbed8e0d157f65d3ddc2420dfd09f2df630e978/src/diffusers/models/vae.py#L392)
9
+ - [Conditional Unet](https://github.com/huggingface/diffusers/blob/5cbed8e0d157f65d3ddc2420dfd09f2df630e978/src/diffusers/models/unet_2d_condition.py#L12)
10
+ - [CLIP text encoder](https://huggingface.co/docs/transformers/main/en/model_doc/clip#transformers.CLIPTextModel)
11
+ - a scheduler component, [scheduler](https://github.com/huggingface/diffusers/blob/main/src/diffusers/schedulers/scheduling_pndm.py),
12
+ - a [CLIPImageProcessor](https://huggingface.co/docs/transformers/main/en/model_doc/clip#transformers.CLIPImageProcessor),
13
+ - as well as a [safety checker](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion/safety_checker.py).
14
+ All of these components are necessary to run stable diffusion in inference even though they were trained
15
+ or created independently from each other.
16
+
17
+ To that end, we strive to offer all open-sourced, state-of-the-art diffusion system under a unified API.
18
+ More specifically, we strive to provide pipelines that
19
+ - 1. can load the officially published weights and yield 1-to-1 the same outputs as the original implementation according to the corresponding paper (*e.g.* [LDMTextToImagePipeline](https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines/latent_diffusion), uses the officially released weights of [High-Resolution Image Synthesis with Latent Diffusion Models](https://huggingface.co/papers/2112.10752)),
20
+ - 2. have a simple user interface to run the model in inference (see the [Pipelines API](#pipelines-api) section),
21
+ - 3. are easy to understand with code that is self-explanatory and can be read along-side the official paper (see [Pipelines summary](#pipelines-summary)),
22
+ - 4. can easily be contributed by the community (see the [Contribution](#contribution) section).
23
+
24
+ **Note** that pipelines do not (and should not) offer any training functionality.
25
+ If you are looking for *official* training examples, please have a look at [examples](https://github.com/huggingface/diffusers/tree/main/examples).
26
+
27
+
28
+ ## Pipelines Summary
29
+
30
+ The following table summarizes all officially supported pipelines, their corresponding paper, and if
31
+ available a colab notebook to directly try them out.
32
+
33
+ | Pipeline | Source | Tasks | Colab
34
+ |-------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------|:---:|:---:|
35
+ | [dance diffusion](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/dance_diffusion) | [**Dance Diffusion**](https://github.com/Harmonai-org/sample-generator) | *Unconditional Audio Generation* |
36
+ | [ddpm](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/ddpm) | [**Denoising Diffusion Probabilistic Models**](https://huggingface.co/papers/2006.11239) | *Unconditional Image Generation* |
37
+ | [ddim](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/ddim) | [**Denoising Diffusion Implicit Models**](https://huggingface.co/papers/2010.02502) | *Unconditional Image Generation* | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/training_example.ipynb)
38
+ | [latent_diffusion](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/latent_diffusion) | [**High-Resolution Image Synthesis with Latent Diffusion Models**](https://huggingface.co/papers/2112.10752) | *Text-to-Image Generation* |
39
+ | [latent_diffusion_uncond](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/latent_diffusion_uncond) | [**High-Resolution Image Synthesis with Latent Diffusion Models**](https://huggingface.co/papers/2112.10752) | *Unconditional Image Generation* |
40
+ | [pndm](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/pndm) | [**Pseudo Numerical Methods for Diffusion Models on Manifolds**](https://huggingface.co/papers/2202.09778) | *Unconditional Image Generation* |
41
+ | [score_sde_ve](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/score_sde_ve) | [**Score-Based Generative Modeling through Stochastic Differential Equations**](https://openreview.net/forum?id=PxTIG12RRHS) | *Unconditional Image Generation* |
42
+ | [score_sde_vp](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/score_sde_vp) | [**Score-Based Generative Modeling through Stochastic Differential Equations**](https://openreview.net/forum?id=PxTIG12RRHS) | *Unconditional Image Generation* |
43
+ | [stable_diffusion](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion) | [**Stable Diffusion**](https://stability.ai/blog/stable-diffusion-public-release) | *Text-to-Image Generation* | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/stable_diffusion.ipynb)
44
+ | [stable_diffusion](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion) | [**Stable Diffusion**](https://stability.ai/blog/stable-diffusion-public-release) | *Image-to-Image Text-Guided Generation* | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/image_2_image_using_diffusers.ipynb)
45
+ | [stable_diffusion](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion) | [**Stable Diffusion**](https://stability.ai/blog/stable-diffusion-public-release) | *Text-Guided Image Inpainting* | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/in_painting_with_stable_diffusion_using_diffusers.ipynb)
46
+ | [stochastic_karras_ve](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stochastic_karras_ve) | [**Elucidating the Design Space of Diffusion-Based Generative Models**](https://huggingface.co/papers/2206.00364) | *Unconditional Image Generation* |
47
+
48
+ **Note**: Pipelines are simple examples of how to play around with the diffusion systems as described in the corresponding papers.
49
+ However, most of them can be adapted to use different scheduler components or even different model components. Some pipeline examples are shown in the [Examples](#examples) below.
50
+
51
+ ## Pipelines API
52
+
53
+ Diffusion models often consist of multiple independently-trained models or other previously existing components.
54
+
55
+
56
+ Each model has been trained independently on a different task and the scheduler can easily be swapped out and replaced with a different one.
57
+ During inference, we however want to be able to easily load all components and use them in inference - even if one component, *e.g.* CLIP's text encoder, originates from a different library, such as [Transformers](https://github.com/huggingface/transformers). To that end, all pipelines provide the following functionality:
58
+
59
+ - [`from_pretrained` method](https://github.com/huggingface/diffusers/blob/5cbed8e0d157f65d3ddc2420dfd09f2df630e978/src/diffusers/pipeline_utils.py#L139) that accepts a Hugging Face Hub repository id, *e.g.* [stable-diffusion-v1-5/stable-diffusion-v1-5](https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5) or a path to a local directory, *e.g.*
60
+ "./stable-diffusion". To correctly retrieve which models and components should be loaded, one has to provide a `model_index.json` file, *e.g.* [stable-diffusion-v1-5/stable-diffusion-v1-5/model_index.json](https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5/blob/main/model_index.json), which defines all components that should be
61
+ loaded into the pipelines. More specifically, for each model/component one needs to define the format `<name>: ["<library>", "<class name>"]`. `<name>` is the attribute name given to the loaded instance of `<class name>` which can be found in the library or pipeline folder called `"<library>"`.
62
+ - [`save_pretrained`](https://github.com/huggingface/diffusers/blob/5cbed8e0d157f65d3ddc2420dfd09f2df630e978/src/diffusers/pipeline_utils.py#L90) that accepts a local path, *e.g.* `./stable-diffusion` under which all models/components of the pipeline will be saved. For each component/model a folder is created inside the local path that is named after the given attribute name, *e.g.* `./stable_diffusion/unet`.
63
+ In addition, a `model_index.json` file is created at the root of the local path, *e.g.* `./stable_diffusion/model_index.json` so that the complete pipeline can again be instantiated
64
+ from the local path.
65
+ - [`to`](https://github.com/huggingface/diffusers/blob/5cbed8e0d157f65d3ddc2420dfd09f2df630e978/src/diffusers/pipeline_utils.py#L118) which accepts a `string` or `torch.device` to move all models that are of type `torch.nn.Module` to the passed device. The behavior is fully analogous to [PyTorch's `to` method](https://pytorch.org/docs/stable/generated/torch.nn.Module.html#torch.nn.Module.to).
66
+ - [`__call__`] method to use the pipeline in inference. `__call__` defines inference logic of the pipeline and should ideally encompass all aspects of it, from pre-processing to forwarding tensors to the different models and schedulers, as well as post-processing. The API of the `__call__` method can strongly vary from pipeline to pipeline. *E.g.* a text-to-image pipeline, such as [`StableDiffusionPipeline`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion.py) should accept among other things the text prompt to generate the image. A pure image generation pipeline, such as [DDPMPipeline](https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines/ddpm) on the other hand can be run without providing any inputs. To better understand what inputs can be adapted for
67
+ each pipeline, one should look directly into the respective pipeline.
68
+
69
+ **Note**: All pipelines have PyTorch's autograd disabled by decorating the `__call__` method with a [`torch.no_grad`](https://pytorch.org/docs/stable/generated/torch.no_grad.html) decorator because pipelines should
70
+ not be used for training. If you want to store the gradients during the forward pass, we recommend writing your own pipeline, see also our [community-examples](https://github.com/huggingface/diffusers/tree/main/examples/community)
71
+
72
+ ## Contribution
73
+
74
+ We are more than happy about any contribution to the officially supported pipelines 🤗. We aspire
75
+ all of our pipelines to be **self-contained**, **easy-to-tweak**, **beginner-friendly** and for **one-purpose-only**.
76
+
77
+ - **Self-contained**: A pipeline shall be as self-contained as possible. More specifically, this means that all functionality should be either directly defined in the pipeline file itself, should be inherited from (and only from) the [`DiffusionPipeline` class](https://github.com/huggingface/diffusers/blob/5cbed8e0d157f65d3ddc2420dfd09f2df630e978/src/diffusers/pipeline_utils.py#L56) or be directly attached to the model and scheduler components of the pipeline.
78
+ - **Easy-to-use**: Pipelines should be extremely easy to use - one should be able to load the pipeline and
79
+ use it for its designated task, *e.g.* text-to-image generation, in just a couple of lines of code. Most
80
+ logic including pre-processing, an unrolled diffusion loop, and post-processing should all happen inside the `__call__` method.
81
+ - **Easy-to-tweak**: Certain pipelines will not be able to handle all use cases and tasks that you might like them to. If you want to use a certain pipeline for a specific use case that is not yet supported, you might have to copy the pipeline file and tweak the code to your needs. We try to make the pipeline code as readable as possible so that each part –from pre-processing to diffusing to post-processing– can easily be adapted. If you would like the community to benefit from your customized pipeline, we would love to see a contribution to our [community-examples](https://github.com/huggingface/diffusers/tree/main/examples/community). If you feel that an important pipeline should be part of the official pipelines but isn't, a contribution to the [official pipelines](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines) would be even better.
82
+ - **One-purpose-only**: Pipelines should be used for one task and one task only. Even if two tasks are very similar from a modeling point of view, *e.g.* image2image translation and in-painting, pipelines shall be used for one task only to keep them *easy-to-tweak* and *readable*.
83
+
84
+ ## Examples
85
+
86
+ ### Text-to-Image generation with Stable Diffusion
87
+
88
+ ```python
89
+ # make sure you're logged in with `hf auth login`
90
+ from diffusers import StableDiffusionPipeline, LMSDiscreteScheduler
91
+
92
+ pipe = StableDiffusionPipeline.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5")
93
+ pipe = pipe.to("cuda")
94
+
95
+ prompt = "a photo of an astronaut riding a horse on mars"
96
+ image = pipe(prompt).images[0]
97
+
98
+ image.save("astronaut_rides_horse.png")
99
+ ```
100
+
101
+ ### Image-to-Image text-guided generation with Stable Diffusion
102
+
103
+ The `StableDiffusionImg2ImgPipeline` lets you pass a text prompt and an initial image to condition the generation of new images.
104
+
105
+ ```python
106
+ import requests
107
+ from PIL import Image
108
+ from io import BytesIO
109
+
110
+ from diffusers import StableDiffusionImg2ImgPipeline
111
+
112
+ # load the pipeline
113
+ device = "cuda"
114
+ pipe = StableDiffusionImg2ImgPipeline.from_pretrained(
115
+ "stable-diffusion-v1-5/stable-diffusion-v1-5",
116
+ torch_dtype=torch.float16,
117
+ ).to(device)
118
+
119
+ # let's download an initial image
120
+ url = "https://raw.githubusercontent.com/CompVis/stable-diffusion/main/assets/stable-samples/img2img/sketch-mountains-input.jpg"
121
+
122
+ response = requests.get(url)
123
+ init_image = Image.open(BytesIO(response.content)).convert("RGB")
124
+ init_image = init_image.resize((768, 512))
125
+
126
+ prompt = "A fantasy landscape, trending on artstation"
127
+
128
+ images = pipe(prompt=prompt, image=init_image, strength=0.75, guidance_scale=7.5).images
129
+
130
+ images[0].save("fantasy_landscape.png")
131
+ ```
132
+ You can also run this example on colab [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/image_2_image_using_diffusers.ipynb)
133
+
134
+ ### Tweak prompts reusing seeds and latents
135
+
136
+ You can generate your own latents to reproduce results, or tweak your prompt on a specific result you liked. [This notebook](https://github.com/pcuenca/diffusers-examples/blob/main/notebooks/stable-diffusion-seeds.ipynb) shows how to do it step by step. You can also run it in Google Colab [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/pcuenca/diffusers-examples/blob/main/notebooks/stable-diffusion-seeds.ipynb).
137
+
138
+
139
+ ### In-painting using Stable Diffusion
140
+
141
+ The `StableDiffusionInpaintPipeline` lets you edit specific parts of an image by providing a mask and text prompt.
142
+
143
+ ```python
144
+ import PIL
145
+ import requests
146
+ import torch
147
+ from io import BytesIO
148
+
149
+ from diffusers import StableDiffusionInpaintPipeline
150
+
151
+ def download_image(url):
152
+ response = requests.get(url)
153
+ return PIL.Image.open(BytesIO(response.content)).convert("RGB")
154
+
155
+ img_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo.png"
156
+ mask_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo_mask.png"
157
+
158
+ init_image = download_image(img_url).resize((512, 512))
159
+ mask_image = download_image(mask_url).resize((512, 512))
160
+
161
+ pipe = StableDiffusionInpaintPipeline.from_pretrained(
162
+ "stable-diffusion-v1-5/stable-diffusion-inpainting",
163
+ torch_dtype=torch.float16,
164
+ )
165
+ pipe = pipe.to("cuda")
166
+
167
+ prompt = "Face of a yellow cat, high resolution, sitting on a park bench"
168
+ image = pipe(prompt=prompt, image=init_image, mask_image=mask_image).images[0]
169
+ ```
170
+
171
+ You can also run this example on colab [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/in_painting_with_stable_diffusion_using_diffusers.ipynb)
diffusers/pipelines/deprecated/README.md ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # Deprecated Pipelines
2
+
3
+ This folder contains pipelines that have very low usage as measured by model downloads, issues and PRs. While you can still use the pipelines just as before, we will stop testing the pipelines and will not accept any changes to existing files.
diffusers/pipelines/stable_diffusion/README.md ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Stable Diffusion
2
+
3
+ ## Overview
4
+
5
+ Stable Diffusion was proposed in [Stable Diffusion Announcement](https://stability.ai/blog/stable-diffusion-announcement) by Patrick Esser and Robin Rombach and the Stability AI team.
6
+
7
+ The summary of the model is the following:
8
+
9
+ *Stable Diffusion is a text-to-image model that will empower billions of people to create stunning art within seconds. It is a breakthrough in speed and quality meaning that it can run on consumer GPUs. You can see some of the amazing output that has been created by this model without pre or post-processing on this page. The model itself builds upon the work of the team at CompVis and Runway in their widely used latent diffusion model combined with insights from the conditional diffusion models by our lead generative AI developer Katherine Crowson, Dall-E 2 by Open AI, Imagen by Google Brain and many others. We are delighted that AI media generation is a cooperative field and hope it can continue this way to bring the gift of creativity to all.*
10
+
11
+ ## Tips:
12
+
13
+ - Stable Diffusion has the same architecture as [Latent Diffusion](https://huggingface.co/papers/2112.10752) but uses a frozen CLIP Text Encoder instead of training the text encoder jointly with the diffusion model.
14
+ - An in-detail explanation of the Stable Diffusion model can be found under [Stable Diffusion with 🧨 Diffusers](https://huggingface.co/blog/stable_diffusion).
15
+ - If you don't want to rely on the Hugging Face Hub and having to pass a authentication token, you can
16
+ download the weights with `git lfs install; git clone https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5` and instead pass the local path to the cloned folder to `from_pretrained` as shown below.
17
+ - Stable Diffusion can work with a variety of different samplers as is shown below.
18
+
19
+ ## Available Pipelines:
20
+
21
+ | Pipeline | Tasks | Colab
22
+ |---|---|:---:|
23
+ | [pipeline_stable_diffusion.py](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion.py) | *Text-to-Image Generation* | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/training_example.ipynb)
24
+ | [pipeline_stable_diffusion_img2img](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion_img2img.py) | *Image-to-Image Text-Guided Generation* | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/image_2_image_using_diffusers.ipynb)
25
+ | [pipeline_stable_diffusion_inpaint](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion_inpaint.py) | *Text-Guided Image Inpainting* | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/in_painting_with_stable_diffusion_using_diffusers.ipynb)
26
+
27
+ ## Examples:
28
+
29
+ ### Using Stable Diffusion without being logged into the Hub.
30
+
31
+ If you want to download the model weights using a single Python line, you need to be logged in via `hf auth login`.
32
+
33
+ ```python
34
+ from diffusers import DiffusionPipeline
35
+
36
+ pipeline = DiffusionPipeline.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5")
37
+ ```
38
+
39
+ This however can make it difficult to build applications on top of `diffusers` as you will always have to pass the token around. A potential way to solve this issue is by downloading the weights to a local path `"./stable-diffusion-v1-5"`:
40
+
41
+ ```
42
+ git lfs install
43
+ git clone https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5
44
+ ```
45
+
46
+ and simply passing the local path to `from_pretrained`:
47
+
48
+ ```python
49
+ from diffusers import StableDiffusionPipeline
50
+
51
+ pipe = StableDiffusionPipeline.from_pretrained("./stable-diffusion-v1-5")
52
+ ```
53
+
54
+ ### Text-to-Image with default PLMS scheduler
55
+
56
+ ```python
57
+ # make sure you're logged in with `hf auth login`
58
+ from diffusers import StableDiffusionPipeline
59
+
60
+ pipe = StableDiffusionPipeline.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5")
61
+ pipe = pipe.to("cuda")
62
+
63
+ prompt = "a photo of an astronaut riding a horse on mars"
64
+ image = pipe(prompt).images[0]
65
+
66
+ image.save("astronaut_rides_horse.png")
67
+ ```
68
+
69
+ ### Text-to-Image with DDIM scheduler
70
+
71
+ ```python
72
+ # make sure you're logged in with `hf auth login`
73
+ from diffusers import StableDiffusionPipeline, DDIMScheduler
74
+
75
+ scheduler = DDIMScheduler.from_pretrained("CompVis/stable-diffusion-v1-4", subfolder="scheduler")
76
+
77
+ pipe = StableDiffusionPipeline.from_pretrained(
78
+ "stable-diffusion-v1-5/stable-diffusion-v1-5",
79
+ scheduler=scheduler,
80
+ ).to("cuda")
81
+
82
+ prompt = "a photo of an astronaut riding a horse on mars"
83
+ image = pipe(prompt).images[0]
84
+
85
+ image.save("astronaut_rides_horse.png")
86
+ ```
87
+
88
+ ### Text-to-Image with K-LMS scheduler
89
+
90
+ ```python
91
+ # make sure you're logged in with `hf auth login`
92
+ from diffusers import StableDiffusionPipeline, LMSDiscreteScheduler
93
+
94
+ lms = LMSDiscreteScheduler.from_pretrained("CompVis/stable-diffusion-v1-4", subfolder="scheduler")
95
+
96
+ pipe = StableDiffusionPipeline.from_pretrained(
97
+ "stable-diffusion-v1-5/stable-diffusion-v1-5",
98
+ scheduler=lms,
99
+ ).to("cuda")
100
+
101
+ prompt = "a photo of an astronaut riding a horse on mars"
102
+ image = pipe(prompt).images[0]
103
+
104
+ image.save("astronaut_rides_horse.png")
105
+ ```
106
+
107
+ ### CycleDiffusion using Stable Diffusion and DDIM scheduler
108
+
109
+ ```python
110
+ import requests
111
+ import torch
112
+ from PIL import Image
113
+ from io import BytesIO
114
+
115
+ from diffusers import CycleDiffusionPipeline, DDIMScheduler
116
+
117
+
118
+ # load the scheduler. CycleDiffusion only supports stochastic schedulers.
119
+
120
+ # load the pipeline
121
+ # make sure you're logged in with `hf auth login`
122
+ model_id_or_path = "CompVis/stable-diffusion-v1-4"
123
+ scheduler = DDIMScheduler.from_pretrained(model_id_or_path, subfolder="scheduler")
124
+ pipe = CycleDiffusionPipeline.from_pretrained(model_id_or_path, scheduler=scheduler).to("cuda")
125
+
126
+ # let's download an initial image
127
+ url = "https://raw.githubusercontent.com/ChenWu98/cycle-diffusion/main/data/dalle2/An%20astronaut%20riding%20a%20horse.png"
128
+ response = requests.get(url)
129
+ init_image = Image.open(BytesIO(response.content)).convert("RGB")
130
+ init_image = init_image.resize((512, 512))
131
+ init_image.save("horse.png")
132
+
133
+ # let's specify a prompt
134
+ source_prompt = "An astronaut riding a horse"
135
+ prompt = "An astronaut riding an elephant"
136
+
137
+ # call the pipeline
138
+ image = pipe(
139
+ prompt=prompt,
140
+ source_prompt=source_prompt,
141
+ image=init_image,
142
+ num_inference_steps=100,
143
+ eta=0.1,
144
+ strength=0.8,
145
+ guidance_scale=2,
146
+ source_guidance_scale=1,
147
+ ).images[0]
148
+
149
+ image.save("horse_to_elephant.png")
150
+
151
+ # let's try another example
152
+ # See more samples at the original repo: https://github.com/ChenWu98/cycle-diffusion
153
+ url = "https://raw.githubusercontent.com/ChenWu98/cycle-diffusion/main/data/dalle2/A%20black%20colored%20car.png"
154
+ response = requests.get(url)
155
+ init_image = Image.open(BytesIO(response.content)).convert("RGB")
156
+ init_image = init_image.resize((512, 512))
157
+ init_image.save("black.png")
158
+
159
+ source_prompt = "A black colored car"
160
+ prompt = "A blue colored car"
161
+
162
+ # call the pipeline
163
+ torch.manual_seed(0)
164
+ image = pipe(
165
+ prompt=prompt,
166
+ source_prompt=source_prompt,
167
+ image=init_image,
168
+ num_inference_steps=100,
169
+ eta=0.1,
170
+ strength=0.85,
171
+ guidance_scale=3,
172
+ source_guidance_scale=1,
173
+ ).images[0]
174
+
175
+ image.save("black_to_blue.png")
176
+ ```
diffusers/schedulers/README.md ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # Schedulers
2
+
3
+ For more information on the schedulers, please refer to the [docs](https://huggingface.co/docs/diffusers/api/schedulers/overview).
diffusers/utils/dummy_torch_and_transformers_objects.py CHANGED
@@ -227,6 +227,36 @@ class Flux3AutoBlocks(metaclass=DummyObject):
227
  requires_backends(cls, ["torch", "transformers"])
228
 
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  class Flux3ModularPipeline(metaclass=DummyObject):
231
  _backends = ["torch", "transformers"]
232
 
 
227
  requires_backends(cls, ["torch", "transformers"])
228
 
229
 
230
+ class Flux3DistilledAutoBlocks(metaclass=DummyObject):
231
+ _backends = ["torch", "transformers"]
232
+
233
+ def __init__(self, *args, **kwargs):
234
+ requires_backends(self, ["torch", "transformers"])
235
+
236
+ @classmethod
237
+ def from_config(cls, *args, **kwargs):
238
+ requires_backends(cls, ["torch", "transformers"])
239
+
240
+ @classmethod
241
+ def from_pretrained(cls, *args, **kwargs):
242
+ requires_backends(cls, ["torch", "transformers"])
243
+
244
+
245
+ class Flux3DistilledModularPipeline(metaclass=DummyObject):
246
+ _backends = ["torch", "transformers"]
247
+
248
+ def __init__(self, *args, **kwargs):
249
+ requires_backends(self, ["torch", "transformers"])
250
+
251
+ @classmethod
252
+ def from_config(cls, *args, **kwargs):
253
+ requires_backends(cls, ["torch", "transformers"])
254
+
255
+ @classmethod
256
+ def from_pretrained(cls, *args, **kwargs):
257
+ requires_backends(cls, ["torch", "transformers"])
258
+
259
+
260
  class Flux3ModularPipeline(metaclass=DummyObject):
261
  _backends = ["torch", "transformers"]
262
 
examples/cat_window.webp CHANGED

Git LFS Details

  • SHA256: caf5b845a31a95fde1f9ef640cc7031b59dd2a634d26b518ea89ce2d4785710f
  • Pointer size: 130 Bytes
  • Size of remote file: 46.2 kB
examples/woman1.webp CHANGED

Git LFS Details

  • SHA256: e998d44c16081047a538457ef257a96100cacbfea0825c9182115098bbfe5cbd
  • Pointer size: 130 Bytes
  • Size of remote file: 20.4 kB
requirements.txt CHANGED
@@ -13,3 +13,4 @@ pillow
13
  huggingface_hub>=1.31.0,<2.0
14
  gradio==5.49.1
15
  spaces
 
 
13
  huggingface_hub>=1.31.0,<2.0
14
  gradio==5.49.1
15
  spaces
16
+ aokit