Docfile commited on
Commit
9b56992
·
verified ·
1 Parent(s): 125fcd4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +106 -25
app.py CHANGED
@@ -143,6 +143,71 @@ def upload_file_to_genai_api(file_data, filename, mime_type):
143
  pass
144
  return None
145
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  def get_all_tasks_info():
147
  """Récupère toutes les informations des tâches pour le centre de gestion."""
148
  tasks_info = []
@@ -159,6 +224,9 @@ def get_all_tasks_info():
159
  'pdf_filename': task_data.get('pdf_filename'),
160
  'error': task_data.get('error'),
161
  'response': task_data.get('response', ''),
 
 
 
162
  'user_images': []
163
  }
164
 
@@ -181,6 +249,10 @@ def get_system_stats():
181
  error_tasks = sum(1 for task in task_results.values() if task['status'] == 'error')
182
  pending_tasks = sum(1 for task in task_results.values() if task['status'] not in ['completed', 'error'])
183
 
 
 
 
 
184
  # Compter les fichiers PDF générés
185
  pdf_files = 0
186
  if os.path.exists(GENERATED_PDF_DIR):
@@ -198,7 +270,9 @@ def get_system_stats():
198
  'pending_tasks': pending_tasks,
199
  'pdf_files': pdf_files,
200
  'user_images': user_images,
201
- 'latex_installed': IS_LATEX_INSTALLED
 
 
202
  }
203
 
204
  def clean_latex_code(latex_code):
@@ -328,24 +402,11 @@ def process_files_background(task_id, files_data, resolution_style):
328
  raise ValueError(f"Le fichier de prompt pour le style '{resolution_style}' est introuvable ou vide.")
329
  contents.append(prompt_to_use)
330
 
331
- task_results[task_id]['status'] = 'generating_latex'
332
- logger.info(f"[Task {task_id}] Envoi de la requête à l'API Gemini (modèle gemini-2.5-flash) avec {len(uploaded_file_refs)} fichier(s).")
333
-
334
- gemini_response = client.models.generate_content(
335
- model="gemini-2.5-flash",
336
- contents=contents,
337
- config=types.GenerateContentConfig(tools=[types.Tool(code_execution=types.ToolCodeExecution)])
338
- )
339
-
340
- logger.info(f"[Task {task_id}] Réponse reçue de Gemini.")
341
- full_latex_response = ""
342
- if gemini_response.candidates and gemini_response.candidates[0].content and gemini_response.candidates[0].content.parts:
343
- for part in gemini_response.candidates[0].content.parts:
344
- if hasattr(part, 'text') and part.text:
345
- full_latex_response += part.text
346
-
347
- if not full_latex_response.strip():
348
- raise ValueError("La réponse de Gemini était vide.")
349
  logger.debug(f"[Task {task_id}] Réponse brute de Gemini:\n---\n{full_latex_response[:500]}...\n---")
350
 
351
  task_results[task_id]['status'] = 'cleaning_latex'
@@ -359,8 +420,9 @@ def process_files_background(task_id, files_data, resolution_style):
359
  if pdf_file_path:
360
  task_results[task_id]['status'] = 'completed'
361
  task_results[task_id]['pdf_filename'] = os.path.basename(pdf_file_path)
362
- task_results[task_id]['response'] = f"PDF généré avec succès: {os.path.basename(pdf_file_path)}"
363
- logger.info(f"[Task {task_id}] Tâche terminée avec succès. PDF: {os.path.basename(pdf_file_path)}")
 
364
  else:
365
  raise RuntimeError(f"Échec de la génération du PDF: {pdf_message}")
366
 
@@ -515,7 +577,14 @@ def get_task_status(task_id):
515
  logger.warning(f"Tentative d'accès à une tâche inexistante: {task_id}")
516
  return jsonify({'error': 'Tâche introuvable'}), 404
517
 
518
- response_data = {'status': task['status'], 'response': task.get('response'), 'error': task.get('error')}
 
 
 
 
 
 
 
519
  if task['status'] == 'completed':
520
  response_data['download_url'] = f"/download/{task_id}"
521
 
@@ -527,6 +596,8 @@ def stream_task_progress(task_id):
527
  def generate():
528
  logger.info(f"Nouvelle connexion de streaming (SSE) pour la tâche {task_id}")
529
  last_status_sent = None
 
 
530
  while True:
531
  task = task_results.get(task_id)
532
  if not task:
@@ -535,17 +606,27 @@ def stream_task_progress(task_id):
535
  break
536
 
537
  current_status = task['status']
538
- if current_status != last_status_sent:
539
- data_to_send = {"status": current_status}
 
 
 
 
 
 
 
 
540
  if current_status == 'completed':
541
  data_to_send["response"] = task.get("response", "")
542
  data_to_send["download_url"] = f"/download/{task_id}"
543
  elif current_status == 'error':
544
  data_to_send["error"] = task.get("error", "Erreur inconnue")
 
545
 
546
- logger.info(f"[Task {task_id}] Envoi de la mise à jour de statut via SSE: {current_status}")
547
  yield f'data: {json.dumps(data_to_send)}\n\n'
548
  last_status_sent = current_status
 
549
 
550
  if current_status in ['completed', 'error']:
551
  logger.info(f"Fermeture de la connexion SSE pour la tâche terminée/échouée {task_id}")
 
143
  pass
144
  return None
145
 
146
+ def call_gemini_with_fallback(contents, task_id):
147
+ """Appelle Gemini 2.5 Pro en premier, puis 2.5 Flash en cas d'échec."""
148
+ models_to_try = [
149
+ {"name": "gemini-2.5-pro", "display_name": "Gemini 2.5 Pro"},
150
+ {"name": "gemini-2.5-flash", "display_name": "Gemini 2.5 Flash"}
151
+ ]
152
+
153
+ last_error = None
154
+
155
+ for i, model_info in enumerate(models_to_try):
156
+ model_name = model_info["name"]
157
+ model_display = model_info["display_name"]
158
+
159
+ try:
160
+ logger.info(f"[Task {task_id}] Tentative {i+1}/2: Appel de {model_display}...")
161
+
162
+ # Mettre à jour le statut pour indiquer quel modèle est en cours d'utilisation
163
+ if model_name == "gemini-2.5-pro":
164
+ task_results[task_id]['status'] = 'generating_latex_pro'
165
+ task_results[task_id]['current_model'] = 'Gemini 2.5 Pro'
166
+ else:
167
+ task_results[task_id]['status'] = 'generating_latex_flash'
168
+ task_results[task_id]['current_model'] = 'Gemini 2.5 Flash (fallback)'
169
+
170
+ # Faire l'appel à l'API
171
+ gemini_response = client.models.generate_content(
172
+ model=model_name,
173
+ contents=contents,
174
+ config=types.GenerateContentConfig(tools=[types.Tool(code_execution=types.ToolCodeExecution)])
175
+ )
176
+
177
+ # Vérifier si la réponse est valide
178
+ full_latex_response = ""
179
+ if gemini_response.candidates and gemini_response.candidates[0].content and gemini_response.candidates[0].content.parts:
180
+ for part in gemini_response.candidates[0].content.parts:
181
+ if hasattr(part, 'text') and part.text:
182
+ full_latex_response += part.text
183
+
184
+ if not full_latex_response.strip():
185
+ raise ValueError(f"Réponse vide de {model_display}")
186
+
187
+ logger.info(f"[Task {task_id}] ✓ Succès avec {model_display}")
188
+ task_results[task_id]['used_model'] = model_display
189
+ return full_latex_response
190
+
191
+ except Exception as e:
192
+ error_msg = str(e)
193
+ logger.warning(f"[Task {task_id}] ✗ Échec avec {model_display}: {error_msg}")
194
+ last_error = e
195
+
196
+ # Si c'est le dernier modèle et qu'il a échoué, on lève l'erreur
197
+ if i == len(models_to_try) - 1:
198
+ logger.error(f"[Task {task_id}] Tous les modèles ont échoué. Dernière erreur: {error_msg}")
199
+ task_results[task_id]['model_failures'] = [
200
+ f"{models_to_try[0]['display_name']}: Échec",
201
+ f"{models_to_try[1]['display_name']}: {error_msg}"
202
+ ]
203
+ raise last_error
204
+
205
+ # Attendre un peu avant d'essayer le modèle suivant
206
+ time.sleep(2)
207
+
208
+ # Cette ligne ne devrait jamais être atteinte, mais au cas où
209
+ raise last_error if last_error else Exception("Erreur inconnue lors des appels aux modèles Gemini")
210
+
211
  def get_all_tasks_info():
212
  """Récupère toutes les informations des tâches pour le centre de gestion."""
213
  tasks_info = []
 
224
  'pdf_filename': task_data.get('pdf_filename'),
225
  'error': task_data.get('error'),
226
  'response': task_data.get('response', ''),
227
+ 'used_model': task_data.get('used_model', 'N/A'),
228
+ 'current_model': task_data.get('current_model', ''),
229
+ 'model_failures': task_data.get('model_failures', []),
230
  'user_images': []
231
  }
232
 
 
249
  error_tasks = sum(1 for task in task_results.values() if task['status'] == 'error')
250
  pending_tasks = sum(1 for task in task_results.values() if task['status'] not in ['completed', 'error'])
251
 
252
+ # Statistiques d'utilisation des modèles
253
+ pro_usage = sum(1 for task in task_results.values() if task.get('used_model') == 'Gemini 2.5 Pro')
254
+ flash_usage = sum(1 for task in task_results.values() if task.get('used_model') == 'Gemini 2.5 Flash (fallback)')
255
+
256
  # Compter les fichiers PDF générés
257
  pdf_files = 0
258
  if os.path.exists(GENERATED_PDF_DIR):
 
270
  'pending_tasks': pending_tasks,
271
  'pdf_files': pdf_files,
272
  'user_images': user_images,
273
+ 'latex_installed': IS_LATEX_INSTALLED,
274
+ 'pro_usage': pro_usage,
275
+ 'flash_usage': flash_usage
276
  }
277
 
278
  def clean_latex_code(latex_code):
 
402
  raise ValueError(f"Le fichier de prompt pour le style '{resolution_style}' est introuvable ou vide.")
403
  contents.append(prompt_to_use)
404
 
405
+ # Appeler Gemini avec fallback Pro -> Flash
406
+ logger.info(f"[Task {task_id}] Début des appels aux modèles Gemini avec {len(uploaded_file_refs)} fichier(s).")
407
+ full_latex_response = call_gemini_with_fallback(contents, task_id)
408
+
409
+ logger.info(f"[Task {task_id}] Réponse reçue de Gemini ({task_results[task_id].get('used_model', 'modèle inconnu')}).")
 
 
 
 
 
 
 
 
 
 
 
 
 
410
  logger.debug(f"[Task {task_id}] Réponse brute de Gemini:\n---\n{full_latex_response[:500]}...\n---")
411
 
412
  task_results[task_id]['status'] = 'cleaning_latex'
 
420
  if pdf_file_path:
421
  task_results[task_id]['status'] = 'completed'
422
  task_results[task_id]['pdf_filename'] = os.path.basename(pdf_file_path)
423
+ used_model = task_results[task_id].get('used_model', 'modèle inconnu')
424
+ task_results[task_id]['response'] = f"PDF généré avec succès: {os.path.basename(pdf_file_path)} (généré par {used_model})"
425
+ logger.info(f"[Task {task_id}] Tâche terminée avec succès. PDF: {os.path.basename(pdf_file_path)} (modèle: {used_model})")
426
  else:
427
  raise RuntimeError(f"Échec de la génération du PDF: {pdf_message}")
428
 
 
577
  logger.warning(f"Tentative d'accès à une tâche inexistante: {task_id}")
578
  return jsonify({'error': 'Tâche introuvable'}), 404
579
 
580
+ response_data = {
581
+ 'status': task['status'],
582
+ 'response': task.get('response'),
583
+ 'error': task.get('error'),
584
+ 'current_model': task.get('current_model', ''),
585
+ 'used_model': task.get('used_model', ''),
586
+ 'model_failures': task.get('model_failures', [])
587
+ }
588
  if task['status'] == 'completed':
589
  response_data['download_url'] = f"/download/{task_id}"
590
 
 
596
  def generate():
597
  logger.info(f"Nouvelle connexion de streaming (SSE) pour la tâche {task_id}")
598
  last_status_sent = None
599
+ last_model_sent = None
600
+
601
  while True:
602
  task = task_results.get(task_id)
603
  if not task:
 
606
  break
607
 
608
  current_status = task['status']
609
+ current_model = task.get('current_model', '')
610
+
611
+ # Envoyer une mise à jour si le statut ou le modèle a changé
612
+ if current_status != last_status_sent or current_model != last_model_sent:
613
+ data_to_send = {
614
+ "status": current_status,
615
+ "current_model": current_model,
616
+ "used_model": task.get('used_model', '')
617
+ }
618
+
619
  if current_status == 'completed':
620
  data_to_send["response"] = task.get("response", "")
621
  data_to_send["download_url"] = f"/download/{task_id}"
622
  elif current_status == 'error':
623
  data_to_send["error"] = task.get("error", "Erreur inconnue")
624
+ data_to_send["model_failures"] = task.get("model_failures", [])
625
 
626
+ logger.info(f"[Task {task_id}] Envoi de la mise à jour de statut via SSE: {current_status} ({current_model})")
627
  yield f'data: {json.dumps(data_to_send)}\n\n'
628
  last_status_sent = current_status
629
+ last_model_sent = current_model
630
 
631
  if current_status in ['completed', 'error']:
632
  logger.info(f"Fermeture de la connexion SSE pour la tâche terminée/échouée {task_id}")