Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
7b5e375
feat(queue): multiprocessing support
cbensimon Apr 22, 2026
3389ab8
add changeset
gradio-pr-bot Apr 22, 2026
43e5fd4
ruff format
cbensimon Apr 22, 2026
e39fbe2
spaces in test requirements
cbensimon May 4, 2026
f5b17a3
No more auto-wrap
cbensimon May 4, 2026
09bb3bd
ruff format
cbensimon May 4, 2026
c4d9244
Not needed anymore
cbensimon May 4, 2026
145e2ff
Merge branch 'main' into queue-multiprocess
cbensimon May 4, 2026
d31eabb
add changeset
gradio-pr-bot May 4, 2026
f67f2d3
ruff check
cbensimon May 4, 2026
d6bf21d
ty is not ready for serious typing
cbensimon May 4, 2026
9dc97e3
ruff format
cbensimon May 4, 2026
0976810
Shorter
cbensimon May 4, 2026
4809ec0
SimpleQueue
cbensimon May 4, 2026
ef07b23
handle RPC thread exceptions
cbensimon May 4, 2026
0cd13c1
Already in pyproject.toml
cbensimon May 5, 2026
e6c84b8
Add docstring for MultiprocessWorkerContextualizer
cbensimon May 5, 2026
6964ddb
Mount Middleware in launch instead of create_app + START_TIMEOUT
cbensimon May 5, 2026
3a4dd7b
Move setup inside Blocks method
cbensimon May 5, 2026
df84193
Also setup for mount_gradio_app
cbensimon May 5, 2026
6bb3b3b
Move in route_utils.py ...
cbensimon May 5, 2026
5940338
Fix utils.is_zero_gpu_space
cbensimon May 6, 2026
e7959f2
Remove max_size override since it has never been used until here
cbensimon May 6, 2026
ac32828
Propagate context to handler
cbensimon May 6, 2026
607023e
Do not wrap fn with partial
cbensimon May 6, 2026
e89c909
Add a test for context propagation
cbensimon May 6, 2026
6661309
Ruff
cbensimon May 6, 2026
424dbaf
Only pass context where needed
cbensimon May 6, 2026
27d267e
Also for generators x async
cbensimon May 7, 2026
7f70042
ty
cbensimon May 7, 2026
43dde93
Move middleware setup in dedicated zerogpu module + add disconnect de…
cbensimon May 7, 2026
99f0df6
spaces 0.50.dev3
cbensimon May 7, 2026
76cc566
Add a test for cursor bot
cbensimon May 7, 2026
7dfee1b
Merge branch 'main' into queue-multiprocess
cbensimon Jun 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/beige-jobs-switch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"gradio": minor
---

feat:ZeroGPU native support
15 changes: 0 additions & 15 deletions gradio/block_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@

from . import utils

try:
import spaces # type: ignore
except Exception:
spaces = None


if TYPE_CHECKING: # Only import for type checking (is False at runtime).
from gradio.components.base import Component
from gradio.renderable import Renderable
Expand Down Expand Up @@ -114,15 +108,6 @@ def __init__(
self.component_prop_inputs = component_prop_inputs or []
self.key = key

self.spaces_auto_wrap()

def spaces_auto_wrap(self):
if spaces is None:
return
if utils.get_space() is None:
return
self.fn = spaces.gradio_auto_wrap(self.fn)

def __str__(self):
return str(
{
Expand Down
41 changes: 29 additions & 12 deletions gradio/blocks.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

import asyncio
import contextvars
import copy
import dataclasses
import hashlib
Expand Down Expand Up @@ -76,7 +78,11 @@
from gradio.helpers import create_tracker, skip, special_args
from gradio.i18n import I18n, I18nData
from gradio.node_server import start_node_server
from gradio.route_utils import API_PREFIX, MediaStream, slugify
from gradio.route_utils import (
API_PREFIX,
MediaStream,
slugify,
)
from gradio.routes import INTERNAL_ROUTES, VERSION, App, Request
from gradio.state_holder import SessionState, StateHolder
from gradio.themes import ThemeClass as Theme
Expand All @@ -96,12 +102,7 @@
get_package_version,
get_upload_folder,
)

try:
import spaces # type: ignore
except Exception:
spaces = None

from gradio.zerogpu import maybe_setup_zerogpu_middleware

if TYPE_CHECKING: # Only import for type checking (is False at runtime).
from gradio.components.base import Component
Expand Down Expand Up @@ -1592,6 +1593,7 @@ async def call_function(
event_data: EventData | None = None,
in_event_listener: bool = False,
state: SessionState | None = None,
context: contextvars.Context | None = None,
):
"""
Calls function with given index and preprocessed input, and measures process time.
Expand Down Expand Up @@ -1652,7 +1654,16 @@ async def call_function(
processed_input[progress_index] = progress_tracker

if inspect.iscoroutinefunction(fn):
prediction = await fn(*processed_input)
if context is not None:
prediction = await context.copy().run(
asyncio.create_task, fn(*processed_input)
)
Comment thread
cursor[bot] marked this conversation as resolved.
else:
prediction = await fn(*processed_input)
elif context is not None:
prediction = await anyio.to_thread.run_sync( # type: ignore
context.copy().run, fn, *processed_input, limiter=self.limiter
Comment thread
cursor[bot] marked this conversation as resolved.
)
else:
prediction = await anyio.to_thread.run_sync( # type: ignore
fn, *processed_input, limiter=self.limiter
Expand All @@ -1665,8 +1676,10 @@ async def call_function(
if iterator is None:
iterator = cast(AsyncIterator[Any], prediction)
if inspect.isgenerator(iterator):
iterator = utils.SyncToAsyncIterator(iterator, self.limiter)
prediction = await utils.async_iteration(iterator)
iterator = utils.SyncToAsyncIterator(
iterator, self.limiter, context
)
prediction = await utils.async_iteration(iterator, context=context)
is_generating = True
except StopAsyncIteration:
n_outputs = len(block_fn.outputs)
Expand Down Expand Up @@ -2188,6 +2201,7 @@ async def process_api(
simple_format: bool = False,
explicit_call: bool = False,
root_path: str | None = None,
context: contextvars.Context | None = None,
) -> dict[str, Any]:
"""
Processes API calls from the frontend. First preprocesses the data,
Expand Down Expand Up @@ -2251,6 +2265,7 @@ async def process_api(
event_data,
in_event_listener,
state,
context=context,
)
manual_cache_used = used_manual_cache()
preds = result["prediction"]
Expand Down Expand Up @@ -2286,6 +2301,7 @@ async def process_api(
event_data,
in_event_listener,
state,
context=context,
)
manual_cache_used = used_manual_cache()

Expand Down Expand Up @@ -2529,8 +2545,6 @@ def queue(
"""
if api_open is not None:
self.api_open = api_open
if utils.is_zero_gpu_space():
max_size = 1 if max_size is None else max_size
self._queue = queueing.Queue(
live_updates=status_update_rate == "auto",
concurrency_count=self.max_threads,
Expand Down Expand Up @@ -2931,6 +2945,9 @@ def reverse(text):
mcp_server=mcp_server,
debug=debug,
)

maybe_setup_zerogpu_middleware(self.app)

if self.mcp_error and not quiet:
print(self.mcp_error)

Expand Down
31 changes: 31 additions & 0 deletions gradio/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,37 @@ class LocalContext:
)


class MultiprocessWorkerContextualizer:
Comment thread
cbensimon marked this conversation as resolved.
"""
Refreshes LocalContext for persistent multiprocessing workers processing consecutive requests.

Example usage:
```
pool = ProcessPoolExecutor()

def handler(value):
contextualize = MultiprocessWorkerContextualizer()
return e.submit(process_wrapper, contextualize, value).result()

def process_wrapper(contextualize, value):
contextualize()
return process(value)

demo = gr.Interface(handler, gr.Text(), gr.Text())
```
"""

def __init__(self):
self.event_id = LocalContext.event_id.get(None)
self.in_event_listener = LocalContext.in_event_listener.get(False)
self.progress = LocalContext.progress.get(None)

def __call__(self):
LocalContext.event_id.set(self.event_id)
LocalContext.in_event_listener.set(self.in_event_listener)
LocalContext.progress.set(self.progress)
Comment thread
cbensimon marked this conversation as resolved.
Comment thread
cbensimon marked this conversation as resolved.


def get_render_context() -> BlockContext | None:
if LocalContext.renderable.get(None):
return LocalContext.render_block.get(None)
Expand Down
3 changes: 2 additions & 1 deletion gradio/http_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
INITIAL_PORT_VALUE = int(os.getenv("GRADIO_SERVER_PORT", "7860"))
TRY_NUM_PORTS = int(os.getenv("GRADIO_NUM_PORTS", "100"))
LOCALHOST_NAME = os.getenv("GRADIO_SERVER_NAME", "127.0.0.1")
START_TIMEOUT = int(os.getenv("GRADIO_START_TIMEOUT", "5"))

GRADIO_HOT_RELOAD = os.getenv("GRADIO_HOT_RELOAD", "false").lower()

Expand Down Expand Up @@ -69,7 +70,7 @@ def run_in_thread(self):
start = time.time()
while not self.started:
time.sleep(1e-3)
if time.time() - start > 5:
if time.time() - start > START_TIMEOUT:
raise ServerFailedToStartError(
"Server failed to start. Please check that the port is available."
)
Expand Down
Loading
Loading