Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
update error message and avoid double benchmark generation
Browse files- backend/{tests/clean_and_restart_eval.py → clean_and_restart_eval.py} +0 -0
- backend/routes/benchmark.py +18 -0
- backend/tasks/create_bench.py +19 -1
- backend/tasks/create_bench_config_file.py +4 -0
- frontend/src/components/BenchmarkCreateForm.jsx +148 -10
- frontend/src/components/BenchmarkGenerator.jsx +49 -33
- frontend/src/pages/BenchmarkGenerationPage.jsx +8 -3
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 |
-
|
|
|
|
|
|
|
|
|
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="
|
284 |
<IconButton
|
285 |
onClick={(e) => {
|
286 |
e.stopPropagation();
|
287 |
-
|
288 |
}}
|
289 |
sx={{
|
290 |
position: "absolute",
|
291 |
top: 4,
|
292 |
right: 4,
|
293 |
color: "text.secondary",
|
294 |
-
opacity: 0.
|
295 |
"&:hover": {
|
296 |
-
opacity:
|
297 |
backgroundColor: alpha(theme.palette.primary.main, 0.05),
|
298 |
},
|
299 |
-
padding: 0.
|
300 |
"& .MuiSvgIcon-root": {
|
301 |
-
fontSize:
|
302 |
},
|
303 |
}}
|
304 |
-
disabled={
|
305 |
>
|
306 |
-
{
|
307 |
-
<CircularProgress size={
|
308 |
) : (
|
309 |
-
<
|
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 |
-
//
|
10 |
-
const SIMULATION_DURATION = 80000; // 20
|
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 |
-
//
|
97 |
let timeoutRef = null;
|
98 |
|
99 |
// Start timer
|
@@ -103,15 +103,15 @@ const BenchmarkGenerator = ({ sessionId, isDefaultDocument, onComplete }) => {
|
|
103 |
);
|
104 |
setElapsedTime(timeElapsed);
|
105 |
|
106 |
-
//
|
107 |
if (timeElapsed > 480 && !isDefault && !generationComplete) {
|
108 |
-
//
|
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 |
-
//
|
115 |
if (pollingIntervalRef.current) {
|
116 |
clearInterval(pollingIntervalRef.current);
|
117 |
}
|
@@ -122,7 +122,7 @@ const BenchmarkGenerator = ({ sessionId, isDefaultDocument, onComplete }) => {
|
|
122 |
}
|
123 |
}, 1000);
|
124 |
|
125 |
-
//
|
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
|
134 |
const checkCurrentState = async () => {
|
135 |
try {
|
136 |
-
//
|
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 |
-
//
|
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 |
-
//
|
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 |
-
//
|
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 |
-
//
|
257 |
-
//
|
258 |
const newCompletedSteps = [];
|
259 |
|
260 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
//
|
275 |
let newActiveStep = activeStep;
|
276 |
|
277 |
if (newCompletedSteps.length > 0) {
|
278 |
-
//
|
279 |
const maxCompletedStepIndex = Math.max(
|
280 |
...newCompletedSteps.map((step) => BENCHMARK_STEPS.indexOf(step))
|
281 |
);
|
282 |
-
//
|
283 |
const calculatedStep = maxCompletedStepIndex + 1;
|
284 |
|
285 |
-
//
|
286 |
if (calculatedStep > activeStep) {
|
287 |
newActiveStep = calculatedStep;
|
288 |
}
|
289 |
|
290 |
-
//
|
291 |
if (newActiveStep >= BENCHMARK_STEPS.length) {
|
292 |
newActiveStep = BENCHMARK_STEPS.length;
|
293 |
}
|
294 |
} else if (activeStep === 0) {
|
295 |
-
//
|
296 |
newActiveStep = 1;
|
297 |
}
|
298 |
|
299 |
-
//
|
300 |
if (JSON.stringify(newCompletedSteps) !== JSON.stringify(completedSteps)) {
|
301 |
setCompletedSteps(newCompletedSteps);
|
302 |
}
|
303 |
|
304 |
-
//
|
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 |
-
//
|
394 |
pollingIntervalRef.current = setInterval(async () => {
|
395 |
-
//
|
396 |
if (generationComplete) {
|
397 |
clearInterval(pollingIntervalRef.current);
|
398 |
return;
|
399 |
}
|
400 |
|
401 |
try {
|
402 |
-
//
|
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 |
-
//
|
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 |
-
//
|
419 |
if (logsResult.is_completed) {
|
420 |
setGenerationComplete(true);
|
421 |
clearInterval(pollingIntervalRef.current);
|
422 |
-
//
|
423 |
}
|
424 |
}
|
425 |
} catch (error) {
|
426 |
console.log("Error polling for logs:", error);
|
427 |
-
//
|
428 |
}
|
429 |
-
}, 2000); //
|
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 |
-
{/*
|
518 |
<Box
|
519 |
sx={{
|
520 |
position: "absolute",
|
@@ -536,7 +552,7 @@ const BenchmarkGenerator = ({ sessionId, isDefaultDocument, onComplete }) => {
|
|
536 |
fontWeight: 500,
|
537 |
}}
|
538 |
>
|
539 |
-
Estimated time: ~
|
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 |
-
|
|
|
|
|
|
|
|
|
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 |
|