Skip to main content

traitclaw_core/traits/
memory.rs

1//! Memory trait for agent state persistence.
2//!
3//! The [`Memory`] trait provides a 3-layer memory system:
4//! - **Conversation**: Short-term message history
5//! - **Working**: Task-specific key-value context
6//! - **Long-term**: Semantic recall across sessions
7
8use async_trait::async_trait;
9use serde_json::Value;
10
11use crate::types::message::Message;
12use crate::Result;
13
14/// A stored memory entry for long-term recall.
15///
16/// This struct is `#[non_exhaustive]` — new fields (e.g., `embedding`, `score`)
17/// may be added in future releases without breaking changes.
18#[derive(Debug, Clone)]
19#[non_exhaustive]
20pub struct MemoryEntry {
21    /// Unique identifier for this entry.
22    pub id: String,
23    /// The content of the memory.
24    pub content: String,
25    /// Optional metadata associated with this entry.
26    pub metadata: Option<Value>,
27    /// Unix timestamp (seconds) when this entry was created.
28    pub created_at: u64,
29}
30
31impl MemoryEntry {
32    /// Create a new `MemoryEntry` with `created_at` set to the current system time.
33    ///
34    /// Falls back to `0` if the system clock is not available (e.g., in WASM).
35    #[must_use]
36    pub fn now(id: impl Into<String>, content: impl Into<String>) -> Self {
37        let created_at = std::time::SystemTime::now()
38            .duration_since(std::time::UNIX_EPOCH)
39            .map(|d| d.as_secs())
40            .unwrap_or(0);
41        Self {
42            id: id.into(),
43            content: content.into(),
44            metadata: None,
45            created_at,
46        }
47    }
48}
49
50/// Trait for the 3-layer memory system.
51///
52/// Provides conversation history, working memory, and long-term recall.
53/// Implement this trait for custom storage backends (`SQLite`, `PostgreSQL`, Redis, etc.).
54#[async_trait]
55pub trait Memory: Send + Sync + 'static {
56    // === Conversation Memory (short-term) ===
57
58    /// Get conversation messages for a session.
59    async fn messages(&self, session_id: &str) -> Result<Vec<Message>>;
60
61    /// Append a message to the conversation history.
62    async fn append(&self, session_id: &str, message: Message) -> Result<()>;
63
64    // === Working Memory (task-specific) ===
65
66    /// Get a value from working memory.
67    async fn get_context(&self, session_id: &str, key: &str) -> Result<Option<Value>>;
68
69    /// Set a value in working memory.
70    async fn set_context(&self, session_id: &str, key: &str, value: Value) -> Result<()>;
71
72    // === Long-term Memory (semantic recall) ===
73
74    /// Search for relevant memories.
75    async fn recall(&self, query: &str, limit: usize) -> Result<Vec<MemoryEntry>>;
76
77    /// Store a new memory entry.
78    async fn store(&self, entry: MemoryEntry) -> Result<()>;
79
80    // === Session Lifecycle (default impls — override for persistent backends) ===
81
82    /// Create a new session and return its ID.
83    ///
84    /// Default impl generates a random UUID v4. Override to use custom ID schemes.
85    async fn create_session(&self) -> Result<String> {
86        Ok(uuid::Uuid::new_v4().to_string())
87    }
88
89    /// List all known session IDs.
90    ///
91    /// Default impl always returns an empty vec. Override for persistent backends.
92    async fn list_sessions(&self) -> Result<Vec<String>> {
93        Ok(vec![])
94    }
95
96    /// Delete a session and all associated conversation history and working memory.
97    ///
98    /// Default impl is a no-op. Override for persistent backends.
99    ///
100    /// # Note on long-term memory
101    ///
102    /// Long-term memory (`store`/`recall`) is **global** across all sessions and
103    /// is intentionally NOT cleared by this method. Use a separate cleanup
104    /// strategy if per-session long-term memory isolation is required.
105    async fn delete_session(&self, _session_id: &str) -> Result<()> {
106        Ok(())
107    }
108}