Skip to main content

Sillage

Your language model forgets everything. Sillage gives it a memory — and a way to keep learning — in a fixed handful of megabytes, with no gradients and no datastore of hidden states that grows.

sillage (n., French) — the trace left behind by something that has passed: a ship's wake, a scent in a room. What a model keeps of what it read.

A frozen language model reads your documents, remembers them, and predicts better next time. No gradients, no fine-tuning, no vector database. One Hebbian matrix written as the model reads, a semantic tier routed by confidence, a cold store that consolidates by surprise, and a rank-16 adapter on the readout — four mechanisms, eight preprints, one command-line tool. Everything runs on a laptop CPU, and on a GPU when there is one.

demo

Try it in your browser first — no install, no account.

Install

pip install sillage

Use

sillage index notes.md                # instant: no model, already queryable
sillage ask "what did the report say?"

sillage read notes.md                 # memorize it (CPU: ~8 min per 10k tokens)
sillage read big_corpus.md --fast     # writes only, ~40x (half with --sem2)
sillage complete "The report said"    # generate WITH the memory
sillage complete "The report said" --fast   # same output, speculative (paper 5)
sillage complete "..." --cold-mass    # weight recalls by surprise, not counts (paper 6)
sillage read notes.md --sem2 auto     # keys from an early layer: paraphrases recall too (paper 8)
sillage serve                         # OpenAI endpoint for any client (paper 7)
sillage watch ~/notes                 # read a folder as it changes, ranked by how unusual
sillage review                        # what is about to be forgotten (paper 6)
sillage export cartridge/             # share the matrices, not your text
sillage pull user/cartridge           # open somebody else's, from the Hub
sillage read notes.md --reflow        # rejoin wrapped lines: 7/8 -> 8/8 facts recalled
sillage complete "..." --readout family     # louder recall, more locality cost (paper 5)
sillage read notes.md --dtype bfloat16  # quantise so a bigger model fits (not for speed)
sillage status                        # what it knows, tier by tier
sillage chat                          # ask and generate in one session
sillage demo notes.md                 # two sessions on one document, start to finish
sillage forget --all
$ sillage read preprint_v1.txt                    # Monday, memory empty
read preprint_v1.txt: 5859 tokens in 4.9 min | PPL 12.14 -> 11.83 (adapter) -> 11.71 (+memory)

$ sillage read preprint_v2.md                     # Tuesday, a new process
read preprint_v2.md: 11041 tokens in 9.5 min | PPL 10.68 -> 9.82 (adapter) -> 5.39 (+memory)

Three numbers per file: what the frozen model alone predicts, what the rank-16 adapter adds, and what the memory of everything read so far adds on top. The state lives in ./.sillage and survives restarts. Its size is fixed the day you start, because the shape of the matrices is: 7.4 MB with GPT-2 and 26.5 MB with Qwen3 (25 MiB — the figure paper 5 quotes) — 4.2 MB of n-gram matrix, 12.6 MB of semantic tier (on by default with --model qwen, opt-in elsewhere) and a vocab x 16 adapter that is 3.2 MB at GPT-2's vocabulary and 9.7 MB at Qwen3's; --sem2-whiten adds 2-4 MB. The file on disk is compressed, so it starts smaller and grows toward that ceiling as the matrices fill — the same after one document or ten thousand. It never learns from its own generations — only from what you give it to read.

The grounded-retrieval index (index.json — the verbatim passages sillage ask quotes) sits beside the memory and does grow with what you read. It is not what the model predicts from, and sillage forget --all clears both.

From Python:

from sillage import Sillage

s = Sillage(model="gpt2")          # any causal LM; omit it and the state
                                   # says which model it belongs to
s.read("notes.md")                 # read, memorize, index -- then save
s.ask("what did the report say?")  # exact passages, nothing generated
print(s.complete("The report said"))

How much better, exactly?

On 36k tokens of technical text the model had never seen (frozen GPT-2 124M, every system tuned identically on a held-out prefix, 95 % bootstrap CIs):

system perplexity change memory used
frozen GPT-2 31.2 — 0
+ RAG-style retrieve & rescore 29.9 −4 % corpus + index
+ kNN-LM, unbounded store 23.6 −24 % 55 MB, grows forever
+ this memory (fixed) 19.2 −38 % 4.2 MB, constant
+ memory and fast weights 16.8 −46 % 7.4 MB, constant

(The last row is the rank-16 adapter the tool ships. Paper 4's +0.633-nat headline uses a rank-256 adapter — 51 MB of adapter for the last 0.015 nats (perplexity 16.8 -> 16.6).)

The fixed 4.2 MB memory beats the unbounded datastore it was designed to approximate — paired bootstrap P = 1.000, replicated over 5 random seeds. On a second model (Qwen3-0.6B) it statistically matches that unbounded store at one-sixteenth the memory; every internal ordering replicates.

Where it does not win: on long, low-repetition narrative text, kNN-LM still beats it. This memory captures verbatim recurrence; that is its regime, and it is measured and published rather than hidden.

Any causal language model, on CPU or GPU

sillage read notes.md --model qwen                          # shortcut
sillage read notes.md --model HuggingFaceTB/SmolLM2-135M    # any hub id
sillage read notes.md --model ./my-finetuned-llama          # any local folder
sillage read notes.md --device cuda                         # if you have one

Verified across three architectures (GPT-2, Qwen3, GPT-NeoX). Three things worth knowing before pointing it at a new model:

  • The readout tunes itself. For a model nobody has tuned, it calibrates on a rolling window of what you read, following the papers' protocol: the winner governs the next read, never the one it was fitted on. For the two models the papers did tune, their published settings are kept — refitting those on a cold memory measurably loses (+0.109 against +0.120 nats).
  • A memory lives in one model's token space. Give each model its own --state directory; the state remembers which model it belongs to and refuses to be opened by another.
  • The GPU only does the frozen forward passes. --device cuda moves the model; the mechanisms stay in numpy on the CPU, where they belong — they are rank-1 updates, not matrix multiplications. Defaults to the GPU when there is one.

Requires Python 3.10+, numpy, torch and transformers. Nothing else, and no network at all for reading, asking or generating once the frozen model is cached — sillage pull is the one command that goes to the Hub, by design.

The eight preprints

# title DOI
1 Sillage: Surprise-Gated Amplitude Memory for Frozen Language Models 10.5281/zenodo.22079016
2 Route the Scores, Not the Keys 10.5281/zenodo.22079444
3 One Signal, Three Tiers 10.5281/zenodo.22079471
4 Memory Remembers, Fast Weights Adapt 10.5281/zenodo.22079481
5 The Memory Pays for Itself 10.5281/zenodo.22109220
6 Stored Is Not Recalled 10.5281/zenodo.22125859
7 Found Is Not Formulated DOI pending
8 The Key Was in the Wrong Layer DOI pending

Sources, figures, every number as committed JSON, the reproduction pipeline and the three negative results are on GitHub: https://github.com/riscoss63/sillage

Citation

@article{sghairi2026sillage,
  title  = {Sillage: Surprise-Gated Amplitude Memory
            for Frozen Language Models},
  author = {Sghairi, Abderrahmane},
  year   = {2026},
  doi    = {10.5281/zenodo.22079016},
  url    = {https://doi.org/10.5281/zenodo.22079016}
}

MIT licensed.

Metadata

Release files for sillage 1.9.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sillage 1.9.1
File Size Uploaded
sillage-1.9.1.tar.gz 92.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sillage 1.9.1
File Interpreter ABI Platform
sillage-1.9.1-py3-none-any.whl Python 3 none any Details

Total release size: 169.8 kB

Release files / sillage-1.9.1.tar.gz

Download URL sillage-1.9.1.tar.gz
Size 92.4 kB
Tags Source
SHA-256 checksum
How to use checksums
f1fd30b506a74b806606dcad5890c4bfc2e6fb2bc4fdea12d9350da1923c3540
BLAKE2b-256 checksum
How to use checksums
f3ae592c96df2d6926c88a3198ac8ced22d4f46c367ef907e72d9ac7db5c0ab4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.11

Release files / sillage-1.9.1-py3-none-any.whl

Download URL sillage-1.9.1-py3-none-any.whl
Size 77.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
02f4b46bd7f07e2a1c360517d41ef87cdff3c96cdee7aa225673a7548a423e57
BLAKE2b-256 checksum
How to use checksums
3f69f254b8c0d15c0071d5aacaad527bea913a59c9f3670901429f08bf8adf7c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.11

Release history Release notifications | RSS feed

1.9.2

2 release files

This release

1.9.1 This release

2 release files

1.9.0

2 release files

1.8.4

2 release files

1.8.3

2 release files

1.8.2

2 release files

1.8.1

2 release files

1.8.0

2 release files

1.7.0

2 release files

1.6.0

2 release files

1.5.0

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page