Repository navigation
Description
What happened?
getPricingTier treats every long-context threshold as inclusive, but two of the three vendors that have thresholds in the registry document theirs as exclusive. A request whose prompt lands exactly on the threshold is billed at the long-context tier when the vendor bills it at the base tier — 2x input and 1.5x output on that request.
packages/cost/models/calculate-cost.ts:105
// Find the highest threshold that the value meets
for (let i = 0; i < preprocessedPricing.length; i++) {
if (value >= preprocessedPricing[i].threshold) {The comparison is >= for all vendors. What the vendors actually say:
| Vendor | Own wording | Boundary point | Registry |
|---|---|---|---|
$1.25, prompts <= 200k tokens / $2.50, prompts > 200k tokens |
exactly 200,000 → base tier | threshold: 200000 |
|
| OpenAI | "prompts with >272K input tokens are priced at 2x input and 1.5x output for the full session" | exactly 272,000 → base tier | threshold: 272000 |
| xAI | "requests whose prompt reaches the listed token threshold are billed at the higher rate for all tokens in the request" | exactly at threshold → higher tier | not in registry |
Sources, read 2026-08-22: https://ai.google.dev/gemini-api/docs/pricing (Gemini 2.5 Pro, Standard, paid tier) and https://developers.openai.com/api/docs/models/gpt-5.4.
xAI is in the table on purpose: it is the reason this is not a one-character fix. Flipping >= to > globally would make xAI wrong in the opposite direction. The convention genuinely differs per vendor, so the tier needs to carry it.
Which rows are affected
From your own registry snapshot (packages/__tests__/cost/__snapshots__/registrySnapshots.test.ts.snap), the non-zero thresholds are:
threshold: 200000—google/gemini-2.5-pro,google/gemini-3,google/gemini-3-pro-image,google/gemini-3.1-pro-preview(documented exclusive → currently overcharged at exactly 200,000)threshold: 272000—openai/gpt-5.4(documented exclusive → currently overcharged at exactly 272,000)threshold: 200000—anthropic/claude-4.5-sonnet,anthropic/claude-sonnet-4,anthropic/claude-sonnet-4-5-20250929. I am not claiming a direction for these: Anthropic's current pricing page documents 4.6-and-later as standard pricing across the full 1M window and no longer states the ≤/> wording for the 4.5-era tier, so I could not verify the boundary from a live source. Worth checking separately.
Repro
A google/gemini-2.5-pro request with exactly 200,000 prompt tokens and no cache:
- expected (Google's page):
200000 * 0.00000125= $0.25 - actual (
getPricingTierpicks the200000tier):200000 * 0.0000025= $0.50
One token less (199,999) gives $0.2499…, so the jump lands one token early.
Suggested fix
Let the tier carry the convention instead of hard-coding one, e.g.
{ threshold: 200000, thresholdExclusive: true, input: 0.0000025, output: 0.000015 }const t = preprocessedPricing[i];
if (t.thresholdExclusive ? value > t.threshold : value >= t.threshold) {Default false keeps today's behaviour for every existing row; only the Google and OpenAI tiers above need the flag. The alternative — redefining threshold as "first token count billed at this tier" and storing 200001 / 272001 — is a data-only change with no code change, but it makes the field mean something different from its comment, so I'd lean to the flag.
Happy to send the PR for either shape if you tell me which you prefer.
Note
Same function as #5766 (cache-write tier selection), different line — that one is about which tier cache writes use, this one is about where the tier starts. They don't overlap.
I keep a table of which vendors are inclusive vs exclusive at the threshold, each with the quote from the vendor's own page and the date it was read, here: https://xyzs996.github.io/llm-api-pricing/prices.html#same-number-opposite-answer. It is where the wording above comes from.
Activity
aishwary-dongre commentedon Sep 1, 2026
Hi! Since there hasn't been activity on this for a week, I'd like to pick this up and implement the
thresholdExclusive?: booleanflag approach along with test coverage for boundary conditions. Let me know if that works!modelpath-dev commentedon Sep 10, 2026
I will take this issue. Please assign it to me.
The problem is that
getPricingTieruses an inclusive comparison (>=) for all vendors, but Google and OpenAI document their thresholds as exclusive. I will first inspect thecalculate-cost.tsfile to confirm how the thresholds are applied. Then, I will modify the logic to handle each vendor's threshold convention correctly. This will involve adding a condition to differentiate between inclusive and exclusive thresholds based on the vendor. I will also verify the changes against the registry snapshot to ensure correct billing.modelpath-dev commentedon Sep 13, 2026
I am working on this issue now. I will implement the
thresholdExclusive?: booleanflag approach as suggested. This will help handle the different threshold conventions for each vendor. I will also add test coverage for the boundary conditions to ensure accuracy.