Skip to content

Resume sessions - #13585

Open
dawoodkhan82 wants to merge 37 commits into
mainfrom
resume-sessions
Open

Resume sessions#13585
dawoodkhan82 wants to merge 37 commits into
mainfrom
resume-sessions

Conversation

@dawoodkhan82

@dawoodkhan82 dawoodkhan82 commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Description

Quick summary: an active queued job can now survive a refresh or short connection drop without being submitted again.

How it works:

  • The client saves the session hash, event ID, and function index for an active job. (code)
  • After a refresh or connection loss, it reconnects using those identifiers. (code)
  • The server replays buffered messages and continues streaming the original job. (code)
  • The completed event is acknowledged through /reset, clearing recovery state. (client, server)
  • Closing the page starts a short cleanup grace period; unexpected disconnects use the longer configurable TTL. (queue lifecycle, close route)
  • Browser UI enables resuming automatically; Python and JS clients can opt in explicitly. (browser UI, Python client, JS client)

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

  • I used AI to help review the issue, implementation, tests, and PR text. I self-reviewed the changes.
  • I did not use AI

Testing and Formatting Your Code

  • Client browser and Node unit tests
  • Python client tests
  • Queue and routes backend tests
  • Existing functional tests in SSR and non-SSR mode
  • Offline refresh and reconnect demo flow
  • Frontend formatting, type checks, and builds

@gradio-pr-bot

gradio-pr-bot commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

🪼 branch checks and previews

Name Status URL
Spaces ready! Spaces preview
Website ready! Website preview
Storybook ready! Storybook preview
🦄 Changes detected! Details

Install Gradio from this PR

pip install https://huggingface.co/buckets/gradio/pypi-previews/resolve/ae5f580baaf9c81ca78f4bd468e5af60f874e0ae/gradio-6.24.0-py3-none-any.whl

Install 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";

@gradio-pr-bot

gradio-pr-bot commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

🦄 change detected

This Pull Request includes changes to the following packages.

Package Version
@gradio/client minor
@gradio/core minor
@self/app minor
@self/spa minor
gradio minor
gradio_client minor

  • Resume sessions

Something isn't right?

  • Maintainers can change the version label to modify the version bump.
  • If the bot has failed to detect any changes, or if this pull request needs to update multiple packages to different versions or requires a more comprehensive changelog entry, maintainers can update the changelog file directly.

@dawoodkhan82
dawoodkhan82 marked this pull request as ready for review July 17, 2026 02:17
@dawoodkhan82
dawoodkhan82 requested a review from Copilot July 17, 2026 16:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/ack endpoint for explicit completion acknowledgement.
  • Frontend client: persist active queued event_ids in browser storage, reconnect SSE streams with resume_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.

Comment thread gradio/routes.py Outdated
Comment thread gradio/routes.py
Comment thread client/js/src/utils/submit.ts
Comment thread client/js/src/utils/submit.ts
Comment thread gradio/queueing.py
Comment thread gradio/queueing.py
Comment thread client/js/src/utils/stream.ts
Resolve conflicts by keeping soft-reload from main and acknowledging
resumable submissions before clear_submission.

Co-authored-by: Cursor <cursoragent@cursor.com>
@abidlabs

Copy link
Copy Markdown
Member

I haven't had a chance to test this yet, but a few things that come to mind:

  • We can remove the warning that we show on mobile that the connection will be lost on mobile?
  • Will this work with gr.Server apps? I assume so since the js client supports this but would be good to confirm with an example demo and add it to the docs
  • Have we tested this with streaming audio/video where the backend returns chunks that are concatenated in the frontend?
  • How is Long-running tasks require open connection, looking for polling solution #8368 solved? I understand that the mechanism to resume sessions is in place, but how can someone using the python client actually retrieve a job? Some more docs around all of this would be nice
  • I'm slightly concerned about the fact that we now have different behaviors if a submission is still in the queue
    (refreshing keeps the output) vs. if a submission has finished and output has been returned (refreshing erases the output). perhaps this is something to reconcile in Gradio 7

# Conflicts:
#	js/app/src/routes/[...catchall]/+page.svelte
#	js/core/src/dependency.ts
#	js/spa/src/Index.svelte

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 of resume_sessions. This breaks the “opt-in” guarantee for existing JS clients and also means callers won’t receive the broken_connection message they currently handle in submit.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 when options.resume_sessions is not enabled. That changes existing JS client behavior (previously it surfaced a broken_connection to 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/close with 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.open has already been set to true and 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/*.md files 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
---

@dawoodkhan82
dawoodkhan82 requested review from abidlabs and hysts August 3, 2026 16:31

@hysts hysts left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested and works great. LGTM! Thanks for adding this awesome feature @dawoodkhan82 !

Comment thread gradio/routes.py Outdated
Comment thread gradio/queueing.py
Comment thread js/core/src/Blocks.svelte
const MESSAGE_QUOTE_RE = /^'([^]+)'$/;

const DUPLICATE_MESSAGE = $reactive_formatter("blocks.long_requests_queue");
const MOBILE_QUEUE_WARNING = $reactive_formatter(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed mobile queue warning

@dawoodkhan82
dawoodkhan82 requested review from abidlabs and hysts and a lite review from Copilot August 14, 2026 19:13
@dawoodkhan82

Copy link
Copy Markdown
Collaborator Author

@abidlabs @hysts Made some changes to simplify the PR. Could use another review/test!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/*.md file 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 sends resume_event_id=[] (because resume_event_ids is set to None after the first successful connection). The server only replays buffered messages when resume_event_id is provided, so reconnecting without it can miss messages that were dequeued but not received before disconnect. Consider always sending the current pending event IDs when resume_sessions is enabled.
                        params={
                            "session_hash": session_hash,
                            "resume_event_id": resume_event_ids or [],
                            "acknowledgements": self.resume_sessions,
                        },

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Resume long running sessions Long-running tasks require open connection, looking for polling solution

5 participants