diff --git a/src/server/bot/core/component_factory.py b/src/server/bot/core/component_factory.py index c7be831..921c92b 100644 --- a/src/server/bot/core/component_factory.py +++ b/src/server/bot/core/component_factory.py @@ -23,6 +23,7 @@ from ..components.llm_tools.end_conversation_handler import EndConversationHandler from ..processors.speech.lipsync_processor import LipsyncProcessor from ..transport.custom_services.kokoro_service import KokoroTTSService +from ..transport.custom_services.emoji_text_filter import EmojiTextFilter from ..utils.device_utils import get_best_device from ..transport.custom_services.ollama_service import CustomOLLamaLLMService from ..components.memory import MemoryHandler @@ -178,6 +179,10 @@ def build_instruction(self) -> str: "discourse markers (you know, I mean), and brief pauses (uh, um). " "Use sparingly; never in numbers or names.\n" ) + instruction += ( + "Important: Do not use emojis or any special unicode symbols. " + "Your responses are converted directly to audio — emojis will be read aloud as their names.\n" + ) return instruction.strip() @@ -377,20 +382,23 @@ def _build_tts_service(self) -> Optional[object]: if not self.tts_type: return None + emoji_filter = EmojiTextFilter() if self.tts_type == "openai": return OpenAITTSService( voice=self._get_voice_for_openai(), model=(self.tts_params or {}).get("model", "gpt-4o-mini-tts"), + text_filters=[emoji_filter], ) elif self.tts_type == "elevenlabs": return ElevenLabsTTSService( api_key=os.getenv("ELEVENLABS_API_KEY"), voice_id=self._get_voice_id_for_elevenlabs(), model="eleven_flash_v2_5", + text_filters=[emoji_filter], ) elif self.tts_type == "kokoro": device = get_best_device() return KokoroTTSService( - voice=self._get_voice_id_for_kokoro(), device=device + voice=self._get_voice_id_for_kokoro(), device=device, text_filters=[emoji_filter] ) return None diff --git a/src/server/bot/transport/custom_services/emoji_text_filter.py b/src/server/bot/transport/custom_services/emoji_text_filter.py new file mode 100644 index 0000000..fe2e091 --- /dev/null +++ b/src/server/bot/transport/custom_services/emoji_text_filter.py @@ -0,0 +1,39 @@ +import re +from typing import Any, Mapping + +from pipecat.utils.text.base_text_filter import BaseTextFilter + +_EMOJI_RE = re.compile( + "[" + "\U0001F600-\U0001F64F" + "\U0001F300-\U0001F5FF" + "\U0001F680-\U0001F6FF" + "\U0001F1E0-\U0001F1FF" + "\U00002600-\U000026FF" + "\U00002700-\U000027BF" + "\U0000FE00-\U0000FE0F" + "\U0001F900-\U0001F9FF" + "\U0001FA70-\U0001FAFF" + "\U00002300-\U000023FF" + "\U00002B50-\U00002B55" + "\U0001F004" + "\U0001F0CF" + "]+", + flags=re.UNICODE, +) + + +class EmojiTextFilter(BaseTextFilter): + """Strips emoji characters from text before TTS synthesis.""" + + async def update_settings(self, settings: Mapping[str, Any]): + pass + + async def filter(self, text: str) -> str: + return _EMOJI_RE.sub("", text).strip() + + async def handle_interruption(self): + pass + + async def reset_interruption(self): + pass