use std::sync::{Arc, Mutex};
use fastembed::{EmbeddingModel, TextEmbedding, TextInitOptions};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum EmbedError {
#[error("fastembed error: {0}")]
Fastembed(#[from] anyhow::Error),
#[error("mutex poisoned")]
Poison,
}
pub const EMBED_DIM: usize = 384;
#[derive(Clone)]
pub struct Embedder {
inner: Arc<Mutex<TextEmbedding>>,
}
impl std::fmt::Debug for Embedder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Embedder")
.field("model", &"AllMiniLML6V2")
.finish()
}
}
impl Embedder {
pub fn new() -> Result<Self, EmbedError> {
let model = TextEmbedding::try_new(
TextInitOptions::new(EmbeddingModel::AllMiniLML6V2).with_show_download_progress(true),
)?;
Ok(Self {
inner: Arc::new(Mutex::new(model)),
})
}
pub fn embed(&self, texts: &[&str]) -> Result<Vec<Vec<f32>>, EmbedError> {
let mut guard = self.inner.lock().map_err(|_| EmbedError::Poison)?;
let results = guard.embed(texts, None)?;
Ok(results)
}
pub fn embed_one(&self, text: &str) -> Result<Vec<f32>, EmbedError> {
let mut results = self.embed(&[text])?;
results
.pop()
.ok_or_else(|| EmbedError::Fastembed(anyhow::anyhow!("empty embedding result")))
}
pub fn to_vector32_json(v: &[f32]) -> String {
let nums: Vec<String> = v.iter().map(|f| f.to_string()).collect();
format!("[{}]", nums.join(","))
}
}