MohamedBouhamed commited on
Commit
516b795
·
1 Parent(s): 8758044

fixing issues-2

Browse files
Files changed (3) hide show
  1. .gitignore +27 -2
  2. app.py +124 -104
  3. prepare_embedding.py +16 -0
.gitignore CHANGED
@@ -1,2 +1,27 @@
1
- mistral-7b-instruct-v0.2.Q4_K_M.gguf
2
- chroma_db
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Fichiers lourds
2
+ RAG_IPCC/
3
+ chroma_db/
4
+ *.pdf
5
+
6
+ # Python
7
+ __pycache__/
8
+ *.pyc
9
+ *.pyo
10
+ *.pyd
11
+ .Python
12
+ env/
13
+ venv/
14
+ *.egg-info/
15
+
16
+ # NLTK data
17
+ nltk_data/
18
+
19
+ # IDE
20
+ .vscode/
21
+ .idea/
22
+ *.swp
23
+ *.swo
24
+
25
+ # OS
26
+ .DS_Store
27
+ Thumbs.db
app.py CHANGED
@@ -26,65 +26,11 @@ from langchain_community.embeddings.sentence_transformer import SentenceTransfor
26
  from huggingface_hub import InferenceClient
27
  import gradio as gr
28
 
29
- """# GESTION DE LA BASE DE DONNÉES
30
 
31
- ## Etape 1 : récupération des fichiers PDFs:
32
- """
33
-
34
- # Chemin du dossier où l'on souhaite télécharger les fichiers
35
- chemin_dossier = "./RAG_IPCC"
36
- if not os.path.exists(chemin_dossier):
37
- os.makedirs(chemin_dossier)
38
-
39
- # URLs des fichiers à télécharger
40
- urls = { "6th_report": "https://www.ipcc.ch/report/ar6/syr/downloads/report/IPCC_AR6_SYR_FullVolume.pdf" }
41
-
42
- # Télécharger les fichiers dans le dossier (seulement s'ils n'existent pas déjà)
43
- for name, url in urls.items():
44
- file_path = os.path.join(chemin_dossier, f"{name}.pdf")
45
- if not os.path.exists(file_path):
46
- print(f"Téléchargement de {name}...")
47
- response = requests.get(url)
48
- with open(file_path, 'wb') as file:
49
- file.write(response.content)
50
- print(f"{name} a été téléchargé.")
51
- else:
52
- print(f"{name} existe déjà, téléchargement ignoré.")
53
-
54
- """## Etape 2 : Extraction du texte des fichiers PDF"""
55
-
56
- # Chemin du dossier contenant les fichiers PDF
57
- chemin_dossier = "./RAG_IPCC"
58
-
59
- # Liste des fichiers PDF dans le dossier
60
- fichiers_pdf = [f for f in os.listdir(chemin_dossier) if f.endswith('.pdf')]
61
-
62
- # Liste pour stocker le texte extrait de chaque PDF
63
- extracted_text = []
64
-
65
- # Boucle à travers chaque fichier PDF
66
- for pdf in fichiers_pdf:
67
- print(f"*** PROCESSING FILE : {pdf} ***")
68
-
69
- # Chemin complet du fichier PDF
70
- chemin_pdf = os.path.join(chemin_dossier, pdf)
71
-
72
- # Ouverture du fichier PDF en mode lecture binaire
73
- with open(chemin_pdf, 'rb') as file:
74
- # Création d'un objet de lecteur PDF
75
- pdf_reader = PyPDF2.PdfReader(file)
76
-
77
- # Boucle à travers chaque page du PDF
78
- for page_num in range(len(pdf_reader.pages)):
79
- # Extraction du texte de la page actuelle
80
- page = pdf_reader.pages[page_num]
81
- text = page.extract_text()
82
-
83
- # Ajout du texte extrait à la liste
84
- extracted_text.append({"document": pdf, "page": page_num, "content": text})
85
-
86
- # Affichage du texte extrait
87
- print(f"Extracted {len(extracted_text)} pages from PDFs")
88
 
89
  """## Etape 3 : Traitement du texte en chunks propres"""
90
 
@@ -186,29 +132,6 @@ def contains_mainly_digits(text, threshold=0.5):
186
  def remove_mostly_digits_chunks(chunks, threshold=0.5):
187
  return [chunk for chunk in chunks if not contains_mainly_digits(chunk['content'])]
188
 
189
- #### EXECUTION ####
190
-
191
- # Split intelligent avec différents paramètres
192
- text_splitter = RecursiveCharacterTextSplitter(
193
- chunk_size=500,
194
- chunk_overlap=20,
195
- length_function=len,
196
- is_separator_regex=False,
197
- )
198
-
199
- # Split pertinent qui garde la structure du document
200
- chunks = []
201
- for page_content in extracted_text:
202
- chunks_list = text_splitter.split_text(page_content['content'])
203
-
204
- for chunk in chunks_list:
205
- text=clean_text(chunk)
206
- chunks.append({"document": page_content['document'],
207
- "page": page_content['page'],
208
- "content": text})
209
- chunks=remove_mostly_digits_chunks(chunks)
210
- print(f"Created {len(chunks)} chunks after processing")
211
-
212
  """# IMPLEMENTATION DU MODELE DE RECHERCHE RETENU"""
213
 
214
  class TextRetriever:
@@ -296,21 +219,102 @@ class TextRetriever:
296
  best_chunks = self.get_best_chunks(query, top_k=1)
297
  return best_chunks[0].page_content
298
 
299
- print("Initializing TextRetriever...")
300
- retriever=TextRetriever()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
 
302
- all_chunks=[]
303
- for chunk in chunks:
304
- all_chunks.append(chunk['content'])
 
 
305
 
306
- # Vérifier si la base de données existe déjà pour éviter de la recréer
307
- db_path = "./chroma_db"
308
- if os.path.exists(db_path):
309
- print("Loading existing embeddings database...")
310
- retriever.load_embeddings(db_path)
311
- else:
312
- print("Creating new embeddings database...")
313
- retriever.store_embeddings(all_chunks, db_path)
314
 
315
  """# MODELE LLM
316
 
@@ -318,8 +322,6 @@ else:
318
  """
319
 
320
  # Initialiser le client d'inférence HuggingFace (modèle gratuit et léger)
321
- # Utilisation de Mistral-7B-Instruct via l'API gratuite au lieu de le télécharger
322
- print("Initializing HuggingFace Inference Client...")
323
  llm_client = InferenceClient(model="mistralai/Mistral-7B-Instruct-v0.2")
324
 
325
  ## FONCTIONS
@@ -406,30 +408,48 @@ ch = ConversationHistoryLoader(k=3)
406
 
407
  # Fonction principale pour répondre aux questions
408
  def get_response(query):
 
 
409
  try:
 
 
 
 
 
 
 
 
 
 
410
  # Obtenir le contexte pertinent
411
  context = get_context_from_query(query)
412
-
413
  # Générer la réponse avec contexte et historique
414
  chat_history = ch.create_conversation_history_prompt()
415
  response = generate_response_with_context(query, context, chat_history)
416
-
417
  # Mettre à jour l historique
418
  ch.update_conversation_history(query, response)
419
-
420
  return response
421
-
422
  except Exception as e:
 
 
 
423
  return f"Erreur: {str(e)}"
424
 
425
  # Interface Gradio
426
  print("Creating Gradio interface...")
427
  iface = gr.Interface(
428
- fn=get_response,
429
- inputs=gr.Textbox(lines=2, placeholder="Posez votre question sur le climat..."),
430
  outputs=gr.Textbox(lines=5, label="Réponse"),
431
- title="🌍 RAG Chatbot - Questions Climatiques",
432
- description="Posez vos questions sur le changement climatique basées sur les rapports IPCC.",
 
 
 
433
  examples=[
434
  "Quels sont les principaux impacts du réchauffement climatique ?",
435
  "Comment les océans sont-ils affectés par le changement climatique ?",
 
26
  from huggingface_hub import InferenceClient
27
  import gradio as gr
28
 
29
+ """# GESTION DE LA BASE DE DONNÉES - VARIABLES GLOBALES"""
30
 
31
+ # Variables globales pour lazy loading
32
+ retriever = None
33
+ is_initialized = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
  """## Etape 3 : Traitement du texte en chunks propres"""
36
 
 
132
  def remove_mostly_digits_chunks(chunks, threshold=0.5):
133
  return [chunk for chunk in chunks if not contains_mainly_digits(chunk['content'])]
134
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  """# IMPLEMENTATION DU MODELE DE RECHERCHE RETENU"""
136
 
137
  class TextRetriever:
 
219
  best_chunks = self.get_best_chunks(query, top_k=1)
220
  return best_chunks[0].page_content
221
 
222
+ """# FONCTION D'INITIALISATION LAZY"""
223
+
224
+ def initialize_system():
225
+ """
226
+ Initialise le système RAG de manière lazy (seulement au premier appel).
227
+ Télécharge les PDFs, extrait le texte, crée les chunks et les embeddings.
228
+ """
229
+ global retriever, is_initialized
230
+
231
+ if is_initialized:
232
+ return "Système déjà initialisé"
233
+
234
+ try:
235
+ print("=" * 50)
236
+ print("INITIALISATION DU SYSTÈME RAG")
237
+ print("=" * 50)
238
+
239
+ # Etape 1: Téléchargement des PDFs
240
+ chemin_dossier = "./RAG_IPCC"
241
+ if not os.path.exists(chemin_dossier):
242
+ os.makedirs(chemin_dossier)
243
+
244
+ urls = { "6th_report": "https://www.ipcc.ch/report/ar6/syr/downloads/report/IPCC_AR6_SYR_FullVolume.pdf" }
245
+
246
+ for name, url in urls.items():
247
+ file_path = os.path.join(chemin_dossier, f"{name}.pdf")
248
+ if not os.path.exists(file_path):
249
+ print(f"📥 Téléchargement de {name}...")
250
+ response = requests.get(url)
251
+ with open(file_path, 'wb') as file:
252
+ file.write(response.content)
253
+ print(f"✅ {name} téléchargé")
254
+ else:
255
+ print(f"✅ {name} existe déjà")
256
+
257
+ # Etape 2: Extraction du texte
258
+ print("\n📄 Extraction du texte des PDFs...")
259
+ fichiers_pdf = [f for f in os.listdir(chemin_dossier) if f.endswith('.pdf')]
260
+ extracted_text = []
261
+
262
+ for pdf in fichiers_pdf:
263
+ chemin_pdf = os.path.join(chemin_dossier, pdf)
264
+ with open(chemin_pdf, 'rb') as file:
265
+ pdf_reader = PyPDF2.PdfReader(file)
266
+ for page_num in range(len(pdf_reader.pages)):
267
+ page = pdf_reader.pages[page_num]
268
+ text = page.extract_text()
269
+ extracted_text.append({"document": pdf, "page": page_num, "content": text})
270
+
271
+ print(f"✅ {len(extracted_text)} pages extraites")
272
+
273
+ # Etape 3: Création des chunks
274
+ print("\n✂️ Création des chunks de texte...")
275
+ text_splitter = RecursiveCharacterTextSplitter(
276
+ chunk_size=500,
277
+ chunk_overlap=20,
278
+ length_function=len,
279
+ is_separator_regex=False,
280
+ )
281
+
282
+ chunks = []
283
+ for page_content in extracted_text:
284
+ chunks_list = text_splitter.split_text(page_content['content'])
285
+ for chunk in chunks_list:
286
+ text = clean_text(chunk)
287
+ chunks.append({"document": page_content['document'],
288
+ "page": page_content['page'],
289
+ "content": text})
290
+
291
+ chunks = remove_mostly_digits_chunks(chunks)
292
+ print(f"✅ {len(chunks)} chunks créés")
293
+
294
+ # Etape 4: Initialisation du retriever et des embeddings
295
+ print("\n🤖 Initialisation du TextRetriever...")
296
+ retriever = TextRetriever()
297
+
298
+ all_chunks = [chunk['content'] for chunk in chunks]
299
+
300
+ # Vérifier si la base de données existe déjà
301
+ db_path = "./chroma_db"
302
+ if os.path.exists(db_path):
303
+ print("📂 Chargement de la base de données existante...")
304
+ retriever.load_embeddings(db_path)
305
+ else:
306
+ print("🔨 Création de la base de données d'embeddings...")
307
+ retriever.store_embeddings(all_chunks, db_path)
308
 
309
+ is_initialized = True
310
+ print("\n" + "=" * 50)
311
+ print("✅ SYSTÈME INITIALISÉ AVEC SUCCÈS")
312
+ print("=" * 50)
313
+ return "✅ Système initialisé avec succès !"
314
 
315
+ except Exception as e:
316
+ print(f"❌ Erreur lors de l'initialisation: {str(e)}")
317
+ return f"❌ Erreur: {str(e)}"
 
 
 
 
 
318
 
319
  """# MODELE LLM
320
 
 
322
  """
323
 
324
  # Initialiser le client d'inférence HuggingFace (modèle gratuit et léger)
 
 
325
  llm_client = InferenceClient(model="mistralai/Mistral-7B-Instruct-v0.2")
326
 
327
  ## FONCTIONS
 
408
 
409
  # Fonction principale pour répondre aux questions
410
  def get_response(query):
411
+ global retriever, is_initialized
412
+
413
  try:
414
+ # Initialiser le système au premier appel
415
+ if not is_initialized:
416
+ init_message = initialize_system()
417
+ if "❌" in init_message:
418
+ return init_message
419
+
420
+ # Vérifier que le retriever est bien initialisé
421
+ if retriever is None:
422
+ return "❌ Le système n'est pas correctement initialisé. Veuillez réessayer."
423
+
424
  # Obtenir le contexte pertinent
425
  context = get_context_from_query(query)
426
+
427
  # Générer la réponse avec contexte et historique
428
  chat_history = ch.create_conversation_history_prompt()
429
  response = generate_response_with_context(query, context, chat_history)
430
+
431
  # Mettre à jour l historique
432
  ch.update_conversation_history(query, response)
433
+
434
  return response
435
+
436
  except Exception as e:
437
+ import traceback
438
+ error_details = traceback.format_exc()
439
+ print(f"Erreur détaillée: {error_details}")
440
  return f"Erreur: {str(e)}"
441
 
442
  # Interface Gradio
443
  print("Creating Gradio interface...")
444
  iface = gr.Interface(
445
+ fn=get_response,
446
+ inputs=gr.Textbox(lines=2, placeholder="Posez votre question sur le climat..."),
447
  outputs=gr.Textbox(lines=5, label="Réponse"),
448
+ title="🌍 RAG Chatbot - Questions Climatiques",
449
+ description="""Posez vos questions sur le changement climatique basées sur les rapports IPCC.
450
+
451
+ ⚠️ **Note**: Le système s'initialise automatiquement au premier appel (téléchargement du PDF + création des embeddings).
452
+ La première requête peut prendre 2-3 minutes. Les requêtes suivantes seront rapides !""",
453
  examples=[
454
  "Quels sont les principaux impacts du réchauffement climatique ?",
455
  "Comment les océans sont-ils affectés par le changement climatique ?",
prepare_embedding.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # prepare_embeddings.py
2
+ from sentence_transformers import SentenceTransformer
3
+ from langchain_chroma import Chroma
4
+ from langchain_community.embeddings import SentenceTransformerEmbeddings
5
+ import pickle
6
+
7
+ # Charger tous les chunks (copier le code de traitement des PDFs)
8
+ # ... [ton code de traitement des PDFs] ...
9
+
10
+ # Créer les embeddings
11
+ embedding_model = SentenceTransformerEmbeddings(model_name="mixedbread-ai/mxbai-embed-large-v1")
12
+ all_chunks = [chunk['content'] for chunk in chunks]
13
+
14
+ # Sauvegarder
15
+ db = Chroma.from_texts(all_chunks, embedding=embedding_model, persist_directory="./chroma_db")
16
+ print("✅ Embeddings saved to ./chroma_db")