"""Local LiveKit agent that calls the HF Space TTS server. Prerequisites: pip install livekit livekit-agents Usage: python livekit_agent.py """ from __future__ import annotations import asyncio import logging import os import uuid from typing import Optional import aiohttp from livekit import rtc from livekit.agents import ( Agent, AgentSession, JobContext, WorkerOptions, cli, ) logger = logging.getLogger("sauti-agent") TTS_URL = os.getenv("TTS_URL", "http://localhost:7860") DEFAULT_REF = os.getenv("DEFAULT_REF_AUDIO", "") class SautiAgent(Agent): """Swahili voice agent wrapping Sauti TTS.""" def __init__(self): super().__init__( instructions=( "You are a helpful Swahili-speaking assistant. " "Keep replies short (1-2 sentences) to keep TTS latency low." ) ) self._session: Optional[aiohttp.ClientSession] = None async def _tts(self, text: str) -> Optional[bytes]: if not self._session: self._session = aiohttp.ClientSession() async with self._session.post( f"{TTS_URL}/tts", json={ "text": text, "ref_text": "", "steps": 10, "cfg": 1.5, "speed": 1.0, }, timeout=aiohttp.ClientTimeout(total=60), ) as resp: if resp.status != 200: logger.error("TTS failed: %s", await resp.text()) return None data = await resp.json() path = data["audio_path"] with open(path, "rb") as f: return f.read() async def say(self, text: str): audio = await self._tts(text) if not audio: return await self.session.output_stream.say(audio) async def run(ctx: JobContext): await ctx.connect() session = AgentSession() await session.start(agent=SautiAgent(), room=ctx.room) def main(): cli.run_app(WorkerOptions(entrypoint_fnc=run)) if __name__ == "__main__": main()