71 lines
2.2 KiB
Python
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
|
|
)
|