paladin_memory/config/
rag.rs1use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
15pub enum RetrievalTrigger {
16 Always,
18 KeywordBased,
20 SemanticThreshold,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct RagConfig {
32 pub top_k: usize,
34 pub min_similarity: f32,
36 pub max_tokens: usize,
38 pub timeout_seconds: u64,
40 pub retrieval_trigger: RetrievalTrigger,
42}
43
44impl Default for RagConfig {
45 fn default() -> Self {
46 Self {
47 top_k: 5,
48 min_similarity: 0.7,
49 max_tokens: 2000,
50 timeout_seconds: 5,
51 retrieval_trigger: RetrievalTrigger::Always,
52 }
53 }
54}
55
56impl RagConfig {
57 pub fn validate(&self) -> Result<(), String> {
61 if self.top_k == 0 {
62 return Err("RAG top_k must be greater than 0".to_string());
63 }
64
65 if self.top_k > 100 {
66 return Err(format!(
67 "RAG top_k {} seems unusually large (max 100)",
68 self.top_k
69 ));
70 }
71
72 if !(0.0..=1.0).contains(&self.min_similarity) {
73 return Err(format!(
74 "RAG min_similarity {} must be between 0.0 and 1.0",
75 self.min_similarity
76 ));
77 }
78
79 if self.max_tokens == 0 {
80 return Err("RAG max_tokens must be greater than 0".to_string());
81 }
82
83 if self.timeout_seconds == 0 {
84 return Err("RAG timeout_seconds must be greater than 0".to_string());
85 }
86
87 Ok(())
88 }
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
95#[serde(rename_all = "snake_case")]
96pub enum MemoryExtractionStrategy {
97 EveryTurn,
99 #[default]
101 OnCompletion,
102 Manual,
104 Threshold { importance: u8 },
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct MemoryExtractionConfig {
113 pub strategy: MemoryExtractionStrategy,
115 pub enabled: bool,
117}
118
119impl Default for MemoryExtractionConfig {
120 fn default() -> Self {
121 Self {
122 strategy: MemoryExtractionStrategy::OnCompletion,
123 enabled: true,
124 }
125 }
126}
127
128impl MemoryExtractionConfig {
129 pub fn validate(&self) -> Result<(), String> {
133 if let MemoryExtractionStrategy::Threshold { importance } = self.strategy
134 && importance == 0
135 {
136 return Err(
137 "Memory extraction threshold importance must be greater than 0".to_string(),
138 );
139 }
140 Ok(())
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn test_rag_config_defaults() {
150 let cfg = RagConfig::default();
151 assert_eq!(cfg.top_k, 5);
152 assert!((cfg.min_similarity - 0.7).abs() < f32::EPSILON);
153 assert_eq!(cfg.max_tokens, 2000);
154 assert_eq!(cfg.timeout_seconds, 5);
155 assert_eq!(cfg.retrieval_trigger, RetrievalTrigger::Always);
156 }
157
158 #[test]
159 fn test_rag_config_validate_ok() {
160 assert!(RagConfig::default().validate().is_ok());
161 }
162
163 #[test]
164 fn test_rag_config_validate_zero_top_k() {
165 let cfg = RagConfig {
166 top_k: 0,
167 ..Default::default()
168 };
169 assert!(cfg.validate().is_err());
170 }
171
172 #[test]
173 fn test_rag_config_validate_bad_similarity() {
174 let cfg = RagConfig {
175 min_similarity: 1.5,
176 ..Default::default()
177 };
178 assert!(cfg.validate().is_err());
179 }
180
181 #[test]
182 fn test_rag_config_validate_zero_timeout() {
183 let cfg = RagConfig {
184 timeout_seconds: 0,
185 ..Default::default()
186 };
187 assert!(cfg.validate().is_err());
188 }
189
190 #[test]
191 fn test_memory_extraction_config_defaults() {
192 let cfg = MemoryExtractionConfig::default();
193 assert!(cfg.enabled);
194 assert_eq!(cfg.strategy, MemoryExtractionStrategy::OnCompletion);
195 }
196
197 #[test]
198 fn test_memory_extraction_threshold_zero_invalid() {
199 let cfg = MemoryExtractionConfig {
200 strategy: MemoryExtractionStrategy::Threshold { importance: 0 },
201 enabled: true,
202 };
203 assert!(cfg.validate().is_err());
204 }
205
206 #[test]
207 fn test_memory_extraction_threshold_nonzero_valid() {
208 let cfg = MemoryExtractionConfig {
209 strategy: MemoryExtractionStrategy::Threshold { importance: 5 },
210 enabled: true,
211 };
212 assert!(cfg.validate().is_ok());
213 }
214}