datbkpro commited on
Commit
1067054
·
verified ·
1 Parent(s): 27cf157

Update services/streaming_voice_service.py

Browse files
Files changed (1) hide show
  1. services/streaming_voice_service.py +193 -250
services/streaming_voice_service.py CHANGED
@@ -990,77 +990,106 @@ from core.silero_vad import SileroVAD
990
 
991
  class VoskStreamingASR:
992
  def __init__(self, model_path: str = None):
993
- """Khởi tạo VOSK ASR streaming"""
994
  self.model = None
995
  self.recognizer = None
996
  self.sample_rate = 16000
997
  self.is_streaming = False
998
- self.partial_results = []
999
 
1000
  # Tự động tải model nếu không có đường dẫn
1001
  if model_path is None:
1002
  model_path = self._download_vosk_model()
1003
 
1004
- if os.path.exists(model_path):
1005
- print("🔄 Đang tải VOSK model...")
1006
- self.model = Model(model_path)
1007
- self.recognizer = KaldiRecognizer(self.model, self.sample_rate)
1008
- print("✅ Đã tải VOSK model thành công")
 
 
 
 
1009
  else:
1010
  print(f"❌ Không tìm thấy VOSK model tại: {model_path}")
1011
 
1012
  def _download_vosk_model(self):
1013
  """Tải VOSK model tiếng Việt tự động"""
1014
- import urllib.request
1015
- import zipfile
1016
-
1017
- model_url = "https://alphacephei.com/vosk/models/vosk-model-small-vn-0.4.zip"
1018
- model_dir = "models/vosk-model-small-vn-0.4"
1019
- zip_path = "models/vosk-model-small-vn-0.4.zip"
1020
-
1021
- # Tạo thư mục nếu chưa có
1022
- os.makedirs("models", exist_ok=True)
1023
-
1024
- if not os.path.exists(model_dir):
1025
- print("📥 Đang tải VOSK Vietnamese model...")
1026
- try:
1027
  urllib.request.urlretrieve(model_url, zip_path)
1028
 
1029
  with zipfile.ZipFile(zip_path, 'r') as zip_ref:
1030
  zip_ref.extractall("models/")
1031
 
1032
- os.rename("models/vosk-model-small-vn-0.4", model_dir)
1033
- os.remove(zip_path)
 
 
 
 
1034
  print("✅ Đã tải VOSK model thành công")
1035
- except Exception as e:
1036
- print(f"❌ Lỗi tải VOSK model: {e}")
1037
- return None
1038
-
1039
- return model_dir
 
1040
 
1041
  def start_stream(self):
1042
  """Bắt đầu stream mới"""
1043
  if self.model is None:
 
1044
  return False
1045
 
1046
- self.recognizer = KaldiRecognizer(self.model, self.sample_rate)
1047
- self.recognizer.SetWords(True)
1048
- self.is_streaming = True
1049
- self.partial_results = []
1050
- return True
 
 
 
 
1051
 
1052
  def process_audio_chunk(self, audio_chunk: np.ndarray, sample_rate: int = None) -> Dict[str, Any]:
1053
- """Xử lý audio chunk và trả về kết quả"""
1054
  if self.recognizer is None or not self.is_streaming:
1055
  return {"text": "", "partial": "", "is_final": False}
1056
 
1057
  try:
1058
- # Chuẩn hóa audio
 
 
 
1059
  if sample_rate and sample_rate != self.sample_rate:
1060
  audio_chunk = self._resample_audio(audio_chunk, sample_rate, self.sample_rate)
1061
 
 
1062
  if audio_chunk.dtype != np.int16:
1063
- audio_chunk = (audio_chunk * 32767).astype(np.int16)
 
 
 
 
 
 
 
 
 
 
 
 
1064
 
1065
  # Chuyển đổi sang bytes
1066
  audio_bytes = audio_chunk.tobytes()
@@ -1068,41 +1097,53 @@ class VoskStreamingASR:
1068
  # Xử lý với VOSK
1069
  if self.recognizer.AcceptWaveform(audio_bytes):
1070
  # Kết quả cuối cùng
1071
- result = json.loads(self.recognizer.Result())
 
1072
  text = result.get('text', '').strip()
 
1073
  if text:
1074
  return {"text": text, "partial": "", "is_final": True}
1075
  else:
1076
  # Kết quả tạm thời
1077
- partial_result = json.loads(self.recognizer.PartialResult())
 
1078
  partial_text = partial_result.get('partial', '').strip()
1079
- return {"text": "", "partial": partial_text, "is_final": False}
 
 
1080
 
1081
  except Exception as e:
1082
  print(f"❌ Lỗi VOSK processing: {e}")
 
1083
 
1084
  return {"text": "", "partial": "", "is_final": False}
1085
 
1086
  def stop_stream(self) -> str:
1087
  """Kết thúc stream và lấy kết quả cuối"""
1088
  if self.recognizer:
1089
- result = json.loads(self.recognizer.FinalResult())
1090
- text = result.get('text', '').strip()
1091
- self.is_streaming = False
1092
- return text
 
 
 
 
 
1093
  return ""
1094
 
1095
  def _resample_audio(self, audio: np.ndarray, orig_sr: int, target_sr: int) -> np.ndarray:
1096
- """Resample audio"""
1097
  if orig_sr == target_sr:
1098
  return audio
1099
  try:
1100
  from scipy import signal
1101
- duration = len(audio) / orig_sr
1102
- new_length = int(duration * target_sr)
1103
- resampled_audio = signal.resample(audio, new_length)
1104
  return resampled_audio.astype(np.int16)
1105
- except Exception:
 
1106
  return audio
1107
 
1108
  class StreamingVoiceService:
@@ -1111,7 +1152,8 @@ class StreamingVoiceService:
1111
  self.rag_system = rag_system
1112
  self.tts_service = tts_service
1113
 
1114
- # Khởi tạo VOSK ASR thay Whisper
 
1115
  self.vosk_asr = VoskStreamingASR()
1116
 
1117
  # Khởi tạo VAD
@@ -1119,7 +1161,6 @@ class StreamingVoiceService:
1119
  self.is_listening = False
1120
  self.speech_callback = None
1121
  self.is_processing = False
1122
- self.processing_lock = threading.Lock()
1123
 
1124
  # Conversation context
1125
  self.conversation_history = []
@@ -1131,37 +1172,50 @@ class StreamingVoiceService:
1131
  self.processing_threads = []
1132
  self.max_workers = 2
1133
 
1134
- # VOSK streaming state
1135
  self.vosk_stream_active = False
1136
  self.last_voice_time = 0
1137
- self.silence_timeout = 2.0 # 2 giây im lặng thì kết thúc câu
 
 
 
 
 
1138
 
1139
  # Latency tracking
1140
  self.latency_metrics = {
1141
- 'asr': [],
1142
- 'rag': [],
1143
- 'llm': [],
1144
- 'tts': [],
1145
- 'total': [],
1146
- 'vad_detection': [],
1147
- 'queue_waiting': [],
1148
- 'vosk_processing': []
1149
  }
1150
 
1151
  self.current_callback = None
1152
 
1153
  def start_listening(self, speech_callback: Callable) -> bool:
1154
- """Bắt đầu lắng nghe với VOSK streaming"""
1155
  if self.is_listening:
 
1156
  return False
1157
 
1158
  self.current_callback = speech_callback
1159
 
 
 
 
 
 
 
 
 
 
 
 
 
1160
  # Khởi động VOSK stream
1161
  if not self.vosk_asr.start_stream():
1162
- print("❌ Không thể khởi động VOSK ASR")
1163
  return False
1164
 
 
1165
  success = self.vad_processor.start_stream(self._on_speech_detected)
1166
 
1167
  if success:
@@ -1169,6 +1223,7 @@ class StreamingVoiceService:
1169
  self.is_processing = False
1170
  self.vosk_stream_active = True
1171
  self.last_voice_time = time.time()
 
1172
 
1173
  # Khởi động worker threads
1174
  if not self.processing_threads:
@@ -1185,74 +1240,76 @@ class StreamingVoiceService:
1185
  threading.Thread(target=self._vosk_streaming_monitor, daemon=True).start()
1186
 
1187
  print("🎙️ Đã bắt đầu lắng nghe với VOSK ASR streaming")
 
 
 
 
 
 
 
 
 
 
1188
  return True
 
1189
  return False
1190
 
1191
- def stop_listening(self):
1192
- """Dừng lắng nghe"""
1193
- self.vad_processor.stop_stream()
1194
- self.is_listening = False
1195
- self.is_processing = False
1196
- self.vosk_stream_active = False
1197
- self.current_callback = None
1198
-
1199
- # Dừng VOSK stream
1200
- final_text = self.vosk_asr.stop_stream()
1201
- if final_text and len(final_text) > 1:
1202
- self._process_final_transcription(final_text)
1203
-
1204
- # Clear queue
1205
- while not self.response_queue.empty():
1206
- try:
1207
- self.response_queue.get_nowait()
1208
- self.response_queue.task_done()
1209
- except queue.Empty:
1210
- break
1211
-
1212
- print("🛑 Đã dừng lắng nghe")
1213
-
1214
  def _vosk_streaming_monitor(self):
1215
  """Theo dõi VOSK streaming và xử lý kết quả real-time"""
1216
  while self.is_listening and self.vosk_stream_active:
1217
  try:
1218
- # Kiểm tra timeout im lặng
1219
  current_time = time.time()
1220
  silence_duration = current_time - self.last_voice_time
1221
 
 
 
 
 
 
 
 
 
 
1222
  if silence_duration > self.silence_timeout and self.partial_transcription:
1223
- # Im lặng quá lâu, xử transcription hiện tại
1224
- print(f"⏰ Silence timeout, xử lý: {self.partial_transcription}")
1225
- self._process_final_transcription(self.partial_transcription)
1226
  self.partial_transcription = ""
1227
- self.vosk_asr.start_stream() # Bắt đầu stream mới
1228
 
1229
- time.sleep(0.1) # Giảm CPU usage
1230
 
1231
  except Exception as e:
1232
  print(f"❌ Lỗi VOSK monitor: {e}")
1233
  break
1234
 
1235
  def _on_speech_detected(self, speech_audio: np.ndarray, sample_rate: int):
1236
- """Callback khi VAD phát hiện speech - sử dụng VOSK real-time"""
1237
- vad_start = time.time()
1238
-
1239
- if not self.vosk_stream_active:
1240
  return
1241
 
1242
  # Cập nhật thời gian có giọng nói
1243
  self.last_voice_time = time.time()
1244
 
1245
- # Xử với VOSK
1246
- vosk_start = time.time()
1247
- result = self.vosk_asr.process_audio_chunk(speech_audio, sample_rate)
1248
- vosk_latency = time.time() - vosk_start
1249
 
1250
- self._add_latency_sample('vosk_processing', vosk_latency)
 
 
 
 
 
 
1251
 
 
 
 
 
1252
  # Xử lý kết quả partial
1253
  if result['partial'] and len(result['partial']) > 1:
1254
  self.partial_transcription = result['partial']
1255
- print(f"🎯 VOSK Partial: {result['partial']}")
1256
 
1257
  # Gửi partial result real-time
1258
  if self.current_callback:
@@ -1265,184 +1322,61 @@ class StreamingVoiceService:
1265
 
1266
  # Xử lý kết quả final
1267
  if result['is_final'] and result['text'] and len(result['text']) > 1:
1268
- print(f"✅ VOSK Final: {result['text']}")
1269
  self._process_final_transcription(result['text'])
1270
  self.partial_transcription = ""
 
1271
  self.vosk_asr.start_stream() # Bắt đầu stream mới
1272
-
1273
- vad_latency = time.time() - vad_start
1274
- self._add_latency_sample('vad_detection', vad_latency)
1275
 
1276
  def _process_final_transcription(self, transcription: str):
1277
  """Xử lý transcription cuối cùng"""
1278
  if not transcription or len(transcription.strip()) < 2:
1279
  return
1280
 
1281
- print(f"📝 Final Transcription: {transcription}")
1282
  self.current_transcription = transcription
1283
 
1284
  # Đưa vào queue để xử lý
1285
  try:
1286
  self.response_queue.put(transcription, timeout=0.5)
 
1287
  except queue.Full:
1288
  print("⚠️ Queue đầy, bỏ qua transcription")
1289
 
1290
- def _process_response_worker(self):
1291
- """Worker thread xử lý transcriptions"""
1292
- while self.is_listening:
1293
- try:
1294
- queue_start = time.time()
1295
- transcription = self.response_queue.get(timeout=1.0)
1296
- queue_latency = time.time() - queue_start
1297
- self._add_latency_sample('queue_waiting', queue_latency)
1298
-
1299
- # Xử lý AI response
1300
- self._process_ai_response(transcription)
1301
-
1302
- self.response_queue.task_done()
1303
-
1304
- except queue.Empty:
1305
- continue
1306
- except Exception as e:
1307
- print(f"❌ Lỗi trong worker thread: {e}")
1308
- continue
1309
-
1310
- def _process_ai_response(self, transcription: str):
1311
- """Xử lý phản hồi AI cho transcription"""
1312
- total_start_time = time.time()
1313
-
1314
- try:
1315
- # 1. AI Response Generation
1316
- rag_start = time.time()
1317
- response = self._generate_ai_response_optimized(transcription)
1318
- rag_latency = time.time() - rag_start
1319
-
1320
- # 2. TTS Conversion
1321
- tts_start = time.time()
1322
- tts_audio_path = self._text_to_speech_optimized(response)
1323
- tts_latency = time.time() - tts_start
1324
-
1325
- total_latency = time.time() - total_start_time
1326
-
1327
- # Log latency metrics
1328
- self._log_latency_metrics({
1329
- 'rag': rag_latency,
1330
- 'tts': tts_latency,
1331
- 'total': total_latency
1332
- })
1333
-
1334
- # Gọi callback
1335
- if self.current_callback:
1336
- result = {
1337
- 'transcription': transcription,
1338
- 'response': response,
1339
- 'tts_audio': tts_audio_path,
1340
- 'status': 'completed',
1341
- 'latency': total_latency
1342
- }
1343
- self.current_callback(result)
1344
-
1345
- except Exception as e:
1346
- print(f"❌ Lỗi xử lý AI response: {e}")
1347
- traceback.print_exc()
1348
-
1349
- if self.current_callback:
1350
- self.current_callback({
1351
- 'transcription': transcription,
1352
- 'response': f"Xin lỗi, có lỗi xảy ra: {str(e)}",
1353
- 'tts_audio': None,
1354
- 'status': 'error'
1355
- })
1356
-
1357
- def _generate_ai_response_optimized(self, user_input: str) -> str:
1358
- """Sinh phản hồi AI với tối ưu hiệu suất"""
1359
- llm_start = time.time()
1360
- try:
1361
- # Thêm vào lịch sử
1362
- self.conversation_history.append({"role": "user", "content": user_input})
1363
-
1364
- # Tìm kiếm RAG
1365
- rag_start = time.time()
1366
- rag_results = self.rag_system.semantic_search(user_input, top_k=2) if self.rag_system else []
1367
- rag_latency = time.time() - rag_start
1368
-
1369
- context_text = "\n".join([f"- {result.get('text', str(result))}" for result in rag_results]) if rag_results else ""
1370
-
1371
- system_prompt = f"""Bạn là trợ lý AI thông minh chuyên về tiếng Việt.
1372
- Hãy trả lời ngắn gọn, tự nhiên và hữu ích (dưới 100 từ).
1373
- Thông tin tham khảo:
1374
- {context_text}
1375
- """
1376
-
1377
- messages = [{"role": "system", "content": system_prompt}]
1378
- messages.extend(self.conversation_history[-6:])
1379
-
1380
- llm_inference_start = time.time()
1381
- completion = self.client.chat.completions.create(
1382
- model=settings.MULTILINGUAL_LLM_MODEL,
1383
- messages=messages,
1384
- max_tokens=300,
1385
- temperature=0.7
1386
- )
1387
- ttft = time.time() - llm_inference_start
1388
-
1389
- response = completion.choices[0].message.content
1390
- self.conversation_history.append({"role": "assistant", "content": response})
1391
-
1392
- total_llm_latency = time.time() - llm_start
1393
-
1394
- # Giới hạn lịch sử
1395
- if len(self.conversation_history) > 12:
1396
- self.conversation_history = self.conversation_history[-12:]
1397
-
1398
- self._add_latency_sample('llm', total_llm_latency)
1399
- self._add_latency_sample('rag', rag_latency)
1400
-
1401
- print(f"✅ RAG Latency: {rag_latency:.2f}s")
1402
- print(f"✅ LLM TTFT: {ttft:.2f}s")
1403
- print(f"✅ Total LLM Latency: {total_llm_latency:.2f}s")
1404
-
1405
- return response
1406
-
1407
- except Exception as e:
1408
- print(f"❌ Lỗi tạo AI response: {e}")
1409
- return "Xin lỗi, tôi gặp lỗi khi tạo phản hồi. Vui lòng thử lại."
1410
-
1411
- def _text_to_speech_optimized(self, text: str) -> Optional[str]:
1412
- """Chuyển văn bản thành giọng nói với tối ưu"""
1413
- tts_start = time.time()
1414
- try:
1415
- if not text or text.startswith("❌") or text.startswith("Xin lỗi"):
1416
- return None
1417
-
1418
- tts_bytes = self.tts_service.text_to_speech(text, 'vi')
1419
- tts_latency = time.time() - tts_start
1420
-
1421
- if tts_bytes:
1422
- audio_path = self.tts_service.save_audio_to_file(tts_bytes)
1423
- self._add_latency_sample('tts', tts_latency)
1424
- return audio_path
1425
- except Exception as e:
1426
- print(f"❌ Lỗi TTS: {e}")
1427
- return None
1428
-
1429
  def process_streaming_audio(self, audio_data: tuple) -> Dict[str, Any]:
1430
- """Xử lý audio streaming manual mode với VOSK"""
1431
  if not audio_data:
1432
  return self._create_error_response("❌ Không có dữ liệu âm thanh")
1433
 
1434
  try:
1435
  sample_rate, audio_array = audio_data
1436
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1437
  # Khởi động VOSK stream tạm thời
1438
- self.vosk_asr.start_stream()
 
1439
 
1440
  # Xử lý audio với VOSK
1441
  result = self.vosk_asr.process_audio_chunk(audio_array, sample_rate)
1442
 
1443
- if result['is_final'] and result['text']:
1444
  transcription = result['text']
1445
- print(f"📝 Manual Transcription: {transcription}")
1446
 
1447
  # Tạo phản hồi AI
1448
  response = self._generate_ai_response_optimized(transcription)
@@ -1454,9 +1388,16 @@ Thông tin tham khảo:
1454
  'tts_audio': tts_audio_path,
1455
  'status': 'completed'
1456
  }
 
 
 
 
 
 
 
1457
  else:
1458
  return {
1459
- 'transcription': result['partial'] or "Đang nghe...",
1460
  'response': "",
1461
  'tts_audio': None,
1462
  'status': 'listening'
@@ -1464,6 +1405,7 @@ Thông tin tham khảo:
1464
 
1465
  except Exception as e:
1466
  print(f"❌ Lỗi xử lý streaming audio: {e}")
 
1467
  return self._create_error_response(f"❌ Lỗi: {str(e)}")
1468
 
1469
  def _create_error_response(self, message: str) -> Dict[str, Any]:
@@ -1524,9 +1466,10 @@ Thông tin tham khảo:
1524
  'queue_size': self.response_queue.qsize(),
1525
  'worker_threads': len(self.processing_threads),
1526
  'vosk_active': self.vosk_stream_active,
 
 
1527
  'last_update': time.strftime("%H:%M:%S")
1528
  }
1529
-
1530
  def clear_conversation(self):
1531
  """Xóa lịch sử hội thoại"""
1532
  self.conversation_history = []
 
990
 
991
  class VoskStreamingASR:
992
  def __init__(self, model_path: str = None):
993
+ """Khởi tạo VOSK ASR streaming với debug"""
994
  self.model = None
995
  self.recognizer = None
996
  self.sample_rate = 16000
997
  self.is_streaming = False
 
998
 
999
  # Tự động tải model nếu không có đường dẫn
1000
  if model_path is None:
1001
  model_path = self._download_vosk_model()
1002
 
1003
+ if model_path and os.path.exists(model_path):
1004
+ print(f"🔄 Đang tải VOSK model từ: {model_path}")
1005
+ try:
1006
+ self.model = Model(model_path)
1007
+ self.recognizer = KaldiRecognizer(self.model, self.sample_rate)
1008
+ self.recognizer.SetWords(True)
1009
+ print("✅ Đã tải VOSK model thành công")
1010
+ except Exception as e:
1011
+ print(f"❌ Lỗi khởi tạo VOSK model: {e}")
1012
  else:
1013
  print(f"❌ Không tìm thấy VOSK model tại: {model_path}")
1014
 
1015
  def _download_vosk_model(self):
1016
  """Tải VOSK model tiếng Việt tự động"""
1017
+ try:
1018
+ import urllib.request
1019
+ import zipfile
1020
+
1021
+ model_url = "https://alphacephei.com/vosk/models/vosk-model-small-vn-0.4.zip"
1022
+ model_dir = "models/vosk-model-small-vn-0.4"
1023
+ zip_path = "models/vosk-model-small-vn-0.4.zip"
1024
+
1025
+ # Tạo thư mục nếu chưa có
1026
+ os.makedirs("models", exist_ok=True)
1027
+
1028
+ if not os.path.exists(model_dir):
1029
+ print("📥 Đang tải VOSK Vietnamese model...")
1030
  urllib.request.urlretrieve(model_url, zip_path)
1031
 
1032
  with zipfile.ZipFile(zip_path, 'r') as zip_ref:
1033
  zip_ref.extractall("models/")
1034
 
1035
+ # Đảm bảo thư mục tồn tại
1036
+ if os.path.exists("models/vosk-model-small-vn-0.4"):
1037
+ os.rename("models/vosk-model-small-vn-0.4", model_dir)
1038
+
1039
+ if os.path.exists(zip_path):
1040
+ os.remove(zip_path)
1041
  print("✅ Đã tải VOSK model thành công")
1042
+
1043
+ return model_dir if os.path.exists(model_dir) else None
1044
+
1045
+ except Exception as e:
1046
+ print(f"❌ Lỗi tải VOSK model: {e}")
1047
+ return None
1048
 
1049
  def start_stream(self):
1050
  """Bắt đầu stream mới"""
1051
  if self.model is None:
1052
+ print("❌ VOSK model chưa được khởi tạo")
1053
  return False
1054
 
1055
+ try:
1056
+ self.recognizer = KaldiRecognizer(self.model, self.sample_rate)
1057
+ self.recognizer.SetWords(True)
1058
+ self.is_streaming = True
1059
+ print("🎤 Đã khởi động VOSK stream")
1060
+ return True
1061
+ except Exception as e:
1062
+ print(f"❌ Lỗi khởi động VOSK stream: {e}")
1063
+ return False
1064
 
1065
  def process_audio_chunk(self, audio_chunk: np.ndarray, sample_rate: int = None) -> Dict[str, Any]:
1066
+ """Xử lý audio chunk và trả về kết quả - FIXED VERSION"""
1067
  if self.recognizer is None or not self.is_streaming:
1068
  return {"text": "", "partial": "", "is_final": False}
1069
 
1070
  try:
1071
+ # DEBUG: Thông tin audio chunk
1072
+ print(f"🔊 Audio chunk: {len(audio_chunk)} samples, dtype: {audio_chunk.dtype}, max: {np.max(audio_chunk):.4f}")
1073
+
1074
+ # Chuẩn hóa audio - QUAN TRỌNG: VOSK cần audio ở dạng int16
1075
  if sample_rate and sample_rate != self.sample_rate:
1076
  audio_chunk = self._resample_audio(audio_chunk, sample_rate, self.sample_rate)
1077
 
1078
+ # Đảm bảo là int16 với giá trị phù hợp
1079
  if audio_chunk.dtype != np.int16:
1080
+ if audio_chunk.dtype in [np.float32, np.float64]:
1081
+ # Audio float cần được scale về [-32768, 32767]
1082
+ audio_chunk = (audio_chunk * 32767).astype(np.int16)
1083
+ else:
1084
+ audio_chunk = audio_chunk.astype(np.int16)
1085
+
1086
+ # Kiểm tra âm lượng
1087
+ audio_rms = np.sqrt(np.mean(audio_chunk.astype(np.float32)**2)) / 32767.0
1088
+ print(f"📊 Audio RMS: {audio_rms:.4f}")
1089
+
1090
+ if audio_rms < 0.01: # Âm lượng quá thấp
1091
+ print("⚠️ Âm lượng quá thấp, bỏ qua")
1092
+ return {"text": "", "partial": "", "is_final": False}
1093
 
1094
  # Chuyển đổi sang bytes
1095
  audio_bytes = audio_chunk.tobytes()
 
1097
  # Xử lý với VOSK
1098
  if self.recognizer.AcceptWaveform(audio_bytes):
1099
  # Kết quả cuối cùng
1100
+ result_json = self.recognizer.Result()
1101
+ result = json.loads(result_json)
1102
  text = result.get('text', '').strip()
1103
+ print(f"✅ VOSK Final Result: '{text}'")
1104
  if text:
1105
  return {"text": text, "partial": "", "is_final": True}
1106
  else:
1107
  # Kết quả tạm thời
1108
+ partial_json = self.recognizer.PartialResult()
1109
+ partial_result = json.loads(partial_json)
1110
  partial_text = partial_result.get('partial', '').strip()
1111
+ if partial_text:
1112
+ print(f"🎯 VOSK Partial: '{partial_text}'")
1113
+ return {"text": "", "partial": partial_text, "is_final": False}
1114
 
1115
  except Exception as e:
1116
  print(f"❌ Lỗi VOSK processing: {e}")
1117
+ traceback.print_exc()
1118
 
1119
  return {"text": "", "partial": "", "is_final": False}
1120
 
1121
  def stop_stream(self) -> str:
1122
  """Kết thúc stream và lấy kết quả cuối"""
1123
  if self.recognizer:
1124
+ try:
1125
+ result_json = self.recognizer.FinalResult()
1126
+ result = json.loads(result_json)
1127
+ text = result.get('text', '').strip()
1128
+ self.is_streaming = False
1129
+ print(f"🛑 VOSK Final: '{text}'")
1130
+ return text
1131
+ except Exception as e:
1132
+ print(f"❌ Lỗi khi dừng VOSK stream: {e}")
1133
  return ""
1134
 
1135
  def _resample_audio(self, audio: np.ndarray, orig_sr: int, target_sr: int) -> np.ndarray:
1136
+ """Resample audio với chất lượng tốt hơn"""
1137
  if orig_sr == target_sr:
1138
  return audio
1139
  try:
1140
  from scipy import signal
1141
+ # Tính số sample mới
1142
+ num_samples = int(len(audio) * target_sr / orig_sr)
1143
+ resampled_audio = signal.resample(audio, num_samples)
1144
  return resampled_audio.astype(np.int16)
1145
+ except Exception as e:
1146
+ print(f"❌ Lỗi resample audio: {e}")
1147
  return audio
1148
 
1149
  class StreamingVoiceService:
 
1152
  self.rag_system = rag_system
1153
  self.tts_service = tts_service
1154
 
1155
+ # Khởi tạo VOSK ASR - FIXED: Thêm timeout và retry
1156
+ print("🔄 Đang khởi tạo VOSK ASR...")
1157
  self.vosk_asr = VoskStreamingASR()
1158
 
1159
  # Khởi tạo VAD
 
1161
  self.is_listening = False
1162
  self.speech_callback = None
1163
  self.is_processing = False
 
1164
 
1165
  # Conversation context
1166
  self.conversation_history = []
 
1172
  self.processing_threads = []
1173
  self.max_workers = 2
1174
 
1175
+ # Streaming state
1176
  self.vosk_stream_active = False
1177
  self.last_voice_time = 0
1178
+ self.silence_timeout = 3.0 # Tăng timeout lên 3 giây
1179
+
1180
+ # Audio buffer để cải thiện nhận diện
1181
+ self.audio_buffer = []
1182
+ self.buffer_duration = 1.0 # Buffer 1 giây
1183
+ self.max_buffer_samples = 16000 # 1 giây ở 16kHz
1184
 
1185
  # Latency tracking
1186
  self.latency_metrics = {
1187
+ 'asr': [], 'rag': [], 'llm': [], 'tts': [], 'total': [],
1188
+ 'vad_detection': [], 'queue_waiting': [], 'vosk_processing': []
 
 
 
 
 
 
1189
  }
1190
 
1191
  self.current_callback = None
1192
 
1193
  def start_listening(self, speech_callback: Callable) -> bool:
1194
+ """Bắt đầu lắng nghe với VOSK streaming - FIXED VERSION"""
1195
  if self.is_listening:
1196
+ print("⚠️ Đã đang lắng nghe")
1197
  return False
1198
 
1199
  self.current_callback = speech_callback
1200
 
1201
+ # Kiểm tra VOSK model
1202
+ if self.vosk_asr.model is None:
1203
+ print("❌ VOSK model không khả dụng")
1204
+ if self.current_callback:
1205
+ self.current_callback({
1206
+ 'transcription': "Lỗi: VOSK model không khả dụng",
1207
+ 'response': "Không thể khởi động nhận diện giọng nói",
1208
+ 'tts_audio': None,
1209
+ 'status': 'error'
1210
+ })
1211
+ return False
1212
+
1213
  # Khởi động VOSK stream
1214
  if not self.vosk_asr.start_stream():
1215
+ print("❌ Không thể khởi động VOSK stream")
1216
  return False
1217
 
1218
+ # Khởi động VAD
1219
  success = self.vad_processor.start_stream(self._on_speech_detected)
1220
 
1221
  if success:
 
1223
  self.is_processing = False
1224
  self.vosk_stream_active = True
1225
  self.last_voice_time = time.time()
1226
+ self.audio_buffer = []
1227
 
1228
  # Khởi động worker threads
1229
  if not self.processing_threads:
 
1240
  threading.Thread(target=self._vosk_streaming_monitor, daemon=True).start()
1241
 
1242
  print("🎙️ Đã bắt đầu lắng nghe với VOSK ASR streaming")
1243
+
1244
+ # Thông báo trạng thái
1245
+ if self.current_callback:
1246
+ self.current_callback({
1247
+ 'transcription': "Đã bắt đầu lắng nghe... Hãy nói gì đó",
1248
+ 'response': "",
1249
+ 'tts_audio': None,
1250
+ 'status': 'listening'
1251
+ })
1252
+
1253
  return True
1254
+
1255
  return False
1256
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1257
  def _vosk_streaming_monitor(self):
1258
  """Theo dõi VOSK streaming và xử lý kết quả real-time"""
1259
  while self.is_listening and self.vosk_stream_active:
1260
  try:
 
1261
  current_time = time.time()
1262
  silence_duration = current_time - self.last_voice_time
1263
 
1264
+ # Xử lý audio buffer nếu có dữ liệu
1265
+ if self.audio_buffer and silence_duration > 0.5: # 0.5 giây im lặng
1266
+ combined_audio = np.concatenate(self.audio_buffer)
1267
+ if len(combined_audio) > 1600: # Ít nhất 0.1 giây audio
1268
+ result = self.vosk_asr.process_audio_chunk(combined_audio, 16000)
1269
+ self._handle_vosk_result(result)
1270
+ self.audio_buffer = []
1271
+
1272
+ # Timeout im lặng
1273
  if silence_duration > self.silence_timeout and self.partial_transcription:
1274
+ print(f"⏰ Silence timeout, xử lý: '{self.partial_transcription}'")
1275
+ if len(self.partial_transcription) > 2: # Chỉ xử nếu có nội dung
1276
+ self._process_final_transcription(self.partial_transcription)
1277
  self.partial_transcription = ""
1278
+ self.vosk_asr.start_stream()
1279
 
1280
+ time.sleep(0.1)
1281
 
1282
  except Exception as e:
1283
  print(f"❌ Lỗi VOSK monitor: {e}")
1284
  break
1285
 
1286
  def _on_speech_detected(self, speech_audio: np.ndarray, sample_rate: int):
1287
+ """Callback khi VAD phát hiện speech - FIXED VERSION"""
1288
+ if not self.vosk_stream_active or not self.is_listening:
 
 
1289
  return
1290
 
1291
  # Cập nhật thời gian có giọng nói
1292
  self.last_voice_time = time.time()
1293
 
1294
+ # Thêm vào audio buffer để cải thiện nhận diện
1295
+ self.audio_buffer.append(speech_audio)
 
 
1296
 
1297
+ # Giới hạn buffer size
1298
+ total_samples = sum(len(chunk) for chunk in self.audio_buffer)
1299
+ if total_samples > self.max_buffer_samples:
1300
+ # Giữ lại các chunk gần nhất
1301
+ while total_samples > self.max_buffer_samples and len(self.audio_buffer) > 1:
1302
+ removed = self.audio_buffer.pop(0)
1303
+ total_samples -= len(removed)
1304
 
1305
+ print(f"🎯 VAD detected: {len(speech_audio)} samples, Buffer: {len(self.audio_buffer)} chunks")
1306
+
1307
+ def _handle_vosk_result(self, result: Dict[str, Any]):
1308
+ """Xử lý kết quả từ VOSK"""
1309
  # Xử lý kết quả partial
1310
  if result['partial'] and len(result['partial']) > 1:
1311
  self.partial_transcription = result['partial']
1312
+ print(f"🎯 VOSK Partial: '{result['partial']}'")
1313
 
1314
  # Gửi partial result real-time
1315
  if self.current_callback:
 
1322
 
1323
  # Xử lý kết quả final
1324
  if result['is_final'] and result['text'] and len(result['text']) > 1:
1325
+ print(f"✅ VOSK Final: '{result['text']}'")
1326
  self._process_final_transcription(result['text'])
1327
  self.partial_transcription = ""
1328
+ self.audio_buffer = [] # Clear buffer
1329
  self.vosk_asr.start_stream() # Bắt đầu stream mới
 
 
 
1330
 
1331
  def _process_final_transcription(self, transcription: str):
1332
  """Xử lý transcription cuối cùng"""
1333
  if not transcription or len(transcription.strip()) < 2:
1334
  return
1335
 
1336
+ print(f"📝 Final Transcription: '{transcription}'")
1337
  self.current_transcription = transcription
1338
 
1339
  # Đưa vào queue để xử lý
1340
  try:
1341
  self.response_queue.put(transcription, timeout=0.5)
1342
+ print(f"📦 Đã đưa vào queue: '{transcription}'")
1343
  except queue.Full:
1344
  print("⚠️ Queue đầy, bỏ qua transcription")
1345
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1346
  def process_streaming_audio(self, audio_data: tuple) -> Dict[str, Any]:
1347
+ """Xử lý audio streaming manual mode với VOSK - FIXED VERSION"""
1348
  if not audio_data:
1349
  return self._create_error_response("❌ Không có dữ liệu âm thanh")
1350
 
1351
  try:
1352
  sample_rate, audio_array = audio_data
1353
 
1354
+ print(f"🎤 Manual audio: {len(audio_array)} samples, {sample_rate}Hz")
1355
+
1356
+ # Kiểm tra âm lượng
1357
+ if isinstance(audio_array, np.ndarray):
1358
+ if audio_array.dtype in [np.float32, np.float64]:
1359
+ audio_rms = np.sqrt(np.mean(audio_array**2))
1360
+ print(f"📊 Manual audio RMS: {audio_rms:.4f}")
1361
+
1362
+ if audio_rms < 0.01:
1363
+ return {
1364
+ 'transcription': "Âm thanh quá nhỏ, hãy nói to hơn",
1365
+ 'response': "",
1366
+ 'tts_audio': None,
1367
+ 'status': 'listening'
1368
+ }
1369
+
1370
  # Khởi động VOSK stream tạm thời
1371
+ if not self.vosk_asr.start_stream():
1372
+ return self._create_error_response("❌ Không thể khởi động VOSK")
1373
 
1374
  # Xử lý audio với VOSK
1375
  result = self.vosk_asr.process_audio_chunk(audio_array, sample_rate)
1376
 
1377
+ if result['is_final'] and result['text'] and len(result['text']) > 1:
1378
  transcription = result['text']
1379
+ print(f"📝 Manual Transcription: '{transcription}'")
1380
 
1381
  # Tạo phản hồi AI
1382
  response = self._generate_ai_response_optimized(transcription)
 
1388
  'tts_audio': tts_audio_path,
1389
  'status': 'completed'
1390
  }
1391
+ elif result['partial']:
1392
+ return {
1393
+ 'transcription': result['partial'],
1394
+ 'response': "",
1395
+ 'tts_audio': None,
1396
+ 'status': 'listening'
1397
+ }
1398
  else:
1399
  return {
1400
+ 'transcription': "Đang nghe... Hãy nói rõ hơn",
1401
  'response': "",
1402
  'tts_audio': None,
1403
  'status': 'listening'
 
1405
 
1406
  except Exception as e:
1407
  print(f"❌ Lỗi xử lý streaming audio: {e}")
1408
+ traceback.print_exc()
1409
  return self._create_error_response(f"❌ Lỗi: {str(e)}")
1410
 
1411
  def _create_error_response(self, message: str) -> Dict[str, Any]:
 
1466
  'queue_size': self.response_queue.qsize(),
1467
  'worker_threads': len(self.processing_threads),
1468
  'vosk_active': self.vosk_stream_active,
1469
+ 'audio_buffer_chunks': len(self.audio_buffer),
1470
+ 'last_voice_time': time.strftime("%H:%M:%S", time.localtime(self.last_voice_time)),
1471
  'last_update': time.strftime("%H:%M:%S")
1472
  }
 
1473
  def clear_conversation(self):
1474
  """Xóa lịch sử hội thoại"""
1475
  self.conversation_history = []