Files
bakery-ia/frontend/src/api/hooks/useTraining.ts
Urtzi Alfaro 8914786973 New Frontend
2025-08-16 20:13:40 +02:00

266 lines
7.6 KiB
TypeScript

// frontend/src/api/hooks/useTraining.ts
/**
* Training Operations Hooks
*/
import { useState, useCallback, useEffect } from 'react';
import { trainingService } from '../services';
import type {
TrainingJobRequest,
TrainingJobResponse,
ModelInfo,
ModelTrainingStats,
SingleProductTrainingRequest,
} from '../types';
interface UseTrainingOptions {
disablePolling?: boolean; // New option to disable HTTP status polling
}
export const useTraining = (options: UseTrainingOptions = {}) => {
const { disablePolling = false } = options;
// Debug logging for option changes
console.log('🔧 useTraining initialized with options:', { disablePolling, options });
const [jobs, setJobs] = useState<TrainingJobResponse[]>([]);
const [currentJob, setCurrentJob] = useState<TrainingJobResponse | null>(null);
const [models, setModels] = useState<ModelInfo[]>([]);
const [stats, setStats] = useState<ModelTrainingStats | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const startTrainingJob = useCallback(async (
tenantId: string,
request: TrainingJobRequest
): Promise<TrainingJobResponse> => {
try {
setIsLoading(true);
setError(null);
const job = await trainingService.startTrainingJob(tenantId, request);
setCurrentJob(job);
setJobs(prev => [job, ...prev]);
return job;
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to start training job';
setError(message);
throw error;
} finally {
setIsLoading(false);
}
}, []);
const startSingleProductTraining = useCallback(async (
tenantId: string,
request: SingleProductTrainingRequest
): Promise<TrainingJobResponse> => {
try {
setIsLoading(true);
setError(null);
const job = await trainingService.startSingleProductTraining(tenantId, request);
setCurrentJob(job);
setJobs(prev => [job, ...prev]);
return job;
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to start product training';
setError(message);
throw error;
} finally {
setIsLoading(false);
}
}, []);
const getTrainingJobStatus = useCallback(async (
tenantId: string,
jobId: string
): Promise<TrainingJobResponse> => {
try {
const job = await trainingService.getTrainingJobStatus(tenantId, jobId);
// Update job in state
setJobs(prev => prev.map(j => j.job_id === jobId ? job : j));
if (currentJob?.job_id === jobId) {
setCurrentJob(job);
}
return job;
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to get job status';
setError(message);
throw error;
}
}, [currentJob]);
const cancelTrainingJob = useCallback(async (
tenantId: string,
jobId: string
): Promise<void> => {
try {
setIsLoading(true);
setError(null);
await trainingService.cancelTrainingJob(tenantId, jobId);
// Update job status in state
setJobs(prev => prev.map(j =>
j.job_id === jobId ? { ...j, status: 'cancelled' } : j
));
if (currentJob?.job_id === jobId) {
setCurrentJob({ ...currentJob, status: 'cancelled' });
}
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to cancel job';
setError(message);
throw error;
} finally {
setIsLoading(false);
}
}, [currentJob]);
const getTrainingJobs = useCallback(async (tenantId: string): Promise<TrainingJobResponse[]> => {
try {
setIsLoading(true);
setError(null);
const response = await trainingService.getTrainingJobs(tenantId);
setJobs(response.data);
return response.data;
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to get training jobs';
setError(message);
throw error;
} finally {
setIsLoading(false);
}
}, []);
const getModels = useCallback(async (tenantId: string): Promise<ModelInfo[]> => {
try {
setIsLoading(true);
setError(null);
const response = await trainingService.getModels(tenantId);
setModels(response.data);
return response.data;
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to get models';
setError(message);
throw error;
} finally {
setIsLoading(false);
}
}, []);
const validateTrainingData = useCallback(async (tenantId: string): Promise<{
is_valid: boolean;
message: string;
details?: any;
}> => {
try {
setIsLoading(true);
setError(null);
const result = await trainingService.validateTrainingData(tenantId);
return result;
} catch (error) {
const message = error instanceof Error ? error.message : 'Data validation failed';
setError(message);
throw error;
} finally {
setIsLoading(false);
}
}, []);
const getTrainingStats = useCallback(async (tenantId: string): Promise<ModelTrainingStats> => {
try {
setIsLoading(true);
setError(null);
const trainingStats = await trainingService.getTrainingStats(tenantId);
setStats(trainingStats);
return trainingStats;
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to get training stats';
setError(message);
throw error;
} finally {
setIsLoading(false);
}
}, []);
useEffect(() => {
// Always check disablePolling first and log for debugging
console.log('🔍 useTraining polling check:', {
disablePolling,
jobsCount: jobs.length,
runningJobs: jobs.filter(job => job.status === 'running' || job.status === 'pending').length
});
// STRICT CHECK: Skip polling if disabled - NO EXCEPTIONS
if (disablePolling === true) {
console.log('🚫 HTTP status polling STRICTLY DISABLED - using WebSocket instead');
console.log('🚫 Effect triggered but polling prevented by disablePolling flag');
return; // Early return - no cleanup needed, no interval creation
}
const runningJobs = jobs.filter(job => job.status === 'running' || job.status === 'pending');
if (runningJobs.length === 0) {
console.log('⏸️ No running jobs - skipping polling setup');
return;
}
console.log('🔄 Starting HTTP status polling for', runningJobs.length, 'jobs');
const interval = setInterval(async () => {
// Double-check disablePolling inside interval to prevent race conditions
if (disablePolling) {
console.log('🚫 Polling disabled during interval - clearing');
clearInterval(interval);
return;
}
for (const job of runningJobs) {
try {
const tenantId = job.tenant_id;
console.log('📡 HTTP polling job status:', job.job_id);
await getTrainingJobStatus(tenantId, job.job_id);
} catch (error) {
console.error('Failed to refresh job status:', error);
}
}
}, 5000); // Refresh every 5 seconds
return () => {
console.log('🛑 Stopping HTTP status polling (cleanup)');
clearInterval(interval);
};
}, [jobs, getTrainingJobStatus, disablePolling]);
return {
jobs,
currentJob,
models,
stats,
isLoading,
error,
startTrainingJob,
startSingleProductTraining,
getTrainingJobStatus,
cancelTrainingJob,
getTrainingJobs,
getModels,
validateTrainingData,
getTrainingStats,
clearError: () => setError(null),
};
};