forge-again v1.7.8
A connection-stability release, driven by a full client + server audit and by forensic data from real sessions. Full test suite green (50 passed, including the GPU tier).
Baseline first, because it reframes the work: across 15,782 logged client events, 190 real generation streams completed with zero drops, and per-stream heartbeat gaps sat at a healthy 15.1s median. The generation stream itself is reliable and the GPU is not blocking the event loop -- the faults were in the polling, recovery and background-load layers around it.
Fixes
- Missed generation results are recovered, and the recovery actually works. A completion lost to a throttled background tab or a dead stream now gets pulled back: the watchdog detects the tell-tale state (server idle, task still undelivered) and re-requests the recorded result over a fresh connection. Three bugs stood between that idea and it working: the restore-progress button was created
visible=False, which gradio 6 unmounts, so the entire restore feature had been dead since the port; the server only remembered the last 2 results by task id, so a couple of later runs evicted the one you were trying to recover (now 16); and the recovery cleared the task id synchronously after clicking, winning a race against gradio's own async handler that reads it, which made every attempt silently no-op. Recovery now reports honestly either way -- a checkmark when it delivers, or a clear "the image is in your output folder" when the result is genuinely gone. - Progress polling survives blips. Both poll chains used "tear everything down" as their error handler, and that fires on any failed request -- so a single transient blip mid-generation removed the progress bar, restored the Generate button, and cleared the task id, leaving a still-running job looking finished and unrecoverable. Up to 5 consecutive failures are now tolerated, and a failed live-preview poll (purely cosmetic) can no longer tear down the run's tracking at all.
- Background tabs keep working. Progress, live previews and completion detection were all paced by main-thread timers, which browsers throttle to roughly once a minute in a hidden tab. They now run on a Web Worker clock (new
javascript/forgeTimer.js, reusable for any correctness-bearing loop). queue(64)mis-port. That call's first positional argument isstatus_update_rate, notmax_size-- so the inherited setting was broadcasting queue estimations every 64 seconds while leaving the size cap unset. Spelled out explicitly; UI events also get a dedicated thread-capacity limiter instead of sharing the process-wide pool with every sync API route.- Stuck Generate button. The 12-second self-recovery for a submit that never reached the backend could never restore the button, because a required argument was never passed at its only call site.
- Less background load. The assistant's watchdog polled roughly once a second forever -- even with the panel never opened -- and its readiness probe could park a server thread when llama-server was down. The cadence is now adaptive (3s in use, 20s idle; measured 12 requests per 12s down to 2) and the probe short-circuits and caches. Separately,
/internal/client-logno longer does blocking disk I/O inside the request,restore_progressno longer busy-waits unbounded while holding a worker, and an extra-networks handler no longer re-attached a listener on every UI update. - Two hot handlers off the generation path. The gallery info round-trip fired on every gallery click -- including the automatic one 250ms after each result lands -- and opened a full event stream each time; it reads no shared state, so it is now unqueued (measured: 0 streams per gallery click, was 1). Token counters read the loaded model's tokenizer and therefore ran under the same lock a generation holds, so a keystroke mid-run parked a server thread for the entire run; they now skip while a job is running and refresh on your next edit.
Upgrading
Pull and restart -- and hard-refresh the browser tab (Ctrl+F5), since several of these fixes live in the page's JavaScript.