File size: 5,453 Bytes
7cf74a5
7791c9a
95112bf
 
40695ca
 
a3e0cb3
 
 
40695ca
 
 
7791c9a
a3e0cb3
95112bf
7791c9a
 
 
 
 
 
95112bf
5d6e27a
95112bf
7791c9a
95112bf
 
 
 
7791c9a
95112bf
 
 
 
 
 
7791c9a
 
95112bf
 
 
 
 
 
 
 
7791c9a
 
 
a3e0cb3
95112bf
7791c9a
95112bf
 
7791c9a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a3e0cb3
 
7791c9a
 
 
a3e0cb3
 
7791c9a
 
 
 
a3e0cb3
 
 
 
7791c9a
 
a3e0cb3
7791c9a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a3e0cb3
 
7791c9a
95112bf
7791c9a
 
 
95112bf
 
7791c9a
a3e0cb3
 
95112bf
a3e0cb3
 
95112bf
5d6e27a
7791c9a
 
95112bf
a3e0cb3
7791c9a
95112bf
 
 
7791c9a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import streamlit as st
from chat_langraph import system, workflow, HumanMessage, AIMessage, get_all_chat_ids, ToolMessage
import uuid
import os
import base64

st.set_page_config(layout="wide")
st.title("College Chatbot")

TEMP_DIR = "/tmp"
os.makedirs(TEMP_DIR, exist_ok=True)


# --- Helpers ---

def set_title(messages):
    if messages:
        title = "New Chat"
        st.session_state.chat_dict[st.session_state.current_chat_id] = title


def set_config():
    return {"configurable": {"thread_id": st.session_state.current_chat_id}}


def load_session_state():
    if "chats" not in st.session_state:
        st.session_state.chats = get_all_chat_ids()
    if "current_chat_id" not in st.session_state:
        if len(st.session_state.chats) > 0:
            st.session_state.current_chat_id = st.session_state.chats[-1]
        else:
            new_id = str(uuid.uuid4())
            st.session_state.chats.append(new_id)
            st.session_state.current_chat_id = new_id
    if "chat_dict" not in st.session_state:
        st.session_state.chat_dict = {}


def render_sidebar():
    with st.sidebar:
        st.title("Chats")
        if st.button("➕ New Chat"):
            new_id = str(uuid.uuid4())
            st.session_state.chats.append(new_id)
            st.session_state.current_chat_id = new_id
            config = {"configurable": {"thread_id": new_id}}
            workflow.update_state(config, {"messages": [system]})
            st.session_state.chat_dict[new_id] = "New Chat"

        for chat_id in st.session_state.chats:
            if st.button(st.session_state.chat_dict.get(chat_id, "New Chat"), key=chat_id):
                st.session_state.current_chat_id = chat_id


def create_download_link(file_path: str, label: str = None) -> str:
    """Generate HTML download link for a file."""
    if not os.path.exists(file_path):
        return ""
    try:
        with open(file_path, "rb") as f:
            data = f.read()
        b64 = base64.b64encode(data).decode()
        label = label or f"📥 Download {os.path.basename(file_path)}"
        href = f'<a href="data:file/octet-stream;base64,{b64}" download="{os.path.basename(file_path)}">{label}</a>'
        return href
    except Exception as e:
        return f"Error creating download link: {e}"


def show_file(file_path: str):
    """Show file inline + download link."""
    if not os.path.exists(file_path):
        return
    ext = os.path.splitext(file_path)[1].lower()
    if ext in [".png", ".jpg", ".jpeg"]:
        st.image(file_path, caption=os.path.basename(file_path))
    elif ext in [".txt", ".py", ".java", ".cpp", ".md"]:
        with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
            st.code(f.read(), language=ext.lstrip("."))
    st.markdown(create_download_link(file_path), unsafe_allow_html=True)


def render_tool_message(tool_message: ToolMessage):
    """Render tool execution based on tool name instead of message content."""
    file_related_keywords = ["read", "write", "file", "save", "export", "create"]
    with st.chat_message("assistant"):
        tool_name = getattr(tool_message, "name", "").lower()
        st.info(f"🧰 Tool used: {tool_name or 'Unknown Tool'}")

        # Check if this is a file-related tool
        if any(k in tool_name for k in file_related_keywords):
            # Find all files in TEMP_DIR (freshly modified ones)
            created_files = sorted(
                [os.path.join(TEMP_DIR, f) for f in os.listdir(TEMP_DIR)],
                key=lambda x: os.path.getmtime(x),
                reverse=True,
            )
            if created_files:
                st.success("📄 File(s) created by tool:")
                for file_path in created_files[:3]:  # show up to 3 recent
                    show_file(file_path)
            else:
                st.warning("No new file detected in /tmp.")


def loadchats():
    if "current_chat_id" not in st.session_state:
        return []
    config = {"configurable": {"thread_id": st.session_state.current_chat_id}}
    state = workflow.get_state(config)
    messages = state.values.get("messages", [])
    for message in messages:
        if isinstance(message, HumanMessage):
            with st.chat_message("human"):
                st.write(message.content)
        elif isinstance(message, AIMessage):
            with st.chat_message("assistant"):
                st.write(message.content)
        elif isinstance(message, ToolMessage):
            render_tool_message(message)
    return messages


# --- Main Chat Flow ---

load_session_state()
render_sidebar()

if "current_chat_id" in st.session_state:
    loadchats()
    if user_input := st.chat_input("Your message:"):
        with st.chat_message("human"):
            st.write(user_input)

        with st.chat_message("assistant"):
            response_placeholder = st.empty()
            full_response = ""

            for message, metadata in workflow.stream(
                {"messages": [system, HumanMessage(user_input)]},
                config={"configurable": {"thread_id": st.session_state.current_chat_id}},
                stream_mode="messages",
            ):
                if isinstance(message, AIMessage):
                    full_response += message.content or ""
                elif isinstance(message, ToolMessage):
                    render_tool_message(message)

            response_placeholder.markdown(full_response)