use std::sync::Arc;
use anyhow::Result;
use derive_builder::Builder;
use fastembed::{SparseTextEmbedding, TextEmbedding};
pub use swiftide_core::EmbeddingModel as _;
pub use swiftide_core::SparseEmbeddingModel as _;
mod embedding_model;
mod rerank;
mod sparse_embedding_model;
pub use rerank::Rerank;
pub enum EmbeddingModelType {
Dense(TextEmbedding),
Sparse(SparseTextEmbedding),
}
impl From<TextEmbedding> for EmbeddingModelType {
fn from(val: TextEmbedding) -> Self {
EmbeddingModelType::Dense(val)
}
}
impl From<SparseTextEmbedding> for EmbeddingModelType {
fn from(val: SparseTextEmbedding) -> Self {
EmbeddingModelType::Sparse(val)
}
}
const DEFAULT_BATCH_SIZE: usize = 256;
#[derive(Builder, Clone)]
#[builder(
pattern = "owned",
setter(strip_option),
build_fn(error = "anyhow::Error")
)]
pub struct FastEmbed {
#[builder(
setter(custom),
default = "Arc::new(tokio::sync::Mutex::new(TextEmbedding::try_new(Default::default())?.into()))"
)]
embedding_model: Arc<tokio::sync::Mutex<EmbeddingModelType>>,
#[builder(default = "Some(DEFAULT_BATCH_SIZE)")]
batch_size: Option<usize>,
}
impl std::fmt::Debug for FastEmbed {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FastEmbedBuilder")
.field("batch_size", &self.batch_size)
.finish()
}
}
impl FastEmbed {
pub fn try_default() -> Result<Self> {
Self::builder().build()
}
pub fn try_default_sparse() -> Result<Self> {
Self::builder()
.embedding_model(SparseTextEmbedding::try_new(
fastembed::SparseInitOptions::default(),
)?)
.build()
}
pub fn builder() -> FastEmbedBuilder {
FastEmbedBuilder::default()
}
}
impl FastEmbedBuilder {
#[must_use]
pub fn embedding_model(mut self, fastembed: impl Into<EmbeddingModelType>) -> Self {
self.embedding_model = Some(Arc::new(tokio::sync::Mutex::new(fastembed.into())));
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[ignore = "downloads HuggingFace embedding models and can hit external rate limits"]
#[tokio::test]
async fn test_fastembed() {
let fastembed = FastEmbed::try_default().unwrap();
let embeddings = fastembed.embed(vec!["hello".to_string()]).await.unwrap();
assert_eq!(embeddings.len(), 1);
}
#[ignore = "downloads HuggingFace embedding models and can hit external rate limits"]
#[tokio::test]
async fn test_sparse_fastembed() {
let fastembed = FastEmbed::try_default_sparse().unwrap();
let embeddings = fastembed
.sparse_embed(vec!["hello".to_string()])
.await
.unwrap();
assert!(embeddings[0].values.len() > 1);
assert!(embeddings[0].values.len() < 100);
assert_eq!(embeddings[0].indices.len(), embeddings[0].values.len());
}
}