Percy3822 commited on
Commit
ca58e4f
·
verified ·
1 Parent(s): 56178e3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +108 -48
app.py CHANGED
@@ -1,51 +1,111 @@
1
- import os, json, time
2
- from pathlib import Path
3
- from fastapi import FastAPI, Request
4
- from fastapi.responses import StreamingResponse, JSONResponse
5
-
6
- BASE_DIR = Path(os.getenv("BASE_DIR","/tmp/brain_app"))
7
- FILES = BASE_DIR / "files"
8
- LOGS = FILES / "logs"
9
- EVENTS = LOGS / "events.jsonl"
10
- FILES.mkdir(parents=True, exist_ok=True)
11
- LOGS.mkdir(parents=True, exist_ok=True)
12
- EVENTS.touch(exist_ok=True)
13
-
14
- app = FastAPI()
15
-
16
- def log_event(kind: str, data: dict):
17
- ev = {"ts": time.time(), "type": kind, "data": data}
18
- with EVENTS.open("a", encoding="utf-8") as f:
19
- f.write(json.dumps(ev, ensure_ascii=False) + "\n")
20
 
21
  @app.get("/health")
22
- async def health():
23
- return {"ok": True, "service": "brain", "base_dir": str(BASE_DIR), "events_file": str(EVENTS)}
24
-
25
- @app.post("/process")
26
- async def process(req: Request):
27
- body = await req.json()
28
- log_event("process", body)
29
- # TODO: Replace this echo with your real brain logic
30
- reply = f"Echo: {(body.get('text') or body)}"
31
- return {"ok": True, "reply": reply}
32
-
33
- @app.get("/stream/logs")
34
- async def stream_logs():
35
- async def gen():
36
- last = EVENTS.stat().st_size
37
- with EVENTS.open("r", encoding="utf-8") as f:
38
- f.seek(0,0)
39
- for line in f:
40
- yield f"data: {line}\n\n"
41
- f.seek(last)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  while True:
43
- where = f.tell()
44
- line = f.readline()
45
- if not line:
46
- await asyncio.sleep(0.5)
47
- f.seek(where)
48
- continue
49
- yield f"data: {line}\n\n"
50
- import asyncio
51
- return StreamingResponse(gen(), media_type="text/event-stream")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, json, asyncio, time
2
+ from typing import Optional
3
+ import httpx
4
+ from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Body
5
+ from fastapi.middleware.cors import CORSMiddleware
6
+ from fastapi.responses import JSONResponse
7
+
8
+ FILES_DIR = os.environ.get("FILES_DIR", "/tmp/brain_app/files")
9
+ TTS_WS_TARGET = os.environ.get("TTS_WS_TARGET", "")
10
+ TTS_HTTP_TARGET = os.environ.get("TTS_HTTP_TARGET", "")
11
+ STT_WS_TARGET = os.environ.get("STT_WS_TARGET", "")
12
+
13
+ app = FastAPI(title="Brain Proxy")
14
+ app.add_middleware(
15
+ CORSMiddleware,
16
+ allow_origins=[""], allow_headers=[""], allow_methods=["*"], allow_credentials=True,
17
+ )
 
 
18
 
19
  @app.get("/health")
20
+ def health():
21
+ return {
22
+ "ok": True,
23
+ "service": "brain",
24
+ "files_dir": FILES_DIR,
25
+ "tts_ws_target": TTS_WS_TARGET,
26
+ "tts_http_target": TTS_HTTP_TARGET,
27
+ "stt_ws_target": STT_WS_TARGET,
28
+ "time": time.time(),
29
+ }
30
+
31
+ # ---------- HTTP proxy to TTS /speak ----------
32
+ @app.post("/speak")
33
+ async def speak_proxy(body: dict = Body(...)):
34
+ if not TTS_HTTP_TARGET:
35
+ return JSONResponse({"ok": False, "error": "TTS HTTP target not configured"}, 500)
36
+ try:
37
+ async with httpx.AsyncClient(timeout=60) as client:
38
+ r = await client.post(TTS_HTTP_TARGET, json=body)
39
+ r.raise_for_status()
40
+ return JSONResponse(r.json(), r.status_code)
41
+ except httpx.HTTPError as e:
42
+ return JSONResponse({"ok": False, "error": f"TTS proxy failed: {e}"}, 502)
43
+
44
+ # ---------- WS proxy helpers ----------
45
+ async def _bridge_ws(client_ws: WebSocket, upstream_url: str):
46
+ import websockets
47
+
48
+ # Establish upstream connection
49
+ try:
50
+ upstream = await websockets.connect(upstream_url, ping_interval=None, max_size=8_000_000)
51
+ except Exception as e:
52
+ await client_ws.send_json({"event":"error","error":f"Upstream connect failed: {repr(e)}"})
53
+ await client_ws.close()
54
+ return
55
+
56
+ async def c2u():
57
+ # client -> upstream (pass text or bytes)
58
+ try:
59
  while True:
60
+ msg = await client_ws.receive()
61
+ if "bytes" in msg and msg["bytes"] is not None:
62
+ await upstream.send(msg["bytes"])
63
+ elif "text" in msg and msg["text"] is not None:
64
+ await upstream.send(msg["text"])
65
+ else:
66
+ # ignore pings/others
67
+ pass
68
+ except WebSocketDisconnect:
69
+ pass
70
+ finally:
71
+ try:
72
+ await upstream.close()
73
+ except Exception:
74
+ pass
75
+
76
+ async def u2c():
77
+ # upstream -> client (pass text or binary)
78
+ try:
79
+ while True:
80
+ msg = await upstream.recv()
81
+ if isinstance(msg, (bytes, bytearray)):
82
+ await client_ws.send_bytes(msg)
83
+ else:
84
+ await client_ws.send_text(msg)
85
+ except Exception:
86
+ pass
87
+ finally:
88
+ try:
89
+ await client_ws.close()
90
+ except Exception:
91
+ pass
92
+
93
+ # run both directions
94
+ await asyncio.gather(c2u(), u2c())
95
+
96
+ # ---------- WS endpoints the client will use ----------
97
+ @app.websocket("/ws/tts_proxy")
98
+ async def ws_tts_proxy(ws: WebSocket):
99
+ await ws.accept()
100
+ if not TTS_WS_TARGET:
101
+ await ws.send_json({"event":"error","error":"TTS WS target not configured"})
102
+ await ws.close(); return
103
+ await _bridge_ws(ws, TTS_WS_TARGET)
104
+
105
+ @app.websocket("/ws/stt_proxy")
106
+ async def ws_stt_proxy(ws: WebSocket):
107
+ await ws.accept()
108
+ if not STT_WS_TARGET:
109
+ await ws.send_json({"event":"error","error":"STT WS target not configured"})
110
+ await ws.close(); return
111
+ await _bridge_ws(ws, STT_WS_TARGET)