quasar-context 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- quasar_context-0.1.0/LICENSE +21 -0
- quasar_context-0.1.0/PKG-INFO +202 -0
- quasar_context-0.1.0/README.md +177 -0
- quasar_context-0.1.0/pyproject.toml +36 -0
- quasar_context-0.1.0/quasar/__init__.py +35 -0
- quasar_context-0.1.0/quasar/core.py +296 -0
- quasar_context-0.1.0/quasar_context.egg-info/PKG-INFO +202 -0
- quasar_context-0.1.0/quasar_context.egg-info/SOURCES.txt +11 -0
- quasar_context-0.1.0/quasar_context.egg-info/dependency_links.txt +1 -0
- quasar_context-0.1.0/quasar_context.egg-info/requires.txt +7 -0
- quasar_context-0.1.0/quasar_context.egg-info/top_level.txt +1 -0
- quasar_context-0.1.0/setup.cfg +4 -0
- quasar_context-0.1.0/tests/test_core.py +108 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sebastian Sabo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: quasar-context
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Faithful context optimization for RAG apps — compress without corrupting critical data
|
|
5
|
+
Author: Sebastian Sabo
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/sebastiansabo/quasar
|
|
8
|
+
Project-URL: Issues, https://github.com/sebastiansabo/quasar/issues
|
|
9
|
+
Keywords: rag,llm,context,compression,prompt,embeddings
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: sentence-transformers>=2.2.0
|
|
19
|
+
Requires-Dist: tiktoken>=0.5.0
|
|
20
|
+
Requires-Dist: numpy>=1.21.0
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# Quasar
|
|
27
|
+
|
|
28
|
+
**Faithful context optimization for RAG apps.**
|
|
29
|
+
|
|
30
|
+
Compress retrieved context before your LLM call — without silently corrupting the
|
|
31
|
+
values that matter. Prices, IBANs, dates, invoice numbers, legal references and
|
|
32
|
+
IDs are preserved **verbatim** when the budget allows. When it doesn't, Quasar
|
|
33
|
+
**tells you** instead of quietly dropping them.
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from quasar import ContextOptimizer
|
|
37
|
+
|
|
38
|
+
opt = ContextOptimizer()
|
|
39
|
+
result = opt.optimize(query, retrieved_chunks, target_tokens=500)
|
|
40
|
+
|
|
41
|
+
llm_answer(query, result.context) # feed the compressed context
|
|
42
|
+
print(result.report.summary())
|
|
43
|
+
# [Quasar] 63% smaller (1284->475 tok, ~$0.0024 saved) | critical 7/7 OK
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Why this exists
|
|
49
|
+
|
|
50
|
+
Every context compressor optimizes for one thing: fewer tokens. None of them tell
|
|
51
|
+
you what they destroyed on the way.
|
|
52
|
+
|
|
53
|
+
If your RAG app retrieves an invoice and the compressor drops or mangles
|
|
54
|
+
`€47,350.00`, your LLM confidently answers with the wrong number and **you never
|
|
55
|
+
find out**. That's fine for a chatbot. It is not fine for finance, legal,
|
|
56
|
+
medical, compliance, or code.
|
|
57
|
+
|
|
58
|
+
Quasar makes one guarantee no other compressor makes:
|
|
59
|
+
|
|
60
|
+
> **It never silently corrupts your critical data.**
|
|
61
|
+
> Critical values survive verbatim, or you get an explicit warning.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## What it does
|
|
66
|
+
|
|
67
|
+
1. **Detects critical spans** — currency, IBANs, dates, VINs, long IDs, legal
|
|
68
|
+
references, contract codes — by pattern.
|
|
69
|
+
2. **Reserves them verbatim.** Critical sentences claim budget first and are
|
|
70
|
+
never rewritten. (Token-deletion compressors *rewrite* text — that's how they
|
|
71
|
+
corrupt exact values.)
|
|
72
|
+
3. **Fills the remaining budget by relevance**, using embedding similarity to the
|
|
73
|
+
query. Filler is dropped.
|
|
74
|
+
4. **Dedupes** repeated sentences so budget isn't wasted on copies.
|
|
75
|
+
5. **Audits and reports.** Every call returns tokens saved, cost saved, and a
|
|
76
|
+
faithfulness status — with warnings naming any critical value it couldn't fit.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Honest benchmarks
|
|
81
|
+
|
|
82
|
+
Measured on LongBench (narrativeqa / qasper / hotpotqa, N=30 per task), real LLM
|
|
83
|
+
judge, token-F1 scoring.
|
|
84
|
+
|
|
85
|
+
**Where Quasar wins:**
|
|
86
|
+
|
|
87
|
+
| Matchup | Result |
|
|
88
|
+
|---|---|
|
|
89
|
+
| vs **truncation** | **9–0** — wins at every task and budget |
|
|
90
|
+
| vs **LLMLingua** | **6–0** on accuracy, **8–13× faster** to compress |
|
|
91
|
+
| Long contexts | Handles 17k+ word documents LLMLingua **cannot process at all** |
|
|
92
|
+
|
|
93
|
+
LLMLingua runs a neural model to compress. Quasar runs embeddings. Same or better
|
|
94
|
+
accuracy, a fraction of the compute.
|
|
95
|
+
|
|
96
|
+
**Where Quasar does not win — stated plainly:**
|
|
97
|
+
|
|
98
|
+
Against a **pure top-k embedding filter** (e.g. LangChain's `EmbeddingsFilter`),
|
|
99
|
+
Quasar goes **3–6** on raw QA accuracy. On pure question-answering, simple
|
|
100
|
+
relevance ranking is competitive or better.
|
|
101
|
+
|
|
102
|
+
**So Quasar is not the accuracy leader on QA, and doesn't claim to be.** Its value
|
|
103
|
+
is the axis those benchmarks don't measure: *faithfulness*. A top-k filter has no
|
|
104
|
+
concept of critical data — it drops a low-relevance sentence containing your
|
|
105
|
+
invoice number without hesitation, and never tells you. Quasar reserves it, and
|
|
106
|
+
warns when it can't.
|
|
107
|
+
|
|
108
|
+
**Use Quasar when correctness of exact values matters more than the last 2% of
|
|
109
|
+
retrieval F1.** If you're building a general chatbot and only care about QA
|
|
110
|
+
accuracy, use a top-k filter — it's simpler and we'll say so.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Install
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
pip install quasar-context
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Usage
|
|
121
|
+
|
|
122
|
+
### Basic
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from quasar import ContextOptimizer
|
|
126
|
+
|
|
127
|
+
opt = ContextOptimizer() # loads the embedding model once
|
|
128
|
+
result = opt.optimize(
|
|
129
|
+
query="What is the total due and the deadline?",
|
|
130
|
+
context=retrieved_chunks, # str or list[str]
|
|
131
|
+
target_tokens=500,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
result.context # compressed text for your LLM
|
|
135
|
+
result.report.tokens_saved # int
|
|
136
|
+
result.report.faithful # bool — did all critical values survive?
|
|
137
|
+
result.report.warnings # list[str] — what got dropped and why
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Guarding a production call
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
result = opt.optimize(query, chunks, target_tokens=500)
|
|
144
|
+
|
|
145
|
+
if not result.report.faithful:
|
|
146
|
+
# budget too tight to hold every critical value — your call:
|
|
147
|
+
logger.warning("Quasar: %s", result.report.warnings)
|
|
148
|
+
result = opt.optimize(query, chunks, target_tokens=1000) # give it room
|
|
149
|
+
|
|
150
|
+
answer = llm(query, result.context)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Configuration
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
from quasar import ContextOptimizer, OptimizerConfig
|
|
157
|
+
|
|
158
|
+
opt = ContextOptimizer(OptimizerConfig(
|
|
159
|
+
cost_per_1k_tokens=0.003, # your model's price, for the savings report
|
|
160
|
+
preserve_critical=True, # the faithfulness behavior (default on)
|
|
161
|
+
min_relevance=0.05, # drop sentences below this query relevance
|
|
162
|
+
model_name="all-MiniLM-L6-v2",
|
|
163
|
+
))
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Custom critical patterns
|
|
167
|
+
|
|
168
|
+
Your domain has its own critical formats. Add them:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
from quasar import core
|
|
172
|
+
core._CRITICAL_PATTERNS.append((r"\bCASE-\d{6}\b", "case_id"))
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Performance
|
|
178
|
+
|
|
179
|
+
- **~10–100 ms per call** after warm-up (embedding + greedy selection).
|
|
180
|
+
- **First call loads the model** (~30–60s, one time). Warm it at startup:
|
|
181
|
+
```python
|
|
182
|
+
opt = ContextOptimizer()
|
|
183
|
+
opt.optimize("warmup", "warmup text.", target_tokens=50)
|
|
184
|
+
```
|
|
185
|
+
- **8–13× faster than LLMLingua**, which runs a full neural model to compress.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## What Quasar is not
|
|
190
|
+
|
|
191
|
+
- **Not the best compressor by ratio.** Token-deletion methods compress harder.
|
|
192
|
+
They also rewrite your text.
|
|
193
|
+
- **Not an accuracy leader on pure QA.** A top-k filter matches or beats it there.
|
|
194
|
+
- **Not a guarantee against physics.** If your critical content is larger than
|
|
195
|
+
your token budget, it cannot all fit. Quasar preserves what it can *and tells
|
|
196
|
+
you what it couldn't* — that's the honest contract.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT.
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Quasar
|
|
2
|
+
|
|
3
|
+
**Faithful context optimization for RAG apps.**
|
|
4
|
+
|
|
5
|
+
Compress retrieved context before your LLM call — without silently corrupting the
|
|
6
|
+
values that matter. Prices, IBANs, dates, invoice numbers, legal references and
|
|
7
|
+
IDs are preserved **verbatim** when the budget allows. When it doesn't, Quasar
|
|
8
|
+
**tells you** instead of quietly dropping them.
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
from quasar import ContextOptimizer
|
|
12
|
+
|
|
13
|
+
opt = ContextOptimizer()
|
|
14
|
+
result = opt.optimize(query, retrieved_chunks, target_tokens=500)
|
|
15
|
+
|
|
16
|
+
llm_answer(query, result.context) # feed the compressed context
|
|
17
|
+
print(result.report.summary())
|
|
18
|
+
# [Quasar] 63% smaller (1284->475 tok, ~$0.0024 saved) | critical 7/7 OK
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Why this exists
|
|
24
|
+
|
|
25
|
+
Every context compressor optimizes for one thing: fewer tokens. None of them tell
|
|
26
|
+
you what they destroyed on the way.
|
|
27
|
+
|
|
28
|
+
If your RAG app retrieves an invoice and the compressor drops or mangles
|
|
29
|
+
`€47,350.00`, your LLM confidently answers with the wrong number and **you never
|
|
30
|
+
find out**. That's fine for a chatbot. It is not fine for finance, legal,
|
|
31
|
+
medical, compliance, or code.
|
|
32
|
+
|
|
33
|
+
Quasar makes one guarantee no other compressor makes:
|
|
34
|
+
|
|
35
|
+
> **It never silently corrupts your critical data.**
|
|
36
|
+
> Critical values survive verbatim, or you get an explicit warning.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## What it does
|
|
41
|
+
|
|
42
|
+
1. **Detects critical spans** — currency, IBANs, dates, VINs, long IDs, legal
|
|
43
|
+
references, contract codes — by pattern.
|
|
44
|
+
2. **Reserves them verbatim.** Critical sentences claim budget first and are
|
|
45
|
+
never rewritten. (Token-deletion compressors *rewrite* text — that's how they
|
|
46
|
+
corrupt exact values.)
|
|
47
|
+
3. **Fills the remaining budget by relevance**, using embedding similarity to the
|
|
48
|
+
query. Filler is dropped.
|
|
49
|
+
4. **Dedupes** repeated sentences so budget isn't wasted on copies.
|
|
50
|
+
5. **Audits and reports.** Every call returns tokens saved, cost saved, and a
|
|
51
|
+
faithfulness status — with warnings naming any critical value it couldn't fit.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Honest benchmarks
|
|
56
|
+
|
|
57
|
+
Measured on LongBench (narrativeqa / qasper / hotpotqa, N=30 per task), real LLM
|
|
58
|
+
judge, token-F1 scoring.
|
|
59
|
+
|
|
60
|
+
**Where Quasar wins:**
|
|
61
|
+
|
|
62
|
+
| Matchup | Result |
|
|
63
|
+
|---|---|
|
|
64
|
+
| vs **truncation** | **9–0** — wins at every task and budget |
|
|
65
|
+
| vs **LLMLingua** | **6–0** on accuracy, **8–13× faster** to compress |
|
|
66
|
+
| Long contexts | Handles 17k+ word documents LLMLingua **cannot process at all** |
|
|
67
|
+
|
|
68
|
+
LLMLingua runs a neural model to compress. Quasar runs embeddings. Same or better
|
|
69
|
+
accuracy, a fraction of the compute.
|
|
70
|
+
|
|
71
|
+
**Where Quasar does not win — stated plainly:**
|
|
72
|
+
|
|
73
|
+
Against a **pure top-k embedding filter** (e.g. LangChain's `EmbeddingsFilter`),
|
|
74
|
+
Quasar goes **3–6** on raw QA accuracy. On pure question-answering, simple
|
|
75
|
+
relevance ranking is competitive or better.
|
|
76
|
+
|
|
77
|
+
**So Quasar is not the accuracy leader on QA, and doesn't claim to be.** Its value
|
|
78
|
+
is the axis those benchmarks don't measure: *faithfulness*. A top-k filter has no
|
|
79
|
+
concept of critical data — it drops a low-relevance sentence containing your
|
|
80
|
+
invoice number without hesitation, and never tells you. Quasar reserves it, and
|
|
81
|
+
warns when it can't.
|
|
82
|
+
|
|
83
|
+
**Use Quasar when correctness of exact values matters more than the last 2% of
|
|
84
|
+
retrieval F1.** If you're building a general chatbot and only care about QA
|
|
85
|
+
accuracy, use a top-k filter — it's simpler and we'll say so.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Install
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pip install quasar-context
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Usage
|
|
96
|
+
|
|
97
|
+
### Basic
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from quasar import ContextOptimizer
|
|
101
|
+
|
|
102
|
+
opt = ContextOptimizer() # loads the embedding model once
|
|
103
|
+
result = opt.optimize(
|
|
104
|
+
query="What is the total due and the deadline?",
|
|
105
|
+
context=retrieved_chunks, # str or list[str]
|
|
106
|
+
target_tokens=500,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
result.context # compressed text for your LLM
|
|
110
|
+
result.report.tokens_saved # int
|
|
111
|
+
result.report.faithful # bool — did all critical values survive?
|
|
112
|
+
result.report.warnings # list[str] — what got dropped and why
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Guarding a production call
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
result = opt.optimize(query, chunks, target_tokens=500)
|
|
119
|
+
|
|
120
|
+
if not result.report.faithful:
|
|
121
|
+
# budget too tight to hold every critical value — your call:
|
|
122
|
+
logger.warning("Quasar: %s", result.report.warnings)
|
|
123
|
+
result = opt.optimize(query, chunks, target_tokens=1000) # give it room
|
|
124
|
+
|
|
125
|
+
answer = llm(query, result.context)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Configuration
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from quasar import ContextOptimizer, OptimizerConfig
|
|
132
|
+
|
|
133
|
+
opt = ContextOptimizer(OptimizerConfig(
|
|
134
|
+
cost_per_1k_tokens=0.003, # your model's price, for the savings report
|
|
135
|
+
preserve_critical=True, # the faithfulness behavior (default on)
|
|
136
|
+
min_relevance=0.05, # drop sentences below this query relevance
|
|
137
|
+
model_name="all-MiniLM-L6-v2",
|
|
138
|
+
))
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Custom critical patterns
|
|
142
|
+
|
|
143
|
+
Your domain has its own critical formats. Add them:
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
from quasar import core
|
|
147
|
+
core._CRITICAL_PATTERNS.append((r"\bCASE-\d{6}\b", "case_id"))
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Performance
|
|
153
|
+
|
|
154
|
+
- **~10–100 ms per call** after warm-up (embedding + greedy selection).
|
|
155
|
+
- **First call loads the model** (~30–60s, one time). Warm it at startup:
|
|
156
|
+
```python
|
|
157
|
+
opt = ContextOptimizer()
|
|
158
|
+
opt.optimize("warmup", "warmup text.", target_tokens=50)
|
|
159
|
+
```
|
|
160
|
+
- **8–13× faster than LLMLingua**, which runs a full neural model to compress.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## What Quasar is not
|
|
165
|
+
|
|
166
|
+
- **Not the best compressor by ratio.** Token-deletion methods compress harder.
|
|
167
|
+
They also rewrite your text.
|
|
168
|
+
- **Not an accuracy leader on pure QA.** A top-k filter matches or beats it there.
|
|
169
|
+
- **Not a guarantee against physics.** If your critical content is larger than
|
|
170
|
+
your token budget, it cannot all fit. Quasar preserves what it can *and tells
|
|
171
|
+
you what it couldn't* — that's the honest contract.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## License
|
|
176
|
+
|
|
177
|
+
MIT.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "quasar-context"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Faithful context optimization for RAG apps — compress without corrupting critical data"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Sebastian Sabo" }]
|
|
13
|
+
keywords = ["rag", "llm", "context", "compression", "prompt", "embeddings"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
dependencies = [
|
|
23
|
+
"sentence-transformers>=2.2.0",
|
|
24
|
+
"tiktoken>=0.5.0",
|
|
25
|
+
"numpy>=1.21.0",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
dev = ["pytest>=7.0", "pytest-cov"]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/sebastiansabo/quasar"
|
|
33
|
+
Issues = "https://github.com/sebastiansabo/quasar/issues"
|
|
34
|
+
|
|
35
|
+
[tool.setuptools.packages.find]
|
|
36
|
+
include = ["quasar*"]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Quasar — faithful context optimization for RAG apps.
|
|
3
|
+
|
|
4
|
+
Compress retrieved context before your LLM call. Critical values
|
|
5
|
+
(prices, IBANs, dates, IDs, legal refs) are preserved verbatim when the
|
|
6
|
+
budget allows — and you get an explicit warning when it can't, instead of
|
|
7
|
+
silent corruption.
|
|
8
|
+
|
|
9
|
+
Quick start:
|
|
10
|
+
from quasar import ContextOptimizer
|
|
11
|
+
|
|
12
|
+
opt = ContextOptimizer()
|
|
13
|
+
result = opt.optimize(query, retrieved_chunks, target_tokens=500)
|
|
14
|
+
|
|
15
|
+
result.context # compressed context -> feed to your LLM
|
|
16
|
+
result.report.faithful # bool: did every critical value survive?
|
|
17
|
+
print(result.report.summary())
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from .core import (
|
|
21
|
+
ContextOptimizer,
|
|
22
|
+
OptimizerConfig,
|
|
23
|
+
OptimizationResult,
|
|
24
|
+
OptimizationReport,
|
|
25
|
+
find_critical,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
__version__ = "0.1.0"
|
|
29
|
+
__all__ = [
|
|
30
|
+
"ContextOptimizer",
|
|
31
|
+
"OptimizerConfig",
|
|
32
|
+
"OptimizationResult",
|
|
33
|
+
"OptimizationReport",
|
|
34
|
+
"find_critical",
|
|
35
|
+
]
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"""
|
|
2
|
+
================================================================================
|
|
3
|
+
quasar_layer.py -- Drop-in context optimization for RAG apps
|
|
4
|
+
================================================================================
|
|
5
|
+
WHAT IT IS
|
|
6
|
+
----------
|
|
7
|
+
A single layer you insert between retrieval and your LLM call. It:
|
|
8
|
+
1. Picks the right compression strategy for the context automatically.
|
|
9
|
+
2. GUARANTEES exact critical content (IDs, prices, dates, citations) survives
|
|
10
|
+
verbatim -- the one thing pure relevance ranking can't promise.
|
|
11
|
+
3. Reports exactly what it did: tokens saved, est. cost saved, what was kept,
|
|
12
|
+
and any faithfulness risk it caught.
|
|
13
|
+
|
|
14
|
+
WHAT IS NOT
|
|
15
|
+
-----------
|
|
16
|
+
Not a magic compressor that beats everyone. Honest benchmarks showed
|
|
17
|
+
embedding-selection beats LLMLingua at far lower cost but ties/loses to simple
|
|
18
|
+
top-k on pure QA. So this layer's value is NOT "best compression" -- it is:
|
|
19
|
+
- zero-config: developer drops it in, no tuning
|
|
20
|
+
- safe-by-reporting: when budget can't fit all critical data, it does NOT
|
|
21
|
+
silently corrupt -- it preserves what fits and WARNS about what it dropped.
|
|
22
|
+
(No compressor can fit N critical facts into a budget smaller than them;
|
|
23
|
+
the honest behavior is to surface that, not hide it.)
|
|
24
|
+
- transparent: shows the savings + what the LLM actually sees
|
|
25
|
+
- cheap: embeddings, not an LLM-to-compress (8-13x faster than LLMLingua)
|
|
26
|
+
|
|
27
|
+
DESIGN PRINCIPLE
|
|
28
|
+
----------------
|
|
29
|
+
The product is the DECISION + the PROOF, not the algorithm. Developers don't
|
|
30
|
+
know whether to compress, how much, or whether it's safe. This answers that
|
|
31
|
+
per-call, automatically, with receipts.
|
|
32
|
+
|
|
33
|
+
INSTALL
|
|
34
|
+
pip install sentence-transformers tiktoken
|
|
35
|
+
|
|
36
|
+
USE (the whole API is three lines)
|
|
37
|
+
from quasar_layer import ContextOptimizer
|
|
38
|
+
opt = ContextOptimizer()
|
|
39
|
+
result = opt.optimize(query, retrieved_chunks, target_tokens=500)
|
|
40
|
+
# result.context -> compressed context, feed to your LLM
|
|
41
|
+
# result.report -> tokens saved, cost saved, faithfulness status
|
|
42
|
+
================================================================================
|
|
43
|
+
"""
|
|
44
|
+
from __future__ import annotations
|
|
45
|
+
import re, time
|
|
46
|
+
from dataclasses import dataclass, field
|
|
47
|
+
from typing import Optional
|
|
48
|
+
|
|
49
|
+
# ---------- optional deps, graceful fallback ----------
|
|
50
|
+
try:
|
|
51
|
+
import tiktoken; _ENC = tiktoken.get_encoding("cl100k_base")
|
|
52
|
+
except Exception:
|
|
53
|
+
_ENC = None
|
|
54
|
+
def ntok(t: str) -> int:
|
|
55
|
+
if _ENC: return len(_ENC.encode(t))
|
|
56
|
+
return max(1, int(len(t.split()) * 1.3))
|
|
57
|
+
|
|
58
|
+
_MODEL = None
|
|
59
|
+
def _model(name="all-MiniLM-L6-v2"):
|
|
60
|
+
global _MODEL
|
|
61
|
+
if _MODEL is None:
|
|
62
|
+
from sentence_transformers import SentenceTransformer
|
|
63
|
+
_MODEL = SentenceTransformer(name)
|
|
64
|
+
return _MODEL
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# =============================================================================
|
|
68
|
+
# CRITICAL CONTENT DETECTION (the faithfulness guarantee)
|
|
69
|
+
# =============================================================================
|
|
70
|
+
# Spans matching these MUST survive verbatim. Extensible by the developer.
|
|
71
|
+
_CRITICAL_PATTERNS = [
|
|
72
|
+
(r'[€$£¥]\s?\d[\d,.]*', "currency"),
|
|
73
|
+
(r'\b\d{1,3}(?:[,.]\d{3})+(?:\.\d+)?\b', "large_number"),
|
|
74
|
+
(r'\b[A-HJ-NPR-Z0-9]{17}\b', "vin"),
|
|
75
|
+
(r'\bRO\d{2}[A-Z0-9]{16,}\b', "iban"),
|
|
76
|
+
(r'\b[A-Z]{2}\d{2}[A-Z0-9]{10,}\b', "account"),
|
|
77
|
+
(r'\b\d{1,2}[/.\-]\d{1,2}[/.\-]\d{2,4}\b', "date"),
|
|
78
|
+
(r'\b(?:Order|Article|Regulation|Section|Clause|Directive)\s+[\w/.\-]+', "legal_ref"),
|
|
79
|
+
(r'\b[A-Z0-9]{6,}\-[A-Z0-9]{3,}\b', "code"),
|
|
80
|
+
(r'\b\d{6,}\b', "long_id"),
|
|
81
|
+
]
|
|
82
|
+
def find_critical(text: str) -> list[str]:
|
|
83
|
+
hits = []
|
|
84
|
+
for pat, kind in _CRITICAL_PATTERNS:
|
|
85
|
+
for m in re.finditer(pat, text):
|
|
86
|
+
hits.append(m.group(0))
|
|
87
|
+
return hits
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# =============================================================================
|
|
91
|
+
# RESULT + REPORT
|
|
92
|
+
# =============================================================================
|
|
93
|
+
@dataclass
|
|
94
|
+
class OptimizationReport:
|
|
95
|
+
tokens_in: int
|
|
96
|
+
tokens_out: int
|
|
97
|
+
tokens_saved: int
|
|
98
|
+
pct_saved: float
|
|
99
|
+
est_cost_saved_usd: float # at a configurable $/1k tokens
|
|
100
|
+
strategy: str # which path was taken + why
|
|
101
|
+
critical_found: int
|
|
102
|
+
critical_preserved: int
|
|
103
|
+
faithful: bool # all critical content survived?
|
|
104
|
+
warnings: list = field(default_factory=list)
|
|
105
|
+
optimize_ms: float = 0.0
|
|
106
|
+
def summary(self) -> str:
|
|
107
|
+
flag = "OK" if self.faithful else "FAITHFULNESS RISK"
|
|
108
|
+
return (f"[Quasar] {self.pct_saved:.0f}% smaller "
|
|
109
|
+
f"({self.tokens_in}->{self.tokens_out} tok, "
|
|
110
|
+
f"~${self.est_cost_saved_usd:.4f} saved) | "
|
|
111
|
+
f"strategy={self.strategy} | critical {self.critical_preserved}/{self.critical_found} {flag}"
|
|
112
|
+
+ ("" if not self.warnings else f" | {len(self.warnings)} warning(s)"))
|
|
113
|
+
|
|
114
|
+
@dataclass
|
|
115
|
+
class OptimizationResult:
|
|
116
|
+
context: str
|
|
117
|
+
report: OptimizationReport
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# =============================================================================
|
|
121
|
+
# THE OPTIMIZER
|
|
122
|
+
# =============================================================================
|
|
123
|
+
@dataclass
|
|
124
|
+
class OptimizerConfig:
|
|
125
|
+
cost_per_1k_tokens: float = 0.003 # for the savings estimate (set to your model)
|
|
126
|
+
skip_if_under: int = 0 # if context already <= target, don't touch it
|
|
127
|
+
min_relevance: float = 0.05 # drop sentences below this query relevance
|
|
128
|
+
preserve_critical: bool = True # the faithfulness guarantee (on by default)
|
|
129
|
+
model_name: str = "all-MiniLM-L6-v2"
|
|
130
|
+
|
|
131
|
+
class ContextOptimizer:
|
|
132
|
+
"""
|
|
133
|
+
Drop-in: retrieved context in, optimized context out, with receipts.
|
|
134
|
+
Auto-selects strategy:
|
|
135
|
+
- context already within budget -> passthrough (no cost, no risk)
|
|
136
|
+
- context over budget -> faithful embedding-selection:
|
|
137
|
+
* reserve critical spans verbatim (guarantee)
|
|
138
|
+
* fill remaining budget by query relevance (the workhorse that
|
|
139
|
+
beat LLMLingua at a fraction of the cost)
|
|
140
|
+
"""
|
|
141
|
+
def __init__(self, config: OptimizerConfig = OptimizerConfig()):
|
|
142
|
+
self.cfg = config
|
|
143
|
+
|
|
144
|
+
def _split(self, text: str) -> list[str]:
|
|
145
|
+
return [s.strip() for s in re.split(r'(?<=[.!?])\s+', text.strip()) if s.strip()]
|
|
146
|
+
|
|
147
|
+
def optimize(self, query: str, context, target_tokens: int = 500) -> OptimizationResult:
|
|
148
|
+
t0 = time.perf_counter()
|
|
149
|
+
# accept either a string or a list of chunks
|
|
150
|
+
if isinstance(context, (list, tuple)):
|
|
151
|
+
context = "\n".join(str(c) for c in context)
|
|
152
|
+
tokens_in = ntok(context)
|
|
153
|
+
crit_all = find_critical(context)
|
|
154
|
+
|
|
155
|
+
# ---- STEP 0: DEDUPE FIRST (always) ----
|
|
156
|
+
# Collapse identical sentences so we never spend budget on copies.
|
|
157
|
+
# This MUST run before the passthrough check: a context of 8 identical
|
|
158
|
+
# sentences may fit under the budget, but shipping 8 copies to the LLM
|
|
159
|
+
# wastes the very budget we exist to protect.
|
|
160
|
+
raw_sents = self._split(context)
|
|
161
|
+
seen = set(); sents = []
|
|
162
|
+
for s in raw_sents:
|
|
163
|
+
key = re.sub(r'\s+', ' ', s.strip().lower())
|
|
164
|
+
if key in seen: continue
|
|
165
|
+
seen.add(key); sents.append(s)
|
|
166
|
+
deduped = " ".join(sents)
|
|
167
|
+
tokens_deduped = ntok(deduped)
|
|
168
|
+
dupes_removed = len(raw_sents) - len(sents)
|
|
169
|
+
|
|
170
|
+
# ---- STRATEGY 1: passthrough if the DEDUPED context already fits ----
|
|
171
|
+
if tokens_deduped <= target_tokens:
|
|
172
|
+
saved = tokens_in - tokens_deduped
|
|
173
|
+
crit_kept = sum(1 for c in set(crit_all) if c in deduped)
|
|
174
|
+
strategy = ("passthrough (already within budget)" if dupes_removed == 0
|
|
175
|
+
else f"dedupe-only ({dupes_removed} duplicate sentence(s) removed)")
|
|
176
|
+
rep = OptimizationReport(
|
|
177
|
+
tokens_in, tokens_deduped, saved,
|
|
178
|
+
100.0 * saved / max(1, tokens_in),
|
|
179
|
+
self.cfg.cost_per_1k_tokens * saved / 1000.0,
|
|
180
|
+
strategy=strategy,
|
|
181
|
+
critical_found=len(set(crit_all)),
|
|
182
|
+
critical_preserved=crit_kept,
|
|
183
|
+
faithful=(crit_kept == len(set(crit_all))),
|
|
184
|
+
optimize_ms=(time.perf_counter()-t0)*1000)
|
|
185
|
+
return OptimizationResult(deduped, rep)
|
|
186
|
+
|
|
187
|
+
# ---- STRATEGY 2: faithful embedding-selection ----
|
|
188
|
+
model = _model(self.cfg.model_name)
|
|
189
|
+
embs = model.encode([query] + sents, normalize_embeddings=True, show_progress_bar=False)
|
|
190
|
+
qv, svs = embs[0], embs[1:]
|
|
191
|
+
sims = svs @ qv
|
|
192
|
+
toks = [ntok(s) for s in sents]
|
|
193
|
+
|
|
194
|
+
chosen, used = [], 0
|
|
195
|
+
warnings = []
|
|
196
|
+
|
|
197
|
+
# 2a. reserve sentences containing critical spans (best-effort preservation)
|
|
198
|
+
if self.cfg.preserve_critical:
|
|
199
|
+
crit_idx = [i for i, s in enumerate(sents) if find_critical(s)]
|
|
200
|
+
# sort critical sentences by relevance so if budget is too tight we keep
|
|
201
|
+
# the most query-relevant critical ones first, and warn about drops
|
|
202
|
+
crit_idx.sort(key=lambda i: -sims[i])
|
|
203
|
+
for i in crit_idx:
|
|
204
|
+
if used + toks[i] <= target_tokens:
|
|
205
|
+
chosen.append(i); used += toks[i]
|
|
206
|
+
else:
|
|
207
|
+
warnings.append(f"critical sentence dropped (budget too tight): "
|
|
208
|
+
f"'{sents[i][:50]}...'")
|
|
209
|
+
|
|
210
|
+
# 2b. fill remaining budget with top relevance (skip already chosen + filler)
|
|
211
|
+
rest = [i for i in range(len(sents)) if i not in chosen]
|
|
212
|
+
rest.sort(key=lambda i: -sims[i])
|
|
213
|
+
for i in rest:
|
|
214
|
+
if float(sims[i]) < self.cfg.min_relevance: break
|
|
215
|
+
if used + toks[i] > target_tokens: continue
|
|
216
|
+
chosen.append(i); used += toks[i]
|
|
217
|
+
|
|
218
|
+
out_text = " ".join(sents[i] for i in sorted(chosen))
|
|
219
|
+
tokens_out = ntok(out_text)
|
|
220
|
+
|
|
221
|
+
# faithfulness audit: did every critical span survive verbatim?
|
|
222
|
+
crit_out = find_critical(out_text)
|
|
223
|
+
preserved = sum(1 for c in set(crit_all) if c in out_text)
|
|
224
|
+
faithful = (preserved == len(set(crit_all)))
|
|
225
|
+
if not faithful:
|
|
226
|
+
missing = [c for c in set(crit_all) if c not in out_text]
|
|
227
|
+
warnings.append(f"{len(missing)} critical value(s) not preserved: {missing[:3]}")
|
|
228
|
+
|
|
229
|
+
saved = tokens_in - tokens_out
|
|
230
|
+
rep = OptimizationReport(
|
|
231
|
+
tokens_in, tokens_out, saved,
|
|
232
|
+
100.0 * saved / max(1, tokens_in),
|
|
233
|
+
self.cfg.cost_per_1k_tokens * saved / 1000.0,
|
|
234
|
+
strategy="faithful-selection (critical reserved + relevance fill)",
|
|
235
|
+
critical_found=len(set(crit_all)),
|
|
236
|
+
critical_preserved=preserved,
|
|
237
|
+
faithful=faithful,
|
|
238
|
+
warnings=warnings,
|
|
239
|
+
optimize_ms=(time.perf_counter()-t0)*1000)
|
|
240
|
+
return OptimizationResult(out_text, rep)
|
|
241
|
+
|
|
242
|
+
# --- convenience: wrap an LLM call so the developer gets one-line integration ---
|
|
243
|
+
def optimized_prompt(self, query: str, context, target_tokens=500,
|
|
244
|
+
template="Context:\n{context}\n\nQuestion: {query}\nAnswer:"):
|
|
245
|
+
res = self.optimize(query, context, target_tokens)
|
|
246
|
+
return template.format(context=res.context, query=query), res.report
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
# =============================================================================
|
|
250
|
+
# DEMO — shows the developer experience
|
|
251
|
+
# =============================================================================
|
|
252
|
+
def _demo():
|
|
253
|
+
sample_chunks = [
|
|
254
|
+
"The quarterly business review covered several operational topics across regions.",
|
|
255
|
+
"Market conditions have remained generally stable over the recent reporting period.",
|
|
256
|
+
"The invoice total amount due is €47,350.00 payable by 15/03/2026 per the agreement.",
|
|
257
|
+
"Various stakeholders provided feedback during the planning session last week.",
|
|
258
|
+
"The applicable regulation is Order 1802/2014 Article 7 governing this transaction.",
|
|
259
|
+
"Overall sector trends suggest continued steady commercial activity this year.",
|
|
260
|
+
"The vehicle identification number on record is WAUZZZ8K9BA123456 for this unit.",
|
|
261
|
+
"Documentation has been updated to reflect the latest procedural changes made.",
|
|
262
|
+
"The team continues to monitor performance metrics across all departments daily.",
|
|
263
|
+
"Payment should be remitted to IBAN RO49AAAA1B31007593840000 before the deadline.",
|
|
264
|
+
] * 3 # triple it to force real compression
|
|
265
|
+
|
|
266
|
+
query = "What is the total amount due and the payment deadline?"
|
|
267
|
+
|
|
268
|
+
print("="*78)
|
|
269
|
+
print("QUASAR CONTEXT OPTIMIZER — developer experience demo")
|
|
270
|
+
print("="*78)
|
|
271
|
+
opt = ContextOptimizer(OptimizerConfig(cost_per_1k_tokens=0.003))
|
|
272
|
+
|
|
273
|
+
for budget in [120, 300]:
|
|
274
|
+
res = opt.optimize(query, sample_chunks, target_tokens=budget)
|
|
275
|
+
print(f"\n--- target_tokens={budget} ---")
|
|
276
|
+
print(res.report.summary())
|
|
277
|
+
print(f" optimize time: {res.report.optimize_ms:.0f} ms")
|
|
278
|
+
if res.report.warnings:
|
|
279
|
+
for w in res.report.warnings: print(f" ! {w}")
|
|
280
|
+
print(f" LLM now sees ({res.report.tokens_out} tok):")
|
|
281
|
+
print(f' "{res.context[:220]}..."')
|
|
282
|
+
|
|
283
|
+
print("\n" + "="*78)
|
|
284
|
+
print("THE PITCH (honest):")
|
|
285
|
+
print(" - 3-line drop-in: optimize(query, chunks, target_tokens)")
|
|
286
|
+
print(" - Auto-passthrough when context already fits (no needless cost/risk)")
|
|
287
|
+
print(" - Preserves critical values (prices, IBANs, dates, refs) verbatim when")
|
|
288
|
+
print(" budget allows -- and WARNS (never silently corrupts) when it can't")
|
|
289
|
+
print(" - Dedupes repeated content so budget isn't wasted on copies")
|
|
290
|
+
print(" - 8-13x cheaper to run than LLMLingua (embeddings, not an LLM)")
|
|
291
|
+
print(" - Every call returns receipts: tokens saved, $ saved, faithfulness status")
|
|
292
|
+
print(" - The product is the DECISION + PROOF, not a 'magic' compressor.")
|
|
293
|
+
print("="*78)
|
|
294
|
+
|
|
295
|
+
if __name__ == "__main__":
|
|
296
|
+
_demo()
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: quasar-context
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Faithful context optimization for RAG apps — compress without corrupting critical data
|
|
5
|
+
Author: Sebastian Sabo
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/sebastiansabo/quasar
|
|
8
|
+
Project-URL: Issues, https://github.com/sebastiansabo/quasar/issues
|
|
9
|
+
Keywords: rag,llm,context,compression,prompt,embeddings
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: sentence-transformers>=2.2.0
|
|
19
|
+
Requires-Dist: tiktoken>=0.5.0
|
|
20
|
+
Requires-Dist: numpy>=1.21.0
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# Quasar
|
|
27
|
+
|
|
28
|
+
**Faithful context optimization for RAG apps.**
|
|
29
|
+
|
|
30
|
+
Compress retrieved context before your LLM call — without silently corrupting the
|
|
31
|
+
values that matter. Prices, IBANs, dates, invoice numbers, legal references and
|
|
32
|
+
IDs are preserved **verbatim** when the budget allows. When it doesn't, Quasar
|
|
33
|
+
**tells you** instead of quietly dropping them.
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from quasar import ContextOptimizer
|
|
37
|
+
|
|
38
|
+
opt = ContextOptimizer()
|
|
39
|
+
result = opt.optimize(query, retrieved_chunks, target_tokens=500)
|
|
40
|
+
|
|
41
|
+
llm_answer(query, result.context) # feed the compressed context
|
|
42
|
+
print(result.report.summary())
|
|
43
|
+
# [Quasar] 63% smaller (1284->475 tok, ~$0.0024 saved) | critical 7/7 OK
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Why this exists
|
|
49
|
+
|
|
50
|
+
Every context compressor optimizes for one thing: fewer tokens. None of them tell
|
|
51
|
+
you what they destroyed on the way.
|
|
52
|
+
|
|
53
|
+
If your RAG app retrieves an invoice and the compressor drops or mangles
|
|
54
|
+
`€47,350.00`, your LLM confidently answers with the wrong number and **you never
|
|
55
|
+
find out**. That's fine for a chatbot. It is not fine for finance, legal,
|
|
56
|
+
medical, compliance, or code.
|
|
57
|
+
|
|
58
|
+
Quasar makes one guarantee no other compressor makes:
|
|
59
|
+
|
|
60
|
+
> **It never silently corrupts your critical data.**
|
|
61
|
+
> Critical values survive verbatim, or you get an explicit warning.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## What it does
|
|
66
|
+
|
|
67
|
+
1. **Detects critical spans** — currency, IBANs, dates, VINs, long IDs, legal
|
|
68
|
+
references, contract codes — by pattern.
|
|
69
|
+
2. **Reserves them verbatim.** Critical sentences claim budget first and are
|
|
70
|
+
never rewritten. (Token-deletion compressors *rewrite* text — that's how they
|
|
71
|
+
corrupt exact values.)
|
|
72
|
+
3. **Fills the remaining budget by relevance**, using embedding similarity to the
|
|
73
|
+
query. Filler is dropped.
|
|
74
|
+
4. **Dedupes** repeated sentences so budget isn't wasted on copies.
|
|
75
|
+
5. **Audits and reports.** Every call returns tokens saved, cost saved, and a
|
|
76
|
+
faithfulness status — with warnings naming any critical value it couldn't fit.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Honest benchmarks
|
|
81
|
+
|
|
82
|
+
Measured on LongBench (narrativeqa / qasper / hotpotqa, N=30 per task), real LLM
|
|
83
|
+
judge, token-F1 scoring.
|
|
84
|
+
|
|
85
|
+
**Where Quasar wins:**
|
|
86
|
+
|
|
87
|
+
| Matchup | Result |
|
|
88
|
+
|---|---|
|
|
89
|
+
| vs **truncation** | **9–0** — wins at every task and budget |
|
|
90
|
+
| vs **LLMLingua** | **6–0** on accuracy, **8–13× faster** to compress |
|
|
91
|
+
| Long contexts | Handles 17k+ word documents LLMLingua **cannot process at all** |
|
|
92
|
+
|
|
93
|
+
LLMLingua runs a neural model to compress. Quasar runs embeddings. Same or better
|
|
94
|
+
accuracy, a fraction of the compute.
|
|
95
|
+
|
|
96
|
+
**Where Quasar does not win — stated plainly:**
|
|
97
|
+
|
|
98
|
+
Against a **pure top-k embedding filter** (e.g. LangChain's `EmbeddingsFilter`),
|
|
99
|
+
Quasar goes **3–6** on raw QA accuracy. On pure question-answering, simple
|
|
100
|
+
relevance ranking is competitive or better.
|
|
101
|
+
|
|
102
|
+
**So Quasar is not the accuracy leader on QA, and doesn't claim to be.** Its value
|
|
103
|
+
is the axis those benchmarks don't measure: *faithfulness*. A top-k filter has no
|
|
104
|
+
concept of critical data — it drops a low-relevance sentence containing your
|
|
105
|
+
invoice number without hesitation, and never tells you. Quasar reserves it, and
|
|
106
|
+
warns when it can't.
|
|
107
|
+
|
|
108
|
+
**Use Quasar when correctness of exact values matters more than the last 2% of
|
|
109
|
+
retrieval F1.** If you're building a general chatbot and only care about QA
|
|
110
|
+
accuracy, use a top-k filter — it's simpler and we'll say so.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Install
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
pip install quasar-context
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Usage
|
|
121
|
+
|
|
122
|
+
### Basic
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from quasar import ContextOptimizer
|
|
126
|
+
|
|
127
|
+
opt = ContextOptimizer() # loads the embedding model once
|
|
128
|
+
result = opt.optimize(
|
|
129
|
+
query="What is the total due and the deadline?",
|
|
130
|
+
context=retrieved_chunks, # str or list[str]
|
|
131
|
+
target_tokens=500,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
result.context # compressed text for your LLM
|
|
135
|
+
result.report.tokens_saved # int
|
|
136
|
+
result.report.faithful # bool — did all critical values survive?
|
|
137
|
+
result.report.warnings # list[str] — what got dropped and why
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Guarding a production call
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
result = opt.optimize(query, chunks, target_tokens=500)
|
|
144
|
+
|
|
145
|
+
if not result.report.faithful:
|
|
146
|
+
# budget too tight to hold every critical value — your call:
|
|
147
|
+
logger.warning("Quasar: %s", result.report.warnings)
|
|
148
|
+
result = opt.optimize(query, chunks, target_tokens=1000) # give it room
|
|
149
|
+
|
|
150
|
+
answer = llm(query, result.context)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Configuration
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
from quasar import ContextOptimizer, OptimizerConfig
|
|
157
|
+
|
|
158
|
+
opt = ContextOptimizer(OptimizerConfig(
|
|
159
|
+
cost_per_1k_tokens=0.003, # your model's price, for the savings report
|
|
160
|
+
preserve_critical=True, # the faithfulness behavior (default on)
|
|
161
|
+
min_relevance=0.05, # drop sentences below this query relevance
|
|
162
|
+
model_name="all-MiniLM-L6-v2",
|
|
163
|
+
))
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Custom critical patterns
|
|
167
|
+
|
|
168
|
+
Your domain has its own critical formats. Add them:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
from quasar import core
|
|
172
|
+
core._CRITICAL_PATTERNS.append((r"\bCASE-\d{6}\b", "case_id"))
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Performance
|
|
178
|
+
|
|
179
|
+
- **~10–100 ms per call** after warm-up (embedding + greedy selection).
|
|
180
|
+
- **First call loads the model** (~30–60s, one time). Warm it at startup:
|
|
181
|
+
```python
|
|
182
|
+
opt = ContextOptimizer()
|
|
183
|
+
opt.optimize("warmup", "warmup text.", target_tokens=50)
|
|
184
|
+
```
|
|
185
|
+
- **8–13× faster than LLMLingua**, which runs a full neural model to compress.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## What Quasar is not
|
|
190
|
+
|
|
191
|
+
- **Not the best compressor by ratio.** Token-deletion methods compress harder.
|
|
192
|
+
They also rewrite your text.
|
|
193
|
+
- **Not an accuracy leader on pure QA.** A top-k filter matches or beats it there.
|
|
194
|
+
- **Not a guarantee against physics.** If your critical content is larger than
|
|
195
|
+
your token budget, it cannot all fit. Quasar preserves what it can *and tells
|
|
196
|
+
you what it couldn't* — that's the honest contract.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
quasar/__init__.py
|
|
5
|
+
quasar/core.py
|
|
6
|
+
quasar_context.egg-info/PKG-INFO
|
|
7
|
+
quasar_context.egg-info/SOURCES.txt
|
|
8
|
+
quasar_context.egg-info/dependency_links.txt
|
|
9
|
+
quasar_context.egg-info/requires.txt
|
|
10
|
+
quasar_context.egg-info/top_level.txt
|
|
11
|
+
tests/test_core.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
quasar
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for Quasar. The critical ones assert the FAITHFULNESS CONTRACT:
|
|
3
|
+
- critical values survive verbatim when budget allows
|
|
4
|
+
- when budget is too tight, the report says so (never silent)
|
|
5
|
+
If these fail, the product's only differentiator is broken.
|
|
6
|
+
"""
|
|
7
|
+
import pytest
|
|
8
|
+
from quasar import ContextOptimizer, OptimizerConfig, find_critical
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# ---------------------------------------------------------------- fixtures
|
|
12
|
+
@pytest.fixture(scope="module")
|
|
13
|
+
def opt():
|
|
14
|
+
return ContextOptimizer(OptimizerConfig(cost_per_1k_tokens=0.003))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
CRITICAL_CHUNKS = [
|
|
18
|
+
"The quarterly business review covered several operational topics.",
|
|
19
|
+
"The invoice total amount due is €47,350.00 payable by 15/03/2026.",
|
|
20
|
+
"Market conditions have remained generally stable this period.",
|
|
21
|
+
"Payment should be remitted to IBAN RO49AAAA1B31007593840000 promptly.",
|
|
22
|
+
"The team continues to monitor performance metrics across departments.",
|
|
23
|
+
"The applicable regulation is Order 1802/2014 governing this deal.",
|
|
24
|
+
"Documentation has been updated to reflect procedural changes made.",
|
|
25
|
+
]
|
|
26
|
+
QUERY = "What is the total amount due and the payment deadline?"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# ---------------------------------------------------------------- detection
|
|
30
|
+
def test_finds_currency():
|
|
31
|
+
assert any("47,350" in c for c in find_critical("Total is €47,350.00 due now."))
|
|
32
|
+
|
|
33
|
+
def test_finds_iban():
|
|
34
|
+
hits = find_critical("Send to RO49AAAA1B31007593840000 today.")
|
|
35
|
+
assert any("RO49AAAA" in h for h in hits)
|
|
36
|
+
|
|
37
|
+
def test_finds_date():
|
|
38
|
+
assert find_critical("Due by 15/03/2026 without fail.")
|
|
39
|
+
|
|
40
|
+
def test_ignores_plain_prose():
|
|
41
|
+
assert find_critical("The weather today is mild and pleasant.") == []
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# ---------------------------------------------------------------- the contract
|
|
45
|
+
def test_critical_preserved_when_budget_allows(opt):
|
|
46
|
+
"""THE core claim: with room, every critical value survives verbatim."""
|
|
47
|
+
res = opt.optimize(QUERY, CRITICAL_CHUNKS, target_tokens=300)
|
|
48
|
+
assert res.report.faithful, f"critical data lost: {res.report.warnings}"
|
|
49
|
+
assert "€47,350.00" in res.context
|
|
50
|
+
assert "RO49AAAA1B31007593840000" in res.context
|
|
51
|
+
assert "15/03/2026" in res.context
|
|
52
|
+
assert res.report.critical_preserved == res.report.critical_found
|
|
53
|
+
|
|
54
|
+
def test_warns_when_budget_too_tight(opt):
|
|
55
|
+
"""THE honesty claim: if it can't fit critical data, it MUST say so."""
|
|
56
|
+
res = opt.optimize(QUERY, CRITICAL_CHUNKS, target_tokens=15)
|
|
57
|
+
if not res.report.faithful:
|
|
58
|
+
assert res.report.warnings, "dropped critical data but issued NO warning"
|
|
59
|
+
assert res.report.critical_preserved < res.report.critical_found
|
|
60
|
+
|
|
61
|
+
def test_never_silently_drops(opt):
|
|
62
|
+
"""faithful==False must always be accompanied by warnings."""
|
|
63
|
+
for budget in (10, 20, 30, 50, 80, 200, 400):
|
|
64
|
+
res = opt.optimize(QUERY, CRITICAL_CHUNKS, target_tokens=budget)
|
|
65
|
+
if not res.report.faithful:
|
|
66
|
+
assert res.report.warnings, f"silent drop at budget={budget}"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# ---------------------------------------------------------------- behavior
|
|
70
|
+
def test_passthrough_when_already_small(opt):
|
|
71
|
+
res = opt.optimize(QUERY, "Short context.", target_tokens=500)
|
|
72
|
+
assert "passthrough" in res.report.strategy
|
|
73
|
+
assert res.report.tokens_saved == 0
|
|
74
|
+
assert res.report.faithful
|
|
75
|
+
|
|
76
|
+
def test_actually_compresses(opt):
|
|
77
|
+
big = CRITICAL_CHUNKS * 6
|
|
78
|
+
res = opt.optimize(QUERY, big, target_tokens=200)
|
|
79
|
+
assert res.report.tokens_out <= 200
|
|
80
|
+
assert res.report.tokens_saved > 0
|
|
81
|
+
assert res.report.pct_saved > 0
|
|
82
|
+
|
|
83
|
+
def test_dedupes_repeats(opt):
|
|
84
|
+
"""Duplicate sentences must not each consume budget."""
|
|
85
|
+
dupes = ["The invoice total amount due is €47,350.00 payable soon."] * 8
|
|
86
|
+
res = opt.optimize(QUERY, dupes, target_tokens=100)
|
|
87
|
+
assert res.context.count("€47,350.00") == 1, "kept duplicate critical sentences"
|
|
88
|
+
|
|
89
|
+
def test_accepts_string_or_list(opt):
|
|
90
|
+
as_list = opt.optimize(QUERY, CRITICAL_CHUNKS, target_tokens=200)
|
|
91
|
+
as_str = opt.optimize(QUERY, " ".join(CRITICAL_CHUNKS), target_tokens=200)
|
|
92
|
+
assert as_list.report.tokens_out == as_str.report.tokens_out
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ---------------------------------------------------------------- report
|
|
96
|
+
def test_report_is_complete(opt):
|
|
97
|
+
res = opt.optimize(QUERY, CRITICAL_CHUNKS * 4, target_tokens=150)
|
|
98
|
+
r = res.report
|
|
99
|
+
assert r.tokens_in > r.tokens_out
|
|
100
|
+
assert r.tokens_saved == r.tokens_in - r.tokens_out
|
|
101
|
+
assert 0 <= r.pct_saved <= 100
|
|
102
|
+
assert r.est_cost_saved_usd >= 0
|
|
103
|
+
assert isinstance(r.faithful, bool)
|
|
104
|
+
assert r.summary()
|
|
105
|
+
|
|
106
|
+
def test_empty_context_does_not_crash(opt):
|
|
107
|
+
res = opt.optimize(QUERY, "", target_tokens=100)
|
|
108
|
+
assert res.context == "" or res.report.faithful
|