feat: Add emoji filter before TTS#788
Conversation
- Added emoji filter betwen LLM and TTS to filter emojis being passed in the transcript to LLM.
WalkthroughThis PR adds emoji text filtering to the text-to-speech pipeline by introducing a new emoji filter module with regex-based character removal, extending Cartesia and Sarvam TTS configs to support configurable text filters, and integrating emoji stripping into Breeze Buddy's TTS generation across all four supported providers. ChangesEmoji Filtering for TTS Synthesis
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR adds an emoji-stripping text filter path for Breeze Buddy TTS so that emoji/pictographic characters don’t reach TTS providers (which can cause spoken emoji names or garbled audio).
Changes:
- Added
text_filterssupport to Sarvam and Cartesia TTS config/builders. - Introduced
EmojiTextFilter+strip_emoji()utility and wired it into Breeze Buddy’s TTS service builders. - Added a pre-provider
strip_emoji()pass ingenerate_audio()for non-pipecat direct API synthesis paths.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| app/ai/voice/tts/sarvam.py | Adds text_filters to Sarvam TTS config and forwards to SarvamTTSService. |
| app/ai/voice/tts/cartesia.py | Adds text_filters to Cartesia TTS config and forwards to CartesiaTTSService. |
| app/ai/voice/agents/breeze_buddy/tts/init.py | Wires emoji filtering into Breeze Buddy TTS service creation and direct audio generation. |
| app/ai/voice/agents/breeze_buddy/processors/emoji_text_filter.py | Implements emoji stripping utility and a pipecat BaseTextFilter. |
| def strip_emoji(text: str) -> str: | ||
| """Remove all emoji characters from *text* and collapse extra whitespace.""" | ||
| return _EMOJI_PATTERN.sub("", text).strip() |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
app/ai/voice/tts/cartesia.py (1)
40-40: ⚡ Quick winUse a concrete element type for
text_filters.
Optional[Sequence]loses type-safety. Please type this asOptional[Sequence[...]](e.g.,BaseTextFilter) so static checks can validate filter objects.As per coding guidelines, "Add type hints on all function signatures" and "Use Optional[T], List[T], Dict[str, Any], Union for type hints."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/ai/voice/tts/cartesia.py` at line 40, The parameter/attribute declaration text_filters: Optional[Sequence] = None should be made concrete by specifying the element type (e.g., text_filters: Optional[Sequence[BaseTextFilter]] = None) so static typing can validate filter objects; update the annotation in cartesia.py where text_filters is declared (and add or import the appropriate BaseTextFilter type/class into that module) and run type checks to ensure all usages of text_filters accept the specific filter type instead of an untyped Sequence.app/ai/voice/tts/sarvam.py (1)
39-39: ⚡ Quick winAdd the element type to
text_filtersannotation.
Optional[Sequence]is too loose for static validation. PreferOptional[Sequence[...]](matching the filter base type used by TTS services).As per coding guidelines, "Use Optional[T], List[T], Dict[str, Any], Union for type hints."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/ai/voice/tts/sarvam.py` at line 39, The annotation for text_filters is too broad; change text_filters: Optional[Sequence] = None to specify the element type used by TTS filters (e.g., text_filters: Optional[Sequence[TextFilter]] = None), import the TextFilter (or the actual filter base type used by your TTS services) and keep Optional/Sequence from typing so static validators can check item types; update any references to match the concrete filter base name.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/ai/voice/agents/breeze_buddy/processors/emoji_text_filter.py`:
- Around line 34-36: strip_emoji currently removes emoji via _EMOJI_PATTERN but
only calls .strip(), so internal multiple whitespace left by removals isn't
collapsed; update strip_emoji to replace any run of whitespace (e.g., using a
regex like r"\s+") with a single space after removing emojis and then .strip()
so internal spaces, tabs, and newlines collapse to single spaces—modify the body
of strip_emoji (the function named strip_emoji and use of _EMOJI_PATTERN)
accordingly.
---
Nitpick comments:
In `@app/ai/voice/tts/cartesia.py`:
- Line 40: The parameter/attribute declaration text_filters: Optional[Sequence]
= None should be made concrete by specifying the element type (e.g.,
text_filters: Optional[Sequence[BaseTextFilter]] = None) so static typing can
validate filter objects; update the annotation in cartesia.py where text_filters
is declared (and add or import the appropriate BaseTextFilter type/class into
that module) and run type checks to ensure all usages of text_filters accept the
specific filter type instead of an untyped Sequence.
In `@app/ai/voice/tts/sarvam.py`:
- Line 39: The annotation for text_filters is too broad; change text_filters:
Optional[Sequence] = None to specify the element type used by TTS filters (e.g.,
text_filters: Optional[Sequence[TextFilter]] = None), import the TextFilter (or
the actual filter base type used by your TTS services) and keep
Optional/Sequence from typing so static validators can check item types; update
any references to match the concrete filter base name.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 52c3853e-a7a8-4b92-adf2-b2caba7673a7
📒 Files selected for processing (4)
app/ai/voice/agents/breeze_buddy/processors/emoji_text_filter.pyapp/ai/voice/agents/breeze_buddy/tts/__init__.pyapp/ai/voice/tts/cartesia.pyapp/ai/voice/tts/sarvam.py
| def strip_emoji(text: str) -> str: | ||
| """Remove all emoji characters from *text* and collapse extra whitespace.""" | ||
| return _EMOJI_PATTERN.sub("", text).strip() |
There was a problem hiding this comment.
strip_emoji does not currently collapse internal whitespace.
Line 35 promises whitespace normalization, but Line 36 only strips ends. This can leave doubled spaces after emoji removal in spoken text.
💡 Suggested fix
def strip_emoji(text: str) -> str:
"""Remove all emoji characters from *text* and collapse extra whitespace."""
- return _EMOJI_PATTERN.sub("", text).strip()
+ text_without_emoji = _EMOJI_PATTERN.sub("", text)
+ return re.sub(r"\s+", " ", text_without_emoji).strip()🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/ai/voice/agents/breeze_buddy/processors/emoji_text_filter.py` around
lines 34 - 36, strip_emoji currently removes emoji via _EMOJI_PATTERN but only
calls .strip(), so internal multiple whitespace left by removals isn't
collapsed; update strip_emoji to replace any run of whitespace (e.g., using a
regex like r"\s+") with a single space after removing emojis and then .strip()
so internal spaces, tabs, and newlines collapse to single spaces—modify the body
of strip_emoji (the function named strip_emoji and use of _EMOJI_PATTERN)
accordingly.
Summary by CodeRabbit