Skip to main content

semantic_memory/
config.rs

1use crate::tokenizer::TokenCounter;
2use serde::{Deserialize, Serialize};
3use std::path::PathBuf;
4use std::sync::Arc;
5
6/// Configuration for the memory system.
7#[derive(Clone, Serialize, Deserialize)]
8pub struct MemoryConfig {
9    /// Base directory for all storage files (SQLite + HNSW sidecar files).
10    /// Replaces the v0.1.0 `database_path` field.
11    pub base_dir: PathBuf,
12
13    /// Embedding provider configuration.
14    pub embedding: EmbeddingConfig,
15
16    /// Search tuning parameters.
17    pub search: SearchConfig,
18
19    /// Chunking parameters.
20    pub chunking: ChunkingConfig,
21
22    /// Custom token counter. None = use EstimateTokenCounter (chars / 4).
23    #[serde(skip)]
24    pub token_counter: Option<Arc<dyn TokenCounter>>,
25
26    /// HNSW index configuration.
27    #[cfg(feature = "hnsw")]
28    #[serde(skip)]
29    pub hnsw: crate::hnsw::HnswConfig,
30}
31
32impl std::fmt::Debug for MemoryConfig {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        let mut s = f.debug_struct("MemoryConfig");
35        s.field("base_dir", &self.base_dir)
36            .field("embedding", &self.embedding)
37            .field("search", &self.search)
38            .field("chunking", &self.chunking)
39            .field(
40                "token_counter",
41                &self.token_counter.as_ref().map(|_| "custom"),
42            );
43        #[cfg(feature = "hnsw")]
44        s.field("hnsw", &self.hnsw);
45        s.finish()
46    }
47}
48
49impl Default for MemoryConfig {
50    fn default() -> Self {
51        Self {
52            base_dir: PathBuf::from("memory"),
53            embedding: EmbeddingConfig::default(),
54            search: SearchConfig::default(),
55            chunking: ChunkingConfig::default(),
56            token_counter: None,
57            #[cfg(feature = "hnsw")]
58            hnsw: crate::hnsw::HnswConfig::default(),
59        }
60    }
61}
62
63/// Embedding provider configuration.
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct EmbeddingConfig {
66    /// Ollama base URL.
67    pub ollama_url: String,
68
69    /// Embedding model name.
70    pub model: String,
71
72    /// Expected embedding dimensions.
73    pub dimensions: usize,
74
75    /// Maximum texts to embed in a single API call.
76    pub batch_size: usize,
77
78    /// Timeout for embedding requests in seconds.
79    pub timeout_secs: u64,
80}
81
82impl Default for EmbeddingConfig {
83    fn default() -> Self {
84        Self {
85            ollama_url: "http://localhost:11434".to_string(),
86            model: "nomic-embed-text".to_string(),
87            dimensions: 768,
88            batch_size: 32,
89            timeout_secs: 30,
90        }
91    }
92}
93
94/// Search tuning parameters.
95#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct SearchConfig {
97    /// Weight for BM25 score in RRF fusion.
98    pub bm25_weight: f64,
99
100    /// Weight for vector similarity in RRF fusion.
101    pub vector_weight: f64,
102
103    /// RRF constant (k). Controls rank importance decay.
104    pub rrf_k: f64,
105
106    /// Number of candidates from each search method before fusion.
107    pub candidate_pool_size: usize,
108
109    /// Default number of results to return.
110    pub default_top_k: usize,
111
112    /// Minimum cosine similarity threshold for vector candidates.
113    pub min_similarity: f64,
114
115    /// Optional recency boost. If enabled, results are boosted based on how
116    /// recently they were created/updated. The value is the half-life in days —
117    /// a fact that is `recency_half_life_days` old gets 50% of the recency boost.
118    /// None = no recency weighting (current behavior, default).
119    pub recency_half_life_days: Option<f64>,
120
121    /// Weight of the recency boost relative to BM25 and vector scores in RRF.
122    /// Only used when recency_half_life_days is Some.
123    /// Default: 0.5
124    pub recency_weight: f64,
125
126    /// When true, rerank top HNSW candidates using exact f32 cosine similarity
127    /// from SQLite. Improves recall at the cost of one batched SQL query.
128    /// Only applies when HNSW feature is enabled.
129    /// Default: true
130    pub rerank_from_f32: bool,
131}
132
133impl Default for SearchConfig {
134    fn default() -> Self {
135        Self {
136            bm25_weight: 1.0,
137            vector_weight: 1.0,
138            rrf_k: 60.0,
139            candidate_pool_size: 50,
140            default_top_k: 5,
141            min_similarity: 0.3,
142            recency_half_life_days: None,
143            recency_weight: 0.5,
144            rerank_from_f32: true,
145        }
146    }
147}
148
149/// Text chunking parameters.
150#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct ChunkingConfig {
152    /// Target chunk size in characters.
153    pub target_size: usize,
154
155    /// Minimum chunk size. Chunks smaller than this are merged with neighbors.
156    pub min_size: usize,
157
158    /// Maximum chunk size. Chunks larger than this are force-split.
159    pub max_size: usize,
160
161    /// Overlap between adjacent chunks in characters.
162    pub overlap: usize,
163}
164
165impl Default for ChunkingConfig {
166    fn default() -> Self {
167        Self {
168            target_size: 1000,
169            min_size: 100,
170            max_size: 2000,
171            overlap: 200,
172        }
173    }
174}