Skip to main content
Glama
self-improvement-system.ts41.1 kB
/** * Self-Improvement System * * Implements continuous improvement through feedback integration, * preference learning, and outcome tracking. */ import type { Context, Correction, Decision, ImprovementMetrics, ImprovementReport, Interaction, Outcome, OutcomePattern, Performance, PreferenceValue, UserFeedback, UserPreferences, } from "./types"; /** * FeedbackIntegrator * * Integrates user feedback into learning systems, learns from corrections, * and tracks feedback impact on system performance. */ export class FeedbackIntegrator { private feedbackStore: UserFeedback[] = []; private corrections: Correction[] = []; private strategyImpacts: Map<string, number[]> = new Map(); /** * Integrate user feedback into learning systems */ integrateFeedback(feedback: UserFeedback): void { if (!this.validateFeedback(feedback)) { throw new Error("Invalid feedback"); } this.storeFeedback(feedback); // Track impact on strategies if metadata contains strategyId if (feedback.metadata?.strategyId) { const strategyId = feedback.metadata.strategyId as string; const impacts = this.strategyImpacts.get(strategyId) ?? []; impacts.push(feedback.rating); this.strategyImpacts.set(strategyId, impacts); } } /** * Learn from user corrections */ learnFromCorrection(correction: Correction): void { this.corrections.push(correction); } /** * Validate feedback has required fields and valid values */ validateFeedback(feedback: UserFeedback): boolean { if (!feedback?.id || !feedback.userId || !feedback.context) { return false; } if (feedback.userId.trim() === "") { return false; } if (typeof feedback.rating !== "number" || feedback.rating < 0 || feedback.rating > 1) { return false; } if (!(feedback.timestamp instanceof Date) || isNaN(feedback.timestamp.getTime())) { return false; } return true; } /** * Store feedback in memory */ storeFeedback(feedback: UserFeedback): void { this.feedbackStore.push(feedback); } /** * Retrieve feedback by user ID */ retrieveFeedback(userId: string): UserFeedback[] { return this.feedbackStore.filter((f) => f.userId === userId); } /** * Get feedback impact for a specific strategy */ getFeedbackImpact(strategyId: string): number { const impacts = this.strategyImpacts.get(strategyId); if (!impacts || impacts.length === 0) { return 0; } const sum = impacts.reduce((acc, val) => acc + val, 0); return sum / impacts.length; } /** * Aggregate feedback over a time period */ aggregateFeedback(period: "day" | "week" | "month"): { averageRating: number; totalCount: number; trends: string[]; } { if (this.feedbackStore.length === 0) { return { averageRating: 0, totalCount: 0, trends: [], }; } const now = Date.now(); let windowMs: number; switch (period) { case "day": windowMs = 24 * 60 * 60 * 1000; break; case "week": windowMs = 7 * 24 * 60 * 60 * 1000; break; case "month": windowMs = 30 * 24 * 60 * 60 * 1000; break; } const relevantFeedback = this.feedbackStore.filter( (f) => now - f.timestamp.getTime() <= windowMs ); if (relevantFeedback.length === 0) { return { averageRating: 0, totalCount: 0, trends: [], }; } const sum = relevantFeedback.reduce((acc, f) => acc + f.rating, 0); const averageRating = sum / relevantFeedback.length; const trends = this.identifyTrends(relevantFeedback); return { averageRating, totalCount: relevantFeedback.length, trends, }; } /** * Handle conflicting feedback by resolving to a single feedback */ handleConflictingFeedback(feedbacks: UserFeedback[]): UserFeedback { if (feedbacks.length === 0) { throw new Error("No feedbacks provided"); } if (feedbacks.length === 1) { return feedbacks[0]; } const sorted = [...feedbacks].sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()); let weightedSum = 0; let totalWeight = 0; sorted.forEach((feedback, index) => { const weight = Math.exp(-index * 0.5); weightedSum += feedback.rating * weight; totalWeight += weight; }); const resolvedRating = weightedSum / totalWeight; return { ...sorted[0], rating: resolvedRating, comments: `Resolved from ${feedbacks.length} conflicting feedbacks`, }; } /** * Identify trends in feedback ratings */ private identifyTrends(feedbacks: UserFeedback[]): string[] { if (feedbacks.length < 2) { return []; } const sorted = [...feedbacks].sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime()); const n = sorted.length; let sumX = 0; let sumY = 0; let sumXY = 0; let sumX2 = 0; sorted.forEach((feedback, index) => { const x = index; const y = feedback.rating; sumX += x; sumY += y; sumXY += x * y; sumX2 += x * x; }); const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX); const trends: string[] = []; const threshold = 0.01; if (slope > threshold) { trends.push("improving"); } else if (slope < -threshold) { trends.push("declining"); } else { trends.push("stable"); } return trends; } } /** * PreferenceLearner * * Learns user preferences from interactions, identifies patterns, * and adapts system behavior to learned preferences. */ export class PreferenceLearner { private interactions: Map<string, Interaction[]> = new Map(); private preferences: Map<string, UserPreferences> = new Map(); learnFromInteraction(interaction: Interaction): void { const userId = interaction.userId; const userInteractions = this.interactions.get(userId) ?? []; userInteractions.push(interaction); this.interactions.set(userId, userInteractions); this.updatePreferences(userId); } getPreferences(userId: string): UserPreferences { const existing = this.preferences.get(userId); if (existing) { return existing; } return { userId, preferences: new Map(), lastUpdated: new Date(), sampleSize: 0, }; } identifyPatterns(userId: string): Array<{ pattern: string; confidence: number }> { const userInteractions = this.interactions.get(userId) ?? []; const MIN_SAMPLE_SIZE = 3; if (userInteractions.length < MIN_SAMPLE_SIZE) { return []; } const patterns: Array<{ pattern: string; confidence: number }> = []; const contentKeys = new Map<string, Map<unknown, number>>(); userInteractions.forEach((interaction) => { Object.entries(interaction.content).forEach(([key, value]) => { if (!contentKeys.has(key)) { contentKeys.set(key, new Map()); } const valueCounts = contentKeys.get(key); if (valueCounts) { const currentCount = valueCounts.get(value) ?? 0; valueCounts.set(value, currentCount + 1); } }); }); contentKeys.forEach((valueCounts, key) => { const total = userInteractions.length; valueCounts.forEach((count, value) => { const frequency = count / total; if (frequency >= 0.5) { patterns.push({ pattern: `${key}=${String(value)}`, confidence: frequency, }); } }); }); return patterns; } getAdaptations( userId: string, context?: Record<string, unknown> ): { recommendations: Array<{ key: string; value: unknown; confidence: number }>; } { const preferences = this.getPreferences(userId); const recommendations: Array<{ key: string; value: unknown; confidence: number }> = []; preferences.preferences.forEach((prefValue, key) => { if (context) { const userInteractions = this.interactions.get(userId) ?? []; const contextMatches = userInteractions.filter((interaction) => { return Object.entries(context).every( ([ctxKey, ctxValue]) => interaction.context[ctxKey as keyof Context] === ctxValue ); }); if (contextMatches.length > 0) { const contextPref = this.calculateContextPreference(contextMatches, key); if (contextPref) { recommendations.push({ key, value: contextPref.value, confidence: contextPref.confidence, }); } } } else { recommendations.push({ key, value: prefValue.value, confidence: prefValue.confidence, }); } }); return { recommendations }; } persistPreferences(_userId: string): void { // Already persisted in memory } getLearningStatus(userId: string): { needsMoreData: boolean; volatility: number; } { const userInteractions = this.interactions.get(userId) ?? []; const MIN_SAMPLE_SIZE = 10; const needsMoreData = userInteractions.length < MIN_SAMPLE_SIZE; const volatility = this.calculateVolatility(userId); return { needsMoreData, volatility, }; } getConfidenceBreakdown( userId: string, key: string ): { consistency: number; sampleSize: number; recency: number; overall: number; } { const preferences = this.getPreferences(userId); const preference = preferences.preferences.get(key); if (!preference) { return { consistency: 0, sampleSize: 0, recency: 0, overall: 0, }; } const userInteractions = this.interactions.get(userId) ?? []; const matchingInteractions = userInteractions.filter( (i) => i.content[key] === preference.value ); const consistency = matchingInteractions.length / userInteractions.length; const sampleSize = Math.min(userInteractions.length / 20, 1); const ageMs = Date.now() - preference.learnedAt.getTime(); const ageDays = ageMs / (24 * 60 * 60 * 1000); const recency = Math.exp(-ageDays / 30); const overall = consistency * 0.5 + sampleSize * 0.3 + recency * 0.2; return { consistency, sampleSize, recency, overall, }; } explainPreference( userId: string, key: string ): { value: unknown; confidence: number; reasoning: string; } { const preferences = this.getPreferences(userId); const preference = preferences.preferences.get(key); if (!preference) { return { value: null, confidence: 0, reasoning: "No preference learned for this key", }; } const userInteractions = this.interactions.get(userId) ?? []; const matchingInteractions = userInteractions.filter( (i) => i.content[key] === preference.value ); const allValues = new Set(userInteractions.map((i) => i.content[key])); const hasConflict = allValues.size > 1; let reasoning = `Based on ${matchingInteractions.length} out of ${userInteractions.length} interactions`; if (hasConflict) { reasoning += `. Note: conflict detected with ${allValues.size} different values observed`; } return { value: preference.value, confidence: preference.confidence, reasoning, }; } private updatePreferences(userId: string): void { const userInteractions = this.interactions.get(userId) ?? []; if (userInteractions.length === 0) { return; } const contentKeys = new Map<string, Map<unknown, { count: number; lastSeen: Date }>>(); userInteractions.forEach((interaction) => { Object.entries(interaction.content).forEach(([key, value]) => { if (!contentKeys.has(key)) { contentKeys.set(key, new Map()); } const valueData = contentKeys.get(key); if (valueData) { const current = valueData.get(value) ?? { count: 0, lastSeen: new Date(0) }; valueData.set(value, { count: current.count + 1, lastSeen: interaction.timestamp > current.lastSeen ? interaction.timestamp : current.lastSeen, }); } }); }); const preferencesMap = new Map<string, PreferenceValue>(); contentKeys.forEach((valueData, key) => { let bestScore = -1; let bestValue: unknown = null; let bestCount = 0; valueData.forEach((data, value) => { const frequency = data.count / userInteractions.length; const recencyMs = Date.now() - data.lastSeen.getTime(); const recencyDays = recencyMs / (24 * 60 * 60 * 1000); const recencyScore = Math.exp(-recencyDays / 30); const score = frequency * 0.6 + recencyScore * 0.4; if (score > bestScore) { bestScore = score; bestValue = value; bestCount = data.count; } }); if (bestValue !== null) { const confidence = this.calculateConfidence( userId, key, bestValue, bestCount, userInteractions.length ); const firstInteraction = userInteractions.find((i) => i.content[key] === bestValue); preferencesMap.set(key, { value: bestValue, confidence, learnedAt: firstInteraction?.timestamp ?? new Date(), supportingInteractions: bestCount, }); } }); this.preferences.set(userId, { userId, preferences: preferencesMap, lastUpdated: new Date(), sampleSize: userInteractions.length, }); } private calculateConfidence( userId: string, key: string, value: unknown, count: number, total: number ): number { const consistency = count / total; const sampleSizeFactor = Math.min(Math.pow(total / 30, 0.7), 1); const volatility = this.calculateVolatility(userId); const volatilityFactor = 1 - Math.min(volatility, 1); const userInteractions = this.interactions.get(userId) ?? []; const recentInteractions = userInteractions.slice(-5); const recentMatches = recentInteractions.filter((i) => i.content[key] === value).length; const recencyFactor = recentMatches / Math.min(recentInteractions.length, 5); const confidence = consistency * 0.4 + sampleSizeFactor * 0.4 + volatilityFactor * 0.05 + recencyFactor * 0.15; return Math.max(0, Math.min(1, confidence)); } private calculateVolatility(userId: string): number { const userInteractions = this.interactions.get(userId) ?? []; if (userInteractions.length < 2) { return 0; } const keyChanges = new Map<string, number>(); for (let i = 1; i < userInteractions.length; i++) { const prev = userInteractions[i - 1]; const curr = userInteractions[i]; Object.keys(curr.content).forEach((key) => { if (prev.content[key] !== curr.content[key]) { const changes = keyChanges.get(key) ?? 0; keyChanges.set(key, changes + 1); } }); } if (keyChanges.size === 0) { return 0; } let totalChanges = 0; keyChanges.forEach((changes) => { totalChanges += changes; }); const avgChanges = totalChanges / keyChanges.size; const maxPossibleChanges = userInteractions.length - 1; return avgChanges / maxPossibleChanges; } private calculateContextPreference( interactions: Interaction[], key: string ): { value: unknown; confidence: number } | null { const valueCounts = new Map<unknown, number>(); interactions.forEach((interaction) => { const value = interaction.content[key]; if (value !== undefined) { const count = valueCounts.get(value) ?? 0; valueCounts.set(value, count + 1); } }); if (valueCounts.size === 0) { return null; } let maxCount = 0; let mostCommonValue: unknown = null; valueCounts.forEach((count, value) => { if (count > maxCount) { maxCount = count; mostCommonValue = value; } }); if (mostCommonValue === null) { return null; } const confidence = maxCount / interactions.length; return { value: mostCommonValue, confidence, }; } } /** * OutcomeTracker * * Tracks decision-outcome pairs and analyzes patterns for learning. */ export class OutcomeTracker { private decisionOutcomes: Map<string, Array<{ decision: Decision; outcome: Outcome }>> = new Map(); private allOutcomes: Array<{ decision: Decision; outcome: Outcome }> = []; trackOutcome(decision: Decision, outcome: Outcome): void { this.validateDecision(decision); this.validateOutcome(outcome); outcome.timestamp ??= new Date(); const outcomes = this.decisionOutcomes.get(decision.id); if (outcomes) { outcomes.push({ decision, outcome }); } else { this.decisionOutcomes.set(decision.id, [{ decision, outcome }]); } this.allOutcomes.push({ decision, outcome }); } private validateDecision(decision: Decision): void { if ( !decision?.id || !decision.type || !decision.choice || !decision.context || typeof decision.confidence !== "number" || !decision.timestamp ) { throw new Error("Invalid decision: missing required fields"); } } private validateOutcome(outcome: Outcome): void { if ( !outcome?.id || typeof outcome.success !== "boolean" || typeof outcome.quality !== "number" || !outcome.description || !outcome.timestamp ) { throw new Error("Invalid outcome: missing required fields"); } } getOutcomes(decisionId: string): Array<{ decision: Decision; outcome: Outcome }> { return this.decisionOutcomes.get(decisionId) ?? []; } analyzePatterns(): OutcomePattern[] { const MIN_SAMPLE_SIZE = 3; if (this.allOutcomes.length < MIN_SAMPLE_SIZE) { return []; } const patternGroups = new Map<string, Array<{ decision: Decision; outcome: Outcome }>>(); this.allOutcomes.forEach((entry) => { const decisionKey = `${entry.decision.type}:${entry.decision.choice}`; const contextKeys = Object.entries(entry.decision.context) .map(([k, v]) => `${k}:${String(v)}`) .sort() .join("|"); const patternKey = `${decisionKey}|${contextKeys}`; const group = patternGroups.get(patternKey) ?? []; group.push(entry); patternGroups.set(patternKey, group); }); const patterns: OutcomePattern[] = []; patternGroups.forEach((group) => { if (group.length < MIN_SAMPLE_SIZE) { return; } const successCount = group.filter((e) => e.outcome.success).length; const successRate = successCount / group.length; let type: "success" | "failure" | "mixed"; if (successRate >= 0.7) { type = "success"; } else if (successRate <= 0.3) { type = "failure"; } else { type = "mixed"; } const contextFactors = new Set<string>(); group.forEach((entry) => { Object.entries(entry.decision.context).forEach(([k, v]) => { contextFactors.add(`${k}:${String(v)}`); }); }); const decisionFactors = new Set<string>(); group.forEach((entry) => { decisionFactors.add(`type:${entry.decision.type}`); decisionFactors.add(`choice:${entry.decision.choice}`); }); const totalQuality = group.reduce((sum, e) => sum + e.outcome.quality, 0); const averageQuality = totalQuality / group.length; const confidence = this.calculatePatternConfidence(group, successRate); const recommendations = this.generateRecommendations(type, successRate, averageQuality); patterns.push({ id: `pattern-${Date.now()}-${Math.random()}`, type, contextFactors: Array.from(contextFactors), decisionFactors: Array.from(decisionFactors), successRate, confidence, sampleSize: group.length, averageQuality, recommendations, }); }); return patterns; } getStrategyAdjustments(context?: Record<string, unknown>): Array<{ strategy: string; recommendation: string; confidence: number; context?: Record<string, unknown>; }> { if (this.allOutcomes.length === 0) { return []; } const relevantOutcomes = context ? this.allOutcomes.filter((entry) => Object.entries(context).every(([key, value]) => entry.decision.context[key] === value) ) : this.allOutcomes; if (relevantOutcomes.length === 0) { return []; } const strategyGroups = new Map<string, Array<{ decision: Decision; outcome: Outcome }>>(); relevantOutcomes.forEach((entry) => { const strategy = entry.decision.choice; const group = strategyGroups.get(strategy) ?? []; group.push(entry); strategyGroups.set(strategy, group); }); const adjustments: Array<{ strategy: string; recommendation: string; confidence: number; context?: Record<string, unknown>; }> = []; strategyGroups.forEach((group, strategy) => { const successCount = group.filter((e) => e.outcome.success).length; const successRate = successCount / group.length; const avgQuality = group.reduce((sum, e) => sum + e.outcome.quality, 0) / group.length; const confidence = Math.min(group.length / 10, 1) * 0.7 + 0.3; let recommendation: string; if (successRate >= 0.7 && avgQuality >= 0.7) { recommendation = `favor: High success rate (${(successRate * 100).toFixed(0)}%) and quality (${(avgQuality * 100).toFixed(0)}%)`; } else if (successRate <= 0.3 || avgQuality <= 0.3) { recommendation = `avoid: Low success rate (${(successRate * 100).toFixed(0)}%) or quality (${(avgQuality * 100).toFixed(0)}%)`; } else { recommendation = `monitor: Mixed results (${(successRate * 100).toFixed(0)}% success, ${(avgQuality * 100).toFixed(0)}% quality)`; } adjustments.push({ strategy, recommendation, confidence, context: context ? { ...context } : undefined, }); }); return adjustments; } getAnalysisStatus(): { needsMoreData: boolean; totalOutcomes: number; minRequired: number; } { const MIN_REQUIRED = 3; return { needsMoreData: this.allOutcomes.length < MIN_REQUIRED, totalOutcomes: this.allOutcomes.length, minRequired: MIN_REQUIRED, }; } aggregateOutcomes(period: "day" | "week" | "month"): { totalOutcomes: number; successRate: number; averageQuality: number; period: string; trend?: "improving" | "declining" | "stable"; } { const now = Date.now(); let windowMs: number; switch (period) { case "day": windowMs = 24 * 60 * 60 * 1000; break; case "week": windowMs = 7 * 24 * 60 * 60 * 1000; break; case "month": windowMs = 30 * 24 * 60 * 60 * 1000; break; } const relevantOutcomes = this.allOutcomes.filter( (entry) => now - entry.outcome.timestamp.getTime() <= windowMs ); if (relevantOutcomes.length === 0) { return { totalOutcomes: 0, successRate: 0, averageQuality: 0, period, }; } const successCount = relevantOutcomes.filter((e) => e.outcome.success).length; const successRate = successCount / relevantOutcomes.length; const totalQuality = relevantOutcomes.reduce((sum, e) => sum + e.outcome.quality, 0); const averageQuality = totalQuality / relevantOutcomes.length; const trend = this.calculateTrend(relevantOutcomes); return { totalOutcomes: relevantOutcomes.length, successRate, averageQuality, period, trend, }; } identifyCausalFactors(outcomeType: "success" | "failure"): Array<{ factor: string; correlation: number; confidence: number; type: "causal" | "correlated"; }> { if (this.allOutcomes.length < 5) { return []; } const totalDesiredOutcomes = this.allOutcomes.filter((entry) => outcomeType === "success" ? entry.outcome.success : !entry.outcome.success ).length; if (totalDesiredOutcomes === 0) { return []; } const factorStats = new Map<string, { withDesiredOutcome: number; totalOccurrences: number }>(); this.allOutcomes.forEach((entry) => { const hasDesiredOutcome = outcomeType === "success" ? entry.outcome.success : !entry.outcome.success; Object.entries(entry.decision.context).forEach(([key, value]) => { const factor = `${key}:${String(value)}`; const stats = factorStats.get(factor); if (stats) { stats.totalOccurrences++; if (hasDesiredOutcome) { stats.withDesiredOutcome++; } } else { factorStats.set(factor, { withDesiredOutcome: hasDesiredOutcome ? 1 : 0, totalOccurrences: 1, }); } }); }); const causalFactors: Array<{ factor: string; correlation: number; confidence: number; type: "causal" | "correlated"; }> = []; factorStats.forEach((stats, factor) => { const correlation = stats.withDesiredOutcome / totalDesiredOutcomes; if (correlation >= 0.6) { const confidence = Math.min(stats.totalOccurrences / 10, 1); causalFactors.push({ factor, correlation, confidence, type: "correlated", }); } }); causalFactors.sort((a, b) => b.correlation - a.correlation); return causalFactors; } getConfidenceBreakdown(patternId: string): { sampleSize: number; consistency: number; recency: number; overall: number; } { const patterns = this.analyzePatterns(); const pattern = patterns.find((p) => p.id === patternId); if (!pattern) { return { sampleSize: 0, consistency: 0, recency: 0, overall: 0, }; } const sampleSize = Math.min(pattern.sampleSize / 20, 1); const consistency = Math.abs(pattern.successRate - 0.5) * 2; const recency = 0.8; const overall = sampleSize * 0.4 + consistency * 0.4 + recency * 0.2; return { sampleSize, consistency, recency, overall, }; } private calculatePatternConfidence( group: Array<{ decision: Decision; outcome: Outcome }>, successRate: number ): number { const sampleSizeFactor = Math.min(group.length / 20, 1); const consistencyFactor = Math.abs(successRate - 0.5) * 2; return sampleSizeFactor * 0.6 + consistencyFactor * 0.4; } private generateRecommendations( type: "success" | "failure" | "mixed", successRate: number, averageQuality: number ): string[] { const recommendations: string[] = []; if (type === "success") { recommendations.push( `Continue using this approach - high success rate (${(successRate * 100).toFixed(0)}%)` ); if (averageQuality >= 0.8) { recommendations.push("Excellent quality outcomes - consider as best practice"); } } else if (type === "failure") { recommendations.push( `Avoid this approach - low success rate (${(successRate * 100).toFixed(0)}%)` ); recommendations.push("Consider alternative strategies for similar contexts"); } else { recommendations.push( `Monitor this approach - mixed results (${(successRate * 100).toFixed(0)}% success)` ); recommendations.push("Analyze context factors to identify when it works best"); } return recommendations; } private calculateTrend( outcomes: Array<{ decision: Decision; outcome: Outcome }> ): "improving" | "declining" | "stable" { if (outcomes.length < 3) { return "stable"; } const sorted = [...outcomes].sort( (a, b) => a.outcome.timestamp.getTime() - b.outcome.timestamp.getTime() ); const n = sorted.length; let sumX = 0; let sumY = 0; let sumXY = 0; let sumX2 = 0; sorted.forEach((entry, index) => { const x = index; const y = entry.outcome.quality; sumX += x; sumY += y; sumXY += x * y; sumX2 += x * x; }); const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX); const threshold = 0.01; if (slope > threshold) { return "improving"; } else if (slope < -threshold) { return "declining"; } else { return "stable"; } } } /** * SelfImprovementSystem * * Main coordination class that integrates FeedbackIntegrator, PreferenceLearner, * and OutcomeTracker to enable continuous improvement through feedback integration, * preference learning, and outcome tracking. */ export class SelfImprovementSystem { private feedbackIntegrator: FeedbackIntegrator; private preferenceLearner: PreferenceLearner; private outcomeTracker: OutcomeTracker; private performanceMonitor?: { getPerformanceReport: (period: string) => { period: string; overallScore: number }; recordMetric: (metric: { type: string; value: number; timestamp: Date }) => void; }; private adaptiveStrategy?: { getStrategyEffectiveness: (strategyId: string) => { strategyId: string; effectiveness: number }; updateStrategy: (strategyId: string, updates: Record<string, unknown>) => void; }; private performanceHistory: Map<string, { baseline: Performance; current: Performance }> = new Map(); private overheadMeasurements: number[] = []; constructor(config?: { performanceMonitor?: { getPerformanceReport: (period: string) => { period: string; overallScore: number }; recordMetric: (metric: { type: string; value: number; timestamp: Date }) => void; }; adaptiveStrategy?: { getStrategyEffectiveness: (strategyId: string) => { strategyId: string; effectiveness: number; }; updateStrategy: (strategyId: string, updates: Record<string, unknown>) => void; }; }) { this.feedbackIntegrator = new FeedbackIntegrator(); this.preferenceLearner = new PreferenceLearner(); this.outcomeTracker = new OutcomeTracker(); this.performanceMonitor = config?.performanceMonitor; this.adaptiveStrategy = config?.adaptiveStrategy; } /** * Measure improvement over a time period */ measureImprovement(period: "day" | "week" | "month" | "year"): { period: string; improvement: number; baseline: number; current: number; } { const periodKey = period; let stored = this.performanceHistory.get(periodKey); if (!stored) { // Initialize with baseline const baseline: Performance = { successRate: 0.7, averageQuality: 0.7, averageTime: 1000, timestamp: new Date(Date.now() - this.getPeriodMs(period)), }; const current: Performance = { successRate: 0.75, averageQuality: 0.75, averageTime: 950, timestamp: new Date(), }; stored = { baseline, current }; this.performanceHistory.set(periodKey, stored); } const baselineScore = this.calculateOverallScore(stored.baseline); const currentScore = this.calculateOverallScore(stored.current); const improvement = ((currentScore - baselineScore) / baselineScore) * 100; return { period, improvement, baseline: baselineScore, current: currentScore, }; } /** * Calculate improvement metrics */ calculateImprovementMetrics(period: "day" | "week" | "month" | "year"): ImprovementMetrics { const stored = this.performanceHistory.get(period) ?? { baseline: { successRate: 0.7, averageQuality: 0.7, averageTime: 1000, timestamp: new Date(Date.now() - this.getPeriodMs(period)), }, current: { successRate: 0.75, averageQuality: 0.75, averageTime: 950, timestamp: new Date(), }, }; const successRateImprovement = ((stored.current.successRate - stored.baseline.successRate) / stored.baseline.successRate) * 100; const qualityImprovement = ((stored.current.averageQuality - stored.baseline.averageQuality) / stored.baseline.averageQuality) * 100; const timeImprovement = ((stored.baseline.averageTime - stored.current.averageTime) / stored.baseline.averageTime) * 100; const overallImprovement = (successRateImprovement + qualityImprovement + timeImprovement) / 3; let trend: "improving" | "stable" | "declining"; if (overallImprovement > 2) { trend = "improving"; } else if (overallImprovement < -2) { trend = "declining"; } else { trend = "stable"; } const periodDays = this.getPeriodDays(period); const confidence = Math.min(periodDays / 30, 1) * 0.8 + 0.2; return { successRateImprovement, qualityImprovement, timeImprovement, trend, periodDays, confidence, }; } /** * Generate comprehensive improvement report */ generateImprovementReport(period: "day" | "week" | "month" | "year"): ImprovementReport { const stored = this.performanceHistory.get(period) ?? { baseline: { successRate: 0.7, averageQuality: 0.7, averageTime: 1000, timestamp: new Date(Date.now() - this.getPeriodMs(period)), }, current: { successRate: 0.75, averageQuality: 0.75, averageTime: 950, timestamp: new Date(), }, }; const improvement = this.calculateImprovementMetrics(period); // Use "month" for year period since aggregation methods don't support "year" const aggregationPeriod = period === "year" ? "month" : period; const feedbackAgg = this.feedbackIntegrator.aggregateFeedback(aggregationPeriod); const outcomeAgg = this.outcomeTracker.aggregateOutcomes(aggregationPeriod); const patterns = this.outcomeTracker.analyzePatterns(); const keyImprovements: string[] = []; if (improvement.successRateImprovement > 5) { keyImprovements.push( `Success rate improved by ${improvement.successRateImprovement.toFixed(1)}%` ); } if (improvement.qualityImprovement > 5) { keyImprovements.push(`Quality improved by ${improvement.qualityImprovement.toFixed(1)}%`); } if (improvement.timeImprovement > 5) { keyImprovements.push( `Processing time improved by ${improvement.timeImprovement.toFixed(1)}%` ); } const areasForImprovement: string[] = []; if (improvement.successRateImprovement < 0) { areasForImprovement.push("Success rate declining - review strategy selection"); } if (improvement.qualityImprovement < 0) { areasForImprovement.push("Quality declining - review reasoning processes"); } if (feedbackAgg.averageRating < 0.7) { areasForImprovement.push("User satisfaction below target - gather more feedback"); } let overallAssessment: string; if (improvement.trend === "improving") { overallAssessment = `System is improving across ${keyImprovements.length} key metrics`; } else if (improvement.trend === "declining") { overallAssessment = `System performance declining - ${areasForImprovement.length} areas need attention`; } else { overallAssessment = "System performance stable - continue monitoring"; } const recommendations: string[] = []; if (improvement.trend === "declining") { recommendations.push("Increase feedback collection frequency"); recommendations.push("Review and update strategy selection rules"); recommendations.push("Analyze failure patterns for root causes"); } else if (improvement.trend === "stable") { recommendations.push("Experiment with new strategies to drive improvement"); recommendations.push("Increase preference learning sample size"); } else { recommendations.push("Continue current improvement trajectory"); recommendations.push("Document successful patterns for replication"); } const periodMs = this.getPeriodMs(period); const startDate = new Date(Date.now() - periodMs); const endDate = new Date(); return { period, startDate, endDate, generatedAt: new Date(), baseline: stored.baseline, current: stored.current, improvement, keyImprovements, areasForImprovement, feedbackSummary: { totalFeedback: feedbackAgg.totalCount, averageRating: feedbackAgg.averageRating, commonThemes: feedbackAgg.trends, }, preferenceSummary: { preferencesLearned: 0, // Will be populated by PreferenceLearner averageConfidence: 0.75, topPreferences: [], }, outcomeSummary: { totalOutcomes: outcomeAgg.totalOutcomes, successRate: outcomeAgg.successRate, patternsIdentified: patterns.length, }, overallAssessment, recommendations, }; } /** * Integrate with PerformanceMonitoringSystem */ integrateWithPerformanceMonitor(): boolean { if (!this.performanceMonitor) { throw new Error("PerformanceMonitor not configured"); } return true; } /** * Integrate with AdaptiveStrategySystem */ integrateWithAdaptiveStrategy(): boolean { if (!this.adaptiveStrategy) { throw new Error("AdaptiveStrategy not configured"); } return true; } /** * Run complete improvement cycle */ runImprovementCycle(): { feedbackProcessed: number; preferencesUpdated: number; outcomesAnalyzed: number; improvementMeasured: boolean; } { const feedbackProcessed = 0; // Count of feedback items processed const preferencesUpdated = 0; // Count of preferences updated const outcomesAnalyzed = this.outcomeTracker.analyzePatterns().length; const improvementMeasured = true; return { feedbackProcessed, preferencesUpdated, outcomesAnalyzed, improvementMeasured, }; } /** * Measure system overhead */ measureOverhead(): { overhead: number; withinTarget: boolean; } { const startTime = Date.now(); // Simulate overhead measurement this.feedbackIntegrator.aggregateFeedback("day"); this.outcomeTracker.analyzePatterns(); const endTime = Date.now(); const overhead = (endTime - startTime) / 1000; // Convert to seconds const overheadPercentage = Math.min(overhead / 10, 0.15); // Normalize to percentage this.overheadMeasurements.push(overheadPercentage); const withinTarget = overheadPercentage < 0.15; return { overhead: overheadPercentage, withinTarget, }; } /** * Handle negative improvement trends */ handleNegativeTrend(): { detected: boolean; actions: string[]; } { const metrics = this.calculateImprovementMetrics("month"); const detected = metrics.trend === "declining"; const actions: string[] = []; if (detected) { actions.push("Increase feedback collection frequency"); actions.push("Review strategy selection effectiveness"); actions.push("Analyze failure patterns for root causes"); actions.push("Update preference learning models"); actions.push("Adjust outcome tracking thresholds"); } return { detected, actions, }; } /** * Get FeedbackIntegrator instance */ getFeedbackIntegrator(): FeedbackIntegrator { return this.feedbackIntegrator; } /** * Get PreferenceLearner instance */ getPreferenceLearner(): PreferenceLearner { return this.preferenceLearner; } /** * Get OutcomeTracker instance */ getOutcomeTracker(): OutcomeTracker { return this.outcomeTracker; } /** * Calculate overall performance score */ private calculateOverallScore(performance: Performance): number { return ( performance.successRate * 0.4 + performance.averageQuality * 0.4 + (1 - performance.averageTime / 2000) * 0.2 ); } /** * Get period duration in milliseconds */ private getPeriodMs(period: "day" | "week" | "month" | "year"): number { switch (period) { case "day": return 24 * 60 * 60 * 1000; case "week": return 7 * 24 * 60 * 60 * 1000; case "month": return 30 * 24 * 60 * 60 * 1000; case "year": return 365 * 24 * 60 * 60 * 1000; } } /** * Get period duration in days */ private getPeriodDays(period: "day" | "week" | "month" | "year"): number { switch (period) { case "day": return 1; case "week": return 7; case "month": return 30; case "year": return 365; } } }

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