Percy3822 commited on
Commit
06ad053
·
verified ·
1 Parent(s): 89bfad0

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +98 -0
app.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, json, httpx
2
+ from typing import Dict, Any
3
+ from fastapi import FastAPI, Body, Request
4
+ from fastapi.middleware.cors import CORSMiddleware
5
+ from fastapi.responses import JSONResponse, PlainTextResponse
6
+
7
+ # ========= Config (set via Secrets) =========
8
+ TTS_BASE = os.getenv("TTS_BASE", "https://Percy3822-ActualTTS.hf.space")
9
+ PY_AI_URL = os.getenv("PY_AI_URL", "https://Percy3822-Python_ai.hf.space") # optional/future
10
+ GODOT_AI_URL = os.getenv("GODOT_AI_URL", "https://Percy3822-Godot_ai.hf.space") # optional/future
11
+ SECRET = os.getenv("BRAIN_SHARED_SECRET") # same header "x-auth" for internal calls
12
+
13
+ ROUTES = {
14
+ "python": PY_AI_URL,
15
+ "godot": GODOT_AI_URL,
16
+ "chat": None, # handled client-side (or future Chat module)
17
+ }
18
+
19
+ app = FastAPI(title="Brain_v2 (Router-Only)", version="2.0.0")
20
+
21
+ # CORS for your local app; restrict later if desired
22
+ app.add_middleware(
23
+ CORSMiddleware,
24
+ allow_origins=["*"],
25
+ allow_methods=["*"],
26
+ allow_headers=["*"],
27
+ )
28
+
29
+ _client = httpx.AsyncClient(timeout=httpx.Timeout(connect=15, read=None, write=15))
30
+
31
+ def _authorized(req: Request) -> bool:
32
+ return not SECRET or req.headers.get("x-auth") == SECRET
33
+
34
+ @app.get("/")
35
+ def root():
36
+ return PlainTextResponse("Brain_v2 router — POST /process to get stream URLs")
37
+
38
+ @app.get("/health")
39
+ async def health():
40
+ return {
41
+ "ok": True,
42
+ "routes": {k: (v or "client") for k, v in ROUTES.items()},
43
+ "tts_ws": f"{TTS_BASE}/ws/tts",
44
+ "version": "2.0.0"
45
+ }
46
+
47
+ @app.post("/process")
48
+ async def process(payload: Dict[str, Any] = Body(...), request: Request = None):
49
+ """
50
+ Input: { "text": "...", "module":"auto|python|godot|chat" }
51
+ Output: { ok, route, stream_url, tts_url }
52
+ - Client connects directly to stream_url for live TEXT tokens
53
+ - Client connects directly to tts_url for AUDIO (WS) when needed
54
+ """
55
+ if request and not _authorized(request):
56
+ return JSONResponse({"error": "unauthorized"}, status_code=401)
57
+
58
+ text = (payload.get("text") or "").strip()
59
+ module = (payload.get("module") or "auto").lower()
60
+ if not text:
61
+ return JSONResponse({"error": "empty text"}, status_code=400)
62
+
63
+ # Lightweight routing
64
+ low = text.lower()
65
+ route = "chat"
66
+ if module in ("python", "godot", "chat"):
67
+ route = module
68
+ elif low.startswith(("python:", "py:")):
69
+ route = "python"
70
+ elif low.startswith(("godot:", "gd:")):
71
+ route = "godot"
72
+
73
+ target = ROUTES.get(route)
74
+ stream_url = f"{target}/ws/stream" if target else None
75
+
76
+ return {
77
+ "ok": True,
78
+ "route": route,
79
+ "stream_url": stream_url, # direct model → client (text)
80
+ "tts_url": f"{TTS_BASE}/ws/tts", # direct TTS → client (audio)
81
+ "note": "Connect directly to these URLs for streaming. Brain does not proxy."
82
+ }
83
+
84
+ # Optional debug: say text via TTS to test wiring (non-streaming HTTP)
85
+ @app.post("/say")
86
+ async def say(payload: Dict[str, Any] = Body(...), request: Request = None):
87
+ if request and not _authorized(request):
88
+ return JSONResponse({"error": "unauthorized"}, status_code=401)
89
+ txt = (payload.get("text") or "").strip()
90
+ if not txt:
91
+ return JSONResponse({"error": "empty text"}, status_code=400)
92
+ try:
93
+ async with _client.stream("POST", f"{TTS_BASE}/speak.wav", json={"text": txt}) as r:
94
+ r.raise_for_status()
95
+ # we don’t proxy bytes here; caller just checks 200 OK for warmup
96
+ return {"ok": True}
97
+ except Exception as e:
98
+ return JSONResponse({"error": str(e)}, status_code=502)