tfrere commited on
Commit
7f7e436
·
1 Parent(s): 57683d7

update error message and avoid double benchmark generation

Browse files
backend/{tests/clean_and_restart_eval.py → clean_and_restart_eval.py} RENAMED
File without changes
backend/routes/benchmark.py CHANGED
@@ -34,6 +34,24 @@ async def generate_benchmark(data: Dict[str, Any]):
34
  if not session_id or session_id not in router.session_files:
35
  return {"error": "Invalid or missing session ID"}
36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  file_path = router.session_files[session_id]
38
  all_logs = []
39
 
 
34
  if not session_id or session_id not in router.session_files:
35
  return {"error": "Invalid or missing session ID"}
36
 
37
+ # Vérifier si un benchmark est déjà en cours ou complété pour cette session
38
+ if session_id in active_tasks:
39
+ task = active_tasks[session_id]
40
+ # Si le benchmark est déjà terminé, retourner les logs existants
41
+ if task.is_task_completed():
42
+ return {
43
+ "status": "already_completed",
44
+ "logs": task.get_logs(),
45
+ "is_completed": True
46
+ }
47
+ # Si le benchmark est en cours d'exécution, retourner les logs actuels
48
+ else:
49
+ return {
50
+ "status": "already_running",
51
+ "logs": task.get_logs(),
52
+ "is_completed": False
53
+ }
54
+
55
  file_path = router.session_files[session_id]
56
  all_logs = []
57
 
backend/tasks/create_bench.py CHANGED
@@ -107,6 +107,9 @@ class CreateBenchTask:
107
  """
108
  self._add_log("[INFO] Starting output capture")
109
 
 
 
 
110
  try:
111
  while self.is_running() and self.process:
112
  line = self.process.stdout.readline()
@@ -122,6 +125,14 @@ class CreateBenchTask:
122
  # Process the output line
123
  line = line.strip()
124
  if line:
 
 
 
 
 
 
 
 
125
  # Log raw output for debugging
126
  self._add_log(f"[DEBUG] Raw output: {line}")
127
  # Filter and format the line as needed
@@ -137,6 +148,9 @@ class CreateBenchTask:
137
  # Standardiser les noms d'étapes pour correspondre au frontend
138
  stage = self._standardize_stage_name(stage)
139
  self._add_log(f"[SUCCESS] Stage completed: {stage}")
 
 
 
140
  else:
141
  self._add_log(f"[INFO] {line}")
142
 
@@ -146,7 +160,11 @@ class CreateBenchTask:
146
  if exit_code == 0:
147
  self._add_log("[SUCCESS] Benchmark process completed successfully")
148
  else:
149
- self._add_log(f"[ERROR] Benchmark process terminated with error code: {exit_code}")
 
 
 
 
150
  except Exception as e:
151
  self._add_log(f"[ERROR] Error during output capture: {str(e)}")
152
  finally:
 
107
  """
108
  self._add_log("[INFO] Starting output capture")
109
 
110
+ # Flag pour détecter les erreurs de rate limiting
111
+ rate_limit_detected = False
112
+
113
  try:
114
  while self.is_running() and self.process:
115
  line = self.process.stdout.readline()
 
125
  # Process the output line
126
  line = line.strip()
127
  if line:
128
+ # Detect rate limiting errors
129
+ if ("too many requests" in line.lower() or
130
+ "rate limit" in line.lower() or
131
+ "429" in line or
132
+ "too many concurrent requests" in line.lower()):
133
+ rate_limit_detected = True
134
+ self._add_log("[ERROR] RATE_LIMIT_EXCEEDED: The demo is under heavy load at the moment.")
135
+
136
  # Log raw output for debugging
137
  self._add_log(f"[DEBUG] Raw output: {line}")
138
  # Filter and format the line as needed
 
148
  # Standardiser les noms d'étapes pour correspondre au frontend
149
  stage = self._standardize_stage_name(stage)
150
  self._add_log(f"[SUCCESS] Stage completed: {stage}")
151
+ # Vérifier spécifiquement la complétion de l'étape upload_ingest_to_hub
152
+ elif "Successfully completed 'upload_ingest_to_hub' stage" in line:
153
+ self._add_log(f"[SUCCESS] Stage completed: upload_ingest_to_hub")
154
  else:
155
  self._add_log(f"[INFO] {line}")
156
 
 
160
  if exit_code == 0:
161
  self._add_log("[SUCCESS] Benchmark process completed successfully")
162
  else:
163
+ # Si une erreur de rate limiting a été détectée, afficher un message spécifique
164
+ if rate_limit_detected:
165
+ self._add_log("[ERROR] Benchmark process failed due to API rate limiting. The demo is under heavy load at the moment.")
166
+ else:
167
+ self._add_log(f"[ERROR] Benchmark process terminated with error code: {exit_code}")
168
  except Exception as e:
169
  self._add_log(f"[ERROR] Error during output capture: {str(e)}")
170
  finally:
backend/tasks/create_bench_config_file.py CHANGED
@@ -146,6 +146,10 @@ class CreateBenchConfigTask:
146
  self._add_log(f"[ERROR] {error_msg}")
147
  raise RuntimeError(error_msg)
148
 
 
 
 
 
149
  # Mark provider check stage as completed
150
  self._add_log("[SUCCESS] Stage completed: provider_check")
151
 
 
146
  self._add_log(f"[ERROR] {error_msg}")
147
  raise RuntimeError(error_msg)
148
 
149
+ # Ajouter un délai minimum de 2 secondes pour l'étape provider_check
150
+ self._add_log("[INFO] Finalizing provider check...")
151
+ time.sleep(2)
152
+
153
  # Mark provider check stage as completed
154
  self._add_log("[SUCCESS] Stage completed: provider_check")
155
 
frontend/src/components/BenchmarkCreateForm.jsx CHANGED
@@ -10,6 +10,10 @@ import {
10
  Grid,
11
  IconButton,
12
  Tooltip,
 
 
 
 
13
  } from "@mui/material";
14
  import { alpha } from "@mui/material/styles";
15
  import CloudUploadIcon from "@mui/icons-material/CloudUpload";
@@ -19,6 +23,8 @@ import DescriptionIcon from "@mui/icons-material/Description";
19
  import ArticleIcon from "@mui/icons-material/Article";
20
  import MenuBookIcon from "@mui/icons-material/MenuBook";
21
  import DownloadIcon from "@mui/icons-material/Download";
 
 
22
  import { useThemeMode } from "../hooks/useThemeMode";
23
  import getTheme from "../config/theme";
24
  import API_CONFIG from "../config/api";
@@ -41,6 +47,10 @@ function BenchmarkCreateForm({ onStartGeneration }) {
41
  const [selectedDocument, setSelectedDocument] = useState(null);
42
  const [isDefaultDocument, setIsDefaultDocument] = useState(false);
43
  const [isDownloading, setIsDownloading] = useState(false);
 
 
 
 
44
  const fileInputRef = useRef(null);
45
 
46
  const defaultDocuments = [
@@ -243,6 +253,48 @@ function BenchmarkCreateForm({ onStartGeneration }) {
243
  }
244
  };
245
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
  return (
247
  <Box sx={{ mt: -2 }}>
248
  <Typography
@@ -280,33 +332,33 @@ function BenchmarkCreateForm({ onStartGeneration }) {
280
  }}
281
  onClick={() => handleDefaultDocClick(doc)}
282
  >
283
- <Tooltip title="Download document">
284
  <IconButton
285
  onClick={(e) => {
286
  e.stopPropagation();
287
- handleDownloadDocument(doc);
288
  }}
289
  sx={{
290
  position: "absolute",
291
  top: 4,
292
  right: 4,
293
  color: "text.secondary",
294
- opacity: 0.6,
295
  "&:hover": {
296
- opacity: 1,
297
  backgroundColor: alpha(theme.palette.primary.main, 0.05),
298
  },
299
- padding: 0.5,
300
  "& .MuiSvgIcon-root": {
301
- fontSize: 18,
302
  },
303
  }}
304
- disabled={isDownloading}
305
  >
306
- {isDownloading ? (
307
- <CircularProgress size={16} />
308
  ) : (
309
- <DownloadIcon />
310
  )}
311
  </IconButton>
312
  </Tooltip>
@@ -431,6 +483,92 @@ function BenchmarkCreateForm({ onStartGeneration }) {
431
  {uploadStatus?.message}
432
  </Alert>
433
  </Snackbar>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
434
  </Box>
435
  );
436
  }
 
10
  Grid,
11
  IconButton,
12
  Tooltip,
13
+ Dialog,
14
+ DialogTitle,
15
+ DialogContent,
16
+ DialogActions,
17
  } from "@mui/material";
18
  import { alpha } from "@mui/material/styles";
19
  import CloudUploadIcon from "@mui/icons-material/CloudUpload";
 
23
  import ArticleIcon from "@mui/icons-material/Article";
24
  import MenuBookIcon from "@mui/icons-material/MenuBook";
25
  import DownloadIcon from "@mui/icons-material/Download";
26
+ import VisibilityIcon from "@mui/icons-material/Visibility";
27
+ import CloseIcon from "@mui/icons-material/Close";
28
  import { useThemeMode } from "../hooks/useThemeMode";
29
  import getTheme from "../config/theme";
30
  import API_CONFIG from "../config/api";
 
47
  const [selectedDocument, setSelectedDocument] = useState(null);
48
  const [isDefaultDocument, setIsDefaultDocument] = useState(false);
49
  const [isDownloading, setIsDownloading] = useState(false);
50
+ const [documentContent, setDocumentContent] = useState("");
51
+ const [openContentModal, setOpenContentModal] = useState(false);
52
+ const [isLoadingContent, setIsLoadingContent] = useState(false);
53
+ const [modalDocument, setModalDocument] = useState(null);
54
  const fileInputRef = useRef(null);
55
 
56
  const defaultDocuments = [
 
253
  }
254
  };
255
 
256
+ const handleViewDocument = async (doc) => {
257
+ setIsLoadingContent(true);
258
+
259
+ try {
260
+ let extension = "";
261
+ if (doc.id === "the-bitter-lesson") {
262
+ extension = "html";
263
+ } else if (doc.id === "hurricane-faq") {
264
+ extension = "md";
265
+ } else {
266
+ extension = "txt";
267
+ }
268
+
269
+ // Mettre à jour l'état du document pour la modale
270
+ setModalDocument(doc);
271
+
272
+ const response = await fetch(`/${doc.id}.${extension}`);
273
+ const text = await response.text();
274
+
275
+ setDocumentContent(text);
276
+ setOpenContentModal(true);
277
+ } catch (error) {
278
+ console.error("Error loading document content:", error);
279
+ setUploadStatus({
280
+ success: false,
281
+ message: "Error loading document content",
282
+ });
283
+ setOpenSnackbar(true);
284
+ } finally {
285
+ setIsLoadingContent(false);
286
+ }
287
+ };
288
+
289
+ const handleCloseContentModal = () => {
290
+ setOpenContentModal(false);
291
+ // Réinitialiser après la fermeture de la modale
292
+ setTimeout(() => {
293
+ setDocumentContent("");
294
+ setModalDocument(null);
295
+ }, 300);
296
+ };
297
+
298
  return (
299
  <Box sx={{ mt: -2 }}>
300
  <Typography
 
332
  }}
333
  onClick={() => handleDefaultDocClick(doc)}
334
  >
335
+ <Tooltip title="View content">
336
  <IconButton
337
  onClick={(e) => {
338
  e.stopPropagation();
339
+ handleViewDocument(doc);
340
  }}
341
  sx={{
342
  position: "absolute",
343
  top: 4,
344
  right: 4,
345
  color: "text.secondary",
346
+ opacity: 0.4,
347
  "&:hover": {
348
+ opacity: 0.8,
349
  backgroundColor: alpha(theme.palette.primary.main, 0.05),
350
  },
351
+ padding: 0.3,
352
  "& .MuiSvgIcon-root": {
353
+ fontSize: 16,
354
  },
355
  }}
356
+ disabled={isLoadingContent}
357
  >
358
+ {isLoadingContent && selectedDocument?.id === doc.id ? (
359
+ <CircularProgress size={14} />
360
  ) : (
361
+ <VisibilityIcon />
362
  )}
363
  </IconButton>
364
  </Tooltip>
 
483
  {uploadStatus?.message}
484
  </Alert>
485
  </Snackbar>
486
+
487
+ <Dialog
488
+ open={openContentModal}
489
+ onClose={handleCloseContentModal}
490
+ maxWidth="md"
491
+ fullWidth
492
+ aria-labelledby="document-content-dialog-title"
493
+ >
494
+ <DialogTitle id="document-content-dialog-title">
495
+ <Box
496
+ sx={{
497
+ display: "flex",
498
+ justifyContent: "space-between",
499
+ alignItems: "flex-start",
500
+ }}
501
+ >
502
+ <Box>
503
+ {modalDocument && (
504
+ <Typography variant="h6" sx={{ fontWeight: 600 }}>
505
+ {modalDocument.name}
506
+ </Typography>
507
+ )}
508
+ <Typography variant="body2" color="text.secondary">
509
+ {modalDocument &&
510
+ (modalDocument.id === "the-bitter-lesson"
511
+ ? "HTML"
512
+ : modalDocument.id === "hurricane-faq"
513
+ ? "Markdown"
514
+ : "Text")}
515
+ </Typography>
516
+ </Box>
517
+ <Box sx={{ display: "flex", gap: 1 }}>
518
+ {modalDocument && (
519
+ <Tooltip title="Download document">
520
+ <IconButton
521
+ edge="end"
522
+ color="inherit"
523
+ onClick={() => handleDownloadDocument(modalDocument)}
524
+ disabled={isDownloading}
525
+ aria-label="download"
526
+ >
527
+ {isDownloading ? (
528
+ <CircularProgress size={20} />
529
+ ) : (
530
+ <DownloadIcon />
531
+ )}
532
+ </IconButton>
533
+ </Tooltip>
534
+ )}
535
+ <IconButton
536
+ edge="end"
537
+ color="inherit"
538
+ onClick={handleCloseContentModal}
539
+ aria-label="close"
540
+ >
541
+ <CloseIcon />
542
+ </IconButton>
543
+ </Box>
544
+ </Box>
545
+ </DialogTitle>
546
+ <DialogContent
547
+ dividers
548
+ sx={{
549
+ padding: 0,
550
+ }}
551
+ >
552
+ {isLoadingContent ? (
553
+ <Box sx={{ display: "flex", justifyContent: "center", my: 4 }}>
554
+ <CircularProgress />
555
+ </Box>
556
+ ) : (
557
+ <Box
558
+ sx={{
559
+ maxHeight: "60vh",
560
+ overflow: "auto",
561
+ whiteSpace: "pre-wrap",
562
+ fontFamily: "monospace",
563
+ fontSize: "0.875rem",
564
+ p: 2.5,
565
+ }}
566
+ >
567
+ {documentContent}
568
+ </Box>
569
+ )}
570
+ </DialogContent>
571
+ </Dialog>
572
  </Box>
573
  );
574
  }
frontend/src/components/BenchmarkGenerator.jsx CHANGED
@@ -6,8 +6,8 @@ import LogDisplay from "./LogDisplay";
6
  import { useNavigate, useSearchParams } from "react-router-dom";
7
  import API_CONFIG from "../config/api";
8
 
9
- // Temps de simulation en millisecondes pour les documents précalculés
10
- const SIMULATION_DURATION = 80000; // 20 secondes
11
 
12
  // Define all benchmark steps in sequence
13
  const BENCHMARK_STEPS = [
@@ -93,7 +93,7 @@ const BenchmarkGenerator = ({ sessionId, isDefaultDocument, onComplete }) => {
93
  // Set start time
94
  startTimeRef.current = Date.now();
95
 
96
- // Référence pour le timeout
97
  let timeoutRef = null;
98
 
99
  // Start timer
@@ -103,15 +103,15 @@ const BenchmarkGenerator = ({ sessionId, isDefaultDocument, onComplete }) => {
103
  );
104
  setElapsedTime(timeElapsed);
105
 
106
- // Vérifier si le temps écoulé dépasse 8 minutes (480 secondes) et que nous ne sommes pas en mode simulation
107
  if (timeElapsed > 480 && !isDefault && !generationComplete) {
108
- // Afficher un message d'erreur en cas de timeout
109
  setError(
110
  "The benchmark generation is taking too long. The demo is currently under heavy load, please try again later."
111
  );
112
  setGenerationComplete(true);
113
 
114
- // Nettoyer les intervalles
115
  if (pollingIntervalRef.current) {
116
  clearInterval(pollingIntervalRef.current);
117
  }
@@ -122,7 +122,7 @@ const BenchmarkGenerator = ({ sessionId, isDefaultDocument, onComplete }) => {
122
  }
123
  }, 1000);
124
 
125
- // Gestionnaire pour détecter quand la page redevient visible
126
  const handleVisibilityChange = () => {
127
  if (
128
  document.visibilityState === "visible" &&
@@ -130,10 +130,10 @@ const BenchmarkGenerator = ({ sessionId, isDefaultDocument, onComplete }) => {
130
  !generationComplete
131
  ) {
132
  console.log("Page became visible, checking for missed steps...");
133
- // Force une nouvelle requête pour récupérer les logs
134
  const checkCurrentState = async () => {
135
  try {
136
- // D'abord essayer de récupérer les logs de benchmark
137
  const logsResponse = await fetch(
138
  `${API_CONFIG.BASE_URL}/benchmark-logs/${sessionId}`
139
  );
@@ -144,7 +144,7 @@ const BenchmarkGenerator = ({ sessionId, isDefaultDocument, onComplete }) => {
144
  setGenerationLogs(logsResult.logs);
145
  }
146
 
147
- // Si la tâche est terminée, mettre à jour l'état
148
  if (logsResult.is_completed) {
149
  setGenerationComplete(true);
150
  if (pollingIntervalRef.current) {
@@ -159,7 +159,7 @@ const BenchmarkGenerator = ({ sessionId, isDefaultDocument, onComplete }) => {
159
  }
160
  }
161
  } else {
162
- // Si la tâche de benchmark n'existe pas, essayer les logs de configuration
163
  const configResponse = await fetch(
164
  `${API_CONFIG.BASE_URL}/config-logs/${sessionId}`
165
  );
@@ -180,7 +180,7 @@ const BenchmarkGenerator = ({ sessionId, isDefaultDocument, onComplete }) => {
180
  }
181
  };
182
 
183
- // Ajouter l'écouteur pour le changement de visibilité
184
  document.addEventListener("visibilitychange", handleVisibilityChange);
185
 
186
  if (isDefault) {
@@ -253,11 +253,27 @@ const BenchmarkGenerator = ({ sessionId, isDefaultDocument, onComplete }) => {
253
  useEffect(() => {
254
  if (generationLogs.length === 0) return;
255
 
256
- // Recalculer complètement les étapes complétées à chaque fois
257
- // au lieu de simplement ajouter les nouvelles étapes
258
  const newCompletedSteps = [];
259
 
260
- // Identifier toutes les étapes complétées dans tous les logs
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  generationLogs.forEach((log) => {
262
  const match = log.match(/\[SUCCESS\] Stage completed: (\w+)/);
263
  if (match && match[1]) {
@@ -271,37 +287,37 @@ const BenchmarkGenerator = ({ sessionId, isDefaultDocument, onComplete }) => {
271
  }
272
  });
273
 
274
- // Déterminer l'étape active basée sur les étapes complétées
275
  let newActiveStep = activeStep;
276
 
277
  if (newCompletedSteps.length > 0) {
278
- // Trouver l'étape la plus avancée dans les logs
279
  const maxCompletedStepIndex = Math.max(
280
  ...newCompletedSteps.map((step) => BENCHMARK_STEPS.indexOf(step))
281
  );
282
- // Passer à l'étape suivante
283
  const calculatedStep = maxCompletedStepIndex + 1;
284
 
285
- // Ne mettre à jour que si la nouvelle étape est plus avancée que l'étape actuelle
286
  if (calculatedStep > activeStep) {
287
  newActiveStep = calculatedStep;
288
  }
289
 
290
- // S'assurer que l'activeStep ne dépasse pas le nombre total d'étapes
291
  if (newActiveStep >= BENCHMARK_STEPS.length) {
292
  newActiveStep = BENCHMARK_STEPS.length;
293
  }
294
  } else if (activeStep === 0) {
295
- // Si aucune étape n'est trouvée et l'étape active est 0, passer à 1
296
  newActiveStep = 1;
297
  }
298
 
299
- // Mettre à jour l'état si les étapes ont changé
300
  if (JSON.stringify(newCompletedSteps) !== JSON.stringify(completedSteps)) {
301
  setCompletedSteps(newCompletedSteps);
302
  }
303
 
304
- // Mettre à jour l'étape active seulement si elle a changé
305
  if (newActiveStep !== activeStep) {
306
  setActiveStep(newActiveStep);
307
  }
@@ -390,16 +406,16 @@ const BenchmarkGenerator = ({ sessionId, isDefaultDocument, onComplete }) => {
390
  if (response.ok) {
391
  setGenerationLogs(result.logs || []);
392
 
393
- // Configurer le polling pour suivre la progression
394
  pollingIntervalRef.current = setInterval(async () => {
395
- // Vérifier si on a déjà terminé
396
  if (generationComplete) {
397
  clearInterval(pollingIntervalRef.current);
398
  return;
399
  }
400
 
401
  try {
402
- // Appeler l'API pour obtenir les derniers logs
403
  const logsResponse = await fetch(
404
  `${API_CONFIG.BASE_URL}/benchmark-progress/${sessionId}`
405
  );
@@ -407,7 +423,7 @@ const BenchmarkGenerator = ({ sessionId, isDefaultDocument, onComplete }) => {
407
  if (logsResponse.ok) {
408
  const logsResult = await logsResponse.json();
409
 
410
- // Mettre à jour les logs s'il y en a de nouveaux
411
  if (
412
  logsResult.logs &&
413
  logsResult.logs.length > generationLogs.length
@@ -415,18 +431,18 @@ const BenchmarkGenerator = ({ sessionId, isDefaultDocument, onComplete }) => {
415
  setGenerationLogs(logsResult.logs);
416
  }
417
 
418
- // Vérifier si la tâche est terminée
419
  if (logsResult.is_completed) {
420
  setGenerationComplete(true);
421
  clearInterval(pollingIntervalRef.current);
422
- // La notification est maintenant gérée dans le useEffect ci-dessus
423
  }
424
  }
425
  } catch (error) {
426
  console.log("Error polling for logs:", error);
427
- // Ne pas arrêter le polling en cas d'erreurs réseau
428
  }
429
- }, 2000); // Interroger toutes les 2 secondes
430
  } else {
431
  // Handle error
432
  setGenerationLogs([`Error: ${result.error || "Unknown error"}`]);
@@ -514,7 +530,7 @@ const BenchmarkGenerator = ({ sessionId, isDefaultDocument, onComplete }) => {
514
  position: "relative",
515
  }}
516
  >
517
- {/* Temps estimé */}
518
  <Box
519
  sx={{
520
  position: "absolute",
@@ -536,7 +552,7 @@ const BenchmarkGenerator = ({ sessionId, isDefaultDocument, onComplete }) => {
536
  fontWeight: 500,
537
  }}
538
  >
539
- Estimated time: ~2 min
540
  </Typography>
541
  </Box>
542
 
 
6
  import { useNavigate, useSearchParams } from "react-router-dom";
7
  import API_CONFIG from "../config/api";
8
 
9
+ // Simulation time in milliseconds for pre-calculated documents
10
+ const SIMULATION_DURATION = 80000; // 20 seconds
11
 
12
  // Define all benchmark steps in sequence
13
  const BENCHMARK_STEPS = [
 
93
  // Set start time
94
  startTimeRef.current = Date.now();
95
 
96
+ // Reference for the timeout
97
  let timeoutRef = null;
98
 
99
  // Start timer
 
103
  );
104
  setElapsedTime(timeElapsed);
105
 
106
+ // Check if the elapsed time exceeds 8 minutes (480 seconds) and we are not in simulation mode
107
  if (timeElapsed > 480 && !isDefault && !generationComplete) {
108
+ // Display an error message in case of timeout
109
  setError(
110
  "The benchmark generation is taking too long. The demo is currently under heavy load, please try again later."
111
  );
112
  setGenerationComplete(true);
113
 
114
+ // Clear intervals
115
  if (pollingIntervalRef.current) {
116
  clearInterval(pollingIntervalRef.current);
117
  }
 
122
  }
123
  }, 1000);
124
 
125
+ // Handler to detect when the page becomes visible again
126
  const handleVisibilityChange = () => {
127
  if (
128
  document.visibilityState === "visible" &&
 
130
  !generationComplete
131
  ) {
132
  console.log("Page became visible, checking for missed steps...");
133
+ // Force a new request to retrieve the logs
134
  const checkCurrentState = async () => {
135
  try {
136
+ // First try to retrieve the benchmark logs
137
  const logsResponse = await fetch(
138
  `${API_CONFIG.BASE_URL}/benchmark-logs/${sessionId}`
139
  );
 
144
  setGenerationLogs(logsResult.logs);
145
  }
146
 
147
+ // If the task is complete, update the state
148
  if (logsResult.is_completed) {
149
  setGenerationComplete(true);
150
  if (pollingIntervalRef.current) {
 
159
  }
160
  }
161
  } else {
162
+ // If the benchmark task does not exist, try the configuration logs
163
  const configResponse = await fetch(
164
  `${API_CONFIG.BASE_URL}/config-logs/${sessionId}`
165
  );
 
180
  }
181
  };
182
 
183
+ // Add the listener for visibility change
184
  document.addEventListener("visibilitychange", handleVisibilityChange);
185
 
186
  if (isDefault) {
 
253
  useEffect(() => {
254
  if (generationLogs.length === 0) return;
255
 
256
+ // Recalculate completed steps completely each time
257
+ // instead of just adding new steps
258
  const newCompletedSteps = [];
259
 
260
+ // Check for rate limiting errors
261
+ const hasRateLimitError = generationLogs.some(
262
+ (log) => log.includes("RATE_LIMIT_EXCEEDED") || log.includes("heavy load")
263
+ );
264
+
265
+ if (hasRateLimitError) {
266
+ setError(
267
+ "The demo is under heavy load at the moment. Please try again later."
268
+ );
269
+ setGenerationComplete(true);
270
+ if (pollingIntervalRef.current) {
271
+ clearInterval(pollingIntervalRef.current);
272
+ }
273
+ return;
274
+ }
275
+
276
+ // Identify all completed steps in all logs
277
  generationLogs.forEach((log) => {
278
  const match = log.match(/\[SUCCESS\] Stage completed: (\w+)/);
279
  if (match && match[1]) {
 
287
  }
288
  });
289
 
290
+ // Determine the active step based on completed steps
291
  let newActiveStep = activeStep;
292
 
293
  if (newCompletedSteps.length > 0) {
294
+ // Find the most advanced step in the logs
295
  const maxCompletedStepIndex = Math.max(
296
  ...newCompletedSteps.map((step) => BENCHMARK_STEPS.indexOf(step))
297
  );
298
+ // Move to the next step
299
  const calculatedStep = maxCompletedStepIndex + 1;
300
 
301
+ // Update only if the new step is more advanced than the current step
302
  if (calculatedStep > activeStep) {
303
  newActiveStep = calculatedStep;
304
  }
305
 
306
+ // Ensure that activeStep does not exceed the total number of steps
307
  if (newActiveStep >= BENCHMARK_STEPS.length) {
308
  newActiveStep = BENCHMARK_STEPS.length;
309
  }
310
  } else if (activeStep === 0) {
311
+ // If no step is found and the active step is 0, move to 1
312
  newActiveStep = 1;
313
  }
314
 
315
+ // Update the state if the steps have changed
316
  if (JSON.stringify(newCompletedSteps) !== JSON.stringify(completedSteps)) {
317
  setCompletedSteps(newCompletedSteps);
318
  }
319
 
320
+ // Update the active step only if it has changed
321
  if (newActiveStep !== activeStep) {
322
  setActiveStep(newActiveStep);
323
  }
 
406
  if (response.ok) {
407
  setGenerationLogs(result.logs || []);
408
 
409
+ // Set up polling to track progress
410
  pollingIntervalRef.current = setInterval(async () => {
411
+ // Check if we have already completed
412
  if (generationComplete) {
413
  clearInterval(pollingIntervalRef.current);
414
  return;
415
  }
416
 
417
  try {
418
+ // Call the API to get the latest logs
419
  const logsResponse = await fetch(
420
  `${API_CONFIG.BASE_URL}/benchmark-progress/${sessionId}`
421
  );
 
423
  if (logsResponse.ok) {
424
  const logsResult = await logsResponse.json();
425
 
426
+ // Update logs if there are new ones
427
  if (
428
  logsResult.logs &&
429
  logsResult.logs.length > generationLogs.length
 
431
  setGenerationLogs(logsResult.logs);
432
  }
433
 
434
+ // Check if the task is complete
435
  if (logsResult.is_completed) {
436
  setGenerationComplete(true);
437
  clearInterval(pollingIntervalRef.current);
438
+ // Notification is now handled in the useEffect above
439
  }
440
  }
441
  } catch (error) {
442
  console.log("Error polling for logs:", error);
443
+ // Do not stop polling in case of network errors
444
  }
445
+ }, 2000); // Poll every 2 seconds
446
  } else {
447
  // Handle error
448
  setGenerationLogs([`Error: ${result.error || "Unknown error"}`]);
 
530
  position: "relative",
531
  }}
532
  >
533
+ {/* Estimated time */}
534
  <Box
535
  sx={{
536
  position: "absolute",
 
552
  fontWeight: 500,
553
  }}
554
  >
555
+ Estimated time: ~ 1 min 30s
556
  </Typography>
557
  </Box>
558
 
frontend/src/pages/BenchmarkGenerationPage.jsx CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect } from "react";
2
  import { Box, CircularProgress } from "@mui/material";
3
  import { useNavigate, useSearchParams, Navigate } from "react-router-dom";
4
  import Intro from "../components/Intro";
@@ -10,6 +10,7 @@ function BenchmarkGenerationPage() {
10
  const sessionId = searchParams.get("session");
11
  const isDefault = searchParams.get("isDefault") === "true";
12
  const [isValidSession, setIsValidSession] = useState(true);
 
13
 
14
  useEffect(() => {
15
  if (!sessionId) {
@@ -19,8 +20,12 @@ function BenchmarkGenerationPage() {
19
 
20
  const handleGenerationComplete = (result) => {
21
  console.log("Benchmark generation completed:", result);
22
- if (result && result.success) {
23
- navigate(`/benchmark-display?session=${sessionId}`);
 
 
 
 
24
  }
25
  };
26
 
 
1
+ import React, { useState, useEffect, useRef } from "react";
2
  import { Box, CircularProgress } from "@mui/material";
3
  import { useNavigate, useSearchParams, Navigate } from "react-router-dom";
4
  import Intro from "../components/Intro";
 
10
  const sessionId = searchParams.get("session");
11
  const isDefault = searchParams.get("isDefault") === "true";
12
  const [isValidSession, setIsValidSession] = useState(true);
13
+ const hasRedirectedRef = useRef(false);
14
 
15
  useEffect(() => {
16
  if (!sessionId) {
 
20
 
21
  const handleGenerationComplete = (result) => {
22
  console.log("Benchmark generation completed:", result);
23
+ if (result && result.success && !hasRedirectedRef.current) {
24
+ hasRedirectedRef.current = true; // Marquer que la redirection a été faite
25
+ // Légère pause avant de naviguer pour éviter les problèmes de synchronisation
26
+ setTimeout(() => {
27
+ navigate(`/benchmark-display?session=${sessionId}`);
28
+ }, 500);
29
  }
30
  };
31