Pipelines¶
mlx_diffuser.pipelines.DiffusionPipeline
¶
Source code in src/mlx_diffuser/pipelines/base.py
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | |
denoising_loop(scheduler, latents, predict, key, progress=False)
¶
Run the reverse process, calling predict(scaled_latents, t) per step.
predict returns the (already guidance-combined) model output. Evaluation
is forced once per step to keep the lazy graph bounded.
Source code in src/mlx_diffuser/pipelines/base.py
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | |
mlx_diffuser.pipelines.ClassConditionalPipeline
¶
Bases: DiffusionPipeline
Source code in src/mlx_diffuser/pipelines/class_conditional.py
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | |
mlx_diffuser.pipelines.TextToVideoPipeline
¶
Bases: DiffusionPipeline
Source code in src/mlx_diffuser/pipelines/text_to_video.py
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | |
__call__(prompt_embeds, *, negative_embeds=None, num_frames=16, height=256, width=256, num_inference_steps=50, guidance_scale=5.0, seed=None, key=None, compile=True, decode=True, progress=False)
¶
Generate video(s) from text embeddings.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prompt_embeds
|
array
|
|
required |
negative_embeds
|
array | None
|
|
None
|
num_frames
|
int
|
number of output frames. |
16
|
height
|
int
|
output height in pixels. |
256
|
width
|
int
|
output width in pixels. |
256
|
decode
|
bool
|
if |
True
|
Returns:
| Type | Description |
|---|---|
array
|
|
array
|
|
Source code in src/mlx_diffuser/pipelines/text_to_video.py
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | |
mlx_diffuser.pipelines.WanPipeline
¶
WAN 2.1 text-to-video pipeline (channels-last (B, T, H, W, C)).
Source code in src/mlx_diffuser/pipelines/wan.py
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | |
__call__(prompt, *, negative_prompt='', num_frames=17, height=256, width=256, num_inference_steps=40, guidance_scale=5.0, seed=0, cache_threshold=0.0, release_text_encoder=True, progress=True)
¶
Generate a video (1, num_frames, height, width, 3) in [-1, 1].
num_frames must satisfy (num_frames - 1) % 4 == 0 and height /
width must be multiples of 8 (the VAE's spatial compression).
cache_threshold enables First-Block Cache (0 = off, exact). On the
1.3B model, 0.1 ≈ 1.5x and 0.2 ≈ 2.2x faster with no visible quality
change; >= 0.3 starts to degrade.
Source code in src/mlx_diffuser/pipelines/wan.py
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | |
encode_text(prompt)
¶
Tokenize and encode prompt into (1, L, text_dim) embeddings.
Source code in src/mlx_diffuser/pipelines/wan.py
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | |
from_diffusers(folder, *, text_encoder=None, quantize_text=4, quantize_transformer=None, transformer_dtype=mx.bfloat16, shift=3.0)
classmethod
¶
Load + convert an official Wan2.1-T2V-*-Diffusers folder into MLX.
quantize_text keeps the 5.6B umT5 encoder small (4-bit ≈ 3 GB);
quantize_transformer optionally quantizes the DiT (defaults to bf16).
text_encoder overrides where the umT5 weights come from — a folder or a
single .safetensors file (e.g. an fp16 community checkpoint, half the
size of the fp32 default); falls back to folder/text_encoder.
Source code in src/mlx_diffuser/pipelines/wan.py
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | |
release_text_encoder()
¶
Free the text encoder (call after encoding to reclaim memory).
Source code in src/mlx_diffuser/pipelines/wan.py
113 114 115 116 117 | |
mlx_diffuser.pipelines.StableDiffusionXLPipeline
¶
SDXL base text-to-image pipeline (channels-last (B, H, W, C)).
Source code in src/mlx_diffuser/pipelines/sdxl.py
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | |
__call__(prompt, *, negative_prompt='', image=None, strength=0.8, height=1024, width=1024, num_inference_steps=30, guidance_scale=5.0, seed=0, cache_interval=1, tile_vae=False, release_text_encoders=False, progress=True)
¶
Generate an image (1, height, width, 3) in [-1, 1].
Pass image (a path, PIL image, or HWC/BHWC array) for image-to-image;
strength controls how far the result may move from the input (0 < strength
<= 1). height / width must be multiples of 8. cache_interval enables
DeepCache (1 = off/exact; 2 ≈ 1.5-1.8x by skipping the deep UNet blocks on every
other step). tile_vae decodes the VAE in tiles to bound memory at high res.
release_text_encoders reclaims both CLIP encoders before denoising.
Source code in src/mlx_diffuser/pipelines/sdxl.py
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | |
encode_prompt(prompt)
¶
Tokenize + encode with both CLIPs.
Returns (prompt_embeds (1, 77, 2048), pooled (1, 1280)): the two encoders'
penultimate hidden states concatenated, and the bigG projected pooled embed.
Source code in src/mlx_diffuser/pipelines/sdxl.py
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | |
from_diffusers(folder, *, dtype=mx.float16, quantize_unet=None)
classmethod
¶
Load + convert a stable-diffusion-xl-base folder into MLX.
The UNet and text encoders run in dtype (fp16 by default, SDXL's native
precision); the VAE runs in fp32 because SDXL's VAE overflows fp16.
quantize_unet (4/8) weight-quantizes the UNet to save memory.
Source code in src/mlx_diffuser/pipelines/sdxl.py
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | |
release_text_encoder_models()
¶
Release both CLIP encoders after prompt encoding to reduce peak memory.
Source code in src/mlx_diffuser/pipelines/sdxl.py
125 126 127 128 129 130 | |
mlx_diffuser.pipelines.FluxPipeline
¶
FLUX.1 text-to-image pipeline (channels-last (B, H, W, C)).
Source code in src/mlx_diffuser/pipelines/flux.py
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | |
__call__(prompt, *, height=1024, width=1024, num_inference_steps=4, guidance_scale=0.0, max_sequence_length=256, seed=0, cache_threshold=0.0, release_text_encoders=False, tile_vae=False, progress=True)
¶
Generate an image (1, height, width, 3) in [-1, 1].
height / width must be multiples of 16 (8× VAE × 2× patch packing).
For schnell use the defaults (4 steps, guidance_scale=0); for dev use ~50
steps and guidance_scale≈3.5. cache_threshold (>0) enables First-Block
caching for a speed-up. release_text_encoders frees CLIP + T5 right after
encoding to lower peak memory before the denoising loop. tile_vae decodes the
final latent in tiles: at 1024px the fp32 VAE decode is the memory high-water mark
(~18 GB), and tiling brings it under 16 GB so it fits a 16 GB Mac without swapping.
Source code in src/mlx_diffuser/pipelines/flux.py
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | |
encode_prompt(prompt, max_sequence_length)
¶
Encode prompt with CLIP (pooled) and T5 (sequence).
Returns (t5_embeds (1, L, 4096), pooled (1, 768)): FLUX conditions joint
attention on the T5 token sequence and modulation on CLIP's pooled embedding.
Source code in src/mlx_diffuser/pipelines/flux.py
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | |
from_diffusers(folder, *, dtype=mx.bfloat16, quantize_transformer=4, quantize_t5=4)
classmethod
¶
Load + convert a FLUX.1-schnell / FLUX.1-dev folder into MLX.
The transformer and CLIP run in dtype (bf16); the VAE runs in fp32. The
12B transformer and the T5 encoder default to 4-bit weights so the pipeline
fits in ~10 GB; pass quantize_transformer=None / quantize_t5=None for
full precision (needs far more memory).
Source code in src/mlx_diffuser/pipelines/flux.py
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | |
release_text_encoders()
¶
Free the CLIP + T5 encoders after encoding (they are ~half the footprint).
Source code in src/mlx_diffuser/pipelines/flux.py
105 106 107 108 109 110 | |
mlx_diffuser.pipelines.LTX2Pipeline
¶
LTX-2.3 text-to-video-with-audio (channels-last MLX tensors).
Calling the pipeline returns (video, audio): frames (1, F, H, W, 3)
in [-1, 1] and a 48 kHz stereo waveform (2, samples) in [-1, 1].
Source code in src/mlx_diffuser/pipelines/ltx2.py
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | |
__call__(prompt, *, negative_prompt='', height=512, width=768, num_frames=121, frame_rate=24.0, guidance_scale=1.0, seed=0, cache_threshold=0.0, release_stages=True, progress=True)
¶
Generate (video, audio) for prompt.
video is (1, num_frames, height, width, 3) in [-1, 1]; audio
is a 48 kHz stereo waveform (2, samples) in [-1, 1] covering the
same duration. height/width must be multiples of 32 and
num_frames must be 1 + 8*k (the VAE's compression grid). The
distilled model runs its fixed 8-step schedule at guidance_scale=1
(no CFG); values > 1 add a classifier-free pass against
negative_prompt.
release_stages frees each component when the next stage begins —
essential on 16 GB machines, only worth disabling for repeated calls on
big-memory hosts.
Source code in src/mlx_diffuser/pipelines/ltx2.py
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | |
encode_prompt(prompt)
¶
Encode prompt into per-modality text streams (video, audio).
Loads the Gemma encoder + connectors on first use; call
:meth:release_text_stack afterwards to reclaim ~8 GB.
Source code in src/mlx_diffuser/pipelines/ltx2.py
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | |
from_converted(folder)
classmethod
¶
Open a converted LTX-2.3 checkpoint folder (components load lazily).
Source code in src/mlx_diffuser/pipelines/ltx2.py
80 81 82 83 | |
Inference caching¶
mlx_diffuser.caching.FirstBlockCache
¶
Decide, per step, whether to reuse the cached transformer residual.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
threshold
|
float
|
accumulated relative first-block change that triggers a full
recompute. |
0.0
|
Source code in src/mlx_diffuser/caching.py
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | |
reset()
¶
Clear state before a new generation.
Source code in src/mlx_diffuser/caching.py
76 77 78 79 80 81 82 | |
should_reuse(first_residual)
¶
Given this step's first-block contribution, return whether to reuse.
Accumulates the relative L1 change of the first block's residual (its output
minus its input); while the running total stays under threshold we reuse,
and we force a recompute (resetting the accumulator) once it crosses. The
first step, and any step before a residual exists, always recomputes.
Source code in src/mlx_diffuser/caching.py
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | |
mlx_diffuser.caching.DeepCache
¶
DeepCache for U-Net diffusion: skip the deep blocks on most steps.
A U-Net's deep (bottleneck) features change slowly across denoising steps, while
the shallow blocks carry the high-frequency detail. DeepCache recomputes the full
network only every interval steps; in between it runs just the shallowest
down/up blocks and reuses the cached deep feature — skipping the most expensive
levels (for SDXL, the 1280-channel / 10-transformer-layer blocks).
interval = 1 disables caching (every step full). interval = 2 caches every
other step (~1.5-1.8x). The first step is always full (no cache yet).
Source code in src/mlx_diffuser/caching.py
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | |
should_reuse()
¶
Return whether this step should reuse the cached deep feature.
Source code in src/mlx_diffuser/caching.py
48 49 50 51 52 53 54 | |