SPB Git forge

spb/immbot-ai

Public
1commits 1branches 0releases
1.5 MBsize
maindefault branch
20 days agolast push
TypeScript 98.3% CSS 0.9% Shell 0.7%
2.2 KB · 61 lines typescript
Raw Blame History
1// Embeddings locaux via transformers.js — multilingual-e5-small (384 dimensions).2// Gratuit, privé, hors-ligne après le premier téléchargement (OpenRouter n'offre pas d'embeddings).3// Convention E5 : préfixes "query: " et "passage: ".45import { resolve } from "node:path";67export const EMBEDDING_DIM = 384;8const MODEL_ID = "Xenova/multilingual-e5-small";910type FeatureExtractor = (texts: string[], opts: { pooling: "mean"; normalize: boolean }) => Promise<{11  tolist(): number[][];12}>;1314let _extractor: Promise<FeatureExtractor> | null = null;1516async function extractor(): Promise<FeatureExtractor> {17  if (!_extractor) {18    _extractor = (async () => {19      const { pipeline, env } = await import("@huggingface/transformers");20      env.cacheDir = resolve(process.cwd(), "data/models");21      const p = await pipeline("feature-extraction", MODEL_ID, { dtype: "fp32" });22      return p as unknown as FeatureExtractor;23    })();24  }25  return _extractor;26}2728export async function embedPassages(texts: string[]): Promise<Float32Array[]> {29  const ex = await extractor();30  const out: Float32Array[] = [];31  const BATCH = 16;32  for (let i = 0; i < texts.length; i += BATCH) {33    const batch = texts.slice(i, i + BATCH).map((t) => "passage: " + t.slice(0, 2000));34    const res = await ex(batch, { pooling: "mean", normalize: true });35    for (const v of res.tolist()) out.push(Float32Array.from(v));36  }37  return out;38}3940export async function embedQuery(text: string): Promise<Float32Array> {41  const ex = await extractor();42  const res = await ex(["query: " + text.slice(0, 2000)], { pooling: "mean", normalize: true });43  return Float32Array.from(res.tolist()[0]);44}4546// --- conversions blob <-> vecteur (SQLite stocke des BLOB) ---47export function vecToBlob(v: Float32Array): Uint8Array {48  return new Uint8Array(v.buffer.slice(0), 0, v.length * 4);49}50export function blobToVec(b: Uint8Array): Float32Array {51  const buf = b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength);52  return new Float32Array(buf);53}5455/** Similarité cosinus — les vecteurs E5 sont déjà normalisés → produit scalaire. */56export function cosine(a: Float32Array, b: Float32Array): number {57  let s = 0;58  for (let i = 0; i < a.length; i++) s += a[i] * b[i];59  return s;60}61