tokenometer 0.0.1

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 ADDED
@@ -0,0 +1,55 @@
1
+ # tokenometer
2
+
3
+ > Empirical token-cost benchmarking for LLM prompts. Tells you what your prompt actually costs across Claude, GPT-4o, and Gemini, in every format.
4
+
5
+ [**Live playground: tokenometer.vercel.app**](https://tokenometer.vercel.app) · [Source](https://github.com/faraa2m/tokenometer) · MIT
6
+
7
+ ```bash
8
+ npx tokenometer ./prompt.md --model claude-opus-4-7,gpt-4o
9
+ ```
10
+
11
+ ```
12
+ model format tokens est. cost tokenizer
13
+ --------------- -------- ------ --------- --------------
14
+ claude-opus-4-7 json ~78 $0.001170 cl100k_base
15
+ claude-opus-4-7 yaml ~84 $0.001260 cl100k_base
16
+ gpt-4o json 77 $0.000192 o200k_base
17
+ gpt-4o yaml 83 $0.000208 o200k_base
18
+
19
+ Cheapest: gpt-4o as json ($0.000192)
20
+ Priciest: claude-opus-4-7 as yaml ($0.001260, 6.74x more)
21
+ ```
22
+
23
+ A leading `~` marks an approximate count (offline mode for Claude / Gemini, since neither vendor publishes a public tokenizer).
24
+
25
+ ## Empirical mode
26
+
27
+ For exact, vendor-billed counts on Claude and Gemini, set the right env var and pass `--empirical`. The tool calls the providers' free `countTokens` endpoints — no charge.
28
+
29
+ ```bash
30
+ ANTHROPIC_API_KEY=… GOOGLE_API_KEY=… \
31
+ npx tokenometer ./prompt.md --empirical
32
+ ```
33
+
34
+ ## Why not just `tiktoken`?
35
+
36
+ `tiktoken`'s `cl100k_base` (the encoding most "Claude tokenizer" libraries fall back on) **under-counts Opus 4.7 by a median of +62%** across a 10-prompt benchmark. Sonnet 4.6 and Haiku 4.5 are closer (~17%). Format choice is a wash. Model choice swings cost by 12×. See [README](https://github.com/faraa2m/tokenometer#findings-anthropic-n150-cells-across-10-prompt-shapes) for the dataset findings.
37
+
38
+ ## Flags
39
+
40
+ ```
41
+ tokenometer <file> [options]
42
+ echo "prompt" | tokenometer - [options]
43
+
44
+ --model <id[,id…]> Default: claude-opus-4-7
45
+ --format <fmt[,fmt…]> Default: all (json,yaml,xml,markdown,text)
46
+ --empirical Use provider countTokens APIs (free, exact)
47
+ --max-spend <usd> Hard ceiling for empirical mode (default 0.05)
48
+ --offline Force offline (overrides --empirical)
49
+ -h, --help
50
+ -v, --version
51
+ ```
52
+
53
+ ## License
54
+
55
+ MIT
package/dist/args.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ import type { Format } from '@tokenometer/core';
2
+ export interface ParsedArgs {
3
+ empirical: boolean;
4
+ formats: Format[];
5
+ help: boolean;
6
+ inputPaths: string[];
7
+ maxSpend: number;
8
+ modelIds: string[];
9
+ offline: boolean;
10
+ version: boolean;
11
+ }
12
+ export declare const HELP_TEXT: string;
13
+ export declare const parseArgs: (argv: readonly string[]) => ParsedArgs;
14
+ //# sourceMappingURL=args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,SAAS,QAsBrB,CAAC;AAKF,eAAO,MAAM,SAAS,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,UAmFnD,CAAC"}
package/dist/args.js ADDED
@@ -0,0 +1,112 @@
1
+ import { KNOWN_MODELS, allFormats, isFormat } from '@tokenometer/core';
2
+ export const HELP_TEXT = `tokenometer — empirical token-cost benchmarking for LLM prompts
3
+
4
+ USAGE
5
+ tokenometer <file> [options]
6
+ echo "prompt" | tokenometer - [options]
7
+
8
+ OPTIONS
9
+ --model <id[,id...]> Comma-separated model ids (default: claude-opus-4-7).
10
+ Known: ${KNOWN_MODELS.join(', ')}
11
+ --format <fmt[,fmt...]> Comma-separated formats (default: all).
12
+ Known: ${allFormats().join(', ')}
13
+ --empirical Run sample API calls and report real charges.
14
+ Requires the matching <PROVIDER>_API_KEY env var.
15
+ --max-spend <usd> Hard ceiling for empirical mode (default: 0.05).
16
+ --offline Force offline mode (overrides --empirical).
17
+ -h, --help Show this help.
18
+ -v, --version Show CLI version.
19
+
20
+ EXAMPLES
21
+ tokenometer ./prompt.md
22
+ tokenometer ./prompt.md --model claude-opus-4-7,gpt-4o
23
+ tokenometer ./prompt.md --format yaml,json --empirical --max-spend 0.01
24
+ `;
25
+ const DEFAULT_MODELS = ['claude-opus-4-7'];
26
+ const DEFAULT_MAX_SPEND_USD = 0.05;
27
+ export const parseArgs = (argv) => {
28
+ const result = {
29
+ empirical: false,
30
+ formats: [...allFormats()],
31
+ help: false,
32
+ inputPaths: [],
33
+ maxSpend: DEFAULT_MAX_SPEND_USD,
34
+ modelIds: [...DEFAULT_MODELS],
35
+ offline: false,
36
+ version: false,
37
+ };
38
+ let modelsSet = false;
39
+ let formatsSet = false;
40
+ for (let i = 0; i < argv.length; i++) {
41
+ const arg = argv[i];
42
+ if (!arg)
43
+ continue;
44
+ if (arg === '-h' || arg === '--help') {
45
+ result.help = true;
46
+ continue;
47
+ }
48
+ if (arg === '-v' || arg === '--version') {
49
+ result.version = true;
50
+ continue;
51
+ }
52
+ if (arg === '--empirical') {
53
+ result.empirical = true;
54
+ continue;
55
+ }
56
+ if (arg === '--offline') {
57
+ result.offline = true;
58
+ continue;
59
+ }
60
+ if (arg === '--model') {
61
+ const next = argv[++i];
62
+ if (!next)
63
+ throw new Error('--model requires a value');
64
+ result.modelIds = next
65
+ .split(',')
66
+ .map((s) => s.trim())
67
+ .filter(Boolean);
68
+ modelsSet = true;
69
+ continue;
70
+ }
71
+ if (arg === '--format') {
72
+ const next = argv[++i];
73
+ if (!next)
74
+ throw new Error('--format requires a value');
75
+ const formats = next
76
+ .split(',')
77
+ .map((s) => s.trim())
78
+ .filter(Boolean);
79
+ for (const fmt of formats) {
80
+ if (!isFormat(fmt)) {
81
+ throw new Error(`Unknown format "${fmt}". Known: ${allFormats().join(', ')}.`);
82
+ }
83
+ }
84
+ result.formats = formats;
85
+ formatsSet = true;
86
+ continue;
87
+ }
88
+ if (arg === '--max-spend') {
89
+ const next = argv[++i];
90
+ if (!next)
91
+ throw new Error('--max-spend requires a value');
92
+ const parsed = Number.parseFloat(next);
93
+ if (!Number.isFinite(parsed) || parsed <= 0) {
94
+ throw new Error(`--max-spend must be a positive number, got "${next}".`);
95
+ }
96
+ result.maxSpend = parsed;
97
+ continue;
98
+ }
99
+ if (arg.startsWith('--')) {
100
+ throw new Error(`Unknown flag: ${arg}`);
101
+ }
102
+ result.inputPaths.push(arg);
103
+ }
104
+ if (!modelsSet && result.modelIds.length === 0) {
105
+ result.modelIds = [...DEFAULT_MODELS];
106
+ }
107
+ if (!formatsSet && result.formats.length === 0) {
108
+ result.formats = [...allFormats()];
109
+ }
110
+ return result;
111
+ };
112
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export declare const main: (argv: readonly string[]) => Promise<number>;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAkCA,eAAO,MAAM,IAAI,GAAU,MAAM,SAAS,MAAM,EAAE,KAAG,OAAO,CAAC,MAAM,CAsDlE,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env node
2
+ import { readFile } from 'node:fs/promises';
3
+ import { tokenizeMatrix, tokenizeMatrixEmpirical } from '@tokenometer/core';
4
+ import { HELP_TEXT, parseArgs } from './args.js';
5
+ import { renderSummary, renderTable } from './render.js';
6
+ const VERSION = '0.0.1';
7
+ const readEnv = () => {
8
+ const env = {};
9
+ const { ANTHROPIC_API_KEY, GEMINI_API_KEY, GOOGLE_API_KEY } = process.env;
10
+ if (ANTHROPIC_API_KEY)
11
+ env.anthropicApiKey = ANTHROPIC_API_KEY;
12
+ const googleKey = GOOGLE_API_KEY ?? GEMINI_API_KEY;
13
+ if (googleKey)
14
+ env.googleApiKey = googleKey;
15
+ return env;
16
+ };
17
+ const readStdin = async () => {
18
+ const chunks = [];
19
+ for await (const chunk of process.stdin) {
20
+ chunks.push(chunk);
21
+ }
22
+ return Buffer.concat(chunks).toString('utf8');
23
+ };
24
+ const readPrompt = async (paths) => {
25
+ if (paths.length === 0 || paths[0] === '-') {
26
+ return readStdin();
27
+ }
28
+ const contents = await Promise.all(paths.map((p) => readFile(p, 'utf8')));
29
+ return contents.join('\n');
30
+ };
31
+ export const main = async (argv) => {
32
+ let parsed;
33
+ try {
34
+ parsed = parseArgs(argv);
35
+ }
36
+ catch (err) {
37
+ process.stderr.write(`${err.message}\n\n${HELP_TEXT}`);
38
+ return 2;
39
+ }
40
+ if (parsed.help) {
41
+ process.stdout.write(HELP_TEXT);
42
+ return 0;
43
+ }
44
+ if (parsed.version) {
45
+ process.stdout.write(`tokenometer ${VERSION}\n`);
46
+ return 0;
47
+ }
48
+ let prompt;
49
+ try {
50
+ prompt = await readPrompt(parsed.inputPaths);
51
+ }
52
+ catch (err) {
53
+ process.stderr.write(`Failed to read input: ${err.message}\n`);
54
+ return 1;
55
+ }
56
+ if (!prompt.trim()) {
57
+ process.stderr.write('Empty prompt — nothing to measure.\n');
58
+ return 1;
59
+ }
60
+ const useEmpirical = parsed.empirical && !parsed.offline;
61
+ const results = useEmpirical
62
+ ? await tokenizeMatrixEmpirical({
63
+ env: readEnv(),
64
+ formats: parsed.formats,
65
+ modelIds: parsed.modelIds,
66
+ prompt,
67
+ })
68
+ : tokenizeMatrix({
69
+ formats: parsed.formats,
70
+ modelIds: parsed.modelIds,
71
+ prompt,
72
+ });
73
+ process.stdout.write(`${renderTable(results)}\n`);
74
+ const summary = renderSummary(results);
75
+ if (summary)
76
+ process.stdout.write(`${summary}\n`);
77
+ if (useEmpirical) {
78
+ process.stdout.write('\n(empirical: Anthropic / Google counts via provider countTokens API; OpenAI via tiktoken o200k_base)\n');
79
+ }
80
+ return 0;
81
+ };
82
+ const scriptUrl = `file://${process.argv[1]}`;
83
+ if (import.meta.url === scriptUrl) {
84
+ main(process.argv.slice(2)).then((code) => process.exit(code), (err) => {
85
+ process.stderr.write(`Unexpected error: ${err.stack ?? String(err)}\n`);
86
+ process.exit(1);
87
+ });
88
+ }
89
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { TokenizeResult } from '@tokenometer/core';
2
+ export declare const renderTable: (results: readonly TokenizeResult[]) => string;
3
+ export declare const renderSummary: (results: readonly TokenizeResult[]) => string;
4
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAcxD,eAAO,MAAM,WAAW,GAAI,SAAS,SAAS,cAAc,EAAE,KAAG,MA8BhE,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,SAAS,cAAc,EAAE,KAAG,MAOlE,CAAC"}
package/dist/render.js ADDED
@@ -0,0 +1,46 @@
1
+ const padRight = (value, width) => value.length >= width ? value : value + ' '.repeat(width - value.length);
2
+ const padLeft = (value, width) => value.length >= width ? value : ' '.repeat(width - value.length) + value;
3
+ const formatCost = (usd) => {
4
+ if (usd >= 0.01)
5
+ return `$${usd.toFixed(4)}`;
6
+ if (usd >= 0.000001)
7
+ return `$${usd.toFixed(6)}`;
8
+ return `$${usd.toExponential(2)}`;
9
+ };
10
+ export const renderTable = (results) => {
11
+ if (results.length === 0)
12
+ return '(no results)';
13
+ const headers = ['model', 'format', 'tokens', 'est. cost'];
14
+ const rows = results.map((r) => [
15
+ r.model,
16
+ r.format,
17
+ `${r.approximate ? '~' : ' '}${r.inputTokens.toLocaleString()}`,
18
+ formatCost(r.inputCost),
19
+ ]);
20
+ const widths = headers.map((h, colIdx) => {
21
+ const maxRowWidth = rows.reduce((acc, row) => Math.max(acc, row[colIdx]?.length ?? 0), 0);
22
+ return Math.max(h.length, maxRowWidth);
23
+ });
24
+ const headerLine = headers.map((h, i) => padRight(h, widths[i] ?? h.length)).join(' ');
25
+ const separator = headers.map((_, i) => '-'.repeat(widths[i] ?? 0)).join(' ');
26
+ const dataLines = rows.map((row) => row
27
+ .map((cell, i) => {
28
+ const isNumeric = i >= 2;
29
+ return isNumeric
30
+ ? padLeft(cell, widths[i] ?? cell.length)
31
+ : padRight(cell, widths[i] ?? cell.length);
32
+ })
33
+ .join(' '));
34
+ return [headerLine, separator, ...dataLines].join('\n');
35
+ };
36
+ export const renderSummary = (results) => {
37
+ if (results.length === 0)
38
+ return '';
39
+ const cheapest = [...results].sort((a, b) => a.inputCost - b.inputCost)[0];
40
+ const priciest = [...results].sort((a, b) => b.inputCost - a.inputCost)[0];
41
+ if (!cheapest || !priciest || cheapest === priciest)
42
+ return '';
43
+ const ratio = priciest.inputCost / Math.max(cheapest.inputCost, Number.EPSILON);
44
+ return `\nCheapest: ${cheapest.model} as ${cheapest.format} (${formatCost(cheapest.inputCost)})\nPriciest: ${priciest.model} as ${priciest.format} (${formatCost(priciest.inputCost)}, ${ratio.toFixed(2)}x more)`;
45
+ };
46
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "tokenometer",
3
+ "version": "0.0.1",
4
+ "description": "Empirical token-cost benchmarking CLI for LLM prompts. Tells you what your prompt actually costs across Claude, GPT-4o, and Gemini, in every format.",
5
+ "license": "MIT",
6
+ "author": "Faraazuddin Mohammed <mohdfaraaz1@gmail.com>",
7
+ "homepage": "https://tokenometer.vercel.app",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/faraa2m/tokenometer.git",
11
+ "directory": "packages/cli"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/faraa2m/tokenometer/issues"
15
+ },
16
+ "keywords": [
17
+ "ai",
18
+ "anthropic",
19
+ "claude",
20
+ "cli",
21
+ "cost",
22
+ "gemini",
23
+ "gpt",
24
+ "llm",
25
+ "openai",
26
+ "prompt",
27
+ "token",
28
+ "tokenizer"
29
+ ],
30
+ "type": "module",
31
+ "main": "./dist/index.js",
32
+ "types": "./dist/index.d.ts",
33
+ "bin": {
34
+ "tokenometer": "./dist/index.js"
35
+ },
36
+ "exports": {
37
+ ".": {
38
+ "types": "./dist/index.d.ts",
39
+ "import": "./dist/index.js"
40
+ }
41
+ },
42
+ "files": ["dist", "README.md"],
43
+ "publishConfig": {
44
+ "access": "public",
45
+ "registry": "https://registry.npmjs.org/"
46
+ },
47
+ "scripts": {
48
+ "build": "tsc -b",
49
+ "clean": "rm -rf dist"
50
+ },
51
+ "dependencies": {
52
+ "@tokenometer/core": "0.0.1"
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "^22.10.5",
56
+ "typescript": "^5.7.2",
57
+ "vitest": "^3.0.0"
58
+ }
59
+ }