Skip to main content
Glama

Self-Improving Memory MCP

by SuperPiTT
performance.js8.37 kB
/** * Performance Monitoring System * Tracks metrics for operations, embeddings, searches, and database size */ import { logger } from './logger.js'; /** * Performance metrics storage */ class PerformanceMetrics { constructor() { this.metrics = { embeddings: { count: 0, totalTime: 0, avgTime: 0, minTime: Infinity, maxTime: 0, history: [] // Last 100 measurements }, searches: { count: 0, totalTime: 0, avgTime: 0, minTime: Infinity, maxTime: 0, history: [] }, database: { size: 0, // Number of vectors lastUpdated: null }, operations: {} // Generic operation tracking }; } /** * Record embedding generation time * @param {number} duration - Time in milliseconds */ recordEmbedding(duration) { const m = this.metrics.embeddings; m.count++; m.totalTime += duration; m.avgTime = m.totalTime / m.count; m.minTime = Math.min(m.minTime, duration); m.maxTime = Math.max(m.maxTime, duration); // Keep last 100 measurements m.history.push({ timestamp: Date.now(), duration }); if (m.history.length > 100) { m.history.shift(); } // Log slow embeddings if (duration > 1000) { logger.warn('Slow embedding generation', { duration: `${duration}ms`, avgTime: `${m.avgTime.toFixed(2)}ms` }); } } /** * Record search latency * @param {number} duration - Time in milliseconds * @param {Object} metadata - Search metadata (query, results count, etc.) */ recordSearch(duration, metadata = {}) { const m = this.metrics.searches; m.count++; m.totalTime += duration; m.avgTime = m.totalTime / m.count; m.minTime = Math.min(m.minTime, duration); m.maxTime = Math.max(m.maxTime, duration); // Keep last 100 measurements m.history.push({ timestamp: Date.now(), duration, ...metadata }); if (m.history.length > 100) { m.history.shift(); } // Log slow searches if (duration > 2000) { logger.warn('Slow search operation', { duration: `${duration}ms`, avgTime: `${m.avgTime.toFixed(2)}ms`, ...metadata }); } } /** * Record generic operation * @param {string} operationName - Operation identifier * @param {number} duration - Time in milliseconds * @param {Object} metadata - Additional metadata */ recordOperation(operationName, duration, metadata = {}) { if (!this.metrics.operations[operationName]) { this.metrics.operations[operationName] = { count: 0, totalTime: 0, avgTime: 0, minTime: Infinity, maxTime: 0, history: [] }; } const m = this.metrics.operations[operationName]; m.count++; m.totalTime += duration; m.avgTime = m.totalTime / m.count; m.minTime = Math.min(m.minTime, duration); m.maxTime = Math.max(m.maxTime, duration); m.history.push({ timestamp: Date.now(), duration, ...metadata }); if (m.history.length > 100) { m.history.shift(); } logger.debug(`Operation: ${operationName}`, { duration: `${duration}ms`, avgTime: `${m.avgTime.toFixed(2)}ms`, ...metadata }); } /** * Update database size metric * @param {number} size - Number of vectors */ updateDatabaseSize(size) { this.metrics.database.size = size; this.metrics.database.lastUpdated = Date.now(); logger.info('Database size updated', { vectors: size, timestamp: new Date().toISOString() }); } /** * Get all metrics * @returns {Object} Current metrics */ getMetrics() { return { embeddings: { count: this.metrics.embeddings.count, avgTime: this.metrics.embeddings.avgTime.toFixed(2), minTime: this.metrics.embeddings.minTime === Infinity ? 0 : this.metrics.embeddings.minTime, maxTime: this.metrics.embeddings.maxTime }, searches: { count: this.metrics.searches.count, avgTime: this.metrics.searches.avgTime.toFixed(2), minTime: this.metrics.searches.minTime === Infinity ? 0 : this.metrics.searches.minTime, maxTime: this.metrics.searches.maxTime }, database: { size: this.metrics.database.size, lastUpdated: this.metrics.database.lastUpdated }, operations: Object.fromEntries( Object.entries(this.metrics.operations).map(([name, m]) => [ name, { count: m.count, avgTime: m.avgTime.toFixed(2), minTime: m.minTime === Infinity ? 0 : m.minTime, maxTime: m.maxTime } ]) ) }; } /** * Get recent performance trends (last 10 measurements) * @param {string} type - 'embeddings' or 'searches' * @returns {Array} Recent measurements */ getRecentTrends(type) { const metrics = this.metrics[type]; if (!metrics || !metrics.history) return []; return metrics.history.slice(-10).map(m => ({ timestamp: m.timestamp, duration: m.duration, date: new Date(m.timestamp).toISOString() })); } /** * Reset all metrics */ reset() { this.metrics = { embeddings: { count: 0, totalTime: 0, avgTime: 0, minTime: Infinity, maxTime: 0, history: [] }, searches: { count: 0, totalTime: 0, avgTime: 0, minTime: Infinity, maxTime: 0, history: [] }, database: { size: 0, lastUpdated: null }, operations: {} }; logger.info('Performance metrics reset'); } /** * Get performance summary report * @returns {string} Human-readable summary */ getSummary() { const m = this.getMetrics(); const lines = [ '=== Performance Metrics Summary ===', '', `Embeddings: ${m.embeddings.count} total`, ` Avg: ${m.embeddings.avgTime}ms | Min: ${m.embeddings.minTime}ms | Max: ${m.embeddings.maxTime}ms`, '', `Searches: ${m.searches.count} total`, ` Avg: ${m.searches.avgTime}ms | Min: ${m.searches.minTime}ms | Max: ${m.searches.maxTime}ms`, '', `Database: ${m.database.size} vectors`, ` Last updated: ${m.database.lastUpdated ? new Date(m.database.lastUpdated).toISOString() : 'Never'}`, '' ]; if (Object.keys(m.operations).length > 0) { lines.push('Operations:'); for (const [name, stats] of Object.entries(m.operations)) { lines.push(` ${name}: ${stats.count} calls, avg ${stats.avgTime}ms`); } } return lines.join('\n'); } } /** * Performance timer utility */ export class PerformanceTimer { constructor(name) { this.name = name; this.startTime = Date.now(); } /** * Stop timer and return duration * @returns {number} Duration in milliseconds */ stop() { return Date.now() - this.startTime; } /** * Stop timer and record to metrics * @param {string} type - 'embedding', 'search', or operation name * @param {Object} metadata - Additional metadata * @returns {number} Duration in milliseconds */ record(type, metadata = {}) { const duration = this.stop(); if (type === 'embedding') { performanceMetrics.recordEmbedding(duration); } else if (type === 'search') { performanceMetrics.recordSearch(duration, metadata); } else { performanceMetrics.recordOperation(type, duration, metadata); } return duration; } } /** * Global metrics instance */ export const performanceMetrics = new PerformanceMetrics(); /** * Helper to time an async operation * @param {string} name - Operation name * @param {Function} fn - Async function to time * @returns {Promise<any>} Result of the function */ export async function timeOperation(name, fn) { const timer = new PerformanceTimer(name); try { const result = await fn(); timer.record(name); return result; } catch (error) { const duration = timer.stop(); logger.error(`Operation ${name} failed after ${duration}ms`, { error: error.message, duration: `${duration}ms` }); throw error; } } export default performanceMetrics;

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/SuperPiTT/self-improving-memory-mcp'

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