Spaces:
Running on Zero
Running on Zero
hanhaonan02 Claude Fable 5.1 commited on
Commit ·
4b73edf
1
Parent(s): 033094c
Adaptive ZeroGPU step length with a quota explanation
Browse files- Ask for a 600 s xlarge step first (one weight transfer, most posters finish in one step) and fall
back to 300/120/60 s when ZeroGPU answers 'larger than the maximum allowed'.
- Generation budget derives from the step length; status shows the step length.
- Quota hint on failures: 27B BF16 needs the full GPU (2x quota), ~15-25 min per poster; PRO 40 min,
free 5 min, anonymous 2 min per day. README says the same.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- README.md +4 -2
- demo_ui.py +24 -9
README.md
CHANGED
|
@@ -19,8 +19,10 @@ Upload a research paper PDF and get an editable academic poster. A 27B vision-la
|
|
| 19 |
and writes the poster as standalone HTML; the Space renders it to PNG.
|
| 20 |
|
| 21 |
- Model: RL checkpoint-240, BF16, no quantization, 16K context.
|
| 22 |
-
-
|
| 23 |
-
|
|
|
|
|
|
|
| 24 |
- API: call `/analyze` once (PDF + settings), then `/step` repeatedly until the status starts with
|
| 25 |
`Done`, `Paused` or `Stopped`; each step is its own request so the ZeroGPU token stays valid.
|
| 26 |
- The examples on the page were generated by the same checkpoint with the default instruction and settings.
|
|
|
|
| 19 |
and writes the poster as standalone HTML; the Space renders it to PNG.
|
| 20 |
|
| 21 |
- Model: RL checkpoint-240, BF16, no quantization, 16K context.
|
| 22 |
+
- The model runs in BF16 on a full 96 GB ZeroGPU slot (`xlarge`, counted 2x against quota). A poster
|
| 23 |
+
takes about 15–25 minutes of ZeroGPU quota; PRO accounts have 40 min/day, free accounts 5 min and
|
| 24 |
+
anonymous visitors 2 min, so sign in with a PRO account (on huggingface.co, not the bare hf.space URL)
|
| 25 |
+
to finish a poster. The demo asks for a 10-minute GPU step first and falls back to shorter steps.
|
| 26 |
- API: call `/analyze` once (PDF + settings), then `/step` repeatedly until the status starts with
|
| 27 |
`Done`, `Paused` or `Stopped`; each step is its own request so the ZeroGPU token stays valid.
|
| 28 |
- The examples on the page were generated by the same checkpoint with the default instruction and settings.
|
demo_ui.py
CHANGED
|
@@ -14,7 +14,10 @@ from poster_render import render
|
|
| 14 |
ROOT = Path(__file__).resolve().parent
|
| 15 |
ARTIFACTS = Path(tempfile.mkdtemp(prefix='rap_demo_'))
|
| 16 |
WINDOW = 16384
|
| 17 |
-
MAX_STEPS = 10 # ZeroGPU steps
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
CSS = '''
|
| 20 |
.gradio-container {max-width: 1360px !important; margin: 0 auto;}
|
|
@@ -64,8 +67,12 @@ def build(model, processor):
|
|
| 64 |
p['input_tokens'] = n; p['continuation_ids'] = []; p['raw'] = ''; p['done'] = False
|
| 65 |
return p
|
| 66 |
|
| 67 |
-
|
|
|
|
|
|
|
|
|
|
| 68 |
def gpu_chunk(p):
|
|
|
|
| 69 |
inputs = processor.apply_chat_template(chat(p), tokenize=True, add_generation_prompt=True,
|
| 70 |
enable_thinking=False, return_dict=True, return_tensors='pt')
|
| 71 |
prefix = p.get('continuation_ids', [])
|
|
@@ -79,7 +86,7 @@ def build(model, processor):
|
|
| 79 |
inputs = inputs.to('cuda')
|
| 80 |
start = time.monotonic()
|
| 81 |
with torch.inference_mode():
|
| 82 |
-
output = model.generate(**inputs, max_new_tokens=min(8192, remaining), max_time=
|
| 83 |
use_cache=True, temperature=None, top_p=None, top_k=None, repetition_penalty=1.1)
|
| 84 |
new_ids = output[0, length:].detach().cpu().tolist()
|
| 85 |
all_ids = prefix + new_ids
|
|
@@ -122,13 +129,21 @@ def build(model, processor):
|
|
| 122 |
return (dict(p, paused=True), f"Stopped after {MAX_STEPS} steps without a complete poster.", None, HIDE, HIDE,
|
| 123 |
gallery, p['spec'], p['raw'], gr.update(visible=True))
|
| 124 |
progress(min(.1 + .12 * k, .9), desc=f'Generating poster HTML (step {k})…')
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
p = dict(p, continuation_ids=r['ids'], raw=r['raw'], done=r['done'], step=k)
|
| 131 |
-
tokens = f"{len(r['ids']):,} tokens · {r['new_tokens'] / max(r['seconds'], .01):.0f} tok/s"
|
| 132 |
if not (r['complete'] or p['done']):
|
| 133 |
return (p, f"Step {k} done · {tokens} · continuing (step {k + 1})…", None, HIDE, HIDE,
|
| 134 |
gallery, p['spec'], p['raw'], gr.update(visible=False))
|
|
|
|
| 14 |
ROOT = Path(__file__).resolve().parent
|
| 15 |
ARTIFACTS = Path(tempfile.mkdtemp(prefix='rap_demo_'))
|
| 16 |
WINDOW = 16384
|
| 17 |
+
MAX_STEPS = 10 # chained ZeroGPU steps per run
|
| 18 |
+
GPU_LADDER = (600, 300, 120, 60) # requested step length; ZeroGPU refuses what the visitor's quota cannot cover
|
| 19 |
+
QUOTA_HINT = (' — this 27B BF16 model needs the full 96 GB ZeroGPU (2x quota) and a poster takes roughly 15–25 min of quota. '
|
| 20 |
+
'Daily ZeroGPU quota: PRO 40 min, free account 5 min, anonymous 2 min, so sign in with a PRO account to finish a poster.')
|
| 21 |
|
| 22 |
CSS = '''
|
| 23 |
.gradio-container {max-width: 1360px !important; margin: 0 auto;}
|
|
|
|
| 67 |
p['input_tokens'] = n; p['continuation_ids'] = []; p['raw'] = ''; p['done'] = False
|
| 68 |
return p
|
| 69 |
|
| 70 |
+
def gpu_seconds(p):
|
| 71 |
+
return int(p.get('gpu_seconds', 120))
|
| 72 |
+
|
| 73 |
+
@spaces.GPU(size='xlarge', duration=gpu_seconds) # 27B BF16 needs the full 96 GB GPU (2x quota)
|
| 74 |
def gpu_chunk(p):
|
| 75 |
+
budget = max(30, gpu_seconds(p) - 90) # leave room for moving the weights to the GPU and the prefill
|
| 76 |
inputs = processor.apply_chat_template(chat(p), tokenize=True, add_generation_prompt=True,
|
| 77 |
enable_thinking=False, return_dict=True, return_tensors='pt')
|
| 78 |
prefix = p.get('continuation_ids', [])
|
|
|
|
| 86 |
inputs = inputs.to('cuda')
|
| 87 |
start = time.monotonic()
|
| 88 |
with torch.inference_mode():
|
| 89 |
+
output = model.generate(**inputs, max_new_tokens=min(8192, remaining), max_time=budget, do_sample=False,
|
| 90 |
use_cache=True, temperature=None, top_p=None, top_k=None, repetition_penalty=1.1)
|
| 91 |
new_ids = output[0, length:].detach().cpu().tolist()
|
| 92 |
all_ids = prefix + new_ids
|
|
|
|
| 129 |
return (dict(p, paused=True), f"Stopped after {MAX_STEPS} steps without a complete poster.", None, HIDE, HIDE,
|
| 130 |
gallery, p['spec'], p['raw'], gr.update(visible=True))
|
| 131 |
progress(min(.1 + .12 * k, .9), desc=f'Generating poster HTML (step {k})…')
|
| 132 |
+
r = None
|
| 133 |
+
for secs in [d for d in GPU_LADDER if d <= p.get('gpu_seconds', GPU_LADDER[0])]:
|
| 134 |
+
p = dict(p, gpu_seconds=secs)
|
| 135 |
+
try:
|
| 136 |
+
r = gpu_chunk(p)
|
| 137 |
+
break
|
| 138 |
+
except Exception as e: # noqa: BLE001 (quota, duration limit, timeout, …)
|
| 139 |
+
msg = str(e)
|
| 140 |
+
if 'larger than the maximum allowed' in msg and secs != GPU_LADDER[-1]:
|
| 141 |
+
continue # ask for a shorter GPU slot
|
| 142 |
+
hint = QUOTA_HINT if ('maximum allowed' in msg or 'quota' in msg.lower()) else ''
|
| 143 |
+
return (dict(p, paused=True), f"Paused after step {k - 1}: {msg[:200]}{hint}", None, HIDE, HIDE,
|
| 144 |
+
gallery, p['spec'], p['raw'], gr.update(visible=bool(p.get('continuation_ids'))))
|
| 145 |
p = dict(p, continuation_ids=r['ids'], raw=r['raw'], done=r['done'], step=k)
|
| 146 |
+
tokens = f"{len(r['ids']):,} tokens · {r['new_tokens'] / max(r['seconds'], .01):.0f} tok/s · {p['gpu_seconds']} s GPU step"
|
| 147 |
if not (r['complete'] or p['done']):
|
| 148 |
return (p, f"Step {k} done · {tokens} · continuing (step {k + 1})…", None, HIDE, HIDE,
|
| 149 |
gallery, p['spec'], p['raw'], gr.update(visible=False))
|