mod manifest;
#[cfg(feature = "onnx")]
mod onnx;
mod pull;
pub use manifest::{Manifest, ModelFile, DEFAULT_MODEL};
#[cfg(feature = "onnx")]
pub use onnx::{OnnxConfig, OnnxEmbedder};
pub use pull::pull;
#[doc(hidden)]
pub use pull::pull_files;
use crate::{Error, Result};
use std::path::PathBuf;
pub trait Embedder {
fn model_id(&self) -> &str;
fn dim(&self) -> usize;
fn embed_passages(&self, texts: &[String]) -> Result<Vec<Vec<f32>>>;
fn embed_query(&self, text: &str) -> Result<Vec<f32>>;
}
pub fn models_dir() -> Result<PathBuf> {
let (base, sub) = match std::env::var_os("PITH_HOME") {
Some(h) if !h.is_empty() => (PathBuf::from(h), "models"),
_ => (
PathBuf::from(std::env::var_os("HOME").unwrap_or_default()),
".pith/models",
),
};
if !base.is_absolute() {
return Err(Error::Invalid(
"cannot locate the models directory: set PITH_HOME or HOME to an absolute path".into(),
));
}
Ok(base.join(sub))
}
fn normalize(v: &mut [f32]) -> Result<()> {
if !v.iter().all(|x| x.is_finite()) {
return Err(Error::NonFinite);
}
let max = v.iter().fold(0.0f32, |m, x| m.max(x.abs()));
if max == 0.0 {
return Err(Error::EmptyVector);
}
v.iter_mut().for_each(|x| *x /= max);
let n = v.iter().map(|x| x * x).sum::<f32>().sqrt(); v.iter_mut().for_each(|x| *x /= n);
Ok(())
}
#[doc(hidden)]
pub fn cls_pool_normalize(
hidden: &[f32],
batch: usize,
seq: usize,
dim: usize,
) -> Result<Vec<Vec<f32>>> {
let want = batch.checked_mul(seq).and_then(|n| n.checked_mul(dim));
if seq == 0 || dim == 0 || want != Some(hidden.len()) {
return Err(Error::Embed(format!(
"hidden state has {} values, expected {batch}x{seq}x{dim}",
hidden.len()
)));
}
(0..batch)
.map(|b| {
let mut v = hidden[b * seq * dim..][..dim].to_vec();
normalize(&mut v)?;
Ok(v)
})
.collect()
}
#[doc(hidden)]
#[derive(Debug, Clone, Copy, Default)]
pub struct FakeEmbedder;
impl FakeEmbedder {
fn one(text: &str) -> Vec<f32> {
let mut bytes = [0u8; 384 * 4];
blake3::Hasher::new()
.update(text.as_bytes())
.finalize_xof()
.fill(&mut bytes);
let mut v: Vec<f32> = bytes
.as_chunks::<4>()
.0
.iter()
.map(|c| u32::from_le_bytes(*c) as f32 / u32::MAX as f32 - 0.5)
.collect();
normalize(&mut v).expect("hash output is finite and nonzero");
v
}
}
impl Embedder for FakeEmbedder {
fn model_id(&self) -> &str {
"fake-384"
}
fn dim(&self) -> usize {
384
}
fn embed_passages(&self, texts: &[String]) -> Result<Vec<Vec<f32>>> {
Ok(texts.iter().map(|t| Self::one(t)).collect())
}
fn embed_query(&self, text: &str) -> Result<Vec<f32>> {
Ok(Self::one(text))
}
}