feat: connect Shane voice sessions to memory

This commit is contained in:
snowspeeder committed 2026-08-28 16:22:18 -04:00
1 parent 2a2a2ff768
commit c940923539
15 files changed
+201 -97

No files matched your search

+36 -5
View File
@@ -23,6 +23,17 @@ from http.server import BaseHTTPRequestHandler, HTTPServer
API_KEY = os.environ.get("LIVEKIT_API_KEY", "devkey")
API_SECRET = os.environ.get("LIVEKIT_API_SECRET", "devsecret")
AGENT_NAME = os.environ.get("VOICE_AGENT_NAME", "voice-assistant")
USERS_FILE = os.environ.get("VOICE_USERS_FILE", "/etc/hope/users.json")
DEFAULT_USER = os.environ.get("VOICE_DEFAULT_USER", "shane")
def load_default_user() -> tuple[str, dict]:
with open(USERS_FILE, encoding="utf-8") as users_file:
registry = json.load(users_file)
profile = registry.get("users", {}).get(DEFAULT_USER)
if not isinstance(profile, dict):
raise ValueError(f"unknown default voice user: {DEFAULT_USER}")
return DEFAULT_USER, profile
def b64url(data: bytes) -> str:
@@ -65,13 +76,33 @@ class Handler(BaseHTTPRequestHandler):
return
length = int(self.headers.get("Content-Length", 0))
body = json.loads(self.rfile.read(length) or b"{}")
# Unique room per session so LiveKit dispatches the agent at creation.
room_name = f"voice-{uuid.uuid4().hex[:8]}"
identity = body.get("identity") or f"user-{uuid.uuid4().hex[:8]}"
# Consume the body for protocol compatibility, but never trust a
# browser-supplied room or identity. This LAN bootstrap deployment is
# pinned to VOICE_DEFAULT_USER until the planned login flow is added.
self.rfile.read(length)
try:
user_id, profile = load_default_user()
except (OSError, ValueError, json.JSONDecodeError) as exc:
resp = json.dumps({"error": str(exc)}).encode()
self.send_response(503)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(resp)))
self.end_headers()
self.wfile.write(resp)
return
room_name = f"voice-{user_id}-{uuid.uuid4().hex[:8]}"
identity = profile.get("livekit_identity") or f"user:{user_id}"
token = make_token(room_name, identity)
resp = json.dumps({"token": token}).encode()
resp = json.dumps(
{
"token": token,
"user": {
"id": user_id,
"display_name": profile.get("display_name") or user_id,
},
}
).encode()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(resp)))