xithead 1.1.0__py3-none-any.whl
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.
- xithead/__init__.py +32 -0
- xithead/_version.py +1 -0
- xithead/cache.py +260 -0
- xithead/claude_optimizer.py +498 -0
- xithead/cli/__init__.py +0 -0
- xithead/cli/main.py +486 -0
- xithead/dashboard/__init__.py +7 -0
- xithead/proxy.py +236 -0
- xithead-1.1.0.dist-info/METADATA +262 -0
- xithead-1.1.0.dist-info/RECORD +14 -0
- xithead-1.1.0.dist-info/WHEEL +5 -0
- xithead-1.1.0.dist-info/entry_points.txt +2 -0
- xithead-1.1.0.dist-info/licenses/LICENSE +190 -0
- xithead-1.1.0.dist-info/top_level.txt +1 -0
xithead/__init__.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""xithead - Claude Context Optimizer by Xtream-ITSolutions UG."""
|
|
2
|
+
|
|
3
|
+
from xithead._version import __version__
|
|
4
|
+
from xithead.claude_optimizer import ClaudeOptimizer, ClaudeOptimizerConfig, optimize_for_claude
|
|
5
|
+
from xithead.cache import XitheadCache
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"__version__",
|
|
9
|
+
"ClaudeOptimizer",
|
|
10
|
+
"ClaudeOptimizerConfig",
|
|
11
|
+
"optimize_for_claude",
|
|
12
|
+
"XitheadCache",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
# Lazy import headroom symbols if available
|
|
16
|
+
try:
|
|
17
|
+
from headroom import compress, HeadroomClient
|
|
18
|
+
__all__ += ["compress", "HeadroomClient"]
|
|
19
|
+
except ImportError:
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def start_proxy(host: str = "127.0.0.1", port: int = 8787, model: str = "claude-sonnet-4-6") -> None:
|
|
24
|
+
"""Start the xithead optimization proxy. Convenience function."""
|
|
25
|
+
import os
|
|
26
|
+
os.environ.setdefault("XITHEAD_MODEL", model)
|
|
27
|
+
from xithead.proxy import create_app
|
|
28
|
+
import uvicorn
|
|
29
|
+
app = create_app()
|
|
30
|
+
print(f"[xithead] Proxy on http://{host}:{port} | Model: {model}")
|
|
31
|
+
print(f"[xithead] Dashboard: http://{host}:{port}/dashboard")
|
|
32
|
+
uvicorn.run(app, host=host, port=port)
|
xithead/_version.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.1.0"
|
xithead/cache.py
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""XitheadCache - Multi-layer caching for maximum Claude token savings."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import sqlite3
|
|
8
|
+
import time
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
_SCHEMA = """
|
|
14
|
+
CREATE TABLE IF NOT EXISTS cache (
|
|
15
|
+
key TEXT PRIMARY KEY,
|
|
16
|
+
value TEXT NOT NULL,
|
|
17
|
+
created_at REAL NOT NULL,
|
|
18
|
+
last_hit REAL,
|
|
19
|
+
hits INTEGER DEFAULT 0,
|
|
20
|
+
ttl REAL NOT NULL DEFAULT 604800
|
|
21
|
+
);
|
|
22
|
+
CREATE INDEX IF NOT EXISTS idx_created ON cache(created_at);
|
|
23
|
+
CREATE INDEX IF NOT EXISTS idx_last_hit ON cache(last_hit);
|
|
24
|
+
|
|
25
|
+
CREATE TABLE IF NOT EXISTS stats (
|
|
26
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
27
|
+
total_sets INTEGER DEFAULT 0,
|
|
28
|
+
total_gets INTEGER DEFAULT 0,
|
|
29
|
+
total_hits INTEGER DEFAULT 0,
|
|
30
|
+
total_misses INTEGER DEFAULT 0,
|
|
31
|
+
tokens_saved INTEGER DEFAULT 0,
|
|
32
|
+
cost_saved_usd REAL DEFAULT 0.0
|
|
33
|
+
);
|
|
34
|
+
INSERT OR IGNORE INTO stats (id) VALUES (1);
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class XitheadCache:
|
|
39
|
+
"""Multi-layer cache: memory → SQLite.
|
|
40
|
+
|
|
41
|
+
Layer 1: In-memory LRU (instant, survives within process)
|
|
42
|
+
Layer 2: SQLite (persistent across restarts, ~100µs per lookup)
|
|
43
|
+
|
|
44
|
+
Features:
|
|
45
|
+
- Per-entry TTL (frequently-hit entries get longer TTL automatically)
|
|
46
|
+
- Hit-rate tracking across restarts
|
|
47
|
+
- LRU eviction in memory layer
|
|
48
|
+
- Automatic DB vacuum when size exceeds limit
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
DEFAULT_TTL = 86400 * 7 # 7 days baseline
|
|
52
|
+
HOT_TTL = 86400 * 30 # 30 days for frequently-hit entries (≥5 hits)
|
|
53
|
+
WARM_TTL = 86400 * 14 # 14 days for entries with ≥2 hits
|
|
54
|
+
|
|
55
|
+
def __init__(
|
|
56
|
+
self,
|
|
57
|
+
cache_dir: Path | None = None,
|
|
58
|
+
max_memory_entries: int = 2000,
|
|
59
|
+
ttl_seconds: int | None = None,
|
|
60
|
+
max_db_size_mb: int = 500,
|
|
61
|
+
) -> None:
|
|
62
|
+
self._dir = cache_dir or (Path.home() / ".xithead" / "cache")
|
|
63
|
+
self._dir.mkdir(parents=True, exist_ok=True)
|
|
64
|
+
self._db_path = self._dir / "xithead_cache.db"
|
|
65
|
+
self._memory: dict[str, tuple[float, float, Any]] = {} # key → (ts, expiry, value)
|
|
66
|
+
self._access_order: list[str] = [] # LRU tracking
|
|
67
|
+
self._max_memory = max_memory_entries
|
|
68
|
+
self._default_ttl = ttl_seconds or self.DEFAULT_TTL
|
|
69
|
+
self._max_db_mb = max_db_size_mb
|
|
70
|
+
self._init_db()
|
|
71
|
+
self._maybe_vacuum()
|
|
72
|
+
|
|
73
|
+
def _init_db(self) -> None:
|
|
74
|
+
with sqlite3.connect(self._db_path) as conn:
|
|
75
|
+
conn.executescript(_SCHEMA)
|
|
76
|
+
conn.commit()
|
|
77
|
+
|
|
78
|
+
def _maybe_vacuum(self) -> None:
|
|
79
|
+
"""Clean up expired entries and vacuum if DB is too large."""
|
|
80
|
+
try:
|
|
81
|
+
with sqlite3.connect(self._db_path) as conn:
|
|
82
|
+
now = time.time()
|
|
83
|
+
# Delete expired entries
|
|
84
|
+
conn.execute("DELETE FROM cache WHERE created_at + ttl < ?", (now,))
|
|
85
|
+
conn.commit()
|
|
86
|
+
size_mb = self._db_path.stat().st_size / 1024 / 1024
|
|
87
|
+
if size_mb > self._max_db_mb:
|
|
88
|
+
# Delete least recently hit entries until under limit
|
|
89
|
+
conn.execute(
|
|
90
|
+
"DELETE FROM cache WHERE key IN ("
|
|
91
|
+
" SELECT key FROM cache ORDER BY COALESCE(last_hit, created_at) ASC"
|
|
92
|
+
" LIMIT (SELECT COUNT(*)/4 FROM cache)"
|
|
93
|
+
")"
|
|
94
|
+
)
|
|
95
|
+
conn.execute("VACUUM")
|
|
96
|
+
conn.commit()
|
|
97
|
+
except Exception:
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
# ------------------------------------------------------------------
|
|
101
|
+
# Key generation
|
|
102
|
+
# ------------------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
def key(self, *parts: Any) -> str:
|
|
105
|
+
payload = json.dumps(parts, sort_keys=True, default=str)
|
|
106
|
+
return hashlib.sha256(payload.encode()).hexdigest()
|
|
107
|
+
|
|
108
|
+
# ------------------------------------------------------------------
|
|
109
|
+
# Get / Set
|
|
110
|
+
# ------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
def get(self, key: str) -> Any | None:
|
|
113
|
+
now = time.time()
|
|
114
|
+
|
|
115
|
+
# L1: memory
|
|
116
|
+
if key in self._memory:
|
|
117
|
+
ts, expiry, val = self._memory[key]
|
|
118
|
+
if now < expiry:
|
|
119
|
+
self._lru_touch(key)
|
|
120
|
+
return val
|
|
121
|
+
del self._memory[key]
|
|
122
|
+
|
|
123
|
+
# L2: SQLite
|
|
124
|
+
try:
|
|
125
|
+
with sqlite3.connect(self._db_path) as conn:
|
|
126
|
+
row = conn.execute(
|
|
127
|
+
"SELECT value, created_at, ttl, hits FROM cache WHERE key = ?", (key,)
|
|
128
|
+
).fetchone()
|
|
129
|
+
if row:
|
|
130
|
+
value_str, created_at, ttl, hits = row
|
|
131
|
+
if now - created_at < ttl:
|
|
132
|
+
# Promote TTL for hot entries
|
|
133
|
+
new_ttl = self._promote_ttl(hits + 1)
|
|
134
|
+
conn.execute(
|
|
135
|
+
"UPDATE cache SET hits = hits + 1, last_hit = ?, ttl = ? WHERE key = ?",
|
|
136
|
+
(now, new_ttl, key),
|
|
137
|
+
)
|
|
138
|
+
conn.execute(
|
|
139
|
+
"UPDATE stats SET total_gets = total_gets + 1, total_hits = total_hits + 1"
|
|
140
|
+
)
|
|
141
|
+
conn.commit()
|
|
142
|
+
val = json.loads(value_str)
|
|
143
|
+
self._lru_set(key, created_at, created_at + new_ttl, val)
|
|
144
|
+
return val
|
|
145
|
+
# Expired
|
|
146
|
+
conn.execute("DELETE FROM cache WHERE key = ?", (key,))
|
|
147
|
+
conn.execute("UPDATE stats SET total_misses = total_misses + 1")
|
|
148
|
+
conn.commit()
|
|
149
|
+
except Exception:
|
|
150
|
+
pass
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
with sqlite3.connect(self._db_path) as conn:
|
|
154
|
+
conn.execute("UPDATE stats SET total_gets = total_gets + 1, total_misses = total_misses + 1")
|
|
155
|
+
conn.commit()
|
|
156
|
+
except Exception:
|
|
157
|
+
pass
|
|
158
|
+
return None
|
|
159
|
+
|
|
160
|
+
def set(self, key: str, value: Any, ttl: int | None = None) -> None:
|
|
161
|
+
now = time.time()
|
|
162
|
+
effective_ttl = ttl or self._default_ttl
|
|
163
|
+
expiry = now + effective_ttl
|
|
164
|
+
|
|
165
|
+
# L1
|
|
166
|
+
self._lru_set(key, now, expiry, value)
|
|
167
|
+
|
|
168
|
+
# L2
|
|
169
|
+
try:
|
|
170
|
+
with sqlite3.connect(self._db_path) as conn:
|
|
171
|
+
conn.execute(
|
|
172
|
+
"INSERT OR REPLACE INTO cache (key, value, created_at, ttl) VALUES (?, ?, ?, ?)",
|
|
173
|
+
(key, json.dumps(value, default=str), now, effective_ttl),
|
|
174
|
+
)
|
|
175
|
+
conn.execute("UPDATE stats SET total_sets = total_sets + 1")
|
|
176
|
+
conn.commit()
|
|
177
|
+
except Exception:
|
|
178
|
+
pass
|
|
179
|
+
|
|
180
|
+
def record_savings(self, tokens_saved: int, cost_usd: float) -> None:
|
|
181
|
+
"""Track cumulative savings across restarts."""
|
|
182
|
+
try:
|
|
183
|
+
with sqlite3.connect(self._db_path) as conn:
|
|
184
|
+
conn.execute(
|
|
185
|
+
"UPDATE stats SET tokens_saved = tokens_saved + ?, cost_saved_usd = cost_saved_usd + ?",
|
|
186
|
+
(tokens_saved, cost_usd),
|
|
187
|
+
)
|
|
188
|
+
conn.commit()
|
|
189
|
+
except Exception:
|
|
190
|
+
pass
|
|
191
|
+
|
|
192
|
+
# ------------------------------------------------------------------
|
|
193
|
+
# Stats
|
|
194
|
+
# ------------------------------------------------------------------
|
|
195
|
+
|
|
196
|
+
def stats(self) -> dict[str, Any]:
|
|
197
|
+
try:
|
|
198
|
+
with sqlite3.connect(self._db_path) as conn:
|
|
199
|
+
total = conn.execute("SELECT COUNT(*) FROM cache").fetchone()[0]
|
|
200
|
+
hot = conn.execute("SELECT COUNT(*) FROM cache WHERE hits >= 5").fetchone()[0]
|
|
201
|
+
st = conn.execute(
|
|
202
|
+
"SELECT total_hits, total_misses, total_sets, tokens_saved, cost_saved_usd FROM stats WHERE id=1"
|
|
203
|
+
).fetchone()
|
|
204
|
+
hits, misses, sets, tok_saved, cost_saved = st or (0, 0, 0, 0, 0.0)
|
|
205
|
+
size_bytes = self._db_path.stat().st_size if self._db_path.exists() else 0
|
|
206
|
+
total_lookups = (hits or 0) + (misses or 0)
|
|
207
|
+
return {
|
|
208
|
+
"total_entries": total,
|
|
209
|
+
"hot_entries": hot, # ≥5 hits each
|
|
210
|
+
"memory_entries": len(self._memory),
|
|
211
|
+
"total_hits": hits or 0,
|
|
212
|
+
"total_misses": misses or 0,
|
|
213
|
+
"hit_rate": round((hits or 0) / total_lookups, 4) if total_lookups else 0.0,
|
|
214
|
+
"tokens_saved": tok_saved or 0,
|
|
215
|
+
"cost_saved_usd": round(cost_saved or 0.0, 4),
|
|
216
|
+
"db_size_mb": round(size_bytes / 1024 / 1024, 2),
|
|
217
|
+
}
|
|
218
|
+
except Exception:
|
|
219
|
+
return {}
|
|
220
|
+
|
|
221
|
+
def clear(self) -> None:
|
|
222
|
+
self._memory.clear()
|
|
223
|
+
self._access_order.clear()
|
|
224
|
+
try:
|
|
225
|
+
with sqlite3.connect(self._db_path) as conn:
|
|
226
|
+
conn.execute("DELETE FROM cache")
|
|
227
|
+
conn.execute("UPDATE stats SET total_hits=0, total_misses=0, total_sets=0")
|
|
228
|
+
conn.commit()
|
|
229
|
+
except Exception:
|
|
230
|
+
pass
|
|
231
|
+
|
|
232
|
+
# ------------------------------------------------------------------
|
|
233
|
+
# LRU helpers
|
|
234
|
+
# ------------------------------------------------------------------
|
|
235
|
+
|
|
236
|
+
def _lru_touch(self, key: str) -> None:
|
|
237
|
+
try:
|
|
238
|
+
self._access_order.remove(key)
|
|
239
|
+
except ValueError:
|
|
240
|
+
pass
|
|
241
|
+
self._access_order.append(key)
|
|
242
|
+
|
|
243
|
+
def _lru_set(self, key: str, ts: float, expiry: float, value: Any) -> None:
|
|
244
|
+
if key in self._memory:
|
|
245
|
+
self._lru_touch(key)
|
|
246
|
+
else:
|
|
247
|
+
if len(self._memory) >= self._max_memory:
|
|
248
|
+
# Evict least-recently-used
|
|
249
|
+
evict = self._access_order.pop(0)
|
|
250
|
+
self._memory.pop(evict, None)
|
|
251
|
+
self._access_order.append(key)
|
|
252
|
+
self._memory[key] = (ts, expiry, value)
|
|
253
|
+
|
|
254
|
+
@staticmethod
|
|
255
|
+
def _promote_ttl(hits: int) -> float:
|
|
256
|
+
if hits >= 5:
|
|
257
|
+
return XitheadCache.HOT_TTL
|
|
258
|
+
if hits >= 2:
|
|
259
|
+
return XitheadCache.WARM_TTL
|
|
260
|
+
return XitheadCache.DEFAULT_TTL
|