wxDai commited on
Commit
302ed23
·
1 Parent(s): 1ec06a4

lease ladder 60/30/10 via free probes; 120s per session is too expensive

Browse files
Files changed (2) hide show
  1. README.md +2 -2
  2. app.py +14 -13
README.md CHANGED
@@ -34,5 +34,5 @@ DiT + streaming VAE pipeline, served on ZeroGPU.
34
  not the first user session.
35
  - `torch.compile` is inert in ZeroGPU forks; each fork instead pays a short warmup
36
  (CUDA-graph capture, KV fill), so the first 2-3 chunks are slower.
37
- - A session runs up to ~120s of GPU time (shorter retries when the queue is busy).
38
- Free ZeroGPU quota is limited — this is a small-scale demo.
 
34
  not the first user session.
35
  - `torch.compile` is inert in ZeroGPU forks; each fork instead pays a short warmup
36
  (CUDA-graph capture, KV fill), so the first 2-3 chunks are slower.
37
+ - A session runs up to ~60s of GPU time. Free ZeroGPU quota is limited — this is a
38
+ small-scale demo.
app.py CHANGED
@@ -81,8 +81,8 @@ warnings.filterwarnings("ignore", message="ZeroGPU: Cannot get Gradio app Queue
81
  import spaces
82
 
83
  CKPT_ROOT = Path(os.environ.get("JOYOMNI_CKPT_ROOT", str(_DATA / "checkpoints")))
84
- # 120s = 240s billed on xlarge; 300s exceeds the per-visitor scheduling cap.
85
- SESSION_DURATION = int(os.environ.get("JOYOMNI_SPACES_DURATION", "120"))
86
 
87
 
88
  # Prebuilt sm_120 wheel: pip runs before repo files exist on Spaces — install here.
@@ -723,12 +723,12 @@ async def ws(websocket: WebSocket):
723
  _visitor = SimpleNamespace(headers=_hdrs)
724
  pockets = [("visitor", _visitor), ("app-pool", None)]
725
 
726
- def _probe(req):
727
  token, _ = _get_token_and_payload(_get_headers(req))
728
  res, _meta = api_client().schedule(
729
  cgroup_path=zutils.self_cgroup_device_path(),
730
  token=token, token_version=2,
731
- duration_seconds=SESSION_DURATION, enable_queue=False, gpu_size="xlarge")
732
  return res
733
 
734
  def _run(name, req, duration):
@@ -748,37 +748,38 @@ async def ws(websocket: WebSocket):
748
 
749
  try:
750
  t0 = time.time()
751
- dry: set = set()
752
  last_quota = None
753
  while not stop.is_set():
754
- live = [(n, r) for n, r in pockets if n not in dry]
755
  if not live:
756
  raise RuntimeError(last_quota or "GPU quota exceeded")
757
  name, req = live[0]
758
- res = _probe(req)
 
759
  if isinstance(res, ScheduleResponse):
760
  release(res.allowToken, allow_404=True)
761
  try:
762
- _run(name, req, SESSION_DURATION)
763
  break
764
  except Exception as exc: # noqa: BLE001
765
  if got_gpu:
766
  raise
767
  emsg = str(exc)
768
- print(f"[ws] {name} lease failed: {emsg!r}", flush=True)
769
  if "quota" in emsg.lower() or "exceeded" in emsg.lower():
770
- dry.add(name)
771
  last_quota = emsg
772
  continue
773
  if isinstance(res, QuotaInfos):
774
- dry.add(name)
775
  if name == "visitor":
776
  last_quota = (f"You have exceeded your GPU quota "
777
- f"({SESSION_DURATION * 2}s requested vs. {res.left}s left). "
778
  f"Try again in {res.wait}.")
779
  continue
780
  if res is httpx.codes.UNAUTHORIZED:
781
- dry.add(name)
782
  continue
783
  close = getattr(res, "close", None)
784
  if close:
 
81
  import spaces
82
 
83
  CKPT_ROOT = Path(os.environ.get("JOYOMNI_CKPT_ROOT", str(_DATA / "checkpoints")))
84
+ # 60s = 120s billed on xlarge.
85
+ SESSION_DURATION = int(os.environ.get("JOYOMNI_SPACES_DURATION", "60"))
86
 
87
 
88
  # Prebuilt sm_120 wheel: pip runs before repo files exist on Spaces — install here.
 
723
  _visitor = SimpleNamespace(headers=_hdrs)
724
  pockets = [("visitor", _visitor), ("app-pool", None)]
725
 
726
+ def _probe(req, duration):
727
  token, _ = _get_token_and_payload(_get_headers(req))
728
  res, _meta = api_client().schedule(
729
  cgroup_path=zutils.self_cgroup_device_path(),
730
  token=token, token_version=2,
731
+ duration_seconds=duration, enable_queue=False, gpu_size="xlarge")
732
  return res
733
 
734
  def _run(name, req, duration):
 
748
 
749
  try:
750
  t0 = time.time()
751
+ durations = {n: list(dict.fromkeys((SESSION_DURATION, 30, 10))) for n, _ in pockets}
752
  last_quota = None
753
  while not stop.is_set():
754
+ live = [(n, r) for n, r in pockets if durations[n]]
755
  if not live:
756
  raise RuntimeError(last_quota or "GPU quota exceeded")
757
  name, req = live[0]
758
+ dur = durations[name][0]
759
+ res = _probe(req, dur)
760
  if isinstance(res, ScheduleResponse):
761
  release(res.allowToken, allow_404=True)
762
  try:
763
+ _run(name, req, dur)
764
  break
765
  except Exception as exc: # noqa: BLE001
766
  if got_gpu:
767
  raise
768
  emsg = str(exc)
769
+ print(f"[ws] {name}@{dur}s lease failed: {emsg!r}", flush=True)
770
  if "quota" in emsg.lower() or "exceeded" in emsg.lower():
771
+ durations[name].pop(0)
772
  last_quota = emsg
773
  continue
774
  if isinstance(res, QuotaInfos):
775
+ durations[name].pop(0)
776
  if name == "visitor":
777
  last_quota = (f"You have exceeded your GPU quota "
778
+ f"({dur * 2}s requested vs. {res.left}s left). "
779
  f"Try again in {res.wait}.")
780
  continue
781
  if res is httpx.codes.UNAUTHORIZED:
782
+ durations[name].clear()
783
  continue
784
  close = getattr(res, "close", None)
785
  if close: