abdull4h commited on
Commit
531fbee
·
verified ·
1 Parent(s): 8607988

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +85 -729
app.py CHANGED
@@ -1,760 +1,116 @@
1
  import os
2
- import re
3
- import json
4
- import torch
5
- import numpy as np
6
- import pandas as pd
7
- from tqdm import tqdm
8
- from pathlib import Path
9
- import spaces # Import spaces for GPU allocation
10
-
11
- # PDF processing
12
- import PyPDF2
13
-
14
- # LLM and embeddings
15
- from transformers import AutoTokenizer, AutoModelForCausalLM
16
- from sentence_transformers import SentenceTransformer
17
-
18
- # RAG components
19
- from langchain.text_splitter import RecursiveCharacterTextSplitter
20
- from langchain_community.vectorstores import FAISS
21
- from langchain.schema import Document
22
- from langchain.embeddings import HuggingFaceEmbeddings
23
-
24
- # Arabic text processing
25
- import arabic_reshaper
26
- from bidi.algorithm import get_display
27
-
28
- # Evaluation
29
- from rouge_score import rouge_scorer
30
- import sacrebleu
31
- from sklearn.metrics import accuracy_score, precision_recall_fscore_support
32
- import matplotlib.pyplot as plt
33
- import seaborn as sns
34
- from collections import defaultdict
35
-
36
- # Gradio for the interface
37
  import gradio as gr
38
 
39
- # Helper functions
40
- def safe_tokenize(text):
41
- """Pure regex tokenizer with no NLTK dependency"""
42
- if not text:
43
- return []
44
- # Replace punctuation with spaces around them
45
- text = re.sub(r'([.,!?;:()\[\]{}"\'/\\])', r' \1 ', text)
46
- # Split on whitespace and filter empty strings
47
- return [token for token in re.split(r'\s+', text.lower()) if token]
48
-
49
- def detect_language(text):
50
- """Detect if text is primarily Arabic or English"""
51
- # Simple heuristic: count Arabic characters
52
- arabic_chars = re.findall(r'[\u0600-\u06FF]', text)
53
- is_arabic = len(arabic_chars) > len(text) * 0.5
54
- return "arabic" if is_arabic else "english"
55
-
56
- # Evaluation metrics
57
- def calculate_bleu(prediction, reference):
58
- """Calculate BLEU score without any NLTK dependency"""
59
- # Tokenize texts using our own tokenizer
60
- pred_tokens = safe_tokenize(prediction.lower())
61
- ref_tokens = [safe_tokenize(reference.lower())]
62
-
63
- # If either is empty, return 0
64
- if not pred_tokens or not ref_tokens[0]:
65
- return {"bleu_1": 0, "bleu_2": 0, "bleu_4": 0}
66
-
67
- # Get n-grams function
68
- def get_ngrams(tokens, n):
69
- return [tuple(tokens[i:i+n]) for i in range(len(tokens) - n + 1)]
70
-
71
- # Calculate precision for each n-gram level
72
- precisions = []
73
- for n in range(1, 5): # 1-gram to 4-gram
74
- if len(pred_tokens) < n:
75
- precisions.append(0)
76
- continue
77
-
78
- pred_ngrams = get_ngrams(pred_tokens, n)
79
- ref_ngrams = get_ngrams(ref_tokens[0], n)
80
-
81
- # Count matches
82
- matches = sum(1 for ng in pred_ngrams if ng in ref_ngrams)
83
-
84
- # Calculate precision
85
- if pred_ngrams:
86
- precisions.append(matches / len(pred_ngrams))
87
- else:
88
- precisions.append(0)
89
-
90
- # Return BLEU scores
91
- return {
92
- "bleu_1": precisions[0],
93
- "bleu_2": (precisions[0] * precisions[1]) ** 0.5 if len(precisions) > 1 else 0,
94
- "bleu_4": (precisions[0] * precisions[1] * precisions[2] * precisions[3]) ** 0.25 if len(precisions) > 3 else 0
95
- }
96
-
97
- def calculate_meteor(prediction, reference):
98
- """Simple word overlap metric as METEOR alternative"""
99
- # Tokenize with our custom tokenizer
100
- pred_tokens = set(safe_tokenize(prediction.lower()))
101
- ref_tokens = set(safe_tokenize(reference.lower()))
102
-
103
- # Calculate Jaccard similarity as METEOR alternative
104
- if not pred_tokens or not ref_tokens:
105
- return 0
106
-
107
- intersection = len(pred_tokens.intersection(ref_tokens))
108
- union = len(pred_tokens.union(ref_tokens))
109
-
110
- return intersection / union if union > 0 else 0
111
-
112
- def calculate_f1_precision_recall(prediction, reference):
113
- """Calculate word-level F1, precision, and recall with custom tokenizer"""
114
- # Tokenize with our custom tokenizer
115
- pred_tokens = set(safe_tokenize(prediction.lower()))
116
- ref_tokens = set(safe_tokenize(reference.lower()))
117
-
118
- # Calculate overlap
119
- common = pred_tokens.intersection(ref_tokens)
120
-
121
- # Calculate precision, recall, F1
122
- precision = len(common) / len(pred_tokens) if pred_tokens else 0
123
- recall = len(common) / len(ref_tokens) if ref_tokens else 0
124
- f1 = 2 * precision * recall / (precision + recall) if (precision + recall) else 0
125
-
126
- return {'precision': precision, 'recall': recall, 'f1': f1}
127
-
128
- def evaluate_retrieval_quality(contexts, query, language):
129
- """Evaluate the quality of retrieved contexts"""
130
- # This is a placeholder function that should be implemented based on
131
- # how you want to evaluate retrieval quality
132
- return {
133
- 'language_match_ratio': 1.0, # Placeholder
134
- 'source_diversity': len(set([ctx.get('source', '') for ctx in contexts])) / max(1, len(contexts)),
135
- 'mrr': 1.0 # Placeholder for Mean Reciprocal Rank
136
- }
137
-
138
- # PDF Processing and Vector Store
139
- def simple_process_pdfs(pdf_paths):
140
- """Process PDF documents and return document objects"""
141
- documents = []
142
-
143
- print(f"Attempting to process PDFs: {pdf_paths}")
144
- print(f"Current directory contents: {os.listdir('.')}")
145
-
146
- for pdf_path in pdf_paths:
147
- try:
148
- if not os.path.exists(pdf_path):
149
- print(f"Warning: {pdf_path} does not exist")
150
- continue
151
-
152
- print(f"Processing {pdf_path}...")
153
- text = ""
154
- with open(pdf_path, 'rb') as file:
155
- reader = PyPDF2.PdfReader(file)
156
- for page in reader.pages:
157
- page_text = page.extract_text()
158
- if page_text: # If we got text from this page
159
- text += page_text + "\n\n"
160
-
161
- if text.strip(): # If we got some text
162
- doc = Document(
163
- page_content=text,
164
- metadata={"source": pdf_path, "filename": os.path.basename(pdf_path)}
165
- )
166
- documents.append(doc)
167
- print(f"Successfully processed: {pdf_path}")
168
- else:
169
- print(f"Warning: No text extracted from {pdf_path}")
170
- except Exception as e:
171
- print(f"Error processing {pdf_path}: {e}")
172
- import traceback
173
- traceback.print_exc()
174
-
175
- print(f"Processed {len(documents)} PDF documents")
176
- return documents
177
-
178
- def create_vector_store(documents):
179
- """Split documents into chunks and create a FAISS vector store"""
180
- # Text splitter for breaking documents into chunks
181
- text_splitter = RecursiveCharacterTextSplitter(
182
- chunk_size=500,
183
- chunk_overlap=50,
184
- separators=["\n\n", "\n", ".", "!", "?", ",", " ", ""]
185
- )
186
-
187
- # Split documents into chunks
188
- chunks = []
189
- for doc in documents:
190
- doc_chunks = text_splitter.split_text(doc.page_content)
191
- # Preserve metadata for each chunk
192
- chunks.extend([
193
- Document(page_content=chunk, metadata=doc.metadata)
194
- for chunk in doc_chunks
195
- ])
196
-
197
- print(f"Created {len(chunks)} chunks from {len(documents)} documents")
198
-
199
- # Create a proper embedding function for LangChain
200
- embedding_function = HuggingFaceEmbeddings(
201
- model_name="sentence-transformers/paraphrase-multilingual-mpnet-base-v2"
202
- )
203
-
204
- # Create FAISS index
205
- vector_store = FAISS.from_documents(
206
- chunks,
207
- embedding_function
208
- )
209
-
210
- return vector_store
211
-
212
- # Model Loading and RAG System
213
- @spaces.GPU # Use GPU for model loading
214
- def load_model_and_tokenizer():
215
- """Load the ALLaM-7B model and tokenizer with error handling"""
216
- model_name = "ALLaM-AI/ALLaM-7B-Instruct-preview"
217
- print(f"Loading model: {model_name}")
218
-
219
  try:
220
- # First attempt with AutoTokenizer
221
- tokenizer = AutoTokenizer.from_pretrained(
222
- model_name,
223
- trust_remote_code=True,
224
- use_fast=False
225
- )
226
-
227
- # Load model with appropriate settings for ALLaM
228
- model = AutoModelForCausalLM.from_pretrained(
229
- model_name,
230
- torch_dtype=torch.bfloat16, # Use bfloat16 for better compatibility
231
- trust_remote_code=True,
232
- device_map="auto",
233
- )
234
-
235
- print("Model loaded successfully with AutoTokenizer!")
236
-
237
  except Exception as e:
238
- print(f"First loading attempt failed: {e}")
239
- print("Trying alternative loading approach...")
240
-
241
- # Try with specific tokenizer class if the first attempt fails
242
- from transformers import LlamaTokenizer
243
-
244
- tokenizer = LlamaTokenizer.from_pretrained(model_name)
245
- model = AutoModelForCausalLM.from_pretrained(
246
- model_name,
247
- torch_dtype=torch.float16,
248
- trust_remote_code=True,
249
- device_map="auto",
250
- )
251
-
252
- print("Model loaded successfully with LlamaTokenizer!")
253
-
254
- return model, tokenizer
255
 
256
- def retrieve_context(query, vector_store, top_k=5):
257
- """Retrieve most relevant document chunks for a given query"""
258
- # Search the vector store using similarity search
259
- results = vector_store.similarity_search_with_score(query, k=top_k)
260
-
261
- # Format the retrieved contexts
262
- contexts = []
263
- for doc, score in results:
264
- contexts.append({
265
- "content": doc.page_content,
266
- "source": doc.metadata.get("source", "Unknown"),
267
- "relevance_score": score
268
- })
269
-
270
- return contexts
271
-
272
- @spaces.GPU # Use GPU for text generation
273
- def generate_response(query, contexts, model, tokenizer, language="auto"):
274
- """Generate a response using retrieved contexts with ALLaM-specific formatting"""
275
- # Auto-detect language if not specified
276
- if language == "auto":
277
- language = detect_language(query)
278
-
279
- # Format the prompt based on language
280
- if language == "arabic":
281
- instruction = (
282
- "أنت مساعد افتراضي يهتم برؤية السعودية 2030. استخدم المعلومات التالية للإجابة على السؤال. "
283
- "إذا لم تعرف الإجابة، فقل بأمانة إنك لا تعرف."
284
- )
285
- else: # english
286
- instruction = (
287
- "You are a virtual assistant for Saudi Vision 2030. Use the following information to answer the question. "
288
- "If you don't know the answer, honestly say you don't know."
289
- )
290
 
291
- # Combine retrieved contexts
292
- context_text = "\n\n".join([f"Document: {ctx['content']}" for ctx in contexts])
293
-
294
- # Format the prompt for ALLaM instruction format
295
- prompt = f"""<s>[INST] {instruction}
296
-
297
- Context:
298
- {context_text}
299
-
300
- Question: {query} [/INST]</s>"""
301
 
 
302
  try:
303
- # Generate response with appropriate parameters for ALLaM
304
- inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
305
-
306
- # Generate with appropriate parameters
307
- outputs = model.generate(
308
- inputs.input_ids,
309
- attention_mask=inputs.attention_mask,
310
- max_new_tokens=512,
311
- temperature=0.7,
312
- top_p=0.9,
313
- do_sample=True,
314
- repetition_penalty=1.1
315
- )
316
-
317
- # Decode the response
318
- full_output = tokenizer.decode(outputs[0], skip_special_tokens=True)
319
-
320
- # Extract just the answer part (after the instruction)
321
- response = full_output.split("[/INST]")[-1].strip()
322
-
323
- # If response is empty for some reason, return the full output
324
- if not response:
325
- response = full_output
326
-
327
- return response
328
-
329
  except Exception as e:
330
- print(f"Error during generation: {e}")
331
- # Fallback response
332
- return "I apologize, but I encountered an error while generating a response."
333
-
334
- # Assistant Class
335
- class Vision2030Assistant:
336
- def __init__(self, model, tokenizer, vector_store):
337
- self.model = model
338
- self.tokenizer = tokenizer
339
- self.vector_store = vector_store
340
- self.conversation_history = []
341
-
342
- def answer(self, user_query):
343
- """Process a user query and return a response with sources"""
344
- # Detect language
345
- language = detect_language(user_query)
346
-
347
- # Add user query to conversation history
348
- self.conversation_history.append({"role": "user", "content": user_query})
349
-
350
- # Get the full conversation context
351
- conversation_context = "\n".join([
352
- f"{'User' if msg['role'] == 'user' else 'Assistant'}: {msg['content']}"
353
- for msg in self.conversation_history[-6:] # Keep last 3 turns (6 messages)
354
- ])
355
-
356
- # Enhance query with conversation context for better retrieval
357
- enhanced_query = f"{conversation_context}\n{user_query}"
358
-
359
- # Retrieve relevant contexts
360
- contexts = retrieve_context(enhanced_query, self.vector_store, top_k=5)
361
-
362
- # Generate response
363
- response = generate_response(user_query, contexts, self.model, self.tokenizer, language)
364
-
365
- # Add response to conversation history
366
- self.conversation_history.append({"role": "assistant", "content": response})
367
-
368
- # Also return sources for transparency
369
- sources = [ctx.get("source", "Unknown") for ctx in contexts]
370
- unique_sources = list(set(sources))
371
-
372
- return response, unique_sources, contexts
373
 
374
- def reset_conversation(self):
375
- """Reset the conversation history"""
376
- self.conversation_history = []
377
- return "Conversation has been reset."
378
-
379
- # Comprehensive evaluation dataset
380
- comprehensive_evaluation_data = [
381
- # === Overview ===
382
- {
383
- "query": "ما هي رؤية السعودية 2030؟",
384
- "reference": "رؤية السعودية 2030 هي خطة استراتيجية ته��ف إلى تنويع الاقتصاد السعودي وتقليل الاعتماد على النفط مع تطوير قطاعات مختلفة مثل الصحة والتعليم والسياحة.",
385
- "category": "overview",
386
- "language": "arabic"
387
- },
388
- {
389
- "query": "What is Saudi Vision 2030?",
390
- "reference": "Saudi Vision 2030 is a strategic framework aiming to diversify Saudi Arabia's economy and reduce dependence on oil, while developing sectors like health, education, and tourism.",
391
- "category": "overview",
392
- "language": "english"
393
- },
394
-
395
- # === Economic Goals ===
396
- {
397
- "query": "ما هي الأهداف الاقتصادية لرؤية 2030؟",
398
- "reference": "تشمل الأهداف الاقتصادية زيادة مساهمة القطاع الخاص إلى 65%، وزيادة الصادرات غير النفطية إلى 50% من الناتج المحلي غير النفطي، وخفض البطالة إلى 7%.",
399
- "category": "economic",
400
- "language": "arabic"
401
- },
402
- {
403
- "query": "What are the economic goals of Vision 2030?",
404
- "reference": "The economic goals of Vision 2030 include increasing private sector contribution from 40% to 65% of GDP, raising non-oil exports from 16% to 50%, reducing unemployment from 11.6% to 7%.",
405
- "category": "economic",
406
- "language": "english"
407
- },
408
 
409
- # === Social Goals ===
410
- {
411
- "query": "كيف تعزز رؤية 2030 الإرث الثقافي السعودي؟",
412
- "reference": "تتضمن رؤية 2030 الحفاظ على الهوية الوطنية، تسجيل مواقع أثرية في اليونسكو، وتعزيز الفعاليات الثقافية.",
413
- "category": "social",
414
- "language": "arabic"
415
- },
416
- {
417
- "query": "How does Vision 2030 aim to improve quality of life?",
418
- "reference": "Vision 2030 plans to enhance quality of life by expanding sports facilities, promoting cultural activities, and boosting tourism and entertainment sectors.",
419
- "category": "social",
420
- "language": "english"
421
- }
422
- ]
423
 
424
- # Gradio Interface
425
- def initialize_system():
426
- """Initialize the Vision 2030 Assistant system"""
427
- # Define paths for PDF files in the root directory
428
- pdf_files = ["saudi_vision203.pdf", "saudi_vision2030_ar.pdf"]
429
-
430
- # Print available files for debugging
431
- print("Files in current directory:", os.listdir("."))
432
 
433
- # Check if PDFs exist
434
- for pdf_file in pdf_files:
435
  if not os.path.exists(pdf_file):
436
- print(f"Warning: {pdf_file} not found")
437
-
438
- # Process PDFs and create vector store
439
- vector_store_dir = "vector_stores"
440
- os.makedirs(vector_store_dir, exist_ok=True)
441
-
442
- if os.path.exists(os.path.join(vector_store_dir, "index.faiss")):
443
- print("Loading existing vector store...")
444
- embedding_function = HuggingFaceEmbeddings(
445
- model_name="sentence-transformers/paraphrase-multilingual-mpnet-base-v2"
446
- )
447
- vector_store = FAISS.load_local(vector_store_dir, embedding_function)
448
- else:
449
- print("Creating new vector store...")
450
- documents = simple_process_pdfs(pdf_files)
451
- if not documents:
452
- raise ValueError("No documents were processed successfully. Cannot continue.")
453
- vector_store = create_vector_store(documents)
454
- vector_store.save_local(vector_store_dir)
455
-
456
- # Load model and tokenizer
457
- model, tokenizer = load_model_and_tokenizer()
458
-
459
- # Initialize assistant
460
- assistant = Vision2030Assistant(model, tokenizer, vector_store)
461
-
462
- return assistant
463
-
464
- def evaluate_response(query, response, reference):
465
- """Evaluate a single response against a reference"""
466
- # Calculate metrics
467
- rouge = rouge_scorer.RougeScorer(['rouge1', 'rouge2', 'rougeL'], use_stemmer=True)
468
- rouge_scores = rouge.score(response, reference)
469
-
470
- bleu_scores = calculate_bleu(response, reference)
471
- meteor = calculate_meteor(response, reference)
472
- word_metrics = calculate_f1_precision_recall(response, reference)
473
-
474
- # Format results
475
- evaluation_results = {
476
- "ROUGE-1": f"{rouge_scores['rouge1'].fmeasure:.4f}",
477
- "ROUGE-2": f"{rouge_scores['rouge2'].fmeasure:.4f}",
478
- "ROUGE-L": f"{rouge_scores['rougeL'].fmeasure:.4f}",
479
- "BLEU-1": f"{bleu_scores['bleu_1']:.4f}",
480
- "BLEU-4": f"{bleu_scores['bleu_4']:.4f}",
481
- "METEOR": f"{meteor:.4f}",
482
- "Word Precision": f"{word_metrics['precision']:.4f}",
483
- "Word Recall": f"{word_metrics['recall']:.4f}",
484
- "Word F1": f"{word_metrics['f1']:.4f}"
485
- }
486
 
487
- return evaluation_results
488
 
489
- @spaces.GPU # Use GPU for conversation handling
490
- def run_conversation(assistant, query):
491
- """Run a query through the assistant and return the response"""
492
- response, sources, contexts = assistant.answer(query)
493
- return response, sources, contexts
494
-
495
- @spaces.GPU # Use GPU for evaluation
496
- def run_evaluation_on_sample(assistant, sample_index=0):
497
- """Run evaluation on a selected sample from the evaluation dataset"""
498
- if sample_index < 0 or sample_index >= len(comprehensive_evaluation_data):
499
- return "Invalid sample index", "", "", {}
500
-
501
- # Get the sample
502
- sample = comprehensive_evaluation_data[sample_index]
503
- query = sample["query"]
504
- reference = sample["reference"]
505
- category = sample["category"]
506
- language = sample["language"]
507
 
508
- # Reset conversation and get response
509
- assistant.reset_conversation()
510
- response, sources, contexts = assistant.answer(query)
511
 
512
- # Evaluate response
513
- evaluation_results = evaluate_response(query, response, reference)
 
 
 
 
 
 
514
 
515
- return query, response, reference, evaluation_results, sources, category, language
516
 
517
- def qualitative_evaluation_interface(assistant=None):
518
- """Create a Gradio interface for qualitative evaluation"""
519
-
520
- # If assistant is None, create a simplified interface
521
- if assistant is None:
522
- with gr.Blocks(title="Vision 2030 Assistant - Initialization Error") as interface:
523
- gr.Markdown("# Vision 2030 Assistant - Initialization Error")
524
- gr.Markdown("There was an error initializing the assistant. Please check the logs for details.")
525
- gr.Textbox(label="Status", value="System initialization failed")
526
- return interface
527
-
528
- sample_options = [f"{i+1}. {item['query'][:50]}..." for i, item in enumerate(comprehensive_evaluation_data)]
529
-
530
- with gr.Blocks(title="Vision 2030 Assistant - Qualitative Evaluation") as interface:
531
- gr.Markdown("# Vision 2030 Assistant - Qualitative Evaluation")
532
- gr.Markdown("This interface allows you to evaluate the Vision 2030 Assistant on predefined samples or your own queries.")
533
-
534
- with gr.Tab("Sample Evaluation"):
535
- gr.Markdown("### Evaluate the assistant on predefined samples")
536
-
537
- sample_dropdown = gr.Dropdown(
538
- choices=sample_options,
539
- label="Select a sample query",
540
- value=sample_options[0] if sample_options else None
541
- )
542
-
543
- eval_button = gr.Button("Evaluate Sample")
544
-
545
- with gr.Row():
546
- with gr.Column():
547
- sample_query = gr.Textbox(label="Query")
548
- sample_category = gr.Textbox(label="Category")
549
- sample_language = gr.Textbox(label="Language")
550
-
551
- with gr.Column():
552
- sample_response = gr.Textbox(label="Assistant Response")
553
- sample_reference = gr.Textbox(label="Reference Answer")
554
- sample_sources = gr.Textbox(label="Sources Used")
555
-
556
- with gr.Row():
557
- metrics_display = gr.JSON(label="Evaluation Metrics")
558
-
559
- with gr.Tab("Custom Evaluation"):
560
- gr.Markdown("### Evaluate the assistant on your own query")
561
-
562
- custom_query = gr.Textbox(
563
- lines=3,
564
- placeholder="Enter your question about Saudi Vision 2030...",
565
- label="Your Query"
566
- )
567
-
568
- custom_reference = gr.Textbox(
569
- lines=3,
570
- placeholder="Enter a reference answer (optional)...",
571
- label="Reference Answer (Optional)"
572
- )
573
-
574
- custom_eval_button = gr.Button("Get Response and Evaluate")
575
-
576
- custom_response = gr.Textbox(label="Assistant Response")
577
- custom_sources = gr.Textbox(label="Sources Used")
578
-
579
- custom_metrics = gr.JSON(
580
- label="Evaluation Metrics (if reference provided)",
581
- visible=True
582
- )
583
 
584
- with gr.Tab("Conversation Mode"):
585
- gr.Markdown("### Have a conversation with the Vision 2030 Assistant")
586
-
587
- chatbot = gr.Chatbot(label="Conversation")
588
 
589
- conv_input = gr.Textbox(
590
- placeholder="Ask about Saudi Vision 2030...",
591
- label="Your message"
592
- )
593
-
594
- with gr.Row():
595
- conv_button = gr.Button("Send")
596
- reset_button = gr.Button("Reset Conversation")
597
-
598
- conv_sources = gr.Textbox(label="Sources Used")
599
 
600
- # Sample evaluation event handlers
601
- def handle_sample_selection(selection):
602
- if not selection:
603
- return "", "", "", "", "", "", ""
604
 
605
- # Extract index from the selection string
606
- try:
607
- index = int(selection.split(".")[0]) - 1
608
- query, response, reference, metrics, sources, category, language = run_evaluation_on_sample(assistant, index)
609
- sources_str = ", ".join(sources)
610
- return query, response, reference, metrics, sources_str, category, language
611
- except Exception as e:
612
- print(f"Error in handle_sample_selection: {e}")
613
- import traceback
614
- traceback.print_exc()
615
- return f"Error processing selection: {e}", "", "", {}, "", "", ""
616
-
617
- eval_button.click(
618
- handle_sample_selection,
619
- inputs=[sample_dropdown],
620
- outputs=[sample_query, sample_response, sample_reference, metrics_display,
621
- sample_sources, sample_category, sample_language]
622
- )
623
-
624
- sample_dropdown.change(
625
- handle_sample_selection,
626
- inputs=[sample_dropdown],
627
- outputs=[sample_query, sample_response, sample_reference, metrics_display,
628
- sample_sources, sample_category, sample_language]
629
- )
630
 
631
- # Custom evaluation event handlers
632
- @spaces.GPU # Use GPU for custom evaluation
633
- def handle_custom_evaluation(query, reference):
634
- if not query:
635
- return "Please enter a query", "", {}
636
-
637
- # Reset conversation to ensure clean state
638
- assistant.reset_conversation()
639
-
640
- # Get response
641
- response, sources, _ = assistant.answer(query)
642
- sources_str = ", ".join(sources)
643
-
644
- # Evaluate if reference is provided
645
- metrics = {}
646
- if reference:
647
- metrics = evaluate_response(query, response, reference)
648
-
649
- return response, sources_str, metrics
650
-
651
- custom_eval_button.click(
652
- handle_custom_evaluation,
653
- inputs=[custom_query, custom_reference],
654
- outputs=[custom_response, custom_sources, custom_metrics]
655
- )
656
-
657
- # Conversation mode event handlers
658
- @spaces.GPU # Use GPU for conversation handling
659
- def handle_conversation(message, history):
660
- if not message:
661
- return history, "", ""
662
-
663
- # Get response
664
- response, sources, _ = assistant.answer(message)
665
- sources_str = ", ".join(sources)
666
-
667
- # Update history
668
- history = history + [[message, response]]
669
 
670
- return history, "", sources_str
671
-
672
- def reset_conv():
673
- result = assistant.reset_conversation()
674
- return [], result, ""
675
-
676
- conv_button.click(
677
- handle_conversation,
678
- inputs=[conv_input, chatbot],
679
- outputs=[chatbot, conv_input, conv_sources]
680
- )
681
-
682
- reset_button.click(
683
- reset_conv,
684
- inputs=[],
685
- outputs=[chatbot, conv_input, conv_sources]
686
- )
687
-
688
- return interface
689
-
690
- # Main function to run in Hugging Face Space
691
- def main():
692
- # Start with a debugging report
693
- print("=" * 50)
694
- print("SYSTEM INITIALIZATION")
695
- print("=" * 50)
696
- print("Current directory:", os.getcwd())
697
- print("Files in directory:", os.listdir("."))
698
- print("=" * 50)
699
 
700
- # Initialize the system
701
- try:
702
- # First check if PDF files exist
703
- pdf_files = ["saudi_vision203.pdf", "saudi_vision2030_ar.pdf"]
704
- for pdf_file in pdf_files:
705
- if not os.path.exists(pdf_file):
706
- print(f"Warning: {pdf_file} not found!")
707
-
708
- # Process with initialization
709
- print("Starting system initialization...")
710
- assistant = initialize_system()
711
-
712
- print("Creating interface...")
713
- interface = qualitative_evaluation_interface(assistant)
714
-
715
- print("Launching interface...")
716
- interface.launch()
717
- except Exception as e:
718
- print(f"Error during initialization: {e}")
719
- import traceback
720
- traceback.print_exc()
721
-
722
- # Create a simple error interface
723
- with gr.Blocks(title="Vision 2030 Assistant - Error") as debug_interface:
724
- gr.Markdown("# Vision 2030 Assistant - Initialization Error")
725
- gr.Markdown("There was an error initializing the assistant.")
726
-
727
- # Display error details
728
- gr.Textbox(
729
- value=f"Error: {str(e)}",
730
- label="Error Details",
731
- lines=5
732
- )
733
-
734
- # Show file system status
735
- files_list = "\n".join(os.listdir("."))
736
- gr.Textbox(
737
- value=files_list,
738
- label="Files in Directory",
739
- lines=10
740
- )
741
-
742
- # Add a button to check PDFs
743
- def check_pdfs():
744
- result = []
745
- for pdf_file in ["saudi_vision203.pdf", "saudi_vision2030_ar.pdf"]:
746
- if os.path.exists(pdf_file):
747
- size = os.path.getsize(pdf_file) / (1024 * 1024) # Size in MB
748
- result.append(f"{pdf_file}: Found ({size:.2f} MB)")
749
- else:
750
- result.append(f"{pdf_file}: Not found")
751
- return "\n".join(result)
752
-
753
- check_btn = gr.Button("Check PDF Files")
754
- pdf_status = gr.Textbox(label="PDF Status", lines=3)
755
- check_btn.click(check_pdfs, inputs=[], outputs=[pdf_status])
756
-
757
- debug_interface.launch()
758
 
759
  if __name__ == "__main__":
760
  main()
 
1
  import os
2
+ import sys
3
+ import traceback
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  import gradio as gr
5
 
6
+ def import_with_error_tracking(module_name):
7
+ """Try to import a module and return detailed error info if it fails"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  try:
9
+ module = __import__(module_name)
10
+ return True, f"Successfully imported {module_name}"
11
+ except ImportError as e:
12
+ return False, f"Failed to import {module_name}: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  except Exception as e:
14
+ return False, f"Error importing {module_name}: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
+ def test_imports():
17
+ # Create a report of all import attempts
18
+ results = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
+ # Core libraries
21
+ for lib in ["torch", "numpy", "pandas", "tqdm", "PyPDF2", "transformers",
22
+ "sentence_transformers", "langchain", "langchain_community",
23
+ "arabic_reshaper", "bidi", "rouge_score", "sacrebleu", "spaces"]:
24
+ success, message = import_with_error_tracking(lib)
25
+ results.append(f"{'✓' if success else '✗'} {message}")
 
 
 
 
26
 
27
+ # Check if specific model is available
28
  try:
29
+ from transformers import AutoTokenizer
30
+ tokenizer = AutoTokenizer.from_pretrained("ALLaM-AI/ALLaM-7B-Instruct-preview", trust_remote_code=True)
31
+ results.append("✓ Model tokenizer accessible")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  except Exception as e:
33
+ results.append(f" Model access error: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
+ # Test vector store
36
+ try:
37
+ from langchain.embeddings import HuggingFaceEmbeddings
38
+ from langchain_community.vectorstores import FAISS
39
+ results.append("✓ Vector store components accessible")
40
+ except Exception as e:
41
+ results.append(f"✗ Vector store error: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
+ return "\n".join(results)
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
+ def check_pdfs():
46
+ """Check if PDF files are readable"""
47
+ results = []
 
 
 
 
 
48
 
49
+ for pdf_file in ["saudi_vision203.pdf", "saudi_vision2030_ar.pdf"]:
 
50
  if not os.path.exists(pdf_file):
51
+ results.append(f"{pdf_file}: Not found")
52
+ continue
53
+
54
+ size = os.path.getsize(pdf_file) / (1024 * 1024) # Size in MB
55
+ results.append(f"{pdf_file}: Found ({size:.2f} MB)")
56
+
57
+ # Try to open and read the file
58
+ try:
59
+ import PyPDF2
60
+ with open(pdf_file, 'rb') as f:
61
+ reader = PyPDF2.PdfReader(f)
62
+ num_pages = len(reader.pages)
63
+ text_sample = reader.pages[0].extract_text()[:100] + "..."
64
+ results.append(f"- Pages: {num_pages}")
65
+ results.append(f"- Sample text: {text_sample}")
66
+ except Exception as e:
67
+ results.append(f"- Error reading file: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
+ return "\n".join(results)
70
 
71
+ def check_environment():
72
+ """Get information about the Python environment"""
73
+ results = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
+ results.append(f"Python version: {sys.version}")
76
+ results.append(f"Python executable: {sys.executable}")
77
+ results.append(f"Working directory: {os.getcwd()}")
78
 
79
+ # List all installed packages
80
+ try:
81
+ import pkg_resources
82
+ installed_packages = [f"{pkg.key}=={pkg.version}" for pkg in pkg_resources.working_set]
83
+ results.append(f"Installed packages ({len(installed_packages)}):")
84
+ results.append("\n".join(installed_packages))
85
+ except:
86
+ results.append("Could not list installed packages")
87
 
88
+ return "\n".join(results)
89
 
90
+ def main():
91
+ with gr.Blocks(title="Vision 2030 Assistant - Debug Mode") as interface:
92
+ gr.Markdown("# Vision 2030 Assistant - Debug Mode")
93
+ gr.Markdown("This interface helps identify import and initialization issues.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
 
95
+ with gr.Tab("Import Testing"):
96
+ test_btn = gr.Button("Test Imports")
97
+ import_results = gr.Textbox(label="Import Test Results", lines=20)
 
98
 
99
+ test_btn.click(test_imports, inputs=[], outputs=[import_results])
 
 
 
 
 
 
 
 
 
100
 
101
+ with gr.Tab("PDF Testing"):
102
+ pdf_btn = gr.Button("Test PDFs")
103
+ pdf_results = gr.Textbox(label="PDF Test Results", lines=20)
 
104
 
105
+ pdf_btn.click(check_pdfs, inputs=[], outputs=[pdf_results])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
 
107
+ with gr.Tab("Environment"):
108
+ env_btn = gr.Button("Check Environment")
109
+ env_results = gr.Textbox(label="Environment Information", lines=30)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
+ env_btn.click(check_environment, inputs=[], outputs=[env_results])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
+ interface.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
 
115
  if __name__ == "__main__":
116
  main()