Spaces:
Running on Zero
Running on Zero
Switch to the recommended ckpt850 EMA LoRA; default steps back to 4
Browse filesckpt850 is the final checkpoint of the round and sharp at 4 steps per
the updated card, so the UI/API default drops 8 -> 4. Also adds
H3_LORA_STRENGTH (default 1.0), the card's sharpness/artifact dial,
applied at fold time and in the per-request toggle.
- README.md +4 -3
- app.py +2 -2
- h3_lora.py +10 -5
- index.html +2 -2
README.md
CHANGED
|
@@ -47,12 +47,12 @@ callers get the same treatment the old upload event gave.
|
|
| 47 |
## 4-step Turbo LoRA
|
| 48 |
|
| 49 |
The transformer runs with [`larryvrh/MiniMax-H3-Turbo-Lora`](https://huggingface.co/larryvrh/MiniMax-H3-Turbo-Lora)
|
| 50 |
-
folded into its bf16 weights at startup (`h3_lora.py`), so the default is **
|
| 51 |
card's comfort zone at the current checkpoint; 4 is the design point but softer). The fold mirrors the diffusers key
|
| 52 |
conversion exactly (fused-QKV thirds, the `SwiGLU` gate/value swap, the shared AdaLN row layout) and happens before
|
| 53 |
the AoTI package is patched in, so compiled blocks carry the update too. The low-rank factors stay resident, so the
|
| 54 |
**Turbo LoRA checkbox** (or the `use_lora` API parameter) folds/unfolds `lora_B @ lora_A` in place per request —
|
| 55 |
-
off gives the base model at whatever step count you pick. `H3_LORA` selects the file (`off` disables entirely).
|
| 56 |
|
| 57 |
## AoTI-compiled blocks
|
| 58 |
|
|
@@ -176,8 +176,9 @@ one-time `PIPE.to("cuda")` is inside the first row's 339 s and does not reappear
|
|
| 176 |
| `H3_ATTENTION` | `_native_cudnn` | cuDNN's fused kernel, 10–20% faster than the SDPA default and needs nothing installed. flash-attention 3 is sm90-only and this pool is sm120. |
|
| 177 |
| `H3_GPU_DURATION` | `900` | Seconds per request; the pool applies a 1.5 duration factor. |
|
| 178 |
| `H3_GPU_SIZE` | `xlarge` | ZeroGPU allocation size. `large` does not fit. |
|
| 179 |
-
| `H3_LORA` | `
|
| 180 |
| `H3_LORA_REPO` | `larryvrh/MiniMax-H3-Turbo-Lora` | Hub repo the LoRA is fetched from. |
|
|
|
|
| 181 |
|
| 182 |
## Whose GPU quota pays
|
| 183 |
|
|
|
|
| 47 |
## 4-step Turbo LoRA
|
| 48 |
|
| 49 |
The transformer runs with [`larryvrh/MiniMax-H3-Turbo-Lora`](https://huggingface.co/larryvrh/MiniMax-H3-Turbo-Lora)
|
| 50 |
+
folded into its bf16 weights at startup (`h3_lora.py`), so the default is **4 sampling steps** instead of 28
|
| 51 |
card's comfort zone at the current checkpoint; 4 is the design point but softer). The fold mirrors the diffusers key
|
| 52 |
conversion exactly (fused-QKV thirds, the `SwiGLU` gate/value swap, the shared AdaLN row layout) and happens before
|
| 53 |
the AoTI package is patched in, so compiled blocks carry the update too. The low-rank factors stay resident, so the
|
| 54 |
**Turbo LoRA checkbox** (or the `use_lora` API parameter) folds/unfolds `lora_B @ lora_A` in place per request —
|
| 55 |
+
off gives the base model at whatever step count you pick. `H3_LORA` selects the file (`off` disables entirely), `H3_LORA_STRENGTH` scales the update (the card's sharpness/artifact dial: >1 against smear, <1 against over-sharp grain).
|
| 56 |
|
| 57 |
## AoTI-compiled blocks
|
| 58 |
|
|
|
|
| 176 |
| `H3_ATTENTION` | `_native_cudnn` | cuDNN's fused kernel, 10–20% faster than the SDPA default and needs nothing installed. flash-attention 3 is sm90-only and this pool is sm120. |
|
| 177 |
| `H3_GPU_DURATION` | `900` | Seconds per request; the pool applies a 1.5 duration factor. |
|
| 178 |
| `H3_GPU_SIZE` | `xlarge` | ZeroGPU allocation size. `large` does not fit. |
|
| 179 |
+
| `H3_LORA` | `minimax_h3_turbo_4step_ema_ckpt850.safetensors` | Turbo LoRA file folded into the transformer at startup. `off` disables. |
|
| 180 |
| `H3_LORA_REPO` | `larryvrh/MiniMax-H3-Turbo-Lora` | Hub repo the LoRA is fetched from. |
|
| 181 |
+
| `H3_LORA_STRENGTH` | `1.0` | Scales the folded LoRA delta (sharpness/artifact trade-off). |
|
| 182 |
|
| 183 |
## Whose GPU quota pays
|
| 184 |
|
app.py
CHANGED
|
@@ -302,7 +302,7 @@ def _fit_keyframe(image_path, current_canvas):
|
|
| 302 |
return image_path, label
|
| 303 |
|
| 304 |
|
| 305 |
-
def generate(prompt, image_path=None, last_image_path=None, canvas=DEFAULT_CANVAS, duration=5, steps=
|
| 306 |
"""One request. `upsample` is last and defaults off, so a positional API client that predates it is unaffected."""
|
| 307 |
if LOAD_ERROR:
|
| 308 |
raise Exception(LOAD_ERROR)
|
|
@@ -379,7 +379,7 @@ app = Server(title="MiniMax-H3 Studio")
|
|
| 379 |
|
| 380 |
@app.api(name="generate")
|
| 381 |
def _generate_api(prompt: str, image_path: FileData | None = None, last_image_path: FileData | None = None,
|
| 382 |
-
canvas: str = DEFAULT_CANVAS, duration: float = 5, steps: int =
|
| 383 |
upsample: bool = False, use_lora: bool = True, request: Request = None) -> tuple[FileData, str, str]:
|
| 384 |
"""Generate a video with a synchronized soundtrack. Returns (video, report, refined prompt)."""
|
| 385 |
# `request` is injected by the event system, not an API input; its x-ip-token bills the conditioner to the caller.
|
|
|
|
| 302 |
return image_path, label
|
| 303 |
|
| 304 |
|
| 305 |
+
def generate(prompt, image_path=None, last_image_path=None, canvas=DEFAULT_CANVAS, duration=5, steps=4, seed=42, upsample=False, use_lora=True, ip_token=None):
|
| 306 |
"""One request. `upsample` is last and defaults off, so a positional API client that predates it is unaffected."""
|
| 307 |
if LOAD_ERROR:
|
| 308 |
raise Exception(LOAD_ERROR)
|
|
|
|
| 379 |
|
| 380 |
@app.api(name="generate")
|
| 381 |
def _generate_api(prompt: str, image_path: FileData | None = None, last_image_path: FileData | None = None,
|
| 382 |
+
canvas: str = DEFAULT_CANVAS, duration: float = 5, steps: int = 4, seed: float = 42,
|
| 383 |
upsample: bool = False, use_lora: bool = True, request: Request = None) -> tuple[FileData, str, str]:
|
| 384 |
"""Generate a video with a synchronized soundtrack. Returns (video, report, refined prompt)."""
|
| 385 |
# `request` is injected by the event system, not an API input; its x-ip-token bills the conditioner to the caller.
|
h3_lora.py
CHANGED
|
@@ -27,8 +27,11 @@ import os
|
|
| 27 |
import torch
|
| 28 |
|
| 29 |
LORA_REPO = os.environ.get("H3_LORA_REPO", "larryvrh/MiniMax-H3-Turbo-Lora")
|
| 30 |
-
# The recommended default per the model card:
|
| 31 |
-
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
|
| 34 |
def _delta_targets(name: str, delta: torch.Tensor, inner_dim: int) -> list[tuple[str, torch.Tensor]]:
|
|
@@ -79,7 +82,7 @@ def apply_lora(transformer) -> str | None:
|
|
| 79 |
for name in bases:
|
| 80 |
a = lora[f"{name}.lora_A.weight"].float()
|
| 81 |
b = lora[f"{name}.lora_B.weight"].float()
|
| 82 |
-
delta = b @ a # alpha == rank, so the scale is 1
|
| 83 |
targets = _delta_targets(name, delta, inner_dim)
|
| 84 |
for key, converted in targets:
|
| 85 |
param = params.get(key)
|
|
@@ -94,12 +97,14 @@ def apply_lora(transformer) -> str | None:
|
|
| 94 |
transformer._lora_state = {
|
| 95 |
"enabled": True,
|
| 96 |
"inner_dim": inner_dim,
|
|
|
|
| 97 |
"entries": [
|
| 98 |
(name, lora[f"{name}.lora_A.weight"], lora[f"{name}.lora_B.weight"])
|
| 99 |
for name in entries
|
| 100 |
],
|
| 101 |
}
|
| 102 |
-
|
|
|
|
| 103 |
|
| 104 |
|
| 105 |
def set_enabled(transformer, enabled: bool) -> bool:
|
|
@@ -109,7 +114,7 @@ def set_enabled(transformer, enabled: bool) -> bool:
|
|
| 109 |
return state["enabled"] if state else False
|
| 110 |
import torch
|
| 111 |
|
| 112 |
-
sign = 1.0 if enabled else -1.0
|
| 113 |
params = dict(transformer.named_parameters())
|
| 114 |
for name, a, b in state["entries"]:
|
| 115 |
delta = b.to(torch.float32) @ a.to(torch.float32)
|
|
|
|
| 27 |
import torch
|
| 28 |
|
| 29 |
LORA_REPO = os.environ.get("H3_LORA_REPO", "larryvrh/MiniMax-H3-Turbo-Lora")
|
| 30 |
+
# The recommended default per the model card: ckpt850 EMA (final checkpoint of the round, sharp at 4 steps).
|
| 31 |
+
# `off` disables.
|
| 32 |
+
LORA_FILE = os.environ.get("H3_LORA", "minimax_h3_turbo_4step_ema_ckpt850.safetensors")
|
| 33 |
+
# The card's sharpness/artifact dial: >1 against blurry ghosting/smear, <1 against over-sharp grain.
|
| 34 |
+
LORA_STRENGTH = float(os.environ.get("H3_LORA_STRENGTH", "1.0"))
|
| 35 |
|
| 36 |
|
| 37 |
def _delta_targets(name: str, delta: torch.Tensor, inner_dim: int) -> list[tuple[str, torch.Tensor]]:
|
|
|
|
| 82 |
for name in bases:
|
| 83 |
a = lora[f"{name}.lora_A.weight"].float()
|
| 84 |
b = lora[f"{name}.lora_B.weight"].float()
|
| 85 |
+
delta = (b @ a) * LORA_STRENGTH # alpha == rank, so the base scale is 1
|
| 86 |
targets = _delta_targets(name, delta, inner_dim)
|
| 87 |
for key, converted in targets:
|
| 88 |
param = params.get(key)
|
|
|
|
| 97 |
transformer._lora_state = {
|
| 98 |
"enabled": True,
|
| 99 |
"inner_dim": inner_dim,
|
| 100 |
+
"strength": LORA_STRENGTH,
|
| 101 |
"entries": [
|
| 102 |
(name, lora[f"{name}.lora_A.weight"], lora[f"{name}.lora_B.weight"])
|
| 103 |
for name in entries
|
| 104 |
],
|
| 105 |
}
|
| 106 |
+
strength = "" if LORA_STRENGTH == 1.0 else f" @ strength {LORA_STRENGTH}"
|
| 107 |
+
return f"LoRA `{LORA_REPO}/{LORA_FILE}`{strength} folded into {folded} weights ({len(bases)} modules)"
|
| 108 |
|
| 109 |
|
| 110 |
def set_enabled(transformer, enabled: bool) -> bool:
|
|
|
|
| 114 |
return state["enabled"] if state else False
|
| 115 |
import torch
|
| 116 |
|
| 117 |
+
sign = (1.0 if enabled else -1.0) * state.get("strength", 1.0)
|
| 118 |
params = dict(transformer.named_parameters())
|
| 119 |
for name, a, b in state["entries"]:
|
| 120 |
delta = b.to(torch.float32) @ a.to(torch.float32)
|
index.html
CHANGED
|
@@ -211,8 +211,8 @@
|
|
| 211 |
<input type="range" id="duration" min="2" max="14" step="1" value="5">
|
| 212 |
</div>
|
| 213 |
<div>
|
| 214 |
-
<div class="slider-row"><span>Steps</span><output id="steps-out">
|
| 215 |
-
<input type="range" id="steps" min="2" max="40" step="1" value="
|
| 216 |
</div>
|
| 217 |
<div>
|
| 218 |
<div class="deck-label">Seed</div>
|
|
|
|
| 211 |
<input type="range" id="duration" min="2" max="14" step="1" value="5">
|
| 212 |
</div>
|
| 213 |
<div>
|
| 214 |
+
<div class="slider-row"><span>Steps</span><output id="steps-out">4</output></div>
|
| 215 |
+
<input type="range" id="steps" min="2" max="40" step="1" value="4">
|
| 216 |
</div>
|
| 217 |
<div>
|
| 218 |
<div class="deck-label">Seed</div>
|