Spaces:
Running on Zero
Running on Zero
Muhammad Uzair Khattak commited on
Commit ·
1f7ccca
1
Parent(s): 9b218f7
Make Generate always run live, never serve the precomputed cache
Browse filesPreviously an exact spec match on the Generate button would replay the
cached result instead of running the model, including for the default
configuration each tab opens on. That's fine for passive browsing (still
handled by _sync_results/_fp_sync_results and the page-load prefill,
unchanged) but meant the one deliberate action a visitor takes -- pressing
Generate -- could be a replay rather than a real run, undermining
replicability. run_demo_stream and run_future_demo no longer check the
cache at all; only browsing stays free, the click itself always costs GPU
time now. Drops the now-dead _cached_arm_updates helper and
CACHE_HIT_DELAY constant, and updates both precompute scripts' docstrings
to stop claiming Generate is ever free.
- app.py +15 -52
- scripts/upload_precomputed_to_hub.py +5 -2
app.py
CHANGED
|
@@ -171,9 +171,6 @@ TEXT_OUTPUT_LINES = 6
|
|
| 171 |
# default configuration of each tab ships pre-generated -- but a visitor must
|
| 172 |
# always be able to tell which is which.
|
| 173 |
CACHED_BADGE = "cached"
|
| 174 |
-
# Cache hits are effectively instant; a beat of delay is what makes the click
|
| 175 |
-
# read as "it did something" rather than "the button is broken".
|
| 176 |
-
CACHE_HIT_DELAY = 0.35
|
| 177 |
|
| 178 |
|
| 179 |
def _save_to_mp4(video_array):
|
|
@@ -542,23 +539,6 @@ def _cleared_frame_updates():
|
|
| 542 |
return updates
|
| 543 |
|
| 544 |
|
| 545 |
-
def _cached_arm_updates(seed_value):
|
| 546 |
-
"""Arming frame for a cache hit. Deliberately NOT _loading_state_html --
|
| 547 |
-
its "Inference has started — the first run can take an extra 1-2 minutes
|
| 548 |
-
while tokenizers load" copy would
|
| 549 |
-
be a lie here. The pause that follows is a button-press acknowledgement,
|
| 550 |
-
not a fake generation: an instantaneous flip reads as a dead button."""
|
| 551 |
-
updates = _reset_frame_updates(seed_value)
|
| 552 |
-
updates[IDX_STATUS] = gr.update(value=_status_html("loading precomputed result"))
|
| 553 |
-
updates[IDX_EMPTY] = gr.update(visible=True, value=(
|
| 554 |
-
'<div class="empty-state loading-state">'
|
| 555 |
-
f'<p class="mono" style="margin-bottom:6px;color:var(--text-2)">precomputed · seed {seed_value}</p>'
|
| 556 |
-
"<p>Loading a saved result — no GPU time used.</p></div>"
|
| 557 |
-
))
|
| 558 |
-
updates[IDX_BTN] = gr.update(value="Loading…", interactive=False)
|
| 559 |
-
return updates
|
| 560 |
-
|
| 561 |
-
|
| 562 |
def _frame(updates):
|
| 563 |
out = [gr.skip()] * N_STREAM_OUTPUTS
|
| 564 |
for idx, upd in updates.items():
|
|
@@ -1803,11 +1783,17 @@ with gr.Blocks(title="A2A-Video: Any-to-All Video Generation", theme=_THEME, css
|
|
| 1803 |
|
| 1804 |
def run_demo_stream(src_mode, example_stem, upload_path, caption_text, abs_mod,
|
| 1805 |
chain, seed_value, top_p, top_k, *hyperparam_values):
|
| 1806 |
-
"""Undecorated on purpose. Validation
|
| 1807 |
-
|
| 1808 |
-
|
| 1809 |
-
|
| 1810 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1811 |
try:
|
| 1812 |
req = resolve_a2a(src_mode, example_stem, upload_path, caption_text,
|
| 1813 |
abs_mod, chain, seed_value, top_p, top_k,
|
|
@@ -1815,14 +1801,7 @@ with gr.Blocks(title="A2A-Video: Any-to-All Video Generation", theme=_THEME, css
|
|
| 1815 |
except ValueError as e:
|
| 1816 |
raise gr.Error(str(e))
|
| 1817 |
|
| 1818 |
-
|
| 1819 |
-
if entry is not None:
|
| 1820 |
-
yield _frame(_cached_arm_updates(req.seed))
|
| 1821 |
-
time.sleep(CACHE_HIT_DELAY)
|
| 1822 |
-
yield _cached_a2a_frames(req, entry)
|
| 1823 |
-
return
|
| 1824 |
-
|
| 1825 |
-
# Miss. Arm the UI before asking for a GPU, so the spinner is
|
| 1826 |
# already up during the ZeroGPU queue wait.
|
| 1827 |
yield _frame(_reset_frame_updates(req.seed))
|
| 1828 |
yield from _run_demo_stream_gpu(req.input_modality, req.chain, req.gen_kwargs,
|
|
@@ -2300,8 +2279,9 @@ with gr.Blocks(title="A2A-Video: Any-to-All Video Generation", theme=_THEME, css
|
|
| 2300 |
|
| 2301 |
def run_future_demo(example_stem, obs_mod, seed_tokens, cond_mode, cond_mod, steer_text, targets,
|
| 2302 |
seed_value, top_p, top_k, complete_seed, *hyperparam_values):
|
| 2303 |
-
"""Undecorated: validation
|
| 2304 |
-
|
|
|
|
| 2305 |
try:
|
| 2306 |
req = resolve_fp(example_stem, obs_mod, seed_tokens, cond_mode, cond_mod,
|
| 2307 |
steer_text, targets, seed_value, top_p, top_k,
|
|
@@ -2310,23 +2290,6 @@ with gr.Blocks(title="A2A-Video: Any-to-All Video Generation", theme=_THEME, css
|
|
| 2310 |
except ValueError as e:
|
| 2311 |
raise gr.Error(str(e))
|
| 2312 |
|
| 2313 |
-
entry = _CACHE.lookup(req.spec)
|
| 2314 |
-
if entry is not None:
|
| 2315 |
-
yield _fp_frame(_fp_arm_updates(
|
| 2316 |
-
req.seed, "loading precomputed result",
|
| 2317 |
-
'<div class="empty-state loading-state">'
|
| 2318 |
-
f'<p class="mono" style="margin-bottom:6px;color:var(--text-2)">precomputed · seed {req.seed}</p>'
|
| 2319 |
-
"<p>Loading a saved result — no GPU time used.</p></div>",
|
| 2320 |
-
"Loading…",
|
| 2321 |
-
))
|
| 2322 |
-
time.sleep(CACHE_HIT_DELAY)
|
| 2323 |
-
results = _CACHE.resolve_results(entry)
|
| 2324 |
-
cards = _fp_cards(req.obs_mod, req.extra, req.chain, req.seed_tokens,
|
| 2325 |
-
results, req.steer_text, complete_seed=req.complete_seed)
|
| 2326 |
-
yield _fp_frame(_fp_final_updates(
|
| 2327 |
-
cards, f"precomputed · seed {req.seed}", "Predict again", CACHED_BADGE))
|
| 2328 |
-
return
|
| 2329 |
-
|
| 2330 |
yield _fp_frame(_fp_arm_updates(
|
| 2331 |
req.seed, f"warming up · seed {req.seed}",
|
| 2332 |
_loading_state_html(req.seed), "Predicting…"))
|
|
|
|
| 171 |
# default configuration of each tab ships pre-generated -- but a visitor must
|
| 172 |
# always be able to tell which is which.
|
| 173 |
CACHED_BADGE = "cached"
|
|
|
|
|
|
|
|
|
|
| 174 |
|
| 175 |
|
| 176 |
def _save_to_mp4(video_array):
|
|
|
|
| 539 |
return updates
|
| 540 |
|
| 541 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 542 |
def _frame(updates):
|
| 543 |
out = [gr.skip()] * N_STREAM_OUTPUTS
|
| 544 |
for idx, upd in updates.items():
|
|
|
|
| 1783 |
|
| 1784 |
def run_demo_stream(src_mode, example_stem, upload_path, caption_text, abs_mod,
|
| 1785 |
chain, seed_value, top_p, top_k, *hyperparam_values):
|
| 1786 |
+
"""Undecorated on purpose. Validation runs here, on CPU. That
|
| 1787 |
+
also means a validation error surfaces with its real message
|
| 1788 |
+
-- spaces' worker wrapper replaces exception messages with a
|
| 1789 |
+
bare "ZeroGPU worker error".
|
| 1790 |
+
|
| 1791 |
+
Deliberately does NOT serve the precomputed cache, even on an
|
| 1792 |
+
exact spec match: pressing Generate is the one action that
|
| 1793 |
+
should always be a real, replicable run of the model, not a
|
| 1794 |
+
replay -- the cache still fronts every other configuration
|
| 1795 |
+
(see _sync_results/the page-load prefill), so browsing stays
|
| 1796 |
+
free; only the click itself always costs GPU time."""
|
| 1797 |
try:
|
| 1798 |
req = resolve_a2a(src_mode, example_stem, upload_path, caption_text,
|
| 1799 |
abs_mod, chain, seed_value, top_p, top_k,
|
|
|
|
| 1801 |
except ValueError as e:
|
| 1802 |
raise gr.Error(str(e))
|
| 1803 |
|
| 1804 |
+
# Arm the UI before asking for a GPU, so the spinner is
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1805 |
# already up during the ZeroGPU queue wait.
|
| 1806 |
yield _frame(_reset_frame_updates(req.seed))
|
| 1807 |
yield from _run_demo_stream_gpu(req.input_modality, req.chain, req.gen_kwargs,
|
|
|
|
| 2279 |
|
| 2280 |
def run_future_demo(example_stem, obs_mod, seed_tokens, cond_mode, cond_mod, steer_text, targets,
|
| 2281 |
seed_value, top_p, top_k, complete_seed, *hyperparam_values):
|
| 2282 |
+
"""Undecorated: validation on CPU, GPU always follows (see
|
| 2283 |
+
run_demo_stream -- pressing Generate never serves the cache,
|
| 2284 |
+
on purpose)."""
|
| 2285 |
try:
|
| 2286 |
req = resolve_fp(example_stem, obs_mod, seed_tokens, cond_mode, cond_mod,
|
| 2287 |
steer_text, targets, seed_value, top_p, top_k,
|
|
|
|
| 2290 |
except ValueError as e:
|
| 2291 |
raise gr.Error(str(e))
|
| 2292 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2293 |
yield _fp_frame(_fp_arm_updates(
|
| 2294 |
req.seed, f"warming up · seed {req.seed}",
|
| 2295 |
_loading_state_html(req.seed), "Predicting…"))
|
scripts/upload_precomputed_to_hub.py
CHANGED
|
@@ -6,8 +6,11 @@ scripts/precompute_demo_outputs.py, which produces the folder this uploads).
|
|
| 6 |
Why precompute at all: ZeroGPU gives free-tier Space visitors a few minutes of
|
| 7 |
GPU per day, and load_pipeline's ~37.5 GB of checkpoints is loaded from inside
|
| 8 |
that allocation. Shipping the default configuration of each tab pre-generated
|
| 9 |
-
means the demo shows real predictions the moment it opens, and
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
The folder is uploaded VERBATIM under the `precomputed/` prefix of the same
|
| 13 |
dataset repo that already holds the example tokens and previews:
|
|
|
|
| 6 |
Why precompute at all: ZeroGPU gives free-tier Space visitors a few minutes of
|
| 7 |
GPU per day, and load_pipeline's ~37.5 GB of checkpoints is loaded from inside
|
| 8 |
that allocation. Shipping the default configuration of each tab pre-generated
|
| 9 |
+
means the demo shows real predictions the moment it opens, and browsing any
|
| 10 |
+
other precomputed clip/chain/modality shows its result instantly too, at no
|
| 11 |
+
GPU cost. Pressing Generate always runs live regardless -- the app never
|
| 12 |
+
serves this cache from the Generate button itself, only from passively
|
| 13 |
+
switching between configurations.
|
| 14 |
|
| 15 |
The folder is uploaded VERBATIM under the `precomputed/` prefix of the same
|
| 16 |
dataset repo that already holds the example tokens and previews:
|