Smaller models.
More possibilities.

thinletter is a Czech AI startup. We compress language models and represent them efficiently, and we tune them to a specific language or domain — measured against the unchanged system they have to fit into.

What we do

Three things, kept deliberately narrow.

Compression

Language models at 2–5 bits per weight that keep the quality of the original where it matters: in the retrieval results, not in a benchmark of the file.

How

We quantise the query encoder of an embedding model and leave the document index untouched: 235–385 MiB files instead of 1.1 GiB, running in llama.cpp and in the browser (WebAssembly or WebGPU). Below the smallest llama.cpp format we run a vector-quantised container at 1.8–2.1 bits per weight with our own WebGPU runtime.

Every file is verified on the index it has to serve: nDCG@10, cosine to the full-precision query vector, top-10 overlap, paired bootstrap over queries.

Language and domain

Quantisation calibrated on the language of your corpus — Czech first — and, where it pays, on your domain.

What we found

At low bit widths the language of the calibration text matters more than its domain: on a Czech legal index, calibrating on Czech instead of English text was worth +0.09 nDCG@10 at 2.6 bits, domain another +0.01–0.02. We release clients calibrated on Czech text for three model families; a Czech calibration does not cost English quality.

Tolerance to compression is a property of the checkpoint, not of the recipe: one 0.6B model holds 99 % at 3.4 bits, another of the same architecture fails below 4.5.

Evidence

Pre-registered predictions, paired comparisons, published negative results. Numbers you can reproduce on your own index.

How we measure

Each run has a prediction and a kill rule written down before it starts. Comparisons change one thing at a time at the same calibration budget. Differences are paired bootstrap intervals over queries (10 000 draws), read against calibration-draw variance (~0.01) and between-machine variation (±0.006): under 0.01 nDCG@10 is a tie.

Negative results stay in the report: a 2.6-bit headline number that did not generalise beyond one corpus, a rotation with no stable effect, a claim that held for one model and not for the recipe.

Results

Measured on the model's own full-precision index, test split. Click a number for the context.

Where the smallest files stand

client (SciFact, harrier-0.6b)MiB% of fp32 nDCG@10what it is
harrier-0.6b fp16, the server model1 143100 %reference
llama.cpp Q3_K with imatrix23598.9 %the free baseline; our GPTQ export ties it, we say so
llama.cpp IQ2_XS, its smallest format17893.9 %below this size llama.cpp has nothing
BitNet-270m, ternary, trained (Microsoft)14097.0 %the strongest small competitor; needs training
thinletter VQ 2.1 bits per weight12098.2 %ours: post-training, no retraining, WebGPU runtime
thinletter VQ 1.8 bits per weight10596.5 %ours
thinletter VQ 1.6 bits per weight9193.6 %ours, the smallest working client

nDCG@10 against the model's own fp32 index, SciFact test split (300 queries, fp32 0.7559). Our rows are torch simulations of the container with synthetic-query calibration; the released SciDocs files of the same recipe (127 / 113 MiB) hold 94.7 / 91.3 % and were verified in the browser to ±0.001. BitNet-270m and the llama.cpp files are measured as files.

98.2 %

SciFact test split, nDCG@10 against the model's own fp32 index (0.7559): 120 MiB at 2.1 bits per weight 98.2 %, 105 MiB at 1.8 bits 96.5 %, 91 MiB at 1.6 bits 93.6 % (torch simulation of the container, synthetic-query calibration, 4-d codebooks per 256-column block, input-side structured rotation). The released SciDocs containers of the same recipe (127 / 113 MiB) hold 94.7 / 91.3 % and were verified in the browser to ±0.001. Below ~180 MiB llama.cpp has no format at all; BitNet-270m gets to 140 MiB only by training the model ternary.

of full-precision nDCG@10 kept by our 120 MiB, 2.1-bit vector-quantised harrier-0.6b client, no retraining: above the trained BitNet-270m (140 MiB, 97.0 %) and below any file llama.cpp can produce (178 MiB, 93.9 %).
99.2 %

bge-m3 Q4_K_M with a 4-bit token table: cosine to the fp32 query vector 0.990, top-10 overlap 0.864; the 321 MiB Q3_K file keeps 98.3 %. Qwen3-Embedding-0.6B: 98.7 % at 340 MiB, 99.5 % at 385 MiB. The same Czech-calibrated files keep 99.6–100 % on SciFact. The Czech evaluation uses synthetic queries; its full-precision level is the same for every base model we tried, so it measures the compression, not the base.

on a Czech supreme-court index of 55 071 segments with a 355 MiB bge-m3 client calibrated on Czech text.
103 ms

harrier-0.6b at 2.10 bits per weight in our WebGPU runtime: p50 103 ms, p95 113 ms, load 2.4 s, 77 % of the peak process memory of the llama.cpp path, at −0.011 nDCG@10 against the 3.4-bit file (97.6 % of fp32 on SciFact). One laptop, one integrated GPU, 300 queries; no discrete-GPU or mobile numbers yet. The runtime is not public; the containers are.

per query for a 119 MiB vector-quantised client on an integrated GPU; llama.cpp's WebGPU path needs 444 ms with a 235 MiB file.

What we learned along the way

Measured the same way, kept on the page because the next reader will ask: ties, negative results and the baseline everyone gets for free.

96–101 %

SciFact 98.9 %, NFCorpus 99.3 %, ArguAna 101 %, SciDocs 99.2 % (microsoft/harrier-oss-v1-0.6b, GPTQ on the llama.cpp Q3_K grid, generic English calibration). At this bit width llama.cpp's own quantiser reaches the same quality with the same text; the value is in the verification, and we say so. The file stays in the table below because a verified client is useful even when it is not novel.

of full-precision nDCG@10 kept by the 3.4-bit harrier-0.6b client on four English corpora, 235 MiB instead of 1 143 MiB. Not our contribution: llama.cpp's own quantiser gets there too.
−0.008

The idea: give the neurons the corpus uses most a larger codebook and the rest a smaller one at the same average rate. Three rotation seeds, matched random control: importance minus random −0.0085 cosine to fp32, nDCG@10 within noise; using the importance only to weight the codebook fit: −0.008. Side finding: two quantisations that differ only in which rows share a codebook differ by up to 0.03 cosine, so on this grid an effect under 0.01 needs three seeds or a paired construction. Pre-registered and closed 2026-09-10.

cosine lost when a 1.75-bit vector-quantised client spends its codebook budget by neuron importance instead of uniformly; a random split does the same. Closed.
+0.010

The prompt in front of every query is the same 19 tokens, so the full-precision model computes its keys and values once (2 MB) and the compressed model only reads the user's words. The naive version, swapping the exact prompt into an existing client, looked huge (+0.037 at 1.6 bits) but only repaired files calibrated on documents; a client calibrated on synthetic queries reproduces the prompt itself and gains nothing (−0.003 / +0.000 on the released files). Calibrating every block with the exact prompt in place and shipping it is the real thing: +0.013 / +0.009 / +0.008 nDCG@10 over the best query-calibrated client of the same seed at 1.8 bits on SciFact, every paired interval above zero, cosine to fp32 0.90 → 0.92. On SciDocs (short title-like queries) +0.002 / +0.004 / +0.000, at 2.1 bits +0.002, at 1.6 bits +0.001: nothing. Such a client needs the prompt at inference (75–84 % without it). Pre-registered, 2026-09-10/11; the recipe is in the runtime, the published containers are unchanged.

nDCG@10 from shipping the query prompt's keys and values in full precision and calibrating the 1.8-bit client with them in place — on SciFact, three seeds. Not detectable on SciDocs, at 2.1 bits or at 1.6 bits, so the released files stay.
Released query clients
clientbase model · licenceMiBquality vs the model's own fp32 index
harrier-0.6b Q3_K, generic English calibrationmicrosoft/harrier-oss-v1-0.6b · MIT235SciFact 98.9 % · NFCorpus 99.3 % · ArguAna 101 % · SciDocs 99.2 %
harrier-0.6b Q2_K, SciDocs synthetic queriesMIT192SciDocs 94.5 %
harrier-0.6b vector-quantised 2.10 / 1.83 bpwMIT127 / 113SciDocs 94.7 % / 91.3 % (needs the thinletter runtime)
Qwen3-Embedding-0.6B Q4_K_M, English calibrationQwen/Qwen3-Embedding-0.6B · Apache-2.0340SciFact 100.0 % · NFCorpus 99.4 % · ArguAna 100.1 % · SciDocs 99.3 %
Qwen3-Embedding-0.6B Q4_K_M / Q5_K_M, Czech calibrationApache-2.0340 / 385Czech legal index 98.7 % / 99.5 %
bge-m3 Q4_K_M / Q3_K, Czech calibrationBAAI/bge-m3 · MIT355 / 321Czech legal index 99.2 % / 98.3 % · SciFact 99.6 % / 100.0 %

Every client is a query encoder for exactly one document encoder and its settings. Each row links to its Hugging Face repository; the model card carries compatibility, recipe and sha256.

Work with us

Pilots start with a measurement: we take your embedding index, quantise the query side, and hand you the paired numbers before anything is decided. The vector-quantised runtime is available to pilot partners by invitation.

Write to us on GitHub

Jan Rosecký, founder · a contact address is being set up