Spaces:
Running on Zero
Running on Zero
Upload app.py
Browse files
app.py
CHANGED
|
@@ -109,7 +109,6 @@ PIPE = None
|
|
| 109 |
LOAD_ERROR: str | None = None
|
| 110 |
LOADED_IN: float | None = None
|
| 111 |
|
| 112 |
-
|
| 113 |
def snap_frames(seconds: float) -> int:
|
| 114 |
"""The frame count MiniMax-H3's video VAE can decode: the next ``17 * n + 5`` at 24 fps."""
|
| 115 |
frames = max(1, round(float(seconds) * FPS))
|
|
@@ -117,14 +116,12 @@ def snap_frames(seconds: float) -> int:
|
|
| 117 |
frames += 1
|
| 118 |
return frames
|
| 119 |
|
| 120 |
-
|
| 121 |
def lower_duration_floor(seconds: float = MIN_UI_DURATION) -> None:
|
| 122 |
"""Let the pipeline generate below its 5 s floor."""
|
| 123 |
from diffusers.modular_pipelines.minimax_h3.modular_pipeline import MiniMaxH3ModularPipeline
|
| 124 |
|
| 125 |
MiniMaxH3ModularPipeline.min_duration = property(lambda self: float(seconds))
|
| 126 |
|
| 127 |
-
|
| 128 |
def shifted_schedule(shift: float) -> list[float]:
|
| 129 |
"""``BASE_SCHEDULE`` pushed through H3's exponential sigma shift ``s*Ο / (1 + (s - 1)*Ο)``.
|
| 130 |
|
|
@@ -142,7 +139,6 @@ def shifted_schedule(shift: float) -> list[float]:
|
|
| 142 |
"""
|
| 143 |
return [shift * sigma / (1 + (shift - 1) * sigma) for sigma in BASE_SCHEDULE]
|
| 144 |
|
| 145 |
-
|
| 146 |
def pin_distilled_schedule(scheduler) -> None:
|
| 147 |
"""Pin the shifted ``BASE_SCHEDULE`` onto a scheduler so *every* ``set_timesteps`` call lands on it.
|
| 148 |
|
|
@@ -173,7 +169,6 @@ def pin_distilled_schedule(scheduler) -> None:
|
|
| 173 |
scheduler.set_timesteps = set_timesteps
|
| 174 |
scheduler._flashgen_pinned = True
|
| 175 |
|
| 176 |
-
|
| 177 |
# ββ LoRA loading (weight folding) βββββββββββββββββββββββββββββββββββββββββββββ
|
| 178 |
#
|
| 179 |
# This mirrors ``merge_lora_ckpt.py``, the merge script the LoRA repo itself ships, rather than a
|
|
@@ -201,7 +196,6 @@ def pin_distilled_schedule(scheduler) -> None:
|
|
| 201 |
# contiguous thirds directly β the naive reading β scatters every head's q/k/v across all three
|
| 202 |
# projections, which turns the adapter into structured noise on all 52 attention blocks.
|
| 203 |
|
| 204 |
-
|
| 205 |
def _reorder_interleaved_qkv(weight, num_heads: int, head_dim: int):
|
| 206 |
"""De-interleave per-head fused-QKV rows into ``[q_all; k_all; v_all]``.
|
| 207 |
|
|
@@ -215,7 +209,6 @@ def _reorder_interleaved_qkv(weight, num_heads: int, head_dim: int):
|
|
| 215 |
[part.reshape(num_heads * head_dim, *weight.shape[1:]) for part in (query, key, value)], dim=0
|
| 216 |
)
|
| 217 |
|
| 218 |
-
|
| 219 |
def _lora_target_name(source_name: str) -> str:
|
| 220 |
"""Map an original-checkpoint base name (without ``.lora_A/B.*``) to the diffusers parameter path."""
|
| 221 |
if source_name.startswith("token_refiner.blocks."):
|
|
@@ -226,7 +219,6 @@ def _lora_target_name(source_name: str) -> str:
|
|
| 226 |
target = source_name
|
| 227 |
return target.replace("final_layer.adaln_proj.linear", "norm_out.linear")
|
| 228 |
|
| 229 |
-
|
| 230 |
def _lora_targets(name: str, b_weight, num_heads: int, head_dim: int):
|
| 231 |
"""Yield ``(diffusers_param_key, row_transformed_B)`` for one original LoRA base name."""
|
| 232 |
target = _lora_target_name(name)
|
|
@@ -250,7 +242,6 @@ def _lora_targets(name: str, b_weight, num_heads: int, head_dim: int):
|
|
| 250 |
# adaln_proj.linear (block-level) and norm_out.linear: identical row layout.
|
| 251 |
yield target + ".weight", b_weight
|
| 252 |
|
| 253 |
-
|
| 254 |
def load_and_apply_lora(transformer) -> str:
|
| 255 |
"""Download the FlashGen LoRA, remap keys, and fold ``scale * (B @ A)`` into the base weights."""
|
| 256 |
import torch
|
|
@@ -317,7 +308,6 @@ def load_and_apply_lora(transformer) -> str:
|
|
| 317 |
f"-> {folded} weight deltas Β· {LORA_FILE}"
|
| 318 |
)
|
| 319 |
|
| 320 |
-
|
| 321 |
# ββ Additional LoRAs βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 322 |
#
|
| 323 |
# These are folded after FlashGen, so the original 4-step FlashGen schedule and
|
|
@@ -422,7 +412,6 @@ def _apply_extra_lora(
|
|
| 422 |
f"{folded} weight deltas Β· scale {float(scale):g}"
|
| 423 |
)
|
| 424 |
|
| 425 |
-
|
| 426 |
def load_and_apply_extra_loras(transformer) -> list[str]:
|
| 427 |
return [
|
| 428 |
_apply_extra_lora(
|
|
@@ -441,10 +430,8 @@ def load_and_apply_extra_loras(transformer) -> list[str]:
|
|
| 441 |
),
|
| 442 |
]
|
| 443 |
|
| 444 |
-
|
| 445 |
# ββ Model loading βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 446 |
|
| 447 |
-
|
| 448 |
def load_models() -> str | None:
|
| 449 |
"""Load the denoising half at startup: transformer + VAEs + schedulers, then fold the LoRA in."""
|
| 450 |
global PIPE, LOAD_ERROR, LOADED_IN
|
|
@@ -504,17 +491,14 @@ def load_models() -> str | None:
|
|
| 504 |
LOAD_ERROR = f"**Loading failed** after {time.time() - started:.0f}s: `{type(error).__name__}: {error}`"
|
| 505 |
return LOAD_ERROR
|
| 506 |
|
| 507 |
-
|
| 508 |
# ββ Remote conditioning ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 509 |
|
| 510 |
-
|
| 511 |
@cache
|
| 512 |
def conditioner():
|
| 513 |
from gradio_client import Client
|
| 514 |
|
| 515 |
return Client(CONDITIONER_SPACE)
|
| 516 |
|
| 517 |
-
|
| 518 |
def encode_remote(prompt, image_path, last_image_path, canvas, num_frames, ip_token=None, rewrite_prompt=False):
|
| 519 |
"""Call the conditioner Space to get ``prompt_embeds`` + ``text_token_tags`` + resolved geometry.
|
| 520 |
|
|
@@ -550,7 +534,6 @@ def encode_remote(prompt, image_path, last_image_path, canvas, num_frames, ip_to
|
|
| 550 |
metadata = handle.metadata()
|
| 551 |
return handle.get_tensor("prompt_embeds"), handle.get_tensor("text_token_tags"), metadata, plan
|
| 552 |
|
| 553 |
-
|
| 554 |
def conditioner_client(ip_token):
|
| 555 |
if not ip_token:
|
| 556 |
return conditioner()
|
|
@@ -558,10 +541,8 @@ def conditioner_client(ip_token):
|
|
| 558 |
|
| 559 |
return Client(CONDITIONER_SPACE, headers={"x-ip-token": ip_token})
|
| 560 |
|
| 561 |
-
|
| 562 |
# ββ Geometry helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 563 |
|
| 564 |
-
|
| 565 |
def _snap_canvas(aspect: float, current_canvas: str) -> str:
|
| 566 |
"""The supported canvas whose aspect ratio is closest to ``aspect``.
|
| 567 |
|
|
@@ -582,7 +563,6 @@ def _snap_canvas(aspect: float, current_canvas: str) -> str:
|
|
| 582 |
return current_canvas
|
| 583 |
return fastest[ratio][0]
|
| 584 |
|
| 585 |
-
|
| 586 |
def _cover_crop(image_path: str, canvas_label: str) -> str:
|
| 587 |
"""Center cover-crop the file at ``image_path`` to ``canvas_label``'s aspect ratio, in place.
|
| 588 |
|
|
@@ -607,7 +587,6 @@ def _cover_crop(image_path: str, canvas_label: str) -> str:
|
|
| 607 |
img.save(image_path)
|
| 608 |
return image_path
|
| 609 |
|
| 610 |
-
|
| 611 |
def _fit_keyframe(image_path, current_canvas):
|
| 612 |
"""Snap the canvas to a keyframe's aspect ratio, then cover-crop the keyframe to that canvas."""
|
| 613 |
from PIL import Image as _Image
|
|
@@ -617,7 +596,6 @@ def _fit_keyframe(image_path, current_canvas):
|
|
| 617 |
label = _snap_canvas(aspect, current_canvas)
|
| 618 |
return _cover_crop(image_path, label), label
|
| 619 |
|
| 620 |
-
|
| 621 |
def _fit_keyframe_ui(image_path, current_canvas):
|
| 622 |
"""``image.upload`` handler β the reference Space's behaviour, ported.
|
| 623 |
|
|
@@ -631,7 +609,6 @@ def _fit_keyframe_ui(image_path, current_canvas):
|
|
| 631 |
cropped, label = _fit_keyframe(path, current_canvas)
|
| 632 |
return gr.update(value=cropped), gr.update(value=label)
|
| 633 |
|
| 634 |
-
|
| 635 |
def _fit_last_keyframe_ui(last_image_path, image_path, current_canvas):
|
| 636 |
"""``last_image.upload`` handler. The first frame owns the canvas when there is one, so a last
|
| 637 |
frame uploaded on top of it is only cover-cropped to the canvas already chosen."""
|
|
@@ -642,20 +619,17 @@ def _fit_last_keyframe_ui(last_image_path, image_path, current_canvas):
|
|
| 642 |
return gr.update(value=_cover_crop(path, current_canvas)), gr.update()
|
| 643 |
return _fit_keyframe_ui(path, current_canvas)
|
| 644 |
|
| 645 |
-
|
| 646 |
def _as_path(value):
|
| 647 |
if isinstance(value, dict):
|
| 648 |
value = value.get("path") or (value.get("url") or "").removeprefix("/gradio_api/file=")
|
| 649 |
return value or None
|
| 650 |
|
| 651 |
-
|
| 652 |
# ββ GPU duration estimation βββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 653 |
|
| 654 |
_DUR_B, _DUR_C = 1.1745e-4, 3.8396e-9
|
| 655 |
_DECODE_BASE, _DECODE_PER_DEFAULT_CANVAS, _DEFAULT_CANVAS_PIXELS = 15, 15, 960 * 544 * 124
|
| 656 |
_PLACEMENT_ALLOWANCE, _PAD = 12, 10
|
| 657 |
|
| 658 |
-
|
| 659 |
def get_duration(prompt_embeds, text_token_tags, image, last_image, height, width, num_frames, seed, *a, **k):
|
| 660 |
"""Estimate GPU duration for the ``_generate`` call. Steps is fixed at NUM_STEPS (4)."""
|
| 661 |
height, width, num_frames = int(height), int(width), int(num_frames)
|
|
@@ -667,10 +641,8 @@ def get_duration(prompt_embeds, text_token_tags, image, last_image, height, widt
|
|
| 667 |
decode = _DECODE_BASE + _DECODE_PER_DEFAULT_CANVAS * (height * width * num_frames) / _DEFAULT_CANVAS_PIXELS
|
| 668 |
return max(60, int(denoise + decode) + _PLACEMENT_ALLOWANCE + _PAD)
|
| 669 |
|
| 670 |
-
|
| 671 |
# ββ Inference βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 672 |
|
| 673 |
-
|
| 674 |
@spaces.GPU(duration=get_duration, size=GPU_SIZE)
|
| 675 |
def _generate(prompt_embeds, text_token_tags, image, last_image, height, width, num_frames, seed):
|
| 676 |
"""Denoise + decode on GPU time. The LoRA is already folded at startup; 4 steps with custom sigmas."""
|
|
@@ -713,14 +685,12 @@ def _generate(prompt_embeds, text_token_tags, image, last_image, height, width,
|
|
| 713 |
sigmas_used,
|
| 714 |
)
|
| 715 |
|
| 716 |
-
|
| 717 |
def _caller_ip_token() -> str | None:
|
| 718 |
from gradio.context import LocalContext
|
| 719 |
|
| 720 |
request = LocalContext.request.get()
|
| 721 |
return request.headers.get("x-ip-token") if request is not None else None
|
| 722 |
|
| 723 |
-
|
| 724 |
def generate(
|
| 725 |
prompt,
|
| 726 |
image_path=None,
|
|
@@ -820,10 +790,8 @@ def generate(
|
|
| 820 |
)
|
| 821 |
return path, report, enhanced, gr.update(visible=bool(enhanced))
|
| 822 |
|
| 823 |
-
|
| 824 |
# ββ UI ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 825 |
|
| 826 |
-
|
| 827 |
INTRO = """# MiniMax-H3 4-Step FlashGen LoRA
|
| 828 |
|
| 829 |
<div align="center">
|
|
|
|
| 109 |
LOAD_ERROR: str | None = None
|
| 110 |
LOADED_IN: float | None = None
|
| 111 |
|
|
|
|
| 112 |
def snap_frames(seconds: float) -> int:
|
| 113 |
"""The frame count MiniMax-H3's video VAE can decode: the next ``17 * n + 5`` at 24 fps."""
|
| 114 |
frames = max(1, round(float(seconds) * FPS))
|
|
|
|
| 116 |
frames += 1
|
| 117 |
return frames
|
| 118 |
|
|
|
|
| 119 |
def lower_duration_floor(seconds: float = MIN_UI_DURATION) -> None:
|
| 120 |
"""Let the pipeline generate below its 5 s floor."""
|
| 121 |
from diffusers.modular_pipelines.minimax_h3.modular_pipeline import MiniMaxH3ModularPipeline
|
| 122 |
|
| 123 |
MiniMaxH3ModularPipeline.min_duration = property(lambda self: float(seconds))
|
| 124 |
|
|
|
|
| 125 |
def shifted_schedule(shift: float) -> list[float]:
|
| 126 |
"""``BASE_SCHEDULE`` pushed through H3's exponential sigma shift ``s*Ο / (1 + (s - 1)*Ο)``.
|
| 127 |
|
|
|
|
| 139 |
"""
|
| 140 |
return [shift * sigma / (1 + (shift - 1) * sigma) for sigma in BASE_SCHEDULE]
|
| 141 |
|
|
|
|
| 142 |
def pin_distilled_schedule(scheduler) -> None:
|
| 143 |
"""Pin the shifted ``BASE_SCHEDULE`` onto a scheduler so *every* ``set_timesteps`` call lands on it.
|
| 144 |
|
|
|
|
| 169 |
scheduler.set_timesteps = set_timesteps
|
| 170 |
scheduler._flashgen_pinned = True
|
| 171 |
|
|
|
|
| 172 |
# ββ LoRA loading (weight folding) βββββββββββββββββββββββββββββββββββββββββββββ
|
| 173 |
#
|
| 174 |
# This mirrors ``merge_lora_ckpt.py``, the merge script the LoRA repo itself ships, rather than a
|
|
|
|
| 196 |
# contiguous thirds directly β the naive reading β scatters every head's q/k/v across all three
|
| 197 |
# projections, which turns the adapter into structured noise on all 52 attention blocks.
|
| 198 |
|
|
|
|
| 199 |
def _reorder_interleaved_qkv(weight, num_heads: int, head_dim: int):
|
| 200 |
"""De-interleave per-head fused-QKV rows into ``[q_all; k_all; v_all]``.
|
| 201 |
|
|
|
|
| 209 |
[part.reshape(num_heads * head_dim, *weight.shape[1:]) for part in (query, key, value)], dim=0
|
| 210 |
)
|
| 211 |
|
|
|
|
| 212 |
def _lora_target_name(source_name: str) -> str:
|
| 213 |
"""Map an original-checkpoint base name (without ``.lora_A/B.*``) to the diffusers parameter path."""
|
| 214 |
if source_name.startswith("token_refiner.blocks."):
|
|
|
|
| 219 |
target = source_name
|
| 220 |
return target.replace("final_layer.adaln_proj.linear", "norm_out.linear")
|
| 221 |
|
|
|
|
| 222 |
def _lora_targets(name: str, b_weight, num_heads: int, head_dim: int):
|
| 223 |
"""Yield ``(diffusers_param_key, row_transformed_B)`` for one original LoRA base name."""
|
| 224 |
target = _lora_target_name(name)
|
|
|
|
| 242 |
# adaln_proj.linear (block-level) and norm_out.linear: identical row layout.
|
| 243 |
yield target + ".weight", b_weight
|
| 244 |
|
|
|
|
| 245 |
def load_and_apply_lora(transformer) -> str:
|
| 246 |
"""Download the FlashGen LoRA, remap keys, and fold ``scale * (B @ A)`` into the base weights."""
|
| 247 |
import torch
|
|
|
|
| 308 |
f"-> {folded} weight deltas Β· {LORA_FILE}"
|
| 309 |
)
|
| 310 |
|
|
|
|
| 311 |
# ββ Additional LoRAs βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 312 |
#
|
| 313 |
# These are folded after FlashGen, so the original 4-step FlashGen schedule and
|
|
|
|
| 412 |
f"{folded} weight deltas Β· scale {float(scale):g}"
|
| 413 |
)
|
| 414 |
|
|
|
|
| 415 |
def load_and_apply_extra_loras(transformer) -> list[str]:
|
| 416 |
return [
|
| 417 |
_apply_extra_lora(
|
|
|
|
| 430 |
),
|
| 431 |
]
|
| 432 |
|
|
|
|
| 433 |
# ββ Model loading βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 434 |
|
|
|
|
| 435 |
def load_models() -> str | None:
|
| 436 |
"""Load the denoising half at startup: transformer + VAEs + schedulers, then fold the LoRA in."""
|
| 437 |
global PIPE, LOAD_ERROR, LOADED_IN
|
|
|
|
| 491 |
LOAD_ERROR = f"**Loading failed** after {time.time() - started:.0f}s: `{type(error).__name__}: {error}`"
|
| 492 |
return LOAD_ERROR
|
| 493 |
|
|
|
|
| 494 |
# ββ Remote conditioning ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 495 |
|
|
|
|
| 496 |
@cache
|
| 497 |
def conditioner():
|
| 498 |
from gradio_client import Client
|
| 499 |
|
| 500 |
return Client(CONDITIONER_SPACE)
|
| 501 |
|
|
|
|
| 502 |
def encode_remote(prompt, image_path, last_image_path, canvas, num_frames, ip_token=None, rewrite_prompt=False):
|
| 503 |
"""Call the conditioner Space to get ``prompt_embeds`` + ``text_token_tags`` + resolved geometry.
|
| 504 |
|
|
|
|
| 534 |
metadata = handle.metadata()
|
| 535 |
return handle.get_tensor("prompt_embeds"), handle.get_tensor("text_token_tags"), metadata, plan
|
| 536 |
|
|
|
|
| 537 |
def conditioner_client(ip_token):
|
| 538 |
if not ip_token:
|
| 539 |
return conditioner()
|
|
|
|
| 541 |
|
| 542 |
return Client(CONDITIONER_SPACE, headers={"x-ip-token": ip_token})
|
| 543 |
|
|
|
|
| 544 |
# ββ Geometry helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 545 |
|
|
|
|
| 546 |
def _snap_canvas(aspect: float, current_canvas: str) -> str:
|
| 547 |
"""The supported canvas whose aspect ratio is closest to ``aspect``.
|
| 548 |
|
|
|
|
| 563 |
return current_canvas
|
| 564 |
return fastest[ratio][0]
|
| 565 |
|
|
|
|
| 566 |
def _cover_crop(image_path: str, canvas_label: str) -> str:
|
| 567 |
"""Center cover-crop the file at ``image_path`` to ``canvas_label``'s aspect ratio, in place.
|
| 568 |
|
|
|
|
| 587 |
img.save(image_path)
|
| 588 |
return image_path
|
| 589 |
|
|
|
|
| 590 |
def _fit_keyframe(image_path, current_canvas):
|
| 591 |
"""Snap the canvas to a keyframe's aspect ratio, then cover-crop the keyframe to that canvas."""
|
| 592 |
from PIL import Image as _Image
|
|
|
|
| 596 |
label = _snap_canvas(aspect, current_canvas)
|
| 597 |
return _cover_crop(image_path, label), label
|
| 598 |
|
|
|
|
| 599 |
def _fit_keyframe_ui(image_path, current_canvas):
|
| 600 |
"""``image.upload`` handler β the reference Space's behaviour, ported.
|
| 601 |
|
|
|
|
| 609 |
cropped, label = _fit_keyframe(path, current_canvas)
|
| 610 |
return gr.update(value=cropped), gr.update(value=label)
|
| 611 |
|
|
|
|
| 612 |
def _fit_last_keyframe_ui(last_image_path, image_path, current_canvas):
|
| 613 |
"""``last_image.upload`` handler. The first frame owns the canvas when there is one, so a last
|
| 614 |
frame uploaded on top of it is only cover-cropped to the canvas already chosen."""
|
|
|
|
| 619 |
return gr.update(value=_cover_crop(path, current_canvas)), gr.update()
|
| 620 |
return _fit_keyframe_ui(path, current_canvas)
|
| 621 |
|
|
|
|
| 622 |
def _as_path(value):
|
| 623 |
if isinstance(value, dict):
|
| 624 |
value = value.get("path") or (value.get("url") or "").removeprefix("/gradio_api/file=")
|
| 625 |
return value or None
|
| 626 |
|
|
|
|
| 627 |
# ββ GPU duration estimation βββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 628 |
|
| 629 |
_DUR_B, _DUR_C = 1.1745e-4, 3.8396e-9
|
| 630 |
_DECODE_BASE, _DECODE_PER_DEFAULT_CANVAS, _DEFAULT_CANVAS_PIXELS = 15, 15, 960 * 544 * 124
|
| 631 |
_PLACEMENT_ALLOWANCE, _PAD = 12, 10
|
| 632 |
|
|
|
|
| 633 |
def get_duration(prompt_embeds, text_token_tags, image, last_image, height, width, num_frames, seed, *a, **k):
|
| 634 |
"""Estimate GPU duration for the ``_generate`` call. Steps is fixed at NUM_STEPS (4)."""
|
| 635 |
height, width, num_frames = int(height), int(width), int(num_frames)
|
|
|
|
| 641 |
decode = _DECODE_BASE + _DECODE_PER_DEFAULT_CANVAS * (height * width * num_frames) / _DEFAULT_CANVAS_PIXELS
|
| 642 |
return max(60, int(denoise + decode) + _PLACEMENT_ALLOWANCE + _PAD)
|
| 643 |
|
|
|
|
| 644 |
# ββ Inference βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 645 |
|
|
|
|
| 646 |
@spaces.GPU(duration=get_duration, size=GPU_SIZE)
|
| 647 |
def _generate(prompt_embeds, text_token_tags, image, last_image, height, width, num_frames, seed):
|
| 648 |
"""Denoise + decode on GPU time. The LoRA is already folded at startup; 4 steps with custom sigmas."""
|
|
|
|
| 685 |
sigmas_used,
|
| 686 |
)
|
| 687 |
|
|
|
|
| 688 |
def _caller_ip_token() -> str | None:
|
| 689 |
from gradio.context import LocalContext
|
| 690 |
|
| 691 |
request = LocalContext.request.get()
|
| 692 |
return request.headers.get("x-ip-token") if request is not None else None
|
| 693 |
|
|
|
|
| 694 |
def generate(
|
| 695 |
prompt,
|
| 696 |
image_path=None,
|
|
|
|
| 790 |
)
|
| 791 |
return path, report, enhanced, gr.update(visible=bool(enhanced))
|
| 792 |
|
|
|
|
| 793 |
# ββ UI ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 794 |
|
|
|
|
| 795 |
INTRO = """# MiniMax-H3 4-Step FlashGen LoRA
|
| 796 |
|
| 797 |
<div align="center">
|