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 + salience journal
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 --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.8.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.8.1
File Size Uploaded
sillage-1.8.1.tar.gz 77.2 kB Details

Built distribution (wheel)

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

Total release size: 143.6 kB

Release files / sillage-1.8.1.tar.gz

Download URL sillage-1.8.1.tar.gz
Size 77.2 kB
Tags Source
SHA-256 checksum
How to use checksums
2eee8856bca24d4353b573bbb05959ed277a7cd58ce71be065be5e28da655101
BLAKE2b-256 checksum
How to use checksums
86c6e16545d28fe5b99b72be284bef274f973aef642d04df52a6d057b29913ab
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.8.1-py3-none-any.whl

Download URL sillage-1.8.1-py3-none-any.whl
Size 66.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bf61b1ed236131fbeb818a4b66a8712e3ecb1babd2d9431b3c04e6a8c61ef569
BLAKE2b-256 checksum
How to use checksums
3293a77e7a6292d085aa5f82c3d28e8bbae72f7cdabdc1336b22368858a663dc
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

1.9.1

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

This release

1.8.1 This release

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