Skip to content

Add master/worker dynamic #11126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions demo/master_worker/run.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: master_worker"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "import sys\n", "import time\n", "\n", "def greet(name, prog = gr.Progress()):\n", " print(\"processing\", name)\n", " prog(0, desc=\"Starting...\")\n", " time.sleep(2)\n", " prog(0.5, desc=\"Halfway there...\")\n", " time.sleep(2)\n", " return \"Hello \" + name + \"!\"\n", "\n", "with gr.Blocks() as demo:\n", " name = gr.Textbox(label=\"Name\")\n", " output = gr.Textbox(label=\"Output Box\")\n", " name.submit(fn=greet, inputs=name, outputs=output, api_name=\"greet\") \n", "\n", "role = \"worker\" if \"-w\" in sys.argv else \"hybrid\"\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch(role=role, master_url=\"http://localhost:7860/\", app_key=\"test123\")\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
21 changes: 21 additions & 0 deletions demo/master_worker/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import gradio as gr
import sys
import time

def greet(name, prog = gr.Progress()):
print("processing", name)
prog(0, desc="Starting...")
time.sleep(2)
prog(0.5, desc="Halfway there...")
time.sleep(2)
return "Hello " + name + "!"

with gr.Blocks() as demo:
name = gr.Textbox(label="Name")
output = gr.Textbox(label="Output Box")
name.submit(fn=greet, inputs=name, outputs=output, api_name="greet")

role = "worker" if "-w" in sys.argv else "hybrid"

if __name__ == "__main__":
demo.launch(role=role, master_url="http://localhost:7860/", app_key="test123")
44 changes: 41 additions & 3 deletions gradio/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,10 +502,10 @@ def __init__(
inputs_as_dict: bool,
targets: list[tuple[int | None, str]],
_id: int,
concurrency_id: str,
concurrency_limit: int | None | Literal["default"] = "default",
batch: bool = False,
max_batch_size: int = 4,
concurrency_limit: int | None | Literal["default"] = "default",
concurrency_id: str | None = None,
tracks_progress: bool = False,
api_name: str | Literal[False] = False,
js: str | Literal[True] | None = None,
Expand Down Expand Up @@ -538,7 +538,7 @@ def __init__(
self.postprocess = postprocess
self.tracks_progress = tracks_progress
self.concurrency_limit: int | None | Literal["default"] = concurrency_limit
self.concurrency_id = concurrency_id or str(id(fn))
self.concurrency_id = concurrency_id
self.batch = batch
self.max_batch_size = max_batch_size
self.total_runtime = 0
Expand Down Expand Up @@ -865,6 +865,19 @@ def set_event_trigger(
"Cannot create event: events with js=True cannot have inputs."
)

if concurrency_id is None:
concurrency_hash = str(id(fn)) if fn is not None else None
if concurrency_hash in self.root_block.concurrency_hash_to_id:
concurrency_id = self.root_block.concurrency_hash_to_id[
concurrency_hash
]
else:
concurrency_id = str(self.root_block.running_concurrency_id)
self.root_block.concurrency_hash_to_id[concurrency_hash] = (
concurrency_id
)
self.root_block.running_concurrency_id += 1

block_fn = BlockFunction(
fn,
inputs,
Expand Down Expand Up @@ -1200,6 +1213,8 @@ def __init__(
self.blocked_paths = []
self.root_path = os.environ.get("GRADIO_ROOT_PATH", "")
self.proxy_urls = set()
self.concurrency_hash_to_id = {}
self.running_concurrency_id = 0

self.pages: list[tuple[str, str]] = [("", "Home")]
self.current_page = ""
Expand Down Expand Up @@ -2432,6 +2447,9 @@ def launch(
node_port: int | None = None,
ssr_mode: bool | None = None,
pwa: bool | None = None,
app_key: str | None = None,
role: Literal["hybrid", "master", "worker"] | None = None,
master_url: str | None = None,
_frontend: bool = True,
) -> tuple[App, str, str]:
"""
Expand Down Expand Up @@ -2472,6 +2490,9 @@ def launch(
strict_cors: If True, prevents external domains from making requests to a Gradio server running on localhost. If False, allows requests to localhost that originate from localhost but also, crucially, from "null". This parameter should normally be True to prevent CSRF attacks but may need to be False when embedding a *locally-running Gradio app* using web components.
ssr_mode: If True, the Gradio app will be rendered using server-side rendering mode, which is typically more performant and provides better SEO, but this requires Node 20+ to be installed on the system. If False, the app will be rendered using client-side rendering mode. If None, will use GRADIO_SSR_MODE environment variable or default to False.
pwa: If True, the Gradio app will be set up as an installable PWA (Progressive Web App). If set to None (default behavior), then the PWA feature will be enabled if this Gradio app is launched on Spaces, but not otherwise.
app_key: Used for communication in master/worker setups - must be the same across master and all workers. If not provided, will use the GRADIO_APP_KEY environment variable.
role: Role in master/worker setup. "hybrid" (default) means this app will both receive and process tasks, and other workers can attach to this app to process tasks. "master" means this app will only receive tasks. "worker" means this app will only process tasks and not receive them, and this requires master_url to be set. Will load from GRADIO_ROLE environment variable if not provided.
master_url: The URL of the master app in a master/worker setup. This is required if role is set to "worker", otherwise this is ignored. Will load from GRADIO_MASTER_URL environment variable if not provided.
Returns:
app: FastAPI app object that is running the demo
local_url: Locally accessible link to the demo
Expand Down Expand Up @@ -2531,6 +2552,23 @@ def reverse(text):
self.favicon_path = favicon_path
self.ssl_verify = ssl_verify
self.state_session_capacity = state_session_capacity
self.app_key = app_key or os.environ.get("GRADIO_APP_KEY", None)
role = role or os.environ.get("GRADIO_ROLE", "hybrid")
if role in ("master", "worker") and self.app_key is None:
raise ValueError(
"You must provide a secret app_key if you are launching a master/worker setup. This can be set via `.launch(app_key='...')` or through the GRADIO_APP_KEY environment variable. Masters and workers must all share the same app key."
)
if role == "worker" and master_url is None:
raise ValueError(
"You must provide a master_url if you are launching a worker."
)
if role == "master" and any(fn.renderable for fn in self.fns.values()):
raise ValueError(
"You cannot use `role='master'` with renderable components, use role='hybrid' instead. This machine will handle all renders."
)
self._queue.role = role
if master_url:
self._queue.master_url = master_url.strip("/")
if root_path is None:
self.root_path = os.environ.get("GRADIO_ROOT_PATH", "")
else:
Expand Down
6 changes: 6 additions & 0 deletions gradio/data_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,3 +428,9 @@ class ImageData(GradioModel):

class Base64ImageData(GradioModel):
url: str = Field(description="base64 encoded image")


class EventAnalytics(BaseModel):
event_id: str
key: str
value: str | float | int | None
Loading