zeroclaw/src/memory/mod.rs
argenis de la rosa ec2d5cc93d feat: enhance agent personality, tool guidance, and memory hygiene
- Expand communication style presets (professional, expressive, custom)
- Enrich SOUL.md with human-like tone and emoji-awareness guidance
- Add crash recovery and sub-task scoping guidance to AGENTS.md scaffold
- Add 'Use when / Don't use when' guidance to TOOLS.md and runtime prompts
- Implement memory hygiene system with configurable archiving and retention
- Add MemoryConfig options: hygiene_enabled, archive_after_days, purge_after_days, conversation_retention_days
- Archive old daily memory and session files to archive subdirectories
- Purge old archives and prune stale SQLite conversation rows
- Add comprehensive tests for new features
2026-02-14 11:28:39 -05:00

106 lines
3.1 KiB
Rust

pub mod chunker;
pub mod embeddings;
pub mod hygiene;
pub mod markdown;
pub mod sqlite;
pub mod traits;
pub mod vector;
pub use markdown::MarkdownMemory;
pub use sqlite::SqliteMemory;
pub use traits::Memory;
#[allow(unused_imports)]
pub use traits::{MemoryCategory, MemoryEntry};
use crate::config::MemoryConfig;
use std::path::Path;
use std::sync::Arc;
/// Factory: create the right memory backend from config
pub fn create_memory(
config: &MemoryConfig,
workspace_dir: &Path,
api_key: Option<&str>,
) -> anyhow::Result<Box<dyn Memory>> {
// Best-effort memory hygiene/retention pass (throttled by state file).
if let Err(e) = hygiene::run_if_due(config, workspace_dir) {
tracing::warn!("memory hygiene skipped: {e}");
}
match config.backend.as_str() {
"sqlite" => {
let embedder: Arc<dyn embeddings::EmbeddingProvider> =
Arc::from(embeddings::create_embedding_provider(
&config.embedding_provider,
api_key,
&config.embedding_model,
config.embedding_dimensions,
));
#[allow(clippy::cast_possible_truncation)]
let mem = SqliteMemory::with_embedder(
workspace_dir,
embedder,
config.vector_weight as f32,
config.keyword_weight as f32,
config.embedding_cache_size,
)?;
Ok(Box::new(mem))
}
"markdown" | "none" => Ok(Box::new(MarkdownMemory::new(workspace_dir))),
other => {
tracing::warn!("Unknown memory backend '{other}', falling back to markdown");
Ok(Box::new(MarkdownMemory::new(workspace_dir)))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn factory_sqlite() {
let tmp = TempDir::new().unwrap();
let cfg = MemoryConfig {
backend: "sqlite".into(),
..MemoryConfig::default()
};
let mem = create_memory(&cfg, tmp.path(), None).unwrap();
assert_eq!(mem.name(), "sqlite");
}
#[test]
fn factory_markdown() {
let tmp = TempDir::new().unwrap();
let cfg = MemoryConfig {
backend: "markdown".into(),
..MemoryConfig::default()
};
let mem = create_memory(&cfg, tmp.path(), None).unwrap();
assert_eq!(mem.name(), "markdown");
}
#[test]
fn factory_none_falls_back_to_markdown() {
let tmp = TempDir::new().unwrap();
let cfg = MemoryConfig {
backend: "none".into(),
..MemoryConfig::default()
};
let mem = create_memory(&cfg, tmp.path(), None).unwrap();
assert_eq!(mem.name(), "markdown");
}
#[test]
fn factory_unknown_falls_back_to_markdown() {
let tmp = TempDir::new().unwrap();
let cfg = MemoryConfig {
backend: "redis".into(),
..MemoryConfig::default()
};
let mem = create_memory(&cfg, tmp.path(), None).unwrap();
assert_eq!(mem.name(), "markdown");
}
}