Merge pull request #2470 from pipecat-ai/mb/11labs-settings-reconnect
Update ElevenLabsTTSService: update runtime configuration
This commit is contained in:
@@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- `ElevenLabsTTSService` now supports additional runtime changes to the `model`,
|
||||||
|
`language`, and `voice_settings` parameters.
|
||||||
|
|
||||||
- Added `MistralLLMService`, using Mistral's chat completion API.
|
- Added `MistralLLMService`, using Mistral's chat completion API.
|
||||||
|
|
||||||
- Added the ability to retry executing a chat completion after a timeout period
|
- Added the ability to retry executing a chat completion after a timeout period
|
||||||
|
|||||||
@@ -370,13 +370,49 @@ class ElevenLabsTTSService(AudioContextWordTTSService):
|
|||||||
await self._connect()
|
await self._connect()
|
||||||
|
|
||||||
async def _update_settings(self, settings: Mapping[str, Any]):
|
async def _update_settings(self, settings: Mapping[str, Any]):
|
||||||
"""Update service settings and reconnect if voice changed."""
|
"""Update service settings and reconnect if voice, model, or language changed."""
|
||||||
|
# Track previous values for settings that require reconnection
|
||||||
prev_voice = self._voice_id
|
prev_voice = self._voice_id
|
||||||
|
prev_model = self.model_name
|
||||||
|
prev_language = self._settings.get("language")
|
||||||
|
# Create snapshot of current voice settings to detect changes after update
|
||||||
|
prev_voice_settings = self._voice_settings.copy() if self._voice_settings else None
|
||||||
|
|
||||||
await super()._update_settings(settings)
|
await super()._update_settings(settings)
|
||||||
if not prev_voice == self._voice_id:
|
|
||||||
logger.info(f"Switching TTS voice to: [{self._voice_id}]")
|
# Update voice settings for the next context creation
|
||||||
|
self._voice_settings = self._set_voice_settings()
|
||||||
|
|
||||||
|
# Check if URL-level settings changed (these require reconnection)
|
||||||
|
url_changed = (
|
||||||
|
prev_voice != self._voice_id
|
||||||
|
or prev_model != self.model_name
|
||||||
|
or prev_language != self._settings.get("language")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if only voice settings changed (speed, stability, etc.)
|
||||||
|
voice_settings_changed = prev_voice_settings != self._voice_settings
|
||||||
|
|
||||||
|
if url_changed:
|
||||||
|
# These settings are in the WebSocket URL, so we need to reconnect
|
||||||
|
logger.debug(
|
||||||
|
f"URL-level setting changed (voice/model/language), reconnecting WebSocket"
|
||||||
|
)
|
||||||
await self._disconnect()
|
await self._disconnect()
|
||||||
await self._connect()
|
await self._connect()
|
||||||
|
elif voice_settings_changed and self._context_id:
|
||||||
|
# Voice settings can be updated by closing current context
|
||||||
|
# so new one gets created with updated voice settings
|
||||||
|
logger.debug(f"Voice settings changed, closing current context to apply changes")
|
||||||
|
try:
|
||||||
|
if self._websocket:
|
||||||
|
await self._websocket.send(
|
||||||
|
json.dumps({"context_id": self._context_id, "close_context": True})
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Error closing context for voice settings update: {e}")
|
||||||
|
self._context_id = None
|
||||||
|
self._started = False
|
||||||
|
|
||||||
async def start(self, frame: StartFrame):
|
async def start(self, frame: StartFrame):
|
||||||
"""Start the ElevenLabs TTS service.
|
"""Start the ElevenLabs TTS service.
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ class TTSService(AIService):
|
|||||||
self._settings[key] = self.language_to_service_language(value)
|
self._settings[key] = self.language_to_service_language(value)
|
||||||
elif key == "model":
|
elif key == "model":
|
||||||
self.set_model_name(value)
|
self.set_model_name(value)
|
||||||
elif key == "voice":
|
elif key == "voice" or key == "voice_id":
|
||||||
self.set_voice(value)
|
self.set_voice(value)
|
||||||
elif key == "text_filter":
|
elif key == "text_filter":
|
||||||
for filter in self._text_filters:
|
for filter in self._text_filters:
|
||||||
|
|||||||
Reference in New Issue
Block a user