absiitr commited on
Commit
ec6ac20
Β·
verified Β·
1 Parent(s): d2e1b02

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +211 -34
src/streamlit_app.py CHANGED
@@ -1,40 +1,217 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
 
 
 
 
 
 
12
 
13
- In the meantime, below is an example of what you can do with just a few lines of code:
 
 
 
 
 
14
  """
 
15
 
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
1
  import streamlit as st
2
+ import requests
3
+ import os
4
 
5
+ # Backend URL (change if deployed)
6
+ BACKEND_URL = "http://localhost:8000"
7
+
8
+ st.set_page_config(page_title="PDF Assistant", page_icon="πŸ“˜", layout="wide")
9
+
10
+ # ---------------- CSS (Dark Theme) ----------------
11
+ # FIX: Added CSS for the footer
12
+ st.markdown("""
13
+ <style>
14
+ /* Streamlit standard setup for dark theme adherence */
15
+ :root {
16
+ --primary-color: #1e3a8a; /* Blue for highlights */
17
+ --background-color: #0e1117;
18
+ --secondary-background-color: #1a1d29;
19
+ --text-color: #f0f2f6;
20
+ }
21
+
22
+ /* Custom Chat Bubbles */
23
+ .chat-user {
24
+ background: #2d3748; /* Dark gray */
25
+ padding: 12px;
26
+ border-radius: 10px 10px 2px 10px; /* Rounded corners for chat bubble */
27
+ margin: 6px 0 6px auto;
28
+ max-width: 85%;
29
+ text-align: right;
30
+ color: var(--text-color);
31
+ }
32
+ .chat-bot {
33
+ background: var(--primary-color); /* Primary blue */
34
+ padding: 12px;
35
+ border-radius: 10px 10px 10px 2px;
36
+ margin: 6px auto 6px 0;
37
+ max-width: 85%;
38
+ text-align: left;
39
+ color: #ffffff; /* White text for contrast */
40
+ }
41
+
42
+ /* Sources section styling */
43
+ .sources {
44
+ font-size: 0.8em;
45
+ opacity: 0.7;
46
+ margin-top: 10px;
47
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
48
+ padding-top: 5px;
49
+ }
50
+
51
+ /* Footer styling */
52
+ .footer {
53
+ position: fixed;
54
+ left: 0;
55
+ bottom: 0;
56
+ width: 100%;
57
+ background-color: var(--secondary-background-color);
58
+ color: var(--text-color);
59
+ text-align: center;
60
+ padding: 10px;
61
+ font-size: 0.85em;
62
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
63
+ }
64
+ .footer a {
65
+ color: var(--primary-color);
66
+ text-decoration: none;
67
+ font-weight: bold;
68
+ }
69
+ .footer a:hover {
70
+ text-decoration: underline;
71
+ }
72
+ </style>
73
+ """, unsafe_allow_html=True)
74
+
75
+ # ---------------- SESSION STATE ----------------
76
+ if "chat" not in st.session_state:
77
+ st.session_state.chat = []
78
+
79
+ if "uploaded_file_name" not in st.session_state:
80
+ st.session_state.uploaded_file_name = None
81
+
82
+ # Add a key to the file uploader to allow it to be reset.
83
+ if "uploader_key" not in st.session_state:
84
+ st.session_state.uploader_key = 0
85
+
86
+ # FIX 1: Change application name
87
+ st.title("πŸ“˜ PDF Assistant")
88
+
89
+
90
+ # ---------------- FUNCTIONS ----------------
91
+ def clear_chat_history():
92
+ """Clears the chat history in the session state."""
93
+ st.session_state.chat = []
94
+
95
+
96
+ def clear_memory():
97
+ """Calls the backend endpoint to clear loaded PDF data and resets UI state."""
98
+ res = requests.post(f"{BACKEND_URL}/clear")
99
+ if res.status_code == 200:
100
+ st.session_state.uploaded_file_name = None
101
+ # Increment the key of the file uploader to clear its value
102
+ st.session_state.uploader_key += 1
103
+ st.success("Memory cleared. Please upload a new PDF.")
104
+ else:
105
+ st.error(f"Failed to clear memory: {res.json().get('detail', 'Unknown error')}")
106
+ # Removed st.rerun() to prevent "no-op" warning
107
+
108
+
109
+ # ---------------- SIDEBAR CONTROLS ----------------
110
+ with st.sidebar:
111
+ st.header("Controls")
112
+ st.button("πŸ—‘οΈ Clear Chat History", on_click=clear_chat_history, use_container_width=True)
113
+ st.button("πŸ”₯ Clear PDF Memory", on_click=clear_memory, use_container_width=True)
114
+
115
+ st.markdown("---")
116
+ if st.session_state.uploaded_file_name:
117
+ st.success(f"βœ… **Active PDF:**\n `{st.session_state.uploaded_file_name}`")
118
+ else:
119
+ st.warning("⬆️ Upload a PDF to start chatting!")
120
+
121
+ # ---------------- UPLOAD PDF ----------------
122
+ # Use the dynamic key for the file uploader.
123
+ uploaded = st.file_uploader(
124
+ "Upload your PDF",
125
+ type=["pdf"],
126
+ key=st.session_state.uploader_key # Use the dynamic key
127
+ )
128
+
129
+ # Only process if a file is uploaded AND it's a NEW file
130
+ if uploaded and uploaded.name != st.session_state.uploaded_file_name:
131
+ st.session_state.uploaded_file_name = None # Clear status while processing
132
+ st.session_state.chat = [] # Clear chat for a new document
133
+
134
+ with st.spinner(f"Processing '{uploaded.name}'..."):
135
+ try:
136
+ files = {"file": (uploaded.name, uploaded.getvalue(), "application/pdf")}
137
+ res = requests.post(f"{BACKEND_URL}/upload", files=files)
138
+
139
+ if res.status_code == 200:
140
+ chunks = res.json().get("chunks", 0)
141
+ st.success(f"PDF processed successfully! {chunks} chunks created.")
142
+ st.session_state.uploaded_file_name = uploaded.name
143
+ else:
144
+ error_msg = res.json().get("detail", "Unknown error during processing.")
145
+ st.error(f"Upload failed: {error_msg}")
146
+ st.session_state.uploaded_file_name = None
147
+
148
+ except requests.exceptions.ConnectionError:
149
+ st.error(f"Could not connect to the backend server at {BACKEND_URL}. Ensure it is running.")
150
+ st.session_state.uploaded_file_name = None
151
+ except Exception as e:
152
+ st.error(f"An unexpected error occurred: {e}")
153
+ st.session_state.uploaded_file_name = None
154
+
155
+ # Rerun the app to update the UI status immediately
156
+ st.rerun()
157
+
158
+ # ---------------- CHAT INPUT ----------------
159
+ # Disable input field if no PDF is loaded
160
+ disabled_input = st.session_state.uploaded_file_name is None
161
+ question = st.text_input(
162
+ "Ask a question about the loaded PDF:",
163
+ key="question_input",
164
+ disabled=disabled_input
165
+ )
166
+
167
+ if st.button("Send", disabled=disabled_input) and question:
168
+ # 1. Add user query to chat history
169
+ st.session_state.chat.append(("user", question))
170
+
171
+ # 2. Call backend
172
+ with st.spinner("Thinking..."):
173
+ try:
174
+ res = requests.post(f"{BACKEND_URL}/ask", json={"question": question})
175
+
176
+ if res.status_code == 200:
177
+ data = res.json()
178
+ answer = data.get("answer", "No answer provided.")
179
+ sources = data.get("sources", 0)
180
+
181
+ # Format the bot's response to include source count
182
+ bot_message = f"{answer}<div class='sources'>Context Chunks Used: {sources}</div>"
183
+ st.session_state.chat.append(("bot", bot_message))
184
+
185
+ else:
186
+ error_detail = res.json().get("detail", "Error while generating answer.")
187
+ st.session_state.chat.append(("bot", f"πŸ”΄ **Error:** {error_detail}"))
188
+
189
+ except requests.exceptions.ConnectionError:
190
+ st.session_state.chat.append(("bot",
191
+ f"πŸ”΄ **Error:** Could not connect to the backend server at {BACKEND_URL}. Ensure it is running."))
192
+ except Exception as e:
193
+ st.session_state.chat.append(("bot", f"πŸ”΄ **An unexpected error occurred:** {e}"))
194
+
195
+ # Rerun to display the updated chat history
196
+ st.rerun()
197
 
198
+ # ---------------- SHOW CHAT HISTORY ----------------
199
+ st.markdown("## Chat History")
200
+ # Reverse the list to show the latest messages at the bottom
201
+ for role, msg in st.session_state.chat:
202
+ if role == "user":
203
+ st.markdown(f"<div class='chat-user'>{msg}</div>", unsafe_allow_html=True)
204
+ else:
205
+ # Bot message includes the source count, so use the HTML content
206
+ st.markdown(f"<div class='chat-bot'>{msg}</div>", unsafe_allow_html=True)
207
 
208
+ # ---------------- FOOTER (Creator Credit) ----------------
209
+ # FIX 2: Add creator credit with LinkedIn link
210
+ footer_html = """
211
+ <div class="footer">
212
+ Created by <a href="https://www.linkedin.com/in/abhishek-iitr/" target="_blank">Abhishek Saxena</a>
213
+ </div>
214
  """
215
+ st.markdown(footer_html, unsafe_allow_html=True)
216
 
217
+ # ---------------- END ----------------