multimodalart HF Staff commited on
Commit
afea884
·
verified ·
1 Parent(s): a4b3082

Forward the caller's ZeroGPU token to the conditioner, plainly

Browse files
Files changed (1) hide show
  1. app.py +19 -77
app.py CHANGED
@@ -200,9 +200,8 @@ def get_duration(prompt_embeds, text_token_tags, references, height, width, num_
200
  PIPE = None
201
  MANAGER = None
202
  LOAD_ERROR: str | None = None
203
- LOG_TAG = "ref2va"
204
- # One `gradio_client.Client` per ZeroGPU identity the conditioner is called with; see `call_conditioner`.
205
- CLIENTS: dict[tuple[str | None, str | None], object] = {}
206
 
207
 
208
  def load_models() -> str | None:
@@ -308,89 +307,33 @@ def _arm_decode_hooks(pipe):
308
  setattr(module, method, armed)
309
 
310
 
311
- def conditioner(ip_token: str | None = None, hf_token: str | None = None):
312
- """A client for the other half, bound to one ZeroGPU identity.
313
 
314
- Cached per identity: building a `Client` costs a round trip to the Space config, and a forwarded token is per
315
- user session.
 
 
 
 
316
  """
317
  from gradio_client import Client
318
 
319
- key = (ip_token, hf_token)
320
- if key in CLIENTS:
321
- return CLIENTS[key]
322
- # No token of this Space's own: the booking is meant to be the caller's, and an unattributed call is the
323
- # fallback rather than a second identity to spend.
324
- client = Client(CONDITIONER_SPACE, headers={"X-IP-Token": ip_token} if ip_token else None)
325
- if len(CLIENTS) >= 32:
326
- CLIENTS.pop(next(iter(CLIENTS)))
327
- CLIENTS[key] = client
328
- return client
329
-
330
-
331
- # What ZeroGPU says when an identity cannot pay for a booking: a proxy token its `/usage-approval` refused (`401`,
332
- # surfaced as "Expired ZeroGPU proxy token"), a duration past what that identity may book, and an exhausted quota.
333
- _UNPAYABLE = ("proxy token", "ZeroGPU quota", "larger than the maximum allowed", "GPU limit")
334
-
335
-
336
- def call_conditioner(ip_token, **arguments):
337
- """One conditioner call, billed to **the requesting user**.
338
-
339
- ZeroGPU attributes a booking to the `X-IP-Token` of the request that triggered it: `/schedule` hands that token to
340
- the Spaces API's `/usage-approval` together with the duration and the calling pod's IP, and nothing else — there is
341
- no Space identity in that call, so a *valid* token minted anywhere is honoured and the user's own quota pays for
342
- both halves of their request. That is the whole reason this Space forwards the header instead of spending a token
343
- of its own.
344
-
345
- A token the Spaces API refuses (`401`) falls back to calling the conditioner with no token, which is billed to this
346
- Space's pod IP off a small shared quota. That is a safety net, not the intended path: the log line
347
- `conditioner call paid for by ...` records which one actually paid, so a Space that keeps falling back is visible.
348
- """
349
- api_name = arguments.pop("api_name")
350
- attempts = [("no token, on this Space's shared IP quota", {})]
351
- if ip_token:
352
- attempts.insert(0, ("the requesting user's own ZeroGPU token", {"ip_token": ip_token}))
353
-
354
- for index, (label, identity) in enumerate(attempts):
355
- try:
356
- result = conditioner(**identity).predict(**arguments, api_name=api_name)
357
- print(f"[{LOG_TAG}] conditioner call paid for by {label}", flush=True)
358
- return result
359
- except Exception as error:
360
- if index == len(attempts) - 1 or not any(reason in str(error) for reason in _UNPAYABLE):
361
- raise
362
- print(f"[{LOG_TAG}] {label} was refused: {error}; falling back", flush=True)
363
- CLIENTS.pop((identity.get("ip_token"), identity.get("hf_token")), None)
364
 
365
 
366
  def ip_token_of(request) -> str | None:
367
  """The requesting user's ZeroGPU identity, as the Spaces router put it on this request.
368
 
369
- Logged, decoded, on every request never the token itself, only what it claims. ZeroGPU refuses a token its
370
- `/usage-approval` considers expired, and that refusal is indistinguishable from a missing one in the outcome, so
371
- the claims are what tell the two apart when a request ends up on the fallback quota. Both the UI path and the
372
- `/generate` API path reach this through the same `gr.Request` gradio injects for a parameter annotated with it.
373
  """
374
  headers = getattr(request, "headers", None)
375
- token = None if headers is None else headers.get("x-ip-token")
376
- if token is None:
377
- print(f"[{LOG_TAG}] no X-IP-Token on this request; the conditioner call cannot be billed to the caller", flush=True)
378
- return None
379
- try:
380
- import base64
381
- import json
382
- import time
383
-
384
- payload = json.loads(base64.urlsafe_b64decode(f"{token.split('.')[1]}=="))
385
- left = payload.get("exp", 0) - time.time()
386
- print(
387
- f"[{LOG_TAG}] X-IP-Token present: {left:.0f}s to expiry, claims "
388
- f"{ {k: v for k, v in payload.items() if k in ('exp', 'iat', 'sub', 'aud', 'error', 'user')} }",
389
- flush=True,
390
- )
391
- except Exception as error: # a token that cannot be read is still worth forwarding; ZeroGPU is the judge
392
- print(f"[{LOG_TAG}] X-IP-Token present but unreadable ({type(error).__name__}: {error})", flush=True)
393
- return token
394
 
395
  def probe(path: str) -> tuple[float | None, float | None]:
396
  """`(video seconds, audio seconds)` of a media file, either being `None` when the stream is absent."""
@@ -503,8 +446,7 @@ def encode_remote(prompt, references, canvas, num_frames, rewrite_prompt=False,
503
  from gradio_client import handle_file
504
  from safetensors import safe_open
505
 
506
- path, plan = call_conditioner(
507
- ip_token,
508
  prompt=prompt,
509
  media=[handle_file(path) for _, path in references],
510
  kinds=",".join(kind for kind, _ in references),
 
200
  PIPE = None
201
  MANAGER = None
202
  LOAD_ERROR: str | None = None
203
+ # One `gradio_client.Client` per forwarded token; see `conditioner`.
204
+ CLIENTS: dict[str | None, object] = {}
 
205
 
206
 
207
  def load_models() -> str | None:
 
307
  setattr(module, method, armed)
308
 
309
 
310
+ def conditioner(ip_token: str | None = None):
311
+ """The other half, over the gradio API, billed to the requesting user.
312
 
313
+ ZeroGPU attributes a booking to the `X-IP-Token` of the request that triggered it: `/schedule` hands that token to
314
+ the Spaces API together with the duration and the calling pod's IP and nothing else — there is no Space identity in
315
+ the decision — so forwarding the caller's header makes the user's own quota pay for both halves of their request,
316
+ the way it would if this were a single Space.
317
+
318
+ Cached per token: building a `Client` costs a round trip to the Space config, and a token is per user session.
319
  """
320
  from gradio_client import Client
321
 
322
+ if ip_token not in CLIENTS:
323
+ if len(CLIENTS) >= 32:
324
+ CLIENTS.pop(next(iter(CLIENTS)))
325
+ CLIENTS[ip_token] = Client(CONDITIONER_SPACE, headers={"X-IP-Token": ip_token} if ip_token else None)
326
+ return CLIENTS[ip_token]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
327
 
328
 
329
  def ip_token_of(request) -> str | None:
330
  """The requesting user's ZeroGPU identity, as the Spaces router put it on this request.
331
 
332
+ The UI path and the `/generate` API path both reach this through the `gr.Request` gradio injects for a parameter
333
+ annotated with it.
 
 
334
  """
335
  headers = getattr(request, "headers", None)
336
+ return None if headers is None else headers.get("x-ip-token")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
337
 
338
  def probe(path: str) -> tuple[float | None, float | None]:
339
  """`(video seconds, audio seconds)` of a media file, either being `None` when the stream is absent."""
 
446
  from gradio_client import handle_file
447
  from safetensors import safe_open
448
 
449
+ path, plan = conditioner(ip_token).predict(
 
450
  prompt=prompt,
451
  media=[handle_file(path) for _, path in references],
452
  kinds=",".join(kind for kind, _ in references),