alwas-ml-models / integration /routes-ml.js
muthuk1's picture
Upload integration/routes-ml.js
ed94e30 verified
/**
* ALWAS ML Routes — Drop-in Express.js routes
* Add to your server/routes/ directory and mount in app.js
*
* Mount: app.use('/api/ml', require('./routes/ml'));
*/
const express = require('express');
const router = express.Router();
const ml = require('../utils/alwas-ml-client'); // adjust path as needed
const Block = require('../models/Block');
const { isAuthenticated, isManager } = require('../middleware/auth');
/**
* POST /api/ml/estimate
* AI-powered block complexity estimation (replaces Groq API)
* Access: Manager
*/
router.post('/estimate', isAuthenticated, isManager, async (req, res) => {
try {
const estimate = await ml.estimateBlock(req.body);
res.json(estimate);
} catch (error) {
console.error('ML estimation error:', error.message);
res.status(500).json({ error: 'ML estimation failed', details: error.message });
}
});
/**
* GET /api/ml/block/:id/risk
* Get bottleneck risk assessment for a specific block
* Access: User
*/
router.get('/block/:id/risk', isAuthenticated, async (req, res) => {
try {
const block = await Block.findById(req.params.id);
if (!block) return res.status(404).json({ error: 'Block not found' });
// Calculate days in current stage
const lastTransition = block.transitions?.[block.transitions.length - 1];
const daysSince = lastTransition
? (Date.now() - new Date(lastTransition.timestamp)) / (1000 * 60 * 60 * 24)
: 0;
const riskData = ml.constructor.formatForBottleneck(block, daysSince);
const risk = await ml.predictBottleneck(riskData);
res.json({ blockId: block._id, blockName: block.name, ...risk });
} catch (error) {
console.error('ML risk prediction error:', error.message);
res.status(500).json({ error: 'Risk prediction failed', details: error.message });
}
});
/**
* GET /api/ml/block/:id/eta
* Get estimated completion time for a specific block
* Access: User
*/
router.get('/block/:id/eta', isAuthenticated, async (req, res) => {
try {
const block = await Block.findById(req.params.id);
if (!block) return res.status(404).json({ error: 'Block not found' });
if (block.status === 'Completed') {
return res.json({ remaining_hours: 0, progress_percent: 100, status: 'completed' });
}
const completionData = ml.constructor.formatForCompletion(block);
const eta = await ml.predictCompletion(completionData);
res.json({ blockId: block._id, blockName: block.name, ...eta });
} catch (error) {
console.error('ML completion prediction error:', error.message);
res.status(500).json({ error: 'Completion prediction failed', details: error.message });
}
});
/**
* POST /api/ml/bulk-estimate
* Bulk estimation for CSV import
* Access: Manager
*/
router.post('/bulk-estimate', isAuthenticated, isManager, async (req, res) => {
try {
const { blocks } = req.body;
if (!blocks || !Array.isArray(blocks)) {
return res.status(400).json({ error: 'blocks array required' });
}
if (blocks.length > 200) {
return res.status(400).json({ error: 'Maximum 200 blocks per request' });
}
const estimates = await ml.bulkEstimate(blocks);
res.json(estimates);
} catch (error) {
console.error('ML bulk estimation error:', error.message);
res.status(500).json({ error: 'Bulk estimation failed', details: error.message });
}
});
/**
* GET /api/ml/scan/bottlenecks
* Scan ALL in-progress blocks for bottleneck risks
* Access: Manager
*/
router.get('/scan/bottlenecks', isAuthenticated, isManager, async (req, res) => {
try {
const blocks = await Block.find({ status: { $nin: ['Not Started', 'Completed'] } });
const results = [];
for (const block of blocks) {
const lastTransition = block.transitions?.[block.transitions.length - 1];
const daysSince = lastTransition
? (Date.now() - new Date(lastTransition.timestamp)) / (1000 * 60 * 60 * 24)
: 0;
const riskData = ml.constructor.formatForBottleneck(block, daysSince);
const risk = await ml.predictBottleneck(riskData);
if (risk.risk_level !== 'Low') {
results.push({
blockId: block._id,
blockName: block.name,
stage: block.status,
engineer: block.assignedTo,
...risk,
});
}
}
// Sort by risk (High first)
results.sort((a, b) => {
const order = { High: 0, Medium: 1, Low: 2 };
return (order[a.risk_level] || 2) - (order[b.risk_level] || 2);
});
res.json({
total_scanned: blocks.length,
at_risk: results.length,
high_risk: results.filter(r => r.risk_level === 'High').length,
medium_risk: results.filter(r => r.risk_level === 'Medium').length,
blocks: results
});
} catch (error) {
console.error('ML bottleneck scan error:', error.message);
res.status(500).json({ error: 'Bottleneck scan failed', details: error.message });
}
});
/**
* GET /api/ml/health
* ML service health check
*/
router.get('/health', async (req, res) => {
try {
const health = await ml.healthCheck();
res.json(health);
} catch (error) {
res.status(503).json({ status: 'unhealthy', error: error.message });
}
});
/**
* GET /api/ml/metrics
* Get model performance metrics
* Access: Manager
*/
router.get('/metrics', isAuthenticated, isManager, async (req, res) => {
try {
const metrics = await ml.getMetrics();
res.json(metrics);
} catch (error) {
res.status(500).json({ error: 'Failed to fetch metrics', details: error.message });
}
});
module.exports = router;