From e5fc806ccc2de4c1a1fcd7558c4b6ba5762217eb Mon Sep 17 00:00:00 2001 From: Moeadham Date: Tue, 28 Jul 2026 09:29:20 -0700 Subject: [PATCH] fix(tts): support configurable ElevenLabs URLs Salvaged from PR #66311 (@moeadham), rebased onto the current streaming registry. tts.elevenlabs.base_url (+ optional wss_url, derived from base_url when omitted) routes both the sync ElevenLabs path and the chunked ElevenLabsStreamer through an ElevenLabsEnvironment, matching the STT side's ELEVENLABS_STT_BASE_URL/config override pattern. --- tools/tts_streaming.py | 6 +++++- tools/tts_tool.py | 20 +++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/tools/tts_streaming.py b/tools/tts_streaming.py index 7753a8ccf69d..9d772da71d50 100644 --- a/tools/tts_streaming.py +++ b/tools/tts_streaming.py @@ -178,10 +178,14 @@ class ElevenLabsStreamer(StreamingTTSProvider): from tools.tts_tool import ( DEFAULT_ELEVENLABS_STREAMING_MODEL_ID, DEFAULT_ELEVENLABS_VOICE_ID, + _elevenlabs_environment_kwargs, _import_elevenlabs, ) - client = _import_elevenlabs()(api_key=get_env_value("ELEVENLABS_API_KEY")) + client = _import_elevenlabs()( + api_key=get_env_value("ELEVENLABS_API_KEY"), + **_elevenlabs_environment_kwargs(self.section), + ) voice_id = self.section.get("voice_id", DEFAULT_ELEVENLABS_VOICE_ID) model_id = self.section.get( "streaming_model_id", diff --git a/tools/tts_tool.py b/tools/tts_tool.py index 8edbe4af1b16..5dd8d3d0922d 100644 --- a/tools/tts_tool.py +++ b/tools/tts_tool.py @@ -133,6 +133,24 @@ def _import_elevenlabs(): from elevenlabs.client import ElevenLabs return ElevenLabs + +def _elevenlabs_environment_kwargs(el_config: Dict[str, Any]) -> Dict[str, Any]: + """Build ElevenLabs client kwargs honoring config base_url/wss_url. + + ``tts.elevenlabs.base_url`` (and optionally ``wss_url``) redirect the SDK + to a self-hosted / proxy endpoint via an ``ElevenLabsEnvironment``. When + neither is set the SDK default environment is used. ``wss_url`` defaults + to the ``base_url`` host with a ``wss://`` scheme when omitted. + """ + base_url = (el_config.get("base_url") or "").rstrip("/") + if not base_url: + return {} + wss_url = (el_config.get("wss_url") or "").rstrip("/") + if not wss_url: + wss_url = re.sub(r"^http", "ws", base_url) + from elevenlabs.environment import ElevenLabsEnvironment + return {"environment": ElevenLabsEnvironment(base=base_url, wss=wss_url)} + def _import_openai_client(): """Lazy import OpenAI client. Returns the class or raises ImportError.""" from openai import OpenAI as OpenAIClient @@ -1124,7 +1142,7 @@ def _generate_elevenlabs(text: str, output_path: str, tts_config: Dict[str, Any] output_format = "mp3_44100_128" ElevenLabs = _import_elevenlabs() - client = ElevenLabs(api_key=api_key) + client = ElevenLabs(api_key=api_key, **_elevenlabs_environment_kwargs(el_config)) audio_generator = client.text_to_speech.convert( text=text, voice_id=voice_id,