use anyhow::{Result, anyhow}; pub trait TextEmbeddingBackend { fn embed_texts(&mut self, input: &[String]) -> Result>>; } pub fn l2_normalize(v: &mut [f32]) { let norm = v.iter().map(|x| x * x).sum::().sqrt(); if norm > 0.0 { for x in v.iter_mut() { *x /= norm; } } } pub fn mean_pool(embeddings: &[Vec]) -> Result> { let first = embeddings .first() .ok_or_else(|| anyhow!("embedding hidden state is empty"))?; let mut pooled = vec![0f32; first.len()]; for row in embeddings { if row.len() != first.len() { return Err(anyhow!("inconsistent embedding width in hidden state")); } for (idx, value) in row.iter().enumerate() { pooled[idx] += *value; } } let inv = 1.0f32 / embeddings.len() as f32; for value in &mut pooled { *value *= inv; } Ok(pooled) } pub fn cosine_similarity(lhs: &[f32], rhs: &[f32]) -> Result { if lhs.len() != rhs.len() { return Err(anyhow!("embedding dimension mismatch")); } Ok(lhs.iter().zip(rhs.iter()).map(|(l, r)| l * r).sum::()) }