YiYiXu HF Staff commited on
Commit
8540d8b
·
verified ·
1 Parent(s): 387a4de

flux2 latent preview via Modular Diffusers streaming API (pipe.stream, diffusers PR #14159)

Browse files
Files changed (3) hide show
  1. README.md +13 -6
  2. app.py +163 -0
  3. requirements.txt +5 -0
README.md CHANGED
@@ -1,13 +1,20 @@
1
  ---
2
  title: Flux2 Latent Preview
3
- emoji: 👀
4
- colorFrom: pink
5
- colorTo: yellow
6
  sdk: gradio
7
- sdk_version: 6.24.0
8
- python_version: '3.13'
9
  app_file: app.py
10
  pinned: false
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
1
  ---
2
  title: Flux2 Latent Preview
3
+ emoji: 🌊
4
+ colorFrom: yellow
5
+ colorTo: indigo
6
  sdk: gradio
7
+ sdk_version: 6.5.1
8
+ python_version: '3.12'
9
  app_file: app.py
10
  pinned: false
11
  ---
12
 
13
+ Live latent preview for FLUX.2 [dev] using the Modular Diffusers streaming API (`pipe.stream()`)
14
+ from [huggingface/diffusers#14159](https://github.com/huggingface/diffusers/pull/14159).
15
+
16
+ Unlike the [flux-latent-preview](https://huggingface.co/spaces/diffusers-internal-dev/flux-latent-preview)
17
+ space (which inserts a custom preview block into the denoise loop and drains a queue from a worker
18
+ thread), this app just iterates `pipe.stream()` — the pipeline yields its live state after every
19
+ denoising step — and renders each state with a preview pipeline assembled from flux2's own
20
+ unpack + decode blocks, sharing the main pipeline's VAE.
app.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+
3
+ import gradio as gr
4
+ import numpy as np
5
+ import spaces
6
+ import torch
7
+
8
+ from diffusers import ModularPipeline
9
+ from diffusers.modular_pipelines import SequentialPipelineBlocks
10
+ from diffusers.modular_pipelines.flux2.decoders import Flux2DecodeStep, Flux2UnpackLatentsStep
11
+
12
+
13
+ pipe = ModularPipeline.from_pretrained("diffusers/flux2-bnb-4bit-modular")
14
+ pipe.load_components(torch_dtype=torch.bfloat16, device_map="cuda")
15
+
16
+ # The live-preview decoder is a second pipeline assembled from flux2's own blocks,
17
+ # sharing the main pipeline's VAE — no custom block, no queue, no thread.
18
+ preview = SequentialPipelineBlocks.from_blocks_dict(
19
+ {"unpack": Flux2UnpackLatentsStep(), "decode": Flux2DecodeStep()}
20
+ ).init_pipeline()
21
+ preview.update_components(vae=pipe.vae)
22
+
23
+ MAX_SEED = np.iinfo(np.int32).max
24
+ MAX_IMAGE_SIZE = 2048
25
+
26
+
27
+ @spaces.GPU(duration=120)
28
+ def infer(
29
+ prompt,
30
+ seed=42,
31
+ randomize_seed=False,
32
+ width=1024,
33
+ height=1024,
34
+ guidance_scale=4.0,
35
+ num_inference_steps=28,
36
+ progress=gr.Progress(track_tqdm=True),
37
+ ):
38
+ if randomize_seed:
39
+ seed = random.randint(0, MAX_SEED)
40
+ generator = torch.Generator().manual_seed(seed)
41
+
42
+ # `pipe.stream()` yields an event with the live pipeline state after every denoising step
43
+ stream = pipe.stream(
44
+ prompt=prompt,
45
+ guidance_scale=guidance_scale,
46
+ num_inference_steps=num_inference_steps,
47
+ width=width,
48
+ height=height,
49
+ generator=generator,
50
+ )
51
+ while True:
52
+ try:
53
+ event = next(stream)
54
+ except StopIteration as e:
55
+ state = e.value
56
+ break
57
+ # flow matching: after step i the latents sit at sigmas[i + 1]; project to the predicted
58
+ # clean image x0 = x_t - sigma * v so the preview shows the image forming, not noise
59
+ latents = event.state.get("latents")
60
+ sigma = pipe.scheduler.sigmas[event.loop_kwargs["i"] + 1].to(latents.device, latents.dtype)
61
+ x0 = latents - sigma * event.state.get("noise_pred")
62
+ image = preview(
63
+ latents=x0,
64
+ latent_ids=event.state.get("latent_ids"),
65
+ output="images",
66
+ )[0]
67
+ yield image, seed
68
+
69
+ # the final image decoded by the pipeline's own decode step
70
+ yield state.get("images")[0], seed
71
+
72
+
73
+ examples = [
74
+ "a tiny astronaut hatching from an egg on the moon",
75
+ "a cat holding a sign that says hello world",
76
+ "an anime illustration of a wiener schnitzel",
77
+ ]
78
+
79
+ css = """
80
+ #col-container {
81
+ margin: 0 auto;
82
+ max-width: 520px;
83
+ }
84
+ """
85
+
86
+ with gr.Blocks() as demo:
87
+ with gr.Column(elem_id="col-container"):
88
+ gr.Markdown(
89
+ """# FLUX.2 [dev] — Live Preview with Modular Diffusers
90
+ Live latent preview powered by `pipe.stream()`: the pipeline yields its live state after every
91
+ denoising step, and a preview pipeline built from flux2's own unpack + decode blocks renders it.
92
+ No custom blocks, queues, or threads — see [huggingface/diffusers#14159](https://github.com/huggingface/diffusers/pull/14159).
93
+ """
94
+ )
95
+
96
+ with gr.Row():
97
+ prompt = gr.Text(
98
+ label="Prompt",
99
+ show_label=False,
100
+ max_lines=1,
101
+ placeholder="Enter your prompt",
102
+ container=False,
103
+ )
104
+
105
+ run_button = gr.Button("Run", scale=0)
106
+
107
+ result = gr.Image(label="Result", show_label=False)
108
+
109
+ with gr.Accordion("Advanced Settings", open=False):
110
+ seed = gr.Slider(
111
+ label="Seed",
112
+ minimum=0,
113
+ maximum=MAX_SEED,
114
+ step=1,
115
+ value=0,
116
+ )
117
+
118
+ randomize_seed = gr.Checkbox(label="Randomize seed", value=True)
119
+
120
+ with gr.Row():
121
+ width = gr.Slider(
122
+ label="Width",
123
+ minimum=256,
124
+ maximum=MAX_IMAGE_SIZE,
125
+ step=32,
126
+ value=1024,
127
+ )
128
+
129
+ height = gr.Slider(
130
+ label="Height",
131
+ minimum=256,
132
+ maximum=MAX_IMAGE_SIZE,
133
+ step=32,
134
+ value=1024,
135
+ )
136
+
137
+ with gr.Row():
138
+ guidance_scale = gr.Slider(
139
+ label="Guidance Scale",
140
+ minimum=1,
141
+ maximum=15,
142
+ step=0.1,
143
+ value=4.0,
144
+ )
145
+
146
+ num_inference_steps = gr.Slider(
147
+ label="Number of inference steps",
148
+ minimum=1,
149
+ maximum=50,
150
+ step=1,
151
+ value=28,
152
+ )
153
+
154
+ gr.Examples(examples=examples, fn=infer, inputs=[prompt], outputs=[result, seed], cache_examples=False)
155
+
156
+ gr.on(
157
+ triggers=[run_button.click, prompt.submit],
158
+ fn=infer,
159
+ inputs=[prompt, seed, randomize_seed, width, height, guidance_scale, num_inference_steps],
160
+ outputs=[result, seed],
161
+ )
162
+
163
+ demo.launch(css=css)
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ accelerate
2
+ git+https://github.com/huggingface/diffusers.git@refs/pull/14159/head
3
+ torch
4
+ transformers
5
+ bitsandbytes