multimodalart HF Staff commited on
Commit
a0bf481
·
verified ·
1 Parent(s): 9da256f

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 -3
  2. app.py +10 -17
  3. h3_aoti.py +9 -7
README.md CHANGED
@@ -38,7 +38,7 @@ exported at all.
38
  ## AoTI-compiled blocks
39
 
40
  With `H3_AOTI=1` the 50 repeated transformer blocks run from a compiled package,
41
- `diffusers-internal-dev/minimax-h3-aoti:bf16/torch2.11/sm120/dynamic` — a single dynamic-sequence artifact that serves
42
  every canvas, duration and prompt length. It carries no weights (it reads each block's live ones), so patching it in
43
  is startup CPU work and costs no GPU time.
44
 
@@ -160,8 +160,11 @@ one-time `PIPE.to("cuda")` is inside the first row's 339 s and does not reappear
160
 
161
  ## Secrets
162
 
163
- The weights and the conditioner Space are both public, so neither needs a token. `HF_TOKEN` is still read by
164
- `h3_aoti`, whose compiled-package repository is private — without it, set `H3_AOTI=0`.
 
 
 
165
 
166
  ## Where diffusers comes from
167
 
 
38
  ## AoTI-compiled blocks
39
 
40
  With `H3_AOTI=1` the 50 repeated transformer blocks run from a compiled package,
41
+ [`multimodalart/minimax-h3-aoti`](https://huggingface.co/multimodalart/minimax-h3-aoti)`:bf16/torch2.11/sm120/dynamic` — a single dynamic-sequence artifact that serves
42
  every canvas, duration and prompt length. It carries no weights (it reads each block's live ones), so patching it in
43
  is startup CPU work and costs no GPU time.
44
 
 
160
 
161
  ## Secrets
162
 
163
+ Nothing this Space loads is private any more: the weights are the public
164
+ [`MiniMaxAI/MiniMax-H3`](https://huggingface.co/MiniMaxAI/MiniMax-H3) checkpoint, the compiled AoTI packages are the
165
+ public [`multimodalart/minimax-h3-aoti`](https://huggingface.co/multimodalart/minimax-h3-aoti) model repo, and the
166
+ conditioner is a public Space called without a token — so that round trip runs on the caller's own quota rather than
167
+ this org's. No `HF_TOKEN` is required.
168
 
169
  ## Where diffusers comes from
170
 
app.py CHANGED
@@ -117,8 +117,8 @@ def load_models() -> str | None:
117
  blocks = MiniMaxH3GeneratorBlocks()
118
  print(f"[gen] loading {[c.name for c in blocks.expected_components]} from {MODEL_REPO} ...", flush=True)
119
  pipe = blocks.init_pipeline(MODEL_REPO, components_manager=manager, collection="h3")
120
- # `MiniMaxAI/MiniMax-H3` is public, so the weights are fetched without a token. `HF_TOKEN` is still read by
121
- # `h3_aoti`, whose compiled-package repository is not.
122
  pipe.load_components(dtype=torch.bfloat16)
123
  pipe.transformer.set_attention_backend(ATTENTION)
124
 
@@ -312,7 +312,7 @@ def generate(prompt, image_path=None, last_image_path=None, canvas=DEFAULT_CANVA
312
  f"denoise + decode {generate_seconds:.0f}s ({generate_seconds / int(steps):.1f} s/step) · seed {int(seed)}"
313
  )
314
  print(f"[gen] {report}", flush=True)
315
- return path, report, refined
316
 
317
 
318
 
@@ -383,6 +383,7 @@ with gr.Blocks(title="MiniMax-H3") as demo:
383
  lines=3,
384
  value="A red fox trotting through a snowy pine forest at dawn, snow crunching underfoot",
385
  )
 
386
  with gr.Row():
387
  image = gr.Image(label="First frame (optional)", type="filepath")
388
  last_image = gr.Image(label="Last frame (optional)", type="filepath")
@@ -392,22 +393,14 @@ with gr.Blocks(title="MiniMax-H3") as demo:
392
  duration = gr.Slider(label="Duration (s)", minimum=2, maximum=MAX_UI_DURATION, step=1, value=5)
393
  steps = gr.Slider(label="Steps", minimum=10, maximum=40, step=1, value=28)
394
  seed = gr.Number(label="Seed", value=42, precision=0)
395
- upsample = gr.Checkbox(
396
- label="Upsample prompt",
397
- value=False,
398
- info="Rewrites the prompt into the model's trained format with the conditioner's Qwen3-VL before encoding.",
399
- )
400
 
401
  with gr.Column():
402
  video = gr.Video(label="Video + soundtrack")
403
  report = gr.Markdown(visible=False)
404
- with gr.Accordion("Upsampled prompt", open=False):
405
- upsampled = gr.Textbox(
406
- show_label=False,
407
- lines=8,
408
- interactive=False,
409
- placeholder="Turn on “Upsample prompt” to see the rewrite that was encoded.",
410
- )
411
 
412
  image.upload(_fit_keyframe, [image, canvas], [image, canvas])
413
 
@@ -420,7 +413,7 @@ with gr.Blocks(title="MiniMax-H3") as demo:
420
  ["A slow seamless camera move from the first view to the last", "examples/first.png", "examples/last.png", "1344x768 · 16:9 full"],
421
  ],
422
  inputs=[prompt, image, last_image, canvas],
423
- outputs=[video, report, upsampled],
424
  fn=generate,
425
  cache_examples=True,
426
  cache_mode="lazy",
@@ -432,7 +425,7 @@ with gr.Blocks(title="MiniMax-H3") as demo:
432
  run.click(
433
  generate,
434
  [prompt, image, last_image, canvas, duration, steps, seed, upsample],
435
- [video, report, upsampled],
436
  api_name="generate",
437
  )
438
 
 
117
  blocks = MiniMaxH3GeneratorBlocks()
118
  print(f"[gen] loading {[c.name for c in blocks.expected_components]} from {MODEL_REPO} ...", flush=True)
119
  pipe = blocks.init_pipeline(MODEL_REPO, components_manager=manager, collection="h3")
120
+ # Every repository this Space reads is public the checkpoint, the AoTI packages and the conditioner
121
+ # Space so no token is passed anywhere.
122
  pipe.load_components(dtype=torch.bfloat16)
123
  pipe.transformer.set_attention_backend(ATTENTION)
124
 
 
312
  f"denoise + decode {generate_seconds:.0f}s ({generate_seconds / int(steps):.1f} s/step) · seed {int(seed)}"
313
  )
314
  print(f"[gen] {report}", flush=True)
315
+ return path, report, refined, gr.update(visible=bool(refined))
316
 
317
 
318
 
 
383
  lines=3,
384
  value="A red fox trotting through a snowy pine forest at dawn, snow crunching underfoot",
385
  )
386
+ upsample = gr.Checkbox(label="Upsample prompt", value=False)
387
  with gr.Row():
388
  image = gr.Image(label="First frame (optional)", type="filepath")
389
  last_image = gr.Image(label="Last frame (optional)", type="filepath")
 
393
  duration = gr.Slider(label="Duration (s)", minimum=2, maximum=MAX_UI_DURATION, step=1, value=5)
394
  steps = gr.Slider(label="Steps", minimum=10, maximum=40, step=1, value=28)
395
  seed = gr.Number(label="Seed", value=42, precision=0)
 
 
 
 
 
396
 
397
  with gr.Column():
398
  video = gr.Video(label="Video + soundtrack")
399
  report = gr.Markdown(visible=False)
400
+ # Only shown for a request that actually asked for a rewrite, so a plain request is not left with an
401
+ # empty panel. The accordion is an output for that reason: its visibility is part of the answer.
402
+ with gr.Accordion("Upsampled prompt", open=False, visible=False) as upsampled_panel:
403
+ upsampled = gr.Textbox(show_label=False, lines=8, interactive=False)
 
 
 
404
 
405
  image.upload(_fit_keyframe, [image, canvas], [image, canvas])
406
 
 
413
  ["A slow seamless camera move from the first view to the last", "examples/first.png", "examples/last.png", "1344x768 · 16:9 full"],
414
  ],
415
  inputs=[prompt, image, last_image, canvas],
416
+ outputs=[video, report, upsampled, upsampled_panel],
417
  fn=generate,
418
  cache_examples=True,
419
  cache_mode="lazy",
 
425
  run.click(
426
  generate,
427
  [prompt, image, last_image, canvas, duration, steps, seed, upsample],
428
+ [video, report, upsampled, upsampled_panel],
429
  api_name="generate",
430
  )
431
 
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",