Spaces:
Build error
Build error
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 |
|