Add split_text_by_spaces string util

This commit is contained in:
Mark Backman
2025-11-25 18:04:07 -05:00
parent 3ca94363ec
commit 535b85cf90
5 changed files with 96 additions and 19 deletions

View File

@@ -24,6 +24,7 @@ from pipecat.frames.frames import (
LLMTextFrame,
)
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
from pipecat.utils.string import split_text_by_characters
from pipecat.utils.text.base_text_aggregator import BaseTextAggregator
from pipecat.utils.text.simple_text_aggregator import SimpleTextAggregator
@@ -83,14 +84,19 @@ class LLMTextProcessor(FrameProcessor):
await self._text_aggregator.reset()
async def _handle_llm_text(self, in_frame: LLMTextFrame):
aggregation = await self._text_aggregator.aggregate(in_frame.text)
if aggregation:
out_frame = AggregatedTextFrame(
text=aggregation.text,
aggregated_by=aggregation.type,
)
out_frame.skip_tts = in_frame.skip_tts
await self.push_frame(out_frame)
# Split text by characters to normalize LLM output into individual characters
# This ensures consistent aggregation behavior regardless of LLM chunk size
characters = split_text_by_characters(in_frame.text)
for character in characters:
aggregation = await self._text_aggregator.aggregate(character)
if aggregation:
out_frame = AggregatedTextFrame(
text=aggregation.text,
aggregated_by=aggregation.type,
)
out_frame.skip_tts = in_frame.skip_tts
await self.push_frame(out_frame)
async def _handle_llm_end(self, skip_tts: Optional[bool] = None):
# Flush any remaining aggregated text at the end of the LLM response

View File

@@ -51,6 +51,7 @@ from pipecat.processors.frame_processor import FrameDirection
from pipecat.services.ai_service import AIService
from pipecat.services.websocket_service import WebsocketService
from pipecat.transcriptions.language import Language
from pipecat.utils.string import split_text_by_characters
from pipecat.utils.text.base_text_aggregator import BaseTextAggregator
from pipecat.utils.text.base_text_filter import BaseTextFilter
from pipecat.utils.text.simple_text_aggregator import SimpleTextAggregator
@@ -539,17 +540,26 @@ class TTSService(AIService):
text = frame.text
includes_inter_frame_spaces = frame.includes_inter_frame_spaces
aggregated_by = "token"
else:
aggregate = await self._text_aggregator.aggregate(frame.text)
if aggregate:
text = aggregate.text
aggregated_by = aggregate.type
if text:
logger.trace(f"Pushing TTS frames for text: {text}, {aggregated_by}")
await self._push_tts_frames(
AggregatedTextFrame(text, aggregated_by), includes_inter_frame_spaces
)
if text:
logger.trace(f"Pushing TTS frames for text: {text}, {aggregated_by}")
await self._push_tts_frames(
AggregatedTextFrame(text, aggregated_by), includes_inter_frame_spaces
)
else:
# Split text by characters to normalize input into individual characters
# This ensures consistent aggregation behavior regardless of input chunk size
characters = split_text_by_characters(frame.text)
for character in characters:
aggregate = await self._text_aggregator.aggregate(character)
if aggregate:
text = aggregate.text
aggregated_by = aggregate.type
logger.trace(f"Pushing TTS frames for text: {text}, {aggregated_by}")
await self._push_tts_frames(
AggregatedTextFrame(text, aggregated_by), includes_inter_frame_spaces
)
async def _push_tts_frames(
self, src_frame: AggregatedTextFrame, includes_inter_frame_spaces: Optional[bool] = False

View File

@@ -280,3 +280,27 @@ def concatenate_aggregated_text(text_parts: List[TextPartForConcatenation]) -> s
result = result.strip()
return result
def split_text_by_characters(text: str) -> List[str]:
"""Split text into individual characters.
Returns each character as a separate string element, allowing character-by-character
processing while maintaining the ability to reconstruct the original text.
Args:
text: The text to split into characters.
Returns:
A list of individual characters.
Example::
>>> split_text_by_characters("Hello world!")
["H", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d", "!"]
>>> split_text_by_characters("Hi")
["H", "i"]
>>> split_text_by_characters("")
[]
"""
return list(text)