steeldb/text/
model2vec.rs1use std::error::Error;
9use std::path::Path;
10use tokenizers::Tokenizer;
11
12pub struct Model2Vec {
13 tok: Tokenizer,
14 mat: Vec<f32>,
15 rows: usize,
16 dim: usize,
17}
18
19fn l2_normalize(v: &mut [f32]) {
20 let mut n = 0.0f32;
21 for &x in v.iter() {
22 n += x * x;
23 }
24 n = n.sqrt() + 1e-9;
25 for x in v.iter_mut() {
26 *x /= n;
27 }
28}
29
30impl Model2Vec {
31 pub fn load(dir: &Path) -> Result<Model2Vec, Box<dyn Error + Send + Sync>> {
33 let bytes = std::fs::read(dir.join("potion.f32"))?;
34 if bytes.len() < 8 {
35 return Err("potion.f32 too short".into());
36 }
37 let rows = u32::from_le_bytes(bytes[0..4].try_into().unwrap()) as usize;
38 let dim = u32::from_le_bytes(bytes[4..8].try_into().unwrap()) as usize;
39 let need = rows * dim * 4;
40 if bytes.len() < 8 + need {
41 return Err(format!("potion.f32 truncated: want {} floats", rows * dim).into());
42 }
43 let mut mat = Vec::with_capacity(rows * dim);
44 for chunk in bytes[8..8 + need].chunks_exact(4) {
45 mat.push(f32::from_le_bytes(chunk.try_into().unwrap()));
46 }
47 let tok = Tokenizer::from_file(dir.join("tokenizer.json"))?;
48 Ok(Model2Vec { tok, mat, rows, dim })
49 }
50
51 pub fn dim(&self) -> usize {
52 self.dim
53 }
54 pub fn rows(&self) -> usize {
55 self.rows
56 }
57
58 pub fn embed(&self, text: &str) -> Option<Vec<f32>> {
60 let enc = self.tok.encode(text, false).ok()?;
61 let mut acc = vec![0.0f32; self.dim];
62 let mut n = 0usize;
63 for &id in enc.get_ids() {
64 let id = id as usize;
65 if id >= self.rows {
66 continue;
67 }
68 let off = id * self.dim;
69 let row = &self.mat[off..off + self.dim];
70 for i in 0..self.dim {
71 acc[i] += row[i];
72 }
73 n += 1;
74 }
75 if n == 0 {
76 return None;
77 }
78 for x in acc.iter_mut() {
79 *x /= n as f32;
80 }
81 l2_normalize(&mut acc);
82 Some(acc)
83 }
84
85 pub fn token_count(&self, text: &str) -> usize {
87 self.tok.encode(text, false).map(|e| e.get_ids().len()).unwrap_or(0)
88 }
89}
90
91pub fn cosine(a: &[f32], b: &[f32]) -> f32 {
92 let mut s = 0.0f32;
93 for i in 0..a.len().min(b.len()) {
94 s += a[i] * b[i];
95 }
96 s
97}