use std::path::Path;
use anyhow::{Context, Result};
use super::KbEmbedder;
use crate::embed::{Embedder, EmbedderBackend, LocalBgeEmbedder, OpenAiEmbedder};
pub struct LocalKbEmbedder {
backend: EmbedderBackend,
dim: usize,
id: String,
}
impl LocalKbEmbedder {
pub fn load(model_dir: &Path) -> Result<Self> {
let inner = LocalBgeEmbedder::load(model_dir)
.with_context(|| format!("load BGE model from {}", model_dir.display()))?;
let dim = Embedder::dimension(&inner) as usize;
let name = model_dir
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("bge");
Ok(Self {
backend: EmbedderBackend::Local(inner),
dim,
id: format!("local-{name}-{dim}"),
})
}
pub fn remote_openai(
base_url: String,
model: String,
api_key: Option<String>,
dim: usize,
) -> Self {
let inner = OpenAiEmbedder::new(
api_key.unwrap_or_default(),
Some(model.clone()),
Some(base_url),
Some(dim as i32),
);
Self {
backend: EmbedderBackend::OpenAi(inner),
dim,
id: format!("remote-{model}-{dim}"),
}
}
}
impl KbEmbedder for LocalKbEmbedder {
fn embed_batch(&self, texts: &[String]) -> Result<Vec<Vec<f32>>> {
Ok(texts.iter().map(|t| self.backend.embed(t)).collect())
}
fn dimension(&self) -> usize {
self.dim
}
fn embedder_id(&self) -> &str {
&self.id
}
}