The only prompt compressor that proves it did not break your prompt.
Most "compress my prompt" tools are a wrapper around "make it shorter" — they run a regex or an LLM over your text and hope nothing important got deleted. PromptTrim is built the other way around: code, quoted strings, URLs and template variables are protected by construction, and every compression comes with a checklist showing exactly which instructions, formats and literals survived — so you don't have to trust it, you can verify it.
Live Demo GitHub stars License
A real run in Aggressive mode. Note the ✗ on "AI" in the Verification panel — the ledger caught its own compression losing a constraint anchor and offers Restore, instead of silently reporting a clean pass. That's the whole point.
Prompt compressors have one job and a well-known failure mode: they cut
things they shouldn't. Filler words removed from inside a quoted string. A
"never" deleted along with the sentence around it. A "70% average savings"
banner with no benchmark behind it. PromptTrim's core bet is that the fix is
verification, not a smarter regex:
- Protected regions. Code fences, inline code, quoted strings, URLs,
template variables (
{{x}},{x},${x},<tag>), tables and few-shot examples are marked before any rule runs and are never offered to a rule as a candidate — not "usually skipped." - Constraint ledger. Before compressing, PromptTrim inventories every imperative, negation ("never", "do not"), output format, number, quoted literal and variable in your prompt. After compressing, it verifies each one survived and shows a ✓/✗ checklist with a one-click Restore for anything lost. In AI mode, a second model call audits the result independently and a third repairs what's missing — automatically.
- Honest Cost Advisor. Real tokenizers per provider, prices with a verification date, and a straight comparison of compressing vs. caching: caching a static prefix usually saves ~90% of input cost on every repeated call, against 15–30% once for compression. The app recommends whichever actually saves more money — including "don't compress, reorder and cache."
Built for developers who run system prompts and templates thousands of times a day, where a shrinking prefix and a wrong deletion both have a real dollar cost.
No signup. No install. No backend. Just paste and compress.
No invented numbers. These are produced by running npm run bench against
bench/corpus/ (40 prompts) and are regenerated, not hand-edited, every time
the script runs.
Generated by npm run bench on 2026年09月05日. Tokens counted with the real o200k tokenizer (js-tiktoken) — never estimated. The ledger is enforced at every level here (the product default only enforces it at Aggressive) so blocked changes are visible at all three.
Production-style system prompts (40 prompts)
bench/corpus/phase0 + bench/corpus/phase2 — regression fixtures written to be already careful. A near-zero reduction here is the expected result: nothing gets invented to cut.
| Level | Avg. token reduction | Aggregate token reduction | Critical constraints preserved | Changes blocked by the ledger |
|---|---|---|---|---|
| Light | 0.0% | 0.0% (4,523 → 4,523 tokens) | 100.0% (434/434) | 0 (0 prompts) |
| Balanced | 1.4% | 0.7% (4,523 → 4,491 tokens) | 100.0% (434/434) | 0 (0 prompts) |
| Aggressive | 1.6% | 0.9% (4,523 → 4,484 tokens) | 100.0% (434/434) | 0 (0 prompts) |
Everyday chat-style prompts (10 prompts)
bench/corpus/phase6 — unengineered requests padded with the framing and hedges people type without thinking about it.
| Level | Avg. token reduction | Aggregate token reduction | Critical constraints preserved | Changes blocked by the ledger |
|---|---|---|---|---|
| Light | 0.0% | 0.0% (2,426 → 2,426 tokens) | 100.0% (60/60) | 0 (0 prompts) |
| Balanced | 9.0% | 8.9% (2,426 → 2,209 tokens) | 100.0% (60/60) | 0 (0 prompts) |
| Aggressive | 12.3% | 12.2% (2,426 → 2,129 tokens) | 100.0% (60/60) | 0 (0 prompts) |
AI mode not measured for: Anthropic, OpenAI, Google Gemini. Set the matching API key (ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY) and re-run npm run bench to include it — no percentage is published for a provider that was not actually called.
Local ML mode (TinyBERT via LLMLingua-2, real on-device inference), Balanced level, 3-prompt sample from the everyday corpus:
| Critical constraints preserved | Protected-region violations |
|---|---|
| 25.0% (5/20) | 0/8 |
Two corpora, on purpose: production-style system prompts are already terse, so near-zero reduction there is the expected, honest result — it proves the safety claim, not the savings one. The everyday corpus (unengineered, chatty requests) is where the savings claim gets measured.
Honesty as a feature, not a disclaimer:
- Does not touch protected regions. Code, quoted strings, URLs, template variables and examples are structurally excluded from every rule.
- Does not delete instruction words. "never", "always", "must", "only",
"step by step" and anything like them are never removed, at any level —
see
packages/core/src/rules/discarded.tsfor the legacy rules that did this and were cut. - Does not prove full meaning is preserved. The ledger verifies specific constraints survive, not that a human would read the output as identical. Review Aggressive output before shipping it.
- Does not send Fast mode prompts anywhere. There is no backend. AI mode goes straight from your browser to the provider you pick, with your own key — never through a PromptTrim server, because none exists.
- Does not check requirements in every language yet. The ledger's prohibition/requirement/format detection is English-only today; other languages still get the protected-region safety net, not the checklist.
- Does not run batch prompts through AI mode. Batch mode (prompts
separated by
---) is Fast mode only, so one click can't fire an unbounded number of paid API calls. - Does not retry automatically on a provider rate limit. A 429 in AI mode is shown with a wait time; retrying — and spending again — is your call.
- Does not trust Local ML mode's output on its own. LLMLingua-2 drops tokens by statistical importance, not meaning — our own benchmark measured it keeping only 25% of critical constraints unaided (see Benchmark). It is marked experimental for exactly that reason; the protected regions and Constraint Ledger it always runs through are what make it usable at all, not a claim that the model itself is reliable.
Shrink prompts while a constraint ledger verifies nothing important was lost.
Rule-based, zero API calls, works offline after your first visit (it's a PWA).
Model-powered rewriting on Anthropic, OpenAI or Google — with a second model auditing the result and an automatic repair pass.
A small on-device model (TinyBERT, LLMLingua-2) compresses entirely in your browser — no API key, nothing leaves your machine. Its output still goes through protected regions and the Constraint Ledger, which is what makes an unsupervised local model usable at all — see How Local ML Mode Works.
A ✓/✗ checklist of every prohibition, format, number and literal, with one-click Restore for anything lost.
See every change with the rule that made it and why, and undo any single one — or a whole rule — without recompressing.
Compressing vs. caching, priced against your own call volume, with the cheaper option recommended honestly.
Share a prompt by URL (never your API key), or move it in and out as .txt,
.md or .json.
Paste several prompts separated by --- and compress them all at once, with
a per-prompt summary table.
Light, Balanced, or Aggressive — Aggressive is the only one the ledger can veto a change on.
promptrim check puts the same engine in CI: a token budget per file, the
delta against your base branch, duplicated instructions, and a verified trim
it can apply for you. See CLI and GitHub Action.
No backend, no prompt storage anywhere. Your API key never leaves your browser, and by default never leaves memory.
🔗 https://fabianimv.github.io/promptrim/
No signup. No install. Just paste and compress. Ctrl+Enter (⌘+Enter on
Mac) compresses from anywhere on the page.
AI Mode calls the provider you choose straight from the browser — no proxy, no backend, no stored prompts. It is a three-step pipeline, not a "make it shorter" wrapper:
- Compress. The system prompt carries the protection rules (code, quoted literals, URLs, template variables, tables, examples) and the constraint ledger extracted from your prompt. The model answers with structured JSON.
- Verify. A second call — by default a cheaper model — receives the original, the compressed version and the ledger, and reports per constraint whether it survived, with evidence.
- Repair. If a critical constraint is missing, a third call puts back only the missing ones, at most twice. Whatever still fails is shown as ✗ with a manual Restore button.
The local verifier from the constraint ledger always owns the ✓/✗ column; the model's opinion is displayed as evidence next to it, never in place of it. Before running, the app shows what the pipeline itself will cost.
| Provider | Models offered | Default | Verifier |
|---|---|---|---|
| Anthropic | Claude Opus 5, Sonnet 5, Haiku 4.5 | claude-opus-5 |
claude-haiku-4-5 |
| OpenAI | GPT-5.6 Sol, Terra, Luna | gpt-5.6-sol |
gpt-5.6-luna |
| Gemini 3.8 Flash, 2.5 Pro, 2.5 Flash | gemini-3.8-flash |
gemini-2.5-flash |
Local ML mode runs LLMLingua-2 — Microsoft's token-classification compressor, ported to the browser by llmlingua-2-js — entirely on your device via Transformers.js. No API key, no server call: the first run downloads a small model (TinyBERT, ~57 MB) once, your browser caches it, and every compression after that runs locally.
Unlike AI mode, there is no verify-and-repair loop here — a local model call is on-device compute, not a cheap API round trip, and there is nothing an extra call could reason its way into fixing. Instead, task 2 of docs/PLAN.md Phase 8 sets the actual safety mechanism:
- Protected regions first. The prompt is segmented exactly like Fast
mode (
packages/core/src/segment.ts); LLMLingua-2 only ever sees the plain-text segments. Code, quoted strings, URLs and template variables are copied through untouched, never passed to the model. - The Constraint Ledger, always. LLMLingua-2 drops tokens by statistical importance, with no notion of what it is destroying — it can, and does, invert a prohibition by dropping the word "never". The same ledger that verifies Fast and AI mode compression checks the result and shows every ✗, with the manual Restore button from Phase 2 for anything lost.
This is why the mode is labeled experimental: the benchmark measures the model preserving only a quarter of critical constraints on its own. The ledger is not a nice-to-have on top of Local ML mode — it is the only reason its output is usable at all.
AI Mode uses your own key for the provider you pick:
- Anthropic — https://platform.claude.com/settings/keys
- OpenAI — https://platform.openai.com/api-keys
- Google AI Studio — https://aistudio.google.com/app/apikey
Paste it into the key field in AI Mode. By default the key lives in memory
only: it is never written to disk, never put in the URL, and never logged.
Ticking "Remember in this browser" stores it in sessionStorage, which is
cleared when you close the tab; un-ticking it wipes what was stored. Nothing is
ever written to localStorage.
If you do not provide a key, PromptTrim still works in Fast Mode (no API key needed).
- Reducing LLM API costs on system prompts and templates that run at volume
- Fitting prompts into a smaller context window without losing instructions
- Catching duplicated or contradictory instructions in a long system prompt
- Deciding whether to compress a prompt or just cache its static prefix
- Cleaning up verbose, AI-generated prompt drafts before they ship
- Compressing a batch of prompts at once and reviewing the summary table
The engine is a workspace package (packages/core), so the code that runs in
the browser is the code that runs in CI — no second implementation to keep in
step. packages/cli wraps it in a promptrim binary and action.yml wraps
that in a GitHub Action.
Where a token linter reports, promptrim check also offers a compressed
version and proves it is safe: a trim is suggested only when every
critical constraint in the original still verifies against the compressed
text, and --write applies only those. Anything the ledger vetoes is reported
as withheld, never applied quietly.
Not on npm yet. Run it from a clone —
npm run cli -- check "prompts/**/*.md"— or through the Action, which builds it for you. Thenpx promptrimform below is the interface the published package will expose.
npx promptrim check "prompts/**/*.md" --budget 2000 --model claude-opus-5PromptTrim check — 2 files
claude-opus-5 · balanced · budget 2,000 · 1,000 calls/day · base main
prompts/system_prompt.md
~2,640 tokens · 39ドル.60/month · 2,100 → 2,640 vs base (+540, +26%)
budget OVER by 640 tokens (limit 2,000)
trim -312 tokens (-12%), 4ドル.68/month
verified 14/14 critical constraints preserved, 1 change blocked by the ledger
dupes 3 duplicated instructions
| Flag | What it does |
|---|---|
--budget <n> |
Per-file token ceiling. Files above it are flagged. |
--model <id> |
Tokenizer and price to use. Any id in packages/core/src/data/pricing.json. Default claude-sonnet-5. |
--level <level> |
light, balanced (default) or aggressive — which rule set the suggested trim comes from. |
--base <ref> / --no-base |
Git ref the token delta is measured against. Default main; falls back to origin/<ref>. |
--calls-per-day <n> |
Volume behind the monthly cost projection. Default 1,000. |
--write |
Overwrite each file with its verified compressed version. Never touches a file whose trim the ledger vetoed. |
--format <fmt> |
text (default), markdown (the PR comment body) or json. |
--out <path> |
Also write the report to a file. |
--fail-on <list> |
budget, regression, duplicates, or none. Default budget. |
--cwd <dir> |
Run as if started in this directory. |
Exit codes: 0 clean, 1 a gate in --fail-on tripped, 2 the invocation or
the filesystem was wrong.
Token counts are exact for OpenAI models (js-tiktoken, o200k_base) and for
Anthropic and Gemini models when ANTHROPIC_API_KEY / GEMINI_API_KEY are in
the environment. Otherwise they are the calibrated estimate, and every
estimated number is printed with a ~ rather than passed off as a
measurement.
name: PromptTrim on: pull_request permissions: contents: read pull-requests: write jobs: prompts: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # so the token delta has a base to compare with - uses: FabianIMV/promptrim@main with: files: | prompts/**/*.md budget: '2000' model: claude-opus-5 calls-per-day: '50000' fail-on: budget
It posts one sticky comment per pull request (found by the
<!-- promptrim-report --> marker and edited in place, so a ten-commit branch
does not collect ten reports), writes the same report to the job summary, and
fails the job only for the gates you listed in fail-on — the default is
none, report-only.
| Input | Default | |
|---|---|---|
files |
prompts/**/*.md |
Glob patterns, one per line. Directories are scanned for .md, .markdown, .txt, .prompt. |
budget |
(none) | Per-file token ceiling. |
model |
claude-sonnet-5 |
Tokenizer and price. |
level |
balanced |
Level the suggested trim comes from. |
base-ref |
the PR's base branch | none disables the comparison. |
calls-per-day |
1000 |
Volume behind the cost projection. |
fail-on |
none |
budget, regression, duplicates, comma-separated. |
comment |
true |
Post/update the pull request comment. |
working-directory |
. |
Directory to run in. |
node-version |
22 |
Node used to build and run the CLI. |
github-token |
github.token |
Needs pull-requests: write to comment. |
Outputs: exit-code and report-path.
This repository runs the action on itself
(.github/workflows/promptrim.yml) over
bench/corpus/, so a break in the action fails a pull request here first.
- Vite + TypeScript + Preact
- npm workspaces: the engine is
packages/core, the CLI ispackages/cli, the web app is the root - Compression engine as pure functions in
packages/core/src/(no DOM), covered by Vitest - Anthropic, OpenAI and Gemini REST APIs (optional, browser-side, bring your own key)
@atjsh/llmlingua-2+@huggingface/transformersfor Local ML mode (optional, lazy-loaded, experimental)lz-stringfor the shareable-URL state, a service worker for offline Fast mode- GitHub Pages, built and deployed by GitHub Actions
npm ci npm run dev # local dev server npm run lint # ESLint + Prettier npm test # Vitest npm run build # type-check + production build into dist/ npm run bench # regenerate bench/results/ and the tables above npm run cli -- check "bench/corpus/phase6/*.md" --budget 400 # build + run the CLI
npm run build builds the workspace packages first (npm run build:packages),
then type-checks and builds the site. The web app and the tests import
@promptrim/core through a Vite alias pointing at packages/core/src, so
npm run dev and npm test need no package build in between; only the CLI
consumes the emitted packages/core/dist.
Layout:
| Path | What lives there |
|---|---|
packages/core/src/segment.ts |
Marks code, strings, URLs, JSON, tables, variables and examples as protected regions |
packages/core/src/rules/ |
Compression rules, each with id, level, lossy, a readable "why" and its own test cases |
packages/core/src/compress.ts |
Applies rules outside protected regions and returns a list of Changes |
packages/core/src/ledger/ |
Extracts, verifies and restores the constraint checklist |
packages/core/src/cache-advisor/ |
Compress-vs-cache economics per provider |
packages/core/src/share.ts, transfer.ts, batch.ts |
Share-by-URL, .txt/.md/.json import-export, and ----separated batch mode |
packages/core/src/data/ |
pricing.json and caching.json, each with its last_verified date and source URL |
packages/cli/src/ |
The promptrim check binary: globbing, git deltas, the report renderers and the exit-code gates |
action.yml |
The GitHub Action wrapping the CLI, with the sticky pull request comment |
src/providers/ |
Browser-side LLM providers (Anthropic, OpenAI, Gemini) and the compress → verify → repair pipeline |
src/local-ml/ |
Local ML mode (experimental): segment-aware LLMLingua-2 compression, always through the ledger |
src/ui/ |
Preact components mounted into the static SEO page |
bench/run.ts |
The npm run bench script — see Benchmark |
bench/corpus/ |
Prompts used as regression fixtures and benchmark input |
docs/PLAN.md |
Phased plan and status table |
packages/core/src/rules/discarded.ts records the legacy rules that were deliberately
not ported (anything that deleted instructions such as "step by step",
"ensure", "always" or whole sentences), with the reason for each.
- Full Spanish localization of the interactive tool (the landing page has an
/es/version; the compressor UI itself is still English-only) - CLI + GitHub Action — see CLI and GitHub Action. Not published to npm yet
- Publish
promptrimto npm sonpx promptrim checkworks without a clone - Optional local ML compression mode (LLMLingua-2, no API key) — see docs/PLAN.md Phase 8. Ships as experimental: the benchmark measures it preserving only 25% of critical constraints unaided, which is exactly why it always runs through the same protected regions and ledger as the other modes.
Issues and pull requests are welcome.
If PromptTrim saves you tokens, consider giving the repo a ⭐
MIT License — see LICENSE