datbkpro commited on
Commit
4a0d9d6
·
verified ·
1 Parent(s): 6613b27

Update ui/tabs.py

Browse files
Files changed (1) hide show
  1. ui/tabs.py +200 -149
ui/tabs.py CHANGED
@@ -56,26 +56,26 @@ def create_all_tabs(audio_service: AudioService, chat_service: ChatService,
56
  with gr.Tab("Stream Object Detection"):
57
  create_streaming_object_detection()
58
  def create_gemini_realtime_tab():
59
- """Tạo tab cho Gemini Realtime API"""
60
 
61
  with gr.Blocks() as gemini_tab:
62
  gr.Markdown("""
63
- # 🎯 Gemini Realtime API
64
- **Streaming hội thoại thời gian thực với Google Gemini**
65
  """)
66
 
67
  with gr.Row():
68
  with gr.Column(scale=1):
69
  # Connection controls
70
  with gr.Group():
71
- gr.Markdown("### 🔗 Kết nối")
72
 
73
  api_key = gr.Textbox(
74
  label="Gemini API Key",
75
  type="password",
76
  placeholder="Nhập API key của bạn...",
77
  value=os.getenv("GEMINI_API_KEY", ""),
78
- info="Lấy từ https://aistudio.google.com/"
79
  )
80
 
81
  voice_select = gr.Dropdown(
@@ -86,7 +86,7 @@ def create_gemini_realtime_tab():
86
  )
87
 
88
  with gr.Row():
89
- connect_btn = gr.Button("🔗 Kết nối", variant="primary")
90
  disconnect_btn = gr.Button("🔌 Ngắt kết nối", variant="secondary")
91
 
92
  # Status panel
@@ -94,7 +94,7 @@ def create_gemini_realtime_tab():
94
  gr.Markdown("### 📊 Trạng thái")
95
 
96
  status_display = gr.Textbox(
97
- label="Trạng thái kết nối",
98
  value="Chưa kết nối",
99
  interactive=False
100
  )
@@ -113,217 +113,268 @@ def create_gemini_realtime_tab():
113
  chatbot = gr.Chatbot(
114
  label="Gemini Chat",
115
  type="messages",
116
- height=400,
117
  show_copy_button=True,
118
- value=[] # Khởi tạo giá trị mặc định
119
  )
120
 
121
- # Audio interface - Simplified for now
122
  with gr.Group():
123
- gr.Markdown("### 🎤 Audio Streaming (Coming Soon)")
124
 
125
- gr.Markdown("""
126
- **Tính năng audio realtime đang được phát triển**
127
- - 🎙️ Real-time voice recognition
128
- - 🔊 Real-time audio response
129
- - ⚡ Low latency streaming
130
- """)
131
-
132
- # Tạm thời ẩn các nút audio
133
- # with gr.Row():
134
- # start_audio_btn = gr.Button("🎙️ Bắt đầu nói", variant="primary", visible=False)
135
- # stop_audio_btn = gr.Button("⏹️ Dừng nói", variant="secondary", visible=False)
136
 
137
  transcription_display = gr.Textbox(
138
  label="Bạn nói",
139
  interactive=False,
140
  lines=2,
141
- placeholder="Tính năng audio đang được phát triển...",
142
- visible=False
143
  )
144
 
 
145
  audio_output = gr.Audio(
146
- label="Gemini trả lời",
147
  interactive=False,
148
- autoplay=True,
149
- visible=False
 
 
 
 
 
 
 
150
  )
151
 
152
  # State management
153
  connection_state = gr.State(value=False)
154
- # audio_stream_state = gr.State(value=False)
 
155
 
156
- # Khởi tạo service (không khởi tạo global để tránh lỗi)
157
- gemini_service = None
158
-
159
- async def initialize_gemini_service(api_key: str):
160
- """Khởi tạo Gemini service"""
161
- nonlocal gemini_service
162
- try:
163
- from services.gemini_realtime_service import GeminiRealtimeService
164
- gemini_service = GeminiRealtimeService(api_key)
165
- await gemini_service.initialize()
166
- return True, "✅ Đã khởi tạo Gemini service"
167
- except Exception as e:
168
- return False, f"❌ Lỗi khởi tạo: {str(e)}"
169
-
170
- async def gemini_callback(data: dict):
171
- """Callback function để xử lý events từ Gemini service"""
172
- if data['type'] == 'status':
173
- return data['message'], data['message']
174
- elif data['type'] == 'text':
175
- return f"📝 Gemini: {data['content']}", data['content']
176
- elif data['type'] == 'error':
177
- return f"❌ Lỗi: {data['message']}", data['message']
178
- return "Unknown event", "Unknown event"
179
-
180
- # Event handlers for connection
181
- async def connect_gemini(api_key: str, voice_name: str, current_chat):
182
- """Kết nối đến Gemini Realtime API"""
183
  try:
184
  if not api_key:
185
- return False, "❌ Vui lòng nhập API Key", "Chưa kết nối", current_chat
186
-
187
- # Khởi tạo service
188
- success, message = await initialize_gemini_service(api_key)
189
- if not success:
190
- return False, message, "Lỗi khởi tạo", current_chat
191
-
192
- # Kết nối session
193
- success = await gemini_service.start_session(
194
- voice_name=voice_name,
195
- callback=gemini_callback
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  )
197
 
198
  if success:
199
- # Thêm message chào mừng
200
- new_chat = current_chat + [{"role": "assistant", "content": f"Xin chào! Tôi là Gemini AI với giọng {voice_name}. Hãy bắt đầu trò chuyện!"}]
201
- return True, "Đã kết nối Gemini", f"Đang sử dụng giọng: {voice_name}", new_chat
 
202
  else:
203
- return False, "❌ Không thể kết nối Gemini", "Lỗi kết nối", current_chat
204
-
205
  except Exception as e:
206
- return False, f"❌ Lỗi kết nối: {str(e)}", "Lỗi kết nối", current_chat
207
-
208
- async def disconnect_gemini(current_chat):
209
- """Ngắt kết nối Gemini"""
210
- nonlocal gemini_service
211
- if gemini_service:
212
- await gemini_service.close()
213
- gemini_service = None
214
 
215
- # Thêm message thông báo ngắt kết nối
216
- new_chat = current_chat + [{"role": "assistant", "content": "Đã ngắt kết nối với Gemini."}]
217
- return False, "🔌 Đã ngắt kết nối", "Ngắt kết nối", new_chat
218
-
219
- # Text chat functionality
220
- async def send_text_message(message: str, history):
221
- """Gửi tin nhắn text đến Gemini"""
222
- if not gemini_service or not gemini_service.is_active:
223
- return history, " Chưa kết nối Gemini. Vui lòng kết nối trước."
 
 
 
 
 
 
 
 
 
 
 
 
224
 
225
  try:
226
- # Thêm tin nhắn user vào history
227
- new_history = history + [{"role": "user", "content": message}]
228
-
229
- # Gửi đến Gemini (sử dụng text fallback)
230
- response_text = await gemini_service.send_text(message)
231
 
232
- if response_text:
233
- # Thêm response vào history
234
- new_history.append({"role": "assistant", "content": response_text})
235
- return new_history, f"✅ Đã nhận phản hồi ({len(response_text)} ký tự)"
 
 
 
 
 
 
 
 
 
 
 
 
236
  else:
237
- error_msg = "❌ Không nhận được phản hồi từ Gemini"
238
- new_history.append({"role": "assistant", "content": error_msg})
239
- return new_history, error_msg
240
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  except Exception as e:
242
  error_msg = f"❌ Lỗi: {str(e)}"
243
- new_history = history + [
244
- {"role": "user", "content": message},
245
- {"role": "assistant", "content": error_msg}
246
- ]
247
- return new_history, error_msg
248
 
249
- # Thêm text input cho chat
 
 
 
 
250
  with gr.Row():
251
  text_input = gr.Textbox(
252
- label="Tin nhắn của bạn",
253
- placeholder="Nhập tin nhắn và nhấn Enter...",
254
  scale=4
255
  )
256
- send_text_btn = gr.Button("📤 Gửi", scale=1)
257
 
258
- # Connection events
259
  connect_btn.click(
260
  connect_gemini,
261
  inputs=[api_key, voice_select, chatbot],
262
- outputs=[connection_state, status_display, connection_info, chatbot]
263
  )
264
 
265
  disconnect_btn.click(
266
  disconnect_gemini,
267
- inputs=[chatbot],
268
- outputs=[connection_state, status_display, connection_info, chatbot]
 
 
 
 
 
 
 
 
 
 
 
 
269
  )
270
 
271
- # Text message events
 
 
 
 
 
 
 
272
  send_text_btn.click(
273
  send_text_message,
274
- inputs=[text_input, chatbot],
275
- outputs=[chatbot, connection_info]
 
 
 
276
  )
277
 
278
  text_input.submit(
279
  send_text_message,
280
- inputs=[text_input, chatbot],
281
- outputs=[chatbot, connection_info]
282
  ).then(
283
- lambda: "", # Clear input
284
  outputs=[text_input]
285
  )
286
 
287
- # Đơn giản hóa JavaScript - sửa lỗi syntax
288
- gemini_tab.load(
289
- fn=None,
290
- inputs=[],
291
- outputs=[],
292
- js="""function() {
293
- console.log('Gemini Realtime Tab Loaded Successfully');
294
- return [];
295
- }"""
296
  )
297
 
298
  # Hướng dẫn sử dụng
299
- with gr.Accordion("📖 Hướng dẫn sử dụng", open=False):
300
  gr.Markdown("""
301
- ### Cách sử dụng Gemini Realtime:
302
-
303
- 1. **Lấy API Key**:
304
- - Truy cập [Google AI Studio](https://aistudio.google.com/)
305
- - Tạo API key mới trong mục API Keys
306
- - Sao chép và dán vào ô API Key
307
 
308
- 2. **Kết nối**:
309
- - Chọn giọng nói yêu thích
310
- - Nhấn **"Kết nối"** để thiết lập session
 
311
 
312
- 3. **Trò chuyện**:
313
- - Nhập tin nhắn vào ô text
314
- - Nhấn **Enter** hoặc **"Gửi"** để gửi
315
- - Gemini sẽ trả lời ngay lập tức
316
 
317
- ### Tính năng hiện có:
318
- - 💬 Text chat với Gemini
319
- - 🔗 Kết nối realtime
320
- - 🎯 Multiple voice options
321
- - 📊 Status monitoring
322
 
323
- ### Tính năng sắp có:
324
  - 🎙️ Real-time voice recognition
325
  - 🔊 Real-time audio response
326
- - ⚡ Low latency streaming
 
 
 
 
 
 
327
  """)
328
 
329
  return gemini_tab
 
56
  with gr.Tab("Stream Object Detection"):
57
  create_streaming_object_detection()
58
  def create_gemini_realtime_tab():
59
+ """Tạo tab cho Gemini Realtime API với Audio Streaming"""
60
 
61
  with gr.Blocks() as gemini_tab:
62
  gr.Markdown("""
63
+ # 🎯 Gemini Realtime API
64
+ **Audio Streaming Thời Gian Thực với Google Gemini**
65
  """)
66
 
67
  with gr.Row():
68
  with gr.Column(scale=1):
69
  # Connection controls
70
  with gr.Group():
71
+ gr.Markdown("### 🔗 Kết nối Audio")
72
 
73
  api_key = gr.Textbox(
74
  label="Gemini API Key",
75
  type="password",
76
  placeholder="Nhập API key của bạn...",
77
  value=os.getenv("GEMINI_API_KEY", ""),
78
+ info="Cần cho Audio Streaming"
79
  )
80
 
81
  voice_select = gr.Dropdown(
 
86
  )
87
 
88
  with gr.Row():
89
+ connect_btn = gr.Button("🔗 Kết nối Audio", variant="primary")
90
  disconnect_btn = gr.Button("🔌 Ngắt kết nối", variant="secondary")
91
 
92
  # Status panel
 
94
  gr.Markdown("### 📊 Trạng thái")
95
 
96
  status_display = gr.Textbox(
97
+ label="Trạng thái",
98
  value="Chưa kết nối",
99
  interactive=False
100
  )
 
113
  chatbot = gr.Chatbot(
114
  label="Gemini Chat",
115
  type="messages",
116
+ height=300,
117
  show_copy_button=True,
118
+ value=[]
119
  )
120
 
121
+ # Audio Streaming Interface
122
  with gr.Group():
123
+ gr.Markdown("### 🎤 Audio Streaming")
124
 
125
+ with gr.Row():
126
+ start_audio_btn = gr.Button("🎙️ Bắt đầu nói", variant="primary")
127
+ stop_audio_btn = gr.Button("⏹️ Dừng nói", variant="secondary")
 
 
 
 
 
 
 
 
128
 
129
  transcription_display = gr.Textbox(
130
  label="Bạn nói",
131
  interactive=False,
132
  lines=2,
133
+ placeholder="Văn bản nhận diện sẽ hiển thị ở đây..."
 
134
  )
135
 
136
+ # Audio output for Gemini responses
137
  audio_output = gr.Audio(
138
+ label="🔊 Gemini trả lời",
139
  interactive=False,
140
+ autoplay=True
141
+ )
142
+
143
+ # Audio input for user
144
+ audio_input = gr.Audio(
145
+ label="🎤 Micro của bạn",
146
+ sources=["microphone"],
147
+ type="numpy",
148
+ interactive=True
149
  )
150
 
151
  # State management
152
  connection_state = gr.State(value=False)
153
+ audio_streaming_state = gr.State(value=False)
154
+ gemini_service_state = gr.State(value=None)
155
 
156
+ async def connect_gemini(api_key, voice_name, current_chat):
157
+ """Kết nối Gemini Audio Streaming"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  try:
159
  if not api_key:
160
+ return False, "❌ Vui lòng nhập API Key", "Chưa kết nối", current_chat, None
161
+
162
+ service = GeminiRealtimeService(api_key)
163
+
164
+ # Callback để xử lý real-time events
165
+ async def handle_gemini_callback(data):
166
+ if data['type'] == 'status':
167
+ gr.Info(data['message'])
168
+ return data['message'], data['message'], current_chat
169
+ elif data['type'] == 'text':
170
+ # Cập nhật transcription
171
+ new_chat = current_chat + [{"role": "assistant", "content": data['content']}]
172
+ return f"📝 {data['content']}", data['content'], new_chat
173
+ elif data['type'] == 'audio':
174
+ # Xử lý audio stream (sẽ được xử lý trong audio output)
175
+ return "🔊 Đang nhận audio...", "audio_received", current_chat
176
+ elif data['type'] == 'error':
177
+ gr.Warning(data['message'])
178
+ return f"❌ {data['message']}", data['message'], current_chat
179
+ return "Unknown", "unknown", current_chat
180
+
181
+ success = await service.start_session(
182
+ voice_name=voice_name,
183
+ callback=handle_gemini_callback
184
  )
185
 
186
  if success:
187
+ welcome_msg = f"Xin chào! Tôi là Gemini với giọng {voice_name}. Hãy bắt đầu nói chuyện!"
188
+ new_chat = current_chat + [{"role": "assistant", "content": welcome_msg}]
189
+ info_msg = f"Đã kết nối audio streaming - Giọng: {voice_name}"
190
+ return True, "✅ Đã kết nối Audio Streaming", info_msg, new_chat, service
191
  else:
192
+ return False, "❌ Không thể kết nối audio", "Lỗi kết nối", current_chat, None
193
+
194
  except Exception as e:
195
+ error_msg = f"❌ Lỗi kết nối: {str(e)}"
196
+ return False, error_msg, f"Lỗi: {str(e)}", current_chat, None
197
+
198
+ async def disconnect_gemini(current_chat, service):
199
+ """Ngắt kết nối"""
200
+ if service:
201
+ await service.close()
 
202
 
203
+ new_chat = current_chat + [{"role": "assistant", "content": "Đã ngắt kết nối audio streaming."}]
204
+ return False, "🔌 Đã ngắt kết nối", "Ngắt kết nối", new_chat, None
205
+
206
+ async def start_audio_stream(service, audio_state):
207
+ """Bắt đầu stream audio"""
208
+ if not service or not service.is_active:
209
+ return False, " Chưa kết nối. Vui lòng kết nối trước."
210
+
211
+ return True, "🎙️ Đang nghe... Hãy bắt đầu nói!"
212
+
213
+ async def stop_audio_stream(service, audio_state):
214
+ """Dừng stream audio"""
215
+ return False, "⏹️ Đã dừng thu âm"
216
+
217
+ async def process_audio_input(audio_data, sample_rate, service, current_chat):
218
+ """Xử lý audio input từ user"""
219
+ if not service or not service.is_active:
220
+ return current_chat, "❌ Chưa kết nối audio", current_chat
221
+
222
+ if audio_data is None:
223
+ return current_chat, "⚠️ Không có audio input", current_chat
224
 
225
  try:
226
+ # Gửi audio đến Gemini
227
+ success = await service.send_audio_chunk(audio_data, sample_rate)
 
 
 
228
 
229
+ if success:
230
+ # Nhận audio response từ Gemini
231
+ audio_response = await service.receive_audio()
232
+
233
+ if audio_response:
234
+ resp_sample_rate, resp_audio_data = audio_response
235
+ # Lưu audio response để phát
236
+ audio_path = f"gemini_response_{int(asyncio.get_event_loop().time())}.wav"
237
+ import scipy.io.wavfile as wavfile
238
+ wavfile.write(audio_path, resp_sample_rate, resp_audio_data)
239
+
240
+ info_msg = "🔊 Đã nhận phản hồi audio từ Gemini"
241
+ return current_chat, info_msg, audio_path
242
+ else:
243
+ info_msg = "⏳ Đang chờ phản hồi audio..."
244
+ return current_chat, info_msg, None
245
  else:
246
+ return current_chat, "❌ Lỗi gửi audio", current_chat
 
 
247
 
248
+ except Exception as e:
249
+ error_msg = f"❌ Lỗi xử lý audio: {str(e)}"
250
+ return current_chat, error_msg, current_chat
251
+
252
+ async def send_text_message(message, current_chat, service):
253
+ """Gửi tin nhắn text (fallback)"""
254
+ if not service or not service.is_active:
255
+ error_msg = "❌ Chưa kết nối. Vui lòng kết nối trước."
256
+ new_chat = current_chat + [{"role": "user", "content": message}, {"role": "assistant", "content": error_msg}]
257
+ return new_chat, "Lỗi kết nối", new_chat
258
+
259
+ if not message.strip():
260
+ return current_chat, "⚠️ Vui lòng nhập tin nhắn", current_chat
261
+
262
+ try:
263
+ # Hiển thị tin nhắn user
264
+ new_chat = current_chat + [{"role": "user", "content": message}]
265
+
266
+ # Gửi text
267
+ response = await service.send_text(message)
268
+
269
+ # Cập nhật response
270
+ new_chat = new_chat + [{"role": "assistant", "content": response}]
271
+
272
+ return new_chat, f"✅ Đã nhận phản hồi ({len(response)} ký tự)", new_chat
273
+
274
  except Exception as e:
275
  error_msg = f"❌ Lỗi: {str(e)}"
276
+ new_chat = current_chat + [{"role": "user", "content": message}, {"role": "assistant", "content": error_msg}]
277
+ return new_chat, error_msg, new_chat
 
 
 
278
 
279
+ def clear_chat():
280
+ """Xóa chat"""
281
+ return [], "🧹 Đã xóa chat", []
282
+
283
+ # Thêm text input
284
  with gr.Row():
285
  text_input = gr.Textbox(
286
+ label="Hoặc nhập tin nhắn text",
287
+ placeholder="Nhập tin nhắn text và nhấn Enter...",
288
  scale=4
289
  )
290
+ send_text_btn = gr.Button("📤 Gửi text", scale=1)
291
 
292
+ # Event handlers
293
  connect_btn.click(
294
  connect_gemini,
295
  inputs=[api_key, voice_select, chatbot],
296
+ outputs=[connection_state, status_display, connection_info, chatbot, gemini_service_state]
297
  )
298
 
299
  disconnect_btn.click(
300
  disconnect_gemini,
301
+ inputs=[chatbot, gemini_service_state],
302
+ outputs=[connection_state, status_display, connection_info, chatbot, gemini_service_state]
303
+ )
304
+
305
+ start_audio_btn.click(
306
+ start_audio_stream,
307
+ inputs=[gemini_service_state, audio_streaming_state],
308
+ outputs=[audio_streaming_state, transcription_display]
309
+ )
310
+
311
+ stop_audio_btn.click(
312
+ stop_audio_stream,
313
+ inputs=[gemini_service_state, audio_streaming_state],
314
+ outputs=[audio_streaming_state, transcription_display]
315
  )
316
 
317
+ # Audio input processing
318
+ audio_input.stop_recording(
319
+ process_audio_input,
320
+ inputs=[audio_input, audio_input, gemini_service_state, chatbot],
321
+ outputs=[chatbot, connection_info, audio_output]
322
+ )
323
+
324
+ # Text message handling
325
  send_text_btn.click(
326
  send_text_message,
327
+ inputs=[text_input, chatbot, gemini_service_state],
328
+ outputs=[chatbot, connection_info, chatbot]
329
+ ).then(
330
+ lambda: "",
331
+ outputs=[text_input]
332
  )
333
 
334
  text_input.submit(
335
  send_text_message,
336
+ inputs=[text_input, chatbot, gemini_service_state],
337
+ outputs=[chatbot, connection_info, chatbot]
338
  ).then(
339
+ lambda: "",
340
  outputs=[text_input]
341
  )
342
 
343
+ clear_btn = gr.Button("🧹 Xóa chat", variant="secondary")
344
+ clear_btn.click(
345
+ clear_chat,
346
+ outputs=[chatbot, connection_info, chatbot]
 
 
 
 
 
347
  )
348
 
349
  # Hướng dẫn sử dụng
350
+ with gr.Accordion("📖 Hướng dẫn sử dụng Audio Streaming", open=True):
351
  gr.Markdown("""
352
+ ### 🎯 Cách sử dụng Audio Streaming:
 
 
 
 
 
353
 
354
+ 1. **Kết nối**:
355
+ - Nhập API Key Gemini
356
+ - Chọn giọng nói
357
+ - Nhấn **"Kết nối Audio"**
358
 
359
+ 2. **Trò chuyện bằng giọng nói**:
360
+ - Nhấn **"Bắt đầu nói"**
361
+ - Nói vào micro
362
+ - Gemini sẽ trả lời bằng audio ngay lập tức
363
 
364
+ 3. **Hoặc chat bằng text**:
365
+ - Nhập tin nhắn trong ô text
366
+ - Nhấn Enter hoặc **"Gửi text"**
 
 
367
 
368
+ ### 🔊 Tính năng Audio Streaming:
369
  - 🎙️ Real-time voice recognition
370
  - 🔊 Real-time audio response
371
+ - ⚡ Ultra low latency
372
+ - 🎯 Multiple voice options
373
+
374
+ ### 💡 Mẹo sử dụng:
375
+ - Sử dụng headset để chất lượng tốt hơn
376
+ - Nói rõ ràng, không nói quá nhanh
377
+ - Môi trường yên tĩnh cho kết quả tốt nhất
378
  """)
379
 
380
  return gemini_tab