Docfile commited on
Commit
0ab8eed
·
verified ·
1 Parent(s): d319de5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +116 -141
app.py CHANGED
@@ -6,39 +6,36 @@ import json
6
  from datetime import datetime
7
  from google import genai
8
  from pydantic import BaseModel, Field
9
- import enum
10
  import uuid
11
- from typing import List
12
 
13
  app = Flask(__name__)
14
 
15
  # Configuration
16
  GOOGLE_API_KEY = "AIzaSyAMYpF67aqFnWDJESWOx1dC-w3sEU29VcM" # Remplacez par votre clé API
17
- MODEL_ID = "gemini-2.0-flash-lite" # Modèle recommandé selon la documentation
18
  UPLOAD_FOLDER = 'uploads'
19
  RESULTS_FOLDER = 'results'
20
 
21
- # Créker les dossiers s'ils n'existent pas
22
  os.makedirs(UPLOAD_FOLDER, exist_ok=True)
23
  os.makedirs(RESULTS_FOLDER, exist_ok=True)
24
 
25
  # Définition des schémas Pydantic selon la documentation
26
  class TranslationPair(BaseModel):
 
27
  fang: str = Field(description="Phrase en langue fang")
28
  francais: str = Field(description="Traduction française de la phrase")
29
 
30
  class SyntheticDataResponse(BaseModel):
 
31
  request_number: int = Field(description="Numéro de la requête")
32
- generated_pairs: List[TranslationPair] = Field(description="Liste des paires de traduction générées")
 
 
 
33
  timestamp: str = Field(description="Horodatage de la génération")
34
-
35
- class Config:
36
- # Configuration pour un ordre de propriétés cohérent
37
- fields = {
38
- "request_number": {"title": "Numéro de requête"},
39
- "generated_pairs": {"title": "Paires générées"},
40
- "timestamp": {"title": "Horodatage"}
41
- }
42
 
43
  # Stockage des tâches en cours
44
  class TaskManager:
@@ -85,16 +82,18 @@ def generate_synthetic_data(file_path, task_id):
85
  client = genai.Client(api_key=GOOGLE_API_KEY)
86
 
87
  # Uploader le fichier
88
- with open(file_path, 'rb') as f:
89
- uploaded_file = client.files.upload(file=f)
90
 
91
- # Prompt optimisé pour la génération de données synthétiques
92
- prompt = """À partir du contenu de ce fichier, génère exactement 400 nouvelles paires de phrases :
93
- - Une phrase en langue fang
 
 
94
  - Sa traduction en français
95
 
96
- Varie les structures grammaticales, les contextes et le vocabulaire pour créer des données d'entraînement diversifiées.
97
- Assure-toi que chaque paire soit cohérente et naturelle dans les deux langues."""
 
98
 
99
  # Fichier de résultats JSON
100
  results_file = os.path.join(RESULTS_FOLDER, f'results_{task_id}.json')
@@ -106,7 +105,7 @@ def generate_synthetic_data(file_path, task_id):
106
  "start_time": datetime.now().isoformat(),
107
  "total_requests": 470,
108
  "model_used": MODEL_ID,
109
- "schema_version": "1.0"
110
  },
111
  "requests": [],
112
  "summary": {
@@ -119,138 +118,140 @@ def generate_synthetic_data(file_path, task_id):
119
 
120
  for i in range(470):
121
  try:
122
- print(f"Traitement de la requête {i+1}/470...")
123
 
124
- # Faire la requête avec schéma JSON selon la documentation
125
  response = client.models.generate_content(
126
  model=MODEL_ID,
127
- contents=[uploaded_file, prompt],
128
  config={
129
  'response_mime_type': 'application/json',
130
  'response_schema': SyntheticDataResponse,
131
  }
132
  )
133
 
134
- # Parser la réponse avec le schéma Pydantic
135
- try:
136
- # Utiliser la méthode .parsed pour récupérer l'objet structuré
137
- if hasattr(response, 'parsed') and response.parsed:
138
- structured_data = response.parsed
139
- request_data = {
140
- "request_number": i + 1,
141
- "timestamp": datetime.now().isoformat(),
142
- "response": {
143
- "request_number": structured_data.request_number,
144
- "generated_pairs": [
145
- {"fang": pair.fang, "francais": pair.francais}
146
- for pair in structured_data.generated_pairs
147
- ],
148
- "timestamp": structured_data.timestamp
149
- },
150
- "pairs_count": len(structured_data.generated_pairs),
151
- "status": "success"
152
- }
153
- else:
154
- # Fallback : parser manuellement la réponse JSON
155
- response_json = json.loads(response.text)
156
- request_data = {
157
- "request_number": i + 1,
158
- "timestamp": datetime.now().isoformat(),
159
- "response": response_json,
160
- "pairs_count": len(response_json.get("generated_pairs", [])),
161
- "status": "success"
162
- }
163
 
164
- all_results["requests"].append(request_data)
165
- all_results["summary"]["total_pairs"] += request_data["pairs_count"]
166
- all_results["summary"]["completed_requests"] += 1
167
-
168
- except (json.JSONDecodeError, AttributeError) as parse_error:
169
- # En cas d'erreur de parsing, sauvegarder la réponse brute
170
- error_data = {
171
  "request_number": i + 1,
172
  "timestamp": datetime.now().isoformat(),
173
- "raw_response": response.text,
174
- "pairs_count": 0,
175
- "status": "parse_error",
176
- "error": str(parse_error)
 
 
 
 
 
 
 
 
177
  }
178
- all_results["requests"].append(error_data)
179
- all_results["summary"]["failed_requests"] += 1
180
 
181
- error_msg = f"Erreur de parsing requête {i+1}: {str(parse_error)}"
182
- task_manager.add_error(task_id, error_msg)
183
- all_results["summary"]["errors"].append({
184
- "request_number": i + 1,
185
- "error": error_msg,
186
- "timestamp": datetime.now().isoformat()
187
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
 
189
- # Sauvegarder après chaque requête
190
  with open(results_file, 'w', encoding='utf-8') as f:
191
  json.dump(all_results, f, ensure_ascii=False, indent=2)
192
 
193
  # Mettre à jour le progrès
194
  task_manager.update_progress(task_id, i + 1)
195
 
196
- print(f"Requête {i+1}/470 complétée avec {request_data.get('pairs_count', 0)} paires")
197
-
198
- # Pause pour respecter les limites de l'API
199
- time.sleep(2) # Réduit à 2 secondes selon les bonnes pratiques
200
 
201
  except Exception as e:
202
- # Gestion des erreurs de requête
203
  error_msg = f"Erreur requête {i+1}: {str(e)}"
 
 
204
  task_manager.add_error(task_id, error_msg)
 
 
 
 
 
 
205
 
206
- error_data = {
 
207
  "request_number": i + 1,
208
  "timestamp": datetime.now().isoformat(),
 
209
  "pairs_count": 0,
210
- "status": "request_error",
211
  "error": error_msg
212
  }
213
- all_results["requests"].append(error_data)
214
- all_results["summary"]["failed_requests"] += 1
215
- all_results["summary"]["errors"].append({
216
- "request_number": i + 1,
217
- "error": error_msg,
218
- "timestamp": datetime.now().isoformat()
219
- })
220
 
221
  # Sauvegarder même en cas d'erreur
222
  with open(results_file, 'w', encoding='utf-8') as f:
223
  json.dump(all_results, f, ensure_ascii=False, indent=2)
224
 
225
- print(error_msg)
 
226
 
227
  # Pause plus longue en cas d'erreur
228
  time.sleep(5)
229
 
230
- # Finaliser le fichier JSON avec statistiques complètes
231
- all_results["metadata"]["end_time"] = datetime.now().isoformat()
232
  start_time = datetime.fromisoformat(all_results["metadata"]["start_time"])
233
- duration = (datetime.now() - start_time).total_seconds()
 
 
234
  all_results["metadata"]["duration_seconds"] = duration
235
- all_results["metadata"]["duration_minutes"] = round(duration / 60, 2)
 
 
 
236
 
237
  # Statistiques finales
238
- all_results["summary"]["success_rate"] = round(
239
- (all_results["summary"]["completed_requests"] / 470) * 100, 2
240
- )
 
 
 
241
 
242
  with open(results_file, 'w', encoding='utf-8') as f:
243
  json.dump(all_results, f, ensure_ascii=False, indent=2)
244
 
245
  task_manager.complete_task(task_id)
246
- print(f"Tâche {task_id} terminée avec succès")
247
- print(f"Total de paires générées: {all_results['summary']['total_pairs']}")
248
- print(f"Taux de succès: {all_results['summary']['success_rate']}%")
249
 
250
  except Exception as e:
251
  error_msg = f"Erreur générale: {str(e)}"
252
  task_manager.add_error(task_id, error_msg)
253
- print(error_msg)
254
 
255
  @app.route('/')
256
  def index():
@@ -288,7 +289,7 @@ def upload_file():
288
  return jsonify({
289
  'task_id': task_id,
290
  'message': 'Traitement démarré en arrière-plan',
291
- 'estimated_duration': '15-20 minutes'
292
  })
293
 
294
  @app.route('/status/<task_id>')
@@ -297,6 +298,15 @@ def get_status(task_id):
297
  if not task:
298
  return jsonify({'error': 'Tâche non trouvée'}), 404
299
 
 
 
 
 
 
 
 
 
 
300
  return jsonify({
301
  'status': task['status'],
302
  'progress': task['progress'],
@@ -305,7 +315,7 @@ def get_status(task_id):
305
  'errors_count': len(task['errors']),
306
  'start_time': task['start_time'].strftime('%Y-%m-%d %H:%M:%S'),
307
  'last_update': task['last_update'].strftime('%Y-%m-%d %H:%M:%S'),
308
- 'estimated_remaining': max(0, (task['total'] - task['progress']) * 2) if task['status'] == 'running' else 0
309
  })
310
 
311
  @app.route('/download/<task_id>')
@@ -400,7 +410,7 @@ def cleanup_temp_files():
400
 
401
  @app.route('/preview/<task_id>')
402
  def preview_results(task_id):
403
- """Aperçu des résultats JSON avec statistiques"""
404
  task = task_manager.get_task(task_id)
405
  if not task:
406
  return jsonify({'error': 'Tâche non trouvée'}), 404
@@ -414,17 +424,12 @@ def preview_results(task_id):
414
  with open(results_file, 'r', encoding='utf-8') as f:
415
  data = json.load(f)
416
 
417
- # Retourner un aperçu enrichi des données
418
  preview = {
419
  "metadata": data.get("metadata", {}),
420
  "summary": data.get("summary", {}),
421
  "sample_requests": data.get("requests", [])[:3], # 3 premiers échantillons
422
- "total_requests": len(data.get("requests", [])),
423
- "structure_info": {
424
- "schema_used": "SyntheticDataResponse",
425
- "fields": ["request_number", "generated_pairs", "timestamp"],
426
- "pair_structure": ["fang", "francais"]
427
- }
428
  }
429
 
430
  return jsonify(preview)
@@ -432,40 +437,10 @@ def preview_results(task_id):
432
  except Exception as e:
433
  return jsonify({'error': f'Erreur lors de la lecture du fichier: {str(e)}'}), 500
434
 
435
- @app.route('/schema')
436
- def get_schema_info():
437
- """Endpoint pour obtenir des informations sur le schéma utilisé"""
438
- schema_info = {
439
- "schema_version": "1.0",
440
- "models": {
441
- "TranslationPair": {
442
- "fields": {
443
- "fang": "string - Phrase en langue fang",
444
- "francais": "string - Traduction française"
445
- }
446
- },
447
- "SyntheticDataResponse": {
448
- "fields": {
449
- "request_number": "integer - Numéro de la requête",
450
- "generated_pairs": "array[TranslationPair] - Liste des paires générées",
451
- "timestamp": "string - Horodatage ISO 8601"
452
- }
453
- }
454
- },
455
- "api_configuration": {
456
- "model": MODEL_ID,
457
- "response_mime_type": "application/json",
458
- "structured_output": True
459
- }
460
- }
461
-
462
- return jsonify(schema_info)
463
-
464
  if __name__ == '__main__':
465
- print("🚀 Démarrage du serveur avec configuration Gemini API optimisée...")
466
  print("📂 Dossiers créés:", UPLOAD_FOLDER, RESULTS_FOLDER)
467
  print("🌐 Application disponible sur: http://localhost:5000")
468
  print("📊 Sortie JSON structurée activée avec schémas Pydantic")
469
  print("🔧 Modèle utilisé:", MODEL_ID)
470
- print("📋 Endpoint de schéma disponible: /schema")
471
  app.run(debug=True, threaded=True)
 
6
  from datetime import datetime
7
  from google import genai
8
  from pydantic import BaseModel, Field
9
+ import typing_extensions as typing
10
  import uuid
11
+ import enum
12
 
13
  app = Flask(__name__)
14
 
15
  # Configuration
16
  GOOGLE_API_KEY = "AIzaSyAMYpF67aqFnWDJESWOx1dC-w3sEU29VcM" # Remplacez par votre clé API
17
+ MODEL_ID = "gemini-2.0-flash" # Modèle recommandé selon la documentation
18
  UPLOAD_FOLDER = 'uploads'
19
  RESULTS_FOLDER = 'results'
20
 
21
+ # Créer les dossiers s'ils n'existent pas
22
  os.makedirs(UPLOAD_FOLDER, exist_ok=True)
23
  os.makedirs(RESULTS_FOLDER, exist_ok=True)
24
 
25
  # Définition des schémas Pydantic selon la documentation
26
  class TranslationPair(BaseModel):
27
+ """Paire de traductions fang/français"""
28
  fang: str = Field(description="Phrase en langue fang")
29
  francais: str = Field(description="Traduction française de la phrase")
30
 
31
  class SyntheticDataResponse(BaseModel):
32
+ """Réponse structurée pour la génération de données synthétiques"""
33
  request_number: int = Field(description="Numéro de la requête")
34
+ generated_pairs: list[TranslationPair] = Field(
35
+ description="Liste des paires de traduction générées",
36
+ min_items=1
37
+ )
38
  timestamp: str = Field(description="Horodatage de la génération")
 
 
 
 
 
 
 
 
39
 
40
  # Stockage des tâches en cours
41
  class TaskManager:
 
82
  client = genai.Client(api_key=GOOGLE_API_KEY)
83
 
84
  # Uploader le fichier
85
+ file_ref = client.files.upload(path=file_path)
 
86
 
87
+ # Prompt optimisé pour une sortie structurée
88
+ prompt = """
89
+ À partir du fichier fourni, génère exactement 400 nouvelles paires de phrases synthétiques.
90
+ Chaque paire doit contenir :
91
+ - Une phrase en fang (langue locale)
92
  - Sa traduction en français
93
 
94
+ Les phrases doivent être variées, naturelles et représentatives de la structure linguistique
95
+ présente dans le fichier source. Respecte strictement le nombre de 400 paires demandées.
96
+ """
97
 
98
  # Fichier de résultats JSON
99
  results_file = os.path.join(RESULTS_FOLDER, f'results_{task_id}.json')
 
105
  "start_time": datetime.now().isoformat(),
106
  "total_requests": 470,
107
  "model_used": MODEL_ID,
108
+ "expected_pairs_per_request": 400
109
  },
110
  "requests": [],
111
  "summary": {
 
118
 
119
  for i in range(470):
120
  try:
121
+ print(f"🔄 Démarrage requête {i+1}/470...")
122
 
123
+ # Configuration selon la documentation avec schéma Pydantic
124
  response = client.models.generate_content(
125
  model=MODEL_ID,
126
+ contents=[file_ref, prompt],
127
  config={
128
  'response_mime_type': 'application/json',
129
  'response_schema': SyntheticDataResponse,
130
  }
131
  )
132
 
133
+ # Utiliser la propriété .parsed selon la documentation
134
+ if hasattr(response, 'parsed') and response.parsed:
135
+ parsed_response = response.parsed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
+ # Structurer la réponse selon le schéma
138
+ request_data = {
 
 
 
 
 
139
  "request_number": i + 1,
140
  "timestamp": datetime.now().isoformat(),
141
+ "response": {
142
+ "request_number": parsed_response.request_number,
143
+ "generated_pairs": [
144
+ {
145
+ "fang": pair.fang,
146
+ "francais": pair.francais
147
+ } for pair in parsed_response.generated_pairs
148
+ ],
149
+ "timestamp": parsed_response.timestamp
150
+ },
151
+ "pairs_count": len(parsed_response.generated_pairs),
152
+ "status": "success"
153
  }
 
 
154
 
155
+ all_results["requests"].append(request_data)
156
+ all_results["summary"]["total_pairs"] += request_data["pairs_count"]
157
+ all_results["summary"]["completed_requests"] += 1
158
+
159
+ print(f"✅ Requête {i+1} réussie - {request_data['pairs_count']} paires générées")
160
+
161
+ else:
162
+ # Fallback : parser le JSON manuellement si .parsed n'est pas disponible
163
+ try:
164
+ response_data = json.loads(response.text)
165
+ request_data = {
166
+ "request_number": i + 1,
167
+ "timestamp": datetime.now().isoformat(),
168
+ "response": response_data,
169
+ "pairs_count": len(response_data.get("generated_pairs", [])),
170
+ "status": "success_fallback"
171
+ }
172
+
173
+ all_results["requests"].append(request_data)
174
+ all_results["summary"]["total_pairs"] += request_data["pairs_count"]
175
+ all_results["summary"]["completed_requests"] += 1
176
+
177
+ print(f"✅ Requête {i+1} réussie (fallback) - {request_data['pairs_count']} paires")
178
+
179
+ except json.JSONDecodeError as json_error:
180
+ raise Exception(f"Impossible de parser la réponse JSON: {json_error}")
181
 
182
+ # Sauvegarder après chaque requête réussie
183
  with open(results_file, 'w', encoding='utf-8') as f:
184
  json.dump(all_results, f, ensure_ascii=False, indent=2)
185
 
186
  # Mettre à jour le progrès
187
  task_manager.update_progress(task_id, i + 1)
188
 
189
+ # Pause pour respecter les limites de l'API (ajustable selon vos besoins)
190
+ time.sleep(1) # Réduit à 1 seconde, ajustez selon vos limites API
 
 
191
 
192
  except Exception as e:
 
193
  error_msg = f"Erreur requête {i+1}: {str(e)}"
194
+ print(f"❌ {error_msg}")
195
+
196
  task_manager.add_error(task_id, error_msg)
197
+ all_results["summary"]["errors"].append({
198
+ "request_number": i + 1,
199
+ "error": error_msg,
200
+ "timestamp": datetime.now().isoformat()
201
+ })
202
+ all_results["summary"]["failed_requests"] += 1
203
 
204
+ # Créer une entrée vide pour cette requête échouée
205
+ failed_request_data = {
206
  "request_number": i + 1,
207
  "timestamp": datetime.now().isoformat(),
208
+ "response": None,
209
  "pairs_count": 0,
210
+ "status": "failed",
211
  "error": error_msg
212
  }
213
+ all_results["requests"].append(failed_request_data)
 
 
 
 
 
 
214
 
215
  # Sauvegarder même en cas d'erreur
216
  with open(results_file, 'w', encoding='utf-8') as f:
217
  json.dump(all_results, f, ensure_ascii=False, indent=2)
218
 
219
+ # Mettre à jour le progrès même en cas d'erreur
220
+ task_manager.update_progress(task_id, i + 1)
221
 
222
  # Pause plus longue en cas d'erreur
223
  time.sleep(5)
224
 
225
+ # Finaliser le fichier JSON
226
+ end_time = datetime.now()
227
  start_time = datetime.fromisoformat(all_results["metadata"]["start_time"])
228
+ duration = (end_time - start_time).total_seconds()
229
+
230
+ all_results["metadata"]["end_time"] = end_time.isoformat()
231
  all_results["metadata"]["duration_seconds"] = duration
232
+ all_results["metadata"]["duration_minutes"] = duration / 60
233
+ all_results["summary"]["success_rate"] = (
234
+ all_results["summary"]["completed_requests"] / 470 * 100
235
+ )
236
 
237
  # Statistiques finales
238
+ print(f"\n📊 STATISTIQUES FINALES:")
239
+ print(f" • Requêtes réussies: {all_results['summary']['completed_requests']}/470")
240
+ print(f" • Requêtes échouées: {all_results['summary']['failed_requests']}/470")
241
+ print(f" • Total paires générées: {all_results['summary']['total_pairs']}")
242
+ print(f" • Taux de réussite: {all_results['summary']['success_rate']:.1f}%")
243
+ print(f" • Durée totale: {duration/60:.1f} minutes")
244
 
245
  with open(results_file, 'w', encoding='utf-8') as f:
246
  json.dump(all_results, f, ensure_ascii=False, indent=2)
247
 
248
  task_manager.complete_task(task_id)
249
+ print(f"🎉 Tâche {task_id} terminée avec succès")
 
 
250
 
251
  except Exception as e:
252
  error_msg = f"Erreur générale: {str(e)}"
253
  task_manager.add_error(task_id, error_msg)
254
+ print(f"💥 {error_msg}")
255
 
256
  @app.route('/')
257
  def index():
 
289
  return jsonify({
290
  'task_id': task_id,
291
  'message': 'Traitement démarré en arrière-plan',
292
+ 'expected_duration_minutes': 8 # Estimation basée sur 1s par requête
293
  })
294
 
295
  @app.route('/status/<task_id>')
 
298
  if not task:
299
  return jsonify({'error': 'Tâche non trouvée'}), 404
300
 
301
+ # Calculer ETA si la tâche est en cours
302
+ eta_minutes = None
303
+ if task['status'] == 'running' and task['progress'] > 0:
304
+ elapsed = (datetime.now() - task['start_time']).total_seconds()
305
+ rate = task['progress'] / elapsed # requêtes par seconde
306
+ remaining = task['total'] - task['progress']
307
+ eta_seconds = remaining / rate if rate > 0 else None
308
+ eta_minutes = eta_seconds / 60 if eta_seconds else None
309
+
310
  return jsonify({
311
  'status': task['status'],
312
  'progress': task['progress'],
 
315
  'errors_count': len(task['errors']),
316
  'start_time': task['start_time'].strftime('%Y-%m-%d %H:%M:%S'),
317
  'last_update': task['last_update'].strftime('%Y-%m-%d %H:%M:%S'),
318
+ 'eta_minutes': round(eta_minutes) if eta_minutes else None
319
  })
320
 
321
  @app.route('/download/<task_id>')
 
410
 
411
  @app.route('/preview/<task_id>')
412
  def preview_results(task_id):
413
+ """Aperçu des résultats JSON pour debug"""
414
  task = task_manager.get_task(task_id)
415
  if not task:
416
  return jsonify({'error': 'Tâche non trouvée'}), 404
 
424
  with open(results_file, 'r', encoding='utf-8') as f:
425
  data = json.load(f)
426
 
427
+ # Retourner un aperçu des données
428
  preview = {
429
  "metadata": data.get("metadata", {}),
430
  "summary": data.get("summary", {}),
431
  "sample_requests": data.get("requests", [])[:3], # 3 premiers échantillons
432
+ "total_requests": len(data.get("requests", []))
 
 
 
 
 
433
  }
434
 
435
  return jsonify(preview)
 
437
  except Exception as e:
438
  return jsonify({'error': f'Erreur lors de la lecture du fichier: {str(e)}'}), 500
439
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
440
  if __name__ == '__main__':
441
+ print("🚀 Démarrage du serveur Flask...")
442
  print("📂 Dossiers créés:", UPLOAD_FOLDER, RESULTS_FOLDER)
443
  print("🌐 Application disponible sur: http://localhost:5000")
444
  print("📊 Sortie JSON structurée activée avec schémas Pydantic")
445
  print("🔧 Modèle utilisé:", MODEL_ID)
 
446
  app.run(debug=True, threaded=True)