slash-tokens 1.6.5 → 1.6.7
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/CHANGELOG.md +37 -0
- package/README.md +8 -2
- package/dist/cli.js +172 -66
- package/dist/intercept.d.ts +8 -0
- package/dist/intercept.js +20 -5
- package/dist/models.d.ts +7 -0
- package/dist/models.js +61 -2
- package/dist/preflight.js +10 -4
- package/dist/providers.d.ts +13 -0
- package/dist/providers.js +36 -7
- package/dist/report.js +1 -0
- package/dist/slash.js +26 -3
- package/dist/wasm.d.ts +11 -0
- package/dist/wasm.js +12 -2
- package/package.json +7 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.6.7] — The Fixed Deal Edition
|
|
4
|
+
|
|
5
|
+
*2026-10-08*
|
|
6
|
+
|
|
7
|
+
One very large request (about 1 MB of text) no longer skews every count after it.
|
|
8
|
+
|
|
9
|
+
Solo $20 mailbox, 10% waived. Team $39 for the data.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- **A single request over ~1.04 MB of text no longer skews every later estimate in the same process.** Input was written into WASM memory from byte 4096, so a big one ran over the module's stack and lookup tables (at 1 MiB), and later estimates came out wrong — a 14-token prompt read as 28. Input now starts above them (byte 1,114,112). Estimates for inputs under the threshold are unchanged (checked against 1.6.6 on the 29-sample corpus and five models). Older versions are affected too (any version that writes input at byte 4096, including 1.6.6).
|
|
13
|
+
- **The test suite now passes file by file.** `preflight.test.ts` failed when run alone: on a tiny prompt the routed model's cost rounds to the same 6-decimal value as the original's. It passed in the full suite only because the bug above had already inflated the counts. The test now checks a lower list price, and strictly lower cost on a prompt big enough to show it.
|
|
14
|
+
|
|
15
|
+
No Team/Solo price change. No routing or pricing change.
|
|
16
|
+
|
|
17
|
+
## [1.6.6] — The Fixed Deal Edition
|
|
18
|
+
|
|
19
|
+
*2026-10-07*
|
|
20
|
+
|
|
21
|
+
`--version` / `--help` answer, and today's models price correctly.
|
|
22
|
+
|
|
23
|
+
Solo $20 mailbox, 10% waived. Team $39 for the data.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
- **`slash-tokens --version` and `--help` print and exit.** In 1.6.5 every flag fell through to a full scan of the current folder, so `--version` run from `~` scanned the home folder. An empty `bunx` run now tells you to run it in an app.
|
|
27
|
+
- **`preflight()` no longer throws on current models or real API IDs.** IDs are canonicalised strictly (`claude-opus-4-7` → `claude-opus-4.7`, date stamps dropped). There is no family guessing: an unknown version still fails with "Unknown model", so a new model never silently gets an older model's price.
|
|
28
|
+
- **`npm publish` always builds first** (`prepublishOnly`), so a stale `dist/` can't ship again (1.6.4).
|
|
29
|
+
- **`report()` sends numbers only.** A test pins its payload to `tokens_estimated`, `tokens_saved`, `model`, `action`, `cost_saved_usd`; prompt content never leaves the machine.
|
|
30
|
+
- **`npm test` runs offline.** The live integration suite (`tests/z-integration.test.ts`, which registers keys on mcpaas.live) runs only with `SLASH_LIVE=1`; the weekly `integration.yml` sets it.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
- **Model table, prices as of 2026-10-07** (checked against the Anthropic, OpenAI, xAI and Google pricing pages). Added Claude Opus 5.5 ($4/$20), Sonnet 5.5 ($2/$10), Opus 4.8 / 4.6 / 4.5, Sonnet 4.6 / 4.5, Fable 5 / 5.1 and Mythos 5 / 5.1 ($10/$50); Grok 4.7 and 4.5 ($2/$6, doubling above 200K), grok-build-0.1 ($1/$2); GPT-6 Astra ($10/$50), GPT-6.1 Sol and GPT-6 Sol ($2/$10), GPT-6 Luna ($0.10/$0.50); Gemini 3.8 / 3.7 / 3.6 Flash ($0.75/$3.75 launch price through 2026-12-31), 3.5 Flash ($1.50/$9), 3.1 Flash-Lite ($0.25/$1.50), 3.1 Pro Preview.
|
|
34
|
+
- **`slash-tokens/auto` routing is unchanged within 1.6.x.** `/auto` rewrites live requests only to the models it routed to in 1.6.5 (`AUTO_ROUTE_TARGETS`), using the same identification, so a patch upgrade never changes where production calls go. New models are now recognised and priced correctly in `/auto` events (`identifyModel`: `claude-opus-5-5` at $4/$20, `gpt-6-astra` at $10/$50); /auto starts routing to them in 1.7.0.
|
|
35
|
+
- **Routing (`preflightRoute`) follows the new ladder.** Cheapest same-provider is now GPT-6 Luna for OpenAI and Gemini 3.1 Flash-Lite for Google (Anthropic and xAI unchanged). grok-build-0.1 is priced but never a routing target (`NOT_ROUTE_TARGETS`): it's a coding-agent model. When two alternatives' costs round to the same value (tiny prompts), the lower list price wins.
|
|
36
|
+
- **Calibration:** new Claude, Grok and Gemini models use their family's measured factor. GPT-6 is a new generation with an unbenchmarked tokenizer, so it takes the conservative default until a bench run adds it (slash never under-reports).
|
|
37
|
+
|
|
38
|
+
No Team/Solo price change.
|
|
39
|
+
|
|
3
40
|
## [1.6.5] — The Fixed Deal Edition
|
|
4
41
|
|
|
5
42
|
*2026-08-25*
|
package/README.md
CHANGED
|
@@ -15,10 +15,16 @@ Know the cost before the call leaves your machine.
|
|
|
15
15
|
Models change. Windows grow. Slash adapts — you keep building.
|
|
16
16
|
Cheaper tokens haven't shrunk the bill — usage has.
|
|
17
17
|
|
|
18
|
-
## v1.6.
|
|
18
|
+
## v1.6.7 — The Fixed Deal Edition
|
|
19
|
+
|
|
20
|
+
One very large request (about 1 MB of text) no longer skews every count after it.
|
|
19
21
|
|
|
20
22
|
Solo $20 mailbox, 10% waived. Team $39 for the data.
|
|
21
23
|
|
|
24
|
+
New in 1.6.7: a single request over ~1 MB of text used to overwrite the token counter's lookup tables, so every later count in that process came out wrong; prompts now go above them. Counts for normal prompts are unchanged.
|
|
25
|
+
|
|
26
|
+
New in 1.6.6: Claude Opus 5.5 / Sonnet 5.5 / Fable 5.1, Grok 4.7, GPT-6 (Astra, Sol, Luna), Gemini 3.6–3.8 Flash and 3.1 Flash-Lite, priced as of 2026-10-07. Real API IDs (`claude-opus-4-7`) work in `preflight()`. `preflightRoute()` now finds GPT-6 Luna and Gemini 3.1 Flash-Lite as the cheapest same-provider options.
|
|
27
|
+
|
|
22
28
|
**Free forever is bunx** — no account. A one-person account is email → key, **$20 on the house**. We show the savings. We don't charge. 10% is the model, waived. Team is **$39 for the data** (`$390`/year).
|
|
23
29
|
|
|
24
30
|
```bash
|
|
@@ -26,7 +32,7 @@ bunx slash-tokens
|
|
|
26
32
|
# or: npx --yes slash-tokens
|
|
27
33
|
```
|
|
28
34
|
|
|
29
|
-
Run it in a project that already calls an LLM. An empty folder prints that nothing was found — that's normal.
|
|
35
|
+
Run it in a project that already calls an LLM. An empty folder prints that nothing was found, then tells you to run it in an app — that's normal. `--version` / `--help` print and exit (they do not scan). Pin proof: `slash-tokens --version` or `npm view slash-tokens version`.
|
|
30
36
|
|
|
31
37
|
```bash
|
|
32
38
|
npm install slash-tokens
|
package/dist/cli.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
5
|
+
import { dirname, join as join2 } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
3
8
|
// src/scanner.ts
|
|
4
9
|
import { readdirSync, readFileSync, statSync } from "fs";
|
|
5
10
|
import { join, extname } from "path";
|
|
6
11
|
|
|
7
12
|
// src/wasm.ts
|
|
8
13
|
var WASM_BASE64 = "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AA4AAABLAxAAEwAAALABEAAQAAAAGgIQABYAAAAwBBAADQAAAJ0DEAAOAAAAsQMQAA0AAAB6AxAADgAAAMsCEAAQAAAAbAMQAA0AAAA+BBAACwAAAFYEEAAKAAAAvwMQAAwAAAAjAxAADAAAAP0DEAAVAAAAsQIQABEAAAAxAhAAEgAAAMEBEAARAAAAiQMQABMAAADoAhAAEgAAAPABEAARAAAAIQQQAA4AAABLAxAAEwAAALABEAAQAAAAGgIQABYAAAAwBBAADQAAAJ0DEAAOAAAAsQMQAA0AAAB6AxAADgAAAMsCEAAQAAAAbAMQAA0AAAA+BBAACwAAAFYEEAAKAAAACgMQABMAAACbAhAAFQAAAG4CEAAQAAAAAgIQABcAAAB/AhAAGwAAANwCEAALAAAA8QMQAAsAAABfAxAADAAAABMEEAANAAAA0wEQABwAAABWAhAAFwAAANsDEAAVAAAAc3RhY2sudWlfbGlicmFyeQBodW1hbl9jb250ZXh0LndoeQBtb25vcmVwby52ZXJzaW9uaW5nX3N0cmF0ZWd5AGh1bWFuX2NvbnRleHQuaG93AG1vbm9yZXBvLnBhY2thZ2VzX2NvdW50AHN0YWNrLnN0YXRlX21hbmFnZW1lbnQAaHVtYW5fY29udGV4dC53aGF0AGRldmVsb3BtZW50IHRlYW1zAG1vbm9yZXBvLnNoYXJlZF9jb25maWdzAHN0YWNrLndvcmtzcGFjZXMAbW9ub3JlcG8uYnVpbGRfb3JjaGVzdHJhdG9yAHN0YWNrLnBhY2thZ2VfbWFuYWdlcgBodW1hbl9jb250ZXh0LndobwB1bmtub3duAHN0YWNrLmNvbm5lY3Rpb24Ac3RhY2suYWRtaW4AaHVtYW5fY29udGV4dC53aGVuAGNsb3VkIHBsYXRmb3JtAHN0YWNrLm1vbm9yZXBvX3Rvb2wAbnVsbABwcm9qZWN0LmdvYWwAZGVzY3JpYmUgeW91ciBwcm9qZWN0IGdvYWwAc3RhY2suY3NzX2ZyYW1ld29yawBzdGFjay5zZWFyY2gAc3RhY2suaG9zdGluZwBzdGFjay5kYXRhYmFzZQBodW1hbl9jb250ZXh0LndoZXJlAHN0YWNrLmFwaV90eXBlAG5vbmUAc3RhY2sucnVudGltZQBwcm9qZWN0Lm5hbWUAbm90IGFwcGxpY2FibGUAbW9ub3JlcG8ucmVtb3RlX2NhY2hlAHN0YWNrLmNhY2hlAHByb2plY3QubWFpbl9sYW5ndWFnZQBzdGFjay5zdG9yYWdlAHN0YWNrLmZyb250ZW5kAHN0YWNrLmJhY2tlbmQAc3RhY2suYnVpbGQAc2xvdGlnbm9yZWQAc3RhY2suY2ljZABuL2EAOgAuACAJDQoiJwAgCSInAA0ACgAgCQAwAxAAGgAAAEQCEAARAAAA+wIQAA4AAAAeAxAABAAAAKwDEAAEAAAAwwIQAAcAAABhBBAAAwAAAMwDEAAOAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAAAAAAAAAAA=";
|
|
9
|
-
var WASM_INPUT_OFFSET =
|
|
14
|
+
var WASM_INPUT_OFFSET = 1114112;
|
|
10
15
|
var encoder = new TextEncoder;
|
|
11
16
|
var _instance = null;
|
|
12
17
|
function decodeBase64(b64) {
|
|
@@ -31,7 +36,7 @@ function getInstance() {
|
|
|
31
36
|
var MAX_WASM_INPUT_BYTES = 32 * 1024 * 1024;
|
|
32
37
|
var WASM_PAGE_BYTES = 65536;
|
|
33
38
|
function ensureCapacity(memory, neededBytes) {
|
|
34
|
-
const cappedNeeded = Math.min(neededBytes, MAX_WASM_INPUT_BYTES);
|
|
39
|
+
const cappedNeeded = Math.min(neededBytes, WASM_INPUT_OFFSET + MAX_WASM_INPUT_BYTES);
|
|
35
40
|
if (memory.buffer.byteLength >= cappedNeeded)
|
|
36
41
|
return;
|
|
37
42
|
const additionalPages = Math.ceil((cappedNeeded - memory.buffer.byteLength) / WASM_PAGE_BYTES);
|
|
@@ -49,10 +54,136 @@ function writeToMemory(content) {
|
|
|
49
54
|
return maxLen;
|
|
50
55
|
}
|
|
51
56
|
|
|
57
|
+
// src/models.ts
|
|
58
|
+
var OPUS = { input: 5, output: 25, context: 1e6 };
|
|
59
|
+
var OPUS_55 = { input: 4, output: 20, context: 1e6 };
|
|
60
|
+
var FABLE = { input: 10, output: 50, context: 1e6 };
|
|
61
|
+
var SONNET_4X = { input: 3, output: 15, context: 1e6 };
|
|
62
|
+
var SONNET = { input: 2, output: 10, context: 1e6 };
|
|
63
|
+
var HAIKU = { input: 1, output: 5, context: 200000 };
|
|
64
|
+
var GROK_46 = {
|
|
65
|
+
input: 2,
|
|
66
|
+
output: 6,
|
|
67
|
+
context: 500000,
|
|
68
|
+
longContextThreshold: 200000,
|
|
69
|
+
longContextInput: 4,
|
|
70
|
+
longContextOutput: 12
|
|
71
|
+
};
|
|
72
|
+
var GROK_43 = {
|
|
73
|
+
input: 1.25,
|
|
74
|
+
output: 2.5,
|
|
75
|
+
context: 1e6,
|
|
76
|
+
longContextThreshold: 200000,
|
|
77
|
+
longContextInput: 2.5,
|
|
78
|
+
longContextOutput: 5
|
|
79
|
+
};
|
|
80
|
+
var GEMINI_PRO = {
|
|
81
|
+
input: 2,
|
|
82
|
+
output: 12,
|
|
83
|
+
context: 1e6,
|
|
84
|
+
longContextThreshold: 200000,
|
|
85
|
+
longContextInput: 4,
|
|
86
|
+
longContextOutput: 18
|
|
87
|
+
};
|
|
88
|
+
var GEMINI_FLASH = { input: 0.3, output: 2.5, context: 1e6 };
|
|
89
|
+
var GEMINI_FLASH_3X = { input: 0.75, output: 3.75, context: 1e6 };
|
|
90
|
+
var GEMINI_35_FLASH = { input: 1.5, output: 9, context: 1e6 };
|
|
91
|
+
var GEMINI_31_FLASH_LITE = { input: 0.25, output: 1.5, context: 1e6 };
|
|
92
|
+
var GROK_BUILD = {
|
|
93
|
+
input: 1,
|
|
94
|
+
output: 2,
|
|
95
|
+
context: 256000,
|
|
96
|
+
longContextThreshold: 200000,
|
|
97
|
+
longContextInput: 2,
|
|
98
|
+
longContextOutput: 4
|
|
99
|
+
};
|
|
100
|
+
var GPT_6_ASTRA = { input: 10, output: 50, context: 1050000 };
|
|
101
|
+
var GPT_6_SOL = { input: 2, output: 10, context: 1050000 };
|
|
102
|
+
var GPT_6_LUNA = { input: 0.1, output: 0.5, context: 1050000 };
|
|
103
|
+
var GPT_SOL = { input: 4, output: 20, context: 1050000 };
|
|
104
|
+
var GPT_TERRA = { input: 2, output: 12, context: 1050000 };
|
|
105
|
+
var GPT_LUNA = { input: 0.2, output: 1.2, context: 1050000 };
|
|
106
|
+
var GPT_54 = { input: 2.5, output: 15, context: 1e6 };
|
|
107
|
+
var GPT_54_MINI = { input: 0.75, output: 4.5, context: 128000 };
|
|
108
|
+
var GPT_54_NANO = { input: 0.2, output: 1.25, context: 128000 };
|
|
109
|
+
var MODELS = {
|
|
110
|
+
"claude-fable-5.1": { ...FABLE },
|
|
111
|
+
"claude-fable-5": { ...FABLE },
|
|
112
|
+
"claude-mythos-5.1": { ...FABLE },
|
|
113
|
+
"claude-mythos-5": { ...FABLE },
|
|
114
|
+
"claude-opus-5.5": { ...OPUS_55 },
|
|
115
|
+
"claude-opus-5": { ...OPUS },
|
|
116
|
+
"claude-opus-4.8": { ...OPUS },
|
|
117
|
+
"claude-opus": { ...OPUS },
|
|
118
|
+
"claude-opus-4.7": { ...OPUS },
|
|
119
|
+
"claude-opus-4.6": { ...OPUS },
|
|
120
|
+
"claude-opus-4.5": { ...OPUS },
|
|
121
|
+
"claude-sonnet-5.5": { ...SONNET },
|
|
122
|
+
"claude-sonnet-5": { ...SONNET },
|
|
123
|
+
"claude-sonnet": { ...SONNET },
|
|
124
|
+
"claude-sonnet-4.6": { ...SONNET_4X },
|
|
125
|
+
"claude-sonnet-4.5": { ...SONNET_4X },
|
|
126
|
+
"claude-haiku-4.5": { ...HAIKU },
|
|
127
|
+
"claude-haiku": { ...HAIKU },
|
|
128
|
+
"grok-4.7": { ...GROK_46 },
|
|
129
|
+
"grok-4.6": { ...GROK_46 },
|
|
130
|
+
"grok-4.5": { ...GROK_46 },
|
|
131
|
+
"grok-build-0.1": { ...GROK_BUILD },
|
|
132
|
+
"grok-4.3": { ...GROK_43 },
|
|
133
|
+
"grok-4.20": { ...GROK_43 },
|
|
134
|
+
"grok-4-1-fast": { ...GROK_43 },
|
|
135
|
+
"gemini-3.1-pro": { ...GEMINI_PRO },
|
|
136
|
+
"gemini-3.1-pro-preview": { ...GEMINI_PRO },
|
|
137
|
+
"gemini-3.8-flash": { ...GEMINI_FLASH_3X },
|
|
138
|
+
"gemini-3.7-flash": { ...GEMINI_FLASH_3X },
|
|
139
|
+
"gemini-3.6-flash": { ...GEMINI_FLASH_3X },
|
|
140
|
+
"gemini-3.5-flash": { ...GEMINI_35_FLASH },
|
|
141
|
+
"gemini-3.1-flash-lite": { ...GEMINI_31_FLASH_LITE },
|
|
142
|
+
"gemini-3.5-flash-lite": { ...GEMINI_FLASH },
|
|
143
|
+
"gemini-2.5-flash": { ...GEMINI_FLASH },
|
|
144
|
+
"gpt-6-astra": { ...GPT_6_ASTRA },
|
|
145
|
+
"gpt-6.1-sol": { ...GPT_6_SOL },
|
|
146
|
+
"gpt-6-sol": { ...GPT_6_SOL },
|
|
147
|
+
"gpt-6-luna": { ...GPT_6_LUNA },
|
|
148
|
+
"gpt-5.6-sol": { ...GPT_SOL },
|
|
149
|
+
"gpt-5.6-terra": { ...GPT_TERRA },
|
|
150
|
+
"gpt-5.6-luna": { ...GPT_LUNA },
|
|
151
|
+
"gpt-5.4": { ...GPT_54 },
|
|
152
|
+
"gpt-5.4-mini": { ...GPT_54_MINI },
|
|
153
|
+
"gpt-5.4-nano": { ...GPT_54_NANO }
|
|
154
|
+
};
|
|
155
|
+
function canonicalModel(name) {
|
|
156
|
+
if (MODELS[name])
|
|
157
|
+
return name;
|
|
158
|
+
let n = name.trim().toLowerCase();
|
|
159
|
+
if (MODELS[n])
|
|
160
|
+
return n;
|
|
161
|
+
n = n.replace(/-\d{8}$/, "");
|
|
162
|
+
if (MODELS[n])
|
|
163
|
+
return n;
|
|
164
|
+
const dotted = n.replace(/(\d)-(\d)(?=$|-)/g, "$1.$2");
|
|
165
|
+
if (MODELS[dotted])
|
|
166
|
+
return dotted;
|
|
167
|
+
return n;
|
|
168
|
+
}
|
|
169
|
+
function getModel(name) {
|
|
170
|
+
return MODELS[canonicalModel(name)];
|
|
171
|
+
}
|
|
172
|
+
|
|
52
173
|
// src/slash.ts
|
|
53
|
-
var WASM_INPUT_OFFSET2 = 4096;
|
|
54
174
|
var CALIBRATION = {
|
|
175
|
+
"claude-fable-5.1": 2.05,
|
|
176
|
+
"claude-fable-5": 2.05,
|
|
177
|
+
"claude-mythos-5.1": 2.05,
|
|
178
|
+
"claude-mythos-5": 2.05,
|
|
179
|
+
"claude-opus-5.5": 2.05,
|
|
55
180
|
"claude-opus-5": 2.05,
|
|
181
|
+
"claude-opus-4.8": 2.05,
|
|
182
|
+
"claude-opus-4.6": 2.05,
|
|
183
|
+
"claude-opus-4.5": 2.05,
|
|
184
|
+
"claude-sonnet-5.5": 2.05,
|
|
185
|
+
"claude-sonnet-4.6": 2.05,
|
|
186
|
+
"claude-sonnet-4.5": 2.05,
|
|
56
187
|
"claude-opus": 2.05,
|
|
57
188
|
"claude-opus-4.7": 2.05,
|
|
58
189
|
"claude-sonnet-5": 2.05,
|
|
@@ -60,9 +191,18 @@ var CALIBRATION = {
|
|
|
60
191
|
"claude-haiku-4.5": 1.45,
|
|
61
192
|
"claude-haiku": 1.45,
|
|
62
193
|
"gemini-3.1-pro": 1.45,
|
|
194
|
+
"gemini-3.1-pro-preview": 1.45,
|
|
195
|
+
"gemini-3.8-flash": 1.45,
|
|
196
|
+
"gemini-3.7-flash": 1.45,
|
|
197
|
+
"gemini-3.6-flash": 1.45,
|
|
198
|
+
"gemini-3.5-flash": 1.45,
|
|
199
|
+
"gemini-3.1-flash-lite": 1.45,
|
|
63
200
|
"gemini-3.5-flash-lite": 1.45,
|
|
64
201
|
"gemini-2.5-flash": 1.45,
|
|
202
|
+
"grok-4.7": 1.15,
|
|
65
203
|
"grok-4.6": 1.15,
|
|
204
|
+
"grok-4.5": 1.15,
|
|
205
|
+
"grok-build-0.1": 1.15,
|
|
66
206
|
"grok-4.3": 1.15,
|
|
67
207
|
"grok-4.20": 1.15,
|
|
68
208
|
"grok-4-1-fast": 1.15,
|
|
@@ -79,10 +219,10 @@ function slash(content, model) {
|
|
|
79
219
|
return 0;
|
|
80
220
|
const len = writeToMemory(content);
|
|
81
221
|
const instance = getInstance();
|
|
82
|
-
const raw = instance.exports.estimate_tokens(
|
|
222
|
+
const raw = instance.exports.estimate_tokens(WASM_INPUT_OFFSET, len);
|
|
83
223
|
if (!model)
|
|
84
224
|
return raw;
|
|
85
|
-
const factor = CALIBRATION[model] ?? DEFAULT_UNKNOWN_MODEL_FACTOR;
|
|
225
|
+
const factor = CALIBRATION[canonicalModel(model)] ?? DEFAULT_UNKNOWN_MODEL_FACTOR;
|
|
86
226
|
return factor === 1 ? raw : Math.ceil(raw * factor);
|
|
87
227
|
}
|
|
88
228
|
|
|
@@ -211,67 +351,6 @@ function scan(dir) {
|
|
|
211
351
|
};
|
|
212
352
|
}
|
|
213
353
|
|
|
214
|
-
// src/models.ts
|
|
215
|
-
var OPUS = { input: 5, output: 25, context: 1e6 };
|
|
216
|
-
var SONNET = { input: 2, output: 10, context: 1e6 };
|
|
217
|
-
var HAIKU = { input: 1, output: 5, context: 200000 };
|
|
218
|
-
var GROK_46 = {
|
|
219
|
-
input: 2,
|
|
220
|
-
output: 6,
|
|
221
|
-
context: 500000,
|
|
222
|
-
longContextThreshold: 200000,
|
|
223
|
-
longContextInput: 4,
|
|
224
|
-
longContextOutput: 12
|
|
225
|
-
};
|
|
226
|
-
var GROK_43 = {
|
|
227
|
-
input: 1.25,
|
|
228
|
-
output: 2.5,
|
|
229
|
-
context: 1e6,
|
|
230
|
-
longContextThreshold: 200000,
|
|
231
|
-
longContextInput: 2.5,
|
|
232
|
-
longContextOutput: 5
|
|
233
|
-
};
|
|
234
|
-
var GEMINI_PRO = {
|
|
235
|
-
input: 2,
|
|
236
|
-
output: 12,
|
|
237
|
-
context: 1e6,
|
|
238
|
-
longContextThreshold: 200000,
|
|
239
|
-
longContextInput: 4,
|
|
240
|
-
longContextOutput: 18
|
|
241
|
-
};
|
|
242
|
-
var GEMINI_FLASH = { input: 0.3, output: 2.5, context: 1e6 };
|
|
243
|
-
var GPT_SOL = { input: 4, output: 20, context: 1050000 };
|
|
244
|
-
var GPT_TERRA = { input: 2, output: 12, context: 1050000 };
|
|
245
|
-
var GPT_LUNA = { input: 0.2, output: 1.2, context: 1050000 };
|
|
246
|
-
var GPT_54 = { input: 2.5, output: 15, context: 1e6 };
|
|
247
|
-
var GPT_54_MINI = { input: 0.75, output: 4.5, context: 128000 };
|
|
248
|
-
var GPT_54_NANO = { input: 0.2, output: 1.25, context: 128000 };
|
|
249
|
-
var MODELS = {
|
|
250
|
-
"claude-opus-5": { ...OPUS },
|
|
251
|
-
"claude-opus": { ...OPUS },
|
|
252
|
-
"claude-opus-4.7": { ...OPUS },
|
|
253
|
-
"claude-sonnet-5": { ...SONNET },
|
|
254
|
-
"claude-sonnet": { ...SONNET },
|
|
255
|
-
"claude-haiku-4.5": { ...HAIKU },
|
|
256
|
-
"claude-haiku": { ...HAIKU },
|
|
257
|
-
"grok-4.6": { ...GROK_46 },
|
|
258
|
-
"grok-4.3": { ...GROK_43 },
|
|
259
|
-
"grok-4.20": { ...GROK_43 },
|
|
260
|
-
"grok-4-1-fast": { ...GROK_43 },
|
|
261
|
-
"gemini-3.1-pro": { ...GEMINI_PRO },
|
|
262
|
-
"gemini-3.5-flash-lite": { ...GEMINI_FLASH },
|
|
263
|
-
"gemini-2.5-flash": { ...GEMINI_FLASH },
|
|
264
|
-
"gpt-5.6-sol": { ...GPT_SOL },
|
|
265
|
-
"gpt-5.6-terra": { ...GPT_TERRA },
|
|
266
|
-
"gpt-5.6-luna": { ...GPT_LUNA },
|
|
267
|
-
"gpt-5.4": { ...GPT_54 },
|
|
268
|
-
"gpt-5.4-mini": { ...GPT_54_MINI },
|
|
269
|
-
"gpt-5.4-nano": { ...GPT_54_NANO }
|
|
270
|
-
};
|
|
271
|
-
function getModel(name) {
|
|
272
|
-
return MODELS[name] || MODELS[name.toLowerCase()];
|
|
273
|
-
}
|
|
274
|
-
|
|
275
354
|
// src/report.ts
|
|
276
355
|
var R = "\x1B[0m";
|
|
277
356
|
var B = "\x1B[1m";
|
|
@@ -296,6 +375,7 @@ function printReport(sites, filesScanned, timeMs, cwd) {
|
|
|
296
375
|
if (sites.length === 0) {
|
|
297
376
|
console.log(`${WHITE} No AI API call sites detected.${R}`);
|
|
298
377
|
console.log(`${GRAY} Supported: OpenAI, Anthropic, Vercel AI, LangChain, Gemini, Bedrock, Grok${R}`);
|
|
378
|
+
console.log(`${GRAY} Run this in a project that already calls an LLM.${R}`);
|
|
299
379
|
console.log("");
|
|
300
380
|
return;
|
|
301
381
|
}
|
|
@@ -367,6 +447,14 @@ function init(opts) {
|
|
|
367
447
|
|
|
368
448
|
// src/cli.ts
|
|
369
449
|
var args = process.argv.slice(2);
|
|
450
|
+
if (args.includes("--version") || args.includes("-V")) {
|
|
451
|
+
console.log(packageVersion());
|
|
452
|
+
process.exit(0);
|
|
453
|
+
}
|
|
454
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
455
|
+
printHelp();
|
|
456
|
+
process.exit(0);
|
|
457
|
+
}
|
|
370
458
|
var keyArg = args.find((a) => a.startsWith("--key="));
|
|
371
459
|
var key = keyArg?.split("=")[1] || process.env.SLASH_KEY;
|
|
372
460
|
if (key)
|
|
@@ -388,3 +476,21 @@ if (sites.length > 0) {
|
|
|
388
476
|
}
|
|
389
477
|
console.log("");
|
|
390
478
|
}
|
|
479
|
+
function packageVersion() {
|
|
480
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
481
|
+
const pkg = JSON.parse(readFileSync2(join2(here, "..", "package.json"), "utf8"));
|
|
482
|
+
return pkg.version;
|
|
483
|
+
}
|
|
484
|
+
function printHelp() {
|
|
485
|
+
console.log(`slash-tokens — Token Optimization for Context Engineers
|
|
486
|
+
|
|
487
|
+
bunx slash-tokens try (scan this directory, no account)
|
|
488
|
+
npm install slash-tokens SDK
|
|
489
|
+
|
|
490
|
+
--version, -V print version and exit
|
|
491
|
+
--help, -h print this help and exit
|
|
492
|
+
--key=KEY optional; CLI scans never charge
|
|
493
|
+
|
|
494
|
+
Run in a project that already calls an LLM.
|
|
495
|
+
Empty folder → no call sites. That's normal.`);
|
|
496
|
+
}
|
package/dist/intercept.d.ts
CHANGED
|
@@ -11,6 +11,14 @@ export interface InterceptEvent {
|
|
|
11
11
|
routed: boolean;
|
|
12
12
|
timestamp: string;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* The model a request actually names, for PRICING: the exact table entry when
|
|
16
|
+
* the strict canonical ID matches (claude-opus-5-5 → claude-opus-5.5,
|
|
17
|
+
* gpt-6-luna), else the legacy family mapping. Routing keeps using
|
|
18
|
+
* normalizeModel() so /auto decisions are unchanged within 1.6.x.
|
|
19
|
+
*/
|
|
20
|
+
export declare function identifyModel(raw: string): string;
|
|
14
21
|
export declare function normalizeModel(raw: string): string;
|
|
22
|
+
export declare function findCheapestRoute(provider: string, tokens: number, currentModel: string): string | null;
|
|
15
23
|
export declare function onIntercept(handler: (event: InterceptEvent) => void): void;
|
|
16
24
|
export declare function patchFetch(): void;
|
package/dist/intercept.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { slash } from './slash.js';
|
|
2
|
-
import { getModel } from './models.js';
|
|
2
|
+
import { getModel, MODELS, canonicalModel } from './models.js';
|
|
3
3
|
import { shouldRoute, isModelAllowed } from './config.js';
|
|
4
|
-
import { PROVIDER_MODELS } from './providers.js';
|
|
4
|
+
import { PROVIDER_MODELS, AUTO_ROUTE_TARGETS, NOT_ROUTE_TARGETS } from './providers.js';
|
|
5
5
|
// Reverse lookup: model name → provider model names in the API
|
|
6
6
|
// (what to put back in the request body)
|
|
7
7
|
const MODEL_API_NAMES = {
|
|
@@ -62,6 +62,16 @@ const AI_ENDPOINTS = [
|
|
|
62
62
|
// (getModel() returning undefined → $0 reported cost, see slash.ts's
|
|
63
63
|
// DEFAULT_UNKNOWN_MODEL_FACTOR comment for why "unrecognized" defaulting
|
|
64
64
|
// to a falsely-safe-looking value is the dangerous case).
|
|
65
|
+
/**
|
|
66
|
+
* The model a request actually names, for PRICING: the exact table entry when
|
|
67
|
+
* the strict canonical ID matches (claude-opus-5-5 → claude-opus-5.5,
|
|
68
|
+
* gpt-6-luna), else the legacy family mapping. Routing keeps using
|
|
69
|
+
* normalizeModel() so /auto decisions are unchanged within 1.6.x.
|
|
70
|
+
*/
|
|
71
|
+
export function identifyModel(raw) {
|
|
72
|
+
const c = canonicalModel(raw);
|
|
73
|
+
return MODELS[c] ? c : normalizeModel(raw);
|
|
74
|
+
}
|
|
65
75
|
export function normalizeModel(raw) {
|
|
66
76
|
const lower = raw.toLowerCase();
|
|
67
77
|
// Anthropic — specific versions before generic family
|
|
@@ -148,7 +158,7 @@ function extractContent(body) {
|
|
|
148
158
|
return JSON.stringify(body);
|
|
149
159
|
}
|
|
150
160
|
// Find cheapest model from same provider that fits
|
|
151
|
-
function findCheapestRoute(provider, tokens, currentModel) {
|
|
161
|
+
export function findCheapestRoute(provider, tokens, currentModel) {
|
|
152
162
|
const providerModels = PROVIDER_MODELS[provider];
|
|
153
163
|
if (!providerModels)
|
|
154
164
|
return null;
|
|
@@ -156,6 +166,10 @@ function findCheapestRoute(provider, tokens, currentModel) {
|
|
|
156
166
|
for (const model of providerModels) {
|
|
157
167
|
if (model === currentModel)
|
|
158
168
|
continue;
|
|
169
|
+
if (!AUTO_ROUTE_TARGETS.has(model))
|
|
170
|
+
continue; // 1.6.x: /auto rewrites only to its 1.6.5 targets
|
|
171
|
+
if (NOT_ROUTE_TARGETS.has(model))
|
|
172
|
+
continue; // specialised models are never targets
|
|
159
173
|
if (!isModelAllowed(model))
|
|
160
174
|
continue; // user excluded this model
|
|
161
175
|
const info = getModel(model);
|
|
@@ -188,13 +202,14 @@ export function patchFetch() {
|
|
|
188
202
|
const body = JSON.parse(bodyStr);
|
|
189
203
|
const content = extractContent(body);
|
|
190
204
|
const rawModel = match.modelExtractor(body, url);
|
|
191
|
-
const
|
|
205
|
+
const routingModel = normalizeModel(rawModel); // decides routes (unchanged in 1.6.x)
|
|
206
|
+
const originalModel = identifyModel(rawModel); // prices the request it really names
|
|
192
207
|
const tokens = slash(content, originalModel);
|
|
193
208
|
const originalInfo = getModel(originalModel);
|
|
194
209
|
const originalCost = originalInfo ? Math.round(((tokens / 1000000) * originalInfo.input) * 1000000) / 1000000 : 0;
|
|
195
210
|
const fits = originalInfo ? tokens <= originalInfo.context : true;
|
|
196
211
|
// Find cheapest route within same provider (if routing enabled)
|
|
197
|
-
const routeModel = shouldRoute() ? findCheapestRoute(match.provider, tokens,
|
|
212
|
+
const routeModel = shouldRoute() ? findCheapestRoute(match.provider, tokens, routingModel) : null;
|
|
198
213
|
const routedInfo = routeModel ? getModel(routeModel) : null;
|
|
199
214
|
const routedCost = routedInfo ? Math.round(((tokens / 1000000) * routedInfo.input) * 1000000) / 1000000 : originalCost;
|
|
200
215
|
const salvaged = routeModel ? Math.round((originalCost - routedCost) * 1000000) / 1000000 : 0;
|
package/dist/models.d.ts
CHANGED
|
@@ -7,6 +7,13 @@ export interface ModelInfo {
|
|
|
7
7
|
longContextOutput?: number;
|
|
8
8
|
}
|
|
9
9
|
export declare const MODELS: Record<string, ModelInfo>;
|
|
10
|
+
/**
|
|
11
|
+
* Real API IDs → table keys, strictly: lowercase, drop a trailing date stamp
|
|
12
|
+
* (`-20250514`), and write version numbers with dots (`claude-opus-4-7` →
|
|
13
|
+
* `claude-opus-4.7`). No family guessing: an unknown version stays unknown,
|
|
14
|
+
* so a new model never silently gets an older model's price.
|
|
15
|
+
*/
|
|
16
|
+
export declare function canonicalModel(name: string): string;
|
|
10
17
|
export declare function getModel(name: string): ModelInfo | undefined;
|
|
11
18
|
export declare function effectiveRate(tokens: number, info: ModelInfo): {
|
|
12
19
|
input: number;
|
package/dist/models.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
const OPUS = { input: 5.00, output: 25.00, context: 1000000 };
|
|
2
|
+
const OPUS_55 = { input: 4.00, output: 20.00, context: 1000000 };
|
|
3
|
+
const FABLE = { input: 10.00, output: 50.00, context: 1000000 };
|
|
4
|
+
const SONNET_4X = { input: 3.00, output: 15.00, context: 1000000 };
|
|
2
5
|
const SONNET = { input: 2.00, output: 10.00, context: 1000000 };
|
|
3
6
|
const HAIKU = { input: 1.00, output: 5.00, context: 200000 };
|
|
4
7
|
const GROK_46 = {
|
|
@@ -14,13 +17,25 @@ const GEMINI_PRO = {
|
|
|
14
17
|
longContextThreshold: 200000, longContextInput: 4.00, longContextOutput: 18.00,
|
|
15
18
|
};
|
|
16
19
|
const GEMINI_FLASH = { input: 0.30, output: 2.50, context: 1000000 };
|
|
20
|
+
// Gemini 3.6–3.8 Flash: launch price through 2026-12-31; Google lists $1.50/$7.50
|
|
21
|
+
// from 2027-01-01. Update this entry before then.
|
|
22
|
+
const GEMINI_FLASH_3X = { input: 0.75, output: 3.75, context: 1000000 };
|
|
23
|
+
const GEMINI_35_FLASH = { input: 1.50, output: 9.00, context: 1000000 };
|
|
24
|
+
const GEMINI_31_FLASH_LITE = { input: 0.25, output: 1.50, context: 1000000 };
|
|
25
|
+
const GROK_BUILD = {
|
|
26
|
+
input: 1.00, output: 2.00, context: 256000,
|
|
27
|
+
longContextThreshold: 200000, longContextInput: 2.00, longContextOutput: 4.00,
|
|
28
|
+
};
|
|
29
|
+
const GPT_6_ASTRA = { input: 10.00, output: 50.00, context: 1050000 };
|
|
30
|
+
const GPT_6_SOL = { input: 2.00, output: 10.00, context: 1050000 };
|
|
31
|
+
const GPT_6_LUNA = { input: 0.10, output: 0.50, context: 1050000 };
|
|
17
32
|
const GPT_SOL = { input: 4.00, output: 20.00, context: 1050000 };
|
|
18
33
|
const GPT_TERRA = { input: 2.00, output: 12.00, context: 1050000 };
|
|
19
34
|
const GPT_LUNA = { input: 0.20, output: 1.20, context: 1050000 };
|
|
20
35
|
const GPT_54 = { input: 2.50, output: 15.00, context: 1000000 };
|
|
21
36
|
const GPT_54_MINI = { input: 0.75, output: 4.50, context: 128000 };
|
|
22
37
|
const GPT_54_NANO = { input: 0.20, output: 1.25, context: 128000 };
|
|
23
|
-
// Pricing as of 2026-
|
|
38
|
+
// Pricing as of 2026-10-07 — USD per million tokens (checked against the four pages below).
|
|
24
39
|
// First-party: platform.claude.com/docs/en/about-claude/pricing
|
|
25
40
|
// developers.openai.com/api/docs/models
|
|
26
41
|
// docs.x.ai/developers/models
|
|
@@ -28,23 +43,47 @@ const GPT_54_NANO = { input: 0.20, output: 1.25, context: 128000 };
|
|
|
28
43
|
// Old keys stay as aliases so existing call sites don't throw.
|
|
29
44
|
export const MODELS = {
|
|
30
45
|
// Anthropic — live names + generic aliases (same rates)
|
|
46
|
+
'claude-fable-5.1': { ...FABLE },
|
|
47
|
+
'claude-fable-5': { ...FABLE },
|
|
48
|
+
'claude-mythos-5.1': { ...FABLE },
|
|
49
|
+
'claude-mythos-5': { ...FABLE },
|
|
50
|
+
'claude-opus-5.5': { ...OPUS_55 },
|
|
31
51
|
'claude-opus-5': { ...OPUS },
|
|
52
|
+
'claude-opus-4.8': { ...OPUS },
|
|
32
53
|
'claude-opus': { ...OPUS },
|
|
33
54
|
'claude-opus-4.7': { ...OPUS },
|
|
55
|
+
'claude-opus-4.6': { ...OPUS },
|
|
56
|
+
'claude-opus-4.5': { ...OPUS },
|
|
57
|
+
'claude-sonnet-5.5': { ...SONNET },
|
|
34
58
|
'claude-sonnet-5': { ...SONNET },
|
|
35
59
|
'claude-sonnet': { ...SONNET },
|
|
60
|
+
'claude-sonnet-4.6': { ...SONNET_4X },
|
|
61
|
+
'claude-sonnet-4.5': { ...SONNET_4X },
|
|
36
62
|
'claude-haiku-4.5': { ...HAIKU },
|
|
37
63
|
'claude-haiku': { ...HAIKU },
|
|
38
64
|
// xAI — flagship 4.6, cheap same-provider 4.3. 4.20 / fast are aliases.
|
|
65
|
+
'grok-4.7': { ...GROK_46 },
|
|
39
66
|
'grok-4.6': { ...GROK_46 },
|
|
67
|
+
'grok-4.5': { ...GROK_46 },
|
|
68
|
+
'grok-build-0.1': { ...GROK_BUILD },
|
|
40
69
|
'grok-4.3': { ...GROK_43 },
|
|
41
70
|
'grok-4.20': { ...GROK_43 },
|
|
42
71
|
'grok-4-1-fast': { ...GROK_43 },
|
|
43
72
|
// Google
|
|
44
73
|
'gemini-3.1-pro': { ...GEMINI_PRO },
|
|
74
|
+
'gemini-3.1-pro-preview': { ...GEMINI_PRO },
|
|
75
|
+
'gemini-3.8-flash': { ...GEMINI_FLASH_3X },
|
|
76
|
+
'gemini-3.7-flash': { ...GEMINI_FLASH_3X },
|
|
77
|
+
'gemini-3.6-flash': { ...GEMINI_FLASH_3X },
|
|
78
|
+
'gemini-3.5-flash': { ...GEMINI_35_FLASH },
|
|
79
|
+
'gemini-3.1-flash-lite': { ...GEMINI_31_FLASH_LITE },
|
|
45
80
|
'gemini-3.5-flash-lite': { ...GEMINI_FLASH },
|
|
46
81
|
'gemini-2.5-flash': { ...GEMINI_FLASH },
|
|
47
82
|
// OpenAI — live 5.6 ladder. 5.4 family kept as aliases (old prices).
|
|
83
|
+
'gpt-6-astra': { ...GPT_6_ASTRA },
|
|
84
|
+
'gpt-6.1-sol': { ...GPT_6_SOL },
|
|
85
|
+
'gpt-6-sol': { ...GPT_6_SOL },
|
|
86
|
+
'gpt-6-luna': { ...GPT_6_LUNA },
|
|
48
87
|
'gpt-5.6-sol': { ...GPT_SOL },
|
|
49
88
|
'gpt-5.6-terra': { ...GPT_TERRA },
|
|
50
89
|
'gpt-5.6-luna': { ...GPT_LUNA },
|
|
@@ -52,8 +91,28 @@ export const MODELS = {
|
|
|
52
91
|
'gpt-5.4-mini': { ...GPT_54_MINI },
|
|
53
92
|
'gpt-5.4-nano': { ...GPT_54_NANO },
|
|
54
93
|
};
|
|
94
|
+
/**
|
|
95
|
+
* Real API IDs → table keys, strictly: lowercase, drop a trailing date stamp
|
|
96
|
+
* (`-20250514`), and write version numbers with dots (`claude-opus-4-7` →
|
|
97
|
+
* `claude-opus-4.7`). No family guessing: an unknown version stays unknown,
|
|
98
|
+
* so a new model never silently gets an older model's price.
|
|
99
|
+
*/
|
|
100
|
+
export function canonicalModel(name) {
|
|
101
|
+
if (MODELS[name])
|
|
102
|
+
return name;
|
|
103
|
+
let n = name.trim().toLowerCase();
|
|
104
|
+
if (MODELS[n])
|
|
105
|
+
return n;
|
|
106
|
+
n = n.replace(/-\d{8}$/, '');
|
|
107
|
+
if (MODELS[n])
|
|
108
|
+
return n;
|
|
109
|
+
const dotted = n.replace(/(\d)-(\d)(?=$|-)/g, '$1.$2');
|
|
110
|
+
if (MODELS[dotted])
|
|
111
|
+
return dotted;
|
|
112
|
+
return n;
|
|
113
|
+
}
|
|
55
114
|
export function getModel(name) {
|
|
56
|
-
return MODELS[name
|
|
115
|
+
return MODELS[canonicalModel(name)];
|
|
57
116
|
}
|
|
58
117
|
export function effectiveRate(tokens, info) {
|
|
59
118
|
if (info.longContextThreshold !== undefined && tokens > info.longContextThreshold) {
|
package/dist/preflight.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { slash } from './slash.js';
|
|
2
|
-
import { getModel, MODELS, effectiveRate } from './models.js';
|
|
3
|
-
import { PROVIDER_MODELS, providerOf } from './providers.js';
|
|
2
|
+
import { getModel, MODELS, effectiveRate, canonicalModel } from './models.js';
|
|
3
|
+
import { PROVIDER_MODELS, providerOf, NOT_ROUTE_TARGETS } from './providers.js';
|
|
4
4
|
import { shouldRoute, isModelAllowed } from './config.js';
|
|
5
5
|
/**
|
|
6
6
|
* Compute cost of a prompt of `tokens` tokens on the given ModelInfo.
|
|
@@ -99,9 +99,12 @@ export function preflightRoute(content, model) {
|
|
|
99
99
|
return null;
|
|
100
100
|
const originalCost = computeCost(tokens, info);
|
|
101
101
|
let cheapest = null;
|
|
102
|
+
const self = canonicalModel(model);
|
|
102
103
|
for (const m of providerModels) {
|
|
103
|
-
if (m ===
|
|
104
|
+
if (m === self)
|
|
104
105
|
continue;
|
|
106
|
+
if (NOT_ROUTE_TARGETS.has(m))
|
|
107
|
+
continue; // specialised: never a target
|
|
105
108
|
if (!isModelAllowed(m))
|
|
106
109
|
continue; // user excluded this model
|
|
107
110
|
const altInfo = getModel(m);
|
|
@@ -112,7 +115,10 @@ export function preflightRoute(content, model) {
|
|
|
112
115
|
if (altInfo.input >= info.input)
|
|
113
116
|
continue; // not cheaper
|
|
114
117
|
const alt = buildAlternative(m, originalCost, tokens, altInfo);
|
|
115
|
-
|
|
118
|
+
// Tiny prompts round costs to the same value; break ties on the list price
|
|
119
|
+
// so the genuinely cheaper model wins (gpt-5.6-sol → gpt-6-luna, not 5.4-nano).
|
|
120
|
+
if (!cheapest || alt.cost < cheapest.cost ||
|
|
121
|
+
(alt.cost === cheapest.cost && altInfo.input < (getModel(cheapest.model)?.input ?? Infinity))) {
|
|
116
122
|
cheapest = alt;
|
|
117
123
|
}
|
|
118
124
|
}
|
package/dist/providers.d.ts
CHANGED
|
@@ -8,4 +8,17 @@
|
|
|
8
8
|
* strictly-cheaper hit wins (findCheapestRoute / preflightRoute).
|
|
9
9
|
*/
|
|
10
10
|
export declare const PROVIDER_MODELS: Record<string, string[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Priced and grouped, but never a routing TARGET: specialised models a general
|
|
13
|
+
* call shouldn't be moved to (grok-build-0.1 is a coding-agent model).
|
|
14
|
+
*/
|
|
15
|
+
export declare const NOT_ROUTE_TARGETS: ReadonlySet<string>;
|
|
16
|
+
/**
|
|
17
|
+
* Models `slash-tokens/auto` may rewrite a live request TO. Frozen at the
|
|
18
|
+
* 1.6.5 set so a patch release never changes where production calls go:
|
|
19
|
+
* models added in 1.6.6 (GPT-6, Gemini 3.1 Flash-Lite / 3.6–3.8 Flash, Grok
|
|
20
|
+
* 4.7 / 4.5, Claude 5.5 / Fable …) are priced and shown by preflight() /
|
|
21
|
+
* preflightRoute(), but /auto only starts routing to them in 1.7.0.
|
|
22
|
+
*/
|
|
23
|
+
export declare const AUTO_ROUTE_TARGETS: ReadonlySet<string>;
|
|
11
24
|
export declare function providerOf(model: string): string | null;
|
package/dist/providers.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { canonicalModel } from './models.js';
|
|
1
2
|
/**
|
|
2
3
|
* Provider groups — single source of truth.
|
|
3
4
|
*
|
|
@@ -9,21 +10,49 @@
|
|
|
9
10
|
*/
|
|
10
11
|
export const PROVIDER_MODELS = {
|
|
11
12
|
Anthropic: [
|
|
12
|
-
'claude-
|
|
13
|
-
'claude-
|
|
13
|
+
'claude-fable-5.1', 'claude-fable-5', 'claude-mythos-5.1', 'claude-mythos-5',
|
|
14
|
+
'claude-opus-5', 'claude-opus', 'claude-opus-4.8', 'claude-opus-4.7',
|
|
15
|
+
'claude-opus-4.6', 'claude-opus-4.5', 'claude-opus-5.5',
|
|
16
|
+
'claude-sonnet-4.6', 'claude-sonnet-4.5',
|
|
17
|
+
'claude-sonnet-5.5', 'claude-sonnet-5', 'claude-sonnet',
|
|
14
18
|
'claude-haiku', 'claude-haiku-4.5',
|
|
15
19
|
],
|
|
16
20
|
OpenAI: [
|
|
21
|
+
'gpt-6-astra',
|
|
17
22
|
'gpt-5.6-sol', 'gpt-5.6-terra',
|
|
18
|
-
'gpt-5.4', 'gpt-5.4-mini', 'gpt-5.4-nano',
|
|
19
|
-
'gpt-5.6-luna',
|
|
23
|
+
'gpt-5.4', 'gpt-6.1-sol', 'gpt-6-sol', 'gpt-5.4-mini', 'gpt-5.4-nano',
|
|
24
|
+
'gpt-5.6-luna', 'gpt-6-luna',
|
|
25
|
+
],
|
|
26
|
+
xAI: ['grok-4.7', 'grok-4.6', 'grok-4.5', 'grok-4.3', 'grok-4.20', 'grok-4-1-fast', 'grok-build-0.1'],
|
|
27
|
+
Google: [
|
|
28
|
+
'gemini-3.1-pro', 'gemini-3.1-pro-preview',
|
|
29
|
+
'gemini-3.5-flash', 'gemini-3.8-flash', 'gemini-3.7-flash', 'gemini-3.6-flash',
|
|
30
|
+
'gemini-3.5-flash-lite', 'gemini-2.5-flash', 'gemini-3.1-flash-lite',
|
|
20
31
|
],
|
|
21
|
-
xAI: ['grok-4.6', 'grok-4.3', 'grok-4.20', 'grok-4-1-fast'],
|
|
22
|
-
Google: ['gemini-3.1-pro', 'gemini-3.5-flash-lite', 'gemini-2.5-flash'],
|
|
23
32
|
};
|
|
33
|
+
/**
|
|
34
|
+
* Priced and grouped, but never a routing TARGET: specialised models a general
|
|
35
|
+
* call shouldn't be moved to (grok-build-0.1 is a coding-agent model).
|
|
36
|
+
*/
|
|
37
|
+
export const NOT_ROUTE_TARGETS = new Set(['grok-build-0.1']);
|
|
38
|
+
/**
|
|
39
|
+
* Models `slash-tokens/auto` may rewrite a live request TO. Frozen at the
|
|
40
|
+
* 1.6.5 set so a patch release never changes where production calls go:
|
|
41
|
+
* models added in 1.6.6 (GPT-6, Gemini 3.1 Flash-Lite / 3.6–3.8 Flash, Grok
|
|
42
|
+
* 4.7 / 4.5, Claude 5.5 / Fable …) are priced and shown by preflight() /
|
|
43
|
+
* preflightRoute(), but /auto only starts routing to them in 1.7.0.
|
|
44
|
+
*/
|
|
45
|
+
export const AUTO_ROUTE_TARGETS = new Set([
|
|
46
|
+
'claude-opus-5', 'claude-opus', 'claude-opus-4.7', 'claude-sonnet-5', 'claude-sonnet',
|
|
47
|
+
'claude-haiku', 'claude-haiku-4.5',
|
|
48
|
+
'gpt-5.6-sol', 'gpt-5.6-terra', 'gpt-5.4', 'gpt-5.4-mini', 'gpt-5.4-nano', 'gpt-5.6-luna',
|
|
49
|
+
'grok-4.6', 'grok-4.3', 'grok-4.20', 'grok-4-1-fast',
|
|
50
|
+
'gemini-3.1-pro', 'gemini-3.5-flash-lite', 'gemini-2.5-flash',
|
|
51
|
+
]);
|
|
24
52
|
export function providerOf(model) {
|
|
53
|
+
const id = canonicalModel(model);
|
|
25
54
|
for (const [provider, models] of Object.entries(PROVIDER_MODELS)) {
|
|
26
|
-
if (models.includes(
|
|
55
|
+
if (models.includes(id))
|
|
27
56
|
return provider;
|
|
28
57
|
}
|
|
29
58
|
return null;
|
package/dist/report.js
CHANGED
|
@@ -24,6 +24,7 @@ export function printReport(sites, filesScanned, timeMs, cwd) {
|
|
|
24
24
|
if (sites.length === 0) {
|
|
25
25
|
console.log(`${WHITE} No AI API call sites detected.${R}`);
|
|
26
26
|
console.log(`${GRAY} Supported: OpenAI, Anthropic, Vercel AI, LangChain, Gemini, Bedrock, Grok${R}`);
|
|
27
|
+
console.log(`${GRAY} Run this in a project that already calls an LLM.${R}`);
|
|
27
28
|
console.log('');
|
|
28
29
|
return;
|
|
29
30
|
}
|
package/dist/slash.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getInstance, writeToMemory, ensureCapacity } from './wasm.js';
|
|
2
|
-
|
|
1
|
+
import { getInstance, writeToMemory, ensureCapacity, WASM_INPUT_OFFSET } from './wasm.js';
|
|
2
|
+
import { canonicalModel } from './models.js';
|
|
3
3
|
/**
|
|
4
4
|
* Per-model calibration factors.
|
|
5
5
|
* Factor > 1.0 means "model uses more tokens than WASM predicts."
|
|
@@ -94,7 +94,18 @@ const WASM_INPUT_OFFSET = 4096;
|
|
|
94
94
|
* Slash must NEVER under-report. Over-reporting is safe (go/no-go only).
|
|
95
95
|
*/
|
|
96
96
|
const CALIBRATION = {
|
|
97
|
+
'claude-fable-5.1': 2.05,
|
|
98
|
+
'claude-fable-5': 2.05,
|
|
99
|
+
'claude-mythos-5.1': 2.05,
|
|
100
|
+
'claude-mythos-5': 2.05,
|
|
101
|
+
'claude-opus-5.5': 2.05,
|
|
97
102
|
'claude-opus-5': 2.05,
|
|
103
|
+
'claude-opus-4.8': 2.05,
|
|
104
|
+
'claude-opus-4.6': 2.05,
|
|
105
|
+
'claude-opus-4.5': 2.05,
|
|
106
|
+
'claude-sonnet-5.5': 2.05,
|
|
107
|
+
'claude-sonnet-4.6': 2.05,
|
|
108
|
+
'claude-sonnet-4.5': 2.05,
|
|
98
109
|
'claude-opus': 2.05,
|
|
99
110
|
'claude-opus-4.7': 2.05,
|
|
100
111
|
'claude-sonnet-5': 2.05,
|
|
@@ -102,9 +113,18 @@ const CALIBRATION = {
|
|
|
102
113
|
'claude-haiku-4.5': 1.45,
|
|
103
114
|
'claude-haiku': 1.45,
|
|
104
115
|
'gemini-3.1-pro': 1.45,
|
|
116
|
+
'gemini-3.1-pro-preview': 1.45,
|
|
117
|
+
'gemini-3.8-flash': 1.45,
|
|
118
|
+
'gemini-3.7-flash': 1.45,
|
|
119
|
+
'gemini-3.6-flash': 1.45,
|
|
120
|
+
'gemini-3.5-flash': 1.45,
|
|
121
|
+
'gemini-3.1-flash-lite': 1.45,
|
|
105
122
|
'gemini-3.5-flash-lite': 1.45,
|
|
106
123
|
'gemini-2.5-flash': 1.45,
|
|
124
|
+
'grok-4.7': 1.15,
|
|
107
125
|
'grok-4.6': 1.15,
|
|
126
|
+
'grok-4.5': 1.15,
|
|
127
|
+
'grok-build-0.1': 1.15,
|
|
108
128
|
'grok-4.3': 1.15,
|
|
109
129
|
'grok-4.20': 1.15,
|
|
110
130
|
'grok-4-1-fast': 1.15,
|
|
@@ -141,7 +161,10 @@ export function slash(content, model) {
|
|
|
141
161
|
const raw = instance.exports.estimate_tokens(WASM_INPUT_OFFSET, len);
|
|
142
162
|
if (!model)
|
|
143
163
|
return raw;
|
|
144
|
-
|
|
164
|
+
// GPT-6 (gpt-6-astra / 6.1-sol / 6-sol / 6-luna) is deliberately NOT in
|
|
165
|
+
// CALIBRATION: a new generation with an unbenchmarked tokenizer takes the
|
|
166
|
+
// conservative default (never under-report) until a bench run adds it.
|
|
167
|
+
const factor = CALIBRATION[canonicalModel(model)] ?? DEFAULT_UNKNOWN_MODEL_FACTOR;
|
|
145
168
|
return factor === 1.0 ? raw : Math.ceil(raw * factor);
|
|
146
169
|
}
|
|
147
170
|
/**
|
package/dist/wasm.d.ts
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where input bytes are written: 1,114,112, the end of the module's initial
|
|
3
|
+
* memory (17 pages). The module's stack grows down from 1,048,576 and its
|
|
4
|
+
* lookup tables sit at 1,048,576–1,049,916; nothing lives above them.
|
|
5
|
+
*
|
|
6
|
+
* Fixed 2026-10-07: input used to start at 4096, so any input over ~1.04 MB ran
|
|
7
|
+
* over the stack and then the lookup tables, and every later estimate in
|
|
8
|
+
* that process came out wrong (a 14-token prompt read as 28). Input above
|
|
9
|
+
* the tables can't reach them, whatever its size.
|
|
10
|
+
*/
|
|
11
|
+
export declare const WASM_INPUT_OFFSET = 1114112;
|
|
1
12
|
export declare function getInstance(): WebAssembly.Instance;
|
|
2
13
|
/**
|
|
3
14
|
* Grow WASM linear memory if needed so `neededBytes` of input (plus the
|
package/dist/wasm.js
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
// Auto-generated WASM binary (4,865 bytes / xai-faf-zig ghost engine)
|
|
2
2
|
// Contains estimate_tokens(ptr, len) -> u32
|
|
3
3
|
const WASM_BASE64 = "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AA4AAABLAxAAEwAAALABEAAQAAAAGgIQABYAAAAwBBAADQAAAJ0DEAAOAAAAsQMQAA0AAAB6AxAADgAAAMsCEAAQAAAAbAMQAA0AAAA+BBAACwAAAFYEEAAKAAAAvwMQAAwAAAAjAxAADAAAAP0DEAAVAAAAsQIQABEAAAAxAhAAEgAAAMEBEAARAAAAiQMQABMAAADoAhAAEgAAAPABEAARAAAAIQQQAA4AAABLAxAAEwAAALABEAAQAAAAGgIQABYAAAAwBBAADQAAAJ0DEAAOAAAAsQMQAA0AAAB6AxAADgAAAMsCEAAQAAAAbAMQAA0AAAA+BBAACwAAAFYEEAAKAAAACgMQABMAAACbAhAAFQAAAG4CEAAQAAAAAgIQABcAAAB/AhAAGwAAANwCEAALAAAA8QMQAAsAAABfAxAADAAAABMEEAANAAAA0wEQABwAAABWAhAAFwAAANsDEAAVAAAAc3RhY2sudWlfbGlicmFyeQBodW1hbl9jb250ZXh0LndoeQBtb25vcmVwby52ZXJzaW9uaW5nX3N0cmF0ZWd5AGh1bWFuX2NvbnRleHQuaG93AG1vbm9yZXBvLnBhY2thZ2VzX2NvdW50AHN0YWNrLnN0YXRlX21hbmFnZW1lbnQAaHVtYW5fY29udGV4dC53aGF0AGRldmVsb3BtZW50IHRlYW1zAG1vbm9yZXBvLnNoYXJlZF9jb25maWdzAHN0YWNrLndvcmtzcGFjZXMAbW9ub3JlcG8uYnVpbGRfb3JjaGVzdHJhdG9yAHN0YWNrLnBhY2thZ2VfbWFuYWdlcgBodW1hbl9jb250ZXh0LndobwB1bmtub3duAHN0YWNrLmNvbm5lY3Rpb24Ac3RhY2suYWRtaW4AaHVtYW5fY29udGV4dC53aGVuAGNsb3VkIHBsYXRmb3JtAHN0YWNrLm1vbm9yZXBvX3Rvb2wAbnVsbABwcm9qZWN0LmdvYWwAZGVzY3JpYmUgeW91ciBwcm9qZWN0IGdvYWwAc3RhY2suY3NzX2ZyYW1ld29yawBzdGFjay5zZWFyY2gAc3RhY2suaG9zdGluZwBzdGFjay5kYXRhYmFzZQBodW1hbl9jb250ZXh0LndoZXJlAHN0YWNrLmFwaV90eXBlAG5vbmUAc3RhY2sucnVudGltZQBwcm9qZWN0Lm5hbWUAbm90IGFwcGxpY2FibGUAbW9ub3JlcG8ucmVtb3RlX2NhY2hlAHN0YWNrLmNhY2hlAHByb2plY3QubWFpbl9sYW5ndWFnZQBzdGFjay5zdG9yYWdlAHN0YWNrLmZyb250ZW5kAHN0YWNrLmJhY2tlbmQAc3RhY2suYnVpbGQAc2xvdGlnbm9yZWQAc3RhY2suY2ljZABuL2EAOgAuACAJDQoiJwAgCSInAA0ACgAgCQAwAxAAGgAAAEQCEAARAAAA+wIQAA4AAAAeAxAABAAAAKwDEAAEAAAAwwIQAAcAAABhBBAAAwAAAMwDEAAOAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAAAAAAAAAAA=";
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Where input bytes are written: 1,114,112, the end of the module's initial
|
|
6
|
+
* memory (17 pages). The module's stack grows down from 1,048,576 and its
|
|
7
|
+
* lookup tables sit at 1,048,576–1,049,916; nothing lives above them.
|
|
8
|
+
*
|
|
9
|
+
* Fixed 2026-10-07: input used to start at 4096, so any input over ~1.04 MB ran
|
|
10
|
+
* over the stack and then the lookup tables, and every later estimate in
|
|
11
|
+
* that process came out wrong (a 14-token prompt read as 28). Input above
|
|
12
|
+
* the tables can't reach them, whatever its size.
|
|
13
|
+
*/
|
|
14
|
+
export const WASM_INPUT_OFFSET = 1114112;
|
|
5
15
|
const encoder = new TextEncoder();
|
|
6
16
|
let _instance = null;
|
|
7
17
|
function decodeBase64(b64) {
|
|
@@ -51,7 +61,7 @@ const WASM_PAGE_BYTES = 65536;
|
|
|
51
61
|
* do lazily on every call with no effect on correctness for small inputs.
|
|
52
62
|
*/
|
|
53
63
|
export function ensureCapacity(memory, neededBytes) {
|
|
54
|
-
const cappedNeeded = Math.min(neededBytes, MAX_WASM_INPUT_BYTES);
|
|
64
|
+
const cappedNeeded = Math.min(neededBytes, WASM_INPUT_OFFSET + MAX_WASM_INPUT_BYTES);
|
|
55
65
|
if (memory.buffer.byteLength >= cappedNeeded)
|
|
56
66
|
return;
|
|
57
67
|
const additionalPages = Math.ceil((cappedNeeded - memory.buffer.byteLength) / WASM_PAGE_BYTES);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "slash-tokens",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.7",
|
|
4
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",
|
|
@@ -26,8 +26,10 @@
|
|
|
26
26
|
],
|
|
27
27
|
"sideEffects": false,
|
|
28
28
|
"scripts": {
|
|
29
|
+
"prepublishOnly": "npm run build",
|
|
29
30
|
"build": "npx tsc && bun build src/cli.ts --outfile=dist/cli.js --target=node",
|
|
30
|
-
"test": "bun test"
|
|
31
|
+
"test": "bun test",
|
|
32
|
+
"check:engines": "node scripts/check-engines.mjs"
|
|
31
33
|
},
|
|
32
34
|
"author": "wolfejam",
|
|
33
35
|
"license": "MIT",
|
|
@@ -54,5 +56,8 @@
|
|
|
54
56
|
"@types/node": "^25.5.0",
|
|
55
57
|
"js-tiktoken": "^1.0.21",
|
|
56
58
|
"typescript": "^6.0.2"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=22.0.0"
|
|
57
62
|
}
|
|
58
63
|
}
|