fix: track real per-request cost for OpenCode Go subscriptions#1885
Merged
guillaumegay13 merged 7 commits intoMay 26, 2026
Merged
Conversation
OpenCode Go bills against a published dollar quota ($12 / 5h) on a per-request basis, so the existing 'subscription => $0' shortcut zeroed out costs that the docs actually attribute to each call. Parse the Usage Limits table from go.mdx alongside the existing Endpoints table, derive per-request USD cost per model (e.g. GLM-5.1 = $12 / 880 = $0.013636), and write that value to agent_messages.cost_usd when authType is 'subscription' and provider is 'opencode-go'. Other subscription providers (Claude Max, ChatGPT Plus, GLM Coding, Copilot) keep their flat-fee $0 behavior. Closes mnfst#1859
Addresses two cold-path holes flagged in review: - OpencodeGoCatalogService implements OnModuleInit and fire-and-forgets a list() call at boot so the per-request cost index is populated before the proxy hot path runs. Discovery-driven warming still works as before; this closes the window where a process restart could record $0 for the first OpenCode Go calls until a user opened the routing page. - ProxyMessageRecorder canonicalizes the provider through PROVIDER_BY_ID_OR_ALIAS, so subscriptions stored under the 'opencodego' alias (or other casings) also resolve to the OpenCode Go cost path.
Add resolveCostPerRequest() async sibling to the sync getter. It awaits the already-running list() call when the in-memory index is empty, so the first proxy call after a process restart blocks once on the (cached) docs fetch instead of recording $0. After list() resolves, subsequent calls hit the in-memory map and stay sync-fast. Combined with the existing OnModuleInit warmup, the race window is now bounded to 'recorder runs before warmup fetch resolves' on the very first call only — and even that call now waits instead of undercounting.
list() now holds an inflight promise so a warmup-plus-proxy race shares the same fetch instead of issuing two parallel requests against the docs URL. The promise is cleared on settle (success or failure) so the next post-cache expiry call refetches normally. resetCache() also clears it for test hooks.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1885 +/- ##
=======================================
Coverage 99.41% 99.41%
=======================================
Files 186 186
Lines 17367 17372 +5
Branches 6886 6886
=======================================
+ Hits 17266 17271 +5
Misses 99 99
Partials 2 2
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
CostCell hardcoded $0.00 for every subscription auth row, which hid the real OpenCode Go per-request cost behind 'Included in subscription'. Now the cell shows the recorded cost when it's non-zero (per-request subscriptions like OpenCode Go) and falls back to the flat-fee treatment when cost is null/zero (Claude Max, ChatGPT Plus, GLM Coding, Copilot).
Collaborator
Author
# Conflicts: # packages/backend/src/routing/proxy/__tests__/proxy-message-recorder.routes.spec.ts # packages/backend/src/routing/proxy/__tests__/proxy-message-recorder.spec.ts # packages/backend/src/routing/proxy/__tests__/proxy.controller.spec.ts # packages/backend/src/routing/proxy/proxy-message-recorder.ts # packages/frontend/src/components/message-table-cells.tsx
…eRecorder The post-merge constructor takes both deps (11 args). Two non-conflicting test instantiations each passed only one of them, breaking the backend typecheck. Add the missing mock to each.
This was referenced May 26, 2026
guillaumegay13
added a commit
that referenced
this pull request
May 26, 2026
OpenCode Go bills a per-request slice of its dollar quota ($12 / 5h) rather than a flat fee, but the model picker and routing tier cards showed every subscription model as "Included in subscription". The per-request cost was already computed for cost recording (#1885) — it just wasn't surfaced. The available-models endpoint now includes `cost_per_request` for OpenCode Go models (from OpencodeGoCatalogService), and the picker / HeaderTierCard / RoutingTierCard render it via a new `formatPerRequestCost` helper (e.g. "$0.0136/req"), falling back to "Included in subscription" for flat-fee subscriptions (Claude Max, ChatGPT Plus, GLM Coding, Copilot).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
cost_usd = $0.00and the dashboard reinforced it by always rendering$0.00for any subscription row. OpenCode Go is a per-request dollar quota ($12 / 5h), so each call now records the docs-attributed USD cost (e.g. GLM-5.1 =$12 / 880 = $0.013636) and the dashboard surfaces it.$0.00and keep the existing "Included in subscription" treatment.agent_messages.cost_usdalready accepts the value.Approach
OpencodeGoCatalogServicenow parses the Usage Limits markdown table alongside the existing Endpoints table, derivescost = $12 / requestsPer5hper model, and matches across the two tables via a name normalizer (lowercase, strip parenthesized suffixes like(≤ 256K)).computeTokenCostaccepts an optionalperRequestCostUsd; for subscription rows it returns that value when positive, else falls back to0.ProxyMessageRecordercanonicalizes the provider throughPROVIDER_BY_ID_OR_ALIASand looks up cost via the catalog only whenprovider === opencode-goANDauthType === subscription.OnModuleInit+ an awaitedresolveCostPerRequest()sibling closes the cold-start window. Concurrentlist()calls are coalesced through an inflight promise.CostCelldistinguishes per-request subscriptions (non-zero cost → render real value with a "Per-request subscription cost: $0.xxxxxx" tooltip) from flat-fee ones (null/zero cost → keep "$0.00 / Included in subscription").Test plan
cost-calculator.tsandopencode-go-catalog.service.ts).$0.00 for subscriptiontests updated to the new per-request contract).tsc --noEmitclean (backend + frontend).npm run lint(no new errors).npm run buildclean.go.mdx— all 12 OpenCode Go models resolve to plausible per-request costs (GLM-5.1 $0.013636,DeepSeek V4 Flash $0.000379, etc)./v1/chat/completions(modelopencode-go/glm-5.1) recordedcost_usd = 0.013636inagent_messagesand rendered as$0.01in the Messages dashboard.Out of scope
The reporter also asks for quota tracking against the
$12 / 5h,$30 / week,$60 / monthlimits — that's a separate feature and not part of this fix.