Ternlight: Cómo Instalar un Motor de Embeddings de 7 MB que Corre Enteramente en tu Navegador

Ternlight: Cómo Instalar un Motor de Embeddings de 7 MB que Corre Enteramente en tu Navegador

La búsqueda semántica casi siempre asume un servidor en el medio: hacés embed de tus documentos a través de una API, hacés embed de la query del usuario con esa misma API, pagás por token, y esperás que el round trip sea lo suficientemente rápido como para sentirse instantáneo. Ternlight, un proyecto que llegó a Hacker News esta semana, se salta esa suposición por completo. Empaqueta un sentence encoder funcional en un bundle de WebAssembly de 7 MB que corre todo el pipeline de embeddings en la CPU del propio usuario — sin llamadas de red después de la carga inicial de la página, sin costo por token, sin backend que mantener.

Qué es en realidad

Ternlight es un sentence encoder destilado de all-MiniLM-L6-v2 usando quantization-aware training al estilo de la investigación BitNet b1.58 de Microsoft — es decir, cada weight queda restringido a solo tres valores (-1, 0, +1) desde el inicio del entrenamiento, en vez de comprimirse después. Esa es la diferencia entre un modelo que se construyó para ser chico y uno que se achicó a la fuerza, y es la razón por la que Ternlight mantiene calidad pese a la compresión extrema.

El motor de inferencia es Rust escrito a mano y compilado a WASM con soporte SIMD, y el modelo, el tokenizer y el motor van todos dentro de un único archivo .wasm — sin descarga de modelo en runtime, sin paso de postinstall. Le metés texto y te devuelve un vector de 384 dimensiones; comparás dos vectores con cosine similarity y obtenés un score de relación semántica incluso cuando las palabras no coinciden en nada (el ejemplo del README: “reset my password” e “I forgot my password” dan un score de ~0.88).

Hay dos tiers publicados, misma API:

npm install @ternlight/base   # 7 MB en el wire, ~5 ms/embedding (reportado por el autor)
npm install @ternlight/mini   # 5.5 MB en el wire, ~2 ms/embedding, calidad levemente menor
import { embed, cosineSim } from '@ternlight/base';

const v1 = await embed("reset my password");
const v2 = await embed("I forgot my password");
cosineSim(v1, v2); // ~0.88

// Armando un índice de búsqueda local simple
const index = docs.map((d) => ({ d, v: embed(d.text) }));
const q = embed(query);
index.sort((a, b) => cosineSim(q, b.v) - cosineSim(q, a.v));

Corre en cualquier lugar donde haya V8 o un motor de browser — Node ≥18, Cloudflare Workers, Vercel Edge, Deno, Bun, y cualquier frontend con bundler (Vite necesita vite-plugin-wasm; Webpack 5 necesita asyncWebAssembly: true). El demo en vivo indexa 2.000 páginas de documentación de React y corre search-as-you-type enteramente del lado del cliente.

Dónde esto es realmente útil

Como nada sale del navegador, Ternlight encaja de forma natural en: widgets de búsqueda de FAQ y soporte, apps de notas o knowledge bases que quieren búsqueda offline, extensiones de navegador, y cualquier caso donde prefieras no mandar el texto del usuario — notas de RRHH, borradores legales, tickets de soporte — a una API de embeddings de terceros. Es una capa de retrieval, no de generación: el patrón natural es “hacé embed y ranking localmente, y mandá solo los top resultados a una API de LLM si necesitás una respuesta generada.”

Qué tener en cuenta antes de usarlo

Antes de tratar esto como infraestructura lista para producción, van algunas cosas para tener en cuenta:

  • Es recién nacido. El repositorio se publicó junto con el lanzamiento en Hacker News esta semana. El código es real y el demo funciona, pero es tracción de día uno, no una librería probada en batalla — conviene tratarlo como un proyecto temprano y en movimiento rápido, no como infraestructura asentada.
  • Los benchmarks son self-reported. Números como el score de correlación de Spearman de ~0.844 y las cifras de velocidad de embedding salen de la carpeta de evals del propio autor. El autor comentó que las comparaciones directas contra modelos de tamaño similar como gte-small están en el roadmap pero todavía no se publicaron — así que “cómo se compara contra alternativas” es una pregunta abierta, no algo zanjado.
  • El rendimiento puede variar más de lo que sugieren los números principales. Al menos un comentario en el thread de Hacker News reportó velocidades de embedding muy por debajo de lo que reclama el autor en su hardware, posiblemente relacionado a un fallback sin SIMD. Si el throughput te importa, benchmarkealo en tus browsers objetivo reales — especialmente Safari mobile en dispositivos viejos — antes de comprometerte.

Nada de esto descalifica al proyecto — un ternary WASM encoder a escala hobby, con un demo funcional y un autor que responde activamente en los comentarios, es un logro técnico genuinamente interesante. Solo significa que por ahora el marco correcto es “interesante para seguir de cerca y prototipar”, no “reemplazá tu API de embeddings hoy.”