Skip to main content
Glama
ForgettingEvaluationEngine.ts12.2 kB
/** * Forgetting Evaluation Engine * * Coordinates multiple forgetting strategies to evaluate memories * for potential forgetting, providing comprehensive analysis and recommendations. */ import { ForgettingContext, ForgettingEvaluation, ForgettingImpact, ForgettingRecommendation, ForgettingScore, ForgettingStrategy, IForgettingEvaluationEngine, } from "../../interfaces/forgetting.js"; import { Concept, Episode } from "../../types/core.js"; import { ImportanceBasedStrategyImpl } from "./ImportanceBasedStrategy.js"; import { InterferenceBasedStrategyImpl } from "./InterferenceBasedStrategy.js"; import { TemporalDecayStrategyImpl } from "./TemporalDecayStrategy.js"; export interface ForgettingEvaluationEngineConfig { enable_temporal_decay: boolean; enable_interference_based: boolean; enable_importance_based: boolean; strategy_weights: Record<string, number>; recommendation_threshold: number; high_confidence_threshold: number; } export class ForgettingEvaluationEngine implements IForgettingEvaluationEngine { private strategies: Map<string, ForgettingStrategy> = new Map(); private config: ForgettingEvaluationEngineConfig; constructor(config?: Partial<ForgettingEvaluationEngineConfig>) { this.config = { enable_temporal_decay: true, enable_interference_based: true, enable_importance_based: true, strategy_weights: { temporal_decay: 0.3, interference_based: 0.3, importance_based: 0.4, }, recommendation_threshold: 0.6, high_confidence_threshold: 0.8, ...config, }; this.initializeDefaultStrategies(); } private initializeDefaultStrategies(): void { if (this.config.enable_temporal_decay) { this.addStrategy(new TemporalDecayStrategyImpl()); } if (this.config.enable_interference_based) { this.addStrategy(new InterferenceBasedStrategyImpl()); } if (this.config.enable_importance_based) { this.addStrategy(new ImportanceBasedStrategyImpl()); } } async evaluateMemories( memories: (Episode | Concept)[], context: ForgettingContext ): Promise<ForgettingEvaluation[]> { const evaluations: ForgettingEvaluation[] = []; for (const memory of memories) { const evaluation = await this.evaluateMemory(memory, context); evaluations.push(evaluation); } // Sort by combined score (highest forgetting score first) return evaluations.sort((a, b) => b.combined_score - a.combined_score); } private async evaluateMemory( memory: Episode | Concept, context: ForgettingContext ): Promise<ForgettingEvaluation> { const strategyScores: ForgettingScore[] = []; // Evaluate with each strategy for (const strategy of this.strategies.values()) { try { const score = await strategy.evaluateForForgetting(memory, context); strategyScores.push(score); } catch (error) { console.warn( `Strategy ${strategy.name} failed for memory evaluation:`, error ); } } // Calculate combined score const combinedScore = this.calculateCombinedScore(strategyScores); // Generate recommendation const recommendation = this.generateRecommendation( combinedScore, strategyScores, memory ); // Assess impact const impact = this.assessForgettingImpact(memory, strategyScores); // Check if user consent is required const requiresConsent = this.requiresUserConsent( memory, combinedScore, context ); return { memory_id: this.getMemoryId(memory), memory_type: this.getMemoryType(memory), memory_content_summary: this.generateContentSummary(memory), strategy_scores: strategyScores, combined_score: combinedScore, recommendation, requires_user_consent: requiresConsent, estimated_impact: impact, }; } private calculateCombinedScore(scores: ForgettingScore[]): number { if (scores.length === 0) return 0; let weightedSum = 0; let totalWeight = 0; for (const score of scores) { const weight = this.config.strategy_weights[score.strategy_name] || 1; weightedSum += score.score * score.confidence * weight; totalWeight += score.confidence * weight; } return totalWeight > 0 ? weightedSum / totalWeight : 0; } private generateRecommendation( combinedScore: number, strategyScores: ForgettingScore[], memory: Episode | Concept ): ForgettingRecommendation { let action: "forget" | "retain" | "degrade" | "archive"; let confidence: number; let reasoning: string; const alternativeActions: string[] = []; // Determine primary action based on combined score if (combinedScore >= this.config.recommendation_threshold) { if (combinedScore >= this.config.high_confidence_threshold) { action = "forget"; confidence = combinedScore; reasoning = "High forgetting score across multiple strategies indicates memory should be forgotten"; } else { action = "degrade"; confidence = combinedScore * 0.8; reasoning = "Moderate forgetting score suggests gradual degradation"; alternativeActions.push("archive", "forget"); } } else { if (combinedScore >= 0.3) { action = "archive"; confidence = 1 - combinedScore; reasoning = "Low-moderate forgetting score suggests archiving for potential future retrieval"; alternativeActions.push("retain", "degrade"); } else { action = "retain"; confidence = 1 - combinedScore; reasoning = "Low forgetting score indicates memory should be retained"; alternativeActions.push("archive"); } } // Adjust based on memory importance const importance = this.getMemoryImportance(memory); if (importance > 0.8 && action === "forget") { action = "degrade"; reasoning += " (adjusted due to high importance)"; alternativeActions.unshift("archive"); } // Consider strategy consensus const highScoreStrategies = strategyScores.filter( (s) => s.score > 0.7 ).length; const lowScoreStrategies = strategyScores.filter( (s) => s.score < 0.3 ).length; if (highScoreStrategies >= 2 && action !== "forget") { alternativeActions.unshift("forget"); } if (lowScoreStrategies >= 2 && action === "forget") { action = "retain"; reasoning += " (multiple strategies indicate retention)"; } return { action, confidence: Math.max(0.1, Math.min(1, confidence)), reasoning, alternative_actions: alternativeActions, }; } private assessForgettingImpact( memory: Episode | Concept, strategyScores: ForgettingScore[] ): ForgettingImpact { const importance = this.getMemoryImportance(memory); // Estimate retrieval loss probability const avgForgettingScore = strategyScores.reduce((sum, s) => sum + s.score, 0) / strategyScores.length; const retrievalLossProbability = avgForgettingScore * (1 - importance); // Estimate related memories affected (simplified heuristic) let relatedMemoriesAffected = 0; if ("relations" in memory && memory.relations) { relatedMemoriesAffected = memory.relations.length; } else if ("context" in memory && memory.context) { // Estimate based on context similarity relatedMemoriesAffected = Math.floor(Math.random() * 5) + 1; // Simplified } // Assess knowledge gap risk const knowledgeGapRisk = importance * (1 - this.getMemoryRedundancy(memory)); // Assess recovery difficulty const recoveryDifficulty = this.assessRecoveryDifficulty( memory, strategyScores ); return { retrieval_loss_probability: Math.max( 0, Math.min(1, retrievalLossProbability) ), related_memories_affected: relatedMemoriesAffected, knowledge_gap_risk: Math.max(0, Math.min(1, knowledgeGapRisk)), recovery_difficulty: Math.max(0, Math.min(1, recoveryDifficulty)), }; } private requiresUserConsent( memory: Episode | Concept, combinedScore: number, context: ForgettingContext ): boolean { // Always require consent if user preferences demand it if (context.user_preferences.consent_required) { return true; } // Require consent for high-importance memories const importance = this.getMemoryImportance(memory); if (importance > context.user_preferences.max_auto_forget_importance) { return true; } // Require consent for protected categories if ("context" in memory && memory.context && memory.context.domain) { if ( context.user_preferences.protected_categories.includes( memory.context.domain ) ) { return true; } } // Require consent for emotional memories if ("emotional_tags" in memory && memory.emotional_tags) { const hasProtectedEmotion = memory.emotional_tags.some((tag) => context.user_preferences.protected_categories.includes(tag) ); if (hasProtectedEmotion) { return true; } } // Require consent for high-confidence forgetting decisions if (combinedScore > this.config.high_confidence_threshold) { return true; } return false; } addStrategy(strategy: ForgettingStrategy): void { this.strategies.set(strategy.name, strategy); } removeStrategy(strategyName: string): void { this.strategies.delete(strategyName); } getStrategies(): ForgettingStrategy[] { return Array.from(this.strategies.values()); } // Helper methods private getMemoryId(memory: Episode | Concept): string { if ("id" in memory && memory.id) { return memory.id; } // Generate temporary ID for episodes return `temp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } private getMemoryType(memory: Episode | Concept): "episodic" | "semantic" { return "timestamp" in memory ? "episodic" : "semantic"; } private generateContentSummary(memory: Episode | Concept): string { if ("content" in memory) { const content = typeof memory.content === "string" ? memory.content : JSON.stringify(memory.content); return content.length > 100 ? content.substring(0, 100) + "..." : content; } return "No content summary available"; } private getMemoryImportance(memory: Episode | Concept): number { if ("importance" in memory) { return memory.importance; } else if ("activation" in memory) { return memory.activation; } return 0.5; // Default moderate importance } private getMemoryRedundancy(memory: Episode | Concept): number { // Simplified heuristic for memory redundancy // In a real implementation, this would analyze similar memories in the system if ( "relations" in memory && memory.relations && memory.relations.length > 3 ) { return 0.7; // High redundancy if many relations } return 0.3; // Default low redundancy } private assessRecoveryDifficulty( memory: Episode | Concept, strategyScores: ForgettingScore[] ): number { let difficulty = 0.5; // Base difficulty // Higher difficulty for older memories if ("timestamp" in memory) { const ageInDays = (Date.now() - memory.timestamp) / (1000 * 60 * 60 * 24); difficulty += Math.min(ageInDays / 365, 0.3); // Up to 30% increase for age } // Lower difficulty if memory has strong associations if ( "relations" in memory && memory.relations && memory.relations.length > 0 ) { difficulty -= Math.min(memory.relations.length / 10, 0.2); // Up to 20% decrease } // Consider strategy consensus on difficulty const temporalScore = strategyScores.find((s) => s.strategy_name === "temporal_decay")?.score ?? 0; difficulty += temporalScore * 0.2; // Temporal decay increases recovery difficulty return Math.max(0.1, Math.min(1, difficulty)); } }

Latest Blog Posts

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/keyurgolani/ThoughtMcp'

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