|
import React, { useState, useEffect, useRef } from "react"; |
|
import { Box, Typography, CircularProgress, Alert, Paper } from "@mui/material"; |
|
import PlayArrowIcon from "@mui/icons-material/PlayArrow"; |
|
import AccessTimeIcon from "@mui/icons-material/AccessTime"; |
|
import LogDisplay from "./LogDisplay"; |
|
|
|
|
|
const BENCHMARK_STEPS = [ |
|
"ingestion", |
|
"upload_ingest_to_hub", |
|
"summarization", |
|
"chunking", |
|
"single_shot_question_generation", |
|
"multi_hop_question_generation", |
|
"lighteval", |
|
]; |
|
|
|
|
|
const STEP_LABELS = { |
|
ingestion: "Ingestion", |
|
upload_ingest_to_hub: "Upload to Hub", |
|
summarization: "Summarization", |
|
chunking: "Chunking", |
|
single_shot_question_generation: "Single-shot QG", |
|
multi_hop_question_generation: "Multi-hop QG", |
|
lighteval: "LightEval", |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const BenchmarkGenerator = ({ sessionId, onComplete }) => { |
|
const [generating, setGenerating] = useState(false); |
|
const [generationComplete, setGenerationComplete] = useState(false); |
|
const [generationLogs, setGenerationLogs] = 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); |
|
|
|
|
|
useEffect(() => { |
|
|
|
startTimeRef.current = Date.now(); |
|
|
|
|
|
timerIntervalRef.current = setInterval(() => { |
|
const timeElapsed = Math.floor( |
|
(Date.now() - startTimeRef.current) / 1000 |
|
); |
|
setElapsedTime(timeElapsed); |
|
}, 1000); |
|
|
|
generateBenchmark(); |
|
|
|
|
|
return () => { |
|
if (pollingIntervalRef.current) { |
|
clearInterval(pollingIntervalRef.current); |
|
} |
|
if (timerIntervalRef.current) { |
|
clearInterval(timerIntervalRef.current); |
|
} |
|
}; |
|
}, []); |
|
|
|
|
|
useEffect(() => { |
|
if (generationLogs.length === 0) return; |
|
|
|
|
|
const newCompletedSteps = [...completedSteps]; |
|
let newActiveStep = activeStep; |
|
|
|
generationLogs.forEach((log) => { |
|
const match = log.match(/\[SUCCESS\] Stage completed: (\w+)/); |
|
if (match && match[1]) { |
|
const completedStep = match[1].trim(); |
|
if ( |
|
BENCHMARK_STEPS.includes(completedStep) && |
|
!newCompletedSteps.includes(completedStep) |
|
) { |
|
newCompletedSteps.push(completedStep); |
|
|
|
const stepIndex = BENCHMARK_STEPS.indexOf(completedStep); |
|
if (stepIndex >= 0 && stepIndex + 1 > newActiveStep) { |
|
newActiveStep = stepIndex + 1; |
|
if (newActiveStep >= BENCHMARK_STEPS.length) { |
|
newActiveStep = BENCHMARK_STEPS.length; |
|
} |
|
} |
|
} |
|
} |
|
}); |
|
|
|
|
|
if (newCompletedSteps.length > completedSteps.length) { |
|
setCompletedSteps(newCompletedSteps); |
|
setActiveStep(newActiveStep); |
|
} |
|
|
|
|
|
const recentLogs = generationLogs.slice(-10); |
|
|
|
|
|
const isComplete = |
|
recentLogs.some((log) => |
|
log.includes("[SUCCESS] Ingestion process completed successfully") |
|
) || |
|
recentLogs.some((log) => |
|
log.includes( |
|
"[SUCCESS] Configuration and ingestion completed successfully" |
|
) |
|
) || |
|
completedSteps.includes("lighteval") || |
|
newCompletedSteps.includes("lighteval"); |
|
|
|
if (isComplete) { |
|
setCurrentPhase("complete"); |
|
setGenerationComplete(true); |
|
|
|
if (pollingIntervalRef.current) { |
|
clearInterval(pollingIntervalRef.current); |
|
} |
|
|
|
if (onComplete) { |
|
console.log("Notifying parent that generation is complete"); |
|
onComplete({ |
|
success: true, |
|
sessionId, |
|
logs: generationLogs, |
|
}); |
|
} |
|
} else if ( |
|
recentLogs.some((log) => log.includes("starting benchmark creation")) |
|
) { |
|
setCurrentPhase("benchmarking"); |
|
} else if ( |
|
recentLogs.some((log) => log.includes("Generating base configuration")) |
|
) { |
|
setCurrentPhase("configuring"); |
|
} |
|
}, [generationLogs, completedSteps, activeStep, sessionId, onComplete]); |
|
|
|
const generateBenchmark = async () => { |
|
if (!sessionId) { |
|
setError("Missing session ID"); |
|
return; |
|
} |
|
|
|
setGenerating(true); |
|
setGenerationLogs([]); |
|
setError(null); |
|
setCurrentPhase("initializing"); |
|
setCompletedSteps([]); |
|
setActiveStep(0); |
|
|
|
try { |
|
|
|
const response = await fetch("http://localhost:3001/generate-benchmark", { |
|
method: "POST", |
|
headers: { |
|
"Content-Type": "application/json", |
|
}, |
|
body: JSON.stringify({ |
|
session_id: sessionId, |
|
}), |
|
}); |
|
|
|
const result = await response.json(); |
|
|
|
if (response.ok) { |
|
setGenerationLogs(result.logs || []); |
|
|
|
|
|
const pollConfigLogs = async () => { |
|
try { |
|
|
|
const configLogsResponse = await fetch( |
|
`http://localhost:3001/config-logs/${sessionId}` |
|
); |
|
|
|
if (configLogsResponse.ok) { |
|
const configLogsResult = await configLogsResponse.json(); |
|
|
|
|
|
if ( |
|
configLogsResult.logs && |
|
configLogsResult.logs.length > generationLogs.length |
|
) { |
|
setGenerationLogs(configLogsResult.logs); |
|
} |
|
|
|
|
|
if (configLogsResult.is_completed) { |
|
|
|
setTimeout(() => { |
|
console.log( |
|
"Configuration completed, switching to benchmark polling" |
|
); |
|
clearInterval(configPollingIntervalRef.current); |
|
pollBenchmarkLogs(); |
|
}, 1000); |
|
} |
|
} |
|
} catch (error) { |
|
console.log("Error polling for config logs:", error); |
|
|
|
} |
|
}; |
|
|
|
|
|
const pollBenchmarkLogs = async () => { |
|
|
|
pollingIntervalRef.current = setInterval(async () => { |
|
|
|
if (generationComplete) { |
|
clearInterval(pollingIntervalRef.current); |
|
return; |
|
} |
|
|
|
try { |
|
|
|
const logsResponse = await fetch( |
|
`http://localhost:3001/benchmark-logs/${sessionId}` |
|
); |
|
|
|
if (logsResponse.ok) { |
|
const logsResult = await logsResponse.json(); |
|
|
|
|
|
if ( |
|
logsResult.logs && |
|
logsResult.logs.length > generationLogs.length |
|
) { |
|
setGenerationLogs(logsResult.logs); |
|
} |
|
|
|
|
|
if (logsResult.is_completed) { |
|
setGenerationComplete(true); |
|
clearInterval(pollingIntervalRef.current); |
|
|
|
} |
|
} |
|
} catch (error) { |
|
console.log("Error polling for benchmark logs:", error); |
|
|
|
} |
|
}, 3000); |
|
}; |
|
|
|
|
|
const configPollingIntervalRef = { current: null }; |
|
configPollingIntervalRef.current = setInterval(pollConfigLogs, 1000); |
|
} else { |
|
|
|
setGenerationLogs([`Error: ${result.error || "Unknown error"}`]); |
|
setError(result.error || "Benchmark generation failed"); |
|
} |
|
} catch (error) { |
|
console.error("Error generating benchmark:", error); |
|
setGenerationLogs([`Error: ${error.message || "Unknown error"}`]); |
|
setError("Server connection error"); |
|
} finally { |
|
setGenerating(false); |
|
} |
|
}; |
|
|
|
|
|
const getPhaseTitle = () => { |
|
switch (currentPhase) { |
|
case "initializing": |
|
return "Benchmark generation..."; |
|
case "configuring": |
|
return "Generating configuration file..."; |
|
case "benchmarking": |
|
return "Creating benchmark..."; |
|
case "complete": |
|
return "Benchmark generated successfully!"; |
|
default: |
|
return "Processing..."; |
|
} |
|
}; |
|
|
|
|
|
const getCurrentStepInfo = () => { |
|
const totalSteps = BENCHMARK_STEPS.length; |
|
const currentStepIndex = activeStep; |
|
|
|
|
|
if (currentStepIndex === 0 && completedSteps.length === 0) { |
|
return `Starting... (0%)`; |
|
} |
|
|
|
|
|
if (currentStepIndex >= totalSteps) { |
|
return `Complete (100%)`; |
|
} |
|
|
|
|
|
const percentage = Math.round((currentStepIndex / totalSteps) * 100); |
|
|
|
|
|
const currentStepName = |
|
STEP_LABELS[BENCHMARK_STEPS[currentStepIndex]] || "Processing"; |
|
|
|
return `${currentStepName} (${percentage}%)`; |
|
}; |
|
|
|
|
|
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(":"); |
|
}; |
|
|
|
|
|
useEffect(() => { |
|
if (generationComplete && timerIntervalRef.current) { |
|
clearInterval(timerIntervalRef.current); |
|
} |
|
}, [generationComplete]); |
|
|
|
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> |
|
) : ( |
|
<> |
|
<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", |
|
}} |
|
> |
|
<Typography variant="body2" sx={{ opacity: 0.5 }}> |
|
{formatElapsedTime()} |
|
</Typography> |
|
</Box> |
|
</> |
|
)} |
|
|
|
{} |
|
<LogDisplay logs={generationLogs} height={300} /> |
|
</Paper> |
|
); |
|
}; |
|
|
|
export default BenchmarkGenerator; |
|
|