"""TTS provider: delegates to existing Memory Service /voice/tts. Memory Service accepts: JSON {text, voice, speed} Returns: StreamingResponse — audio/mpeg (MP3 bytes) Fabric contract output: {audio_b64, format, meta} """ import base64 import logging import os from typing import Any, Dict import httpx logger = logging.getLogger("provider.tts_memory_service") MEMORY_SERVICE_URL = os.getenv("MEMORY_SERVICE_URL", "http://memory-service:8000") MAX_TEXT_CHARS = int(os.getenv("TTS_MAX_TEXT_CHARS", "500")) # Memory Service limits to 500 async def synthesize(payload: Dict[str, Any]) -> Dict[str, Any]: """Fabric TTS entry point — delegates to Memory Service. Payload: text: str (required) voice: str (optional; Polina/Ostap/default/uk-UA-PolinaNeural/etc.) speed: float (optional, default 1.0) Returns Fabric contract: {audio_b64, format, meta, provider, model} Note: Memory Service uses edge-tts and returns MP3. No format conversion — caller receives base64-encoded MP3. """ text = payload.get("text", "").strip() if not text: raise ValueError("text is required") orig_len = len(text) truncated = orig_len > MAX_TEXT_CHARS if truncated: text = text[:MAX_TEXT_CHARS] logger.warning(f"TTS text truncated {orig_len} → {MAX_TEXT_CHARS} chars") voice = payload.get("voice", "default") speed = float(payload.get("speed", 1.0)) async with httpx.AsyncClient(timeout=30) as c: resp = await c.post( f"{MEMORY_SERVICE_URL}/voice/tts", json={"text": text, "voice": voice, "speed": speed}, ) resp.raise_for_status() audio_bytes = resp.content engine = resp.headers.get("X-TTS-Engine", "edge-tts") tts_voice = resp.headers.get("X-TTS-Voice", voice) content_type = resp.headers.get("content-type", "audio/mpeg") fmt = "mp3" if "mpeg" in content_type else "wav" audio_b64 = base64.b64encode(audio_bytes).decode() return { "audio_b64": audio_b64, "format": fmt, "meta": { "model": engine, "voice": tts_voice, "provider": "memory_service", "engine": engine, "audio_bytes": len(audio_bytes), "service_url": MEMORY_SERVICE_URL, "truncated": truncated, "orig_len": orig_len, "used_len": len(text), }, "provider": "memory_service", "model": engine, }