Files
jarvis/main.py
2026-03-24 00:11:34 -05:00

71 lines
2.2 KiB
Python

"""
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
)