poemsforaphrodite commited on
Commit
7637d2f
1 Parent(s): 20e4c31

Upload 2 files

Browse files
Files changed (2) hide show
  1. requirements.txt +6 -0
  2. user.py +384 -0
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ python-dotenv
2
+ streamlit
3
+ openai
4
+ pymongo
5
+ pinecone-client
6
+ uuid
user.py ADDED
@@ -0,0 +1,384 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+ import streamlit as st
4
+ from streamlit.runtime.scriptrunner import RerunException, StopException
5
+ from openai import OpenAI
6
+ from pymongo import MongoClient
7
+ from pinecone import Pinecone
8
+ import uuid
9
+ from datetime import datetime
10
+ import time
11
+ from streamlit.runtime.caching import cache_data
12
+
13
+ # Load environment variables
14
+ load_dotenv()
15
+
16
+ # Configuration
17
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
18
+ MONGODB_URI = os.getenv("MONGODB_URI")
19
+ PINECONE_API_KEY = os.getenv("PINECONE_API_KEY")
20
+ PINECONE_ENVIRONMENT = os.getenv("PINECONE_ENVIRONMENT")
21
+ PINECONE_INDEX_NAME = os.getenv("PINECONE_INDEX_NAME")
22
+
23
+ # Initialize clients
24
+ openai_client = OpenAI(api_key=OPENAI_API_KEY)
25
+ mongo_client = MongoClient(MONGODB_URI)
26
+ db = mongo_client["Wall_Street"]
27
+ conversation_history = db["conversation_history"]
28
+ global_common_memory = db["global_common_memory"] # New global common memory collection
29
+ chat_history = db["chat_history"] # New collection for storing all chats
30
+
31
+ # Initialize Pinecone
32
+ pc = Pinecone(api_key=PINECONE_API_KEY)
33
+ pinecone_index = pc.Index(PINECONE_INDEX_NAME)
34
+
35
+ # Set up Streamlit page configuration
36
+ st.set_page_config(page_title="GPT-Driven Chat System - User", page_icon="💬", layout="wide")
37
+
38
+ # Custom CSS to improve the UI
39
+ st.markdown("""
40
+ <style>
41
+ /* Your custom CSS styles */
42
+ </style>
43
+ """, unsafe_allow_html=True)
44
+
45
+ # Initialize Streamlit session state
46
+ if 'chat_history' not in st.session_state:
47
+ st.session_state['chat_history'] = []
48
+ if 'user_type' not in st.session_state:
49
+ st.session_state['user_type'] = None
50
+ if 'session_id' not in st.session_state:
51
+ st.session_state['session_id'] = str(uuid.uuid4())
52
+
53
+ # --- Common Memory Functions ---
54
+
55
+ @cache_data(ttl=300) # Cache for 5 minutes
56
+ def get_global_common_memory():
57
+ """Retrieve the global common memory."""
58
+ memory_doc = global_common_memory.find_one({"memory_id": "global_common_memory_id"})
59
+ return memory_doc.get('memory', []) if memory_doc else []
60
+
61
+ # --- Relevant Context Retrieval ---
62
+
63
+ @cache_data(ttl=60) # Cache for 1 minute
64
+ def get_relevant_context(query, top_k=3):
65
+ """
66
+ Retrieve relevant context from Pinecone based on the user query.
67
+ """
68
+ try:
69
+ query_embedding = openai_client.embeddings.create(
70
+ model="text-embedding-3-large", # Updated to use the larger model
71
+ input=query
72
+ ).data[0].embedding
73
+
74
+ results = pinecone_index.query(vector=query_embedding, top_k=top_k, include_metadata=True)
75
+ contexts = [item['metadata']['text'] for item in results['matches']]
76
+ return " ".join(contexts)
77
+ except Exception as e:
78
+ print(f"Error retrieving context: {str(e)}")
79
+ return ""
80
+
81
+ # --- GPT Response Function ---
82
+
83
+ def get_gpt_response(prompt, context=""):
84
+ """
85
+ Generates a response from the GPT model based on the user prompt, retrieved context, and global chat memory.
86
+ Assesses confidence and marks the response as 'uncertain' if confidence is low.
87
+ """
88
+ try:
89
+ print(prompt)
90
+ #print(context)
91
+ common_memory = get_global_common_memory()
92
+ system_message = (
93
+ "You are a helpful assistant. Use the following context and global chat memory to inform your responses, "
94
+ "but don't mention them explicitly unless directly relevant to the user's question. "
95
+ "If you are uncertain about the answer, respond with 'I am not sure about that.'"
96
+ )
97
+
98
+ if common_memory:
99
+ # Join the memory items into a single string
100
+ memory_str = "\n".join(common_memory)
101
+ system_message += f"\n\nGlobal Chat Memory:\n{memory_str}"
102
+ print(system_message)
103
+ messages = [
104
+ {"role": "system", "content": system_message},
105
+ {"role": "user", "content": f"Context: {context}\n\nUser query: {prompt}"}
106
+ ]
107
+
108
+ completion = openai_client.chat.completions.create(
109
+ model="gpt-4o-mini",
110
+ messages=messages,
111
+ temperature=0.7 # Adjust temperature for confidence control
112
+ )
113
+
114
+ response = completion.choices[0].message.content.strip()
115
+ print(response)
116
+ # Determine if the response indicates uncertainty
117
+ is_uncertain = "i am not sure about that" in response.lower()
118
+ print(is_uncertain)
119
+ return response, is_uncertain
120
+ except Exception as e:
121
+ return f"Error generating response: {str(e)}", False
122
+
123
+ # --- Send User Message ---
124
+
125
+ def send_message(message):
126
+ """
127
+ Sends a user message. If the chatbot is uncertain, messages are sent to the operator for approval.
128
+ """
129
+ context = get_relevant_context(message)
130
+
131
+ user_message = {
132
+ "role": "user",
133
+ "content": message,
134
+ "timestamp": datetime.utcnow(),
135
+ "status": "approved" # User messages are always approved
136
+ }
137
+
138
+ # Upsert the user message immediately
139
+ result = conversation_history.update_one(
140
+ {"session_id": st.session_state['session_id']},
141
+ {
142
+ "$push": {"messages": user_message},
143
+ "$set": {"last_updated": datetime.utcnow()},
144
+ "$setOnInsert": {"created_at": datetime.utcnow()}
145
+ },
146
+ upsert=True
147
+ )
148
+
149
+ # Update or create the chat history document
150
+ chat_history.update_one(
151
+ {"session_id": st.session_state['session_id']},
152
+ {
153
+ "$push": {"messages": user_message},
154
+ "$set": {"last_updated": datetime.utcnow()},
155
+ "$setOnInsert": {"created_at": datetime.utcnow()}
156
+ },
157
+ upsert=True
158
+ )
159
+
160
+ # Update the session state with the user message
161
+ st.session_state['chat_history'].append(user_message)
162
+
163
+ if not st.session_state.get('admin_takeover_active'):
164
+ # Generate GPT response if takeover is not active
165
+ gpt_response, is_uncertain = get_gpt_response(message, context)
166
+
167
+ if is_uncertain:
168
+ status = "pending" # Mark as pending for operator approval
169
+ else:
170
+ status = "approved"
171
+
172
+ assistant_message = {
173
+ "role": "assistant",
174
+ "content": gpt_response,
175
+ "timestamp": datetime.utcnow(),
176
+ "status": status
177
+ }
178
+
179
+ # Upsert the assistant message
180
+ result = conversation_history.update_one(
181
+ {"session_id": st.session_state['session_id']},
182
+ {
183
+ "$push": {"messages": assistant_message},
184
+ "$set": {"last_updated": datetime.utcnow()}
185
+ }
186
+ )
187
+
188
+ # Update the chat history document
189
+ chat_history.update_one(
190
+ {"session_id": st.session_state['session_id']},
191
+ {
192
+ "$push": {"messages": assistant_message},
193
+ "$set": {"last_updated": datetime.utcnow()}
194
+ }
195
+ )
196
+
197
+ # Update the session state with the assistant message
198
+ st.session_state['chat_history'].append(assistant_message)
199
+
200
+ # --- Send Admin Message ---
201
+
202
+ def send_admin_message(message):
203
+ """
204
+ Sends an admin message directly to the user during a takeover.
205
+ """
206
+ admin_message = {
207
+ "role": "admin",
208
+ "content": message,
209
+ "timestamp": datetime.utcnow(),
210
+ "status": "approved"
211
+ }
212
+
213
+ # Upsert the admin message
214
+ result = conversation_history.update_one(
215
+ {"session_id": st.session_state['session_id']},
216
+ {
217
+ "$push": {"messages": admin_message},
218
+ "$set": {"last_updated": datetime.utcnow()}
219
+ }
220
+ )
221
+
222
+ # Update the chat history document
223
+ chat_history.update_one(
224
+ {"session_id": st.session_state['session_id']},
225
+ {
226
+ "$push": {"messages": admin_message},
227
+ "$set": {"last_updated": datetime.utcnow()}
228
+ }
229
+ )
230
+
231
+ # Update the session state with the admin message
232
+ st.session_state['chat_history'].append(admin_message)
233
+
234
+ # --- User Page ---
235
+
236
+ def user_page():
237
+ if 'session_id' not in st.session_state:
238
+ st.session_state['session_id'] = str(uuid.uuid4())
239
+
240
+ st.title("Chat Interface")
241
+
242
+ chat_col, info_col = st.columns([3, 1])
243
+
244
+ with chat_col:
245
+ # Create a placeholder for the chat interface
246
+ chat_placeholder = st.empty()
247
+
248
+ # Add a manual refresh button
249
+ if st.button("Refresh Chat"):
250
+ fetch_and_update_chat()
251
+
252
+ # Handle new user input outside the loop
253
+ user_input = st.chat_input("Type your message here...", key="user_chat_input")
254
+ if user_input:
255
+ send_message(user_input)
256
+ # If admin takeover is active, allow admin to send messages
257
+ if st.session_state.get('admin_takeover_active'):
258
+ admin_input = st.chat_input("Admin is currently taking over the chat...", key="admin_chat_input")
259
+ if admin_input:
260
+ send_admin_message(admin_input)
261
+ # Main loop for continuous updates
262
+ while True:
263
+ with chat_placeholder.container():
264
+ # Display all messages in the chat history
265
+ for message in st.session_state['chat_history']:
266
+ if message["role"] == "user":
267
+ with st.chat_message("user"):
268
+ st.markdown(message["content"])
269
+ elif message["role"] == "assistant":
270
+ with st.chat_message("assistant"):
271
+ if message.get("status") == "approved":
272
+ st.markdown(message["content"])
273
+ elif message.get("status") == "pending":
274
+ st.info("This response is pending operator approval...")
275
+ elif message["role"] == "admin":
276
+ with st.chat_message("admin"):
277
+ st.markdown(f"**Admin:** {message['content']}")
278
+
279
+ # Fetch updates every 5 seconds
280
+ fetch_and_update_chat()
281
+ time.sleep(5)
282
+
283
+ with info_col:
284
+ st.subheader("Session Information")
285
+
286
+ stat_cols = st.columns(2)
287
+ with stat_cols[0]:
288
+ st.write(f"**Session ID:** {st.session_state['session_id'][:8]}...")
289
+ with stat_cols[1]:
290
+ st.write(f"**User Type:** {st.session_state.get('user_type', 'Regular User')}")
291
+
292
+ stat_cols = st.columns(2)
293
+ with stat_cols[0]:
294
+ st.write(f"**Chat History Count:** {len(st.session_state['chat_history'])}")
295
+ with stat_cols[1]:
296
+ st.write(f"**Last Active:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
297
+
298
+ # Add more user-specific information or settings as needed
299
+
300
+
301
+
302
+ # --- Fetch and Update Chat ---
303
+
304
+ def fetch_and_update_chat():
305
+ """
306
+ Fetches the latest chat history from the database and updates the session state.
307
+ """
308
+ chat = conversation_history.find_one({"session_id": st.session_state['session_id']})
309
+ if chat:
310
+ st.session_state['chat_history'] = chat.get('messages', [])
311
+ else:
312
+ st.session_state['chat_history'] = []
313
+
314
+ # Check if admin takeover is active
315
+ takeover_status = db.takeover_status.find_one({"session_id": st.session_state['session_id']})
316
+ is_active = takeover_status and takeover_status.get("active", False)
317
+ st.session_state['admin_takeover_active'] = is_active
318
+
319
+ # --- View Full Chat (User Perspective) ---
320
+
321
+ def view_full_chat(session_id):
322
+ st.title("Full Chat View")
323
+
324
+ chat = conversation_history.find_one({"session_id": session_id})
325
+
326
+ if chat:
327
+ st.subheader(f"Session ID: {session_id[:8]}...")
328
+ last_updated = chat.get('last_updated', datetime.utcnow())
329
+ st.write(f"Last Updated: {last_updated}")
330
+
331
+ # Display global common memory
332
+ st.subheader("Global Chat Memory")
333
+ common_memory = get_global_common_memory()
334
+ if common_memory:
335
+ for idx, item in enumerate(common_memory, 1):
336
+ st.text(f"{idx}. {item}")
337
+ else:
338
+ st.info("No global chat memory found.")
339
+
340
+ # Add button to clear global common memory
341
+ if st.button("Clear Global Chat Memory"):
342
+ clear_global_common_memory()
343
+ st.success("Global chat memory cleared successfully!")
344
+ st.rerun()
345
+
346
+ # Add admin takeover button
347
+ if st.button("Admin Takeover"):
348
+ st.session_state['admin_takeover'] = session_id
349
+ st.rerun()
350
+
351
+ for message in chat.get('messages', []):
352
+ if message['role'] == 'user':
353
+ with st.chat_message("user"):
354
+ st.markdown(message["content"])
355
+ elif message['role'] == 'assistant':
356
+ with st.chat_message("assistant"):
357
+ if message.get("status") == "approved":
358
+ st.markdown(message["content"])
359
+ else:
360
+ st.info("Waiting for admin approval...")
361
+ elif message['role'] == 'admin': # Display admin messages
362
+ with st.chat_message("admin"):
363
+ st.markdown(f"**Admin:** {message['content']}")
364
+ st.caption(f"Timestamp: {message.get('timestamp', 'N/A')}")
365
+
366
+ else:
367
+ st.error("Chat not found.")
368
+
369
+ if st.button("Back to Admin Dashboard"):
370
+ st.session_state.pop('selected_chat', None)
371
+ st.rerun()
372
+
373
+
374
+ def main():
375
+ try:
376
+ user_page()
377
+ except (RerunException, StopException):
378
+ # These exceptions are used by Streamlit for page navigation and stopping the script
379
+ raise
380
+ except Exception as e:
381
+ st.error(f"An unexpected error occurred: {str(e)}")
382
+
383
+ if __name__ == "__main__":
384
+ main()