@aituber-onair/core
Version:
Core library for AITuber OnAir providing voice synthesis and chat processing
209 lines • 7.65 kB
JavaScript
import { AITuberOnAirCoreEvent } from './AITuberOnAirCore';
import { EventEmitter } from './EventEmitter';
/**
* Manager for managing chat history and summarization
*/
export class MemoryManager extends EventEmitter {
/**
* Constructor
* @param options Settings options
* @param summarizer Summarizer implementation (if omitted, summarization is disabled)
* @param storage Storage implementation for persistence (optional)
*/
constructor(options, summarizer, storage) {
super();
this.memories = [];
this.options = {
enableSummarization: options.enableSummarization,
shortTermDuration: options.shortTermDuration || 60 * 1000, // Default 1 minute
midTermDuration: options.midTermDuration || 4 * 60 * 1000, // Default 4 minutes
longTermDuration: options.longTermDuration || 9 * 60 * 1000, // Default 9 minutes
maxMessagesBeforeSummarization: options.maxMessagesBeforeSummarization || 20,
maxSummaryLength: options.maxSummaryLength || 256,
memoryRetentionPeriod: options.memoryRetentionPeriod || 60 * 60 * 1000, // Default 1 hour
summaryPromptTemplate: options.summaryPromptTemplate,
};
this.summarizer = summarizer;
this.storage = storage;
// Load memories from storage if available
this.loadFromStorage();
}
/**
* Load memories from storage
*/
async loadFromStorage() {
if (!this.storage)
return;
try {
const loadedMemories = await this.storage.load();
if (loadedMemories && loadedMemories.length > 0) {
this.memories = loadedMemories;
this.emit(AITuberOnAirCoreEvent.MEMORY_LOADED, loadedMemories);
}
}
catch (error) {
console.error('Error loading memories from storage:', error);
this.emit(AITuberOnAirCoreEvent.ERROR, error);
}
}
/**
* Save memories to storage
*/
async saveToStorage() {
if (!this.storage)
return;
try {
await this.storage.save(this.memories);
this.emit(AITuberOnAirCoreEvent.MEMORY_SAVED, this.memories);
}
catch (error) {
console.error('Error saving memories to storage:', error);
this.emit(AITuberOnAirCoreEvent.ERROR, error);
}
}
/**
* Import memory records (used for restoring from external storage)
* @param records Memory records to import
*/
importMemoryRecords(records) {
if (!records || records.length === 0)
return;
// Filter out any records with invalid types
const validRecords = records.filter((record) => ['short', 'mid', 'long'].includes(record.type));
// Replace existing memories of same types
const existingTypes = new Set(validRecords.map((r) => r.type));
this.memories = [
...this.memories.filter((m) => !existingTypes.has(m.type)),
...validRecords,
];
this.emit('memoriesImported', validRecords);
// Save to storage if available
this.saveToStorage();
}
/**
* Summarize chat log and save as memory
* @param chatLog Chat log
* @param chatStartTime Chat start time
*/
async createMemoryIfNeeded(chatLog, chatStartTime) {
if (!this.options.enableSummarization || !this.summarizer) {
return;
}
const now = Date.now();
const elapsedTime = now - chatStartTime;
try {
// Create short-term memory (1 minute passed)
if (elapsedTime >= this.options.shortTermDuration) {
await this.createMemory('short', chatLog, now);
}
// Create mid-term memory (4 minutes passed)
if (elapsedTime >= this.options.midTermDuration) {
await this.createMemory('mid', chatLog, now);
}
// Create long-term memory (9 minutes passed)
if (elapsedTime >= this.options.longTermDuration) {
await this.createMemory('long', chatLog, now);
}
}
catch (error) {
console.error('Error creating memory:', error);
this.emit('error', error);
}
}
/**
* Create memory of specified type
* @param type Memory type
* @param chatLog Chat log
* @param timestamp Timestamp
*/
async createMemory(type, chatLog, timestamp) {
if (!this.summarizer)
return;
try {
// Delete old memories of the same type
this.memories = this.memories.filter((mem) => mem.type !== type);
// Summarize chat log
const summary = await this.summarizer.summarize(chatLog, this.options.maxSummaryLength);
// Add new memory
const memoryRecord = {
type,
summary,
timestamp,
};
this.memories.push(memoryRecord);
this.emit(AITuberOnAirCoreEvent.MEMORY_CREATED, memoryRecord);
// Save to storage if available
await this.saveToStorage();
}
catch (error) {
console.error(`Error creating ${type} memory:`, error);
this.emit('error', error);
}
}
/**
* Get all memories
*/
getAllMemories() {
return [...this.memories];
}
/**
* Get memory of specified type
* @param type Memory type
*/
getMemoryByType(type) {
return this.memories.find((mem) => mem.type === type);
}
/**
* Generate memory text for AI prompt
*/
getMemoryForPrompt() {
const memoryParts = [];
const shortMemory = this.getMemoryByType('short');
if (shortMemory) {
memoryParts.push(`[Short-term memory: 1min]\n${shortMemory.summary}`);
}
const midMemory = this.getMemoryByType('mid');
if (midMemory) {
memoryParts.push(`[Mid-term memory: 4min]\n${midMemory.summary}`);
}
const longMemory = this.getMemoryByType('long');
if (longMemory) {
memoryParts.push(`[Long-term memory: 9min]\n${longMemory.summary}`);
}
return memoryParts.join('\n\n');
}
/**
* Clean up old memories
*/
async cleanupOldMemories() {
const now = Date.now();
const retentionPeriod = this.options.memoryRetentionPeriod || 60 * 60 * 1000; // Default 1 hour
const oldMemories = this.memories.filter((mem) => now - mem.timestamp > retentionPeriod);
if (oldMemories.length > 0) {
this.memories = this.memories.filter((mem) => now - mem.timestamp <= retentionPeriod);
this.emit(AITuberOnAirCoreEvent.MEMORY_REMOVED, oldMemories);
// Save to storage if available
await this.saveToStorage();
}
}
/**
* Clear all memories
*/
async clearAllMemories() {
const oldMemories = [...this.memories];
this.memories = [];
this.emit(AITuberOnAirCoreEvent.MEMORY_REMOVED, oldMemories);
// Clear storage if available
if (this.storage) {
try {
await this.storage.clear();
this.emit(AITuberOnAirCoreEvent.STORAGE_CLEARED);
}
catch (error) {
console.error('Error clearing storage:', error);
this.emit(AITuberOnAirCoreEvent.ERROR, error);
}
}
}
}
//# sourceMappingURL=MemoryManager.js.map