Skip to main content
Glama

documcp

by tosin2013
learning.ts18.9 kB
/** * Incremental Learning System for DocuMCP * Implements Issue #47: Incremental Learning System * * Enables continuous improvement of recommendations based on historical patterns, * success rates, and user feedback to optimize SSG suggestions and documentation strategies. */ import { MemoryManager } from "./manager.js"; import { MemoryEntry } from "./storage.js"; export interface LearningPattern { id: string; type: | "ssg_preference" | "deployment_success" | "project_similarity" | "user_behavior"; pattern: Record<string, any>; confidence: number; sampleSize: number; lastUpdated: string; metadata: { projectTypes?: string[]; technologies?: string[]; successRate?: number; frequency?: number; }; } export interface LearningInsight { type: "recommendation" | "warning" | "optimization"; message: string; confidence: number; actionable: boolean; data: Record<string, any>; } export interface ProjectFeatures { language: string; framework?: string; size: "small" | "medium" | "large"; complexity: "simple" | "moderate" | "complex"; hasTests: boolean; hasCI: boolean; hasDocs: boolean; teamSize?: number; isOpenSource: boolean; } export class IncrementalLearningSystem { private memoryManager: MemoryManager; private patterns: Map<string, LearningPattern>; private learningEnabled: boolean = true; private readonly minSampleSize = 3; private readonly confidenceThreshold = 0.7; constructor(memoryManager: MemoryManager) { this.memoryManager = memoryManager; this.patterns = new Map(); } async initialize(): Promise<void> { await this.loadPatterns(); await this.updatePatterns(); } /** * Learn from a new interaction result */ async learn( interaction: MemoryEntry, outcome: "success" | "failure" | "neutral", feedback?: Record<string, any>, ): Promise<void> { if (!this.learningEnabled) return; const features = this.extractFeatures(interaction); // Update SSG preference patterns if (interaction.type === "recommendation" && interaction.metadata.ssg) { await this.updateSSGPattern(features, interaction.metadata.ssg, outcome); } // Update deployment success patterns if (interaction.type === "deployment") { await this.updateDeploymentPattern(features, outcome); } // Update project similarity patterns if (interaction.type === "analysis") { await this.updateSimilarityPattern(features, interaction); } // Learn from user feedback if (feedback) { await this.updateUserBehaviorPattern(features, feedback); } await this.persistPatterns(); } /** * Get improved recommendations based on learned patterns */ async getImprovedRecommendation( projectFeatures: ProjectFeatures, baseRecommendation: any, ): Promise<{ recommendation: any; confidence: number; insights: LearningInsight[]; }> { const insights: LearningInsight[] = []; const adjustedRecommendation = { ...baseRecommendation }; let confidenceBoost = 0; // Apply SSG preference patterns const ssgPattern = await this.getSSGPreferencePattern(projectFeatures); if (ssgPattern && ssgPattern.confidence > this.confidenceThreshold) { const preferredSSG = ssgPattern.pattern.preferredSSG; if (preferredSSG !== baseRecommendation.recommended) { insights.push({ type: "recommendation", message: `Based on ${ ssgPattern.sampleSize } similar projects, ${preferredSSG} has a ${( ssgPattern.pattern.successRate * 100 ).toFixed(0)}% success rate`, confidence: ssgPattern.confidence, actionable: true, data: { suggestedSSG: preferredSSG, pattern: ssgPattern }, }); adjustedRecommendation.recommended = preferredSSG; adjustedRecommendation.learningAdjusted = true; confidenceBoost += 0.2; } } // Apply deployment success patterns const deploymentPattern = await this.getDeploymentPattern(projectFeatures); if ( deploymentPattern && deploymentPattern.confidence > this.confidenceThreshold ) { const riskFactors = deploymentPattern.pattern.riskFactors || []; if (riskFactors.length > 0) { insights.push({ type: "warning", message: `Projects with similar characteristics have ${riskFactors.length} common deployment issues`, confidence: deploymentPattern.confidence, actionable: true, data: { riskFactors, pattern: deploymentPattern }, }); adjustedRecommendation.deploymentWarnings = riskFactors; } } // Apply optimization patterns const optimizations = await this.getOptimizationInsights(projectFeatures); insights.push(...optimizations); const finalConfidence = Math.min( baseRecommendation.confidence + confidenceBoost, 1.0, ); return { recommendation: adjustedRecommendation, confidence: finalConfidence, insights, }; } /** * Extract features from project data for pattern matching */ private extractFeatures(interaction: MemoryEntry): ProjectFeatures { const data = interaction.data; return { language: data.language?.primary || "unknown", framework: data.framework?.name, size: this.categorizeSize(data.stats?.files || 0), complexity: this.categorizeComplexity(data), hasTests: Boolean(data.testing?.hasTests), hasCI: Boolean(data.ci?.hasCI), hasDocs: Boolean(data.documentation?.exists), isOpenSource: Boolean(data.repository?.isPublic), }; } private categorizeSize(fileCount: number): "small" | "medium" | "large" { if (fileCount < 50) return "small"; if (fileCount < 200) return "medium"; return "large"; } private categorizeComplexity(data: any): "simple" | "moderate" | "complex" { let complexity = 0; if (data.dependencies?.count > 20) complexity++; if (data.framework?.name) complexity++; if (data.testing?.frameworks?.length > 1) complexity++; if (data.ci?.workflows?.length > 2) complexity++; if (data.architecture?.patterns?.length > 3) complexity++; if (complexity <= 1) return "simple"; if (complexity <= 3) return "moderate"; return "complex"; } /** * Update SSG preference patterns based on outcomes */ private async updateSSGPattern( features: ProjectFeatures, ssg: string, outcome: "success" | "failure" | "neutral", ): Promise<void> { const patternKey = this.generatePatternKey("ssg_preference", features); const existing = this.patterns.get(patternKey); if (existing) { // Update existing pattern const totalCount = existing.sampleSize; const successCount = existing.pattern.successCount || 0; const newSuccessCount = outcome === "success" ? successCount + 1 : successCount; existing.pattern.preferredSSG = ssg; existing.pattern.successCount = newSuccessCount; existing.pattern.successRate = newSuccessCount / (totalCount + 1); existing.sampleSize = totalCount + 1; existing.confidence = Math.min(existing.sampleSize / 10, 1.0); existing.lastUpdated = new Date().toISOString(); } else { // Create new pattern const pattern: LearningPattern = { id: patternKey, type: "ssg_preference", pattern: { preferredSSG: ssg, successCount: outcome === "success" ? 1 : 0, successRate: outcome === "success" ? 1.0 : 0.0, }, confidence: 0.1, sampleSize: 1, lastUpdated: new Date().toISOString(), metadata: { projectTypes: [features.language], technologies: features.framework ? [features.framework] : [], }, }; this.patterns.set(patternKey, pattern); } } /** * Update deployment success patterns */ private async updateDeploymentPattern( features: ProjectFeatures, outcome: "success" | "failure" | "neutral", ): Promise<void> { const patternKey = this.generatePatternKey("deployment_success", features); const existing = this.patterns.get(patternKey); const riskFactors: string[] = []; if (!features.hasTests) riskFactors.push("no_tests"); if (!features.hasCI) riskFactors.push("no_ci"); if (features.complexity === "complex") riskFactors.push("high_complexity"); if (features.size === "large") riskFactors.push("large_codebase"); if (existing) { const totalCount = existing.sampleSize; const successCount = existing.pattern.successCount || 0; const newSuccessCount = outcome === "success" ? successCount + 1 : successCount; existing.pattern.successCount = newSuccessCount; existing.pattern.successRate = newSuccessCount / (totalCount + 1); existing.pattern.riskFactors = riskFactors; existing.sampleSize = totalCount + 1; existing.confidence = Math.min(existing.sampleSize / 10, 1.0); existing.lastUpdated = new Date().toISOString(); } else { const pattern: LearningPattern = { id: patternKey, type: "deployment_success", pattern: { successCount: outcome === "success" ? 1 : 0, successRate: outcome === "success" ? 1.0 : 0.0, riskFactors, }, confidence: 0.1, sampleSize: 1, lastUpdated: new Date().toISOString(), metadata: { projectTypes: [features.language], successRate: outcome === "success" ? 1.0 : 0.0, }, }; this.patterns.set(patternKey, pattern); } } /** * Update project similarity patterns for better matching */ private async updateSimilarityPattern( features: ProjectFeatures, _interaction: MemoryEntry, ): Promise<void> { const patternKey = this.generatePatternKey("project_similarity", features); const existing = this.patterns.get(patternKey); const characteristics = { language: features.language, framework: features.framework, size: features.size, complexity: features.complexity, }; if (existing) { existing.pattern.characteristics = characteristics; existing.sampleSize += 1; existing.confidence = Math.min(existing.sampleSize / 15, 1.0); existing.lastUpdated = new Date().toISOString(); } else { const pattern: LearningPattern = { id: patternKey, type: "project_similarity", pattern: { characteristics, commonPatterns: [], }, confidence: 0.1, sampleSize: 1, lastUpdated: new Date().toISOString(), metadata: { projectTypes: [features.language], }, }; this.patterns.set(patternKey, pattern); } } /** * Update user behavior patterns from feedback */ private async updateUserBehaviorPattern( features: ProjectFeatures, feedback: Record<string, any>, ): Promise<void> { const patternKey = this.generatePatternKey("user_behavior", features); const existing = this.patterns.get(patternKey); if (existing) { existing.pattern.feedback = { ...existing.pattern.feedback, ...feedback }; existing.sampleSize += 1; existing.confidence = Math.min(existing.sampleSize / 5, 1.0); existing.lastUpdated = new Date().toISOString(); } else { const pattern: LearningPattern = { id: patternKey, type: "user_behavior", pattern: { feedback, preferences: {}, }, confidence: 0.2, sampleSize: 1, lastUpdated: new Date().toISOString(), metadata: {}, }; this.patterns.set(patternKey, pattern); } } /** * Generate a consistent pattern key for grouping similar projects */ private generatePatternKey(type: string, features: ProjectFeatures): string { const keyParts = [ type, features.language, features.framework || "none", features.size, features.complexity, ]; return keyParts.join("_").toLowerCase(); } /** * Get SSG preference pattern for similar projects */ private async getSSGPreferencePattern( features: ProjectFeatures, ): Promise<LearningPattern | null> { const patternKey = this.generatePatternKey("ssg_preference", features); const pattern = this.patterns.get(patternKey); if (pattern && pattern.sampleSize >= this.minSampleSize) { return pattern; } // Try broader matching if exact match not found const broaderKey = `ssg_preference_${features.language}_${features.size}`; return this.patterns.get(broaderKey) || null; } /** * Get deployment success pattern for risk assessment */ private async getDeploymentPattern( features: ProjectFeatures, ): Promise<LearningPattern | null> { const patternKey = this.generatePatternKey("deployment_success", features); const pattern = this.patterns.get(patternKey); if (pattern && pattern.sampleSize >= this.minSampleSize) { return pattern; } return null; } /** * Generate optimization insights based on learned patterns */ private async getOptimizationInsights( features: ProjectFeatures, ): Promise<LearningInsight[]> { const insights: LearningInsight[] = []; // Check for common optimization opportunities if (!features.hasTests) { insights.push({ type: "optimization", message: "Adding tests could improve deployment success rate by 25%", confidence: 0.8, actionable: true, data: { optimization: "add_tests", impact: "deployment_success" }, }); } if (!features.hasCI && features.size !== "small") { insights.push({ type: "optimization", message: "CI/CD setup recommended for projects of this size", confidence: 0.7, actionable: true, data: { optimization: "add_ci", impact: "development_velocity" }, }); } if (features.complexity === "complex" && !features.hasDocs) { insights.push({ type: "optimization", message: "Complex projects benefit significantly from comprehensive documentation", confidence: 0.9, actionable: true, data: { optimization: "improve_docs", impact: "maintainability" }, }); } return insights; } /** * Load patterns from persistent storage */ private async loadPatterns(): Promise<void> { try { const patternMemories = await this.memoryManager.search("learning_pattern"); for (const memory of patternMemories) { if (memory.data.pattern) { this.patterns.set(memory.data.pattern.id, memory.data.pattern); } } } catch (error) { console.error("Failed to load learning patterns:", error); } } /** * Update patterns based on recent memory data */ private async updatePatterns(): Promise<void> { // Analyze recent memories to update patterns const recentMemories = await this.memoryManager.search("", { sortBy: "timestamp", }); const cutoffDate = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); // Last 7 days for (const memory of recentMemories) { if (new Date(memory.timestamp) > cutoffDate) { // Infer outcome based on memory data const outcome = this.inferOutcome(memory); if (outcome) { await this.learn(memory, outcome); } } } } /** * Infer outcome from memory entry data */ private inferOutcome( memory: MemoryEntry, ): "success" | "failure" | "neutral" | null { if (memory.type === "deployment") { if (memory.data.status === "success") return "success"; if (memory.data.status === "failed") return "failure"; } if (memory.type === "recommendation" && memory.data.feedback) { if (memory.data.feedback.rating > 3) return "success"; if (memory.data.feedback.rating < 3) return "failure"; } return "neutral"; } /** * Persist learned patterns to memory */ private async persistPatterns(): Promise<void> { for (const [, pattern] of this.patterns) { if (pattern.sampleSize >= this.minSampleSize) { await this.memoryManager.remember( "interaction", { pattern, type: "learning_pattern", }, { tags: ["learning", "pattern", pattern.type], }, ); } } } /** * Get all learned patterns */ async getPatterns(): Promise<LearningPattern[]> { return Array.from(this.patterns.values()); } /** * Get learning statistics and insights */ async getStatistics(): Promise<{ totalPatterns: number; patternsByType: Record<string, number>; averageConfidence: number; learningVelocity: number; insights: string[]; }> { const stats = { totalPatterns: this.patterns.size, patternsByType: {} as Record<string, number>, averageConfidence: 0, learningVelocity: 0, insights: [] as string[], }; let totalConfidence = 0; for (const pattern of this.patterns.values()) { stats.patternsByType[pattern.type] = (stats.patternsByType[pattern.type] || 0) + 1; totalConfidence += pattern.confidence; } stats.averageConfidence = stats.totalPatterns > 0 ? totalConfidence / stats.totalPatterns : 0; // Calculate learning velocity (patterns learned in last week) const weekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); stats.learningVelocity = Array.from(this.patterns.values()).filter( (p) => new Date(p.lastUpdated) > weekAgo, ).length; // Generate insights if (stats.totalPatterns > 10) { stats.insights.push( `System has learned ${stats.totalPatterns} patterns with ${( stats.averageConfidence * 100 ).toFixed(0)}% average confidence`, ); } if (stats.learningVelocity > 0) { stats.insights.push( `Learning velocity: ${stats.learningVelocity} new patterns this week`, ); } const topPatternType = Object.entries(stats.patternsByType).sort( ([, a], [, b]) => b - a, )[0]; if (topPatternType) { stats.insights.push( `Most common pattern type: ${topPatternType[0]} (${topPatternType[1]} patterns)`, ); } return stats; } /** * Enable or disable learning */ setLearningEnabled(enabled: boolean): void { this.learningEnabled = enabled; } /** * Clear all learned patterns (useful for testing or reset) */ async clearPatterns(): Promise<void> { this.patterns.clear(); } } export default IncrementalLearningSystem;

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/tosin2013/documcp'

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