// ===== KIMI MEMORY DATABASE OPTIMIZATION SUGGESTIONS ===== /** * Performance Optimization Guide for Kimi Memory System * * This file contains recommendations for database schema optimizations * to improve query performance in the memory system. */ // RECOMMENDED DEXIE INDEX CONFIGURATION // Add these indexes to your database schema for optimal performance const RECOMMENDED_MEMORY_INDEXES = { // Current schema (for reference) current: "++id, character, category, type, timestamp, isActive, confidence, importance, keywords", // OPTIMIZED schema with composite indexes optimized: [ "++id", // Primary key (auto-increment) // Single field indexes (existing) "character", "category", "type", "timestamp", "isActive", "confidence", "importance", "*keywords", // Multi-entry index for keyword array // COMPOSITE INDEXES for frequent query patterns "[character+isActive]", // Filter by character and active status "[character+category]", // Get memories by character and category "[character+category+isActive]", // Most common query pattern "[character+timestamp]", // Chronological queries by character "[isActive+importance]", // Get active memories by importance "[isActive+timestamp]", // Recent active memories "[character+type+isActive]", // Filter by character, type, and status // Advanced composite indexes for complex queries "[character+isActive+importance]", // Prioritized active memories by character "[character+category+timestamp]" // Category-specific chronological queries ] }; // QUERY OPTIMIZATION EXAMPLES const OPTIMIZED_QUERY_PATTERNS = { // BEFORE: Multiple filter operations getAllMemoriesOld: ` db.memories .where("character").equals(character) .filter(m => m.isActive !== false) .reverse() .sortBy("timestamp") `, // AFTER: Use composite index getAllMemoriesOptimized: ` db.memories .where("[character+isActive]").equals([character, true]) .reverse() .sortBy("timestamp") `, // BEFORE: Filter after retrieval getMemoriesByCategoryOld: ` db.memories .where("[character+category]").equals([character, category]) .and(m => m.isActive) `, // AFTER: Direct composite index getMemoriesByCategoryOptimized: ` db.memories .where("[character+category+isActive]").equals([character, category, true]) `, // NEW: Efficient importance-based queries getTopMemoriesByImportance: ` db.memories .where("[character+isActive+importance]") .between([character, true, 0.8], [character, true, 1.0]) .reverse() .limit(10) ` }; // PERFORMANCE MONITORING UTILITIES class MemoryDatabaseProfiler { constructor() { this.queryTimes = new Map(); this.queryCount = new Map(); } // Wrap database operations to measure performance async profileQuery(queryName, queryFn) { const start = performance.now(); const result = await queryFn(); const duration = performance.now() - start; // Update statistics if (!this.queryTimes.has(queryName)) { this.queryTimes.set(queryName, []); this.queryCount.set(queryName, 0); } this.queryTimes.get(queryName).push(duration); this.queryCount.set(queryName, this.queryCount.get(queryName) + 1); // Log slow queries if (duration > 50) { // 50ms threshold console.warn(`๐ŸŒ Slow query detected: ${queryName} took ${duration.toFixed(2)}ms`); } return result; } // Get performance statistics getStats() { const stats = {}; for (const [queryName, times] of this.queryTimes.entries()) { const count = this.queryCount.get(queryName); const avgTime = times.reduce((sum, time) => sum + time, 0) / times.length; const maxTime = Math.max(...times); const minTime = Math.min(...times); stats[queryName] = { count, avgTime: Math.round(avgTime * 100) / 100, maxTime: Math.round(maxTime * 100) / 100, minTime: Math.round(minTime * 100) / 100, totalTime: Math.round(times.reduce((sum, time) => sum + time, 0) * 100) / 100 }; } return stats; } // Reset statistics reset() { this.queryTimes.clear(); this.queryCount.clear(); } } // MEMORY USAGE OPTIMIZATION const MEMORY_CLEANUP_STRATEGIES = { // Strategy 1: Batch cleanup operations batchCleanup: async (db, maxBatchSize = 100) => { const oldMemories = await db.memories.where("isActive").equals(false).limit(maxBatchSize).toArray(); if (oldMemories.length > 0) { await db.memories.bulkDelete(oldMemories.map(m => m.id)); console.log(`๐Ÿงน Batch deleted ${oldMemories.length} inactive memories`); } }, // Strategy 2: Incremental keyword cleanup cleanupKeywords: async db => { const memoriesWithEmptyKeywords = await db.memories .filter(m => !m.keywords || m.keywords.length === 0) .limit(50) .toArray(); for (const memory of memoriesWithEmptyKeywords) { const keywords = deriveKeywords(memory.content || ""); await db.memories.update(memory.id, { keywords }); } }, // Strategy 3: Compress old memories compressOldMemories: async (db, daysThreshold = 90) => { const cutoff = new Date(); cutoff.setDate(cutoff.getDate() - daysThreshold); const oldMemories = await db.memories .where("timestamp") .below(cutoff) .and(m => m.isActive && !m.compressed) .limit(20) .toArray(); for (const memory of oldMemories) { // Compress by removing redundant fields and shortening content const compressed = { compressed: true, originalLength: memory.content?.length || 0, content: memory.content?.substring(0, 100) + "...", // Remove non-essential fields sourceText: undefined, tags: memory.tags?.slice(0, 3) // Keep only top 3 tags }; await db.memories.update(memory.id, compressed); } } }; // EXPORT UTILITIES if (typeof window !== "undefined") { window.KIMI_MEMORY_DB_OPTIMIZATION = { RECOMMENDED_MEMORY_INDEXES, OPTIMIZED_QUERY_PATTERNS, MemoryDatabaseProfiler, MEMORY_CLEANUP_STRATEGIES }; } export { RECOMMENDED_MEMORY_INDEXES, OPTIMIZED_QUERY_PATTERNS, MemoryDatabaseProfiler, MEMORY_CLEANUP_STRATEGIES };