Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -790,13 +790,18 @@ RESPONSE_TEMPLATES = {
|
|
| 790 |
" khai_sinh": {
|
| 791 |
"short": "Dạ để khai sinh cho bé, bác/cháu cần mang: Giấy khai sinh, CCCD của cả cha mẹ, và sổ hộ khẩu. Ra UBND xã/phường nơi cư trú là được ạ!",
|
| 792 |
"detailed": """Dạ về khai sinh cho bé, em hướng dẫn chi tiết ạ:
|
|
|
|
| 793 |
📋 **HỒ SƠ CẦN CHUẨN BỊ:**
|
| 794 |
1. Giấy khai sinh (điền đầy đủ thông tin)
|
| 795 |
2. CCCD/CMND của cả cha và mẹ
|
| 796 |
3. Sổ hộ khẩu của cha mẹ
|
|
|
|
| 797 |
🏢 **NƠI LÀM:** UBND xã/phường nơi cư trú
|
|
|
|
| 798 |
⏰ **THỜI GIAN:** 1-3 ngày
|
|
|
|
| 799 |
💰 **LỆ PHÍ:** Miễn phí
|
|
|
|
| 800 |
⚠️ **LƯU Ý:** Làm trong 60 ngày kể từ khi sinh để tránh phạt nhé!"""
|
| 801 |
}
|
| 802 |
}
|
|
@@ -930,6 +935,7 @@ Dựa trên tài liệu và quy định hiện hành:
|
|
| 930 |
# Bước 4: Tổng hợp câu trả lời
|
| 931 |
step4 = f"""BƯỚC 4 - TỔNG HỢP TRẢ LỜI:
|
| 932 |
Hãy trả lời theo phong cách: {persona['tone']}
|
|
|
|
| 933 |
Lưu ý:
|
| 934 |
- Dùng các mẫu câu: {', '.join(persona['greeting'][:2])}
|
| 935 |
- Kết thúc bằng: {', '.join(persona['closing'][:2])}
|
|
@@ -1192,11 +1198,16 @@ QUY TRÌNH: {proc['steps']}
|
|
| 1192 |
|
| 1193 |
# Bước 8: Full prompt cho LLM
|
| 1194 |
full_prompt = f"""Bạn là cán bộ tư vấn thủ tục hành chính công tại UBND. Hãy hỗ trợ người dân.
|
|
|
|
| 1195 |
{cot_prompt}
|
|
|
|
| 1196 |
CÂU HỎI: {query}
|
|
|
|
| 1197 |
{proc_info}
|
|
|
|
| 1198 |
LỊCH SỬ ĐỌC CHUẨN KHI CÓ:
|
| 1199 |
{relevant_history}
|
|
|
|
| 1200 |
HÃY TRẢ LỜI NGƯỜI DÂNG:"""
|
| 1201 |
|
| 1202 |
messages = [
|
|
@@ -1320,22 +1331,23 @@ print()
|
|
| 1320 |
print('='*70)
|
| 1321 |
|
| 1322 |
# ======================================================================
|
| 1323 |
-
#
|
| 1324 |
-
#
|
|
|
|
| 1325 |
|
| 1326 |
import gradio as gr
|
| 1327 |
print('='*70)
|
| 1328 |
-
print('🖥️ KHỞI TẠO GRADIO INTERFACE
|
| 1329 |
print('='*70)
|
| 1330 |
print()
|
| 1331 |
|
| 1332 |
-
#
|
| 1333 |
admin_procedures = list(DICHVUCONG_DATA.values())
|
| 1334 |
|
| 1335 |
def get_procedures_list():
|
| 1336 |
lines = ["| Mã | Tên thủ tục | Cơ quan |"]
|
| 1337 |
lines.append("|---|---|---|")
|
| 1338 |
-
for p in admin_procedures[:20]: #
|
| 1339 |
lines.append(f"| {p['code']} | {p['name']} | {p['coquan']} |")
|
| 1340 |
return "\n".join(lines)
|
| 1341 |
|
|
@@ -1346,18 +1358,20 @@ def admin_add(code, name, docs, coquan, time_val, lephi, note, steps):
|
|
| 1346 |
'time': time_val, 'lephi': lephi, 'note': note, 'steps': steps
|
| 1347 |
}
|
| 1348 |
admin_procedures.append(new_proc)
|
| 1349 |
-
|
|
|
|
|
|
|
| 1350 |
|
| 1351 |
-
#
|
| 1352 |
def process_chat_pro(audio, text, history, persona="thuong"):
|
| 1353 |
try:
|
| 1354 |
if history is None:
|
| 1355 |
history = []
|
| 1356 |
|
| 1357 |
-
# Cập nhật
|
| 1358 |
conversation_memory.set_persona(persona)
|
| 1359 |
|
| 1360 |
-
#
|
| 1361 |
query = ''
|
| 1362 |
if audio and not os.path.isdir(audio):
|
| 1363 |
query = speech_to_text(audio)
|
|
@@ -1367,16 +1381,21 @@ def process_chat_pro(audio, text, history, persona="thuong"):
|
|
| 1367 |
if len(query) < 3:
|
| 1368 |
return history, None, ''
|
| 1369 |
|
| 1370 |
-
#
|
| 1371 |
result = ai_process_pro(query, use_memory=True)
|
| 1372 |
|
| 1373 |
-
#
|
| 1374 |
-
response = f"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1375 |
|
| 1376 |
history.append({"role": "user", "content": query})
|
| 1377 |
history.append({"role": "assistant", "content": response})
|
| 1378 |
|
| 1379 |
-
#
|
| 1380 |
audio_out = None
|
| 1381 |
try:
|
| 1382 |
loop = asyncio.get_event_loop()
|
|
@@ -1394,7 +1413,7 @@ def process_chat_pro(audio, text, history, persona="thuong"):
|
|
| 1394 |
except Exception as e:
|
| 1395 |
import traceback
|
| 1396 |
traceback.print_exc()
|
| 1397 |
-
error_msg = f"
|
| 1398 |
return history, None, error_msg
|
| 1399 |
|
| 1400 |
def clear_chat_pro():
|
|
@@ -1410,23 +1429,28 @@ def change_persona(new_persona):
|
|
| 1410 |
}
|
| 1411 |
return f"✅ Đã chuyển sang: {persona_names.get(new_persona, new_persona)}"
|
| 1412 |
|
| 1413 |
-
#
|
| 1414 |
custom_css = """
|
|
|
|
| 1415 |
.gradio-container {
|
| 1416 |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
|
| 1417 |
background-color: #f8f9fa !important;
|
| 1418 |
}
|
|
|
|
|
|
|
| 1419 |
footer {display: none !important;}
|
| 1420 |
-
|
|
|
|
| 1421 |
.admin-topbar {
|
| 1422 |
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
|
| 1423 |
padding: 20px 30px;
|
| 1424 |
display: flex;
|
| 1425 |
align-items: center;
|
| 1426 |
-
margin: -20px -20px
|
| 1427 |
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
| 1428 |
color: white;
|
| 1429 |
}
|
|
|
|
| 1430 |
.admin-icon {
|
| 1431 |
font-size: 45px;
|
| 1432 |
margin-right: 20px;
|
|
@@ -1434,6 +1458,7 @@ footer {display: none !important;}
|
|
| 1434 |
padding: 10px;
|
| 1435 |
border-radius: 12px;
|
| 1436 |
}
|
|
|
|
| 1437 |
.admin-brand h1 {
|
| 1438 |
color: #ffffff !important;
|
| 1439 |
font-size: 24px !important;
|
|
@@ -1442,45 +1467,15 @@ footer {display: none !important;}
|
|
| 1442 |
letter-spacing: 1px;
|
| 1443 |
text-transform: uppercase;
|
| 1444 |
}
|
|
|
|
| 1445 |
.admin-brand p {
|
| 1446 |
color: #e0e0e0 !important;
|
| 1447 |
font-size: 14px !important;
|
| 1448 |
margin: 5px 0 0 0 !important;
|
| 1449 |
font-weight: 300 !important;
|
| 1450 |
}
|
| 1451 |
-
|
| 1452 |
-
|
| 1453 |
-
background: #ffffff;
|
| 1454 |
-
padding: 15px 20px;
|
| 1455 |
-
margin: 0px -20px 20px -20px; /* Tràn lề sát mép màn hình */
|
| 1456 |
-
border-bottom: 1px solid #dee2e6;
|
| 1457 |
-
box-shadow: 0 2px 8px rgba(0,0,0,0.02);
|
| 1458 |
-
}
|
| 1459 |
-
.section-title-news {
|
| 1460 |
-
color: #1e3c72;
|
| 1461 |
-
font-weight: 600;
|
| 1462 |
-
margin-bottom: 12px;
|
| 1463 |
-
border-bottom: 2px solid #e9ecef;
|
| 1464 |
-
padding-bottom: 6px;
|
| 1465 |
-
font-size: 15px;
|
| 1466 |
-
}
|
| 1467 |
-
.news-grid {
|
| 1468 |
-
display: grid;
|
| 1469 |
-
grid-template-columns: repeat(3, 1fr);
|
| 1470 |
-
gap: 15px;
|
| 1471 |
-
}
|
| 1472 |
-
@media (max-width: 768px) {
|
| 1473 |
-
.news-grid {
|
| 1474 |
-
grid-template-columns: 1fr;
|
| 1475 |
-
}
|
| 1476 |
-
}
|
| 1477 |
-
.news-card {
|
| 1478 |
-
background: #f8fafc;
|
| 1479 |
-
padding: 14px 16px;
|
| 1480 |
-
border-left: 4px solid #1e3c72;
|
| 1481 |
-
border-radius: 6px;
|
| 1482 |
-
box-shadow: 0 2px 4px rgba(0,0,0,0.02);
|
| 1483 |
-
}
|
| 1484 |
button.primary {
|
| 1485 |
background-color: #0056b3 !important;
|
| 1486 |
color: white !important;
|
|
@@ -1489,16 +1484,21 @@ button.primary {
|
|
| 1489 |
border: none !important;
|
| 1490 |
transition: all 0.3s ease;
|
| 1491 |
}
|
|
|
|
| 1492 |
button.primary:hover {
|
| 1493 |
background-color: #004494 !important;
|
| 1494 |
box-shadow: 0 4px 8px rgba(0,86,179,0.3) !important;
|
| 1495 |
}
|
|
|
|
|
|
|
| 1496 |
.main-chatbot {
|
| 1497 |
border: 1px solid #dee2e6 !important;
|
| 1498 |
border-radius: 8px !important;
|
| 1499 |
background-color: #ffffff !important;
|
| 1500 |
box-shadow: 0 2px 15px rgba(0,0,0,0.03) !important;
|
| 1501 |
}
|
|
|
|
|
|
|
| 1502 |
.section-title {
|
| 1503 |
color: #1e3c72;
|
| 1504 |
font-weight: 600;
|
|
@@ -1508,15 +1508,22 @@ button.primary:hover {
|
|
| 1508 |
}
|
| 1509 |
"""
|
| 1510 |
|
| 1511 |
-
# Khởi tạo
|
| 1512 |
with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", neutral_hue="slate"), css=custom_css) as demo:
|
| 1513 |
|
| 1514 |
-
# 1.
|
| 1515 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1516 |
|
| 1517 |
-
# 2. Hệ thống chuyển Tab làm việc chính
|
| 1518 |
with gr.Tabs():
|
| 1519 |
-
#
|
| 1520 |
with gr.Tab("💬 Trợ lý AI"):
|
| 1521 |
with gr.Row():
|
| 1522 |
with gr.Column(scale=7):
|
|
@@ -1533,6 +1540,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", neutral_hue="slate"), cs
|
|
| 1533 |
btn_send = gr.Button('GỬI CÂU HỎI', variant='primary')
|
| 1534 |
btn_clear = gr.Button('Làm mới')
|
| 1535 |
|
|
|
|
| 1536 |
gr.HTML("""
|
| 1537 |
<div style="
|
| 1538 |
margin-top: 15px;
|
|
@@ -1546,9 +1554,9 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", neutral_hue="slate"), cs
|
|
| 1546 |
">
|
| 1547 |
<strong style="color: #1e3c72;">💡 Cách dùng nhanh:</strong>
|
| 1548 |
<ul style="margin: 5px 0 0 15px; padding: 0;">
|
| 1549 |
-
<li>Nhập câu hỏi
|
| 1550 |
-
<li>Nhấn
|
| 1551 |
-
<li>
|
| 1552 |
</ul>
|
| 1553 |
</div>
|
| 1554 |
""")
|
|
@@ -1566,34 +1574,28 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", neutral_hue="slate"), cs
|
|
| 1566 |
inputs=[audio_in, msg_in]
|
| 1567 |
)
|
| 1568 |
|
| 1569 |
-
#
|
| 1570 |
btn_send.click(process_chat_pro, [audio_in, msg_in, chat], [chat, audio_out, msg_in])
|
| 1571 |
msg_in.submit(process_chat_pro, [audio_in, msg_in, chat], [chat, audio_out, msg_in])
|
| 1572 |
btn_clear.click(clear_chat_pro, [], [chat, audio_out, msg_in])
|
| 1573 |
-
|
| 1574 |
-
# TAB 2: Quản trị viên cập nhật thủ tục (CÓ FULL ĐĂNG NHẬP / ĐĂNG XUẤT)
|
| 1575 |
with gr.Tab("⚙️ Admin Panel"):
|
|
|
|
| 1576 |
|
| 1577 |
-
|
| 1578 |
-
|
| 1579 |
-
|
| 1580 |
-
|
| 1581 |
-
|
| 1582 |
-
|
| 1583 |
-
|
| 1584 |
-
|
| 1585 |
-
btn_login = gr.Button("Đăng nhập 🔑", variant="primary")
|
| 1586 |
|
| 1587 |
-
#
|
| 1588 |
admin_content = gr.Group(visible=False)
|
| 1589 |
|
| 1590 |
with admin_content:
|
| 1591 |
-
with gr.Row():
|
| 1592 |
-
gr.Markdown("### 🏛️ KHU VỰC CẬP NHẬT DỮ LIỆU CẤP CAO")
|
| 1593 |
-
btn_logout = gr.Button("Đăng xuất ↩️", variant="stop", scale=1, size="sm")
|
| 1594 |
-
|
| 1595 |
gr.Markdown("---")
|
| 1596 |
-
gr.Markdown("### ➕ Thêm thủ tục
|
| 1597 |
with gr.Row():
|
| 1598 |
with gr.Column():
|
| 1599 |
admin_code = gr.Textbox(label="Mã thủ tục")
|
|
@@ -1602,64 +1604,83 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", neutral_hue="slate"), cs
|
|
| 1602 |
admin_coquan = gr.Textbox(label="Cơ quan tiếp nhận")
|
| 1603 |
|
| 1604 |
with gr.Column():
|
| 1605 |
-
admin_time = gr.Textbox(label="Thời gian
|
| 1606 |
-
admin_lephi = gr.Textbox(label="Lệ phí
|
| 1607 |
-
admin_note = gr.Textbox(label="Lưu ý
|
| 1608 |
-
admin_steps = gr.Textbox(label="Quy trình
|
| 1609 |
|
| 1610 |
btn_add = gr.Button("➕ THÊM VÀO HỆ THỐNG", variant='primary', size='lg')
|
| 1611 |
-
add_output = gr.Textbox(label="
|
| 1612 |
procedures_list = gr.Markdown(value=get_procedures_list())
|
| 1613 |
|
| 1614 |
-
#
|
| 1615 |
-
def
|
| 1616 |
-
if
|
| 1617 |
-
|
| 1618 |
-
|
| 1619 |
-
else:
|
| 1620 |
-
gr.Warning("Tài khoản hoặc mật khẩu không chính xác!")
|
| 1621 |
return gr.update(visible=True), gr.update(visible=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1622 |
|
| 1623 |
-
|
| 1624 |
-
gr.Info("Đã đăng xuất an toàn khỏi tài khoản cán bộ.")
|
| 1625 |
-
return gr.update(visible=True), gr.update(visible=False), gr.update(value=""), gr.update(value="")
|
| 1626 |
-
|
| 1627 |
-
# Đăng ký tương tác sự kiện xác thực
|
| 1628 |
btn_login.click(
|
| 1629 |
-
|
| 1630 |
-
inputs=[
|
| 1631 |
-
outputs=[
|
| 1632 |
-
)
|
| 1633 |
-
|
| 1634 |
-
btn_logout.click(
|
| 1635 |
-
admin_logout,
|
| 1636 |
-
inputs=[],
|
| 1637 |
-
outputs=[login_form, admin_content, input_user, input_pass]
|
| 1638 |
)
|
| 1639 |
|
|
|
|
| 1640 |
btn_add.click(
|
| 1641 |
admin_add,
|
| 1642 |
[admin_code, admin_name, admin_docs, admin_coquan, admin_time, admin_lephi, admin_note, admin_steps],
|
| 1643 |
[add_output, procedures_list]
|
| 1644 |
)
|
| 1645 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1646 |
|
| 1647 |
# ======================================================================
|
| 1648 |
-
#
|
| 1649 |
-
#
|
|
|
|
| 1650 |
|
| 1651 |
print('='*70)
|
| 1652 |
-
print('🚀 KHỞI ĐỘNG ỨNG DỤNG AI
|
| 1653 |
print('='*70)
|
| 1654 |
print()
|
| 1655 |
|
|
|
|
|
|
|
| 1656 |
print('='*70)
|
| 1657 |
-
print(' Đang
|
| 1658 |
-
print('
|
| 1659 |
print('='*70)
|
| 1660 |
print()
|
| 1661 |
|
| 1662 |
-
#
|
| 1663 |
demo.launch(
|
| 1664 |
share=True,
|
| 1665 |
server_name='0.0.0.0',
|
|
|
|
| 790 |
" khai_sinh": {
|
| 791 |
"short": "Dạ để khai sinh cho bé, bác/cháu cần mang: Giấy khai sinh, CCCD của cả cha mẹ, và sổ hộ khẩu. Ra UBND xã/phường nơi cư trú là được ạ!",
|
| 792 |
"detailed": """Dạ về khai sinh cho bé, em hướng dẫn chi tiết ạ:
|
| 793 |
+
|
| 794 |
📋 **HỒ SƠ CẦN CHUẨN BỊ:**
|
| 795 |
1. Giấy khai sinh (điền đầy đủ thông tin)
|
| 796 |
2. CCCD/CMND của cả cha và mẹ
|
| 797 |
3. Sổ hộ khẩu của cha mẹ
|
| 798 |
+
|
| 799 |
🏢 **NƠI LÀM:** UBND xã/phường nơi cư trú
|
| 800 |
+
|
| 801 |
⏰ **THỜI GIAN:** 1-3 ngày
|
| 802 |
+
|
| 803 |
💰 **LỆ PHÍ:** Miễn phí
|
| 804 |
+
|
| 805 |
⚠️ **LƯU Ý:** Làm trong 60 ngày kể từ khi sinh để tránh phạt nhé!"""
|
| 806 |
}
|
| 807 |
}
|
|
|
|
| 935 |
# Bước 4: Tổng hợp câu trả lời
|
| 936 |
step4 = f"""BƯỚC 4 - TỔNG HỢP TRẢ LỜI:
|
| 937 |
Hãy trả lời theo phong cách: {persona['tone']}
|
| 938 |
+
|
| 939 |
Lưu ý:
|
| 940 |
- Dùng các mẫu câu: {', '.join(persona['greeting'][:2])}
|
| 941 |
- Kết thúc bằng: {', '.join(persona['closing'][:2])}
|
|
|
|
| 1198 |
|
| 1199 |
# Bước 8: Full prompt cho LLM
|
| 1200 |
full_prompt = f"""Bạn là cán bộ tư vấn thủ tục hành chính công tại UBND. Hãy hỗ trợ người dân.
|
| 1201 |
+
|
| 1202 |
{cot_prompt}
|
| 1203 |
+
|
| 1204 |
CÂU HỎI: {query}
|
| 1205 |
+
|
| 1206 |
{proc_info}
|
| 1207 |
+
|
| 1208 |
LỊCH SỬ ĐỌC CHUẨN KHI CÓ:
|
| 1209 |
{relevant_history}
|
| 1210 |
+
|
| 1211 |
HÃY TRẢ LỜI NGƯỜI DÂNG:"""
|
| 1212 |
|
| 1213 |
messages = [
|
|
|
|
| 1331 |
print('='*70)
|
| 1332 |
|
| 1333 |
# ======================================================================
|
| 1334 |
+
# ============================================================
|
| 1335 |
+
# PHẦN 12.5: GRADIO INTERFACE
|
| 1336 |
+
# ============================================================
|
| 1337 |
|
| 1338 |
import gradio as gr
|
| 1339 |
print('='*70)
|
| 1340 |
+
print('🖥️ KHỞI TẠO GRADIO INTERFACE')
|
| 1341 |
print('='*70)
|
| 1342 |
print()
|
| 1343 |
|
| 1344 |
+
# Admin functions
|
| 1345 |
admin_procedures = list(DICHVUCONG_DATA.values())
|
| 1346 |
|
| 1347 |
def get_procedures_list():
|
| 1348 |
lines = ["| Mã | Tên thủ tục | Cơ quan |"]
|
| 1349 |
lines.append("|---|---|---|")
|
| 1350 |
+
for p in admin_procedures[:20]: # Limit to 20 for display
|
| 1351 |
lines.append(f"| {p['code']} | {p['name']} | {p['coquan']} |")
|
| 1352 |
return "\n".join(lines)
|
| 1353 |
|
|
|
|
| 1358 |
'time': time_val, 'lephi': lephi, 'note': note, 'steps': steps
|
| 1359 |
}
|
| 1360 |
admin_procedures.append(new_proc)
|
| 1361 |
+
# For simplicity, we are not updating DICHVUCONG_DATA or Pinecone here directly
|
| 1362 |
+
# In a real application, you would update the backend data source
|
| 1363 |
+
return f"✅ Đã thêm: {code} - {name}", get_procedures_list()
|
| 1364 |
|
| 1365 |
+
# Chat function với AI Process PRO
|
| 1366 |
def process_chat_pro(audio, text, history, persona="thuong"):
|
| 1367 |
try:
|
| 1368 |
if history is None:
|
| 1369 |
history = []
|
| 1370 |
|
| 1371 |
+
# Cập nhật persona
|
| 1372 |
conversation_memory.set_persona(persona)
|
| 1373 |
|
| 1374 |
+
# Xử lý input
|
| 1375 |
query = ''
|
| 1376 |
if audio and not os.path.isdir(audio):
|
| 1377 |
query = speech_to_text(audio)
|
|
|
|
| 1381 |
if len(query) < 3:
|
| 1382 |
return history, None, ''
|
| 1383 |
|
| 1384 |
+
# Dùng AI Process PRO
|
| 1385 |
result = ai_process_pro(query, use_memory=True)
|
| 1386 |
|
| 1387 |
+
# Format response
|
| 1388 |
+
response = f"""
|
| 1389 |
+
|
| 1390 |
+
{result['response']}
|
| 1391 |
+
|
| 1392 |
+
---
|
| 1393 |
+
_⏱️ Xử lý: {result['process_time']}s | 📄 Tài liệu: {result['docs_count']} | 🌡️ Temp: {result['temperature']}_"""
|
| 1394 |
|
| 1395 |
history.append({"role": "user", "content": query})
|
| 1396 |
history.append({"role": "assistant", "content": response})
|
| 1397 |
|
| 1398 |
+
# TTS
|
| 1399 |
audio_out = None
|
| 1400 |
try:
|
| 1401 |
loop = asyncio.get_event_loop()
|
|
|
|
| 1413 |
except Exception as e:
|
| 1414 |
import traceback
|
| 1415 |
traceback.print_exc()
|
| 1416 |
+
error_msg = f"Lỗi: {str(e)}"
|
| 1417 |
return history, None, error_msg
|
| 1418 |
|
| 1419 |
def clear_chat_pro():
|
|
|
|
| 1429 |
}
|
| 1430 |
return f"✅ Đã chuyển sang: {persona_names.get(new_persona, new_persona)}"
|
| 1431 |
|
| 1432 |
+
# Build PRO interface
|
| 1433 |
custom_css = """
|
| 1434 |
+
/* Reset font & nền */
|
| 1435 |
.gradio-container {
|
| 1436 |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
|
| 1437 |
background-color: #f8f9fa !important;
|
| 1438 |
}
|
| 1439 |
+
|
| 1440 |
+
/* Ẩn footer mặc định */
|
| 1441 |
footer {display: none !important;}
|
| 1442 |
+
|
| 1443 |
+
/* Thanh Topbar chuyên nghiệp (Gradient Xanh Navy) */
|
| 1444 |
.admin-topbar {
|
| 1445 |
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
|
| 1446 |
padding: 20px 30px;
|
| 1447 |
display: flex;
|
| 1448 |
align-items: center;
|
| 1449 |
+
margin: -20px -20px 20px -20px; /* Tràn viền */
|
| 1450 |
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
| 1451 |
color: white;
|
| 1452 |
}
|
| 1453 |
+
|
| 1454 |
.admin-icon {
|
| 1455 |
font-size: 45px;
|
| 1456 |
margin-right: 20px;
|
|
|
|
| 1458 |
padding: 10px;
|
| 1459 |
border-radius: 12px;
|
| 1460 |
}
|
| 1461 |
+
|
| 1462 |
.admin-brand h1 {
|
| 1463 |
color: #ffffff !important;
|
| 1464 |
font-size: 24px !important;
|
|
|
|
| 1467 |
letter-spacing: 1px;
|
| 1468 |
text-transform: uppercase;
|
| 1469 |
}
|
| 1470 |
+
|
| 1471 |
.admin-brand p {
|
| 1472 |
color: #e0e0e0 !important;
|
| 1473 |
font-size: 14px !important;
|
| 1474 |
margin: 5px 0 0 0 !important;
|
| 1475 |
font-weight: 300 !important;
|
| 1476 |
}
|
| 1477 |
+
|
| 1478 |
+
/* Tinh chỉnh Nút bấm */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1479 |
button.primary {
|
| 1480 |
background-color: #0056b3 !important;
|
| 1481 |
color: white !important;
|
|
|
|
| 1484 |
border: none !important;
|
| 1485 |
transition: all 0.3s ease;
|
| 1486 |
}
|
| 1487 |
+
|
| 1488 |
button.primary:hover {
|
| 1489 |
background-color: #004494 !important;
|
| 1490 |
box-shadow: 0 4px 8px rgba(0,86,179,0.3) !important;
|
| 1491 |
}
|
| 1492 |
+
|
| 1493 |
+
/* Tùy chỉnh Khung Chat */
|
| 1494 |
.main-chatbot {
|
| 1495 |
border: 1px solid #dee2e6 !important;
|
| 1496 |
border-radius: 8px !important;
|
| 1497 |
background-color: #ffffff !important;
|
| 1498 |
box-shadow: 0 2px 15px rgba(0,0,0,0.03) !important;
|
| 1499 |
}
|
| 1500 |
+
|
| 1501 |
+
/* Tiêu đề Box */
|
| 1502 |
.section-title {
|
| 1503 |
color: #1e3c72;
|
| 1504 |
font-weight: 600;
|
|
|
|
| 1508 |
}
|
| 1509 |
"""
|
| 1510 |
|
| 1511 |
+
# Khởi tạo giao diện với Theme base màu xanh thanh lịch
|
| 1512 |
with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", neutral_hue="slate"), css=custom_css) as demo:
|
| 1513 |
|
| 1514 |
+
# 1. TOPBAR CHUYÊN NGHIỆP (Không dùng logo/tên thật)
|
| 1515 |
+
gr.HTML("""
|
| 1516 |
+
<div class="admin-topbar">
|
| 1517 |
+
<div class="admin-icon">🏛️</div>
|
| 1518 |
+
<div class="admin-brand">
|
| 1519 |
+
<h1>HỆ THỐNG TRỢ LÝ ẢO TƯ VẤN HÀNH CHÍNH</h1>
|
| 1520 |
+
<p>Giải đáp thông tin thủ tục - Nhanh chóng, Chính xác, Hỗ trợ 24/7</p>
|
| 1521 |
+
</div>
|
| 1522 |
+
</div>
|
| 1523 |
+
""")
|
| 1524 |
|
|
|
|
| 1525 |
with gr.Tabs():
|
| 1526 |
+
# Chat Tab
|
| 1527 |
with gr.Tab("💬 Trợ lý AI"):
|
| 1528 |
with gr.Row():
|
| 1529 |
with gr.Column(scale=7):
|
|
|
|
| 1540 |
btn_send = gr.Button('GỬI CÂU HỎI', variant='primary')
|
| 1541 |
btn_clear = gr.Button('Làm mới')
|
| 1542 |
|
| 1543 |
+
# --- Ô HƯỚNG DẪN NHỎ THÊM VÀO ĐÂY ---
|
| 1544 |
gr.HTML("""
|
| 1545 |
<div style="
|
| 1546 |
margin-top: 15px;
|
|
|
|
| 1554 |
">
|
| 1555 |
<strong style="color: #1e3c72;">💡 Cách dùng nhanh:</strong>
|
| 1556 |
<ul style="margin: 5px 0 0 15px; padding: 0;">
|
| 1557 |
+
<li>Nhập câu hỏi hoặc dùng 🎤 <b>Micro</b>.</li>
|
| 1558 |
+
<li>Nhấn <b>Gửi</b> để nhận phản hồi từ AI.</li>
|
| 1559 |
+
<li>Hệ thống sẽ tự động 🔊 <b>Phát loa</b> câu trả lời.</li>
|
| 1560 |
</ul>
|
| 1561 |
</div>
|
| 1562 |
""")
|
|
|
|
| 1574 |
inputs=[audio_in, msg_in]
|
| 1575 |
)
|
| 1576 |
|
| 1577 |
+
# (Các sự kiện click giữ nguyên)
|
| 1578 |
btn_send.click(process_chat_pro, [audio_in, msg_in, chat], [chat, audio_out, msg_in])
|
| 1579 |
msg_in.submit(process_chat_pro, [audio_in, msg_in, chat], [chat, audio_out, msg_in])
|
| 1580 |
btn_clear.click(clear_chat_pro, [], [chat, audio_out, msg_in])
|
|
|
|
|
|
|
| 1581 |
with gr.Tab("⚙️ Admin Panel"):
|
| 1582 |
+
gr.Markdown("### 🔐 Xác thực quyền quản trị")
|
| 1583 |
|
| 1584 |
+
with gr.Row():
|
| 1585 |
+
input_pass = gr.Textbox(
|
| 1586 |
+
label="Nhập mật khẩu để mở khóa tính năng Admin",
|
| 1587 |
+
type="password",
|
| 1588 |
+
placeholder="nhap mat khau",
|
| 1589 |
+
scale=4
|
| 1590 |
+
)
|
| 1591 |
+
btn_login = gr.Button("Xác thực 🔑", scale=1)
|
|
|
|
| 1592 |
|
| 1593 |
+
# Group chứa nội dung Admin - Mặc định ẩn (visible=False)
|
| 1594 |
admin_content = gr.Group(visible=False)
|
| 1595 |
|
| 1596 |
with admin_content:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1597 |
gr.Markdown("---")
|
| 1598 |
+
gr.Markdown("### ➕ Thêm thủ tục mới (AI sẽ học ngay)")
|
| 1599 |
with gr.Row():
|
| 1600 |
with gr.Column():
|
| 1601 |
admin_code = gr.Textbox(label="Mã thủ tục")
|
|
|
|
| 1604 |
admin_coquan = gr.Textbox(label="Cơ quan tiếp nhận")
|
| 1605 |
|
| 1606 |
with gr.Column():
|
| 1607 |
+
admin_time = gr.Textbox(label="Thời gian")
|
| 1608 |
+
admin_lephi = gr.Textbox(label="Lệ phí")
|
| 1609 |
+
admin_note = gr.Textbox(label="Lưu ý", lines=2)
|
| 1610 |
+
admin_steps = gr.Textbox(label="Quy trình", lines=3)
|
| 1611 |
|
| 1612 |
btn_add = gr.Button("➕ THÊM VÀO HỆ THỐNG", variant='primary', size='lg')
|
| 1613 |
+
add_output = gr.Textbox(label="Kết quả xử lý")
|
| 1614 |
procedures_list = gr.Markdown(value=get_procedures_list())
|
| 1615 |
|
| 1616 |
+
# --- Logic xử lý ẩn hiện và mật khẩu ---
|
| 1617 |
+
def check_admin(password):
|
| 1618 |
+
if password == ADMIN_PASSWORD:
|
| 1619 |
+
# Nếu đúng: Hiện nội dung admin, ẩn ô nhập mật khẩu và hiện thông báo thành công
|
| 1620 |
+
gr.Info("Đăng nhập thành công! Quyền quản trị đã được mở.")
|
|
|
|
|
|
|
| 1621 |
return gr.update(visible=True), gr.update(visible=False)
|
| 1622 |
+
else:
|
| 1623 |
+
# Nếu sai: Giữ ẩn nội dung và hiện cảnh báo
|
| 1624 |
+
raise gr.Error("Mật khẩu không chính xác. Vui lòng thử lại!")
|
| 1625 |
+
return gr.update(visible=False), gr.update(visible=True)
|
| 1626 |
|
| 1627 |
+
# Sự kiện khi nhấn nút Xác thực
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1628 |
btn_login.click(
|
| 1629 |
+
check_admin,
|
| 1630 |
+
inputs=[input_pass],
|
| 1631 |
+
outputs=[admin_content, input_pass]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1632 |
)
|
| 1633 |
|
| 1634 |
+
# Sự kiện khi nhấn nút THÊM (giữ nguyên logic cũ của bạn)
|
| 1635 |
btn_add.click(
|
| 1636 |
admin_add,
|
| 1637 |
[admin_code, admin_name, admin_docs, admin_coquan, admin_time, admin_lephi, admin_note, admin_steps],
|
| 1638 |
[add_output, procedures_list]
|
| 1639 |
)
|
| 1640 |
|
| 1641 |
+
# Memory Tab (mới)
|
| 1642 |
+
with gr.Tab("🧠 Memory Manager"):
|
| 1643 |
+
gr.Markdown("### 📊 Quản lý bộ nhớ hội thoại")
|
| 1644 |
+
|
| 1645 |
+
memory_info = gr.Textbox(label="Ngữ cảnh hiện tại", interactive=False, lines=3)
|
| 1646 |
+
memory_history = gr.Textbox(label="Lịch sử chat", interactive=False, lines=10)
|
| 1647 |
+
|
| 1648 |
+
with gr.Row():
|
| 1649 |
+
btn_refresh = gr.Button("🔄 Làm mới")
|
| 1650 |
+
btn_clear_mem = gr.Button("🗑️ Xóa memory", variant='stop')
|
| 1651 |
+
|
| 1652 |
+
def refresh_memory():
|
| 1653 |
+
ctx = conversation_memory.get_context_summary()
|
| 1654 |
+
hist = "\n".join([f"{m['role']}: {m['content'][:100]}..."
|
| 1655 |
+
for m in conversation_memory.history[-5:]])
|
| 1656 |
+
return ctx or "Không có ngữ cảnh", hist or "Không có lịch sử"
|
| 1657 |
+
|
| 1658 |
+
btn_refresh.click(refresh_memory, [], [memory_info, memory_history])
|
| 1659 |
+
btn_clear_mem.click(clear_chat_pro, [], [chat, audio_out, msg_in])
|
| 1660 |
+
|
| 1661 |
+
|
| 1662 |
+
|
| 1663 |
+
|
| 1664 |
|
| 1665 |
# ======================================================================
|
| 1666 |
+
# ============================================================
|
| 1667 |
+
# PHẦN 12.7: LAUNCH APP PRO
|
| 1668 |
+
# ============================================================
|
| 1669 |
|
| 1670 |
print('='*70)
|
| 1671 |
+
print('🚀 KHỞI ĐỘNG ỨNG DỤNG AI PRO')
|
| 1672 |
print('='*70)
|
| 1673 |
print()
|
| 1674 |
|
| 1675 |
+
|
| 1676 |
+
|
| 1677 |
print('='*70)
|
| 1678 |
+
print(' Đang tạo public link...')
|
| 1679 |
+
print(' Link sẽ xuất hiện bên dưới sau vài giây...')
|
| 1680 |
print('='*70)
|
| 1681 |
print()
|
| 1682 |
|
| 1683 |
+
# Launch với demo_pro thay vì demo
|
| 1684 |
demo.launch(
|
| 1685 |
share=True,
|
| 1686 |
server_name='0.0.0.0',
|