2025-07-22 17:01:12 +02:00
|
|
|
// src/api/services/TrainingService.ts
|
|
|
|
|
import { apiClient } from '../base/apiClient';
|
|
|
|
|
import {
|
2025-07-23 10:02:48 +02:00
|
|
|
ApiResponse
|
2025-07-22 17:01:12 +02:00
|
|
|
} from '../types/api';
|
|
|
|
|
|
2025-07-23 10:02:48 +02:00
|
|
|
export interface TrainingJobStatus {
|
|
|
|
|
id: string;
|
|
|
|
|
tenant_id: string;
|
|
|
|
|
status: 'queued' | 'running' | 'completed' | 'failed' | 'cancelled';
|
|
|
|
|
progress: number;
|
|
|
|
|
current_step?: string;
|
|
|
|
|
started_at: string;
|
|
|
|
|
completed_at?: string;
|
|
|
|
|
duration_seconds?: number;
|
|
|
|
|
models_trained?: Record<string, any>;
|
|
|
|
|
metrics?: Record<string, any>;
|
|
|
|
|
error_message?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface TrainingRequest {
|
|
|
|
|
force_retrain?: boolean;
|
|
|
|
|
products?: string[];
|
|
|
|
|
training_days?: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface TrainedModel {
|
|
|
|
|
id: string;
|
|
|
|
|
product_name: string;
|
|
|
|
|
model_type: string;
|
|
|
|
|
model_version: string;
|
|
|
|
|
mape?: number;
|
|
|
|
|
rmse?: number;
|
|
|
|
|
mae?: number;
|
|
|
|
|
r2_score?: number;
|
|
|
|
|
training_samples?: number;
|
|
|
|
|
features_used?: string[];
|
|
|
|
|
is_active: boolean;
|
|
|
|
|
created_at: string;
|
|
|
|
|
last_used_at?: string;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-22 17:01:12 +02:00
|
|
|
export interface TrainingJobProgress {
|
|
|
|
|
id: string;
|
|
|
|
|
status: 'queued' | 'running' | 'completed' | 'failed' | 'cancelled';
|
|
|
|
|
progress: number;
|
|
|
|
|
current_step?: string;
|
|
|
|
|
total_steps?: number;
|
|
|
|
|
step_details?: string;
|
|
|
|
|
estimated_completion?: string;
|
|
|
|
|
logs?: string[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ModelMetrics {
|
|
|
|
|
mape: number;
|
|
|
|
|
rmse: number;
|
|
|
|
|
mae: number;
|
|
|
|
|
r2_score: number;
|
|
|
|
|
training_samples: number;
|
|
|
|
|
validation_samples: number;
|
|
|
|
|
features_used: string[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface TrainingConfiguration {
|
|
|
|
|
include_weather: boolean;
|
|
|
|
|
include_traffic: boolean;
|
|
|
|
|
min_data_points: number;
|
|
|
|
|
forecast_horizon_days: number;
|
|
|
|
|
cross_validation_folds: number;
|
|
|
|
|
hyperparameter_tuning: boolean;
|
|
|
|
|
products?: string[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class TrainingService {
|
|
|
|
|
/**
|
|
|
|
|
* Start new training job
|
|
|
|
|
*/
|
|
|
|
|
async startTraining(config: TrainingConfiguration): Promise<TrainingJobStatus> {
|
|
|
|
|
const response = await apiClient.post<ApiResponse<TrainingJobStatus>>(
|
2025-07-23 08:01:50 +02:00
|
|
|
'/api/v1/training/jobs',
|
2025-07-22 17:01:12 +02:00
|
|
|
config
|
|
|
|
|
);
|
|
|
|
|
return response.data!;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get training job status
|
|
|
|
|
*/
|
|
|
|
|
async getTrainingStatus(jobId: string): Promise<TrainingJobProgress> {
|
|
|
|
|
const response = await apiClient.get<ApiResponse<TrainingJobProgress>>(
|
2025-07-23 07:26:04 +02:00
|
|
|
`/api/v1/training/jobs/${jobId}`
|
2025-07-22 17:01:12 +02:00
|
|
|
);
|
|
|
|
|
return response.data!;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get all training jobs
|
|
|
|
|
*/
|
|
|
|
|
async getTrainingHistory(params?: {
|
|
|
|
|
page?: number;
|
|
|
|
|
limit?: number;
|
|
|
|
|
status?: string;
|
|
|
|
|
}): Promise<{
|
|
|
|
|
jobs: TrainingJobStatus[];
|
|
|
|
|
total: number;
|
|
|
|
|
page: number;
|
|
|
|
|
pages: number;
|
|
|
|
|
}> {
|
2025-07-23 07:26:04 +02:00
|
|
|
const response = await apiClient.get<ApiResponse<any>>('/api/v1/training/jobs', { params });
|
2025-07-22 17:01:12 +02:00
|
|
|
return response.data!;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Cancel training job
|
|
|
|
|
*/
|
|
|
|
|
async cancelTraining(jobId: string): Promise<void> {
|
2025-07-23 07:26:04 +02:00
|
|
|
await apiClient.post(`/api/v1/training/jobs/${jobId}/cancel`);
|
2025-07-22 17:01:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get trained models
|
|
|
|
|
*/
|
|
|
|
|
async getModels(params?: {
|
|
|
|
|
productName?: string;
|
|
|
|
|
active?: boolean;
|
|
|
|
|
page?: number;
|
|
|
|
|
limit?: number;
|
|
|
|
|
}): Promise<{
|
|
|
|
|
models: TrainedModel[];
|
|
|
|
|
total: number;
|
|
|
|
|
page: number;
|
|
|
|
|
pages: number;
|
|
|
|
|
}> {
|
2025-07-23 07:26:04 +02:00
|
|
|
const response = await apiClient.get<ApiResponse<any>>('/api/v1/training/models', { params });
|
2025-07-22 17:01:12 +02:00
|
|
|
return response.data!;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get specific model details
|
|
|
|
|
*/
|
|
|
|
|
async getModel(modelId: string): Promise<TrainedModel> {
|
|
|
|
|
const response = await apiClient.get<ApiResponse<TrainedModel>>(
|
2025-07-23 07:26:04 +02:00
|
|
|
`/api/v1/training/models/${modelId}`
|
2025-07-22 17:01:12 +02:00
|
|
|
);
|
|
|
|
|
return response.data!;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get model metrics
|
|
|
|
|
*/
|
|
|
|
|
async getModelMetrics(modelId: string): Promise<ModelMetrics> {
|
|
|
|
|
const response = await apiClient.get<ApiResponse<ModelMetrics>>(
|
2025-07-23 07:26:04 +02:00
|
|
|
`/api/v1/training/models/${modelId}/metrics`
|
2025-07-22 17:01:12 +02:00
|
|
|
);
|
|
|
|
|
return response.data!;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Activate/deactivate model
|
|
|
|
|
*/
|
|
|
|
|
async toggleModelStatus(modelId: string, active: boolean): Promise<TrainedModel> {
|
|
|
|
|
const response = await apiClient.patch<ApiResponse<TrainedModel>>(
|
2025-07-23 07:26:04 +02:00
|
|
|
`/api/v1/training/models/${modelId}`,
|
2025-07-22 17:01:12 +02:00
|
|
|
{ is_active: active }
|
|
|
|
|
);
|
|
|
|
|
return response.data!;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Delete model
|
|
|
|
|
*/
|
|
|
|
|
async deleteModel(modelId: string): Promise<void> {
|
2025-07-23 07:26:04 +02:00
|
|
|
await apiClient.delete(`/api/v1/training/models/${modelId}`);
|
2025-07-22 17:01:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Train specific product
|
|
|
|
|
*/
|
|
|
|
|
async trainProduct(productName: string, config?: Partial<TrainingConfiguration>): Promise<TrainingJobStatus> {
|
|
|
|
|
const response = await apiClient.post<ApiResponse<TrainingJobStatus>>(
|
2025-07-23 07:26:04 +02:00
|
|
|
'/api/v1/training/products/train',
|
2025-07-22 17:01:12 +02:00
|
|
|
{
|
|
|
|
|
product_name: productName,
|
|
|
|
|
...config,
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
return response.data!;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get training statistics
|
|
|
|
|
*/
|
|
|
|
|
async getTrainingStats(): Promise<{
|
|
|
|
|
total_models: number;
|
|
|
|
|
active_models: number;
|
|
|
|
|
avg_accuracy: number;
|
|
|
|
|
last_training_date: string | null;
|
|
|
|
|
products_trained: number;
|
|
|
|
|
training_time_avg_minutes: number;
|
|
|
|
|
}> {
|
2025-07-23 07:26:04 +02:00
|
|
|
const response = await apiClient.get<ApiResponse<any>>('/api/v1/training/stats');
|
2025-07-22 17:01:12 +02:00
|
|
|
return response.data!;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Validate training data
|
|
|
|
|
*/
|
|
|
|
|
async validateTrainingData(products?: string[]): Promise<{
|
|
|
|
|
valid: boolean;
|
|
|
|
|
errors: string[];
|
|
|
|
|
warnings: string[];
|
|
|
|
|
product_data_points: Record<string, number>;
|
|
|
|
|
recommendation: string;
|
|
|
|
|
}> {
|
2025-07-23 07:26:04 +02:00
|
|
|
const response = await apiClient.post<ApiResponse<any>>('/api/v1/training/validate', {
|
2025-07-22 17:01:12 +02:00
|
|
|
products,
|
|
|
|
|
});
|
|
|
|
|
return response.data!;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get training recommendations
|
|
|
|
|
*/
|
|
|
|
|
async getTrainingRecommendations(): Promise<{
|
|
|
|
|
should_retrain: boolean;
|
|
|
|
|
reasons: string[];
|
|
|
|
|
recommended_products: string[];
|
|
|
|
|
optimal_config: TrainingConfiguration;
|
|
|
|
|
}> {
|
2025-07-23 07:26:04 +02:00
|
|
|
const response = await apiClient.get<ApiResponse<any>>('/api/v1/training/recommendations');
|
2025-07-22 17:01:12 +02:00
|
|
|
return response.data!;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get training logs
|
|
|
|
|
*/
|
|
|
|
|
async getTrainingLogs(jobId: string): Promise<string[]> {
|
2025-07-23 07:26:04 +02:00
|
|
|
const response = await apiClient.get<ApiResponse<string[]>>(`/api/v1/training/jobs/${jobId}/logs`);
|
2025-07-22 17:01:12 +02:00
|
|
|
return response.data!;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Export model
|
|
|
|
|
*/
|
|
|
|
|
async exportModel(modelId: string, format: 'pickle' | 'onnx' = 'pickle'): Promise<Blob> {
|
2025-07-23 07:26:04 +02:00
|
|
|
const response = await apiClient.get(`/api/v1/training/models/${modelId}/export`, {
|
2025-07-22 17:01:12 +02:00
|
|
|
params: { format },
|
|
|
|
|
responseType: 'blob',
|
|
|
|
|
});
|
|
|
|
|
return response as unknown as Blob;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const trainingService = new TrainingService();
|