anhkhoiphan commited on
Commit
623ef3c
·
verified ·
1 Parent(s): 5d977a0

Cập nhật app.py sử dụng chainlit_hf.py ở dev

Browse files
Files changed (1) hide show
  1. app.py +165 -27
app.py CHANGED
@@ -24,6 +24,8 @@ class ConversationState:
24
  outputs: Optional[Dict[str, Any]] = None
25
  selected_model: str = "Gemini 2.0 Flash"
26
  product_model_search: bool = False
 
 
27
  # New fields for delayed cleanup - now using asyncio
28
  pending_cleanup: bool = False
29
  cleanup_task: Optional[asyncio.Task] = None
@@ -38,6 +40,8 @@ class ConversationState:
38
  self.outputs = None
39
  self.selected_model = "Gemini 2.0 Flash"
40
  self.product_model_search = False
 
 
41
  # Reset cleanup fields but don't touch tasks
42
  self.pending_cleanup = False
43
  self.last_activity = datetime.now()
@@ -187,6 +191,23 @@ class StateManager:
187
  state.product_model_search = not state.product_model_search
188
  state.last_activity = datetime.now()
189
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  @staticmethod
191
  async def get_session_status() -> Dict[str, Dict[str, Any]]:
192
  """Get status of all sessions (for debugging)"""
@@ -198,7 +219,9 @@ class StateManager:
198
  "has_task": state.cleanup_task is not None and not state.cleanup_task.done(),
199
  "last_activity": state.last_activity.isoformat(),
200
  "selected_model": state.selected_model,
201
- "product_model_search": state.product_model_search
 
 
202
  }
203
  return status
204
 
@@ -213,13 +236,11 @@ class ChatService:
213
  image_path: Optional[str] = None
214
  ) -> str:
215
  """Handle chat responses with image support using async HTTP"""
216
- print(f"🔄 === DEBUG STATE ===\n Chat request with model: {state.selected_model}, Product Model Search: {state.product_model_search}, Session ID: {state.session_id}")
217
 
218
  # Update activity timestamp - this is KEY to prevent cleanup during active use
219
  state.last_activity = datetime.now()
220
-
221
- start = time.perf_counter()
222
-
223
  if not API_BASE_URL:
224
  return "Error: API_BASE_URL not configured"
225
 
@@ -237,9 +258,11 @@ class ChatService:
237
  data = {
238
  "message": message,
239
  "product_model_search": str(state.product_model_search).lower(),
 
240
  "session_id": state.session_id,
241
  "llm_model": state.selected_model,
242
- "debug": "Normal"
 
243
  }
244
 
245
  # Use multipart form data for image upload
@@ -256,7 +279,9 @@ class ChatService:
256
  "session_id": state.session_id,
257
  "debug": "Normal",
258
  "product_model_search": str(state.product_model_search).lower(),
259
- "llm_model": state.selected_model
 
 
260
  }
261
  resp = await client.post(
262
  f"{API_BASE_URL}/chat",
@@ -279,7 +304,6 @@ class ChatService:
279
  response = f"Error calling API: {e}"
280
  specs_advantages, solution_packages, raw_documents, outputs = None, None, None, None
281
 
282
- end = time.perf_counter()
283
 
284
  # Update state
285
  if specs_advantages is not None:
@@ -295,10 +319,10 @@ class ChatService:
295
  if state.specs_advantages is not None:
296
  await ChatService.get_specific_product_from_query(message, state)
297
 
298
- # Format response with 2-column grid for products
299
  formatted_response = ChatService.format_product_grid(response)
300
-
301
- return formatted_response + f"\n\n*Thời gian xử lí: {end - start:.6f}s*"
302
 
303
  @staticmethod
304
  def format_product_grid(response_text: str) -> str:
@@ -392,15 +416,19 @@ class DisplayService:
392
 
393
  print(specs_map)
394
  for prod_id, data in specs_map.items():
395
- spec = data.get("specification", None)
 
 
396
  model = data.get("model", "")
397
  url = data.get("url", "")
398
 
399
  # Handle both products and solution packages
400
  if url:
401
- full_name = f"**[{data['name']} {model}]({url})**"
 
402
  else:
403
- full_name = f"**{data['name']} {model}**"
 
404
 
405
  if full_name not in columns:
406
  columns.append(full_name)
@@ -458,6 +486,9 @@ class DisplayService:
458
  if spec_key == "Vậtl iệu":
459
  spec_key = "Vật liệu"
460
 
 
 
 
461
  existing_row = None
462
  for row in raw_data:
463
  if row["Thông số"] == spec_key:
@@ -494,24 +525,26 @@ class DisplayService:
494
  content = "💡 **Ưu điểm nổi trội**\n\n"
495
 
496
  for prod_id, data in specs_map.items():
497
- adv = data.get("advantages", "Không có ưu điểm")
 
 
 
498
  model = data.get("model", "")
499
  url = data.get("url", "")
500
 
501
  # Handle both products and solution packages
502
  if url:
503
- full_name = f"**[{data['name']} {model}]({url})**"
504
  else:
505
- full_name = f"**{data['name']} {model}**"
506
 
507
- if adv not in ["Không có ưu điểm", "", None]:
508
- content += f"### {full_name}\n"
509
-
510
- # Split by newlines and create bullet points
511
- advantages_list = [line.strip() for line in adv.split('\n') if line.strip()]
512
- for advantage in advantages_list:
513
- content += f"- {advantage}\n"
514
- content += "\n"
515
 
516
  return content
517
 
@@ -558,6 +591,13 @@ class UIService:
558
  def create_action_buttons(state: ConversationState):
559
  """Create persistent action buttons"""
560
  search_status = "🔍 Tìm theo mã sản phẩm (Đang tắt)" if not state.product_model_search else "🔍 Tìm theo mã sản phẩm (Đang bật)"
 
 
 
 
 
 
 
561
 
562
  return [
563
  cl.Action(name="show_specs", value="specs", label="📄 Thông số kỹ thuật", payload={"action": "specs"}),
@@ -565,6 +605,8 @@ class UIService:
565
  cl.Action(name="show_packages", value="packages", label="📦 Gói sản phẩm", payload={"action": "packages"}),
566
  cl.Action(name="show_all_products", value="all_products", label="🛒 Tất cả sản phẩm", payload={"action": "all_products"}),
567
  cl.Action(name="toggle_product_search", value="toggle_search", label=search_status, payload={"action": "toggle_search"}),
 
 
568
  cl.Action(name="change_model", value="model", label="🔄 Đổi model", payload={"action": "model"}),
569
  ]
570
 
@@ -572,9 +614,18 @@ class UIService:
572
  def create_start_buttons(state: ConversationState):
573
  """Create start buttons"""
574
  search_status = "🔍 Tìm theo mã sản phẩm (Đang tắt)" if not state.product_model_search else "🔍 Tìm theo mã sản phẩm (Đang bật)"
 
 
 
 
 
 
 
575
 
576
  return [
577
  cl.Action(name="toggle_product_search", value="toggle_search", label=search_status, payload={"action": "toggle_search"}),
 
 
578
  cl.Action(name="change_model", value="model", label="🔄 Đổi model", payload={"action": "model"}),
579
  ]
580
 
@@ -775,6 +826,92 @@ async def on_toggle_product_search(action):
775
  await UIService.send_message_with_buttons(status_message, app_state, author="assistant")
776
 
777
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
778
  @cl.action_callback("change_model")
779
  async def on_change_model(action):
780
  """Handle model change action"""
@@ -795,7 +932,7 @@ async def on_change_model(action):
795
  )
796
 
797
  await cl.Message(
798
- content=f"**Model hiện tại**: {app_state.selected_model}\n**Tìm kiếm theo mã**: {'Đang bật' if app_state.product_model_search else 'Đang tắt'}\n\nChọn model mới:",
799
  actions=model_actions,
800
  author="assistant"
801
  ).send()
@@ -867,7 +1004,8 @@ async def on_debug_sessions(action):
867
  debug_content += f"- Has task: {info['has_task']}\n"
868
  debug_content += f"- Last activity: {info['last_activity']}\n"
869
  debug_content += f"- Model: {info['selected_model']}\n"
870
- debug_content += f"- Product search: {info['product_model_search']}\n\n"
 
871
 
872
  await cl.Message(content=debug_content, author="assistant").send()
873
  except Exception as e:
 
24
  outputs: Optional[Dict[str, Any]] = None
25
  selected_model: str = "Gemini 2.0 Flash"
26
  product_model_search: bool = False
27
+ method: str = "dense" # "dense", "sparse", "hybrid"
28
+ is_enhance_query: bool = False # New field for query enhancement toggle
29
  # New fields for delayed cleanup - now using asyncio
30
  pending_cleanup: bool = False
31
  cleanup_task: Optional[asyncio.Task] = None
 
40
  self.outputs = None
41
  self.selected_model = "Gemini 2.0 Flash"
42
  self.product_model_search = False
43
+ self.method = "dense"
44
+ self.is_enhance_query = False
45
  # Reset cleanup fields but don't touch tasks
46
  self.pending_cleanup = False
47
  self.last_activity = datetime.now()
 
191
  state.product_model_search = not state.product_model_search
192
  state.last_activity = datetime.now()
193
 
194
+ @staticmethod
195
+ async def toggle_enhance_query(state: ConversationState):
196
+ """Toggle query enhancement mode"""
197
+ state.is_enhance_query = not state.is_enhance_query
198
+ state.last_activity = datetime.now()
199
+
200
+ @staticmethod
201
+ async def cycle_search_method(state: ConversationState):
202
+ """Cycle search method: dense -> sparse -> hybrid -> dense"""
203
+ if state.method == "dense":
204
+ state.method = "sparse"
205
+ elif state.method == "sparse":
206
+ state.method = "hybrid"
207
+ else:
208
+ state.method = "dense"
209
+ state.last_activity = datetime.now()
210
+
211
  @staticmethod
212
  async def get_session_status() -> Dict[str, Dict[str, Any]]:
213
  """Get status of all sessions (for debugging)"""
 
219
  "has_task": state.cleanup_task is not None and not state.cleanup_task.done(),
220
  "last_activity": state.last_activity.isoformat(),
221
  "selected_model": state.selected_model,
222
+ "product_model_search": state.product_model_search,
223
+ "method": state.method,
224
+ "is_enhance_query": state.is_enhance_query
225
  }
226
  return status
227
 
 
236
  image_path: Optional[str] = None
237
  ) -> str:
238
  """Handle chat responses with image support using async HTTP"""
239
+ print(f"🔄 === DEBUG STATE ===\nChat request with model: {state.selected_model}, Product Model Search: {state.product_model_search}, Method: {state.method}, Session ID: {state.session_id}")
240
 
241
  # Update activity timestamp - this is KEY to prevent cleanup during active use
242
  state.last_activity = datetime.now()
243
+
 
 
244
  if not API_BASE_URL:
245
  return "Error: API_BASE_URL not configured"
246
 
 
258
  data = {
259
  "message": message,
260
  "product_model_search": str(state.product_model_search).lower(),
261
+ "method": state.method,
262
  "session_id": state.session_id,
263
  "llm_model": state.selected_model,
264
+ "debug": "Normal",
265
+ "is_enhance_query": str(state.is_enhance_query).lower()
266
  }
267
 
268
  # Use multipart form data for image upload
 
279
  "session_id": state.session_id,
280
  "debug": "Normal",
281
  "product_model_search": str(state.product_model_search).lower(),
282
+ "method": state.method,
283
+ "llm_model": state.selected_model,
284
+ "is_enhance_query": str(state.is_enhance_query).lower()
285
  }
286
  resp = await client.post(
287
  f"{API_BASE_URL}/chat",
 
304
  response = f"Error calling API: {e}"
305
  specs_advantages, solution_packages, raw_documents, outputs = None, None, None, None
306
 
 
307
 
308
  # Update state
309
  if specs_advantages is not None:
 
319
  if state.specs_advantages is not None:
320
  await ChatService.get_specific_product_from_query(message, state)
321
 
322
+ # NEW: Format response with 2-column grid for products
323
  formatted_response = ChatService.format_product_grid(response)
324
+
325
+ return formatted_response
326
 
327
  @staticmethod
328
  def format_product_grid(response_text: str) -> str:
 
416
 
417
  print(specs_map)
418
  for prod_id, data in specs_map.items():
419
+ spec = data.get("specification")
420
+ if spec is None or spec == "" or spec == "None":
421
+ spec = "Hiện tại trong dữ liệu chưa có thông tin về thông số kĩ thuật của sản phẩm này!"
422
  model = data.get("model", "")
423
  url = data.get("url", "")
424
 
425
  # Handle both products and solution packages
426
  if url:
427
+ # full_name = f"**[{data['name']} {model}]({url})**"
428
+ full_name = f"**[{data['name']}]({url})**"
429
  else:
430
+ # full_name = f"**{data['name']} {model}**"
431
+ full_name = f"**{data['name']}**"
432
 
433
  if full_name not in columns:
434
  columns.append(full_name)
 
486
  if spec_key == "Vậtl iệu":
487
  spec_key = "Vật liệu"
488
 
489
+ if "|" in spec_key:
490
+ spec_key = spec_key.strip().replace("|", "").capitalize()
491
+
492
  existing_row = None
493
  for row in raw_data:
494
  if row["Thông số"] == spec_key:
 
525
  content = "💡 **Ưu điểm nổi trội**\n\n"
526
 
527
  for prod_id, data in specs_map.items():
528
+ # adv = data.get("advantages", "Hiện tại trong dữ liệu chưa thông tin về ưu điểm nổi trội của sản phẩm này!")
529
+ adv = data.get("advantages")
530
+ if adv is None or adv == "" or adv == "None":
531
+ adv = "Hiện tại trong dữ liệu chưa có thông tin về ưu điểm nổi trội của sản phẩm này!"
532
  model = data.get("model", "")
533
  url = data.get("url", "")
534
 
535
  # Handle both products and solution packages
536
  if url:
537
+ full_name = f"**[{data['name']}]({url})**"
538
  else:
539
+ full_name = f"**{data['name']}**"
540
 
541
+ content += f"### {full_name}\n"
542
+
543
+ # Split by newlines and create bullet points
544
+ advantages_list = [line.strip() for line in adv.split('\n') if line.strip()]
545
+ for advantage in advantages_list:
546
+ content += f"- {advantage}\n"
547
+ content += "\n"
 
548
 
549
  return content
550
 
 
591
  def create_action_buttons(state: ConversationState):
592
  """Create persistent action buttons"""
593
  search_status = "🔍 Tìm theo mã sản phẩm (Đang tắt)" if not state.product_model_search else "🔍 Tìm theo mã sản phẩm (Đang bật)"
594
+ method_labels = {
595
+ "dense": "🔎 Tìm kiếm: Dense",
596
+ "sparse": "🔎 Tìm kiếm: Sparse (BM25)",
597
+ "hybrid": "🔎 Tìm kiếm: Hybrid"
598
+ }
599
+ method_status = method_labels.get(state.method, "🔎 Tìm kiếm: Dense")
600
+ enhance_status = "🧠 Tăng cường truy vấn (Đang tắt)" if not state.is_enhance_query else "🧠 Tăng cường truy vấn (Đang bật)"
601
 
602
  return [
603
  cl.Action(name="show_specs", value="specs", label="📄 Thông số kỹ thuật", payload={"action": "specs"}),
 
605
  cl.Action(name="show_packages", value="packages", label="📦 Gói sản phẩm", payload={"action": "packages"}),
606
  cl.Action(name="show_all_products", value="all_products", label="🛒 Tất cả sản phẩm", payload={"action": "all_products"}),
607
  cl.Action(name="toggle_product_search", value="toggle_search", label=search_status, payload={"action": "toggle_search"}),
608
+ cl.Action(name="change_search_method", value="change_method", label="🔎 Đổi phương thức tìm kiếm", payload={"action": "change_method"}),
609
+ cl.Action(name="toggle_enhance_query", value="toggle_enhance", label=enhance_status, payload={"action": "toggle_enhance"}),
610
  cl.Action(name="change_model", value="model", label="🔄 Đổi model", payload={"action": "model"}),
611
  ]
612
 
 
614
  def create_start_buttons(state: ConversationState):
615
  """Create start buttons"""
616
  search_status = "🔍 Tìm theo mã sản phẩm (Đang tắt)" if not state.product_model_search else "🔍 Tìm theo mã sản phẩm (Đang bật)"
617
+ method_labels = {
618
+ "dense": "🔎 Tìm kiếm: Dense",
619
+ "sparse": "🔎 Tìm kiếm: Sparse (BM25)",
620
+ "hybrid": "🔎 Tìm kiếm: Hybrid"
621
+ }
622
+ method_status = method_labels.get(state.method, "🔎 Tìm kiếm: Dense")
623
+ enhance_status = "🧠 Tăng cường truy vấn (Đang tắt)" if not state.is_enhance_query else "🧠 Tăng cường truy vấn (Đang bật)"
624
 
625
  return [
626
  cl.Action(name="toggle_product_search", value="toggle_search", label=search_status, payload={"action": "toggle_search"}),
627
+ cl.Action(name="change_search_method", value="change_method", label="🔎 Đổi phương thức tìm kiếm", payload={"action": "change_method"}),
628
+ cl.Action(name="toggle_enhance_query", value="toggle_enhance", label=enhance_status, payload={"action": "toggle_enhance"}),
629
  cl.Action(name="change_model", value="model", label="🔄 Đổi model", payload={"action": "model"}),
630
  ]
631
 
 
826
  await UIService.send_message_with_buttons(status_message, app_state, author="assistant")
827
 
828
 
829
+ @cl.action_callback("toggle_enhance_query")
830
+ async def on_toggle_enhance_query(action):
831
+ """Handle toggle enhance query action"""
832
+ app_state = await ensure_session_state()
833
+ if app_state is None:
834
+ await cl.Message(content="Error: Session state not found", author="assistant").send()
835
+ return
836
+
837
+ await StateManager.toggle_enhance_query(app_state)
838
+
839
+ status_message = (
840
+ "✅ **Đã bật tăng cường truy vấn**\n\n"
841
+ "Hệ thống sẽ tự động cải thiện và mở rộng câu hỏi của bạn để tìm kiếm chính xác hơn."
842
+ if app_state.is_enhance_query
843
+ else "✅ **Đã tắt tăng cường truy vấn**\n\n"
844
+ "Hệ thống sẽ sử dụng câu hỏi gốc của bạn mà không cải thiện."
845
+ )
846
+
847
+ await UIService.send_message_with_buttons(status_message, app_state, author="assistant")
848
+
849
+
850
+ @cl.action_callback("change_search_method")
851
+ async def on_change_search_method(action):
852
+ """Handle change search method action"""
853
+ app_state = await ensure_session_state()
854
+ if app_state is None:
855
+ await cl.Message(content="Error: Session state not found", author="assistant").send()
856
+ return
857
+
858
+ method_actions = [
859
+ cl.Action(name="select_method_dense", value="dense", label="🔎 Dense (Mặc định)", payload={"method": "dense"}),
860
+ cl.Action(name="select_method_sparse", value="sparse", label="🔎 Sparse (BM25)", payload={"method": "sparse"}),
861
+ cl.Action(name="select_method_hybrid", value="hybrid", label="🔎 Hybrid", payload={"method": "hybrid"}),
862
+ cl.Action(name="back_to_main", value="back", label="🔙 Quay lại", payload={"action": "back"})
863
+ ]
864
+
865
+ current_method_labels = {
866
+ "dense": "Dense",
867
+ "sparse": "Sparse (BM25)",
868
+ "hybrid": "Hybrid"
869
+ }
870
+ current = current_method_labels.get(app_state.method, "Dense")
871
+
872
+ await cl.Message(
873
+ content=f"**Model hiện tại**: {app_state.selected_model}\n**Tìm kiếm theo mã**: {'Đang bật' if app_state.product_model_search else 'Đang tắt'}\n**Phương thức tìm kiếm**: {current}\n**Tăng cường truy vấn**: {'Đang bật' if app_state.is_enhance_query else 'Đang tắt'}\n\nChọn phương thức tìm kiếm mới:",
874
+ actions=method_actions,
875
+ author="assistant"
876
+ ).send()
877
+
878
+
879
+ @cl.action_callback("select_method_dense")
880
+ async def on_select_method_dense(action):
881
+ app_state = await ensure_session_state()
882
+ if app_state is None:
883
+ await cl.Message(content="Error: Session state not found", author="assistant").send()
884
+ return
885
+
886
+ app_state.method = "dense"
887
+ app_state.last_activity = datetime.now()
888
+ await UIService.send_message_with_buttons("✅ Đã chuyển sang **Dense Search**\n\nHệ thống sẽ sử dụng tìm kiếm semantic vector thông thường.", app_state, author="assistant")
889
+
890
+
891
+ @cl.action_callback("select_method_sparse")
892
+ async def on_select_method_sparse(action):
893
+ app_state = await ensure_session_state()
894
+ if app_state is None:
895
+ await cl.Message(content="Error: Session state not found", author="assistant").send()
896
+ return
897
+
898
+ app_state.method = "sparse"
899
+ app_state.last_activity = datetime.now()
900
+ await UIService.send_message_with_buttons("✅ Đã chuyển sang **Sparse Search (BM25)**\n\nHệ thống sẽ sử dụng tìm kiếm từ khóa BM25.", app_state, author="assistant")
901
+
902
+
903
+ @cl.action_callback("select_method_hybrid")
904
+ async def on_select_method_hybrid(action):
905
+ app_state = await ensure_session_state()
906
+ if app_state is None:
907
+ await cl.Message(content="Error: Session state not found", author="assistant").send()
908
+ return
909
+
910
+ app_state.method = "hybrid"
911
+ app_state.last_activity = datetime.now()
912
+ await UIService.send_message_with_buttons("✅ Đã chuyển sang **Hybrid Search**\n\nHệ thống sẽ kết hợp cả Dense và Sparse vector.", app_state, author="assistant")
913
+
914
+
915
  @cl.action_callback("change_model")
916
  async def on_change_model(action):
917
  """Handle model change action"""
 
932
  )
933
 
934
  await cl.Message(
935
+ content=f"**Model hiện tại**: {app_state.selected_model}\n**Tìm kiếm theo mã**: {'Đang bật' if app_state.product_model_search else 'Đang tắt'}\n**Phương thức tìm kiếm**: {app_state.method}\n**Tăng cường truy vấn**: {'Đang bật' if app_state.is_enhance_query else 'Đang tắt'}\n\nChọn model mới:",
936
  actions=model_actions,
937
  author="assistant"
938
  ).send()
 
1004
  debug_content += f"- Has task: {info['has_task']}\n"
1005
  debug_content += f"- Last activity: {info['last_activity']}\n"
1006
  debug_content += f"- Model: {info['selected_model']}\n"
1007
+ debug_content += f"- Product search: {info['product_model_search']}\n"
1008
+ debug_content += f"- Method: {info.get('method', 'dense')}\n\n"
1009
 
1010
  await cl.Message(content=debug_content, author="assistant").send()
1011
  except Exception as e: