--- license: apache-2.0 language: - en pipeline_tag: text-ranking tags: - context-compression - prompt-compression - agents - coding-agents - gradient-boosted-trees - lightgbm - extractive --- # trim-1 trim-1 is a small model that decides which lines of a context an AI agent needs for its current ask, and drops the rest. Give it a context (a tool result, a file, a log, a long chat) and the current query. It returns the context's own lines, in their original order, with a short marker wherever lines were removed. It never rewrites or summarizes a line, so identifiers, paths and error text come through exactly as they were. - **1.2 MB.** 712 gradient-boosted trees, not a neural network. - **Milliseconds on one CPU core.** No GPU, no network call, no extra model request. - **Deterministic.** No sampling and no randomness: the same context and query give the same output. - **Verbatim.** Every kept line is a line of the input. It saves the most on long-document retrieval, where it removes most of the input and keeps the evidence. On short coding-agent tool outputs it saves little by design. See [Which setting to use](#which-setting-to-use). trim-1 is the model inside [Caveman](https://github.com/JuliusBrussee/caveman). The code is at [caveman-ai/trim-1](https://github.com/caveman-ai/trim-1). ## How it works 1. The context is split into lines. 2. Each line gets 67 numeric features, computed against the query: - **Lexical match:** BM25 against the query, query-term coverage, shared bigrams and trigrams, compound identifier hits. - **Line kind:** whether the line reads as an error, a test result, a path, a heading, a diff header, a function signature, a JSON key or a stack frame. - **Position and shape:** where the line sits, its length, its indent and how the indent changes. - **Neighbours:** how the lines around it, and its enclosing header, score. 3. The trees turn each feature row into a keep probability. 4. An operating point turns the probabilities into a selection. It keeps every line scoring above a share of the best line's score, the top few lines regardless, one line of context on each side, and the enclosing block header. Three operating points ship: | point | floor (share of best line's score) | use | |---|---:|---| | `conservative` | 0.0035 | cut least | | `balanced` (default) | 0.008 | the default | | `aggressive` | 0.035 | cut most | trim-1 fails closed. It returns the context unchanged when the query is blank, the context is under 2,000 bytes, the context is over 8 MiB or 200,000 lines, or the cut would remove less than 10% of the bytes. ## Results One run on 2026-10-11, on frozen holdout splits of public suites. Operating points were chosen on dev splits and holdout was scored once. **Evidence kept.** A case passes when every piece of labelled evidence is still in the output. Cut is the share of tokens removed (`cl100k_base`, counted locally). | suite | cases | what must survive | point | pass rate | token cut | |---|---:|---|---|---:|---:| | agent traces | 200 | strings a coding agent used in its next turns | balanced | 100.0% | 1.9% | | code context | 150 | the lines the gold patch edits, inside retrieved code | balanced | 90.7% | 21.8% | | code context | 150 | | aggressive | 56.7% | 66.9% | | multi-hop QA | 300 | human-marked supporting facts | balanced | 100.0% | 1.7% | | multi-hop QA, long (8k-32k tokens) | 90 | | balanced | 94.4% | 88.9% | | haystack (10k-50k tokens) | 60 | 1-3 hop facts in logs, JSON, code and prose | balanced | 75.0% | 54.4% | **Answers kept.** A reader model answers from the output and a judge model grades the answer. "Full" is the reader given the uncompressed context. | suite | cases | full | balanced | aggressive | |---|---:|---:|---:|---:| | agent traces | 100 | 100% | 99% | 98% | | multi-hop QA, long | 90 | 88.9% | 85.6% | 76.7% | | multi-hop QA | 100 | 85.0% | 85.0% | 77.0% | | haystack | 60 | 91.7% | 70.0% | 48.3% | **Speed and memory.** Ten real inputs per size, one call at a time after a warm-up, on one CPU core while other jobs ran on the machine. | input tokens | p50 latency | memory (RSS) | |---:|---:|---:| | 700 | 1.1 ms | 24 MB | | 5,000 | 7.2 ms | 28 MB | | 20,000 | 22 ms | 31 MB | | 60,000 | 63 ms | 39 MB | ## Which setting to use The setting decides how hard trim-1 cuts, and cutting harder costs quality. Every line it removes is a line the model can no longer read, so a higher setting saves more tokens and loses more of what the task needed. How much you can cut before quality drops depends on the context: | context | setting | token cut | evidence kept | |---|---|---:|---:| | coding-agent tool outputs | `balanced` | 1.9% | 100.0% | | coding-agent tool outputs | floor 0.06 (between `balanced` and `aggressive`) | 27.1% | 92.0% | | retrieved code | `balanced` | 21.8% | 90.7% | | retrieved code | `aggressive` | 66.9% | 56.7% | | long documents (8k-32k tokens) | `balanced` | 88.9% | 94.4% | **Coding-agent tool outputs: use a low setting.** Stay at `balanced` or `conservative`. A tool output is short and dense: most of its lines are things the agent uses in its next turns. At `balanced` trim-1 keeps all of it and saves about 2%. Pushed to a 27% cut, 8% of cases lose something the agent went on to use. Expect small savings here, and do not raise the setting to get more. **Long-document retrieval: this is what trim-1 is for.** A long document holds a few lines that answer the query and a lot that do not. At `balanced` trim-1 removes 89% of the tokens and keeps the supporting evidence in 94% of cases, and a reader model answers 85.6% of questions from the output against 88.9% from the full document. `aggressive` drops that to 76.7%, so `balanced` is the setting to use here too. **Retrieved code sits in between.** `balanced` cuts 22% and keeps the lines the fix touches in 91% of cases. `aggressive` cuts 67% and keeps them in 57%, which is too lossy for most uses. ## Limits - **Entity chains.** trim-1 drops later hops that share no words with the query. This is the main cause of its haystack losses. - **It needs a query.** With no query it does nothing. - **Line-level only.** It keeps or drops whole lines; it does not shorten a long line. - **English.** Training and evaluation data are English text and source code. - **One run.** The numbers above come from a single run. Suites are clustered by session, repository or conversation, so per-case intervals understate the uncertainty. The judge model was not validated against human labels. - **In-distribution QA.** The multi-hop QA suites use the validation split of a dataset whose train split is in the training data. - **Not a savings claim.** Token cuts are local token counts on benchmark cases, not measured provider billing. ## Training data trim-1 was trained on public, commercially licensed data only. Labels are the ones each source provides: human-marked spans and supporting facts, the lines a gold patch edits, the element a person acted on, and what a coding agent used in its next turns. No language model wrote a label. Sources fall into four groups: - **Agent trajectories:** public coding-agent runs, labelled by what the agent used next. - **Code context:** issue-and-patch datasets over permissively licensed repositories. - **Documents and chat:** question answering, contract, finance and meeting datasets with human evidence labels. - **Structured output:** web pages, tool catalogs and API responses, SQL schemas, tables and CI logs. Non-commercial and unlicensed datasets, and code from copyleft or unlicensed repositories, were left out. Every training context was checked against the evaluation contexts and repositories and removed on a match. No private session data is in the model. ## Files | file | what it is | |---|---| | `model.bin` | the trees, 1,245,358 bytes, sha256 `75478721d9992c3cbe6a1874ddf363ae6d78935a1fa526fb6fac1d0f6f3cdc2d` | | `LICENSE` | Apache-2.0 | `model.bin` layout, little endian: ``` "KEEP" 4 bytes version uint32 (1) features, trees, nodes, leaves uint32 each (67, 712, 44144, 44856) feature names each: uint16 length + bytes tree roots []int32 node feature []int32 node threshold []float64 node left, node right []int32 each leaf values []float64 ``` A child or root below zero is the leaf `^index`. A node sends a row left when `row[feature] <= threshold`. A line's score is the sigmoid of the summed leaf values. ## Use The code is on GitHub: [caveman-ai/trim-1](https://github.com/caveman-ai/trim-1). It is pure Go with no dependencies, and the model is embedded. ```bash go install github.com/caveman-ai/trim-1/cmd/trim@latest trim --query "why does the build fail" build.log ``` From Go: ```go import trim "github.com/caveman-ai/trim-1" res := trim.Compress(context, query, trim.Options{Point: "balanced"}) fmt.Print(res.Output) ``` ## License Apache-2.0.