JirasakJo's picture
Update app.py
0a00490 verified
raw
history blame
16.2 kB
import streamlit as st
import json
import os
from datetime import datetime, timedelta
import subprocess
from huggingface_hub import HfApi
from pathlib import Path
from calendar_rag import (
create_default_config,
AcademicCalendarRAG,
PipelineConfig
)
# Custom CSS for enhanced styling
def load_custom_css():
st.markdown("""
<style>
/* General body styling */
body {
font-family: "Arial", sans-serif !important;
color: #000000 !important;
background-color: white !important;
line-height: 1.7 !important;
}
/* Main container styling */
.main {
padding: 2rem;
color: #000000;
background-color: white;
}
/* Headers styling */
h1 {
color: #000000;
font-size: 2.8rem !important;
font-weight: 700 !important;
margin-bottom: 1.5rem !important;
text-align: center;
padding: 1rem 0;
border-bottom: 3px solid #1E3A8A;
}
h3, h4 {
color: #000000;
font-weight: 600 !important;
font-size: 1.6rem !important;
margin-top: 1.5rem !important;
}
/* Chat message styling */
.chat-message {
padding: 1.5rem;
border-radius: 10px;
margin: 1rem 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
font-size: 1.1rem !important;
line-height: 1.6 !important;
font-family: "Arial", sans-serif !important;
color: #000000 !important;
}
.user-message {
background-color: #F3F4F6 !important;
}
.assistant-message {
background-color: #EFF6FF !important;
}
/* Status indicators */
.status-indicator {
padding: 0.5rem 1rem;
border-radius: 6px;
font-weight: 500;
font-size: 1.2rem;
color: #000000;
}
.status-online {
background-color: #DEF7EC;
color: #03543F;
}
.status-offline {
background-color: #FDE8E8;
color: rgb(255, 255, 255);
}
</style>
""", unsafe_allow_html=True)
def initialize_pipeline():
"""Initialize RAG pipeline with configurations"""
try:
openai_api_key = os.getenv('OPENAI_API_KEY') or st.secrets['OPENAI_API_KEY']
config = create_default_config(openai_api_key)
config.localization.enable_thai_normalization = True
config.retriever.top_k = 5
config.model.temperature = 0.3
pipeline = AcademicCalendarRAG(config)
with open("calendar.json", "r", encoding="utf-8") as f:
calendar_data = json.load(f)
pipeline.load_data(calendar_data)
return pipeline
except Exception as e:
st.error(f"Error initializing pipeline: {str(e)}")
return None
def load_qa_history():
"""Load QA history from local JSON file"""
try:
history_file = Path("qa_history.json")
if history_file.exists():
with open(history_file, "r", encoding="utf-8") as f:
return json.load(f)
return []
except Exception as e:
st.error(f"Error loading QA history: {str(e)}")
return []
def save_qa_history(history_entry):
"""Save QA history entry to local JSON file and push to GitHub"""
try:
history_file = Path("qa_history.json")
# Initialize or load existing history
if history_file.exists():
with open(history_file, "r", encoding="utf-8") as f:
try:
history_data = json.load(f)
except json.JSONDecodeError:
st.error("Error parsing existing JSON file")
history_data = []
else:
history_data = []
# Append new entry
history_data.append(history_entry)
# Save updated history locally
with open("qa_history.json", "w", encoding="utf-8") as f:
json.dump(history_data, f, ensure_ascii=False, indent=2)
# st.info("Saved locally, attempting GitHub upload...")
# Push to GitHub
github_token = os.getenv('GITHUB_TOKEN') or st.secrets['GITHUB_TOKEN']
if not github_token:
st.error("GitHub token not found!")
return
from github import Github
g = Github(github_token)
# Replace with your repository name
repo = g.get_repo("jirasaksaimekJijo/swu-chat-bot-project")
try:
# Try to get the file first
contents = repo.get_contents("qa_history.json")
response = repo.update_file(
path="qa_history.json",
message="Update QA history",
content=json.dumps(history_data, ensure_ascii=False, indent=2),
sha=contents.sha
)
# st.success(f"Successfully updated file on GitHub: {response.commit.html_url}")
except Exception as e:
# st.info(f"File doesn't exist yet, creating new one... Error was: {str(e)}")
# File doesn't exist, create it
response = repo.create_file(
path="qa_history.json",
message="Create QA history",
content=json.dumps(history_data, ensure_ascii=False, indent=2)
)
# st.success(f"Successfully created file on GitHub: {response['commit'].html_url}")
except Exception as e:
# st.error(f"Error saving QA history: {str(e)}")
import traceback
# st.error(f"Full error: {traceback.format_exc()}")
def add_to_qa_history(query: str, answer: str):
"""Add new QA pair to history"""
history_entry = {
"timestamp": (datetime.now() + timedelta(hours=7)).isoformat(),
"query": query,
"answer": answer
}
save_qa_history(history_entry)
return history_entry
def add_to_history(role: str, message: str):
"""Add message to chat history and save if it's a complete QA pair"""
st.session_state.chat_history.append((role, message))
# If this is an assistant response, save the QA pair
if role == "assistant" and len(st.session_state.chat_history) >= 2:
# Get the corresponding user query (previous message)
user_query = st.session_state.chat_history[-2][1]
add_to_qa_history(user_query, message)
def display_chat_history():
"""Display chat history with enhanced styling"""
for i, (role, message) in enumerate(st.session_state.chat_history):
if role == "user":
st.markdown(f"""
<div class="chat-message user-message">
<strong>🧑 คำถาม:</strong><br>
{message}
</div>
""", unsafe_allow_html=True)
else:
st.markdown(f"""
<div class="chat-message assistant-message">
<strong>🤖 คำตอบ:</strong><br>
{message}
</div>
""", unsafe_allow_html=True)
def main():
# Page config
st.set_page_config(
page_title="Academic Calendar Assistant",
page_icon="📅",
layout="wide",
initial_sidebar_state="collapsed"
)
# Load custom CSS
load_custom_css()
# Initialize session states
if 'pipeline' not in st.session_state:
st.session_state.pipeline = None
if 'chat_history' not in st.session_state:
st.session_state.chat_history = []
if 'query' not in st.session_state:
st.session_state.query = ''
# Load QA history at startup
if 'qa_history_loaded' not in st.session_state:
st.session_state.qa_history_loaded = True
load_qa_history()
# Initialize pipeline
if st.session_state.pipeline is None:
with st.spinner("กำลังเริ่มต้นระบบ..."):
st.session_state.pipeline = initialize_pipeline()
# Header
st.markdown("""
<div style="text-align: center; padding: 2rem 0;">
<h1>🎓 ระบบค้นหาข้อมูลปฏิทินการศึกษา</h1>
<p style="font-size: 1.2rem; color: #666;">บัณฑิตวิทยาลัย มหาวิทยาลัยศรีนครินทรวิโรฒ</p>
</div>
""", unsafe_allow_html=True)
chat_col, info_col = st.columns([7, 3])
def clear_text():
st.session_state.query_input = ''
def handle_submit():
query = st.session_state.query_input
if query:
if st.session_state.pipeline is None:
st.error("❌ ไม่สามารถเชื่อมต่อกับระบบได้ กรุณาลองใหม่อีกครั้ง")
return
try:
with st.spinner("🔍 กำลังค้นหาคำตอบ..."):
result = st.session_state.pipeline.process_query(query)
# Update session state
st.session_state.chat_history.append(("user", query))
st.session_state.chat_history.append(("assistant", {
"answer": result["answer"],
"documents": result["documents"],
"query_info": result["query_info"]
}))
# Clear input
clear_text()
except Exception as e:
st.error(f"❌ เกิดข้อผิดพลาด: {str(e)}")
else:
st.warning("⚠️ กรุณาระบุคำถาม")
with chat_col:
# Input section at the top
with st.container():
st.markdown("""
<label for="query_input" style="font-size: 1.2rem; font-weight: 600; margin-bottom: 1rem; display: block;">
<span style="color: #ffffff; border-left: 4px solid #ffffff; padding-left: 0.8rem;">
โปรดระบุคำถามเกี่ยวกับปฏิทินการศึกษา:
</span>
</label>
""", unsafe_allow_html=True)
# Text input
query = st.text_input(
"",
key="query_input",
placeholder="เช่น: วันสุดท้ายของการสอบปากเปล่าในภาคเรียนที่ 1/2567 คือวันที่เท่าไร?",
)
# Button layout
col1, col2, col3 = st.columns([1, 1, 4])
with col1:
if st.button("📤 ส่งคำถาม", type="primary", use_container_width=True):
handle_submit()
with col2:
if st.button("🗑️ ล้างประวัติ", type="secondary", use_container_width=True):
st.session_state.chat_history = []
st.rerun()
# Chat history display
for i, (role, content) in enumerate(st.session_state.chat_history):
if role == "user":
st.markdown(f"""
<div class="chat-message user-message">
<strong>🧑 คำถาม:</strong><br>
{content}
</div>
""", unsafe_allow_html=True)
else:
st.markdown(f"""
<div class="chat-message assistant-message">
<strong>🤖 คำตอบ:</strong><br>
{content['answer']}
</div>
""", unsafe_allow_html=True)
# Add expanders right after the answer
with st.expander("📚 แสดงข้อมูลอ้างอิง", expanded=False):
for i, doc in enumerate(content['documents'], 1):
st.markdown(f"""
<div style="padding: 1rem; background-color: #F9FAFB; border-radius: 8px; margin: 0.5rem 0;">
<strong>เอกสารที่ {i}:</strong><br>
{doc.content}
</div>
""", unsafe_allow_html=True)
with st.expander("🔍 รายละเอียดการวิเคราะห์คำถาม", expanded=False):
st.json(content['query_info'])
# Info column content remains the same
with info_col:
st.markdown("""
<div style="background-color: #F9FAFB; padding: 1.5rem; border-radius: 12px; margin-bottom: 2rem;">
<h3 style="color: #1E3A8A;">ℹ️ เกี่ยวกับระบบ</h3>
<p style="color: #000000;">
ระบบนี้ใช้เทคโนโลยี <strong>RAG (Retrieval-Augmented Generation)</strong>
ในการค้นหาและตอบคำถามเกี่ยวกับปฏิทินการศึกษา
</p>
<h4 style="color: #1E3A8A; margin-top: 1rem;">สามารถสอบถามข้อมูลเกี่ยวกับ:</h4>
<ul style="list-style-type: none; padding-left: 0;">
<li style="color: #000000; margin-bottom: 0.5rem;">📅 กำหนดการต่างๆ ในปฏิทินการศึกษา</li>
<li style="color: #000000; margin-bottom: 0.5rem;">🎯 วันสำคัญและกิจกรรม</li>
<li style="color: #000000; margin-bottom: 0.5rem;">📝 การลงทะเบียนเรียน</li>
<li style="color: #000000; margin-bottom: 0.5rem;">📚 กำหนดการสอบ</li>
<li style="color: #000000; margin-bottom: 0.5rem;">🏖️ วันหยุดการศึกษา</li>
</ul>
</div>
""", unsafe_allow_html=True)
st.markdown("""
<div style="background-color: #f9fafb; padding: 1.5rem; border-radius: 12px;">
<h3 style="color: #1E3A8A;">🔄 สถานะระบบ</h3>
<div style="margin-top: 1rem;">
<p><strong style="color: #000000;">⏰ เวลาปัจจุบัน:</strong><br>
<span style="color: #000000;">{}</span></p>
<p><strong style="color: #000000;">📡 สถานะระบบ:</strong><br>
<span class="status-indicator {}">
{} {}
</span></p>
</div>
</div>
""".format(
(datetime.now() + timedelta(hours=7)).strftime('%Y-%m-%d %H:%M:%S'),
"status-online" if st.session_state.pipeline else "status-offline",
"🟢" if st.session_state.pipeline else "🔴",
"พร้อมใช้งาน" if st.session_state.pipeline else "ไม่พร้อมใช้งาน"
), unsafe_allow_html=True)
if __name__ == "__main__":
main()