memory_visualization
Generate visual representations of memory data using dashboards, timelines, networks, heatmaps, distributions, trends, or custom visualizations to analyze and understand memory usage patterns.
Instructions
Generate visual representations of memory data
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| visualizationType | No | dashboard | |
| options | No | ||
| customVisualization | No |
Implementation Reference
- src/memory/index.ts:524-602 (registration)Registration of the 'memory_visualization' tool in the memoryTools array, including full input schema for different visualization types and options.{ name: "memory_visualization", description: "Generate visual representations of memory data", inputSchema: { type: "object", properties: { visualizationType: { type: "string", enum: [ "dashboard", "timeline", "network", "heatmap", "distribution", "trends", "custom", ], default: "dashboard", }, options: { type: "object", properties: { timeRange: { type: "object", properties: { start: { type: "string", format: "date-time" }, end: { type: "string", format: "date-time" }, }, }, includeCharts: { type: "array", items: { type: "string" } }, config: { type: "object", properties: { width: { type: "number", default: 800 }, height: { type: "number", default: 600 }, theme: { type: "string", enum: ["light", "dark", "auto"], default: "light", }, exportFormat: { type: "string", enum: ["svg", "png", "json", "html"], default: "svg", }, interactive: { type: "boolean", default: true }, }, }, }, }, customVisualization: { type: "object", properties: { type: { type: "string", enum: [ "line", "bar", "scatter", "heatmap", "network", "sankey", "treemap", "timeline", ], }, query: { type: "object", properties: { filters: { type: "object" }, groupBy: { type: "string" }, aggregation: { type: "string" }, }, }, }, }, }, }, },
- src/memory/visualization.ts:157-263 (handler)Main handler method generateDashboard that creates comprehensive memory dashboards with multiple chart types based on input parameters.async generateDashboard(options?: { timeRange?: { start: Date; end: Date }; includeCharts?: string[]; config?: Partial<VisualizationConfig>; }): Promise<DashboardData> { const timeRange = options?.timeRange || this.getDefaultTimeRange(); const config = { ...this.defaultConfig, ...options?.config }; this.emit("dashboard_generation_started", { timeRange }); try { const charts: ChartData[] = []; // Activity Timeline if ( !options?.includeCharts || options.includeCharts.includes("activity") ) { charts.push(await this.generateActivityTimeline(timeRange, config)); } // Memory Type Distribution if ( !options?.includeCharts || options.includeCharts.includes("distribution") ) { charts.push( await this.generateMemoryTypeDistribution(timeRange, config), ); } // Success Rate Trends if ( !options?.includeCharts || options.includeCharts.includes("success") ) { charts.push(await this.generateSuccessRateTrends(timeRange, config)); } // Knowledge Graph Network if ( !options?.includeCharts || options.includeCharts.includes("network") ) { charts.push(await this.generateKnowledgeGraphVisualization(config)); } // Learning Patterns Heatmap if ( !options?.includeCharts || options.includeCharts.includes("learning") ) { charts.push(await this.generateLearningPatternsHeatmap(config)); } // Temporal Patterns if ( !options?.includeCharts || options.includeCharts.includes("temporal") ) { charts.push( await this.generateTemporalPatternsChart(timeRange, config), ); } // Project Correlation Matrix if ( !options?.includeCharts || options.includeCharts.includes("correlation") ) { charts.push( await this.generateProjectCorrelationMatrix(timeRange, config), ); } // Get summary data const entries = await this.getEntriesInTimeRange(timeRange); const keyInsights = await this.generateKeyInsights(entries, timeRange); const healthScore = await this.calculateSystemHealthScore(entries); const dashboard: DashboardData = { title: "DocuMCP Memory System Dashboard", description: `Comprehensive overview of memory system activity from ${timeRange.start.toLocaleDateString()} to ${timeRange.end.toLocaleDateString()}`, charts, summary: { totalEntries: entries.length, timeRange, keyInsights, healthScore, }, generated: new Date(), }; this.emit("dashboard_generated", { charts: charts.length, entries: entries.length, timeRange, }); return dashboard; } catch (error) { this.emit("dashboard_error", { error: error instanceof Error ? error.message : String(error), }); throw error; } }
- src/memory/visualization.ts:110-1394 (handler)Core MemoryVisualizationSystem class containing all the logic for generating various memory visualizations including timelines, networks, heatmaps, and dashboards.export class MemoryVisualizationSystem extends EventEmitter { private storage: JSONLStorage; private manager: MemoryManager; private learningSystem: IncrementalLearningSystem; private knowledgeGraph: KnowledgeGraph; private temporalAnalysis: TemporalMemoryAnalysis; private defaultConfig: VisualizationConfig; private visualizationCache: Map<string, ChartData>; constructor( storage: JSONLStorage, manager: MemoryManager, learningSystem: IncrementalLearningSystem, knowledgeGraph: KnowledgeGraph, temporalAnalysis: TemporalMemoryAnalysis, ) { super(); this.storage = storage; this.manager = manager; this.learningSystem = learningSystem; this.knowledgeGraph = knowledgeGraph; this.temporalAnalysis = temporalAnalysis; this.visualizationCache = new Map(); this.defaultConfig = { width: 800, height: 600, theme: "light", colorScheme: [ "#3B82F6", // Blue "#10B981", // Green "#F59E0B", // Yellow "#EF4444", // Red "#8B5CF6", // Purple "#06B6D4", // Cyan "#F97316", // Orange "#84CC16", // Lime ], interactive: true, exportFormat: "svg", responsive: true, }; } /** * Generate comprehensive dashboard */ async generateDashboard(options?: { timeRange?: { start: Date; end: Date }; includeCharts?: string[]; config?: Partial<VisualizationConfig>; }): Promise<DashboardData> { const timeRange = options?.timeRange || this.getDefaultTimeRange(); const config = { ...this.defaultConfig, ...options?.config }; this.emit("dashboard_generation_started", { timeRange }); try { const charts: ChartData[] = []; // Activity Timeline if ( !options?.includeCharts || options.includeCharts.includes("activity") ) { charts.push(await this.generateActivityTimeline(timeRange, config)); } // Memory Type Distribution if ( !options?.includeCharts || options.includeCharts.includes("distribution") ) { charts.push( await this.generateMemoryTypeDistribution(timeRange, config), ); } // Success Rate Trends if ( !options?.includeCharts || options.includeCharts.includes("success") ) { charts.push(await this.generateSuccessRateTrends(timeRange, config)); } // Knowledge Graph Network if ( !options?.includeCharts || options.includeCharts.includes("network") ) { charts.push(await this.generateKnowledgeGraphVisualization(config)); } // Learning Patterns Heatmap if ( !options?.includeCharts || options.includeCharts.includes("learning") ) { charts.push(await this.generateLearningPatternsHeatmap(config)); } // Temporal Patterns if ( !options?.includeCharts || options.includeCharts.includes("temporal") ) { charts.push( await this.generateTemporalPatternsChart(timeRange, config), ); } // Project Correlation Matrix if ( !options?.includeCharts || options.includeCharts.includes("correlation") ) { charts.push( await this.generateProjectCorrelationMatrix(timeRange, config), ); } // Get summary data const entries = await this.getEntriesInTimeRange(timeRange); const keyInsights = await this.generateKeyInsights(entries, timeRange); const healthScore = await this.calculateSystemHealthScore(entries); const dashboard: DashboardData = { title: "DocuMCP Memory System Dashboard", description: `Comprehensive overview of memory system activity from ${timeRange.start.toLocaleDateString()} to ${timeRange.end.toLocaleDateString()}`, charts, summary: { totalEntries: entries.length, timeRange, keyInsights, healthScore, }, generated: new Date(), }; this.emit("dashboard_generated", { charts: charts.length, entries: entries.length, timeRange, }); return dashboard; } catch (error) { this.emit("dashboard_error", { error: error instanceof Error ? error.message : String(error), }); throw error; } } /** * Generate activity timeline chart */ async generateActivityTimeline( timeRange: { start: Date; end: Date }, config: Partial<VisualizationConfig>, ): Promise<ChartData> { const entries = await this.getEntriesInTimeRange(timeRange); // Group entries by day const dailyData = new Map<string, number>(); const successData = new Map<string, number>(); for (const entry of entries) { const day = entry.timestamp.slice(0, 10); // YYYY-MM-DD dailyData.set(day, (dailyData.get(day) || 0) + 1); if (entry.data.outcome === "success" || entry.data.success === true) { successData.set(day, (successData.get(day) || 0) + 1); } } // Create time series data const datasets = [ { label: "Total Activity", data: Array.from(dailyData.entries()).map(([date, count]) => ({ x: date, y: count, })), borderColor: config.colorScheme?.[0] || "#3B82F6", backgroundColor: config.colorScheme?.[0] || "#3B82F6", fill: false, }, { label: "Successful Activities", data: Array.from(successData.entries()).map(([date, count]) => ({ x: date, y: count, })), borderColor: config.colorScheme?.[1] || "#10B981", backgroundColor: config.colorScheme?.[1] || "#10B981", fill: false, }, ]; return { type: "line", title: "Memory Activity Timeline", description: "Daily memory system activity showing total entries and successful outcomes", data: { datasets, options: { responsive: config.responsive, plugins: { title: { display: true, text: "Memory Activity Over Time", }, legend: { display: true, position: "top", }, }, scales: { x: { type: "time", time: { unit: "day", }, title: { display: true, text: "Date", }, }, y: { title: { display: true, text: "Number of Entries", }, }, }, }, }, config, metadata: { generated: new Date(), dataPoints: entries.length, timeRange, filters: { type: "activity_timeline" }, }, }; } /** * Generate memory type distribution chart */ async generateMemoryTypeDistribution( timeRange: { start: Date; end: Date }, config: Partial<VisualizationConfig>, ): Promise<ChartData> { const entries = await this.getEntriesInTimeRange(timeRange); // Count entries by type const typeCounts = new Map<string, number>(); for (const entry of entries) { typeCounts.set(entry.type, (typeCounts.get(entry.type) || 0) + 1); } // Sort by count const sortedTypes = Array.from(typeCounts.entries()).sort( ([, a], [, b]) => b - a, ); const data = { labels: sortedTypes.map(([type]) => type), datasets: [ { data: sortedTypes.map(([, count]) => count), backgroundColor: config.colorScheme || this.defaultConfig.colorScheme, borderColor: config.colorScheme?.map((c) => this.darkenColor(c)) || this.defaultConfig.colorScheme.map((c) => this.darkenColor(c)), borderWidth: 2, }, ], }; return { type: "bar", title: "Memory Type Distribution", description: "Distribution of memory entries by type", data: { ...data, options: { responsive: config.responsive, plugins: { title: { display: true, text: "Memory Entry Types", }, legend: { display: false, }, }, scales: { y: { beginAtZero: true, title: { display: true, text: "Number of Entries", }, }, x: { title: { display: true, text: "Memory Type", }, }, }, }, }, config, metadata: { generated: new Date(), dataPoints: entries.length, timeRange, filters: { type: "type_distribution" }, }, }; } /** * Generate success rate trends chart */ async generateSuccessRateTrends( timeRange: { start: Date; end: Date }, config: Partial<VisualizationConfig>, ): Promise<ChartData> { const entries = await this.getEntriesInTimeRange(timeRange); // Group by week and calculate success rates const weeklyData = new Map<string, { total: number; successful: number }>(); for (const entry of entries) { const week = this.getWeekKey(new Date(entry.timestamp)); const current = weeklyData.get(week) || { total: 0, successful: 0 }; current.total++; if (entry.data.outcome === "success" || entry.data.success === true) { current.successful++; } weeklyData.set(week, current); } // Calculate success rates const data = Array.from(weeklyData.entries()) .map(([week, stats]) => ({ x: week, y: stats.total > 0 ? (stats.successful / stats.total) * 100 : 0, total: stats.total, successful: stats.successful, })) .sort((a, b) => a.x.localeCompare(b.x)); return { type: "line", title: "Success Rate Trends", description: "Weekly success rate trends for memory system operations", data: { datasets: [ { label: "Success Rate (%)", data: data, borderColor: config.colorScheme?.[1] || "#10B981", backgroundColor: config.colorScheme?.[1] || "#10B981", fill: false, tension: 0.1, }, ], options: { responsive: config.responsive, plugins: { title: { display: true, text: "Success Rate Over Time", }, tooltip: { callbacks: { afterBody: (context: any) => { const point = data[context[0].dataIndex]; return `Total: ${point.total}, Successful: ${point.successful}`; }, }, }, }, scales: { x: { title: { display: true, text: "Week", }, }, y: { beginAtZero: true, max: 100, title: { display: true, text: "Success Rate (%)", }, }, }, }, }, config, metadata: { generated: new Date(), dataPoints: data.length, timeRange, filters: { type: "success_trends" }, }, }; } /** * Generate knowledge graph network visualization */ async generateKnowledgeGraphVisualization( config: Partial<VisualizationConfig>, ): Promise<ChartData> { const allNodes = await this.knowledgeGraph.getAllNodes(); const allEdges = await this.knowledgeGraph.getAllEdges(); // Prepare network data const networkData: NetworkVisualization = { nodes: allNodes.map((node) => ({ id: node.id, label: node.label || node.id.slice(0, 10), group: node.type, size: Math.max(10, Math.min(30, (node.weight || 1) * 10)), color: this.getColorForNodeType(node.type, config.colorScheme), metadata: node.properties, })), edges: allEdges.map((edge) => ({ source: edge.source, target: edge.target, weight: edge.weight, type: edge.type, color: this.getColorForEdgeType(edge.type, config.colorScheme), metadata: edge.properties, })), layout: "force", clustering: true, }; return { type: "network", title: "Knowledge Graph Network", description: "Interactive network visualization of memory relationships and connections", data: networkData, config, metadata: { generated: new Date(), dataPoints: allNodes.length + allEdges.length, filters: { type: "knowledge_graph" }, }, }; } /** * Generate learning patterns heatmap */ async generateLearningPatternsHeatmap( config: Partial<VisualizationConfig>, ): Promise<ChartData> { const patterns = await this.learningSystem.getPatterns(); // Create correlation matrix between different pattern dimensions const frameworks = [ ...new Set( patterns .flatMap((p) => p.metadata.technologies || []) .filter( (t) => t.includes("framework") || t.includes("js") || t.includes("react") || t.includes("vue"), ), ), ]; const languages = [ ...new Set( patterns .flatMap((p) => p.metadata.technologies || []) .filter((t) => !t.includes("framework")), ), ]; const heatmapData: number[][] = []; const labels = { x: frameworks, y: languages }; for (const language of languages) { const row: number[] = []; for (const framework of frameworks) { // Calculate correlation/co-occurrence const langPatterns = patterns.filter( (p) => p.metadata.technologies?.includes(language), ); const frameworkPatterns = patterns.filter( (p) => p.metadata.technologies?.includes(framework), ); const bothPatterns = patterns.filter( (p) => p.metadata.technologies?.includes(language) && p.metadata.technologies?.includes(framework), ); const correlation = langPatterns.length > 0 && frameworkPatterns.length > 0 ? bothPatterns.length / Math.min(langPatterns.length, frameworkPatterns.length) : 0; row.push(correlation); } heatmapData.push(row); } const heatmap: HeatmapVisualization = { data: heatmapData, labels, colorScale: { min: 0, max: 1, colors: ["#F3F4F6", "#93C5FD", "#3B82F6", "#1D4ED8", "#1E3A8A"], }, title: "Language-Framework Learning Patterns", description: "Correlation matrix showing relationships between programming languages and frameworks in learning patterns", }; return { type: "heatmap", title: "Learning Patterns Heatmap", description: "Visualization of learning pattern correlations across languages and frameworks", data: heatmap, config, metadata: { generated: new Date(), dataPoints: patterns.length, filters: { type: "learning_patterns" }, }, }; } /** * Generate temporal patterns chart */ async generateTemporalPatternsChart( timeRange: { start: Date; end: Date }, config: Partial<VisualizationConfig>, ): Promise<ChartData> { const patterns = await this.temporalAnalysis.analyzeTemporalPatterns({ granularity: "day", aggregation: "count", timeRange: { start: timeRange.start, end: timeRange.end, duration: timeRange.end.getTime() - timeRange.start.getTime(), label: "Analysis Period", }, }); // Prepare data for different pattern types const patternData = patterns.map((pattern) => ({ type: pattern.type, confidence: pattern.confidence, description: pattern.description, dataPoints: pattern.dataPoints?.length || 0, })); const data = { labels: patternData.map((p) => p.type), datasets: [ { label: "Pattern Confidence", data: patternData.map((p) => p.confidence * 100), backgroundColor: config.colorScheme || this.defaultConfig.colorScheme, borderColor: config.colorScheme?.map((c) => this.darkenColor(c)) || this.defaultConfig.colorScheme.map((c) => this.darkenColor(c)), borderWidth: 2, }, ], }; return { type: "bar", title: "Temporal Patterns Analysis", description: "Confidence levels of detected temporal patterns in memory activity", data: { ...data, options: { responsive: config.responsive, plugins: { title: { display: true, text: "Detected Temporal Patterns", }, tooltip: { callbacks: { afterBody: (context: any) => { const pattern = patternData[context[0].dataIndex]; return pattern.description; }, }, }, }, scales: { y: { beginAtZero: true, max: 100, title: { display: true, text: "Confidence (%)", }, }, x: { title: { display: true, text: "Pattern Type", }, }, }, }, }, config, metadata: { generated: new Date(), dataPoints: patterns.length, timeRange, filters: { type: "temporal_patterns" }, }, }; } /** * Generate project correlation matrix */ async generateProjectCorrelationMatrix( timeRange: { start: Date; end: Date }, config: Partial<VisualizationConfig>, ): Promise<ChartData> { const entries = await this.getEntriesInTimeRange(timeRange); // Extract unique projects const projects = [ ...new Set( entries .map((e) => e.data.projectPath || e.data.projectId || "Unknown") .filter((p) => p !== "Unknown"), ), ].slice(0, 10); // Limit to top 10 // Calculate correlation matrix const correlationMatrix: number[][] = []; for (const project1 of projects) { const row: number[] = []; for (const project2 of projects) { if (project1 === project2) { row.push(1.0); } else { const correlation = this.calculateProjectCorrelation( entries, project1, project2, ); row.push(correlation); } } correlationMatrix.push(row); } const heatmap: HeatmapVisualization = { data: correlationMatrix, labels: { x: projects, y: projects }, colorScale: { min: -1, max: 1, colors: ["#EF4444", "#F59E0B", "#F3F4F6", "#10B981", "#059669"], }, title: "Project Correlation Matrix", description: "Correlation matrix showing relationships between different projects based on memory patterns", }; return { type: "heatmap", title: "Project Correlations", description: "Visualization of correlations between different projects in the memory system", data: heatmap, config, metadata: { generated: new Date(), dataPoints: projects.length * projects.length, timeRange, filters: { type: "project_correlation" }, }, }; } /** * Generate custom visualization */ async generateCustomVisualization( type: ChartData["type"], query: { filters?: Record<string, any>; timeRange?: { start: Date; end: Date }; aggregation?: string; groupBy?: string; }, config?: Partial<VisualizationConfig>, ): Promise<ChartData> { const activeConfig = { ...this.defaultConfig, ...config }; const timeRange = query.timeRange || this.getDefaultTimeRange(); let entries = await this.getEntriesInTimeRange(timeRange); // Apply filters if (query.filters) { entries = this.applyFilters(entries, query.filters); } switch (type) { case "timeline": return this.generateTimelineVisualization(entries, query, activeConfig); case "scatter": return this.generateScatterPlot(entries, query, activeConfig); case "treemap": return this.generateTreemapVisualization(entries, query, activeConfig); case "sankey": return this.generateSankeyDiagram(entries, query, activeConfig); default: throw new Error(`Unsupported visualization type: ${type}`); } } /** * Export visualization to specified format */ async exportVisualization( chartData: ChartData, format: "svg" | "png" | "json" | "html" = "json", options?: { filename?: string; quality?: number; width?: number; height?: number; }, ): Promise<string | Buffer> { this.emit("export_started", { type: chartData.type, format }); try { switch (format) { case "json": return JSON.stringify(chartData, null, 2); case "html": return this.generateHTMLVisualization(chartData, options); case "svg": return this.generateSVGVisualization(chartData, options); case "png": // This would require a rendering library like Puppeteer throw new Error( "PNG export requires additional rendering capabilities", ); default: throw new Error(`Unsupported export format: ${format}`); } } catch (error) { this.emit("export_error", { error: error instanceof Error ? error.message : String(error), }); throw error; } } /** * Helper methods */ private async getEntriesInTimeRange(timeRange: { start: Date; end: Date; }): Promise<MemoryEntry[]> { const allEntries = await this.storage.getAll(); return allEntries.filter((entry) => { const entryDate = new Date(entry.timestamp); return entryDate >= timeRange.start && entryDate <= timeRange.end; }); } private getDefaultTimeRange(): { start: Date; end: Date } { const end = new Date(); const start = new Date(end.getTime() - 30 * 24 * 60 * 60 * 1000); // 30 days ago return { start, end }; } private getWeekKey(date: Date): string { const year = date.getFullYear(); const week = this.getWeekNumber(date); return `${year}-W${week.toString().padStart(2, "0")}`; } private getWeekNumber(date: Date): number { const d = new Date( Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()), ); const dayNum = d.getUTCDay() || 7; d.setUTCDate(d.getUTCDate() + 4 - dayNum); const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1)); return Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7); } private getColorForNodeType(type: string, colorScheme?: string[]): string { const colors = colorScheme || this.defaultConfig.colorScheme; const index = type.charCodeAt(0) % colors.length; return colors[index]; } private getColorForEdgeType(type: string, colorScheme?: string[]): string { const colors = colorScheme || this.defaultConfig.colorScheme; const typeColors: Record<string, string> = { similarity: colors[0], dependency: colors[1], temporal: colors[2], causal: colors[3], }; return typeColors[type] || colors[4]; } private darkenColor(color: string): string { // Simple color darkening - in production, use a proper color library if (color.startsWith("#")) { const hex = color.slice(1); const num = parseInt(hex, 16); const r = Math.max(0, (num >> 16) - 40); const g = Math.max(0, ((num >> 8) & 0x00ff) - 40); const b = Math.max(0, (num & 0x0000ff) - 40); return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, "0")}`; } return color; } private calculateProjectCorrelation( entries: MemoryEntry[], project1: string, project2: string, ): number { const entries1 = entries.filter( (e) => e.data.projectPath?.includes(project1) || e.data.projectId === project1, ); const entries2 = entries.filter( (e) => e.data.projectPath?.includes(project2) || e.data.projectId === project2, ); if (entries1.length === 0 || entries2.length === 0) return 0; // Simple correlation based on shared characteristics let sharedFeatures = 0; let totalFeatures = 0; // Compare languages const lang1 = new Set(entries1.map((e) => e.data.language).filter(Boolean)); const lang2 = new Set(entries2.map((e) => e.data.language).filter(Boolean)); const sharedLangs = new Set([...lang1].filter((l) => lang2.has(l))); sharedFeatures += sharedLangs.size; totalFeatures += new Set([...lang1, ...lang2]).size; // Compare frameworks const fw1 = new Set(entries1.map((e) => e.data.framework).filter(Boolean)); const fw2 = new Set(entries2.map((e) => e.data.framework).filter(Boolean)); const sharedFws = new Set([...fw1].filter((f) => fw2.has(f))); sharedFeatures += sharedFws.size; totalFeatures += new Set([...fw1, ...fw2]).size; return totalFeatures > 0 ? sharedFeatures / totalFeatures : 0; } private applyFilters( entries: MemoryEntry[], filters: Record<string, any>, ): MemoryEntry[] { return entries.filter((entry) => { for (const [key, value] of Object.entries(filters)) { switch (key) { case "type": if (Array.isArray(value) && !value.includes(entry.type)) return false; if (typeof value === "string" && entry.type !== value) return false; break; case "outcome": if (entry.data.outcome !== value) return false; break; case "language": if (entry.data.language !== value) return false; break; case "framework": if (entry.data.framework !== value) return false; break; case "project": if ( !entry.data.projectPath?.includes(value) && entry.data.projectId !== value ) { return false; } break; case "tags": if ( Array.isArray(value) && !value.some((tag) => entry.tags?.includes(tag)) ) { return false; } break; } } return true; }); } private async generateKeyInsights( entries: MemoryEntry[], timeRange: { start: Date; end: Date }, ): Promise<string[]> { const insights: string[] = []; // Activity insight const dailyAverage = entries.length / Math.max( 1, Math.ceil( (timeRange.end.getTime() - timeRange.start.getTime()) / (24 * 60 * 60 * 1000), ), ); insights.push(`Average ${dailyAverage.toFixed(1)} entries per day`); // Success rate insight const successful = entries.filter( (e) => e.data.outcome === "success" || e.data.success === true, ).length; const successRate = entries.length > 0 ? (successful / entries.length) * 100 : 0; insights.push(`${successRate.toFixed(1)}% success rate`); // Most common type const typeCounts = new Map<string, number>(); entries.forEach((e) => typeCounts.set(e.type, (typeCounts.get(e.type) || 0) + 1), ); const mostCommonType = Array.from(typeCounts.entries()).sort( ([, a], [, b]) => b - a, )[0]; if (mostCommonType) { insights.push( `Most common activity: ${mostCommonType[0]} (${mostCommonType[1]} entries)`, ); } // Growth trend const midpoint = new Date( (timeRange.start.getTime() + timeRange.end.getTime()) / 2, ); const firstHalf = entries.filter( (e) => new Date(e.timestamp) < midpoint, ).length; const secondHalf = entries.filter( (e) => new Date(e.timestamp) >= midpoint, ).length; if (firstHalf > 0) { const growthRate = ((secondHalf - firstHalf) / firstHalf) * 100; insights.push( `Activity ${growthRate >= 0 ? "increased" : "decreased"} by ${Math.abs( growthRate, ).toFixed(1)}%`, ); } return insights.slice(0, 5); // Return top 5 insights } private async calculateSystemHealthScore( entries: MemoryEntry[], ): Promise<number> { let score = 0; // Activity level (0-25 points) const recentEntries = entries.filter( (e) => new Date(e.timestamp) > new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), ); score += Math.min(25, recentEntries.length * 2); // Success rate (0-25 points) const successful = entries.filter( (e) => e.data.outcome === "success" || e.data.success === true, ).length; const successRate = entries.length > 0 ? successful / entries.length : 0; score += successRate * 25; // Diversity (0-25 points) const uniqueTypes = new Set(entries.map((e) => e.type)).size; score += Math.min(25, uniqueTypes * 3); // Consistency (0-25 points) if (entries.length >= 7) { const dailyActivities = new Map<string, number>(); entries.forEach((e) => { const day = e.timestamp.slice(0, 10); dailyActivities.set(day, (dailyActivities.get(day) || 0) + 1); }); const values = Array.from(dailyActivities.values()); const mean = values.reduce((sum, val) => sum + val, 0) / values.length; const variance = values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / values.length; const consistency = mean > 0 ? Math.max(0, 1 - Math.sqrt(variance) / mean) : 0; score += consistency * 25; } return Math.round(Math.min(100, score)); } private generateTimelineVisualization( entries: MemoryEntry[], query: any, config: VisualizationConfig, ): ChartData { const events = entries.map((entry) => ({ id: entry.id, timestamp: new Date(entry.timestamp), title: entry.type, description: entry.data.description || `${entry.type} entry`, type: entry.type, importance: entry.data.outcome === "success" ? 1 : 0.5, color: this.getColorForNodeType(entry.type, config.colorScheme), metadata: entry.data, })); const timelineData: TimelineVisualization = { events, timeRange: { start: new Date(Math.min(...events.map((e) => e.timestamp.getTime()))), end: new Date(Math.max(...events.map((e) => e.timestamp.getTime()))), }, granularity: "day", groupBy: query.groupBy, }; return { type: "timeline", title: "Memory Activity Timeline", description: "Chronological timeline of memory system activities", data: timelineData, config, metadata: { generated: new Date(), dataPoints: events.length, filters: query.filters, }, }; } private generateScatterPlot( entries: MemoryEntry[], query: any, config: VisualizationConfig, ): ChartData { // Create scatter plot data based on timestamp vs some metric const data = entries.map((entry) => ({ x: new Date(entry.timestamp).getTime(), y: entry.data.duration || entry.data.complexity || Math.random(), // Use available metric color: this.getColorForNodeType(entry.type, config.colorScheme), metadata: entry, })); return { type: "scatter", title: "Memory Activity Scatter Plot", description: "Scatter plot visualization of memory activities", data: { datasets: [ { label: "Activities", data: data, backgroundColor: data.map((d) => d.color), }, ], }, config, metadata: { generated: new Date(), dataPoints: data.length, filters: query.filters, }, }; } private generateTreemapVisualization( entries: MemoryEntry[], query: any, config: VisualizationConfig, ): ChartData { // Group entries by type and project for treemap const hierarchy = new Map<string, Map<string, number>>(); for (const entry of entries) { const type = entry.type; const project = entry.data.projectPath || entry.data.projectId || "Unknown"; if (!hierarchy.has(type)) { hierarchy.set(type, new Map()); } hierarchy .get(type)! .set(project, (hierarchy.get(type)!.get(project) || 0) + 1); } // Convert to treemap format const treemapData = Array.from(hierarchy.entries()).map( ([type, projects]) => ({ name: type, value: Array.from(projects.values()).reduce((sum, val) => sum + val, 0), children: Array.from(projects.entries()).map(([project, count]) => ({ name: project, value: count, })), }), ); return { type: "treemap", title: "Memory Type Hierarchy", description: "Hierarchical treemap of memory entries by type and project", data: treemapData, config, metadata: { generated: new Date(), dataPoints: entries.length, filters: query.filters, }, }; } private generateSankeyDiagram( entries: MemoryEntry[], query: any, config: VisualizationConfig, ): ChartData { // Create flow data from entry types to outcomes const flows = new Map<string, Map<string, number>>(); for (const entry of entries) { const source = entry.type; const target = entry.data.outcome || (entry.data.success ? "success" : "unknown"); if (!flows.has(source)) { flows.set(source, new Map()); } flows.get(source)!.set(target, (flows.get(source)!.get(target) || 0) + 1); } // Convert to Sankey format const nodes: string[] = []; const links: Array<{ source: number; target: number; value: number }> = []; // Collect all unique nodes const sources = Array.from(flows.keys()); const targets = new Set<string>(); flows.forEach((targetMap) => { targetMap.forEach((_, target) => targets.add(target)); }); nodes.push( ...sources, ...Array.from(targets).filter((t) => !sources.includes(t)), ); // Create links flows.forEach((targetMap, source) => { targetMap.forEach((value, target) => { const sourceIndex = nodes.indexOf(source); const targetIndex = nodes.indexOf(target); if (sourceIndex !== -1 && targetIndex !== -1) { links.push({ source: sourceIndex, target: targetIndex, value }); } }); }); return { type: "sankey", title: "Memory Flow Diagram", description: "Sankey diagram showing flow from memory types to outcomes", data: { nodes, links }, config, metadata: { generated: new Date(), dataPoints: links.length, filters: query.filters, }, }; } private generateHTMLVisualization( chartData: ChartData, _options?: any, ): string { // Generate basic HTML with embedded Chart.js or D3.js return ` <!DOCTYPE html> <html> <head> <title>${chartData.title}</title> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <style> body { font-family: Arial, sans-serif; margin: 20px; } .chart-container { width: 100%; height: 400px; } .description { margin-bottom: 20px; color: #666; } </style> </head> <body> <h1>${chartData.title}</h1> <p class="description">${chartData.description}</p> <div class="chart-container"> <canvas id="chart"></canvas> </div> <script> const ctx = document.getElementById('chart').getContext('2d'); new Chart(ctx, ${JSON.stringify(chartData.data)}); </script> </body> </html>`; } private generateSVGVisualization( chartData: ChartData, options?: any, ): string { // Generate basic SVG - in production, use a proper chart library const width = options?.width || 800; const height = options?.height || 600; return ` <svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg"> <rect width="100%" height="100%" fill="white"/> <text x="50%" y="30" text-anchor="middle" font-size="18" font-weight="bold"> ${chartData.title} </text> <text x="50%" y="50" text-anchor="middle" font-size="14" fill="#666"> ${chartData.description} </text> <!-- Chart data would be rendered here --> <text x="50%" y="${ height / 2 }" text-anchor="middle" font-size="12" fill="#999"> Chart visualization (${chartData.metadata.dataPoints} data points) </text> </svg>`; } }
- src/memory/visualization.ts:13-21 (schema)Type definitions for visualization configuration used in tool inputs and outputs.export interface VisualizationConfig { width: number; height: number; theme: "light" | "dark" | "auto"; colorScheme: string[]; interactive: boolean; exportFormat: "svg" | "png" | "json" | "html"; responsive: boolean; }
- src/memory/index.ts:56-61 (registration)Export of MemoryVisualizationSystem class and related types from visualization module.MemoryVisualizationSystem, type VisualizationConfig, type ChartData, type DashboardData, type NetworkVisualization, } from "./visualization.js";