use anyhow::{Context, Result};
use fastembed::TextEmbedding;
use tracing::debug;
use crate::backends::{GGUFEmbeddingModel, Qwen3EmbeddingModel};
use crate::models::EmbeddingModel;
pub trait BatchProcessor<T> {
fn process_batch(&mut self, texts: &[T]) -> Result<Vec<Vec<f32>>>;
fn backend_name(&self) -> &'static str;
}
pub fn process_texts_in_batches<T, P: BatchProcessor<T>>(
processor: &mut P,
texts: &[T],
batch_size: usize,
) -> Result<Vec<Vec<f32>>> {
let mut all_embeddings = Vec::with_capacity(texts.len());
let backend_name = processor.backend_name();
for chunk in texts.chunks(batch_size) {
debug!("Processing {} batch of {} texts", backend_name, chunk.len());
let batch_embeddings = processor.process_batch(chunk).with_context(|| {
format!(
"Failed to generate {} embeddings for batch of {} texts",
backend_name,
chunk.len()
)
})?;
all_embeddings.extend(batch_embeddings);
}
debug!(
"Generated {} {} embeddings total",
all_embeddings.len(),
backend_name
);
Ok(all_embeddings)
}
impl BatchProcessor<String> for TextEmbedding {
fn process_batch(&mut self, texts: &[String]) -> Result<Vec<Vec<f32>>> {
let text_refs: Vec<&str> = texts.iter().map(|s| s.as_str()).collect();
self.embed(text_refs, None)
}
fn backend_name(&self) -> &'static str {
"FastEmbed"
}
}
impl BatchProcessor<String> for GGUFEmbeddingModel {
fn process_batch(&mut self, texts: &[String]) -> Result<Vec<Vec<f32>>> {
self.embed(texts)
}
fn backend_name(&self) -> &'static str {
"GGUF"
}
}
impl BatchProcessor<String> for Qwen3EmbeddingModel {
fn process_batch(&mut self, texts: &[String]) -> Result<Vec<Vec<f32>>> {
self.embed(texts)
}
fn backend_name(&self) -> &'static str {
"HuggingFace"
}
}