datbkpro commited on
Commit
cc8629b
·
verified ·
1 Parent(s): b5e51ac

Update services/streaming_voice_service.py

Browse files
Files changed (1) hide show
  1. services/streaming_voice_service.py +297 -215
services/streaming_voice_service.py CHANGED
@@ -1,193 +1,14 @@
1
- # import io
2
- # import numpy as np
3
- # import soundfile as sf
4
- # import threading
5
- # import time
6
- # import sounddevice as sd
7
- # from groq import Groq
8
- # from typing import Optional, Callable
9
- # from config.settings import settings
10
- # from core.speechbrain_vad import SpeechBrainVAD
11
- # from core.rag_system import EnhancedRAGSystem
12
- # from core.tts_service import EnhancedTTSService
13
-
14
-
15
- # class StreamingVoiceService:
16
- # def __init__(self, groq_client: Groq, rag_system: EnhancedRAGSystem, tts_service: EnhancedTTSService):
17
- # self.client = groq_client
18
- # self.rag_system = rag_system
19
- # self.tts_service = tts_service
20
- # self.vad_processor = SpeechBrainVAD()
21
-
22
- # # Streaming state
23
- # self.is_listening = False
24
- # self.audio_stream = None
25
- # self.callback_handler = None
26
-
27
- # # Conversation context
28
- # self.conversation_history = []
29
- # self.current_transcription = ""
30
-
31
- # def start_listening(self, callback_handler: Callable):
32
- # """Bắt đầu lắng nghe với sounddevice"""
33
- # if self.is_listening:
34
- # return False
35
-
36
- # try:
37
- # self.callback_handler = callback_handler
38
- # self.is_listening = True
39
- # self.conversation_history = []
40
-
41
- # # Start VAD processing thread
42
- # self.vad_processor.start_stream(self._process_speech_segment)
43
-
44
- # # Khởi động thread lắng nghe
45
- # threading.Thread(target=self._listen_loop, daemon=True).start()
46
-
47
- # print("🎙️ Bắt đầu lắng nghe (sounddevice)...")
48
- # return True
49
-
50
- # except Exception as e:
51
- # print(f"❌ Lỗi khởi động stream: {e}")
52
- # self.stop_listening()
53
- # return False
54
-
55
- # def stop_listening(self):
56
- # """Dừng lắng nghe"""
57
- # self.is_listening = False
58
- # self.vad_processor.stop_stream()
59
- # print("🛑 Đã dừng lắng nghe")
60
-
61
- # def _listen_loop(self):
62
- # """Luồng lấy mẫu âm thanh liên tục"""
63
- # try:
64
- # with sd.InputStream(
65
- # samplerate=settings.SAMPLE_RATE,
66
- # channels=1,
67
- # dtype="float32",
68
- # blocksize=1024,
69
- # callback=self._audio_callback
70
- # ):
71
- # while self.is_listening:
72
- # time.sleep(0.05)
73
- # except Exception as e:
74
- # print(f"❌ Lỗi luồng âm thanh: {e}")
75
- # self.stop_listening()
76
-
77
- # def _audio_callback(self, in_data, frames, time_info, status):
78
- # """Callback xử lý audio input real-time"""
79
- # if status:
80
- # print(f"⚠️ Trạng thái âm thanh: {status}")
81
-
82
- # if self.is_listening:
83
- # audio_data = np.copy(in_data[:, 0]) # Mono
84
- # self.vad_processor.process_stream(audio_data, settings.SAMPLE_RATE)
85
-
86
- # def _process_speech_segment(self, speech_audio: np.ndarray, sample_rate: int):
87
- # """Xử lý đoạn giọng nói"""
88
- # if not self.is_listening or len(speech_audio) == 0:
89
- # return
90
-
91
- # print(f"🎯 Đang xử lý segment giọng nói ({len(speech_audio)} samples)...")
92
-
93
- # transcription = self._transcribe_audio(speech_audio, sample_rate)
94
- # if transcription and len(transcription.strip()) > 0:
95
- # self.current_transcription = transcription
96
- # print(f"📝 Transcription: {transcription}")
97
-
98
- # response = self._generate_ai_response(transcription)
99
- # tts_audio = self._text_to_speech(response)
100
-
101
- # if self.callback_handler:
102
- # self.callback_handler({
103
- # 'transcription': transcription,
104
- # 'response': response,
105
- # 'tts_audio': tts_audio,
106
- # 'speech_audio': speech_audio
107
- # })
108
-
109
- # def _transcribe_audio(self, audio_data: np.ndarray, sample_rate: int) -> Optional[str]:
110
- # """Chuyển audio -> text"""
111
- # try:
112
- # buffer = io.BytesIO()
113
- # sf.write(buffer, audio_data, sample_rate, format='wav')
114
- # buffer.seek(0)
115
-
116
- # transcription = self.client.audio.transcriptions.create(
117
- # model=settings.WHISPER_MODEL,
118
- # file=("speech.wav", buffer.read()),
119
- # response_format="text",
120
- # language="vi"
121
- # )
122
-
123
- # return transcription.strip()
124
- # except Exception as e:
125
- # print(f"❌ Lỗi transcription: {e}")
126
- # return None
127
-
128
- # def _generate_ai_response(self, user_input: str) -> str:
129
- # """Sinh phản hồi AI"""
130
- # try:
131
- # self.conversation_history.append({"role": "user", "content": user_input})
132
-
133
- # rag_results = self.rag_system.semantic_search(user_input, top_k=2)
134
- # context_text = "\n".join([f"- {doc.text}" for doc in rag_results]) if rag_results else ""
135
-
136
- # system_prompt = f"""Bạn là trợ lý AI thông minh chuyên về tiếng Việt.
137
- # Hãy trả lời ngắn gọn, tự nhiên và hữu ích.
138
-
139
- # Thông tin tham khảo:
140
- # {context_text}
141
- # """
142
-
143
- # messages = [{"role": "system", "content": system_prompt}]
144
- # messages.extend(self.conversation_history[-6:])
145
-
146
- # completion = self.client.chat.completions.create(
147
- # model=settings.LLM_MODEL,
148
- # messages=messages,
149
- # max_tokens=150,
150
- # temperature=0.7
151
- # )
152
-
153
- # response = completion.choices[0].message.content
154
- # self.conversation_history.append({"role": "assistant", "content": response})
155
-
156
- # if len(self.conversation_history) > 10:
157
- # self.conversation_history = self.conversation_history[-10:]
158
-
159
- # return response
160
-
161
- # except Exception as e:
162
- # return f"Xin lỗi, tôi gặp lỗi: {str(e)}"
163
-
164
- # def _text_to_speech(self, text: str) -> Optional[str]:
165
- # """Chuyển văn bản thành giọng nói"""
166
- # try:
167
- # tts_bytes = self.tts_service.text_to_speech(text, 'vi')
168
- # if tts_bytes:
169
- # return self.tts_service.save_audio_to_file(tts_bytes)
170
- # except Exception as e:
171
- # print(f"❌ Lỗi TTS: {e}")
172
- # return None
173
-
174
- # def get_conversation_state(self) -> dict:
175
- # """Lấy trạng thái hội thoại"""
176
- # return {
177
- # 'is_listening': self.is_listening,
178
- # 'history_length': len(self.conversation_history),
179
- # 'current_transcription': self.current_transcription
180
- # }
181
  import io
182
  import numpy as np
183
  import soundfile as sf
184
- import time # THÊM IMPORT NÀY
185
  import traceback
186
  from groq import Groq
187
  from typing import Optional, Dict, Any
188
  from config.settings import settings
189
  from core.rag_system import EnhancedRAGSystem
190
  from core.tts_service import EnhancedTTSService
 
191
 
192
 
193
  class StreamingVoiceService:
@@ -196,12 +17,85 @@ class StreamingVoiceService:
196
  self.rag_system = rag_system
197
  self.tts_service = tts_service
198
 
 
 
 
199
  # Conversation context
200
  self.conversation_history = []
201
  self.current_transcription = ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
 
203
  def process_streaming_audio(self, audio_data: tuple) -> Dict[str, Any]:
204
- """Xử lý audio streaming từ Gradio microphone component"""
205
  if not audio_data:
206
  return {
207
  'transcription': "❌ Không có dữ liệu âm thanh",
@@ -210,23 +104,22 @@ class StreamingVoiceService:
210
  }
211
 
212
  try:
213
- # Lấy dữ liệu audio từ Gradio
214
  sample_rate, audio_array = audio_data
215
 
216
  print(f"🎯 Nhận audio: {len(audio_array)} samples, SR: {sample_rate}")
217
 
218
- # Kiểm tra audio dữ liệu không
219
- if len(audio_array) == 0 or np.max(np.abs(audio_array)) < 0.01:
220
  return {
221
- 'transcription': "❌ Âm thanh quá yếu",
222
- 'response': "Xin vui lòng nói to hơn và rõ hơn",
223
  'tts_audio': None
224
  }
225
 
226
  # Chuyển đổi thành văn bản
227
  transcription = self._transcribe_audio(audio_array, sample_rate)
228
 
229
- if not transcription or len(transcription.strip()) == 0:
230
  return {
231
  'transcription': "❌ Không nghe rõ",
232
  'response': "Xin vui lòng nói lại rõ hơn",
@@ -234,8 +127,6 @@ class StreamingVoiceService:
234
  }
235
 
236
  print(f"📝 Đã chuyển đổi: {transcription}")
237
-
238
- # Cập nhật transcription hiện tại
239
  self.current_transcription = transcription
240
 
241
  # Tạo phản hồi AI
@@ -252,45 +143,46 @@ class StreamingVoiceService:
252
 
253
  except Exception as e:
254
  print(f"❌ Lỗi xử lý streaming audio: {e}")
255
- print(f"Chi tiết lỗi: {traceback.format_exc()}")
256
  return {
257
  'transcription': f"❌ Lỗi: {str(e)}",
258
- 'response': "Xin lỗi, có lỗi xảy ra trong quá trình xử lý",
259
  'tts_audio': None
260
  }
261
 
262
  def _transcribe_audio(self, audio_data: np.ndarray, sample_rate: int) -> Optional[str]:
263
- """Chuyển audio -> text"""
 
264
  try:
265
- # Chuẩn hóa audio data
266
  if audio_data.ndim > 1:
267
- audio_data = np.mean(audio_data, axis=1) # Chuyển sang mono
268
 
269
- # Normalize âm lượng
270
  audio_max = np.max(np.abs(audio_data))
271
- if audio_max > 0:
272
- audio_data = audio_data / audio_max
273
 
274
- # Giới hạn độ dài audio (tránh quá dài)
275
- max_duration = 10 # giây
276
  max_samples = sample_rate * max_duration
277
  if len(audio_data) > max_samples:
278
  audio_data = audio_data[:max_samples]
279
- print(f"⚠️ Cắt audio xuống còn {max_duration} giây")
 
 
 
 
 
280
 
281
  buffer = io.BytesIO()
282
  sf.write(buffer, audio_data, sample_rate, format='wav', subtype='PCM_16')
283
  buffer.seek(0)
284
 
285
- # Gọi API Whisper
286
  transcription = self.client.audio.transcriptions.create(
287
  model=settings.WHISPER_MODEL,
288
  file=("speech.wav", buffer.read(), "audio/wav"),
289
  response_format="text",
290
- language="vi"
 
291
  )
292
 
293
- # Xử lý response
294
  if hasattr(transcription, 'text'):
295
  result = transcription.text.strip()
296
  elif isinstance(transcription, str):
@@ -298,7 +190,6 @@ class StreamingVoiceService:
298
  else:
299
  result = str(transcription).strip()
300
 
301
- print(f"✅ Transcription thành công: {result}")
302
  return result
303
 
304
  except Exception as e:
@@ -306,23 +197,21 @@ class StreamingVoiceService:
306
  return None
307
 
308
  def _generate_ai_response(self, user_input: str) -> str:
309
- """Sinh phản hồi AI"""
 
310
  try:
311
- # Thêm vào lịch sử
312
  self.conversation_history.append({"role": "user", "content": user_input})
313
 
314
- # Tìm kiếm RAG
315
  rag_results = self.rag_system.semantic_search(user_input, top_k=2)
316
  context_text = "\n".join([f"- {result.get('text', str(result))}" for result in rag_results]) if rag_results else ""
317
 
318
  system_prompt = f"""Bạn là trợ lý AI thông minh chuyên về tiếng Việt.
319
- Hãy trả lời ngắn gọn, tự nhiên và hữu ích (dưới 100 từ).
320
  Thông tin tham khảo:
321
  {context_text}
322
  """
323
 
324
  messages = [{"role": "system", "content": system_prompt}]
325
- # Giữ lại 4 tin nhắn gần nhất
326
  messages.extend(self.conversation_history[-4:])
327
 
328
  completion = self.client.chat.completions.create(
@@ -335,17 +224,16 @@ Thông tin tham khảo:
335
  response = completion.choices[0].message.content
336
  self.conversation_history.append({"role": "assistant", "content": response})
337
 
338
- # Giới hạn lịch sử
339
  if len(self.conversation_history) > 8:
340
  self.conversation_history = self.conversation_history[-8:]
341
 
342
  return response
343
 
344
  except Exception as e:
345
- return f"Xin lỗi, tôi gặp lỗi khi tạo phản hồi: {str(e)}"
346
 
347
  def _text_to_speech(self, text: str) -> Optional[str]:
348
- """Chuyển văn bản thành giọng nói"""
349
  try:
350
  if not text or text.startswith("❌") or text.startswith("Xin lỗi"):
351
  return None
@@ -353,7 +241,6 @@ Thông tin tham khảo:
353
  tts_bytes = self.tts_service.text_to_speech(text, 'vi')
354
  if tts_bytes:
355
  audio_path = self.tts_service.save_audio_to_file(tts_bytes)
356
- print(f"✅ Đã tạo TTS: {audio_path}")
357
  return audio_path
358
  except Exception as e:
359
  print(f"❌ Lỗi TTS: {e}")
@@ -368,7 +255,202 @@ Thông tin tham khảo:
368
  def get_conversation_state(self) -> dict:
369
  """Lấy trạng thái hội thoại"""
370
  return {
 
371
  'history_length': len(self.conversation_history),
372
  'current_transcription': self.current_transcription,
373
  'last_update': time.strftime("%H:%M:%S")
374
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import io
2
  import numpy as np
3
  import soundfile as sf
4
+ import time
5
  import traceback
6
  from groq import Groq
7
  from typing import Optional, Dict, Any
8
  from config.settings import settings
9
  from core.rag_system import EnhancedRAGSystem
10
  from core.tts_service import EnhancedTTSService
11
+ from core.speechbrain_vad import SpeechBrainVAD # THÊM IMPORT
12
 
13
 
14
  class StreamingVoiceService:
 
17
  self.rag_system = rag_system
18
  self.tts_service = tts_service
19
 
20
+ # Khởi tạo VAD
21
+ self.vad_processor = SpeechBrainVAD()
22
+
23
  # Conversation context
24
  self.conversation_history = []
25
  self.current_transcription = ""
26
+ self.is_listening = False
27
+
28
+ def start_listening(self) -> bool:
29
+ """Bắt đầu lắng nghe với VAD"""
30
+ if self.is_listening:
31
+ return False
32
+
33
+ success = self.vad_processor.start_stream(self._on_speech_detected)
34
+ if success:
35
+ self.is_listening = True
36
+ print("🎙️ Đã bắt đầu lắng nghe với VAD")
37
+ return success
38
+
39
+ def stop_listening(self):
40
+ """Dừng lắng nghe"""
41
+ self.vad_processor.stop_stream()
42
+ self.is_listening = False
43
+ print("🛑 Đã dừng lắng nghe")
44
+
45
+ def process_audio_chunk(self, audio_data: tuple) -> Dict[str, Any]:
46
+ """Xử lý audio chunk với VAD (dùng cho real-time streaming)"""
47
+ if not audio_data or not self.is_listening:
48
+ return {
49
+ 'transcription': "",
50
+ 'response': "",
51
+ 'tts_audio': None
52
+ }
53
+
54
+ try:
55
+ sample_rate, audio_array = audio_data
56
+
57
+ # Xử lý với VAD
58
+ self.vad_processor.process_stream(audio_array, sample_rate)
59
+
60
+ return {
61
+ 'transcription': "Đang lắng nghe...",
62
+ 'response': "",
63
+ 'tts_audio': None
64
+ }
65
+
66
+ except Exception as e:
67
+ print(f"❌ Lỗi xử lý audio chunk: {e}")
68
+ return {
69
+ 'transcription': "",
70
+ 'response': "",
71
+ 'tts_audio': None
72
+ }
73
+
74
+ def _on_speech_detected(self, speech_audio: np.ndarray, sample_rate: int):
75
+ """Callback khi VAD phát hiện speech"""
76
+ print(f"🎯 VAD phát hiện speech segment: {len(speech_audio)/sample_rate:.2f}s")
77
+
78
+ # Chuyển đổi speech thành text
79
+ transcription = self._transcribe_audio(speech_audio, sample_rate)
80
+
81
+ if not transcription or len(transcription.strip()) < 2:
82
+ print("⚠️ Transcription quá ngắn hoặc trống")
83
+ return
84
+
85
+ print(f"📝 VAD Transcription: {transcription}")
86
+ self.current_transcription = transcription
87
+
88
+ # Tạo phản hồi AI
89
+ response = self._generate_ai_response(transcription)
90
+
91
+ # Tạo TTS
92
+ tts_audio_path = self._text_to_speech(response)
93
+
94
+ # Có thể gửi kết quả đến UI thông qua callback
95
+ # (cần tích hợp với Gradio events)
96
 
97
  def process_streaming_audio(self, audio_data: tuple) -> Dict[str, Any]:
98
+ """Xử lý audio streaming (phương thức cho compatibility)"""
99
  if not audio_data:
100
  return {
101
  'transcription': "❌ Không có dữ liệu âm thanh",
 
104
  }
105
 
106
  try:
 
107
  sample_rate, audio_array = audio_data
108
 
109
  print(f"🎯 Nhận audio: {len(audio_array)} samples, SR: {sample_rate}")
110
 
111
+ # Sử dụng VAD để kiểm tra speech
112
+ if not self.vad_processor.is_speech(audio_array, sample_rate):
113
  return {
114
+ 'transcription': "❌ Không phát hiện giọng nói",
115
+ 'response': "Vui lòng nói rõ hơn",
116
  'tts_audio': None
117
  }
118
 
119
  # Chuyển đổi thành văn bản
120
  transcription = self._transcribe_audio(audio_array, sample_rate)
121
 
122
+ if not transcription or len(transcription.strip()) < 2:
123
  return {
124
  'transcription': "❌ Không nghe rõ",
125
  'response': "Xin vui lòng nói lại rõ hơn",
 
127
  }
128
 
129
  print(f"📝 Đã chuyển đổi: {transcription}")
 
 
130
  self.current_transcription = transcription
131
 
132
  # Tạo phản hồi AI
 
143
 
144
  except Exception as e:
145
  print(f"❌ Lỗi xử lý streaming audio: {e}")
 
146
  return {
147
  'transcription': f"❌ Lỗi: {str(e)}",
148
+ 'response': "Xin lỗi, có lỗi xảy ra",
149
  'tts_audio': None
150
  }
151
 
152
  def _transcribe_audio(self, audio_data: np.ndarray, sample_rate: int) -> Optional[str]:
153
+ """Chuyển audio -> text (giữ nguyên)"""
154
+ # ... giữ nguyên code cũ ...
155
  try:
 
156
  if audio_data.ndim > 1:
157
+ audio_data = np.mean(audio_data, axis=1)
158
 
 
159
  audio_max = np.max(np.abs(audio_data))
160
+ if audio_max > 0.1:
161
+ audio_data = audio_data / audio_max * 0.9
162
 
163
+ max_duration = 15
 
164
  max_samples = sample_rate * max_duration
165
  if len(audio_data) > max_samples:
166
  audio_data = audio_data[:max_samples]
167
+
168
+ min_duration = 1.0
169
+ min_samples = sample_rate * min_duration
170
+ if len(audio_data) < min_samples:
171
+ padding = np.zeros(min_samples - len(audio_data))
172
+ audio_data = np.concatenate([audio_data, padding])
173
 
174
  buffer = io.BytesIO()
175
  sf.write(buffer, audio_data, sample_rate, format='wav', subtype='PCM_16')
176
  buffer.seek(0)
177
 
 
178
  transcription = self.client.audio.transcriptions.create(
179
  model=settings.WHISPER_MODEL,
180
  file=("speech.wav", buffer.read(), "audio/wav"),
181
  response_format="text",
182
+ language="vi",
183
+ temperature=0.0,
184
  )
185
 
 
186
  if hasattr(transcription, 'text'):
187
  result = transcription.text.strip()
188
  elif isinstance(transcription, str):
 
190
  else:
191
  result = str(transcription).strip()
192
 
 
193
  return result
194
 
195
  except Exception as e:
 
197
  return None
198
 
199
  def _generate_ai_response(self, user_input: str) -> str:
200
+ """Sinh phản hồi AI (giữ nguyên)"""
201
+ # ... giữ nguyên code cũ ...
202
  try:
 
203
  self.conversation_history.append({"role": "user", "content": user_input})
204
 
 
205
  rag_results = self.rag_system.semantic_search(user_input, top_k=2)
206
  context_text = "\n".join([f"- {result.get('text', str(result))}" for result in rag_results]) if rag_results else ""
207
 
208
  system_prompt = f"""Bạn là trợ lý AI thông minh chuyên về tiếng Việt.
209
+ Hãy trả lời ngắn gọn, tự nhiên và hữu ích.
210
  Thông tin tham khảo:
211
  {context_text}
212
  """
213
 
214
  messages = [{"role": "system", "content": system_prompt}]
 
215
  messages.extend(self.conversation_history[-4:])
216
 
217
  completion = self.client.chat.completions.create(
 
224
  response = completion.choices[0].message.content
225
  self.conversation_history.append({"role": "assistant", "content": response})
226
 
 
227
  if len(self.conversation_history) > 8:
228
  self.conversation_history = self.conversation_history[-8:]
229
 
230
  return response
231
 
232
  except Exception as e:
233
+ return f"Xin lỗi, tôi gặp lỗi: {str(e)}"
234
 
235
  def _text_to_speech(self, text: str) -> Optional[str]:
236
+ """Chuyển văn bản thành giọng nói (giữ nguyên)"""
237
  try:
238
  if not text or text.startswith("❌") or text.startswith("Xin lỗi"):
239
  return None
 
241
  tts_bytes = self.tts_service.text_to_speech(text, 'vi')
242
  if tts_bytes:
243
  audio_path = self.tts_service.save_audio_to_file(tts_bytes)
 
244
  return audio_path
245
  except Exception as e:
246
  print(f"❌ Lỗi TTS: {e}")
 
255
  def get_conversation_state(self) -> dict:
256
  """Lấy trạng thái hội thoại"""
257
  return {
258
+ 'is_listening': self.is_listening,
259
  'history_length': len(self.conversation_history),
260
  'current_transcription': self.current_transcription,
261
  'last_update': time.strftime("%H:%M:%S")
262
+ }
263
+ # import io
264
+ # import numpy as np
265
+ # import soundfile as sf
266
+ # import time # THÊM IMPORT NÀY
267
+ # import traceback
268
+ # from groq import Groq
269
+ # from typing import Optional, Dict, Any
270
+ # from config.settings import settings
271
+ # from core.rag_system import EnhancedRAGSystem
272
+ # from core.tts_service import EnhancedTTSService
273
+
274
+
275
+ # class StreamingVoiceService:
276
+ # def __init__(self, groq_client: Groq, rag_system: EnhancedRAGSystem, tts_service: EnhancedTTSService):
277
+ # self.client = groq_client
278
+ # self.rag_system = rag_system
279
+ # self.tts_service = tts_service
280
+
281
+ # # Conversation context
282
+ # self.conversation_history = []
283
+ # self.current_transcription = ""
284
+
285
+ # def process_streaming_audio(self, audio_data: tuple) -> Dict[str, Any]:
286
+ # """Xử lý audio streaming từ Gradio microphone component"""
287
+ # if not audio_data:
288
+ # return {
289
+ # 'transcription': "❌ Không có dữ liệu âm thanh",
290
+ # 'response': "Vui lòng nói lại",
291
+ # 'tts_audio': None
292
+ # }
293
+
294
+ # try:
295
+ # # Lấy dữ liệu audio từ Gradio
296
+ # sample_rate, audio_array = audio_data
297
+
298
+ # print(f"🎯 Nhận audio: {len(audio_array)} samples, SR: {sample_rate}")
299
+
300
+ # # Kiểm tra audio có dữ liệu không
301
+ # if len(audio_array) == 0 or np.max(np.abs(audio_array)) < 0.01:
302
+ # return {
303
+ # 'transcription': "❌ Âm thanh quá yếu",
304
+ # 'response': "Xin vui lòng nói to hơn và rõ hơn",
305
+ # 'tts_audio': None
306
+ # }
307
+
308
+ # # Chuyển đổi thành văn bản
309
+ # transcription = self._transcribe_audio(audio_array, sample_rate)
310
+
311
+ # if not transcription or len(transcription.strip()) == 0:
312
+ # return {
313
+ # 'transcription': "❌ Không nghe rõ",
314
+ # 'response': "Xin vui lòng nói lại rõ hơn",
315
+ # 'tts_audio': None
316
+ # }
317
+
318
+ # print(f"📝 Đã chuyển đổi: {transcription}")
319
+
320
+ # # Cập nhật transcription hiện tại
321
+ # self.current_transcription = transcription
322
+
323
+ # # Tạo phản hồi AI
324
+ # response = self._generate_ai_response(transcription)
325
+
326
+ # # Tạo TTS
327
+ # tts_audio_path = self._text_to_speech(response)
328
+
329
+ # return {
330
+ # 'transcription': transcription,
331
+ # 'response': response,
332
+ # 'tts_audio': tts_audio_path
333
+ # }
334
+
335
+ # except Exception as e:
336
+ # print(f"❌ Lỗi xử lý streaming audio: {e}")
337
+ # print(f"Chi tiết lỗi: {traceback.format_exc()}")
338
+ # return {
339
+ # 'transcription': f"❌ Lỗi: {str(e)}",
340
+ # 'response': "Xin lỗi, có lỗi xảy ra trong quá trình xử lý",
341
+ # 'tts_audio': None
342
+ # }
343
+
344
+ # def _transcribe_audio(self, audio_data: np.ndarray, sample_rate: int) -> Optional[str]:
345
+ # """Chuyển audio -> text"""
346
+ # try:
347
+ # # Chuẩn hóa audio data
348
+ # if audio_data.ndim > 1:
349
+ # audio_data = np.mean(audio_data, axis=1) # Chuyển sang mono
350
+
351
+ # # Normalize âm lượng
352
+ # audio_max = np.max(np.abs(audio_data))
353
+ # if audio_max > 0:
354
+ # audio_data = audio_data / audio_max
355
+
356
+ # # Giới hạn độ dài audio (tránh quá dài)
357
+ # max_duration = 10 # giây
358
+ # max_samples = sample_rate * max_duration
359
+ # if len(audio_data) > max_samples:
360
+ # audio_data = audio_data[:max_samples]
361
+ # print(f"⚠️ Cắt audio xuống còn {max_duration} giây")
362
+
363
+ # buffer = io.BytesIO()
364
+ # sf.write(buffer, audio_data, sample_rate, format='wav', subtype='PCM_16')
365
+ # buffer.seek(0)
366
+
367
+ # # Gọi API Whisper
368
+ # transcription = self.client.audio.transcriptions.create(
369
+ # model=settings.WHISPER_MODEL,
370
+ # file=("speech.wav", buffer.read(), "audio/wav"),
371
+ # response_format="text",
372
+ # language="vi"
373
+ # )
374
+
375
+ # # Xử lý response
376
+ # if hasattr(transcription, 'text'):
377
+ # result = transcription.text.strip()
378
+ # elif isinstance(transcription, str):
379
+ # result = transcription.strip()
380
+ # else:
381
+ # result = str(transcription).strip()
382
+
383
+ # print(f"✅ Transcription thành công: {result}")
384
+ # return result
385
+
386
+ # except Exception as e:
387
+ # print(f"❌ Lỗi transcription: {e}")
388
+ # return None
389
+
390
+ # def _generate_ai_response(self, user_input: str) -> str:
391
+ # """Sinh phản hồi AI"""
392
+ # try:
393
+ # # Thêm vào lịch sử
394
+ # self.conversation_history.append({"role": "user", "content": user_input})
395
+
396
+ # # Tìm kiếm RAG
397
+ # rag_results = self.rag_system.semantic_search(user_input, top_k=2)
398
+ # context_text = "\n".join([f"- {result.get('text', str(result))}" for result in rag_results]) if rag_results else ""
399
+
400
+ # system_prompt = f"""Bạn là trợ lý AI thông minh chuyên về tiếng Việt.
401
+ # Hãy trả lời ngắn gọn, tự nhiên và hữu ích (dưới 100 từ).
402
+ # Thông tin tham khảo:
403
+ # {context_text}
404
+ # """
405
+
406
+ # messages = [{"role": "system", "content": system_prompt}]
407
+ # # Giữ lại 4 tin nhắn gần nhất
408
+ # messages.extend(self.conversation_history[-4:])
409
+
410
+ # completion = self.client.chat.completions.create(
411
+ # model="llama-3.1-8b-instant",
412
+ # messages=messages,
413
+ # max_tokens=150,
414
+ # temperature=0.7
415
+ # )
416
+
417
+ # response = completion.choices[0].message.content
418
+ # self.conversation_history.append({"role": "assistant", "content": response})
419
+
420
+ # # Giới hạn lịch sử
421
+ # if len(self.conversation_history) > 8:
422
+ # self.conversation_history = self.conversation_history[-8:]
423
+
424
+ # return response
425
+
426
+ # except Exception as e:
427
+ # return f"Xin lỗi, tôi gặp lỗi khi tạo phản hồi: {str(e)}"
428
+
429
+ # def _text_to_speech(self, text: str) -> Optional[str]:
430
+ # """Chuyển văn bản thành giọng nói"""
431
+ # try:
432
+ # if not text or text.startswith("❌") or text.startswith("Xin lỗi"):
433
+ # return None
434
+
435
+ # tts_bytes = self.tts_service.text_to_speech(text, 'vi')
436
+ # if tts_bytes:
437
+ # audio_path = self.tts_service.save_audio_to_file(tts_bytes)
438
+ # print(f"✅ Đã tạo TTS: {audio_path}")
439
+ # return audio_path
440
+ # except Exception as e:
441
+ # print(f"❌ Lỗi TTS: {e}")
442
+ # return None
443
+
444
+ # def clear_conversation(self):
445
+ # """Xóa lịch sử hội thoại"""
446
+ # self.conversation_history = []
447
+ # self.current_transcription = ""
448
+ # print("🗑️ Đã xóa lịch sử hội thoại")
449
+
450
+ # def get_conversation_state(self) -> dict:
451
+ # """Lấy trạng thái hội thoại"""
452
+ # return {
453
+ # 'history_length': len(self.conversation_history),
454
+ # 'current_transcription': self.current_transcription,
455
+ # 'last_update': time.strftime("%H:%M:%S")
456
+ # }