use anyhow::Result;
use fastembed::{EmbeddingModel, InitOptions, TextEmbedding};
use std::path::PathBuf;
use std::sync::Mutex;
use super::EmbeddingProvider;
pub struct FastEmbedProvider {
model: Mutex<TextEmbedding>,
dim: usize,
}
impl FastEmbedProvider {
pub fn new(cache_dir: PathBuf) -> Result<Self> {
std::fs::create_dir_all(&cache_dir)?;
let opts = InitOptions::new(EmbeddingModel::AllMiniLML6V2).with_cache_dir(cache_dir);
let model = TextEmbedding::try_new(opts)?;
Ok(Self {
model: Mutex::new(model),
dim: 384,
})
}
}
impl EmbeddingProvider for FastEmbedProvider {
async fn embed(&self, texts: &[String]) -> Result<Vec<Vec<f32>>> {
let mut model = self
.model
.lock()
.map_err(|e| anyhow::anyhow!("mutex poisoned: {}", e))?;
let embeddings = model.embed(texts, None)?;
Ok(embeddings)
}
fn dim(&self) -> usize {
self.dim
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_fastembed_provider() {
let dir = tempfile::tempdir().unwrap();
let p = FastEmbedProvider::new(dir.path().to_path_buf()).unwrap();
assert_eq!(p.dim(), 384);
let result = p.embed(&["hello".to_string()]).await.unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0].len(), 384);
}
}