import asyncio import json from contextlib import asynccontextmanager from datetime import datetime, timezone import uvicorn from fastapi import FastAPI, WebSocket, WebSocketDisconnect from app.shared.config import get_settings, Settings from app.shared.database import get_db, init_schema from app.pipeline.ingestion import get_or_create_room, mute_room, unmute_room, is_muted from app.pipeline.processor import process_utterance _settings: Settings | None = None def _get_settings() -> Settings: global _settings if _settings is None: _settings = get_settings() return _settings @asynccontextmanager async def lifespan(app: FastAPI): s = _get_settings() init_schema(s) yield app = FastAPI(lifespan=lifespan) @app.websocket("/ws/{room_name}") async def audio_ws(websocket: WebSocket, room_name: str): await websocket.accept() s = _get_settings() db = get_db(s) # Upsert room in DB db.execute( "INSERT INTO rooms (name, is_active, last_seen) VALUES (?,1,CURRENT_TIMESTAMP) " "ON CONFLICT(name) DO UPDATE SET is_active=1, last_seen=CURRENT_TIMESTAMP", (room_name,), ) db.commit() room = db.execute("SELECT id FROM rooms WHERE name=?", (room_name,)).fetchone() room_id = room["id"] get_or_create_room(room_name, s.vad_silence_ms, s.vad_min_speech_ms) try: while True: data = await websocket.receive() if "bytes" in data: chunk = data["bytes"] if not is_muted(room_name): db.execute( "UPDATE rooms SET last_seen=CURRENT_TIMESTAMP WHERE id=?", (room_id,), ) db.commit() now = datetime.now(timezone.utc) room_state = get_or_create_room(room_name, s.vad_silence_ms, s.vad_min_speech_ms) result = room_state["buffer"].process_chunk(chunk, now) if result is not None: audio_bytes, start_time = result end_time = datetime.now(timezone.utc) asyncio.create_task( process_utterance(room_id, audio_bytes, start_time, end_time, s, db) ) elif "text" in data: msg = json.loads(data["text"]) if msg.get("type") == "mute": mute_room(room_name) elif msg.get("type") == "unmute": unmute_room(room_name) except WebSocketDisconnect: db.execute("UPDATE rooms SET is_active=0 WHERE id=?", (room_id,)) db.commit() @app.post("/internal/rooms/{room_name}/mute") async def internal_mute(room_name: str): mute_room(room_name) return {"status": "muted", "room": room_name} @app.post("/internal/rooms/{room_name}/unmute") async def internal_unmute(room_name: str): unmute_room(room_name) return {"status": "unmuted", "room": room_name} @app.get("/health") async def health(): return {"status": "ok"} if __name__ == "__main__": uvicorn.run("app.pipeline.main:app", host="0.0.0.0", port=8300, reload=False)