superlocalmemory 3.6.13 → 3.6.14
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/.claude-plugin/marketplace.json +17 -0
- package/README.md +187 -741
- package/package.json +12 -5
- package/plugin/.claude-plugin/plugin.json +20 -0
- package/plugin/.mcp.json +12 -0
- package/plugin/CLAUDE.md +43 -0
- package/plugin/_GENERATED.md +6 -0
- package/plugin/agents/slm-memory-advisor.md +43 -0
- package/plugin/agents/slm-optimize-advisor.md +38 -0
- package/plugin/hooks/hooks.json +14 -0
- package/plugin/requirements.txt +1 -0
- package/plugin/scripts/ensure-venv.bat +122 -0
- package/plugin/scripts/ensure-venv.sh +105 -0
- package/plugin/scripts/slm-launch +15 -0
- package/plugin/scripts/slm-launch.bat +17 -0
- package/plugin/settings.json +16 -0
- package/plugin/skills/slm-cache/SKILL.md +140 -0
- package/plugin/skills/slm-compress/SKILL.md +143 -0
- package/plugin/skills/slm-graph/SKILL.md +300 -0
- package/plugin/skills/slm-recall/SKILL.md +196 -0
- package/plugin/skills/slm-remember/SKILL.md +182 -0
- package/plugin/skills/slm-session/SKILL.md +207 -0
- package/plugin/skills/slm-status/SKILL.md +149 -0
- package/plugin-src/.mcp.json +12 -0
- package/plugin-src/agents/slm-memory-advisor.md +43 -0
- package/plugin-src/agents/slm-optimize-advisor.md +38 -0
- package/plugin-src/commands/slm-optimize.md +22 -0
- package/plugin-src/commands/slm-recall.md +16 -0
- package/plugin-src/commands/slm-remember.md +16 -0
- package/plugin-src/commands/slm-status.md +15 -0
- package/plugin-src/hooks/.gitkeep +0 -0
- package/plugin-src/hooks/hooks.json +14 -0
- package/plugin-src/manifest.json +25 -0
- package/plugin-src/requirements.txt +1 -0
- package/plugin-src/rules/AGENTS.md +90 -0
- package/plugin-src/rules/CLAUDE.md.fragment +43 -0
- package/plugin-src/scripts/ensure-venv.bat +122 -0
- package/plugin-src/scripts/ensure-venv.sh +105 -0
- package/plugin-src/scripts/slm-launch +15 -0
- package/plugin-src/scripts/slm-launch.bat +17 -0
- package/plugin-src/settings.json +16 -0
- package/plugin-src/skills/slm-cache/SKILL.md +140 -0
- package/plugin-src/skills/slm-compress/SKILL.md +143 -0
- package/plugin-src/skills/slm-graph/SKILL.md +300 -0
- package/plugin-src/skills/slm-recall/SKILL.md +196 -0
- package/plugin-src/skills/slm-remember/SKILL.md +182 -0
- package/plugin-src/skills/slm-session/SKILL.md +207 -0
- package/plugin-src/skills/slm-status/SKILL.md +149 -0
- package/pyproject.toml +6 -2
- package/scripts/__tests__/build-plugin.test.mjs +613 -0
- package/scripts/_savings_math.py +270 -0
- package/scripts/build-plugin.js +742 -0
- package/scripts/dogfood_savings.py +490 -0
- package/scripts/install-skills.ps1 +4 -334
- package/scripts/install-skills.sh +4 -435
- package/scripts/postinstall-interactive.js +0 -27
- package/scripts/postinstall.js +21 -2
- package/src/superlocalmemory/__init__.py +1 -1
- package/src/superlocalmemory/cli/_lazy_init.py +115 -0
- package/src/superlocalmemory/cli/commands.py +348 -39
- package/src/superlocalmemory/cli/main.py +47 -4
- package/src/superlocalmemory/cli/setup_wizard.py +20 -6
- package/src/superlocalmemory/core/config.py +79 -9
- package/src/superlocalmemory/core/embeddings.py +10 -5
- package/src/superlocalmemory/core/engine.py +2 -2
- package/src/superlocalmemory/hooks/claude_code_hooks.py +27 -3
- package/src/superlocalmemory/hooks/portable_kit.py +506 -0
- package/src/superlocalmemory/infra/cloud_backup.py +99 -23
- package/src/superlocalmemory/mcp/cli_fallback.py +602 -0
- package/src/superlocalmemory/mcp/server.py +75 -4
- package/src/superlocalmemory/mcp/tools_code_graph.py +3 -3
- package/src/superlocalmemory/mcp/tools_core.py +12 -4
- package/src/superlocalmemory/optimize/cache/boundary_store.py +25 -6
- package/src/superlocalmemory/optimize/cache/centroid_store.py +27 -4
- package/src/superlocalmemory/optimize/cache/manager.py +92 -6
- package/src/superlocalmemory/optimize/cache/semantic.py +20 -1
- package/src/superlocalmemory/optimize/compress/ccr.py +12 -0
- package/src/superlocalmemory/optimize/compress/router.py +46 -13
- package/src/superlocalmemory/optimize/config/schema.py +6 -0
- package/src/superlocalmemory/optimize/proxy/_helpers.py +111 -8
- package/src/superlocalmemory/optimize/proxy/anthropic_surface.py +14 -4
- package/src/superlocalmemory/optimize/proxy/gemini_surface.py +23 -6
- package/src/superlocalmemory/optimize/proxy/openai_surface.py +10 -4
- package/src/superlocalmemory/optimize/proxy/server.py +11 -0
- package/src/superlocalmemory/optimize/proxy/vertex_surface.py +246 -0
- package/src/superlocalmemory/optimize/storage/db.py +30 -0
- package/src/superlocalmemory/server/recall_serializer.py +3 -1
- package/src/superlocalmemory/server/unified_daemon.py +24 -6
- package/src/superlocalmemory/ui/css/legacy-dashboard.css +18 -0
- package/src/superlocalmemory/ui/css/neural-glass.css +5 -0
- package/src/superlocalmemory/ui/index.html +2 -2
- package/src/superlocalmemory/ui/js/core.js +98 -0
- package/src/superlocalmemory/ui/js/dashboard.js +8 -1
- package/src/superlocalmemory/ui/js/ide-status.js +16 -3
- package/src/superlocalmemory/ui/js/math-health.js +15 -3
- package/src/superlocalmemory/ui/js/optimize.js +18 -2
- package/src/superlocalmemory/ui/js/trust-dashboard.js +10 -1
- package/src/superlocalmemory.egg-info/PKG-INFO +189 -742
- package/src/superlocalmemory.egg-info/SOURCES.txt +6 -9
- package/src/superlocalmemory.egg-info/requires.txt +1 -0
- package/ide/skills/slm-build-graph/SKILL.md +0 -423
- package/ide/skills/slm-list-recent/SKILL.md +0 -348
- package/ide/skills/slm-recall/SKILL.md +0 -326
- package/ide/skills/slm-remember/SKILL.md +0 -194
- package/ide/skills/slm-show-patterns/SKILL.md +0 -224
- package/ide/skills/slm-status/SKILL.md +0 -363
- package/ide/skills/slm-switch-profile/SKILL.md +0 -442
- package/skills/slm-build-graph/SKILL.md +0 -423
- package/skills/slm-list-recent/SKILL.md +0 -348
- package/skills/slm-optimize/README.md +0 -55
- package/skills/slm-optimize/SKILL.md +0 -139
- package/skills/slm-recall/SKILL.md +0 -343
- package/skills/slm-remember/SKILL.md +0 -194
- package/skills/slm-show-patterns/SKILL.md +0 -224
- package/skills/slm-status/SKILL.md +0 -363
- package/skills/slm-switch-profile/SKILL.md +0 -442
- package/src/superlocalmemory/cli/doctor_cmd.py +0 -152
- package/src/superlocalmemory/skills/slm-build-graph/SKILL.md +0 -423
- package/src/superlocalmemory/skills/slm-list-recent/SKILL.md +0 -348
- package/src/superlocalmemory/skills/slm-recall/SKILL.md +0 -343
- package/src/superlocalmemory/skills/slm-remember/SKILL.md +0 -194
- package/src/superlocalmemory/skills/slm-show-patterns/SKILL.md +0 -224
- package/src/superlocalmemory/skills/slm-status/SKILL.md +0 -363
- package/src/superlocalmemory/skills/slm-switch-profile/SKILL.md +0 -442
|
@@ -0,0 +1,270 @@
|
|
|
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
|
+
"""_savings_math.py — PURE math module for WP-14 metered-pipeline $-savings dogfood.
|
|
6
|
+
|
|
7
|
+
Zero I/O. No network. No side effects.
|
|
8
|
+
|
|
9
|
+
Prices used here come from R6 (2026-06-16):
|
|
10
|
+
Anthropic Sonnet 4.6: $3.00/MTok input, $15.00/MTok output — VERIFIED
|
|
11
|
+
Anthropic Opus 4.8: $5.00/MTok input, $25.00/MTok output — VERIFIED
|
|
12
|
+
Anthropic Haiku 4.5: $1.00/MTok input, $5.00/MTok output — VERIFIED
|
|
13
|
+
Cache-read multiplier: 0.10x (90% off cached portion) — VERIFIED
|
|
14
|
+
Source: https://platform.claude.com/docs/en/about-claude/pricing (fetched 2026-06-16)
|
|
15
|
+
|
|
16
|
+
OpenAI prices: UNVERIFIED-OFFICIAL (openai.com/api/pricing unreachable at fetch time;
|
|
17
|
+
data from aipricing.guru aggregator). Do not cite as authoritative.
|
|
18
|
+
|
|
19
|
+
CRIT notes (from LLD §9):
|
|
20
|
+
1. Blended input-price is a conservative lower-bound (output tokens cost more).
|
|
21
|
+
2. tokens_saved_compress is a word-count proxy (not a real tokenizer).
|
|
22
|
+
3. chars//4 is used in sim for input tokens (char-exact by construction).
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import dataclasses
|
|
28
|
+
import json
|
|
29
|
+
from datetime import datetime, timezone
|
|
30
|
+
from typing import Any
|
|
31
|
+
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
# Public types
|
|
34
|
+
# ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclasses.dataclass(frozen=False)
|
|
38
|
+
class MetricsDelta:
|
|
39
|
+
"""Difference between two MetricsSnapshots, clamped to ≥0.
|
|
40
|
+
|
|
41
|
+
Negative values indicate the DB was reset or shared between runs —
|
|
42
|
+
clamped to 0 (conservative, never overclaim).
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
hits: int
|
|
46
|
+
misses: int
|
|
47
|
+
tokens_saved_input: int
|
|
48
|
+
tokens_saved_output: int
|
|
49
|
+
tokens_saved_compress: int
|
|
50
|
+
|
|
51
|
+
def __post_init__(self) -> None:
|
|
52
|
+
# Clamp all fields to ≥ 0 (immutable-safe: assign back to self)
|
|
53
|
+
self.hits = max(0, self.hits)
|
|
54
|
+
self.misses = max(0, self.misses)
|
|
55
|
+
self.tokens_saved_input = max(0, self.tokens_saved_input)
|
|
56
|
+
self.tokens_saved_output = max(0, self.tokens_saved_output)
|
|
57
|
+
self.tokens_saved_compress = max(0, self.tokens_saved_compress)
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def total_tokens_saved(self) -> int:
|
|
61
|
+
return (
|
|
62
|
+
self.tokens_saved_input
|
|
63
|
+
+ self.tokens_saved_output
|
|
64
|
+
+ self.tokens_saved_compress
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def hit_rate(self) -> float:
|
|
69
|
+
total = self.hits + self.misses
|
|
70
|
+
return self.hits / total if total > 0 else 0.0
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclasses.dataclass(frozen=True)
|
|
74
|
+
class Report:
|
|
75
|
+
"""Immutable dogfood savings report."""
|
|
76
|
+
|
|
77
|
+
mode: str # "sim" or "live"
|
|
78
|
+
provider: str
|
|
79
|
+
prompts: int
|
|
80
|
+
repeat: int
|
|
81
|
+
delta: MetricsDelta
|
|
82
|
+
price_per_1m: float
|
|
83
|
+
price_source: str # e.g. "DEFAULT_COST_PER_MILLION_INPUT_TOKENS[anthropic], 2026-06-07 (R6)"
|
|
84
|
+
savings_usd: float
|
|
85
|
+
savings_inr: float
|
|
86
|
+
inr_rate: float
|
|
87
|
+
|
|
88
|
+
def to_json(self) -> str:
|
|
89
|
+
"""Serialize to raw JSON ledger format per LLD §5."""
|
|
90
|
+
d = self.delta
|
|
91
|
+
formula_str = (
|
|
92
|
+
f"({d.tokens_saved_input} + {d.tokens_saved_output} + "
|
|
93
|
+
f"{d.tokens_saved_compress}) / 1_000_000 * {self.price_per_1m}"
|
|
94
|
+
)
|
|
95
|
+
payload: dict[str, Any] = {
|
|
96
|
+
"mode": self.mode,
|
|
97
|
+
"provider": self.provider,
|
|
98
|
+
"workload": {
|
|
99
|
+
"prompts": self.prompts,
|
|
100
|
+
"repeat": self.repeat,
|
|
101
|
+
"total_requests": self.prompts * (1 + self.repeat),
|
|
102
|
+
},
|
|
103
|
+
"delta": {
|
|
104
|
+
"hits": d.hits,
|
|
105
|
+
"misses": d.misses,
|
|
106
|
+
"tokens_saved_input": d.tokens_saved_input,
|
|
107
|
+
"tokens_saved_output": d.tokens_saved_output,
|
|
108
|
+
"tokens_saved_compress": d.tokens_saved_compress,
|
|
109
|
+
"total_tokens_saved": d.total_tokens_saved,
|
|
110
|
+
"hit_rate": round(d.hit_rate, 4),
|
|
111
|
+
},
|
|
112
|
+
"pricing": {
|
|
113
|
+
"price_per_1m": self.price_per_1m,
|
|
114
|
+
"source": self.price_source,
|
|
115
|
+
"date": "2026-06-07",
|
|
116
|
+
"inr_rate": self.inr_rate,
|
|
117
|
+
},
|
|
118
|
+
"computed": {
|
|
119
|
+
"savings_usd": round(self.savings_usd, 6),
|
|
120
|
+
"savings_inr": round(self.savings_inr, 4),
|
|
121
|
+
"formula": formula_str,
|
|
122
|
+
},
|
|
123
|
+
"generated_at": datetime.now(timezone.utc).isoformat(),
|
|
124
|
+
}
|
|
125
|
+
return json.dumps(payload, indent=2)
|
|
126
|
+
|
|
127
|
+
def to_markdown(self) -> str:
|
|
128
|
+
"""Render human-readable Markdown report per LLD §5 / AC3."""
|
|
129
|
+
d = self.delta
|
|
130
|
+
mode_label = "simulated" if self.mode == "sim" else "live"
|
|
131
|
+
openai_note = (
|
|
132
|
+
"\n> **UNVERIFIED-OFFICIAL:** OpenAI prices sourced from third-party "
|
|
133
|
+
"aggregator (openai.com/api/pricing was unreachable). Do not cite externally."
|
|
134
|
+
if self.provider.lower() == "openai"
|
|
135
|
+
else ""
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
return f"""# SLM Optimize — Metered-Pipeline $-Savings Report ({mode_label.upper()})
|
|
139
|
+
|
|
140
|
+
> Generated by `scripts/dogfood_savings.py` — WP-14 evidence artifact.
|
|
141
|
+
> Mode: **{self.mode}** | Provider: **{self.provider}** | Date: {datetime.now(timezone.utc).strftime("%Y-%m-%d")}
|
|
142
|
+
|
|
143
|
+
## Workload
|
|
144
|
+
|
|
145
|
+
| Parameter | Value |
|
|
146
|
+
|-----------|-------|
|
|
147
|
+
| Prompts (N) | {self.prompts} |
|
|
148
|
+
| Repeats per prompt (R) | {self.repeat} |
|
|
149
|
+
| Total requests | {self.prompts * (1 + self.repeat)} |
|
|
150
|
+
| Hit rate | {d.hit_rate * 100:.1f}% (R/{1 + self.repeat} = {self.repeat}/{1 + self.repeat}) |
|
|
151
|
+
|
|
152
|
+
## Token Savings (delta from MetricsSnapshot)
|
|
153
|
+
|
|
154
|
+
| Component | Tokens saved |
|
|
155
|
+
|-----------|-------------|
|
|
156
|
+
| Input (cache hit) | {d.tokens_saved_input:,} |
|
|
157
|
+
| Output (cache hit) | {d.tokens_saved_output:,} |
|
|
158
|
+
| Compress (word-count proxy) | {d.tokens_saved_compress:,} |
|
|
159
|
+
| **Total** | **{d.total_tokens_saved:,}** |
|
|
160
|
+
|
|
161
|
+
> Tokens saved: produced by SHIPPED CacheManager.on_hit recovery (M-01/M-02), not invented.
|
|
162
|
+
|
|
163
|
+
## Pricing
|
|
164
|
+
|
|
165
|
+
| Field | Value |
|
|
166
|
+
|-------|-------|
|
|
167
|
+
| Price per 1M tokens | ${self.price_per_1m:.2f} (blended input, conservative lower bound) |
|
|
168
|
+
| Source | {self.price_source} |
|
|
169
|
+
| USD→INR rate | {self.inr_rate} |
|
|
170
|
+
{openai_note}
|
|
171
|
+
|
|
172
|
+
## Computed Savings
|
|
173
|
+
|
|
174
|
+
**Formula:** `total_tokens_saved / 1_000_000 × price_per_1m`
|
|
175
|
+
|
|
176
|
+
| Metric | Value |
|
|
177
|
+
|--------|-------|
|
|
178
|
+
| Savings (USD) | **${self.savings_usd:.4f}** |
|
|
179
|
+
| Savings (INR) | **₹{self.savings_inr:.2f}** |
|
|
180
|
+
|
|
181
|
+
> Formula: `({d.tokens_saved_input} + {d.tokens_saved_output} + {d.tokens_saved_compress}) / 1_000_000 × {self.price_per_1m} = {self.savings_usd:.6f} USD`
|
|
182
|
+
|
|
183
|
+
## Methodology & Caveats
|
|
184
|
+
|
|
185
|
+
1. **Simulated workload (this run):** synthetic prompts with char-exact content (chars//4 = exact tokens by construction). No API key or network required.
|
|
186
|
+
2. **Savings metric is REAL:** tokens_saved_* fields come from `CacheManager.on_hit` M-01/M-02 recovery in the SHIPPED proxy — not a hardcoded percentage.
|
|
187
|
+
3. **Blended input price is a conservative lower bound:** output tokens cost 5× more ($15/MTok for Sonnet 4.6 output vs $3 input). This report uses input price only → understates true savings.
|
|
188
|
+
4. **Hit rate is workload-dependent:** this run simulates R={self.repeat} repeats per prompt → hit_rate = R/(1+R) = {self.repeat/(1+self.repeat)*100:.1f}%. Real workloads vary.
|
|
189
|
+
5. **tokens_saved_compress is a word-count proxy** (not a real tokenizer). Field name "bytes" in DB schema is legacy; unit is word count.
|
|
190
|
+
6. **Pricing is a point-in-time snapshot** (source date: 2026-06-07). Verify before citing externally.
|
|
191
|
+
7. **Local/self-hosted usage = $0 savings** (no metered API). Savings apply only to metered API workloads (Anthropic, OpenAI, Gemini).
|
|
192
|
+
8. **Live mode:** `--mode live --i-will-spend-money + SLM_DOGFOOD_API_KEY` routes real HTTP through proxy at 127.0.0.1:8765. ~$0.05–$0.20 per run. Never run in CI.
|
|
193
|
+
|
|
194
|
+
## Verification
|
|
195
|
+
|
|
196
|
+
Cross-check with: `slm optimize savings --provider {self.provider} --json`
|
|
197
|
+
(same formula as `optimize_cmd.py:145-155`; numbers should agree within rounding).
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
# ---------------------------------------------------------------------------
|
|
202
|
+
# Pure functions
|
|
203
|
+
# ---------------------------------------------------------------------------
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def compute_savings_usd(
|
|
207
|
+
tokens_saved_input: int,
|
|
208
|
+
tokens_saved_output: int,
|
|
209
|
+
tokens_saved_compress: int,
|
|
210
|
+
*,
|
|
211
|
+
price_per_1m: float,
|
|
212
|
+
) -> float:
|
|
213
|
+
"""Compute estimated USD savings from token counts.
|
|
214
|
+
|
|
215
|
+
Contract-twin of optimize_cmd.py:145-155:
|
|
216
|
+
tokens_saved = ti + to + tc
|
|
217
|
+
estimated_savings_usd = tokens_saved / 1_000_000 * rate
|
|
218
|
+
|
|
219
|
+
CRIT-1: This uses the blended input price — a conservative lower bound.
|
|
220
|
+
Output tokens cost 5× more than input (e.g. $15 vs $3/MTok for Sonnet 4.6).
|
|
221
|
+
"""
|
|
222
|
+
total = tokens_saved_input + tokens_saved_output + tokens_saved_compress
|
|
223
|
+
return total / 1_000_000 * price_per_1m
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def resolve_price(
|
|
227
|
+
provider: str,
|
|
228
|
+
default_table: dict[str, float],
|
|
229
|
+
overrides: dict[str, Any],
|
|
230
|
+
) -> tuple[float, str]:
|
|
231
|
+
"""Resolve price per 1M tokens for a provider.
|
|
232
|
+
|
|
233
|
+
Priority: overrides[provider].input_per_1m_usd > default_table[provider] > default_table["default"].
|
|
234
|
+
|
|
235
|
+
Returns (price, source_description).
|
|
236
|
+
"""
|
|
237
|
+
# 1. Check config overrides
|
|
238
|
+
if provider in overrides:
|
|
239
|
+
override_val = overrides[provider]
|
|
240
|
+
if isinstance(override_val, dict) and "input_per_1m_usd" in override_val:
|
|
241
|
+
price = float(override_val["input_per_1m_usd"])
|
|
242
|
+
return price, f"pricing_override[{provider}].input_per_1m_usd"
|
|
243
|
+
|
|
244
|
+
# 2. Default table lookup
|
|
245
|
+
if provider in default_table:
|
|
246
|
+
price = float(default_table[provider])
|
|
247
|
+
return price, f"default_table[{provider}]"
|
|
248
|
+
|
|
249
|
+
# 3. Fallback to "default"
|
|
250
|
+
price = float(default_table.get("default", 3.00))
|
|
251
|
+
return price, "default_table[default]"
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def to_inr(usd: float, rate: float) -> float:
|
|
255
|
+
"""Convert USD to INR at the given exchange rate."""
|
|
256
|
+
return usd * rate
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def metrics_delta(before: Any, after: Any) -> MetricsDelta:
|
|
260
|
+
"""Compute the delta between two MetricsSnapshots, clamping to ≥0.
|
|
261
|
+
|
|
262
|
+
Uses attribute access (compatible with MetricsSnapshot dataclass).
|
|
263
|
+
"""
|
|
264
|
+
return MetricsDelta(
|
|
265
|
+
hits=after.hits - before.hits,
|
|
266
|
+
misses=after.misses - before.misses,
|
|
267
|
+
tokens_saved_input=after.tokens_saved_input - before.tokens_saved_input,
|
|
268
|
+
tokens_saved_output=after.tokens_saved_output - before.tokens_saved_output,
|
|
269
|
+
tokens_saved_compress=after.tokens_saved_compress - before.tokens_saved_compress,
|
|
270
|
+
)
|