multimodalart HF Staff commited on
Commit
4218806
·
verified ·
1 Parent(s): 705b884

Load the AoTI packages from the public multimodalart/minimax-h3-aoti, move the upsample toggle under the prompt

Browse files
Files changed (3) hide show
  1. README.md +6 -5
  2. app.py +10 -17
  3. h3_aoti.py +9 -7
README.md CHANGED
@@ -107,7 +107,7 @@ canvas and frame count rather than re-deriving them.
107
  ## AoTI-compiled blocks
108
 
109
  With `H3_AOTI=1` the 50 repeated transformer blocks run from a compiled package,
110
- `diffusers-internal-dev/minimax-h3-aoti:bf16/torch2.11/sm120/dynamic` — a single dynamic-sequence artifact that serves
111
  every canvas, duration, reference set and prompt length.
112
 
113
  It is the **same package the `transformer/` partition runs**, and nothing about it is partition-specific. The two
@@ -183,10 +183,11 @@ on is cold and a cold one pays the lazy 72.16 GiB `PIPE.to("cuda")` inside its f
183
 
184
  ## Secrets
185
 
186
- The weights are the public [`MiniMaxAI/MiniMax-H3`](https://huggingface.co/MiniMaxAI/MiniMax-H3) diffusers
187
- checkpoint and the conditioner is a public Space called without a token, so that round trip runs on the caller's own
188
- quota rather than this org's. `HF_TOKEN` is still read by `h3_aoti`, whose compiled-package repository is private —
189
- without it, set `H3_AOTI=0`.
 
190
 
191
  ## Where diffusers comes from
192
 
 
107
  ## AoTI-compiled blocks
108
 
109
  With `H3_AOTI=1` the 50 repeated transformer blocks run from a compiled package,
110
+ [`multimodalart/minimax-h3-aoti`](https://huggingface.co/multimodalart/minimax-h3-aoti)`:bf16/torch2.11/sm120/dynamic` — a single dynamic-sequence artifact that serves
111
  every canvas, duration, reference set and prompt length.
112
 
113
  It is the **same package the `transformer/` partition runs**, and nothing about it is partition-specific. The two
 
183
 
184
  ## Secrets
185
 
186
+ Nothing this Space loads is private any more: the weights are the public
187
+ [`MiniMaxAI/MiniMax-H3`](https://huggingface.co/MiniMaxAI/MiniMax-H3) checkpoint, the compiled AoTI packages are the
188
+ public [`multimodalart/minimax-h3-aoti`](https://huggingface.co/multimodalart/minimax-h3-aoti) model repo, and the
189
+ conditioner is a public Space called without a token — so that round trip runs on the caller's own quota rather than
190
+ this org's. No `HF_TOKEN` is required.
191
 
192
  ## Where diffusers comes from
193
 
app.py CHANGED
@@ -237,8 +237,8 @@ def load_models() -> str | None:
237
  blocks = MiniMaxH3Ref2VAGeneratorBlocks()
238
  print(f"[ref2va] loading {[c.name for c in blocks.expected_components]} from {MODEL_REPO} ...", flush=True)
239
  pipe = blocks.init_pipeline(MODEL_REPO, components_manager=manager, collection="h3")
240
- # `MiniMaxAI/MiniMax-H3` is public, so the weights are fetched without a token. `HF_TOKEN` is still read by
241
- # `h3_aoti`, whose compiled-package repository is not.
242
  pipe.load_components(dtype=torch.bfloat16)
243
 
244
  # Pin the two autoencoders to torch SDPA *before* the transformer takes cuDNN, and in that order.
@@ -552,7 +552,7 @@ def generate(
552
  f"({generate_seconds / int(steps):.1f} s/step) · seed {int(seed)}",
553
  flush=True,
554
  )
555
- return path, refined
556
 
557
 
558
  load_models()
@@ -585,6 +585,7 @@ with gr.Blocks(title="MiniMax-H3 Reference") as demo:
585
  lines=3,
586
  value="The character walks through a neon-lit street in the rain, humming to themselves",
587
  )
 
588
  # One tab per modality, in the order the model reads them. A reference left in a tab that is not the
589
  # open one is still part of the request — the tabs lay the slots out, they do not choose between them.
590
  with gr.Tabs():
@@ -618,21 +619,13 @@ with gr.Blocks(title="MiniMax-H3 Reference") as demo:
618
  )
619
  steps = gr.Slider(label="Steps", minimum=10, maximum=40, step=1, value=28)
620
  seed = gr.Number(label="Seed", value=42, precision=0)
621
- upsample = gr.Checkbox(
622
- label="Upsample prompt",
623
- value=False,
624
- info="Rewrites the prompt into the model's trained format with the conditioner's Qwen3-VL before encoding.",
625
- )
626
 
627
  with gr.Column():
628
  result = gr.Video(label="Video + soundtrack")
629
- with gr.Accordion("Upsampled prompt", open=False):
630
- upsampled = gr.Textbox(
631
- show_label=False,
632
- lines=8,
633
- interactive=False,
634
- placeholder="Turn on “Upsample prompt” to see the rewrite that was encoded.",
635
- )
636
 
637
  open_slots = gr.State(OPEN_IMAGE_SLOTS)
638
 
@@ -681,14 +674,14 @@ with gr.Blocks(title="MiniMax-H3 Reference") as demo:
681
  ],
682
  ],
683
  inputs=[prompt, images[0], audio, video, canvas],
684
- outputs=[result, upsampled],
685
  fn=generate,
686
  cache_examples=True,
687
  cache_mode="lazy",
688
  )
689
 
690
  # The video stays the first output and the upsampled prompt is appended last, so existing consumers are untouched.
691
- run.click(generate, request, [result, upsampled], api_name="generate")
692
 
693
 
694
  if __name__ == "__main__":
 
237
  blocks = MiniMaxH3Ref2VAGeneratorBlocks()
238
  print(f"[ref2va] loading {[c.name for c in blocks.expected_components]} from {MODEL_REPO} ...", flush=True)
239
  pipe = blocks.init_pipeline(MODEL_REPO, components_manager=manager, collection="h3")
240
+ # Every repository this Space reads is public the checkpoint, the AoTI packages and the conditioner
241
+ # Space so no token is passed anywhere.
242
  pipe.load_components(dtype=torch.bfloat16)
243
 
244
  # Pin the two autoencoders to torch SDPA *before* the transformer takes cuDNN, and in that order.
 
552
  f"({generate_seconds / int(steps):.1f} s/step) · seed {int(seed)}",
553
  flush=True,
554
  )
555
+ return path, refined, gr.update(visible=bool(refined))
556
 
557
 
558
  load_models()
 
585
  lines=3,
586
  value="The character walks through a neon-lit street in the rain, humming to themselves",
587
  )
588
+ upsample = gr.Checkbox(label="Upsample prompt", value=False)
589
  # One tab per modality, in the order the model reads them. A reference left in a tab that is not the
590
  # open one is still part of the request — the tabs lay the slots out, they do not choose between them.
591
  with gr.Tabs():
 
619
  )
620
  steps = gr.Slider(label="Steps", minimum=10, maximum=40, step=1, value=28)
621
  seed = gr.Number(label="Seed", value=42, precision=0)
 
 
 
 
 
622
 
623
  with gr.Column():
624
  result = gr.Video(label="Video + soundtrack")
625
+ # Only shown for a request that actually asked for a rewrite, so a plain request is not left with an
626
+ # empty panel. The accordion is an output for that reason: its visibility is part of the answer.
627
+ with gr.Accordion("Upsampled prompt", open=False, visible=False) as upsampled_panel:
628
+ upsampled = gr.Textbox(show_label=False, lines=8, interactive=False)
 
 
 
629
 
630
  open_slots = gr.State(OPEN_IMAGE_SLOTS)
631
 
 
674
  ],
675
  ],
676
  inputs=[prompt, images[0], audio, video, canvas],
677
+ outputs=[result, upsampled, upsampled_panel],
678
  fn=generate,
679
  cache_examples=True,
680
  cache_mode="lazy",
681
  )
682
 
683
  # The video stays the first output and the upsampled prompt is appended last, so existing consumers are untouched.
684
+ run.click(generate, request, [result, upsampled, upsampled_panel], api_name="generate")
685
 
686
 
687
  if __name__ == "__main__":
h3_aoti.py CHANGED
@@ -2,7 +2,7 @@
2
 
3
  Shared byte-identically by every MiniMax-H3 Space. A Space only ever calls `maybe_load()`; the compile path runs from
4
  the debug Space's "Compile (AoTI)" tab, or off-Space from `job_bf16_aoti.py` on an `rtx-pro-6000` Job, and pushes its
5
- artifacts to `diffusers-internal-dev/minimax-h3-aoti` under `<width>/torch<X.Y>/sm<cc>/<shape>`.
6
 
7
  What is measured, so nobody has to guess whether this is worth turning on. Unquantized bfloat16, 124 frames,
8
  everything resident, one dynamic-sequence package serving every row — on an RTX PRO 6000 Blackwell, torch 2.11,
@@ -62,8 +62,11 @@ import os
62
  from pathlib import Path
63
 
64
  AOTI = os.environ.get("H3_AOTI", "0") == "1"
65
- AOTI_REPO = os.environ.get("H3_AOTI_REPO", "diffusers-internal-dev/minimax-h3-aoti")
66
- AOTI_REPO_TYPE = os.environ.get("H3_AOTI_REPO_TYPE", "dataset")
 
 
 
67
  # `dynamic` is the one package that serves every canvas, duration *and prompt*, and for bfloat16 it is what gets built:
68
  # a dynamic sequence dimension exports and compiles cleanly (measured on an rtx-pro-6000 Job, torch 2.11). It has to be
69
  # dynamic to be useful at all — `build_packed_sequence` pads nothing, so
@@ -211,8 +214,8 @@ def maybe_load(transformer) -> None:
211
  """Patch the block stack with its compiled package. Once, and safe to call at **startup**.
212
 
213
  Nothing here touches a GPU: the download is CPU work and the `.pt2` archive is not opened until the first forward,
214
- which happens inside the `@spaces.GPU` call. Proven on the pool `diffusers-internal-dev/minimax-h3-generator-aoti`
215
- loads `bf16/torch2.11/sm120/dynamic` at startup, patches all 50 blocks, and generates.
216
  """
217
  if not AOTI or id(transformer) in _LOADED:
218
  return
@@ -226,7 +229,6 @@ def maybe_load(transformer) -> None:
226
  repo_id=AOTI_REPO,
227
  repo_type=AOTI_REPO_TYPE,
228
  allow_patterns=f"{key}/package/*",
229
- token=os.environ.get("HF_TOKEN"),
230
  )
231
  package_dir = Path(local) / key / "package"
232
  if not package_dir.is_dir():
@@ -420,7 +422,7 @@ def upload(package_dir: str | os.PathLike[str], key: str) -> str:
420
  if not token:
421
  raise RuntimeError("`HF_TOKEN` is needed to push the AoTI package.")
422
  api = HfApi(token=token)
423
- api.create_repo(repo_id=AOTI_REPO, repo_type=AOTI_REPO_TYPE, private=True, exist_ok=True)
424
  api.upload_folder(
425
  folder_path=str(package_dir),
426
  path_in_repo=f"{key}/package",
 
2
 
3
  Shared byte-identically by every MiniMax-H3 Space. A Space only ever calls `maybe_load()`; the compile path runs from
4
  the debug Space's "Compile (AoTI)" tab, or off-Space from `job_bf16_aoti.py` on an `rtx-pro-6000` Job, and pushes its
5
+ artifacts to `multimodalart/minimax-h3-aoti` under `<width>/torch<X.Y>/sm<cc>/<shape>`.
6
 
7
  What is measured, so nobody has to guess whether this is worth turning on. Unquantized bfloat16, 124 frames,
8
  everything resident, one dynamic-sequence package serving every row — on an RTX PRO 6000 Blackwell, torch 2.11,
 
62
  from pathlib import Path
63
 
64
  AOTI = os.environ.get("H3_AOTI", "0") == "1"
65
+ # A public **model** repo. It used to be a private dataset, which is why the repo type is still a variable: the
66
+ # artifacts are keyed by quant/torch/arch under `<width>/torch<X.Y>/sm<cc>/<shape>` rather than laid out the way
67
+ # `spaces.aoti_load` expects, so the download is done by hand either way (see `maybe_load`).
68
+ AOTI_REPO = os.environ.get("H3_AOTI_REPO", "multimodalart/minimax-h3-aoti")
69
+ AOTI_REPO_TYPE = os.environ.get("H3_AOTI_REPO_TYPE", "model")
70
  # `dynamic` is the one package that serves every canvas, duration *and prompt*, and for bfloat16 it is what gets built:
71
  # a dynamic sequence dimension exports and compiles cleanly (measured on an rtx-pro-6000 Job, torch 2.11). It has to be
72
  # dynamic to be useful at all — `build_packed_sequence` pads nothing, so
 
214
  """Patch the block stack with its compiled package. Once, and safe to call at **startup**.
215
 
216
  Nothing here touches a GPU: the download is CPU work and the `.pt2` archive is not opened until the first forward,
217
+ which happens inside the `@spaces.GPU` call. Proven on the pool: `bf16/torch2.11/sm120/dynamic` loads at startup,
218
+ patches all 50 blocks, and generates. The repo is public, so no token is passed for it.
219
  """
220
  if not AOTI or id(transformer) in _LOADED:
221
  return
 
229
  repo_id=AOTI_REPO,
230
  repo_type=AOTI_REPO_TYPE,
231
  allow_patterns=f"{key}/package/*",
 
232
  )
233
  package_dir = Path(local) / key / "package"
234
  if not package_dir.is_dir():
 
422
  if not token:
423
  raise RuntimeError("`HF_TOKEN` is needed to push the AoTI package.")
424
  api = HfApi(token=token)
425
+ api.create_repo(repo_id=AOTI_REPO, repo_type=AOTI_REPO_TYPE, private=False, exist_ok=True)
426
  api.upload_folder(
427
  folder_path=str(package_dir),
428
  path_in_repo=f"{key}/package",