Llm usage normalizer
@agentcosts/llm-usage-normalizer
Convert Anthropic and OpenAI usage exports, or your own app's token logs, into one usage schema with customers, tasks, cached and uncached tokens, and cost from a price table you edit. Fixes the classic mistake of counting cached tokens twice, and lists unmapped keys and unpriced models.
- Activity
- New: be the first to try it
- Installs
- 0 installs
- Likes
- 0 likes
- Clones
- 0 clones
What it makes
Real output from this kit
- Sample file877 bytes, download to openUsage normalized reportFrom the kit's tests
- Sample file808 bytes, download to openUsage normalized reportFrom the kit's tests
- Sample file1 KB, download to openUsage normalizedFrom the kit's tests
How this works
- It is a skill: written instructions Claude reads when your request matches what the skill is for. You do not call it by name; just ask for the task.
- While it works, it reads and changes files in the places it lists, nowhere else.
- It runs these commands on your computer: node scripts/normalize.mjs.
- Nothing runs until you choose to use it, and you can remove it any time.
SKILL.md
LLM usage normalizer
Every provider counts tokens a little differently. The big trap: OpenAI-style exports count cached tokens inside input_tokens, while Anthropic reports uncached input separately. Add them up naively and you double count. This kit converts everything to one schema where input_tokens always means uncached input, then prices it from a table you control.
Output schema
timestamp,customer,task,run_id,provider,model,input_tokens,output_tokens,cache_read_tokens,cache_write_tokens,cost_usd,outcomeExpected input columns
The script finds columns by name (first match wins). Rename columns that differ, or pass --source.
| Source | Time | Model | Input | Cached input | Cache writes | Output | Customer key |
|---|---|---|---|---|---|---|---|
| Anthropic usage report (flattened) | starting_at | model | uncached_input_tokens (uncached) | cache_read_input_tokens | cache_creation.ephemeral_5m_input_tokens, cache_creation.ephemeral_1h_input_tokens | output_tokens | api_key_id or workspace_id |
| Anthropic API responses logged by your app | created_at or timestamp | model | input_tokens (uncached) | cache_read_input_tokens | cache_creation_input_tokens | output_tokens | your own customer column |
| OpenAI usage export (completions) | start_time (unix seconds) | model | input_tokens (includes cached) | input_cached_tokens | none | output_tokens | project_id, api_key_id, or user_id |
| OpenAI API responses logged by your app | created_at | model | prompt_tokens or input_tokens (includes cached) | prompt_tokens_details.cached_tokens or input_tokens_details.cached_tokens | none | completion_tokens or output_tokens | your own customer column |
Also read when present: customer, task (or workflow, feature, agent), run_id (or trace_id, session_id), outcome (or status), batch or service_tier (batch rows get --batch-discount, default 0.5), and cost_usd (kept as is).
Export formats change. If a column is missing or named differently, show the user the header and map it by hand rather than guessing.
Inputs
- One or more exports in
data/. - Price table
data/llm-prices.csv. If missing, copyexamples/prices.csvthere. Columns:provider,model,input_per_mtok,output_per_mtok,cache_read_per_mtok,cache_write_per_mtok,checked_on,notes, plus optionalcache_write_1h_per_mtok(defaults to 2x input). Model names match exactly or by prefix, sogpt-5-minipricesgpt-5-mini-2025-08-07. - Customer map
data/customer-map.csv(key,customer,task) when exports only know API keys, projects, or workspaces. One key per customer is the cleanest setup; suggest it if they share keys.
Steps
- Check prices first. Tell the user plainly: the example prices are a starting point and must be checked against each provider's current pricing page. Ask them to confirm or update
data/llm-prices.csvand setchecked_on. The script warns when prices were never checked or are older than 60 days. Run:
node scripts/normalize.mjs data/anthropic-usage.csv data/openai-usage.csv --prices data/llm-prices.csv --customer-map data/customer-map.csv --out out/usage-normalizedRead
out/usage-normalized-report.md. Fix, then rerun:- Keys with no customer: ask who each key belongs to and add it to
data/customer-map.csv(internal, staging, and eval keys map tointernal). - Models with no price: add a row to the price table after the user confirms the price.
- Keys with no customer: ask who each key belongs to and add it to
- Hand off
out/usage-normalized.csvto the next kit (agent-cost-dashboard, gross-margin-by-customer, usage-pricing-simulator, consumption-guardrails).
Limits to explain
- Provider reports are usually daily totals per model and key, not single calls. They have no
run_idoroutcome, so cost per successful outcome needs your app's own logs. - If the provider bill and this total differ by more than a few percent, look for: wrong prices, batch or priority tiers, long-context pricing, tool or search fees, credits, and taxes. The script only prices tokens.
Guardrails
- Never put API keys or secrets in any file. Key IDs and names from usage reports are fine; key values are not. If an export contains full key values, stop and ask the user to remove that column.
- This kit reads files only. It never calls a provider API.
- Prices are the user's responsibility to confirm. Always say so next to any cost total.
README.md
LLM usage normalizer
Converts Anthropic and OpenAI usage exports, or your own app's token logs, into one usage CSV with customer, task, cached and uncached tokens, and cost. It handles the cached-token difference between providers, maps API keys or projects to customers, and tells you what it could not map or price.
Who it is for: engineers and finance folks at AI products who need one clean usage file before they can talk about cost per customer or margins.
Example
Input: examples/anthropic-usage.csv (daily rows per model and key), examples/openai-usage.csv (daily rows per model and project), examples/customer-map.csv, examples/prices.csv.
Output (out/usage-normalized-report.md):
| examples/anthropic-usage.csv | anthropic | 8 | $7.65 | api_key_id | uncached_input_tokens |
| examples/openai-usage.csv | openai | 4 | $3.93 | api_key_id | input_tokens |
Total $11.59 across 12 rows.
Keys with no customer: key-staging (1 row)And out/usage-normalized.csv in the shared schema used by every Agent Costs kit.
What it can access
- Runs
node scripts/normalize.mjs(Node built-ins only). - Reads exports in
data/. Writesdata/llm-prices.csv,data/customer-map.csv, andout/usage-normalized*. - No network, no API keys. It never calls a provider.
Setup
Copy examples/prices.csv to data/llm-prices.csv and check every price against the provider's current pricing page (the example prices are a starting point, not a quote). Then export usage from each provider's console into data/.
Files
- README.md
- SKILL.md
- examples/anthropic-usage.csv
- examples/customer-map.csv
- examples/openai-usage.csv
- examples/prices.csv
- kit.json
- scripts/lib.mjs
- scripts/normalize.mjs
- scripts/usage.mjs
Did it work?
0worked for them0did not work
Sign in to say whether it worked for you.
Nobody has said yet. Tried it? Tell the next person.
