superlocalmemory 3.5.7 → 3.6.0
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.
- package/ATTRIBUTION.md +24 -0
- package/CHANGELOG.md +40 -0
- package/README.md +142 -35
- package/package.json +1 -1
- package/pyproject.toml +2 -1
- package/src/superlocalmemory/__init__.py +1 -1
- package/src/superlocalmemory/cli/cache_cmd.py +198 -0
- package/src/superlocalmemory/cli/commands.py +100 -2
- package/src/superlocalmemory/cli/compress_cmd.py +179 -0
- package/src/superlocalmemory/cli/help_cmd.py +197 -0
- package/src/superlocalmemory/cli/main.py +122 -0
- package/src/superlocalmemory/cli/optimize_cmd.py +175 -0
- package/src/superlocalmemory/cli/optimize_constants.py +31 -0
- package/src/superlocalmemory/cli/proxy_cmd.py +95 -0
- package/src/superlocalmemory/core/config.py +5 -0
- package/src/superlocalmemory/core/engine.py +23 -0
- package/src/superlocalmemory/core/mcp_embedder_proxy.py +89 -0
- package/src/superlocalmemory/infra/process_reaper.py +12 -1
- package/src/superlocalmemory/llm/backbone.py +10 -4
- package/src/superlocalmemory/mcp/server.py +34 -0
- package/src/superlocalmemory/mcp/tools_v3.py +6 -2
- package/src/superlocalmemory/optimize/NOTICE +11 -0
- package/src/superlocalmemory/optimize/__init__.py +0 -0
- package/src/superlocalmemory/optimize/adapters/__init__.py +68 -0
- package/src/superlocalmemory/optimize/adapters/_agent_registry.py +120 -0
- package/src/superlocalmemory/optimize/adapters/anthropic_adapter.py +115 -0
- package/src/superlocalmemory/optimize/adapters/openai_adapter.py +125 -0
- package/src/superlocalmemory/optimize/adapters/wrap.py +188 -0
- package/src/superlocalmemory/optimize/cache/__init__.py +31 -0
- package/src/superlocalmemory/optimize/cache/boundary_store.py +455 -0
- package/src/superlocalmemory/optimize/cache/centroid_store.py +158 -0
- package/src/superlocalmemory/optimize/cache/context_key.py +67 -0
- package/src/superlocalmemory/optimize/cache/exact.py +85 -0
- package/src/superlocalmemory/optimize/cache/invalidation.py +36 -0
- package/src/superlocalmemory/optimize/cache/key_builder.py +98 -0
- package/src/superlocalmemory/optimize/cache/manager.py +452 -0
- package/src/superlocalmemory/optimize/cache/semantic.py +568 -0
- package/src/superlocalmemory/optimize/cache/stampede.py +50 -0
- package/src/superlocalmemory/optimize/compress/__init__.py +17 -0
- package/src/superlocalmemory/optimize/compress/align.py +153 -0
- package/src/superlocalmemory/optimize/compress/ccr.py +157 -0
- package/src/superlocalmemory/optimize/compress/extractive_code.py +311 -0
- package/src/superlocalmemory/optimize/compress/extractive_json.py +72 -0
- package/src/superlocalmemory/optimize/compress/prose_llmlingua.py +77 -0
- package/src/superlocalmemory/optimize/compress/router.py +548 -0
- package/src/superlocalmemory/optimize/config/__init__.py +35 -0
- package/src/superlocalmemory/optimize/config/defaults.py +48 -0
- package/src/superlocalmemory/optimize/config/schema.py +255 -0
- package/src/superlocalmemory/optimize/config/store.py +209 -0
- package/src/superlocalmemory/optimize/metrics/__init__.py +8 -0
- package/src/superlocalmemory/optimize/metrics/counters.py +138 -0
- package/src/superlocalmemory/optimize/metrics/estimator.py +90 -0
- package/src/superlocalmemory/optimize/metrics/exporters.py +77 -0
- package/src/superlocalmemory/optimize/metrics/persistence.py +115 -0
- package/src/superlocalmemory/optimize/proxy/__init__.py +28 -0
- package/src/superlocalmemory/optimize/proxy/_helpers.py +257 -0
- package/src/superlocalmemory/optimize/proxy/anthropic_surface.py +171 -0
- package/src/superlocalmemory/optimize/proxy/gemini_surface.py +121 -0
- package/src/superlocalmemory/optimize/proxy/lifecycle.py +126 -0
- package/src/superlocalmemory/optimize/proxy/openai_surface.py +125 -0
- package/src/superlocalmemory/optimize/proxy/server.py +151 -0
- package/src/superlocalmemory/optimize/storage/__init__.py +0 -0
- package/src/superlocalmemory/optimize/storage/db.py +1016 -0
- package/src/superlocalmemory/optimize/storage/schema.py +184 -0
- package/src/superlocalmemory/server/routes/optimize.py +166 -0
- package/src/superlocalmemory/server/routes/v3_api.py +63 -1
- package/src/superlocalmemory/server/unified_daemon.py +105 -0
- package/src/superlocalmemory/ui/index.html +98 -0
- package/src/superlocalmemory/ui/js/ng-shell.js +2 -1
- package/src/superlocalmemory/ui/js/optimize.js +173 -0
- package/src/superlocalmemory.egg-info/PKG-INFO +2 -1
- package/src/superlocalmemory.egg-info/SOURCES.txt +51 -0
- package/src/superlocalmemory.egg-info/requires.txt +1 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# compress/align.py
|
|
2
|
+
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
3
|
+
# Licensed under AGPL-3.0-or-later
|
|
4
|
+
#
|
|
5
|
+
# Volatile-detection algorithm adapted from:
|
|
6
|
+
# headroom/transforms/cache_aligner.py (Apache-2.0, Headroom contributors)
|
|
7
|
+
# Specifically: _is_uuid(), _is_iso8601(), _is_jwt_shape(), _is_hex_hash(),
|
|
8
|
+
# _classify_token(), _split_tokens(), detect_volatile_content()
|
|
9
|
+
# Lines: cache_aligner.py:76-200
|
|
10
|
+
# Attribution: See ATTRIBUTION.md.
|
|
11
|
+
|
|
12
|
+
"""CacheAligner — volatile-token detector for system prompt prefix stability.
|
|
13
|
+
|
|
14
|
+
Phase 2: Detection only. No mutation of the system prompt.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import base64
|
|
20
|
+
import binascii
|
|
21
|
+
import logging
|
|
22
|
+
import uuid as _uuid
|
|
23
|
+
from dataclasses import dataclass, field
|
|
24
|
+
from datetime import datetime
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger("slm.optimize.compress.align")
|
|
27
|
+
|
|
28
|
+
_HEX_HASH_LENGTHS = frozenset({32, 40, 64})
|
|
29
|
+
_UUID_CANONICAL_LEN = 36 # L-02: 36 chars INCLUDING 4 dashes (RFC 4122 canonical form: 8-4-4-4-12)
|
|
30
|
+
_JWT_SEGMENT_COUNT = 3
|
|
31
|
+
_JWT_MIN_SEGMENT_BYTES = 4
|
|
32
|
+
|
|
33
|
+
_LABEL_UUID = "uuid"
|
|
34
|
+
_LABEL_ISO8601 = "iso8601"
|
|
35
|
+
_LABEL_JWT = "jwt"
|
|
36
|
+
_LABEL_HEX_HASH = "hex_hash"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(frozen=True)
|
|
40
|
+
class VolatileFinding:
|
|
41
|
+
label: str
|
|
42
|
+
sample: str
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class AlignResult:
|
|
47
|
+
prefix_stable: bool = True
|
|
48
|
+
stability_score: float = 1.0
|
|
49
|
+
findings: list[VolatileFinding] = field(default_factory=list)
|
|
50
|
+
total_tokens_scanned: int = 0
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class CacheAligner:
|
|
54
|
+
"""Detects volatile tokens in system prompts. No mutation. Thread-safe."""
|
|
55
|
+
|
|
56
|
+
def detect(self, system_prompt: str) -> AlignResult:
|
|
57
|
+
try:
|
|
58
|
+
return _detect(system_prompt)
|
|
59
|
+
except Exception as exc:
|
|
60
|
+
logger.debug("CacheAligner.detect failed (non-fatal): %s", exc)
|
|
61
|
+
return AlignResult()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _detect(text: str) -> AlignResult:
|
|
65
|
+
tokens = _split_tokens(text)
|
|
66
|
+
findings: list[VolatileFinding] = []
|
|
67
|
+
volatile_count = 0
|
|
68
|
+
|
|
69
|
+
for token in tokens:
|
|
70
|
+
label = _classify_token(token)
|
|
71
|
+
if label is not None:
|
|
72
|
+
volatile_count += 1
|
|
73
|
+
if len(findings) < 20:
|
|
74
|
+
findings.append(VolatileFinding(label=label, sample=token[:20]))
|
|
75
|
+
|
|
76
|
+
total = len(tokens)
|
|
77
|
+
score = 1.0 - (volatile_count / total) if total > 0 else 1.0
|
|
78
|
+
|
|
79
|
+
return AlignResult(
|
|
80
|
+
prefix_stable=(volatile_count == 0),
|
|
81
|
+
stability_score=round(score, 4),
|
|
82
|
+
findings=findings,
|
|
83
|
+
total_tokens_scanned=total,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _split_tokens(content: str) -> list[str]:
|
|
88
|
+
if not content:
|
|
89
|
+
return []
|
|
90
|
+
tokens: list[str] = []
|
|
91
|
+
for raw in content.split():
|
|
92
|
+
cleaned = raw.strip(".,;:!?\"'()[]{}<>`|\\")
|
|
93
|
+
if cleaned:
|
|
94
|
+
tokens.append(cleaned)
|
|
95
|
+
return tokens
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _classify_token(token: str) -> str | None:
|
|
99
|
+
if _is_uuid(token):
|
|
100
|
+
return _LABEL_UUID
|
|
101
|
+
if "." in token and _is_jwt_shape(token):
|
|
102
|
+
return _LABEL_JWT
|
|
103
|
+
if _is_iso8601(token):
|
|
104
|
+
return _LABEL_ISO8601
|
|
105
|
+
if _is_hex_hash(token):
|
|
106
|
+
return _LABEL_HEX_HASH
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _is_uuid(token: str) -> bool:
|
|
111
|
+
if len(token) != _UUID_CANONICAL_LEN or token.count("-") != 4:
|
|
112
|
+
return False
|
|
113
|
+
try:
|
|
114
|
+
_uuid.UUID(token)
|
|
115
|
+
except (ValueError, AttributeError):
|
|
116
|
+
return False
|
|
117
|
+
return True
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _is_iso8601(token: str) -> bool:
|
|
121
|
+
if len(token) < 8 or ("T" not in token and "-" not in token):
|
|
122
|
+
return False
|
|
123
|
+
candidate = token[:-1] + "+00:00" if token.endswith("Z") else token
|
|
124
|
+
try:
|
|
125
|
+
datetime.fromisoformat(candidate)
|
|
126
|
+
except (ValueError, TypeError):
|
|
127
|
+
return False
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _is_jwt_shape(token: str) -> bool:
|
|
132
|
+
if token.count(".") != _JWT_SEGMENT_COUNT - 1:
|
|
133
|
+
return False
|
|
134
|
+
segments = token.split(".")
|
|
135
|
+
for seg in segments:
|
|
136
|
+
if len(seg) < _JWT_MIN_SEGMENT_BYTES:
|
|
137
|
+
return False
|
|
138
|
+
padded = seg + "=" * (-len(seg) % 4)
|
|
139
|
+
try:
|
|
140
|
+
base64.urlsafe_b64decode(padded.encode("ascii"))
|
|
141
|
+
except (binascii.Error, ValueError, UnicodeEncodeError):
|
|
142
|
+
return False
|
|
143
|
+
return True
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _is_hex_hash(token: str) -> bool:
|
|
147
|
+
if len(token) not in _HEX_HASH_LENGTHS:
|
|
148
|
+
return False
|
|
149
|
+
try:
|
|
150
|
+
int(token, 16)
|
|
151
|
+
except ValueError:
|
|
152
|
+
return False
|
|
153
|
+
return True
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# compress/ccr.py
|
|
2
|
+
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
3
|
+
# Licensed under AGPL-3.0-or-later
|
|
4
|
+
#
|
|
5
|
+
# CCR (Compressed Context Retrieval) concept and pattern:
|
|
6
|
+
# headroom/ccr/ package (Apache-2.0, Headroom contributors)
|
|
7
|
+
# Specifically: batch_store.py (BatchContext dataclass, TTL pattern),
|
|
8
|
+
# tool_injection.py (MCP tool injection pattern)
|
|
9
|
+
# Attribution: See ATTRIBUTION.md.
|
|
10
|
+
# Storage: llmcache_ccr table defined in INTERFACE-CONTRACT §1.
|
|
11
|
+
# Database access: CacheDB.ccr_put() and CacheDB.ccr_get() per INTERFACE-CONTRACT §1.
|
|
12
|
+
|
|
13
|
+
"""CCRStore — stores pre-compression originals, provides retrieval tool."""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
import re
|
|
19
|
+
import threading
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger("slm.optimize.compress.ccr")
|
|
22
|
+
|
|
23
|
+
# SEC-C-04: UUID4 format validator — only ^UUID4^ values pass
|
|
24
|
+
_UUID4_RE = re.compile(
|
|
25
|
+
r'^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class CCRStore:
|
|
30
|
+
"""Stores compression originals and retrieves them by ccr_id.
|
|
31
|
+
|
|
32
|
+
Thread-safe. Singleton per daemon instance.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
_instance: "CCRStore | None" = None
|
|
36
|
+
_lock: threading.Lock = threading.Lock()
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def get_instance(cls) -> "CCRStore":
|
|
40
|
+
if cls._instance is None:
|
|
41
|
+
with cls._lock:
|
|
42
|
+
if cls._instance is None:
|
|
43
|
+
cls._instance = cls()
|
|
44
|
+
return cls._instance
|
|
45
|
+
|
|
46
|
+
def __init__(self) -> None:
|
|
47
|
+
self._db: "CacheDB | None" = None
|
|
48
|
+
|
|
49
|
+
def store(
|
|
50
|
+
self,
|
|
51
|
+
original: bytes,
|
|
52
|
+
model: str = "",
|
|
53
|
+
tenant_id: str = "default",
|
|
54
|
+
ttl_seconds: int | None = None,
|
|
55
|
+
) -> str:
|
|
56
|
+
"""Store a pre-compression original. Returns ccr_id (UUID4) or '' on failure.
|
|
57
|
+
|
|
58
|
+
B-03: Called BEFORE compression runs.
|
|
59
|
+
RB-05: Only original bytes accepted; compressed bytes stored via update_compressed().
|
|
60
|
+
"""
|
|
61
|
+
try:
|
|
62
|
+
import uuid as _uuid_mod
|
|
63
|
+
import time as _time_mod
|
|
64
|
+
ccr_id = str(_uuid_mod.uuid4())
|
|
65
|
+
db = self._get_db()
|
|
66
|
+
ttl_expires = (
|
|
67
|
+
_time_mod.time() + ttl_seconds
|
|
68
|
+
if ttl_seconds is not None
|
|
69
|
+
else None
|
|
70
|
+
)
|
|
71
|
+
db.ccr_put(ccr_id, original, ttl_expires=ttl_expires)
|
|
72
|
+
logger.debug("CCR stored ccr_id=%s orig_bytes=%d", ccr_id, len(original))
|
|
73
|
+
return ccr_id
|
|
74
|
+
except Exception as exc:
|
|
75
|
+
logger.warning("CCRStore.store failed (fail-open): %s", exc)
|
|
76
|
+
return ""
|
|
77
|
+
|
|
78
|
+
def update_compressed(self, ccr_id: str, compressed_bytes: bytes) -> None:
|
|
79
|
+
"""Update the compressed_hash for an existing CCR row. Non-fatal if fails."""
|
|
80
|
+
try:
|
|
81
|
+
db = self._get_db()
|
|
82
|
+
if hasattr(db, "ccr_update_compressed"):
|
|
83
|
+
db.ccr_update_compressed(ccr_id, compressed_bytes)
|
|
84
|
+
except Exception as exc:
|
|
85
|
+
logger.debug("CCRStore.update_compressed failed (non-fatal): %s", exc)
|
|
86
|
+
|
|
87
|
+
def retrieve(self, ccr_id: str) -> bytes | None:
|
|
88
|
+
"""Retrieve a CCR original by ccr_id. Returns None if not found or TTL expired."""
|
|
89
|
+
try:
|
|
90
|
+
db = self._get_db()
|
|
91
|
+
return db.ccr_get(ccr_id)
|
|
92
|
+
except Exception as exc:
|
|
93
|
+
logger.warning("CCRStore.retrieve failed (ccr_id=%s): %s", ccr_id, exc)
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
def get_mcp_tool_definition(self) -> dict:
|
|
97
|
+
return {
|
|
98
|
+
"name": "headroom_retrieve",
|
|
99
|
+
"description": (
|
|
100
|
+
"Retrieve the original (pre-compression) text for a compressed content block. "
|
|
101
|
+
"Use this when you need the full, uncompressed version of content that was "
|
|
102
|
+
"compressed by SLM. Provide the ccr_id from the compression stub comment."
|
|
103
|
+
),
|
|
104
|
+
"inputSchema": {
|
|
105
|
+
"type": "object",
|
|
106
|
+
"properties": {
|
|
107
|
+
"ccr_id": {
|
|
108
|
+
"type": "string",
|
|
109
|
+
"description": "The ccr_id from the compression stub comment.",
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
"required": ["ccr_id"],
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async def handle_mcp_call(self, arguments: dict) -> dict:
|
|
117
|
+
ccr_id = arguments.get("ccr_id", "")
|
|
118
|
+
if not ccr_id:
|
|
119
|
+
return {
|
|
120
|
+
"isError": True,
|
|
121
|
+
"content": [{"type": "text", "text": "ccr_id is required"}],
|
|
122
|
+
}
|
|
123
|
+
if not _UUID4_RE.match(ccr_id):
|
|
124
|
+
return {
|
|
125
|
+
"isError": True,
|
|
126
|
+
"content": [{
|
|
127
|
+
"type": "text",
|
|
128
|
+
"text": f"ccr_id must be a UUID4, got {ccr_id!r}",
|
|
129
|
+
}],
|
|
130
|
+
}
|
|
131
|
+
original = self.retrieve(ccr_id)
|
|
132
|
+
if original is None:
|
|
133
|
+
return {
|
|
134
|
+
"isError": True,
|
|
135
|
+
"content": [{
|
|
136
|
+
"type": "text",
|
|
137
|
+
"text": (
|
|
138
|
+
f"CCR original not found for ccr_id={ccr_id!r}. "
|
|
139
|
+
"Possible causes: entry expired, never stored, or ccr_id incorrect."
|
|
140
|
+
),
|
|
141
|
+
}],
|
|
142
|
+
}
|
|
143
|
+
try:
|
|
144
|
+
text = original.decode("utf-8")
|
|
145
|
+
except UnicodeDecodeError:
|
|
146
|
+
logger.warning(
|
|
147
|
+
"CCR ccr_id=%s: original bytes not valid UTF-8; falling back to latin-1",
|
|
148
|
+
ccr_id,
|
|
149
|
+
)
|
|
150
|
+
text = original.decode("latin-1")
|
|
151
|
+
return {"content": [{"type": "text", "text": text}]}
|
|
152
|
+
|
|
153
|
+
def _get_db(self) -> "CacheDB":
|
|
154
|
+
if self._db is None:
|
|
155
|
+
from superlocalmemory.optimize.storage.db import CacheDB
|
|
156
|
+
self._db = CacheDB()
|
|
157
|
+
return self._db
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# compress/extractive_code.py
|
|
2
|
+
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
3
|
+
# Licensed under AGPL-3.0-or-later
|
|
4
|
+
#
|
|
5
|
+
# AST structural patterns adapted from:
|
|
6
|
+
# headroom/compression/handlers/code_handler.py:66-138 (Apache-2.0)
|
|
7
|
+
# Specifically: _STRUCTURAL_NODE_TYPES per-language dict, CodeLanguage enum
|
|
8
|
+
# headroom/compression/handlers/code_handler.py:141-150 — _SIGNATURE_PATTERNS regex fallback
|
|
9
|
+
# Attribution: See ATTRIBUTION.md.
|
|
10
|
+
|
|
11
|
+
"""CodeCompressor — AST-aware extractive code compressor.
|
|
12
|
+
|
|
13
|
+
Supported languages: Python, JavaScript, Go, Rust, Java, C++.
|
|
14
|
+
Path A (tree-sitter): used if tree-sitter-language-pack installed.
|
|
15
|
+
Path B (regex fallback): used otherwise. Tests pass on both paths.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import logging
|
|
21
|
+
import re
|
|
22
|
+
from dataclasses import dataclass
|
|
23
|
+
from enum import Enum
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger("slm.optimize.compress.code")
|
|
26
|
+
|
|
27
|
+
_BODY_STUB_BY_LANG: dict[str, str] = {
|
|
28
|
+
"python": " # [slm: body compressed — retrieve with ccr_id={ccr_id}]",
|
|
29
|
+
"javascript": " // [slm: body compressed — retrieve with ccr_id={ccr_id}]",
|
|
30
|
+
"go": " // [slm: body compressed — retrieve with ccr_id={ccr_id}]",
|
|
31
|
+
"rust": " // [slm: body compressed — retrieve with ccr_id={ccr_id}]",
|
|
32
|
+
"java": " // [slm: body compressed — retrieve with ccr_id={ccr_id}]",
|
|
33
|
+
"cpp": " // [slm: body compressed — retrieve with ccr_id={ccr_id}]",
|
|
34
|
+
}
|
|
35
|
+
_BODY_STUB_NO_CCR_BY_LANG: dict[str, str] = {
|
|
36
|
+
"python": " # [slm: body compressed]",
|
|
37
|
+
"javascript": " // [slm: body compressed]",
|
|
38
|
+
"go": " // [slm: body compressed]",
|
|
39
|
+
"rust": " // [slm: body compressed]",
|
|
40
|
+
"java": " // [slm: body compressed]",
|
|
41
|
+
"cpp": " // [slm: body compressed]",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
_MIN_BODY_LINES: int = 4
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class CodeLanguage(Enum):
|
|
48
|
+
PYTHON = "python"
|
|
49
|
+
JAVASCRIPT = "javascript"
|
|
50
|
+
GO = "go"
|
|
51
|
+
RUST = "rust"
|
|
52
|
+
JAVA = "java"
|
|
53
|
+
CPP = "cpp"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True)
|
|
57
|
+
class CodeSpan:
|
|
58
|
+
start_line: int
|
|
59
|
+
end_line: int
|
|
60
|
+
role: str # "import" | "signature" | "body" | "class_header" | "decorator"
|
|
61
|
+
is_structural: bool
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# ── Signature patterns for regex path (Path B) ────────────────────────────────
|
|
65
|
+
|
|
66
|
+
_SIGNATURE_PATTERNS: dict[str, list[str]] = {
|
|
67
|
+
"python": [
|
|
68
|
+
r"^\s*(async\s+)?def\s+\w+\s*\([^)]*\)\s*(->\s*[^:]+)?:",
|
|
69
|
+
r"^\s*class\s+\w+(\([^)]*\))?:",
|
|
70
|
+
r"^\s*import\s+",
|
|
71
|
+
r"^\s*from\s+\w+\s+import",
|
|
72
|
+
r"^\s*@\w+",
|
|
73
|
+
],
|
|
74
|
+
"javascript": [
|
|
75
|
+
r"^\s*(async\s+)?function\s+\w+\s*\([^)]*\)",
|
|
76
|
+
r"^\s*class\s+\w+(\s+extends\s+\w+)?",
|
|
77
|
+
r"^\s*import\s+",
|
|
78
|
+
r"^\s*const\s+\w+\s*=\s*(async\s+)?\(",
|
|
79
|
+
r"^\s*export\s+(default\s+|const\s+|class\s+|function\s+)",
|
|
80
|
+
],
|
|
81
|
+
"go": [
|
|
82
|
+
r"^\s*func\s+(\(\w+\s+\*?\w+\)\s*)?\w+\s*\(",
|
|
83
|
+
r"^\s*import\s+",
|
|
84
|
+
r"^\s*package\s+",
|
|
85
|
+
r"^\s*type\s+\w+\s+(struct|interface)\s*\{",
|
|
86
|
+
],
|
|
87
|
+
"rust": [
|
|
88
|
+
r"^\s*(pub\s+)?(async\s+)?fn\s+\w+",
|
|
89
|
+
r"^\s*use\s+",
|
|
90
|
+
r"^\s*impl\s+",
|
|
91
|
+
r"^\s*struct\s+",
|
|
92
|
+
r"^\s*enum\s+",
|
|
93
|
+
r"^\s*trait\s+",
|
|
94
|
+
],
|
|
95
|
+
"java": [
|
|
96
|
+
r"^\s*(public|private|protected)\s+(static\s+)?\w+\s+\w+\s*\(",
|
|
97
|
+
r"^\s*import\s+",
|
|
98
|
+
r"^\s*(public|private|protected)?\s*class\s+\w+",
|
|
99
|
+
],
|
|
100
|
+
"cpp": [
|
|
101
|
+
r"^\s*\w[\w\s\*&:<,>]*\s+\w+\s*\([^)]*\)\s*(const\s*)?\{",
|
|
102
|
+
r"^\s*#include\s+",
|
|
103
|
+
r"^\s*(class|struct)\s+\w+",
|
|
104
|
+
],
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class CodeCompressor:
|
|
109
|
+
"""AST-aware extractive code compressor. Thread-safe (no mutable state)."""
|
|
110
|
+
|
|
111
|
+
def compress(self, code: str, language: str, ccr_id: str = "") -> str:
|
|
112
|
+
try:
|
|
113
|
+
lang = CodeLanguage(language)
|
|
114
|
+
except ValueError:
|
|
115
|
+
logger.warning("CodeCompressor: unknown language %r — passthrough", language)
|
|
116
|
+
return code
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
if _tree_sitter_available():
|
|
120
|
+
return self._compress_with_tree_sitter(code, lang, ccr_id)
|
|
121
|
+
else:
|
|
122
|
+
return self._compress_with_regex(code, lang, ccr_id)
|
|
123
|
+
except Exception as exc:
|
|
124
|
+
logger.warning("CodeCompressor.compress failed — passthrough: %s", exc)
|
|
125
|
+
return code
|
|
126
|
+
|
|
127
|
+
def _compress_with_tree_sitter(self, code: str, lang: CodeLanguage, ccr_id: str) -> str:
|
|
128
|
+
from tree_sitter_language_pack import get_parser # type: ignore[import]
|
|
129
|
+
parser = get_parser(lang.value)
|
|
130
|
+
tree = parser.parse(code.encode())
|
|
131
|
+
lines = code.split("\n")
|
|
132
|
+
spans = _collect_spans_tree_sitter(tree.root_node, lang)
|
|
133
|
+
return _apply_spans(lines, spans, ccr_id, lang.value)
|
|
134
|
+
|
|
135
|
+
def _compress_with_regex(self, code: str, lang: CodeLanguage, ccr_id: str) -> str:
|
|
136
|
+
lines = code.split("\n")
|
|
137
|
+
spans = _collect_spans_regex(lines, lang)
|
|
138
|
+
return _apply_spans(lines, spans, ccr_id, lang.value)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
# ── Span collection ───────────────────────────────────────────────────────────
|
|
142
|
+
|
|
143
|
+
def _collect_spans_tree_sitter(root_node: object, lang: CodeLanguage) -> list[CodeSpan]:
|
|
144
|
+
_STRUCTURAL_TYPES: dict[str, set[str]] = {
|
|
145
|
+
"python": {"import_statement", "import_from_statement", "function_definition",
|
|
146
|
+
"class_definition", "decorated_definition", "type_alias_statement"},
|
|
147
|
+
"javascript": {"import_statement", "export_statement", "function_declaration",
|
|
148
|
+
"class_declaration", "method_definition", "arrow_function"},
|
|
149
|
+
"go": {"import_declaration", "function_declaration", "method_declaration",
|
|
150
|
+
"type_declaration", "interface_type"},
|
|
151
|
+
"rust": {"use_declaration", "function_item", "impl_item", "struct_item",
|
|
152
|
+
"enum_item", "trait_item"},
|
|
153
|
+
"java": {"import_declaration", "class_declaration", "method_declaration",
|
|
154
|
+
"interface_declaration", "annotation"},
|
|
155
|
+
"cpp": {"function_definition", "preproc_include", "class_specifier"},
|
|
156
|
+
}
|
|
157
|
+
structural = _STRUCTURAL_TYPES.get(lang.value, set())
|
|
158
|
+
spans: list[CodeSpan] = []
|
|
159
|
+
|
|
160
|
+
def _walk(node: object, depth: int = 0) -> None:
|
|
161
|
+
start_row: int = getattr(node, "start_point", (0, 0))[0]
|
|
162
|
+
end_row: int = getattr(node, "end_point", (0, 0))[0]
|
|
163
|
+
node_type: str = getattr(node, "type", "")
|
|
164
|
+
is_struct = node_type in structural
|
|
165
|
+
|
|
166
|
+
if is_struct and node_type in {
|
|
167
|
+
"function_definition", "method_definition", "function_declaration",
|
|
168
|
+
"function_item", "method_declaration",
|
|
169
|
+
}:
|
|
170
|
+
body_node = _find_child(node, "block") or _find_child(node, "body")
|
|
171
|
+
if body_node is not None:
|
|
172
|
+
body_start = getattr(body_node, "start_point", (0, 0))[0]
|
|
173
|
+
sig_end = body_start - 1
|
|
174
|
+
if sig_end > start_row:
|
|
175
|
+
spans.append(CodeSpan(start_row, sig_end, "signature", True))
|
|
176
|
+
spans.append(CodeSpan(body_start, end_row, "body", False))
|
|
177
|
+
else:
|
|
178
|
+
spans.append(CodeSpan(start_row, end_row, "signature", True))
|
|
179
|
+
elif is_struct and node_type in {"decorated_definition"}:
|
|
180
|
+
def_node = _find_child(node, "function_definition") or _find_child(node, "class_definition")
|
|
181
|
+
if def_node is not None:
|
|
182
|
+
def_start = getattr(def_node, "start_point", (0, 0))[0]
|
|
183
|
+
spans.append(CodeSpan(start_row, def_start - 1, "decorator", True))
|
|
184
|
+
_walk(def_node, depth + 1)
|
|
185
|
+
else:
|
|
186
|
+
spans.append(CodeSpan(start_row, end_row, node_type, True))
|
|
187
|
+
elif is_struct:
|
|
188
|
+
spans.append(CodeSpan(start_row, end_row, "signature", True))
|
|
189
|
+
else:
|
|
190
|
+
children = getattr(node, "children", None)
|
|
191
|
+
if children is not None:
|
|
192
|
+
has_named = False
|
|
193
|
+
for child in children:
|
|
194
|
+
if getattr(child, "is_named", False):
|
|
195
|
+
has_named = True
|
|
196
|
+
_walk(child, depth + 1)
|
|
197
|
+
if not has_named:
|
|
198
|
+
spans.append(CodeSpan(start_row, end_row, "body", False))
|
|
199
|
+
|
|
200
|
+
_walk(root_node)
|
|
201
|
+
return spans
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _find_child(node: object, child_type: str) -> object | None:
|
|
205
|
+
children = getattr(node, "children", None)
|
|
206
|
+
if children is None:
|
|
207
|
+
return None
|
|
208
|
+
for child in children:
|
|
209
|
+
if hasattr(child, "type") and child.type == child_type:
|
|
210
|
+
return child
|
|
211
|
+
return None
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _collect_spans_regex(lines: list[str], lang: CodeLanguage) -> list[CodeSpan]:
|
|
215
|
+
patterns = _SIGNATURE_PATTERNS.get(lang.value, [])
|
|
216
|
+
spans: list[CodeSpan] = []
|
|
217
|
+
i = 0
|
|
218
|
+
n = len(lines)
|
|
219
|
+
|
|
220
|
+
while i < n:
|
|
221
|
+
line = lines[i]
|
|
222
|
+
matched = False
|
|
223
|
+
for pat in patterns:
|
|
224
|
+
if re.search(pat, line):
|
|
225
|
+
body_start = i + 1
|
|
226
|
+
body_end = body_start
|
|
227
|
+
while body_end < n and (
|
|
228
|
+
lines[body_end].startswith((" ", "\t", "", "#", "//", "/*", "*/", "{"))
|
|
229
|
+
or re.match(r"^\s*$", lines[body_end])
|
|
230
|
+
):
|
|
231
|
+
if re.match(r"^\s*($|#|//|/\*|\*/|\*|}|\)|\])", lines[body_end]):
|
|
232
|
+
body_end += 1
|
|
233
|
+
else:
|
|
234
|
+
break
|
|
235
|
+
body_len = body_end - body_start
|
|
236
|
+
if body_len >= _MIN_BODY_LINES and _looks_like_body(lines, body_start, body_end):
|
|
237
|
+
spans.append(CodeSpan(i, i, "signature", True))
|
|
238
|
+
spans.append(CodeSpan(body_start, body_end, "body", False))
|
|
239
|
+
i = body_end
|
|
240
|
+
matched = True
|
|
241
|
+
break
|
|
242
|
+
else:
|
|
243
|
+
body_start = body_end
|
|
244
|
+
# else: pattern didn't match
|
|
245
|
+
if not matched:
|
|
246
|
+
i += 1
|
|
247
|
+
return spans
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _looks_like_body(lines: list[str], start: int, end: int) -> bool:
|
|
251
|
+
body_lines = [l for l in lines[start:end] if l.strip() and not l.strip().startswith(("#", "//"))]
|
|
252
|
+
return len(body_lines) >= _MIN_BODY_LINES
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def _apply_spans(
|
|
256
|
+
lines: list[str],
|
|
257
|
+
spans: list[CodeSpan],
|
|
258
|
+
ccr_id: str,
|
|
259
|
+
lang: str = "python",
|
|
260
|
+
) -> str:
|
|
261
|
+
if not spans:
|
|
262
|
+
return "\n".join(lines)
|
|
263
|
+
|
|
264
|
+
stub_tmpl = _BODY_STUB_BY_LANG.get(lang, _BODY_STUB_BY_LANG["python"])
|
|
265
|
+
stub_no_ccr = _BODY_STUB_NO_CCR_BY_LANG.get(lang, _BODY_STUB_NO_CCR_BY_LANG["python"])
|
|
266
|
+
stub = stub_tmpl.format(ccr_id=ccr_id) if ccr_id else stub_no_ccr
|
|
267
|
+
|
|
268
|
+
sorted_spans = sorted(spans, key=lambda s: s.start_line)
|
|
269
|
+
start_set = set()
|
|
270
|
+
deduped: list[CodeSpan] = []
|
|
271
|
+
for s in sorted_spans:
|
|
272
|
+
if s.start_line not in start_set:
|
|
273
|
+
deduped.append(s)
|
|
274
|
+
start_set.add(s.start_line)
|
|
275
|
+
|
|
276
|
+
span_by_start: dict[int, CodeSpan] = {s.start_line: s for s in deduped}
|
|
277
|
+
|
|
278
|
+
result_lines: list[str] = []
|
|
279
|
+
i = 0
|
|
280
|
+
n = len(lines)
|
|
281
|
+
|
|
282
|
+
while i < n:
|
|
283
|
+
span = span_by_start.get(i)
|
|
284
|
+
if span is not None and not span.is_structural:
|
|
285
|
+
end = min(span.end_line, n - 1)
|
|
286
|
+
body_len = end - i + 1
|
|
287
|
+
if body_len >= _MIN_BODY_LINES:
|
|
288
|
+
result_lines.append(stub)
|
|
289
|
+
i = end + 1
|
|
290
|
+
else:
|
|
291
|
+
for j in range(i, end + 1):
|
|
292
|
+
result_lines.append(lines[j])
|
|
293
|
+
i = end + 1
|
|
294
|
+
elif span is not None and span.is_structural:
|
|
295
|
+
end = min(span.end_line, n - 1)
|
|
296
|
+
for j in range(i, end + 1):
|
|
297
|
+
result_lines.append(lines[j])
|
|
298
|
+
i = end + 1
|
|
299
|
+
else:
|
|
300
|
+
result_lines.append(lines[i])
|
|
301
|
+
i += 1
|
|
302
|
+
|
|
303
|
+
return "\n".join(result_lines)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def _tree_sitter_available() -> bool:
|
|
307
|
+
try:
|
|
308
|
+
import tree_sitter_language_pack # noqa: F401
|
|
309
|
+
return True
|
|
310
|
+
except ImportError:
|
|
311
|
+
return False
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# compress/extractive_json.py
|
|
2
|
+
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
3
|
+
# Licensed under AGPL-3.0-or-later
|
|
4
|
+
#
|
|
5
|
+
# Structural masking pattern adapted from:
|
|
6
|
+
# headroom/compression/handlers/json_handler.py (Apache-2.0, Headroom contributors)
|
|
7
|
+
# Specifically: JSONStructureHandler._extract_mask(), JSONToken, JSONTokenType
|
|
8
|
+
# Attribution: See ATTRIBUTION.md.
|
|
9
|
+
#
|
|
10
|
+
# HARD RULE: This compressor MUST NEVER prune or reorder JSON keys.
|
|
11
|
+
# Keys pruned = structured semantics corrupted non-recoverably. Not configurable.
|
|
12
|
+
|
|
13
|
+
"""JSONCompressor — lossless-ish extractive JSON compressor."""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
import logging
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger("slm.optimize.compress.json")
|
|
22
|
+
|
|
23
|
+
VALUE_TRUNCATE_CHARS: int = 120
|
|
24
|
+
VALUE_TRUNCATE_SUFFIX: str = "\u2026" # ellipsis
|
|
25
|
+
MAX_ARRAY_ITEMS_SHOWN: int = 5
|
|
26
|
+
ARRAY_REMAINDER_KEY: str = "__slm_omitted__"
|
|
27
|
+
MIN_VALUE_LEN_TO_TRUNCATE: int = 40
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class JSONCompressor:
|
|
31
|
+
"""Lossless-ish JSON compressor. Thread-safe (no mutable state)."""
|
|
32
|
+
|
|
33
|
+
def compress(self, parsed: Any) -> str:
|
|
34
|
+
try:
|
|
35
|
+
masked = self._mask(parsed, depth=0)
|
|
36
|
+
return json.dumps(masked, ensure_ascii=False, separators=(",", ":"))
|
|
37
|
+
except Exception as exc:
|
|
38
|
+
logger.warning("JSONCompressor.compress failed — returning original: %s", exc)
|
|
39
|
+
return json.dumps(parsed, ensure_ascii=False, separators=(",", ":"))
|
|
40
|
+
|
|
41
|
+
def _mask(self, obj: Any, depth: int) -> Any:
|
|
42
|
+
if isinstance(obj, dict):
|
|
43
|
+
return {k: self._mask(v, depth + 1) for k, v in obj.items()}
|
|
44
|
+
if isinstance(obj, list):
|
|
45
|
+
return self._mask_array(obj, depth)
|
|
46
|
+
if isinstance(obj, str):
|
|
47
|
+
return self._mask_string(obj)
|
|
48
|
+
return obj
|
|
49
|
+
|
|
50
|
+
def _mask_array(self, arr: list, depth: int) -> list:
|
|
51
|
+
if len(arr) <= MAX_ARRAY_ITEMS_SHOWN:
|
|
52
|
+
return [self._mask(item, depth + 1) for item in arr]
|
|
53
|
+
shown = [self._mask(item, depth + 1) for item in arr[:MAX_ARRAY_ITEMS_SHOWN]]
|
|
54
|
+
collision = any(
|
|
55
|
+
isinstance(item, dict) and ARRAY_REMAINDER_KEY in item
|
|
56
|
+
for item in arr
|
|
57
|
+
)
|
|
58
|
+
if collision:
|
|
59
|
+
logger.warning(
|
|
60
|
+
"JSONCompressor: input contains reserved key %r — skipping array sentinel",
|
|
61
|
+
ARRAY_REMAINDER_KEY,
|
|
62
|
+
)
|
|
63
|
+
else:
|
|
64
|
+
shown.append({ARRAY_REMAINDER_KEY: len(arr) - MAX_ARRAY_ITEMS_SHOWN})
|
|
65
|
+
return shown
|
|
66
|
+
|
|
67
|
+
def _mask_string(self, s: str) -> str:
|
|
68
|
+
if len(s) < MIN_VALUE_LEN_TO_TRUNCATE:
|
|
69
|
+
return s
|
|
70
|
+
if len(s) <= VALUE_TRUNCATE_CHARS:
|
|
71
|
+
return s
|
|
72
|
+
return s[:VALUE_TRUNCATE_CHARS] + VALUE_TRUNCATE_SUFFIX
|