Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions crates/kestrel-memory/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@ tracing = { workspace = true }
toml = { workspace = true }
dirs = { workspace = true }
lru = { workspace = true }
lancedb = { workspace = true }
arrow-array = { workspace = true }
arrow-schema = { workspace = true }
futures = { workspace = true }
fs4 = { workspace = true }
tantivy = "0.26"
tantivy-jieba = "0.19"

[dev-dependencies]
tempfile = { workspace = true }
futures = { workspace = true }
41 changes: 12 additions & 29 deletions crates/kestrel-memory/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ use std::path::PathBuf;
/// ```toml
/// max_entries = 1000
/// hot_store_path = "/home/user/.kestrel/memory/hot.jsonl"
/// warm_store_path = "/home/user/.kestrel/memory/warm"
/// embedding_dim = 1536
/// tantivy_index_path = "/home/user/.kestrel/memory/tantivy"
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryConfig {
Expand All @@ -25,13 +24,9 @@ pub struct MemoryConfig {
#[serde(default = "default_hot_store_path")]
pub hot_store_path: PathBuf,

/// Path to the warm store data directory.
#[serde(default = "default_warm_store_path")]
pub warm_store_path: PathBuf,

/// Dimension of embedding vectors for semantic search.
#[serde(default = "default_embedding_dim")]
pub embedding_dim: usize,
/// Path to the tantivy full-text search index directory.
#[serde(default = "default_tantivy_index_path")]
pub tantivy_index_path: PathBuf,

/// Character budget for recalled memory content injected into prompts.
#[serde(default = "default_memory_char_budget")]
Expand All @@ -54,16 +49,12 @@ fn default_hot_store_path() -> PathBuf {
.join("hot.jsonl")
}

fn default_warm_store_path() -> PathBuf {
fn default_tantivy_index_path() -> PathBuf {
dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".kestrel")
.join("memory")
.join("warm")
}

fn default_embedding_dim() -> usize {
1536
.join("tantivy")
}

fn default_memory_char_budget() -> usize {
Expand All @@ -79,8 +70,7 @@ impl Default for MemoryConfig {
Self {
max_entries: default_max_entries(),
hot_store_path: default_hot_store_path(),
warm_store_path: default_warm_store_path(),
embedding_dim: default_embedding_dim(),
tantivy_index_path: default_tantivy_index_path(),
memory_char_budget: default_memory_char_budget(),
memory_char_budget_overflow: default_memory_char_budget_overflow(),
}
Expand All @@ -93,8 +83,7 @@ impl MemoryConfig {
Self {
max_entries: 100,
hot_store_path: temp_dir.join("hot.jsonl"),
warm_store_path: temp_dir.join("warm"),
embedding_dim: 8,
tantivy_index_path: temp_dir.join("tantivy"),
memory_char_budget: default_memory_char_budget(),
memory_char_budget_overflow: default_memory_char_budget_overflow(),
}
Expand All @@ -119,12 +108,11 @@ mod tests {
fn test_default_config() {
let config = MemoryConfig::default();
assert_eq!(config.max_entries, 1000);
assert_eq!(config.embedding_dim, 1536);
assert_eq!(config.memory_char_budget, 2200);
assert_eq!(config.memory_char_budget_overflow, 1375);
assert!(config.hot_store_path.to_string_lossy().contains(".kestrel"));
assert!(config
.warm_store_path
.tantivy_index_path
.to_string_lossy()
.contains(".kestrel"));
}
Expand All @@ -134,38 +122,33 @@ mod tests {
let temp = std::env::temp_dir();
let config = MemoryConfig::for_test(&temp);
assert_eq!(config.max_entries, 100);
assert_eq!(config.embedding_dim, 8);
assert!(config.hot_store_path.starts_with(&temp));
assert!(config.warm_store_path.starts_with(&temp));
assert!(config.tantivy_index_path.starts_with(&temp));
}

#[test]
fn test_toml_roundtrip() {
let config = MemoryConfig {
max_entries: 500,
hot_store_path: PathBuf::from("/tmp/hot.jsonl"),
warm_store_path: PathBuf::from("/tmp/warm"),
embedding_dim: 768,
tantivy_index_path: PathBuf::from("/tmp/tantivy"),
memory_char_budget: 3000,
memory_char_budget_overflow: 1500,
};
let toml_str = config.to_toml().unwrap();
let parsed = MemoryConfig::from_toml(&toml_str).unwrap();
assert_eq!(parsed.max_entries, 500);
assert_eq!(parsed.embedding_dim, 768);
assert_eq!(parsed.memory_char_budget, 3000);
assert_eq!(parsed.memory_char_budget_overflow, 1500);
assert_eq!(parsed.hot_store_path, PathBuf::from("/tmp/hot.jsonl"));
assert_eq!(parsed.warm_store_path, PathBuf::from("/tmp/warm"));
assert_eq!(parsed.tantivy_index_path, PathBuf::from("/tmp/tantivy"));
}

#[test]
fn test_toml_parse_partial() {
let toml_str = "max_entries = 42";
let config = MemoryConfig::from_toml(toml_str).unwrap();
assert_eq!(config.max_entries, 42);
// Other fields get defaults
assert_eq!(config.embedding_dim, 1536);
assert_eq!(config.memory_char_budget, 2200);
assert_eq!(config.memory_char_budget_overflow, 1375);
}
Expand Down
206 changes: 0 additions & 206 deletions crates/kestrel-memory/src/embedding.rs

This file was deleted.

26 changes: 5 additions & 21 deletions crates/kestrel-memory/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,13 @@ pub enum MemoryError {
current: usize,
},

/// An invalid embedding vector was provided.
#[error("Invalid embedding: expected dimension {expected}, got {actual}")]
InvalidEmbedding {
/// Expected embedding dimension.
expected: usize,
/// Actual embedding dimension provided.
actual: usize,
},

/// A configuration error occurred.
#[error("Configuration error: {0}")]
Config(String),

/// A LanceDB error occurred.
#[error("LanceDB error: {0}")]
LanceDb(String),
/// A search engine (tantivy) error occurred.
#[error("Search engine error: {0}")]
SearchEngine(String),

/// A security violation was detected in a memory entry.
#[error("Security violation: {0}")]
Expand Down Expand Up @@ -70,18 +61,11 @@ mod tests {
};
assert!(err.to_string().contains("100"));

let err = MemoryError::InvalidEmbedding {
expected: 1536,
actual: 512,
};
assert!(err.to_string().contains("1536"));
assert!(err.to_string().contains("512"));

let err = MemoryError::Config("bad config".to_string());
assert!(err.to_string().contains("bad config"));

let err = MemoryError::LanceDb("table not found".to_string());
assert!(err.to_string().contains("table not found"));
let err = MemoryError::SearchEngine("index corrupted".to_string());
assert!(err.to_string().contains("index corrupted"));
}

#[test]
Expand Down
Loading
Loading