Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
add session clenaup and timeout messages
Browse files- backend/routes/__init__.py +4 -1
- backend/routes/cleanup.py +58 -0
- frontend/server.js +1 -0
- frontend/src/components/BenchmarkEvaluation.jsx +27 -0
- frontend/src/components/BenchmarkGenerator.jsx +48 -0
- frontend/src/components/EvaluationDisplay.jsx +93 -149
- frontend/src/pages/EvaluationDisplayPage.jsx +83 -10
backend/routes/__init__.py
CHANGED
@@ -5,6 +5,7 @@ from .benchmark import router as benchmark_router, active_tasks
|
|
5 |
from .questions import router as questions_router
|
6 |
from .download import router as download_router
|
7 |
from .evaluation import router as evaluation_router, active_evaluation_tasks
|
|
|
8 |
|
9 |
# Exposer les routeurs
|
10 |
routers = [
|
@@ -13,11 +14,13 @@ routers = [
|
|
13 |
benchmark_router,
|
14 |
questions_router,
|
15 |
download_router,
|
16 |
-
evaluation_router
|
|
|
17 |
]
|
18 |
|
19 |
# Référencer les données partagées entre routes
|
20 |
benchmark_router.session_files = session_files
|
|
|
21 |
|
22 |
# Exposer les variables partagées pour main.py
|
23 |
__all__ = ['routers', 'session_files', 'active_tasks', 'active_evaluation_tasks']
|
|
|
5 |
from .questions import router as questions_router
|
6 |
from .download import router as download_router
|
7 |
from .evaluation import router as evaluation_router, active_evaluation_tasks
|
8 |
+
from .cleanup import router as cleanup_router
|
9 |
|
10 |
# Exposer les routeurs
|
11 |
routers = [
|
|
|
14 |
benchmark_router,
|
15 |
questions_router,
|
16 |
download_router,
|
17 |
+
evaluation_router,
|
18 |
+
cleanup_router
|
19 |
]
|
20 |
|
21 |
# Référencer les données partagées entre routes
|
22 |
benchmark_router.session_files = session_files
|
23 |
+
cleanup_router.session_files = session_files
|
24 |
|
25 |
# Exposer les variables partagées pour main.py
|
26 |
__all__ = ['routers', 'session_files', 'active_tasks', 'active_evaluation_tasks']
|
backend/routes/cleanup.py
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import APIRouter, HTTPException
|
2 |
+
import os
|
3 |
+
import shutil
|
4 |
+
from .upload import session_files
|
5 |
+
|
6 |
+
router = APIRouter(tags=["cleanup"])
|
7 |
+
|
8 |
+
# Dossier racine pour les uploads
|
9 |
+
UPLOAD_ROOT = "uploaded_files"
|
10 |
+
|
11 |
+
# Liste des documents de base qui ne doivent pas être supprimés
|
12 |
+
BASE_DOCUMENTS = ["the-bitter-lesson", "hurricane-faq", "pokemon-guide"]
|
13 |
+
|
14 |
+
@router.delete("/cleanup-session/{session_id}")
|
15 |
+
async def cleanup_session(session_id: str):
|
16 |
+
"""
|
17 |
+
Supprime le dossier de session après que l'utilisateur a visualisé les résultats d'évaluation.
|
18 |
+
Ne supprime pas les documents de base.
|
19 |
+
|
20 |
+
Args:
|
21 |
+
session_id: ID de la session à supprimer
|
22 |
+
|
23 |
+
Returns:
|
24 |
+
Dictionary avec statut et message
|
25 |
+
"""
|
26 |
+
# Vérifier si le session_id existe et n'est pas un document de base
|
27 |
+
if session_id in BASE_DOCUMENTS:
|
28 |
+
return {
|
29 |
+
"success": False,
|
30 |
+
"message": f"Cannot delete base document: {session_id}"
|
31 |
+
}
|
32 |
+
|
33 |
+
session_dir = os.path.join(UPLOAD_ROOT, session_id)
|
34 |
+
|
35 |
+
# Vérifier si le dossier existe
|
36 |
+
if not os.path.exists(session_dir):
|
37 |
+
return {
|
38 |
+
"success": False,
|
39 |
+
"message": f"Session directory not found: {session_id}"
|
40 |
+
}
|
41 |
+
|
42 |
+
try:
|
43 |
+
# Supprimer la référence du fichier de session
|
44 |
+
if session_id in session_files:
|
45 |
+
del session_files[session_id]
|
46 |
+
|
47 |
+
# Supprimer le dossier de session
|
48 |
+
shutil.rmtree(session_dir)
|
49 |
+
|
50 |
+
return {
|
51 |
+
"success": True,
|
52 |
+
"message": f"Session cleaned up successfully: {session_id}"
|
53 |
+
}
|
54 |
+
except Exception as e:
|
55 |
+
return {
|
56 |
+
"success": False,
|
57 |
+
"message": f"Error cleaning up session: {str(e)}"
|
58 |
+
}
|
frontend/server.js
CHANGED
@@ -42,6 +42,7 @@ app.use(
|
|
42 |
"/evaluation-logs",
|
43 |
"/evaluation-results",
|
44 |
"/download-dataset",
|
|
|
45 |
],
|
46 |
createProxyMiddleware({
|
47 |
target: backendUrl,
|
|
|
42 |
"/evaluation-logs",
|
43 |
"/evaluation-results",
|
44 |
"/download-dataset",
|
45 |
+
"/cleanup-session",
|
46 |
],
|
47 |
createProxyMiddleware({
|
48 |
target: backendUrl,
|
frontend/src/components/BenchmarkEvaluation.jsx
CHANGED
@@ -282,8 +282,35 @@ const BenchmarkEvaluation = ({ sessionId, isDefaultDocument, onComplete }) => {
|
|
282 |
alignItems: "center",
|
283 |
justifyContent: "center",
|
284 |
minHeight: 200,
|
|
|
285 |
}}
|
286 |
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
287 |
{error ? (
|
288 |
<Alert severity="error" sx={{ width: "100%" }}>
|
289 |
{error}
|
|
|
282 |
alignItems: "center",
|
283 |
justifyContent: "center",
|
284 |
minHeight: 200,
|
285 |
+
position: "relative",
|
286 |
}}
|
287 |
>
|
288 |
+
{/* Temps estimé */}
|
289 |
+
<Box
|
290 |
+
sx={{
|
291 |
+
position: "absolute",
|
292 |
+
top: 12,
|
293 |
+
right: 12,
|
294 |
+
backgroundColor: "rgba(0, 0, 0, 0.04)",
|
295 |
+
borderRadius: "4px",
|
296 |
+
px: 1,
|
297 |
+
py: 0.5,
|
298 |
+
display: "inline-flex",
|
299 |
+
alignItems: "center",
|
300 |
+
}}
|
301 |
+
>
|
302 |
+
<Typography
|
303 |
+
variant="caption"
|
304 |
+
sx={{
|
305 |
+
fontSize: "0.675rem",
|
306 |
+
color: "text.secondary",
|
307 |
+
fontWeight: 500,
|
308 |
+
}}
|
309 |
+
>
|
310 |
+
Estimated time: ~1 min
|
311 |
+
</Typography>
|
312 |
+
</Box>
|
313 |
+
|
314 |
{error ? (
|
315 |
<Alert severity="error" sx={{ width: "100%" }}>
|
316 |
{error}
|
frontend/src/components/BenchmarkGenerator.jsx
CHANGED
@@ -86,12 +86,33 @@ const BenchmarkGenerator = ({ sessionId, isDefaultDocument, onComplete }) => {
|
|
86 |
// Set start time
|
87 |
startTimeRef.current = Date.now();
|
88 |
|
|
|
|
|
|
|
89 |
// Start timer
|
90 |
timerIntervalRef.current = setInterval(() => {
|
91 |
const timeElapsed = Math.floor(
|
92 |
(Date.now() - startTimeRef.current) / 1000
|
93 |
);
|
94 |
setElapsedTime(timeElapsed);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
95 |
}, 1000);
|
96 |
|
97 |
// Gestionnaire pour détecter quand la page redevient visible
|
@@ -483,8 +504,35 @@ const BenchmarkGenerator = ({ sessionId, isDefaultDocument, onComplete }) => {
|
|
483 |
alignItems: "center",
|
484 |
justifyContent: "center",
|
485 |
minHeight: 200,
|
|
|
486 |
}}
|
487 |
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
488 |
{error ? (
|
489 |
<Alert severity="error" sx={{ width: "100%" }}>
|
490 |
{error}
|
|
|
86 |
// Set start time
|
87 |
startTimeRef.current = Date.now();
|
88 |
|
89 |
+
// Référence pour le timeout
|
90 |
+
let timeoutRef = null;
|
91 |
+
|
92 |
// Start timer
|
93 |
timerIntervalRef.current = setInterval(() => {
|
94 |
const timeElapsed = Math.floor(
|
95 |
(Date.now() - startTimeRef.current) / 1000
|
96 |
);
|
97 |
setElapsedTime(timeElapsed);
|
98 |
+
|
99 |
+
// Vérifier si le temps écoulé dépasse 8 minutes (480 secondes) et que nous ne sommes pas en mode simulation
|
100 |
+
if (timeElapsed > 480 && !isDefault && !generationComplete) {
|
101 |
+
// Afficher un message d'erreur en cas de timeout
|
102 |
+
setError(
|
103 |
+
"The benchmark generation is taking too long. The demo is currently under heavy load, please try again later."
|
104 |
+
);
|
105 |
+
setGenerationComplete(true);
|
106 |
+
|
107 |
+
// Nettoyer les intervalles
|
108 |
+
if (pollingIntervalRef.current) {
|
109 |
+
clearInterval(pollingIntervalRef.current);
|
110 |
+
}
|
111 |
+
|
112 |
+
if (timerIntervalRef.current) {
|
113 |
+
clearInterval(timerIntervalRef.current);
|
114 |
+
}
|
115 |
+
}
|
116 |
}, 1000);
|
117 |
|
118 |
// Gestionnaire pour détecter quand la page redevient visible
|
|
|
504 |
alignItems: "center",
|
505 |
justifyContent: "center",
|
506 |
minHeight: 200,
|
507 |
+
position: "relative",
|
508 |
}}
|
509 |
>
|
510 |
+
{/* Temps estimé */}
|
511 |
+
<Box
|
512 |
+
sx={{
|
513 |
+
position: "absolute",
|
514 |
+
top: 12,
|
515 |
+
right: 12,
|
516 |
+
backgroundColor: "rgba(0, 0, 0, 0.04)",
|
517 |
+
borderRadius: "4px",
|
518 |
+
px: 1,
|
519 |
+
py: 0.5,
|
520 |
+
display: "inline-flex",
|
521 |
+
alignItems: "center",
|
522 |
+
}}
|
523 |
+
>
|
524 |
+
<Typography
|
525 |
+
variant="caption"
|
526 |
+
sx={{
|
527 |
+
fontSize: "0.675rem",
|
528 |
+
color: "text.secondary",
|
529 |
+
fontWeight: 500,
|
530 |
+
}}
|
531 |
+
>
|
532 |
+
Estimated time: ~2 min
|
533 |
+
</Typography>
|
534 |
+
</Box>
|
535 |
+
|
536 |
{error ? (
|
537 |
<Alert severity="error" sx={{ width: "100%" }}>
|
538 |
{error}
|
frontend/src/components/EvaluationDisplay.jsx
CHANGED
@@ -85,47 +85,7 @@ const getMedalStyle = (rank) => {
|
|
85 |
};
|
86 |
};
|
87 |
|
88 |
-
const EvaluationDisplay = ({ sessionId }) => {
|
89 |
-
const [results, setResults] = useState(null);
|
90 |
-
const [loading, setLoading] = useState(true);
|
91 |
-
const [error, setError] = useState(null);
|
92 |
-
|
93 |
-
useEffect(() => {
|
94 |
-
const fetchEvaluationResults = async () => {
|
95 |
-
if (!sessionId) {
|
96 |
-
setError("No session ID provided");
|
97 |
-
setLoading(false);
|
98 |
-
return;
|
99 |
-
}
|
100 |
-
|
101 |
-
try {
|
102 |
-
// Fetch evaluation results from the API
|
103 |
-
const response = await fetch(
|
104 |
-
`http://localhost:3001/evaluation-results/${sessionId}`
|
105 |
-
);
|
106 |
-
|
107 |
-
if (!response.ok) {
|
108 |
-
throw new Error(`Failed to fetch results: ${response.status}`);
|
109 |
-
}
|
110 |
-
|
111 |
-
const data = await response.json();
|
112 |
-
|
113 |
-
if (!data.success) {
|
114 |
-
throw new Error(data.message || "Failed to fetch evaluation results");
|
115 |
-
}
|
116 |
-
|
117 |
-
setResults(data.results);
|
118 |
-
} catch (err) {
|
119 |
-
console.error("Error fetching evaluation results:", err);
|
120 |
-
setError(err.message);
|
121 |
-
} finally {
|
122 |
-
setLoading(false);
|
123 |
-
}
|
124 |
-
};
|
125 |
-
|
126 |
-
fetchEvaluationResults();
|
127 |
-
}, [sessionId]);
|
128 |
-
|
129 |
// Format accuracy as percentage
|
130 |
const formatAccuracy = (value) => {
|
131 |
return `${(value * 100).toFixed(2)}\u2009%`;
|
@@ -152,34 +112,6 @@ const EvaluationDisplay = ({ sessionId }) => {
|
|
152 |
return `${seconds.toFixed(2)}s`;
|
153 |
};
|
154 |
|
155 |
-
if (loading) {
|
156 |
-
return (
|
157 |
-
<Box
|
158 |
-
sx={{
|
159 |
-
width: "100%",
|
160 |
-
mt: 4,
|
161 |
-
mb: 4,
|
162 |
-
display: "flex",
|
163 |
-
flexDirection: "column",
|
164 |
-
alignItems: "center",
|
165 |
-
}}
|
166 |
-
>
|
167 |
-
<Typography variant="h5" gutterBottom>
|
168 |
-
Loading Evaluation Results...
|
169 |
-
</Typography>
|
170 |
-
<CircularProgress />
|
171 |
-
</Box>
|
172 |
-
);
|
173 |
-
}
|
174 |
-
|
175 |
-
if (error) {
|
176 |
-
return (
|
177 |
-
<Alert severity="error" sx={{ mt: 4, mb: 4 }}>
|
178 |
-
{error}
|
179 |
-
</Alert>
|
180 |
-
);
|
181 |
-
}
|
182 |
-
|
183 |
if (
|
184 |
!results ||
|
185 |
!results.models_comparison ||
|
@@ -187,7 +119,21 @@ const EvaluationDisplay = ({ sessionId }) => {
|
|
187 |
) {
|
188 |
return (
|
189 |
<Alert severity="info" sx={{ mt: 4, mb: 4 }}>
|
190 |
-
No evaluation results found for this benchmark.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
191 |
</Alert>
|
192 |
);
|
193 |
}
|
@@ -241,98 +187,96 @@ const EvaluationDisplay = ({ sessionId }) => {
|
|
241 |
</TableRow>
|
242 |
</TableHead>
|
243 |
<TableBody>
|
244 |
-
{
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
249 |
sx={{
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
}}
|
254 |
>
|
255 |
-
<
|
256 |
-
<Box sx={{ display: "flex", alignItems: "center" }}>
|
257 |
-
<Box sx={getMedalStyle(index + 1)}>{index + 1}</Box>
|
258 |
-
</Box>
|
259 |
-
</TableCell>
|
260 |
-
<TableCell component="th" scope="row">
|
261 |
-
<Tooltip title={model.model_name} placement="top">
|
262 |
-
<Link
|
263 |
-
href={`https://huggingface.co/${model.model_name}`}
|
264 |
-
target="_blank"
|
265 |
-
rel="noopener noreferrer"
|
266 |
-
sx={{
|
267 |
-
textDecoration: "none",
|
268 |
-
"&:hover": {
|
269 |
-
textDecoration: "underline",
|
270 |
-
},
|
271 |
-
display: "flex",
|
272 |
-
alignItems: "center",
|
273 |
-
}}
|
274 |
-
>
|
275 |
-
{model.model_name.length > 40
|
276 |
-
? `${model.model_name.substring(0, 40)}...`
|
277 |
-
: model.model_name}
|
278 |
-
<OpenInNewIcon sx={{ ml: 0.5, fontSize: 16 }} />
|
279 |
-
</Link>
|
280 |
-
</Tooltip>
|
281 |
-
</TableCell>
|
282 |
-
<TableCell
|
283 |
-
align="left"
|
284 |
sx={{
|
285 |
-
|
286 |
-
|
287 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
288 |
}}
|
289 |
>
|
290 |
<Box
|
291 |
sx={{
|
292 |
position: "absolute",
|
293 |
-
width: "100%",
|
294 |
-
height: "100%",
|
295 |
left: 0,
|
296 |
top: 0,
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
301 |
}}
|
302 |
>
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
<Typography
|
316 |
-
sx={{
|
317 |
-
position: "relative",
|
318 |
-
zIndex: 1,
|
319 |
-
fontWeight: model.accuracy > 0.7 ? "bold" : "normal",
|
320 |
-
py: 1.5,
|
321 |
-
textAlign: "left",
|
322 |
-
}}
|
323 |
-
>
|
324 |
-
{formatAccuracy(model.accuracy)}
|
325 |
-
</Typography>
|
326 |
-
</Box>
|
327 |
-
</TableCell>
|
328 |
-
<TableCell align="left">
|
329 |
-
{formatTime(model.evaluation_time)}
|
330 |
-
</TableCell>
|
331 |
-
<TableCell align="right">
|
332 |
-
<span style={{ color: "green" }}>✓ Success</span>
|
333 |
-
</TableCell>
|
334 |
-
</TableRow>
|
335 |
-
))}
|
336 |
</TableBody>
|
337 |
</Table>
|
338 |
</TableContainer>
|
|
|
85 |
};
|
86 |
};
|
87 |
|
88 |
+
const EvaluationDisplay = ({ sessionId, results }) => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
// Format accuracy as percentage
|
90 |
const formatAccuracy = (value) => {
|
91 |
return `${(value * 100).toFixed(2)}\u2009%`;
|
|
|
112 |
return `${seconds.toFixed(2)}s`;
|
113 |
};
|
114 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
115 |
if (
|
116 |
!results ||
|
117 |
!results.models_comparison ||
|
|
|
119 |
) {
|
120 |
return (
|
121 |
<Alert severity="info" sx={{ mt: 4, mb: 4 }}>
|
122 |
+
No evaluation results found for this benchmark. The demo is currently
|
123 |
+
under heavy load, please try again later.
|
124 |
+
</Alert>
|
125 |
+
);
|
126 |
+
}
|
127 |
+
|
128 |
+
// Vérifier s'il n'y a aucun modèle réussi
|
129 |
+
const successfulModels = results.models_comparison.filter(
|
130 |
+
(model) => model.success
|
131 |
+
);
|
132 |
+
if (successfulModels.length === 0) {
|
133 |
+
return (
|
134 |
+
<Alert severity="warning" sx={{ mt: 4, mb: 4 }}>
|
135 |
+
All model evaluations failed. The demo is currently under heavy load,
|
136 |
+
please try again later.
|
137 |
</Alert>
|
138 |
);
|
139 |
}
|
|
|
187 |
</TableRow>
|
188 |
</TableHead>
|
189 |
<TableBody>
|
190 |
+
{successfulModels.map((model, index) => (
|
191 |
+
<TableRow
|
192 |
+
key={`${model.model_name}-${model.provider}`}
|
193 |
+
sx={{
|
194 |
+
"&:nth-of-type(even)": {
|
195 |
+
backgroundColor: "rgba(0, 0, 0, 0.02)",
|
196 |
+
},
|
197 |
+
}}
|
198 |
+
>
|
199 |
+
<TableCell>
|
200 |
+
<Box sx={{ display: "flex", alignItems: "center" }}>
|
201 |
+
<Box sx={getMedalStyle(index + 1)}>{index + 1}</Box>
|
202 |
+
</Box>
|
203 |
+
</TableCell>
|
204 |
+
<TableCell component="th" scope="row">
|
205 |
+
<Tooltip title={model.model_name} placement="top">
|
206 |
+
<Link
|
207 |
+
href={`https://huggingface.co/${model.model_name}`}
|
208 |
+
target="_blank"
|
209 |
+
rel="noopener noreferrer"
|
210 |
+
sx={{
|
211 |
+
textDecoration: "none",
|
212 |
+
"&:hover": {
|
213 |
+
textDecoration: "underline",
|
214 |
+
},
|
215 |
+
display: "flex",
|
216 |
+
alignItems: "center",
|
217 |
+
}}
|
218 |
+
>
|
219 |
+
{model.model_name.length > 40
|
220 |
+
? `${model.model_name.substring(0, 40)}...`
|
221 |
+
: model.model_name}
|
222 |
+
<OpenInNewIcon sx={{ ml: 0.5, fontSize: 16 }} />
|
223 |
+
</Link>
|
224 |
+
</Tooltip>
|
225 |
+
</TableCell>
|
226 |
+
<TableCell
|
227 |
+
align="left"
|
228 |
sx={{
|
229 |
+
padding: 0,
|
230 |
+
position: "relative",
|
231 |
+
overflow: "hidden",
|
232 |
}}
|
233 |
>
|
234 |
+
<Box
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
235 |
sx={{
|
236 |
+
position: "absolute",
|
237 |
+
width: "100%",
|
238 |
+
height: "100%",
|
239 |
+
left: 0,
|
240 |
+
top: 0,
|
241 |
+
display: "flex",
|
242 |
+
alignItems: "center",
|
243 |
+
justifyContent: "flex-start",
|
244 |
+
pl: 2,
|
245 |
}}
|
246 |
>
|
247 |
<Box
|
248 |
sx={{
|
249 |
position: "absolute",
|
|
|
|
|
250 |
left: 0,
|
251 |
top: 0,
|
252 |
+
height: "100%",
|
253 |
+
width: `${model.accuracy * 100}%`,
|
254 |
+
backgroundColor: getColorForScore(model.accuracy),
|
255 |
+
opacity: 0.2,
|
256 |
+
zIndex: 0,
|
257 |
+
}}
|
258 |
+
/>
|
259 |
+
<Typography
|
260 |
+
sx={{
|
261 |
+
position: "relative",
|
262 |
+
zIndex: 1,
|
263 |
+
fontWeight: model.accuracy > 0.7 ? "bold" : "normal",
|
264 |
+
py: 1.5,
|
265 |
+
textAlign: "left",
|
266 |
}}
|
267 |
>
|
268 |
+
{formatAccuracy(model.accuracy)}
|
269 |
+
</Typography>
|
270 |
+
</Box>
|
271 |
+
</TableCell>
|
272 |
+
<TableCell align="left">
|
273 |
+
{formatTime(model.evaluation_time)}
|
274 |
+
</TableCell>
|
275 |
+
<TableCell align="right">
|
276 |
+
<span style={{ color: "green" }}>✓ Success</span>
|
277 |
+
</TableCell>
|
278 |
+
</TableRow>
|
279 |
+
))}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
280 |
</TableBody>
|
281 |
</Table>
|
282 |
</TableContainer>
|
frontend/src/pages/EvaluationDisplayPage.jsx
CHANGED
@@ -1,19 +1,26 @@
|
|
1 |
import React, { useState, useEffect } from "react";
|
2 |
-
import { Box, CircularProgress } from "@mui/material";
|
3 |
import { useSearchParams, Navigate } from "react-router-dom";
|
4 |
import Intro from "../components/Intro";
|
5 |
import EvaluationDisplay from "../components/EvaluationDisplay";
|
6 |
import { useThemeMode } from "../hooks/useThemeMode";
|
7 |
import getTheme from "../config/theme";
|
|
|
8 |
|
9 |
function EvaluationDisplayPage() {
|
10 |
const [searchParams] = useSearchParams();
|
11 |
const sessionId = searchParams.get("session");
|
12 |
const [isValidSession, setIsValidSession] = useState(true);
|
13 |
const [isLoading, setIsLoading] = useState(true);
|
|
|
|
|
14 |
const { mode } = useThemeMode();
|
15 |
const theme = getTheme(mode);
|
16 |
|
|
|
|
|
|
|
|
|
17 |
useEffect(() => {
|
18 |
if (!sessionId) {
|
19 |
console.log(
|
@@ -23,29 +30,88 @@ function EvaluationDisplayPage() {
|
|
23 |
return;
|
24 |
}
|
25 |
|
26 |
-
const
|
27 |
try {
|
28 |
-
|
29 |
-
|
|
|
30 |
);
|
31 |
|
32 |
-
if (!
|
33 |
console.error(
|
34 |
-
`Session invalide ou erreur serveur: ${
|
35 |
);
|
36 |
setIsValidSession(false);
|
|
|
37 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
} catch (error) {
|
39 |
-
console.error("
|
40 |
-
|
41 |
} finally {
|
42 |
setIsLoading(false);
|
43 |
}
|
44 |
};
|
45 |
|
46 |
-
|
47 |
}, [sessionId]);
|
48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
if (!isValidSession) {
|
50 |
return <Navigate to="/" />;
|
51 |
}
|
@@ -65,6 +131,10 @@ function EvaluationDisplayPage() {
|
|
65 |
>
|
66 |
<CircularProgress size={60} />
|
67 |
</Box>
|
|
|
|
|
|
|
|
|
68 |
) : (
|
69 |
<Box
|
70 |
sx={{
|
@@ -74,7 +144,10 @@ function EvaluationDisplayPage() {
|
|
74 |
bgcolor: "background.paper",
|
75 |
}}
|
76 |
>
|
77 |
-
<EvaluationDisplay
|
|
|
|
|
|
|
78 |
</Box>
|
79 |
)}
|
80 |
</>
|
|
|
1 |
import React, { useState, useEffect } from "react";
|
2 |
+
import { Box, CircularProgress, Alert } from "@mui/material";
|
3 |
import { useSearchParams, Navigate } from "react-router-dom";
|
4 |
import Intro from "../components/Intro";
|
5 |
import EvaluationDisplay from "../components/EvaluationDisplay";
|
6 |
import { useThemeMode } from "../hooks/useThemeMode";
|
7 |
import getTheme from "../config/theme";
|
8 |
+
import API_CONFIG from "../config/api";
|
9 |
|
10 |
function EvaluationDisplayPage() {
|
11 |
const [searchParams] = useSearchParams();
|
12 |
const sessionId = searchParams.get("session");
|
13 |
const [isValidSession, setIsValidSession] = useState(true);
|
14 |
const [isLoading, setIsLoading] = useState(true);
|
15 |
+
const [evaluationResults, setEvaluationResults] = useState(null);
|
16 |
+
const [error, setError] = useState(null);
|
17 |
const { mode } = useThemeMode();
|
18 |
const theme = getTheme(mode);
|
19 |
|
20 |
+
// Liste des documents de base qui ne doivent pas être supprimés
|
21 |
+
const baseDocuments = ["the-bitter-lesson", "hurricane-faq", "pokemon-guide"];
|
22 |
+
const isBaseDocument = baseDocuments.includes(sessionId);
|
23 |
+
|
24 |
useEffect(() => {
|
25 |
if (!sessionId) {
|
26 |
console.log(
|
|
|
30 |
return;
|
31 |
}
|
32 |
|
33 |
+
const fetchEvaluationResults = async () => {
|
34 |
try {
|
35 |
+
// Vérifier d'abord si la session existe
|
36 |
+
const sessionCheckResponse = await fetch(
|
37 |
+
`${API_CONFIG.BASE_URL}/benchmark-questions/${sessionId}`
|
38 |
);
|
39 |
|
40 |
+
if (!sessionCheckResponse.ok) {
|
41 |
console.error(
|
42 |
+
`Session invalide ou erreur serveur: ${sessionCheckResponse.status}`
|
43 |
);
|
44 |
setIsValidSession(false);
|
45 |
+
return;
|
46 |
}
|
47 |
+
|
48 |
+
// Récupérer les résultats d'évaluation
|
49 |
+
const evalResponse = await fetch(
|
50 |
+
`${API_CONFIG.BASE_URL}/evaluation-results/${sessionId}`
|
51 |
+
);
|
52 |
+
|
53 |
+
if (!evalResponse.ok) {
|
54 |
+
setError(`Failed to fetch results: ${evalResponse.status}`);
|
55 |
+
setIsLoading(false);
|
56 |
+
return;
|
57 |
+
}
|
58 |
+
|
59 |
+
const data = await evalResponse.json();
|
60 |
+
|
61 |
+
if (!data.success) {
|
62 |
+
setError(data.message || "Failed to fetch evaluation results");
|
63 |
+
setIsLoading(false);
|
64 |
+
return;
|
65 |
+
}
|
66 |
+
|
67 |
+
setEvaluationResults(data.results);
|
68 |
} catch (error) {
|
69 |
+
console.error("Error fetching evaluation results:", error);
|
70 |
+
setError(error.message);
|
71 |
} finally {
|
72 |
setIsLoading(false);
|
73 |
}
|
74 |
};
|
75 |
|
76 |
+
fetchEvaluationResults();
|
77 |
}, [sessionId]);
|
78 |
|
79 |
+
// Effet pour nettoyer le dossier de session après avoir affiché les résultats
|
80 |
+
useEffect(() => {
|
81 |
+
// Ne pas nettoyer si c'est un document de base ou si les résultats ne sont pas encore chargés
|
82 |
+
if (isBaseDocument || isLoading || !evaluationResults) {
|
83 |
+
return;
|
84 |
+
}
|
85 |
+
|
86 |
+
// Fonction pour supprimer le dossier de session
|
87 |
+
const cleanupSession = async () => {
|
88 |
+
try {
|
89 |
+
const response = await fetch(
|
90 |
+
`${API_CONFIG.BASE_URL}/cleanup-session/${sessionId}`,
|
91 |
+
{
|
92 |
+
method: "DELETE",
|
93 |
+
}
|
94 |
+
);
|
95 |
+
|
96 |
+
if (response.ok) {
|
97 |
+
console.log(`Session ${sessionId} cleaned up successfully`);
|
98 |
+
} else {
|
99 |
+
console.warn(`Failed to clean up session ${sessionId}`);
|
100 |
+
}
|
101 |
+
} catch (error) {
|
102 |
+
console.error("Error cleaning up session:", error);
|
103 |
+
}
|
104 |
+
};
|
105 |
+
|
106 |
+
// Appeler la fonction après un délai pour s'assurer que l'utilisateur a eu le temps de voir les résultats
|
107 |
+
const cleanupTimeout = setTimeout(() => {
|
108 |
+
cleanupSession();
|
109 |
+
}, 2000);
|
110 |
+
|
111 |
+
// Nettoyer le timeout si le composant est démonté
|
112 |
+
return () => clearTimeout(cleanupTimeout);
|
113 |
+
}, [sessionId, isBaseDocument, isLoading, evaluationResults]);
|
114 |
+
|
115 |
if (!isValidSession) {
|
116 |
return <Navigate to="/" />;
|
117 |
}
|
|
|
131 |
>
|
132 |
<CircularProgress size={60} />
|
133 |
</Box>
|
134 |
+
) : error ? (
|
135 |
+
<Alert severity="error" sx={{ mt: 4, mb: 4 }}>
|
136 |
+
{error}
|
137 |
+
</Alert>
|
138 |
) : (
|
139 |
<Box
|
140 |
sx={{
|
|
|
144 |
bgcolor: "background.paper",
|
145 |
}}
|
146 |
>
|
147 |
+
<EvaluationDisplay
|
148 |
+
sessionId={sessionId}
|
149 |
+
results={evaluationResults}
|
150 |
+
/>
|
151 |
</Box>
|
152 |
)}
|
153 |
</>
|