toolhub-recommender 0.1.0

Hybrid FTS + sqlite-vec cosine recommender with success-rate reranker for toolhub
Documentation
use std::path::PathBuf;

use anyhow::Context;
use fastembed::{EmbeddingModel, InitOptions, TextEmbedding};

pub struct Embedder {
    model: TextEmbedding,
}

impl Embedder {
    pub fn new() -> anyhow::Result<Self> {
        let cache = cache_dir();
        std::fs::create_dir_all(&cache).ok();
        let opts = InitOptions::new(EmbeddingModel::BGESmallENV15)
            .with_cache_dir(cache)
            .with_show_download_progress(false);
        let model = TextEmbedding::try_new(opts).context("init bge-small-en-v1.5 model")?;
        Ok(Self { model })
    }

    pub fn embed_one(&self, text: &str) -> anyhow::Result<Vec<f32>> {
        let mut out = self.model.embed(vec![text.to_string()], None)?;
        out.pop()
            .ok_or_else(|| anyhow::anyhow!("embed() returned no vectors"))
    }

    pub fn embed_batch(&self, texts: Vec<String>) -> anyhow::Result<Vec<Vec<f32>>> {
        self.model.embed(texts, None)
    }
}

fn cache_dir() -> PathBuf {
    std::env::var("XDG_CACHE_HOME")
        .map(PathBuf::from)
        .ok()
        .or_else(|| {
            std::env::var("HOME")
                .ok()
                .map(|h| PathBuf::from(h).join(".cache"))
        })
        .unwrap_or_else(|| PathBuf::from("."))
        .join("toolhub")
        .join("models")
}