""" JARVIS — main FastAPI entrypoint """ import os from fastapi import FastAPI, WebSocket, WebSocketDisconnect from fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse from dotenv import load_dotenv from core.websocket_manager import ConnectionManager from core.voice_input import transcribe_audio from core.claude_agent import get_response from core.voice_output import speak load_dotenv() app = FastAPI(title="JARVIS", version="1.0.0") manager = ConnectionManager() # Serve the dashboard frontend app.mount("/static", StaticFiles(directory="client"), name="static") @app.get("/") async def root(): return FileResponse("client/jarvis-dashboard.html") @app.get("/health") async def health(): return {"status": "online", "version": "1.0.0"} @app.websocket("/ws/voice") async def voice_endpoint(websocket: WebSocket): """ Main voice WebSocket endpoint. Client sends raw audio bytes → server transcribes → Claude → TTS → sends audio bytes back. """ await manager.connect(websocket) try: while True: # Receive audio chunk from client audio_chunk = await websocket.receive_bytes() # 1. Transcribe with Whisper await websocket.send_json({"type": "status", "state": "processing"}) text = await transcribe_audio(audio_chunk) await websocket.send_json({"type": "transcript", "text": text}) # 2. Get Claude response (with tool use) await websocket.send_json({"type": "status", "state": "thinking"}) response_text = await get_response(text) await websocket.send_json({"type": "response", "text": response_text}) # 3. Synthesize speech with Fish Audio await websocket.send_json({"type": "status", "state": "speaking"}) audio_response = await speak(response_text) await websocket.send_bytes(audio_response) await websocket.send_json({"type": "status", "state": "idle"}) except WebSocketDisconnect: manager.disconnect(websocket) if __name__ == "__main__": import uvicorn uvicorn.run( "main:app", host=os.getenv("HOST", "0.0.0.0"), port=int(os.getenv("PORT", 8000)), reload=True )