contextpress 0.5.3__tar.gz → 0.5.5__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.
- contextpress-0.5.5/AGENTS.md +45 -0
- contextpress-0.5.5/AUDIT.md +89 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/CHANGELOG.md +10 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/PKG-INFO +10 -1
- {contextpress-0.5.3 → contextpress-0.5.5}/README.md +9 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/__init__.py +1 -1
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/core.py +27 -0
- contextpress-0.5.5/examples/agent_pipeline.py +29 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/pyproject.toml +2 -2
- contextpress-0.5.5/tests/test_v054.py +53 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/.gitignore +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/CITATION.cff +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/CONTRIBUTING.md +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/LICENSE +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/NOTICE +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/_bootstrap.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/compression.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/llm/__init__.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/llm/_helpers.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/llm/adapters.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/llm/base.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/models.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/normalizer.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/pipeline.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/profiles.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/py.typed +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/registry.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/stats.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/strategies/__init__.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/strategies/base.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/strategies/budget.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/strategies/filler.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/strategies/recency.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/strategies/repetition.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/strategies/resolution.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/contextpress/warnings_capture.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/examples/benchmark_presets.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/examples/dry_run_preview.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/examples/estimate_and_stats.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/examples/llm_tier_claude.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/examples/llm_tier_gemini.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/examples/llm_tier_ollama.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/examples/llm_tier_openai.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/examples/pick_preset.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/tests/__init__.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/tests/test_budget.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/tests/test_filler.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/tests/test_llm_helpers.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/tests/test_models.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/tests/test_normalizer.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/tests/test_pipeline.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/tests/test_recency.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/tests/test_repetition.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/tests/test_resolution.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/tests/test_stats.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/tests/test_v03.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/tests/test_v04.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/tests/test_v05.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/tests/test_v051.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/tests/test_v052.py +0 -0
- {contextpress-0.5.3 → contextpress-0.5.5}/tests/test_v053.py +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Notes for agents and contributors
|
|
2
|
+
|
|
3
|
+
## Branches
|
|
4
|
+
|
|
5
|
+
- **`dev`** — default working branch; all feature work and fixes land here first.
|
|
6
|
+
- **`main`** — release branch; merge from `dev` when cutting a PyPI release.
|
|
7
|
+
|
|
8
|
+
Always `git checkout dev` before making changes in this repo.
|
|
9
|
+
|
|
10
|
+
## Where things live
|
|
11
|
+
|
|
12
|
+
| Area | Path |
|
|
13
|
+
|------|------|
|
|
14
|
+
| Public API | `contextpress/core.py` → `ContextManager` |
|
|
15
|
+
| I/O formats | `contextpress/normalizer.py` |
|
|
16
|
+
| Stage presets | `contextpress/compression.py` (`STAGE_ORDER`, `VALID_STAGES`) |
|
|
17
|
+
| Tier 1 stages | `contextpress/strategies/*.py` |
|
|
18
|
+
| Tier 2 | `contextpress/llm/` (`LLMBackend`, `adapters.py`) |
|
|
19
|
+
| Custom stages | `contextpress/registry.py` + `ContextManager.register_stage()` |
|
|
20
|
+
| Orchestration | `contextpress/pipeline.py` (behavior contract at top of file) |
|
|
21
|
+
| Profiles | `contextpress/profiles.py` |
|
|
22
|
+
|
|
23
|
+
`contextpress/__init__.py` lazy-loads `ContextManager` so `from contextpress.models import Turn` avoids pulling optional Tier-2 / sumy import chains when possible.
|
|
24
|
+
|
|
25
|
+
## Commands
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install -e ".[dev]"
|
|
29
|
+
pytest tests -q
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Invariants (non-negotiable)
|
|
33
|
+
|
|
34
|
+
See the numbered block at the top of `contextpress/pipeline.py`. Tests assume Tier 1 is deterministic (no live LLM calls in `tests/`).
|
|
35
|
+
|
|
36
|
+
## Stabilization backlog
|
|
37
|
+
|
|
38
|
+
See [`AUDIT.md`](AUDIT.md) for pre-0.6.0 findings (contract gaps, refactors, fixture gaps). Prefer low-risk items from that backlog before adding stages or new dependencies.
|
|
39
|
+
|
|
40
|
+
## Adding a stage
|
|
41
|
+
|
|
42
|
+
1. Subclass `BaseStrategy`, implement `process`.
|
|
43
|
+
2. Register it in `Pipeline._build_strategy`.
|
|
44
|
+
3. Add the name to `compression.STAGE_ORDER` and presets if it should be user-selectable.
|
|
45
|
+
4. Extend `profiles.Profile` / `PROFILES` if it needs per-type defaults.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Pre-0.6.0 stabilization audit (0.5.5)
|
|
2
|
+
|
|
3
|
+
Recorded after the 0.5.x ergonomics arc. This is the Phase 1 deliverable:
|
|
4
|
+
findings + backlog for Phases 2–4. Behavior changes are deferred unless marked
|
|
5
|
+
**fix now**.
|
|
6
|
+
|
|
7
|
+
## Architecture (summary)
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
ContextManager (core.py)
|
|
11
|
+
→ normalize_messages (normalizer.py)
|
|
12
|
+
→ apply_stage_selection (compression.py)
|
|
13
|
+
→ Pipeline.run (pipeline.py)
|
|
14
|
+
→ filler → repetition → resolution → recency → budget
|
|
15
|
+
→ optional Tier 2 LLM
|
|
16
|
+
→ denormalize_output
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Profiles: `chat`, `rag_doc`, `agent`. Tier 1 deps: nltk, scikit-learn, sumy, tiktoken.
|
|
20
|
+
|
|
21
|
+
## Findings
|
|
22
|
+
|
|
23
|
+
### Contract / correctness
|
|
24
|
+
|
|
25
|
+
| ID | Severity | Finding | Action |
|
|
26
|
+
|----|----------|---------|--------|
|
|
27
|
+
| C1 | Medium | Invariant 1 says system turns are untouched; `BudgetStrategy` truncates system as last resort (`budget.py`) | Document in contract (Phase 2) or change behavior later |
|
|
28
|
+
| C2 | Medium | LLM dedup failures in `Pipeline._run_llm_tier` fall back silently (no warning); summarize path warns | Add warning (Phase 2) |
|
|
29
|
+
| C3 | Medium | Filler drops ack phrases like `"sounds good"` before resolution can use them (`filler.py` / `resolution.py`) | Document + fixture test (Phases 3–4) |
|
|
30
|
+
| C4 | Low | `Turn.importance` / `Turn.resolved` set by resolution but unused by pipeline | Leave; optional cleanup later |
|
|
31
|
+
|
|
32
|
+
### Duplication / maintainability (Phase 2)
|
|
33
|
+
|
|
34
|
+
| ID | Finding | Action |
|
|
35
|
+
|----|---------|--------|
|
|
36
|
+
| R1 | TF-IDF + cosine similarity copied in `repetition.py`, `recency.py`, `resolution.py` | Extract shared helper |
|
|
37
|
+
| R2 | Token counting duplicated in `stats.py` and `budget.py` | Budget uses `stats` helpers |
|
|
38
|
+
| R3 | `_bootstrap.py` downloads unused `stopwords`; warning mentions recency (doesn't use NLTK) | Trim packages; fix message |
|
|
39
|
+
| R4 | Strategies use ad hoc `deepcopy` vs `clone_turn` | Prefer `clone_turn` where safe |
|
|
40
|
+
|
|
41
|
+
### Test / fixture gaps (Phases 3–4)
|
|
42
|
+
|
|
43
|
+
| ID | Gap |
|
|
44
|
+
|----|-----|
|
|
45
|
+
| T1 | No assert that input `messages` are immutable after `compress()` |
|
|
46
|
+
| T2 | No end-to-end `rag_doc` / richer `agent` pipeline tests |
|
|
47
|
+
| T3 | Normalizer edge cases: `None`, bad types, tuple skip, LangChain turn-count mismatch |
|
|
48
|
+
| T4 | No offline real-chat fixtures under `tests/fixtures/` |
|
|
49
|
+
| T5 | Cross-stage filler → resolution interaction untested |
|
|
50
|
+
|
|
51
|
+
### Normalizer fragility (document; fix only if fixtures break)
|
|
52
|
+
|
|
53
|
+
| ID | Area |
|
|
54
|
+
|----|------|
|
|
55
|
+
| N1 | LangChain detection heuristic on first list element |
|
|
56
|
+
| N2 | Dict/tuple skips change turn count vs input |
|
|
57
|
+
| N3 | New turns (RESOLVED / LLM summary) lose `_original_dict` keys |
|
|
58
|
+
| N4 | `apply_text_to_turn` only replaces the first text block |
|
|
59
|
+
|
|
60
|
+
### Deferred (not in 0.5.x stabilization)
|
|
61
|
+
|
|
62
|
+
- Resolution / normalizer rewrites
|
|
63
|
+
- New pipeline stages
|
|
64
|
+
- New LLM backends
|
|
65
|
+
- New hard dependencies
|
|
66
|
+
- Changing stage order without a proven bug
|
|
67
|
+
|
|
68
|
+
## Phase plan
|
|
69
|
+
|
|
70
|
+
| Phase | Version | Scope |
|
|
71
|
+
|-------|---------|--------|
|
|
72
|
+
| 1 Analysis | **0.5.5** | This document + backlog |
|
|
73
|
+
| 2 Refactor | **0.5.6** | R1–R3, C1 doc, C2 warning (no semantic stage changes) |
|
|
74
|
+
| 3 Fixtures | **0.5.7** | Offline chat samples + smoke/invariant tests |
|
|
75
|
+
| 4 Fixes | **0.5.8** | Bugs proven by fixtures + T1–T5 regression tests |
|
|
76
|
+
|
|
77
|
+
## Invariants checklist (from `pipeline.py`)
|
|
78
|
+
|
|
79
|
+
1. System turns pass through stages untouched — **except** budget last-resort truncation (C1).
|
|
80
|
+
2. Input never mutated — enforced by clones; **needs T1**.
|
|
81
|
+
3. Output format mirrors input — denormalize; weaker after turn collapse.
|
|
82
|
+
4. Last 3 non-system not compressed by recency — unit tested.
|
|
83
|
+
5. Last 2 non-system not removed by budget — unit tested.
|
|
84
|
+
6. Resolution (chat) needs both sides — unit tested; C3 may weaken signals.
|
|
85
|
+
7. Repetition: more recent wins — unit tested.
|
|
86
|
+
8. Tier 1 deterministic — assumed; sumy/NLTK have fallbacks.
|
|
87
|
+
9. LLM failure → Tier 1 + warning — summarize yes; dedup **C2**.
|
|
88
|
+
10. `token_budget=None` skips budget enforcement — OK.
|
|
89
|
+
11–12. Presets + Tier 2 modes — covered by existing tests.
|
|
@@ -4,6 +4,16 @@ All notable changes to `contextpress` are recorded here.
|
|
|
4
4
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.5.5] - 2026-07-26
|
|
8
|
+
|
|
9
|
+
- **Stabilization Phase 1** — codebase audit and prioritized backlog in [`AUDIT.md`](AUDIT.md).
|
|
10
|
+
- No runtime API changes; prep for low-risk refactors (0.5.6) and fixture-driven fixes (0.5.7–0.5.8).
|
|
11
|
+
|
|
12
|
+
## [0.5.4] - 2026-07-25
|
|
13
|
+
|
|
14
|
+
- **`compress_many()`** — batch ``compress()`` over a list of conversations.
|
|
15
|
+
- Example: `examples/agent_pipeline.py` for ``type="agent"`` tool/task threads.
|
|
16
|
+
|
|
7
17
|
## [0.5.3] - 2026-07-24
|
|
8
18
|
|
|
9
19
|
- **`recommend_preset()`** — pick the mildest preset (low → medium → high) that fits a token budget.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: contextpress
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.5
|
|
4
4
|
Summary: Deterministic context compression for LLM chat, RAG, and agent pipelines
|
|
5
5
|
Project-URL: Homepage, https://github.com/Taha-azizi/contextpress
|
|
6
6
|
Project-URL: Documentation, https://github.com/Taha-azizi/contextpress#readme
|
|
@@ -323,6 +323,12 @@ preset = cm.recommend_preset(messages, token_budget=500)
|
|
|
323
323
|
out = cm.compress(messages, token_budget=500, compression=preset)
|
|
324
324
|
```
|
|
325
325
|
|
|
326
|
+
**Batch compress** (0.5.4+):
|
|
327
|
+
|
|
328
|
+
```python
|
|
329
|
+
results = cm.compress_many(list_of_conversations, token_budget=2000, return_stats=True)
|
|
330
|
+
```
|
|
331
|
+
|
|
326
332
|
**Async** (0.5+):
|
|
327
333
|
|
|
328
334
|
```python
|
|
@@ -391,6 +397,8 @@ ContextManager(type="rag_doc")
|
|
|
391
397
|
ContextManager(type="agent")
|
|
392
398
|
```
|
|
393
399
|
|
|
400
|
+
Runnable agent example: [`examples/agent_pipeline.py`](examples/agent_pipeline.py).
|
|
401
|
+
|
|
394
402
|
## Pipeline stages
|
|
395
403
|
|
|
396
404
|
1. **Filler** — Removes low-semantic filler words and (in chat/agent) drops acknowledgement-only assistant turns.
|
|
@@ -553,6 +561,7 @@ For academic use, cite this package in your paper’s software or methods sectio
|
|
|
553
561
|
|
|
554
562
|
## Extension and growth
|
|
555
563
|
|
|
564
|
+
- **Stabilization audit** — See [`AUDIT.md`](AUDIT.md) for known contract gaps and the 0.5.5–0.5.8 backlog (refactors, fixtures, fixes).
|
|
556
565
|
- **Custom stages** — Subclass `contextpress.strategies.base.BaseStrategy` and plug in via a custom `Pipeline` subclass or future registry hooks.
|
|
557
566
|
- **Tier 2** — Implement `LLMBackend` (`summarize`, `deduplicate`) for provider-specific semantic compression; failures fall back to Tier 1.
|
|
558
567
|
- **Presets API** — `from contextpress.compression import VALID_STAGES, STAGE_ORDER` for tooling and experiments.
|
|
@@ -82,6 +82,12 @@ preset = cm.recommend_preset(messages, token_budget=500)
|
|
|
82
82
|
out = cm.compress(messages, token_budget=500, compression=preset)
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
+
**Batch compress** (0.5.4+):
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
results = cm.compress_many(list_of_conversations, token_budget=2000, return_stats=True)
|
|
89
|
+
```
|
|
90
|
+
|
|
85
91
|
**Async** (0.5+):
|
|
86
92
|
|
|
87
93
|
```python
|
|
@@ -150,6 +156,8 @@ ContextManager(type="rag_doc")
|
|
|
150
156
|
ContextManager(type="agent")
|
|
151
157
|
```
|
|
152
158
|
|
|
159
|
+
Runnable agent example: [`examples/agent_pipeline.py`](examples/agent_pipeline.py).
|
|
160
|
+
|
|
153
161
|
## Pipeline stages
|
|
154
162
|
|
|
155
163
|
1. **Filler** — Removes low-semantic filler words and (in chat/agent) drops acknowledgement-only assistant turns.
|
|
@@ -312,6 +320,7 @@ For academic use, cite this package in your paper’s software or methods sectio
|
|
|
312
320
|
|
|
313
321
|
## Extension and growth
|
|
314
322
|
|
|
323
|
+
- **Stabilization audit** — See [`AUDIT.md`](AUDIT.md) for known contract gaps and the 0.5.5–0.5.8 backlog (refactors, fixtures, fixes).
|
|
315
324
|
- **Custom stages** — Subclass `contextpress.strategies.base.BaseStrategy` and plug in via a custom `Pipeline` subclass or future registry hooks.
|
|
316
325
|
- **Tier 2** — Implement `LLMBackend` (`summarize`, `deduplicate`) for provider-specific semantic compression; failures fall back to Tier 1.
|
|
317
326
|
- **Presets API** — `from contextpress.compression import VALID_STAGES, STAGE_ORDER` for tooling and experiments.
|
|
@@ -210,6 +210,33 @@ class ContextManager:
|
|
|
210
210
|
return CompressionResult(messages=messages_out, stats=stats)
|
|
211
211
|
return messages_out
|
|
212
212
|
|
|
213
|
+
def compress_many(
|
|
214
|
+
self,
|
|
215
|
+
conversations: list[Any],
|
|
216
|
+
token_budget: int | None = None,
|
|
217
|
+
*,
|
|
218
|
+
compression: str | None = None,
|
|
219
|
+
stages: list[str] | None = None,
|
|
220
|
+
disable: list[str] | None = None,
|
|
221
|
+
return_stats: bool = False,
|
|
222
|
+
dry_run: bool = False,
|
|
223
|
+
) -> list[Any] | list[CompressionResult]:
|
|
224
|
+
"""Run ``compress()`` on each conversation in ``conversations``."""
|
|
225
|
+
if not isinstance(conversations, list):
|
|
226
|
+
raise TypeError("conversations must be a list")
|
|
227
|
+
return [
|
|
228
|
+
self.compress(
|
|
229
|
+
messages,
|
|
230
|
+
token_budget=token_budget,
|
|
231
|
+
compression=compression,
|
|
232
|
+
stages=stages,
|
|
233
|
+
disable=disable,
|
|
234
|
+
return_stats=return_stats,
|
|
235
|
+
dry_run=dry_run,
|
|
236
|
+
)
|
|
237
|
+
for messages in conversations
|
|
238
|
+
]
|
|
239
|
+
|
|
213
240
|
async def compress_async(
|
|
214
241
|
self,
|
|
215
242
|
messages: Any,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Agent thread compression — ``type=\"agent\"`` for tool-using pipelines."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from contextpress import ContextManager
|
|
6
|
+
|
|
7
|
+
messages = [
|
|
8
|
+
{"role": "system", "content": "You are a task agent with tools."},
|
|
9
|
+
{"role": "user", "content": "Check whether api-v2 is deployed to staging."},
|
|
10
|
+
{
|
|
11
|
+
"role": "assistant",
|
|
12
|
+
"content": "Checking deploy status <tool_call> get_deploy_status(api-v2)",
|
|
13
|
+
},
|
|
14
|
+
{"role": "user", "content": "We've decided on using the new pipeline for deploy."},
|
|
15
|
+
{"role": "assistant", "content": "Confirmed — staging deploy is scheduled."},
|
|
16
|
+
{"role": "user", "content": "What's the current status?"},
|
|
17
|
+
{"role": "assistant", "content": "Basically, honestly, " + "still rolling out " * 30},
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
cm = ContextManager(type="agent", compression="high")
|
|
21
|
+
result = cm.compress(messages, token_budget=300, return_stats=True)
|
|
22
|
+
|
|
23
|
+
print("context type:", result.stats.context_type)
|
|
24
|
+
print("turns:", result.stats.turns_before, "->", result.stats.turns_after)
|
|
25
|
+
print("tokens:", result.stats.tokens_before, "->", result.stats.tokens_after)
|
|
26
|
+
print("stages:", result.stats.stages_run)
|
|
27
|
+
for i, m in enumerate(result.messages):
|
|
28
|
+
preview = str(m.get("content", ""))[:120]
|
|
29
|
+
print(i, m["role"] + ":", preview)
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "contextpress"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.5"
|
|
8
8
|
description = "Deterministic context compression for LLM chat, RAG, and agent pipelines"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -59,7 +59,7 @@ packages = ["contextpress"]
|
|
|
59
59
|
"contextpress/py.typed" = "contextpress/py.typed"
|
|
60
60
|
|
|
61
61
|
[tool.hatch.build.targets.sdist]
|
|
62
|
-
include = ["/contextpress", "/tests", "/examples", "/README.md", "/LICENSE", "/NOTICE", "/CHANGELOG.md", "/CITATION.cff", "/CONTRIBUTING.md"]
|
|
62
|
+
include = ["/contextpress", "/tests", "/examples", "/README.md", "/LICENSE", "/NOTICE", "/CHANGELOG.md", "/CITATION.cff", "/CONTRIBUTING.md", "/AUDIT.md", "/AGENTS.md"]
|
|
63
63
|
|
|
64
64
|
[tool.black]
|
|
65
65
|
line-length = 100
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from contextpress import CompressionResult, ContextManager
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_compress_many_returns_list():
|
|
7
|
+
cm = ContextManager(type="chat")
|
|
8
|
+
batches = [
|
|
9
|
+
[{"role": "user", "content": "hello basically"}],
|
|
10
|
+
[{"role": "user", "content": "thanks basically"}],
|
|
11
|
+
]
|
|
12
|
+
out = cm.compress_many(batches, token_budget=200)
|
|
13
|
+
assert isinstance(out, list)
|
|
14
|
+
assert len(out) == 2
|
|
15
|
+
assert all(isinstance(item, list) for item in out)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_compress_many_with_stats():
|
|
19
|
+
cm = ContextManager(type="chat")
|
|
20
|
+
batches = [
|
|
21
|
+
[{"role": "user", "content": "hello basically there"}],
|
|
22
|
+
[{"role": "assistant", "content": "sounds good"}],
|
|
23
|
+
]
|
|
24
|
+
results = cm.compress_many(batches, token_budget=500, return_stats=True)
|
|
25
|
+
assert len(results) == 2
|
|
26
|
+
assert all(isinstance(r, CompressionResult) for r in results)
|
|
27
|
+
assert results[0].stats.turns_before == 1
|
|
28
|
+
assert results[1].stats.turns_before == 1
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_compress_many_empty():
|
|
32
|
+
cm = ContextManager(type="chat")
|
|
33
|
+
assert cm.compress_many([], token_budget=100) == []
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_compress_many_requires_list():
|
|
37
|
+
cm = ContextManager(type="chat")
|
|
38
|
+
with pytest.raises(TypeError, match="conversations must be a list"):
|
|
39
|
+
cm.compress_many("not a list") # type: ignore[arg-type]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_agent_pipeline_compresses():
|
|
43
|
+
cm = ContextManager(type="agent", compression="high")
|
|
44
|
+
messages = [
|
|
45
|
+
{"role": "system", "content": "Agent."},
|
|
46
|
+
{"role": "user", "content": "Deploy api-v2?"},
|
|
47
|
+
{"role": "assistant", "content": "Using pipeline <tool_call> deploy"},
|
|
48
|
+
{"role": "user", "content": "We've decided on using the new pipeline for deploy."},
|
|
49
|
+
{"role": "assistant", "content": "Confirmed."},
|
|
50
|
+
]
|
|
51
|
+
result = cm.compress(messages, token_budget=200, return_stats=True)
|
|
52
|
+
assert result.stats.context_type == "agent"
|
|
53
|
+
assert result.stats.tokens_after <= result.stats.tokens_before
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|