Spaces:
Running on Zero
Running on Zero
Do not bind a port; let the Space runner serve the ASGI app
Browse filesThe runner already serves the module-level app, so our own uvicorn.run was a
second server competing for 7860 — the container died on 'address already in
use' right after startup completed. Mount the Blocks UI onto the same app at
import time instead, keeping /asr and /translate at the root.
app.py
CHANGED
|
@@ -225,34 +225,40 @@ def translate(body: TranslateIn, x_service_token: str = Header(default=None)):
|
|
| 225 |
|
| 226 |
|
| 227 |
# ------------------------------------------------------------------- LAUNCH ---
|
| 228 |
-
#
|
| 229 |
-
#
|
| 230 |
-
#
|
| 231 |
-
#
|
| 232 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 233 |
import json as _json
|
| 234 |
return _json.dumps(health(), indent=2)
|
| 235 |
|
| 236 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 237 |
def _build_ui():
|
| 238 |
import gradio as gr
|
| 239 |
with gr.Blocks(title="ScriptFlow Hausa inference") as demo:
|
| 240 |
-
gr.Markdown(
|
| 241 |
-
"## ScriptFlow inference service\n"
|
| 242 |
-
"Hausa ASR + Hausa→English MT. The REST API is the real interface:\n"
|
| 243 |
-
"`POST /asr` (raw audio body) and `POST /translate` "
|
| 244 |
-
"(`{\"texts\": [...]}`), both with an `X-Service-Token` header.\n\n"
|
| 245 |
-
"Press the button to see whether the models have loaded — the first "
|
| 246 |
-
"call after a cold start downloads ~1GB and takes several minutes."
|
| 247 |
-
)
|
| 248 |
out = gr.Code(label="GET /", language="json")
|
| 249 |
-
gr.Button("Check status").click(_ui_check, inputs=
|
| 250 |
return demo
|
| 251 |
|
| 252 |
|
| 253 |
-
|
| 254 |
-
import uvicorn
|
| 255 |
import gradio as gr
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
|
|
|
| 225 |
|
| 226 |
|
| 227 |
# ------------------------------------------------------------------- LAUNCH ---
|
| 228 |
+
# The Space runner serves the module-level `app` itself. Binding a port here as
|
| 229 |
+
# well is what produced "address already in use" and killed the container: two
|
| 230 |
+
# servers competing for 7860. So this module defines the ASGI app and never
|
| 231 |
+
# listens.
|
| 232 |
+
#
|
| 233 |
+
# The Blocks UI is grafted onto that same app at import time, which keeps /asr
|
| 234 |
+
# and /translate at the root where the backend expects them, with a status page
|
| 235 |
+
# at /ui.
|
| 236 |
+
def _ui_check():
|
| 237 |
import json as _json
|
| 238 |
return _json.dumps(health(), indent=2)
|
| 239 |
|
| 240 |
|
| 241 |
+
_UI_TEXT = (
|
| 242 |
+
"## ScriptFlow inference service\n\n"
|
| 243 |
+
"Hausa ASR + Hausa->English MT. The REST API is the real interface:\n\n"
|
| 244 |
+
"- `POST /asr` — raw audio bytes as the body\n"
|
| 245 |
+
"- `POST /translate` — `{\"texts\": [...]}`\n\n"
|
| 246 |
+
"Both take an `X-Service-Token` header. The first call after a cold start "
|
| 247 |
+
"downloads ~1GB of weights and takes several minutes."
|
| 248 |
+
)
|
| 249 |
+
|
| 250 |
+
|
| 251 |
def _build_ui():
|
| 252 |
import gradio as gr
|
| 253 |
with gr.Blocks(title="ScriptFlow Hausa inference") as demo:
|
| 254 |
+
gr.Markdown(_UI_TEXT)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 255 |
out = gr.Code(label="GET /", language="json")
|
| 256 |
+
gr.Button("Check status").click(_ui_check, inputs=None, outputs=out)
|
| 257 |
return demo
|
| 258 |
|
| 259 |
|
| 260 |
+
try:
|
|
|
|
| 261 |
import gradio as gr
|
| 262 |
+
app = gr.mount_gradio_app(app, _build_ui(), path="/ui")
|
| 263 |
+
except Exception as _e: # never let the UI break the API
|
| 264 |
+
print(f"gradio UI unavailable ({type(_e).__name__}: {_e}) — REST only", flush=True)
|