MathieuGAL commited on
Commit
349eb6e
·
verified ·
1 Parent(s): c2d7780

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +46 -46
app.py CHANGED
@@ -39,7 +39,7 @@ MAX_CONVERSATION_HISTORY = 10
39
 
40
  # Configuration pour l'accès externe (host et port)
41
  API_HOST = '0.0.0.0'
42
- API_PORT = 1212 # Le port 1212 est conservé, il doit être configuré dans le README.md
43
 
44
  # ======================================================================
45
  # VARIABLES GLOBALES
@@ -122,52 +122,52 @@ def initialize_gemini_client():
122
  def setup_chromadb_collection(client, df, model_paraphrase):
123
  """Configure et remplit la collection ChromaDB."""
124
  total_docs = len(df) * 2
125
-
126
  # S'assurer que le répertoire de la DB existe
127
  os.makedirs(CHROMA_DB_PATH, exist_ok=True)
128
-
129
  try:
130
  collection = client.get_or_create_collection(name=COLLECTION_NAME)
131
  except Exception as e:
132
  print(f"❌ Erreur lors de l'accès à la collection ChromaDB: {e}")
133
  raise
134
-
135
  if collection.count() == total_docs and total_docs > 0:
136
  print(f"✅ Collection déjà remplie ({collection.count()} docs) dans {CHROMA_DB_PATH}.")
137
  return collection
138
-
139
  if total_docs == 0:
140
  print("⚠️ DataFrame vide. Collection non remplie.")
141
  return collection
142
-
143
  print(f"⏳ Remplissage de ChromaDB ({len(df)} lignes) à l'emplacement: {CHROMA_DB_PATH}...")
144
-
145
  docs, metadatas, ids = [], [], []
146
-
147
  for i, row in df.iterrows():
148
  question = str(row[Q_COLUMN_NAME])
149
  reponse = str(row[R_COLUMN_NAME])
150
  meta = {Q_COLUMN_NAME: question, R_COLUMN_NAME: reponse, "source_row": i}
151
-
152
  docs.append(question)
153
  metadatas.append({**meta, "type": "question"})
154
  ids.append(f"id_{i}_Q")
155
-
156
  docs.append(reponse)
157
  metadatas.append({**meta, "type": "reponse"})
158
  ids.append(f"id_{i}_R")
159
-
160
  embeddings = model_paraphrase.encode(docs, show_progress_bar=False).tolist()
161
-
162
  # Nettoyage et recréation (pour le cas où les données CSV ont changé)
163
  try:
164
  client.delete_collection(name=COLLECTION_NAME)
165
  except:
166
  pass
167
-
168
  collection = client.get_or_create_collection(name=COLLECTION_NAME)
169
  collection.add(embeddings=embeddings, documents=docs, metadatas=metadatas, ids=ids)
170
-
171
  print(f"✅ Collection remplie: {collection.count()} documents.")
172
  return collection
173
 
@@ -178,21 +178,21 @@ def setup_chromadb_collection(client, df, model_paraphrase):
178
  def retrieve_and_rerank(query_text, collection, model_paraphrase, model_cross_encoder):
179
  """Récupère et rerank les résultats."""
180
  print(f"🔍 Récupération pour: '{query_text[:40]}...'")
181
-
182
  query_emb = model_paraphrase.encode([query_text]).tolist()
183
  results = collection.query(
184
  query_embeddings=query_emb,
185
  n_results=N_RESULTS_RETRIEVAL,
186
  include=['documents', 'metadatas', 'distances']
187
  )
188
-
189
  if not results['ids'][0]:
190
  print("⚠️ Aucun résultat trouvé.")
191
  return pd.DataFrame()
192
-
193
  candidates = []
194
  cross_input = []
195
-
196
  for i, doc in enumerate(results['documents'][0]):
197
  meta = results['metadatas'][0][i]
198
  candidates.append({
@@ -203,14 +203,14 @@ def retrieve_and_rerank(query_text, collection, model_paraphrase, model_cross_en
203
  'initial_distance': results['distances'][0][i]
204
  })
205
  cross_input.append([query_text, doc])
206
-
207
  scores = model_cross_encoder.predict(cross_input)
208
  for i, score in enumerate(scores):
209
  candidates[i]['rerank_score'] = score
210
-
211
  df = pd.DataFrame(candidates).sort_values('rerank_score', ascending=False)
212
  df = df.drop_duplicates(subset=['question', 'reponse'], keep='first')
213
-
214
  return df.head(N_RESULTS_RERANK)
215
 
216
  def generate_rag_prompt(query_text, df_results, conversation_history):
@@ -219,9 +219,9 @@ def generate_rag_prompt(query_text, df_results, conversation_history):
219
  if not df_results.empty:
220
  for _, row in df_results.iterrows():
221
  context.append(f"Q: {row['question']}\nR: {row['reponse']}")
222
-
223
  context_str = "\n---\n".join(context)
224
-
225
  history_str = ""
226
  if conversation_history:
227
  history_str = "HISTORIQUE:\n"
@@ -233,7 +233,7 @@ def generate_rag_prompt(query_text, df_results, conversation_history):
233
  # On utilise 'content' pour le texte du message
234
  history_str += f"{role}: {msg['content']}\n"
235
  history_str += "\n"
236
-
237
  return f"""{history_str}UTILISATEUR: {query_text}
238
 
239
  CONTEXTE (si utile):
@@ -258,9 +258,9 @@ def add_to_history(session_id, role, content):
258
  """Ajoute un message à l'historique."""
259
  if session_id not in conversation_histories:
260
  conversation_histories[session_id] = []
261
-
262
  conversation_histories[session_id].append({"role": role, "content": content})
263
-
264
  # Limiter la taille de l'historique conservé en mémoire
265
  if len(conversation_histories[session_id]) > MAX_CONVERSATION_HISTORY * 2:
266
  conversation_histories[session_id] = conversation_histories[session_id][-(MAX_CONVERSATION_HISTORY * 2):]
@@ -294,10 +294,10 @@ def get_answer(query_text, collection, model_paraphrase, model_cross_encoder, co
294
  print(f"\n{'='*50}")
295
  print(f"🚀 Traitement: '{query_text}'")
296
  print(f"{'='*50}")
297
-
298
  df_results = retrieve_and_rerank(query_text, collection, model_paraphrase, model_cross_encoder)
299
  final_prompt = generate_rag_prompt(query_text, df_results, conversation_history)
300
-
301
  # On retourne le prompt final RAG pour référence, mais l'appel Gemini est fait après
302
  return final_prompt
303
 
@@ -308,13 +308,13 @@ def get_answer(query_text, collection, model_paraphrase, model_cross_encoder, co
308
  def initialize_global_resources():
309
  """Initialise tous les modèles et ressources."""
310
  global model_cross_encoder, model_paraphrase, collection, system_prompt, gemini_client
311
-
312
  print("\n" + "="*50)
313
  print("⚙️ INITIALISATION RAG")
314
  print("="*50)
315
-
316
  # Le répertoire /tmp est géré par la variable CHROMA_DB_PATH
317
-
318
  try:
319
  model_cross_encoder, model_paraphrase = load_models()
320
  df = load_data()
@@ -323,7 +323,7 @@ def initialize_global_resources():
323
  except Exception:
324
  # L'erreur est déjà print dans les fonctions de chargement
325
  return False
326
-
327
  try:
328
  print(f"⏳ Initialisation de ChromaDB à l'emplacement: {CHROMA_DB_PATH}")
329
  # Le PersistentClient créera les fichiers dans le chemin spécifié (maintenant dans /tmp)
@@ -353,31 +353,31 @@ def api_get_answer():
353
  """Endpoint principal pour obtenir une réponse."""
354
  if any(x is None for x in [model_cross_encoder, model_paraphrase, collection, system_prompt, gemini_client]):
355
  return jsonify({"error": "Ressources non chargées. Veuillez vérifier les logs d'initialisation."}), 500
356
-
357
  try:
358
  data = request.get_json()
359
  query_text = data.get('query_text')
360
  session_id = data.get('session_id', 'archive')
361
-
362
  if not query_text:
363
  generic_message = "Problème avec l'API, veuillez réessayer plus tard."
364
  return jsonify({"error": generic_message}), 500
365
-
366
  # Récupère historique
367
  history = get_conversation_history(session_id)
368
-
369
  # Génère prompt RAG
370
  rag_prompt = get_answer(query_text, collection, model_paraphrase, model_cross_encoder, history)
371
-
372
  # Appelle Gemini
373
  response = call_gemini(rag_prompt, system_prompt, gemini_client)
374
-
375
  # Sauvegarde réponse
376
  add_to_history(session_id, "user", query_text)
377
  add_to_history(session_id, "assistant", response)
378
-
379
  return jsonify({"generated_response": response})
380
-
381
  except Exception as e:
382
  print(f"❌ Erreur générale de l'API: {e}")
383
  generic_message = "Problème avec l'API, veuillez réessayer plus tard."
@@ -390,7 +390,7 @@ def api_clear_history():
390
  data = request.get_json()
391
  session_id = data.get('session_id', 'archive')
392
  clear_history(session_id)
393
-
394
  return jsonify({"message": f"Historique effacé: {session_id}"})
395
  except Exception as e:
396
  generic_message = "Problème avec l'API, veuillez réessayer plus tard."
@@ -403,24 +403,24 @@ def api_clear_history():
403
  if __name__ == '__main__':
404
  print("start app.py")
405
  if initialize_global_resources():
406
-
407
  # Récupération de l'adresse IP si possible (pour l'affichage)
408
  try:
409
  import socket
410
  s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
411
- s.connect(("8.8.8.8", 80)) # Connecte à un serveur externe pour trouver l'IP locale utilisée
412
  local_ip = s.getsockname()[0]
413
  s.close()
414
  except Exception:
415
- local_ip = "127.0.0.1" # Fallback si échec
416
-
417
  print("\n" + "="*50)
418
  print("🌐 SERVEUR DÉMARRÉ")
419
  print(f"✅ API accessible à l'URL (via l'interface réseau locale): http://{local_ip}:{API_PORT}")
420
  print(f"✅ Route Status: http://{local_ip}:{API_PORT}/status")
421
  print(f"💡 N'oubliez pas de configurer 'app_port: 1212' et 'sdk: docker' dans votre README.md !")
422
  print("="*50 + "\n")
423
-
424
  # L'utilisation de host='0.0.0.0' dans app.run() permet l'accès depuis l'extérieur
425
  app.run(host=API_HOST, port=API_PORT, debug=False)
426
  else:
 
39
 
40
  # Configuration pour l'accès externe (host et port)
41
  API_HOST = '0.0.0.0'
42
+ API_PORT = 1212 # Le port 1212 est conservé, il doit être configuré dans le README.md
43
 
44
  # ======================================================================
45
  # VARIABLES GLOBALES
 
122
  def setup_chromadb_collection(client, df, model_paraphrase):
123
  """Configure et remplit la collection ChromaDB."""
124
  total_docs = len(df) * 2
125
+
126
  # S'assurer que le répertoire de la DB existe
127
  os.makedirs(CHROMA_DB_PATH, exist_ok=True)
128
+
129
  try:
130
  collection = client.get_or_create_collection(name=COLLECTION_NAME)
131
  except Exception as e:
132
  print(f"❌ Erreur lors de l'accès à la collection ChromaDB: {e}")
133
  raise
134
+
135
  if collection.count() == total_docs and total_docs > 0:
136
  print(f"✅ Collection déjà remplie ({collection.count()} docs) dans {CHROMA_DB_PATH}.")
137
  return collection
138
+
139
  if total_docs == 0:
140
  print("⚠️ DataFrame vide. Collection non remplie.")
141
  return collection
142
+
143
  print(f"⏳ Remplissage de ChromaDB ({len(df)} lignes) à l'emplacement: {CHROMA_DB_PATH}...")
144
+
145
  docs, metadatas, ids = [], [], []
146
+
147
  for i, row in df.iterrows():
148
  question = str(row[Q_COLUMN_NAME])
149
  reponse = str(row[R_COLUMN_NAME])
150
  meta = {Q_COLUMN_NAME: question, R_COLUMN_NAME: reponse, "source_row": i}
151
+
152
  docs.append(question)
153
  metadatas.append({**meta, "type": "question"})
154
  ids.append(f"id_{i}_Q")
155
+
156
  docs.append(reponse)
157
  metadatas.append({**meta, "type": "reponse"})
158
  ids.append(f"id_{i}_R")
159
+
160
  embeddings = model_paraphrase.encode(docs, show_progress_bar=False).tolist()
161
+
162
  # Nettoyage et recréation (pour le cas où les données CSV ont changé)
163
  try:
164
  client.delete_collection(name=COLLECTION_NAME)
165
  except:
166
  pass
167
+
168
  collection = client.get_or_create_collection(name=COLLECTION_NAME)
169
  collection.add(embeddings=embeddings, documents=docs, metadatas=metadatas, ids=ids)
170
+
171
  print(f"✅ Collection remplie: {collection.count()} documents.")
172
  return collection
173
 
 
178
  def retrieve_and_rerank(query_text, collection, model_paraphrase, model_cross_encoder):
179
  """Récupère et rerank les résultats."""
180
  print(f"🔍 Récupération pour: '{query_text[:40]}...'")
181
+
182
  query_emb = model_paraphrase.encode([query_text]).tolist()
183
  results = collection.query(
184
  query_embeddings=query_emb,
185
  n_results=N_RESULTS_RETRIEVAL,
186
  include=['documents', 'metadatas', 'distances']
187
  )
188
+
189
  if not results['ids'][0]:
190
  print("⚠️ Aucun résultat trouvé.")
191
  return pd.DataFrame()
192
+
193
  candidates = []
194
  cross_input = []
195
+
196
  for i, doc in enumerate(results['documents'][0]):
197
  meta = results['metadatas'][0][i]
198
  candidates.append({
 
203
  'initial_distance': results['distances'][0][i]
204
  })
205
  cross_input.append([query_text, doc])
206
+
207
  scores = model_cross_encoder.predict(cross_input)
208
  for i, score in enumerate(scores):
209
  candidates[i]['rerank_score'] = score
210
+
211
  df = pd.DataFrame(candidates).sort_values('rerank_score', ascending=False)
212
  df = df.drop_duplicates(subset=['question', 'reponse'], keep='first')
213
+
214
  return df.head(N_RESULTS_RERANK)
215
 
216
  def generate_rag_prompt(query_text, df_results, conversation_history):
 
219
  if not df_results.empty:
220
  for _, row in df_results.iterrows():
221
  context.append(f"Q: {row['question']}\nR: {row['reponse']}")
222
+
223
  context_str = "\n---\n".join(context)
224
+
225
  history_str = ""
226
  if conversation_history:
227
  history_str = "HISTORIQUE:\n"
 
233
  # On utilise 'content' pour le texte du message
234
  history_str += f"{role}: {msg['content']}\n"
235
  history_str += "\n"
236
+
237
  return f"""{history_str}UTILISATEUR: {query_text}
238
 
239
  CONTEXTE (si utile):
 
258
  """Ajoute un message à l'historique."""
259
  if session_id not in conversation_histories:
260
  conversation_histories[session_id] = []
261
+
262
  conversation_histories[session_id].append({"role": role, "content": content})
263
+
264
  # Limiter la taille de l'historique conservé en mémoire
265
  if len(conversation_histories[session_id]) > MAX_CONVERSATION_HISTORY * 2:
266
  conversation_histories[session_id] = conversation_histories[session_id][-(MAX_CONVERSATION_HISTORY * 2):]
 
294
  print(f"\n{'='*50}")
295
  print(f"🚀 Traitement: '{query_text}'")
296
  print(f"{'='*50}")
297
+
298
  df_results = retrieve_and_rerank(query_text, collection, model_paraphrase, model_cross_encoder)
299
  final_prompt = generate_rag_prompt(query_text, df_results, conversation_history)
300
+
301
  # On retourne le prompt final RAG pour référence, mais l'appel Gemini est fait après
302
  return final_prompt
303
 
 
308
  def initialize_global_resources():
309
  """Initialise tous les modèles et ressources."""
310
  global model_cross_encoder, model_paraphrase, collection, system_prompt, gemini_client
311
+
312
  print("\n" + "="*50)
313
  print("⚙️ INITIALISATION RAG")
314
  print("="*50)
315
+
316
  # Le répertoire /tmp est géré par la variable CHROMA_DB_PATH
317
+
318
  try:
319
  model_cross_encoder, model_paraphrase = load_models()
320
  df = load_data()
 
323
  except Exception:
324
  # L'erreur est déjà print dans les fonctions de chargement
325
  return False
326
+
327
  try:
328
  print(f"⏳ Initialisation de ChromaDB à l'emplacement: {CHROMA_DB_PATH}")
329
  # Le PersistentClient créera les fichiers dans le chemin spécifié (maintenant dans /tmp)
 
353
  """Endpoint principal pour obtenir une réponse."""
354
  if any(x is None for x in [model_cross_encoder, model_paraphrase, collection, system_prompt, gemini_client]):
355
  return jsonify({"error": "Ressources non chargées. Veuillez vérifier les logs d'initialisation."}), 500
356
+
357
  try:
358
  data = request.get_json()
359
  query_text = data.get('query_text')
360
  session_id = data.get('session_id', 'archive')
361
+
362
  if not query_text:
363
  generic_message = "Problème avec l'API, veuillez réessayer plus tard."
364
  return jsonify({"error": generic_message}), 500
365
+
366
  # Récupère historique
367
  history = get_conversation_history(session_id)
368
+
369
  # Génère prompt RAG
370
  rag_prompt = get_answer(query_text, collection, model_paraphrase, model_cross_encoder, history)
371
+
372
  # Appelle Gemini
373
  response = call_gemini(rag_prompt, system_prompt, gemini_client)
374
+
375
  # Sauvegarde réponse
376
  add_to_history(session_id, "user", query_text)
377
  add_to_history(session_id, "assistant", response)
378
+
379
  return jsonify({"generated_response": response})
380
+
381
  except Exception as e:
382
  print(f"❌ Erreur générale de l'API: {e}")
383
  generic_message = "Problème avec l'API, veuillez réessayer plus tard."
 
390
  data = request.get_json()
391
  session_id = data.get('session_id', 'archive')
392
  clear_history(session_id)
393
+
394
  return jsonify({"message": f"Historique effacé: {session_id}"})
395
  except Exception as e:
396
  generic_message = "Problème avec l'API, veuillez réessayer plus tard."
 
403
  if __name__ == '__main__':
404
  print("start app.py")
405
  if initialize_global_resources():
406
+
407
  # Récupération de l'adresse IP si possible (pour l'affichage)
408
  try:
409
  import socket
410
  s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
411
+ s.connect(("8.8.8.8", 80)) # Connecte à un serveur externe pour trouver l'IP locale utilisée
412
  local_ip = s.getsockname()[0]
413
  s.close()
414
  except Exception:
415
+ local_ip = "127.0.0.1" # Fallback si échec
416
+
417
  print("\n" + "="*50)
418
  print("🌐 SERVEUR DÉMARRÉ")
419
  print(f"✅ API accessible à l'URL (via l'interface réseau locale): http://{local_ip}:{API_PORT}")
420
  print(f"✅ Route Status: http://{local_ip}:{API_PORT}/status")
421
  print(f"💡 N'oubliez pas de configurer 'app_port: 1212' et 'sdk: docker' dans votre README.md !")
422
  print("="*50 + "\n")
423
+
424
  # L'utilisation de host='0.0.0.0' dans app.run() permet l'accès depuis l'extérieur
425
  app.run(host=API_HOST, port=API_PORT, debug=False)
426
  else: