Update services/streaming_voice_service.py
Browse files
services/streaming_voice_service.py
CHANGED
|
@@ -181,11 +181,10 @@
|
|
| 181 |
import io
|
| 182 |
import numpy as np
|
| 183 |
import soundfile as sf
|
| 184 |
-
import
|
| 185 |
-
import time
|
| 186 |
import traceback
|
| 187 |
from groq import Groq
|
| 188 |
-
from typing import Optional,
|
| 189 |
from config.settings import settings
|
| 190 |
from core.rag_system import EnhancedRAGSystem
|
| 191 |
from core.tts_service import EnhancedTTSService
|
|
@@ -197,15 +196,11 @@ class StreamingVoiceService:
|
|
| 197 |
self.rag_system = rag_system
|
| 198 |
self.tts_service = tts_service
|
| 199 |
|
| 200 |
-
# Streaming state
|
| 201 |
-
self.is_listening = False
|
| 202 |
-
self.callback_handler = None
|
| 203 |
-
|
| 204 |
# Conversation context
|
| 205 |
self.conversation_history = []
|
| 206 |
self.current_transcription = ""
|
| 207 |
|
| 208 |
-
def process_streaming_audio(self, audio_data: tuple) ->
|
| 209 |
"""Xử lý audio streaming từ Gradio microphone component"""
|
| 210 |
if not audio_data:
|
| 211 |
return {
|
|
@@ -220,6 +215,14 @@ class StreamingVoiceService:
|
|
| 220 |
|
| 221 |
print(f"🎯 Nhận audio: {len(audio_array)} samples, SR: {sample_rate}")
|
| 222 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
# Chuyển đổi thành văn bản
|
| 224 |
transcription = self._transcribe_audio(audio_array, sample_rate)
|
| 225 |
|
|
@@ -232,6 +235,9 @@ class StreamingVoiceService:
|
|
| 232 |
|
| 233 |
print(f"📝 Đã chuyển đổi: {transcription}")
|
| 234 |
|
|
|
|
|
|
|
|
|
|
| 235 |
# Tạo phản hồi AI
|
| 236 |
response = self._generate_ai_response(transcription)
|
| 237 |
|
|
@@ -246,9 +252,10 @@ class StreamingVoiceService:
|
|
| 246 |
|
| 247 |
except Exception as e:
|
| 248 |
print(f"❌ Lỗi xử lý streaming audio: {e}")
|
|
|
|
| 249 |
return {
|
| 250 |
'transcription': f"❌ Lỗi: {str(e)}",
|
| 251 |
-
'response': "Xin lỗi, có lỗi xảy ra",
|
| 252 |
'tts_audio': None
|
| 253 |
}
|
| 254 |
|
|
@@ -259,14 +266,23 @@ class StreamingVoiceService:
|
|
| 259 |
if audio_data.ndim > 1:
|
| 260 |
audio_data = np.mean(audio_data, axis=1) # Chuyển sang mono
|
| 261 |
|
| 262 |
-
# Normalize
|
| 263 |
-
|
| 264 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 265 |
|
| 266 |
buffer = io.BytesIO()
|
| 267 |
sf.write(buffer, audio_data, sample_rate, format='wav', subtype='PCM_16')
|
| 268 |
buffer.seek(0)
|
| 269 |
|
|
|
|
| 270 |
transcription = self.client.audio.transcriptions.create(
|
| 271 |
model=settings.WHISPER_MODEL,
|
| 272 |
file=("speech.wav", buffer.read(), "audio/wav"),
|
|
@@ -276,11 +292,14 @@ class StreamingVoiceService:
|
|
| 276 |
|
| 277 |
# Xử lý response
|
| 278 |
if hasattr(transcription, 'text'):
|
| 279 |
-
|
| 280 |
elif isinstance(transcription, str):
|
| 281 |
-
|
| 282 |
else:
|
| 283 |
-
|
|
|
|
|
|
|
|
|
|
| 284 |
|
| 285 |
except Exception as e:
|
| 286 |
print(f"❌ Lỗi transcription: {e}")
|
|
@@ -323,14 +342,19 @@ Thông tin tham khảo:
|
|
| 323 |
return response
|
| 324 |
|
| 325 |
except Exception as e:
|
| 326 |
-
return f"Xin lỗi, tôi gặp lỗi: {str(e)}"
|
| 327 |
|
| 328 |
def _text_to_speech(self, text: str) -> Optional[str]:
|
| 329 |
"""Chuyển văn bản thành giọng nói"""
|
| 330 |
try:
|
|
|
|
|
|
|
|
|
|
| 331 |
tts_bytes = self.tts_service.text_to_speech(text, 'vi')
|
| 332 |
if tts_bytes:
|
| 333 |
-
|
|
|
|
|
|
|
| 334 |
except Exception as e:
|
| 335 |
print(f"❌ Lỗi TTS: {e}")
|
| 336 |
return None
|
|
@@ -338,11 +362,13 @@ Thông tin tham khảo:
|
|
| 338 |
def clear_conversation(self):
|
| 339 |
"""Xóa lịch sử hội thoại"""
|
| 340 |
self.conversation_history = []
|
|
|
|
| 341 |
print("🗑️ Đã xóa lịch sử hội thoại")
|
| 342 |
|
| 343 |
def get_conversation_state(self) -> dict:
|
| 344 |
"""Lấy trạng thái hội thoại"""
|
| 345 |
return {
|
| 346 |
'history_length': len(self.conversation_history),
|
| 347 |
-
'current_transcription': self.current_transcription
|
|
|
|
| 348 |
}
|
|
|
|
| 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
|
|
|
|
| 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 {
|
|
|
|
| 215 |
|
| 216 |
print(f"🎯 Nhận audio: {len(audio_array)} samples, SR: {sample_rate}")
|
| 217 |
|
| 218 |
+
# Kiểm tra audio có 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 |
|
|
|
|
| 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
|
| 242 |
response = self._generate_ai_response(transcription)
|
| 243 |
|
|
|
|
| 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 |
|
|
|
|
| 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"),
|
|
|
|
| 292 |
|
| 293 |
# Xử lý response
|
| 294 |
if hasattr(transcription, 'text'):
|
| 295 |
+
result = transcription.text.strip()
|
| 296 |
elif isinstance(transcription, str):
|
| 297 |
+
result = transcription.strip()
|
| 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:
|
| 305 |
print(f"❌ Lỗi transcription: {e}")
|
|
|
|
| 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
|
| 352 |
+
|
| 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}")
|
| 360 |
return None
|
|
|
|
| 362 |
def clear_conversation(self):
|
| 363 |
"""Xóa lịch sử hội thoại"""
|
| 364 |
self.conversation_history = []
|
| 365 |
+
self.current_transcription = ""
|
| 366 |
print("🗑️ Đã xóa lịch sử hội thoại")
|
| 367 |
|
| 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 |
}
|