diff --git a/.changeset/fresh-lizards-knock.md b/.changeset/fresh-lizards-knock.md new file mode 100644 index 0000000000..89efcb198c --- /dev/null +++ b/.changeset/fresh-lizards-knock.md @@ -0,0 +1,5 @@ +--- +"gradio": minor +--- + +feat:workflow: forward x-ip-token so zerogpu spaces bill the caller diff --git a/gradio/routes.py b/gradio/routes.py index 87b8665c22..5ec81c353d 100644 --- a/gradio/routes.py +++ b/gradio/routes.py @@ -1718,12 +1718,19 @@ async def component_server( request, # type: ignore None, ) + fn = utils.get_function_with_locals( + fn, + app.get_blocks(), + event_id=None, + in_event_listener=False, + request=request, # type: ignore + state=state, + ) if inspect.iscoroutinefunction(fn): return await fn(*processed_input) - else: - return await anyio.to_thread.run_sync( - fn, *processed_input, limiter=app.get_blocks().limiter - ) + return await anyio.to_thread.run_sync( + fn, *processed_input, limiter=app.get_blocks().limiter + ) @router.get( "/queue/status", diff --git a/test/test_routes.py b/test/test_routes.py index 252e314178..131c09bbab 100644 --- a/test/test_routes.py +++ b/test/test_routes.py @@ -2553,6 +2553,41 @@ def get_url(self, request: gr.Request): assert response.json()["_url"].endswith("/gradio_api/component_server") +def test_server_fn_forwards_x_ip_token_via_local_context(): + """/component_server must set LocalContext.request so gradio_client.Client + can forward the caller's x-ip-token to downstream ZeroGPU Spaces.""" + import requests + + from gradio.components.base import server + from gradio.context import LocalContext + + def get_ip_token(self, _data): + req = LocalContext.request.get(None) + return req.headers.get("x-ip-token") if req else None + + tb = gr.Textbox() + tb.get_ip_token = server(get_ip_token) # type: ignore + iface = gr.Interface(lambda x: x, inputs=tb, outputs="text") + component_id = next( + c["id"] + for c in iface.config["components"] + if c["type"] == "textbox" # type: ignore + ) + _, local_url, _ = iface.launch(prevent_thread_lock=True) + response = requests.post( + f"{local_url}/gradio_api/component_server", + json={ + "session_hash": "foo", + "component_id": component_id, + "fn_name": "get_ip_token", + "data": json.dumps({}), + }, + headers={"x-ip-token": "test-token"}, + ) + assert response.status_code == 200 + assert response.json() == "test-token" + + def test_slugify(): items = ( ("Hello, World!", "hello-world"),