/**
* Model controller
*
* Implements model management and operation endpoints
*/
import { modelService } from '../services/model.service.js';
import { APIError } from '../middleware/errorHandler.js';
import { logger } from '../utils/logger.js';
import { cache } from '../utils/cache.js';
export const modelController = {
/**
* List available models
*/
listModels: async (req, res, next) => {
try {
// Check cache first
const cacheKey = 'models_list';
const cachedModels = cache.get(cacheKey);
if (cachedModels) {
return res.json(cachedModels);
}
const models = await modelService.listModels(req.query);
// Store in cache
cache.set(cacheKey, models);
res.json(models);
} catch (error) {
next(new APIError(`Failed to retrieve models: ${error.message}`, 500));
}
},
/**
* Get details for a specific model
*/
getModelDetails: async (req, res, next) => {
try {
const { modelId } = req.params;
const cacheKey = `model_${modelId}`;
const cachedModel = cache.get(cacheKey);
if (cachedModel) {
return res.json(cachedModel);
}
const model = await modelService.getModelDetails(modelId);
if (!model) {
return next(new APIError(`Model not found: ${modelId}`, 404, 'MODEL_NOT_FOUND'));
}
// Store in cache
cache.set(cacheKey, model);
res.json(model);
} catch (error) {
next(new APIError(`Failed to retrieve model details: ${error.message}`, 500));
}
},
/**
* List versions for a specific model
*/
listModelVersions: async (req, res, next) => {
try {
const { modelId } = req.params;
const versions = await modelService.listModelVersions(modelId, req.query);
if (!versions) {
return next(new APIError(`Model not found: ${modelId}`, 404, 'MODEL_NOT_FOUND'));
}
res.json(versions);
} catch (error) {
next(new APIError(`Failed to retrieve model versions: ${error.message}`, 500));
}
},
/**
* Get details for a specific model version
*/
getModelVersionDetails: async (req, res, next) => {
try {
const { modelId, versionId } = req.params;
const version = await modelService.getModelVersionDetails(modelId, versionId);
if (!version) {
return next(new APIError(`Model version not found: ${modelId}/${versionId}`, 404, 'MODEL_VERSION_NOT_FOUND'));
}
res.json(version);
} catch (error) {
next(new APIError(`Failed to retrieve model version details: ${error.message}`, 500));
}
},
/**
* Make a prediction using a model
*/
predictWithModel: async (req, res, next) => {
try {
const { modelId } = req.params;
const { inputs, options } = req.body;
if (!inputs) {
return next(new APIError('Missing required input data', 400, 'MISSING_INPUTS'));
}
const startTime = Date.now();
const prediction = await modelService.predict(modelId, inputs, options);
const duration = Date.now() - startTime;
// Log the model call
logger.modelCall(modelId, 'predict', duration, true);
res.json({
model: modelId,
prediction,
metadata: {
processingTime: duration,
timestamp: new Date().toISOString()
}
});
} catch (error) {
logger.modelCall(req.params.modelId, 'predict', 0, false);
next(new APIError(`Prediction failed: ${error.message}`, 500));
}
},
/**
* Analyze data using a model
*/
analyzeWithModel: async (req, res, next) => {
try {
const { modelId } = req.params;
const { data, options } = req.body;
if (!data) {
return next(new APIError('Missing required data for analysis', 400, 'MISSING_DATA'));
}
const startTime = Date.now();
const analysis = await modelService.analyze(modelId, data, options);
const duration = Date.now() - startTime;
// Log the model call
logger.modelCall(modelId, 'analyze', duration, true);
res.json({
model: modelId,
analysis,
metadata: {
processingTime: duration,
timestamp: new Date().toISOString()
}
});
} catch (error) {
logger.modelCall(req.params.modelId, 'analyze', 0, false);
next(new APIError(`Analysis failed: ${error.message}`, 500));
}
},
/**
* Generate content using a model
*/
generateWithModel: async (req, res, next) => {
try {
const { modelId } = req.params;
const { prompt, options } = req.body;
if (!prompt) {
return next(new APIError('Missing required prompt for generation', 400, 'MISSING_PROMPT'));
}
const startTime = Date.now();
const generated = await modelService.generate(modelId, prompt, options);
const duration = Date.now() - startTime;
// Log the model call
logger.modelCall(modelId, 'generate', duration, true);
res.json({
model: modelId,
generated,
metadata: {
processingTime: duration,
timestamp: new Date().toISOString()
}
});
} catch (error) {
logger.modelCall(req.params.modelId, 'generate', 0, false);
next(new APIError(`Generation failed: ${error.message}`, 500));
}
},
/**
* Create embeddings using a model
*/
embedWithModel: async (req, res, next) => {
try {
const { modelId } = req.params;
const { texts, options } = req.body;
if (!texts || !Array.isArray(texts)) {
return next(new APIError('Missing or invalid texts for embedding', 400, 'INVALID_TEXTS'));
}
const startTime = Date.now();
const embeddings = await modelService.createEmbeddings(modelId, texts, options);
const duration = Date.now() - startTime;
// Log the model call
logger.modelCall(modelId, 'embed', duration, true);
res.json({
model: modelId,
embeddings,
metadata: {
processingTime: duration,
timestamp: new Date().toISOString()
}
});
} catch (error) {
logger.modelCall(req.params.modelId, 'embed', 0, false);
next(new APIError(`Embedding failed: ${error.message}`, 500));
}
},
/**
* Process batch of inputs
*/
batchProcess: async (req, res, next) => {
try {
const { modelId } = req.params;
const { operation, batch, options } = req.body;
if (!operation || !batch || !Array.isArray(batch)) {
return next(new APIError('Missing or invalid batch operation parameters', 400, 'INVALID_BATCH'));
}
const startTime = Date.now();
const results = await modelService.processBatch(modelId, operation, batch, options);
const duration = Date.now() - startTime;
// Log the model call
logger.modelCall(modelId, `batch_${operation}`, duration, true);
res.json({
model: modelId,
operation,
results,
metadata: {
processingTime: duration,
timestamp: new Date().toISOString(),
batchSize: batch.length
}
});
} catch (error) {
logger.modelCall(req.params.modelId, 'batch', 0, false);
next(new APIError(`Batch processing failed: ${error.message}`, 500));
}
},
/**
* Fine-tune a model
*/
fineTuneModel: async (req, res, next) => {
try {
const { modelId } = req.params;
const { trainingData, hyperparameters, options } = req.body;
if (!trainingData) {
return next(new APIError('Missing training data for fine-tuning', 400, 'MISSING_TRAINING_DATA'));
}
const startTime = Date.now();
const job = await modelService.startFineTune(modelId, trainingData, hyperparameters, options);
const duration = Date.now() - startTime;
// Log the model call
logger.modelCall(modelId, 'fine_tune', duration, true);
res.json({
model: modelId,
job,
metadata: {
timestamp: new Date().toISOString()
}
});
} catch (error) {
logger.modelCall(req.params.modelId, 'fine_tune', 0, false);
next(new APIError(`Fine-tuning failed: ${error.message}`, 500));
}
},
/**
* Get fine-tune job status
*/
getFineTuneStatus: async (req, res, next) => {
try {
const { modelId, jobId } = req.params;
const status = await modelService.getFineTuneStatus(modelId, jobId);
if (!status) {
return next(new APIError(`Fine-tune job not found: ${jobId}`, 404, 'JOB_NOT_FOUND'));
}
res.json({
model: modelId,
job: jobId,
status
});
} catch (error) {
next(new APIError(`Failed to retrieve fine-tune status: ${error.message}`, 500));
}
},
/**
* Register a new model
*/
registerModel: async (req, res, next) => {
try {
const { name, description, type, initialVersion, metadata } = req.body;
if (!name || !type) {
return next(new APIError('Missing required model information', 400, 'MISSING_MODEL_INFO'));
}
const model = await modelService.registerModel(name, description, type, initialVersion, metadata);
res.status(201).json(model);
} catch (error) {
next(new APIError(`Failed to register model: ${error.message}`, 500));
}
},
/**
* Update an existing model
*/
updateModel: async (req, res, next) => {
try {
const { modelId } = req.params;
const { name, description, type, status, metadata } = req.body;
const model = await modelService.updateModel(modelId, { name, description, type, status, metadata });
if (!model) {
return next(new APIError(`Model not found: ${modelId}`, 404, 'MODEL_NOT_FOUND'));
}
// Invalidate cache
cache.delete(`model_${modelId}`);
cache.delete('models_list');
res.json(model);
} catch (error) {
next(new APIError(`Failed to update model: ${error.message}`, 500));
}
},
/**
* Delete a model
*/
deleteModel: async (req, res, next) => {
try {
const { modelId } = req.params;
const success = await modelService.deleteModel(modelId);
if (!success) {
return next(new APIError(`Model not found: ${modelId}`, 404, 'MODEL_NOT_FOUND'));
}
// Invalidate cache
cache.delete(`model_${modelId}`);
cache.delete('models_list');
res.status(204).end();
} catch (error) {
next(new APIError(`Failed to delete model: ${error.message}`, 500));
}
},
/**
* Create a new model version
*/
createModelVersion: async (req, res, next) => {
try {
const { modelId } = req.params;
const { version, artifacts, metadata } = req.body;
if (!version || !artifacts) {
return next(new APIError('Missing required version information', 400, 'MISSING_VERSION_INFO'));
}
const modelVersion = await modelService.createModelVersion(modelId, version, artifacts, metadata);
res.status(201).json(modelVersion);
} catch (error) {
next(new APIError(`Failed to create model version: ${error.message}`, 500));
}
},
/**
* Update a model version
*/
updateModelVersion: async (req, res, next) => {
try {
const { modelId, versionId } = req.params;
const { status, metadata } = req.body;
const modelVersion = await modelService.updateModelVersion(modelId, versionId, { status, metadata });
if (!modelVersion) {
return next(new APIError(`Model version not found: ${modelId}/${versionId}`, 404, 'MODEL_VERSION_NOT_FOUND'));
}
res.json(modelVersion);
} catch (error) {
next(new APIError(`Failed to update model version: ${error.message}`, 500));
}
},
/**
* Delete a model version
*/
deleteModelVersion: async (req, res, next) => {
try {
const { modelId, versionId } = req.params;
const success = await modelService.deleteModelVersion(modelId, versionId);
if (!success) {
return next(new APIError(`Model version not found: ${modelId}/${versionId}`, 404, 'MODEL_VERSION_NOT_FOUND'));
}
res.status(204).end();
} catch (error) {
next(new APIError(`Failed to delete model version: ${error.message}`, 500));
}
},
};
export default modelController;