- Ternlight permet d’effectuer des embeddings de texte et des recherches de similarité directement dans le navigateur, sans appel serveur, afin de mettre en place rapidement une petite recherche sémantique côté client
- Le package de base pèse 7 Mo en combinant moteur et poids, tandis que la variante mini fait 5 Mo, et fonctionne sur CPU sans GPU
- Après installation de
@ternlight/base, il suffit d’importer embed et similar pour créer un flux de recherche sémantique en environ 3 lignes
- L’exemple d’appel classe les 3 meilleurs résultats dans une liste de recettes, avec environ 5 ms et aucun appel réseau mis en avant
- La démo de recherche dans la documentation React effectue la recherche dans le navigateur lorsque l’utilisateur saisit une question, et elle est propulsée par @ternlight/mini, le palier à 5 Mo
Des embeddings entièrement dans le navigateur
- Ternlight met en avant un « modèle d’embeddings de 7 Mo » qui encode du texte en quelques millisecondes, sans appeler de serveur
-
Caractéristiques d’exécution
- Aucun appel API
- Moteur + poids : 7 Mo
- Variante mini : 5 Mo
- Embeddings rapides : environ 5 ms
- CPU uniquement, pas de GPU
Installation et exemple d’utilisation
- Il est fourni sous forme d’un unique package npm et peut être utilisé sans étape séparée de téléchargement de modèle ni serveur
- La commande d’installation est la suivante
npm install @ternlight/base
- Importez
embed et similar depuis @ternlight/base pour exécuter une recherche sémantique
import { embed, similar } from '@ternlight/base';
similar('easy weeknight dinner ideas', recipes, { topK: 3 });
// → ranked matches · ~5 ms · zero network
- La démo de recherche dans la documentation React effectue la recherche dans le navigateur lorsque l’utilisateur saisit une question, et fonctionne avec le palier 5 Mo @ternlight/mini
1 commentaires
Avis sur Hacker News
Pour un projet perso, je voulais faire tourner un modèle utile dans le navigateur ; j’ai donc distillé un petit encodeur de phrases à partir de MiniLM, puis appliqué un entraînement avec quantification ternaire consciente
J’ai aussi écrit moi-même le moteur d’inférence et je le déploie en Rust → WASM SIMD
Ce n’est pas un LLM, mais un modèle d’embeddings : quand on lui donne du texte, il produit un vecteur à 384 dimensions, et on juge la pertinence entre deux textes via la similarité cosinus de deux vecteurs. Par exemple, « reset my password » et « I forgot my password » peuvent donner 0,88
Il peut servir à la recherche sémantique, au matching FAQ/intention et au clustering ; comme il s’exécute sur l’appareil, il permet une recherche sémantique rapide dès la saisie, sans dépendre d’une API
La démo recherche dans 2 000 documents React entièrement sur l’appareil : https://ternlight-demo.vercel.app
Sur npm, il y a deux niveaux : @ternlight/base (7 Mo, environ 5 ms par embedding, embeddings plus performants) et @ternlight/mini (5 Mo transférés, environ 2,5 ms par embedding), empaquetés pour Node et le navigateur
Le dépôt contient les détails techniques, la licence MIT et le pipeline d’entraînement : https://github.com/soycaporal/ternlight
Je me demande si les embeddings sur l’appareil sont réellement utiles, et pour quels cas d’usage
Je me demande si cela pourrait aider à trouver crêpe quand l’utilisateur saisit « pancake », sans devoir ajouter explicitement une entrée de dictionnaire « pancake = crêpe »
Si j’ai bien compris, la bibliothèque télécharge 5 Mo une première fois, puis on l’utilise ensuite un peu comme j’utilise Fuse.js aujourd’hui ?
J’aimerais aussi savoir dans quelle mesure elle gère les langues autres que l’anglais, et si on peut l’entraîner avec le wiki des tags OpenStreetMap
Je me demande s’il existe une comparaison avec d’autres modèles d’embeddings ultralégers. Difficile de savoir si le choix de partir de MiniLM-L6 vient du fait que c’est un modèle particulièrement bon dans cette catégorie, puisque la seule métrique fournie est « Retrieval (SciFact NDCG@10) »
Cela dit, je suis assez loin des performances annoncées : dans Firefox sur un i5-4570, je n’obtiens que 35 embeddings par seconde, pas 400. Je soupçonne un basculement vers un chemin sans SIMD, et je compte aussi essayer le binaire Rust natif
C’est cool, mais ce serait bien d’avoir un bouton pour lancer la démo sur la landing page. J’ai été assez surpris d’entendre les ventilateurs s’emballer dès l’ouverture de la page
Ce serait bien d’en faire un plugin Astro ou de méta-framework générique, qui parserait automatiquement tous les fichiers HTML générés et créerait une petite base de données d’embeddings
Côté frontend, on pourrait la charger paresseusement, et peut-être stocker aussi HNSW par chunks pour ne charger que les parties nécessaires à la requête
Par exemple, quelque chose de proche de https://pagefind.app/, mais avec une recherche vectorielle entièrement statique
Plusieurs mois, voire années, ont passé, et si c’est toujours le cas, c’est assez décevant : cela ressemble à un signe que le projet n’a pas les moyens de vraiment aboutir. En plus, je l’avais recommandé comme bon candidat dans une subvention à laquelle j’ai postulé ; eux ont été retenus et moi non
Si quelqu’un connaît une bonne solution dans ce domaine, ou si je me trompe au sujet de SQLite-vec, j’aimerais le savoir. Pour notre SSG, on a plus ou moins décidé de travailler quelques mois sur une autre infrastructure, puis de le construire nous-mêmes si ce n’est toujours pas prêt
Ça pourrait être un ajout vraiment sympa au projet de recherche HNSW avec DuckDB que j’avais vu ici auparavant : https://github.com/jasonjmcghee/portable-hnsw
Le fait que la recherche s’effectue sur des fichiers Parquet hébergés statiquement en utilisant des requêtes HTTP Range est vraiment intéressant
Je pense que ce genre de choses pourrait devenir un écosystème de recherche relativement ouvert et distribué, non contrôlé par les grandes entreprises
https://news.ycombinator.com/item?id=27016630
C’est vraiment cool, et ça pourrait être la pièce manquante d’une chose que je voulais construire depuis un moment
Avec https://github.com/npiesco/absurder-sql, on peut persister tout le corpus source dans IndexedDB/SQLite à l’intérieur du navigateur
Ensuite, comme dans https://weaviate.io/blog/chunking-strategies-for-rag, au lieu de tout indexer à l’avance, on peut utiliser Ternlight pour générer et mettre en cache les embeddings à la demande
Cela permettrait aussi une recherche hybride via Reciprocal Rank Fusion, en combinant FTS5/BM25 de SQLite natif avec la recherche sémantique de Ternlight
Beau travail
C’est présenté comme faisant 7 Mo, mais il existe aussi une version mini à 5 Mo
Il semble que mini utilise en interne des vecteurs de 256 éléments, et non 384, pour réduire l’espace, puis les projette à la fin en 384 pour des raisons de compatibilité
La taille baisse d’un tiers, mais la perte n’est pas linéaire ; même avec un chemin de données plus petit, la perte d’information semble inférieure à un tiers
Super projet
J’avais essayé quelque chose de similaire auparavant : http://sol.quipu-strands.com/
Je voulais charger un modèle d’embeddings dans le navigateur pour ordonner sémantiquement du texte
J’ai récupéré des poids ONNX (MPNet, MiniLM) depuis HuggingFace, généré les embeddings avec Transformers.js, puis utilisé un algorithme de clustering de scikit-learn exécuté dans la page via pyodide. Tout tournait côté client, et j’ai été surpris que ça fonctionne parfaitement
La démo se comporte de façon assez étrange. Par exemple, si l’on cherche « how to use typescript with createContext », les meilleurs résultats ne sont que des entrées typescript, ce qui donne l’impression que la recherche par similarité échoue
Merci. Les modèles locaux apporteront un jour la confidentialité, et je connais déjà un excellent cas d’usage qui convient parfaitement à ce genre de petits modèles d’embeddings : la recherche bon marché et rapide dans une base de données produits
Dans mon cas, le fait de s’appuyer sur le CPU est aussi un avantage
Peut-on précalculer la génération des embeddings qui prend 30 secondes et les envoyer au navigateur ?
Ensuite, l’inférence est rapide et agréable