Skip to main content
Glama

MCP Memory Service

performance-manager.js11.8 kB
/** * Performance Manager for Memory Hooks * Provides intelligent performance monitoring and adaptive hook management */ class PerformanceManager { constructor(config = {}) { this.config = config; this.metrics = { totalLatency: [], hookLatencies: new Map(), userSatisfaction: [], degradationEvents: 0 }; // Performance tiers this.tiers = { instant: { maxLatency: 50, priority: 'critical' }, fast: { maxLatency: 150, priority: 'high' }, intensive: { maxLatency: 500, priority: 'medium' } }; // Current performance profile this.activeProfile = config.defaultProfile || 'balanced'; this.performanceBudget = this.getProfileBudget(this.activeProfile); // Adaptive learning this.userPreferences = { toleranceLevel: 0.5, // 0 = speed focused, 1 = memory focused learningEnabled: true, feedbackHistory: [] }; } /** * Get performance budget for a profile */ getProfileBudget(profileName) { // Use config profiles first, with hardcoded fallbacks const configProfiles = this.config.profiles || {}; // If profile exists in config, use it (with fallback for missing adaptive calculations) if (configProfiles[profileName]) { const profile = { ...configProfiles[profileName] }; // Handle adaptive profile calculations if needed if (profileName === 'adaptive') { profile.maxLatency = profile.maxLatency || this.calculateAdaptiveLatency(); profile.enabledTiers = profile.enabledTiers || this.calculateAdaptiveTiers(); } return profile; } // Fallback to hardcoded profiles if not found in config const fallbackProfiles = { speed_focused: { maxLatency: 100, enabledTiers: ['instant'], backgroundProcessing: false, degradeThreshold: 200 }, balanced: { maxLatency: 200, enabledTiers: ['instant', 'fast'], backgroundProcessing: true, degradeThreshold: 400 }, memory_aware: { maxLatency: 500, enabledTiers: ['instant', 'fast', 'intensive'], backgroundProcessing: true, degradeThreshold: 1000 }, adaptive: { maxLatency: this.calculateAdaptiveLatency(), enabledTiers: this.calculateAdaptiveTiers(), backgroundProcessing: true, degradeThreshold: 800, autoAdjust: true } }; return fallbackProfiles[profileName] || fallbackProfiles.balanced; } /** * Calculate adaptive latency based on user behavior */ calculateAdaptiveLatency() { if (this.metrics.totalLatency.length < 10) { return 200; // Default for new users } const avgLatency = this.metrics.totalLatency.reduce((a, b) => a + b, 0) / this.metrics.totalLatency.length; const userTolerance = this.userPreferences?.toleranceLevel || 0.5; // Adaptive calculation: balance observed tolerance with user preference return Math.min(500, Math.max(100, avgLatency * (1 + userTolerance))); } /** * Calculate which tiers should be enabled adaptively */ calculateAdaptiveTiers() { const tolerance = this.userPreferences?.toleranceLevel || 0.5; if (tolerance < 0.3) return ['instant']; if (tolerance < 0.7) return ['instant', 'fast']; return ['instant', 'fast', 'intensive']; } /** * Start timing a hook operation */ startTiming(hookName, tier = 'fast') { return { hookName, tier, startTime: Date.now(), expectedLatency: this.tiers[tier]?.maxLatency || 150 }; } /** * End timing and record metrics */ endTiming(timingContext) { const endTime = Date.now(); const latency = endTime - timingContext.startTime; // Record metrics this.recordHookLatency(timingContext.hookName, latency, timingContext.tier); this.recordTotalLatency(latency); // Check if we exceeded performance budget const exceedsThreshold = latency > this.performanceBudget.degradeThreshold; if (exceedsThreshold) { this.handlePerformanceDegradation(timingContext.hookName, latency); } return { latency, tier: timingContext.tier, withinBudget: latency <= this.performanceBudget.maxLatency, exceedsThreshold }; } /** * Record hook-specific latency */ recordHookLatency(hookName, latency, tier) { if (!this.metrics.hookLatencies.has(hookName)) { this.metrics.hookLatencies.set(hookName, []); } const hookMetrics = this.metrics.hookLatencies.get(hookName); hookMetrics.push({ latency, tier, timestamp: Date.now() }); // Keep only recent measurements (last 50) if (hookMetrics.length > 50) { hookMetrics.splice(0, hookMetrics.length - 50); } } /** * Record total request latency */ recordTotalLatency(latency) { this.metrics.totalLatency.push(latency); // Keep rolling window of recent measurements if (this.metrics.totalLatency.length > 100) { this.metrics.totalLatency.splice(0, this.metrics.totalLatency.length - 100); } } /** * Handle performance degradation */ handlePerformanceDegradation(hookName, latency) { this.metrics.degradationEvents++; console.warn(`[Performance] Hook "${hookName}" exceeded threshold: ${latency}ms`); // Adaptive response based on profile if (this.performanceBudget.autoAdjust) { this.adaptToPerformance(hookName, latency); } } /** * Adapt hooks based on performance */ adaptToPerformance(hookName, latency) { // If a hook consistently performs poorly, suggest tier reduction const hookHistory = this.metrics.hookLatencies.get(hookName) || []; const recentHistory = hookHistory.slice(-10); if (recentHistory.length >= 5) { const avgLatency = recentHistory.reduce((a, b) => a + b.latency, 0) / recentHistory.length; if (avgLatency > this.performanceBudget.maxLatency * 1.5) { // Suggest moving hook to lower tier or disabling this.suggestHookOptimization(hookName, avgLatency); } } } /** * Suggest hook optimization */ suggestHookOptimization(hookName, avgLatency) { const suggestion = { hookName, avgLatency, suggestion: avgLatency > 300 ? 'disable' : 'reduce_tier', timestamp: Date.now() }; console.log(`[Performance] Suggestion for ${hookName}: ${suggestion.suggestion} (avg: ${avgLatency}ms)`); return suggestion; } /** * Check if a hook should run based on current performance profile */ shouldRunHook(hookName, tier = 'fast') { const profile = this.performanceBudget; // Check if tier is enabled if (!profile.enabledTiers.includes(tier)) { return false; } // Check recent performance const hookHistory = this.metrics.hookLatencies.get(hookName); if (hookHistory && hookHistory.length > 5) { const recentLatencies = hookHistory.slice(-5); const avgLatency = recentLatencies.reduce((a, b) => a + b.latency, 0) / recentLatencies.length; // Don't run if consistently exceeds budget if (avgLatency > profile.maxLatency * 1.2) { return false; } } return true; } /** * Switch performance profile */ switchProfile(profileName) { if (!['speed_focused', 'balanced', 'memory_aware', 'adaptive'].includes(profileName)) { throw new Error(`Invalid profile: ${profileName}`); } this.activeProfile = profileName; this.performanceBudget = this.getProfileBudget(profileName); console.log(`[Performance] Switched to profile: ${profileName}`); return this.performanceBudget; } /** * Learn from user feedback */ recordUserFeedback(isPositive, context = {}) { if (!this.userPreferences.learningEnabled) return; const feedback = { positive: isPositive, context, latency: context.latency || 0, timestamp: Date.now() }; this.userPreferences.feedbackHistory.push(feedback); // Update tolerance based on feedback this.updateUserTolerance(feedback); // Keep feedback history manageable if (this.userPreferences.feedbackHistory.length > 50) { this.userPreferences.feedbackHistory.splice(0, 10); } } /** * Update user tolerance based on feedback patterns */ updateUserTolerance(feedback) { const recent = this.userPreferences?.feedbackHistory?.slice(-10) || []; const positiveCount = recent.filter(f => f.positive).length; const negativeCount = recent.length - positiveCount; // Ensure userPreferences is initialized if (!this.userPreferences) { this.userPreferences = { toleranceLevel: 0.5, learningEnabled: true, feedbackHistory: [] }; } // Adjust tolerance based on feedback patterns if (feedback.positive && feedback.latency > 200) { // User satisfied with higher latency, increase tolerance this.userPreferences.toleranceLevel = Math.min(1.0, this.userPreferences.toleranceLevel + 0.1); } else if (!feedback.positive && feedback.latency > 100) { // User dissatisfied with latency, decrease tolerance this.userPreferences.toleranceLevel = Math.max(0.0, this.userPreferences.toleranceLevel - 0.1); } } /** * Get performance report */ getPerformanceReport() { const totalRequests = this.metrics.totalLatency.length; const avgLatency = totalRequests > 0 ? this.metrics.totalLatency.reduce((a, b) => a + b, 0) / totalRequests : 0; const hookSummary = {}; this.metrics.hookLatencies.forEach((latencies, hookName) => { const avgHookLatency = latencies.reduce((a, b) => a + b.latency, 0) / latencies.length; hookSummary[hookName] = { avgLatency: Math.round(avgHookLatency), calls: latencies.length, tier: latencies[latencies.length - 1]?.tier || 'unknown' }; }); return { profile: this.activeProfile, totalRequests, avgLatency: Math.round(avgLatency), degradationEvents: this.metrics.degradationEvents, userTolerance: this.userPreferences.toleranceLevel, hookPerformance: hookSummary, budget: this.performanceBudget }; } /** * Reset metrics (useful for testing) */ resetMetrics() { this.metrics = { totalLatency: [], hookLatencies: new Map(), userSatisfaction: [], degradationEvents: 0 }; } } module.exports = { PerformanceManager };

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/doobidoo/mcp-memory-service'

If you have feedback or need assistance with the MCP directory API, please join our Discord server