Skip to content

[Bug]: Long-context tier starts one token early — getPricingTier is inclusive, Google and OpenAI document their thresholds as exclusive #5788

Open

Description

@xyzs996

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
Google $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 (getPricingTier picks the 200000 tier): 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

  1. aishwary-dongre commented on Sep 1, 2026

    @aishwary-dongre

    Hi! Since there hasn't been activity on this for a week, I'd like to pick this up and implement the thresholdExclusive?: boolean flag approach along with test coverage for boundary conditions. Let me know if that works!

  2. modelpath-dev commented on Sep 10, 2026

    @modelpath-dev

    I will take this issue. Please assign it to me.

    The problem is that getPricingTier uses an inclusive comparison (>=) for all vendors, but Google and OpenAI document their thresholds as exclusive. I will first inspect the calculate-cost.ts file 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.

  3. modelpath-dev commented on Sep 13, 2026

    @modelpath-dev

    I am working on this issue now. I will implement the thresholdExclusive?: boolean flag 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions