Image-Text-to-Video
Diffusers
Safetensors
MiniMax H3
modular-diffusers
ref2va
fl2va
Merge
synchronized-audio-video
experimental
Instructions to use diffusers-modular/MiniMax-H3-Pruned-Ref-Delta-Fused-r1024 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use diffusers-modular/MiniMax-H3-Pruned-Ref-Delta-Fused-r1024 with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("diffusers-modular/MiniMax-H3-Pruned-Ref-Delta-Fused-r1024", dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
File size: 12,880 Bytes
dbc0953 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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 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 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 | """A MiniMax-H3 modular workflow that accepts keyframes **and** references in the same run.
Why this exists. MiniMax-H3 denoises one packed sequence, and that sequence can hold keyframe conditioning rows and
reference conditioning rows at the same time. `diffusers`' shipped blocks cannot express it: their conditional steps
dispatch either/or (`select_block` checks `references` first), so a request carrying both is accepted and the
keyframes are **silently dropped** — no error, no warning, just a reference-only generation.
`MiniMaxH3CombinedBlocks` is `MiniMaxH3Blocks` with three of its conditional steps replaced by ones that know a
fourth shape. Nothing else changes: `t2va`, `fl2va` and `ref2va` requests take exactly the same path they always did,
and the denoising loop is untouched, because the conditioning rows are simply the leading rows of the sequence and it
only ever steps what comes after them.
from diffusers import ModularPipeline
pipe = ModularPipeline.from_pretrained(REPO, trust_remote_code=True, workflow="combined")
pipe.load_components(dtype=torch.bfloat16, trust_remote_code=True)
out = pipe(prompt=..., references=[...], image=first, last_image=last, num_frames=124, ...)
The layout itself lives in `combined_layout.py`, which is pinned by reproducing both shipped builders bit for bit in
their degenerate cases.
"""
from __future__ import annotations
import numpy as np
import torch
from diffusers.modular_pipelines import ConditionalPipelineBlocks, SequentialPipelineBlocks
from diffusers.modular_pipelines.minimax_h3.before_denoise import (
MiniMaxH3PrepareConditionLatentsStep,
MiniMaxH3PrepareLatentsStep,
MiniMaxH3Ref2VAPrepareLatentsStep,
MiniMaxH3Ref2VAPrepareLayoutStep,
MiniMaxH3SetTimestepsStep,
)
from diffusers.modular_pipelines.minimax_h3.before_encoder import MiniMaxH3Ref2VASetupStep
from diffusers.modular_pipelines.minimax_h3.decoders import MiniMaxH3AfterDenoiseStep
from diffusers.modular_pipelines.minimax_h3.denoise import MiniMaxH3Ref2VADenoiseStep
from diffusers.modular_pipelines.minimax_h3.encoders import (
MiniMaxH3Ref2VAReferenceEncoderStep,
encode_vae_condition,
)
from diffusers.modular_pipelines.minimax_h3.modular_blocks_minimax_h3 import (
MiniMaxH3AutoDenoiseStep,
MiniMaxH3AutoTextEncoderStep,
MiniMaxH3AutoVaeEncoderStep,
MiniMaxH3AutoBeforeEncodeStep,
MiniMaxH3Blocks,
MiniMaxH3DecodeStep,
)
from diffusers.modular_pipelines.modular_pipeline import ModularPipelineBlocks, PipelineState
from diffusers.modular_pipelines.modular_pipeline_utils import ComponentSpec, InputParam, OutputParam
from .combined_layout import build_combined_packed_sequence
def _anchors_of(image, last_image) -> tuple[str, ...]:
"""Which end of the clip each keyframe is anchored to, in packed order."""
return tuple(name for name, value in (("first", image), ("last", last_image)) if value is not None)
class MiniMaxH3KeyframesOnCanvasStep(ModularPipelineBlocks):
"""Put the keyframes on the canvas the reference setup already resolved.
The `fl2va` resize step *derives* the canvas from the keyframe's aspect ratio. Here the references have already
settled it, so the keyframes are stretched onto it — which is what `fl2va` does to a keyframe whose aspect does
not match the target anyway.
"""
model_name = "minimax-h3"
@property
def description(self) -> str:
return "Stretches the keyframes of a combined request onto the canvas the reference setup resolved."
@property
def inputs(self) -> list[InputParam]:
return [
InputParam(name="image", description="Keyframe the video starts from."),
InputParam(name="last_image", description="Keyframe the video ends on."),
InputParam(name="height", type_hint=int, required=True, description="Resolved height in pixels."),
InputParam(name="width", type_hint=int, required=True, description="Resolved width in pixels."),
]
@property
def intermediate_outputs(self) -> list[OutputParam]:
return [
OutputParam("keyframes", type_hint=list, description="The keyframes on the target canvas, packed order."),
OutputParam("keyframe_anchors", type_hint=tuple, description="Which end each keyframe is anchored to."),
]
@torch.no_grad()
def __call__(self, components, state: PipelineState) -> PipelineState:
block_state = self.get_block_state(state)
size = (block_state.width, block_state.height)
block_state.keyframe_anchors = _anchors_of(block_state.image, block_state.last_image)
block_state.keyframes = [
frame.convert("RGB").resize(size)
for frame in (block_state.image, block_state.last_image)
if frame is not None
]
self.set_block_state(state, block_state)
return components, state
class MiniMaxH3CombinedKeyframeEncoderStep(ModularPipelineBlocks):
"""Encode the keyframes and put them **in front of** the reference latents.
Order is the contract: the combined layout reserves `[text | keyframe cond | reference blocks | targets]`, and the
stock prepare-latents step concatenates this list in order — then asserts the rows it produced equal the rows the
layout reserved, so a mismatch raises instead of degrading quietly.
"""
model_name = "minimax-h3"
@property
def description(self) -> str:
return "Encodes a combined request's keyframes and prepends them to the reference conditioning latents."
@property
def expected_components(self) -> list[ComponentSpec]:
return [ComponentSpec("vae")]
@property
def inputs(self) -> list[InputParam]:
return [
InputParam(name="keyframes", type_hint=list, required=True, description="Keyframes on the canvas."),
InputParam(name="condition_latents", type_hint=list, required=True,
description="Reference conditioning latents, packed order."),
]
@property
def intermediate_outputs(self) -> list[OutputParam]:
return [
OutputParam("condition_latents", type_hint=list,
description="Keyframe latents first, then the reference latents."),
]
@torch.no_grad()
def __call__(self, components, state: PipelineState) -> PipelineState:
block_state = self.get_block_state(state)
device = components._execution_device
keyframe_latents = [
encode_vae_condition(
components.vae,
torch.from_numpy(np.array(image)).to(device).permute(2, 0, 1)[None, :, None],
components.pixel_mean,
components.pixel_std,
components.keyframe_encode_seed,
)
for image in block_state.keyframes
]
block_state.condition_latents = keyframe_latents + list(block_state.condition_latents)
self.set_block_state(state, block_state)
return components, state
class MiniMaxH3CombinedPrepareLayoutStep(MiniMaxH3Ref2VAPrepareLayoutStep):
"""The `ref2va` layout step, with keyframe rows packed ahead of the reference blocks."""
@property
def description(self) -> str:
return (
"Resolves the latent shapes of a combined request and builds its packed layout — "
"`[text | keyframe conditions | reference blocks | target audio | target video]`. The references push the "
"target timeline out, so the keyframe anchors ride on the timeline their spans leave behind."
)
@property
def inputs(self) -> list[InputParam]:
return super().inputs + [
InputParam(name="keyframe_anchors", type_hint=tuple, default=(),
description="Which end of the video each keyframe is anchored to, in packed order."),
]
# Overrides the parent's `@staticmethod` as a bound method, which is how the anchors reach the builder: the parent
# calls `self.build_ref2va_packed_sequence(...)` positionally and knows nothing about keyframes.
def build_ref2va_packed_sequence(self, *args, **kwargs):
return build_combined_packed_sequence(*args, keyframe_anchors=self._keyframe_anchors, **kwargs)
@torch.no_grad()
def __call__(self, components, state: PipelineState) -> PipelineState:
block_state = self.get_block_state(state)
self._keyframe_anchors = tuple(getattr(block_state, "keyframe_anchors", ()) or ())
try:
return super().__call__(components, state)
finally:
self._keyframe_anchors = ()
class MiniMaxH3CombinedSetupStep(SequentialPipelineBlocks):
model_name = "minimax-h3"
block_classes = [MiniMaxH3Ref2VASetupStep, MiniMaxH3KeyframesOnCanvasStep]
block_names = ["references", "keyframes"]
@property
def description(self) -> str:
return "Resolves the request plan from the references, then puts the keyframes on the resolved canvas."
class MiniMaxH3CombinedVaeEncoderStep(SequentialPipelineBlocks):
model_name = "minimax-h3"
block_classes = [MiniMaxH3Ref2VAReferenceEncoderStep, MiniMaxH3CombinedKeyframeEncoderStep]
block_names = ["references", "keyframes"]
@property
def description(self) -> str:
return "Encodes the references, then the keyframes, leaving the keyframe latents first in packed order."
class MiniMaxH3CombinedCoreDenoiseStep(SequentialPipelineBlocks):
model_name = "minimax-h3"
block_classes = [
MiniMaxH3CombinedPrepareLayoutStep,
MiniMaxH3PrepareConditionLatentsStep,
MiniMaxH3PrepareLatentsStep,
MiniMaxH3Ref2VAPrepareLatentsStep,
MiniMaxH3SetTimestepsStep,
MiniMaxH3Ref2VADenoiseStep,
MiniMaxH3AfterDenoiseStep,
]
block_names = [
"prepare_layout",
"prepare_condition_latents",
"prepare_latents",
"prepare_ref_latents",
"set_timesteps",
"denoise",
"after_denoise",
]
@property
def description(self) -> str:
return "Core denoising for a combined request: the `ref2va` chain over the combined packed layout."
def _is_combined(kwargs) -> bool:
return kwargs.get("references") is not None and (
kwargs.get("image") is not None or kwargs.get("last_image") is not None
)
class MiniMaxH3CombinedAutoBeforeEncodeStep(MiniMaxH3AutoBeforeEncodeStep):
block_classes = [MiniMaxH3CombinedSetupStep] + MiniMaxH3AutoBeforeEncodeStep.block_classes
block_names = ["combined"] + MiniMaxH3AutoBeforeEncodeStep.block_names
def select_block(self, **kwargs) -> str | None:
return "combined" if _is_combined(kwargs) else super().select_block(**kwargs)
class MiniMaxH3CombinedAutoVaeEncoderStep(MiniMaxH3AutoVaeEncoderStep):
block_classes = [MiniMaxH3CombinedVaeEncoderStep] + MiniMaxH3AutoVaeEncoderStep.block_classes
block_names = ["combined"] + MiniMaxH3AutoVaeEncoderStep.block_names
def select_block(self, **kwargs) -> str | None:
return "combined" if _is_combined(kwargs) else super().select_block(**kwargs)
class MiniMaxH3CombinedAutoDenoiseStep(MiniMaxH3AutoDenoiseStep):
block_classes = [MiniMaxH3CombinedCoreDenoiseStep] + MiniMaxH3AutoDenoiseStep.block_classes
block_names = ["combined"] + MiniMaxH3AutoDenoiseStep.block_names
def select_block(self, **kwargs) -> str | None:
return "combined" if _is_combined(kwargs) else super().select_block(**kwargs)
class MiniMaxH3CombinedBlocks(MiniMaxH3Blocks):
"""`MiniMaxH3Blocks` plus a fourth shape: keyframes and references in the same generation.
Supported workflows: `t2va`, `fl2va`, `ref2va` — unchanged — and `combined`, which needs `prompt`, `references`
and at least one of `image` / `last_image`. A combined request runs against the `transformer_ref` partition, the
same one `ref2va` uses.
"""
block_classes = [
MiniMaxH3CombinedAutoBeforeEncodeStep,
MiniMaxH3AutoTextEncoderStep,
MiniMaxH3CombinedAutoVaeEncoderStep,
MiniMaxH3CombinedAutoDenoiseStep,
MiniMaxH3DecodeStep,
]
block_names = ["before_encode", "text_encoder", "vae_encoder", "denoise", "decode"]
_workflow_map = dict(
MiniMaxH3Blocks._workflow_map,
combined=(
{"prompt": True, "references": True, "image": True},
{"prompt": True, "references": True, "last_image": True},
),
)
@property
def description(self) -> str:
return (
"MiniMax-H3 blocks for joint video + audio generation, with the `t2va`, `fl2va` and `ref2va` workflows "
"unchanged and a fourth, `combined`, that carries keyframe *and* reference conditioning in one packed "
"sequence instead of dropping one of them."
)
|