Load the AoTI packages from the public multimodalart/minimax-h3-aoti, move the upsample toggle under the prompt
Browse files- README.md +6 -5
- app.py +10 -17
- 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 |
-
`
|
| 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 |
-
|
| 187 |
-
checkpoint
|
| 188 |
-
|
| 189 |
-
without
|
|
|
|
| 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 |
-
#
|
| 241 |
-
#
|
| 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 |
-
|
| 630 |
-
|
| 631 |
-
|
| 632 |
-
|
| 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 `
|
| 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 |
-
|
| 66 |
-
|
|
|
|
|
|
|
|
|
|
| 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
|
| 215 |
-
|
| 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=
|
| 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",
|