Update ui/tabs.py
Browse files- ui/tabs.py +121 -52
ui/tabs.py
CHANGED
|
@@ -200,65 +200,134 @@ def create_audio_tab(audio_service: AudioService):
|
|
| 200 |
outputs=[transcription_output, response_output, tts_audio_output, language_display] # UPDATED
|
| 201 |
)
|
| 202 |
def create_streaming_voice_tab(streaming_service: StreamingVoiceService):
|
| 203 |
-
"""Tạo tab streaming voice
|
| 204 |
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
|
| 214 |
-
|
| 215 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 216 |
|
| 217 |
-
|
| 218 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 219 |
|
| 220 |
-
|
|
|
|
| 221 |
state = streaming_service.get_conversation_state()
|
| 222 |
-
status
|
| 223 |
-
|
| 224 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 225 |
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 258 |
|
| 259 |
-
|
| 260 |
-
status_display.value = "Sẵn sàng - nhấn nút microphone để nói"
|
| 261 |
-
state_display.value = streaming_service.get_conversation_state()
|
| 262 |
def create_image_tab(image_service: ImageService):
|
| 263 |
gr.Markdown("## Phân tích hình ảnh")
|
| 264 |
with gr.Row():
|
|
|
|
| 200 |
outputs=[transcription_output, response_output, tts_audio_output, language_display] # UPDATED
|
| 201 |
)
|
| 202 |
def create_streaming_voice_tab(streaming_service: StreamingVoiceService):
|
| 203 |
+
"""Tạo tab streaming voice với VAD"""
|
| 204 |
|
| 205 |
+
with gr.Blocks() as streaming_tab:
|
| 206 |
+
gr.Markdown("## 🎤 Trò chuyện giọng nói thời gian thực với VAD")
|
| 207 |
+
gr.Markdown("""
|
| 208 |
+
### 🎯 Chế độ VAD (Voice Activity Detection)
|
| 209 |
+
- **Tự động phát hiện** khi bạn bắt đầu nói
|
| 210 |
+
- **Không cần giữ nút** - hệ thống tự nhận diện
|
| 211 |
+
- **Loại bỏ tiếng ồn** - chỉ xử lý giọng nói thật
|
| 212 |
+
""")
|
| 213 |
|
| 214 |
+
with gr.Row():
|
| 215 |
+
with gr.Column(scale=1):
|
| 216 |
+
# VAD Controls
|
| 217 |
+
with gr.Row():
|
| 218 |
+
start_vad_btn = gr.Button("🎙️ Bắt đầu VAD", variant="primary")
|
| 219 |
+
stop_vad_btn = gr.Button("🛑 Dừng VAD", variant="secondary")
|
| 220 |
+
|
| 221 |
+
# Microphone component (vẫn giữ cho manual mode)
|
| 222 |
+
microphone = gr.Microphone(
|
| 223 |
+
label="🎤 Hoặc nhấn để nói thủ công",
|
| 224 |
+
type="numpy",
|
| 225 |
+
streaming=True,
|
| 226 |
+
show_download_button=False
|
| 227 |
+
)
|
| 228 |
+
|
| 229 |
+
# Clear conversation button
|
| 230 |
+
clear_btn = gr.Button("🗑️ Xóa hội thoại", variant="secondary")
|
| 231 |
+
|
| 232 |
+
# Status display
|
| 233 |
+
status_display = gr.Textbox(
|
| 234 |
+
label="Trạng thái",
|
| 235 |
+
value="Chưa bắt đầu - nhấn 'Bắt đầu VAD'",
|
| 236 |
+
interactive=False
|
| 237 |
+
)
|
| 238 |
+
|
| 239 |
+
# Conversation state
|
| 240 |
+
state_display = gr.JSON(
|
| 241 |
+
label="Thông tin hệ thống",
|
| 242 |
+
value={}
|
| 243 |
+
)
|
| 244 |
|
| 245 |
+
with gr.Column(scale=2):
|
| 246 |
+
# Real-time transcription
|
| 247 |
+
realtime_transcription = gr.Textbox(
|
| 248 |
+
label="📝 Bạn vừa nói",
|
| 249 |
+
lines=2,
|
| 250 |
+
interactive=False,
|
| 251 |
+
placeholder="Văn bản được chuyển đổi sẽ xuất hiện ở đây..."
|
| 252 |
+
)
|
| 253 |
+
|
| 254 |
+
# AI Response
|
| 255 |
+
ai_response = gr.Textbox(
|
| 256 |
+
label="🤖 Phản hồi AI",
|
| 257 |
+
lines=3,
|
| 258 |
+
interactive=False,
|
| 259 |
+
placeholder="Phản hồi của AI sẽ xuất hiện ở đây..."
|
| 260 |
+
)
|
| 261 |
+
|
| 262 |
+
# TTS Audio output
|
| 263 |
+
tts_output = gr.Audio(
|
| 264 |
+
label="🔊 Phản hồi bằng giọng nói",
|
| 265 |
+
interactive=False,
|
| 266 |
+
autoplay=True
|
| 267 |
+
)
|
| 268 |
+
|
| 269 |
+
def start_vad():
|
| 270 |
+
"""Bắt đầu VAD listening"""
|
| 271 |
+
def vad_callback(result):
|
| 272 |
+
"""Callback khi VAD phát hiện speech"""
|
| 273 |
+
# Cập nhật UI với kết quả
|
| 274 |
+
# Cần sử dụng Gradio events để cập nhật real-time
|
| 275 |
+
print(f"VAD Result: {result}")
|
| 276 |
|
| 277 |
+
success = streaming_service.start_listening(vad_callback)
|
| 278 |
+
status = "✅ Đang lắng nghe với VAD..." if success else "❌ Lỗi khởi động VAD"
|
| 279 |
state = streaming_service.get_conversation_state()
|
| 280 |
+
return status, state
|
| 281 |
+
|
| 282 |
+
def stop_vad():
|
| 283 |
+
"""Dừng VAD listening"""
|
| 284 |
+
streaming_service.stop_listening()
|
| 285 |
+
state = streaming_service.get_conversation_state()
|
| 286 |
+
return "🛑 Đã dừng VAD", state
|
| 287 |
+
|
| 288 |
+
def process_manual_audio(audio_data):
|
| 289 |
+
"""Xử lý audio manual (không dùng VAD)"""
|
| 290 |
+
if audio_data is None:
|
| 291 |
+
return "❌ Không có âm thanh", "Vui lòng nói lại", None, "Đang chờ...", {}
|
| 292 |
|
| 293 |
+
try:
|
| 294 |
+
result = streaming_service.process_streaming_audio(audio_data)
|
| 295 |
+
state = streaming_service.get_conversation_state()
|
| 296 |
+
status = "✅ Đã xử lý manual audio"
|
| 297 |
+
return result['transcription'], result['response'], result['tts_audio'], status, state
|
| 298 |
+
except Exception as e:
|
| 299 |
+
error_msg = f"❌ Lỗi: {str(e)}"
|
| 300 |
+
return error_msg, "Xin lỗi, có lỗi xảy ra", None, "❌ Lỗi", {}
|
| 301 |
+
|
| 302 |
+
def clear_conversation():
|
| 303 |
+
"""Xóa hội thoại"""
|
| 304 |
+
streaming_service.clear_conversation()
|
| 305 |
+
state = streaming_service.get_conversation_state()
|
| 306 |
+
return "", "", None, "🗑️ Đã xóa hội thoại", state
|
| 307 |
+
|
| 308 |
+
# Event handlers
|
| 309 |
+
start_vad_btn.click(
|
| 310 |
+
start_vad,
|
| 311 |
+
outputs=[status_display, state_display]
|
| 312 |
+
)
|
| 313 |
+
|
| 314 |
+
stop_vad_btn.click(
|
| 315 |
+
stop_vad,
|
| 316 |
+
outputs=[status_display, state_display]
|
| 317 |
+
)
|
| 318 |
+
|
| 319 |
+
microphone.stream(
|
| 320 |
+
process_manual_audio,
|
| 321 |
+
inputs=[microphone],
|
| 322 |
+
outputs=[realtime_transcription, ai_response, tts_output, status_display, state_display]
|
| 323 |
+
)
|
| 324 |
+
|
| 325 |
+
clear_btn.click(
|
| 326 |
+
clear_conversation,
|
| 327 |
+
outputs=[realtime_transcription, ai_response, tts_output, status_display, state_display]
|
| 328 |
+
)
|
| 329 |
|
| 330 |
+
return streaming_tab
|
|
|
|
|
|
|
| 331 |
def create_image_tab(image_service: ImageService):
|
| 332 |
gr.Markdown("## Phân tích hình ảnh")
|
| 333 |
with gr.Row():
|