UNPKG

@codai/memorai-core

Version:

Simplified advanced memory engine - no tiers, just powerful semantic search with persistence

354 lines (353 loc) 14.8 kB
/** * Advanced Pattern Recognition System * Detects patterns, trends, and relationships in memory data */ export class PatternRecognitionEngine { constructor(config = {}) { this.config = { minConfidence: 0.7, maxPatterns: 50, timeWindowDays: 30, semanticThreshold: 0.8, enablePrediction: true, ...config, }; } /** * Analyze memories to detect patterns */ async analyzePatterns(memories) { const patterns = []; // Temporal pattern analysis const temporalPatterns = await this.detectTemporalPatterns(memories); patterns.push(...temporalPatterns); // Semantic pattern analysis const semanticPatterns = await this.detectSemanticPatterns(memories); patterns.push(...semanticPatterns); // Behavioral pattern analysis const behavioralPatterns = await this.detectBehavioralPatterns(memories); patterns.push(...behavioralPatterns); // Relationship pattern analysis const relationshipPatterns = await this.detectRelationshipPatterns(memories); patterns.push(...relationshipPatterns); // Frequency pattern analysis const frequencyPatterns = await this.detectFrequencyPatterns(memories); patterns.push(...frequencyPatterns); // Filter by confidence and limit return patterns .filter(pattern => pattern.confidence >= this.config.minConfidence) .sort((a, b) => b.confidence - a.confidence) .slice(0, this.config.maxPatterns); } /** * Detect temporal patterns in memory access and creation */ async detectTemporalPatterns(memories) { const patterns = []; const now = new Date(); const timeWindow = new Date(now.getTime() - this.config.timeWindowDays * 24 * 60 * 60 * 1000); // Group memories by time periods const hourlyActivity = new Map(); const dailyActivity = new Map(); memories .filter(memory => memory.createdAt >= timeWindow) .forEach(memory => { const hour = memory.createdAt.getHours(); const day = memory.createdAt.toDateString(); if (!hourlyActivity.has(hour)) { hourlyActivity.set(hour, []); } hourlyActivity.get(hour).push(memory); if (!dailyActivity.has(day)) { dailyActivity.set(day, []); } dailyActivity.get(day).push(memory); }); // Detect peak activity hours const peakHours = Array.from(hourlyActivity.entries()) .filter(([_, mems]) => mems.length > 5) .sort(([_, a], [__, b]) => b.length - a.length) .slice(0, 3); for (const [hour, mems] of peakHours) { patterns.push({ id: `temporal_peak_${hour}`, type: 'temporal', description: `Peak memory activity at ${hour}:00 (${mems.length} memories)`, confidence: Math.min(0.9, mems.length / 20), memories: mems.map(m => m.id), metadata: { frequency: mems.length, timeRange: { start: timeWindow, end: now, }, }, insights: [ `Users are most active around ${hour}:00`, `Consider optimizing system performance for this time`, `Memory creation peaks during ${hour}:00-${hour + 1}:00`, ], predictedOutcomes: this.config.enablePrediction ? [ `Next peak activity expected around ${hour}:00 tomorrow`, `System load will increase by ~${Math.round(mems.length * 1.1)} memories`, ] : undefined, }); } return patterns; } /** * Detect semantic patterns in memory content */ async detectSemanticPatterns(memories) { const patterns = []; // Group by content similarity and tags const tagGroups = new Map(); const typeGroups = new Map(); memories.forEach(memory => { // Group by tags memory.tags.forEach(tag => { if (!tagGroups.has(tag)) { tagGroups.set(tag, []); } tagGroups.get(tag).push(memory); }); // Group by type if (!typeGroups.has(memory.type)) { typeGroups.set(memory.type, []); } typeGroups.get(memory.type).push(memory); }); // Analyze tag patterns for (const [tag, mems] of tagGroups.entries()) { if (mems.length >= 3) { const avgImportance = mems.reduce((sum, m) => sum + m.importance, 0) / mems.length; patterns.push({ id: `semantic_tag_${tag}`, type: 'semantic', description: `Semantic cluster around "${tag}" (${mems.length} memories)`, confidence: Math.min(0.95, mems.length / 10), memories: mems.map(m => m.id), metadata: { keywords: [tag], frequency: mems.length, }, insights: [ `Strong semantic clustering around "${tag}"`, `Average importance: ${avgImportance.toFixed(2)}`, `Related memories often accessed together`, ], predictedOutcomes: this.config.enablePrediction ? [ `Future memories likely to be tagged with "${tag}"`, `Consider creating automated workflows for "${tag}" memories`, ] : undefined, }); } } return patterns; } /** * Detect behavioral patterns in memory usage */ async detectBehavioralPatterns(memories) { const patterns = []; // Analyze access patterns const highAccessMemories = memories .filter(memory => memory.accessCount > 10) .sort((a, b) => b.accessCount - a.accessCount); if (highAccessMemories.length > 0) { patterns.push({ id: 'behavioral_high_access', type: 'behavioral', description: `High-access memory pattern (${highAccessMemories.length} frequently accessed memories)`, confidence: 0.85, memories: highAccessMemories.slice(0, 10).map(m => m.id), metadata: { frequency: highAccessMemories.reduce((sum, m) => sum + m.accessCount, 0), }, insights: [ 'Certain memories are accessed significantly more than others', 'Consider caching these high-access memories', 'Users show consistent preference for specific information', ], predictedOutcomes: this.config.enablePrediction ? [ 'These memories will continue to be accessed frequently', 'System should prioritize quick access to these memories', ] : undefined, }); } return patterns; } /** * Detect relationship patterns between memories */ async detectRelationshipPatterns(memories) { const patterns = []; // Analyze co-occurrence of memories by agent_id const agentGroups = new Map(); memories .filter(memory => memory.agent_id) .forEach(memory => { const agentId = memory.agent_id; if (!agentGroups.has(agentId)) { agentGroups.set(agentId, []); } agentGroups.get(agentId).push(memory); }); for (const [agentId, mems] of agentGroups.entries()) { if (mems.length >= 5) { const relationships = this.analyzeMemoryRelationships(mems); patterns.push({ id: `relationship_agent_${agentId}`, type: 'relationship', description: `Agent memory relationship pattern (${mems.length} memories for agent ${agentId})`, confidence: Math.min(0.9, mems.length / 20), memories: mems.map(m => m.id), metadata: { relationships, entities: [agentId], }, insights: [ `Agent ${agentId} has strong memory interconnections`, `Memory types show consistent patterns`, `Cross-referenced memories indicate complex workflows`, ], predictedOutcomes: this.config.enablePrediction ? [ `Agent ${agentId} will continue similar memory patterns`, 'Related memories should be pre-loaded together', ] : undefined, }); } } return patterns; } /** * Detect frequency patterns in memory creation and access */ async detectFrequencyPatterns(memories) { const patterns = []; // Analyze creation frequency by type const typeFrequency = new Map(); memories.forEach(memory => { typeFrequency.set(memory.type, (typeFrequency.get(memory.type) || 0) + 1); }); const dominantTypes = Array.from(typeFrequency.entries()) .filter(([_, count]) => count > 5) .sort(([_, a], [__, b]) => b - a); for (const [type, count] of dominantTypes) { const typeMemories = memories.filter(m => m.type === type); patterns.push({ id: `frequency_type_${type}`, type: 'frequency', description: `High frequency pattern for "${type}" memories (${count} instances)`, confidence: Math.min(0.9, count / 50), memories: typeMemories.slice(0, 10).map(m => m.id), metadata: { frequency: count, keywords: [type], }, insights: [ `"${type}" is the most common memory type`, `System optimized for ${type} memory handling`, `Users heavily rely on ${type} information`, ], predictedOutcomes: this.config.enablePrediction ? [ `Continue expecting high volume of "${type}" memories`, `Consider specialized handling for "${type}" memories`, ] : undefined, }); } return patterns; } /** * Analyze relationships between memories */ analyzeMemoryRelationships(memories) { const relationships = []; // Simple relationship detection based on shared tags and similar content for (let i = 0; i < memories.length; i++) { for (let j = i + 1; j < memories.length; j++) { const memA = memories[i]; const memB = memories[j]; // Tag overlap const sharedTags = memA.tags.filter(tag => memB.tags.includes(tag)); if (sharedTags.length > 0) { relationships.push({ source: memA.id, target: memB.id, type: 'shared_tags', strength: sharedTags.length / Math.max(memA.tags.length, memB.tags.length), }); } // Type similarity if (memA.type === memB.type) { relationships.push({ source: memA.id, target: memB.id, type: 'same_type', strength: 0.6, }); } // Temporal proximity (created within 1 hour) const timeDiff = Math.abs(memA.createdAt.getTime() - memB.createdAt.getTime()); if (timeDiff < 60 * 60 * 1000) { // 1 hour relationships.push({ source: memA.id, target: memB.id, type: 'temporal_proximity', strength: 1 - timeDiff / (60 * 60 * 1000), }); } } } return relationships.filter(rel => rel.strength > 0.3); } /** * Generate insights from detected patterns */ async generateInsights(patterns) { const insights = { summary: '', recommendations: [], trends: [], predictions: [], }; if (patterns.length === 0) { insights.summary = 'No significant patterns detected in the current memory dataset.'; return insights; } // Generate summary const patternTypes = patterns.reduce((acc, pattern) => { acc[pattern.type] = (acc[pattern.type] || 0) + 1; return acc; }, {}); insights.summary = `Detected ${patterns.length} patterns across ${Object.keys(patternTypes).length} categories. ` + `Strongest patterns: ${patterns .slice(0, 3) .map(p => p.type) .join(', ')}.`; // Extract unique insights const allInsights = patterns.flatMap(p => p.insights); insights.trends = [...new Set(allInsights)]; // Extract predictions const allPredictions = patterns .filter(p => p.predictedOutcomes) .flatMap(p => p.predictedOutcomes); insights.predictions = [...new Set(allPredictions)]; // Generate recommendations insights.recommendations.push('Optimize caching for high-frequency patterns', 'Consider automated categorization for semantic clusters', 'Implement predictive pre-loading for temporal patterns', 'Create specialized workflows for dominant memory types'); return insights; } }