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"""
{msg['content']}
""", 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"""
{icon} {agent_name}
""", 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"""
{len(user_msgs)}
Queries
""", 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("""
๐ŸŽ“

Academic AI System

3 specialized agents ยท Router ยท Synthesizer

""", 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()