datbkpro commited on
Commit
4c41d4a
·
verified ·
1 Parent(s): be8cb43

Update services/streaming_voice_service.py

Browse files
Files changed (1) hide show
  1. services/streaming_voice_service.py +93 -15
services/streaming_voice_service.py CHANGED
@@ -39,6 +39,15 @@ class StreamingVoiceService:
39
  self.response_queue = queue.Queue()
40
  self.current_task = None
41
 
 
 
 
 
 
 
 
 
 
42
  def start_listening(self, speech_callback: Callable) -> bool:
43
  """Bắt đầu lắng nghe với VAD tối ưu"""
44
  if self.is_listening:
@@ -99,11 +108,13 @@ class StreamingVoiceService:
99
 
100
  with self.processing_lock:
101
  self.is_processing = True
102
-
103
  try:
104
  # Chuyển đổi speech thành text
 
 
105
  transcription = self._transcribe_audio(speech_audio, sample_rate)
106
-
107
  if not transcription or len(transcription.strip()) < 2:
108
  print("⚠️ Transcription quá ngắn hoặc trống")
109
  return
@@ -112,11 +123,21 @@ class StreamingVoiceService:
112
  self.current_transcription = transcription
113
 
114
  # Tạo phản hồi AI
 
115
  response = self._generate_ai_response(transcription)
116
-
117
  # Tạo TTS
 
118
  tts_audio_path = self._text_to_speech(response)
119
-
 
 
 
 
 
 
 
 
120
  # Gửi kết quả đến callback
121
  if self.speech_callback:
122
  self.speech_callback({
@@ -132,7 +153,7 @@ class StreamingVoiceService:
132
  finally:
133
  with self.processing_lock:
134
  self.is_processing = False
135
- # THÊM LẠI PHƯƠNG THỨC process_streaming_audio ĐÃ BỊ THIẾU
136
  def process_streaming_audio(self, audio_data: tuple) -> Dict[str, Any]:
137
  """Xử lý audio streaming (phương thức cũ cho compatibility với Gradio)"""
138
  if not audio_data:
@@ -284,6 +305,7 @@ class StreamingVoiceService:
284
 
285
  def _transcribe_audio(self, audio_data: np.ndarray, sample_rate: int) -> Optional[str]:
286
  """Chuyển audio -> text với xử lý cải tiến"""
 
287
  try:
288
  # Đảm bảo kiểu dữ liệu và chuẩn hóa
289
  if audio_data.dtype != np.int16:
@@ -303,13 +325,13 @@ class StreamingVoiceService:
303
  sample_rate = target_sample_rate
304
 
305
  # Giới hạn độ dài audio
306
- max_duration = 15 # giây
307
  max_samples = sample_rate * max_duration
308
  if len(audio_data) > max_samples:
309
  audio_data = audio_data[:max_samples]
310
 
311
  # Đảm bảo audio đủ dài
312
- min_duration = 0.8 # giây
313
  min_samples = int(sample_rate * min_duration)
314
  if len(audio_data) < min_samples:
315
  padding = np.zeros(min_samples - len(audio_data), dtype=np.int16)
@@ -322,7 +344,9 @@ class StreamingVoiceService:
322
  sf.write(buffer, audio_data, sample_rate, format='wav', subtype='PCM_16')
323
  buffer.seek(0)
324
 
 
325
  # Gọi API Whisper
 
326
  try:
327
  transcription = self.client.audio.transcriptions.create(
328
  model=settings.WHISPER_MODEL,
@@ -334,7 +358,7 @@ class StreamingVoiceService:
334
  except Exception as e:
335
  print(f"❌ Lỗi Whisper API: {e}")
336
  return None
337
-
338
  # Xử lý response
339
  if hasattr(transcription, 'text'):
340
  result = transcription.text.strip()
@@ -342,7 +366,8 @@ class StreamingVoiceService:
342
  result = transcription.strip()
343
  else:
344
  result = str(transcription).strip()
345
-
 
346
  print(f"✅ Transcription: '{result}'")
347
  return result
348
 
@@ -352,12 +377,15 @@ class StreamingVoiceService:
352
 
353
  def _generate_ai_response(self, user_input: str) -> str:
354
  """Sinh phản hồi AI với xử lý lỗi"""
 
355
  try:
356
  # Thêm vào lịch sử
357
  self.conversation_history.append({"role": "user", "content": user_input})
358
 
359
  # Tìm ki���m RAG
 
360
  rag_results = self.rag_system.semantic_search(user_input, top_k=2)
 
361
  context_text = "\n".join([f"- {result.get('text', str(result))}" for result in rag_results]) if rag_results else ""
362
 
363
  system_prompt = f"""Bạn là trợ lý AI thông minh chuyên về tiếng Việt.
@@ -371,19 +399,21 @@ Thông tin tham khảo:
371
  messages.extend(self.conversation_history[-6:])
372
 
373
  completion = self.client.chat.completions.create(
374
- model="llama-3.1-8b-instant",
375
  messages=messages,
376
- max_tokens=150,
377
  temperature=0.7
378
  )
379
-
380
  response = completion.choices[0].message.content
381
  self.conversation_history.append({"role": "assistant", "content": response})
382
-
383
  # Giới hạn lịch sử
384
  if len(self.conversation_history) > 12:
385
  self.conversation_history = self.conversation_history[-12:]
386
-
 
 
387
  return response
388
 
389
  except Exception as e:
@@ -391,14 +421,18 @@ Thông tin tham khảo:
391
  return "Xin lỗi, tôi gặp lỗi khi tạo phản hồi. Vui lòng thử lại."
392
 
393
  def _text_to_speech(self, text: str) -> Optional[str]:
394
- """Chuyển văn bản thành giọng nói"""
 
395
  try:
396
  if not text or text.startswith("❌") or text.startswith("Xin lỗi"):
397
  return None
398
 
399
  tts_bytes = self.tts_service.text_to_speech(text, 'vi')
 
 
400
  if tts_bytes:
401
  audio_path = self.tts_service.save_audio_to_file(tts_bytes)
 
402
  print(f"✅ Đã tạo TTS: {audio_path}")
403
  return audio_path
404
  except Exception as e:
@@ -421,7 +455,51 @@ Thông tin tham khảo:
421
  self.conversation_history = []
422
  self.current_transcription = ""
423
  print("🗑️ Đã xóa lịch sử hội thoại")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
424
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
425
  def get_conversation_state(self) -> dict:
426
  """Lấy trạng thái hội thoại"""
427
  return {
 
39
  self.response_queue = queue.Queue()
40
  self.current_task = None
41
 
42
+ #Latency
43
+ self.latency_metrics = {
44
+ 'asr': [],
45
+ 'rag' : [],
46
+ 'llm' : [],
47
+ 'tts' : [],
48
+ 'total' : []
49
+ }
50
+
51
  def start_listening(self, speech_callback: Callable) -> bool:
52
  """Bắt đầu lắng nghe với VAD tối ưu"""
53
  if self.is_listening:
 
108
 
109
  with self.processing_lock:
110
  self.is_processing = True
111
+ total_start_time = time.time()
112
  try:
113
  # Chuyển đổi speech thành text
114
+ # 1. ASR
115
+ asr_start = time.time()
116
  transcription = self._transcribe_audio(speech_audio, sample_rate)
117
+ asr_latency = time.time() - asr_start
118
  if not transcription or len(transcription.strip()) < 2:
119
  print("⚠️ Transcription quá ngắn hoặc trống")
120
  return
 
123
  self.current_transcription = transcription
124
 
125
  # Tạo phản hồi AI
126
+ rag_start = time.time()
127
  response = self._generate_ai_response(transcription)
128
+ rag_latency = time.time() - rag_start
129
  # Tạo TTS
130
+ tts_start = time.time()
131
  tts_audio_path = self._text_to_speech(response)
132
+ tts_latency = time.time() - tts_start
133
+ total_latency = time.time() - total_start_time
134
+ # Log latency metrics
135
+ self._log_latency_metrics({
136
+ 'asr': asr_latency,
137
+ 'rag': rag_latency,
138
+ 'tts': tts_latency,
139
+ 'total': total_latency
140
+ })
141
  # Gửi kết quả đến callback
142
  if self.speech_callback:
143
  self.speech_callback({
 
153
  finally:
154
  with self.processing_lock:
155
  self.is_processing = False
156
+
157
  def process_streaming_audio(self, audio_data: tuple) -> Dict[str, Any]:
158
  """Xử lý audio streaming (phương thức cũ cho compatibility với Gradio)"""
159
  if not audio_data:
 
305
 
306
  def _transcribe_audio(self, audio_data: np.ndarray, sample_rate: int) -> Optional[str]:
307
  """Chuyển audio -> text với xử lý cải tiến"""
308
+ asr_start = time.time()
309
  try:
310
  # Đảm bảo kiểu dữ liệu và chuẩn hóa
311
  if audio_data.dtype != np.int16:
 
325
  sample_rate = target_sample_rate
326
 
327
  # Giới hạn độ dài audio
328
+ max_duration = 30 # giây
329
  max_samples = sample_rate * max_duration
330
  if len(audio_data) > max_samples:
331
  audio_data = audio_data[:max_samples]
332
 
333
  # Đảm bảo audio đủ dài
334
+ min_duration = 2 # giây
335
  min_samples = int(sample_rate * min_duration)
336
  if len(audio_data) < min_samples:
337
  padding = np.zeros(min_samples - len(audio_data), dtype=np.int16)
 
344
  sf.write(buffer, audio_data, sample_rate, format='wav', subtype='PCM_16')
345
  buffer.seek(0)
346
 
347
+
348
  # Gọi API Whisper
349
+ api_start = time.time()
350
  try:
351
  transcription = self.client.audio.transcriptions.create(
352
  model=settings.WHISPER_MODEL,
 
358
  except Exception as e:
359
  print(f"❌ Lỗi Whisper API: {e}")
360
  return None
361
+ api_latency = time.time() - api_start
362
  # Xử lý response
363
  if hasattr(transcription, 'text'):
364
  result = transcription.text.strip()
 
366
  result = transcription.strip()
367
  else:
368
  result = str(transcription).strip()
369
+ total_asr_latency = time.time() - asr_start
370
+ print(f"✅ ASR Latency: {total_asr_latency:.2f}s (API: {api_latency:.2f}s)")
371
  print(f"✅ Transcription: '{result}'")
372
  return result
373
 
 
377
 
378
  def _generate_ai_response(self, user_input: str) -> str:
379
  """Sinh phản hồi AI với xử lý lỗi"""
380
+ llm_start = time.time()
381
  try:
382
  # Thêm vào lịch sử
383
  self.conversation_history.append({"role": "user", "content": user_input})
384
 
385
  # Tìm ki���m RAG
386
+ rag_start = time.time()
387
  rag_results = self.rag_system.semantic_search(user_input, top_k=2)
388
+ rag_latency = time.time() - rag_start
389
  context_text = "\n".join([f"- {result.get('text', str(result))}" for result in rag_results]) if rag_results else ""
390
 
391
  system_prompt = f"""Bạn là trợ lý AI thông minh chuyên về tiếng Việt.
 
399
  messages.extend(self.conversation_history[-6:])
400
 
401
  completion = self.client.chat.completions.create(
402
+ model=settings.MULTILINGUAL_LLM_MODEL,
403
  messages=messages,
404
+ max_tokens=300,
405
  temperature=0.7
406
  )
407
+ ttft = time.time() - llm_inference_start # Time To First Token
408
  response = completion.choices[0].message.content
409
  self.conversation_history.append({"role": "assistant", "content": response})
410
+ total_llm_latency = time.time() - llm_start
411
  # Giới hạn lịch sử
412
  if len(self.conversation_history) > 12:
413
  self.conversation_history = self.conversation_history[-12:]
414
+ print(f"✅ RAG Latency: {rag_latency:.2f}s")
415
+ print(f"✅ LLM TTFT: {ttft:.2f}s")
416
+ print(f"✅ Total LLM Latency: {total_llm_latency:.2f}s")
417
  return response
418
 
419
  except Exception as e:
 
421
  return "Xin lỗi, tôi gặp lỗi khi tạo phản hồi. Vui lòng thử lại."
422
 
423
  def _text_to_speech(self, text: str) -> Optional[str]:
424
+ """Chuyển văn bản thành giọng nói với latency tracking"""
425
+ tts_start = time.time()
426
  try:
427
  if not text or text.startswith("❌") or text.startswith("Xin lỗi"):
428
  return None
429
 
430
  tts_bytes = self.tts_service.text_to_speech(text, 'vi')
431
+ tts_latency = time.time() - tts_start
432
+
433
  if tts_bytes:
434
  audio_path = self.tts_service.save_audio_to_file(tts_bytes)
435
+ print(f"✅ TTS Latency: {tts_latency:.2f}s")
436
  print(f"✅ Đã tạo TTS: {audio_path}")
437
  return audio_path
438
  except Exception as e:
 
455
  self.conversation_history = []
456
  self.current_transcription = ""
457
  print("🗑️ Đã xóa lịch sử hội thoại")
458
+ def _log_latency_metrics(self, latencies: dict):
459
+ """Log và theo dõi latency metrics"""
460
+ for key, value in latencies.items():
461
+ if key in self.latency_metrics:
462
+ self.latency_metrics[key].append(value)
463
+
464
+ # Giữ chỉ 100 mẫu gần nhất
465
+ if len(self.latency_metrics[key]) > 100:
466
+ self.latency_metrics[key] = self.latency_metrics[key][-100:]
467
+
468
+ # Log tổng hợp
469
+ print("📊 LATENCY REPORT:")
470
+ print(f" ASR: {latencies['asr']:.2f}s")
471
+ print(f" RAG: {latencies['rag']:.2f}s")
472
+ print(f" TTS: {latencies['tts']:.2f}s")
473
+ print(f" TOTAL: {latencies['total']:.2f}s")
474
+
475
+ # Tính toán và hiển thị latency trung bình
476
+ self._print_average_latencies()
477
+
478
+ def _print_average_latencies(self):
479
+ """In ra latency trung bình"""
480
+ if len(self.latency_metrics['total']) > 0:
481
+ print("📈 AVERAGE LATENCIES (last 10 requests):")
482
+ for component in ['asr', 'rag', 'tts', 'total']:
483
+ recent_latencies = self.latency_metrics[component][-10:]
484
+ if recent_latencies:
485
+ avg = sum(recent_latencies) / len(recent_latencies)
486
+ print(f" {component.upper()}: {avg:.2f}s")
487
 
488
+ def get_latency_stats(self) -> dict:
489
+ """Lấy thống kê latency"""
490
+ stats = {}
491
+ for component, latencies in self.latency_metrics.items():
492
+ if latencies:
493
+ stats[component] = {
494
+ 'avg': sum(latencies) / len(latencies),
495
+ 'min': min(latencies),
496
+ 'max': max(latencies),
497
+ 'count': len(latencies),
498
+ 'recent_avg': sum(latencies[-10:]) / min(10, len(latencies)) if latencies else 0
499
+ }
500
+ else:
501
+ stats[component] = {'avg': 0, 'min': 0, 'max': 0, 'count': 0, 'recent_avg': 0}
502
+ return stats
503
  def get_conversation_state(self) -> dict:
504
  """Lấy trạng thái hội thoại"""
505
  return {