do_memory_core/embeddings/
simple.rs1use anyhow::Result;
17use serde::{Deserialize, Serialize};
18use tracing;
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct EmbeddingConfig {
23 pub similarity_threshold: f32,
25 pub batch_size: usize,
27 pub cache_embeddings: bool,
29}
30
31impl Default for EmbeddingConfig {
32 fn default() -> Self {
33 Self {
34 similarity_threshold: 0.7,
35 batch_size: 32,
36 cache_embeddings: true,
37 }
38 }
39}
40
41#[must_use]
43pub fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
44 crate::embeddings::cosine_similarity(a, b)
45}
46
47pub fn text_to_embedding(text: &str) -> Vec<f32> {
68 use std::collections::hash_map::DefaultHasher;
69 use std::hash::{Hash, Hasher};
70
71 tracing::warn!(
73 "PRODUCTION WARNING: Using hash-based pseudo-embeddings - semantic search will not work correctly! \
74 Text: '{}'. Use real embedding models for production.",
75 text.chars().take(20).collect::<String>()
76 );
77
78 let mut hasher = DefaultHasher::new();
80 text.hash(&mut hasher);
81 let hash = hasher.finish();
82
83 let dimension = 384; let mut embedding = Vec::with_capacity(dimension);
85 let mut seed = hash;
86
87 for _ in 0..dimension {
88 seed = seed.wrapping_mul(1_103_515_245).wrapping_add(12345);
90 let value = ((seed >> 16) as f32) / 32768.0 - 1.0; embedding.push(value);
92 }
93
94 let magnitude = embedding.iter().map(|x| x * x).sum::<f32>().sqrt();
96 if magnitude > 0.0 {
97 for x in &mut embedding {
98 *x /= magnitude;
99 }
100 }
101
102 embedding
103}
104
105#[cfg(test)]
110#[must_use]
111pub fn text_to_embedding_test(text: &str) -> Vec<f32> {
112 use std::collections::hash_map::DefaultHasher;
113 use std::hash::{Hash, Hasher};
114
115 let mut hasher = DefaultHasher::new();
117 text.hash(&mut hasher);
118 let hash = hasher.finish();
119
120 let dimension = 384; let mut embedding = Vec::with_capacity(dimension);
122 let mut seed = hash;
123
124 for _ in 0..dimension {
125 seed = seed.wrapping_mul(1_103_515_245).wrapping_add(12345);
127 let value = ((seed >> 16) as f32) / 32768.0 - 1.0; embedding.push(value);
129 }
130
131 let magnitude = embedding.iter().map(|x| x * x).sum::<f32>().sqrt();
133 if magnitude > 0.0 {
134 for x in &mut embedding {
135 *x /= magnitude;
136 }
137 }
138
139 embedding
140}
141
142pub fn find_similar_texts(
151 query: &str,
152 candidates: &[String],
153 limit: usize,
154 threshold: f32,
155) -> Vec<(usize, f32, String)> {
156 tracing::warn!(
157 "Using mock embeddings for semantic search - results are not semantically meaningful!"
158 );
159
160 let query_embedding = text_to_embedding(query);
161
162 let mut similarities: Vec<(usize, f32, String)> = candidates
163 .iter()
164 .enumerate()
165 .map(|(i, text)| {
166 let embedding = text_to_embedding(text);
167 let similarity = cosine_similarity(&query_embedding, &embedding);
168 (i, similarity, text.clone())
169 })
170 .filter(|(_, similarity, _)| *similarity >= threshold)
171 .collect();
172
173 similarities.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
175
176 similarities.into_iter().take(limit).collect()
178}
179
180pub fn demonstrate_semantic_search() -> Result<()> {
190 tracing::warn!("🧠 Semantic Search Demonstration (Mock Embeddings)");
191 tracing::warn!("WARNING: This demonstration uses hash-based pseudo-embeddings");
192 tracing::warn!("that are NOT semantically meaningful. Similarity scores are");
193 tracing::warn!("essentially random and do not reflect actual semantic similarity.");
194 tracing::warn!("For production semantic search, use real embedding models.");
195 tracing::info!("Enable with: cargo run --features local-embeddings");
196
197 let episodes = vec![
199 "Implement user authentication with JWT tokens".to_string(),
200 "Build REST API endpoints for user management".to_string(),
201 "Create data validation middleware for API requests".to_string(),
202 "Add rate limiting to prevent API abuse".to_string(),
203 "Implement OAuth2 authentication flow".to_string(),
204 "Design database schema for user profiles".to_string(),
205 "Write unit tests for authentication module".to_string(),
206 "Deploy API to production with Docker".to_string(),
207 "Monitor API performance and error rates".to_string(),
208 "Document API endpoints with OpenAPI spec".to_string(),
209 ];
210
211 let queries = vec![
213 "How to secure API with authentication?",
214 "Need to create user management endpoints",
215 "Add validation to API requests",
216 "Prevent API abuse and rate limiting",
217 ];
218
219 for query in queries {
220 tracing::debug!("Query: \"{}\"", query);
221 let results = find_similar_texts(query, &episodes, 3, 0.5);
222
223 tracing::debug!("Top {} similar episodes:", results.len());
224 for (i, (idx, similarity, text)) in results.iter().enumerate() {
225 tracing::debug!(
226 " {}. [{}] {} (similarity: {:.3})",
227 i + 1,
228 idx,
229 text,
230 similarity
231 );
232 }
233 }
234
235 tracing::debug!("Direct Similarity Examples:");
237 let pairs = vec![
238 ("user authentication", "login system"),
239 ("REST API", "web service endpoints"),
240 ("data validation", "input verification"),
241 ("rate limiting", "API throttling"),
242 ];
243
244 for (text1, text2) in pairs {
245 let emb1 = text_to_embedding(text1);
246 let emb2 = text_to_embedding(text2);
247 let similarity = cosine_similarity(&emb1, &emb2);
248 tracing::debug!(" \"{}\" <-> \"{}\" = {:.3}", text1, text2, similarity);
249 }
250
251 tracing::info!("For real semantic search, use memory-core::embeddings modules");
252 tracing::info!("with proper ONNX models and sentence transformers.");
253
254 Ok(())
255}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260
261 #[test]
262 fn test_cosine_similarity() {
263 let vec1 = vec![1.0, 2.0, 3.0];
265 let vec2 = vec![1.0, 2.0, 3.0];
266 let similarity = cosine_similarity(&vec1, &vec2);
267 assert!((similarity - 1.0).abs() < 0.001);
268
269 let vec3 = vec![1.0, 0.0];
271 let vec4 = vec![0.0, 1.0];
272 let similarity = cosine_similarity(&vec3, &vec4);
273 assert!((similarity - 0.5).abs() < 0.001);
274 }
275
276 #[test]
277 fn test_text_to_embedding() {
278 let embedding1 = text_to_embedding("hello world");
279 let embedding2 = text_to_embedding("hello world");
280 let embedding3 = text_to_embedding("different text");
281
282 assert_eq!(embedding1, embedding2);
284
285 assert_ne!(embedding1, embedding3);
287
288 let magnitude1: f32 = embedding1.iter().map(|x| x * x).sum::<f32>().sqrt();
290 assert!((magnitude1 - 1.0).abs() < 0.001);
291 }
292
293 #[test]
294 fn test_find_similar_texts() {
295 let candidates = vec![
296 "implement user authentication".to_string(),
297 "create REST API endpoints".to_string(),
298 "add input validation".to_string(),
299 "deploy with Docker".to_string(),
300 ];
301
302 let results = find_similar_texts("user login system", &candidates, 2, 0.0);
303
304 assert!(results.len() <= 2);
306
307 if results.len() > 1 {
309 assert!(results[0].1 >= results[1].1);
310 }
311 }
312
313 #[test]
314 fn test_embedding_config() {
315 let config = EmbeddingConfig::default();
316 assert_eq!(config.similarity_threshold, 0.7);
317 assert_eq!(config.batch_size, 32);
318 assert!(config.cache_embeddings);
319 }
320}