Skip to main content
Glama

Gemini MCP

by emmron
cache.js8.74 kB
import crypto from 'crypto'; import { logger } from './logger.js'; import { storage } from '../storage/storage.js'; class IntelligentCache { constructor() { this.memoryCache = new Map(); this.cacheStats = { hits: 0, misses: 0, evictions: 0, totalRequests: 0 }; this.maxMemoryItems = 1000; this.defaultTTL = 3600000; // 1 hour this.cleanupInterval = 300000; // 5 minutes // Start cleanup process this.startCleanupProcess(); logger.info('Intelligent cache initialized', { maxItems: this.maxMemoryItems, defaultTTL: this.defaultTTL }); } // Generate cache key from prompt and options generateKey(prompt, modelType, options = {}) { const keyData = { prompt: prompt.trim(), modelType, maxTokens: options.maxTokens || 2000, temperature: options.temperature || 0.7, complexity: options.complexity || 'medium' }; const keyString = JSON.stringify(keyData); return crypto.createHash('sha256').update(keyString).digest('hex').substring(0, 16); } // Check if response should be cached (avoid caching dynamic/time-sensitive content) shouldCache(prompt, modelType, options = {}) { const noCachePatterns = [ /current time/i, /today/i, /now/i, /latest/i, /recent/i, /this (week|month|year)/i, /random/i, /generate.*unique/i ]; // Don't cache time-sensitive or random content for (const pattern of noCachePatterns) { if (pattern.test(prompt)) { return false; } } // Don't cache very short prompts (likely to be dynamic) if (prompt.length < 20) { return false; } // Don't cache high temperature (creative) requests if ((options.temperature || 0.7) > 0.8) { return false; } return true; } // Get TTL based on content type and complexity getTTL(prompt, modelType) { // Longer TTL for stable content types const longTTL = 24 * 60 * 60 * 1000; // 24 hours const mediumTTL = 6 * 60 * 60 * 1000; // 6 hours const shortTTL = 1 * 60 * 60 * 1000; // 1 hour // Code analysis and reviews can be cached longer if (modelType === 'analysis' || modelType === 'review') { return longTTL; } // Documentation and explanations if (prompt.includes('explain') || prompt.includes('documentation')) { return mediumTTL; } // Security and debugging content if (modelType === 'debug' || modelType === 'security') { return mediumTTL; } // Default TTL return shortTTL; } // Get from cache async get(prompt, modelType, options = {}) { this.cacheStats.totalRequests++; if (!this.shouldCache(prompt, modelType, options)) { this.cacheStats.misses++; return null; } const key = this.generateKey(prompt, modelType, options); // Check memory cache first const memoryItem = this.memoryCache.get(key); if (memoryItem && memoryItem.expires > Date.now()) { this.cacheStats.hits++; memoryItem.lastAccessed = Date.now(); logger.debug('Cache hit (memory)', { key, modelType }); return memoryItem.data; } // Check persistent cache try { const persistentCache = await storage.read('cache'); const persistentItem = persistentCache[key]; if (persistentItem && persistentItem.expires > Date.now()) { this.cacheStats.hits++; // Promote to memory cache this.memoryCache.set(key, { data: persistentItem.data, expires: persistentItem.expires, lastAccessed: Date.now(), modelType }); logger.debug('Cache hit (persistent)', { key, modelType }); return persistentItem.data; } } catch (error) { logger.error('Persistent cache read failed', { error: error.message }); } this.cacheStats.misses++; return null; } // Set cache entry async set(prompt, modelType, options, data) { if (!this.shouldCache(prompt, modelType, options)) { return; } const key = this.generateKey(prompt, modelType, options); const ttl = this.getTTL(prompt, modelType); const expires = Date.now() + ttl; const cacheItem = { data, expires, lastAccessed: Date.now(), modelType, size: JSON.stringify(data).length }; // Add to memory cache this.memoryCache.set(key, cacheItem); // Evict if memory cache is too large if (this.memoryCache.size > this.maxMemoryItems) { this.evictLRU(); } // Add to persistent cache for larger/important items if (cacheItem.size > 1000 || modelType === 'analysis' || modelType === 'review') { try { const persistentCache = await storage.read('cache'); persistentCache[key] = { data, expires, modelType, cached: Date.now() }; await storage.write('cache', persistentCache); logger.debug('Cache set (persistent)', { key, modelType, ttl }); } catch (error) { logger.error('Persistent cache write failed', { error: error.message }); } } logger.debug('Cache set (memory)', { key, modelType, ttl }); } // Evict least recently used items from memory evictLRU() { const entries = Array.from(this.memoryCache.entries()); entries.sort((a, b) => a[1].lastAccessed - b[1].lastAccessed); const evictCount = Math.ceil(this.maxMemoryItems * 0.1); // Evict 10% for (let i = 0; i < evictCount && entries.length > 0; i++) { const [key] = entries.shift(); this.memoryCache.delete(key); this.cacheStats.evictions++; } logger.debug('Cache LRU eviction completed', { evicted: evictCount }); } // Start background cleanup process startCleanupProcess() { setInterval(() => { this.cleanup(); }, this.cleanupInterval); } // Clean up expired entries async cleanup() { const now = Date.now(); let expiredCount = 0; // Clean memory cache for (const [key, item] of this.memoryCache.entries()) { if (item.expires <= now) { this.memoryCache.delete(key); expiredCount++; } } // Clean persistent cache try { const persistentCache = await storage.read('cache'); const keys = Object.keys(persistentCache); for (const key of keys) { if (persistentCache[key].expires <= now) { delete persistentCache[key]; expiredCount++; } } if (expiredCount > 0) { await storage.write('cache', persistentCache); } } catch (error) { logger.error('Cache cleanup failed', { error: error.message }); } if (expiredCount > 0) { logger.debug('Cache cleanup completed', { expired: expiredCount }); } } // Get cache statistics getStats() { const hitRate = this.cacheStats.totalRequests > 0 ? (this.cacheStats.hits / this.cacheStats.totalRequests * 100).toFixed(2) : 0; return { ...this.cacheStats, hitRate: `${hitRate}%`, memoryItems: this.memoryCache.size, memoryUsage: this.calculateMemoryUsage() }; } calculateMemoryUsage() { let totalSize = 0; for (const item of this.memoryCache.values()) { totalSize += item.size || 0; } return `${(totalSize / 1024).toFixed(2)} KB`; } // Clear all cache async clear() { this.memoryCache.clear(); try { await storage.write('cache', {}); logger.info('All cache cleared'); } catch (error) { logger.error('Failed to clear persistent cache', { error: error.message }); } } // Warm up cache with common patterns async warmup(commonPrompts = []) { logger.info('Starting cache warmup', { prompts: commonPrompts.length }); const defaultPrompts = [ 'Explain the concept of dependency injection in JavaScript', 'What are the best practices for React component architecture?', 'How to implement proper error handling in Node.js?', 'Explain the differences between SQL and NoSQL databases', 'What is the purpose of Docker containerization?' ]; const allPrompts = [...commonPrompts, ...defaultPrompts]; // Pre-generate cache keys for common patterns for (const prompt of allPrompts) { const key = this.generateKey(prompt, 'main', {}); logger.debug('Warmup cache key generated', { key, prompt: prompt.substring(0, 50) }); } logger.info('Cache warmup completed'); } } export const intelligentCache = new IntelligentCache();

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/emmron/gemini-mcp'

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