A self-hosted workspace built on the compression ideas from Headroom and the workspace design of Odysseus.
AI tools send a lot of text to the model. Most of it is repetitive — file listings, build logs, JSON blobs. You pay for every character of it.
Sensei sits in the middle and squeezes that text down before it's sent. The model still gets everything it needs; you just stop paying for the padding.
flowchart LR
A["🧑💻 Your AI tool<br/><sub>Claude Code, Cursor, Aider…</sub>"]
B["🥋 Sensei<br/><sub>runs on your machine</sub>"]
C["☁️ The model<br/><sub>Anthropic, OpenAI, or local</sub>"]
A -- "50,000 tokens" --> B
B -- "10,000 tokens" --> C
C -- "answer" --> B
B -- "answer" --> A
style B fill:#16a34a,stroke:#22c55e,color:#fff
Nothing changes about how you work. Same tools, same API key, same answers. Just a smaller bill.
These are real measurements, not estimates. You can run them yourself with one command (see For the nerds).
xychart-beta
title "Tokens removed, by kind of content"
x-axis ["Build logs", "JSON output", "Search results", "Stack traces", "Prose", "Code"]
y-axis "% removed" 0 --> 100
bar [88, 79, 69, 61, 44, 40]
Across a realistic mix: 79% fewer tokens.
For JSON and logs — which is where AI coding tools spend most of their budget — nothing is lost. Sensei restructures the text rather than summarising it, so the model sees the same facts.
What that means in money. If you spend €100/month on API calls, a 79% reduction on the input side is a large part of that bill. Sensei shows you the running total:
sensei stats.
You need Python 3.11 or newer. That's it.
1. Install
pip install -e ./backend2. Start it
sensei upYour browser opens and asks you one question: do you want to run a model on your own computer (free), or use a service like OpenAI (paste a key). Pick one, click save. Done.
3. Point a tool at it
sensei wrap claudeClaude Code now runs through Sensei. Everything works exactly as before.
Same for codex, aider, cursor-agent, cline, continue, opencode,
goose and crush — just swap the name.
Or use it as an MCP server
If your tool speaks MCP, Sensei plugs in directly — no base URL to change:
The agent gets three tools: compress a blob before putting it in context, get
the exact original back if it turns out it needed it, and check what's been
saved. Needs pip install "sensei[mcp]".
Prefer Docker?
git clone https://github.com/SenseiIssei/Sensei.git && cd Sensei
docker compose up -dThen open http://localhost:7000/app/.
Screenshots wanted. These aren't in the repo yet. If you run Sensei, a screenshot of the setup screen and the chat window would be a genuinely useful contribution — drop them in
docs/screenshots/and open a PR. Seedocs/screenshots/README.md.
Here is what the command line shows, which is real output:
$ sensei models
Your machine
System Windows 11 (AMD64)
CPU 16 cores
RAM 15.3 GB
GPU none detected — models will run on the CPU
Budget 7.7 GB usable for a model
Suggestions for this machine
Qwen2.5 Coder 7B 7B 4.6 GB fits comfortably
Code completion and review. Pairs well with the gateway.
Llama 3.2 3B 3B 2.0 GB fits comfortably
General chat on modest hardware. A sensible floor.
GLM-5.2 744B MoE 390.6 GB too large
Start here: sensei models --pull qwen2.5-coder:7bSomething not working? Sensei tells you what to do about it:
$ sensei doctor
[+] Python 3.12.10 on Windows AMD64
[+] Config file D:\Sensei\.env
[!] Ollama not installed
-> Optional. For free local inference: https://ollama.com
[x] Model access no local model and no API key — Sensei cannot answer anything
-> Either install Ollama (free, local) or run 'sensei up' and paste an
API key into the setup wizard.
[+] Port 127.0.0.1:7000 is free
[+] Bind address 127.0.0.1This matters more than the token savings, so it's worth being specific.
| 🔒 Runs only on your machine | Sensei listens on 127.0.0.1. Nothing on your network can reach it unless you deliberately change that. |
| 🚫 No telemetry, ever | No analytics, no crash reporting, no "anonymous usage data". Not in any build, not behind any flag. |
| 📡 No third-party requests | The interface loads no fonts, scripts or trackers from anyone. This is checked automatically on every commit. |
| 🔑 Your key stays yours | Sensei forwards your API key to the provider you chose and to nobody else. It's encrypted on disk, never written in plain text. |
| ✂️ Secrets stripped | Optionally, Sensei removes passwords, tokens and keys from prompts before they leave your computer. |
| 📴 Works offline | If the server isn't running, the interface still opens and tells you how to start it. |
Does this make the AI worse?
For logs and JSON — the bulk of what coding tools send — no. Sensei rewrites that text into a denser form containing the same facts, rather than summarising it. For prose it removes filler ("it is important to note that" → nothing), which is the kind of text a model ignores anyway.
If the model ever does need the original, it can ask for it: Sensei keeps a local copy of everything it compressed and hands it back on request.
Do I need to change my code or my tools?
No. sensei wrap claude sets one environment variable for that program and
launches it. Nothing is written to your shell profile, nothing is installed into
your tool, and closing it puts everything back.
Do I still need an API key?
Only if you want to use a hosted model. Sensei can run entirely on your own
computer with Ollama — sensei models tells you which
models actually fit in your memory.
Is it really free?
Yes. MIT licensed, no paid tier, no feature held back. See the principles.
What if I don't like it?
pip uninstall sensei. Sensei keeps its data in dot-directories next to where
you ran it; delete them and nothing remains.
Architecture
Sensei is a FastAPI service that speaks both the OpenAI and Anthropic wire formats, so anything with a configurable base URL routes through it unchanged.
flowchart TB
subgraph client["Clients"]
CC["Claude Code<br/><sub>ANTHROPIC_BASE_URL</sub>"]
CX["Codex / Cursor / Aider<br/><sub>OPENAI_BASE_URL</sub>"]
WEB["Web UI"]
CLI["CLI · Qt app · VS Code ext"]
end
subgraph sensei["Sensei"]
GW["Gateway<br/><sub>/v1/chat/completions · /v1/messages</sub>"]
RT["ContentRouter<br/><sub>detects type, picks a compressor</sub>"]
subgraph comp["Compressors"]
SC["SmartCrusher<br/><sub>JSON → CSV schema</sub>"]
LC["LogCompressor<br/><sub>triage + dedupe</sub>"]
CD["CodeCompressor<br/><sub>comments, imports</sub>"]
TC["TextCompressor<br/><sub>filler, boilerplate</sub>"]
end
CA["CacheAligner<br/><sub>keeps the prefix byte-exact</sub>"]
CCR["CCR store<br/><sub>originals, retrievable</sub>"]
RED["DLP redaction<br/><sub>strips secrets</sub>"]
end
PROV["Provider<br/><sub>Anthropic · OpenAI · Ollama · 11 more</sub>"]
CC --> GW
CX --> GW
WEB --> GW
CLI --> GW
GW --> RT
RT --> SC & LC & CD & TC
SC & LC & CD & TC --> CA
CA --> RED
RED --> PROV
RT -.stores originals.-> CCR
CCR -.on request.-> PROV
style sensei fill:#0a0a0f,stroke:#22c55e
style comp fill:#111827,stroke:#374151
The system prompt is left byte-identical on purpose. Providers cache on an exact prefix match, so touching it would invalidate the cache and cost more in latency than compression saves.
How each compressor works
SmartCrusher — JSON. An array of objects with the same keys repeats every key name on every element. It's rewritten as a header plus rows, which is what a CSV is, and drops redundant link/self/href keys. Lossless.
[{"id":1,"name":"a","url":"…"},{"id":2,"name":"b","url":"…"}, …20 more]
↓
id|name|url
1|a|…
2|b|…
LogCompressor — build and test output. Keeps the head, the tail, every line matching an error/warning/summary pattern, and a few lines of context after each. Identical lines are collapsed after normalising timestamps, hex addresses and UUIDs, so a thousand near-identical worker lines become one with a count.
CodeCompressor — source. Strips comments, docstrings, blank lines and trailing whitespace, and folds consecutive import blocks. Regex-driven per language rather than a full parse, for speed and zero dependencies.
TextCompressor — prose. Phrase substitution ("in order to" → "to"), filler removal, boilerplate sentence stripping, and line-level dedupe.
CCR — reversible compression. Every original is written to a local cache keyed by id. If the model decides it needs the untouched text, it asks for it by id and gets it back. Compression is therefore never destructive in practice.
MCP server
sensei mcp speaks the Model Context Protocol over stdio, which covers the
tools that don't let you redirect a base URL — and the case where an agent wants
to compress one specific blob rather than route its whole conversation.
| Tool | What it does |
|---|---|
sensei_compress |
Compress text. Returns the compressed form, a ccr_id, the token counts and the percentage saved. Optionally force a compressor with content_type. |
sensei_retrieve |
Exchange a ccr_id for the byte-identical original. |
sensei_stats |
Totals saved, plus CCR cache state. |
sensei_retrieve is what makes this safe rather than lossy-by-default.
Compression is never a one-way door: if the model decides the compressed form is
missing something, it asks for the original instead of guessing. The server's
instructions tell the client exactly that, because a capability a model doesn't
know about might as well not exist.
Verified end to end against a real MCP client — 582 → 287 tokens on a 40-record JSON array, original recovered byte-identical.
pip install "sensei[mcp]"
sensei mcp # stdio; normally your client spawns this, not youPerformance
Compression sits on the hot path of every request, so its own cost matters. Measured on an 86 kB agent turn — system prompt, exchanges, a large JSON tool result, build logs and source:
| before optimisation | after | |
|---|---|---|
| median | 8.14 ms | 4.68 ms |
| p95 | 12.53 ms | 6.31 ms |
| cold start | 9.34 ms | 6.91 ms |
What that took: compiling the detector patterns once instead of per line,
bounding the type-detection scan to the sample it actually reads instead of
splitting the whole payload, and removing ~11,000 re cache lookups per request.
An optional Rust accelerator (sensei_core, PyO3, abi3) makes the JSON hot path
roughly twice as fast again. CI builds the wheel and re-runs the entire test
suite against it on every commit, so "byte-identical to the Python path" is
verified rather than asserted.
Reproducing the numbers
python backend/benchmarks/compression_benchmark.pyReal tiktoken (cl100k_base) counts over a fixed corpus of tool outputs, logs,
stack traces, source and prose. --json gives machine-readable output and
--min-aggregate 75 exits non-zero below a floor — which is what nightly CI
runs, so a regression in compression quality fails the build rather than being
noticed months later.
Security model
Sensei assumes the machine it runs on is trusted and that anyone who can reach the port is authorised. Everything else follows from that:
- Binds to
127.0.0.1. Exposing it is a deliberate act, andsensei doctorreports it as a failure if you do it without enabling auth. - API keys live in an encrypted vault (AES-256-GCM), not in
.env. - Optional DLP redaction strips API keys, tokens, private keys, JWTs and optionally PII from prompts before they leave the machine.
- Optional per-user auth, JWT sessions, OIDC SSO, and RBAC on admin endpoints.
run_pythonis off by default. When enabled it is a subprocess with a timeout, not a container — the docs say so plainly.
Full scope and reporting process: SECURITY.md.
Repository layout
backend/ FastAPI service, compression pipeline, CLI, tests
sensei/
compression/ SmartCrusher, LogCompressor, CodeCompressor, TextCompressor,
CacheAligner, CCR, ContentRouter
routers/ gateway, chat, RAG, agent, settings, setup, stats, …
security/ auth, crypto, vault, redaction, RBAC, OIDC, sessions
cli/ up · wrap · doctor · models · stats · chat
benchmarks/ the compression benchmark
frontend/ React 19 + Tailwind 4 + Vite 8, PWA
rust/sensei_core/ optional PyO3 accelerator
extensions/vscode VS Code extension
training/ Sensei-Compressor fine-tuning scaffold
deploy/ nginx · Caddy · Traefik · systemd
docs/ configuration reference (generated), the plan
What CI actually enforces
Every action is pinned to a commit SHA. On each pull request:
- backend tests across {Linux, macOS, Windows} × Python {3.11, 3.12, 3.13}
rufflint and format, with the rule set pinned so a new ruff release can't fail an unrelated PR- the Rust accelerator built and the whole suite re-run against it to prove byte-parity
- Playwright on three operating systems
- a 450 kB JavaScript budget
- the config reference regenerated from the pydantic model and compared
- the built UI checked for third-party origins, and for root-absolute asset
URLs that would blank the page when mounted under
/app/ - Docker image built and
/healthsmoke-tested - pip-audit, npm audit, cargo audit, CodeQL, gitleaks, Trivy, SBOM, Scorecard
Nightly: the compression benchmark with a regression floor, Rust/Python output diffed for parity, and the suite against eagerly-upgraded dependencies.
Sensei didn't invent any of this. Two projects did the hard thinking, and both are worth your time independently of whether you ever use Sensei:
🪶 Headroom · Apache-2.0
The reason this project exists. Headroom worked out that the way to cut an agent's token bill isn't to summarise its context — it's to notice that most of what agents send is structurally redundant, and restructure it losslessly. Tabular JSON compaction, log triage, cache-aligned prefixes, and reversible compression with a retrieval tool are all Headroom's ideas.
If you want context compression as a mature, standalone product with a much larger surface than Sensei's — output-token shaping, a learned prose compressor, LangChain and LiteLLM adapters, agent wrapping for a dozen tools — go use Headroom. It's excellent, and it's further along than this.
Sensei's angle is different: compression as one part of a self-hosted workspace you own end to end, MIT-licensed, that also gives you a chat UI, RAG, an agent and a desktop app.
🏛️ Odysseus · AGPL-3.0
The shape of the thing. Odysseus — by Felix Kjellberg — made the case that a
self-hosted AI workspace should feel like a finished product rather than a pile
of scripts: hardware-aware model recommendations, a real editor, agents, memory,
and security defaults that assume you'll actually deploy it. The "what can my
machine actually run?" idea behind sensei models is straight from its Cookbook.
Sensei is a clean-room implementation — no Odysseus code is copied, which is what lets it stay MIT instead of inheriting AGPL. If you want the full workspace with email, calendar, documents and image editing, Odysseus is the more complete answer.
Neither project endorses this one. Any bugs here are ours.
Also worth knowing: GLM-5.2 is the model Sensei's defaults point at, and the reason a lot of the compression tuning assumes a very large context window.
Issues and PRs welcome — CONTRIBUTING.md has the setup.
docs/ROADMAP-NEXT.md is the queue — what's planned, in what order, and why. It also lists the things that cost real time to discover and aren't visible from reading the code.
Most useful right now: compression heuristics for more languages, output-token shaping, mobile polish, and screenshots for this page.
{ "mcpServers": { "sensei": { "command": "sensei", "args": ["mcp"] } } }