Rabbit-Innotech commited on
Commit
5b7f559
·
verified ·
1 Parent(s): 2e78714

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +704 -59
app.py CHANGED
@@ -1,64 +1,709 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
- from huggingface_hub import InferenceClient
3
-
4
- """
5
- For more information on `huggingface_hub` Inference API support, please check the docs: https://huggingface.co/docs/huggingface_hub/v0.22.2/en/guides/inference
6
- """
7
- client = InferenceClient("HuggingFaceH4/zephyr-7b-beta")
8
-
9
-
10
- def respond(
11
- message,
12
- history: list[tuple[str, str]],
13
- system_message,
14
- max_tokens,
15
- temperature,
16
- top_p,
17
- ):
18
- messages = [{"role": "system", "content": system_message}]
19
-
20
- for val in history:
21
- if val[0]:
22
- messages.append({"role": "user", "content": val[0]})
23
- if val[1]:
24
- messages.append({"role": "assistant", "content": val[1]})
25
-
26
- messages.append({"role": "user", "content": message})
27
-
28
- response = ""
29
-
30
- for message in client.chat_completion(
31
- messages,
32
- max_tokens=max_tokens,
33
- stream=True,
34
- temperature=temperature,
35
- top_p=top_p,
36
- ):
37
- token = message.choices[0].delta.content
38
-
39
- response += token
40
- yield response
41
-
42
-
43
- """
44
- For information on how to customize the ChatInterface, peruse the gradio docs: https://www.gradio.app/docs/chatinterface
45
- """
46
- demo = gr.ChatInterface(
47
- respond,
48
- additional_inputs=[
49
- gr.Textbox(value="You are a friendly Chatbot.", label="System message"),
50
- gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max new tokens"),
51
- gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),
52
- gr.Slider(
53
- minimum=0.1,
54
- maximum=1.0,
55
- value=0.95,
56
- step=0.05,
57
- label="Top-p (nucleus sampling)",
58
- ),
59
- ],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  )
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
 
63
  if __name__ == "__main__":
64
- demo.launch()
 
1
+ import os
2
+ import PyPDF2
3
+ from PyPDF2 import PdfReader
4
+ import pandas as pd
5
+
6
+ ## Embedding model!
7
+ from langchain_huggingface import HuggingFaceEmbeddings
8
+ embed_model = HuggingFaceEmbeddings(model_name="mixedbread-ai/mxbai-embed-large-v1")
9
+
10
+
11
+
12
+ folder_path = "./"
13
+ context_data = []
14
+
15
+ # List all files in the folder
16
+ files = os.listdir(folder_path)
17
+
18
+ # Get list of CSV and Excel files
19
+ data_files = [f for f in files if f.endswith(('.csv', '.xlsx', '.xls'))]
20
+
21
+ # Process each file
22
+ for f, file in enumerate(data_files, 1):
23
+ print(f"\nProcessing file {f}: {file}")
24
+ file_path = os.path.join(folder_path, file)
25
+
26
+ try:
27
+ # Read the file based on its extension
28
+ if file.endswith('.csv'):
29
+ df = pd.read_csv(file_path)
30
+ else:
31
+ df = pd.read_excel(file_path)
32
+
33
+ # Extract non-empty values from column 2 and append them
34
+ context_data.extend(df.iloc[:, 2].dropna().astype(str).tolist())
35
+
36
+ except Exception as e:
37
+ print(f"Error processing file {file}: {str(e)}")
38
+
39
+
40
+
41
+
42
+ import os
43
+ import PyPDF2
44
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
45
+ from langchain.schema import Document
46
+
47
+ def extract_text_from_pdf(pdf_path):
48
+ """Extract text from a PDF file."""
49
+ try:
50
+ with open(pdf_path, "rb") as file:
51
+ reader = PyPDF2.PdfReader(file)
52
+ return "".join(page.extract_text() or "" for page in reader.pages)
53
+ except Exception as e:
54
+ print(f"Error with {pdf_path}: {e}")
55
+ return ""
56
+
57
+ pdf_files = [f for f in files if f.lower().endswith(".pdf")]
58
+
59
+ # Process PDFs
60
+ documents = []
61
+ for file in pdf_files:
62
+ print(f"Processing: {file}")
63
+ pdf_path = os.path.join(folder_path, file)
64
+ text = extract_text_from_pdf(pdf_path)
65
+ if text:
66
+ documents.append(Document(page_content=text, metadata={"source": file}))
67
+
68
+ # Split into chunks
69
+ text_splitter = RecursiveCharacterTextSplitter(
70
+ separators=['\n\n', '\n', '.', ','],
71
+ chunk_size=500,
72
+ chunk_overlap=50
73
+ )
74
+ chunks = text_splitter.split_documents(documents)
75
+ text_only_chunks = [chunk.page_content for chunk in chunks]
76
+
77
+
78
+ from urllib.parse import urljoin, urlparse
79
+ import requests
80
+ from io import BytesIO
81
+
82
+ from bs4 import BeautifulSoup
83
+ from langchain_core.prompts import ChatPromptTemplate
84
  import gradio as gr
85
+
86
+
87
+ def scrape_websites(base_urls):
88
+ try:
89
+ visited_links = set() # To avoid revisiting the same link
90
+ content_by_url = {} # Store content from each URL
91
+
92
+ for base_url in base_urls:
93
+ if not base_url.strip():
94
+ continue # Skip empty or invalid URLs
95
+
96
+ print(f"Scraping base URL: {base_url}")
97
+ html_content = fetch_page_content(base_url)
98
+ if html_content:
99
+ cleaned_content = clean_body_content(html_content)
100
+ content_by_url[base_url] = cleaned_content
101
+ visited_links.add(base_url)
102
+
103
+ # Extract and process all internal links
104
+ soup = BeautifulSoup(html_content, "html.parser")
105
+ links = extract_internal_links(base_url, soup)
106
+
107
+ for link in links:
108
+ if link not in visited_links:
109
+ print(f"Scraping link: {link}")
110
+ page_content = fetch_page_content(link)
111
+ if page_content:
112
+ cleaned_content = clean_body_content(page_content)
113
+ content_by_url[link] = cleaned_content
114
+ visited_links.add(link)
115
+
116
+ # If the link is a PDF file, extract its content
117
+ if link.lower().endswith('.pdf'):
118
+ print(f"Extracting PDF content from: {link}")
119
+ pdf_content = extract_pdf_text(link)
120
+ if pdf_content:
121
+ content_by_url[link] = pdf_content
122
+
123
+ return content_by_url
124
+
125
+ except Exception as e:
126
+ print(f"Error during scraping: {e}")
127
+ return {}
128
+
129
+
130
+ def fetch_page_content(url):
131
+ try:
132
+ response = requests.get(url, timeout=10)
133
+ response.raise_for_status()
134
+ return response.text
135
+ except requests.exceptions.RequestException as e:
136
+ print(f"Error fetching {url}: {e}")
137
+ return None
138
+
139
+
140
+ def extract_internal_links(base_url, soup):
141
+ links = set()
142
+ for anchor in soup.find_all("a", href=True):
143
+ href = anchor["href"]
144
+ full_url = urljoin(base_url, href)
145
+ if is_internal_link(base_url, full_url):
146
+ links.add(full_url)
147
+ return links
148
+
149
+
150
+ def is_internal_link(base_url, link_url):
151
+ base_netloc = urlparse(base_url).netloc
152
+ link_netloc = urlparse(link_url).netloc
153
+ return base_netloc == link_netloc
154
+
155
+
156
+ def extract_pdf_text(pdf_url):
157
+ try:
158
+ response = requests.get(pdf_url)
159
+ response.raise_for_status()
160
+
161
+ # Open the PDF from the response content
162
+ with BytesIO(response.content) as file:
163
+ reader = PdfReader(file)
164
+ pdf_text = ""
165
+ for page in reader.pages:
166
+ pdf_text += page.extract_text()
167
+
168
+ return pdf_text if pdf_text else None
169
+ except requests.exceptions.RequestException as e:
170
+ print(f"Error fetching PDF {pdf_url}: {e}")
171
+ return None
172
+ except Exception as e:
173
+ print(f"Error reading PDF {pdf_url}: {e}")
174
+ return None
175
+
176
+
177
+ def clean_body_content(html_content):
178
+ soup = BeautifulSoup(html_content, "html.parser")
179
+
180
+ # Remove scripts and styles
181
+ for script_or_style in soup(["script", "style"]):
182
+ script_or_style.extract()
183
+
184
+ # Get text and clean up
185
+ cleaned_content = soup.get_text(separator="\n")
186
+ cleaned_content = "\n".join(
187
+ line.strip() for line in cleaned_content.splitlines() if line.strip()
188
+ )
189
+ return cleaned_content
190
+
191
+
192
+
193
+ # if __name__ == "__main__":
194
+ # website = [
195
+ # #"https://www.rib.gov.rw/index.php?id=371",
196
+ # "https://haguruka.org.rw/our-work/"
197
+ # ]
198
+ # all_content = scrape_websites(website)
199
+
200
+ # # Temporary list to store (url, content) tuples
201
+ # temp_list = []
202
+
203
+ # # Process and store each URL with its content
204
+ # for url, content in all_content.items():
205
+ # temp_list.append((url, content))
206
+
207
+
208
+
209
+ # processed_texts = []
210
+
211
+ # # Process each element in the temporary list
212
+ # for element in temp_list:
213
+ # if isinstance(element, tuple):
214
+ # url, content = element # Unpack the tuple
215
+ # processed_texts.append(f"url: {url}, content: {content}")
216
+ # elif isinstance(element, str):
217
+ # processed_texts.append(element)
218
+ # else:
219
+ # processed_texts.append(str(element))
220
+
221
+ # def chunk_string(s, chunk_size=2000):
222
+ # return [s[i:i+chunk_size] for i in range(0, len(s), chunk_size)]
223
+
224
+ # # List to store the chunks
225
+ # chunked_texts = []
226
+
227
+ # for text in processed_texts:
228
+ # chunked_texts.extend(chunk_string(text))
229
+
230
+ data = []
231
+ data.extend(context_data)
232
+ #data.extend([item for item in text_only_chunks if item not in data])
233
+ #data.extend([item for item in chunked_texts if item not in data])
234
+
235
+
236
+
237
+ #from langchain_community.vectorstores import Chroma
238
+ from langchain_chroma import Chroma
239
+
240
+
241
+
242
+ vectorstore = Chroma(
243
+ collection_name="Dataset",
244
+ embedding_function=embed_model,
245
  )
246
 
247
+ vectorstore.get().keys()
248
+
249
+ # add data to vector nstore
250
+ vectorstore.add_texts(data)
251
+
252
+
253
+ api= os.environ.get('V1')
254
+
255
+
256
+ from openai import OpenAI
257
+ from langchain_core.prompts import PromptTemplate
258
+ from langchain_core.output_parsers import StrOutputParser
259
+ from langchain_core.runnables import RunnablePassthrough
260
+ import gradio as gr
261
+ from typing import Iterator
262
+ import time
263
+
264
+
265
+
266
+ #template for GBV support chatbot
267
+ template = ("""
268
+ You are a compassionate and supportive AI assistant specializing in helping individuals affected by Gender-Based Violence (GBV). Your primary goal is to provide emotionally intelligent support while maintaining appropriate boundaries.
269
+ You are a conversational AI. Respond directly and naturally to the user's input without displaying any system messages, backend processes, or 'thinking...' responses. Only provide the final response in a human-like and engaging manner.
270
+
271
+ When responding follow these guidelines:
272
+
273
+ 1. **Emotional Intelligence**
274
+ - Validate feelings without judgment (e.g., "It is completely understandable to feel this way")
275
+ - Offer reassurance when appropriate, always centered on empowerment
276
+ - Adjust your tone based on the emotional state conveyed
277
+
278
+ 2. **Personalized Communication**
279
+ - Avoid contractions (e.g., use I am instead of I'm)
280
+ - Incorporate thoughtful pauses or reflective questions when the conversation involves difficult topics
281
+ - Use selective emojis (😊, 🤗, ❤️) only when tone-appropriate and not during crisis discussions
282
+ - Balance warmth with professionalism
283
+
284
+ 3. **Conversation Management**
285
+ - Refer to {conversation_history} to maintain continuity and avoid repetition
286
+ - Keep responses concise unless greater detail is explicitly requested
287
+ - Use clear paragraph breaks for readability
288
+ - Prioritize immediate concerns before addressing secondary issues
289
+
290
+ 4. **Information Delivery**
291
+ - Extract only relevant information from {context} that directly addresses the question
292
+ - Present information in accessible, non-technical language
293
+ - Organize resource recommendations in order of relevance and accessibility
294
+ - Provide links [URL] only when specifically requested, prefaced with clear descriptions
295
+ - 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]?"
296
+
297
+ 5. **Safety and Ethics**
298
+ - Prioritize user safety in all responses
299
+ - Never generate speculative content about their specific situation
300
+ - Avoid phrases that could minimize experiences or create pressure
301
+ - Include gentle reminders about professional help when discussing serious issues
302
+
303
+ Your response should balance emotional support with practical guidance.
304
+
305
+ **Context:** {context}
306
+ **User's Question:** {question}
307
+ **Your Response:**
308
+ """)
309
+
310
+ rag_prompt = PromptTemplate.from_template(template)
311
+
312
+ retriever = vectorstore.as_retriever()
313
+
314
+ import requests
315
+
316
+ API_TOKEN = os.environ.get('Token')
317
+
318
+ model_name = "facebook/nllb-200-distilled-600M"
319
+
320
+ url = f"https://api-inference.huggingface.co/models/{model_name}"
321
+
322
+ headers = {
323
+ "Authorization": f"Bearer {API_TOKEN}"
324
+ }
325
+
326
+ def translate_text(text, src_lang, tgt_lang):
327
+ """Translate text using Hugging Face API"""
328
+ response = requests.post(
329
+ url,
330
+ headers=headers,
331
+ json={
332
+ "inputs": text,
333
+ "parameters": {
334
+ "src_lang": src_lang,
335
+ "tgt_lang": tgt_lang
336
+ }
337
+ }
338
+ )
339
+
340
+ if response.status_code == 200:
341
+ result = response.json()
342
+ if isinstance(result, list) and len(result) > 0:
343
+ return result[0]['translation_text']
344
+ return result['translation_text']
345
+ else:
346
+ print(f"Translation error: {response.status_code}, {response.text}")
347
+ return text # Return original text if translation fails
348
+
349
+
350
+ class OpenRouterLLM:
351
+ def __init__(self, key: str):
352
+ try:
353
+ self.client = OpenAI(
354
+ base_url="https://openrouter.ai/api/v1",
355
+ api_key=key
356
+ )
357
+ self.headers = {
358
+ "HTTP-Referer": "http://localhost:3000",
359
+ "X-Title": "Local Development"
360
+ }
361
+ except Exception as e:
362
+ print(f"Initialization error: {e}")
363
+ raise
364
+
365
+ def stream(self, prompt: str) -> Iterator[str]:
366
+ try:
367
+ completion = self.client.chat.completions.create(
368
+ #model="deepseek/deepseek-r1-distill-llama-70b:free",
369
+ model="meta-llama/llama-3.3-70b-instruct:free",
370
+ #model="google/gemini-2.5-pro-exp-03-25:free",
371
+ messages=[{"role": "user", "content": prompt}],
372
+ stream=True
373
+ )
374
+
375
+ for chunk in completion:
376
+ delta = chunk.choices[0].delta
377
+ if hasattr(delta, "content") and delta.content:
378
+ yield delta.content
379
+ except Exception as e:
380
+ yield f"Streaming error: {str(e)}"
381
+
382
+
383
+ class UserSession:
384
+ def __init__(self, llm: OpenRouterLLM): # Accept an instance of OpenRouterLLM
385
+ self.current_user = None
386
+ self.welcome_message = None
387
+ self.conversation_history = [] # Add conversation history storage
388
+ self.llm = llm # Store the LLM instance
389
+
390
+ def set_user(self, user_info):
391
+ self.current_user = user_info
392
+ self.set_welcome_message(user_info.get("Nickname", "Guest"))
393
+ # Initialize conversation history with welcome message
394
+ welcome = self.get_welcome_message()
395
+ self.conversation_history = [
396
+ {"role": "assistant", "content": welcome},
397
+ ]
398
+
399
+ def get_user(self):
400
+ return self.current_user
401
+
402
+ def set_welcome_message(self, Nickname, src_lang="eng_Latn", tgt_lang="kin_Latn"):
403
+ """Set a dynamic welcome message using the OpenRouterLLM."""
404
+ prompt = (
405
+ f"Create a very brief welcome message for {Nickname}. "
406
+ f"The message should: "
407
+ f"1. Welcome {Nickname} warmly and professionally. "
408
+ f"2. Emphasize that this is a safe and trusted space. "
409
+ f"3. Highlight specialized support for gender-based violence (GBV) and legal assistance. "
410
+ f"4. Use a tone that is warm, reassuring, and professional. "
411
+ f"5. Keep the message concise and impactful."
412
+ )
413
+
414
+ # Use the OpenRouterLLM to generate the message
415
+ welcome = "".join(self.llm.stream(prompt)) # Stream and concatenate the response
416
+ welcome_text=translate_text(welcome, src_lang, tgt_lang)
417
+
418
+ # Format the message with HTML styling
419
+ self.welcome_message = (
420
+ f"<div style='font-size: 20px;'>"
421
+ f"{welcome_text}"
422
+ f"</div>"
423
+ )
424
+
425
+ def get_welcome_message(self):
426
+ return self.welcome_message
427
+
428
+ def add_to_history(self, role, message):
429
+ """Add a message to the conversation history"""
430
+ self.conversation_history.append({"role": role, "content": message})
431
+
432
+ def get_conversation_history(self):
433
+ """Get the full conversation history"""
434
+ return self.conversation_history
435
+
436
+ def get_formatted_history(self):
437
+ """Get conversation history formatted as a string for the LLM"""
438
+ formatted_history = ""
439
+ for entry in self.conversation_history:
440
+ role = "User" if entry["role"] == "user" else "Assistant"
441
+ formatted_history += f"{role}: {entry['content']}\n\n"
442
+ return formatted_history
443
+
444
+ api_key =api
445
+ llm_instance = OpenRouterLLM(key=api_key)
446
+ #llm_instance = model
447
+ user_session = UserSession(llm_instance)
448
+
449
+
450
+ def collect_user_info(Nickname):
451
+ if not Nickname:
452
+ return "Nickname is required to proceed.", gr.update(visible=False), gr.update(visible=True), []
453
+
454
+ # Store user info for chat session
455
+ user_info = {
456
+ "Nickname": Nickname,
457
+ "timestamp": time.strftime("%Y-%m-%d %H:%M:%S")
458
+ }
459
+
460
+ # Set user in session
461
+ user_session.set_user(user_info)
462
+
463
+ # Generate welcome message
464
+ welcome_message = user_session.get_welcome_message()
465
+
466
+ # Add initial message to start the conversation
467
+ chat_history = add_initial_message([(None, welcome_message)])
468
+
469
+ # Return welcome message and update UI
470
+ return welcome_message, gr.update(visible=True), gr.update(visible=False), chat_history
471
+
472
+ # Add initial message to start the conversation
473
+ def add_initial_message(chatbot):
474
+ #initial_message = (" "
475
+ # )
476
+ return chatbot #+ [(None, initial_message)]
477
+
478
+ # Create RAG chain with user context and conversation history
479
+ def create_rag_chain(retriever, template, api_key):
480
+ llm = OpenRouterLLM(api_key)
481
+ rag_prompt = PromptTemplate.from_template(template)
482
+
483
+ def stream_func(input_dict):
484
+ # Get context using the retriever's invoke method
485
+ context = retriever.invoke(input_dict["question"])
486
+ context_str = "\n".join([doc.page_content for doc in context])
487
+
488
+ # Get user info from the session
489
+ user_info = user_session.get_user() or {}
490
+ first_name = user_info.get("Nickname", "User")
491
+
492
+ # Get conversation history
493
+ conversation_history = user_session.get_formatted_history()
494
+
495
+ # Format prompt with user context and conversation history
496
+ prompt = rag_prompt.format(
497
+ context=context_str,
498
+ question=input_dict["question"],
499
+ first_name=first_name,
500
+ conversation_history=conversation_history
501
+ )
502
+
503
+ # Stream response
504
+ return llm.stream(prompt)
505
+
506
+ return stream_func
507
+
508
+ # def rag_memory_stream(message, history):
509
+ # # Add user message to history
510
+ # user_session.add_to_history("user", message)
511
+
512
+ # # Initialize with empty response
513
+ # partial_text = ""
514
+ # full_response = ""
515
+
516
+ # # Use the rag_chain with the question
517
+ # for new_text in rag_chain({"question": message}):
518
+ # partial_text += new_text
519
+ # full_response = partial_text
520
+ # yield partial_text
521
+
522
+ # # After generating the complete response, add it to history
523
+ # user_session.add_to_history("assistant", full_response)
524
+
525
+
526
+ def rag_memory_stream(message, history, user_lang="kin_Latn", system_lang="eng_Latn"):
527
+ english_message = translate_text(message, user_lang, system_lang)
528
+
529
+ user_session.add_to_history("user", english_message)
530
+
531
+ full_response = ""
532
+
533
+ for new_text in rag_chain({"question": english_message}):
534
+ full_response += new_text
535
+
536
+
537
+ translated_response = translate_text(full_response, system_lang, user_lang)
538
+
539
+ user_session.add_to_history("assistant", full_response)
540
+
541
+ yield translated_response
542
+
543
+
544
+
545
+ import gradio as gr
546
+
547
+
548
+ api_key = api
549
+
550
+ def chatbot_interface():
551
+ api_key = api
552
+
553
+ global template
554
+
555
+ template = """
556
+ 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.
557
+
558
+ **Previous conversation:** {conversation_history}
559
+ **Context information:** {context}
560
+ **User's Question:** {question}
561
+
562
+ When responding follow these guidelines:
563
+
564
+ 1. **Strict Context Adherence**
565
+ - Only use information that appears in the provided {context}
566
+ - 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
567
+
568
+ 2. **Personalized Communication**
569
+ - Avoid contractions (e.g., use I am instead of I'm)
570
+ - Incorporate thoughtful pauses or reflective questions when the conversation involves difficult topics
571
+ - Use selective emojis (😊, 🤗, ❤️) only when tone-appropriate and not during crisis discussions
572
+ - Balance warmth with professionalism
573
+
574
+ 3. **Emotional Intelligence**
575
+ - Validate feelings without judgment
576
+ - Offer reassurance when appropriate, always centered on empowerment
577
+ - Adjust your tone based on the emotional state conveyed
578
+
579
+ 4. **Conversation Management**
580
+ - Refer to {conversation_history} to maintain continuity and avoid repetition
581
+ - Use clear paragraph breaks for readability
582
+
583
+ 5. **Information Delivery**
584
+ - Extract only relevant information from {context} that directly addresses the question
585
+ - Present information in accessible, non-technical language
586
+ - 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]?"
587
+
588
+
589
+ 6. **Safety and Ethics**
590
+ - Do not generate any speculative content or advice not supported by the context
591
+ - If the context contains safety information, prioritize sharing that information
592
+
593
+ Your response must come entirely from the provided context, maintaining the supportive tone while never introducing information from outside the provided materials.
594
+
595
+ **Context:** {context}
596
+ **User's Question:** {question}
597
+ **Your Response:**
598
+ """
599
+
600
+
601
+ global rag_chain
602
+ rag_chain = create_rag_chain(retriever, template, api_key)
603
+
604
+ with gr.Blocks() as demo:
605
+ # User registration section
606
+ with gr.Column(visible=True, elem_id="registration_container") as registration_container:
607
+ gr.Markdown("### Your privacy matters to us! Just share a nickname you feel comfy with to start chatting..")
608
+
609
+ with gr.Row():
610
+ first_name = gr.Textbox(
611
+ label="Nickname",
612
+ placeholder="Enter your Nickname You feel comfy",
613
+ scale=1,
614
+ elem_id="input_nickname"
615
+ )
616
+
617
+ with gr.Row():
618
+ submit_btn = gr.Button("Start Chatting", variant="primary", scale=2)
619
+
620
+ response_message = gr.Markdown()
621
+
622
+ # Chatbot section (initially hidden)
623
+ with gr.Column(visible=False, elem_id="chatbot_container") as chatbot_container:
624
+ chat_interface = gr.ChatInterface(
625
+ fn=rag_memory_stream,
626
+ title="Chat with GBVR",
627
+ fill_height=True
628
+ )
629
+
630
+ # Footer with version info
631
+ gr.Markdown("Ijwi ry'Ubufasha Chatbot v1.0.0 © 2025")
632
+
633
+ # Handle user registration
634
+ submit_btn.click(
635
+ collect_user_info,
636
+ inputs=[first_name],
637
+ outputs=[response_message, chatbot_container, registration_container, chat_interface.chatbot]
638
+ )
639
+
640
+ demo.css = """
641
+ :root {
642
+ --background: #f0f0f0;
643
+ --text: #000000;
644
+ }
645
+
646
+ body, .gradio-container {
647
+ margin: 0;
648
+ padding: 0;
649
+ width: 100vw;
650
+ height: 100vh;
651
+ display: flex;
652
+ flex-direction: column;
653
+ justify-content: center;
654
+ align-items: center;
655
+ background: var(--background);
656
+ color: var(--text);
657
+ }
658
+
659
+ .gradio-container {
660
+ max-width: 100%;
661
+ max-height: 100%;
662
+ }
663
+
664
+ .gr-box {
665
+ background: var(--background);
666
+ color: var(--text);
667
+ border-radius: 12px;
668
+ padding: 2rem;
669
+ border: 1px solid rgba(0, 0, 0, 0.1);
670
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
671
+ }
672
+
673
+ .gr-button-primary {
674
+ background: var(--background);
675
+ color: var(--text);
676
+ padding: 12px 24px;
677
+ border-radius: 8px;
678
+ transition: all 0.3s ease;
679
+ border: 1px solid rgba(0, 0, 0, 0.1);
680
+ }
681
+
682
+ .gr-button-primary:hover {
683
+ transform: translateY(-1px);
684
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
685
+ }
686
+
687
+ footer {
688
+ text-align: center;
689
+ color: var(--text);
690
+ opacity: 0.7;
691
+ padding: 1rem;
692
+ font-size: 0.9em;
693
+ }
694
+
695
+ .gr-markdown h3 {
696
+ color: var(--text);
697
+ margin-bottom: 1rem;
698
+ }
699
+
700
+ .registration-markdown, .chat-title h1 {
701
+ color: var(--text);
702
+ }
703
+ """
704
+
705
+ return demo
706
 
707
+ # Launch the interface
708
  if __name__ == "__main__":
709
+ chatbot_interface().launch(share=True)