/** * Cache Augmentation - Optional Search Result Caching * * Replaces the hardcoded SearchCache in Brainy with an optional augmentation. * This reduces core size and allows custom cache implementations. * * Zero-config: Automatically enabled with sensible defaults * Can be disabled or customized via augmentation registry */ import { BaseAugmentation } from './brainyAugmentation.js'; import { SearchCache } from '../utils/searchCache.js'; /** * CacheAugmentation - Makes search caching optional and pluggable * * Features: * - Transparent search result caching * - Automatic invalidation on data changes * - Memory-aware cache management * - Zero-config with smart defaults */ export class CacheAugmentation extends BaseAugmentation { constructor(config) { super(config); this.name = 'cache'; this.timing = 'around'; this.metadata = 'none'; // Cache doesn't access metadata this.operations = ['search', 'find', 'similar', 'add', 'update', 'delete', 'clear', 'all']; this.priority = 50; // Mid-priority, runs after data operations // Augmentation metadata this.category = 'core'; this.description = 'Transparent search result caching with automatic invalidation'; this.searchCache = null; } getManifest() { return { id: 'cache', name: 'Cache', version: '2.0.0', description: 'Intelligent caching for search and query operations', longDescription: 'Provides transparent caching for search results with automatic invalidation on data changes. Significantly improves performance for repeated queries while maintaining data consistency.', category: 'performance', configSchema: { type: 'object', properties: { enabled: { type: 'boolean', default: true, description: 'Enable or disable caching' }, maxSize: { type: 'number', default: 1000, minimum: 10, maximum: 100000, description: 'Maximum number of cached entries' }, ttl: { type: 'number', default: 300000, // 5 minutes minimum: 1000, // 1 second maximum: 3600000, // 1 hour description: 'Time to live for cache entries in milliseconds' }, invalidateOnWrite: { type: 'boolean', default: true, description: 'Automatically invalidate cache on data modifications' } }, additionalProperties: false }, configDefaults: { enabled: true, maxSize: 1000, ttl: 300000, invalidateOnWrite: true }, configExamples: [ { name: 'High Performance', description: 'Large cache with longer TTL for read-heavy workloads', config: { enabled: true, maxSize: 10000, ttl: 1800000, // 30 minutes invalidateOnWrite: true } }, { name: 'Conservative', description: 'Small cache with short TTL for frequently changing data', config: { enabled: true, maxSize: 100, ttl: 60000, // 1 minute invalidateOnWrite: true } } ], minBrainyVersion: '2.0.0', keywords: ['cache', 'performance', 'search', 'optimization'], documentation: 'https://docs.brainy.dev/augmentations/cache', status: 'stable', performance: { memoryUsage: 'medium', cpuUsage: 'low', networkUsage: 'none' }, features: ['search-caching', 'auto-invalidation', 'ttl-support', 'memory-management'], enhancedOperations: ['search', 'searchText', 'findSimilar'], metrics: [ { name: 'cache_hits', type: 'counter', description: 'Number of cache hits' }, { name: 'cache_misses', type: 'counter', description: 'Number of cache misses' }, { name: 'cache_size', type: 'gauge', description: 'Current cache size' } ], ui: { icon: '⚡', color: '#FFC107' } }; } async onInitialize() { if (!this.config.enabled) { this.log('Cache augmentation disabled by configuration'); return; } // Initialize search cache with config this.searchCache = new SearchCache({ maxSize: this.config.maxSize, maxAge: this.config.ttl, // SearchCache uses maxAge, not ttl enabled: true }); this.log(`Cache augmentation initialized (maxSize: ${this.config.maxSize}, ttl: ${this.config.ttl}ms)`); } async onShutdown() { if (this.searchCache) { this.searchCache.clear(); this.searchCache = null; } this.log('Cache augmentation shut down'); } /** * Execute augmentation - wrap operations with caching logic */ async execute(operation, params, next) { // If cache is disabled, just pass through if (!this.searchCache || !this.config.enabled) { return next(); } switch (operation) { case 'search': return this.handleSearch(params, next); case 'add': case 'update': case 'delete': // Invalidate cache on data changes if (this.config.invalidateOnWrite) { const result = await next(); this.searchCache.invalidateOnDataChange(operation); this.log(`Cache invalidated due to ${operation} operation`); return result; } return next(); case 'clear': // Clear cache when all data is cleared const result = await next(); this.searchCache.clear(); this.log('Cache cleared due to clear operation'); return result; default: return next(); } } /** * Handle search operation with caching */ async handleSearch(params, next) { if (!this.searchCache) return next(); // Extract search parameters const { query, k, options = {} } = params; // Skip cache if explicitly disabled or has complex filters if (options.skipCache || options.metadata) { return next(); } // Generate cache key const cacheKey = this.searchCache.getCacheKey(query, k, options); // Check cache const cachedResult = this.searchCache.get(cacheKey); if (cachedResult) { this.log('Cache hit for search query'); // Update metrics if available if (this.context?.brain) { const metrics = this.context.brain.augmentations?.get('metrics'); if (metrics) { metrics.recordCacheHit?.(); } } return cachedResult; } // Execute search const result = await next(); // Cache the result this.searchCache.set(cacheKey, result); this.log('Search result cached'); // Update metrics if available if (this.context?.brain) { const metrics = this.context.brain.augmentations?.get('metrics'); if (metrics) { metrics.recordCacheMiss?.(); } } return result; } /** * Get cache statistics */ getStats() { if (!this.searchCache) { return { enabled: false, hits: 0, misses: 0, size: 0, memoryUsage: 0 }; } const stats = this.searchCache.getStats(); return { ...stats, memoryUsage: this.searchCache.getMemoryUsage() }; } /** * Clear the cache manually */ clear() { if (this.searchCache) { this.searchCache.clear(); this.log('Cache manually cleared'); } } /** * Handle runtime configuration changes */ async onConfigChange(newConfig, oldConfig) { if (this.searchCache && newConfig.enabled) { this.searchCache.updateConfig({ maxSize: newConfig.maxSize, maxAge: newConfig.ttl, // SearchCache uses maxAge enabled: newConfig.enabled }); this.log('Cache configuration updated'); } else if (!newConfig.enabled && this.searchCache) { this.searchCache.clear(); this.log('Cache disabled and cleared'); } } /** * Clean up expired entries */ cleanupExpiredEntries() { if (!this.searchCache) return 0; const cleaned = this.searchCache.cleanupExpiredEntries(); if (cleaned > 0) { this.log(`Cleaned ${cleaned} expired cache entries`); } return cleaned; } /** * Invalidate cache when data changes */ invalidateOnDataChange(operation) { if (!this.searchCache) return; this.searchCache.invalidateOnDataChange(operation); this.log(`Cache invalidated due to ${operation} operation`); } /** * Get cache key for a query */ getCacheKey(query, options) { if (!this.searchCache) return ''; return this.searchCache.getCacheKey(query, options); } /** * Direct cache get */ get(key) { if (!this.searchCache) return null; return this.searchCache.get(key); } /** * Direct cache set */ set(key, value) { if (!this.searchCache) return; this.searchCache.set(key, value); } /** * Get the underlying SearchCache instance (for compatibility) */ getSearchCache() { return this.searchCache; } /** * Get memory usage */ getMemoryUsage() { if (!this.searchCache) return 0; return this.searchCache.getMemoryUsage(); } } /** * Factory function for zero-config cache augmentation */ export function createCacheAugmentation(config) { return new CacheAugmentation(config); } //# sourceMappingURL=cacheAugmentation.js.map