Remove deprecated text_aggregator and text_filter params from TTS
Remove the deprecated text_aggregator parameter from TTSService, CartesiaTTSService, and RimeTTSService, and the deprecated text_filter parameter from TTSService. Users should use LLMTextProcessor before the TTS service instead. Update the voice-switching example to use LLMTextProcessor with PatternPairAggregator.
This commit is contained in:
@@ -45,7 +45,7 @@ from dotenv import load_dotenv
|
|||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.frames.frames import LLMRunFrame
|
from pipecat.frames.frames import LLMRunFrame, TTSUpdateSettingsFrame
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
@@ -54,6 +54,7 @@ from pipecat.processors.aggregators.llm_response_universal import (
|
|||||||
LLMContextAggregatorPair,
|
LLMContextAggregatorPair,
|
||||||
LLMUserAggregatorParams,
|
LLMUserAggregatorParams,
|
||||||
)
|
)
|
||||||
|
from pipecat.processors.aggregators.llm_text_processor import LLMTextProcessor
|
||||||
from pipecat.runner.types import RunnerArguments
|
from pipecat.runner.types import RunnerArguments
|
||||||
from pipecat.runner.utils import create_transport
|
from pipecat.runner.utils import create_transport
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
@@ -100,39 +101,43 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments):
|
|||||||
logger.info(f"Starting bot")
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
# Create pattern pair aggregator for voice switching
|
# Create pattern pair aggregator for voice switching
|
||||||
pattern_aggregator = PatternPairAggregator()
|
llm_text_aggregator = PatternPairAggregator()
|
||||||
|
|
||||||
# Add pattern for voice switching
|
# Add pattern for voice switching
|
||||||
pattern_aggregator.add_pattern(
|
llm_text_aggregator.add_pattern(
|
||||||
type="voice",
|
type="voice",
|
||||||
start_pattern="<voice>",
|
start_pattern="<voice>",
|
||||||
end_pattern="</voice>",
|
end_pattern="</voice>",
|
||||||
action=MatchAction.REMOVE, # Remove tags from final text
|
action=MatchAction.AGGREGATE,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Register handler for voice switching
|
# Register handler for voice switching
|
||||||
async def on_voice_tag(match: PatternMatch):
|
async def on_voice_tag(match: PatternMatch):
|
||||||
voice_name = match.text.strip().lower()
|
voice_name = match.text.strip().lower()
|
||||||
if voice_name in VOICE_IDS:
|
if voice_name in VOICE_IDS:
|
||||||
# First flush any existing audio to finish the current context
|
await llm_text_processor.push_frame(
|
||||||
await tts.flush_audio()
|
TTSUpdateSettingsFrame(
|
||||||
# Then set the new voice
|
delta=CartesiaTTSService.Settings(voice=VOICE_IDS[voice_name])
|
||||||
await tts.set_voice(VOICE_IDS[voice_name])
|
)
|
||||||
|
)
|
||||||
logger.info(f"Switched to {voice_name} voice")
|
logger.info(f"Switched to {voice_name} voice")
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Unknown voice: {voice_name}")
|
logger.warning(f"Unknown voice: {voice_name}")
|
||||||
|
|
||||||
pattern_aggregator.on_pattern_match("voice", on_voice_tag)
|
llm_text_aggregator.on_pattern_match("voice", on_voice_tag)
|
||||||
|
|
||||||
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
|
|
||||||
|
# Process LLM text through the pattern aggregator before TTS
|
||||||
|
llm_text_processor = LLMTextProcessor(text_aggregator=llm_text_aggregator)
|
||||||
|
|
||||||
# Initialize TTS with narrator voice as default
|
# Initialize TTS with narrator voice as default
|
||||||
tts = CartesiaTTSService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
settings=CartesiaTTSService.Settings(
|
settings=CartesiaTTSService.Settings(
|
||||||
voice=VOICE_IDS["narrator"],
|
voice=VOICE_IDS["narrator"],
|
||||||
),
|
),
|
||||||
text_aggregator=pattern_aggregator,
|
skip_aggregator_types=["voice"], # Skip voice tags in TTS speech
|
||||||
)
|
)
|
||||||
|
|
||||||
# System prompt for storytelling with voice switching
|
# System prompt for storytelling with voice switching
|
||||||
@@ -204,7 +209,8 @@ Remember: Use narrator voice for EVERYTHING except the actual quoted dialogue.""
|
|||||||
stt,
|
stt,
|
||||||
user_aggregator,
|
user_aggregator,
|
||||||
llm,
|
llm,
|
||||||
tts, # TTS with pattern aggregator
|
llm_text_processor,
|
||||||
|
tts,
|
||||||
transport.output(),
|
transport.output(),
|
||||||
assistant_aggregator,
|
assistant_aggregator,
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ from pipecat.frames.frames import (
|
|||||||
from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven
|
from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven
|
||||||
from pipecat.services.tts_service import TextAggregationMode, TTSService, WebsocketTTSService
|
from pipecat.services.tts_service import TextAggregationMode, TTSService, WebsocketTTSService
|
||||||
from pipecat.transcriptions.language import Language, resolve_language
|
from pipecat.transcriptions.language import Language, resolve_language
|
||||||
from pipecat.utils.text.base_text_aggregator import BaseTextAggregator
|
|
||||||
from pipecat.utils.text.skip_tags_aggregator import SkipTagsAggregator
|
from pipecat.utils.text.skip_tags_aggregator import SkipTagsAggregator
|
||||||
from pipecat.utils.tracing.service_decorators import traced_tts
|
from pipecat.utils.tracing.service_decorators import traced_tts
|
||||||
|
|
||||||
@@ -240,7 +239,6 @@ class CartesiaTTSService(WebsocketTTSService):
|
|||||||
container: str = "raw",
|
container: str = "raw",
|
||||||
params: Optional[InputParams] = None,
|
params: Optional[InputParams] = None,
|
||||||
settings: Optional[Settings] = None,
|
settings: Optional[Settings] = None,
|
||||||
text_aggregator: Optional[BaseTextAggregator] = None,
|
|
||||||
text_aggregation_mode: Optional[TextAggregationMode] = None,
|
text_aggregation_mode: Optional[TextAggregationMode] = None,
|
||||||
aggregate_sentences: Optional[bool] = None,
|
aggregate_sentences: Optional[bool] = None,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
@@ -271,11 +269,6 @@ class CartesiaTTSService(WebsocketTTSService):
|
|||||||
|
|
||||||
settings: Runtime-updatable settings. When provided alongside deprecated
|
settings: Runtime-updatable settings. When provided alongside deprecated
|
||||||
parameters, ``settings`` values take precedence.
|
parameters, ``settings`` values take precedence.
|
||||||
text_aggregator: Custom text aggregator for processing input text.
|
|
||||||
|
|
||||||
.. deprecated:: 0.0.95
|
|
||||||
Use an LLMTextProcessor before the TTSService for custom text aggregation.
|
|
||||||
|
|
||||||
text_aggregation_mode: How to aggregate incoming text before synthesis.
|
text_aggregation_mode: How to aggregate incoming text before synthesis.
|
||||||
aggregate_sentences: Whether to aggregate sentences within the TTSService.
|
aggregate_sentences: Whether to aggregate sentences within the TTSService.
|
||||||
|
|
||||||
@@ -337,20 +330,18 @@ class CartesiaTTSService(WebsocketTTSService):
|
|||||||
pause_frame_processing=False,
|
pause_frame_processing=False,
|
||||||
sample_rate=sample_rate,
|
sample_rate=sample_rate,
|
||||||
push_start_frame=True,
|
push_start_frame=True,
|
||||||
text_aggregator=text_aggregator,
|
|
||||||
settings=default_settings,
|
settings=default_settings,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not text_aggregator:
|
# Always skip tags added for spelled-out text
|
||||||
# Always skip tags added for spelled-out text
|
# Note: This is primarily to support backwards compatibility.
|
||||||
# Note: This is primarily to support backwards compatibility.
|
# The preferred way of taking advantage of Cartesia SSML Tags is
|
||||||
# The preferred way of taking advantage of Cartesia SSML Tags is
|
# to use an LLMTextProcessor and/or a text_transformer to identify
|
||||||
# to use an LLMTextProcessor and/or a text_transformer to identify
|
# and insert these tags for the purpose of the TTS service alone.
|
||||||
# and insert these tags for the purpose of the TTS service alone.
|
self._text_aggregator = SkipTagsAggregator(
|
||||||
self._text_aggregator = SkipTagsAggregator(
|
[("<spell>", "</spell>")], aggregation_type=self._text_aggregation_mode
|
||||||
[("<spell>", "</spell>")], aggregation_type=self._text_aggregation_mode
|
)
|
||||||
)
|
|
||||||
|
|
||||||
self._api_key = api_key
|
self._api_key = api_key
|
||||||
self._cartesia_version = cartesia_version
|
self._cartesia_version = cartesia_version
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ from pipecat.services.tts_service import (
|
|||||||
WebsocketTTSService,
|
WebsocketTTSService,
|
||||||
)
|
)
|
||||||
from pipecat.transcriptions.language import Language, resolve_language
|
from pipecat.transcriptions.language import Language, resolve_language
|
||||||
from pipecat.utils.text.base_text_aggregator import BaseTextAggregator
|
|
||||||
from pipecat.utils.text.skip_tags_aggregator import SkipTagsAggregator
|
from pipecat.utils.text.skip_tags_aggregator import SkipTagsAggregator
|
||||||
from pipecat.utils.tracing.service_decorators import traced_tts
|
from pipecat.utils.tracing.service_decorators import traced_tts
|
||||||
|
|
||||||
@@ -176,7 +175,6 @@ class RimeTTSService(WebsocketTTSService):
|
|||||||
sample_rate: Optional[int] = None,
|
sample_rate: Optional[int] = None,
|
||||||
params: Optional[InputParams] = None,
|
params: Optional[InputParams] = None,
|
||||||
settings: Optional[Settings] = None,
|
settings: Optional[Settings] = None,
|
||||||
text_aggregator: Optional[BaseTextAggregator] = None,
|
|
||||||
text_aggregation_mode: Optional[TextAggregationMode] = None,
|
text_aggregation_mode: Optional[TextAggregationMode] = None,
|
||||||
aggregate_sentences: Optional[bool] = None,
|
aggregate_sentences: Optional[bool] = None,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
@@ -204,11 +202,6 @@ class RimeTTSService(WebsocketTTSService):
|
|||||||
|
|
||||||
settings: Runtime-updatable settings. When provided alongside deprecated
|
settings: Runtime-updatable settings. When provided alongside deprecated
|
||||||
parameters, ``settings`` values take precedence.
|
parameters, ``settings`` values take precedence.
|
||||||
text_aggregator: Custom text aggregator for processing input text.
|
|
||||||
|
|
||||||
.. deprecated:: 0.0.95
|
|
||||||
Use an LLMTextProcessor before the TTSService for custom text aggregation.
|
|
||||||
|
|
||||||
text_aggregation_mode: How to aggregate incoming text before synthesis.
|
text_aggregation_mode: How to aggregate incoming text before synthesis.
|
||||||
aggregate_sentences: Deprecated. Use text_aggregation_mode instead.
|
aggregate_sentences: Deprecated. Use text_aggregation_mode instead.
|
||||||
|
|
||||||
@@ -282,15 +275,14 @@ class RimeTTSService(WebsocketTTSService):
|
|||||||
self._audio_format = "pcm"
|
self._audio_format = "pcm"
|
||||||
self._sampling_rate = 0 # updated in start()
|
self._sampling_rate = 0 # updated in start()
|
||||||
|
|
||||||
if not text_aggregator:
|
# Always skip tags added for spelled-out text
|
||||||
# Always skip tags added for spelled-out text
|
# Note: This is primarily to support backwards compatibility.
|
||||||
# Note: This is primarily to support backwards compatibility.
|
# The preferred way of taking advantage of Rime spelling is
|
||||||
# The preferred way of taking advantage of Rime spelling is
|
# to use an LLMTextProcessor and/or a text_transformer to identify
|
||||||
# to use an LLMTextProcessor and/or a text_transformer to identify
|
# and insert these tags for the purpose of the TTS service alone.
|
||||||
# and insert these tags for the purpose of the TTS service alone.
|
self._text_aggregator = SkipTagsAggregator(
|
||||||
self._text_aggregator = SkipTagsAggregator(
|
[("spell(", ")")], aggregation_type=self._text_aggregation_mode
|
||||||
[("spell(", ")")], aggregation_type=self._text_aggregation_mode
|
)
|
||||||
)
|
|
||||||
|
|
||||||
# Store service configuration
|
# Store service configuration
|
||||||
self._api_key = api_key
|
self._api_key = api_key
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ from pipecat.services.ai_service import AIService
|
|||||||
from pipecat.services.settings import TTSSettings, is_given
|
from pipecat.services.settings import TTSSettings, is_given
|
||||||
from pipecat.services.websocket_service import WebsocketService
|
from pipecat.services.websocket_service import WebsocketService
|
||||||
from pipecat.transcriptions.language import Language
|
from pipecat.transcriptions.language import Language
|
||||||
from pipecat.utils.text.base_text_aggregator import BaseTextAggregator
|
|
||||||
from pipecat.utils.text.base_text_filter import BaseTextFilter
|
from pipecat.utils.text.base_text_filter import BaseTextFilter
|
||||||
from pipecat.utils.text.simple_text_aggregator import SimpleTextAggregator
|
from pipecat.utils.text.simple_text_aggregator import SimpleTextAggregator
|
||||||
from pipecat.utils.time import seconds_to_nanoseconds
|
from pipecat.utils.time import seconds_to_nanoseconds
|
||||||
@@ -168,8 +167,6 @@ class TTSService(AIService):
|
|||||||
append_trailing_space: bool = False,
|
append_trailing_space: bool = False,
|
||||||
# TTS output sample rate
|
# TTS output sample rate
|
||||||
sample_rate: Optional[int] = None,
|
sample_rate: Optional[int] = None,
|
||||||
# Text aggregator to aggregate incoming tokens and decide when to push to the TTS.
|
|
||||||
text_aggregator: Optional[BaseTextAggregator] = None,
|
|
||||||
# Types of text aggregations that should not be spoken.
|
# Types of text aggregations that should not be spoken.
|
||||||
skip_aggregator_types: Optional[List[str]] = [],
|
skip_aggregator_types: Optional[List[str]] = [],
|
||||||
# A list of callables to transform text before just before sending it to TTS.
|
# A list of callables to transform text before just before sending it to TTS.
|
||||||
@@ -182,7 +179,6 @@ class TTSService(AIService):
|
|||||||
] = None,
|
] = None,
|
||||||
# Text filter executed after text has been aggregated.
|
# Text filter executed after text has been aggregated.
|
||||||
text_filters: Optional[Sequence[BaseTextFilter]] = None,
|
text_filters: Optional[Sequence[BaseTextFilter]] = None,
|
||||||
text_filter: Optional[BaseTextFilter] = None,
|
|
||||||
# Audio transport destination of the generated frames.
|
# Audio transport destination of the generated frames.
|
||||||
transport_destination: Optional[str] = None,
|
transport_destination: Optional[str] = None,
|
||||||
settings: Optional[TTSSettings] = None,
|
settings: Optional[TTSSettings] = None,
|
||||||
@@ -215,11 +211,6 @@ class TTSService(AIService):
|
|||||||
append_trailing_space: Whether to append a trailing space to text before sending to TTS.
|
append_trailing_space: Whether to append a trailing space to text before sending to TTS.
|
||||||
This helps prevent some TTS services from vocalizing trailing punctuation (e.g., "dot").
|
This helps prevent some TTS services from vocalizing trailing punctuation (e.g., "dot").
|
||||||
sample_rate: Output sample rate for generated audio.
|
sample_rate: Output sample rate for generated audio.
|
||||||
text_aggregator: Custom text aggregator for processing incoming text.
|
|
||||||
|
|
||||||
.. deprecated:: 0.0.95
|
|
||||||
Use an LLMTextProcessor before the TTSService for custom text aggregation.
|
|
||||||
|
|
||||||
skip_aggregator_types: List of aggregation types that should not be spoken.
|
skip_aggregator_types: List of aggregation types that should not be spoken.
|
||||||
text_transforms: A list of callables to transform text before just before sending it
|
text_transforms: A list of callables to transform text before just before sending it
|
||||||
to TTS. Each callable takes the aggregated text and its type, and returns the
|
to TTS. Each callable takes the aggregated text and its type, and returns the
|
||||||
@@ -227,11 +218,6 @@ class TTSService(AIService):
|
|||||||
(aggregation_type | '*', transform_function).
|
(aggregation_type | '*', transform_function).
|
||||||
|
|
||||||
text_filters: Sequence of text filters to apply after aggregation.
|
text_filters: Sequence of text filters to apply after aggregation.
|
||||||
text_filter: Single text filter (deprecated, use text_filters).
|
|
||||||
|
|
||||||
.. deprecated:: 0.0.59
|
|
||||||
Use `text_filters` instead, which allows multiple filters.
|
|
||||||
|
|
||||||
transport_destination: Destination for generated audio frames.
|
transport_destination: Destination for generated audio frames.
|
||||||
settings: The runtime-updatable settings for the TTS service.
|
settings: The runtime-updatable settings for the TTS service.
|
||||||
reuse_context_id_within_turn: Whether the service should reuse context IDs within the
|
reuse_context_id_within_turn: Whether the service should reuse context IDs within the
|
||||||
@@ -300,18 +286,7 @@ class TTSService(AIService):
|
|||||||
self._append_trailing_space: bool = append_trailing_space
|
self._append_trailing_space: bool = append_trailing_space
|
||||||
self._init_sample_rate = sample_rate
|
self._init_sample_rate = sample_rate
|
||||||
self._sample_rate = 0
|
self._sample_rate = 0
|
||||||
self._text_aggregator: BaseTextAggregator = text_aggregator or SimpleTextAggregator(
|
self._text_aggregator = SimpleTextAggregator(aggregation_type=self._text_aggregation_mode)
|
||||||
aggregation_type=self._text_aggregation_mode
|
|
||||||
)
|
|
||||||
if text_aggregator:
|
|
||||||
import warnings
|
|
||||||
|
|
||||||
with warnings.catch_warnings():
|
|
||||||
warnings.simplefilter("always")
|
|
||||||
warnings.warn(
|
|
||||||
"Parameter 'text_aggregator' is deprecated. Use an LLMTextProcessor before the TTSService for custom text aggregation.",
|
|
||||||
DeprecationWarning,
|
|
||||||
)
|
|
||||||
|
|
||||||
self._skip_aggregator_types: List[str] = skip_aggregator_types or []
|
self._skip_aggregator_types: List[str] = skip_aggregator_types or []
|
||||||
self._text_transforms: List[
|
self._text_transforms: List[
|
||||||
@@ -320,16 +295,6 @@ class TTSService(AIService):
|
|||||||
# TODO: Deprecate _text_filters when added to LLMTextProcessor
|
# TODO: Deprecate _text_filters when added to LLMTextProcessor
|
||||||
self._text_filters: Sequence[BaseTextFilter] = text_filters or []
|
self._text_filters: Sequence[BaseTextFilter] = text_filters or []
|
||||||
self._transport_destination: Optional[str] = transport_destination
|
self._transport_destination: Optional[str] = transport_destination
|
||||||
if text_filter:
|
|
||||||
import warnings
|
|
||||||
|
|
||||||
with warnings.catch_warnings():
|
|
||||||
warnings.simplefilter("always")
|
|
||||||
warnings.warn(
|
|
||||||
"Parameter 'text_filter' is deprecated, use 'text_filters' instead.",
|
|
||||||
DeprecationWarning,
|
|
||||||
)
|
|
||||||
self._text_filters = [text_filter]
|
|
||||||
|
|
||||||
self._resampler = create_stream_resampler()
|
self._resampler = create_stream_resampler()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user