slash-tokens 1.1.0 → 1.1.2
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/README.md +3 -4
- package/dist/auto.js +49 -18
- package/dist/cli.js +26 -75
- package/dist/config.d.ts +7 -1
- package/dist/config.js +22 -1
- package/dist/intercept.d.ts +4 -0
- package/dist/intercept.js +71 -14
- package/dist/preflight.d.ts +2 -2
- package/dist/preflight.js +3 -3
- package/dist/report.js +9 -9
- package/dist/transact.d.ts +1 -1
- package/package.json +19 -4
package/README.md
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# /slash-tokens
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
**Fly First. Pay Economy.**
|
|
3
|
+
Token Optimization for Context Engineers.
|
|
4
|
+
For anyone building with LLMs. 4.8 KB WASM. Sub-millisecond. Zero dependencies.
|
|
6
5
|
|
|
7
6
|
```bash
|
|
8
7
|
npm install slash-tokens
|
|
@@ -15,7 +14,7 @@ const check = preflight(prompt, 'claude-opus')
|
|
|
15
14
|
// tokens: 47,000 | cost: $0.71 | 11 cheaper options | save 99%
|
|
16
15
|
```
|
|
17
16
|
|
|
18
|
-
Or one line — every API call pre-
|
|
17
|
+
Or one line — every API call checked pre-call:
|
|
19
18
|
|
|
20
19
|
```js
|
|
21
20
|
import 'slash-tokens/auto'
|
package/dist/auto.js
CHANGED
|
@@ -1,30 +1,61 @@
|
|
|
1
1
|
import { patchFetch, onIntercept } from './intercept.js';
|
|
2
2
|
import { report } from './transact.js';
|
|
3
|
-
import { hasKey } from './config.js';
|
|
4
|
-
// Auto mode: import this file and every LLM call is
|
|
3
|
+
import { hasKey, isQuiet } from './config.js';
|
|
4
|
+
// Auto mode: import this file and every LLM call is optimized.
|
|
5
5
|
// Usage: import 'slash-tokens/auto'
|
|
6
6
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
7
|
+
// Silent by default. Routes to cheapest model within same provider.
|
|
8
|
+
// Tracks cumulative savings. Nudges at thresholds.
|
|
9
9
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
|
|
10
|
+
// No key: routing still works (free forever), no dashboard tracking
|
|
11
|
+
// With key (init or SLASH_KEY): savings reported to MCPaaS
|
|
12
|
+
// Session state
|
|
13
|
+
let totalSalvaged = 0;
|
|
14
|
+
let totalCalls = 0;
|
|
15
|
+
let totalRouted = 0;
|
|
16
|
+
let nextNudge = 5; // First nudge at $5
|
|
17
|
+
// ANSI
|
|
18
|
+
const R = '\x1b[0m';
|
|
19
|
+
const B = '\x1b[1m';
|
|
20
|
+
const GREEN = '\x1b[38;2;74;222;128m';
|
|
21
|
+
const GOLD = '\x1b[38;2;230;161;65m';
|
|
14
22
|
patchFetch();
|
|
15
23
|
onIntercept((event) => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
24
|
+
totalCalls++;
|
|
25
|
+
totalSalvaged += event.salvaged;
|
|
26
|
+
if (event.routed) {
|
|
27
|
+
totalRouted++;
|
|
28
|
+
}
|
|
29
|
+
// Verbose mode (default) — log every call
|
|
30
|
+
if (!isQuiet()) {
|
|
31
|
+
const route = event.routed
|
|
32
|
+
? `${GOLD}${event.originalModel} → ${event.model}${R}`
|
|
33
|
+
: `${event.model}`;
|
|
34
|
+
const salvageTag = event.salvaged > 0 ? ` ${GREEN}saved $${event.salvaged.toFixed(4)}${R}` : '';
|
|
35
|
+
console.log(`${GREEN}[slash]${R} ${event.provider} ${route} | ${event.tokens.toLocaleString()} tok | $${event.cost.toFixed(4)}${salvageTag}`);
|
|
36
|
+
}
|
|
37
|
+
// Nudge at thresholds (always, even in quiet mode)
|
|
38
|
+
if (totalSalvaged >= nextNudge) {
|
|
39
|
+
console.log(`${GREEN}${B} ⚡ $${Math.floor(totalSalvaged)} salvaged${R}${GOLD} — keep building${R}`);
|
|
40
|
+
if (nextNudge < 10)
|
|
41
|
+
nextNudge = 10;
|
|
42
|
+
else if (nextNudge < 25)
|
|
43
|
+
nextNudge = 25;
|
|
44
|
+
else if (nextNudge < 50)
|
|
45
|
+
nextNudge = 50;
|
|
46
|
+
else if (nextNudge < 100)
|
|
47
|
+
nextNudge = 100;
|
|
48
|
+
else
|
|
49
|
+
nextNudge += 100;
|
|
50
|
+
}
|
|
51
|
+
// Report real savings to MCPaaS (non-blocking)
|
|
52
|
+
if (hasKey() && event.salvaged > 0) {
|
|
20
53
|
report({
|
|
21
54
|
tokens_estimated: event.tokens,
|
|
22
|
-
tokens_saved:
|
|
23
|
-
model: event.
|
|
55
|
+
tokens_saved: event.tokens,
|
|
56
|
+
model: event.originalModel,
|
|
24
57
|
action: 'routed',
|
|
25
|
-
cost_saved_usd:
|
|
26
|
-
}).catch(() => {
|
|
27
|
-
// Silent — never break the app for billing failures
|
|
28
|
-
});
|
|
58
|
+
cost_saved_usd: event.salvaged,
|
|
59
|
+
}).catch(() => { });
|
|
29
60
|
}
|
|
30
61
|
});
|
package/dist/cli.js
CHANGED
|
@@ -181,9 +181,9 @@ function formatNum(n) {
|
|
|
181
181
|
function printReport(sites, filesScanned, timeMs, cwd) {
|
|
182
182
|
console.log("");
|
|
183
183
|
console.log(`${ORANGE}${B} ⚡ /slash${R}`);
|
|
184
|
-
console.log(`${GRAY} Token
|
|
184
|
+
console.log(`${GRAY} Token optimization analysis${R}`);
|
|
185
185
|
console.log("");
|
|
186
|
-
console.log(`${DIM}
|
|
186
|
+
console.log(`${DIM} Evaluated ${filesScanned} files in ${timeMs}ms${R}`);
|
|
187
187
|
console.log("");
|
|
188
188
|
if (sites.length === 0) {
|
|
189
189
|
console.log(`${WHITE} No AI API call sites detected.${R}`);
|
|
@@ -206,7 +206,7 @@ function printReport(sites, filesScanned, timeMs, cwd) {
|
|
|
206
206
|
const monthlyCostInput = dailyInput * 30 / 1e6 * 3;
|
|
207
207
|
const monthlyCostOutput = dailyOutput * 30 / 1e6 * 15;
|
|
208
208
|
const monthlyCost = monthlyCostInput + monthlyCostOutput;
|
|
209
|
-
console.log(`${ORANGE} DAILY
|
|
209
|
+
console.log(`${ORANGE} DAILY TOKEN VOLUME ${GRAY}(${callsPerDay} calls/site/day)${R}`);
|
|
210
210
|
console.log(`${DIM} ${"─".repeat(60)}${R}`);
|
|
211
211
|
console.log(`${WHITE} Input tokens: ${B}${formatNum(dailyInput)}${R}`);
|
|
212
212
|
console.log(`${WHITE} Output tokens: ${B}${formatNum(dailyOutput)}${R} ${GRAY}(estimated)${R}`);
|
|
@@ -218,12 +218,12 @@ function printReport(sites, filesScanned, timeMs, cwd) {
|
|
|
218
218
|
console.log(`${WHITE} Output: ${B}$${monthlyCostOutput.toFixed(2)}/mo${R}`);
|
|
219
219
|
console.log(`${WHITE} Total: ${ORANGE}${B}$${monthlyCost.toFixed(2)}/mo${R}`);
|
|
220
220
|
console.log("");
|
|
221
|
-
const
|
|
222
|
-
const
|
|
223
|
-
console.log(`${GOLD} ⚡
|
|
221
|
+
const recoveryPct = 10;
|
|
222
|
+
const monthlyRecovered = monthlyCost * recoveryPct / 100;
|
|
223
|
+
console.log(`${GOLD} ⚡ TOKENS SALVAGED ${GRAY}(${recoveryPct}% gate efficiency)${R}`);
|
|
224
224
|
console.log(`${DIM} ${"─".repeat(60)}${R}`);
|
|
225
|
-
console.log(`${WHITE} Monthly
|
|
226
|
-
console.log(`${WHITE} Annual
|
|
225
|
+
console.log(`${WHITE} Monthly salvaged: ${GOLD}${B}$${monthlyRecovered.toFixed(2)}/mo${R}`);
|
|
226
|
+
console.log(`${WHITE} Annual salvaged: ${GOLD}${B}$${(monthlyRecovered * 12).toFixed(2)}/yr${R}`);
|
|
227
227
|
console.log("");
|
|
228
228
|
console.log(`${GRAY} 4.8 KB WASM · sub-ms · zero deps${R}`);
|
|
229
229
|
console.log(`${GRAY} → ${ORANGE}slashtokens.com${R}`);
|
|
@@ -233,50 +233,20 @@ function printReport(sites, filesScanned, timeMs, cwd) {
|
|
|
233
233
|
// src/config.ts
|
|
234
234
|
var _key = null;
|
|
235
235
|
var _endpoint = "https://mcpaas.live/api/slash/transact";
|
|
236
|
+
var _route = true;
|
|
237
|
+
var _models = null;
|
|
238
|
+
var _quiet = false;
|
|
236
239
|
function init(opts) {
|
|
237
|
-
|
|
240
|
+
if (opts.key !== undefined)
|
|
241
|
+
_key = opts.key;
|
|
238
242
|
if (opts.endpoint)
|
|
239
243
|
_endpoint = opts.endpoint;
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
return key;
|
|
248
|
-
}
|
|
249
|
-
function getEndpoint() {
|
|
250
|
-
return _endpoint;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// src/transact.ts
|
|
254
|
-
async function report(opts) {
|
|
255
|
-
const key = resolveKey(opts.key);
|
|
256
|
-
const endpoint = getEndpoint();
|
|
257
|
-
const res = await fetch(endpoint, {
|
|
258
|
-
method: "POST",
|
|
259
|
-
headers: {
|
|
260
|
-
"Content-Type": "application/json",
|
|
261
|
-
Authorization: `Bearer ${key}`
|
|
262
|
-
},
|
|
263
|
-
body: JSON.stringify({
|
|
264
|
-
tokens_estimated: opts.tokens_estimated,
|
|
265
|
-
tokens_saved: opts.tokens_saved,
|
|
266
|
-
model: opts.model || "unknown",
|
|
267
|
-
action: opts.action,
|
|
268
|
-
cost_saved_usd: opts.cost_saved_usd
|
|
269
|
-
})
|
|
270
|
-
});
|
|
271
|
-
if (res.status === 401)
|
|
272
|
-
throw new Error("Invalid Slash API key");
|
|
273
|
-
if (res.status === 402)
|
|
274
|
-
throw new Error("Insufficient balance. Purchase credits at slashtokens.com");
|
|
275
|
-
if (res.status === 429)
|
|
276
|
-
throw new Error("Rate limited. Try again shortly.");
|
|
277
|
-
if (!res.ok)
|
|
278
|
-
throw new Error(`Slash API error: ${res.status}`);
|
|
279
|
-
return res.json();
|
|
244
|
+
if (opts.route !== undefined)
|
|
245
|
+
_route = opts.route;
|
|
246
|
+
if (opts.models)
|
|
247
|
+
_models = new Set(opts.models.map((m) => m.toLowerCase()));
|
|
248
|
+
if (opts.quiet !== undefined)
|
|
249
|
+
_quiet = opts.quiet;
|
|
280
250
|
}
|
|
281
251
|
|
|
282
252
|
// src/cli.ts
|
|
@@ -291,33 +261,14 @@ printReport(sites, filesScanned, timeMs, cwd);
|
|
|
291
261
|
var R2 = "\x1B[0m";
|
|
292
262
|
var B2 = "\x1B[1m";
|
|
293
263
|
var GREEN = "\x1B[38;2;74;222;128m";
|
|
294
|
-
var GOLD2 = "\x1B[38;2;230;161;65m";
|
|
295
264
|
var GRAY2 = "\x1B[90m";
|
|
296
265
|
var ORANGE2 = "\x1B[38;2;255;68;0m";
|
|
297
|
-
if (
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
const monthlySaved = monthlyCost * 0.1;
|
|
305
|
-
report({
|
|
306
|
-
tokens_estimated: dailyInput * 30,
|
|
307
|
-
tokens_saved: Math.round(dailyInput * 30 * 0.1),
|
|
308
|
-
model: "scan",
|
|
309
|
-
action: "reduced",
|
|
310
|
-
cost_saved_usd: monthlySaved
|
|
311
|
-
}).then((result) => {
|
|
312
|
-
console.log(`${GREEN}${B2} ✓ Reported to MCPaaS${R2} ${GRAY2}(${result.transaction_id})${R2}`);
|
|
313
|
-
console.log(`${GRAY2} Fee: $${result.fee_usd.toFixed(4)} · Balance: $${result.balance_remaining_usd.toFixed(2)}${R2}`);
|
|
314
|
-
console.log("");
|
|
315
|
-
}).catch((err) => {
|
|
316
|
-
console.log(`${GOLD2} ⚠ Report failed: ${err.message}${R2}`);
|
|
317
|
-
console.log("");
|
|
318
|
-
});
|
|
319
|
-
} else if (!key && sites.length > 0) {
|
|
320
|
-
console.log(`${GRAY2} → Report savings: ${ORANGE2}bunx slash-tokens --key=YOUR_KEY${R2}`);
|
|
321
|
-
console.log(`${GRAY2} → Get a key: POST https://mcpaas.live/api/slash/register${R2}`);
|
|
266
|
+
if (sites.length > 0) {
|
|
267
|
+
if (key) {
|
|
268
|
+
console.log(`${GREEN}${B2} ✓ Key active${R2} ${GRAY2}— evaluation complete, no charges on CLI scans${R2}`);
|
|
269
|
+
console.log(`${GRAY2} → Dashboard: ${ORANGE2}https://mcpaas.live/slash/dashboard${R2}`);
|
|
270
|
+
} else {
|
|
271
|
+
console.log(`${GRAY2} → Next: ${ORANGE2}https://mcpaas.live/slash/setup${R2}`);
|
|
272
|
+
}
|
|
322
273
|
console.log("");
|
|
323
274
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
export declare function init(opts: {
|
|
2
|
-
key
|
|
2
|
+
key?: string;
|
|
3
3
|
endpoint?: string;
|
|
4
|
+
route?: boolean;
|
|
5
|
+
models?: string[];
|
|
6
|
+
quiet?: boolean;
|
|
4
7
|
}): void;
|
|
5
8
|
export declare function resolveKey(perCallKey?: string): string;
|
|
6
9
|
export declare function getEndpoint(): string;
|
|
7
10
|
export declare function hasKey(): boolean;
|
|
11
|
+
export declare function shouldRoute(): boolean;
|
|
12
|
+
export declare function isModelAllowed(model: string): boolean;
|
|
13
|
+
export declare function isQuiet(): boolean;
|
package/dist/config.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
let _key = null;
|
|
2
2
|
let _endpoint = 'https://mcpaas.live/api/slash/transact';
|
|
3
|
+
let _route = true; // default: route to cheapest model
|
|
4
|
+
let _models = null; // null = all models allowed
|
|
5
|
+
let _quiet = false; // default: verbose (show every call)
|
|
3
6
|
export function init(opts) {
|
|
4
|
-
|
|
7
|
+
if (opts.key !== undefined)
|
|
8
|
+
_key = opts.key;
|
|
5
9
|
if (opts.endpoint)
|
|
6
10
|
_endpoint = opts.endpoint;
|
|
11
|
+
if (opts.route !== undefined)
|
|
12
|
+
_route = opts.route;
|
|
13
|
+
if (opts.models)
|
|
14
|
+
_models = new Set(opts.models.map(m => m.toLowerCase()));
|
|
15
|
+
if (opts.quiet !== undefined)
|
|
16
|
+
_quiet = opts.quiet;
|
|
7
17
|
}
|
|
8
18
|
export function resolveKey(perCallKey) {
|
|
9
19
|
const key = perCallKey || _key || process.env.SLASH_KEY;
|
|
@@ -19,3 +29,14 @@ export function getEndpoint() {
|
|
|
19
29
|
export function hasKey() {
|
|
20
30
|
return !!(_key || process.env.SLASH_KEY);
|
|
21
31
|
}
|
|
32
|
+
export function shouldRoute() {
|
|
33
|
+
return _route;
|
|
34
|
+
}
|
|
35
|
+
export function isModelAllowed(model) {
|
|
36
|
+
if (!_models)
|
|
37
|
+
return true; // null = all allowed
|
|
38
|
+
return _models.has(model.toLowerCase());
|
|
39
|
+
}
|
|
40
|
+
export function isQuiet() {
|
|
41
|
+
return _quiet;
|
|
42
|
+
}
|
package/dist/intercept.d.ts
CHANGED
|
@@ -2,9 +2,13 @@ export interface InterceptEvent {
|
|
|
2
2
|
endpoint: string;
|
|
3
3
|
provider: string;
|
|
4
4
|
model: string;
|
|
5
|
+
originalModel: string;
|
|
5
6
|
tokens: number;
|
|
6
7
|
cost: number;
|
|
8
|
+
originalCost: number;
|
|
9
|
+
salvaged: number;
|
|
7
10
|
fits: boolean;
|
|
11
|
+
routed: boolean;
|
|
8
12
|
timestamp: string;
|
|
9
13
|
}
|
|
10
14
|
export declare function onIntercept(handler: (event: InterceptEvent) => void): void;
|
package/dist/intercept.js
CHANGED
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
import { slash } from './slash.js';
|
|
2
2
|
import { getModel } from './models.js';
|
|
3
|
-
|
|
3
|
+
import { shouldRoute, isModelAllowed } from './config.js';
|
|
4
|
+
// Provider groups — routing only happens within same provider
|
|
5
|
+
const PROVIDER_MODELS = {
|
|
6
|
+
'Anthropic': ['claude-opus', 'claude-sonnet', 'claude-haiku'],
|
|
7
|
+
'OpenAI': ['gpt-5.4', 'gpt-5.4-mini', 'gpt-5.4-nano'],
|
|
8
|
+
'xAI': ['grok-4.20', 'grok-4-1-fast'],
|
|
9
|
+
'Google': ['gemini-3.1-pro', 'gemini-2.5-flash'],
|
|
10
|
+
};
|
|
11
|
+
// Reverse lookup: model name → provider model names in the API
|
|
12
|
+
// (what to put back in the request body)
|
|
13
|
+
const MODEL_API_NAMES = {
|
|
14
|
+
'claude-opus': 'claude-opus-4-20250514',
|
|
15
|
+
'claude-sonnet': 'claude-sonnet-4-20250514',
|
|
16
|
+
'claude-haiku': 'claude-haiku-4-5-20251001',
|
|
17
|
+
'gpt-5.4': 'gpt-5.4',
|
|
18
|
+
'gpt-5.4-mini': 'gpt-5.4-mini',
|
|
19
|
+
'gpt-5.4-nano': 'gpt-5.4-nano',
|
|
20
|
+
'grok-4.20': 'grok-4.20',
|
|
21
|
+
'grok-4-1-fast': 'grok-4-1-fast',
|
|
22
|
+
'gemini-3.1-pro': 'gemini-3.1-pro',
|
|
23
|
+
'gemini-2.5-flash': 'gemini-2.5-flash',
|
|
24
|
+
};
|
|
25
|
+
// AI API endpoint detection
|
|
4
26
|
const AI_ENDPOINTS = [
|
|
5
27
|
{
|
|
6
28
|
pattern: /api\.anthropic\.com/,
|
|
@@ -10,17 +32,17 @@ const AI_ENDPOINTS = [
|
|
|
10
32
|
{
|
|
11
33
|
pattern: /api\.openai\.com/,
|
|
12
34
|
provider: 'OpenAI',
|
|
13
|
-
modelExtractor: (body) => body?.model || 'gpt-
|
|
35
|
+
modelExtractor: (body) => body?.model || 'gpt-5.4',
|
|
14
36
|
},
|
|
15
37
|
{
|
|
16
38
|
pattern: /generativelanguage\.googleapis\.com/,
|
|
17
39
|
provider: 'Google',
|
|
18
|
-
modelExtractor: (body) => 'gemini-2.
|
|
40
|
+
modelExtractor: (body) => 'gemini-2.5-flash',
|
|
19
41
|
},
|
|
20
42
|
{
|
|
21
43
|
pattern: /api\.x\.ai/,
|
|
22
44
|
provider: 'xAI',
|
|
23
|
-
modelExtractor: (body) => body?.model || 'grok-
|
|
45
|
+
modelExtractor: (body) => body?.model || 'grok-4.20',
|
|
24
46
|
},
|
|
25
47
|
];
|
|
26
48
|
// Normalize model names to our pricing table keys
|
|
@@ -63,17 +85,13 @@ function normalizeModel(raw) {
|
|
|
63
85
|
function extractContent(body) {
|
|
64
86
|
if (!body)
|
|
65
87
|
return '';
|
|
66
|
-
// Anthropic: { messages: [{ content: "..." }] }
|
|
67
|
-
// OpenAI: { messages: [{ content: "..." }] }
|
|
68
88
|
if (body.messages && Array.isArray(body.messages)) {
|
|
69
89
|
return body.messages
|
|
70
90
|
.map((m) => typeof m.content === 'string' ? m.content : JSON.stringify(m.content))
|
|
71
91
|
.join('\n');
|
|
72
92
|
}
|
|
73
|
-
// Anthropic: { prompt: "..." }
|
|
74
93
|
if (typeof body.prompt === 'string')
|
|
75
94
|
return body.prompt;
|
|
76
|
-
// Google: { contents: [{ parts: [{ text: "..." }] }] }
|
|
77
95
|
if (body.contents && Array.isArray(body.contents)) {
|
|
78
96
|
return body.contents
|
|
79
97
|
.flatMap((c) => c.parts || [])
|
|
@@ -82,6 +100,30 @@ function extractContent(body) {
|
|
|
82
100
|
}
|
|
83
101
|
return JSON.stringify(body);
|
|
84
102
|
}
|
|
103
|
+
// Find cheapest model from same provider that fits
|
|
104
|
+
function findCheapestRoute(provider, tokens, currentModel) {
|
|
105
|
+
const providerModels = PROVIDER_MODELS[provider];
|
|
106
|
+
if (!providerModels)
|
|
107
|
+
return null;
|
|
108
|
+
let cheapest = null;
|
|
109
|
+
for (const model of providerModels) {
|
|
110
|
+
if (model === currentModel)
|
|
111
|
+
continue;
|
|
112
|
+
if (!isModelAllowed(model))
|
|
113
|
+
continue; // user excluded this model
|
|
114
|
+
const info = getModel(model);
|
|
115
|
+
if (!info)
|
|
116
|
+
continue;
|
|
117
|
+
if (tokens > info.context)
|
|
118
|
+
continue; // doesn't fit
|
|
119
|
+
if (info.input >= (getModel(currentModel)?.input ?? 0))
|
|
120
|
+
continue; // not cheaper
|
|
121
|
+
if (!cheapest || info.input < cheapest.inputCost) {
|
|
122
|
+
cheapest = { model, inputCost: info.input };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return cheapest?.model ?? null;
|
|
126
|
+
}
|
|
85
127
|
let _onIntercept = null;
|
|
86
128
|
export function onIntercept(handler) {
|
|
87
129
|
_onIntercept = handler;
|
|
@@ -99,23 +141,38 @@ export function patchFetch() {
|
|
|
99
141
|
const body = JSON.parse(bodyStr);
|
|
100
142
|
const content = extractContent(body);
|
|
101
143
|
const rawModel = match.modelExtractor(body);
|
|
102
|
-
const
|
|
144
|
+
const originalModel = normalizeModel(rawModel);
|
|
103
145
|
const tokens = slash(content);
|
|
104
|
-
const
|
|
105
|
-
const
|
|
106
|
-
const fits =
|
|
146
|
+
const originalInfo = getModel(originalModel);
|
|
147
|
+
const originalCost = originalInfo ? Math.round(((tokens / 1000000) * originalInfo.input) * 1000000) / 1000000 : 0;
|
|
148
|
+
const fits = originalInfo ? tokens <= originalInfo.context : true;
|
|
149
|
+
// Find cheapest route within same provider (if routing enabled)
|
|
150
|
+
const routeModel = shouldRoute() ? findCheapestRoute(match.provider, tokens, originalModel) : null;
|
|
151
|
+
const routedInfo = routeModel ? getModel(routeModel) : null;
|
|
152
|
+
const routedCost = routedInfo ? Math.round(((tokens / 1000000) * routedInfo.input) * 1000000) / 1000000 : originalCost;
|
|
153
|
+
const salvaged = routeModel ? Math.round((originalCost - routedCost) * 1000000) / 1000000 : 0;
|
|
107
154
|
const event = {
|
|
108
155
|
endpoint: url,
|
|
109
156
|
provider: match.provider,
|
|
110
|
-
model,
|
|
157
|
+
model: routeModel || originalModel,
|
|
158
|
+
originalModel,
|
|
111
159
|
tokens,
|
|
112
|
-
cost,
|
|
160
|
+
cost: routeModel ? routedCost : originalCost,
|
|
161
|
+
originalCost,
|
|
162
|
+
salvaged,
|
|
113
163
|
fits,
|
|
164
|
+
routed: !!routeModel,
|
|
114
165
|
timestamp: new Date().toISOString(),
|
|
115
166
|
};
|
|
116
167
|
if (_onIntercept) {
|
|
117
168
|
_onIntercept(event);
|
|
118
169
|
}
|
|
170
|
+
// If routed, rewrite the request body with the cheaper model
|
|
171
|
+
if (routeModel && init) {
|
|
172
|
+
const apiModelName = MODEL_API_NAMES[routeModel] || routeModel;
|
|
173
|
+
body.model = apiModelName;
|
|
174
|
+
init = { ...init, body: JSON.stringify(body) };
|
|
175
|
+
}
|
|
119
176
|
}
|
|
120
177
|
}
|
|
121
178
|
catch {
|
package/dist/preflight.d.ts
CHANGED
package/dist/preflight.js
CHANGED
|
@@ -17,11 +17,11 @@ export function preflight(content, model) {
|
|
|
17
17
|
return {
|
|
18
18
|
model: m,
|
|
19
19
|
cost: altCost,
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
salvaged: Math.round((cost - altCost) * 1000000) / 1000000,
|
|
21
|
+
salvagePercent: cost > 0 ? Math.round(((cost - altCost) / cost) * 10000) / 100 : 0,
|
|
22
22
|
};
|
|
23
23
|
})
|
|
24
|
-
.filter(o => o.
|
|
24
|
+
.filter(o => o.salvaged > 0)
|
|
25
25
|
.sort((a, b) => a.cost - b.cost);
|
|
26
26
|
return { tokens, cost, fits, model, context: info.context, utilization, options };
|
|
27
27
|
}
|
package/dist/report.js
CHANGED
|
@@ -15,9 +15,9 @@ function formatNum(n) {
|
|
|
15
15
|
export function printReport(sites, filesScanned, timeMs, cwd) {
|
|
16
16
|
console.log('');
|
|
17
17
|
console.log(`${ORANGE}${B} ⚡ /slash${R}`);
|
|
18
|
-
console.log(`${GRAY} Token
|
|
18
|
+
console.log(`${GRAY} Token optimization analysis${R}`);
|
|
19
19
|
console.log('');
|
|
20
|
-
console.log(`${DIM}
|
|
20
|
+
console.log(`${DIM} Evaluated ${filesScanned} files in ${timeMs}ms${R}`);
|
|
21
21
|
console.log('');
|
|
22
22
|
if (sites.length === 0) {
|
|
23
23
|
console.log(`${WHITE} No AI API call sites detected.${R}`);
|
|
@@ -43,7 +43,7 @@ export function printReport(sites, filesScanned, timeMs, cwd) {
|
|
|
43
43
|
const monthlyCostInput = (dailyInput * 30 / 1000000) * 3;
|
|
44
44
|
const monthlyCostOutput = (dailyOutput * 30 / 1000000) * 15;
|
|
45
45
|
const monthlyCost = monthlyCostInput + monthlyCostOutput;
|
|
46
|
-
console.log(`${ORANGE} DAILY
|
|
46
|
+
console.log(`${ORANGE} DAILY TOKEN VOLUME ${GRAY}(${callsPerDay} calls/site/day)${R}`);
|
|
47
47
|
console.log(`${DIM} ${'─'.repeat(60)}${R}`);
|
|
48
48
|
console.log(`${WHITE} Input tokens: ${B}${formatNum(dailyInput)}${R}`);
|
|
49
49
|
console.log(`${WHITE} Output tokens: ${B}${formatNum(dailyOutput)}${R} ${GRAY}(estimated)${R}`);
|
|
@@ -55,13 +55,13 @@ export function printReport(sites, filesScanned, timeMs, cwd) {
|
|
|
55
55
|
console.log(`${WHITE} Output: ${B}$${monthlyCostOutput.toFixed(2)}/mo${R}`);
|
|
56
56
|
console.log(`${WHITE} Total: ${ORANGE}${B}$${monthlyCost.toFixed(2)}/mo${R}`);
|
|
57
57
|
console.log('');
|
|
58
|
-
// Slash
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
console.log(`${GOLD} ⚡
|
|
58
|
+
// Slash — sunk cost recovery (conservative 10% gate efficiency)
|
|
59
|
+
const recoveryPct = 10;
|
|
60
|
+
const monthlyRecovered = monthlyCost * recoveryPct / 100;
|
|
61
|
+
console.log(`${GOLD} ⚡ TOKENS SALVAGED ${GRAY}(${recoveryPct}% gate efficiency)${R}`);
|
|
62
62
|
console.log(`${DIM} ${'─'.repeat(60)}${R}`);
|
|
63
|
-
console.log(`${WHITE} Monthly
|
|
64
|
-
console.log(`${WHITE} Annual
|
|
63
|
+
console.log(`${WHITE} Monthly salvaged: ${GOLD}${B}$${monthlyRecovered.toFixed(2)}/mo${R}`);
|
|
64
|
+
console.log(`${WHITE} Annual salvaged: ${GOLD}${B}$${(monthlyRecovered * 12).toFixed(2)}/yr${R}`);
|
|
65
65
|
console.log('');
|
|
66
66
|
console.log(`${GRAY} 4.8 KB WASM · sub-ms · zero deps${R}`);
|
|
67
67
|
console.log(`${GRAY} → ${ORANGE}slashtokens.com${R}`);
|
package/dist/transact.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "slash-tokens",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.1.2",
|
|
4
|
+
"description": "Token Optimization for Context Engineers. 4.8 KB WASM. Sub-millisecond. Zero dependencies.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
"types": "./dist/auto.d.ts"
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
|
-
"files": [
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
23
25
|
"sideEffects": false,
|
|
24
26
|
"scripts": {
|
|
25
27
|
"build": "npx tsc && bun build src/cli.ts --outfile=dist/cli.js --target=node",
|
|
@@ -27,7 +29,20 @@
|
|
|
27
29
|
},
|
|
28
30
|
"author": "wolfejam",
|
|
29
31
|
"license": "MIT",
|
|
30
|
-
"keywords": [
|
|
32
|
+
"keywords": [
|
|
33
|
+
"tokens",
|
|
34
|
+
"wasm",
|
|
35
|
+
"edge",
|
|
36
|
+
"budget",
|
|
37
|
+
"estimation",
|
|
38
|
+
"llm",
|
|
39
|
+
"ai",
|
|
40
|
+
"openai",
|
|
41
|
+
"anthropic",
|
|
42
|
+
"claude",
|
|
43
|
+
"grok",
|
|
44
|
+
"gemini"
|
|
45
|
+
],
|
|
31
46
|
"repository": {
|
|
32
47
|
"type": "git",
|
|
33
48
|
"url": "https://github.com/Wolfe-Jam/slash-tokens"
|