/**
* Learning controller
*
* Implements learning system endpoints for datasets, samples, and training
*/
import { learningService } from '../services/learning.service.js';
import { APIError } from '../middleware/errorHandler.js';
import { logger } from '../utils/logger.js';
export const learningController = {
/**
* List datasets
*/
listDatasets: async (req, res, next) => {
try {
const datasets = await learningService.listDatasets(req.query);
res.json(datasets);
} catch (error) {
next(new APIError(`Failed to retrieve datasets: ${error.message}`, 500));
}
},
/**
* Get dataset details
*/
getDatasetDetails: async (req, res, next) => {
try {
const { datasetId } = req.params;
const dataset = await learningService.getDatasetDetails(datasetId);
if (!dataset) {
return next(new APIError(`Dataset not found: ${datasetId}`, 404, 'DATASET_NOT_FOUND'));
}
res.json(dataset);
} catch (error) {
next(new APIError(`Failed to retrieve dataset details: ${error.message}`, 500));
}
},
/**
* Create a new dataset
*/
createDataset: async (req, res, next) => {
try {
const { name, description, type, initialSamples, metadata } = req.body;
if (!name) {
return next(new APIError('Missing required dataset name', 400, 'MISSING_DATASET_NAME'));
}
const dataset = await learningService.createDataset(name, description, type, initialSamples, metadata);
res.status(201).json(dataset);
} catch (error) {
next(new APIError(`Failed to create dataset: ${error.message}`, 500));
}
},
/**
* Update a dataset
*/
updateDataset: async (req, res, next) => {
try {
const { datasetId } = req.params;
const { name, description, status, metadata } = req.body;
const dataset = await learningService.updateDataset(datasetId, { name, description, status, metadata });
if (!dataset) {
return next(new APIError(`Dataset not found: ${datasetId}`, 404, 'DATASET_NOT_FOUND'));
}
res.json(dataset);
} catch (error) {
next(new APIError(`Failed to update dataset: ${error.message}`, 500));
}
},
/**
* Delete a dataset
*/
deleteDataset: async (req, res, next) => {
try {
const { datasetId } = req.params;
const success = await learningService.deleteDataset(datasetId);
if (!success) {
return next(new APIError(`Dataset not found: ${datasetId}`, 404, 'DATASET_NOT_FOUND'));
}
res.status(204).end();
} catch (error) {
next(new APIError(`Failed to delete dataset: ${error.message}`, 500));
}
},
/**
* List samples in a dataset
*/
listSamples: async (req, res, next) => {
try {
const { datasetId } = req.params;
const samples = await learningService.listSamples(datasetId, req.query);
if (!samples) {
return next(new APIError(`Dataset not found: ${datasetId}`, 404, 'DATASET_NOT_FOUND'));
}
res.json(samples);
} catch (error) {
next(new APIError(`Failed to retrieve samples: ${error.message}`, 500));
}
},
/**
* Get a specific sample
*/
getSample: async (req, res, next) => {
try {
const { datasetId, sampleId } = req.params;
const sample = await learningService.getSample(datasetId, sampleId);
if (!sample) {
return next(new APIError(`Sample not found: ${datasetId}/${sampleId}`, 404, 'SAMPLE_NOT_FOUND'));
}
res.json(sample);
} catch (error) {
next(new APIError(`Failed to retrieve sample: ${error.message}`, 500));
}
},
/**
* Add samples to a dataset
*/
addSamples: async (req, res, next) => {
try {
const { datasetId } = req.params;
const { samples } = req.body;
if (!samples || !Array.isArray(samples) || samples.length === 0) {
return next(new APIError('Missing or invalid samples data', 400, 'INVALID_SAMPLES'));
}
const result = await learningService.addSamples(datasetId, samples);
if (!result) {
return next(new APIError(`Dataset not found: ${datasetId}`, 404, 'DATASET_NOT_FOUND'));
}
res.status(201).json(result);
} catch (error) {
next(new APIError(`Failed to add samples: ${error.message}`, 500));
}
},
/**
* Update a sample
*/
updateSample: async (req, res, next) => {
try {
const { datasetId, sampleId } = req.params;
const { input, output, metadata } = req.body;
const sample = await learningService.updateSample(datasetId, sampleId, { input, output, metadata });
if (!sample) {
return next(new APIError(`Sample not found: ${datasetId}/${sampleId}`, 404, 'SAMPLE_NOT_FOUND'));
}
res.json(sample);
} catch (error) {
next(new APIError(`Failed to update sample: ${error.message}`, 500));
}
},
/**
* Delete a sample
*/
deleteSample: async (req, res, next) => {
try {
const { datasetId, sampleId } = req.params;
const success = await learningService.deleteSample(datasetId, sampleId);
if (!success) {
return next(new APIError(`Sample not found: ${datasetId}/${sampleId}`, 404, 'SAMPLE_NOT_FOUND'));
}
res.status(204).end();
} catch (error) {
next(new APIError(`Failed to delete sample: ${error.message}`, 500));
}
},
/**
* Submit feedback for model outputs
*/
submitFeedback: async (req, res, next) => {
try {
const { modelId, input, output, rating, comments, metadata } = req.body;
if (!modelId || !input || !output || rating === undefined) {
return next(new APIError('Missing required feedback information', 400, 'MISSING_FEEDBACK_INFO'));
}
const feedback = await learningService.submitFeedback(modelId, input, output, rating, comments, metadata);
res.status(201).json(feedback);
} catch (error) {
next(new APIError(`Failed to submit feedback: ${error.message}`, 500));
}
},
/**
* List feedback entries
*/
listFeedback: async (req, res, next) => {
try {
const feedback = await learningService.listFeedback(req.query);
res.json(feedback);
} catch (error) {
next(new APIError(`Failed to retrieve feedback: ${error.message}`, 500));
}
},
/**
* Get feedback details
*/
getFeedbackDetails: async (req, res, next) => {
try {
const { feedbackId } = req.params;
const feedback = await learningService.getFeedbackDetails(feedbackId);
if (!feedback) {
return next(new APIError(`Feedback not found: ${feedbackId}`, 404, 'FEEDBACK_NOT_FOUND'));
}
res.json(feedback);
} catch (error) {
next(new APIError(`Failed to retrieve feedback details: ${error.message}`, 500));
}
},
/**
* Start a training job
*/
startTrainingJob: async (req, res, next) => {
try {
const { modelId, datasets, hyperparameters, options } = req.body;
if (!modelId || !datasets || !Array.isArray(datasets) || datasets.length === 0) {
return next(new APIError('Missing required training information', 400, 'MISSING_TRAINING_INFO'));
}
const job = await learningService.startTrainingJob(modelId, datasets, hyperparameters, options);
res.status(201).json(job);
} catch (error) {
next(new APIError(`Failed to start training job: ${error.message}`, 500));
}
},
/**
* List training jobs
*/
listTrainingJobs: async (req, res, next) => {
try {
const jobs = await learningService.listTrainingJobs(req.query);
res.json(jobs);
} catch (error) {
next(new APIError(`Failed to retrieve training jobs: ${error.message}`, 500));
}
},
/**
* Get training job status
*/
getTrainingJobStatus: async (req, res, next) => {
try {
const { jobId } = req.params;
const job = await learningService.getTrainingJobStatus(jobId);
if (!job) {
return next(new APIError(`Training job not found: ${jobId}`, 404, 'JOB_NOT_FOUND'));
}
res.json(job);
} catch (error) {
next(new APIError(`Failed to retrieve training job status: ${error.message}`, 500));
}
},
/**
* Cancel a training job
*/
cancelTrainingJob: async (req, res, next) => {
try {
const { jobId } = req.params;
const job = await learningService.cancelTrainingJob(jobId);
if (!job) {
return next(new APIError(`Training job not found: ${jobId}`, 404, 'JOB_NOT_FOUND'));
}
res.json(job);
} catch (error) {
next(new APIError(`Failed to cancel training job: ${error.message}`, 500));
}
}
};
export default learningController;