227 lines
6.2 KiB
TypeScript
227 lines
6.2 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';
|
||
|
|
|
||
|
|
export const useTraining = () => {
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
// Auto-refresh job status for running jobs
|
||
|
|
useEffect(() => {
|
||
|
|
const runningJobs = jobs.filter(job => job.status === 'running' || job.status === 'pending');
|
||
|
|
|
||
|
|
if (runningJobs.length === 0) return;
|
||
|
|
|
||
|
|
const interval = setInterval(async () => {
|
||
|
|
for (const job of runningJobs) {
|
||
|
|
try {
|
||
|
|
const tenantId = job.tenant_id;
|
||
|
|
await getTrainingJobStatus(tenantId, job.job_id);
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Failed to refresh job status:', error);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}, 5000); // Refresh every 5 seconds
|
||
|
|
|
||
|
|
return () => clearInterval(interval);
|
||
|
|
}, [jobs, getTrainingJobStatus]);
|
||
|
|
|
||
|
|
return {
|
||
|
|
jobs,
|
||
|
|
currentJob,
|
||
|
|
models,
|
||
|
|
stats,
|
||
|
|
isLoading,
|
||
|
|
error,
|
||
|
|
startTrainingJob,
|
||
|
|
startSingleProductTraining,
|
||
|
|
getTrainingJobStatus,
|
||
|
|
cancelTrainingJob,
|
||
|
|
getTrainingJobs,
|
||
|
|
getModels,
|
||
|
|
validateTrainingData,
|
||
|
|
getTrainingStats,
|
||
|
|
clearError: () => setError(null),
|
||
|
|
};
|
||
|
|
};
|