Nad147 commited on
Commit
d55ace9
Β·
verified Β·
1 Parent(s): 6d0af80

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +336 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,338 @@
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 pandas as pd
3
+ import requests
4
+ import base64
5
+ import io
6
+ from PIL import Image
7
+
8
+
9
+
10
+ # Configuration
11
+ BACKEND_URL = "Not set"
12
+
13
+
14
+ # Streamlit Page Setup
15
+ st.set_page_config(page_title="AI CSV Analyst", layout="wide")
16
+
17
+
18
+
19
+ def get_chat_history(user_id):
20
+ """Fetch or create a new chat session from backend."""
21
+ try:
22
+ # If user_id is None, create a new session by calling with 'new' or empty
23
+ session_id = user_id if user_id else "new"
24
+ response = requests.get(f"{BACKEND_URL}/chat/history/{session_id}", timeout=10)
25
+ if response.status_code == 200:
26
+ data = response.json()
27
+ return data.get("user_id") # Returns the user_id (new or existing)
28
+ else:
29
+ st.warning("Unable to fetch chat history.")
30
+ return None
31
+ except requests.exceptions.RequestException as e:
32
+ st.warning("Backend not reachable.")
33
+ return None
34
+
35
+
36
+
37
+
38
+
39
+
40
+ # File Upload Function
41
+
42
+ def file_upload_interface():
43
+ """Returns file_id if a file is uploaded and selected, None otherwise"""
44
+
45
+ # Initialize session state for storing multiple files
46
+ if "uploaded_files" not in st.session_state:
47
+ st.session_state.uploaded_files = {}
48
+ if "selected_file_id" not in st.session_state:
49
+ st.session_state.selected_file_id = None
50
+
51
+ # File uploader
52
+ uploaded_file = st.file_uploader("Upload CSV file", type=["csv"], accept_multiple_files=False)
53
+
54
+ # If a file is uploaded by the user
55
+ if uploaded_file:
56
+ df_preview = pd.read_csv(uploaded_file)
57
+
58
+ # Check if this file is already uploaded (by name)
59
+ file_already_exists = any(
60
+ file_data["name"] == uploaded_file.name
61
+ for file_data in st.session_state.uploaded_files.values()
62
+ )
63
+
64
+ if not file_already_exists:
65
+ with st.spinner(f"Uploading {uploaded_file.name} to backend..."):
66
+ files = {"file": (uploaded_file.name, uploaded_file.getvalue(), "text/csv")}
67
+ try:
68
+ response = requests.post(f"{BACKEND_URL}/upload/", files=files)
69
+ if response.status_code == 200:
70
+ data = response.json()
71
+ file_id = data["file_id"]
72
+
73
+ st.session_state.uploaded_files[file_id] = {
74
+ "name": uploaded_file.name,
75
+ "df": df_preview
76
+ }
77
+
78
+ st.session_state.selected_file_id = file_id
79
+ st.success(f"{uploaded_file.name} uploaded successfully!")
80
+ st.rerun()
81
+ else:
82
+ st.error(f"Upload failed: {response.text}")
83
+ except Exception as e:
84
+ st.error(f"Error uploading file: {e}")
85
+ else:
86
+ st.info(f" {uploaded_file.name} is already uploaded.")
87
+
88
+ # Display file selector if files are uploaded
89
+ if st.session_state.uploaded_files:
90
+ st.subheader("πŸ“‚ Uploaded Files")
91
+
92
+ file_options = {
93
+ file_id: file_data["name"]
94
+ for file_id, file_data in st.session_state.uploaded_files.items()
95
+ }
96
+
97
+ selected_file_name = st.selectbox(
98
+ "Select a file to work with:",
99
+ options=list(file_options.keys()),
100
+ format_func=lambda x: file_options[x],
101
+ index=list(file_options.keys()).index(st.session_state.selected_file_id)
102
+ if st.session_state.selected_file_id in file_options else 0
103
+ )
104
+
105
+ st.session_state.selected_file_id = selected_file_name
106
+
107
+ # Display preview
108
+ st.subheader(f"Preview: {file_options[selected_file_name]}")
109
+ st.dataframe(st.session_state.uploaded_files[selected_file_name]["df"].head())
110
+
111
+ # Remove file button
112
+ col1, col2 = st.columns([3, 1])
113
+ with col2:
114
+ if st.button("πŸ—‘οΈ Remove Selected File"):
115
+ del st.session_state.uploaded_files[st.session_state.selected_file_id]
116
+ st.session_state.selected_file_id = (
117
+ list(st.session_state.uploaded_files.keys())[0]
118
+ if st.session_state.uploaded_files else None
119
+ )
120
+ st.rerun()
121
+
122
+ return st.session_state.selected_file_id
123
+
124
+ return None
125
+
126
+
127
+ # Chat Interface
128
+ def run_chat_interface(file_id, chat_name):
129
+ """Display the chat interface for asking questions"""
130
+
131
+ st.title("πŸ“Š AI-Powered CSV Data Analyst")
132
+ st.caption(f"πŸ’¬ **{chat_name}** | File ID: `{file_id}`")
133
+
134
+ # Get file info
135
+ if file_id in st.session_state.uploaded_files:
136
+ file_name = st.session_state.uploaded_files[file_id]["name"]
137
+ st.info(f"πŸ“„ Working with: **{file_name}**")
138
+
139
+ st.markdown("---")
140
+
141
+ # Query input
142
+ st.subheader("πŸ’¬ Ask a question about your data:")
143
+ query = st.text_area("Type your question here...", placeholder="e.g., What is the average revenue by region?")
144
+
145
+ if st.button("Run Query", type="primary"):
146
+ if not query.strip():
147
+ st.warning("Please enter a question first.")
148
+ else:
149
+ with st.spinner("Analyzing data..."):
150
+ try:
151
+ payload = {
152
+ "query": query,
153
+ "file_id": file_id,
154
+ "user_id": st.session_state.user_id
155
+ }
156
+ response = requests.post(f"{BACKEND_URL}/chat/", json=payload)
157
+
158
+ if response.status_code == 200:
159
+ data = response.json()
160
+
161
+ # Store result in session state so it persists
162
+ st.session_state.last_result = data
163
+ else:
164
+ st.error(f"Backend error: {response.text}")
165
+ except Exception as e:
166
+ st.error(f"⚠️ Something went wrong: {e}")
167
+
168
+ # Display results if they exist in session state
169
+ if "last_result" in st.session_state:
170
+ data = st.session_state.last_result
171
+
172
+ st.write("###Generated Code:")
173
+ st.code(data.get("generated_code", "No code generated"), language="python")
174
+
175
+ st.write("### πŸ“Š Result:")
176
+
177
+ result = data.get("result")
178
+ image = data.get("image")
179
+ error = data.get("error")
180
+
181
+ if result is not None:
182
+ st.write(result)
183
+ if image is not None:
184
+ try:
185
+ img_data = base64.b64decode(image)
186
+ image = Image.open(io.BytesIO(img_data))
187
+ st.image(image, caption="Generated plot")
188
+ except Exception:
189
+ st.error("Could not decode backend image")
190
+ if error is not None:
191
+ st.error(error)
192
+
193
+
194
+ st.markdown("---")
195
+ st.write("### πŸ‘ Was this helpful?")
196
+
197
+ col1, col2 = st.columns(2)
198
+ with col1:
199
+ if st.button("πŸ‘ Thumbs Up"):
200
+ try:
201
+ feedback_payload = {
202
+ "query": query,
203
+ "code": data.get("generated_code", ""),
204
+ "feedback": "thumbs_up"
205
+ }
206
+ feedback_response = requests.post(
207
+ f"{BACKEND_URL}/chat/feedback",
208
+ json=feedback_payload
209
+ )
210
+ if feedback_response.status_code == 200:
211
+ st.success("Thanks for your feedback!")
212
+ else:
213
+ st.error("Failed to submit feedback")
214
+ except Exception as e:
215
+ st.error(f"Error submitting feedback: {e}")
216
+
217
+ with col2:
218
+ if st.button("πŸ‘Ž Thumbs Down"):
219
+ try:
220
+ feedback_payload = {
221
+ "query": query,
222
+ "code": data.get("generated_code", ""),
223
+ "feedback": "thumbs_down"
224
+ }
225
+ feedback_response = requests.post(
226
+ f"{BACKEND_URL}/chat/feedback",
227
+ json=feedback_payload
228
+ )
229
+ if feedback_response.status_code == 200:
230
+ st.success("Thanks for your feedback!")
231
+ else:
232
+ st.error("Failed to submit feedback")
233
+ except Exception as e:
234
+ st.error(f"Error submitting feedback: {e}")
235
+
236
+
237
+ # Main Application Logic
238
+
239
+ # Initialize session state
240
+ if "chat_sessions" not in st.session_state:
241
+ st.session_state.chat_sessions = {} # {user_id: [chat_name, file_id]}
242
+ if "user_id" not in st.session_state:
243
+ st.session_state.user_id = None
244
+ if "creating_new_chat" not in st.session_state:
245
+ st.session_state.creating_new_chat = False
246
+
247
+
248
+ # Sidebar
249
+
250
+ st.sidebar.title("πŸ’¬ Chat Sessions")
251
+
252
+ # New Chat Button
253
+ if st.sidebar.button("βž• New Chat"):
254
+ st.session_state.creating_new_chat = True
255
+ st.session_state.user_id = None # Deselect current chat
256
+ st.rerun()
257
+
258
+ # Show Existing Chats
259
+ if st.session_state.chat_sessions:
260
+ st.sidebar.subheader("πŸ—‚ Your Chats")
261
+
262
+ for uid, data in st.session_state.chat_sessions.items():
263
+ chat_name, file_id = data
264
+ # Highlight the active chat
265
+ button_type = "primary" if uid == st.session_state.user_id else "secondary"
266
+
267
+ if st.sidebar.button(f"{'🟒 ' if uid == st.session_state.user_id else ''}{chat_name}", key=uid, type=button_type):
268
+ st.session_state.user_id = uid
269
+ st.session_state.creating_new_chat = False
270
+ st.rerun()
271
+
272
+
273
+ # Main Content Area
274
+
275
+
276
+ # Case 1: Creating a new chat
277
+ if st.session_state.creating_new_chat:
278
+ st.title("πŸ“ Create New Chat")
279
+ st.caption("Upload a CSV file to start analyzing")
280
+ st.markdown("---")
281
+
282
+ file_id = file_upload_interface()
283
+
284
+ if file_id is not None:
285
+ st.markdown("---")
286
+ col1, col2 = st.columns(2)
287
+
288
+ with col1:
289
+ if st.button("βœ… Start Chat with This File", type="primary", use_container_width=True):
290
+
291
+ chat_name = f"Chat {len(st.session_state.chat_sessions) + 1}"
292
+ new_id = get_chat_history(user_id=None)
293
+ st.session_state.chat_sessions[new_id] = [chat_name, file_id]
294
+ st.session_state.user_id = new_id
295
+ st.session_state.creating_new_chat = False
296
+ st.success(f"βœ… Started {chat_name}!")
297
+ st.rerun()
298
+
299
+ with col2:
300
+ if st.button("❌ Cancel", use_container_width=True):
301
+ st.session_state.creating_new_chat = False
302
+ st.rerun()
303
+ else:
304
+ st.info("Please upload a CSV file to continue")
305
+
306
+ if st.button("❌ Cancel"):
307
+ st.session_state.creating_new_chat = False
308
+ st.rerun()
309
+
310
+ # Case 2: Active chat session
311
+ elif st.session_state.user_id and st.session_state.user_id in st.session_state.chat_sessions:
312
+ chat_name, file_id = st.session_state.chat_sessions[st.session_state.user_id]
313
+ run_chat_interface(file_id, chat_name)
314
+
315
+ # Case 3: No chat selected
316
+ else:
317
+ st.title("πŸ“Š AI-Powered CSV Data Analyst")
318
+ st.caption("Ask questions about your data in plain English β€” powered by OpenAI & FastAPI backend")
319
+ st.markdown("---")
320
+
321
+ st.info("Click **'βž• New Chat'** in the sidebar to get started!")
322
+
323
+ # Show a welcome message or instructions
324
+ st.markdown("""
325
+ ### Welcome to AI CSV Analyst!
326
+
327
+ **How to use:**
328
+ 1. Click "βž• New Chat" in the sidebar
329
+ 2. Upload your CSV file
330
+ 3. Ask questions in plain English
331
+ 4. Get instant insights and visualizations
332
+
333
+ **Example questions:**
334
+ - "What is the total revenue by region?"
335
+ - "Show me a bar chart of sales by category"
336
+ - "What are the top 5 customers by purchase amount?"
337
+ """)
338