ragcompress
Extractive, provenance-preserving context compression for RAG pipelines.
ragcompress is a drop-in reranker/compression stage between your retriever
and your generator LLM. Given a query and a list of retrieved chunks, it
selects the minimal set of original, verbatim sentences needed to answer
the query — instead of summarizing (which can hallucinate) or truncating to a
fixed top-k (which wastes budget on irrelevant sentences inside relevant
chunks).
retriever ──> [ragcompress] ──> generator LLM
keeps only the sentences the query needs,
verbatim, with per-sentence source attribution
Why
- Extractive, never abstractive. Every output sentence exists verbatim in an input chunk. Nothing is paraphrased, so the compression stage cannot hallucinate — and citations stay valid.
- Provenance built in. Each kept sentence carries its source
doc_idandchunk_id, queryable on the result object and optionally inlined as[doc_id]markers in the context string. - Adaptive token budget. Instead of a fixed top-k, an elbow-detection budget keeps sentences until relevance falls off a cliff — short context for easy questions, longer for multi-hop ones.
- Framework-native adapters. A LlamaIndex
NodePostprocessorand a LangChainBaseDocumentCompressor(drop-in forContextualCompressionRetriever). - CPU-friendly. The default scorer is a small pretrained cross-encoder
(
ms-marco-MiniLM-L-6-v2) plus a MiniLM embedder for redundancy removal — no fine-tuning, no GPU required.
Install
pip install ragcompress # core
pip install ragcompress[llamaindex] # + LlamaIndex adapter
pip install ragcompress[langchain] # + LangChain adapter
pip install ragcompress[attention] # + attention-based scorer (local HF models)
pip install ragcompress[benchmarks] # + benchmark suite deps
Quickstart
from ragcompress import Chunk, Compressor, FixedBudget
chunks = [
Chunk(doc_id="dune-wiki", chunk_id="c0", text="Dune is a novel by Frank Herbert. ..."),
# ... more retrieved chunks
]
compressor = Compressor(budget=FixedBudget(max_tokens=500))
result = compressor.compress("Who wrote Dune?", chunks)
result.text # compressed context, ready for your prompt
result.compression_ratio # e.g. 0.18
result.sentences[0].source_doc_id # provenance for every kept sentence
result.provenance() # [(doc_id, chunk_id), ...] in output order
Swap in the adaptive budget:
from ragcompress import AdaptiveBudget, Compressor
compressor = Compressor(budget=AdaptiveBudget(min_tokens=64, max_tokens=1024))
How it works
- Split — chunks are segmented into sentences with provenance tags. Code fences, markdown tables, and list items are kept atomic (never split mid-fence).
- Score — each
(query, sentence)pair is scored by a cross-encoder reranker; an MMR-style redundancy pass (cosine similarity between candidates) penalizes near-duplicates of higher-ranked sentences so diverse evidence wins. - Budget — a controller selects sentences:
FixedBudget(top-k and/or token cap) orAdaptiveBudget(elbow detection on the score distribution, bounded by amin_tokens/max_tokensrange). - Reassemble — survivors are deduplicated, restored to original reading
order (doc → chunk → sentence position, which reads more coherently to
the generator than score order), and joined into the final context with
optional
[doc_id]provenance markers.
Scoring backends
| Scorer | When to use |
|---|---|
CrossEncoderScorer (default) |
Portable, CPU-friendly; any pretrained reranker from the HF hub |
AttentionScorer (opt-in, [attention] extra) |
You run a local HF model and want AttnComp-style scores from query→sentence attention mass in one forward pass |
from ragcompress.scoring import AttentionScorer
compressor = Compressor(scorer=AttentionScorer(model="path/or/hf-id-of-local-model"))
Framework adapters
LlamaIndex (pip install ragcompress[llamaindex]):
from ragcompress.adapters.llamaindex import RagCompressPostprocessor
postprocessor = RagCompressPostprocessor.from_compressor(compressor) # mode="merge" | "filter"
query_engine = index.as_query_engine(node_postprocessors=[postprocessor])
LangChain (pip install ragcompress[langchain]):
from langchain.retrievers import ContextualCompressionRetriever
from ragcompress.adapters.langchain import RagCompressDocumentCompressor
retriever = ContextualCompressionRetriever(
base_compressor=RagCompressDocumentCompressor(compressor=compressor),
base_retriever=vectorstore.as_retriever(),
)
Runnable examples: examples/basic_usage.py,
examples/llamaindex_pipeline.py,
examples/langchain_pipeline.py.
Benchmarks
The suite in benchmarks/ compares against no-compression,
naive top-k truncation, and LLMLingua-2 on HotpotQA, TriviaQA, and Natural
Questions — measuring token reduction, latency, citation validity (does
the compressed context still contain a gold-supporting sentence), and
downstream EM/F1 when a generator LLM is configured:
pip install ragcompress[benchmarks]
python benchmarks/run_benchmark.py --dataset hotpotqa --n 500 --generator anthropic
Sample run — HotpotQA distractor (n=30), 500-token budget, CPU (Apple
Silicon), default cross-encoder scorer, retention metrics (add
--generator anthropic for downstream EM/F1; llmlingua2 skipped — run
with the [benchmarks] extra installed to include it):
| Method | Token reduction | Latency (ms) | Citation validity |
|---|---|---|---|
| no_compression | 0.0% | 0.0 | 100.0% |
| top3_truncation | 67.1% | 0.0 | 43.3% |
| ragcompress_fixed | 58.8% | 718.8 | 100.0% |
| ragcompress_adaptive | 84.2% | 406.9 | 96.7% |
Naive truncation at a similar budget silently drops a gold-supporting
sentence in 57% of questions; ragcompress keeps it while cutting more
tokens. Full report: benchmarks/results/hotpotqa_n30.md.
API sketch
@dataclass
class Chunk:
doc_id
chunk_id
text
metadata
@dataclass
class ScoredSentence:
text
score
source_chunk_id
source_doc_id
sentence_index_in_chunk
@dataclass
class CompressedContext:
text
sentences
original_token_count
compressed_token_count
compression_ratio
CompressedContext.provenance() returns (doc_id, chunk_id) per kept
sentence; .sources() returns the contributing doc IDs.
Guarantees
- Extractiveness invariant (tested): every output sentence is a verbatim substring of some input chunk — never paraphrased or altered.
- Provenance invariant (tested): every output sentence's
source_chunk_id/source_doc_idmaps back to the chunk that contains it.
Development
git clone https://github.com/ragcompress/ragcompress && cd ragcompress
pip install -e ".[dev,langchain,llamaindex]"
pytest # fast unit tests, no downloads
RAGCOMPRESS_RUN_MODEL_TESTS=1 pytest -m model # real-model smoke tests
ruff check . && mypy
See CONTRIBUTING.md. Licensed under Apache-2.0.
Metadata
Release files for ragcompress 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| ragcompress-0.1.0.tar.gz | 42.6 kB | Without provenance | Details for ragcompress-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| ragcompress-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for ragcompress-0.1.0-py3-none-any.whl |
Total release size: 78.2 kB
Release files / ragcompress-0.1.0.tar.gz
| Download URL | ragcompress-0.1.0.tar.gz |
|---|---|
| Size | 42.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
839e1f24ab6864bdcd34ae1f67cb191d57bdfbc0d5b4e463b7f8c4947e4e18a7
|
|
BLAKE2b-256 checksum How to use checksums |
c281ae63bed62713278b4967d717d68803650a0a5876cc788b8cf4d5a5f25a43
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.7.13
|
Release files / ragcompress-0.1.0-py3-none-any.whl
| Download URL | ragcompress-0.1.0-py3-none-any.whl |
|---|---|
| Size | 35.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0372d13b8404967da73b75f5eb029a768bb98c04e8f6f4c618402ec333dfc6c1
|
|
BLAKE2b-256 checksum How to use checksums |
2a1040304a8b54fed6c6abf2d162d76708ef396794b4157fd60125c5e250e6c3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.7.13
|
Security for ragcompress 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.