Skip to content

fix(core): price Copilot long-context tiers and cache writes - #59803

Open
golfromeo-fr wants to merge 3 commits into
anomalyco:devfrom
golfromeo-fr:copilot-tier-pricing
Open

golfromeo-fr wants to merge 3 commits into
anomalyco:devfrom
golfromeo-fr:copilot-tier-pricing

Conversation

@golfromeo-fr

@golfromeo-fr golfromeo-fr commented Oct 28, 2026 •

Copy link
Copy Markdown

Issue for this PR

Closes #52089
Closes #59671

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

GitHub Copilot costs were undercounted badly on long sessions. In my case (#59671): a 3h session on Copilot / Claude Haiku 5.5, 348 requests, ~344K average context, OpenCode showed $1.56 when the real bill is roughly $9.34 (between $2.03 and $10.15, since Copilot picks the price tier per request by prompt size).

Two causes, both in the Copilot provider code:

  1. Copilot's /models payload ships two price tiers (default and long_context, the latter starting above default.max_prompt_tokens). The builder only read the default tier.
  2. The builder hardcoded cache writes to $0 (cache_write_price is in the payload, unread), and both usage adapters dropped cache_write_tokens, so cache writes weren't counted at all.

Pricing that session's totals at the ≤100K rates with free writes gives $1.5557 — cent-exact with the displayed $1.56, which is how I know exactly which rates the old code applied.

The builder now emits the write price and a context-tier cost entry, which SessionUsage.calculateCost already applies per request, so no calculator change was needed. The adapters map cache_write_tokens into cacheWrite. While there I replaced their hand-rolled noCache arithmetic with ProviderShared.subtractTokens/sumTokens like the shared protocols use: three of the four paths left noCache undefined on responses without cache details, which downstream prices the entire prompt at $0, and none of them clamped against broken breakdowns.

Deliberately not addressed: Copilot's own reported billed amount (#35765) and the richer pricing shape from #59460.

How did you verify your code works?

New unit tests against the Copilot rate card for Claude Haiku 5.5: long-context tier applies above 100K prompt tokens, default tier at exactly 100K, cache writes priced, reasoning billed as output, a malformed long_context object no longer drops the model from the catalog, cache-write mapping in all four adapter paths, plus regressions for the $0-prompt and negative-noCache cases. The adapter regressions fail on the parent commit and pass with this change. bun run check passes, and the packages/core suite has the same 4 failures as a clean dev checkout (all environment-related).

Visual

<svg xmlns="http://www.w3.org/2000/svg" width="240" height="72" viewBox="0 0 240 72" role="img" aria-label="Copilot session cost: displayed $1.56, actual about $9.34">
  <style>
    .bar { animation: grow 2.8s ease-in-out infinite; }
    .tag { animation: fade 2.8s ease-in-out infinite; }
    @keyframes grow { 0%, 15% { width: 26px } 55%, 100% { width: 142px } }
    @keyframes fade { 0%, 30% { opacity: 0 } 65%, 100% { opacity: .9 } }
    @media (prefers-reduced-motion: reduce) { .bar, .tag { animation: none } }
  </style>
  <g fill="currentColor" font-family="ui-monospace,SFMono-Regular,monospace" font-size="11">
    <text class="tag" x="12" y="11" font-size="9" opacity="0">6x undercount: tier + cache writes</text>
    <text x="12" y="27" opacity=".5">billed</text>
    <rect x="62" y="18" width="26" height="9" rx="2" opacity=".5"/>
    <text x="94" y="27" opacity=".5">$1.56</text>
    <text x="12" y="53">actual</text>
    <rect class="bar" x="62" y="44" width="142" height="9" rx="2" opacity=".9"/>
    <text x="206" y="53">$9.34</text>
  </g>
</svg>

Screenshots / recordings

Not a UI change, so none attached.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Added by the opencode bot (AI-generated).

Fixes #51845

Related: #35765, #53521

Copilot's /models payload exposes per-request price tiers (default plus
long_context with its own prices and max_prompt_tokens) and a
cache_write_price, but the model builder read only the default tier and
hardcoded cache writes to zero, and both usage adapters dropped
cache_write_tokens entirely. Long sessions on tiered models like Claude
Haiku 5.5 were undercounted ~6x.

The builder now emits the cache write price and a context-tier cost entry
bounded by the default tier's max_prompt_tokens, which
SessionUsage.calculateCost already applies per request. Both Copilot
adapters map cache_write_tokens (or Anthropic-style
cache_created_input_tokens) into cacheWrite and keep the non-cached
input consistent with the total.
@github-actions github-actions Bot added needs:compliance This means the issue will auto-close after 2 hours. needs:issue labels Oct 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions github-actions Bot removed needs:issue needs:compliance This means the issue will auto-close after 2 hours. labels Oct 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

@golfromeo-fr
golfromeo-fr marked this pull request as ready for review October 28, 2026 02:51

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to merge. I ran the #52089 reproduction (a local mock of Copilot's /models and /responses) on the base and with this change:

  • Prices: cache writes go from $0 to $5/M, and a long-context tier is added above default.max_prompt_tokens.
  • Usage: the Responses generate and stream paths now report cacheWrite: 8270 and noCache: 3, where the base reported no cacheWrite and noCache: 8273.

The tier boundary matches how SessionUsage.calculateCost picks tiers (input plus cache reads plus cache writes, strictly above the boundary). The Copilot and session-usage tests pass. I couldn't run the package type check in this environment.

Public API is unchanged: only the internal parsing of Copilot's /models and usage responses changes.

One small point is inline: the new session-usage.test.ts cases exercise unchanged code and can be dropped.

Fixes #51845
Fixes #59671
Related: #35765, #53521

})
})

// GitHub Copilot's Claude Haiku 5.5 rate card: a per-request tier above 100K prompt tokens at 5x.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These four cases test SessionUsage.calculateCost, which this PR doesn't change, and they pass on the base too. The new coverage is the tier and cache-write prices from CopilotModels, which models.test.ts already checks. Could you drop these so the change stays focused?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 238a59b — dropped the session-usage.test.ts cases. The changed code stays covered by the builder and adapter tests.

Review feedback on anomalyco#59803: the tier and cache-write pricing behavior
they pinned lives in SessionUsage.calculateCost, which this PR does not
change, and the changed code is already covered by the builder and
adapter tests.

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new commit removes the session-usage.test.ts cases I flagged, which addresses my earlier comment. It changes nothing else. I re-ran the Copilot tests and the #52089 and #51845 reproductions at this head: cache-write prices, the long-context tier and the cacheWrite/noCache mapping are all correct. No new concerns. The public API is unchanged.

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new commit only merges dev. I checked that the PR's own changes are the same as before and that the merged dev commits don't touch the Copilot provider or the cost calculation. I re-ran the Copilot tests and the #52089 and #51845 reproductions at this head, and all are still fixed. My earlier test comment was addressed in the previous push. No new concerns, and the public API is unchanged.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

1 participant