ABAO77 commited on
Commit
3fde6b6
·
1 Parent(s): 1ea1cfa

feat: add text cleaning functionality for TTS service to enhance input processing

Browse files
src/agents/role_play/__pycache__/prompt.cpython-311.pyc CHANGED
Binary files a/src/agents/role_play/__pycache__/prompt.cpython-311.pyc and b/src/agents/role_play/__pycache__/prompt.cpython-311.pyc differ
 
src/services/tts_service.py CHANGED
@@ -5,6 +5,7 @@ Text-to-Speech (TTS) Service using Deepgram API
5
  import requests
6
  import os
7
  import base64
 
8
  from src.utils.logger import logger
9
  from typing import Optional
10
 
@@ -20,6 +21,69 @@ class TTSService:
20
  logger.error("Deepgram API key not found in environment variables")
21
  raise ValueError("Deepgram API key is required")
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  async def text_to_speech(
24
  self,
25
  text: str,
@@ -43,7 +107,12 @@ class TTSService:
43
  return None
44
 
45
  # Clean and prepare text
46
- cleaned_text = text.strip()
 
 
 
 
 
47
  if len(cleaned_text) > 2000: # Limit text length for TTS
48
  cleaned_text = cleaned_text[:2000] + "..."
49
  logger.warning(f"Text truncated to 2000 characters for TTS")
@@ -57,7 +126,7 @@ class TTSService:
57
  "Content-Type": "application/json"
58
  }
59
 
60
- logger.info(f"Converting text to speech: {cleaned_text[:100]}...")
61
 
62
  # Make request to Deepgram API
63
  response = requests.post(
 
5
  import requests
6
  import os
7
  import base64
8
+ import re
9
  from src.utils.logger import logger
10
  from typing import Optional
11
 
 
21
  logger.error("Deepgram API key not found in environment variables")
22
  raise ValueError("Deepgram API key is required")
23
 
24
+ def clean_text_for_speech(self, text: str) -> str:
25
+ """
26
+ Clean text for speech synthesis by removing problematic characters
27
+
28
+ Args:
29
+ text (str): The text to clean
30
+
31
+ Returns:
32
+ str: Cleaned text suitable for speech synthesis
33
+ """
34
+ if not text or not isinstance(text, str):
35
+ return ""
36
+
37
+ # Remove markdown formatting
38
+ text = re.sub(r'\*\*(.*?)\*\*', r'\1', text) # Remove bold **text**
39
+ text = re.sub(r'\*(.*?)\*', r'\1', text) # Remove italic *text*
40
+ text = re.sub(r'`(.*?)`', r'\1', text) # Remove code `text`
41
+ text = re.sub(r'#{1,6}\s', '', text) # Remove headers # ## ###
42
+ text = re.sub(r'\[(.*?)\]\(.*?\)', r'\1', text) # Remove links [text](url) -> text
43
+
44
+ # Remove emojis and special unicode characters
45
+ # Emoticons
46
+ text = re.sub(r'[\U0001F600-\U0001F64F]', '', text)
47
+ # Misc symbols
48
+ text = re.sub(r'[\U0001F300-\U0001F5FF]', '', text)
49
+ # Transport & map
50
+ text = re.sub(r'[\U0001F680-\U0001F6FF]', '', text)
51
+ # Regional indicators
52
+ text = re.sub(r'[\U0001F1E0-\U0001F1FF]', '', text)
53
+ # Misc symbols
54
+ text = re.sub(r'[\U00002600-\U000026FF]', '', text)
55
+ # Dingbats
56
+ text = re.sub(r'[\U00002700-\U000027BF]', '', text)
57
+ # Variation selectors
58
+ text = re.sub(r'[\U0000FE00-\U0000FE0F]', '', text)
59
+ # Supplemental symbols
60
+ text = re.sub(r'[\U0001F900-\U0001F9FF]', '', text)
61
+
62
+ # Remove problematic punctuation and special characters
63
+ text = re.sub(r'[""'']', '"', text) # Replace smart quotes with regular quotes
64
+ text = re.sub(r'[–—]', '-', text) # Replace em/en dashes with hyphens
65
+ text = re.sub(r'[…]', '...', text) # Replace ellipsis character
66
+ text = re.sub(r'[«»]', '"', text) # Replace angle quotes
67
+ text = re.sub(r'[‹›]', "'", text) # Replace single angle quotes
68
+
69
+ # Remove control characters and zero-width characters
70
+ text = re.sub(r'[\u200B-\u200D\uFEFF]', '', text) # Zero-width chars
71
+ text = re.sub(r'[\u0000-\u001F\u007F-\u009F]', '', text) # Control chars
72
+
73
+ # Clean up extra whitespace
74
+ text = re.sub(r'\s+', ' ', text) # Multiple spaces to single space
75
+ text = text.strip() # Trim leading/trailing spaces
76
+
77
+ # Remove multiple consecutive punctuation
78
+ text = re.sub(r'\.{3,}', '...', text) # Multiple dots to ellipsis
79
+ text = re.sub(r'!{2,}', '!', text) # Multiple exclamations to single
80
+ text = re.sub(r'\?{2,}', '?', text) # Multiple questions to single
81
+
82
+ # Ensure proper sentence endings
83
+ text = re.sub(r'([.!?])\s*([A-Z])', r'\1 \2', text) # Space after sentence endings
84
+
85
+ return text
86
+
87
  async def text_to_speech(
88
  self,
89
  text: str,
 
107
  return None
108
 
109
  # Clean and prepare text
110
+ cleaned_text = self.clean_text_for_speech(text)
111
+
112
+ if not cleaned_text or not cleaned_text.strip():
113
+ logger.warning("Text became empty after cleaning for TTS")
114
+ return None
115
+
116
  if len(cleaned_text) > 2000: # Limit text length for TTS
117
  cleaned_text = cleaned_text[:2000] + "..."
118
  logger.warning(f"Text truncated to 2000 characters for TTS")
 
126
  "Content-Type": "application/json"
127
  }
128
 
129
+ logger.info(f"Converting text to speech: '{cleaned_text[:100]}...' (original: '{text[:50]}...')")
130
 
131
  # Make request to Deepgram API
132
  response = requests.post(
test_tts_cleaning.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Test script for TTS text cleaning functionality
4
+ """
5
+
6
+ import sys
7
+ import os
8
+
9
+ # Add the parent directory to sys.path to import from src
10
+ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
11
+
12
+ from src.services.tts_service import TTSService
13
+
14
+ def test_text_cleaning():
15
+ """Test the text cleaning functionality"""
16
+
17
+ print("🧪 Testing TTS Text Cleaning Function\n")
18
+
19
+ # Create TTS service instance (without API key for testing cleaning function only)
20
+ os.environ["YOUR_DEEPGRAM_API_KEY"] = "test_key" # Dummy key for testing
21
+ try:
22
+ tts = TTSService()
23
+ except:
24
+ # If there are import issues, create a simple test version
25
+ class TestTTSService:
26
+ def clean_text_for_speech(self, text):
27
+ import re
28
+ if not text or not isinstance(text, str):
29
+ return ""
30
+
31
+ # Remove markdown formatting
32
+ text = re.sub(r'\*\*(.*?)\*\*', r'\1', text) # Remove bold **text**
33
+ text = re.sub(r'\*(.*?)\*', r'\1', text) # Remove italic *text*
34
+ text = re.sub(r'`(.*?)`', r'\1', text) # Remove code `text`
35
+ text = re.sub(r'#{1,6}\s', '', text) # Remove headers # ## ###
36
+ text = re.sub(r'\[(.*?)\]\(.*?\)', r'\1', text) # Remove links [text](url) -> text
37
+
38
+ # Remove emojis and special unicode characters
39
+ text = re.sub(r'[\U0001F600-\U0001F64F]', '', text)
40
+ text = re.sub(r'[\U0001F300-\U0001F5FF]', '', text)
41
+ text = re.sub(r'[\U0001F680-\U0001F6FF]', '', text)
42
+ text = re.sub(r'[\U0001F1E0-\U0001F1FF]', '', text)
43
+ text = re.sub(r'[\U00002600-\U000026FF]', '', text)
44
+ text = re.sub(r'[\U00002700-\U000027BF]', '', text)
45
+ text = re.sub(r'[\U0000FE00-\U0000FE0F]', '', text)
46
+ text = re.sub(r'[\U0001F900-\U0001F9FF]', '', text)
47
+
48
+ # Remove problematic punctuation and special characters
49
+ text = re.sub(r'[""'']', '"', text)
50
+ text = re.sub(r'[–—]', '-', text)
51
+ text = re.sub(r'[…]', '...', text)
52
+ text = re.sub(r'[«»]', '"', text)
53
+ text = re.sub(r'[‹›]', "'", text)
54
+
55
+ # Remove control characters and zero-width characters
56
+ text = re.sub(r'[\u200B-\u200D\uFEFF]', '', text)
57
+ text = re.sub(r'[\u0000-\u001F\u007F-\u009F]', '', text)
58
+
59
+ # Clean up extra whitespace
60
+ text = re.sub(r'\s+', ' ', text)
61
+ text = text.strip()
62
+
63
+ # Remove multiple consecutive punctuation
64
+ text = re.sub(r'\.{3,}', '...', text)
65
+ text = re.sub(r'!{2,}', '!', text)
66
+ text = re.sub(r'\?{2,}', '?', text)
67
+
68
+ # Ensure proper sentence endings
69
+ text = re.sub(r'([.!?])\s*([A-Z])', r'\1 \2', text)
70
+
71
+ return text
72
+
73
+ tts = TestTTSService()
74
+
75
+ # Test cases
76
+ test_cases = [
77
+ {
78
+ "name": "Simple text",
79
+ "input": "Hello, how are you today?",
80
+ "expected_clean": True
81
+ },
82
+ {
83
+ "name": "Text with emojis",
84
+ "input": "Great job! 🎉 You're doing amazing! 🌟 Keep it up! 💪",
85
+ "expected_clean": True
86
+ },
87
+ {
88
+ "name": "Markdown formatting",
89
+ "input": "This is **bold** and this is *italic* and `code`",
90
+ "expected_clean": True
91
+ },
92
+ {
93
+ "name": "Complex markdown with links",
94
+ "input": "Check out [this link](https://example.com) and ## Header text",
95
+ "expected_clean": True
96
+ },
97
+ {
98
+ "name": "Mixed content",
99
+ "input": "🎯 **Practice Goal**: Learn English conversation skills! Visit [our website](https://wise.com) for more tips. 📚✨",
100
+ "expected_clean": True
101
+ },
102
+ {
103
+ "name": "Smart quotes and dashes",
104
+ "input": "\"Hello world\" and 'smart quotes' with em—dash and en–dash…",
105
+ "expected_clean": True
106
+ },
107
+ {
108
+ "name": "Multiple punctuation",
109
+ "input": "Wow!!! This is amazing??? Really......",
110
+ "expected_clean": True
111
+ },
112
+ {
113
+ "name": "Real AI response",
114
+ "input": "🌟 **Excellent!** You did a great job with that conversation! Here are some tips:\n\n- Use *natural* expressions\n- Practice `daily`\n- Visit [practice site](https://example.com)\n\n💪 Keep practicing! 🎯",
115
+ "expected_clean": True
116
+ }
117
+ ]
118
+
119
+ print("Testing text cleaning function:\n")
120
+
121
+ for i, test_case in enumerate(test_cases, 1):
122
+ print(f"Test {i}: {test_case['name']}")
123
+ print(f"Input: '{test_case['input']}'")
124
+
125
+ cleaned = tts.clean_text_for_speech(test_case['input'])
126
+ print(f"Output: '{cleaned}'")
127
+
128
+ # Check if cleaning was successful
129
+ has_emojis = any(char for char in cleaned if ord(char) > 127 and (
130
+ 0x1F600 <= ord(char) <= 0x1F64F or # Emoticons
131
+ 0x1F300 <= ord(char) <= 0x1F5FF or # Misc symbols
132
+ 0x1F680 <= ord(char) <= 0x1F6FF or # Transport
133
+ 0x2600 <= ord(char) <= 0x26FF # Misc symbols
134
+ ))
135
+
136
+ has_markdown = '**' in cleaned or '*' in cleaned or '`' in cleaned or '#' in cleaned
137
+
138
+ if has_emojis:
139
+ print("❌ Still contains emojis")
140
+ elif has_markdown:
141
+ print("❌ Still contains markdown")
142
+ elif not cleaned.strip():
143
+ print("⚠️ Text became empty after cleaning")
144
+ else:
145
+ print("✅ Cleaned successfully")
146
+
147
+ print("-" * 50)
148
+ print()
149
+
150
+ if __name__ == "__main__":
151
+ test_text_cleaning()