MathieuGAL commited on
Commit
4ff76a6
·
verified ·
1 Parent(s): 73f5b7d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +91 -37
app.py CHANGED
@@ -31,9 +31,11 @@ SRC_PARAPHRASE = "models/paraphrase-mpnet-base-v2"
31
  N_RESULTS_RETRIEVAL = 10
32
  N_RESULTS_RERANK = 3
33
 
34
- # Récupération de la clé depuis l'environnement (Hugging Face Secrets)
35
- # Si non trouvée, utilise la clé de placeholder.
36
- GEMINI_API_KEY = os.getenv("GEMINI_API_KEY", "AIzaSyDXXY7uSXryTxZ51jQFsSLcPnC_Ivt9V1g")
 
 
37
  GEMINI_MODEL = "gemini-2.5-flash"
38
 
39
  MAX_CONVERSATION_HISTORY = 10
@@ -50,7 +52,8 @@ model_cross_encoder: CrossEncoder = None
50
  model_paraphrase: SentenceTransformer = None
51
  collection: chromadb.Collection = None
52
  system_prompt: str = None
53
- gemini_client: genai.Client = None
 
54
 
55
  conversation_histories: Dict[str, List[Dict[str, str]]] = {}
56
  conversation_start_times: Dict[str, str] = {}
@@ -106,14 +109,17 @@ def load_system_prompt():
106
  print(f"⚠️ System prompt non trouvé à {SYSTEM_PROMPT_PATH}. Utilisation du prompt par défaut.")
107
  return default
108
 
109
- def initialize_gemini_client():
110
- """Initialise le client Google Gemini."""
111
- if GEMINI_API_KEY == "AIzaSyDXXY7uSXryTxZ51jQFsSLcPnC_Ivt9V1g":
112
- print("⚠️ AVIS: Clé Gemini par défaut/placeholder détectée. Veuillez la remplacer par un secret d'environnement nommé 'GEMINI_API_KEY' pour la production.")
 
 
113
  try:
114
- return genai.Client(api_key=GEMINI_API_KEY)
 
115
  except Exception as e:
116
- print(f"❌ Erreur lors de l'initialisation du client Gemini: {e}")
117
  raise
118
 
119
  # ======================================================================
@@ -273,26 +279,26 @@ def clear_history(session_id):
273
  # ======================================================================
274
  # CALL GEMINI
275
  # ======================================================================
276
- def call_gemini(rag_prompt, system_prompt, gemini_client):
 
277
  """
278
  Appelle Google Gemini avec une logique de réessai en cas d'échec de l'API.
279
  Maximum de 10 tentatives.
280
  """
281
  MAX_RETRIES = 10
282
 
283
- # L'API Gemini Python ne retourne pas directement les codes HTTP comme 503,
284
- # mais lève des exceptions `APIError` ou `ResourceExhaustedError` qui correspondent
285
- # à des erreurs de service. Nous allons intercepter l'exception générique
286
- # pour capturer toutes les erreurs potentielles liées à l'appel.
287
 
288
  for attempt in range(MAX_RETRIES):
289
  try:
290
- print(f" 📞 Tentative d'appel Gemini #{attempt + 1}...")
291
  # L'API Python de Google lève des exceptions `APIError` pour les échecs,
292
  # y compris ceux qui correspondent aux 5xx.
293
  response = gemini_client.models.generate_content(
294
  model=GEMINI_MODEL,
295
- contents=f"{system_prompt}\n\n{rag_prompt}"
296
  )
297
  # Si la réponse réussit, on sort de la boucle
298
  return response.text.replace("*", "")
@@ -301,56 +307,64 @@ def call_gemini(rag_prompt, system_prompt, gemini_client):
301
  # Ici, on capture toute erreur d'API ou de connexion.
302
  # On considère cela comme une erreur de service transitoire pour les réessais.
303
  error_message = str(e)
304
- print(f" ❌ Erreur Gemini (Tentative {attempt + 1}/{MAX_RETRIES}): {error_message}")
305
 
306
  if attempt < MAX_RETRIES - 1:
307
  # Si ce n'est pas la dernière tentative, on attend avant de réessayer
308
  sleep_time = 2 # Attente de 2 secondes
309
- print(f" 😴 Attente de {sleep_time} secondes avant de réessayer...")
310
  time.sleep(sleep_time)
311
  else:
312
  # Dernière tentative échouée
313
- print(" 🛑 Toutes les tentatives de réessai ont échoué.")
314
  return f"Erreur fatale après {MAX_RETRIES} tentatives: {error_message}"
315
 
316
  # Ne devrait jamais être atteint, mais par sécurité
317
  return "Erreur inconnue dans la boucle de réessai de Gemini."
318
 
319
  # ======================================================================
320
- # ANSWER PROCESS
321
  # ======================================================================
322
 
323
- def get_answer(query_text, collection, model_paraphrase, model_cross_encoder, conversation_history):
324
  """Exécute le processus RAG complet."""
325
  print(f"\n{'='*50}")
326
- print(f"🚀 Traitement: '{query_text}'")
327
  print(f"{'='*50}")
328
 
329
  df_results = retrieve_and_rerank(query_text, collection, model_paraphrase, model_cross_encoder)
330
  final_prompt = generate_rag_prompt(query_text, df_results, conversation_history)
331
 
332
- # On retourne le prompt final RAG pour référence, mais l'appel Gemini est fait après
333
  return final_prompt
334
 
 
 
 
 
 
 
 
 
335
  # ======================================================================
336
  # INITIALISATION GLOBALE
337
  # ======================================================================
338
 
339
  def initialize_global_resources():
340
  """Initialise tous les modèles et ressources."""
341
- global model_cross_encoder, model_paraphrase, collection, system_prompt, gemini_client
 
342
 
343
  print("\n" + "="*50)
344
- print("⚙️ INITIALISATION RAG")
345
  print("="*50)
346
 
347
- # Le répertoire /tmp est géré par la variable CHROMA_DB_PATH
348
-
349
  try:
350
  model_cross_encoder, model_paraphrase = load_models()
351
  df = load_data()
352
  system_prompt = load_system_prompt()
353
- gemini_client = initialize_gemini_client()
 
 
354
  except Exception:
355
  # L'erreur est déjà print dans les fonctions de chargement
356
  return False
@@ -381,9 +395,10 @@ def api_status():
381
 
382
  @app.route('/api/get_answer', methods=['POST'])
383
  def api_get_answer():
384
- """Endpoint principal pour obtenir une réponse."""
385
- if any(x is None for x in [model_cross_encoder, model_paraphrase, collection, system_prompt, gemini_client]):
386
- return jsonify({"error": "Ressources non chargées. Veuillez vérifier les logs d'initialisation."}), 500
 
387
 
388
  try:
389
  data = request.get_json()
@@ -398,10 +413,10 @@ def api_get_answer():
398
  history = get_conversation_history(session_id)
399
 
400
  # Génère prompt RAG
401
- rag_prompt = get_answer(query_text, collection, model_paraphrase, model_cross_encoder, history)
402
 
403
- # Appelle Gemini
404
- response = call_gemini(rag_prompt, system_prompt, gemini_client)
405
 
406
  # Sauvegarde réponse
407
  add_to_history(session_id, "user", query_text)
@@ -410,10 +425,47 @@ def api_get_answer():
410
  return jsonify({"generated_response": response})
411
 
412
  except Exception as e:
413
- print(f"❌ Erreur générale de l'API: {e}")
414
- generic_message = "Problème avec l'API, veuillez réessayer plus tard."
415
  return jsonify({"error": generic_message}), 500
416
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
417
  @app.route('/api/clear_history', methods=['POST'])
418
  def api_clear_history():
419
  """Efface l'historique d'une session."""
@@ -449,6 +501,8 @@ if __name__ == '__main__':
449
  print("🌐 SERVEUR DÉMARRÉ")
450
  print(f"✅ API accessible à l'URL (via l'interface réseau locale): http://{local_ip}:{API_PORT}")
451
  print(f"✅ Route Status: http://{local_ip}:{API_PORT}/status")
 
 
452
  print(f"💡 N'oubliez pas de configurer 'app_port: 1212' et 'sdk: docker' dans votre README.md !")
453
  print("="*50 + "\n")
454
 
 
31
  N_RESULTS_RETRIEVAL = 10
32
  N_RESULTS_RERANK = 3
33
 
34
+ # Clé pour la route RAG (récupérée de l'environnement ou par défaut)
35
+ GEMINI_API_KEY_RAG = os.getenv("GEMINI_API_KEY", "AIzaSyDXXY7uSXryTxZ51jQFsSLcPnC_Ivt9V1g")
36
+ # NOUVELLE CLÉ demandée, mise en dur pour la route directe
37
+ GEMINI_API_KEY_DIRECT = "AIzaSyCpG2G3K0cZmTxWFO-c4OoOrW1fcTYQwgo"
38
+
39
  GEMINI_MODEL = "gemini-2.5-flash"
40
 
41
  MAX_CONVERSATION_HISTORY = 10
 
52
  model_paraphrase: SentenceTransformer = None
53
  collection: chromadb.Collection = None
54
  system_prompt: str = None
55
+ gemini_client_rag: genai.Client = None # Client pour la route RAG
56
+ gemini_client_direct: genai.Client = None # Client pour la route directe
57
 
58
  conversation_histories: Dict[str, List[Dict[str, str]]] = {}
59
  conversation_start_times: Dict[str, str] = {}
 
109
  print(f"⚠️ System prompt non trouvé à {SYSTEM_PROMPT_PATH}. Utilisation du prompt par défaut.")
110
  return default
111
 
112
+ def initialize_gemini_client(api_key, client_name):
113
+ """Initialise un client Google Gemini."""
114
+ if api_key == "AIzaSyDXXY7uSXryTxZ51jQFsSLcPnC_Ivt9V1g":
115
+ print(f"⚠️ AVIS pour {client_name}: Clé Gemini par défaut/placeholder détectée.")
116
+ if api_key == "AIzaSyCpG2G3K0cZmTxWFO-c4OoOrW1fcTYQwgo":
117
+ print(f"💡 AVIS pour {client_name}: Clé Gemini directe mise en dur détectée.")
118
  try:
119
+ print(f"✅ Client Gemini '{client_name}' initialisé.")
120
+ return genai.Client(api_key=api_key)
121
  except Exception as e:
122
+ print(f"❌ Erreur lors de l'initialisation du client Gemini '{client_name}': {e}")
123
  raise
124
 
125
  # ======================================================================
 
279
  # ======================================================================
280
  # CALL GEMINI
281
  # ======================================================================
282
+
283
+ def call_gemini(final_prompt, system_prompt, gemini_client):
284
  """
285
  Appelle Google Gemini avec une logique de réessai en cas d'échec de l'API.
286
  Maximum de 10 tentatives.
287
  """
288
  MAX_RETRIES = 10
289
 
290
+ # S'assurer que le client est bien initialisé
291
+ if gemini_client is None:
292
+ return "Erreur: Client Gemini non initialisé."
 
293
 
294
  for attempt in range(MAX_RETRIES):
295
  try:
296
+ print(f" 📞 Tentative d'appel Gemini #{attempt + 1}...")
297
  # L'API Python de Google lève des exceptions `APIError` pour les échecs,
298
  # y compris ceux qui correspondent aux 5xx.
299
  response = gemini_client.models.generate_content(
300
  model=GEMINI_MODEL,
301
+ contents=f"{system_prompt}\n\n{final_prompt}"
302
  )
303
  # Si la réponse réussit, on sort de la boucle
304
  return response.text.replace("*", "")
 
307
  # Ici, on capture toute erreur d'API ou de connexion.
308
  # On considère cela comme une erreur de service transitoire pour les réessais.
309
  error_message = str(e)
310
+ print(f" ❌ Erreur Gemini (Tentative {attempt + 1}/{MAX_RETRIES}): {error_message}")
311
 
312
  if attempt < MAX_RETRIES - 1:
313
  # Si ce n'est pas la dernière tentative, on attend avant de réessayer
314
  sleep_time = 2 # Attente de 2 secondes
315
+ print(f" 😴 Attente de {sleep_time} secondes avant de réessayer...")
316
  time.sleep(sleep_time)
317
  else:
318
  # Dernière tentative échouée
319
+ print(" 🛑 Toutes les tentatives de réessai ont échoué.")
320
  return f"Erreur fatale après {MAX_RETRIES} tentatives: {error_message}"
321
 
322
  # Ne devrait jamais être atteint, mais par sécurité
323
  return "Erreur inconnue dans la boucle de réessai de Gemini."
324
 
325
  # ======================================================================
326
+ # PROCESSUS DE RÉPONSE - RAG
327
  # ======================================================================
328
 
329
+ def get_answer_rag_process(query_text, collection, model_paraphrase, model_cross_encoder, conversation_history):
330
  """Exécute le processus RAG complet."""
331
  print(f"\n{'='*50}")
332
+ print(f"🚀 Traitement RAG: '{query_text}'")
333
  print(f"{'='*50}")
334
 
335
  df_results = retrieve_and_rerank(query_text, collection, model_paraphrase, model_cross_encoder)
336
  final_prompt = generate_rag_prompt(query_text, df_results, conversation_history)
337
 
 
338
  return final_prompt
339
 
340
+ # ======================================================================
341
+ # PROCESSUS DE RÉPONSE - DIRECT
342
+ # ======================================================================
343
+
344
+ def get_answer_direct_process(query_text):
345
+ """Génère le prompt direct sans RAG."""
346
+ return f"UTILISATEUR: {query_text}"
347
+
348
  # ======================================================================
349
  # INITIALISATION GLOBALE
350
  # ======================================================================
351
 
352
  def initialize_global_resources():
353
  """Initialise tous les modèles et ressources."""
354
+ global model_cross_encoder, model_paraphrase, collection, system_prompt
355
+ global gemini_client_rag, gemini_client_direct
356
 
357
  print("\n" + "="*50)
358
+ print("⚙️ INITIALISATION RAG & Clients Gemini")
359
  print("="*50)
360
 
 
 
361
  try:
362
  model_cross_encoder, model_paraphrase = load_models()
363
  df = load_data()
364
  system_prompt = load_system_prompt()
365
+ # Initialisation des deux clients
366
+ gemini_client_rag = initialize_gemini_client(GEMINI_API_KEY_RAG, "RAG (Env/Default)")
367
+ gemini_client_direct = initialize_gemini_client(GEMINI_API_KEY_DIRECT, "Direct (Hardcoded)")
368
  except Exception:
369
  # L'erreur est déjà print dans les fonctions de chargement
370
  return False
 
395
 
396
  @app.route('/api/get_answer', methods=['POST'])
397
  def api_get_answer():
398
+ """Endpoint principal pour obtenir une réponse avec RAG."""
399
+ # Le client RAG utilise la clé d'environnement/par défaut
400
+ if any(x is None for x in [model_cross_encoder, model_paraphrase, collection, system_prompt, gemini_client_rag]):
401
+ return jsonify({"error": "Ressources RAG non chargées. Veuillez vérifier les logs d'initialisation."}), 500
402
 
403
  try:
404
  data = request.get_json()
 
413
  history = get_conversation_history(session_id)
414
 
415
  # Génère prompt RAG
416
+ rag_prompt = get_answer_rag_process(query_text, collection, model_paraphrase, model_cross_encoder, history)
417
 
418
+ # Appelle Gemini avec le client RAG
419
+ response = call_gemini(rag_prompt, system_prompt, gemini_client_rag)
420
 
421
  # Sauvegarde réponse
422
  add_to_history(session_id, "user", query_text)
 
425
  return jsonify({"generated_response": response})
426
 
427
  except Exception as e:
428
+ print(f"❌ Erreur générale de l'API RAG: {e}")
429
+ generic_message = "Problème avec l'API RAG, veuillez réessayer plus tard."
430
  return jsonify({"error": generic_message}), 500
431
 
432
+ @app.route('/api/gemini_only', methods=['POST'])
433
+ def api_gemini_only():
434
+ """NOUVELLE ROUTE : Endpoint pour les requêtes directes à Gemini sans RAG. Utilise la clé mise en dur."""
435
+ # Le client direct utilise la clé mise en dur
436
+ if gemini_client_direct is None:
437
+ return jsonify({"error": "Client Gemini direct non initialisé. Vérifiez les logs."}), 500
438
+
439
+ try:
440
+ data = request.get_json()
441
+ query_text = data.get('query_text')
442
+ # On peut optionally récupérer un 'system_prompt_direct' pour customiser, sinon on utilise le prompt par défaut
443
+ custom_system_prompt = data.get('system_prompt', system_prompt)
444
+
445
+ if not query_text:
446
+ return jsonify({"error": "Paramètre 'query_text' manquant."}), 400
447
+
448
+ print(f"\n{'='*50}")
449
+ print(f"⚡ Traitement Direct: '{query_text}'")
450
+ print(f"{'='*50}")
451
+
452
+ # Génère le prompt final (juste la question)
453
+ final_prompt = get_answer_direct_process(query_text)
454
+
455
+ # Appelle Gemini avec le client direct
456
+ # On utilise le 'system_prompt' par défaut ou un custom s'il est fourni
457
+ response = call_gemini(final_prompt, custom_system_prompt, gemini_client_direct)
458
+
459
+ # Pas d'ajout à l'historique de conversation ici car c'est une route directe sans session RAG/Historique
460
+
461
+ return jsonify({"generated_response": response})
462
+
463
+ except Exception as e:
464
+ print(f"❌ Erreur générale de l'API Direct: {e}")
465
+ generic_message = "Problème avec l'API directe, veuillez réessayer plus tard."
466
+ return jsonify({"error": generic_message}), 500
467
+
468
+
469
  @app.route('/api/clear_history', methods=['POST'])
470
  def api_clear_history():
471
  """Efface l'historique d'une session."""
 
501
  print("🌐 SERVEUR DÉMARRÉ")
502
  print(f"✅ API accessible à l'URL (via l'interface réseau locale): http://{local_ip}:{API_PORT}")
503
  print(f"✅ Route Status: http://{local_ip}:{API_PORT}/status")
504
+ print(f"✅ Route RAG (avec Historique): http://{local_ip}:{API_PORT}/api/get_answer")
505
+ print(f"✅ Route DIRECTE (Clé spéciale): http://{local_ip}:{API_PORT}/api/gemini_only")
506
  print(f"💡 N'oubliez pas de configurer 'app_port: 1212' et 'sdk: docker' dans votre README.md !")
507
  print("="*50 + "\n")
508