Spaces:
Sleeping
Sleeping
| 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() | |