mod deepseek;
mod local;
mod mock;
mod openai;
mod qwen;
pub use deepseek::{DeepSeekEmbeddings, DeepSeekEmbeddingsConfig, DEEPSEEK_EMBED_MODEL};
pub use local::{BagOfWordsEmbeddings, LocalEmbeddings};
pub use mock::MockEmbeddings;
pub use openai::{OpenAIEmbeddings, OpenAIEmbeddingsConfig};
pub use qwen::{QwenEmbeddings, QwenEmbeddingsConfig, QWEN_EMBED_MODEL};
use async_trait::async_trait;
#[derive(Debug, thiserror::Error)]
pub enum EmbeddingError {
#[error("HTTP error: {0}")]
HttpError(String),
#[error("API error: {0}")]
ApiError(String),
#[error("Parse error: {0}")]
ParseError(String),
#[error("Input is empty")]
EmptyInput,
}
#[async_trait]
pub trait Embeddings: Send + Sync {
async fn embed_query(&self, text: &str) -> Result<Vec<f32>, EmbeddingError>;
async fn embed_documents(&self, texts: &[&str]) -> Result<Vec<Vec<f32>>, EmbeddingError> {
let mut embeddings = Vec::new();
for text in texts {
embeddings.push(self.embed_query(text).await?);
}
Ok(embeddings)
}
fn dimension(&self) -> usize;
fn model_name(&self) -> &str;
}
pub use crate::core::math::cosine_similarity;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cosine_similarity() {
let a = vec![1.0, 0.0, 0.0];
let b = vec![1.0, 0.0, 0.0];
assert!((cosine_similarity(&a, &b).unwrap() - 1.0).abs() < 0.0001);
let a = vec![1.0, 0.0, 0.0];
let b = vec![0.0, 1.0, 0.0];
assert!((cosine_similarity(&a, &b).unwrap() - 0.0).abs() < 0.0001);
let a = vec![1.0, 0.0, 0.0];
let b = vec![-1.0, 0.0, 0.0];
assert!((cosine_similarity(&a, &b).unwrap() - (-1.0)).abs() < 0.0001);
}
#[test]
fn test_cosine_similarity_different_lengths() {
let a = vec![1.0, 0.0];
let b = vec![1.0, 0.0, 0.0];
assert!(cosine_similarity(&a, &b).is_err());
}
}