use case Β· multi-model Β· MiniLM + DistilBERT
Rank by meaning, then read the mood. First MiniLM embeds a corpus of reviews and finds the entries closest to your query; then DistilBERT labels each hit POSITIVE or NEGATIVE. Search "battery life" and instantly see who's praising it and who's complaining β two tiny models, one on-device pipeline, no server.
This page loads two models (~25 MB + ~65 MB). A returning visitor with both cached sees them auto-initialise; a first-time visitor gets a Download control for each. Once both are ready, index the corpus once, then search β each hit is ranked by cosine similarity and tagged with its sentiment.
MiniLM embeddings (~25 MB)
DistilBERT sentiment (~65 MB)
Each model does the one thing it's good at, and the output of the first shapes the work of the second:
// 1. MiniLM β rank the corpus by meaning (cosine over 384-d embeddings)
const q = await embed([query]);
const hits = corpus
.map((t, i) => ({ t, sim: dot(q[0], corpusVecs[i]) }))
.sort((a, b) => b.sim - a.sim)
.slice(0, TOP_K);
// 2. DistilBERT β label just the top hits' tone (cheap: only TOP_K, not the whole corpus)
for (const hit of hits) hit.tone = await classify(hit.t); // POSITIVE / NEGATIVE
Only the top hits get the sentiment pass, so adding the second model costs almost nothing. Both run in their own Web Workers β the embedding worker and the sentiment worker β so the page stays responsive throughout.
Ranking and tone are independent signals: a highly-relevant hit can still be scathing. That's the point β you find what's about your query, then immediately see whether it's praise or a complaint, without reading every line.