Text Generation
Transformers
Safetensors
English
llama
hand-crafted
compiled
autocomplete
no-training
n-gram
code
browser
text-generation-inference
Instructions to use tmickleydoyle/tiny-autocomplete with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use tmickleydoyle/tiny-autocomplete with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="tmickleydoyle/tiny-autocomplete")# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("tmickleydoyle/tiny-autocomplete") model = AutoModelForCausalLM.from_pretrained("tmickleydoyle/tiny-autocomplete", device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use tmickleydoyle/tiny-autocomplete with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "tmickleydoyle/tiny-autocomplete" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "tmickleydoyle/tiny-autocomplete", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/tmickleydoyle/tiny-autocomplete
- SGLang
How to use tmickleydoyle/tiny-autocomplete with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "tmickleydoyle/tiny-autocomplete" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "tmickleydoyle/tiny-autocomplete", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "tmickleydoyle/tiny-autocomplete" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "tmickleydoyle/tiny-autocomplete", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use tmickleydoyle/tiny-autocomplete with Docker Model Runner:
docker model run hf.co/tmickleydoyle/tiny-autocomplete
File size: 11,675 Bytes
ab4f92b b46c110 4113449 b46c110 ab4f92b b46c110 ab4f92b 4113449 b8f3723 4113449 b8f3723 4113449 b8f3723 4113449 b8f3723 ab4f92b 4113449 ab4f92b 4113449 ab4f92b 4113449 ab4f92b 4113449 ab4f92b 4113449 ab4f92b 4113449 ab4f92b 4113449 ab4f92b 4113449 ab4f92b 4113449 ab4f92b b46c110 4113449 b46c110 ab4f92b 4113449 ab4f92b 4113449 ab4f92b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 | ---
license: mit
language:
- en
library_name: transformers
pipeline_tag: text-generation
tags:
- llama
- hand-crafted
- compiled
- autocomplete
- no-training
- n-gram
- code
- browser
datasets:
- OpenAssistant/oasst2
- google-research-datasets/mbpp
- neulab/conala
widget:
- text: In Python,
- text: I hope this email
---
# tiny-autocomplete
Ghost-text autocomplete for prompts to a coding assistant. **It's compiled, not
trained.** No gradient descent was run, and no other model or pretrained weights were used.
Every number comes from a closed-form rule in [`build_weights.py`](build_weights.py) applied to
~21,000 lines of human-written text ([sources](data/SOURCES.md)).
It's built to be **confident rather than chatty**. It only suggests when its probability is
high, and otherwise stays quiet.
The same compile produces two artifacts:
| | Browser export (`web/model.bin`) | Transformers model (`model.safetensors`) |
|---|---|---|
| Use it for | embedding in an app: on-device, no server | `transformers`, `pipeline`, research |
| Size | **4.5 MB** | 1.5 GB (380M params, 93% zeros) |
| Per keystroke | **0.5 ms median, 0.9 ms p95** (Bun) | 58 ms per next token (PyTorch CPU) |
| Vocabulary / contexts | 6,612 words / 107,864 contexts | 3,000 words / 24,773 contexts (pruned to fit the dense budget) |
Both are the same kind of network. The browser export stores only the non-zero parts of the
compiled transformer; see [how it works](#how-it-works).
## What it suggests
Real output of `web/autocomplete.ts` with the shipped confidence threshold (72%). Gray is the
ghost text; *(quiet)* means it suggests nothing.
| Typed | Ghost text | Confidence |
|---|---|---|
| `write a python fun` | `ction to` | 98% |
| `how do i conv` | `ert` | 96% |
| `I'm getting an err` | `or` | 100% |
| `can you explain the differ` | `ence between` | 98% |
| `Write a python function to check whether` | ` the` | 72% |
| `I hope this email` | ` finds you` | 85% |
| `what is the best way to` | *(quiet)* | |
| `how to convert a string to` | *(quiet)* | |
## How often it's right
Measured with `python scripts/benchmark.py`. The model is compiled **without** 10% of the
prompts (711 prompts, 42,185 characters), and each of those prompts is then typed
character by character. After every keystroke the model either shows ghost text or stays
quiet.
- **Precision:** the share of shown suggestions that exactly match what the user typed next.
A wrong word anywhere in the ghost counts as a miss.
- **Coverage:** the share of keystrokes where something is shown.
- **Keystrokes saved:** the share of keystrokes saved when every correct suggestion is accepted
with Tab.
| Confidence threshold | Precision | Coverage | Keystrokes saved |
|---|---|---|---|
| **0.72 (shipped default)** | **90.3%** | 38.7% | **23.0%** |
| 0.80 | 93.5% | 34.1% | 20.7% |
| 0.90 | 96.4% | 26.9% | 16.4% |
| 0.95 | 97.7% | 20.5% | 11.8% |
The threshold is a runtime setting, so an app can trade coverage for precision without
recompiling. The dense transformers model, with its smaller, more heavily pruned tables,
gets 92.4% precision / 19.0% saved at 0.72.
These numbers come from held-out OpenAssistant, MBPP and CoNaLa prompts. Your users'
prompts may differ, so measure on your own text if you can.
## Use it in your app (browser)
`web/autocomplete.ts` and `tokenizer.ts` are zero-dependency TypeScript with no DOM access, so
they run on the main thread, in a Web Worker, or in Bun/Node.
```ts
import { CompiledAutocomplete } from "./web/autocomplete.ts";
const ac = await CompiledAutocomplete.load(
"https://huggingface.co/tmickleydoyle/tiny-autocomplete/resolve/main/web/model.bin",
);
input.addEventListener("input", () => {
const { ghost, prob } = ac.suggest(input.value); // "" when not confident
showGhostText(ghost); // Tab -> input.value += ghost
});
ac.suggest(text, { ...ac.policy, tau: 0.9 }); // stricter: ~96% precision
```
Try the demo locally. It's a static page with no server code:
```bash
git lfs install
git clone https://huggingface.co/tmickleydoyle/tiny-autocomplete && cd tiny-autocomplete
bun build web/demo.ts --outfile web/demo.js --target browser # demo.js is also committed
python3 -m http.server 8080 -d web # open http://localhost:8080
bun web/cli.ts "write a python fun" # or from the terminal
```
The demo shows the ghost text, the confidence and the latency per keystroke, the model's best
guess when it stays quiet, and a slider for the threshold.
## Use it with transformers
```python
from transformers import pipeline
gen = pipeline("text-generation", model="tmickleydoyle/tiny-autocomplete")
gen("In Python,")[0]["generated_text"] # 'In Python, you can use the following code:'
```
`model.safetensors` is a plain `LlamaForCausalLM` (no `trust_remote_code`). Its logits are the
compiled n-gram's log-probabilities, matching the reference to within 2e-5 nats. To get the
same confidence-gated ghost text in Python, use `suggest.py` (`Suggester.suggest`).
## Performance: why compile instead of train
On a 10-core Apple Silicon laptop (32 GB RAM), CPU only, no GPU. Raw numbers are in
[`docs/benchmarks.json`](docs/benchmarks.json).
| | |
|---|---|
| Compile the browser export from text | **6.6 s** |
| Compile the 380M-parameter transformers model | **6.9 s** (peak memory 2.4 GB) |
| GPU hours / compute cost | **0 / $0** |
| Reproducible | rebuilds are byte-identical (both artifacts) |
| Browser export: load / keystroke | 51–64 ms load in Chrome (local); 0.5 ms median, 0.9 ms p95 per keystroke |
| Transformers model: next token | 58 ms (PyTorch CPU, 8 threads); 18 tokens/s greedy |
For scale: the sibling experiment [tiny-research](https://tiny-research.vercel.app/)
trained a 34M model with a 5-minute budget on an RTX 4090.
**Why build a model like this:**
- **Iteration in seconds:** edit text, recompile, and measure in under a minute.
- **Calibrated on purpose:** a suggestion only appears when the compiled probability clears a
threshold whose precision is measured.
- **Auditable:** every neuron is one context from the source text, so any suggestion can be
traced to the lines that produced it.
- **Editable:** delete a line and recompile to remove a behavior.
- **Deterministic and exact:** the same text always produces the same bytes.
**What it costs you:** it doesn't generalize like a trained model. Next-token top-1
accuracy on held-out prompts is 26.7% (top-3: 40.6%), and 7% of next tokens are outside the
vocabulary. That's why it only speaks when confident.
## How it works
- **The network:** the compiled network is an exact transformer implementation of an
**interpolated Kneser-Ney 5-gram language model**. It predicts the next token from the
current token plus the 3 before it, backing off to shorter contexts when a longer one is
unknown.
- **Attention:** 3 heads with constant queries and keys. Their RoPE phases make each head look
exactly 1, 2 or 3 tokens back, and each head copies that token's code into its own slot.
- **MLP:** one neuron per kept context (1–4 tokens). A neuron fires only when every token
matches. It adds `log p(next | context) − log p(next | shorter context)` to the logits, and
these additions telescope to the log-probability under the longest known context.
- **Browser export:** stores, for each neuron, its context tokens and the logit changes it
writes (float32), plus the always-on unigram neuron. The runtime evaluates only the neurons
whose context matches: the same math as the dense model, without the zeros. A test exports
the dense model's own tables and checks the runtime against PyTorch.
- **Confidence policy** (`suggest.py`, mirrored by `web/autocomplete.ts`):
- **Partial words:** if the user is mid-word, only words starting with the typed prefix are
candidates. Their probability is scaled by the chance that a word with this prefix is in
the vocabulary at all, so rare words don't trigger confident wrong completions.
- **When to show:** only if that probability is ≥ τ.
- **Extension:** the suggestion grows word by word while the combined probability stays
≥ τ, stopping at end-of-text or after `. ? ! :`.
- **Settings were benchmarked, not tuned by hand:** `scripts/sweep.py` compiled 24
combinations and picked the one with the most keystrokes saved at ≥ 90% precision. The
combinations varied context length (4–10 tokens), Witten-Bell vs Kneser-Ney, source weights,
vocabulary and pruning. The weights themselves are always closed-form counts.
- **Context length:** 4 tokens won. Longer contexts were slightly worse (22.4–22.8% saved),
because with ~21k lines they rarely repeat.
- **Topic head:** an experimental "topic" attention head, conditioning on the last
language or tool name mentioned, didn't help either (`scripts/experiment_topic.py`).
Details, formulas and the proof of exactness are in [docs/how-it-works.md](docs/how-it-works.md).
## Limitations
- **Short memory and a lookup:** it sees 4 tokens and reproduces frequent patterns. It
doesn't understand code, and it doesn't write code (code blocks were removed from the data).
- **Narrow domain:** strongest on how people *phrase requests* about code ("write a python
function to…", "can you explain the difference between…"). It's quiet on most other text.
- **Lowercase:** input is lowercased, so ghost text is lowercase.
- **Limits:** the transformers model has 128 positions. The browser runtime has no length
limit, but it only ever looks at the last 4 tokens.
- **Authorship:** ~1,100 everyday phrases in `phrases/` were written by an AI coding assistant
(Claude) for this project. All coding data is human-written; see
[data/SOURCES.md](data/SOURCES.md).
## Rebuild and verify
```bash
pip install -r requirements.txt
python scripts/fetch_coding_prompts.py # optional: re-download data/prompts.txt, data/replies.txt
python build_weights.py # transformers model ("hf" in compile_settings.json)
python export_web.py # browser export ("web" + "policy")
python test_model.py # HF loading, exactness, TS parity, web runtime == Python == PyTorch
python scripts/sweep.py # re-run the settings comparison -> docs/sweep.json
python scripts/benchmark.py # -> docs/benchmarks.json
```
Tested with transformers 4.52.4 and 5.22.1, Bun 1.3, Node 24 and Chrome.
## Licenses
The model, code and hand-written phrases are MIT. `data/prompts.txt` and `data/replies.txt`
contain excerpts from OpenAssistant oasst2 (Apache-2.0), MBPP (CC-BY-4.0) and CoNaLa (MIT); see
[data/SOURCES.md](data/SOURCES.md) for attribution.
## Files
| File | Purpose |
|---|---|
| `web/model.bin`, `web/autocomplete.ts`, `tokenizer.ts` | browser export and its runtime |
| `web/index.html`, `web/demo.ts`, `web/demo.js`, `web/cli.ts` | demo page and CLI |
| `config.json`, `generation_config.json`, `model.safetensors`, `tokenizer*.json` | transformers model |
| `build_weights.py`, `export_web.py`, `compile_settings.json` | the compiler and its settings |
| `suggest.py` | confidence policy (Python reference) |
| `phrases/*.txt`, `data/*.txt`, `data/SOURCES.md` | source text and attribution |
| `scripts/sweep.py`, `scripts/benchmark.py`, `scripts/experiment_topic.py`, `docs/*.json` | measurements |
| `test_model.py` | verification suite |
| `model.ts`, `autocomplete.ts`, `ui.ts`, `ui.html` | TypeScript runtime for the dense model, CLI and server UI |
| `docs/how-it-works.md` | full construction details |
|