import os import tempfile import uuid from pathlib import Path from urllib.parse import urlparse DEFAULT_TTS_URL = "https://veronicaulises0--virtual-characters-tts-charactertts-tts.modal.run" def synthesize_sentence(text: str, character: dict, voice_state: dict | None = None) -> str | None: url = _tts_endpoint_url(os.environ.get("VC_MODAL_TTS_URL") or DEFAULT_TTS_URL) if not url or not text.strip(): return None import httpx voice = character.get("voice", {}) voice_state = voice_state or {} payload = { "text": text, "voice_id": voice_state.get("voice_id") or voice.get("voice_id", "default"), "emotion": voice_state.get("emotion") or voice_state.get("style") or "neutral", "speed": voice_state.get("speed", 1.0), "energy": voice_state.get("energy", voice.get("energy", 0.5)), "audio_prompt_path": voice_state.get("audio_prompt_path") or voice.get("audio_prompt_path"), "backend": voice.get("backend", "chatterbox"), } response = httpx.post(url, json=payload, timeout=180, trust_env=False) response.raise_for_status() path = Path(tempfile.gettempdir()) / f"virtual_characters_tts_{uuid.uuid4().hex}.wav" path.write_bytes(response.content) return str(path) def _tts_endpoint_url(url: str | None) -> str | None: if not url: return None base = url.rstrip("/") parsed = urlparse(base) if not parsed.path or parsed.path == "/": return base if parsed.path.rstrip("/").rsplit("/", 1)[-1] == "tts": return base return base + "/tts"