Repository navigation
Conversation
Replace the hardcoded price tables in src/prices.mjs with src/pricing/: an append-only ledger of dated price entries built from the vendors' official pricing pages (Anthropic and OpenAI, read as the Markdown they publish), a bundled last-known snapshot, and a local cache refreshed at most daily in the background. - Unknown models were priced at $0 and rendered as "$0.00" (e.g. every claude-opus-5-5 session). Prices now come from the ledger, and a model with no price anywhere is "n/a" end to end: never $0.00 in the UI, the terminal tree or the CLI, never hidden by the min-cost filter. - A live price change applies from the moment it was observed, so an already computed historical cost never moves; a failed or malformed refresh changes nothing and never blocks session discovery. - Every node carries cost.provenance and every call its price id. - Corrects Sonnet 5 (stays at $2/$10 after 2026-09-01), Fable 5.1 and Opus 5.5 cache-read rates, and drops the guessed gpt-5.5 cache-write rate; adds OpenAI long-context rates. - Fix the index worker's change signature, which looked summaries up by the wrong key and never saw a re-priced session. Closes #20
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was asked
Issue #20: model prices must come from live provider price sources resolved at runtime, not from tables in the source, with provenance on every amount, a cache policy, deterministic historical results, and offline operation through a bundled snapshot or an explicit "unavailable". First and foremost: every session on Claude Opus 5.5 (
claude-opus-5-5), and on any other model missing from the old table, showed a cost of $0.Root cause of the $0
priceClaude()/priceCodex()looked the model up in a hardcoded table. A miss returnedusd: 0with confidencen/a, and the session aggregate then labelled the nodepartial, notn/a, even when no call was priced. From there the zero was indistinguishable from a real one: the React list and session views print$0.00for0, the CLI prints"$" + cost.toFixed(2), and the index's defaultmin $0.5filter hid those sessions entirely.claude-opus-5-5,claude-fable-5-1,claude-sonnet-5-5,claude-opus-4-8,gpt-5.6-terra,gpt-5.6-luna,gpt-6-solandgpt-6-lunawere all missing from the table.What changed
src/pricing/replacessrc/prices.mjs:sources.mjs: the two official price pages and a strict parser for each. A page that does not parse, or yields fewer than five priced rows, is a failed refresh, never a price. A rate a page does not publish staysnull. It is never derived from a multiplier.index.mjs: an append-only ledger of dated entries merged from the bundledsnapshot.jsonand a local cache ($XDG_CACHE_HOME/agent-atlas/prices.json, overrideAGENT_ATLAS_PRICES_CACHE).snapshot.json: the last-known prices, generated bynpm run prices:snapshotwith the same parsers. Entries are only appended, so a release's snapshot diff is exactly the prices that changed.Cache and refresh policy.
serverefreshes in the index worker's background and never waits for it.list/tree/exportgive a due refresh up to 2 s per source.AGENT_ATLAS_PRICING=offlinedisables the network.The README's "no runtime outbound network" line is updated to say this is now the one outbound request.
Provenance. Every node has
cost.provenance, listing the ledger entries its own calls were priced with: vendor, model, currency, per-class rates, long-context rates,from,observedAt, and source kind and URL. Every call carries thepriceid of its entry. Nodes also getcost.ownConfidencenext to the subtreecost.confidence.Unknown is never $0.00.
n/a. One with some unpriced calls, or an unpublished token class, ispartialand renders as a lower bound,≥ $x.costisnull.n/aonly if nothing under it is priced.src/ui/money.ts: session list, summary tiles, per-model and per-agent breakdowns, and the session's flow, trace, agent table and call dialog. The terminal tree and CLI list usecostLabel().costConfidenceandunknownModels. The summary countsunpricedsessions. A session whose cost is not fully known is never hidden by themin $filter.Price corrections that fall out of using the official pages:
One adjacent fix, needed for re-pricing to reach the index: the index worker's change signature looked
summariesup by bare session id while the map is keyedprovider:id. It always missed, so a session whose cost changed never registered as changed.Price sources: choice and trade-offs
https://platform.claude.com/docs/en/about-claude/pricing.md, the Model pricing tablehttps://developers.openai.com/api/docs/pricing.md, the Standard table, short and long context.mdURL, which is far more stable to parse than HTML./v1/modelsexposes capabilities, not prices, and needs a key.model_prices_and_context_window.json, OpenRouter's/api/v1/models): they add a second party that must keep up with new models and can lag or diverge from list price.speed/service_tier, but every call on this host isstandard, so priced tiers are left for later.gpt-5.6-sol's rate is the published promotional one, through at least 2026-11-21. When OpenAI changes it, the next refresh records the new rate from that moment on.Verification
npm run typecheck,npm run buildandnpm testpass. The Chromium browser test is skipped locally because the runtime is unavailable there; CI runs it.test/pricing.test.mjs:n/aand never$0.00through the whole stack (price, tree, index row, breakdowns, min-cost filter, renderers).claude-opus-5-5. Onmain, 16 of them show $0.00. On this branch, all 21 that build are priced (for example $3.00, $9.54, $31.11, $89.05) and none is $0. The 22nd transcript cannot be read on either branch (see below).gpt-6-astra,gpt-6-sol,gpt-6-luna,gpt-5.6-sol/terra/lunaandgpt-5.5are priced.codex-auto-review, which no official page lists, isn/a.Found, not fixed here (out of scope)
mainas well, withCannot create a string longer than 0x1fffffe8 characters, because the Claude parser reads a >512 MB transcript as one string. This is the memory and indexing work tracked in perf: bounded memory footprint — don't hold every session in memory (4 GB for ~2k sessions today) #25.node src/cli.mjs list|treecrashes onmain(ERR_INVALID_ARG_TYPE).cli.mjsstill callsclaude.buildTree(path)instead of going through the provider registry. This PR only touches its cost rendering and the bounded price refresh.Closes #20