tokenmaxxing-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -0
- package/dist/bucket.js +56 -0
- package/dist/cli.js +283 -0
- package/dist/config.js +49 -0
- package/dist/cursors.js +24 -0
- package/dist/dedup.js +46 -0
- package/dist/detect.js +30 -0
- package/dist/doctor.js +48 -0
- package/dist/format.js +49 -0
- package/dist/fsutil.js +61 -0
- package/dist/hook.js +194 -0
- package/dist/lines.js +61 -0
- package/dist/parsers/claude.js +203 -0
- package/dist/parsers/codex.js +201 -0
- package/dist/parsers/context.js +9 -0
- package/dist/parsers/cursor.js +11 -0
- package/dist/parsers/gemini.js +110 -0
- package/dist/paths.js +32 -0
- package/dist/period.js +37 -0
- package/dist/plans.js +169 -0
- package/dist/plans.json +6 -0
- package/dist/pricing/index.js +190 -0
- package/dist/pricing/litellm.snapshot.json +1 -0
- package/dist/pricing/modelsdev.snapshot.json +1 -0
- package/dist/pricing/overrides.json +4 -0
- package/dist/prompt.js +61 -0
- package/dist/report.js +186 -0
- package/dist/schedule.js +160 -0
- package/dist/site.js +196 -0
- package/dist/store.js +59 -0
- package/dist/sync.js +115 -0
- package/dist/types.js +9 -0
- package/dist/verify.js +118 -0
- package/package.json +46 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
export const SNAPSHOT_DATE = '2026-09-22';
|
|
3
|
+
let litellm;
|
|
4
|
+
let modelsdev;
|
|
5
|
+
let overrides;
|
|
6
|
+
function load(name) {
|
|
7
|
+
return JSON.parse(readFileSync(new URL(`./${name}`, import.meta.url), 'utf8'));
|
|
8
|
+
}
|
|
9
|
+
function tables() {
|
|
10
|
+
litellm ??= load('litellm.snapshot.json');
|
|
11
|
+
modelsdev ??= load('modelsdev.snapshot.json');
|
|
12
|
+
overrides ??= load('overrides.json');
|
|
13
|
+
return { litellm: litellm, modelsdev: modelsdev, overrides: overrides };
|
|
14
|
+
}
|
|
15
|
+
/** Test hook: replace the bundled tables (pass `undefined` to reload the bundled one). */
|
|
16
|
+
export function _setTables(t) {
|
|
17
|
+
if ('litellm' in t)
|
|
18
|
+
litellm = t.litellm;
|
|
19
|
+
if ('modelsdev' in t)
|
|
20
|
+
modelsdev = t.modelsdev;
|
|
21
|
+
if ('overrides' in t)
|
|
22
|
+
overrides = t.overrides;
|
|
23
|
+
cache.clear();
|
|
24
|
+
normIndex = undefined;
|
|
25
|
+
zeroCanon = undefined;
|
|
26
|
+
}
|
|
27
|
+
const isNum = (v) => typeof v === 'number' && Number.isFinite(v);
|
|
28
|
+
/** Note for a model that upstream lists at $0 input and $0 output: that is not a price, so it is unpriced. */
|
|
29
|
+
export const ZERO_UPSTREAM_NOTE = 'listed at $0 upstream';
|
|
30
|
+
const zeroLitellm = (e) => !!e && e.input_cost_per_token === 0 && e.output_cost_per_token === 0;
|
|
31
|
+
const zeroModelsdev = (e) => !!e?.cost && e.cost.input === 0 && e.cost.output === 0;
|
|
32
|
+
function fromLitellm(e) {
|
|
33
|
+
if (!e || !isNum(e.input_cost_per_token) || !isNum(e.output_cost_per_token) || zeroLitellm(e))
|
|
34
|
+
return null;
|
|
35
|
+
const input = e.input_cost_per_token;
|
|
36
|
+
const w5 = isNum(e.cache_creation_input_token_cost) ? e.cache_creation_input_token_cost : input;
|
|
37
|
+
return {
|
|
38
|
+
input,
|
|
39
|
+
cache_read: isNum(e.cache_read_input_token_cost) ? e.cache_read_input_token_cost : input,
|
|
40
|
+
cache_write_5m: w5,
|
|
41
|
+
cache_write_1h: isNum(e.cache_creation_input_token_cost_above_1hr) ? e.cache_creation_input_token_cost_above_1hr : w5,
|
|
42
|
+
output: e.output_cost_per_token,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function fromModelsdev(provider, e) {
|
|
46
|
+
const c = e?.cost;
|
|
47
|
+
if (!c || !isNum(c.input) || !isNum(c.output) || zeroModelsdev(e))
|
|
48
|
+
return null;
|
|
49
|
+
const input = c.input / 1e6;
|
|
50
|
+
const write = isNum(c.cache_write) ? c.cache_write / 1e6 : input;
|
|
51
|
+
return {
|
|
52
|
+
input,
|
|
53
|
+
cache_read: isNum(c.cache_read) ? c.cache_read / 1e6 : input,
|
|
54
|
+
cache_write_5m: write,
|
|
55
|
+
cache_write_1h: provider === 'anthropic' ? write * 1.6 : write,
|
|
56
|
+
output: c.output / 1e6,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* overrides.json: `{ "<model>": { input, output, cache_read?, cache_write_5m?, cache_write_1h? } }` in USD per MILLION tokens,
|
|
61
|
+
* or `{ "<model>": { "unpriced": "<note>" } }` to pin a model as unpriced with a note.
|
|
62
|
+
*/
|
|
63
|
+
function fromOverride(e) {
|
|
64
|
+
if (!e || !isNum(e.input) || !isNum(e.output))
|
|
65
|
+
return null;
|
|
66
|
+
const input = e.input / 1e6;
|
|
67
|
+
const w5 = isNum(e.cache_write_5m) ? e.cache_write_5m / 1e6 : input;
|
|
68
|
+
return {
|
|
69
|
+
input,
|
|
70
|
+
cache_read: isNum(e.cache_read) ? e.cache_read / 1e6 : input,
|
|
71
|
+
cache_write_5m: w5,
|
|
72
|
+
cache_write_1h: isNum(e.cache_write_1h) ? e.cache_write_1h / 1e6 : w5,
|
|
73
|
+
output: e.output / 1e6,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function modelsdevExact(id) {
|
|
77
|
+
const md = tables().modelsdev;
|
|
78
|
+
for (const provider of Object.keys(md)) {
|
|
79
|
+
const e = md[provider]?.models?.[id];
|
|
80
|
+
const r = e && fromModelsdev(provider, e);
|
|
81
|
+
if (r)
|
|
82
|
+
return r;
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
/** lowercase, strip provider prefix (anthropic/…, us.anthropic.…), bedrock `-v1:0` and date suffixes. */
|
|
87
|
+
export function normalize(model) {
|
|
88
|
+
let m = model.trim().toLowerCase();
|
|
89
|
+
m = m.replace(/^.*\//, '');
|
|
90
|
+
m = m.replace(/^(?:[a-z]{2,4}\.)?(?:anthropic|openai|google|meta|amazon)\./, '');
|
|
91
|
+
m = m.replace(/-v\d+(?::\d+)?$/, '');
|
|
92
|
+
m = m.replace(/[-@]\d{8}$/, '').replace(/-\d{4}-\d{2}-\d{2}$/, '');
|
|
93
|
+
return m;
|
|
94
|
+
}
|
|
95
|
+
/** Canonical form for fuzzy matching: normalised, with version dots as dashes (claude-opus-5.5 == claude-opus-5-5). */
|
|
96
|
+
export function canon(model) {
|
|
97
|
+
return normalize(model).replace(/(\d)\.(\d)/g, '$1-$2');
|
|
98
|
+
}
|
|
99
|
+
/** Candidate ids to try as exact keys, most specific first. */
|
|
100
|
+
export function candidates(model) {
|
|
101
|
+
const out = new Set();
|
|
102
|
+
for (const s of [model.trim().toLowerCase(), normalize(model)]) {
|
|
103
|
+
out.add(s);
|
|
104
|
+
out.add(s.replace(/(\d)-(\d)/g, '$1.$2'));
|
|
105
|
+
out.add(s.replace(/(\d)\.(\d)/g, '$1-$2'));
|
|
106
|
+
}
|
|
107
|
+
return [...out];
|
|
108
|
+
}
|
|
109
|
+
let normIndex;
|
|
110
|
+
let zeroCanon;
|
|
111
|
+
function litellmCanonIndex() {
|
|
112
|
+
if (normIndex)
|
|
113
|
+
return normIndex;
|
|
114
|
+
normIndex = new Map();
|
|
115
|
+
zeroCanon = new Set();
|
|
116
|
+
const ll = tables().litellm;
|
|
117
|
+
// Shortest key first so an un-prefixed id wins over bedrock/vertex variants. Entries listed at $0/$0 never
|
|
118
|
+
// take the slot (they are not prices); they are only remembered for the unpriced note.
|
|
119
|
+
const keys = Object.keys(ll).sort((a, b) => a.length - b.length || (a < b ? -1 : 1));
|
|
120
|
+
for (const k of keys) {
|
|
121
|
+
const c = canon(k);
|
|
122
|
+
if (zeroLitellm(ll[k]))
|
|
123
|
+
zeroCanon.add(c);
|
|
124
|
+
else if (!normIndex.has(c))
|
|
125
|
+
normIndex.set(c, k);
|
|
126
|
+
}
|
|
127
|
+
return normIndex;
|
|
128
|
+
}
|
|
129
|
+
/** True when some lookup step for this model hit an upstream entry listed at $0 input and $0 output. */
|
|
130
|
+
function listedAtZero(model) {
|
|
131
|
+
const { litellm: ll, modelsdev: md } = tables();
|
|
132
|
+
for (const c of [model, ...candidates(model)]) {
|
|
133
|
+
if (zeroLitellm(ll[c]))
|
|
134
|
+
return true;
|
|
135
|
+
for (const provider of Object.keys(md))
|
|
136
|
+
if (zeroModelsdev(md[provider]?.models?.[c]))
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
litellmCanonIndex();
|
|
140
|
+
return zeroCanon.has(canon(model));
|
|
141
|
+
}
|
|
142
|
+
const cache = new Map();
|
|
143
|
+
/** Rates for a model in USD/token, or null when unknown (reported as unpriced, never $0). */
|
|
144
|
+
export function resolve(model) {
|
|
145
|
+
if (cache.has(model))
|
|
146
|
+
return cache.get(model);
|
|
147
|
+
const r = resolveUncached(model);
|
|
148
|
+
cache.set(model, r);
|
|
149
|
+
return r;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Why an unpriced model is unpriced, when we know: the note of an `unpriced` pin in overrides.json (bundled
|
|
153
|
+
* models with no list price), or ZERO_UPSTREAM_NOTE when upstream lists it at $0/$0 and nothing else prices it.
|
|
154
|
+
*/
|
|
155
|
+
export function unpricedNote(model) {
|
|
156
|
+
const e = tables().overrides[model];
|
|
157
|
+
if (e && typeof e.unpriced === 'string')
|
|
158
|
+
return e.unpriced;
|
|
159
|
+
return resolve(model) === null && listedAtZero(model) ? ZERO_UPSTREAM_NOTE : undefined;
|
|
160
|
+
}
|
|
161
|
+
function resolveUncached(model) {
|
|
162
|
+
const { litellm: ll, overrides: ov } = tables();
|
|
163
|
+
if (ov[model]) {
|
|
164
|
+
if (typeof ov[model].unpriced === 'string')
|
|
165
|
+
return null; // pinned unpriced: never fuzzy-match a price
|
|
166
|
+
const r = fromOverride(ov[model]);
|
|
167
|
+
if (r)
|
|
168
|
+
return r;
|
|
169
|
+
}
|
|
170
|
+
let r = fromLitellm(ll[model]);
|
|
171
|
+
if (r)
|
|
172
|
+
return r;
|
|
173
|
+
r = modelsdevExact(model);
|
|
174
|
+
if (r)
|
|
175
|
+
return r;
|
|
176
|
+
for (const c of candidates(model)) {
|
|
177
|
+
r = fromOverride(ov[c]) ?? fromLitellm(ll[c]) ?? modelsdevExact(c);
|
|
178
|
+
if (r)
|
|
179
|
+
return r;
|
|
180
|
+
}
|
|
181
|
+
const k = litellmCanonIndex().get(canon(model));
|
|
182
|
+
return k ? fromLitellm(ll[k]) : null;
|
|
183
|
+
}
|
|
184
|
+
export function cost(b, rates) {
|
|
185
|
+
return (b.input * rates.input +
|
|
186
|
+
b.cache_read * rates.cache_read +
|
|
187
|
+
b.cache_write_5m * rates.cache_write_5m +
|
|
188
|
+
b.cache_write_1h * rates.cache_write_1h +
|
|
189
|
+
b.output * rates.output);
|
|
190
|
+
}
|