Spaces:
Sleeping
Sleeping
Fix /generate route being shadowed by Gradio's own catch-all -- mount Gradio at /ui instead of adding routes post-hoc to demo.app
Browse files- __pycache__/app.cpython-314.pyc +0 -0
- app.py +32 -16
- requirements.txt +1 -0
__pycache__/app.cpython-314.pyc
ADDED
|
Binary file (6.19 kB). View file
|
|
|
app.py
CHANGED
|
@@ -4,8 +4,17 @@ chatPDB inference API — HuggingFace Space (Gradio SDK, ZeroGPU)
|
|
| 4 |
Exposes POST /generate (SSE) consumed by the Flask PTY app on the droplet.
|
| 5 |
Gradio SDK is required for ZeroGPU (confirmed live 2026-07-23: requesting ZeroGPU hardware for a
|
| 6 |
Docker-SDK Space returns "ZeroGPU Spaces only work with Gradio SDK") -- the Gradio UI itself is
|
| 7 |
-
just a minimal landing page
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
Cold-start note: first request after idle downloads the GGUF and allocates the GPU
|
| 11 |
(~60-120 s). Subsequent requests within the same GPU lease are fast.
|
|
@@ -16,7 +25,8 @@ import json
|
|
| 16 |
|
| 17 |
import gradio as gr
|
| 18 |
import spaces
|
| 19 |
-
|
|
|
|
| 20 |
from fastapi.responses import StreamingResponse
|
| 21 |
from huggingface_hub import hf_hub_download
|
| 22 |
|
|
@@ -71,20 +81,12 @@ def _generate_tokens(
|
|
| 71 |
return tokens
|
| 72 |
|
| 73 |
|
| 74 |
-
# --
|
| 75 |
-
|
| 76 |
-
with gr.Blocks(title="chatPDB API") as demo:
|
| 77 |
-
gr.Markdown(
|
| 78 |
-
"## 🧬 chatPDB Inference API\n\n"
|
| 79 |
-
"Internal endpoint for [chatpdb.mdeller.com](https://chatpdb.mdeller.com). "
|
| 80 |
-
"Use `POST /generate` — returns `text/event-stream` of token chunks.\n\n"
|
| 81 |
-
"**Cold start:** first request after idle takes ~60-120 s (GGUF download + GPU alloc)."
|
| 82 |
-
)
|
| 83 |
|
|
|
|
| 84 |
|
| 85 |
-
# -- Custom FastAPI route mounted on Gradio's app --
|
| 86 |
|
| 87 |
-
@
|
| 88 |
async def generate(request: Request):
|
| 89 |
body = await request.json()
|
| 90 |
prompt = body.get("prompt", "")
|
|
@@ -102,10 +104,24 @@ async def generate(request: Request):
|
|
| 102 |
return StreamingResponse(event_stream(), media_type="text/event-stream")
|
| 103 |
|
| 104 |
|
| 105 |
-
@
|
| 106 |
async def health():
|
| 107 |
return {"status": "ok"}
|
| 108 |
|
| 109 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
if __name__ == "__main__":
|
| 111 |
-
|
|
|
|
| 4 |
Exposes POST /generate (SSE) consumed by the Flask PTY app on the droplet.
|
| 5 |
Gradio SDK is required for ZeroGPU (confirmed live 2026-07-23: requesting ZeroGPU hardware for a
|
| 6 |
Docker-SDK Space returns "ZeroGPU Spaces only work with Gradio SDK") -- the Gradio UI itself is
|
| 7 |
+
just a minimal landing page.
|
| 8 |
+
|
| 9 |
+
Real fix (found + fixed 2026-07-23): mounting a custom route via `@demo.app.post(...)` on the
|
| 10 |
+
Blocks object's own `.app` attribute does NOT reliably take effect on HF Spaces -- confirmed live
|
| 11 |
+
against the actual deployed Space: `curl -X POST .../generate` returned 405 with `allow: GET`,
|
| 12 |
+
meaning Gradio's own catch-all SPA route claimed the path instead (Gradio's launch machinery
|
| 13 |
+
appears to reconstruct/finalize its FastAPI app at launch time, after the route was added to the
|
| 14 |
+
earlier `demo.app` reference). Fixed using Gradio's own documented pattern for combining custom
|
| 15 |
+
FastAPI routes with a Gradio UI: build a plain FastAPI `app` first, register routes on it, then
|
| 16 |
+
mount Gradio as a sub-application at a distinct path (routes registered directly on the outer
|
| 17 |
+
`app` are not shadowed by Gradio's own routing, which only applies within its mounted sub-path).
|
| 18 |
|
| 19 |
Cold-start note: first request after idle downloads the GGUF and allocates the GPU
|
| 20 |
(~60-120 s). Subsequent requests within the same GPU lease are fast.
|
|
|
|
| 25 |
|
| 26 |
import gradio as gr
|
| 27 |
import spaces
|
| 28 |
+
import uvicorn
|
| 29 |
+
from fastapi import FastAPI, Request
|
| 30 |
from fastapi.responses import StreamingResponse
|
| 31 |
from huggingface_hub import hf_hub_download
|
| 32 |
|
|
|
|
| 81 |
return tokens
|
| 82 |
|
| 83 |
|
| 84 |
+
# -- Plain FastAPI app owns the real API routes --
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
|
| 86 |
+
app = FastAPI(title="chatPDB API")
|
| 87 |
|
|
|
|
| 88 |
|
| 89 |
+
@app.post("/generate")
|
| 90 |
async def generate(request: Request):
|
| 91 |
body = await request.json()
|
| 92 |
prompt = body.get("prompt", "")
|
|
|
|
| 104 |
return StreamingResponse(event_stream(), media_type="text/event-stream")
|
| 105 |
|
| 106 |
|
| 107 |
+
@app.get("/health")
|
| 108 |
async def health():
|
| 109 |
return {"status": "ok"}
|
| 110 |
|
| 111 |
|
| 112 |
+
# -- Gradio UI (minimal -- required for Gradio SDK / ZeroGPU), mounted at a sub-path so it can't
|
| 113 |
+
# shadow the routes registered directly on `app` above --
|
| 114 |
+
|
| 115 |
+
with gr.Blocks(title="chatPDB API") as demo:
|
| 116 |
+
gr.Markdown(
|
| 117 |
+
"## 🧬 chatPDB Inference API\n\n"
|
| 118 |
+
"Internal endpoint for [chatpdb.mdeller.com](https://chatpdb.mdeller.com). "
|
| 119 |
+
"Use `POST /generate` — returns `text/event-stream` of token chunks.\n\n"
|
| 120 |
+
"**Cold start:** first request after idle takes ~60-120 s (GGUF download + GPU alloc)."
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
app = gr.mount_gradio_app(app, demo, path="/ui")
|
| 124 |
+
|
| 125 |
+
|
| 126 |
if __name__ == "__main__":
|
| 127 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|
requirements.txt
CHANGED
|
@@ -3,3 +3,4 @@ llama-cpp-python
|
|
| 3 |
gradio>=4.0
|
| 4 |
spaces
|
| 5 |
huggingface_hub
|
|
|
|
|
|
| 3 |
gradio>=4.0
|
| 4 |
spaces
|
| 5 |
huggingface_hub
|
| 6 |
+
uvicorn
|