use async_trait::async_trait;
use crate::error::SqliteMemoryError;
pub type EmbedResult<T> = Result<T, SqliteMemoryError>;
#[async_trait]
pub trait Embedder: Send + Sync {
fn dim(&self) -> usize;
fn model_name(&self) -> &str;
async fn embed(&self, text: &str) -> EmbedResult<Vec<f32>>;
async fn embed_batch(&self, texts: &[String]) -> EmbedResult<Vec<Vec<f32>>> {
let mut out = Vec::with_capacity(texts.len());
for t in texts {
out.push(self.embed(t).await?);
}
Ok(out)
}
}
#[derive(Debug, Clone)]
pub struct MockEmbedder {
dim: usize,
model: String,
}
impl MockEmbedder {
pub fn new() -> Self {
Self {
dim: 384,
model: "mock-384".into(),
}
}
pub fn with_dim(dim: usize) -> Self {
Self {
dim,
model: format!("mock-{dim}"),
}
}
}
impl Default for MockEmbedder {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl Embedder for MockEmbedder {
fn dim(&self) -> usize {
self.dim
}
fn model_name(&self) -> &str {
&self.model
}
async fn embed(&self, text: &str) -> EmbedResult<Vec<f32>> {
let mut seed: u64 = 0xcbf29ce484222325;
for b in text.bytes() {
seed ^= b as u64;
seed = seed.wrapping_mul(0x100000001b3);
}
let mut out = Vec::with_capacity(self.dim);
for i in 0..self.dim {
seed = seed
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
let f = ((seed >> (i % 32)) as i32) as f32 / i32::MAX as f32;
out.push(f.clamp(-1.0, 1.0));
}
Ok(out)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn mock_embedder_deterministic() {
let e = MockEmbedder::new();
let a = e.embed("hello").await.unwrap();
let b = e.embed("hello").await.unwrap();
assert_eq!(a, b);
assert_eq!(a.len(), 384);
}
#[tokio::test]
async fn mock_embedder_different_for_different_input() {
let e = MockEmbedder::new();
let a = e.embed("hello").await.unwrap();
let b = e.embed("world").await.unwrap();
assert_ne!(a, b);
}
#[tokio::test]
async fn mock_embedder_with_dim_honors_dim() {
let e = MockEmbedder::with_dim(8);
let v = e.embed("hi").await.unwrap();
assert_eq!(v.len(), 8);
assert_eq!(e.dim(), 8);
}
#[tokio::test]
async fn batch_default_works() {
let e = MockEmbedder::new();
let v = e
.embed_batch(&["a".to_string(), "b".to_string()])
.await
.unwrap();
assert_eq!(v.len(), 2);
assert_eq!(v[0].len(), 384);
}
}