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:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user