""" VoiceCoach pipeline entry point -- v2. Runs token server + pipeline with unique identity per session. """ import asyncio import logging import os import threading import uuid from pathlib import Path import structlog import uvicorn from fastapi import FastAPI from pydantic import BaseModel from livekit import api as livekit_api from pipeline import run_voice_coach_pipeline logging.basicConfig(level=logging.INFO) logger = structlog.get_logger() LIVEKIT_API_KEY = os.environ["LIVEKIT_API_KEY"] LIVEKIT_API_SECRET = os.environ["LIVEKIT_API_SECRET"] LIVEKIT_WS_URL = os.getenv("LIVEKIT_URL", "ws://livekit-server:7880") LIVEKIT_PUBLIC_URL = os.getenv("LIVEKIT_PUBLIC_URL", "wss://voice.hobbs.farm") OPENCLAW_TOKEN = os.getenv("OPENCLAW_TOKEN", "") # --- Token HTTP server --- token_app = FastAPI(title="VoiceCoach Token Service") class TokenRequest(BaseModel): identity: str = "jeremy" class TokenResponse(BaseModel): token: str url: str @token_app.get("/health") async def health(): return {"status": "ok", "version": "2.0"} @token_app.post("/token") async def get_token(req: TokenRequest): token = livekit_api.AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET) token.with_grants(livekit_api.VideoGrants( room_join=True, room="voicecoach", can_publish=True, can_subscribe=True, )) token.identity = req.identity return TokenResponse(token=token.to_jwt(), url=LIVEKIT_PUBLIC_URL) def run_token_server(): uvicorn.run(token_app, host="0.0.0.0", port=8080, log_level="info") # --- Main --- async def main(): server_thread = threading.Thread(target=run_token_server, daemon=True) server_thread.start() logger.info("Token server started on :8080") while True: agent_identity = f"voicecoach-agent-{uuid.uuid4().hex[:8]}" token = livekit_api.AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET) token.with_grants(livekit_api.VideoGrants(room_join=True, room="voicecoach")) token.identity = agent_identity livekit_token = token.to_jwt() logger.info("Starting VoiceCoach v2 pipeline", identity=agent_identity) try: await run_voice_coach_pipeline( livekit_url=LIVEKIT_WS_URL, livekit_token=livekit_token, openclaw_token=OPENCLAW_TOKEN, ) except Exception as e: logger.error("Pipeline error, restarting in 35s", error=str(e)) await asyncio.sleep(35) if __name__ == "__main__": asyncio.run(main())