Update ui/tabs.py
Browse files- ui/tabs.py +77 -118
ui/tabs.py
CHANGED
|
@@ -200,176 +200,135 @@ 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 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
|
| 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 |
-
#
|
| 217 |
with gr.Row():
|
| 218 |
-
|
| 219 |
-
|
| 220 |
|
| 221 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 222 |
microphone = gr.Microphone(
|
| 223 |
-
label="🎤
|
| 224 |
type="numpy",
|
| 225 |
-
streaming=True
|
| 226 |
-
show_download_button=False
|
| 227 |
)
|
| 228 |
|
| 229 |
-
|
| 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 |
-
#
|
| 240 |
-
|
| 241 |
label="Thông tin hệ thống",
|
| 242 |
-
value=
|
|
|
|
|
|
|
| 243 |
)
|
| 244 |
|
| 245 |
with gr.Column(scale=2):
|
| 246 |
-
|
| 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 |
-
|
| 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 |
-
|
| 263 |
-
|
| 264 |
-
label="🔊 Phản hồi bằng giọng nói",
|
| 265 |
interactive=False,
|
| 266 |
autoplay=True
|
| 267 |
)
|
| 268 |
|
| 269 |
-
# Biến
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
vad_audio = gr.State(value=None)
|
| 273 |
-
vad_status = gr.State(value="waiting")
|
| 274 |
|
| 275 |
def start_vad():
|
| 276 |
-
"""Bắt đầu VAD
|
| 277 |
-
def
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 285 |
|
| 286 |
-
success = streaming_service.start_listening(vad_callback)
|
| 287 |
-
status = "✅ Đang lắng nghe với VAD..." if success else "❌ Lỗi khởi động VAD"
|
| 288 |
-
state = streaming_service.get_conversation_state()
|
| 289 |
return status, state
|
| 290 |
|
| 291 |
def stop_vad():
|
| 292 |
-
"""Dừng VAD
|
| 293 |
streaming_service.stop_listening()
|
| 294 |
-
|
| 295 |
-
|
|
|
|
|
|
|
| 296 |
|
| 297 |
-
def
|
| 298 |
-
"""Xử lý
|
| 299 |
if audio_data is None:
|
| 300 |
-
return "
|
| 301 |
|
| 302 |
try:
|
| 303 |
result = streaming_service.process_streaming_audio(audio_data)
|
| 304 |
-
state = streaming_service.
|
| 305 |
-
|
| 306 |
-
return result['transcription'], result['response'], result['tts_audio'], status, state
|
| 307 |
except Exception as e:
|
| 308 |
-
|
| 309 |
-
return error_msg, "Xin lỗi, có lỗi xảy ra", None, "❌ Lỗi", {}
|
| 310 |
|
| 311 |
-
def
|
| 312 |
"""Xóa hội thoại"""
|
| 313 |
streaming_service.clear_conversation()
|
| 314 |
-
state =
|
| 315 |
-
|
| 316 |
-
vad_transcription.value = ""
|
| 317 |
-
vad_response.value = ""
|
| 318 |
-
vad_audio.value = None
|
| 319 |
-
return "", "", None, "🗑️ Đã xóa hội thoại", state
|
| 320 |
-
|
| 321 |
-
def get_vad_results():
|
| 322 |
-
"""Lấy kết quả từ VAD và cập nhật UI"""
|
| 323 |
-
if vad_status.value == "completed":
|
| 324 |
-
# Reset status sau khi lấy kết quả
|
| 325 |
-
vad_status.value = "waiting"
|
| 326 |
-
state = streaming_service.get_conversation_state()
|
| 327 |
-
return (vad_transcription.value, vad_response.value, vad_audio.value,
|
| 328 |
-
"✅ VAD đã xử lý", state)
|
| 329 |
-
else:
|
| 330 |
-
# Không có kết quả mới, trả về None để không cập nhật
|
| 331 |
-
return gr.skip(), gr.skip(), gr.skip(), gr.skip(), gr.skip()
|
| 332 |
-
|
| 333 |
-
def refresh_state():
|
| 334 |
-
"""Chỉ cập nhật state display"""
|
| 335 |
-
state = streaming_service.get_conversation_state()
|
| 336 |
-
return state
|
| 337 |
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
|
|
|
|
|
|
|
|
|
| 343 |
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
)
|
| 348 |
|
| 349 |
microphone.stream(
|
| 350 |
-
|
| 351 |
inputs=[microphone],
|
| 352 |
-
outputs=[
|
| 353 |
)
|
| 354 |
|
| 355 |
clear_btn.click(
|
| 356 |
-
|
| 357 |
-
outputs=[
|
| 358 |
-
)
|
| 359 |
-
|
| 360 |
-
# Nút refresh để cập nhật kết quả VAD thủ công
|
| 361 |
-
refresh_btn = gr.Button("🔄 Kiểm tra kết quả VAD", variant="secondary", visible=False)
|
| 362 |
-
|
| 363 |
-
refresh_btn.click(
|
| 364 |
-
get_vad_results,
|
| 365 |
-
outputs=[realtime_transcription, ai_response, tts_output, status_display, state_display]
|
| 366 |
-
)
|
| 367 |
-
|
| 368 |
-
# Tự động cập nhật state display mỗi 2 giây
|
| 369 |
-
streaming_tab.load(
|
| 370 |
-
refresh_state,
|
| 371 |
-
outputs=[state_display],
|
| 372 |
-
every=2
|
| 373 |
)
|
| 374 |
|
| 375 |
return streaming_tab
|
|
|
|
| 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 - Phiên bản cực kỳ đơn giản"""
|
| 204 |
|
| 205 |
with gr.Blocks() as streaming_tab:
|
| 206 |
+
gr.Markdown("## 🎤 Trò chuyện giọng nói thời gian thực")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 207 |
|
| 208 |
with gr.Row():
|
| 209 |
with gr.Column(scale=1):
|
| 210 |
+
# Controls
|
| 211 |
with gr.Row():
|
| 212 |
+
start_btn = gr.Button("🎙️ Bắt đầu VAD", variant="primary")
|
| 213 |
+
stop_btn = gr.Button("🛑 Dừng VAD", variant="secondary")
|
| 214 |
|
| 215 |
+
gr.Markdown("### Chế độ tự động (VAD)")
|
| 216 |
+
gr.Markdown("Hệ thống tự động nhận diện khi bạn bắt đầu nói")
|
| 217 |
+
|
| 218 |
+
with gr.Row():
|
| 219 |
+
vad_status = gr.Textbox(
|
| 220 |
+
label="Trạng thái VAD",
|
| 221 |
+
value="Chưa bắt đầu",
|
| 222 |
+
interactive=False
|
| 223 |
+
)
|
| 224 |
+
|
| 225 |
+
gr.Markdown("### Chế độ thủ công")
|
| 226 |
microphone = gr.Microphone(
|
| 227 |
+
label="🎤 Nhấn để nói thủ công",
|
| 228 |
type="numpy",
|
| 229 |
+
streaming=True
|
|
|
|
| 230 |
)
|
| 231 |
|
| 232 |
+
clear_btn = gr.Button("🗑️ Xóa hội thoại")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 233 |
|
| 234 |
+
# State info
|
| 235 |
+
state_info = gr.Textbox(
|
| 236 |
label="Thông tin hệ thống",
|
| 237 |
+
value="Khởi tạo...",
|
| 238 |
+
lines=3,
|
| 239 |
+
interactive=False
|
| 240 |
)
|
| 241 |
|
| 242 |
with gr.Column(scale=2):
|
| 243 |
+
transcription_box = gr.Textbox(
|
|
|
|
| 244 |
label="📝 Bạn vừa nói",
|
| 245 |
lines=2,
|
| 246 |
+
interactive=False
|
|
|
|
| 247 |
)
|
| 248 |
|
| 249 |
+
response_box = gr.Textbox(
|
|
|
|
| 250 |
label="🤖 Phản hồi AI",
|
| 251 |
lines=3,
|
| 252 |
+
interactive=False
|
|
|
|
| 253 |
)
|
| 254 |
|
| 255 |
+
audio_output = gr.Audio(
|
| 256 |
+
label="🔊 Giọng nói AI",
|
|
|
|
| 257 |
interactive=False,
|
| 258 |
autoplay=True
|
| 259 |
)
|
| 260 |
|
| 261 |
+
# Biến đơn giản để track VAD
|
| 262 |
+
is_vad_active = gr.State(value=False)
|
| 263 |
+
last_vad_result = gr.State(value=None)
|
|
|
|
|
|
|
| 264 |
|
| 265 |
def start_vad():
|
| 266 |
+
"""Bắt đầu VAD"""
|
| 267 |
+
def callback(result):
|
| 268 |
+
last_vad_result.value = result
|
| 269 |
+
print(f"VAD: {result.get('transcription', '')}")
|
| 270 |
+
|
| 271 |
+
success = streaming_service.start_listening(callback)
|
| 272 |
+
if success:
|
| 273 |
+
is_vad_active.value = True
|
| 274 |
+
status = "✅ VAD đang chạy - Hãy nói gì đó!"
|
| 275 |
+
state = f"VAD: Đang hoạt động\n"
|
| 276 |
+
state += f"Processing: {streaming_service.is_processing}\n"
|
| 277 |
+
state += f"History: {len(streaming_service.conversation_history)} messages"
|
| 278 |
+
else:
|
| 279 |
+
status = "❌ Không thể khởi động VAD"
|
| 280 |
+
state = "Lỗi khởi động"
|
| 281 |
|
|
|
|
|
|
|
|
|
|
| 282 |
return status, state
|
| 283 |
|
| 284 |
def stop_vad():
|
| 285 |
+
"""Dừng VAD"""
|
| 286 |
streaming_service.stop_listening()
|
| 287 |
+
is_vad_active.value = False
|
| 288 |
+
state = f"VAD: Đã dừng\n"
|
| 289 |
+
state += f"History: {len(streaming_service.conversation_history)} messages"
|
| 290 |
+
return "🛑 VAD đã dừng", state
|
| 291 |
|
| 292 |
+
def process_microphone(audio_data):
|
| 293 |
+
"""Xử lý microphone input"""
|
| 294 |
if audio_data is None:
|
| 295 |
+
return "Chưa có âm thanh", "Hãy nói gì đó...", None, "VAD: Đang chạy" if is_vad_active.value else "VAD: Dừng"
|
| 296 |
|
| 297 |
try:
|
| 298 |
result = streaming_service.process_streaming_audio(audio_data)
|
| 299 |
+
state = f"Manual mode\nHistory: {len(streaming_service.conversation_history)} messages"
|
| 300 |
+
return result['transcription'], result['response'], result['tts_audio'], state
|
|
|
|
| 301 |
except Exception as e:
|
| 302 |
+
return f"Lỗi: {e}", "Xin lỗi, có lỗi xảy ra", None, "Lỗi xử lý"
|
|
|
|
| 303 |
|
| 304 |
+
def clear_chat():
|
| 305 |
"""Xóa hội thoại"""
|
| 306 |
streaming_service.clear_conversation()
|
| 307 |
+
state = f"Đã xóa hội thoại\nHistory: 0 messages"
|
| 308 |
+
return "", "", None, state
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 309 |
|
| 310 |
+
def check_vad_auto():
|
| 311 |
+
"""Tự động kiểm tra VAD results (nếu có)"""
|
| 312 |
+
if is_vad_active.value and last_vad_result.value:
|
| 313 |
+
result = last_vad_result.value
|
| 314 |
+
last_vad_result.value = None # Reset sau khi dùng
|
| 315 |
+
state = f"VAD: Đã xử lý\nHistory: {len(streaming_service.conversation_history)} messages"
|
| 316 |
+
return result['transcription'], result['response'], result['tts_audio'], state
|
| 317 |
+
return gr.skip(), gr.skip(), gr.skip(), gr.skip()
|
| 318 |
|
| 319 |
+
# Event handlers đơn giản
|
| 320 |
+
start_btn.click(start_vad, outputs=[vad_status, state_info])
|
| 321 |
+
stop_btn.click(stop_vad, outputs=[vad_status, state_info])
|
|
|
|
| 322 |
|
| 323 |
microphone.stream(
|
| 324 |
+
process_microphone,
|
| 325 |
inputs=[microphone],
|
| 326 |
+
outputs=[transcription_box, response_box, audio_output, state_info]
|
| 327 |
)
|
| 328 |
|
| 329 |
clear_btn.click(
|
| 330 |
+
clear_chat,
|
| 331 |
+
outputs=[transcription_box, response_box, audio_output, state_info]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 332 |
)
|
| 333 |
|
| 334 |
return streaming_tab
|