Skip to content

fix(pricing): price gpt-6.1-sol codex usage rows - #351

Merged
mikehasa merged 1 commit into
mainfrom
fix/gpt-6-1-sol-pricing
Sep 30, 2026
Merged

mikehasa merged 1 commit into
mainfrom
fix/gpt-6-1-sol-pricing

Conversation

@mikehasa

Copy link
Copy Markdown
Owner

Problem

Codex is reporting gpt-6.1-sol, and nothing in agentacct priced it. The live ledger showed it plainly:

Client  Model        Rows  Why
codex   gpt-6.1-sol    11  no local price row covers it

Two rows of the price stack both missed it:

  • the builtin table stops at gpt-5.5 (gpt-5.6* was deliberately left to the snapshot, which is why codex/gpt-5.6-sol has rows too — those stay excluded for a different reason: codex reported no input/output split for them);
  • the local LiteLLM snapshot was fetched before the model appeared upstream, so the codex -> openai provider alias had nothing to resolve to.

Stored rows do not heal on their own: a scan only reprices what a catalog row now covers, so the 11 rows would have stayed cost_confidence: unknown until a snapshot refresh happened to pick the model up.

Change

The builtin table now carries gpt-6.1-sol at its published rates, on both routes a codex row can report — the client's own ("codex", ...) key and OpenAI's ("openai", ...) key:

per 1M tokens
input $2.00
output $10.00
cache read $0.10
cache write (5m) $2.50

Source: LiteLLM's model_prices_and_context_window.json (gpt-6.1-sol, litellm_provider: openai), the same upstream table the snapshot is built from.

  • No multiplier. The 2.5x ccusage fast-pricing convention stays scoped to gpt-5.5, so the codex and openai routes price identically rather than one silently carrying a convention. Cost basis is pricing_table / estimated_from_tokens, never provider-billed.
  • Both keys, like gpt-5.5. Exact-key lookup answers for the codex route instead of depending on the snapshot's TTL refresh to alias it onto OpenAI's row; if a snapshot does carry the model, the builtin row still answers for its exact codex key and the dollars agree.
  • Coverage stays per model. A sibling id no row carries (gpt-6.1-sol-pro) stays cost-unknown rather than borrowing this row's price.

Tests

  • tests/test_pricing_catalog.py (+2): both routes resolve to the builtin row with the published rates, no multiplier, cache read/write columns priced through estimate_model_cost_breakdown_usd; sibling/uncovered ids stay unpriced; and a snapshot that does carry the model leaves the price and provenance unchanged.
  • tests/test_stored_usage_repricing.py (+1): the stored-row reprice path prices a codex gpt-6.1-sol row from the builtin table with no snapshot at all, and produces the exact row a fresh import does (dollars + pricing_source provenance), leaving the stored row untouched while planning.
  • pytest -q tests/test_pricing_catalog.py tests/test_stored_usage_repricing.py → 53 passed.
  • Full suite: pytest -q → 4894 passed, 7 failed. All 7 are in tests/test_onboard_global.py and are pre-existing and unrelated — they fail identically on unmodified origin/main (an environment string drift: the test asserts the word "yourself" appears in onboarding copy this machine does not produce).

Verification against a real ledger

Run on a copy of a real store, with the pricing snapshot deleted so nothing but the builtin table is in play, and auto-refresh pinned off (no network):

usage import-local --store-dir <copy> --client codex --refresh --estimate-costs

→ 11/11 gpt-6.1-sol rows priced, cost_confidence: estimated_from_tokens, cost_basis: pricing_table, pricing_source: agent_sentinel_builtin, $6.528786 total. The copied store's 11 stored rows are the only thing written; the live store was never touched. Spot-checked one row by hand: 5,859 in × $2/1M + 400 out × $10/1M + 125,440 cache read × $0.10/1M = $0.028262, matching the stored value exactly.

Docs / safety

  • CHANGELOG.md under Unreleased → Fixed.
  • Preserves local-first behavior (no network on the builtin path); no keys or secrets; no API calls were run — the prices come from the published upstream table.

Codex was writing gpt-6.1-sol rows and nothing priced them: the builtin
table stopped at gpt-5.5, and the local LiteLLM snapshot predated the
model, so 11 stored rows imported cost-unknown and stayed that way
because a scan only reprices what a catalog row now covers.

The builtin table now carries the model's published rates on both routes
a codex row can report — the client's own ("codex", ...) key and
OpenAI's — at list price: $2/1M in, $10/1M out, $0.10/1M cache read,
$2.50/1M cache write. No multiplier: the 2.5x ccusage fast-pricing
convention stays scoped to gpt-5.5, so the codex and openai routes price
identically instead of one of them silently carrying a convention.

Coverage stays per model. A sibling sol id no row carries (for example
gpt-6.1-sol-pro) remains cost-unknown rather than borrowing this row.

Verified on a copy of a live ledger with no pricing snapshot at all
(builtin table only, no network): 11/11 gpt-6.1-sol rows repriced to
cost_confidence estimated_from_tokens, cost_basis pricing_table,
pricing_source agent_sentinel_builtin, $6.528786 total — the same
dollars a fresh import produces, and the stored rows are the only thing
written. The snapshot path is unchanged: when a snapshot does carry the
model, the builtin row still answers for its exact codex key.
@mikehasa
mikehasa merged commit f4dacb7 into main Sep 30, 2026
4 checks passed
@mikehasa mikehasa mentioned this pull request Sep 30, 2026
11 of 12 tasks
mikehasa added a commit that referenced this pull request Sep 30, 2026
Version bump only (pyproject.toml + CHANGELOG.md). This release carries the
gpt-6.1-sol pricing fix (#351) merged just before it.

DMG preflight (§3a): src/agentacct/cost.py changed since v0.12.8 and the DMG
embeds the frozen Python CLI, so the payload changed even though no Swift file
did. Decision: REBUILD (§3c) from this release's own squash commit.
@mikehasa
mikehasa deleted the fix/gpt-6-1-sol-pricing branch September 30, 2026 21:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant