abdull4h commited on
Commit
9e85002
·
verified ·
1 Parent(s): b7669f4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +193 -106
app.py CHANGED
@@ -1,5 +1,5 @@
1
  # Vision 2030 Virtual Assistant with RAG and Evaluation Framework
2
- # Modified for Hugging Face Spaces compatibility
3
 
4
  import gradio as gr
5
  import time
@@ -16,6 +16,8 @@ import json
16
  from langdetect import detect
17
  from sentence_transformers import SentenceTransformer
18
  import faiss
 
 
19
 
20
  # Configure logging
21
  logging.basicConfig(
@@ -27,6 +29,10 @@ logging.basicConfig(
27
  )
28
  logger = logging.getLogger('vision2030_assistant')
29
 
 
 
 
 
30
  class Vision2030Assistant:
31
  def __init__(self, pdf_path=None, eval_data_path=None):
32
  """
@@ -64,18 +70,53 @@ class Vision2030Assistant:
64
  self.response_history = []
65
  logger.info("Vision 2030 Assistant initialized successfully")
66
 
 
67
  def load_embedding_models(self):
68
- """Load embedding models for retrieval"""
69
- logger.info("Loading embedding models...")
70
 
71
  try:
72
  # Load embedding models
73
  self.arabic_embedder = SentenceTransformer('CAMeL-Lab/bert-base-arabic-camelbert-ca')
74
  self.english_embedder = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
 
 
 
 
 
 
 
75
  logger.info("Embedding models loaded successfully")
76
  except Exception as e:
77
  logger.error(f"Error loading embedding models: {str(e)}")
78
- raise
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
  def load_and_process_documents(self, pdf_path):
81
  """Load and process the Vision 2030 document from PDF"""
@@ -152,16 +193,28 @@ class Vision2030Assistant:
152
  "تتضمن رؤية 2030 خططًا لتطوير البنية التحتية الرقمية والدعم للشركات الناشئة التكنولوجية في المملكة العربية السعودية."
153
  ]
154
 
 
155
  def _create_indices(self):
156
- """Create FAISS indices for fast text retrieval"""
157
  logger.info("Creating FAISS indices for text retrieval")
158
 
159
  try:
160
  # Process and embed English texts
161
  self.english_vectors = []
162
  for text in self.english_texts:
163
- vec = self.english_embedder.encode(text)
164
- self.english_vectors.append(vec)
 
 
 
 
 
 
 
 
 
 
 
165
 
166
  # Create English index
167
  if self.english_vectors:
@@ -174,8 +227,19 @@ class Vision2030Assistant:
174
  # Process and embed Arabic texts
175
  self.arabic_vectors = []
176
  for text in self.arabic_texts:
177
- vec = self.arabic_embedder.encode(text)
178
- self.arabic_vectors.append(vec)
 
 
 
 
 
 
 
 
 
 
 
179
 
180
  # Create Arabic index
181
  if self.arabic_vectors:
@@ -225,17 +289,28 @@ class Vision2030Assistant:
225
  ]
226
  logger.info(f"Created {len(self.eval_data)} sample evaluation examples")
227
 
 
228
  def retrieve_context(self, query, lang):
229
- """Retrieve relevant context for a query based on language"""
230
  start_time = time.time()
231
 
232
  try:
233
  if lang == "ar":
234
- query_vec = self.arabic_embedder.encode(query)
 
 
 
 
 
235
  D, I = self.arabic_index.search(np.array([query_vec]), k=2) # Get top 2 most relevant chunks
236
  context = "\n".join([self.arabic_texts[i] for i in I[0] if i < len(self.arabic_texts) and i >= 0])
237
  else:
238
- query_vec = self.english_embedder.encode(query)
 
 
 
 
 
239
  D, I = self.english_index.search(np.array([query_vec]), k=2) # Get top 2 most relevant chunks
240
  context = "\n".join([self.english_texts[i] for i in I[0] if i < len(self.english_texts) and i >= 0])
241
 
@@ -345,8 +420,9 @@ class Vision2030Assistant:
345
 
346
  return accuracy
347
 
 
348
  def evaluate_on_test_set(self):
349
- """Evaluate the assistant on the test set"""
350
  logger.info("Running evaluation on test set")
351
 
352
  eval_results = []
@@ -442,102 +518,113 @@ class Vision2030Assistant:
442
 
443
  # Create the Gradio interface
444
  def create_gradio_interface():
445
- # Initialize the assistant
446
- assistant = Vision2030Assistant()
447
-
448
- def chat(message, history):
449
- if not message.strip():
450
- return history, ""
451
 
452
- # Generate response
453
- reply = assistant.generate_response(message)
454
-
455
- # Update history
456
- history.append((message, reply))
457
-
458
- return history, ""
459
-
460
- def provide_feedback(history, rating, feedback_text):
461
- # Record feedback for the last conversation
462
- if history and len(history) > 0:
463
- last_interaction = history[-1]
464
- assistant.record_user_feedback(last_interaction[0], last_interaction[1], rating, feedback_text)
465
- return f"Thank you for your feedback! (Rating: {rating}/5)"
466
- return "No conversation found to rate."
467
-
468
- def run_evaluation():
469
- results = assistant.evaluate_on_test_set()
470
-
471
- # Create summary text
472
- summary = f"""
473
- Evaluation Results:
474
- ------------------
475
- Total questions evaluated: {len(results['detailed_results'])}
476
- Overall factual accuracy: {results['average_factual_accuracy']:.2f}
477
- Average response time: {results['average_response_time']:.4f} seconds
478
-
479
- Detailed Results:
480
- """
481
-
482
- for i, result in enumerate(results['detailed_results']):
483
- summary += f"\nQ{i+1}: {result['question']}\n"
484
- summary += f"Reference: {result['reference']}\n"
485
- summary += f"Response: {result['response']}\n"
486
- summary += f"Accuracy: {result['factual_accuracy']:.2f}\n"
487
- summary += "-" * 40 + "\n"
488
-
489
- # Return both the results summary and visualization
490
- fig = assistant.visualize_evaluation_results(results)
491
 
492
- return summary, fig
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
493
 
494
- def process_uploaded_file(file):
495
- if file is not None:
496
- # Create a new assistant with the uploaded PDF
497
- global assistant
498
- assistant = Vision2030Assistant(pdf_path=file.name)
499
- return f"Successfully processed {file.name}. The assistant is ready to use."
500
- return "No file uploaded. Using sample data."
501
-
502
- # Create the Gradio interface
503
- with gr.Blocks() as demo:
504
- gr.Markdown("# Vision 2030 Virtual Assistant 🌟")
505
- gr.Markdown("Ask questions about Saudi Arabia's Vision 2030 in both Arabic and English")
506
-
507
- with gr.Tab("Chat"):
508
- chatbot = gr.Chatbot(height=400)
509
- msg = gr.Textbox(label="Your Question", placeholder="Ask about Vision 2030...")
510
- with gr.Row():
511
- submit_btn = gr.Button("Submit")
512
- clear_btn = gr.Button("Clear Chat")
513
-
514
- gr.Markdown("### Provide Feedback")
515
- with gr.Row():
516
- rating = gr.Slider(minimum=1, maximum=5, step=1, value=3, label="Rate the Response (1-5)")
517
- feedback_text = gr.Textbox(label="Additional Comments (Optional)")
518
- feedback_btn = gr.Button("Submit Feedback")
519
- feedback_result = gr.Textbox(label="Feedback Status")
520
-
521
- with gr.Tab("Evaluation"):
522
- evaluate_btn = gr.Button("Run Evaluation on Test Set")
523
- eval_output = gr.Textbox(label="Evaluation Results", lines=20)
524
- eval_chart = gr.Plot(label="Evaluation Metrics")
525
-
526
- with gr.Tab("Upload PDF"):
527
- file_input = gr.File(label="Upload Vision 2030 PDF")
528
- upload_result = gr.Textbox(label="Upload Status")
529
- upload_btn = gr.Button("Process PDF")
530
-
531
- # Set up event handlers
532
- msg.submit(chat, [msg, chatbot], [chatbot, msg])
533
- submit_btn.click(chat, [msg, chatbot], [chatbot, msg])
534
- clear_btn.click(lambda: [], None, chatbot)
535
- feedback_btn.click(provide_feedback, [chatbot, rating, feedback_text], feedback_result)
536
- evaluate_btn.click(run_evaluation, None, [eval_output, eval_chart])
537
- upload_btn.click(process_uploaded_file, [file_input], upload_result)
538
-
539
- return demo
 
 
 
 
 
 
 
 
 
540
 
541
- # Launch the app
542
  demo = create_gradio_interface()
543
  demo.launch()
 
1
  # Vision 2030 Virtual Assistant with RAG and Evaluation Framework
2
+ # Modified for Hugging Face Spaces compatibility with GPU support
3
 
4
  import gradio as gr
5
  import time
 
16
  from langdetect import detect
17
  from sentence_transformers import SentenceTransformer
18
  import faiss
19
+ import torch
20
+ import spaces
21
 
22
  # Configure logging
23
  logging.basicConfig(
 
29
  )
30
  logger = logging.getLogger('vision2030_assistant')
31
 
32
+ # Check for GPU availability
33
+ has_gpu = torch.cuda.is_available()
34
+ logger.info(f"GPU available: {has_gpu}")
35
+
36
  class Vision2030Assistant:
37
  def __init__(self, pdf_path=None, eval_data_path=None):
38
  """
 
70
  self.response_history = []
71
  logger.info("Vision 2030 Assistant initialized successfully")
72
 
73
+ @spaces.GPU
74
  def load_embedding_models(self):
75
+ """Load embedding models for retrieval with GPU support"""
76
+ logger.info("Loading embedding models with GPU support...")
77
 
78
  try:
79
  # Load embedding models
80
  self.arabic_embedder = SentenceTransformer('CAMeL-Lab/bert-base-arabic-camelbert-ca')
81
  self.english_embedder = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
82
+
83
+ # Move to GPU if available
84
+ if has_gpu:
85
+ self.arabic_embedder = self.arabic_embedder.to('cuda')
86
+ self.english_embedder = self.english_embedder.to('cuda')
87
+ logger.info("Models moved to GPU")
88
+
89
  logger.info("Embedding models loaded successfully")
90
  except Exception as e:
91
  logger.error(f"Error loading embedding models: {str(e)}")
92
+ # Create simple placeholder models if loading fails
93
+ self._create_fallback_embedders()
94
+
95
+ def _create_fallback_embedders(self):
96
+ """Create fallback embedding methods if model loading fails"""
97
+ logger.warning("Using fallback embedding methods")
98
+
99
+ # Simple fallback using character-level encoding (not a real embedding, just for demo)
100
+ def simple_encode(text, dim=384):
101
+ import hashlib
102
+ # Create a hash of the text
103
+ hash_object = hashlib.md5(text.encode())
104
+ # Use the hash to seed a random number generator
105
+ import numpy as np
106
+ np.random.seed(int(hash_object.hexdigest(), 16) % 2**32)
107
+ # Generate a random vector
108
+ return np.random.randn(dim).astype(np.float32)
109
+
110
+ # Create embedding function objects
111
+ class SimpleEmbedder:
112
+ def __init__(self, dim=384):
113
+ self.dim = dim
114
+
115
+ def encode(self, text):
116
+ return simple_encode(text, self.dim)
117
+
118
+ self.arabic_embedder = SimpleEmbedder()
119
+ self.english_embedder = SimpleEmbedder()
120
 
121
  def load_and_process_documents(self, pdf_path):
122
  """Load and process the Vision 2030 document from PDF"""
 
193
  "تتضمن رؤية 2030 خططًا لتطوير البنية التحتية الرقمية والدعم للشركات الناشئة التكنولوجية في المملكة العربية السعودية."
194
  ]
195
 
196
+ @spaces.GPU
197
  def _create_indices(self):
198
+ """Create FAISS indices for fast text retrieval with GPU support"""
199
  logger.info("Creating FAISS indices for text retrieval")
200
 
201
  try:
202
  # Process and embed English texts
203
  self.english_vectors = []
204
  for text in self.english_texts:
205
+ try:
206
+ if has_gpu and hasattr(self.english_embedder, 'to') and callable(getattr(self.english_embedder, 'to')):
207
+ # If it's a real model on GPU
208
+ with torch.no_grad():
209
+ vec = self.english_embedder.encode(text)
210
+ else:
211
+ # If it's our fallback
212
+ vec = self.english_embedder.encode(text)
213
+ self.english_vectors.append(vec)
214
+ except Exception as e:
215
+ logger.error(f"Error encoding English text: {str(e)}")
216
+ # Use a random vector as fallback
217
+ self.english_vectors.append(np.random.randn(384).astype(np.float32))
218
 
219
  # Create English index
220
  if self.english_vectors:
 
227
  # Process and embed Arabic texts
228
  self.arabic_vectors = []
229
  for text in self.arabic_texts:
230
+ try:
231
+ if has_gpu and hasattr(self.arabic_embedder, 'to') and callable(getattr(self.arabic_embedder, 'to')):
232
+ # If it's a real model on GPU
233
+ with torch.no_grad():
234
+ vec = self.arabic_embedder.encode(text)
235
+ else:
236
+ # If it's our fallback
237
+ vec = self.arabic_embedder.encode(text)
238
+ self.arabic_vectors.append(vec)
239
+ except Exception as e:
240
+ logger.error(f"Error encoding Arabic text: {str(e)}")
241
+ # Use a random vector as fallback
242
+ self.arabic_vectors.append(np.random.randn(384).astype(np.float32))
243
 
244
  # Create Arabic index
245
  if self.arabic_vectors:
 
289
  ]
290
  logger.info(f"Created {len(self.eval_data)} sample evaluation examples")
291
 
292
+ @spaces.GPU
293
  def retrieve_context(self, query, lang):
294
+ """Retrieve relevant context for a query based on language with GPU support"""
295
  start_time = time.time()
296
 
297
  try:
298
  if lang == "ar":
299
+ if has_gpu and hasattr(self.arabic_embedder, 'to') and callable(getattr(self.arabic_embedder, 'to')):
300
+ with torch.no_grad():
301
+ query_vec = self.arabic_embedder.encode(query)
302
+ else:
303
+ query_vec = self.arabic_embedder.encode(query)
304
+
305
  D, I = self.arabic_index.search(np.array([query_vec]), k=2) # Get top 2 most relevant chunks
306
  context = "\n".join([self.arabic_texts[i] for i in I[0] if i < len(self.arabic_texts) and i >= 0])
307
  else:
308
+ if has_gpu and hasattr(self.english_embedder, 'to') and callable(getattr(self.english_embedder, 'to')):
309
+ with torch.no_grad():
310
+ query_vec = self.english_embedder.encode(query)
311
+ else:
312
+ query_vec = self.english_embedder.encode(query)
313
+
314
  D, I = self.english_index.search(np.array([query_vec]), k=2) # Get top 2 most relevant chunks
315
  context = "\n".join([self.english_texts[i] for i in I[0] if i < len(self.english_texts) and i >= 0])
316
 
 
420
 
421
  return accuracy
422
 
423
+ @spaces.GPU
424
  def evaluate_on_test_set(self):
425
+ """Evaluate the assistant on the test set with GPU support"""
426
  logger.info("Running evaluation on test set")
427
 
428
  eval_results = []
 
518
 
519
  # Create the Gradio interface
520
  def create_gradio_interface():
521
+ try:
522
+ # Initialize the assistant
523
+ assistant = Vision2030Assistant()
 
 
 
524
 
525
+ def chat(message, history):
526
+ if not message.strip():
527
+ return history, ""
528
+
529
+ # Generate response
530
+ reply = assistant.generate_response(message)
531
+
532
+ # Update history
533
+ history.append((message, reply))
534
+
535
+ return history, ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
536
 
537
+ def provide_feedback(history, rating, feedback_text):
538
+ # Record feedback for the last conversation
539
+ if history and len(history) > 0:
540
+ last_interaction = history[-1]
541
+ assistant.record_user_feedback(last_interaction[0], last_interaction[1], rating, feedback_text)
542
+ return f"Thank you for your feedback! (Rating: {rating}/5)"
543
+ return "No conversation found to rate."
544
+
545
+ @spaces.GPU
546
+ def run_evaluation():
547
+ results = assistant.evaluate_on_test_set()
548
+
549
+ # Create summary text
550
+ summary = f"""
551
+ Evaluation Results:
552
+ ------------------
553
+ Total questions evaluated: {len(results['detailed_results'])}
554
+ Overall factual accuracy: {results['average_factual_accuracy']:.2f}
555
+ Average response time: {results['average_response_time']:.4f} seconds
556
+
557
+ Detailed Results:
558
+ """
559
+
560
+ for i, result in enumerate(results['detailed_results']):
561
+ summary += f"\nQ{i+1}: {result['question']}\n"
562
+ summary += f"Reference: {result['reference']}\n"
563
+ summary += f"Response: {result['response']}\n"
564
+ summary += f"Accuracy: {result['factual_accuracy']:.2f}\n"
565
+ summary += "-" * 40 + "\n"
566
+
567
+ # Return both the results summary and visualization
568
+ fig = assistant.visualize_evaluation_results(results)
569
+
570
+ return summary, fig
571
 
572
+ @spaces.GPU
573
+ def process_uploaded_file(file):
574
+ if file is not None:
575
+ # Create a new assistant with the uploaded PDF
576
+ global assistant
577
+ assistant = Vision2030Assistant(pdf_path=file.name)
578
+ return f"Successfully processed {file.name}. The assistant is ready to use."
579
+ return "No file uploaded. Using sample data."
580
+
581
+ # Create the Gradio interface
582
+ with gr.Blocks() as demo:
583
+ gr.Markdown("# Vision 2030 Virtual Assistant 🌟")
584
+ gr.Markdown("Ask questions about Saudi Arabia's Vision 2030 in both Arabic and English")
585
+
586
+ with gr.Tab("Chat"):
587
+ chatbot = gr.Chatbot(height=400)
588
+ msg = gr.Textbox(label="Your Question", placeholder="Ask about Vision 2030...")
589
+ with gr.Row():
590
+ submit_btn = gr.Button("Submit")
591
+ clear_btn = gr.Button("Clear Chat")
592
+
593
+ gr.Markdown("### Provide Feedback")
594
+ with gr.Row():
595
+ rating = gr.Slider(minimum=1, maximum=5, step=1, value=3, label="Rate the Response (1-5)")
596
+ feedback_text = gr.Textbox(label="Additional Comments (Optional)")
597
+ feedback_btn = gr.Button("Submit Feedback")
598
+ feedback_result = gr.Textbox(label="Feedback Status")
599
+
600
+ with gr.Tab("Evaluation"):
601
+ evaluate_btn = gr.Button("Run Evaluation on Test Set")
602
+ eval_output = gr.Textbox(label="Evaluation Results", lines=20)
603
+ eval_chart = gr.Plot(label="Evaluation Metrics")
604
+
605
+ with gr.Tab("Upload PDF"):
606
+ file_input = gr.File(label="Upload Vision 2030 PDF")
607
+ upload_result = gr.Textbox(label="Upload Status")
608
+ upload_btn = gr.Button("Process PDF")
609
+
610
+ # Set up event handlers
611
+ msg.submit(chat, [msg, chatbot], [chatbot, msg])
612
+ submit_btn.click(chat, [msg, chatbot], [chatbot, msg])
613
+ clear_btn.click(lambda: [], None, chatbot)
614
+ feedback_btn.click(provide_feedback, [chatbot, rating, feedback_text], feedback_result)
615
+ evaluate_btn.click(run_evaluation, None, [eval_output, eval_chart])
616
+ upload_btn.click(process_uploaded_file, [file_input], upload_result)
617
+
618
+ return demo
619
+ except Exception as e:
620
+ logger.error(f"Error creating Gradio interface: {str(e)}")
621
+ # Create a simple demo for fallback
622
+ with gr.Blocks() as demo:
623
+ gr.Markdown("# Vision 2030 Virtual Assistant")
624
+ gr.Markdown("There was an error initializing the assistant. Please check the logs.")
625
+ gr.Markdown(f"Error: {str(e)}")
626
+ return demo
627
 
628
+ # Launch the app with proper GPU initialization
629
  demo = create_gradio_interface()
630
  demo.launch()