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
|
@@ -64,6 +64,36 @@ def _cmd_escape_rotate_token(args: Namespace) -> None:
|
|
|
64
64
|
cmd_rotate_token(args)
|
|
65
65
|
|
|
66
66
|
|
|
67
|
+
# ---- SLM v3.6 Optimize dispatch functions (additive) ----
|
|
68
|
+
|
|
69
|
+
def _cmd_optimize(args: Namespace) -> None:
|
|
70
|
+
from superlocalmemory.cli.optimize_cmd import cmd_optimize
|
|
71
|
+
cmd_optimize(args)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _cmd_cache(args: Namespace) -> None:
|
|
75
|
+
from superlocalmemory.cli.cache_cmd import cmd_cache
|
|
76
|
+
cmd_cache(args)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _cmd_compress(args: Namespace) -> None:
|
|
80
|
+
from superlocalmemory.cli.compress_cmd import cmd_compress
|
|
81
|
+
cmd_compress(args)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _cmd_proxy(args: Namespace) -> None:
|
|
85
|
+
from superlocalmemory.cli.proxy_cmd import cmd_proxy
|
|
86
|
+
cmd_proxy(args)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _cmd_help_optimize(args: Namespace) -> None:
|
|
90
|
+
from superlocalmemory.cli.help_cmd import cmd_help_optimize
|
|
91
|
+
cmd_help_optimize(args)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# ---- end SLM v3.6 Optimize dispatch functions ----
|
|
95
|
+
|
|
96
|
+
|
|
67
97
|
def dispatch(args: Namespace) -> None:
|
|
68
98
|
"""Route CLI command to the appropriate handler."""
|
|
69
99
|
# Auto-install/upgrade hooks on version change (single file read, ~0.1ms)
|
|
@@ -126,6 +156,14 @@ def dispatch(args: Namespace) -> None:
|
|
|
126
156
|
"reconfigure": _cmd_escape_reconfigure,
|
|
127
157
|
"benchmark": _cmd_escape_benchmark,
|
|
128
158
|
"rotate-token": _cmd_escape_rotate_token,
|
|
159
|
+
# LLD-06 — `slm wrap <agent> [args...]` activates the Optimize proxy.
|
|
160
|
+
"wrap": _cmd_wrap,
|
|
161
|
+
# V3.6 Optimize subcommands (additive)
|
|
162
|
+
"optimize": _cmd_optimize,
|
|
163
|
+
"cache": _cmd_cache,
|
|
164
|
+
"compress": _cmd_compress,
|
|
165
|
+
"proxy": _cmd_proxy,
|
|
166
|
+
"help-optimize": _cmd_help_optimize,
|
|
129
167
|
}
|
|
130
168
|
handler = handlers.get(args.command)
|
|
131
169
|
if handler:
|
|
@@ -135,6 +173,39 @@ def dispatch(args: Namespace) -> None:
|
|
|
135
173
|
sys.exit(1)
|
|
136
174
|
|
|
137
175
|
|
|
176
|
+
def _cmd_wrap(args: Namespace) -> None:
|
|
177
|
+
"""LLD-06 §6.6 — `slm wrap <agent> [args...]` activates the Optimize proxy.
|
|
178
|
+
|
|
179
|
+
Routes the Optimize layer's per-agent launch/activation. See
|
|
180
|
+
optimize.adapters._agent_registry for the full agent table.
|
|
181
|
+
"""
|
|
182
|
+
if getattr(args, "list", False):
|
|
183
|
+
from superlocalmemory.optimize.adapters.wrap import list_agents
|
|
184
|
+
from superlocalmemory.optimize.adapters._agent_registry import AGENT_REGISTRY
|
|
185
|
+
print("Registered agents for `slm wrap`:")
|
|
186
|
+
for key in list_agents():
|
|
187
|
+
spec = AGENT_REGISTRY.get(key, {})
|
|
188
|
+
mech = spec.get("mechanism", "unknown")
|
|
189
|
+
print(f" {key:20s} mechanism={mech}")
|
|
190
|
+
return
|
|
191
|
+
|
|
192
|
+
agent = getattr(args, "agent", None)
|
|
193
|
+
if not agent:
|
|
194
|
+
print("Usage: slm wrap <agent> [args...]\n"
|
|
195
|
+
" slm wrap --list\n"
|
|
196
|
+
" slm wrap --help")
|
|
197
|
+
sys.exit(2)
|
|
198
|
+
|
|
199
|
+
agent_args = list(getattr(args, "agent_args", []) or [])
|
|
200
|
+
persistent = bool(getattr(args, "persistent", False))
|
|
201
|
+
dry_run = bool(getattr(args, "dry_run", False))
|
|
202
|
+
|
|
203
|
+
from superlocalmemory.optimize.adapters.wrap import wrap_agent
|
|
204
|
+
rc = wrap_agent(agent, agent_args, persistent=persistent, dry_run=dry_run)
|
|
205
|
+
if rc:
|
|
206
|
+
sys.exit(rc)
|
|
207
|
+
|
|
208
|
+
|
|
138
209
|
# -- Daemon serve mode (V3.3.21) ------------------------------------------
|
|
139
210
|
|
|
140
211
|
def cmd_serve(args: Namespace) -> None:
|
|
@@ -1661,6 +1732,26 @@ def cmd_trace(args: Namespace) -> None:
|
|
|
1661
1732
|
|
|
1662
1733
|
def cmd_mcp(_args: Namespace) -> None:
|
|
1663
1734
|
"""Start the V3 MCP server (stdio transport for IDE integration)."""
|
|
1735
|
+
# SINGLETON GUARD (v3.5.8): Reap fresh orphans before binding stdio.
|
|
1736
|
+
# Root cause: every IDE session spawns a new `slm mcp`; dead sessions'
|
|
1737
|
+
# processes survive indefinitely (~400 MB each) because the reaper's
|
|
1738
|
+
# 1-hour age gate misses them. Fix: run find_orphans with age_threshold=0
|
|
1739
|
+
# so any `slm mcp` whose parent IDE/Claude process has died is killed
|
|
1740
|
+
# immediately. Only orphans (dead parent) are killed — live sessions safe.
|
|
1741
|
+
# CRITICAL: No stdout anywhere below — MCP stdio transport, any print
|
|
1742
|
+
# corrupts the JSON-RPC protocol.
|
|
1743
|
+
try:
|
|
1744
|
+
from superlocalmemory.infra.process_reaper import (
|
|
1745
|
+
ReaperConfig,
|
|
1746
|
+
find_orphans,
|
|
1747
|
+
kill_orphan,
|
|
1748
|
+
)
|
|
1749
|
+
_reaper_cfg = ReaperConfig(orphan_age_threshold_hours=0.0)
|
|
1750
|
+
for _orphan in find_orphans(_reaper_cfg):
|
|
1751
|
+
kill_orphan(_orphan.pid, graceful_timeout_seconds=1.0)
|
|
1752
|
+
except Exception:
|
|
1753
|
+
pass # Never block MCP startup on cleanup failure
|
|
1754
|
+
|
|
1664
1755
|
# Auto-install hooks on MCP startup (fast path: ~0.1ms if already current)
|
|
1665
1756
|
# CRITICAL: No stdout — MCP uses stdio transport, any print corrupts protocol
|
|
1666
1757
|
try:
|
|
@@ -2553,6 +2644,11 @@ def cmd_reap(args: Namespace) -> None:
|
|
|
2553
2644
|
"""Find and kill orphaned SLM processes."""
|
|
2554
2645
|
use_json = getattr(args, "json", False)
|
|
2555
2646
|
dry_run = not getattr(args, "force", False)
|
|
2647
|
+
# V3.5.9: --all bypasses orphan detection and kills every slm mcp process
|
|
2648
|
+
# except the current one. Use after switching IDEs to clear stale sessions.
|
|
2649
|
+
use_force_all = getattr(args, "all", False)
|
|
2650
|
+
if use_force_all:
|
|
2651
|
+
dry_run = False # --all always kills; --force is implied
|
|
2556
2652
|
|
|
2557
2653
|
try:
|
|
2558
2654
|
from superlocalmemory.infra.process_reaper import (
|
|
@@ -2561,7 +2657,7 @@ def cmd_reap(args: Namespace) -> None:
|
|
|
2561
2657
|
)
|
|
2562
2658
|
|
|
2563
2659
|
config = ReaperConfig()
|
|
2564
|
-
result = cleanup_all_orphans(config, dry_run=dry_run)
|
|
2660
|
+
result = cleanup_all_orphans(config, dry_run=dry_run, force=use_force_all)
|
|
2565
2661
|
except Exception as exc:
|
|
2566
2662
|
if use_json:
|
|
2567
2663
|
from superlocalmemory.cli.json_output import json_print
|
|
@@ -2580,7 +2676,8 @@ def cmd_reap(args: Namespace) -> None:
|
|
|
2580
2676
|
"killed": result.get("killed", 0),
|
|
2581
2677
|
"skipped": result.get("skipped", 0),
|
|
2582
2678
|
}, next_actions=[
|
|
2583
|
-
{"command": "slm reap --force --json", "description": "Kill
|
|
2679
|
+
{"command": "slm reap --force --json", "description": "Kill orphan processes"},
|
|
2680
|
+
{"command": "slm reap --all --json", "description": "Kill ALL slm mcp sessions (IDE switch)"},
|
|
2584
2681
|
{"command": "slm status --json", "description": "Check status"},
|
|
2585
2682
|
])
|
|
2586
2683
|
return
|
|
@@ -2600,3 +2697,4 @@ def cmd_reap(args: Namespace) -> None:
|
|
|
2600
2697
|
print(f" Skipped: {skipped}")
|
|
2601
2698
|
if dry_run and orphans > 0:
|
|
2602
2699
|
print("\n Use --force to kill orphaned processes.")
|
|
2700
|
+
print(" Use --all to kill ALL slm mcp sessions (after IDE switch).")
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
2
|
+
# Licensed under AGPL-3.0-or-later - see LICENSE file
|
|
3
|
+
# Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
|
|
4
|
+
|
|
5
|
+
"""Handlers for ``slm compress status|mode|code|prose|ccr``."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import dataclasses
|
|
10
|
+
import json
|
|
11
|
+
import sys
|
|
12
|
+
from argparse import Namespace
|
|
13
|
+
|
|
14
|
+
from superlocalmemory.cli.optimize_constants import AGGRESSIVE_MODE_WARNING
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _get_store():
|
|
18
|
+
from superlocalmemory.optimize.config.store import ConfigStore
|
|
19
|
+
return ConfigStore()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _get_cache_db():
|
|
23
|
+
from superlocalmemory.optimize.storage.db import CacheDB
|
|
24
|
+
return CacheDB()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _write_config(**fields) -> None:
|
|
28
|
+
"""5-step immutable config-write."""
|
|
29
|
+
store = _get_store()
|
|
30
|
+
cfg = store.get()
|
|
31
|
+
try:
|
|
32
|
+
cfg = dataclasses.replace(cfg, **fields)
|
|
33
|
+
store.save(cfg)
|
|
34
|
+
except ValueError as e:
|
|
35
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
36
|
+
sys.exit(1)
|
|
37
|
+
except OSError as e:
|
|
38
|
+
print(f"Error writing config: {e}", file=sys.stderr)
|
|
39
|
+
sys.exit(1)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def cmd_compress(args: Namespace) -> None:
|
|
43
|
+
"""Top-level dispatcher for ``slm compress <subcommand>``."""
|
|
44
|
+
sub = getattr(args, "compress_command", None)
|
|
45
|
+
_dispatch = {
|
|
46
|
+
"status": cmd_compress_status,
|
|
47
|
+
"mode": cmd_compress_mode,
|
|
48
|
+
"code": cmd_compress_code,
|
|
49
|
+
"prose": cmd_compress_prose,
|
|
50
|
+
"ccr": cmd_compress_ccr,
|
|
51
|
+
"align": cmd_compress_align,
|
|
52
|
+
}
|
|
53
|
+
handler = _dispatch.get(sub or "")
|
|
54
|
+
if handler:
|
|
55
|
+
handler(args)
|
|
56
|
+
else:
|
|
57
|
+
print("Usage: slm compress status|mode|code|prose|ccr [options]")
|
|
58
|
+
sys.exit(0)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def cmd_compress_status(args: Namespace) -> None:
|
|
62
|
+
"""Print compression status."""
|
|
63
|
+
use_json = getattr(args, "json", False)
|
|
64
|
+
cfg = _get_store().get()
|
|
65
|
+
|
|
66
|
+
if use_json:
|
|
67
|
+
data = {
|
|
68
|
+
"status": "ok",
|
|
69
|
+
"compress_enabled": cfg.compress_enabled,
|
|
70
|
+
"compress_mode": cfg.compress_mode,
|
|
71
|
+
"compress_code": cfg.compress_code,
|
|
72
|
+
"compress_prose": cfg.compress_prose,
|
|
73
|
+
"compress_ccr": cfg.compress_ccr,
|
|
74
|
+
}
|
|
75
|
+
print(json.dumps(data, indent=2))
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
print("Compression status:")
|
|
79
|
+
print(f" Enabled: {'yes' if cfg.compress_enabled else 'no'}")
|
|
80
|
+
print(f" Mode: {cfg.compress_mode}")
|
|
81
|
+
print(f" Code: {'ON' if cfg.compress_code else 'OFF'}"
|
|
82
|
+
" (extractive JSON/code — lossless structure)")
|
|
83
|
+
print(f" Prose: {'ON' if cfg.compress_prose else 'OFF'}")
|
|
84
|
+
print(f" CCR: {'ON' if cfg.compress_ccr else 'OFF'}"
|
|
85
|
+
" (reversible context retrieval)")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def cmd_compress_mode(args: Namespace) -> None:
|
|
89
|
+
"""Set compression mode to safe or aggressive."""
|
|
90
|
+
use_json = getattr(args, "json", False)
|
|
91
|
+
mode_value = getattr(args, "mode_value", "safe")
|
|
92
|
+
|
|
93
|
+
if mode_value == "aggressive":
|
|
94
|
+
print(AGGRESSIVE_MODE_WARNING)
|
|
95
|
+
|
|
96
|
+
_write_config(compress_mode=mode_value)
|
|
97
|
+
|
|
98
|
+
if use_json:
|
|
99
|
+
print(json.dumps({"status": "ok", "compress_mode": mode_value}))
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
print(f"Compression mode set to: {mode_value}.")
|
|
103
|
+
print("Daemon hot-reload: active within 2s. No restart required.")
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def cmd_compress_code(args: Namespace) -> None:
|
|
107
|
+
"""Enable or disable code/JSON compression."""
|
|
108
|
+
use_json = getattr(args, "json", False)
|
|
109
|
+
value = getattr(args, "code_value", "on")
|
|
110
|
+
|
|
111
|
+
_write_config(compress_code=(value == "on"))
|
|
112
|
+
|
|
113
|
+
if use_json:
|
|
114
|
+
print(json.dumps({"status": "ok", "compress_code": value == "on"}))
|
|
115
|
+
return
|
|
116
|
+
|
|
117
|
+
print(f"Code compression: {'ENABLED' if value == 'on' else 'DISABLED'}.")
|
|
118
|
+
print("Daemon hot-reload: active within 2s.")
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def cmd_compress_prose(args: Namespace) -> None:
|
|
122
|
+
"""Enable or disable prose compression."""
|
|
123
|
+
use_json = getattr(args, "json", False)
|
|
124
|
+
value = getattr(args, "prose_value", "off")
|
|
125
|
+
|
|
126
|
+
store = _get_store()
|
|
127
|
+
cfg = store.get()
|
|
128
|
+
|
|
129
|
+
fields: dict = {"compress_prose": (value == "on")}
|
|
130
|
+
if value == "on" and not cfg.compress_enabled:
|
|
131
|
+
fields["compress_enabled"] = True
|
|
132
|
+
|
|
133
|
+
try:
|
|
134
|
+
cfg = dataclasses.replace(cfg, **fields)
|
|
135
|
+
store.save(cfg)
|
|
136
|
+
except (ValueError, OSError) as e:
|
|
137
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
138
|
+
sys.exit(1)
|
|
139
|
+
|
|
140
|
+
if use_json:
|
|
141
|
+
print(json.dumps({"status": "ok", "compress_prose": value == "on"}))
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
print(f"Prose compression: {'ENABLED' if value == 'on' else 'DISABLED'}.")
|
|
145
|
+
if value == "on" and "compress_enabled" in fields:
|
|
146
|
+
print(" (also enabled global compress)")
|
|
147
|
+
print("Daemon hot-reload: active within 2s.")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def cmd_compress_align(args: Namespace) -> None:
|
|
151
|
+
"""Enable or disable alignment compression."""
|
|
152
|
+
use_json = getattr(args, "json", False)
|
|
153
|
+
value = getattr(args, "align_value", "on")
|
|
154
|
+
|
|
155
|
+
_write_config(compress_align=(value == "on"))
|
|
156
|
+
|
|
157
|
+
if use_json:
|
|
158
|
+
print(json.dumps({"status": "ok", "compress_align": value == "on"}))
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
print(f"Alignment compression: {'ENABLED' if value == 'on' else 'DISABLED'}.")
|
|
162
|
+
print("Daemon hot-reload: active within 2s.")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def cmd_compress_ccr(args: Namespace) -> None:
|
|
166
|
+
"""Enable or disable CCR (Compressed Context Retrieval)."""
|
|
167
|
+
use_json = getattr(args, "json", False)
|
|
168
|
+
value = getattr(args, "ccr_value", "off")
|
|
169
|
+
|
|
170
|
+
_write_config(compress_ccr=(value == "on"))
|
|
171
|
+
|
|
172
|
+
if use_json:
|
|
173
|
+
print(json.dumps({"status": "ok", "compress_ccr": value == "on"}))
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
print(f"CCR (Compressed Context Retrieval): {'ENABLED' if value == 'on' else 'DISABLED'}.")
|
|
177
|
+
if value == "on":
|
|
178
|
+
print("Originals stored in llmcache.db for reversible retrieval.")
|
|
179
|
+
print("Daemon hot-reload: active within 2s.")
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
2
|
+
# Licensed under AGPL-3.0-or-later - see LICENSE file
|
|
3
|
+
# Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
|
|
4
|
+
|
|
5
|
+
"""Handler for ``slm help-optimize``."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import sys
|
|
10
|
+
from argparse import Namespace
|
|
11
|
+
|
|
12
|
+
_HELP_SECTIONS: dict[str, str] = {
|
|
13
|
+
"header": """\
|
|
14
|
+
SLM v3.6 Optimize — Developer Reference
|
|
15
|
+
========================================
|
|
16
|
+
Every feature is configurable via CLI. UI provides the same controls for
|
|
17
|
+
non-technical users. All CLI commands write config at runtime — daemon
|
|
18
|
+
hot-reloads within 2 seconds. No restart required.
|
|
19
|
+
""",
|
|
20
|
+
"optimize": """\
|
|
21
|
+
slm optimize — Optimize module control
|
|
22
|
+
---------------------------------------
|
|
23
|
+
slm optimize status Show all Optimize settings
|
|
24
|
+
slm optimize on Enable cache + compress
|
|
25
|
+
slm optimize off Disable cache + compress (proxy keeps running)
|
|
26
|
+
slm optimize savings [--since N] Token/cost savings report (default: 7 days)
|
|
27
|
+
--since N Days to look back (integer, default 7)
|
|
28
|
+
--provider P Filter by: anthropic|openai|gemini
|
|
29
|
+
All: --json Machine-readable JSON output
|
|
30
|
+
""",
|
|
31
|
+
"cache": """\
|
|
32
|
+
slm cache — Cache control
|
|
33
|
+
--------------------------
|
|
34
|
+
slm cache status Entry count, DB size, TTLs, hit rate
|
|
35
|
+
slm cache clear Delete all entries for this tenant
|
|
36
|
+
slm cache invalidate --tag <t> Delete entries whose tag array contains <t>
|
|
37
|
+
slm cache ttl --set <s> Set exact-cache TTL (seconds)
|
|
38
|
+
--semantic <s> Set semantic-cache TTL (seconds)
|
|
39
|
+
slm cache semantic on|off Enable/disable semantic cache
|
|
40
|
+
All: --json --tenant <id>
|
|
41
|
+
|
|
42
|
+
TTL examples:
|
|
43
|
+
slm cache ttl --set 3600 # 1 hour exact cache
|
|
44
|
+
slm cache ttl --set 86400 # 24 hour exact cache (default)
|
|
45
|
+
slm cache ttl --semantic 7200 # 2 hour semantic cache
|
|
46
|
+
""",
|
|
47
|
+
"compress": """\
|
|
48
|
+
slm compress — Compression control
|
|
49
|
+
-----------------------------------
|
|
50
|
+
slm compress status Show compression mode, per-channel state
|
|
51
|
+
slm compress mode safe|aggressive Set compression aggressiveness
|
|
52
|
+
slm compress code on|off Enable/disable code/JSON compression
|
|
53
|
+
slm compress prose on|off Enable/disable prose compression
|
|
54
|
+
slm compress ccr on|off Enable/disable Compressed Context Retrieval
|
|
55
|
+
All: --json
|
|
56
|
+
""",
|
|
57
|
+
"safety": """\
|
|
58
|
+
COMPRESSION SAFETY WARNING
|
|
59
|
+
---------------------------
|
|
60
|
+
Safe mode (default):
|
|
61
|
+
- Code/JSON compression: extractive, structure-preserving, lossless.
|
|
62
|
+
- Prose compression: DISABLED in safe mode. Enable manually.
|
|
63
|
+
- CCR: DISABLED by default. Originals stored for reversible retrieval.
|
|
64
|
+
- This mode is production-safe for all use cases.
|
|
65
|
+
|
|
66
|
+
Aggressive mode:
|
|
67
|
+
!! RISK: May reduce output fidelity. Use with caution. !!
|
|
68
|
+
- Prose compression: LLMLingua-2-style extractive summarization.
|
|
69
|
+
- May omit nuance, hedges, or low-salience context.
|
|
70
|
+
- DO NOT use for:
|
|
71
|
+
- Code generation or code review
|
|
72
|
+
- Legal, compliance, or regulatory text
|
|
73
|
+
- Math, formulas, or structured data generation
|
|
74
|
+
- Any task requiring exact reproduction of input
|
|
75
|
+
- Suitable for:
|
|
76
|
+
- Open-ended brainstorming
|
|
77
|
+
- Summarization of long documents
|
|
78
|
+
- Casual conversation and exploration
|
|
79
|
+
- To revert: slm compress mode safe
|
|
80
|
+
""",
|
|
81
|
+
"proxy": """\
|
|
82
|
+
slm proxy — Optimization proxy
|
|
83
|
+
--------------------------------
|
|
84
|
+
slm proxy [--port P] [--provider anthropic|openai|gemini]
|
|
85
|
+
[--no-compress] [--semantic] [--json]
|
|
86
|
+
|
|
87
|
+
Starts the SLM proxy (or reports existing). Proxy intercepts LLM calls,
|
|
88
|
+
applies cache lookup, and optionally compresses context before forwarding.
|
|
89
|
+
|
|
90
|
+
Default port: 8765
|
|
91
|
+
Anthropic surface: http://127.0.0.1:8765
|
|
92
|
+
OpenAI surface: http://127.0.0.1:8765/v1
|
|
93
|
+
""",
|
|
94
|
+
"agents": """\
|
|
95
|
+
PER-AGENT SETUP RECIPES
|
|
96
|
+
------------------------
|
|
97
|
+
|
|
98
|
+
--- Claude Code (ANTHROPIC_BASE_URL) ---
|
|
99
|
+
The simplest redirect. Set this env var before launching Claude Code.
|
|
100
|
+
|
|
101
|
+
Option A — environment variable (per-session):
|
|
102
|
+
export ANTHROPIC_BASE_URL=http://127.0.0.1:8765
|
|
103
|
+
claude # Claude Code picks up the env var
|
|
104
|
+
|
|
105
|
+
Option B — slm wrap (one command, recommended):
|
|
106
|
+
slm wrap claude # starts proxy + sets env + launches claude
|
|
107
|
+
|
|
108
|
+
Option C — permanent (add to ~/.zshrc or ~/.bashrc):
|
|
109
|
+
echo 'export ANTHROPIC_BASE_URL=http://127.0.0.1:8765' >> ~/.zshrc
|
|
110
|
+
source ~/.zshrc
|
|
111
|
+
|
|
112
|
+
Verify: slm optimize status -> look for "proxy: running on :8765"
|
|
113
|
+
|
|
114
|
+
--- Antigravity (config.toml base_url) ---
|
|
115
|
+
Antigravity reads base_url from its config.toml.
|
|
116
|
+
Default config location: ~/.config/antigravity/config.toml
|
|
117
|
+
|
|
118
|
+
Add or update this line under the [api] section:
|
|
119
|
+
[api]
|
|
120
|
+
base_url = "http://127.0.0.1:8765"
|
|
121
|
+
|
|
122
|
+
Or use slm wrap (if supported in your version):
|
|
123
|
+
slm wrap antigravity
|
|
124
|
+
|
|
125
|
+
Verify: antigravity --debug -> look for "base_url: http://127.0.0.1:8765"
|
|
126
|
+
|
|
127
|
+
--- Generic OpenAI-compatible clients (Cline, Cursor, Aider, OpenCode) ---
|
|
128
|
+
Any client that accepts an OpenAI base_url can use the /v1 surface:
|
|
129
|
+
base_url = http://127.0.0.1:8765/v1
|
|
130
|
+
api_key = (use your real provider key — proxy forwards it)
|
|
131
|
+
|
|
132
|
+
Aider:
|
|
133
|
+
aider --openai-api-base http://127.0.0.1:8765/v1
|
|
134
|
+
|
|
135
|
+
Cline (VS Code settings.json):
|
|
136
|
+
"cline.openAiBaseUrl": "http://127.0.0.1:8765/v1"
|
|
137
|
+
|
|
138
|
+
Cursor (Settings > Models > Base URL):
|
|
139
|
+
http://127.0.0.1:8765/v1
|
|
140
|
+
|
|
141
|
+
OpenCode / other CLI tools:
|
|
142
|
+
OPENAI_BASE_URL=http://127.0.0.1:8765/v1 opencode
|
|
143
|
+
|
|
144
|
+
Or use slm wrap:
|
|
145
|
+
slm wrap aider [-- <aider args>]
|
|
146
|
+
slm wrap cursor
|
|
147
|
+
slm wrap opencode
|
|
148
|
+
|
|
149
|
+
--- SDK adapters (Python) ---
|
|
150
|
+
from superlocalmemory.optimize.adapters.openai_adapter import withSLM
|
|
151
|
+
from openai import OpenAI
|
|
152
|
+
client = withSLM(OpenAI()) # same interface as OpenAI() — zero API change
|
|
153
|
+
|
|
154
|
+
from superlocalmemory.optimize.adapters.anthropic_adapter import withSLM
|
|
155
|
+
from anthropic import Anthropic
|
|
156
|
+
client = withSLM(Anthropic())
|
|
157
|
+
|
|
158
|
+
NOTE: withSLM() is a pass-through when optimize is OFF.
|
|
159
|
+
No behavioral change until you run: slm optimize on
|
|
160
|
+
""",
|
|
161
|
+
"footer": """\
|
|
162
|
+
More commands:
|
|
163
|
+
slm help-optimize cache Cache subcommand reference
|
|
164
|
+
slm help-optimize compress Compress reference + safety warning
|
|
165
|
+
slm help-optimize agents Per-agent setup recipes
|
|
166
|
+
slm help-optimize safety Compression safety warning only
|
|
167
|
+
|
|
168
|
+
Documentation: https://superlocalmemory.com
|
|
169
|
+
GitHub: https://github.com/qualixar/superlocalmemory
|
|
170
|
+
AI Reliability Engineering by @varunPbhardwaj — https://qualixar.com
|
|
171
|
+
""",
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def cmd_help_optimize(args: Namespace) -> None:
|
|
176
|
+
"""Print the full slm help-optimize page or a topic-specific section."""
|
|
177
|
+
topic = getattr(args, "topic", None)
|
|
178
|
+
no_pager = getattr(args, "no_pager", False)
|
|
179
|
+
|
|
180
|
+
if topic and topic not in _HELP_SECTIONS:
|
|
181
|
+
print(f"Unknown topic: {topic}."
|
|
182
|
+
f" Topics: {' '.join(k for k in _HELP_SECTIONS if k != 'footer')}")
|
|
183
|
+
sys.exit(1)
|
|
184
|
+
|
|
185
|
+
if topic:
|
|
186
|
+
text = _HELP_SECTIONS[topic]
|
|
187
|
+
else:
|
|
188
|
+
text = "\n".join(_HELP_SECTIONS.values())
|
|
189
|
+
|
|
190
|
+
if no_pager or not sys.stdout.isatty():
|
|
191
|
+
print(text)
|
|
192
|
+
else:
|
|
193
|
+
try:
|
|
194
|
+
import pydoc
|
|
195
|
+
pydoc.pager(text)
|
|
196
|
+
except Exception:
|
|
197
|
+
print(text)
|
|
@@ -240,6 +240,28 @@ def main() -> None:
|
|
|
240
240
|
help="Run only the fast checks (deps + config); skip daemon/embedding probes",
|
|
241
241
|
)
|
|
242
242
|
|
|
243
|
+
# LLD-06 §6.6 — `slm wrap <agent> [args...]` activates the Optimize
|
|
244
|
+
# proxy for a specific agent. Supported agents: claude, claude-settings,
|
|
245
|
+
# codex, aider, cline, generic, etc. See optimize.adapters._agent_registry.
|
|
246
|
+
wrap_p = sub.add_parser(
|
|
247
|
+
"wrap",
|
|
248
|
+
help="Activate Optimize proxy for a specific agent (claude, codex, aider, ...)",
|
|
249
|
+
)
|
|
250
|
+
wrap_p.add_argument(
|
|
251
|
+
"--list", action="store_true",
|
|
252
|
+
help="List all registered agents and their mechanisms",
|
|
253
|
+
)
|
|
254
|
+
wrap_p.add_argument(
|
|
255
|
+
"--persistent", action="store_true",
|
|
256
|
+
help="Persist env vars to the agent's config file (~/.claude/settings.json) instead of launching",
|
|
257
|
+
)
|
|
258
|
+
wrap_p.add_argument(
|
|
259
|
+
"--dry-run", action="store_true",
|
|
260
|
+
help="Print the action that would be taken without executing it",
|
|
261
|
+
)
|
|
262
|
+
wrap_p.add_argument("agent", nargs="?", default=None, help="Agent key (run `slm wrap --list` to see all)")
|
|
263
|
+
wrap_p.add_argument("agent_args", nargs=argparse.REMAINDER, help="Args passed to the agent binary")
|
|
264
|
+
|
|
243
265
|
# -- Services ------------------------------------------------------
|
|
244
266
|
sub.add_parser("mcp", help="Start MCP server (stdio transport for IDE integration)")
|
|
245
267
|
sub.add_parser("warmup", help="Pre-download embedding model (~500MB, one-time)")
|
|
@@ -349,6 +371,10 @@ def main() -> None:
|
|
|
349
371
|
"--force", action="store_true",
|
|
350
372
|
help="Kill orphans (default: dry run only)",
|
|
351
373
|
)
|
|
374
|
+
reap_p.add_argument(
|
|
375
|
+
"--all", action="store_true", dest="all",
|
|
376
|
+
help="Kill ALL slm mcp processes regardless of orphan status (use after IDE switch)",
|
|
377
|
+
)
|
|
352
378
|
reap_p.add_argument("--json", action="store_true", help="Output structured JSON (agent-native)")
|
|
353
379
|
|
|
354
380
|
# V3.4.3: Ingestion adapters
|
|
@@ -445,6 +471,95 @@ def main() -> None:
|
|
|
445
471
|
help="Rotate the SLM install token (run `slm restart` afterwards)",
|
|
446
472
|
)
|
|
447
473
|
|
|
474
|
+
# ---- SLM v3.6 Optimize subcommands (additive, never modify above) ----
|
|
475
|
+
|
|
476
|
+
# slm optimize status|on|off|savings
|
|
477
|
+
opt_p = sub.add_parser("optimize", help="Optimize module control (cache + compress)")
|
|
478
|
+
opt_sub = opt_p.add_subparsers(dest="opt_command", title="optimize subcommands")
|
|
479
|
+
opt_sub.add_parser("status", help="Show Optimize status")
|
|
480
|
+
opt_sub.add_parser("on", help="Enable cache + compress")
|
|
481
|
+
opt_sub.add_parser("off", help="Disable cache + compress")
|
|
482
|
+
savings_p = opt_sub.add_parser("savings", help="Token/cost savings report")
|
|
483
|
+
savings_p.add_argument("--since", type=int, default=7, help="Days to look back (default 7)")
|
|
484
|
+
savings_p.add_argument("--provider", default=None,
|
|
485
|
+
choices=["anthropic", "openai", "gemini"],
|
|
486
|
+
help="Filter by provider")
|
|
487
|
+
for _sp in opt_sub.choices.values():
|
|
488
|
+
if not any(a.option_strings == ["--json"] for a in _sp._actions):
|
|
489
|
+
_sp.add_argument("--json", action="store_true",
|
|
490
|
+
help="Output structured JSON (agent-native)")
|
|
491
|
+
|
|
492
|
+
# slm cache status|clear|invalidate|ttl|semantic
|
|
493
|
+
cache_p = sub.add_parser("cache", help="Cache control (TTL, clear, invalidate, semantic)")
|
|
494
|
+
cache_sub = cache_p.add_subparsers(dest="cache_command", title="cache subcommands")
|
|
495
|
+
cache_sub.add_parser("status", help="Show cache state")
|
|
496
|
+
cache_sub.add_parser("clear", help="Delete all entries for tenant")
|
|
497
|
+
cache_inv_p = cache_sub.add_parser("invalidate", help="Delete entries by tag")
|
|
498
|
+
cache_inv_p.add_argument("--tag", required=True, help="Tag string to match")
|
|
499
|
+
cache_ttl_p = cache_sub.add_parser("ttl", help="Set cache TTL in seconds")
|
|
500
|
+
cache_ttl_p.add_argument("--set", dest="ttl_set", type=int, default=None,
|
|
501
|
+
help="Exact-cache TTL (seconds, >0)")
|
|
502
|
+
cache_ttl_p.add_argument("--semantic", dest="ttl_semantic", type=int, default=None,
|
|
503
|
+
help="Semantic-cache TTL (seconds, >0)")
|
|
504
|
+
cache_sem_p = cache_sub.add_parser("semantic", help="Enable/disable semantic cache")
|
|
505
|
+
cache_sem_p.add_argument("semantic_value", choices=["on", "off"], help="on or off")
|
|
506
|
+
for _sp in cache_sub.choices.values():
|
|
507
|
+
if not any(a.option_strings == ["--json"] for a in _sp._actions):
|
|
508
|
+
_sp.add_argument("--json", action="store_true",
|
|
509
|
+
help="Output structured JSON (agent-native)")
|
|
510
|
+
if not any(a.option_strings == ["--tenant"] for a in _sp._actions):
|
|
511
|
+
_sp.add_argument("--tenant", default="default", help="Tenant ID (default: 'default')")
|
|
512
|
+
|
|
513
|
+
# slm compress status|mode|code|prose|ccr
|
|
514
|
+
compress_p = sub.add_parser("compress", help="Compression control (mode, code, prose, CCR)")
|
|
515
|
+
comp_sub = compress_p.add_subparsers(dest="compress_command", title="compress subcommands")
|
|
516
|
+
comp_sub.add_parser("status", help="Show compression state")
|
|
517
|
+
comp_mode_p = comp_sub.add_parser("mode", help="Set compression mode")
|
|
518
|
+
comp_mode_p.add_argument("mode_value", choices=["safe", "aggressive"],
|
|
519
|
+
help="safe (default) or aggressive")
|
|
520
|
+
comp_code_p = comp_sub.add_parser("code", help="Enable/disable code compression")
|
|
521
|
+
comp_code_p.add_argument("code_value", choices=["on", "off"], help="on or off")
|
|
522
|
+
comp_prose_p = comp_sub.add_parser("prose", help="Enable/disable prose compression")
|
|
523
|
+
comp_prose_p.add_argument("prose_value", choices=["on", "off"], help="on or off")
|
|
524
|
+
comp_ccr_p = comp_sub.add_parser("ccr", help="Enable/disable CCR")
|
|
525
|
+
comp_ccr_p.add_argument("ccr_value", choices=["on", "off"], help="on or off")
|
|
526
|
+
comp_align_p = comp_sub.add_parser("align", help="Enable/disable alignment compression")
|
|
527
|
+
comp_align_p.add_argument("align_value", choices=["on", "off"], help="on or off")
|
|
528
|
+
for _sp in comp_sub.choices.values():
|
|
529
|
+
if not any(a.option_strings == ["--json"] for a in _sp._actions):
|
|
530
|
+
_sp.add_argument("--json", action="store_true",
|
|
531
|
+
help="Output structured JSON (agent-native)")
|
|
532
|
+
|
|
533
|
+
# slm proxy
|
|
534
|
+
proxy_p = sub.add_parser("proxy", help="Start SLM optimization proxy (Anthropic + OpenAI)")
|
|
535
|
+
proxy_p.add_argument("--port", type=int, default=8765, help="Port (default: 8765)")
|
|
536
|
+
proxy_p.add_argument("--provider", default="anthropic",
|
|
537
|
+
choices=["anthropic", "openai", "gemini"],
|
|
538
|
+
help="Target provider (default: anthropic)")
|
|
539
|
+
proxy_p.add_argument("--no-compress", action="store_true", dest="no_compress",
|
|
540
|
+
help="Disable compression for this session")
|
|
541
|
+
proxy_p.add_argument("--semantic", action="store_true",
|
|
542
|
+
help="Enable semantic cache for this session")
|
|
543
|
+
proxy_p.add_argument("--json", action="store_true",
|
|
544
|
+
help="Output structured JSON (agent-native)")
|
|
545
|
+
|
|
546
|
+
# slm help-optimize
|
|
547
|
+
help_opt_p = sub.add_parser(
|
|
548
|
+
"help-optimize",
|
|
549
|
+
help="Full Optimize reference: subcommands + agent recipes + safety notes",
|
|
550
|
+
)
|
|
551
|
+
help_opt_p.add_argument(
|
|
552
|
+
"topic", nargs="?", default=None,
|
|
553
|
+
choices=["cache", "compress", "optimize", "proxy", "agents", "safety"],
|
|
554
|
+
help="Topic to display (default: all)",
|
|
555
|
+
)
|
|
556
|
+
help_opt_p.add_argument(
|
|
557
|
+
"--no-pager", action="store_true", dest="no_pager", default=False,
|
|
558
|
+
help="Print to stdout instead of piping through a pager",
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
# ---- end SLM v3.6 Optimize subcommands ----
|
|
562
|
+
|
|
448
563
|
args = parser.parse_args()
|
|
449
564
|
|
|
450
565
|
if not args.command:
|
|
@@ -464,6 +579,13 @@ def main() -> None:
|
|
|
464
579
|
# v3.4.22 escape hatches — never auto-start the daemon on these.
|
|
465
580
|
"disable", "enable", "clear-cache", "reconfigure", "benchmark",
|
|
466
581
|
"rotate-token",
|
|
582
|
+
# LLD-06 — `slm wrap` may launch the agent binary directly without
|
|
583
|
+
# needing the daemon running (the agent will start the daemon on
|
|
584
|
+
# first LLM call, or the wrap command can be --dry-run).
|
|
585
|
+
"wrap",
|
|
586
|
+
# V3.6 Optimize commands that are config read/write only (no daemon needed)
|
|
587
|
+
"optimize", "cache", "compress", "help-optimize",
|
|
588
|
+
# NOTE: "proxy" NOT here — proxy needs daemon running
|
|
467
589
|
}
|
|
468
590
|
if args.command not in _NO_DAEMON_COMMANDS:
|
|
469
591
|
try:
|