2024-11-07 10:25:49 -06:00
|
|
|
import { writeFileSync, readFileSync, mkdirSync, existsSync } from 'fs';
|
2024-03-05 12:05:46 -08:00
|
|
|
import { NPCData } from './npc/data.js';
|
2024-10-10 22:15:55 -05:00
|
|
|
import settings from '../../settings.js';
|
2023-11-12 14:53:23 -08:00
|
|
|
|
|
|
|
|
2023-11-07 09:44:56 -06:00
|
|
|
export class History {
|
2023-12-21 20:42:01 -07:00
|
|
|
constructor(agent) {
|
2024-02-25 14:13:32 -06:00
|
|
|
this.agent = agent;
|
2023-11-12 14:53:23 -08:00
|
|
|
this.name = agent.name;
|
2024-02-25 14:13:32 -06:00
|
|
|
this.memory_fp = `./bots/${this.name}/memory.json`;
|
2024-10-10 22:15:55 -05:00
|
|
|
this.full_history_fp = undefined;
|
|
|
|
|
|
|
|
mkdirSync(`./bots/${this.name}/histories`, { recursive: true });
|
|
|
|
|
2023-11-15 13:18:38 -08:00
|
|
|
this.turns = [];
|
2023-11-12 14:53:23 -08:00
|
|
|
|
2024-10-10 22:15:55 -05:00
|
|
|
// Natural language memory as a summary of recent messages + previous memory
|
2023-11-12 14:53:23 -08:00
|
|
|
this.memory = '';
|
|
|
|
|
2024-10-10 22:15:55 -05:00
|
|
|
// Maximum number of messages to keep in context before saving chunk to memory
|
|
|
|
this.max_messages = settings.max_messages;
|
|
|
|
|
|
|
|
// Number of messages to remove from current history and save into memory
|
|
|
|
this.summary_chunk_size = 5;
|
|
|
|
// chunking reduces expensive calls to promptMemSaving and appendFullHistory
|
2024-11-18 23:02:37 -06:00
|
|
|
// and improves the quality of the memory summary
|
2023-11-07 09:44:56 -06:00
|
|
|
}
|
2023-08-15 23:39:02 -07:00
|
|
|
|
2024-02-25 14:13:32 -06:00
|
|
|
getHistory() { // expects an Examples object
|
|
|
|
return JSON.parse(JSON.stringify(this.turns));
|
2023-11-13 20:33:34 -08:00
|
|
|
}
|
2023-11-12 14:53:23 -08:00
|
|
|
|
2024-10-10 22:15:55 -05:00
|
|
|
async summarizeMemories(turns) {
|
2024-02-25 14:13:32 -06:00
|
|
|
console.log("Storing memories...");
|
2024-10-10 22:15:55 -05:00
|
|
|
this.memory = await this.agent.prompter.promptMemSaving(turns);
|
2024-10-11 14:49:40 -05:00
|
|
|
|
|
|
|
if (this.memory.length > 500) {
|
|
|
|
this.memory = this.memory.slice(0, 500);
|
|
|
|
this.memory += '...(Memory truncated to 500 chars. Compress it more next time)';
|
|
|
|
}
|
|
|
|
|
2024-02-25 14:13:32 -06:00
|
|
|
console.log("Memory updated to: ", this.memory);
|
2023-11-15 13:18:38 -08:00
|
|
|
}
|
|
|
|
|
2025-01-23 12:33:21 -08:00
|
|
|
async appendFullHistory(to_store) {
|
2024-10-10 22:15:55 -05:00
|
|
|
if (this.full_history_fp === undefined) {
|
|
|
|
const string_timestamp = new Date().toLocaleString().replace(/[/:]/g, '-').replace(/ /g, '').replace(/,/g, '_');
|
|
|
|
this.full_history_fp = `./bots/${this.name}/histories/${string_timestamp}.json`;
|
|
|
|
writeFileSync(this.full_history_fp, '[]', 'utf8');
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
const data = readFileSync(this.full_history_fp, 'utf8');
|
|
|
|
let full_history = JSON.parse(data);
|
|
|
|
full_history.push(...to_store);
|
|
|
|
writeFileSync(this.full_history_fp, JSON.stringify(full_history, null, 4), 'utf8');
|
|
|
|
} catch (err) {
|
|
|
|
console.error(`Error reading ${this.name}'s full history file: ${err.message}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-12 14:53:23 -08:00
|
|
|
async add(name, content) {
|
2023-11-07 09:44:56 -06:00
|
|
|
let role = 'assistant';
|
2023-11-12 13:57:22 -06:00
|
|
|
if (name === 'system') {
|
|
|
|
role = 'system';
|
|
|
|
}
|
2023-11-13 20:33:34 -08:00
|
|
|
else if (name !== this.name) {
|
2023-11-07 09:44:56 -06:00
|
|
|
role = 'user';
|
2023-11-07 12:00:55 -06:00
|
|
|
content = `${name}: ${content}`;
|
2023-08-17 00:00:57 -07:00
|
|
|
}
|
2023-11-07 09:44:56 -06:00
|
|
|
this.turns.push({role, content});
|
2023-11-12 14:53:23 -08:00
|
|
|
|
|
|
|
if (this.turns.length >= this.max_messages) {
|
2024-10-10 22:15:55 -05:00
|
|
|
let chunk = this.turns.splice(0, this.summary_chunk_size);
|
2024-10-11 14:49:40 -05:00
|
|
|
while (this.turns.length > 0 && this.turns[0].role === 'assistant')
|
|
|
|
chunk.push(this.turns.shift()); // remove until turns starts with system/user message
|
|
|
|
|
2024-10-10 22:15:55 -05:00
|
|
|
await this.summarizeMemories(chunk);
|
2025-01-23 12:33:21 -08:00
|
|
|
await this.appendFullHistory(chunk);
|
2023-11-12 14:53:23 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-07 10:25:49 -06:00
|
|
|
async save() {
|
|
|
|
try {
|
|
|
|
const data = {
|
|
|
|
memory: this.memory,
|
|
|
|
turns: this.turns,
|
2025-02-20 17:17:21 -06:00
|
|
|
self_prompting_state: this.agent.self_prompter.state,
|
|
|
|
self_prompt: this.agent.self_prompter.isStopped() ? null : this.agent.self_prompter.prompt,
|
2024-11-18 23:02:37 -06:00
|
|
|
last_sender: this.agent.last_sender
|
2024-11-07 10:25:49 -06:00
|
|
|
};
|
|
|
|
writeFileSync(this.memory_fp, JSON.stringify(data, null, 2));
|
|
|
|
console.log('Saved memory to:', this.memory_fp);
|
|
|
|
} catch (error) {
|
|
|
|
console.error('Failed to save history:', error);
|
|
|
|
throw error;
|
2024-05-08 23:59:44 -05:00
|
|
|
}
|
2023-11-12 14:53:23 -08:00
|
|
|
}
|
|
|
|
|
2024-02-25 14:13:32 -06:00
|
|
|
load() {
|
2023-11-12 14:53:23 -08:00
|
|
|
try {
|
2024-11-07 10:25:49 -06:00
|
|
|
if (!existsSync(this.memory_fp)) {
|
|
|
|
console.log('No memory file found.');
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
const data = JSON.parse(readFileSync(this.memory_fp, 'utf8'));
|
|
|
|
this.memory = data.memory || '';
|
|
|
|
this.turns = data.turns || [];
|
|
|
|
console.log('Loaded memory:', this.memory);
|
|
|
|
return data;
|
|
|
|
} catch (error) {
|
|
|
|
console.error('Failed to load history:', error);
|
|
|
|
throw error;
|
2023-11-12 14:53:23 -08:00
|
|
|
}
|
2023-08-17 00:00:57 -07:00
|
|
|
}
|
2024-01-27 19:16:54 -06:00
|
|
|
|
|
|
|
clear() {
|
|
|
|
this.turns = [];
|
|
|
|
this.memory = '';
|
|
|
|
}
|
2023-11-07 09:44:56 -06:00
|
|
|
}
|