UNPKG

vibe-coder-mcp

Version:

Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.

391 lines (390 loc) 15 kB
import { PerformanceMonitor } from './performance-monitor.js'; import { AppError } from '../../../utils/errors.js'; import logger from '../../../logger.js'; export class PerformanceBenchmarks { static instance = null; config; performanceMonitor; operations = new Map(); results = new Map(); baselines = new Map(); constructor(config) { this.config = config; this.performanceMonitor = PerformanceMonitor.getInstance(); this.initializeDefaultOperations(); logger.info({ config }, 'Performance Benchmarks initialized'); } static getInstance(config) { if (!PerformanceBenchmarks.instance) { if (!config) { throw new AppError('Benchmark configuration required for first initialization'); } PerformanceBenchmarks.instance = new PerformanceBenchmarks(config); } return PerformanceBenchmarks.instance; } initializeDefaultOperations() { this.registerOperation({ name: 'task_creation', category: 'task_management', targetTime: 10, description: 'Create a new task', operation: async () => { const start = performance.now(); await new Promise(resolve => setTimeout(resolve, 1)); return performance.now() - start; } }); this.registerOperation({ name: 'task_listing', category: 'task_management', targetTime: 20, description: 'List tasks with filtering', operation: async () => { const start = performance.now(); await new Promise(resolve => setTimeout(resolve, 2)); return performance.now() - start; } }); this.registerOperation({ name: 'task_execution', category: 'execution', targetTime: 50, description: 'Execute a task', operation: async () => { const start = performance.now(); await new Promise(resolve => setTimeout(resolve, 5)); return performance.now() - start; } }); this.registerOperation({ name: 'status_checking', category: 'task_management', targetTime: 15, description: 'Check task status', operation: async () => { const start = performance.now(); await new Promise(resolve => setTimeout(resolve, 1)); return performance.now() - start; } }); this.registerOperation({ name: 'storage_read', category: 'storage', targetTime: 10, description: 'Read from storage', operation: async () => { const start = performance.now(); await new Promise(resolve => setTimeout(resolve, 1)); return performance.now() - start; } }); this.registerOperation({ name: 'storage_write', category: 'storage', targetTime: 15, description: 'Write to storage', operation: async () => { const start = performance.now(); await new Promise(resolve => setTimeout(resolve, 2)); return performance.now() - start; } }); this.registerOperation({ name: 'cache_get', category: 'cache', targetTime: 5, description: 'Get from cache', operation: async () => { const start = performance.now(); await new Promise(resolve => setTimeout(resolve, 0.5)); return performance.now() - start; } }); this.registerOperation({ name: 'cache_set', category: 'cache', targetTime: 8, description: 'Set cache value', operation: async () => { const start = performance.now(); await new Promise(resolve => setTimeout(resolve, 1)); return performance.now() - start; } }); this.registerOperation({ name: 'memory_allocation', category: 'memory', targetTime: 5, description: 'Memory allocation and cleanup', operation: async () => { const start = performance.now(); const data = new Array(1000).fill(0); data.length = 0; return performance.now() - start; } }); logger.info(`Initialized ${this.operations.size} default benchmark operations`); } registerOperation(operation) { this.operations.set(operation.name, operation); logger.debug({ operationName: operation.name }, 'Benchmark operation registered'); } async runBenchmark(operationName) { const operation = this.operations.get(operationName); if (!operation) { throw new AppError(`Benchmark operation not found: ${operationName}`); } logger.info({ operationName }, 'Running benchmark'); if (operation.setup) { await operation.setup(); } try { for (let i = 0; i < this.config.warmupIterations; i++) { await operation.operation(); } const times = []; const memoryUsages = []; for (let i = 0; i < this.config.iterations; i++) { const initialMemory = process.memoryUsage().heapUsed; const operationId = `benchmark_${operationName}_${i}`; this.performanceMonitor.startOperation(operationId); await operation.operation(); const duration = this.performanceMonitor.endOperation(operationId, { benchmark: true, iteration: i, operationName }); const finalMemory = process.memoryUsage().heapUsed; const memoryUsed = (finalMemory - initialMemory) / 1024 / 1024; times.push(duration); memoryUsages.push(memoryUsed); } const averageTime = times.reduce((sum, time) => sum + time, 0) / times.length; const averageMemory = memoryUsages.reduce((sum, mem) => sum + mem, 0) / memoryUsages.length; const passed = averageTime <= operation.targetTime; const baseline = this.baselines.get(operationName); let improvement; let regression; if (baseline) { const change = ((baseline - averageTime) / baseline) * 100; if (change > 0) { improvement = change; } else { regression = Math.abs(change); } } else { this.baselines.set(operationName, averageTime); } const result = { operationName, category: operation.category, targetTime: operation.targetTime, actualTime: averageTime, memoryUsed: averageMemory, iterations: this.config.iterations, timestamp: new Date(), passed, improvement, regression }; if (!this.results.has(operationName)) { this.results.set(operationName, []); } this.results.get(operationName).push(result); const operationResults = this.results.get(operationName); if (operationResults.length > 100) { operationResults.splice(0, operationResults.length - 100); } logger.info({ operationName, averageTime: averageTime.toFixed(2), targetTime: operation.targetTime, passed, improvement: improvement?.toFixed(2), regression: regression?.toFixed(2) }, 'Benchmark completed'); return result; } finally { if (operation.cleanup) { await operation.cleanup(); } } } async runAllBenchmarks() { const results = []; logger.info(`Running ${this.operations.size} benchmark operations`); for (const operationName of this.operations.keys()) { try { const result = await this.runBenchmark(operationName); results.push(result); } catch (error) { logger.error({ operationName, error }, 'Benchmark failed'); } } return results; } async runCategoryBenchmarks(category) { const results = []; for (const [name, operation] of this.operations) { if (operation.category === category) { try { const result = await this.runBenchmark(name); results.push(result); } catch (error) { logger.error({ operationName: name, error }, 'Category benchmark failed'); } } } return results; } getBenchmarkResults(operationName, limit) { const results = this.results.get(operationName) || []; return limit ? results.slice(-limit) : results; } getAllBenchmarkResults() { return new Map(this.results); } detectRegressions() { if (!this.config.enableRegression) { return []; } const regressions = []; const now = Date.now(); const baselineWindow = this.config.baselineWindow * 60 * 60 * 1000; for (const [operationName, results] of this.results) { if (results.length < 2) continue; const baselineResults = results.filter(r => now - r.timestamp.getTime() >= baselineWindow); const recentResults = results.filter(r => now - r.timestamp.getTime() < baselineWindow); if (baselineResults.length === 0 || recentResults.length === 0) continue; const baselineAvg = baselineResults.reduce((sum, r) => sum + r.actualTime, 0) / baselineResults.length; const recentAvg = recentResults.reduce((sum, r) => sum + r.actualTime, 0) / recentResults.length; const regressionPercentage = ((recentAvg - baselineAvg) / baselineAvg) * 100; if (regressionPercentage > 10) { let severity = 'low'; let recommendation = 'Monitor performance trends'; if (regressionPercentage > 50) { severity = 'critical'; recommendation = 'Immediate optimization required'; } else if (regressionPercentage > 30) { severity = 'high'; recommendation = 'Performance optimization needed'; } else if (regressionPercentage > 20) { severity = 'medium'; recommendation = 'Consider performance improvements'; } regressions.push({ operationName, baselineTime: baselineAvg, currentTime: recentAvg, regressionPercentage, severity, recommendation }); } } return regressions; } getPerformanceSummary() { const latestResults = []; for (const results of this.results.values()) { if (results.length > 0) { latestResults.push(results[results.length - 1]); } } const totalOperations = latestResults.length; const passedOperations = latestResults.filter(r => r.passed).length; const failedOperations = totalOperations - passedOperations; const averagePerformance = totalOperations > 0 ? latestResults.reduce((sum, r) => sum + (r.actualTime / r.targetTime), 0) / totalOperations : 0; const regressions = this.detectRegressions(); const regressionCount = regressions.length; let overallHealth = 'excellent'; if (failedOperations > totalOperations * 0.5 || regressions.some(r => r.severity === 'critical')) { overallHealth = 'critical'; } else if (failedOperations > totalOperations * 0.3 || regressions.some(r => r.severity === 'high')) { overallHealth = 'warning'; } else if (failedOperations > 0 || regressions.length > 0) { overallHealth = 'good'; } return { totalOperations, passedOperations, failedOperations, averagePerformance, regressionCount, overallHealth }; } updateBaseline(operationName, baselineTime) { if (baselineTime !== undefined) { this.baselines.set(operationName, baselineTime); } else { const results = this.results.get(operationName); if (results && results.length > 0) { const latestResult = results[results.length - 1]; this.baselines.set(operationName, latestResult.actualTime); } } logger.info({ operationName, baseline: this.baselines.get(operationName) }, 'Baseline updated'); } clearResults(operationName) { if (operationName) { this.results.delete(operationName); this.baselines.delete(operationName); logger.info({ operationName }, 'Benchmark results cleared'); } else { this.results.clear(); this.baselines.clear(); logger.info('All benchmark results cleared'); } } exportResults() { const operations = Array.from(this.operations.values()); const results = {}; const baselines = {}; for (const [name, resultArray] of this.results) { results[name] = resultArray; } for (const [name, baseline] of this.baselines) { baselines[name] = baseline; } return { operations, results, baselines, summary: this.getPerformanceSummary(), regressions: this.detectRegressions() }; } } export const DEFAULT_BENCHMARK_CONFIG = { iterations: 10, warmupIterations: 3, targetOverhead: 50, memoryThreshold: 100, enableRegression: true, baselineWindow: 24 }; export function getPerformanceBenchmarks() { try { return PerformanceBenchmarks.getInstance(); } catch { return null; } }