Heng2004's picture
Update ui.py
a60d307 verified
raw
history blame
9.44 kB
# ui.py – Gradio UI (Student + Teacher tabs)
import gradio as gr
from rag import answer_question
from data.manual_answers import (
add_manual_qa,
list_manual_qa,
get_manual_qa,
update_manual_qa,
delete_manual_qa,
)
# ========= Student side =========
def chat_fn(message: str, history: list):
# history is ignored for now
return answer_question(message)
# ========= Teacher helpers =========
def _refresh_manual_data():
"""
Helper: get table data + dropdown choices.
"""
rows = list_manual_qa()
table = [[r["norm_q"], r["q"], r["a"]] for r in rows]
keys = [r["norm_q"] for r in rows]
return table, keys
def save_teacher_qa(question: str, answer: str):
"""
Add a new Q&A (or overwrite existing same question).
Returns: status_text, table_data, dropdown_choices, editing_key
"""
if not question.strip() or not answer.strip():
table, keys = _refresh_manual_data()
return (
"❗ ກະລຸນາພິມທັງຄໍາຖາມ ແລະຄໍາຕອບ.",
table,
keys,
"",
)
try:
norm_key = add_manual_qa(question, answer)
table, keys = _refresh_manual_data()
return (
"✅ ບັນທຶກສໍາເລັດ! ຈາກນີ້ chatbot ຈະໃຊ້ຄໍາຕອບນີ້ທັນທີ.\n"
f"🔑 ຄີທີ່ໃຊ້ຄົ້ນຫາ (normalized): `{norm_key}`",
table,
keys,
norm_key,
)
except Exception as e:
table, keys = _refresh_manual_data()
return (f"⚠️ ບັນທຶກບໍ່ສໍາເລັດ: {e}", table, keys, "")
def load_for_edit(selected_key: str):
"""
Load one row into the question/answer textboxes.
Returns: question, answer, editing_key
"""
if not selected_key:
return "", "", ""
row = get_manual_qa(selected_key)
if not row:
return "", "", ""
return row["q"], row["a"], row["norm_q"]
def update_teacher_qa(current_key: str, question: str, answer: str):
"""
Update existing Q&A (identified by current_key).
Returns: status_text, table_data, dropdown_choices, new_editing_key
"""
if not current_key:
table, keys = _refresh_manual_data()
return (
"❗ ກະລຸນາເລືອກ Q&A ຈາກຊ່ອງ Key ຫຼືຕາຕະລາງກ່ອນ.",
table,
keys,
current_key,
)
if not question.strip() or not answer.strip():
table, keys = _refresh_manual_data()
return (
"❗ ຄໍາຖາມ ແລະຄໍາຕອບຕ້ອງບໍ່ວ່າງ.",
table,
keys,
current_key,
)
try:
new_key = update_manual_qa(current_key, question, answer)
table, keys = _refresh_manual_data()
return (
"✅ ແກ້ໄຂ Q&A ສໍາເລັດ.",
table,
keys,
new_key,
)
except Exception as e:
table, keys = _refresh_manual_data()
return (
f"⚠️ ແກ້ໄຂບໍ່ສໍາເລັດ: {e}",
table,
keys,
current_key,
)
def delete_teacher_qa(current_key: str):
"""
Delete selected Q&A.
Returns: status_text, table_data, dropdown_choices, new_editing_key, q_text, a_text
"""
if not current_key:
table, keys = _refresh_manual_data()
return (
"❗ ກະລຸນາເລືອກ Q&A ທີ່ຈະລົບກ່ອນ.",
table,
keys,
"",
"",
"",
)
try:
delete_manual_qa(current_key)
table, keys = _refresh_manual_data()
return (
"🗑️ ລົບ Q&A ສໍາເລັດ.",
table,
keys,
"",
"",
"",
)
except Exception as e:
table, keys = _refresh_manual_data()
return (
f"⚠️ ລົບບໍ່ສໍາເລັດ: {e}",
table,
keys,
current_key,
"",
"",
)
# ========= Build Gradio app =========
def create_app():
with gr.Blocks() as demo:
gr.Markdown(
"""
# 📚 Laos History Chatbot (M1)
- 👩‍🎓 **Student Chat**: ນັກຮຽນຖາມຄໍາຖາມປະຫວັດສາດພາສາລາວ
- 👩‍🏫 **Teacher Panel**: ຄູເພີ່ມ / ແກ້ໄຂ / ລົບ Q&A ທີ່ຖືກຕ້ອງເອງ
"""
)
# ---------- Tab 1: Student Chat ----------
with gr.Tab("👩‍🎓 Student Chat"):
gr.ChatInterface(
fn=chat_fn,
title="Laos History Chatbot (Lao language)",
description="ຖາມຂໍ້ມູນກ່ຽວກັບປະຫວັດສາດຂອງປະເທດລາວ (ສໍາລັບນັກຮຽນ ມ.1)",
examples=[
"ປະຫວັດສາດແມ່ນຫຍັງ?",
"ປະຫວັດສາດມີຄວາມສໍາຄັນຈັ່ງໃດ໋?",
"ຫຼັກຖານປະຫວັດສາດມີຫຍັງແດ່?",
"ປະຕິທິນຈັນທະລະຄະຕິແມ່ນຫຍັງ?",
],
)
# ---------- Tab 2: Teacher Panel ----------
with gr.Tab("👩‍🏫 Teacher Panel"):
gr.Markdown(
"""
### ✍️ ເພີ່ມ / ແກ້ໄຂ / ລົບ Q&A ສໍາລັບຄູ
1. ພິມ **ຄໍາຖາມ** ແລະ **ຄໍາຕອບ** ແລ້ວກົດ **💾 Save Q&A** ເພື່ອເພີ່ມໃໝ່
2. ຫາກຈະແກ້ໄຂ: ເລືອກ Key, ກົດ **📥 Load**, ແກ້ໄຂ ແລ້ວກົດ **✏️ Update**
3. ຖ້າຕ້ອງການລົບ: ເລືອກ Key ແລ້ວກົດ **🗑️ Delete**
"""
)
# Input boxes
q_box = gr.Textbox(
label="ຄໍາຖາມ (ພາສາລາວ)",
lines=2,
placeholder="ຕົວຢ່າງ: ປະຫວັດສາດມີຄວາມສໍາຄັນຈັ່ງໃດ໋?",
)
a_box = gr.Textbox(
label="ຄໍາຕອບ (ພາສາລາວ)",
lines=4,
placeholder="ຕົວຢ່າງ: ປະຫວັດສາດມີຄວາມສໍາຄັນເພາະ...",
)
editing_key = gr.State("") # current selected key for edit/delete
with gr.Row():
save_btn = gr.Button("💾 Save Q&A (Add / Overwrite)")
update_btn = gr.Button("✏️ Update current")
delete_btn = gr.Button("🗑️ Delete current")
status = gr.Markdown()
gr.Markdown("### 📋 Manual Q&A List")
manual_df = gr.Dataframe(
headers=[
"Key (normalized)",
"Question (Lao)",
"Answer (Lao)",
],
interactive=False,
wrap=True,
row_count=(0, "dynamic"),
label="ລາຍການ Q&A ທີ່ chatbot ໃຊ້ຢູ່",
)
key_dropdown = gr.Dropdown(
label="ເລືອກ Q&A ເພື່ອແກ້ໄຂ / ລົບ (Key)",
choices=[],
interactive=True,
)
load_btn = gr.Button("📥 Load selected for edit")
# --- Wiring events ---
# Load selected row into text boxes
load_btn.click(
fn=load_for_edit,
inputs=key_dropdown,
outputs=[q_box, a_box, editing_key],
)
# Save new Q&A (or overwrite) and refresh table
save_btn.click(
fn=save_teacher_qa,
inputs=[q_box, a_box],
outputs=[status, manual_df, key_dropdown, editing_key],
)
# Update existing Q&A
update_btn.click(
fn=update_teacher_qa,
inputs=[editing_key, q_box, a_box],
outputs=[status, manual_df, key_dropdown, editing_key],
)
# Delete existing Q&A
delete_btn.click(
fn=delete_teacher_qa,
inputs=[editing_key],
outputs=[status, manual_df, key_dropdown, editing_key, q_box, a_box],
)
gr.Markdown(
"""
> ℹ️ ໝາຍເຫດ: Q&A ທັງໝົດຈະຖືກບັນທຶກໃນ `data/manual_qa.jsonl`.
> ເມື່ອເຈົ້າກົດ Save / Update / Delete chatbot ຈະອັບເດດທັນທີ (ບໍ່ຕ້ອງ restart Space).
"""
)
# On app load, fill table + dropdown
demo.load(
fn=_refresh_manual_data,
inputs=None,
outputs=[manual_df, key_dropdown],
)
return demo