Upload backend/hue_portal/chatbot/chatbot.py with huggingface_hub
Browse files
backend/hue_portal/chatbot/chatbot.py
CHANGED
|
@@ -6,9 +6,11 @@ import copy
|
|
| 6 |
import logging
|
| 7 |
import json
|
| 8 |
import time
|
|
|
|
|
|
|
| 9 |
from typing import Dict, Any, Optional
|
| 10 |
from hue_portal.core.chatbot import Chatbot as CoreChatbot, get_chatbot as get_core_chatbot
|
| 11 |
-
from hue_portal.chatbot.router import decide_route, IntentRoute, RouteDecision
|
| 12 |
from hue_portal.chatbot.context_manager import ConversationContext
|
| 13 |
from hue_portal.chatbot.llm_integration import LLMGenerator
|
| 14 |
from hue_portal.core.models import LegalSection
|
|
@@ -158,6 +160,68 @@ class Chatbot(CoreChatbot):
|
|
| 158 |
except Exception as e:
|
| 159 |
print(f"⚠️ Failed to save cached bot message: {e}")
|
| 160 |
return cached_response
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
|
| 162 |
# Always send legal intent through Slow Path RAG
|
| 163 |
if intent == "search_legal":
|
|
@@ -501,6 +565,25 @@ class Chatbot(CoreChatbot):
|
|
| 501 |
if not response.get("results"):
|
| 502 |
return False
|
| 503 |
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 504 |
|
| 505 |
def _handle_legal_query(self, query: str, session_id: Optional[str] = None) -> Dict[str, Any]:
|
| 506 |
"""
|
|
|
|
| 6 |
import logging
|
| 7 |
import json
|
| 8 |
import time
|
| 9 |
+
import unicodedata
|
| 10 |
+
import re
|
| 11 |
from typing import Dict, Any, Optional
|
| 12 |
from hue_portal.core.chatbot import Chatbot as CoreChatbot, get_chatbot as get_core_chatbot
|
| 13 |
+
from hue_portal.chatbot.router import decide_route, IntentRoute, RouteDecision, DOCUMENT_CODE_PATTERNS
|
| 14 |
from hue_portal.chatbot.context_manager import ConversationContext
|
| 15 |
from hue_portal.chatbot.llm_integration import LLMGenerator
|
| 16 |
from hue_portal.core.models import LegalSection
|
|
|
|
| 160 |
except Exception as e:
|
| 161 |
print(f"⚠️ Failed to save cached bot message: {e}")
|
| 162 |
return cached_response
|
| 163 |
+
|
| 164 |
+
# Wizard / option-first ngay tại chatbot layer:
|
| 165 |
+
# Nếu là câu hỏi search_legal chung, chưa chọn văn bản, không có mã văn bản trong câu hỏi
|
| 166 |
+
# => trả về danh sách văn bản để người dùng chọn, không sinh câu trả lời chi tiết.
|
| 167 |
+
if (
|
| 168 |
+
intent == "search_legal"
|
| 169 |
+
and not selected_doc_code
|
| 170 |
+
and not self._query_has_document_code(query)
|
| 171 |
+
):
|
| 172 |
+
intro_message = (
|
| 173 |
+
"Tôi tìm thấy một số nhóm văn bản có thể liên quan đến câu hỏi của bạn.\n\n"
|
| 174 |
+
"Bạn hãy chọn văn bản muốn tra cứu trước, sau đó tôi sẽ trả lời chi tiết hơn:"
|
| 175 |
+
)
|
| 176 |
+
clarification_options = [
|
| 177 |
+
{
|
| 178 |
+
"code": "264-QD-TW",
|
| 179 |
+
"title": "Quyết định 264-QĐ/TW về kỷ luật đảng viên",
|
| 180 |
+
"reason": "Quy định chung về xử lý kỷ luật đối với đảng viên vi phạm.",
|
| 181 |
+
},
|
| 182 |
+
{
|
| 183 |
+
"code": "QD-69-TW",
|
| 184 |
+
"title": "Quy định 69-QĐ/TW về kỷ luật tổ chức đảng, đảng viên",
|
| 185 |
+
"reason": "Quy định chi tiết về các hành vi vi phạm và hình thức kỷ luật.",
|
| 186 |
+
},
|
| 187 |
+
{
|
| 188 |
+
"code": "TT-02-CAND",
|
| 189 |
+
"title": "Thông tư 02/2021/TT-BCA về điều lệnh CAND",
|
| 190 |
+
"reason": "Quy định về điều lệnh, lễ tiết, tác phong trong CAND.",
|
| 191 |
+
},
|
| 192 |
+
{
|
| 193 |
+
"code": "__other__",
|
| 194 |
+
"title": "Khác",
|
| 195 |
+
"reason": "Tôi muốn hỏi văn bản hoặc chủ đề pháp luật khác.",
|
| 196 |
+
},
|
| 197 |
+
]
|
| 198 |
+
response = {
|
| 199 |
+
"message": intro_message,
|
| 200 |
+
"intent": intent,
|
| 201 |
+
"confidence": confidence,
|
| 202 |
+
"results": [],
|
| 203 |
+
"count": 0,
|
| 204 |
+
"routing": "legal_wizard",
|
| 205 |
+
"type": "options",
|
| 206 |
+
"wizard_stage": "choose_document",
|
| 207 |
+
"clarification": {
|
| 208 |
+
"message": intro_message,
|
| 209 |
+
"options": clarification_options,
|
| 210 |
+
},
|
| 211 |
+
"options": clarification_options,
|
| 212 |
+
}
|
| 213 |
+
if session_id:
|
| 214 |
+
response["session_id"] = session_id
|
| 215 |
+
try:
|
| 216 |
+
ConversationContext.add_message(
|
| 217 |
+
session_id=session_id,
|
| 218 |
+
role="bot",
|
| 219 |
+
content=intro_message,
|
| 220 |
+
intent=intent,
|
| 221 |
+
)
|
| 222 |
+
except Exception as e:
|
| 223 |
+
print(f"⚠️ Failed to save wizard bot message: {e}")
|
| 224 |
+
return response
|
| 225 |
|
| 226 |
# Always send legal intent through Slow Path RAG
|
| 227 |
if intent == "search_legal":
|
|
|
|
| 565 |
if not response.get("results"):
|
| 566 |
return False
|
| 567 |
return True
|
| 568 |
+
|
| 569 |
+
def _query_has_document_code(self, query: str) -> bool:
|
| 570 |
+
"""
|
| 571 |
+
Check if the raw query string explicitly contains a known document code pattern
|
| 572 |
+
(ví dụ: '264/QĐ-TW', 'QD-69-TW', 'TT-02-CAND').
|
| 573 |
+
"""
|
| 574 |
+
if not query:
|
| 575 |
+
return False
|
| 576 |
+
# Remove accents để regex đơn giản hơn
|
| 577 |
+
normalized = unicodedata.normalize("NFD", query)
|
| 578 |
+
normalized = "".join(ch for ch in normalized if unicodedata.category(ch) != "Mn")
|
| 579 |
+
normalized = normalized.upper()
|
| 580 |
+
for pattern in DOCUMENT_CODE_PATTERNS:
|
| 581 |
+
try:
|
| 582 |
+
if re.search(pattern, normalized):
|
| 583 |
+
return True
|
| 584 |
+
except re.error:
|
| 585 |
+
continue
|
| 586 |
+
return False
|
| 587 |
|
| 588 |
def _handle_legal_query(self, query: str, session_id: Optional[str] = None) -> Dict[str, Any]:
|
| 589 |
"""
|