Migrate HumeTTSService to standard TTSSettings pattern and remove dead TTSService.update_setting

HumeTTSService now stores its params (description, speed, trailing_silence) in a proper `HumeTTSSettings` dataclass instead of a separate `_params` Pydantic model, making it work with `TTSUpdateSettingsFrame(update=...)`. The old `update_setting(key, value)` method is kept but deprecated.

Also removes the unused no-op `TTSService.update_setting` base method, which was never called by the `TTSUpdateSettingsFrame` pipeline.
This commit is contained in:
Paul Kompfner
2026-02-17 15:44:41 -05:00
parent 94a651cee2
commit 68ebd3d063
2 changed files with 60 additions and 32 deletions

View File

@@ -6,6 +6,8 @@
import base64 import base64
import os import os
import warnings
from dataclasses import dataclass, field
from typing import Any, AsyncGenerator, Optional from typing import Any, AsyncGenerator, Optional
import httpx import httpx
@@ -24,6 +26,7 @@ from pipecat.frames.frames import (
TTSStoppedFrame, TTSStoppedFrame,
) )
from pipecat.processors.frame_processor import FrameDirection from pipecat.processors.frame_processor import FrameDirection
from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven
from pipecat.services.tts_service import WordTTSService from pipecat.services.tts_service import WordTTSService
from pipecat.utils.tracing.service_decorators import traced_tts from pipecat.utils.tracing.service_decorators import traced_tts
@@ -46,6 +49,21 @@ DEFAULT_HEADERS = {
} }
@dataclass
class HumeTTSSettings(TTSSettings):
"""Settings for Hume TTS service.
Parameters:
description: Natural-language acting directions (up to 100 characters).
speed: Speaking-rate multiplier (0.5-2.0).
trailing_silence: Seconds of silence to append at the end (0-5).
"""
description: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
speed: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
trailing_silence: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
class HumeTTSService(WordTTSService): class HumeTTSService(WordTTSService):
"""Hume Octave Text-to-Speech service. """Hume Octave Text-to-Speech service.
@@ -61,6 +79,8 @@ class HumeTTSService(WordTTSService):
- Provides metrics for Time To First Byte (TTFB) and TTS usage. - Provides metrics for Time To First Byte (TTFB) and TTS usage.
""" """
_settings: HumeTTSSettings
class InputParams(BaseModel): class InputParams(BaseModel):
"""Optional synthesis parameters for Hume TTS. """Optional synthesis parameters for Hume TTS.
@@ -114,9 +134,14 @@ class HumeTTSService(WordTTSService):
self._http_client = httpx.AsyncClient(headers=DEFAULT_HEADERS) self._http_client = httpx.AsyncClient(headers=DEFAULT_HEADERS)
self._client = AsyncHumeClient(api_key=api_key, httpx_client=self._http_client) self._client = AsyncHumeClient(api_key=api_key, httpx_client=self._http_client)
self._params = params or HumeTTSService.InputParams()
# Store voice in the base class (mirrors other services) params = params or HumeTTSService.InputParams()
self._settings = HumeTTSSettings(
voice=voice_id,
description=params.description,
speed=params.speed,
trailing_silence=params.trailing_silence,
)
self._voice_id = voice_id self._voice_id = voice_id
self._audio_bytes = b"" self._audio_bytes = b""
@@ -183,7 +208,10 @@ class HumeTTSService(WordTTSService):
await self.add_word_timestamps([("Reset", 0)]) await self.add_word_timestamps([("Reset", 0)])
async def update_setting(self, key: str, value: Any) -> None: async def update_setting(self, key: str, value: Any) -> None:
"""Runtime updates via `TTSUpdateSettingsFrame`. """Runtime updates via key/value pair.
.. deprecated:: 0.0.103
Use ``TTSUpdateSettingsFrame(update=HumeTTSSettings(...))`` instead.
Args: Args:
key: The name of the setting to update. Recognized keys are: key: The name of the setting to update. Recognized keys are:
@@ -193,20 +221,29 @@ class HumeTTSService(WordTTSService):
- "trailing_silence" - "trailing_silence"
value: The new value for the setting. value: The new value for the setting.
""" """
key_l = (key or "").lower() with warnings.catch_warnings():
warnings.simplefilter("always")
warnings.warn(
"'update_setting' is deprecated, use "
"'TTSUpdateSettingsFrame(update=HumeTTSSettings(...))' instead.",
DeprecationWarning,
stacklevel=2,
)
if key_l == "voice_id": key_l = (key or "").lower()
await self.set_voice(str(value)) known_keys = {"voice_id", "voice", "description", "speed", "trailing_silence"}
logger.debug(f"HumeTTSService voice_id set to: {self.voice}")
elif key_l == "description": if key_l in known_keys:
self._params.description = None if value is None else str(value) kwargs: dict[str, Any] = {}
elif key_l == "speed": if key_l in ("voice_id", "voice"):
self._params.speed = None if value is None else float(value) kwargs["voice"] = str(value)
elif key_l == "trailing_silence": elif key_l == "description":
self._params.trailing_silence = None if value is None else float(value) kwargs["description"] = None if value is None else str(value)
else: elif key_l == "speed":
# Defer unknown keys to the base class kwargs["speed"] = None if value is None else float(value)
await super().update_setting(key, value) elif key_l == "trailing_silence":
kwargs["trailing_silence"] = None if value is None else float(value)
await self._update_settings(HumeTTSSettings(**kwargs))
@traced_tts @traced_tts
async def run_tts(self, text: str, context_id: str) -> AsyncGenerator[Frame, None]: async def run_tts(self, text: str, context_id: str) -> AsyncGenerator[Frame, None]:
@@ -228,12 +265,12 @@ class HumeTTSService(WordTTSService):
"text": text, "text": text,
"voice": PostedUtteranceVoiceWithId(id=self._voice_id), "voice": PostedUtteranceVoiceWithId(id=self._voice_id),
} }
if self._params.description is not None: if self._settings.description is not None:
utterance_kwargs["description"] = self._params.description utterance_kwargs["description"] = self._settings.description
if self._params.speed is not None: if self._settings.speed is not None:
utterance_kwargs["speed"] = self._params.speed utterance_kwargs["speed"] = self._settings.speed
if self._params.trailing_silence is not None: if self._settings.trailing_silence is not None:
utterance_kwargs["trailing_silence"] = self._params.trailing_silence utterance_kwargs["trailing_silence"] = self._settings.trailing_silence
utterance = PostedUtterance(**utterance_kwargs) utterance = PostedUtterance(**utterance_kwargs)
@@ -257,7 +294,7 @@ class HumeTTSService(WordTTSService):
# Use version "2" by default if no description is provided # Use version "2" by default if no description is provided
# Version "1" is needed when description is used # Version "1" is needed when description is used
version = "1" if self._params.description is not None else "2" version = "1" if self._settings.description is not None else "2"
# Track the duration of this utterance based on the last timestamp # Track the duration of this utterance based on the last timestamp
utterance_duration = 0.0 utterance_duration = 0.0

View File

@@ -352,15 +352,6 @@ class TTSService(AIService):
return text + " " return text + " "
return text return text
async def update_setting(self, key: str, value: Any):
"""Update a service-specific setting.
Args:
key: The setting key to update.
value: The new value for the setting.
"""
pass
async def flush_audio(self): async def flush_audio(self):
"""Flush any buffered audio data.""" """Flush any buffered audio data."""
pass pass