feat: add Hermes WebUI provisioning and English Dragon HD Omni voices
Rebuild & Deploy / rebuild (push) Successful in 2m17s
Rebuild & Deploy / rebuild (push) Successful in 2m17s
This commit is contained in:
1 parent
47cc9c88a0
commit
6b8d55fc92
59 files changed
+1175
-70
No files matched your search
@@ -12,6 +12,11 @@ import httpx
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
try:
|
||||
from web.voice_catalog import catalog
|
||||
except ModuleNotFoundError: # Direct execution: python /app/web/token_server.py
|
||||
from voice_catalog import catalog
|
||||
|
||||
API_KEY = os.getenv("LIVEKIT_API_KEY", "")
|
||||
API_SECRET = os.getenv("LIVEKIT_API_SECRET", "")
|
||||
AGENT_NAME = os.getenv("VOICE_AGENT_NAME", "voice-assistant")
|
||||
@@ -75,6 +80,42 @@ async def token(request: Request):
|
||||
except (httpx.HTTPError, ValueError, KeyError):
|
||||
return JSONResponse({"error": "Account service unavailable"}, 503, headers=headers)
|
||||
|
||||
@app.get("/voices")
|
||||
async def voices(request: Request):
|
||||
"""Resolve the caller at the trusted control boundary before reading the cache."""
|
||||
headers = {"Cache-Control": "no-store"}
|
||||
if not CONTROL_SECRET:
|
||||
return JSONResponse({"error": "Service configuration unavailable"}, 503, headers=headers)
|
||||
auth = request.headers.get("authorization", "")
|
||||
bearer = auth[7:] if auth.startswith("Bearer ") else ""
|
||||
credential = bearer or request.cookies.get(COOKIE_NAME, "")
|
||||
if not credential or len(credential) > 512:
|
||||
return JSONResponse({"error": "Sign in required"}, 401, headers=headers)
|
||||
# Safe GET requests normally have no Origin. If supplied with cookie auth,
|
||||
# enforce the same configured origin allowlist as the bootstrap boundary.
|
||||
origin = request.headers.get("origin", "")
|
||||
if not bearer and origin:
|
||||
allowed = set(filter(None, os.getenv("CONTROL_ALLOWED_ORIGINS", "").split(",")))
|
||||
if origin not in allowed:
|
||||
return JSONResponse({"error": "Invalid request origin"}, 403, headers=headers)
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=10) as client:
|
||||
response = await client.post(f"{CONTROL_URL}/internal/resolve-session",
|
||||
headers={"X-Control-Secret": CONTROL_SECRET},
|
||||
json={"token": credential})
|
||||
if response.status_code != 200:
|
||||
status = response.status_code if response.status_code in (401, 403, 503) else 503
|
||||
return JSONResponse({"error": "Sign in required" if status == 401 else
|
||||
"Your private agent is not ready"}, status, headers=headers)
|
||||
# Verify the expected control response without exposing private metadata.
|
||||
account = response.json()
|
||||
if not isinstance(account, dict) or not isinstance(account.get("user"), dict) or not account["user"].get("id"):
|
||||
return JSONResponse({"error": "Account service unavailable"}, 503, headers=headers)
|
||||
except (httpx.HTTPError, ValueError, TypeError):
|
||||
return JSONResponse({"error": "Account service unavailable"}, 503, headers=headers)
|
||||
return JSONResponse(await catalog.get(), headers=headers)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="127.0.0.1", port=8091, access_log=False)
|
||||
Reference in new issue
Block a user