""" Entry point for the Pipecat VoiceCoach agent. Runs both the token HTTP server (for web clients) and the voice pipeline. """ import asyncio import logging import os import threading 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") # --- Token HTTP server (serves /token for web clients) --- 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"} @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 pipeline --- async def main(): anthropic_key = os.environ["ANTHROPIC_API_KEY"] # Start token server in a background thread server_thread = threading.Thread(target=run_token_server, daemon=True) server_thread.start() logger.info("Token server started on :8080") # Generate agent's own LiveKit token token = livekit_api.AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET) token.with_grants(livekit_api.VideoGrants(room_join=True, room="voicecoach")) token.identity = "voicecoach-agent" livekit_token = token.to_jwt() session_context = { "baseline_context": "", "session_history": "", "current_mode": "coaching mode", } logger.info("Starting VoiceCoach pipeline", livekit_url=LIVEKIT_WS_URL) await run_voice_coach_pipeline( livekit_url=LIVEKIT_WS_URL, livekit_token=livekit_token, anthropic_key=anthropic_key, session_context=session_context, ) if __name__ == "__main__": asyncio.run(main())