- Ternlight permite realizar embeddings de texto y búsquedas por similitud dentro del navegador, sin llamadas al servidor, lo que facilita armar rápidamente una pequeña búsqueda semántica del lado del cliente
- El paquete base combina motor y pesos en 7 MB; la variante mini pesa 5 MB y funciona en CPU, sin GPU
- Tras instalar
@ternlight/base, al importar embed y similar se puede crear un flujo de búsqueda semántica en unas 3 líneas
- La llamada de ejemplo ordena los 3 mejores resultados en una lista de recetas y destaca unos 5 ms y cero llamadas de red
- La demo de búsqueda en la documentación de React ejecuta la búsqueda en el navegador cuando el usuario ingresa una pregunta, impulsada por @ternlight/mini, el tier de 5 MB
Embeddings que se resuelven dentro del navegador
- Ternlight se presenta como un “modelo de embeddings de 7 MB” que genera embeddings de texto en milisegundos y no llama al servidor
-
Características de ejecución
- Sin llamadas a API
- Motor + pesos: 7 MB
- Variante mini: 5 MB
- Embeddings rápidos: alrededor de 5 ms
- Solo CPU, sin GPU
Instalación y ejemplo de uso
- Se ofrece como un único paquete npm y puede usarse sin un paso separado de descarga del modelo ni servidor
- El comando de instalación es el siguiente
npm install @ternlight/base
- Desde
@ternlight/base se importan embed y similar para ejecutar una búsqueda semántica
import { embed, similar } from '@ternlight/base';
similar('easy weeknight dinner ideas', recipes, { topK: 3 });
// → ranked matches · ~5 ms · zero network
- La demo de búsqueda en la documentación de React realiza la búsqueda en el navegador cuando el usuario ingresa una pregunta, y está impulsada por el tier de 5 MB @ternlight/mini
1 comentarios
Opiniones de Hacker News
Quería probar un modelo útil en el navegador como proyecto personal, así que destilé un codificador de oraciones pequeño a partir de MiniLM y apliqué entrenamiento consciente de cuantización ternaria.
También escribí el motor de inferencia yo mismo y lo distribuí como Rust → WASM SIMD.
No es un LLM, sino un modelo de embeddings: si ingresas texto, devuelve un vector de 384 dimensiones, y la relevancia entre textos se determina con la similitud coseno de dos vectores. Por ejemplo, "reset my password" y "I forgot my password" dan algo como 0.88.
Se puede usar para búsqueda semántica, matching de FAQ/intenciones y clustering; como corre en el dispositivo, permite búsqueda semántica rápida al ingresar datos, sin depender de una API.
La demo busca entre 2.000 documentos de React completamente en el dispositivo: https://ternlight-demo.vercel.app
En npm hay dos niveles: @ternlight/base (7 MB, aprox. 5 ms por embedding, embeddings de mejor rendimiento) y @ternlight/mini (5 MB transferidos, aprox. 2,5 ms por embedding), empaquetados para Node y navegador.
El repositorio incluye detalles técnicos, licencia MIT y el pipeline de entrenamiento: https://github.com/soycaporal/ternlight
Me da curiosidad si los embeddings en el dispositivo son realmente útiles y qué casos de uso hay.
Me pregunto si ayudaría a que, cuando el usuario escriba "pancake", encuentre crêpe sin tener que poner explícitamente una entrada de diccionario "pancake = crêpe".
Si entendí bien, también me pregunto si la estructura es que la biblioteca descarga 5 MB una vez al inicio y luego se usa como ahora uso Fuse.js.
También quisiera saber qué tan bien maneja idiomas distintos del inglés y si se podría entrenar con la wiki de etiquetas de OpenStreetMap.
Me pregunto si hay comparaciones con otros modelos de embeddings ultrapequeños. Es difícil saber si empezaron con MiniLM-L6 porque es un modelo particularmente bueno en esa categoría, ya que la única métrica provista es "Retrieval (SciFact NDCG@10)".
Dicho eso, veo una diferencia considerable respecto del rendimiento declarado: en Firefox sobre un i5-4570 obtengo solo 35 embeddings por segundo, no 400. Sospecho que puede estar cayendo a una ruta sin SIMD, y también voy a probar el binario nativo en Rust.
Está genial, pero estaría bueno poner un botón en la landing page para iniciar la demo. Me sorprendió bastante que, apenas abrí la página, el ventilador empezara a sonar como loco.
Sería bueno convertirlo en un plugin para Astro o para metaframeworks generales, que parseara automáticamente todos los archivos HTML generados y creara una pequeña base de datos de embeddings.
En el frontend se podría cargar de forma diferida, y tal vez HNSW también podría guardarse por chunks para cargar solo las partes necesarias para la consulta de búsqueda.
Sería algo parecido a https://pagefind.app/, por ejemplo, pero ofreciendo búsqueda vectorial completamente estática.
Me decepcionó bastante que, si después de meses o años seguía así, pareciera una señal de que al proyecto le falta capacidad para completarlo bien. Incluso recomendé ese proyecto como buen candidato para una beca que apoyé; ellos fueron seleccionados y yo no.
Si alguien conoce una buena solución en este espacio, o si me equivoco sobre SQLite-vec, me gustaría saberlo. En nuestro SSG casi decidimos trabajar unos meses en otra infraestructura y, si para entonces todavía no está listo, hacerlo nosotros mismos.
Podría ser un agregado bastante interesante para aquel proyecto de búsqueda HNSW con DuckDB que vi antes por aquí: https://github.com/jasonjmcghee/portable-hnsw
Es muy interesante que la búsqueda ocurra sobre archivos Parquet alojados estáticamente usando solicitudes HTTP de rango.
Creo que cosas como estas podrían crecer hasta convertirse en un ecosistema de búsqueda relativamente abierto y distribuido, no controlado por grandes empresas.
https://news.ycombinator.com/item?id=27016630
Esto está realmente genial, y podría ser la pieza que faltaba para algo que quise construir antes.
Con https://github.com/npiesco/absurder-sql se puede persistir todo el corpus original dentro del navegador en IndexedDB/SQLite.
Luego, como en https://weaviate.io/blog/chunking-strategies-for-rag, se podría usar Ternlight para generar y cachear embeddings cuando haga falta, en vez de indexar todo de antemano.
Eso también permitiría búsqueda híbrida con Reciprocal Rank Fusion, combinando FTS5/BM25 de SQLite nativo con la búsqueda semántica de Ternlight.
Está bien hecho.
Se promociona como de 7 MB, pero también hay una versión mini de 5 MB.
Parece que mini usa internamente vectores de 256 elementos en vez de 384 para reducir espacio, y al final los proyecta a 384 por compatibilidad.
El tamaño baja un tercio, pero la pérdida no es lineal, así que, aunque use una ruta de datos más pequeña, la pérdida de información parece ser menor que un tercio.
Gran proyecto.
Antes intenté algo parecido: http://sol.quipu-strands.com/
Quería cargar un modelo de embeddings dentro del navegador para ordenar textos semánticamente.
Traje pesos ONNX (MPNet, MiniLM) desde HuggingFace, generé embeddings con Transformers.js y luego usé un clusterizador de scikit-learn ejecutado con pyodide dentro de la página. Todo corría del lado del cliente, y me sorprendió que funcionara perfectamente.
La demo se comporta de forma bastante rara. Por ejemplo, si busco "how to use typescript with createContext", los primeros resultados son solo entradas de typescript, así que parece que la búsqueda por similitud falla.
Gracias. Los modelos locales algún día traerán privacidad, y ya conozco un gran caso de uso que encaja perfecto con estos modelos de embeddings pequeños: búsqueda barata y rápida en una base de datos de productos.
En mi caso, que dependa de la CPU también es una ventaja.
¿Se puede pregenerar la generación de embeddings que toma 30 segundos y enviarla al navegador?
Después la inferencia es rápida y buena.