Skip to content

Add Gradio-compatible /gradio_api routes on Spaces#515

Merged
abidlabs merged 5 commits into
mainfrom
fix-issue-514-gradio-api-info
Apr 17, 2026
Merged

Add Gradio-compatible /gradio_api routes on Spaces#515
abidlabs merged 5 commits into
mainfrom
fix-issue-514-gradio-api-info

Conversation

@abidlabs
Copy link
Copy Markdown
Member

Short description

This pull request registers Gradio-style HTTP routes when Trackio runs on Hugging Face Spaces (SYSTEM=spaces): GET /gradio_api/info, POST /gradio_api/call/{api_name}, GET /gradio_api/call/{api_name}/{event_id}, and POST /gradio_api/upload (mirroring /api/upload). The info endpoint returns a schema shaped like Gradios named_endpoints/unnamed_endpointspayload. Call endpoints reuse the existing Trackio API implementation: POST returns anevent_id, and GET streams a single SSE complete` event with the result, matching the pattern shown in the Spaces Agents tab.

AI Disclosure

  • I used AI to draft the initial implementation and PR text; changes were self-reviewed.
  • I did not use AI

Type of Change

  • New feature (non-breaking)
  • Bug fix
  • New feature (breaking change)
  • Documentation update
  • Test improvements

Related Issues

Closes: #514

Testing and linting

  • ruff check --fix --select I && ruff format (passed)
  • pytest tests/ --ignore=tests/ui/ — 127 passed (UI Playwright tests were not run; they failed locally due to missing frontend build / timeouts unrelated to this change)

Made with Cursor

Serve GET /gradio_api/info (and trailing slash), POST /gradio_api/call/{name},
GET /gradio_api/call/{name}/{event_id}, and POST /gradio_api/upload so HF Spaces
Agents tab and Gradio-style clients can discover and call the Trackio API.

Closes #514

Made-with: Cursor
@gradio-pr-bot
Copy link
Copy Markdown
Contributor

gradio-pr-bot commented Apr 17, 2026

🪼 branch checks and previews

Name Status URL
🦄 Changes detected! Details

@gradio-pr-bot
Copy link
Copy Markdown
Contributor

🦄 change detected

This Pull Request includes changes to the following packages.

Package Version
trackio minor

  • Add Gradio-compatible /gradio_api routes on Spaces

‼️ Changeset not approved. Ensure the version bump is appropriate for all packages before approving.

  • Maintainers can approve the changeset by checking this checkbox.

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.

@abidlabs abidlabs requested a review from Copilot April 17, 2026 20:25
@HuggingFaceDocBuilderDev
Copy link
Copy Markdown

HuggingFaceDocBuilderDev commented Apr 17, 2026

🪼 branch checks and previews

Name Status URL
Spaces ready! Spaces preview

Install Trackio from this PR (includes built frontend)

pip install "https://huggingface.co/buckets/trackio/trackio-wheels/resolve/e85ebf7b939378ecedd4681957d78c9728af3b4f/trackio-0.23.0-py3-none-any.whl"

@HuggingFaceDocBuilderDev
Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds Gradio-compatible /gradio_api/* HTTP routes when Trackio is running on Hugging Face Spaces (SYSTEM=spaces), so the Spaces “Agents” tab can discover and invoke Trackio’s existing HTTP APIs via a Gradio-shaped protocol.

Changes:

  • Implement /gradio_api/info, /gradio_api/call/{api_name} (POST), /gradio_api/call/{api_name}/{event_id} (GET SSE), and /gradio_api/upload handlers on Spaces.
  • Generate a Gradio-style named_endpoints schema from the existing api_registry.
  • Add a changeset marking this as a minor feature release.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
trackio/asgi_app.py Adds Gradio-compat handlers/routes (conditional on Spaces) and a small in-memory event store for POST→poll SSE flow.
.changeset/silly-coats-go.md Declares a minor-version changeset for the new feature.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread trackio/asgi_app.py
Comment on lines +227 to 231
async def run_api_request(request: Request, api_name: str) -> Response:
api_registry = request.app.state.api_registry
api_name = request.path_params["api_name"]
fn = api_registry.get(api_name)
if fn is None:
return JSONResponse({"error": f"Unknown API: {api_name}"}, status_code=404)
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

On Spaces, the PR description (and HF Agents tab) indicates calls should authenticate via Authorization: Bearer $HF_TOKEN, but this request handler path never reads the Authorization header. Endpoints that require an hf_token argument (e.g., write/mutation APIs) will fail unless callers explicitly pass hf_token in the JSON body. Consider extracting the bearer token from request.headers['authorization'] on Spaces and injecting it into the computed kwargs when the target function accepts an hf_token parameter and it wasn't provided in the request body.

Copilot uses AI. Check for mistakes.
Comment thread trackio/asgi_app.py Outdated
Comment on lines +285 to +296
_store_gradio_call_result(request, event_id, body["data"])
return JSONResponse({"event_id": event_id})


async def gradio_call_poll_handler(request: Request) -> Response:
event_id = request.path_params["event_id"]
with request.app.state.gradio_call_events_lock:
data = request.app.state.gradio_call_events.pop(event_id, None)
if data is None:
return JSONResponse({"error": "Unknown or expired event_id"}, status_code=404)

payload = json.dumps(_json_safe([data]))
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

/gradio_api/call/{api_name}/{event_id} includes api_name in the route, but the poll handler ignores it and looks up results solely by event_id. This makes api_name effectively decorative and can return a result even if the caller polls with the wrong endpoint name. Consider storing (api_name, result) (or nesting the event map by api_name) and validating that the polled api_name matches the one used in the POST.

Suggested change
_store_gradio_call_result(request, event_id, body["data"])
return JSONResponse({"event_id": event_id})
async def gradio_call_poll_handler(request: Request) -> Response:
event_id = request.path_params["event_id"]
with request.app.state.gradio_call_events_lock:
data = request.app.state.gradio_call_events.pop(event_id, None)
if data is None:
return JSONResponse({"error": "Unknown or expired event_id"}, status_code=404)
payload = json.dumps(_json_safe([data]))
with request.app.state.gradio_call_events_lock:
request.app.state.gradio_call_events[event_id] = {
"api_name": api_name,
"data": body["data"],
}
return JSONResponse({"event_id": event_id})
async def gradio_call_poll_handler(request: Request) -> Response:
api_name = request.path_params["api_name"]
event_id = request.path_params["event_id"]
with request.app.state.gradio_call_events_lock:
event = request.app.state.gradio_call_events.pop(event_id, None)
if event is None:
return JSONResponse({"error": "Unknown or expired event_id"}, status_code=404)
if event.get("api_name") != api_name:
return JSONResponse({"error": "Unknown or expired event_id"}, status_code=404)
payload = json.dumps(_json_safe([event["data"]]))

Copilot uses AI. Check for mistakes.
Comment thread trackio/asgi_app.py Outdated
async def sse() -> Any:
yield f"event: complete\ndata: {payload}\n\n"

return StreamingResponse(sse(), media_type="text/event-stream")
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

The SSE poll response doesn’t set any anti-caching headers. Since event_id results are one-time and user-specific, it should likely mirror the info endpoint’s Cache-Control: no-store (and optionally X-Accel-Buffering: no) to avoid intermediary/proxy caching or buffering of text/event-stream responses.

Suggested change
return StreamingResponse(sse(), media_type="text/event-stream")
return StreamingResponse(
sse(),
media_type="text/event-stream",
headers={
"Cache-Control": "no-store",
"X-Accel-Buffering": "no",
},
)

Copilot uses AI. Check for mistakes.
Comment thread trackio/asgi_app.py
Comment on lines +353 to +357
if on_spaces():
routes.extend(
[
Route(
"/gradio_api/info",
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

New /gradio_api/* routes are introduced here, but there don’t appear to be any tests covering them (there are existing HTTP API tests for /api/*). Consider adding pytest coverage that sets SYSTEM=spaces and verifies: GET /gradio_api/info shape, POST /gradio_api/call/{api} returns an event_id, GET .../{event_id} returns a complete SSE event, and /gradio_api/upload aliases /api/upload.

Copilot uses AI. Check for mistakes.
abidlabs and others added 3 commits April 17, 2026 13:35
… SSE no-store

- Apply Authorization Bearer to hf_token when missing (Spaces only)
- Store api_name with gradio call events; validate on poll; restore event on mismatch
- Add Cache-Control and X-Accel-Buffering on SSE poll responses
- Add unit tests for gradio_api routes under SYSTEM=spaces

Made-with: Cursor
…log poll outcomes

- Treat None and blank hf_token (incl. whitespace-only) as unset for Bearer injection
- On gradio poll api_name mismatch, evict oldest if needed then restore event
- Log distinct messages for unknown event_id vs api_name mismatch (same 404 body)

Made-with: Cursor
@abidlabs abidlabs merged commit 0a242b8 into main Apr 17, 2026
9 checks passed
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.

Add /api_info/ endpoints to Trackio

4 participants