Virtual-Kimi / kimi-js /kimi-memory-database-optimization.js
VirtualKimi's picture
Upload 34 files
bcbb712 verified
// ===== 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 };