File size: 4,896 Bytes
e706de2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
import fs from 'fs/promises';
import path from 'path';
import {fileURLToPath} from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
export class MemoryManager {
constructor(memoryFileName = './memory.json') {
this.memoryFilePath = path.resolve(__dirname, memoryFileName);
}
async loadMemories() {
try {
const data = await fs.readFile(this.memoryFilePath, 'utf-8');
const json = JSON.parse(data);
// 🔧 Migrate old schema if needed
if (!json.memories) {
const upgraded = {memories: [], conversationHistory: []};
if (Array.isArray(json.facts)) {
for (const f of json.facts) {
upgraded.memories.push({
type: 'fact',
key: this.extractKey(f.content),
value: this.extractValue(f.content),
source: 'migration',
timestamp: f.timestamp || new Date().toISOString()
});
}
}
if (json.preferences && typeof json.preferences === 'object') {
for (const [key, val] of Object.entries(json.preferences)) {
upgraded.memories.push({
type: 'preference',
key,
value: this.extractValue(val),
source: 'migration',
timestamp: new Date().toISOString()
});
}
}
await this.saveMemories(upgraded);
return upgraded;
}
if (!Array.isArray(json.memories)) json.memories = [];
if (!Array.isArray(json.conversationHistory)) json.conversationHistory = [];
return json;
} catch {
return {memories: [], conversationHistory: []};
}
}
async saveMemories(memories) {
await fs.writeFile(this.memoryFilePath, JSON.stringify(memories, null, 2));
}
// Add or update memory without duplicates
async addMemory({type, key, value, source = 'user'}) {
const data = await this.loadMemories();
// Normalize for comparison
const normType = type.trim().toLowerCase();
const normKey = key.trim().toLowerCase();
const normValue = value.trim();
// Check if same key+type already exists
const existingIndex = data.memories.findIndex(
m => m.type === normType && m.key.toLowerCase() === normKey
);
if (existingIndex >= 0) {
const existing = data.memories[existingIndex];
// Update value if changed
if (existing.value !== normValue) {
existing.value = normValue;
existing.timestamp = new Date().toISOString();
existing.source = source;
console.log(`Updated memory: ${normKey} → ${normValue}`);
} else {
console.log(`Skipped duplicate memory: ${normKey}`);
}
} else {
// Add new memory
data.memories.push({
type: normType,
key: normKey,
value: normValue,
source,
timestamp: new Date().toISOString()
});
console.log(`Added memory: ${normKey} = ${normValue}`);
}
await this.saveMemories(data);
}
async getMemorySummary() {
const data = await this.loadMemories();
const facts = Array.isArray(data.memories)
? data.memories.filter(m => m.type === 'fact')
: [];
const prefs = Array.isArray(data.memories)
? data.memories.filter(m => m.type === 'preference')
: [];
let summary = "\n=== LONG-TERM MEMORY ===\n";
if (facts.length > 0) {
summary += "\nKnown Facts:\n";
for (const f of facts) summary += `- ${f.key}: ${f.value}\n`;
}
if (prefs.length > 0) {
summary += "\nUser Preferences:\n";
for (const p of prefs) summary += `- ${p.key}: ${p.value}\n`;
}
return summary;
}
extractKey(content) {
if (typeof content !== 'string') return 'unknown';
const [key] = content.split(':').map(s => s.trim());
return key || 'unknown';
}
extractValue(content) {
if (typeof content !== 'string') return '';
const parts = content.split(':').map(s => s.trim());
return parts.length > 1 ? parts.slice(1).join(':') : content;
}
} |