Spaces:
Runtime error
Runtime error
Commit History
-
Revert "feat(spaces): user-controlled GPU budget slider + pre-flight gate"
Verified signatureThis reverts commit ecc159588e928140424b9f985f7f40d5fbc03157.a2c44d8 -
feat(spaces): user-controlled GPU budget slider + pre-flight gate
Verified signatureThe fixed 240 s ceiling in `_duration_for` was the only thing standing between heavy modes (style, lipsync at high frame counts) and HF Pro+ / credit-topped accounts whose per-call cap actually allows longer durations. Replace it with a per-call budget the user picks, plus a pre-flight gate so jobs that won't fit are refused before submitting — no GPU time wasted after several minutes of compute. - `_duration_for` honours `user_budget` when set; otherwise returns the unclamped estimate (only the 60 s floor remains). - `_estimate_duration_unclamped` helper exposes the formula for the pre-flight diff message in app.py. - GPU-budget slider in the sidebar drawer: 60-1800 s, default 240 s, step 30 s, placed under the mode list for discoverability. - Pre-flight refuses with a friendly diff: "config estimates ~X s, slider is Y s" - user raises the slider or reduces preset/frames. - `illegal_duration` + `gpu_timeout` friendly errors now point at the slider as the first thing to try.…ecc1595
-
fix(spaces): bump duration cap, drop broken auto-retry, add friendly errors + like banner
Unverified signatureLogs from run #202 (style transfer) confirmed the cascade: 1. style submitted, ran ~127s of GPU time 2. @spaces.GPU(duration=120) cap hit -> 'GPU task aborted' (line 217) 3. our auto-retry fired with the SAME captured request 4. retry's schedule call -> 'Expired ZeroGPU proxy token' (401, line 226) because the captured request's X-IP-Token had aged past TTL during run 1 The visible 'expired token' was the symptom of the abort. Two real bugs: - 120s cap was too tight for style+lipsync detailer paths (~120-180s actual on H200) - auto-retry captured stale request tokens; second attempt always 401'd Fixes: - _duration_for: clamp [60, 240]. Pro identity accepts these; if a server rejects, the user sees a clear illegal_duration error. - _on_generate: drop the for attempt in (0,1) retry. Single attempt; timeout/expired surfaces as a friendly message; user clicks Generate again -> fresh request -> fresh token -> succeeds. - _classify expanded: expired_t…3c69062 -
fix(spaces): clamp _duration_for to 120s (ZeroGPU per-call hard max)
Unverified signatureAfter fixing the contextvar propagation in 1e054fe, requests now reach the scheduler with the user's X-IP-Token. The next failure mode surfaced: ZeroGPU illegal duration (raised at spaces/zero/client.py:137 when res.wait < timedelta(0) from the scheduler) The server enforces a per-call duration cap of 120s on the standard ZeroGPU tier, regardless of the user's remaining daily quota. Our _duration_for was clamping to 900s which the scheduler rejected for any preset+mode combination above ~2 minutes of estimated work. Drop the ceiling to 120s. On H200 every preset we ship completes well under that; longer requested durations just fail the guard check before any GPU is attached.444a596 -
fix(spaces): propagate Gradio request contextvar to GPU worker thread
Unverified signatureReal root cause of 'Space app has reached its GPU limit. Try re-running outside of examples' (raised after a few gens, while the user dashboard still shows 0/25 minutes used). ZeroGPU's @spaces.GPU wrapper reads the user's identity from the current Gradio request via gradio.context.LocalContext.request — a contextvar. The wrapper at spaces/zero/wrappers.py:187 does: request = request_var.get(None) schedule_response = client.schedule(task_id, request=request, ...) Then spaces/zero/client.py:_get_token_and_payload reads X-IP-Token from the request headers (set by HF's reverse proxy when the user is signed into huggingface.co). If the token is None, client.schedule:138 raises the 'Space app has reached its GPU limit' message and falls back to the per-Space anonymous rate limit — which exhausts after a few calls. Our backend ran _execute_workflow from a plain threading.Thread for the event-streaming pattern. Python threads don't inherit contextvars, so inside the worker the req…1e054fe -
fix(spaces): _duration_for must accept progress kwarg to match _execute_workflow
Unverified signatureZeroGPU calls the duration estimator with the same args it'll pass to the @spaces.GPU function. After adding progress as the 7th arg to _execute_workflow in 27ffbbc, _duration_for raised TypeError on every generation. Add progress to its signature; we ignore it for estimation.0a98b65 -
feat(spaces+style): hf_oauth + gr.Progress + style 2-input + DWPose deps
Unverified signatureSpaces — Pro quota attribution: enable hf_oauth in README YAML and add a gr.LoginButton in the drawer (rendered only when OAUTH_CLIENT_ID is set, so local dev stays clean). Without OAuth the Space scheduler bucketed Pro users as signed-in/free (300s/day), causing "GPU quota exceeded" errors after a few generations while the profile dashboard still showed 0/25 used. Spaces — progress bar through @spaces.GPU subprocess: thread a gr.Progress instance from _make_handler -> _on_generate -> backend.submit -> _execute_workflow. The existing event-based ProgressEvent flow can't cross the @spaces.GPU subprocess boundary on Spaces (asyncio.run_coroutine_threadsafe targets the parent loop the subprocess never inherits). gr.Progress is the only progress channel ZeroGPU's IPC wraps. Inside _execute_workflow, install a ComfyUI PROGRESS_BAR_HOOK that calls progress(v/t, desc=...) and chains to the prior hook so the local event-driven status banner keeps working. Style mode — actually use the upload…27ffbbc
-
feat(spaces): dynamic ZeroGPU duration + auto-retry on timeout
Unverified signatureTier 1 — dynamic duration estimator: @spaces.GPU(duration=callable) where the callable receives the same args as _execute_workflow and returns int seconds. Estimate from (mode, preset, frames) with per-mode base (t2v 90s, lipsync 240s, style 360s, etc.), preset multiplier (fast 1×, balanced 1.5×, quality 3×), +60s cold-cache buffer, +0.3s/frame VAE decode. Clamped to [60s, 900s]. Effect: light T2V calls reserve ~190s and jump the ZeroGPU queue; heavy lipsync/style calls reserve real headroom only when needed. No more one-size-fits-all 300s/600s wall. Tier 2 — auto-retry on timeout: ErrorEvent.category 'gpu_timeout' is now distinguished from generic execution errors via _classify (matches "GPU task aborted" in the exception message). The _on_generate handler catches this on the first attempt only, shows a friendly "Retrying with extended budget" banner, then re-submits with duration_multiplier=2.0 (which clamps to the same 900s ceiling). One retry — if that…14dcc06
-
fix(spaces): bump @spaces.GPU duration 300s → 600s
Unverified signatureLipsync hit GPU task aborted exactly at the 300s wall. The workflow runs: init/load (~3 min on cold cache) + sampler 1 (~50s) + sampler 2 (~30s) + VAE decode + ffmpeg audio-mux. Total ran 305s — killed mid-mux just as the video was about to land on disk. The duration param is a timeout, not a billing cap (HF bills actual usage). Bumping to 600s (10 min) gives heavy modes — lipsync, future keyframe and style transfer with bigger workflows — headroom on cold caches without affecting lighter T2V calls (those still finish in ~30s and release the GPU back to the pool immediately).d3e4302
-
fix(backend): compute video path inside @spaces.GPU + filesystem fallback
Unverified signatureexecutor.history_result wasn't surviving ZeroGPU's GPU-context wrap reliably — sampler completed and files were written to disk, but the parent process saw an empty history_result, so the OutputEvent's video_path was "" and the gr.Video output stayed blank. Two changes: 1. _execute_workflow now reads history_result and returns the video path inline (a plain str pickles cleanly across the boundary). 2. If that still returns "", _newest_recent_video scans the output dir for the newest mp4/webm/mov modified in the last 60 s — files are on shared disk so they're visible from the parent regardless of how ZeroGPU isolates state. Also adds a "[backend] workflow done; video_path=..." log line so the next time this hiccups we see where the chain breaks.e8627ee -
fix(spaces): module-level @spaces.GPU so ZeroGPU's startup detector finds it
Unverified signatureZeroGPU rejected the previous build with "No @spaces.GPU function detected during startup". The decorator was being applied dynamically inside submit() per-call, which the startup analyzer can't see. Refactored to a module-level _execute_workflow(executor, workflow, output_ids) decorated at import time with spaces.GPU(duration=300) (Pro per-call cap). _worker now calls this synchronous helper from its thread; the per-call gpu_duration plumbing is no longer used because ZeroGPU dispatch is bound at decoration time, not call time.be7dc01 -
fix(spaces): use \$HOME instead of /data (no persistent-storage add-on needed)
Unverified signatureHF Pro grants ZeroGPU minutes but persistent storage at /data is a separate paid add-on. Without it /data is unwritable, which crashed the Space at startup. Switching the Spaces clone target and models dir to ~/comfyui — \$HOME is writable, and ZeroGPU containers freeze on sleep rather than tear down, so the clone effectively persists across calls within a single deploy.bc868d5 -
feat: API-format workflows + sidebar nav + responsive UI
Unverified signatureSwitch to ComfyUI API-format JSON for all 6 modes (saved from editor's "Save API Format"), avoiding the editor->API converter. workflow.py is now just load_template + set_input over a flat {id: {class_type, inputs}} dict. modes.py shrinks to ~10 patches per mode (prompt, dimensions, seed, media inputs). backend.py wires PromptExecutor with cache_args, server stub, async nodes.init_extra_nodes via worker thread, sys.path ordering so KJNodes/VHS find utils.install_util, and a per-sampler stage counter. app.py: sidebar nav (gr.Tabs hidden offscreen, sidebar buttons drive gr.Tabs.selected); device/memory badge; preset budget per mode; seconds-driven length slider with derived frames display; W/H sliders to 4096; randomize-seed toggle; responsive CSS for tablet (≤1024px) and mobile (≤700px).bce3dbc
-
fix(backend): pass cache_args to PromptExecutor + dump worker tracebacks
Unverified signaturePromptExecutor.execute_async unconditionally reads self.cache_args["ram"] even when cache_type is the default false (line 727). NoneType-subscript otherwise. Default to {ram: 16.0, lru: 0}. Also stderr-print worker tracebacks so the full failure stack reaches the log.107f495 -
fix(backend,models): runtime corrections from end-to-end testing
Unverified signature- backend: PromptExecutor needs (server, ...) not server_instance=None; init_extra_nodes is async — can't use asyncio.run() inside Gradio's event loop, run on a worker thread with a fresh loop instead; add minimal _StubServer with the surface PromptExecutor requires. - models: short-circuit ensure_models when the file already exists at comfyui/models/<type>/<file> — local dev path with user's existing ComfyUI models symlinked in shouldn't trigger HF Hub downloads.19489a8 -
ci: run unit tests + ruff lint on every push
Unverified signature3ea399a -
feat(backend): submit() async generator with progress hooks + ZeroGPU
Unverified signatureb23fbf5 -
feat(backend): ComfyUILibraryBackend skeleton + event dataclasses
Unverified signature15811ca