|
| 1 | +# tokentally |
| 2 | + |
| 3 | +Token usage in, dollar totals out. |
| 4 | + |
| 5 | +Small TypeScript library for: |
| 6 | +- Normalizing token usage across providers |
| 7 | +- Resolving per-token pricing (static maps, LiteLLM catalog, OpenRouter catalog) |
| 8 | +- Estimating and aggregating USD cost |
| 9 | + |
| 10 | +## Install |
| 11 | + |
| 12 | +```bash |
| 13 | +pnpm add tokentally |
| 14 | +``` |
| 15 | + |
| 16 | +## Core usage (browser-safe) |
| 17 | + |
| 18 | +```ts |
| 19 | +import { estimateUsdCost, normalizeTokenUsage, pricingFromUsdPerMillion } from 'tokentally'; |
| 20 | + |
| 21 | +const usage = normalizeTokenUsage({ prompt_tokens: 1000, completion_tokens: 250 }); |
| 22 | +const pricing = pricingFromUsdPerMillion({ inputUsdPerMillion: 1.75, outputUsdPerMillion: 14 }); |
| 23 | + |
| 24 | +const cost = estimateUsdCost({ usage, pricing }); |
| 25 | +// { inputUsd: ..., outputUsd: ..., totalUsd: ... } |
| 26 | +``` |
| 27 | + |
| 28 | +## Node helpers (catalog sources) |
| 29 | + |
| 30 | +### LiteLLM pricing + limits |
| 31 | + |
| 32 | +```ts |
| 33 | +import { loadLiteLlmCatalog, resolveLiteLlmPricing, resolveLiteLlmMaxOutputTokens } from 'tokentally/node'; |
| 34 | + |
| 35 | +const { catalog } = await loadLiteLlmCatalog({ env: process.env, fetchImpl: fetch }); |
| 36 | +const pricing = catalog ? resolveLiteLlmPricing(catalog, 'openai/gpt-5.2') : null; |
| 37 | +const maxOut = catalog ? resolveLiteLlmMaxOutputTokens(catalog, 'openai/gpt-5.2') : null; |
| 38 | +``` |
| 39 | + |
| 40 | +### OpenRouter pricing (optional) |
| 41 | + |
| 42 | +```ts |
| 43 | +import { fetchOpenRouterPricingMap, resolvePricingFromMap } from 'tokentally/node'; |
| 44 | + |
| 45 | +const map = await fetchOpenRouterPricingMap({ apiKey: process.env.OPENROUTER_API_KEY!, fetchImpl: fetch }); |
| 46 | +const pricing = resolvePricingFromMap(map, 'openai/gpt-5.2'); |
| 47 | +``` |
| 48 | + |
| 49 | +## API |
| 50 | + |
| 51 | +- `normalizeTokenUsage(raw)` → `{ inputTokens, outputTokens, reasoningTokens, totalTokens } | null` |
| 52 | +- `pricingFromUsdPerMillion({ inputUsdPerMillion, outputUsdPerMillion })` |
| 53 | +- `estimateUsdCost({ usage, pricing })` |
| 54 | +- `tallyCosts(calls)` → totals + per-model breakdown |
| 55 | + |
| 56 | +## Non-goals |
| 57 | + |
| 58 | +- Perfect accounting. This is a **best-effort estimate** based on the pricing source you provide. |
| 59 | +- Provider-specific invoice reconciliation. |
| 60 | + |
| 61 | +## License |
| 62 | + |
| 63 | +MIT |
| 64 | + |
0 commit comments