context_harness_core/
embedding.rs1pub trait EmbeddingProvider: Send + Sync {
16 fn model_name(&self) -> &str;
18 fn dims(&self) -> usize;
20}
21
22pub fn vec_to_blob(vec: &[f32]) -> Vec<u8> {
38 let mut bytes = Vec::with_capacity(vec.len() * 4);
39 for &v in vec {
40 bytes.extend_from_slice(&v.to_le_bytes());
41 }
42 bytes
43}
44
45pub fn blob_to_vec(blob: &[u8]) -> Vec<f32> {
50 blob.chunks_exact(4)
51 .map(|chunk| f32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]))
52 .collect()
53}
54
55pub fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
64 if a.len() != b.len() || a.is_empty() {
65 return 0.0;
66 }
67
68 let mut dot = 0.0f32;
69 let mut norm_a = 0.0f32;
70 let mut norm_b = 0.0f32;
71
72 for (x, y) in a.iter().zip(b.iter()) {
73 dot += x * y;
74 norm_a += x * x;
75 norm_b += y * y;
76 }
77
78 let denom = norm_a.sqrt() * norm_b.sqrt();
79 if denom < f32::EPSILON {
80 return 0.0;
81 }
82
83 dot / denom
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn test_vec_blob_roundtrip() {
92 let vec = vec![1.0f32, -2.5, 3.125, 0.0, -0.001];
93 let blob = vec_to_blob(&vec);
94 let restored = blob_to_vec(&blob);
95 assert_eq!(vec, restored);
96 }
97
98 #[test]
99 fn test_cosine_identical() {
100 let v = vec![1.0, 2.0, 3.0];
101 let sim = cosine_similarity(&v, &v);
102 assert!((sim - 1.0).abs() < 1e-6);
103 }
104
105 #[test]
106 fn test_cosine_orthogonal() {
107 let a = vec![1.0, 0.0, 0.0];
108 let b = vec![0.0, 1.0, 0.0];
109 let sim = cosine_similarity(&a, &b);
110 assert!(sim.abs() < 1e-6);
111 }
112
113 #[test]
114 fn test_cosine_opposite() {
115 let a = vec![1.0, 0.0];
116 let b = vec![-1.0, 0.0];
117 let sim = cosine_similarity(&a, &b);
118 assert!((sim + 1.0).abs() < 1e-6);
119 }
120
121 #[test]
122 fn test_cosine_empty() {
123 let sim = cosine_similarity(&[], &[]);
124 assert_eq!(sim, 0.0);
125 }
126
127 #[test]
128 fn test_cosine_different_lengths() {
129 let a = vec![1.0, 2.0];
130 let b = vec![1.0];
131 let sim = cosine_similarity(&a, &b);
132 assert_eq!(sim, 0.0);
133 }
134}