Skip to main content

rustyclaw_core/mnemo/
traits.rs

1//! Core memory storage and summarization traits.
2//!
3//! Follows existing RustyClaw patterns (RuntimeAdapter, Observer, Messenger).
4
5use anyhow::Result;
6use async_trait::async_trait;
7use std::time::Duration;
8
9/// A single memory entry (message or summary).
10#[derive(Debug, Clone)]
11pub struct MemoryEntry {
12    /// Unique identifier.
13    pub id: i64,
14    /// Message role ("user", "assistant", "system").
15    pub role: String,
16    /// Content text.
17    pub content: String,
18    /// Estimated token count.
19    pub token_count: usize,
20    /// Unix timestamp (seconds since epoch).
21    pub timestamp: i64,
22    /// Summary depth: 0 = raw message, 1+ = summary level.
23    pub depth: u8,
24}
25
26/// Search result with relevance score.
27#[derive(Debug, Clone)]
28pub struct MemoryHit {
29    /// The matching entry.
30    pub entry: MemoryEntry,
31    /// Relevance score (higher is better).
32    pub score: f32,
33    /// Highlighted snippet for display.
34    pub snippet: String,
35}
36
37/// Statistics from a compaction pass.
38#[derive(Debug, Default)]
39pub struct CompactionStats {
40    /// Number of messages compacted into summaries.
41    pub messages_compacted: usize,
42    /// Number of new summaries created.
43    pub summaries_created: usize,
44    /// Estimated tokens saved.
45    pub tokens_saved: usize,
46    /// Time taken for compaction.
47    pub duration: Duration,
48}
49
50/// Core memory storage trait.
51///
52/// Implementations must be `Send + Sync` for sharing across async tasks.
53/// The trait abstracts over storage backends (SQLite, in-memory, remote)
54/// while ensuring consistent behavior for compaction and retrieval.
55#[async_trait]
56pub trait MemoryStore: Send + Sync {
57    /// Return the human-readable name of this memory backend.
58    fn name(&self) -> &str;
59
60    /// Ingest a new message into the store.
61    ///
62    /// Called synchronously on the message processing path.
63    /// Implementations should avoid blocking I/O where possible.
64    async fn ingest(&self, role: &str, content: &str, token_count: usize) -> Result<i64>;
65
66    /// Full-text search across all stored messages and summaries.
67    ///
68    /// Returns up to `limit` results ordered by relevance score.
69    async fn search(&self, query: &str, limit: usize) -> Result<Vec<MemoryHit>>;
70
71    /// Generate context string for agent bootstrap.
72    ///
73    /// Returns a formatted string of recent messages and summaries,
74    /// respecting `max_tokens` budget. Used for system prompt injection.
75    async fn get_context(&self, max_tokens: usize) -> Result<String>;
76
77    /// Get recent context entries for the agent.
78    ///
79    /// Returns the most recent entries up to `max_tokens` budget.
80    async fn get_context_entries(&self, max_tokens: usize) -> Result<Vec<MemoryEntry>>;
81
82    /// Run compaction if thresholds are exceeded.
83    ///
84    /// Compresses older messages into summaries using the configured
85    /// summarization backend. Safe to call frequently; returns early
86    /// if no compaction needed.
87    async fn compact(&self, summarizer: &dyn Summarizer) -> Result<CompactionStats>;
88
89    /// Return total message count (including compacted).
90    async fn message_count(&self) -> Result<usize>;
91
92    /// Return total summary count.
93    async fn summary_count(&self) -> Result<usize>;
94
95    /// Flush any buffered data to persistent storage.
96    async fn flush(&self) -> Result<()> {
97        Ok(())
98    }
99}
100
101/// Kind of summary being generated.
102#[derive(Debug, Clone, Copy)]
103pub enum SummaryKind {
104    /// Summarizing raw messages into a leaf summary.
105    Leaf,
106    /// Summarizing summaries into a higher-level condensed summary.
107    Condensed,
108}
109
110/// Summarization backend for memory compaction.
111///
112/// Implementations call LLM APIs or use deterministic fallbacks.
113#[async_trait]
114pub trait Summarizer: Send + Sync {
115    /// Return the backend name (e.g., "openrouter", "deterministic").
116    fn name(&self) -> &str;
117
118    /// Summarize a batch of entries into a single summary string.
119    async fn summarize(&self, entries: &[MemoryEntry], kind: SummaryKind) -> Result<String>;
120}