use crate::error::Result;
pub trait Embedder: Send + Sync {
fn embed(&self, text: &str) -> Result<Vec<f32>>;
fn embed_batch(&self, texts: &[&str]) -> Result<Vec<Vec<f32>>>;
fn dimensions(&self) -> usize;
}
pub struct FastEmbedder {
model: std::sync::Mutex<fastembed::TextEmbedding>,
}
impl FastEmbedder {
pub fn new() -> Result<Self> {
use fastembed::{EmbeddingModel, InitOptions, TextEmbedding};
let model = TextEmbedding::try_new(
InitOptions::new(EmbeddingModel::AllMiniLML6V2)
.with_show_download_progress(true),
)
.map_err(|e| {
crate::error::RemembrallError::Internal(format!(
"Failed to load embedding model: {e}"
))
})?;
Ok(Self {
model: std::sync::Mutex::new(model),
})
}
}
impl Embedder for FastEmbedder {
fn embed(&self, text: &str) -> Result<Vec<f32>> {
let mut model = self.model.lock().map_err(|e| {
crate::error::RemembrallError::Internal(format!(
"Embedding model lock poisoned: {e}"
))
})?;
let results = model
.embed(vec![text], None)
.map_err(|e| {
crate::error::RemembrallError::Internal(format!("Embedding failed: {e}"))
})?;
results.into_iter().next().ok_or_else(|| {
crate::error::RemembrallError::Internal("No embedding returned".into())
})
}
fn embed_batch(&self, texts: &[&str]) -> Result<Vec<Vec<f32>>> {
let mut model = self.model.lock().map_err(|e| {
crate::error::RemembrallError::Internal(format!(
"Embedding model lock poisoned: {e}"
))
})?;
let owned: Vec<String> = texts.iter().map(|s| s.to_string()).collect();
model.embed(owned, None).map_err(|e| {
crate::error::RemembrallError::Internal(format!("Batch embedding failed: {e}"))
})
}
fn dimensions(&self) -> usize {
384
}
}