Dellboy commited on
Commit
83e90ad
·
verified ·
1 Parent(s): 00bec90

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
Files changed (3) hide show
  1. __pycache__/app.cpython-314.pyc +0 -0
  2. app.py +32 -16
  3. 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; a custom FastAPI route is mounted on Gradio's own underlying app to
8
- keep the exact same /generate contract chat_remote.py already expects.
 
 
 
 
 
 
 
 
 
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
- from fastapi import Request
 
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
- # -- Gradio UI (minimal -- required for Gradio SDK / ZeroGPU) --
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
- @demo.app.post("/generate")
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
- @demo.app.get("/health")
106
  async def health():
107
  return {"status": "ok"}
108
 
109
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  if __name__ == "__main__":
111
- demo.launch()
 
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