Skip to main content

walrus_core/memory/
mod.rs

1//! Memory abstractions for Walrus agents.
2//!
3//! Defines the [`Memory`] trait, [`Embedder`] trait, and associated types.
4//! Concrete implementations (`InMemory`, `SqliteMemory`) live in `walrus-memory`.
5//!
6//! Memory is **not chat history**. It is structured knowledge — extracted facts,
7//! user preferences, agent persona — that gets compiled into the system prompt.
8
9use anyhow::Result;
10use compact_str::CompactString;
11pub use embedder::{Embedder, NoEmbedder};
12use serde_json::Value;
13use std::future::Future;
14
15pub mod embedder;
16pub mod tools;
17
18const MEMORY_PROMPT: &str = include_str!("../../prompts/memory.md");
19
20/// Structured knowledge memory for LLM agents.
21///
22/// Implementations store named key-value pairs that get compiled
23/// into the system prompt via [`compile()`](Memory::compile).
24///
25/// Uses `&self` for all methods — implementations must handle
26/// interior mutability (e.g. via `Mutex`).
27pub trait Memory: Send + Sync {
28    /// Get the value for a key (owned).
29    fn get(&self, key: &str) -> Option<String>;
30
31    /// Get all key-value pairs (owned).
32    fn entries(&self) -> Vec<(String, String)>;
33
34    /// Set (upsert) a key-value pair. Returns the previous value if the key existed.
35    fn set(&self, key: impl Into<String>, value: impl Into<String>) -> Option<String>;
36
37    /// Remove a key. Returns the removed value if it existed.
38    fn remove(&self, key: &str) -> Option<String>;
39
40    /// Compile memory into a string for system prompt injection.
41    ///
42    /// Always returns the memory usage instructions. Appends a `## Your profile`
43    /// section containing only `user.*` and `soul.*` entries — stable identity
44    /// facts safe to embed unconditionally. All other entries are retrieval-only
45    /// and surface exclusively via the `recall` tool.
46    fn compile(&self) -> String {
47        let mut out = MEMORY_PROMPT.to_string();
48        let profile: Vec<_> = self
49            .entries()
50            .into_iter()
51            .filter(|(k, _)| k.starts_with("user.") || k.starts_with("soul."))
52            .collect();
53        if !profile.is_empty() {
54            out.push_str("\n\n## Your profile\n\n");
55            for (key, value) in &profile {
56                out.push_str(&format!("**{key}**: {value}\n"));
57            }
58        }
59        out
60    }
61
62    /// Store a key-value pair (async). Default delegates to `set`.
63    fn store(
64        &self,
65        key: impl Into<String> + Send,
66        value: impl Into<String> + Send,
67    ) -> impl Future<Output = Result<()>> + Send {
68        self.set(key, value);
69        async { Ok(()) }
70    }
71
72    /// Search for relevant entries (async). Default returns empty.
73    fn recall(
74        &self,
75        _query: &str,
76        _options: RecallOptions,
77    ) -> impl Future<Output = Result<Vec<MemoryEntry>>> + Send {
78        async { Ok(Vec::new()) }
79    }
80
81    /// Compile relevant entries for a query (async). Default delegates to `compile`.
82    fn compile_relevant(&self, _query: &str) -> impl Future<Output = String> + Send {
83        let compiled = self.compile();
84        async move { compiled }
85    }
86}
87
88/// A structured memory entry with metadata and optional embedding.
89#[derive(Debug, Clone, Default)]
90pub struct MemoryEntry {
91    /// Entry key (identity string).
92    pub key: CompactString,
93    /// Entry value (unbounded content).
94    pub value: String,
95    /// Optional structured metadata (JSON).
96    pub metadata: Option<Value>,
97    /// Unix timestamp when the entry was created.
98    pub created_at: u64,
99    /// Unix timestamp when the entry was last accessed.
100    pub accessed_at: u64,
101    /// Number of times the entry has been accessed.
102    pub access_count: u32,
103    /// Optional embedding vector for semantic search.
104    pub embedding: Option<Vec<f32>>,
105}
106
107/// Options controlling memory recall behavior.
108#[derive(Debug, Clone, Default)]
109pub struct RecallOptions {
110    /// Maximum number of results (0 = implementation default).
111    pub limit: usize,
112    /// Filter by creation time range (start, end) in unix seconds.
113    pub time_range: Option<(u64, u64)>,
114    /// Minimum relevance score threshold (0.0–1.0).
115    pub relevance_threshold: Option<f32>,
116}