import os
import streamlit as st
import uuid
import time
# from teacher import teacher_agent
from synthesize import workflow
# ================================================================
# PAGE CONFIG
# ================================================================
st.set_page_config(
page_title="Academic AI System",
page_icon="๐",
layout="wide",
initial_sidebar_state="expanded",
)
# ================================================================
# CUSTOM CSS
# ================================================================
st.markdown("""
""", unsafe_allow_html=True)
# ================================================================
# SESSION STATE INIT
# ================================================================
if "sessions" not in st.session_state:
st.session_state.sessions = {} # {session_id: {"name": str, "messages": []}}
if "active_session" not in st.session_state:
# create default session
sid = str(uuid.uuid4())[:8]
st.session_state.sessions[sid] = {"name": "Session 1", "messages": []}
st.session_state.active_session = sid
if "is_thinking" not in st.session_state:
st.session_state.is_thinking = False
if "total_queries" not in st.session_state:
st.session_state.total_queries = 0
# ================================================================
# HELPERS
# ================================================================
def get_active_messages():
return st.session_state.sessions[st.session_state.active_session]["messages"]
def add_message(role: str, content: str, agent: str = None):
get_active_messages().append({
"role": role,
"content": content,
"agent": agent,
"time": time.strftime("%H:%M"),
})
def new_session():
sid = str(uuid.uuid4())[:8]
name = f"Session {len(st.session_state.sessions) + 1}"
st.session_state.sessions[sid] = {"name": name, "messages": []}
st.session_state.active_session = sid
def clear_session():
st.session_state.sessions[st.session_state.active_session]["messages"] = []
def render_message(msg: dict):
if msg["role"] == "user":
st.markdown(f"""
""", unsafe_allow_html=True)
else:
agent = msg.get("agent", "system")
badge_class = f"badge-{agent}" if agent in ["teacher","teacher_assistant","planner","router"] else "badge-router"
agent_label = {
"teacher": "๐งโ๐ซ Teacher",
"teacher_assistant": "๐ค Assistant",
"planner": "๐ Planner",
"router": "๐งญ Router",
}.get(agent, "๐ค Agent")
st.markdown(f"""
๐
{agent_label}
{msg['content']}
{msg.get('time','')}
""", unsafe_allow_html=True)
def query_workflow(user_input: str) -> str:
# โ
build history from current session messages
history = get_active_messages()
# format history as conversation context
history_text = ""
for msg in history[:-1]: # exclude the last user message (already in query)
role = "User" if msg["role"] == "user" else "Assistant"
history_text += f"{role}: {msg['content']}\n\n"
# โ
inject history into query
full_query = user_input
if history_text:
full_query = f"""Previous conversation:
{history_text}
Current question: {user_input}"""
inputs = {
"query": full_query, # โ
query + history
"classifications": [],
"results": [],
"final_answer": "",
}
thread_id = f"session_{st.session_state.active_session}"
config = {"configurable": {"thread_id": thread_id}}
final_text = ""
try:
for chunk in workflow.stream(inputs, config=config, stream_mode="values"):
if "final_answer" in chunk and chunk["final_answer"]:
final_text = chunk["final_answer"]
except Exception as e:
return f"โ ๏ธ Error: {str(e)}"
return final_text or "I could not generate a response. Please try again."
# def query_workflow(user_input: str) -> str:
# # โ
build history
# history = get_active_messages()
# history_text = ""
# for msg in history[:-1]:
# role = "User" if msg["role"] == "user" else "Assistant"
# history_text += f"{role}: {msg['content']}\n\n"
# full_query = user_input
# if history_text:
# full_query = f"""Previous conversation:
# {history_text}
# Current question: {user_input}"""
# # โ
ุงุณุชุฎุฏู
teacher_agent ู
ุจุงุดุฑุฉ โ ู
ุด workflow
# thread_id = f"session_{st.session_state.active_session}"
# config = {"configurable": {"thread_id": thread_id}}
# final_text = ""
# try:
# result = teacher_agent.invoke(
# {"messages": [("user", full_query)]},
# config=config
# )
# messages = result.get("messages", [])
# for msg in reversed(messages):
# if hasattr(msg, "content") and msg.content:
# if not hasattr(msg, "tool_calls") or not msg.tool_calls:
# final_text = msg.content
# break
# except Exception as e:
# return f"โ ๏ธ Error: {str(e)}"
# return final_text or "I could not generate a response. Please try again."
# ================================================================
# SIDEBAR
# ================================================================
with st.sidebar:
st.markdown("""
๐ Academic AI
Multi-Agent System
""", unsafe_allow_html=True)
st.markdown("---")
# โโ New session button โโ
if st.button("๏ผ New Session", use_container_width=True, type="primary"):
new_session()
st.rerun()
st.markdown("SESSIONS
", unsafe_allow_html=True)
# โโ Session list โโ
for sid, sdata in st.session_state.sessions.items():
is_active = sid == st.session_state.active_session
msg_count = len(sdata["messages"])
label = f"{sdata['name']} ยท {msg_count // 2} msg{'s' if msg_count != 2 else ''}"
cls = "session-item session-active" if is_active else "session-item"
st.markdown(f'{label}
', unsafe_allow_html=True)
if st.button(sdata["name"], key=f"btn_{sid}", use_container_width=True,
help="Switch to this session"):
st.session_state.active_session = sid
st.rerun()
st.markdown("---")
# โโ Agents status โโ
st.markdown("AGENTS
", unsafe_allow_html=True)
for agent_name, color, icon in [
("Teacher", "#4f8ef7", "๐งโ๐ซ"),
("Teacher Assistant", "#a78bfa", "๐ค"),
("Planner", "#34d399", "๐"),
("Router", "#fbbf24", "๐งญ"),
]:
st.markdown(f"""
""", unsafe_allow_html=True)
st.markdown("---")
# โโ Stats โโ
msgs = get_active_messages()
user_msgs = [m for m in msgs if m["role"] == "user"]
c1, c2 = st.columns(2)
with c1:
st.markdown(f"""
""", unsafe_allow_html=True)
with c2:
st.markdown(f"""
{len(st.session_state.sessions)}
Sessions
""", unsafe_allow_html=True)
st.markdown("---")
if st.button("๐๏ธ Clear Chat", use_container_width=True):
clear_session()
st.rerun()
# ================================================================
# MAIN AREA
# ================================================================
st.markdown("""
""", unsafe_allow_html=True)
# โโ Chat history โโ
messages = get_active_messages()
if not messages:
st.markdown("""
๐
Ask anything academic
Theory ยท Technical ยท Planning โ the right agent will handle it
๐ก Explain transformers in deep learning
๐ ๏ธ Fix my PyTorch CUDA error
๐ Create a 3-month ML roadmap
""", unsafe_allow_html=True)
else:
for msg in messages:
render_message(msg)
# โโ Thinking indicator โโ
thinking_placeholder = st.empty()
# โโ Chat input โโ
user_input = st.chat_input("Ask your question...")
if user_input:
# Add user message
add_message("user", user_input)
st.session_state.total_queries += 1
# Show thinking
with thinking_placeholder:
st.markdown("""
๐งญ Routing & thinking
""", unsafe_allow_html=True)
# Get full response
response = query_workflow(user_input)
# Clear thinking
thinking_placeholder.empty()
# โ
stream character by character in the UI
stream_placeholder = st.empty()
displayed = ""
for char in response:
displayed += char
stream_placeholder.markdown(f"""
๐
๐งโ๐ซ Teacher
{displayed}โ
""", unsafe_allow_html=True)
time.sleep(0.008) # โก adjust speed โ lower = faster
# Final render without cursor
stream_placeholder.markdown(f"""
๐
๐งโ๐ซ Teacher
{displayed}
""", unsafe_allow_html=True)
# Save to history
add_message("assistant", response, agent="teacher")
st.rerun()