powerailabs-squeeze 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.
- powerailabs_squeeze-0.1.0/.gitignore +16 -0
- powerailabs_squeeze-0.1.0/PKG-INFO +38 -0
- powerailabs_squeeze-0.1.0/README.md +28 -0
- powerailabs_squeeze-0.1.0/pyproject.toml +16 -0
- powerailabs_squeeze-0.1.0/src/powerailabs/squeeze/__init__.py +238 -0
- powerailabs_squeeze-0.1.0/src/powerailabs/squeeze/py.typed +0 -0
- powerailabs_squeeze-0.1.0/tests/test_squeeze.py +96 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: powerailabs-squeeze
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Compress: shrink verbose context (JSON/logs/prose) 60-90% โ reversibly. compress() returns a handle; expand() restores the original.
|
|
5
|
+
Author: Raghav Mishra
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Requires-Dist: powerailabs-core<0.2,>=0.1
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# powerailabs-squeeze
|
|
12
|
+
|
|
13
|
+
Shrink verbose context โ JSON, logs, prose โ without throwing anything away. Compression returns
|
|
14
|
+
a *handle*; the original is always restorable. Content-aware: each type gets a purpose-built,
|
|
15
|
+
deterministic compressor (no LLM).
|
|
16
|
+
|
|
17
|
+
**80% smaller, 100% reversible.**
|
|
18
|
+
|
|
19
|
+
 
|
|
20
|
+
|
|
21
|
+
๐ง building (v0) ยท `pip install powerailabs-squeeze` ยท `from powerailabs.squeeze import compress`
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from powerailabs.squeeze import compress
|
|
25
|
+
|
|
26
|
+
small, handle = compress(huge_json, kind="auto") # detect + route (JSON/logs/prose)
|
|
27
|
+
small, handle = compress(logs, kind="logs", target_tokens=400) # compress to a budget
|
|
28
|
+
original = handle.expand() # restore on demand, byte-for-byte
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Inbound** โ usually you don't call it directly; `contextkit` does, when a block is marked
|
|
32
|
+
`evict="compress"` (`pip install powerailabs-contextkit[squeeze]`). It satisfies core's
|
|
33
|
+
`Compressor` protocol by shape, so contextkit never imports squeeze. Call it directly to shrink a
|
|
34
|
+
single known-huge blob (e.g. a 50k-token tool response) before it ever enters the window.
|
|
35
|
+
Reversibility comes from a content-addressed store that keeps each original keyed by hash โ
|
|
36
|
+
structural compressors (JSON folding, log dedup) are deterministic; prose is extractive.
|
|
37
|
+
|
|
38
|
+
See [`docs/squeeze.md`](../../docs/squeeze.md). *Part of the PowerAI Labs stack โ github.com/PowerAI-Labs/powerailabs.*
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# powerailabs-squeeze
|
|
2
|
+
|
|
3
|
+
Shrink verbose context โ JSON, logs, prose โ without throwing anything away. Compression returns
|
|
4
|
+
a *handle*; the original is always restorable. Content-aware: each type gets a purpose-built,
|
|
5
|
+
deterministic compressor (no LLM).
|
|
6
|
+
|
|
7
|
+
**80% smaller, 100% reversible.**
|
|
8
|
+
|
|
9
|
+
 
|
|
10
|
+
|
|
11
|
+
๐ง building (v0) ยท `pip install powerailabs-squeeze` ยท `from powerailabs.squeeze import compress`
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from powerailabs.squeeze import compress
|
|
15
|
+
|
|
16
|
+
small, handle = compress(huge_json, kind="auto") # detect + route (JSON/logs/prose)
|
|
17
|
+
small, handle = compress(logs, kind="logs", target_tokens=400) # compress to a budget
|
|
18
|
+
original = handle.expand() # restore on demand, byte-for-byte
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Inbound** โ usually you don't call it directly; `contextkit` does, when a block is marked
|
|
22
|
+
`evict="compress"` (`pip install powerailabs-contextkit[squeeze]`). It satisfies core's
|
|
23
|
+
`Compressor` protocol by shape, so contextkit never imports squeeze. Call it directly to shrink a
|
|
24
|
+
single known-huge blob (e.g. a 50k-token tool response) before it ever enters the window.
|
|
25
|
+
Reversibility comes from a content-addressed store that keeps each original keyed by hash โ
|
|
26
|
+
structural compressors (JSON folding, log dedup) are deterministic; prose is extractive.
|
|
27
|
+
|
|
28
|
+
See [`docs/squeeze.md`](../../docs/squeeze.md). *Part of the PowerAI Labs stack โ github.com/PowerAI-Labs/powerailabs.*
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "powerailabs-squeeze"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Compress: shrink verbose context (JSON/logs/prose) 60-90% โ reversibly. compress() returns a handle; expand() restores the original."
|
|
5
|
+
requires-python = ">=3.11"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
authors = [{ name = "Raghav Mishra" }]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
dependencies = ["powerailabs-core>=0.1,<0.2"]
|
|
10
|
+
|
|
11
|
+
[build-system]
|
|
12
|
+
requires = ["hatchling"]
|
|
13
|
+
build-backend = "hatchling.build"
|
|
14
|
+
|
|
15
|
+
[tool.hatch.build.targets.wheel]
|
|
16
|
+
packages = ["src/powerailabs"] # contributes powerailabs/squeeze only โ NEVER add src/powerailabs/__init__.py
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""powerailabs.squeeze โ content-aware, reversible context compression.
|
|
2
|
+
|
|
3
|
+
Shrink verbose context without throwing anything away: :func:`compress` returns ``(small, handle)``
|
|
4
|
+
and ``handle.expand()`` restores the original on demand. Content is routed by type โ JSON, logs,
|
|
5
|
+
and prose each get a purpose-built, deterministic compressor (no LLM). Reversibility is guaranteed
|
|
6
|
+
by a **content-addressed store** (CCR): every original is kept keyed by its hash, deduped across
|
|
7
|
+
calls, so ``expand()`` is always exact no matter how hard we squeeze.
|
|
8
|
+
|
|
9
|
+
Satisfies ``powerailabs.core.protocols.Compressor`` by shape, so ``contextkit`` uses it for
|
|
10
|
+
``Block(evict="compress")`` via the ``contextkit[squeeze]`` extra โ without importing this package.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import hashlib
|
|
16
|
+
import json
|
|
17
|
+
import re
|
|
18
|
+
import uuid
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
from powerailabs.core import tokens
|
|
23
|
+
|
|
24
|
+
__all__ = ["compress", "decompress", "detect", "Handle", "SqueezeCompressor"]
|
|
25
|
+
|
|
26
|
+
# Content-addressed store: sha256(original) -> original. Deduped; the basis of reversibility.
|
|
27
|
+
_STORE: dict[str, str] = {}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class Handle:
|
|
32
|
+
"""Restore handle for a compression. ``expand()`` returns the exact original. (docs ยง5)"""
|
|
33
|
+
|
|
34
|
+
id: str
|
|
35
|
+
kind: str
|
|
36
|
+
original_ref: str # CCR key into the local content store
|
|
37
|
+
restore_map: dict = field(default_factory=dict)
|
|
38
|
+
|
|
39
|
+
def expand(self) -> str:
|
|
40
|
+
"""Return the original content, byte-for-byte."""
|
|
41
|
+
return _STORE[self.original_ref]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _store(original: str) -> str:
|
|
45
|
+
key = hashlib.sha256(original.encode("utf-8")).hexdigest()
|
|
46
|
+
_STORE.setdefault(key, original)
|
|
47
|
+
return key
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# --------------------------------------------------------------------------- detection
|
|
51
|
+
|
|
52
|
+
_TS = re.compile(r"\b\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?\b")
|
|
53
|
+
_UUID = re.compile(r"\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b")
|
|
54
|
+
_LEVEL = re.compile(r"\b(?:DEBUG|INFO|WARN|WARNING|ERROR|CRITICAL|TRACE|FATAL)\b")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def detect(content: str) -> str:
|
|
58
|
+
"""Detect the content kind: ``"json"`` | ``"logs"`` | ``"prose"``. docs/squeeze.md ยง4."""
|
|
59
|
+
s = content.strip()
|
|
60
|
+
if not s:
|
|
61
|
+
return "prose"
|
|
62
|
+
if s[0] in "{[":
|
|
63
|
+
try:
|
|
64
|
+
json.loads(s)
|
|
65
|
+
return "json"
|
|
66
|
+
except (ValueError, TypeError):
|
|
67
|
+
pass
|
|
68
|
+
if _looks_like_logs(s):
|
|
69
|
+
return "logs"
|
|
70
|
+
return "prose"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _looks_like_logs(s: str) -> bool:
|
|
74
|
+
lines = [ln for ln in s.splitlines() if ln.strip()]
|
|
75
|
+
if len(lines) < 3:
|
|
76
|
+
return False
|
|
77
|
+
hits = sum(1 for ln in lines if _TS.search(ln) or _LEVEL.search(ln))
|
|
78
|
+
return hits >= len(lines) * 0.5
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# --------------------------------------------------------------------------- public API
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def compress(
|
|
85
|
+
content: Any,
|
|
86
|
+
kind: str = "auto",
|
|
87
|
+
target_tokens: int | None = None,
|
|
88
|
+
model: str = "gpt-4o",
|
|
89
|
+
) -> tuple[str, Handle]:
|
|
90
|
+
"""Compress ``content`` and return ``(small, handle)``. ``handle.expand()`` restores it.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
content: A string, or a JSON-serializable object (dict/list).
|
|
94
|
+
kind: ``"auto"`` (detect) or one of ``"json"`` | ``"logs"`` | ``"prose"``.
|
|
95
|
+
target_tokens: If given, compress *to* this budget (best effort, never exceeds it).
|
|
96
|
+
model: Model id used for token counting.
|
|
97
|
+
"""
|
|
98
|
+
if isinstance(content, str):
|
|
99
|
+
original = content
|
|
100
|
+
else:
|
|
101
|
+
original = json.dumps(content, ensure_ascii=False, separators=(",", ":"))
|
|
102
|
+
if kind == "auto":
|
|
103
|
+
kind = "json"
|
|
104
|
+
|
|
105
|
+
if kind == "auto":
|
|
106
|
+
kind = detect(original)
|
|
107
|
+
|
|
108
|
+
if kind == "json":
|
|
109
|
+
small, restore_map = _compress_json(original, target_tokens, model)
|
|
110
|
+
elif kind == "logs":
|
|
111
|
+
small, restore_map = _compress_logs(original, target_tokens, model)
|
|
112
|
+
else:
|
|
113
|
+
small, restore_map = _compress_prose(original, target_tokens, model)
|
|
114
|
+
|
|
115
|
+
handle = Handle(
|
|
116
|
+
id=uuid.uuid4().hex,
|
|
117
|
+
kind=kind,
|
|
118
|
+
original_ref=_store(original),
|
|
119
|
+
restore_map=restore_map,
|
|
120
|
+
)
|
|
121
|
+
return small, handle
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def decompress(handle: Handle) -> str:
|
|
125
|
+
"""Restore the original content for a handle (same as ``handle.expand()``)."""
|
|
126
|
+
return handle.expand()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class SqueezeCompressor:
|
|
130
|
+
"""Object form satisfying ``core.protocols.Compressor`` (delegates to :func:`compress`)."""
|
|
131
|
+
|
|
132
|
+
def compress(
|
|
133
|
+
self,
|
|
134
|
+
content: Any,
|
|
135
|
+
*,
|
|
136
|
+
target_tokens: int | None = None,
|
|
137
|
+
model: str | None = None,
|
|
138
|
+
kind: str = "auto",
|
|
139
|
+
) -> tuple[str, Handle]:
|
|
140
|
+
return compress(content, kind=kind, target_tokens=target_tokens, model=model or "gpt-4o")
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# --------------------------------------------------------------------------- compressors
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _strip_nulls(obj: Any) -> Any:
|
|
147
|
+
if isinstance(obj, dict):
|
|
148
|
+
return {k: _strip_nulls(v) for k, v in obj.items() if v is not None}
|
|
149
|
+
if isinstance(obj, list):
|
|
150
|
+
return [_strip_nulls(v) for v in obj]
|
|
151
|
+
return obj
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _compress_json(text: str, target_tokens: int | None, model: str) -> tuple[str, dict]:
|
|
155
|
+
"""Lossless-ish: minify whitespace and drop null-valued keys. Original kept in the CCR store."""
|
|
156
|
+
try:
|
|
157
|
+
obj = json.loads(text)
|
|
158
|
+
except (ValueError, TypeError):
|
|
159
|
+
return _compress_prose(text, target_tokens, model)
|
|
160
|
+
small = json.dumps(_strip_nulls(obj), ensure_ascii=False, separators=(",", ":"))
|
|
161
|
+
if target_tokens is not None and tokens.count(small, model) > target_tokens:
|
|
162
|
+
small = _truncate_to_tokens(small, target_tokens, model)
|
|
163
|
+
return small, {"technique": "minify+dropnulls"}
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _compress_logs(text: str, target_tokens: int | None, model: str) -> tuple[str, dict]:
|
|
167
|
+
"""Normalize volatile fields (timestamps/UUIDs) then dedup repeated lines into ``(รN)``."""
|
|
168
|
+
counts: dict[str, int] = {}
|
|
169
|
+
order: list[str] = []
|
|
170
|
+
for line in text.splitlines():
|
|
171
|
+
norm = _UUID.sub("<uuid>", _TS.sub("<ts>", line))
|
|
172
|
+
if norm not in counts:
|
|
173
|
+
order.append(norm)
|
|
174
|
+
counts[norm] = counts.get(norm, 0) + 1
|
|
175
|
+
|
|
176
|
+
if target_tokens is not None:
|
|
177
|
+
order.sort(key=lambda ln: counts[ln], reverse=True) # keep the noisiest patterns first
|
|
178
|
+
|
|
179
|
+
out: list[str] = []
|
|
180
|
+
for norm in order:
|
|
181
|
+
rendered = f"{norm} (ร{counts[norm]})" if counts[norm] > 1 else norm
|
|
182
|
+
if target_tokens is not None:
|
|
183
|
+
candidate = "\n".join([*out, rendered])
|
|
184
|
+
if tokens.count(candidate, model) > target_tokens:
|
|
185
|
+
break
|
|
186
|
+
out.append(rendered)
|
|
187
|
+
return "\n".join(out), {"technique": "normalize+dedup", "patterns": len(order)}
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
_SENT = re.compile(r"(?<=[.!?])\s+")
|
|
191
|
+
_STOP = frozenset(
|
|
192
|
+
"the a an and or but of to in on for with is are was were be been it this that as at by".split()
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _compress_prose(text: str, target_tokens: int | None, model: str) -> tuple[str, dict]:
|
|
197
|
+
"""Extractive: rank sentences by keyword density, keep the top ones in original order."""
|
|
198
|
+
sentences = [s for s in _SENT.split(text.strip()) if s.strip()]
|
|
199
|
+
if len(sentences) <= 1:
|
|
200
|
+
return text, {"technique": "extractive", "kept": len(sentences)}
|
|
201
|
+
|
|
202
|
+
freq: dict[str, int] = {}
|
|
203
|
+
for word in re.findall(r"[a-zA-Z']+", text.lower()):
|
|
204
|
+
if word not in _STOP:
|
|
205
|
+
freq[word] = freq.get(word, 0) + 1
|
|
206
|
+
|
|
207
|
+
def score(sentence: str) -> float:
|
|
208
|
+
words = re.findall(r"[a-zA-Z']+", sentence.lower())
|
|
209
|
+
if not words:
|
|
210
|
+
return 0.0
|
|
211
|
+
return sum(freq.get(w, 0) for w in words) / len(words)
|
|
212
|
+
|
|
213
|
+
ranked = sorted(range(len(sentences)), key=lambda i: score(sentences[i]), reverse=True)
|
|
214
|
+
|
|
215
|
+
if target_tokens is not None:
|
|
216
|
+
keep: set[int] = set()
|
|
217
|
+
for i in ranked:
|
|
218
|
+
trial = " ".join(sentences[j] for j in sorted(keep | {i}))
|
|
219
|
+
if tokens.count(trial, model) > target_tokens and keep:
|
|
220
|
+
break
|
|
221
|
+
keep.add(i)
|
|
222
|
+
else:
|
|
223
|
+
keep = set(ranked[: max(1, len(sentences) // 2)]) # default: keep the top half
|
|
224
|
+
|
|
225
|
+
small = " ".join(sentences[i] for i in sorted(keep))
|
|
226
|
+
return small, {"technique": "extractive", "kept": len(keep), "of": len(sentences)}
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _truncate_to_tokens(text: str, target: int, model: str) -> str:
|
|
230
|
+
if target <= 0:
|
|
231
|
+
return ""
|
|
232
|
+
if tokens.count(text, model) <= target:
|
|
233
|
+
return text
|
|
234
|
+
ratio = max(1, len(text)) / max(1, tokens.count(text, model))
|
|
235
|
+
cut = text[: int(target * ratio)]
|
|
236
|
+
while cut and tokens.count(cut, model) > target:
|
|
237
|
+
cut = cut[: int(len(cut) * 0.9)]
|
|
238
|
+
return cut
|
|
File without changes
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Compression is content-aware, deterministic, and 100% reversible. No network."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from powerailabs.core import protocols, tokens
|
|
7
|
+
from powerailabs.squeeze import SqueezeCompressor, compress, decompress, detect
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.fixture(autouse=True)
|
|
11
|
+
def _heuristic_tokens(monkeypatch):
|
|
12
|
+
monkeypatch.setattr(tokens, "_tiktoken_encoding", lambda model: None)
|
|
13
|
+
yield
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_detect():
|
|
17
|
+
assert detect('{"a": 1}') == "json"
|
|
18
|
+
assert detect("[1, 2, 3]") == "json"
|
|
19
|
+
logs = "\n".join(f"2026-06-01T00:00:0{i} INFO started worker" for i in range(5))
|
|
20
|
+
assert detect(logs) == "logs"
|
|
21
|
+
assert detect("The cat sat on the mat. It was a sunny day.") == "prose"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_json_compression_is_smaller_and_reversible():
|
|
25
|
+
obj = {"name": "alice", "age": 30, "note": None, "tags": ["x", "y"], "extra": None}
|
|
26
|
+
pretty = json.dumps(obj, indent=4)
|
|
27
|
+
small, handle = compress(pretty, kind="auto")
|
|
28
|
+
assert detect(pretty) == "json"
|
|
29
|
+
assert len(small) < len(pretty) # whitespace + nulls gone
|
|
30
|
+
assert "note" not in small and "null" not in small # nulls dropped
|
|
31
|
+
assert handle.expand() == pretty # exact original restored
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_logs_dedup_collapses_repeats():
|
|
35
|
+
logs = "\n".join(["2026-06-01T00:00:00Z INFO retry attempt"] * 20)
|
|
36
|
+
small, handle = compress(logs, kind="logs")
|
|
37
|
+
assert "(ร20)" in small
|
|
38
|
+
assert tokens.count(small, "gpt-4o") < tokens.count(logs, "gpt-4o")
|
|
39
|
+
assert handle.expand() == logs
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_prose_extractive_hits_target_tokens():
|
|
43
|
+
text = (
|
|
44
|
+
"Refunds are processed within five business days. "
|
|
45
|
+
"The weather today is mild and pleasant. "
|
|
46
|
+
"Customers must contact support to request a refund. "
|
|
47
|
+
"Our office cat is named Mittens. "
|
|
48
|
+
"Refund eligibility depends on the purchase date."
|
|
49
|
+
)
|
|
50
|
+
target = 20
|
|
51
|
+
small, handle = compress(text, kind="prose", target_tokens=target)
|
|
52
|
+
assert tokens.count(small, "gpt-4o") <= target
|
|
53
|
+
assert len(small) < len(text)
|
|
54
|
+
assert handle.expand() == text # original always restorable
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_object_input_serialized_and_restored():
|
|
58
|
+
small, handle = compress({"k": "v", "n": None}, kind="auto")
|
|
59
|
+
assert "null" not in small
|
|
60
|
+
restored = json.loads(handle.expand())
|
|
61
|
+
assert restored == {"k": "v", "n": None}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_decompress_matches_expand():
|
|
65
|
+
small, handle = compress("hello world. goodbye world.", kind="prose")
|
|
66
|
+
assert decompress(handle) == handle.expand()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_ccr_store_dedupes_identical_originals():
|
|
70
|
+
from powerailabs.squeeze import _STORE
|
|
71
|
+
|
|
72
|
+
before = len(_STORE)
|
|
73
|
+
_, h1 = compress("identical content here", kind="prose")
|
|
74
|
+
_, h2 = compress("identical content here", kind="prose")
|
|
75
|
+
assert h1.original_ref == h2.original_ref # same hash key
|
|
76
|
+
assert len(_STORE) == before + 1 # stored once
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_satisfies_core_compressor_protocol():
|
|
80
|
+
assert isinstance(SqueezeCompressor(), protocols.Compressor)
|
|
81
|
+
small, handle = SqueezeCompressor().compress("a. b. c. d.", target_tokens=5, model="gpt-4o")
|
|
82
|
+
assert isinstance(handle.expand(), str)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_contextkit_compress_routes_through_squeeze():
|
|
86
|
+
# End-to-end: contextkit discovers squeeze by shape via the [squeeze] extra (both installed).
|
|
87
|
+
from powerailabs.contextkit import Block, Context
|
|
88
|
+
|
|
89
|
+
ctx = Context(budget_tokens=30, model="gpt-4o")
|
|
90
|
+
ctx.add(Block("keep me", priority=10, role="system"))
|
|
91
|
+
big = " ".join(f"Sentence number {i} about refunds and billing." for i in range(20))
|
|
92
|
+
ctx.add(Block(big, priority=1, role="user", evict="compress"))
|
|
93
|
+
ctx.assemble()
|
|
94
|
+
decision = next(d for d in ctx.report().decisions if d.role == "user")
|
|
95
|
+
assert decision.action == "compressed"
|
|
96
|
+
assert decision.tokens_after < decision.tokens_before
|