Glint-Embed

An 8.41M-parameter static text embedding model for retrieval. It is 33.6 MB on disk, encodes about 17,800 documents a second on a laptop CPU, and with hybrid scoring it ties Qwen3-Embedding-8B on SciQ Recall@1 (88.9 against 88.9) at roughly one nine-hundredth of the parameters.

parameters 8,406,016
output dimensions 512, truncatable to 256, 128, 64, or 32
model file 33.6 MB
vocabulary 16,384-token BPE, English
document window 128 tokens
query window 32 tokens
CPU throughput 17,831 documents/second

A smaller sibling, Glint-Embed-Flash (1.07M parameters, 64 dimensions, 4.3 MB), is released separately and is compared against this model throughout. This is the one to reach for by default. Take Flash instead if you are storing 64 dimensions anyway, running on constrained hardware, or indexing very large corpora on CPU.

Quick start

pip install -r requirements.txt

python infer.py --similarity "how do i set up postgres" "postgresql installation guide"
python infer.py --encode "a sentence to embed"
python infer.py --folder ./my-notes --query "what were the deployment steps"

Everything needed to run the model is in this directory. infer.py imports only torch and tokenizers, and carries its own copy of the model definition, so there is no project to clone and no package to install.

from infer import Encoder, Retriever

encoder = Encoder()
corpus = ["Postgres tuning notes ...", "Kubernetes ingress setup ...", "..."]

retriever = Retriever(corpus, encoder)
for score, index, text in retriever.search("how do i tune postgres", top_k=5):
    print(score, text[:80])

vectors = encoder.encode(corpus)                  # (n, 512), L2 normalised
queries = encoder.encode(["a query"], is_query=True)
small = encoder.encode(corpus, dim=64)            # Matryoshka prefix, 8x smaller

Accuracy

Every number below was measured on the same machine and the same benchmark. None are quoted from a leaderboard, and each reference model was run with the pooling and prompt format its authors specify.

Retrieval accuracy

The benchmark is 1,000 SQuAD-dev questions and 884 SciQ-test questions searching a shared corpus of 6,951 documents: their gold passages plus 4,000 FineWeb-Edu distractors.

model parameters SQuAD R@1 SQuAD MRR@10 SciQ R@1 SciQ MRR@10
Glint-Embed hybrid 8.41M 58.2 68.2 88.9 91.9
Glint-Embed-Flash hybrid 1.07M 56.4 66.4 88.6 91.3
BM25 alone 0 53.9 63.4 87.4 90.0
all-MiniLM-L6-v2 22.7M 60.3 71.2 77.4 84.4
nomic-embed-text-v1.5 137M 63.6 75.0 84.6 89.3
Qwen3-Embedding-0.6B 596M 65.6 75.3 87.0 91.4
Qwen3-Embedding-8B 7.57B 72.8 81.8 88.9 92.6

Two things are worth reading carefully here. First, this model matches the 8B model on SciQ and beats every other transformer in the table on it, because those queries are short science questions where term overlap carries most of the signal. Second, it loses SQuAD by 14.6 points to Qwen3-8B, because SQuAD passages are long and full of near-miss distractors, and a static model has no attention and so no representation of word order. That limit is structural. It will not close with a longer training run.

Accuracy per parameter

Results on a real corpus

Benchmarks are built to be measured against. To check the model on prose written for other reasons, we indexed a personal Obsidian vault of 3,477 notes (10,355 chunks) and derived queries from the notes' own structure: a note title, a natural question templated from a section heading, and a sentence held out of the chunk it came from.

Real corpus results

query kind Glint-Embed hybrid BM25 MiniLM-L6 Qwen3-Embed 0.6B
title lookup 64.0 59.8 79.5 84.8
question from heading 55.5 44.5 76.0 79.3
held-out sentence 99.0 98.8 72.0 78.0

The split is consistent enough to plan around. Where the query shares wording with the target, this model is at the top of the table. Where the query is a natural question phrased differently from the passage, the transformers lead by 20 to 25 points. For a RAG stack over documents like these, the recommended shape is two stages: this model as the first-stage filter over the whole corpus, and a transformer reranking the top 20 to 50 candidates for question-shaped queries.

Hybrid retrieval is the default

Retriever blends the dense score with Okapi BM25, both z-scored, with alpha weighting the dense side. This is what the published numbers use, and the margin is large: hybrid is worth 12.9 Recall@1 over dense alone on SQuAD (58.2 against 45.3) and 10.4 on SciQ. Anything between alpha=0.5 and alpha=0.6 performs the same.

Dense-only and BM25-only modes are available for comparison:

python infer.py --folder ./notes --query "..." --mode dense
python infer.py --folder ./notes --query "..." --mode bm25 --top-k 10
python infer.py --folder ./notes --query "..." --alpha 0.7

BM25 and the blend both live in infer.py. There is no search service to run.

Matryoshka dimensions

The model was trained so that any prefix of the output vector is a working embedding. Storing 64 dimensions instead of 512 cuts index size by 8x and costs 14.7 Recall@1 on SQuAD. Pick the width against your storage budget with this table in hand.

Truncation curve

dimensions index size per 1M docs SQuAD R@1 SciQ R@1
32 128 MB 23.2 55.5
64 256 MB 30.6 66.9
128 512 MB 37.7 74.0
256 1.0 GB 42.8 77.0
512 2.0 GB 45.3 78.5

These are dense-only figures, without the BM25 blend. Note the crossover: at 64 dimensions this model scores 30.6 SQuAD while Glint-Embed-Flash scores 31.3 at its native width. If your storage budget is 64 dimensions, ship Flash instead and save 29 MB.

Throughput

Encoding throughput

Measured on one desktop CPU, batches of 6,951 documents at a 128-token window. The 10,355-chunk vault above indexes in under a second. The transformer rows are shown for scale, with the two Qwen rows measured on a GPU and the rest on the same CPU.

How the model works

A static embedder looks up one vector per token, weights each token by a learned scalar initialised from corpus IDF, takes the masked mean, applies LayerNorm, and L2 normalises. There is no attention, no positional encoding, and no layer stack, so cost scales with the number of tokens rather than with their square, and almost all of the parameter count sits in a lookup table that never runs a matmul. This is the same family as Model2Vec and potion.

Training took nine CPU minutes against 600,000 query-passage pairs from SQuAD, SciQ, and inverse-cloze crops of FineWeb-Edu, with a symmetric in-batch InfoNCE loss averaged over the Matryoshka prefixes. The embedding table was initialised by embedding each token of the vocabulary on its own with all-MiniLM-L6-v2, and the model additionally regressed its query-passage similarity geometry toward Qwen3-Embedding-0.6B.

No evaluation data was trained on. The benchmark corpus and the vault were held out of every training pass.

Files

infer.py                        standalone inference, BM25, and hybrid retrieval
requirements.txt                torch and tokenizers
models/glint-embed.pt           weights and config
models/tokenizer.json           16,384-token BPE vocabulary
charts/                         the figures above

The checkpoint holds plain tensors and a JSON-safe config dict, loadable with torch.load(..., weights_only=True). The model definition is about 20 lines at the top of infer.py if you would rather port it than import it.

Limits

  • English only. The vocabulary is a 16,384-token BPE trained on English text.
  • 128-token document window and a 32-token query window. Chunk longer documents, which is what read_folder in infer.py does.
  • No instruction prefixes or task prompts. This is a single-purpose retrieval encoder.
  • Symmetric tasks such as clustering and deduplication were not measured. The training objective was asymmetric retrieval.
  • The word-order limit described under Accuracy is structural to the architecture.
Downloads last month
1
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support