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
text/typescript
// 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 };