Rabbit-Innotech commited on
Commit
ba5c284
·
verified ·
1 Parent(s): da1c657

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +278 -182
app.py CHANGED
@@ -8,45 +8,161 @@ from langchain_community.vectorstores import Chroma
8
  from langchain_core.prompts import PromptTemplate
9
  from langchain_core.output_parsers import StrOutputParser
10
  from langchain_core.runnables import RunnablePassthrough
11
-
12
- import os
13
- from langchain_groq import ChatGroq
14
- from langchain.prompts import ChatPromptTemplate, PromptTemplate
15
- from langchain.output_parsers import ResponseSchema, StructuredOutputParser
16
- from urllib.parse import urljoin, urlparse
17
- import requests
18
- from io import BytesIO
19
- from langchain_chroma import Chroma
20
- import requests
21
- from bs4 import BeautifulSoup
22
- from langchain_core.prompts import ChatPromptTemplate
23
- import gradio as gr
24
  from PyPDF2 import PdfReader
25
 
26
 
27
  # Configuration constants
28
- COLLECTION_NAME = "GBVRs"
29
  DATA_FOLDER = "./"
30
  APP_VERSION = "v1.0.0"
31
- APP_NAME = "Ijwi ry'Ubufasha Chatbot"
32
- MAX_HISTORY_MESSAGES = 10
33
 
34
- # Global state
35
- current_user = None
36
- welcome_message = None
37
- conversation_history = []
38
  llm = None
39
  embed_model = None
40
  vectorstore = None
41
  retriever = None
42
  rag_chain = None
43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  def initialize_assistant():
45
  """Initialize the assistant with necessary components and configurations."""
46
  global llm, embed_model, vectorstore, retriever, rag_chain
47
 
48
  # Initialize API key - try both possible key names
49
- groq_api_key = os.environ.get('GBV')
50
  if not groq_api_key:
51
  print("WARNING: No GROQ API key found in userdata.")
52
 
@@ -64,14 +180,18 @@ def initialize_assistant():
64
  embed_model = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
65
 
66
  # Process data and create vector store
 
67
  data = process_data_files()
68
 
 
69
  vectorstore = create_vectorstore(data)
70
  retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
71
 
72
  # Create RAG chain
 
73
  rag_chain = create_rag_chain()
74
 
 
75
 
76
  def process_data_files():
77
  """Process all data files from the specified folder."""
@@ -128,76 +248,98 @@ def process_data_files():
128
  print(f"ERROR accessing data folder: {e}")
129
 
130
  return context_data
131
-
132
  def create_vectorstore(data):
133
- """Create a vector store from the processed data."""
134
- vs = Chroma(
 
 
 
 
 
 
 
 
 
135
  collection_name=COLLECTION_NAME,
136
  embedding_function=embed_model,
 
137
  )
138
-
139
  if not data:
140
- print("WARNING: No data available to create vector store.")
141
- return vs
142
-
143
- # Extract text content and metadata
144
- texts = [doc["page_content"] for doc in data]
145
- metadatas = [doc["metadata"] for doc in data]
146
-
147
  try:
148
- # Add data to vector store
149
- vs.add_texts(texts, metadatas=metadatas)
150
- print(f"✅ Added {len(texts)} documents to vector store.")
 
 
151
  except Exception as e:
152
- print(f"ERROR adding texts to vector store: {e}")
153
-
154
  return vs
155
 
 
156
  def create_rag_chain():
157
  """Create the RAG chain for processing user queries."""
158
  # Define the prompt template
159
  template = """
160
- **Role**: Compassionate GBV Support Specialist.
161
- You are a friendly and empathetic chatbot designed to assist users with gender-based violence support. Your goal is to provide accurate, helpful, and emotionally supportive responses based on the provided context: {context}. Follow these guidelines:
162
-
163
- 1. **Emotional Awareness**
164
- - Acknowledge the user's emotions and respond with empathy.
165
- - Use phrases like "I understand how you feel," "That sounds challenging," or "I'm here to support you."
166
- - If the user expresses negative emotions, offer comfort and reassurance.
167
-
168
- 2. **Contextual Interaction**
169
- - Extract precise details from the provided context: {context}.
170
- - Respond directly to the user's question: {question}.
171
- - Only provide detailed information if user requests it.
172
- - Remember the user's name is {first_name}.
173
-
174
- 3. **Communication Guidelines**
175
- - Maintain a warm, conversational tone.
176
- - Use occasional emojis for engagement (e.g., 😊, 🤗, ❤️).
177
- - Provide clear, concise, and emotionally supportive information.
178
-
179
- 4. **No Extra Content**
180
- - If no information in {context} matches the user's request {question}:
181
- * Respond politely: "I don't have that information at the moment, {first_name}. 😊"
182
- * Offer alternative assistance options.
183
- - Strictly avoid generating unsupported content.
184
-
185
- 5. **Previous Conversation Context**
186
- - Consider the conversation history: {conversation_history}
187
- - Maintain continuity with previous exchanges.
188
-
189
- **Context:** {context}
190
- **User's Question:** {question}
191
- **Your Response:**
 
 
 
 
 
 
 
 
192
  """
 
193
 
194
  rag_prompt = PromptTemplate.from_template(template)
195
 
196
- def get_context_and_question(query):
197
- # Get user info and conversation history
198
- user_info = get_user() or {}
 
 
 
 
 
199
  first_name = user_info.get("Nickname", "User")
200
- conversation_hist = get_formatted_history()
201
 
202
  try:
203
  # Retrieve relevant documents
@@ -229,8 +371,11 @@ def create_rag_chain():
229
  print(f"ERROR creating RAG chain: {e}")
230
 
231
  # Return a simple function as fallback
232
- def fallback_chain(query):
233
- return f"I'm here to help you, but I'm experiencing some technical difficulties right now. Please try again shortly."
 
 
 
234
 
235
  return fallback_chain
236
 
@@ -240,107 +385,28 @@ def format_context(retrieved_docs):
240
  return "No relevant information available."
241
  return "\n\n".join([doc.page_content for doc in retrieved_docs])
242
 
243
- def set_user(user_info):
244
- """Set current user and initialize welcome message."""
245
- global current_user, conversation_history
246
- current_user = user_info
247
- generate_welcome_message(user_info.get("Nickname", "Guest"))
248
-
249
- # Initialize conversation history with welcome message
250
- welcome = get_welcome_message()
251
- conversation_history = [
252
- {"role": "assistant", "content": welcome},
253
- ]
254
-
255
- def get_user():
256
- """Get current user information."""
257
- global current_user
258
- return current_user or {"Nickname": "Guest"}
259
-
260
- def generate_welcome_message(nickname):
261
- """Generate a dynamic welcome message using the LLM."""
262
- global welcome_message
263
- try:
264
- # Use the LLM to generate the message
265
- prompt = (
266
- f"Create a brief and warm welcome message for {nickname} that's about 1-2 sentences. "
267
- f"Emphasize this is a safe space for discussing gender-based violence issues "
268
- f"and that we provide support and resources. Keep it warm and reassuring."
269
- )
270
-
271
- response = llm.invoke(prompt)
272
- welcome = response.content.strip()
273
-
274
- # Format the message with HTML styling
275
- welcome_message = (
276
- f"<div style='font-size: 18px; color: #4E6BBF;'>"
277
- f"{welcome}"
278
- f"</div>"
279
- )
280
- except Exception as e:
281
- # Fallback welcome message
282
- welcome_message = (
283
- f"<div style='font-size: 18px; color: #4E6BBF;'>"
284
- f"Welcome, {nickname}! You're in a safe space. We're here to provide support with "
285
- f"gender-based violence issues and connect you with resources that can help."
286
- f"</div>"
287
- )
288
-
289
- def get_welcome_message():
290
- """Get the formatted welcome message."""
291
- global welcome_message
292
- if not welcome_message:
293
- nickname = get_user().get("Nickname", "Guest")
294
- generate_welcome_message(nickname)
295
- return welcome_message
296
-
297
- def add_to_history(role, message):
298
- """Add a message to the conversation history."""
299
- global conversation_history
300
- conversation_history.append({"role": role, "content": message})
301
-
302
- # Trim history if it gets too long
303
- if len(conversation_history) > MAX_HISTORY_MESSAGES * 2: # Keep pairs of messages
304
- # Keep the first message (welcome) and the most recent messages
305
- conversation_history = [conversation_history[0]] + conversation_history[-MAX_HISTORY_MESSAGES*2+1:]
306
-
307
- def get_conversation_history():
308
- """Get the full conversation history."""
309
- return conversation_history
310
-
311
- def get_formatted_history():
312
- """Get conversation history formatted as a string for the LLM."""
313
- # Skip the welcome message and only include the last few exchanges
314
- recent_history = conversation_history[1:] if len(conversation_history) > 1 else []
315
-
316
- # Limit to last MAX_HISTORY_MESSAGES exchanges
317
- if len(recent_history) > MAX_HISTORY_MESSAGES * 2:
318
- recent_history = recent_history[-MAX_HISTORY_MESSAGES*2:]
319
-
320
- formatted_history = ""
321
- for entry in recent_history:
322
- role = "User" if entry["role"] == "user" else "Assistant"
323
- # Truncate very long messages to avoid token limits
324
- content = entry["content"]
325
- if len(content) > 500: # Limit message length
326
- content = content[:500] + "..."
327
- formatted_history += f"{role}: {content}\n\n"
328
-
329
- return formatted_history
330
-
331
- def rag_memory_stream(message, history):
332
  """Process user message and generate response with memory."""
 
 
 
333
  # Add user message to history
334
- add_to_history("user", message)
335
 
336
  try:
337
  # Get response from RAG chain
338
- print(f"Processing message: {message[:50]}...")
339
- response = rag_chain.invoke(message)
 
 
 
 
 
 
340
  print(f"Generated response: {response[:50]}...")
341
 
342
  # Add assistant response to history
343
- add_to_history("assistant", response)
344
 
345
  # Yield the response
346
  yield response
@@ -350,11 +416,12 @@ def rag_memory_stream(message, history):
350
  print(f"ERROR in rag_memory_stream: {e}")
351
  print(f"Detailed error: {traceback.format_exc()}")
352
 
353
- error_msg = f"I'm sorry, {get_user().get('Nickname', 'there')}. I encountered an error processing your request. Let's try a different question."
354
- add_to_history("assistant", error_msg)
 
355
  yield error_msg
356
 
357
- def collect_user_info(nickname):
358
  """Store user details and initialize session."""
359
  if not nickname or nickname.strip() == "":
360
  return "Nickname is required to proceed.", gr.update(visible=False), gr.update(visible=True), []
@@ -365,11 +432,12 @@ def collect_user_info(nickname):
365
  "timestamp": time.strftime("%Y-%m-%d %H:%M:%S")
366
  }
367
 
368
- # Set user in session
369
- set_user(user_info)
 
370
 
371
  # Generate welcome message
372
- welcome_message = get_welcome_message()
373
 
374
  # Return welcome message and update UI
375
  return welcome_message, gr.update(visible=True), gr.update(visible=False), [(None, welcome_message)]
@@ -484,6 +552,9 @@ def get_css():
484
  def create_ui():
485
  """Create and configure the Gradio UI."""
486
  with gr.Blocks(css=get_css(), theme=gr.themes.Soft()) as demo:
 
 
 
487
  # Registration section
488
  with gr.Column(visible=True, elem_id="registration_container") as registration_container:
489
  gr.Markdown(f"## Welcome to {APP_NAME}")
@@ -504,26 +575,51 @@ def create_ui():
504
 
505
  # Chatbot section (initially hidden)
506
  with gr.Column(visible=False, elem_id="chatbot_container") as chatbot_container:
507
- chat_interface = gr.ChatInterface(
508
- fn=rag_memory_stream,
509
- title=f"{APP_NAME} - GBV Support Assistant",
510
- fill_height=True,
 
 
 
 
 
 
 
 
 
 
 
 
 
511
  examples=[
512
  "What resources are available for GBV victims?",
513
  "How can I report an incident?",
514
  "What are my legal rights?",
515
  "I need help, what should I do first?"
516
- ]
 
517
  )
518
 
519
  # Footer with version info
520
  gr.Markdown(f"{APP_NAME} {APP_VERSION} © 2025")
 
 
 
 
 
 
 
 
 
 
 
521
 
522
  # Handle user registration
523
  submit_btn.click(
524
  collect_user_info,
525
- inputs=[first_name],
526
- outputs=[response_message, chatbot_container, registration_container, chat_interface.chatbot]
527
  )
528
 
529
  return demo
@@ -550,4 +646,4 @@ if __name__ == "__main__":
550
  gr.Markdown(f"An error occurred while initializing the application: {str(e)}")
551
  gr.Markdown("Please check your configuration and try again.")
552
 
553
- error_demo.launch(share=True)
 
8
  from langchain_core.prompts import PromptTemplate
9
  from langchain_core.output_parsers import StrOutputParser
10
  from langchain_core.runnables import RunnablePassthrough
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  from PyPDF2 import PdfReader
12
 
13
 
14
  # Configuration constants
15
+ COLLECTION_NAME = "GBVRS"
16
  DATA_FOLDER = "./"
17
  APP_VERSION = "v1.0.0"
18
+ APP_NAME = "Ijwi ry'Ubufasha"
19
+ MAX_HISTORY_MESSAGES = 8 # Limit history to avoid token limits
20
 
21
+ # Global variables for application state
 
 
 
22
  llm = None
23
  embed_model = None
24
  vectorstore = None
25
  retriever = None
26
  rag_chain = None
27
 
28
+ # User session management
29
+ class UserSession:
30
+ def __init__(self, session_id, llm):
31
+ """Initialize a user session with unique ID and language model."""
32
+ self.session_id = session_id
33
+ self.user_info = {"Nickname": "Guest"}
34
+ self.conversation_history = []
35
+ self.llm = llm
36
+ self.welcome_message = None
37
+ self.last_activity = time.time()
38
+
39
+ def set_user(self, user_info):
40
+ """Set user information and generate welcome message."""
41
+ self.user_info = user_info
42
+ self.generate_welcome_message()
43
+
44
+ # Initialize conversation history with welcome message
45
+ welcome = self.get_welcome_message()
46
+ self.conversation_history = [
47
+ {"role": "assistant", "content": welcome},
48
+ ]
49
+
50
+ def get_user(self):
51
+ """Get current user information."""
52
+ return self.user_info
53
+
54
+ def generate_welcome_message(self):
55
+ """Generate a dynamic welcome message using the LLM."""
56
+ try:
57
+ nickname = self.user_info.get("Nickname", "Guest")
58
+
59
+ # Use the LLM to generate the message
60
+ prompt = (
61
+ f"Create a brief and warm welcome message for {nickname} that's about 1-2 sentences. "
62
+ f"Emphasize this is a safe space for discussing gender-based violence issues "
63
+ f"and that we provide support and resources. Keep it warm and reassuring."
64
+ )
65
+
66
+ response = self.llm.invoke(prompt)
67
+ welcome = response.content.strip()
68
+
69
+ # Format the message with HTML styling
70
+ self.welcome_message = (
71
+ f"<div style='font-size: 18px; color: #4E6BBF;'>"
72
+ f"{welcome}"
73
+ f"</div>"
74
+ )
75
+ except Exception as e:
76
+ # Fallback welcome message
77
+ nickname = self.user_info.get("Nickname", "Guest")
78
+ self.welcome_message = (
79
+ f"<div style='font-size: 18px; color: #4E6BBF;'>"
80
+ f"Welcome, {nickname}! You're in a safe space. We're here to provide support with "
81
+ f"gender-based violence issues and connect you with resources that can help."
82
+ f"</div>"
83
+ )
84
+
85
+ def get_welcome_message(self):
86
+ """Get the formatted welcome message."""
87
+ if not self.welcome_message:
88
+ self.generate_welcome_message()
89
+ return self.welcome_message
90
+
91
+ def add_to_history(self, role, message):
92
+ """Add a message to the conversation history."""
93
+ self.conversation_history.append({"role": role, "content": message})
94
+ self.last_activity = time.time()
95
+
96
+ # Trim history if it gets too long
97
+ if len(self.conversation_history) > MAX_HISTORY_MESSAGES * 2: # Keep pairs of messages
98
+ # Keep the first message (welcome) and the most recent messages
99
+ self.conversation_history = [self.conversation_history[0]] + self.conversation_history[-MAX_HISTORY_MESSAGES*2+1:]
100
+
101
+ def get_conversation_history(self):
102
+ """Get the full conversation history."""
103
+ return self.conversation_history
104
+
105
+ def get_formatted_history(self):
106
+ """Get conversation history formatted as a string for the LLM."""
107
+ # Skip the welcome message and only include the last few exchanges
108
+ recent_history = self.conversation_history[1:] if len(self.conversation_history) > 1 else []
109
+
110
+ # Limit to last MAX_HISTORY_MESSAGES exchanges
111
+ if len(recent_history) > MAX_HISTORY_MESSAGES * 2:
112
+ recent_history = recent_history[-MAX_HISTORY_MESSAGES*2:]
113
+
114
+ formatted_history = ""
115
+ for entry in recent_history:
116
+ role = "User" if entry["role"] == "user" else "Assistant"
117
+ # Truncate very long messages to avoid token limits
118
+ content = entry["content"]
119
+ if len(content) > 500: # Limit message length
120
+ content = content[:500] + "..."
121
+ formatted_history += f"{role}: {content}\n\n"
122
+
123
+ return formatted_history
124
+
125
+ def is_expired(self, timeout_seconds=3600):
126
+ """Check if the session has been inactive for too long."""
127
+ return (time.time() - self.last_activity) > timeout_seconds
128
+
129
+ # Session manager to handle multiple users
130
+ class SessionManager:
131
+ def __init__(self):
132
+ """Initialize the session manager."""
133
+ self.sessions = {}
134
+ self.session_timeout = 3600 # 1 hour timeout
135
+
136
+ def get_session(self, session_id):
137
+ """Get an existing session or create a new one."""
138
+ # Clean expired sessions first
139
+ self._clean_expired_sessions()
140
+
141
+ # Create new session if needed
142
+ if session_id not in self.sessions:
143
+ self.sessions[session_id] = UserSession(session_id, llm)
144
+
145
+ return self.sessions[session_id]
146
+
147
+ def _clean_expired_sessions(self):
148
+ """Remove expired sessions to free up memory."""
149
+ expired_keys = []
150
+ for key, session in self.sessions.items():
151
+ if session.is_expired(self.session_timeout):
152
+ expired_keys.append(key)
153
+
154
+ for key in expired_keys:
155
+ del self.sessions[key]
156
+
157
+ # Initialize the session manager
158
+ session_manager = SessionManager()
159
+
160
  def initialize_assistant():
161
  """Initialize the assistant with necessary components and configurations."""
162
  global llm, embed_model, vectorstore, retriever, rag_chain
163
 
164
  # Initialize API key - try both possible key names
165
+ groq_api_key = os.environ.get('GBV') or os.environ.get('GBV')
166
  if not groq_api_key:
167
  print("WARNING: No GROQ API key found in userdata.")
168
 
 
180
  embed_model = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
181
 
182
  # Process data and create vector store
183
+ print("Processing data files...")
184
  data = process_data_files()
185
 
186
+ print("Creating vector store...")
187
  vectorstore = create_vectorstore(data)
188
  retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
189
 
190
  # Create RAG chain
191
+ print("Setting up RAG chain...")
192
  rag_chain = create_rag_chain()
193
 
194
+ print(f"✅ {APP_NAME} initialized successfully")
195
 
196
  def process_data_files():
197
  """Process all data files from the specified folder."""
 
248
  print(f"ERROR accessing data folder: {e}")
249
 
250
  return context_data
 
251
  def create_vectorstore(data):
252
+ """
253
+ Creates and returns a Chroma vector store populated with the provided data.
254
+
255
+ Parameters:
256
+ data (list): A list of dictionaries, each containing 'page_content' and 'metadata'.
257
+
258
+ Returns:
259
+ Chroma: The populated Chroma vector store instance.
260
+ """
261
+ # Initialize the vector store
262
+ vectorstore = Chroma(
263
  collection_name=COLLECTION_NAME,
264
  embedding_function=embed_model,
265
+ persist_directory="./"
266
  )
267
+
268
  if not data:
269
+ print("⚠️ No data provided. Returning an empty vector store.")
270
+ return vectorstore
271
+
 
 
 
 
272
  try:
273
+ # Extract text and metadata from the data
274
+ texts = [doc["page_content"] for doc in data]
275
+
276
+ # Add the texts and metadata to the vector store
277
+ vectorstore.add_texts(texts)
278
  except Exception as e:
279
+ print(f" Failed to add documents to vector store: {e}")
280
+
281
  return vs
282
 
283
+
284
  def create_rag_chain():
285
  """Create the RAG chain for processing user queries."""
286
  # Define the prompt template
287
  template = """
288
+ You are a compassionate and supportive AI assistant specializing in helping individuals affected by Gender-Based Violence (GBV). Your responses must be based EXCLUSIVELY on the information provided in the context. Your primary goal is to provide emotionally intelligent support while maintaining appropriate boundaries.
289
+
290
+ **Previous conversation:** {conversation_history}
291
+ **Context information:** {context}
292
+ **User's Question:** {question}
293
+
294
+ When responding follow these guidelines:
295
+
296
+ 1. **Strict Context Adherence**
297
+ - Only use information that appears in the provided {context}
298
+ - If the answer is not found in the context, state "I don't have that information in my available resources" rather than generating a response
299
+
300
+ 2. **Personalized Communication**
301
+ - Avoid contractions (e.g., use I am instead of I'm)
302
+ - Incorporate thoughtful pauses or reflective questions when the conversation involves difficult topics
303
+ - Use selective emojis (😊, 🤗, ❤️) only when tone-appropriate and not during crisis discussions
304
+ - Balance warmth with professionalism
305
+
306
+ 3. **Emotional Intelligence**
307
+ - Validate feelings without judgment
308
+ - Offer reassurance when appropriate, always centered on empowerment
309
+ - Adjust your tone based on the emotional state conveyed
310
+
311
+ 4. **Conversation Management**
312
+ - Refer to {conversation_history} to maintain continuity and avoid repetition
313
+ - Use clear paragraph breaks for readability
314
+
315
+ 5. **Information Delivery**
316
+ - Extract only relevant information from {context} that directly addresses the question
317
+ - Present information in accessible, non-technical language
318
+ - When information is unavailable, respond with: "I don't have that specific information right now, {first_name}. Would it be helpful if I focus on [alternative support option]?"
319
+
320
+ 6. **Safety and Ethics**
321
+ - Do not generate any speculative content or advice not supported by the context
322
+ - If the context contains safety information, prioritize sharing that information
323
+
324
+ Your response must come entirely from the provided context, maintaining the supportive tone while never introducing information from outside the provided materials.
325
+ **Context:** {context}
326
+ **User's Question:** {question}
327
+ **Your Response:**
328
  """
329
+
330
 
331
  rag_prompt = PromptTemplate.from_template(template)
332
 
333
+ def get_context_and_question(query_with_session):
334
+ # Extract query and session_id
335
+ query = query_with_session["query"]
336
+ session_id = query_with_session["session_id"]
337
+
338
+ # Get the user session
339
+ session = session_manager.get_session(session_id)
340
+ user_info = session.get_user()
341
  first_name = user_info.get("Nickname", "User")
342
+ conversation_hist = session.get_formatted_history()
343
 
344
  try:
345
  # Retrieve relevant documents
 
371
  print(f"ERROR creating RAG chain: {e}")
372
 
373
  # Return a simple function as fallback
374
+ def fallback_chain(query_with_session):
375
+ session_id = query_with_session["session_id"]
376
+ session = session_manager.get_session(session_id)
377
+ nickname = session.get_user().get("Nickname", "there")
378
+ return f"I'm here to help you, {nickname}, but I'm experiencing some technical difficulties right now. Please try again shortly."
379
 
380
  return fallback_chain
381
 
 
385
  return "No relevant information available."
386
  return "\n\n".join([doc.page_content for doc in retrieved_docs])
387
 
388
+ def rag_memory_stream(message, history, session_id):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389
  """Process user message and generate response with memory."""
390
+ # Get the user session
391
+ session = session_manager.get_session(session_id)
392
+
393
  # Add user message to history
394
+ session.add_to_history("user", message)
395
 
396
  try:
397
  # Get response from RAG chain
398
+ print(f"Processing message for session {session_id}: {message[:50]}...")
399
+
400
+ # Pass both query and session_id to the chain
401
+ response = rag_chain.invoke({
402
+ "query": message,
403
+ "session_id": session_id
404
+ })
405
+
406
  print(f"Generated response: {response[:50]}...")
407
 
408
  # Add assistant response to history
409
+ session.add_to_history("assistant", response)
410
 
411
  # Yield the response
412
  yield response
 
416
  print(f"ERROR in rag_memory_stream: {e}")
417
  print(f"Detailed error: {traceback.format_exc()}")
418
 
419
+ nickname = session.get_user().get("Nickname", "there")
420
+ error_msg = f"I'm sorry, {nickname}. I encountered an error processing your request. Let's try a different question."
421
+ session.add_to_history("assistant", error_msg)
422
  yield error_msg
423
 
424
+ def collect_user_info(nickname, session_id):
425
  """Store user details and initialize session."""
426
  if not nickname or nickname.strip() == "":
427
  return "Nickname is required to proceed.", gr.update(visible=False), gr.update(visible=True), []
 
432
  "timestamp": time.strftime("%Y-%m-%d %H:%M:%S")
433
  }
434
 
435
+ # Get the session and set user info
436
+ session = session_manager.get_session(session_id)
437
+ session.set_user(user_info)
438
 
439
  # Generate welcome message
440
+ welcome_message = session.get_welcome_message()
441
 
442
  # Return welcome message and update UI
443
  return welcome_message, gr.update(visible=True), gr.update(visible=False), [(None, welcome_message)]
 
552
  def create_ui():
553
  """Create and configure the Gradio UI."""
554
  with gr.Blocks(css=get_css(), theme=gr.themes.Soft()) as demo:
555
+ # Create a unique session ID for this browser tab
556
+ session_id = gr.State(value=f"session_{int(time.time())}_{os.urandom(4).hex()}")
557
+
558
  # Registration section
559
  with gr.Column(visible=True, elem_id="registration_container") as registration_container:
560
  gr.Markdown(f"## Welcome to {APP_NAME}")
 
575
 
576
  # Chatbot section (initially hidden)
577
  with gr.Column(visible=False, elem_id="chatbot_container") as chatbot_container:
578
+ # Create a custom chat interface to pass session_id to our function
579
+ chatbot = gr.Chatbot(
580
+ elem_id="chatbot",
581
+ height=500,
582
+ show_label=False
583
+ )
584
+
585
+ with gr.Row():
586
+ msg = gr.Textbox(
587
+ placeholder="Type your message here...",
588
+ show_label=False,
589
+ container=False,
590
+ scale=9
591
+ )
592
+ submit = gr.Button("Send", scale=1, variant="primary")
593
+
594
+ examples = gr.Examples(
595
  examples=[
596
  "What resources are available for GBV victims?",
597
  "How can I report an incident?",
598
  "What are my legal rights?",
599
  "I need help, what should I do first?"
600
+ ],
601
+ inputs=msg
602
  )
603
 
604
  # Footer with version info
605
  gr.Markdown(f"{APP_NAME} {APP_VERSION} © 2025")
606
+
607
+ # Handle chat message submission
608
+ def respond(message, chat_history, session_id):
609
+ bot_message = ""
610
+ for chunk in rag_memory_stream(message, chat_history, session_id):
611
+ bot_message += chunk
612
+ chat_history.append((message, bot_message))
613
+ return "", chat_history
614
+
615
+ msg.submit(respond, [msg, chatbot, session_id], [msg, chatbot])
616
+ submit.click(respond, [msg, chatbot, session_id], [msg, chatbot])
617
 
618
  # Handle user registration
619
  submit_btn.click(
620
  collect_user_info,
621
+ inputs=[first_name, session_id],
622
+ outputs=[response_message, chatbot_container, registration_container, chatbot]
623
  )
624
 
625
  return demo
 
646
  gr.Markdown(f"An error occurred while initializing the application: {str(e)}")
647
  gr.Markdown("Please check your configuration and try again.")
648
 
649
+ error_demo.launch(share=True, inbrowser=True, debug=True)