import React, { useState, useEffect, useRef } from "react"; import { Box, Typography, CircularProgress, Alert, Paper } from "@mui/material"; import { useNavigate, useSearchParams } from "react-router-dom"; import API_CONFIG from "../config/api"; // Temps de simulation en millisecondes pour les documents précalculés const SIMULATION_DURATION = 120000; // 20 secondes // Intervalle de changement des messages pour les documents standards vs précalculés const MESSAGE_CHANGE_INTERVAL = { DEFAULT: 25000, // 20 secondes pour documents standards PRECALCULATED: 25000, // 5 secondes pour documents précalculés }; // Starting messages with their timing const STARTING_MESSAGES = [ { message: "Initializing evaluation environment", step: 1, totalSteps: 5 }, { message: "Finding available model providers", step: 2, totalSteps: 5 }, { message: "Starting evaluation process", step: 3, totalSteps: 5 }, { message: "Evaluating models", step: 4, totalSteps: 5 }, { message: "Storing evaluation results", step: 5, totalSteps: 5 }, ]; const BenchmarkEvaluation = ({ sessionId, isDefaultDocument, onComplete }) => { const [searchParams] = useSearchParams(); const isDefault = isDefaultDocument || ["the-bitter-lesson", "hurricane-faq", "pokemon-guide"].includes(sessionId); const [evaluationComplete, setEvaluationComplete] = useState(false); const [error, setError] = useState(null); const [elapsedTime, setElapsedTime] = useState(0); const [startingMessageIndex, setStartingMessageIndex] = useState(0); const [evaluationStarted, setEvaluationStarted] = useState(false); const timerIntervalRef = useRef(null); const startTimeRef = useRef(null); const startingMessageIntervalRef = useRef(null); const pollingIntervalRef = useRef(null); const simulationTimeoutRef = useRef(null); const navigate = useNavigate(); // Add effect to handle automatic redirection when evaluation is complete useEffect(() => { if (evaluationComplete) { navigate(`/evaluation-display?session=${sessionId}`); } }, [evaluationComplete, sessionId, navigate]); // Add effect to handle starting messages useEffect(() => { // Ne configurer l'intervalle automatique que pour les documents par défaut // Pour les évaluations réelles, on se fiera uniquement aux mises à jour de l'API if (isDefault) { startingMessageIntervalRef.current = setInterval(() => { setStartingMessageIndex((prev) => { if (prev < STARTING_MESSAGES.length - 1) { return prev + 1; } return prev; }); }, MESSAGE_CHANGE_INTERVAL.PRECALCULATED); } return () => { if (startingMessageIntervalRef.current) { clearInterval(startingMessageIntervalRef.current); } }; }, [isDefault]); // Start evaluation when component mounts useEffect(() => { // Set start time startTimeRef.current = Date.now(); // Start timer timerIntervalRef.current = setInterval(() => { const timeElapsed = Math.floor( (Date.now() - startTimeRef.current) / 1000 ); setElapsedTime(timeElapsed); }, 1000); // Gestionnaire pour détecter quand la page redevient visible const handleVisibilityChange = () => { if ( document.visibilityState === "visible" && !isDefault && !evaluationComplete && evaluationStarted // Vérifier si l'évaluation a déjà commencé ) { console.log("Page became visible, checking evaluation status..."); // Force une nouvelle requête pour récupérer l'état d'évaluation const checkEvaluationStatus = async () => { try { const logsResponse = await fetch( `${API_CONFIG.BASE_URL}/evaluation-logs/${sessionId}` ); if (logsResponse.ok) { const logsResult = await logsResponse.json(); if (logsResult.is_completed) { // Mettre fin à l'évaluation si elle est terminée setEvaluationComplete(true); // Avancer à la dernière étape des messages setStartingMessageIndex(STARTING_MESSAGES.length - 1); // Nettoyer les intervalles if (pollingIntervalRef.current) { clearInterval(pollingIntervalRef.current); } if (startingMessageIntervalRef.current) { clearInterval(startingMessageIntervalRef.current); } } else { // Si l'évaluation est toujours en cours, utiliser l'étape actuelle du backend if (logsResult.current_step) { // Utiliser la fonction de mappage pour déterminer l'index du message const newIndex = mapStepToMessageIndex( logsResult.current_step ); setStartingMessageIndex(newIndex); } else { // Fallback basé sur le temps si l'étape n'est pas disponible const progress = Math.min( Math.floor( (Date.now() - startTimeRef.current) / MESSAGE_CHANGE_INTERVAL.DEFAULT ), STARTING_MESSAGES.length - 1 ); setStartingMessageIndex(progress); } } } } catch (error) { console.error("Error checking evaluation status:", error); } }; checkEvaluationStatus(); } }; // Ajouter l'écouteur pour le changement de visibilité document.addEventListener("visibilitychange", handleVisibilityChange); if (isDefault) { simulateEvaluation(); } else { // Démarrer l'évaluation seulement si elle n'a pas déjà été lancée if (!evaluationStarted) { startEvaluation(); } } // Clean up intervals on unmount return () => { if (pollingIntervalRef.current) { clearInterval(pollingIntervalRef.current); } if (timerIntervalRef.current) { clearInterval(timerIntervalRef.current); } if (simulationTimeoutRef.current) { clearTimeout(simulationTimeoutRef.current); } document.removeEventListener("visibilitychange", handleVisibilityChange); }; }, [isDefault, sessionId, evaluationComplete, evaluationStarted]); // Simulate the evaluation process for pre-calculated documents const simulateEvaluation = () => { // Complete after 20 seconds simulationTimeoutRef.current = setTimeout(() => { setEvaluationComplete(true); if (startingMessageIntervalRef.current) { clearInterval(startingMessageIntervalRef.current); } setStartingMessageIndex(STARTING_MESSAGES.length - 1); // Set to last message }, SIMULATION_DURATION); }; // Format elapsed time as HH:MM:SS 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(":"); }; // Fonction pour mapper le nom de l'étape backend vers l'index dans STARTING_MESSAGES const mapStepToMessageIndex = (currentStep) => { switch (currentStep) { case "initializing": return 0; case "finding_available_model_providers": return 1; case "starting_evaluation_process": return 2; case "evaluating_models": return 3; case "storing_evaluation_results": case "completed": return 4; default: // Calculer l'étape en fonction du temps écoulé si l'étape n'est pas reconnue const elapsedSinceStart = Date.now() - startTimeRef.current; const estimatedTotalTime = 80000; // 80 secondes const estimatedProgress = Math.min( elapsedSinceStart / estimatedTotalTime, 1 ); return Math.min( Math.floor(estimatedProgress * STARTING_MESSAGES.length), STARTING_MESSAGES.length - 1 ); } }; // Start benchmark evaluation const startEvaluation = async () => { if (!sessionId) { setError("Missing session ID"); return; } // Marquer que l'évaluation a commencé setEvaluationStarted(true); try { // Call API to start evaluation const response = await fetch( `${API_CONFIG.BASE_URL}/evaluate-benchmark`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ session_id: sessionId, }), } ); const result = await response.json(); if (response.ok) { // Set up polling to check completion pollingIntervalRef.current = setInterval(async () => { try { const logsResponse = await fetch( `${API_CONFIG.BASE_URL}/evaluation-logs/${sessionId}` ); if (logsResponse.ok) { const logsResult = await logsResponse.json(); // Vérifier si l'évaluation est terminée if (logsResult.is_completed) { setEvaluationComplete(true); // Avancer à la dernière étape du message setStartingMessageIndex(STARTING_MESSAGES.length - 1); // Arrêter les intervalles clearInterval(pollingIntervalRef.current); if (startingMessageIntervalRef.current) { clearInterval(startingMessageIntervalRef.current); } } else { // Récupérer l'étape actuelle à partir de l'API, si disponible if (logsResult.current_step) { // Utiliser la fonction de mappage pour déterminer l'index du message const newIndex = mapStepToMessageIndex( logsResult.current_step ); setStartingMessageIndex(newIndex); } else { // Fallback: Si l'API ne renvoie pas d'étape, estimer en fonction du temps const elapsedSinceStart = Date.now() - startTimeRef.current; const estimatedTotalTime = 80000; // 80 secondes const estimatedProgress = Math.min( elapsedSinceStart / estimatedTotalTime, 1 ); const estimatedStepIndex = Math.min( Math.floor(estimatedProgress * STARTING_MESSAGES.length), STARTING_MESSAGES.length - 1 ); setStartingMessageIndex(estimatedStepIndex); } } } } catch (error) { console.log("Error polling logs:", error); // Ne pas arrêter le polling en cas d'erreurs réseau temporaires } }, 2000); } else { setError(result.error || "Benchmark evaluation failed"); } } catch (error) { console.error("Error starting evaluation:", error); setError("Error connecting to server"); } }; return ( {/* Temps estimé */} Estimated time ~ 1m30s {error ? ( {error} ) : ( <> {evaluationComplete ? ( Evaluation completed successfully! ) : ( <> Benchmark evaluation... {/* Step progress indicator */} {`${STARTING_MESSAGES[startingMessageIndex].message} (${STARTING_MESSAGES[startingMessageIndex].step}/${STARTING_MESSAGES[startingMessageIndex].totalSteps})`} {/* Timer display */} {formatElapsedTime()} )} )} ); }; export default BenchmarkEvaluation;