Update services/streaming_voice_service.py
Browse files
services/streaming_voice_service.py
CHANGED
|
@@ -9,6 +9,7 @@ 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
|
|
|
|
| 12 |
|
| 13 |
|
| 14 |
class StreamingVoiceService:
|
|
@@ -18,7 +19,7 @@ class StreamingVoiceService:
|
|
| 18 |
self.tts_service = tts_service
|
| 19 |
|
| 20 |
# Khởi tạo VAD
|
| 21 |
-
self.vad_processor =
|
| 22 |
self.is_listening = False
|
| 23 |
self.speech_callback = None
|
| 24 |
|
|
@@ -364,197 +365,3 @@ Thông tin tham khảo:
|
|
| 364 |
'current_transcription': self.current_transcription,
|
| 365 |
'last_update': time.strftime("%H:%M:%S")
|
| 366 |
}
|
| 367 |
-
# import io
|
| 368 |
-
# import numpy as np
|
| 369 |
-
# import soundfile as sf
|
| 370 |
-
# import time # THÊM IMPORT NÀY
|
| 371 |
-
# import traceback
|
| 372 |
-
# from groq import Groq
|
| 373 |
-
# from typing import Optional, Dict, Any
|
| 374 |
-
# from config.settings import settings
|
| 375 |
-
# from core.rag_system import EnhancedRAGSystem
|
| 376 |
-
# from core.tts_service import EnhancedTTSService
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
# class StreamingVoiceService:
|
| 380 |
-
# def __init__(self, groq_client: Groq, rag_system: EnhancedRAGSystem, tts_service: EnhancedTTSService):
|
| 381 |
-
# self.client = groq_client
|
| 382 |
-
# self.rag_system = rag_system
|
| 383 |
-
# self.tts_service = tts_service
|
| 384 |
-
|
| 385 |
-
# # Conversation context
|
| 386 |
-
# self.conversation_history = []
|
| 387 |
-
# self.current_transcription = ""
|
| 388 |
-
|
| 389 |
-
# def process_streaming_audio(self, audio_data: tuple) -> Dict[str, Any]:
|
| 390 |
-
# """Xử lý audio streaming từ Gradio microphone component"""
|
| 391 |
-
# if not audio_data:
|
| 392 |
-
# return {
|
| 393 |
-
# 'transcription': "❌ Không có dữ liệu âm thanh",
|
| 394 |
-
# 'response': "Vui lòng nói lại",
|
| 395 |
-
# 'tts_audio': None
|
| 396 |
-
# }
|
| 397 |
-
|
| 398 |
-
# try:
|
| 399 |
-
# # Lấy dữ liệu audio từ Gradio
|
| 400 |
-
# sample_rate, audio_array = audio_data
|
| 401 |
-
|
| 402 |
-
# print(f"🎯 Nhận audio: {len(audio_array)} samples, SR: {sample_rate}")
|
| 403 |
-
|
| 404 |
-
# # Kiểm tra audio có dữ liệu không
|
| 405 |
-
# if len(audio_array) == 0 or np.max(np.abs(audio_array)) < 0.01:
|
| 406 |
-
# return {
|
| 407 |
-
# 'transcription': "❌ Âm thanh quá yếu",
|
| 408 |
-
# 'response': "Xin vui lòng nói to hơn và rõ hơn",
|
| 409 |
-
# 'tts_audio': None
|
| 410 |
-
# }
|
| 411 |
-
|
| 412 |
-
# # Chuyển đổi thành văn bản
|
| 413 |
-
# transcription = self._transcribe_audio(audio_array, sample_rate)
|
| 414 |
-
|
| 415 |
-
# if not transcription or len(transcription.strip()) == 0:
|
| 416 |
-
# return {
|
| 417 |
-
# 'transcription': "❌ Không nghe rõ",
|
| 418 |
-
# 'response': "Xin vui lòng nói lại rõ hơn",
|
| 419 |
-
# 'tts_audio': None
|
| 420 |
-
# }
|
| 421 |
-
|
| 422 |
-
# print(f"📝 Đã chuyển đổi: {transcription}")
|
| 423 |
-
|
| 424 |
-
# # Cập nhật transcription hiện tại
|
| 425 |
-
# self.current_transcription = transcription
|
| 426 |
-
|
| 427 |
-
# # Tạo phản hồi AI
|
| 428 |
-
# response = self._generate_ai_response(transcription)
|
| 429 |
-
|
| 430 |
-
# # Tạo TTS
|
| 431 |
-
# tts_audio_path = self._text_to_speech(response)
|
| 432 |
-
|
| 433 |
-
# return {
|
| 434 |
-
# 'transcription': transcription,
|
| 435 |
-
# 'response': response,
|
| 436 |
-
# 'tts_audio': tts_audio_path
|
| 437 |
-
# }
|
| 438 |
-
|
| 439 |
-
# except Exception as e:
|
| 440 |
-
# print(f"❌ Lỗi xử lý streaming audio: {e}")
|
| 441 |
-
# print(f"Chi tiết lỗi: {traceback.format_exc()}")
|
| 442 |
-
# return {
|
| 443 |
-
# 'transcription': f"❌ Lỗi: {str(e)}",
|
| 444 |
-
# 'response': "Xin lỗi, có lỗi xảy ra trong quá trình xử lý",
|
| 445 |
-
# 'tts_audio': None
|
| 446 |
-
# }
|
| 447 |
-
|
| 448 |
-
# def _transcribe_audio(self, audio_data: np.ndarray, sample_rate: int) -> Optional[str]:
|
| 449 |
-
# """Chuyển audio -> text"""
|
| 450 |
-
# try:
|
| 451 |
-
# # Chuẩn hóa audio data
|
| 452 |
-
# if audio_data.ndim > 1:
|
| 453 |
-
# audio_data = np.mean(audio_data, axis=1) # Chuyển sang mono
|
| 454 |
-
|
| 455 |
-
# # Normalize âm lượng
|
| 456 |
-
# audio_max = np.max(np.abs(audio_data))
|
| 457 |
-
# if audio_max > 0:
|
| 458 |
-
# audio_data = audio_data / audio_max
|
| 459 |
-
|
| 460 |
-
# # Giới hạn độ dài audio (tránh quá dài)
|
| 461 |
-
# max_duration = 10 # giây
|
| 462 |
-
# max_samples = sample_rate * max_duration
|
| 463 |
-
# if len(audio_data) > max_samples:
|
| 464 |
-
# audio_data = audio_data[:max_samples]
|
| 465 |
-
# print(f"⚠️ Cắt audio xuống còn {max_duration} giây")
|
| 466 |
-
|
| 467 |
-
# buffer = io.BytesIO()
|
| 468 |
-
# sf.write(buffer, audio_data, sample_rate, format='wav', subtype='PCM_16')
|
| 469 |
-
# buffer.seek(0)
|
| 470 |
-
|
| 471 |
-
# # Gọi API Whisper
|
| 472 |
-
# transcription = self.client.audio.transcriptions.create(
|
| 473 |
-
# model=settings.WHISPER_MODEL,
|
| 474 |
-
# file=("speech.wav", buffer.read(), "audio/wav"),
|
| 475 |
-
# response_format="text",
|
| 476 |
-
# language="vi"
|
| 477 |
-
# )
|
| 478 |
-
|
| 479 |
-
# # Xử lý response
|
| 480 |
-
# if hasattr(transcription, 'text'):
|
| 481 |
-
# result = transcription.text.strip()
|
| 482 |
-
# elif isinstance(transcription, str):
|
| 483 |
-
# result = transcription.strip()
|
| 484 |
-
# else:
|
| 485 |
-
# result = str(transcription).strip()
|
| 486 |
-
|
| 487 |
-
# print(f"✅ Transcription thành công: {result}")
|
| 488 |
-
# return result
|
| 489 |
-
|
| 490 |
-
# except Exception as e:
|
| 491 |
-
# print(f"❌ Lỗi transcription: {e}")
|
| 492 |
-
# return None
|
| 493 |
-
|
| 494 |
-
# def _generate_ai_response(self, user_input: str) -> str:
|
| 495 |
-
# """Sinh phản hồi AI"""
|
| 496 |
-
# try:
|
| 497 |
-
# # Thêm vào lịch sử
|
| 498 |
-
# self.conversation_history.append({"role": "user", "content": user_input})
|
| 499 |
-
|
| 500 |
-
# # Tìm kiếm RAG
|
| 501 |
-
# rag_results = self.rag_system.semantic_search(user_input, top_k=2)
|
| 502 |
-
# context_text = "\n".join([f"- {result.get('text', str(result))}" for result in rag_results]) if rag_results else ""
|
| 503 |
-
|
| 504 |
-
# system_prompt = f"""Bạn là trợ lý AI thông minh chuyên về tiếng Việt.
|
| 505 |
-
# Hãy trả lời ngắn gọn, tự nhiên và hữu ích (dưới 100 từ).
|
| 506 |
-
# Thông tin tham khảo:
|
| 507 |
-
# {context_text}
|
| 508 |
-
# """
|
| 509 |
-
|
| 510 |
-
# messages = [{"role": "system", "content": system_prompt}]
|
| 511 |
-
# # Giữ lại 4 tin nhắn gần nhất
|
| 512 |
-
# messages.extend(self.conversation_history[-4:])
|
| 513 |
-
|
| 514 |
-
# completion = self.client.chat.completions.create(
|
| 515 |
-
# model="llama-3.1-8b-instant",
|
| 516 |
-
# messages=messages,
|
| 517 |
-
# max_tokens=150,
|
| 518 |
-
# temperature=0.7
|
| 519 |
-
# )
|
| 520 |
-
|
| 521 |
-
# response = completion.choices[0].message.content
|
| 522 |
-
# self.conversation_history.append({"role": "assistant", "content": response})
|
| 523 |
-
|
| 524 |
-
# # Giới hạn lịch sử
|
| 525 |
-
# if len(self.conversation_history) > 8:
|
| 526 |
-
# self.conversation_history = self.conversation_history[-8:]
|
| 527 |
-
|
| 528 |
-
# return response
|
| 529 |
-
|
| 530 |
-
# except Exception as e:
|
| 531 |
-
# return f"Xin lỗi, tôi gặp lỗi khi tạo phản hồi: {str(e)}"
|
| 532 |
-
|
| 533 |
-
# def _text_to_speech(self, text: str) -> Optional[str]:
|
| 534 |
-
# """Chuyển văn bản thành giọng nói"""
|
| 535 |
-
# try:
|
| 536 |
-
# if not text or text.startswith("❌") or text.startswith("Xin lỗi"):
|
| 537 |
-
# return None
|
| 538 |
-
|
| 539 |
-
# tts_bytes = self.tts_service.text_to_speech(text, 'vi')
|
| 540 |
-
# if tts_bytes:
|
| 541 |
-
# audio_path = self.tts_service.save_audio_to_file(tts_bytes)
|
| 542 |
-
# print(f"✅ Đã tạo TTS: {audio_path}")
|
| 543 |
-
# return audio_path
|
| 544 |
-
# except Exception as e:
|
| 545 |
-
# print(f"❌ Lỗi TTS: {e}")
|
| 546 |
-
# return None
|
| 547 |
-
|
| 548 |
-
# def clear_conversation(self):
|
| 549 |
-
# """Xóa lịch sử hội thoại"""
|
| 550 |
-
# self.conversation_history = []
|
| 551 |
-
# self.current_transcription = ""
|
| 552 |
-
# print("🗑️ Đã xóa lịch sử hội thoại")
|
| 553 |
-
|
| 554 |
-
# def get_conversation_state(self) -> dict:
|
| 555 |
-
# """Lấy trạng thái hội thoại"""
|
| 556 |
-
# return {
|
| 557 |
-
# 'history_length': len(self.conversation_history),
|
| 558 |
-
# 'current_transcription': self.current_transcription,
|
| 559 |
-
# 'last_update': time.strftime("%H:%M:%S")
|
| 560 |
-
# }
|
|
|
|
| 9 |
from core.rag_system import EnhancedRAGSystem
|
| 10 |
from core.tts_service import EnhancedTTSService
|
| 11 |
from core.speechbrain_vad import SpeechBrainVAD
|
| 12 |
+
from core.silero_vad import SileroVAD
|
| 13 |
|
| 14 |
|
| 15 |
class StreamingVoiceService:
|
|
|
|
| 19 |
self.tts_service = tts_service
|
| 20 |
|
| 21 |
# Khởi tạo VAD
|
| 22 |
+
self.vad_processor = SileroVAD()
|
| 23 |
self.is_listening = False
|
| 24 |
self.speech_callback = None
|
| 25 |
|
|
|
|
| 365 |
'current_transcription': self.current_transcription,
|
| 366 |
'last_update': time.strftime("%H:%M:%S")
|
| 367 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|