import asyncio import os import httpx from aiohttp import web from livekit import api from livekit.agents import Agent, AgentSession, JobContext, WorkerOptions, cli from livekit.plugins import openai, silero, google async def entrypoint(ctx: JobContext): await ctx.connect() # 1. Grab Secrets hf_token = os.getenv("HF_TOKEN") # 2. Setup ultra-patient timeout for Hugging Face Free Tier custom_client = httpx.AsyncClient(timeout=httpx.Timeout(90.0)) try: # 3. Initialize Session (REPLACE YOUR_USER AND SPACE NAMES BELOW) session = AgentSession( vad=silero.VAD.load(), stt=openai.STT( base_url="https://abc1181-livekit-stt.hf.space/v1", api_key=hf_token, http_client=custom_client ), llm=google.LLM(model="gemini-2.5-flash"), tts=openai.TTS( base_url="https://abc1181-livekit-tts.hf.space", api_key=hf_token, http_client=custom_client ), ) agent = Agent(instructions="You are Cortana. Keep your answers to one sentence.") # 4. Start the session await session.start(agent=agent, room=ctx.room) # 5. The absolute proof that the TTS and Agent are working await session.say("Systems online. I am ready.", allow_interruptions=True) except Exception as e: print(f"CRITICAL ERROR IN AGENT: {e}") # 6. Generate a Token dynamically based on the room the frontend asks for async def generate_token(request): try: # 1. Get Secrets with error handling api_key = os.getenv("LIVEKIT_API_KEY") api_secret = os.getenv("LIVEKIT_API_SECRET") livekit_url = os.getenv("LIVEKIT_URL") # Check if any secret is missing to avoid the 500 crash missing = [k for k, v in { "LIVEKIT_API_KEY": api_key, "LIVEKIT_API_SECRET": api_secret, "LIVEKIT_URL": livekit_url }.items() if not v] if missing: return web.json_response( {"error": f"Missing secrets: {', '.join(missing)}"}, status_code=400 ) # 2. Get params from the React Frontend room_name = request.query.get("room", "voice_assistant_room") identity = request.query.get("participantName", "replit_user") # 3. Create the Token token = api.AccessToken(api_key, api_secret) \ .with_identity(identity) \ .with_grants(api.VideoGrants(room_join=True, room=room_name)) \ .to_jwt() # 4. Return the exact JSON format React expects data = { "serverUrl": livekit_url, "participantToken": token } print(f"DEBUG: Successfully generated token for room {room_name}") return web.json_response(data, headers={"Access-Control-Allow-Origin": "*"}) except Exception as e: print(f"ERROR in generate_token: {e}") return web.json_response({"error": str(e)}, status_code=500) def run_worker(): app = web.Application() app.router.add_get("/", lambda r: web.Response(text="Agent Online")) # The React repo hits this exact path: app.router.add_get("/api/connection-details", generate_token) # ... (rest of your start_web and cli.run_app logic) async def start_web(): runner = web.AppRunner(app) await runner.setup() await web.TCPSite(runner, "0.0.0.0", 7860).start() loop = asyncio.get_event_loop() loop.run_until_complete(start_web()) cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint)) if __name__ == "__main__": run_worker()