Repository navigation
feat(usage): tier-aware pricing for prompt-length-priced models (Haiku 5.5) #642
Description
Problem
Claude Haiku 5.5 is priced by prompt length, per request (https://platform.claude.com/docs/en/about-claude/pricing, "Model pricing" and "Long context pricing", retrieved 2026-10-17), in USD per MTok:
| tier | input | 5m cache write | cache hit | output |
|---|---|---|---|---|
| prompt up to 100,000 tokens | 0.10 | 0.125 | 0.01 | 0.50 |
| prompt over 100,000 tokens | 0.50 | 0.625 | 0.05 | 2.50 |
The prompt length counts all input tokens of the request, including cache reads and cache writes. A request over the threshold pays the higher rates for everything, cache hits included.
internal/usage/pricing.go has one flat rate set per (canonical_model, provider, effective_from). A usage record also aggregates a whole query result, (session_id, result_uuid, model_key), which can span many API requests, so the per-request prompt length that selects the tier is not on the record at all.
The usage pricing catalog in mctl-gitops therefore carries a flat claude-haiku-5-5 entry at the up-to-100k rates (mctl-gitops PR linked below). It under-prices any request whose prompt is over 100k tokens by up to 5x. This is not hypothetical: a single-turn smoke query run with a full local MCP tool set sent a 146k-token prompt. claude-remote runs on claude-haiku-5-5 with MCP servers attached.
Proposed direction
- Producer side. The CLI's
ResultMessage.usage.iterations[]reports input, cache read and cache write tokens for each API request. Emit, per record, the token counts split by tier: requests with a prompt of at most 100k tokens versus over 100k. mctl-agents'usage_ledger.pyand claude-remote's steward collector would both need to emit this. - Schema. Add optional long-context token fields to the ADR-012 record (for example
long_context_input_tokens, and the same for cache read, cache write and output). Add optional long-context rates and along_context_threshold_tokensto a catalog entry. Both are additive, and old records and entries keep their meaning. - Pricing.
Catalog.Calculateprices each tier's tokens at that tier's rates. A record for a tiered model without the split must not silently take the base rate. It should be marked as an estimate or lower bound, so that "could not observe the tier" never reads as "short prompt". - Follow the append-only rule (ADR-012 invariant 7): the tiered rates go in a new catalog entry with a later
effective_from, never as an edit to the flat one.
Acceptance
- A record with a tier split is priced exactly. A test reproduces the pricing page's numbers for one request under and one over the threshold.
- A Haiku 5.5 record without a split is visibly flagged, not priced as if every request were short. A test proves this both ways.
- The flat
claude-haiku-5-5entry is superseded, and the README caveat is removed.
Context: mctlhq/mctl-agents#663 / #666 moved the cheap profile to claude-haiku-5-5.
Activity