#![deny(missing_docs)]
#![deny(rust_2018_idioms)]
use async_trait::async_trait;
use klieo_core::error::MemoryError;
#[async_trait]
pub trait Embedder: Send + Sync {
fn dimension(&self) -> usize;
async fn embed(&self, texts: &[String]) -> Result<Vec<Vec<f32>>, MemoryError>;
}
pub struct DummyEmbedder;
#[async_trait]
impl Embedder for DummyEmbedder {
fn dimension(&self) -> usize {
384
}
async fn embed(&self, texts: &[String]) -> Result<Vec<Vec<f32>>, MemoryError> {
Ok(texts.iter().map(|_| vec![0.0f32; 384]).collect())
}
}
#[cfg(any(test, feature = "test-utils"))]
pub struct FakeEmbedder {
dim: usize,
}
#[cfg(any(test, feature = "test-utils"))]
impl FakeEmbedder {
pub fn new(dim: usize) -> Self {
Self { dim }
}
}
#[cfg(any(test, feature = "test-utils"))]
impl Default for FakeEmbedder {
fn default() -> Self {
Self::new(8)
}
}
#[cfg(any(test, feature = "test-utils"))]
#[async_trait]
impl Embedder for FakeEmbedder {
fn dimension(&self) -> usize {
self.dim
}
async fn embed(&self, texts: &[String]) -> Result<Vec<Vec<f32>>, MemoryError> {
let dim = self.dim;
Ok(texts
.iter()
.map(|text| {
let mut v = vec![0.0f32; dim];
let bytes = text.as_bytes();
for (i, slot) in v.iter_mut().enumerate() {
const FNV_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
const FNV_PRIME: u64 = 0x0000_0001_0000_01b3;
let mut h: u64 = FNV_OFFSET;
h ^= i as u64;
h = h.wrapping_mul(FNV_PRIME);
for &b in bytes {
h ^= b as u64;
h = h.wrapping_mul(FNV_PRIME);
}
*slot = (h as f32 / u64::MAX as f32) - 0.5;
}
let norm: f32 = v.iter().map(|x| x * x).sum::<f32>().sqrt();
if norm > 0.0 {
for x in &mut v {
*x /= norm;
}
}
v
})
.collect())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn dummy_returns_zero_vectors_of_dim_384() {
let e = DummyEmbedder;
assert_eq!(e.dimension(), 384);
let out = e.embed(&["a".into(), "b".into()]).await.unwrap();
assert_eq!(out.len(), 2);
assert_eq!(out[0].len(), 384);
assert!(out[0].iter().all(|x| *x == 0.0));
}
#[tokio::test]
async fn fake_embedder_is_deterministic() {
let e = FakeEmbedder::new(16);
let a = e.embed(&["hello".into()]).await.unwrap();
let b = e.embed(&["hello".into()]).await.unwrap();
assert_eq!(a, b);
}
#[tokio::test]
async fn fake_embedder_distinguishes_inputs() {
let e = FakeEmbedder::new(16);
let a = e.embed(&["alpha".into()]).await.unwrap();
let b = e.embed(&["beta".into()]).await.unwrap();
assert_ne!(a, b);
}
#[tokio::test]
async fn fake_embedder_outputs_unit_vectors() {
let e = FakeEmbedder::new(8);
let v = e.embed(&["hello".into()]).await.unwrap();
let norm: f32 = v[0].iter().map(|x| x * x).sum::<f32>().sqrt();
assert!(
(norm - 1.0).abs() < 1e-5,
"fake embedder must produce unit vectors, got norm={norm}"
);
}
}