Repository navigation
fix(pricing): one rate lookup for every path; no double normalization on reload - #642
Merged
Merged
Conversation
… on reload The same Codex turn priced three ways: the proxy used the LiteLLM table ($0.18609 exact), the importer and cost-worker used only DEFAULT_PRICING and substituted claude-sonnet-4 rates for unknown models ($0.055827 fallback), and reload/cold-load subtracted cached tokens a second time from imported Codex usage that lacked the normalized marker ($0.1405 in total). - lookupRates (default-rates.js) is the only rate lookup, over one table built by one pure builder, buildRateTable (xai/ mirror, DEFAULT_PRICING floor, lag overrides). The table is read synchronously from pricing-cache.json on first use and replaced when fetchPricing lands; buildPricingTable is a thin wrapper around the same builder. calculateCost and calculateCostSimple both use it, and so do the proxy, WebSocket, importer, import --once and --target-transcript, cost-worker, rebuild-index, restore and cold-load. Every path passes the same provider key. - A model the table does not match is cost null / unknown on every path; the substitute-rate fallback is removed. - The Codex importer marks its cache-exclusive usage as normalized; reload and cold-load treat existing imported openai lines the same way. - Reload and cold-load reprice fallback/unknown costs with the current lookup (exact, prefix and legacy numeric costs are untouched; nothing is rewritten on disk), and session aggregates carry PRICING_REV so sessions.json rebuilds once. - Tests pin pricing-cache.json (docs/testing.md), isolate their homes, and cover every path with fail-on-685321f / pass-on-fix evidence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
摘要
統一 ccxray 各條路徑的計價。
gpt-6-astra)用量,proxy、CLI 匯入、伺服器重新載入會算出三個金額:$0.18609、$0.0558、$0.1405。重新載入時連 token 數都被扣錯。lookupRates,同一筆用量得到同一個金額和同一個可信度標示。unknown,不再用替代價格估一個可能錯三倍的數字。fallback和unknown的紀錄,已經是exact的不動。這是 agentflow 整合規格(#638)A1a 的前置條件。
Details
685321f, with an isolatedCCXRAY_HOME/HOME, a copy of the samepricing-cache.json, and network blocked):rebuild-indexusedpricing.calculateCost(built-ins plus the LiteLLM cache). The importer and cost-worker useddefault-rates.calculateCostSimple: built-ins only, with claude-sonnet-4 rates as afallbackfor anything unknown.gpt-6-astraexists only in the cache, so imports were priced at about 1/3 of the real rate._ccxrayUsageNormalizedmarker.restore.jsand cold load (routes/api.js) then subtracted cached tokens a second time: input 17,695 → 10,655 and 225 → 0, repriced to $0.1405.fetchPricing()finished, so reload repricing depended on how far the table had loaded.lookupRates(model, provider)is the only rate lookup. Its table is built by onebuildRateTable(built-ins, the pricing cache,xai/key mirroring, lag overrides) and loads the cache synchronously on first use. There is no startup wait and no race.import --once/--target-transcript), the cost-worker child,rebuild-index, restore and cold load.cost: null/unknownon every path, shown as a lower bound per ADR 0017. Theforward.jslive-path invariant comment is updated to match.fallbackandunknownrows are repriced on read when a rate is now available;exactrows are untouched. APRICING_REVstamp makessessions.jsonrebuild once on the next start.pricing-cache.json.test/default-rates.test.jsandtest/grok-wire.test.jspoint the cache at a missing path, anddocs/testing.mddocuments this.Verification
CCXRAY_HOME=$(mktemp -d) CCXRAY_EXPORT_DISABLE=1 npm test: 2683/2683 (run again independently before opening this PR). The real~/.ccxraywas untouched during the run.test/pricing-single-source.test.js(31 cases) covers every path listed above, plus repricing, the revision stamp and the no-cache case.685321f, the 26 behaviour-changing cases fail and the invariance checks pass. With the fix, all pass.gpt-6-astra, 2 turns), imported withimport --target-transcriptagainst the same pricing cache.fallback.exact, matching what the proxy recorded for the same turns.claude -pworker $0.3473501 via proxy; interactive host $0.7334226 via proxy).xai/key mirroring, so import and proxy could pricexai/-prefixed models differently. This is fixed here with a shared table builder and regression tests.Not verified:
sessions.jsontotals refresh on the next rebuild, not immediately, when the pricing cache first learns a model; per-entry views reprice at once.import --once/--target-transcriptare covered in tests through their shared import function; the real CLI was exercised by the end-to-end check above.🤖 Generated with Claude Code