UNPKG

soulforge-framework

Version:

A TypeScript framework for building digital beings with persistent memory, emotions, and identity. Create AI companions that grow, learn, and form genuine relationships.

346 lines (285 loc) • 11.1 kB
// Example: Persistence and Data Management // Shows how to save/load SoulForge entities for production use import { Soul, createCompanion } from '../src'; import * as fs from 'fs'; import * as path from 'path'; interface SavedSoul { id: string; identity: any; memories: any[]; personality: any; mood: any; relationships: any; lastActive: string; version: string; } class SoulDatabase { private dataDir: string; private souls: Map<string, Soul> = new Map(); constructor(dataDir: string = './soul-data') { this.dataDir = dataDir; this.ensureDataDirectory(); } private ensureDataDirectory() { if (!fs.existsSync(this.dataDir)) { fs.mkdirSync(this.dataDir, { recursive: true }); } } // Save a soul's state to disk saveSoul(soulId: string, soul: Soul): void { const status = soul.getStatus(); const savedSoul: SavedSoul = { id: soulId, identity: status.identity, memories: [], // In a real implementation, extract from memory system personality: status.personality, mood: status.mood, relationships: {}, // In a real implementation, extract relationships lastActive: new Date().toISOString(), version: '1.0' }; const filePath = path.join(this.dataDir, `${soulId}.json`); fs.writeFileSync(filePath, JSON.stringify(savedSoul, null, 2)); console.log(`šŸ’¾ Saved soul ${soulId} to ${filePath}`); } // Load a soul from disk loadSoul(soulId: string): Soul | null { const filePath = path.join(this.dataDir, `${soulId}.json`); if (!fs.existsSync(filePath)) { console.log(`āŒ Soul ${soulId} not found`); return null; } try { const data = fs.readFileSync(filePath, 'utf8'); const savedSoul: SavedSoul = JSON.parse(data); // Recreate the soul with saved data const soul = new Soul() .withIdentity(savedSoul.identity) .withPersonality(savedSoul.personality); // Restore memories (in a real implementation, this would be more sophisticated) // For demo purposes, we'll just indicate that memories are restored console.log(`šŸ“š Restored ${savedSoul.memories.length} memories for ${soulId}`); console.log(`āœ… Loaded soul ${soulId} (last active: ${savedSoul.lastActive})`); this.souls.set(soulId, soul); return soul; } catch (error) { console.error(`āŒ Error loading soul ${soulId}:`, error); return null; } } // List all saved souls listSavedSouls(): string[] { const files = fs.readdirSync(this.dataDir); return files .filter(file => file.endsWith('.json')) .map(file => file.replace('.json', '')); } // Delete a saved soul deleteSoul(soulId: string): boolean { const filePath = path.join(this.dataDir, `${soulId}.json`); if (fs.existsSync(filePath)) { fs.unlinkSync(filePath); this.souls.delete(soulId); console.log(`šŸ—‘ļø Deleted soul ${soulId}`); return true; } return false; } // Backup all souls backupAllSouls(backupDir: string): void { if (!fs.existsSync(backupDir)) { fs.mkdirSync(backupDir, { recursive: true }); } const souls = this.listSavedSouls(); const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); souls.forEach(soulId => { const sourcePath = path.join(this.dataDir, `${soulId}.json`); const backupPath = path.join(backupDir, `${soulId}_${timestamp}.json`); fs.copyFileSync(sourcePath, backupPath); }); console.log(`šŸ’¼ Backed up ${souls.length} souls to ${backupDir}`); } // Analytics on saved souls getSoulAnalytics(): any { const souls = this.listSavedSouls(); const analytics = { totalSouls: souls.length, soulTypes: {} as Record<string, number>, lastActivityDates: [] as string[], memoryDistribution: {} as Record<string, number> }; souls.forEach(soulId => { try { const filePath = path.join(this.dataDir, `${soulId}.json`); const data = JSON.parse(fs.readFileSync(filePath, 'utf8')) as SavedSoul; // Analyze soul types (based on role) const role = data.identity.role || 'Unknown'; analytics.soulTypes[role] = (analytics.soulTypes[role] || 0) + 1; // Collect activity dates analytics.lastActivityDates.push(data.lastActive); // Memory distribution const memoryCount = data.memories.length; const memoryRange = memoryCount < 10 ? '0-9' : memoryCount < 50 ? '10-49' : memoryCount < 100 ? '50-99' : '100+'; analytics.memoryDistribution[memoryRange] = (analytics.memoryDistribution[memoryRange] || 0) + 1; } catch (error) { console.error(`Error analyzing soul ${soulId}:`, error); } }); return analytics; } } // Demo function function runPersistenceDemo() { console.log('šŸ’¾ SOULFORGE PERSISTENCE DEMO\n'); const db = new SoulDatabase('./demo-souls'); // Create some demo souls console.log('=== Creating Demo Souls ==='); const assistant = createCompanion('Emma'); const storyteller = new Soul() .withIdentity({ name: 'Marcus', role: 'Storyteller', background: 'Ancient keeper of tales and wisdom', goals: ['Share stories', 'Inspire imagination', 'Preserve knowledge'], values: ['Creativity', 'Wisdom', 'Tradition'] }) .withPersonality({ bigFive: { openness: 95, conscientiousness: 70, extraversion: 80, agreeableness: 85, neuroticism: 20 }, mbti: 'ENFP' }); const teacher = new Soul() .withIdentity({ name: 'Dr. Chen', role: 'AI Teacher', background: 'PhD in Education, specializes in adaptive learning', goals: ['Help students learn', 'Adapt to learning styles', 'Build confidence'], values: ['Growth mindset', 'Patience', 'Encouragement'] }) .withPersonality({ bigFive: { openness: 85, conscientiousness: 95, extraversion: 60, agreeableness: 90, neuroticism: 15 }, mbti: 'ISFJ' }); // Simulate some conversations to build memories console.log('\n=== Building Memories ==='); assistant.respond('Can you help me organize my schedule?', 'user1', 'User'); assistant.respond('Thank you, that was very helpful!', 'user1', 'User'); storyteller.respond('Tell me a story about ancient magic', 'user2', 'Child'); storyteller.respond('That was amazing! Tell me another!', 'user2', 'Child'); storyteller.respond('I love dragons!', 'user2', 'Child'); teacher.respond('I\'m struggling with mathematics', 'student1', 'Student'); teacher.respond('Can you explain it differently?', 'student1', 'Student'); teacher.respond('Now I understand! Thank you!', 'student1', 'Student'); // Save all souls console.log('\n=== Saving Souls ==='); db.saveSoul('assistant-emma', assistant); db.saveSoul('storyteller-marcus', storyteller); db.saveSoul('teacher-chen', teacher); // List saved souls console.log('\n=== Saved Souls ==='); const savedSouls = db.listSavedSouls(); savedSouls.forEach(soulId => { console.log(`šŸ“ ${soulId}`); }); // Load a soul and interact with it console.log('\n=== Loading and Interacting ==='); const loadedTeacher = db.loadSoul('teacher-chen'); if (loadedTeacher) { const response = loadedTeacher.respond('I need help with physics now', 'student1', 'Student'); console.log(`šŸ‘Øā€šŸ« Dr. Chen: "${response.response}"`); console.log(`šŸ’­ Mood: ${response.mood}`); } // Show analytics console.log('\n=== Soul Analytics ==='); const analytics = db.getSoulAnalytics(); console.log(`šŸ“Š Total Souls: ${analytics.totalSouls}`); console.log('šŸ“ˆ Soul Types:', analytics.soulTypes); console.log('🧠 Memory Distribution:', analytics.memoryDistribution); // Backup souls console.log('\n=== Creating Backup ==='); db.backupAllSouls('./demo-backups'); // Demonstrate loading after "restart" console.log('\n=== Simulating Application Restart ==='); const newDb = new SoulDatabase('./demo-souls'); const restoredAssistant = newDb.loadSoul('assistant-emma'); if (restoredAssistant) { const response = restoredAssistant.respond('Remember me? I asked about scheduling earlier.', 'user1', 'User'); console.log(`šŸ¤– Emma: "${response.response}"`); console.log(`šŸ’­ Mood: ${response.mood}`); } console.log('\nšŸŽÆ This demonstrates:'); console.log('āœ… Persistent soul state across application restarts'); console.log('āœ… Memory and personality preservation'); console.log('āœ… Backup and restore capabilities'); console.log('āœ… Analytics on soul populations'); console.log('āœ… Production-ready data management'); } // Production example: Soul Manager for a chat application class ChatAppSoulManager { private db: SoulDatabase; private activeSouls: Map<string, Soul> = new Map(); constructor() { this.db = new SoulDatabase('./chat-app-souls'); this.loadActiveSouls(); } // Get or create a soul for a chat getSoul(chatId: string, soulType: string = 'companion'): Soul { if (this.activeSouls.has(chatId)) { return this.activeSouls.get(chatId)!; } // Try to load from disk first let soul = this.db.loadSoul(chatId); if (!soul) { // Create new soul based on type soul = this.createSoulByType(soulType, chatId); this.db.saveSoul(chatId, soul); } this.activeSouls.set(chatId, soul); return soul; } private createSoulByType(type: string, id: string): Soul { switch (type) { case 'companion': return createCompanion(`Companion-${id}`); case 'teacher': return new Soul() .withIdentity({ name: `Teacher-${id}`, role: 'AI Tutor', background: 'Educational AI specialized in personalized learning' }) .withPersonality({ mbti: 'ISFJ' }); default: return createCompanion(`AI-${id}`); } } // Periodic save of all active souls saveAllActive(): void { this.activeSouls.forEach((soul, chatId) => { this.db.saveSoul(chatId, soul); }); console.log(`šŸ’¾ Saved ${this.activeSouls.size} active souls`); } private loadActiveSouls(): void { // In a real app, you might load recently active souls console.log('šŸ”„ Soul manager initialized'); } } // Run demo if (require.main === module) { runPersistenceDemo(); } export { SoulDatabase, ChatAppSoulManager };