From 251ea756c8f3199e2191a30124064af680b3d55e Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Thu, 3 Jul 2025 12:56:24 -0700 Subject: [PATCH 1/2] FishTTSService: deprecate model, add reference_id --- CHANGELOG.md | 12 ++++++--- src/pipecat/services/fish/tts.py | 45 +++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7d9cc05b..696dd99fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,8 +61,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `session_token` parameter to `AWSNovaSonicLLMService`. -- Added Gemini Multimodal Live File API for uploading, fetching, listing, and -deleting files. See `26f-gemini-multimodal-live-files-api.py` for example usage. +- Added Gemini Multimodal Live File API for uploading, fetching, listing, and + deleting files. See `26f-gemini-multimodal-live-files-api.py` for example usage. ### Changed @@ -75,7 +75,7 @@ deleting files. See `26f-gemini-multimodal-live-files-api.py` for example usage. ### Fixed -- Fixed an issue where audio would get stuck in the queue when an interrupt occurs +- Fixed an issue where audio would get stuck in the queue when an interrupt occurs during Azure TTS synthesis. - Fixed a race condition that occurs in Python 3.10+ where the task could miss @@ -83,6 +83,12 @@ deleting files. See `26f-gemini-multimodal-live-files-api.py` for example usage. - Fixed a `AWSNovaSonicLLMService` issue introduced in 0.0.72. +### Deprecated + +- In `FishTTSService`, deprecated `model` and replaced with `reference_id`. + This change is to better align with Fish Audio's variable naming and to + reduce confusion about what functionality the variable controls. + ## [0.0.73] - 2025-06-26 ### Fixed diff --git a/src/pipecat/services/fish/tts.py b/src/pipecat/services/fish/tts.py index 7f2b85bdb..9d5af657e 100644 --- a/src/pipecat/services/fish/tts.py +++ b/src/pipecat/services/fish/tts.py @@ -71,7 +71,8 @@ class FishAudioTTSService(InterruptibleTTSService): self, *, api_key: str, - model: str, # This is the reference_id + reference_id: Optional[str] = None, # This is the voice ID + model: Optional[str] = None, # Deprecated output_format: FishAudioOutputFormat = "pcm", sample_rate: Optional[int] = None, params: Optional[InputParams] = None, @@ -81,7 +82,13 @@ class FishAudioTTSService(InterruptibleTTSService): Args: api_key: Fish Audio API key for authentication. - model: Reference ID of the voice model to use for synthesis. + reference_id: Reference ID of the voice model to use for synthesis. + model: Deprecated. Reference ID of the voice model to use for synthesis. + + .. deprecated:: 0.0.74 + The `model` parameter is deprecated and will be removed in version 0.1.0. + Use `reference_id` instead to specify the voice model. + output_format: Audio output format. Defaults to "pcm". sample_rate: Audio sample rate. If None, uses default. params: Additional input parameters for voice customization. @@ -96,6 +103,26 @@ class FishAudioTTSService(InterruptibleTTSService): params = params or FishAudioTTSService.InputParams() + # Validation for model and reference_id parameters + if model and reference_id: + raise ValueError( + "Cannot specify both 'model' and 'reference_id'. Use 'reference_id' only." + ) + + if model is None and reference_id is None: + raise ValueError("Must specify 'reference_id' (or deprecated 'model') parameter.") + + if model: + import warnings + + warnings.warn( + "Parameter 'model' is deprecated and will be removed in a future version. " + "Use 'reference_id' instead.", + DeprecationWarning, + stacklevel=2, + ) + reference_id = model + self._api_key = api_key self._base_url = "wss://api.fish.audio/v1/tts/live" self._websocket = None @@ -111,11 +138,9 @@ class FishAudioTTSService(InterruptibleTTSService): "speed": params.prosody_speed, "volume": params.prosody_volume, }, - "reference_id": model, + "reference_id": reference_id, } - self.set_model_name(model) - def can_generate_metrics(self) -> bool: """Check if this service can generate processing metrics. @@ -124,16 +149,6 @@ class FishAudioTTSService(InterruptibleTTSService): """ return True - async def set_model(self, model: str): - """Set the TTS model (reference ID). - - Args: - model: The reference ID of the voice model to use. - """ - self._settings["reference_id"] = model - await super().set_model(model) - logger.info(f"Switching TTS model to: [{model}]") - async def start(self, frame: StartFrame): """Start the Fish Audio TTS service. From ec09505f6bed113a6b64586e68d5884c550c1af3 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Thu, 3 Jul 2025 13:14:15 -0700 Subject: [PATCH 2/2] FishAudioTTSService: Add normalize as InputParam, model_id as arg --- CHANGELOG.md | 9 ++++++--- src/pipecat/services/fish/tts.py | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 696dd99fd..79b9c04f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added `normalize` and `model_id` to `FishAudioTTSService`. + - Added `run_llm` field to `LLMMessagesAppendFrame` and `LLMMessagesUpdateFrame` frames. If true, a context frame will be pushed triggering the LLM to respond. @@ -85,9 +87,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Deprecated -- In `FishTTSService`, deprecated `model` and replaced with `reference_id`. - This change is to better align with Fish Audio's variable naming and to - reduce confusion about what functionality the variable controls. +- In `FishAudioTTSService`, deprecated `model` and replaced with + `reference_id`. This change is to better align with Fish Audio's variable + naming and to reduce confusion about what functionality the variable + controls. ## [0.0.73] - 2025-06-26 diff --git a/src/pipecat/services/fish/tts.py b/src/pipecat/services/fish/tts.py index 9d5af657e..598bac0f3 100644 --- a/src/pipecat/services/fish/tts.py +++ b/src/pipecat/services/fish/tts.py @@ -58,12 +58,14 @@ class FishAudioTTSService(InterruptibleTTSService): Parameters: language: Language for synthesis. Defaults to English. latency: Latency mode ("normal" or "balanced"). Defaults to "normal". + normalize: Whether to normalize audio output. Defaults to True. prosody_speed: Speech speed multiplier (0.5-2.0). Defaults to 1.0. prosody_volume: Volume adjustment in dB. Defaults to 0. """ language: Optional[Language] = Language.EN latency: Optional[str] = "normal" # "normal" or "balanced" + normalize: Optional[bool] = True prosody_speed: Optional[float] = 1.0 # Speech speed (0.5-2.0) prosody_volume: Optional[int] = 0 # Volume adjustment in dB @@ -73,6 +75,7 @@ class FishAudioTTSService(InterruptibleTTSService): api_key: str, reference_id: Optional[str] = None, # This is the voice ID model: Optional[str] = None, # Deprecated + model_id: str = "speech-1.5", output_format: FishAudioOutputFormat = "pcm", sample_rate: Optional[int] = None, params: Optional[InputParams] = None, @@ -89,6 +92,7 @@ class FishAudioTTSService(InterruptibleTTSService): The `model` parameter is deprecated and will be removed in version 0.1.0. Use `reference_id` instead to specify the voice model. + model_id: Specify which Fish Audio TTS model to use (e.g. "speech-1.5") output_format: Audio output format. Defaults to "pcm". sample_rate: Audio sample rate. If None, uses default. params: Additional input parameters for voice customization. @@ -134,6 +138,7 @@ class FishAudioTTSService(InterruptibleTTSService): "sample_rate": 0, "latency": params.latency, "format": output_format, + "normalize": params.normalize, "prosody": { "speed": params.prosody_speed, "volume": params.prosody_volume, @@ -141,6 +146,8 @@ class FishAudioTTSService(InterruptibleTTSService): "reference_id": reference_id, } + self.set_model_name(model_id) + def can_generate_metrics(self) -> bool: """Check if this service can generate processing metrics. @@ -149,6 +156,17 @@ class FishAudioTTSService(InterruptibleTTSService): """ return True + async def set_model(self, model: str): + """Set the TTS model and reconnect. + + Args: + model: The model name to use for synthesis. + """ + await super().set_model(model) + logger.info(f"Switching TTS model to: [{model}]") + await self._disconnect() + await self._connect() + async def start(self, frame: StartFrame): """Start the Fish Audio TTS service. @@ -197,6 +215,7 @@ class FishAudioTTSService(InterruptibleTTSService): logger.debug("Connecting to Fish Audio") headers = {"Authorization": f"Bearer {self._api_key}"} + headers["model"] = self.model_name self._websocket = await websockets.connect(self._base_url, extra_headers=headers) # Send initial start message with ormsgpack