jarvis-cloud / backend /routes /voice_routes.py
Jarvis2345's picture
Squash history β€” remove all prior commits (secret hygiene, S4)
a31f556
Raw
History Blame Contribute Delete
4.71 kB
from fastapi import HTTPException, APIRouter, Response
from pydantic import BaseModel
router = APIRouter()
class SpeakPayload(BaseModel):
text: str
agent: str = "friday"
format: str = "wav" # always wav
class DevicePayload(BaseModel):
id: str
class SpaceAckPayload(BaseModel):
space: str # "dark_space" | "family_friendly"
persona: str = "friday"
@router.post("/speak")
async def speak(p: SpeakPayload):
"""Synthesise TTS via XTTS-v2 and return raw WAV bytes."""
try:
from backend.voice.tts import TTSPipeline
tts = TTSPipeline()
audio_bytes: bytes = await tts.synthesize(p.text, personality=p.agent)
return Response(content=audio_bytes, media_type="audio/wav")
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.post("/space_ack")
async def space_ack(p: SpaceAckPayload):
"""
Β§2.1b β€” Space Context Voice Injection entry point.
Returns the persona-aware acknowledgement text. Android will take this text
and silently inject it into the local WAV recording without playing out loud.
"""
from backend.agent.personas import get_space_ack_prompt
prompt_text = get_space_ack_prompt(p.persona, p.space)
return {"status": "ok", "space": p.space, "persona": p.persona, "text": prompt_text}
@router.get("/devices")
async def get_devices():
return []
@router.post("/set_device")
async def set_device(p: DevicePayload):
return {"status": "ok"}
@router.get("/wake_word_status")
async def wake_status():
return {"status": "listening"}
from fastapi import UploadFile, File, BackgroundTasks
import os
import shutil
def process_offline_log_stt(file_path: str):
import asyncio
import logging
try:
from backend.voice.stt import STTPipeline
stt = STTPipeline()
with open(file_path, "rb") as f:
audio_bytes = f.read()
# Enqueue STT (wait for it as it's async)
text = asyncio.run(stt.transcribe(audio_bytes))
if text.strip():
logging.info(f"Offline Audio Parsed: {text}")
from backend.agent.react_agent import run_agent_pipeline
asyncio.run(run_agent_pipeline(text))
except Exception as e:
logging.error(f"Failed to process offline log STT: {e}")
@router.post("/upload_log")
async def upload_log(file: UploadFile = File(...), background_tasks: BackgroundTasks = BackgroundTasks()):
try:
sync_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "voice", "audio_logs", "phone_sync")
os.makedirs(sync_dir, exist_ok=True)
file_path = os.path.join(sync_dir, file.filename)
with open(file_path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
# Enqueue STT parsing for the offline log
background_tasks.add_task(process_offline_log_stt, file_path)
return {"status": "ok", "filename": file.filename, "message": "Successfully uploaded"}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
# ─── Β§0.4 JARVIS Voice Profile Preview & Test ────────────────────────────────
@router.get("/settings/preview/jarvis")
async def get_jarvis_voice_sample():
"""Returns current tuned JARVIS voice sample WAV for UI playback."""
import os
from fastapi.responses import FileResponse
app_data = os.environ.get("JARVIS_APP_DATA_DIR", ".")
sample_path = os.path.join(app_data, "voices", "jarvis.wav")
if os.path.exists(sample_path):
return FileResponse(sample_path, media_type="audio/wav")
raise HTTPException(status_code=404, detail="JARVIS voice sample not found. Place a jarvis.wav file in the voices directory.")
@router.post("/settings/test/jarvis")
async def test_jarvis_voice():
"""Synthesises a short test line through the full tuned Β§0.4 JARVIS pipeline."""
try:
from backend.voice.tts import TTSPipeline
tts = TTSPipeline()
# context="conversation" β†’ routes through XTTS with full tuned profile
audio_bytes = await tts.synthesize(
"Good evening, sir. All systems are fully operational.",
personality="jarvis",
language="en",
context="conversation"
)
from fastapi.responses import Response
return Response(content=audio_bytes, media_type="audio/wav")
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))