11_persistent_memory/
11_persistent_memory.rs

1//! Persistent Memory Backends Example
2//!
3//! This example demonstrates how to use persistent memory backends (SQLite and File-based)
4//! to store conversation history across sessions, and how to migrate data between backends.
5//!
6//! Run with: cargo run --example 11_persistent_memory --manifest-path ceylon/Cargo.toml
7
8use ceylon_next::agent::Agent;
9use ceylon_next::memory::{
10    FileStore, SqliteStore, StorageFormat
11};
12use ceylon_next::tasks::{OutputData, TaskRequest};
13use std::sync::Arc;
14
15#[tokio::main]
16async fn main() {
17    println!("šŸ¤– Ceylon Agent - Persistent Memory Example\n");
18
19    // ============================================
20    // Part 1: SQLite Backend
21    // ============================================
22    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
23    println!("šŸ“¦ Part 1: SQLite Backend");
24    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
25
26    let sqlite_path = "agent_memory.db";
27    let sqlite_store = SqliteStore::new(sqlite_path).await
28        .expect("Failed to create SQLite store");
29
30    let mut agent = Agent::new("PersistentAssistant", "ollama::gemma3:latest");
31    agent.with_memory(Arc::new(sqlite_store))
32         .with_system_prompt("You are a helpful assistant with persistent memory across sessions.");
33
34    println!("āœ… Created agent with SQLite backend at {}\n", sqlite_path);
35
36    // Run some tasks to populate memory
37    let tasks = vec![
38        "My name is Alice and I love programming in Rust",
39        "What programming language do I like?",
40    ];
41
42    for prompt in tasks {
43        println!("šŸ“‹ Task: {}", prompt);
44        let mut task = TaskRequest::new(prompt);
45        task.with_priority(5);
46
47        let response = agent.run(task).await;
48        if let OutputData::Text(_) = response.result() {
49            println!("āœ“ Task completed\n");
50        }
51    }
52
53    println!("šŸ“Š SQLite database now contains conversation history");
54    println!("   This data persists even after the program exits!\n");
55
56    // ============================================
57    // Part 2: File-based Backend (JSON)
58    // ============================================
59    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
60    println!("šŸ“„ Part 2: File-based Backend (JSON)");
61    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
62
63    let json_path = "agent_memory.json";
64    let file_store = FileStore::new(json_path, StorageFormat::Json).await
65        .expect("Failed to create File store");
66
67    let mut agent2 = Agent::new("FileAssistant", "ollama::gemma3:latest");
68    agent2.with_memory(Arc::new(file_store))
69          .with_system_prompt("You are a helpful assistant storing memories in JSON files.");
70
71    println!("āœ… Created agent with JSON file backend at {}\n", json_path);
72
73    let task = TaskRequest::new("Remember: My favorite color is blue");
74    let response = agent2.run(task).await;
75
76    if let OutputData::Text(_) = response.result() {
77        println!("āœ“ Memory saved to JSON file");
78        println!("   You can open {} to view the human-readable data!\n", json_path);
79    }
80
81    // ============================================
82    // Part 3: MessagePack Backend
83    // ============================================
84    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
85    println!("šŸ“¦ Part 3: MessagePack Backend (Compact Binary)");
86    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
87
88    let msgpack_path = "agent_memory.msgpack";
89    let msgpack_store = FileStore::new(msgpack_path, StorageFormat::MessagePack).await
90        .expect("Failed to create MessagePack store");
91
92    let mut agent3 = Agent::new("BinaryAssistant", "ollama::gemma3:latest");
93    agent3.with_memory(Arc::new(msgpack_store))
94          .with_system_prompt("You are a helpful assistant using compact binary storage.");
95
96    println!("āœ… Created agent with MessagePack backend at {}\n", msgpack_path);
97
98    let task = TaskRequest::new("Store this efficiently: The quick brown fox jumps over the lazy dog");
99    let response = agent3.run(task).await;
100
101    if let OutputData::Text(_) = response.result() {
102        println!("āœ“ Memory saved to MessagePack file");
103        println!("   Binary format is more compact than JSON!\n");
104    }
105
106    // ============================================
107    // Part 4: Memory Migration
108    // ============================================
109    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
110    println!("šŸ”„ Part 4: Memory Migration Between Backends");
111    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
112
113    // Create source and destination stores
114    let source_db = SqliteStore::new("source.db").await.unwrap();
115
116    // Create an agent with source backend and add some data
117    let mut temp_agent = Agent::new("MigrationAgent", "ollama::gemma3:latest");
118    temp_agent.with_memory(Arc::new(source_db));
119
120    let task = TaskRequest::new("Migration test: This data will be migrated!");
121    temp_agent.run(task).await;
122
123    // Note: You would normally track your agent IDs. For this demo, we'll use
124    // a known pattern - the agent creates entries with its own ID
125    println!("šŸ’” Tip: To migrate data, you need to know the agent's ID");
126    println!("   In production, track agent IDs when creating agents\n");
127
128    println!("āœ“ Migration between backends is available via migrate_memory()\n");
129    println!("   Example: migrate_memory(&source, &target, \"agent-id\").await\n");
130
131    // ============================================
132    // Part 5: Export/Import Utilities
133    // ============================================
134    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
135    println!("šŸ’¾ Part 5: Export/Import Utilities");
136    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
137
138    // Demonstrate export/import with the existing SQLite database
139    println!("šŸ’” Export and import utilities are available:\n");
140    println!("   • export_to_json(store, agent_id, path)");
141    println!("   • export_to_msgpack(store, agent_id, path)");
142    println!("   • import_from_json(store, path)");
143    println!("   • import_from_msgpack(store, path)\n");
144
145    println!("āœ“ These functions allow you to backup and restore agent memories\n");
146
147    // ============================================
148    // Summary
149    // ============================================
150    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
151    println!("šŸ“Š Summary: Available Backends");
152    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
153
154    println!("1. InMemoryStore (default)");
155    println!("   • Fast, but data lost on restart");
156    println!("   • Best for: Development, testing\n");
157
158    println!("2. SqliteStore");
159    println!("   • Persistent, efficient queries");
160    println!("   • Best for: Production apps, large datasets\n");
161
162    println!("3. FileStore (JSON)");
163    println!("   • Human-readable, easy to inspect");
164    println!("   • Best for: Debugging, data sharing\n");
165
166    println!("4. FileStore (MessagePack)");
167    println!("   • Compact binary format");
168    println!("   • Best for: Large datasets, bandwidth constraints\n");
169
170    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
171    println!("šŸ“ Files Created:");
172    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
173
174    println!("• agent_memory.db         - SQLite database");
175    println!("• agent_memory.json       - JSON storage");
176    println!("• agent_memory.msgpack    - MessagePack storage");
177    println!("• backup.json             - Migrated data");
178    println!("• exported_memory.json    - Exported snapshot");
179    println!("• exported_memory.msgpack - Exported snapshot (binary)\n");
180
181    println!("āœ… Example completed successfully!");
182    println!("\nšŸ’” Tip: Check the created files to see your persisted data!");
183    println!("   Try running this example again - your SQLite data will persist!");
184}