Spaces:
Running on Zero
Running on Zero
Pass the caller's x-ip-token to the conditioner explicitly
Browse filesLocalContext-based token forwarding is unreliable in Server mode, so the
conditioner's ZeroGPU booking fell back to this Space's pod IP and its
shared quota ('quota exceeded' despite the caller having quota). Extract
the header from the injected Request and hand it to a per-request
gradio_client, per the gradio ZeroGPU docs.
app.py
CHANGED
|
@@ -12,7 +12,7 @@ from functools import cache
|
|
| 12 |
# startup rather than on GPU time.
|
| 13 |
import spaces
|
| 14 |
from fastapi.responses import HTMLResponse
|
| 15 |
-
from gradio import Server
|
| 16 |
from gradio.data_classes import FileData
|
| 17 |
|
| 18 |
MODEL_REPO = os.environ.get("H3_MODEL_REPO", "MiniMaxAI/MiniMax-H3")
|
|
@@ -177,20 +177,31 @@ def _arm_decode_hooks(pipe):
|
|
| 177 |
|
| 178 |
@cache
|
| 179 |
def conditioner():
|
| 180 |
-
"""The other half, over the gradio API.
|
| 181 |
-
|
| 182 |
from gradio_client import Client
|
| 183 |
|
| 184 |
return Client(CONDITIONER_SPACE)
|
| 185 |
|
| 186 |
|
| 187 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
"""`/encode` on the conditioner Space: a safetensors file holding `prompt_embeds` + `text_token_tags`, with the
|
| 189 |
resolved `height` / `width` / `num_frames` in its metadata, plus the plan. `canvas` is the label."""
|
| 190 |
from gradio_client import handle_file
|
| 191 |
from safetensors import safe_open
|
| 192 |
|
| 193 |
-
path, plan =
|
| 194 |
prompt=prompt,
|
| 195 |
image_path=handle_file(image_path) if image_path else None,
|
| 196 |
last_image_path=handle_file(last_image_path) if last_image_path else None,
|
|
@@ -286,7 +297,7 @@ def _fit_keyframe(image_path, current_canvas):
|
|
| 286 |
return image_path, label
|
| 287 |
|
| 288 |
|
| 289 |
-
def generate(prompt, image_path=None, last_image_path=None, canvas=DEFAULT_CANVAS, duration=5, steps=4, seed=42, upsample=False):
|
| 290 |
"""One request. `upsample` is last and defaults off, so a positional API client that predates it is unaffected."""
|
| 291 |
if LOAD_ERROR:
|
| 292 |
raise Exception(LOAD_ERROR)
|
|
@@ -312,7 +323,7 @@ def generate(prompt, image_path=None, last_image_path=None, canvas=DEFAULT_CANVA
|
|
| 312 |
|
| 313 |
conditioned = time.time()
|
| 314 |
prompt_embeds, text_token_tags, metadata, plan = encode_remote(
|
| 315 |
-
prompt, first, last, canvas, num_frames, rewrite_prompt=upsample
|
| 316 |
)
|
| 317 |
condition_seconds = time.time() - conditioned
|
| 318 |
height, width, num_frames = (int(metadata[key]) for key in ("height", "width", "num_frames"))
|
|
@@ -362,9 +373,11 @@ app = Server(title="MiniMax-H3 Studio")
|
|
| 362 |
@app.api(name="generate")
|
| 363 |
def _generate_api(prompt: str, image_path: FileData | None = None, last_image_path: FileData | None = None,
|
| 364 |
canvas: str = DEFAULT_CANVAS, duration: float = 5, steps: int = 4, seed: float = 42,
|
| 365 |
-
upsample: bool = False) -> tuple[FileData, str, str]:
|
| 366 |
"""Generate a video with a synchronized soundtrack. Returns (video, report, refined prompt)."""
|
| 367 |
-
|
|
|
|
|
|
|
| 368 |
|
| 369 |
|
| 370 |
@app.get("/status")
|
|
|
|
| 12 |
# startup rather than on GPU time.
|
| 13 |
import spaces
|
| 14 |
from fastapi.responses import HTMLResponse
|
| 15 |
+
from gradio import Request, Server
|
| 16 |
from gradio.data_classes import FileData
|
| 17 |
|
| 18 |
MODEL_REPO = os.environ.get("H3_MODEL_REPO", "MiniMaxAI/MiniMax-H3")
|
|
|
|
| 177 |
|
| 178 |
@cache
|
| 179 |
def conditioner():
|
| 180 |
+
"""The other half, over the gradio API. Used only when the caller's token could not be extracted; the booking is
|
| 181 |
+
then billed to this Space's pod IP and its small shared quota."""
|
| 182 |
from gradio_client import Client
|
| 183 |
|
| 184 |
return Client(CONDITIONER_SPACE)
|
| 185 |
|
| 186 |
|
| 187 |
+
def conditioner_client(ip_token):
|
| 188 |
+
"""A conditioner client billed to the caller. `LocalContext`-based token forwarding is not reliable in Server
|
| 189 |
+
mode, so the `x-ip-token` header is extracted from the incoming request and passed explicitly (per the gradio
|
| 190 |
+
ZeroGPU docs); a per-request Client is cheap next to a 45s encode."""
|
| 191 |
+
if not ip_token:
|
| 192 |
+
return conditioner()
|
| 193 |
+
from gradio_client import Client
|
| 194 |
+
|
| 195 |
+
return Client(CONDITIONER_SPACE, headers={"x-ip-token": ip_token})
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
def encode_remote(prompt, image_path, last_image_path, canvas, num_frames, rewrite_prompt=False, ip_token=None):
|
| 199 |
"""`/encode` on the conditioner Space: a safetensors file holding `prompt_embeds` + `text_token_tags`, with the
|
| 200 |
resolved `height` / `width` / `num_frames` in its metadata, plus the plan. `canvas` is the label."""
|
| 201 |
from gradio_client import handle_file
|
| 202 |
from safetensors import safe_open
|
| 203 |
|
| 204 |
+
path, plan = conditioner_client(ip_token).predict(
|
| 205 |
prompt=prompt,
|
| 206 |
image_path=handle_file(image_path) if image_path else None,
|
| 207 |
last_image_path=handle_file(last_image_path) if last_image_path else None,
|
|
|
|
| 297 |
return image_path, label
|
| 298 |
|
| 299 |
|
| 300 |
+
def generate(prompt, image_path=None, last_image_path=None, canvas=DEFAULT_CANVAS, duration=5, steps=4, seed=42, upsample=False, ip_token=None):
|
| 301 |
"""One request. `upsample` is last and defaults off, so a positional API client that predates it is unaffected."""
|
| 302 |
if LOAD_ERROR:
|
| 303 |
raise Exception(LOAD_ERROR)
|
|
|
|
| 323 |
|
| 324 |
conditioned = time.time()
|
| 325 |
prompt_embeds, text_token_tags, metadata, plan = encode_remote(
|
| 326 |
+
prompt, first, last, canvas, num_frames, rewrite_prompt=upsample, ip_token=ip_token
|
| 327 |
)
|
| 328 |
condition_seconds = time.time() - conditioned
|
| 329 |
height, width, num_frames = (int(metadata[key]) for key in ("height", "width", "num_frames"))
|
|
|
|
| 373 |
@app.api(name="generate")
|
| 374 |
def _generate_api(prompt: str, image_path: FileData | None = None, last_image_path: FileData | None = None,
|
| 375 |
canvas: str = DEFAULT_CANVAS, duration: float = 5, steps: int = 4, seed: float = 42,
|
| 376 |
+
upsample: bool = False, request: Request = None) -> tuple[FileData, str, str]:
|
| 377 |
"""Generate a video with a synchronized soundtrack. Returns (video, report, refined prompt)."""
|
| 378 |
+
# `request` is injected by the event system, not an API input; its x-ip-token bills the conditioner to the caller.
|
| 379 |
+
ip_token = request.headers.get("x-ip-token") if request is not None else None
|
| 380 |
+
return generate(prompt, image_path, last_image_path, canvas, duration, steps, seed, upsample, ip_token=ip_token)
|
| 381 |
|
| 382 |
|
| 383 |
@app.get("/status")
|