mobiusfr commited on
Commit
5c1626c
·
verified ·
1 Parent(s): 9bdc986

Port h3_split_blocks.py from mrfakename/minimax-h3-ultra-fast

Browse files
Files changed (1) hide show
  1. h3_split_blocks.py +210 -0
h3_split_blocks.py ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Composable conditioner and generator halves of MiniMax-H3, for both checkpoint partitions.
2
+
3
+ The blocks cut `MiniMaxH3Blocks` at its `text_encoder` step. They can run in separate Spaces with `prompt_embeds` and
4
+ `text_token_tags` as a wire format, or sequentially in one GPU worker when a compact local conditioner fits beside
5
+ the generator.
6
+
7
+ `resize` / `setup` run on **both** sides: they own no pretrained component, and each half needs the canvas and the
8
+ prepared keyframes or normalized references. Both conditioner halves also return the resolved `height` / `width` /
9
+ `num_frames`, which the generating half pins rather than re-deriving.
10
+
11
+ Two things the blocks leave to the caller: a keyframe reaches them EXIF-transposed and in RGB, and the `t2va` / `fl2va`
12
+ frame count is aligned to `17 * n + 5` before the call, since that arithmetic lives on the denoising side of the cut.
13
+ """
14
+
15
+ from diffusers.modular_pipelines.minimax_h3.before_encoder import MiniMaxH3Ref2VASetupStep
16
+ from diffusers.modular_pipelines.minimax_h3.before_denoise import (
17
+ MiniMaxH3PrepareLatentsStep,
18
+ MiniMaxH3PrepareLayoutStep,
19
+ MiniMaxH3Ref2VAPrepareLayoutStep,
20
+ MiniMaxH3SetTimestepsStep,
21
+ )
22
+ from diffusers.modular_pipelines.minimax_h3.decoders import MiniMaxH3AfterDenoiseStep
23
+ from diffusers.modular_pipelines.minimax_h3.denoise import MiniMaxH3DenoiseStep, MiniMaxH3Ref2VADenoiseStep
24
+ from diffusers.modular_pipelines.minimax_h3.encoders import (
25
+ MiniMaxH3Ref2VAReferenceEncoderStep,
26
+ MiniMaxH3Ref2VATextEncoderStep,
27
+ MiniMaxH3TextEncoderStep,
28
+ )
29
+ from diffusers.modular_pipelines.minimax_h3.modular_blocks_minimax_h3 import (
30
+ MiniMaxH3AutoKeyframeVaeEncoderStep,
31
+ MiniMaxH3AutoResizeStep,
32
+ MiniMaxH3DecodeStep,
33
+ _generation_outputs,
34
+ )
35
+ from diffusers.modular_pipelines.modular_pipeline import SequentialPipelineBlocks
36
+ from diffusers.modular_pipelines.modular_pipeline_utils import InputParam, OutputParam
37
+
38
+
39
+ class _PreviewLoopMixin:
40
+ """The pinned Diffusers loop plus four non-authoritative TAE preview emissions."""
41
+
42
+ @property
43
+ def loop_inputs(self):
44
+ # The stock loop has no reason to carry geometry into its inner BlockState. TAE has to invert the packed rows,
45
+ # so explicitly retain the three dimensions produced by `prepare_layout` alongside the ordinary timesteps.
46
+ return [
47
+ *super().loop_inputs,
48
+ InputParam("num_latent_frames", type_hint=int, required=True),
49
+ InputParam("latent_height", type_hint=int, required=True),
50
+ InputParam("latent_width", type_hint=int, required=True),
51
+ ]
52
+
53
+ def __call__(self, components, state):
54
+ from h3_tae import maybe_emit_preview
55
+
56
+ block_state = self.get_block_state(state)
57
+ total = len(block_state.timesteps)
58
+ with self.progress_bar(total=total) as progress_bar:
59
+ for index, timestep in enumerate(block_state.timesteps):
60
+ components, block_state = self.loop_step(components, block_state, i=index, t=timestep)
61
+ maybe_emit_preview(components, block_state, index, total)
62
+ progress_bar.update()
63
+ self.set_block_state(state, block_state)
64
+ return components, state
65
+
66
+
67
+ class MiniMaxH3PreviewDenoiseStep(_PreviewLoopMixin, MiniMaxH3DenoiseStep):
68
+ pass
69
+
70
+
71
+ class MiniMaxH3Ref2VAPreviewDenoiseStep(_PreviewLoopMixin, MiniMaxH3Ref2VADenoiseStep):
72
+ pass
73
+
74
+
75
+ class MiniMaxH3PreviewCoreDenoiseStep(SequentialPipelineBlocks):
76
+ model_name = "minimax-h3"
77
+ block_classes = [
78
+ MiniMaxH3PrepareLayoutStep,
79
+ MiniMaxH3PrepareLatentsStep,
80
+ MiniMaxH3SetTimestepsStep,
81
+ MiniMaxH3PreviewDenoiseStep,
82
+ ]
83
+ block_names = ["prepare_layout", "prepare_latents", "set_timesteps", "denoise"]
84
+
85
+
86
+ class MiniMaxH3Ref2VAPreviewCoreDenoiseStep(SequentialPipelineBlocks):
87
+ model_name = "minimax-h3"
88
+ block_classes = [
89
+ MiniMaxH3Ref2VAPrepareLayoutStep,
90
+ MiniMaxH3PrepareLatentsStep,
91
+ MiniMaxH3SetTimestepsStep,
92
+ MiniMaxH3Ref2VAPreviewDenoiseStep,
93
+ ]
94
+ block_names = ["prepare_layout", "prepare_latents", "set_timesteps", "denoise"]
95
+
96
+
97
+ def _wire_outputs(num_frames: bool = True) -> list[OutputParam]:
98
+ """The wire format of the split. `num_frames` is declared by the `ref2va` half alone, whose setup resolves one."""
99
+ return [
100
+ OutputParam.template("prompt_embeds"),
101
+ OutputParam("text_token_tags", description="The per-row modality tag of every row of `prompt_embeds`."),
102
+ OutputParam("height", type_hint=int, description="Resolved height of the generated video in pixels."),
103
+ OutputParam("width", type_hint=int, description="Resolved width of the generated video in pixels."),
104
+ *(
105
+ [OutputParam("num_frames", type_hint=int, description="Resolved number of frames, of the form 17 * n + 5.")]
106
+ if num_frames
107
+ else []
108
+ ),
109
+ ]
110
+
111
+
112
+ class MiniMaxH3ConditionerBlocks(SequentialPipelineBlocks):
113
+ """The conditioner half of a split MiniMax-H3: the keyframes on the canvas plus the Qwen3-VL read at layer 50."""
114
+
115
+ model_name = "minimax-h3"
116
+ block_classes = [MiniMaxH3AutoResizeStep, MiniMaxH3TextEncoderStep]
117
+ block_names = ["resize", "text_encoder"]
118
+
119
+ @property
120
+ def description(self):
121
+ return (
122
+ "The conditioner half of a split MiniMax-H3 deployment: puts the keyframes onto the target canvas and "
123
+ "encodes MiniMax-H3's presentation of the request into the `prompt_embeds` / `text_token_tags` pair the "
124
+ "denoising half consumes. The frame count is the caller's to align."
125
+ )
126
+
127
+ @property
128
+ def outputs(self):
129
+ return _wire_outputs(num_frames=False)
130
+
131
+
132
+ class MiniMaxH3GeneratorBlocks(SequentialPipelineBlocks):
133
+ """The denoising half of a split MiniMax-H3: `MiniMaxH3Blocks` with its `text_encoder` step removed."""
134
+
135
+ model_name = "minimax-h3"
136
+ block_classes = [
137
+ MiniMaxH3AutoResizeStep,
138
+ MiniMaxH3AutoKeyframeVaeEncoderStep,
139
+ MiniMaxH3PreviewCoreDenoiseStep,
140
+ MiniMaxH3AfterDenoiseStep,
141
+ MiniMaxH3DecodeStep,
142
+ ]
143
+ block_names = ["resize", "vae_encoder", "denoise", "after_denoise", "decode"]
144
+
145
+ @property
146
+ def description(self):
147
+ return (
148
+ "The denoising half of a split MiniMax-H3 deployment: the `t2va` / `fl2va` branch of `MiniMaxH3Blocks` "
149
+ "without its text-encoder step, so `prompt_embeds` and `text_token_tags` come in as inputs and the "
150
+ "conditioner is supplied by the caller or by the preceding local conditioner half."
151
+ )
152
+
153
+ @property
154
+ def outputs(self):
155
+ return _generation_outputs()
156
+
157
+
158
+ class MiniMaxH3Ref2VAConditionerBlocks(SequentialPipelineBlocks):
159
+ """The conditioner half of a split `ref2va`: the resolved plan plus the Qwen3-VL read at its 50th layer.
160
+
161
+ Component for component this is `MiniMaxH3ConditionerBlocks`, so one conditioner Space serves both partitions.
162
+ What differs is the presentation: `ref2va` prepends a label per reference and a vision block per image and per
163
+ merged video frame pair, so the references themselves have to reach this half.
164
+ """
165
+
166
+ model_name = "minimax-h3"
167
+ block_classes = [MiniMaxH3Ref2VASetupStep, MiniMaxH3Ref2VATextEncoderStep]
168
+ block_names = ["setup", "text_encoder"]
169
+
170
+ @property
171
+ def description(self):
172
+ return (
173
+ "The conditioner half of a split MiniMax-H3 `ref2va` deployment: resolves the request plan (canvas, frame "
174
+ "count, references normalized onto MiniMax-H3's own rates and resolutions) and encodes MiniMax-H3's "
175
+ "presentation of it into the `prompt_embeds` / `text_token_tags` pair the denoising half consumes."
176
+ )
177
+
178
+ @property
179
+ def outputs(self):
180
+ return _wire_outputs()
181
+
182
+
183
+ class MiniMaxH3Ref2VAGeneratorBlocks(SequentialPipelineBlocks):
184
+ """The denoising half of a split `ref2va`: the `ref2va` branch with its `text_encoder` step removed.
185
+
186
+ `reference_encoder` stays here, next to the two autoencoders it runs: its output shapes are where every reference
187
+ block's geometry in the packed layout comes from.
188
+ """
189
+
190
+ model_name = "minimax-h3"
191
+ block_classes = [
192
+ MiniMaxH3Ref2VASetupStep,
193
+ MiniMaxH3Ref2VAReferenceEncoderStep,
194
+ MiniMaxH3Ref2VAPreviewCoreDenoiseStep,
195
+ MiniMaxH3AfterDenoiseStep,
196
+ MiniMaxH3DecodeStep,
197
+ ]
198
+ block_names = ["setup", "reference_encoder", "denoise", "after_denoise", "decode"]
199
+
200
+ @property
201
+ def description(self):
202
+ return (
203
+ "The denoising half of a split MiniMax-H3 `ref2va` deployment: the `ref2va` branch of `MiniMaxH3Blocks` "
204
+ "without its text-encoder step, so `prompt_embeds` and `text_token_tags` come in as inputs and the "
205
+ "conditioner is supplied by the caller or preceding local half. The transformer is the `transformer_ref` partition."
206
+ )
207
+
208
+ @property
209
+ def outputs(self):
210
+ return _generation_outputs()