|
import React, { useState, useEffect, useRef } from "react"; |
|
import { |
|
Box, |
|
Typography, |
|
CircularProgress, |
|
Alert, |
|
Paper, |
|
Divider, |
|
Button, |
|
} from "@mui/material"; |
|
import AccessTimeIcon from "@mui/icons-material/AccessTime"; |
|
import LogDisplay from "./LogDisplay"; |
|
import { useNavigate } from "react-router-dom"; |
|
|
|
|
|
const EVALUATION_STEPS = [ |
|
"preparation", |
|
"model_evaluation", |
|
"results_compilation", |
|
]; |
|
|
|
|
|
const STEP_LABELS = { |
|
preparation: "Preparation", |
|
model_evaluation: "Model Evaluation", |
|
results_compilation: "Results Compilation", |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const BenchmarkEvaluation = ({ sessionId, onComplete }) => { |
|
const [evaluating, setEvaluating] = useState(false); |
|
const [evaluationComplete, setEvaluationComplete] = useState(false); |
|
const [evaluationLogs, setEvaluationLogs] = useState([]); |
|
const [error, setError] = useState(null); |
|
const [currentPhase, setCurrentPhase] = useState("initializing"); |
|
const [completedSteps, setCompletedSteps] = useState([]); |
|
const [activeStep, setActiveStep] = useState(0); |
|
const [elapsedTime, setElapsedTime] = useState(0); |
|
|
|
const pollingIntervalRef = useRef(null); |
|
const timerIntervalRef = useRef(null); |
|
const startTimeRef = useRef(null); |
|
|
|
const navigate = useNavigate(); |
|
|
|
|
|
useEffect(() => { |
|
|
|
startTimeRef.current = Date.now(); |
|
|
|
|
|
timerIntervalRef.current = setInterval(() => { |
|
const timeElapsed = Math.floor( |
|
(Date.now() - startTimeRef.current) / 1000 |
|
); |
|
setElapsedTime(timeElapsed); |
|
}, 1000); |
|
|
|
startEvaluation(); |
|
|
|
|
|
return () => { |
|
if (pollingIntervalRef.current) { |
|
clearInterval(pollingIntervalRef.current); |
|
} |
|
if (timerIntervalRef.current) { |
|
clearInterval(timerIntervalRef.current); |
|
} |
|
}; |
|
}, []); |
|
|
|
|
|
useEffect(() => { |
|
if (evaluationLogs.length === 0) return; |
|
|
|
|
|
const newCompletedSteps = [...completedSteps]; |
|
let newActiveStep = activeStep; |
|
|
|
evaluationLogs.forEach((log) => { |
|
|
|
const match = log.match(/\[SUCCESS\] Stage completed: (\w+)/); |
|
if (match && match[1]) { |
|
const completedStep = match[1].trim(); |
|
if ( |
|
EVALUATION_STEPS.includes(completedStep) && |
|
!newCompletedSteps.includes(completedStep) |
|
) { |
|
newCompletedSteps.push(completedStep); |
|
|
|
const stepIndex = EVALUATION_STEPS.indexOf(completedStep); |
|
if (stepIndex >= 0 && stepIndex + 1 > newActiveStep) { |
|
newActiveStep = stepIndex + 1; |
|
if (newActiveStep >= EVALUATION_STEPS.length) { |
|
newActiveStep = EVALUATION_STEPS.length; |
|
} |
|
} |
|
} |
|
} |
|
}); |
|
|
|
|
|
if (newCompletedSteps.length > completedSteps.length) { |
|
setCompletedSteps(newCompletedSteps); |
|
setActiveStep(newActiveStep); |
|
} |
|
|
|
|
|
const recentLogs = evaluationLogs.slice(-10); |
|
|
|
|
|
const isComplete = |
|
recentLogs.some((log) => |
|
log.includes("[SUCCESS] Evaluation completed") |
|
) || |
|
completedSteps.includes("results_compilation") || |
|
newCompletedSteps.includes("results_compilation"); |
|
|
|
if (isComplete) { |
|
setCurrentPhase("complete"); |
|
setEvaluationComplete(true); |
|
|
|
if (pollingIntervalRef.current) { |
|
clearInterval(pollingIntervalRef.current); |
|
} |
|
if (timerIntervalRef.current) { |
|
clearInterval(timerIntervalRef.current); |
|
} |
|
|
|
if (onComplete) { |
|
onComplete({ |
|
success: true, |
|
sessionId, |
|
logs: evaluationLogs, |
|
}); |
|
} |
|
} else if (recentLogs.some((log) => log.includes("Comparing models"))) { |
|
setCurrentPhase("compiling_results"); |
|
} else if (recentLogs.some((log) => log.includes("Starting evaluations"))) { |
|
setCurrentPhase("evaluating"); |
|
} else if (recentLogs.some((log) => log.includes("Initialization"))) { |
|
setCurrentPhase("preparing"); |
|
} |
|
}, [evaluationLogs, completedSteps, activeStep, sessionId, onComplete]); |
|
|
|
|
|
const formatElapsedTime = () => { |
|
const hours = Math.floor(elapsedTime / 3600); |
|
const minutes = Math.floor((elapsedTime % 3600) / 60); |
|
const seconds = elapsedTime % 60; |
|
|
|
return [ |
|
hours.toString().padStart(2, "0"), |
|
minutes.toString().padStart(2, "0"), |
|
seconds.toString().padStart(2, "0"), |
|
].join(":"); |
|
}; |
|
|
|
|
|
const startEvaluation = async () => { |
|
if (!sessionId) { |
|
setError("Missing session ID"); |
|
return; |
|
} |
|
|
|
setEvaluating(true); |
|
setEvaluationLogs([]); |
|
setError(null); |
|
setCurrentPhase("initializing"); |
|
setCompletedSteps([]); |
|
setActiveStep(0); |
|
|
|
try { |
|
|
|
const response = await fetch("http://localhost:3001/evaluate-benchmark", { |
|
method: "POST", |
|
headers: { |
|
"Content-Type": "application/json", |
|
}, |
|
body: JSON.stringify({ |
|
session_id: sessionId, |
|
}), |
|
}); |
|
|
|
const result = await response.json(); |
|
|
|
if (response.ok) { |
|
setEvaluationLogs(result.logs || []); |
|
|
|
|
|
pollingIntervalRef.current = setInterval(async () => { |
|
|
|
if (evaluationComplete) { |
|
clearInterval(pollingIntervalRef.current); |
|
return; |
|
} |
|
|
|
try { |
|
|
|
const logsResponse = await fetch( |
|
`http://localhost:3001/evaluation-logs/${sessionId}` |
|
); |
|
|
|
if (logsResponse.ok) { |
|
const logsResult = await logsResponse.json(); |
|
|
|
|
|
if ( |
|
logsResult.logs && |
|
logsResult.logs.length > evaluationLogs.length |
|
) { |
|
setEvaluationLogs(logsResult.logs); |
|
} |
|
|
|
|
|
if (logsResult.is_completed) { |
|
setEvaluationComplete(true); |
|
clearInterval(pollingIntervalRef.current); |
|
} |
|
} |
|
} catch (error) { |
|
console.log("Error polling logs:", error); |
|
|
|
} |
|
}, 2000); |
|
} else { |
|
|
|
setEvaluationLogs([`Error: ${result.error || "Unknown error"}`]); |
|
setError(result.error || "Benchmark evaluation failed"); |
|
} |
|
} catch (error) { |
|
console.error("Error starting evaluation:", error); |
|
setEvaluationLogs([`Error: ${error.message || "Unknown error"}`]); |
|
setError("Error connecting to server"); |
|
} finally { |
|
setEvaluating(false); |
|
} |
|
}; |
|
|
|
|
|
const getPhaseTitle = () => { |
|
switch (currentPhase) { |
|
case "initializing": |
|
return "Preparing evaluation..."; |
|
case "preparing": |
|
return "Preparing models..."; |
|
case "evaluating": |
|
return "Evaluating models..."; |
|
case "compiling_results": |
|
return "Compiling results..."; |
|
case "complete": |
|
return "Evaluation completed successfully!"; |
|
default: |
|
return "Processing..."; |
|
} |
|
}; |
|
|
|
|
|
const getCurrentStepInfo = () => { |
|
const totalSteps = EVALUATION_STEPS.length; |
|
const currentStepIndex = activeStep; |
|
|
|
|
|
if (currentStepIndex === 0 && completedSteps.length === 0) { |
|
return `Starting... (0%)`; |
|
} |
|
|
|
|
|
if (currentStepIndex >= totalSteps) { |
|
return `Completed (100%)`; |
|
} |
|
|
|
|
|
const percentage = Math.round((currentStepIndex / totalSteps) * 100); |
|
|
|
|
|
const currentStepName = |
|
STEP_LABELS[EVALUATION_STEPS[currentStepIndex]] || "Processing"; |
|
|
|
return `${currentStepName} (${percentage}%)`; |
|
}; |
|
|
|
|
|
const viewResults = () => { |
|
navigate(`/evaluation-display?session=${sessionId}`); |
|
}; |
|
|
|
return ( |
|
<Paper |
|
elevation={3} |
|
sx={{ |
|
p: 4, |
|
mt: 3, |
|
mb: 3, |
|
display: "flex", |
|
flexDirection: "column", |
|
alignItems: "center", |
|
justifyContent: "center", |
|
minHeight: 200, |
|
}} |
|
> |
|
{error ? ( |
|
<Alert severity="error" sx={{ width: "100%" }}> |
|
{error} |
|
</Alert> |
|
) : ( |
|
<> |
|
{evaluationComplete ? ( |
|
<> |
|
<Alert severity="success" sx={{ width: "100%", mb: 3 }}> |
|
Evaluation completed successfully! |
|
</Alert> |
|
<Button |
|
variant="contained" |
|
color="primary" |
|
onClick={viewResults} |
|
sx={{ mb: 3 }} |
|
> |
|
View Results Leaderboard |
|
</Button> |
|
</> |
|
) : ( |
|
<> |
|
<CircularProgress size={60} sx={{ mb: 2 }} /> |
|
<Typography variant="h6" component="div" gutterBottom> |
|
{getPhaseTitle()} |
|
</Typography> |
|
|
|
{/* Step progress indicator */} |
|
<Typography variant="body1" color="text.secondary"> |
|
{getCurrentStepInfo()} |
|
</Typography> |
|
|
|
{/* Timer display */} |
|
<Box |
|
sx={{ |
|
display: "flex", |
|
alignItems: "center", |
|
mt: 1, |
|
color: "text.secondary", |
|
opacity: 0.5, |
|
}} |
|
> |
|
<Typography variant="body2">{formatElapsedTime()}</Typography> |
|
</Box> |
|
</> |
|
)} |
|
</> |
|
)} |
|
|
|
{} |
|
<LogDisplay logs={evaluationLogs} height={300} /> |
|
</Paper> |
|
); |
|
}; |
|
|
|
export default BenchmarkEvaluation; |
|
|