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
18/// Structured knowledge memory for LLM agents.
19///
20/// Implementations store named key-value pairs that get compiled
21/// into the system prompt via [`compile()`](Memory::compile).
22///
23/// Uses `&self` for all methods — implementations must handle
24/// interior mutability (e.g. via `Mutex`).
25pub trait Memory: Send + Sync {
26    /// Get the value for a key (owned).
27    fn get(&self, key: &str) -> Option<String>;
28
29    /// Get all key-value pairs (owned).
30    fn entries(&self) -> Vec<(String, String)>;
31
32    /// Set (upsert) a key-value pair. Returns the previous value if the key existed.
33    fn set(&self, key: impl Into<String>, value: impl Into<String>) -> Option<String>;
34
35    /// Remove a key. Returns the removed value if it existed.
36    fn remove(&self, key: &str) -> Option<String>;
37
38    /// Compile all entries into a string for system prompt injection.
39    fn compile(&self) -> String {
40        let entries = self.entries();
41        if entries.is_empty() {
42            return String::new();
43        }
44
45        let mut out = String::from("<memory>\n");
46        for (key, value) in &entries {
47            out.push_str(&format!("<{key}>\n"));
48            out.push_str(value);
49            if !value.ends_with('\n') {
50                out.push('\n');
51            }
52            out.push_str(&format!("</{key}>\n"));
53        }
54        out.push_str("</memory>");
55        out
56    }
57
58    /// Store a key-value pair (async). Default delegates to `set`.
59    fn store(
60        &self,
61        key: impl Into<String> + Send,
62        value: impl Into<String> + Send,
63    ) -> impl Future<Output = Result<()>> + Send {
64        self.set(key, value);
65        async { Ok(()) }
66    }
67
68    /// Search for relevant entries (async). Default returns empty.
69    fn recall(
70        &self,
71        _query: &str,
72        _options: RecallOptions,
73    ) -> impl Future<Output = Result<Vec<MemoryEntry>>> + Send {
74        async { Ok(Vec::new()) }
75    }
76
77    /// Compile relevant entries for a query (async). Default delegates to `compile`.
78    fn compile_relevant(&self, _query: &str) -> impl Future<Output = String> + Send {
79        let compiled = self.compile();
80        async move { compiled }
81    }
82}
83
84/// A structured memory entry with metadata and optional embedding.
85#[derive(Debug, Clone, Default)]
86pub struct MemoryEntry {
87    /// Entry key (identity string).
88    pub key: CompactString,
89    /// Entry value (unbounded content).
90    pub value: String,
91    /// Optional structured metadata (JSON).
92    pub metadata: Option<Value>,
93    /// Unix timestamp when the entry was created.
94    pub created_at: u64,
95    /// Unix timestamp when the entry was last accessed.
96    pub accessed_at: u64,
97    /// Number of times the entry has been accessed.
98    pub access_count: u32,
99    /// Optional embedding vector for semantic search.
100    pub embedding: Option<Vec<f32>>,
101}
102
103/// Options controlling memory recall behavior.
104#[derive(Debug, Clone, Default)]
105pub struct RecallOptions {
106    /// Maximum number of results (0 = implementation default).
107    pub limit: usize,
108    /// Filter by creation time range (start, end) in unix seconds.
109    pub time_range: Option<(u64, u64)>,
110    /// Minimum relevance score threshold (0.0–1.0).
111    pub relevance_threshold: Option<f32>,
112}