Resume sessions - #13585
Conversation
# Conflicts: # js/gallery/Gallery.test.ts # js/gallery/shared/Gallery.svelte
🪼 branch checks and previews
Install Gradio from this PR pip install https://huggingface.co/buckets/gradio/pypi-previews/resolve/ae5f580baaf9c81ca78f4bd468e5af60f874e0ae/gradio-6.24.0-py3-none-any.whlInstall Gradio Python Client from this PR pip install "gradio-client @ git+https://github.com/gradio-app/gradio@ae5f580baaf9c81ca78f4bd468e5af60f874e0ae#subdirectory=client/python"Import Gradio JS Client from this PR via CDN import { Client } from "https://huggingface.co/buckets/gradio/npm-previews/resolve/ae5f580baaf9c81ca78f4bd468e5af60f874e0ae/browser.js"; |
🦄 change detectedThis Pull Request includes changes to the following packages.
|
There was a problem hiding this comment.
Pull request overview
Adds opt-in “resume sessions” support so queued jobs can survive brief disconnects/page refreshes by reconnecting to the same session_hash + event_id, replaying buffered queue output, and then acknowledging completion to clear server/client recovery state.
Changes:
- Backend queue: track per-session message history, mark sessions detached/attached, expire detached sessions via TTL, and add a new
/queue/ackendpoint for explicit completion acknowledgement. - Frontend client: persist active queued
event_ids in browser storage, reconnect SSE streams withresume_event_id, and acknowledge completed events to clear recovery state. - Tests: add unit coverage for client stream reconnect/session storage and a backend test validating detached session resumption + acknowledgement cleanup.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| test/test_queueing.py | Adds backend test covering detach → resume replay → ack cleanup flow. |
| js/spa/test/cancel_events.spec.ts | Updates expectation: iterative job continues after page close. |
| js/spa/src/Index.svelte | Enables resume_sessions for the SPA client connections. |
| js/core/src/dependency.ts | Acknowledges submissions and adds dependency-level resume flow. |
| js/core/src/Blocks.svelte | Resumes events on ready when resumable events exist, otherwise dispatches load events. |
| js/app/src/routes/[...catchall]/+page.ts | Enables resume_sessions in SvelteKit load client connect. |
| js/app/src/routes/[...catchall]/+page.svelte | Enables resume_sessions on reconnect in the app page. |
| gradio/routes.py | Adds resume params to /queue/data, session attach/detach handling, and /queue/ack. |
| gradio/queueing.py | Adds message history, detach TTL tracking, resume/ack helpers, and expiry cleanup. |
| gradio/data_classes.py | Introduces QueueAckBody request model. |
| client/js/src/utils/submit.ts | Adds resumable-event tracking, resume submission path, and acknowledgement hook. |
| client/js/src/utils/stream.ts | Adds SSE reconnect/backoff, resume query params, and “normal close” reopening. |
| client/js/src/utils/session.ts | New session storage + cookie utilities for resumable queued events. |
| client/js/src/types.ts | Adds resume_sessions option and SubmitIterable.acknowledge(). |
| client/js/src/test/stream.test.ts | Adds coverage for normal close reopen + reconnect/resume behavior. |
| client/js/src/test/session.test.ts | Adds coverage for session storage semantics and SSR cookie read. |
| client/js/src/test/init.test.ts | Adds test ensuring sessions are not restored across different apps. |
| client/js/src/constants.ts | Adds ACK_URL constant. |
| client/js/src/client.ts | Adds restored session hash support, resume helper, and reconnect timer cleanup. |
| .changeset/quiet-sessions-return.md | Announces minor feature release for both gradio and @gradio/client. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Resolve conflicts by keeping soft-reload from main and acknowledging resumable submissions before clear_submission. Co-authored-by: Cursor <cursoragent@cursor.com>
|
I haven't had a chance to test this yet, but a few things that come to mind:
|
# Conflicts: # js/app/src/routes/[...catchall]/+page.svelte # js/core/src/dependency.ts # js/spa/src/Index.svelte
# Conflicts: # client/js/src/client.ts # client/js/src/test/init.test.ts # client/js/src/utils/submit.ts
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 32 out of 32 changed files in this pull request and generated no new comments.
Suppressed comments (5)
client/js/src/utils/stream.ts:109
open_stream()retries the SSE connection on any stream error, regardless ofresume_sessions. This breaks the “opt-in” guarantee for existing JS clients and also means callers won’t receive thebroken_connectionmessage they currently handle insubmit.ts.
Consider preserving the old behavior when resume_sessions is false: dispatch a broken_connection message to active callbacks and return without scheduling reconnect; only do the resume/reconnect path when resume_sessions is true.
stream.onerror = async function (e) {
if (that.stream_instance !== stream) {
return;
}
console.error(e);
client/js/src/utils/stream.ts:1
- The SSE stream currently always schedules automatic reconnects on
onerror, even whenoptions.resume_sessionsis not enabled. That changes existing JS client behavior (previously it surfaced abroken_connectionto callbacks and did not attempt to resume jobs) and effectively makes session resume non-opt-in.
This issue also appears on line 105 of the same file.
import { SSE_URL } from "../constants";
js/spa/test/cancel_events.spec.ts:2
- This Playwright test that asserted “closing the page stops the Python function” was removed, but the PR description introduces new close semantics (
/queue/closewith a ~5s grace) that should still be covered end-to-end. Without a replacement, regressions could leave abandoned jobs running and holding queue/GPU resources.
Recommend reintroducing an updated test that closes the page, waits slightly longer than the close grace period, then asserts the server-side job stopped/cleaned up (adjusting assertions to the new timing/behavior).
import { test, expect } from "@self/tootils";
client/js/src/utils/stream.ts:55
- If
this.stream(url)returns null/undefined,stream_status.openhas already been set totrueand never reset. That leaves the client thinking an SSE stream is open and prevents future reconnect attempts.
if (!stream) {
console.warn("Cannot connect to SSE endpoint: " + url.toString());
return;
}
.changeset/quiet-sessions-return.md:5
- This repo’s contribution guidelines explicitly ask contributors not to add
.changeset/*.mdfiles manually because a GitHub Action generates them from the PR title (and a hand-written changeset will silently replace the title in the changelog). Please remove this changeset file and rely on the Action instead.
---
"@gradio/client": minor
"gradio": minor
"gradio_client": minor
---
hysts
left a comment
There was a problem hiding this comment.
Tested and works great. LGTM! Thanks for adding this awesome feature @dawoodkhan82 !
# Conflicts: # client/js/src/index.ts # client/js/src/utils/submit.ts # js/core/src/Blocks.svelte
# Conflicts: # js/core/src/Blocks.svelte
| const MESSAGE_QUOTE_RE = /^'([^]+)'$/; | ||
|
|
||
| const DUPLICATE_MESSAGE = $reactive_formatter("blocks.long_requests_queue"); | ||
| const MOBILE_QUEUE_WARNING = $reactive_formatter( |
There was a problem hiding this comment.
removed mobile queue warning
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 31 out of 31 changed files in this pull request and generated no new comments.
Suppressed comments (2)
.changeset/whole-radios-lick.md:5
- This
.changeset/*.mdfile appears to be hand-authored, but the repo guidance says not to add changeset files manually because a GitHub Action generates them from the PR title and manual ones override the changelog entry. Please remove this changeset file from the PR.
---
"@gradio/client": minor
"@gradio/core": minor
"@self/app": minor
"@self/spa": minor
client/python/gradio_client/client.py:285
- On reconnect,
stream_messages()currently sendsresume_event_id=[](becauseresume_event_idsis set toNoneafter the first successful connection). The server only replays buffered messages whenresume_event_idis provided, so reconnecting without it can miss messages that were dequeued but not received before disconnect. Consider always sending the current pending event IDs whenresume_sessionsis enabled.
params={
"session_hash": session_hash,
"resume_event_id": resume_event_ids or [],
"acknowledgements": self.resume_sessions,
},
Description
Quick summary: an active queued job can now survive a refresh or short connection drop without being submitted again.
How it works:
Live test demo
Try the refresh/reconnect flow on Test Space. Start the long-running task, refresh while progress is moving, and confirm the same Run ID continues through completion.
Closes: #13584
Closes: #8368
Screen.Recording.2026-07-16.at.3.36.05.PM.mov
AI Disclosure
Testing and Formatting Your Code