Skip to content

fix: track real per-request cost for OpenCode Go subscriptions#1885

Merged
guillaumegay13 merged 7 commits into
mnfst:mainfrom
guillaumegay13:codex/issue-1859-opencode-go-cost
May 26, 2026
Merged

fix: track real per-request cost for OpenCode Go subscriptions#1885
guillaumegay13 merged 7 commits into
mnfst:mainfrom
guillaumegay13:codex/issue-1859-opencode-go-cost

Conversation

@guillaumegay13

@guillaumegay13 guillaumegay13 commented May 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Fixes [BUG] Opencode-Go Cost Tracking = $0.00 #1859 — OpenCode Go messages were logging cost_usd = $0.00 and the dashboard reinforced it by always rendering $0.00 for 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.
  • Other flat-fee subscriptions (Claude Max, ChatGPT Plus, GLM Coding, Copilot) keep recording $0.00 and keep the existing "Included in subscription" treatment.
  • No schema migration. agent_messages.cost_usd already accepts the value.

Approach

  1. OpencodeGoCatalogService now parses the Usage Limits markdown table alongside the existing Endpoints table, derives cost = $12 / requestsPer5h per model, and matches across the two tables via a name normalizer (lowercase, strip parenthesized suffixes like (≤ 256K)).
  2. computeTokenCost accepts an optional perRequestCostUsd; for subscription rows it returns that value when positive, else falls back to 0.
  3. ProxyMessageRecorder canonicalizes the provider through PROVIDER_BY_ID_OR_ALIAS and looks up cost via the catalog only when provider === opencode-go AND authType === subscription.
  4. Catalog warmup on OnModuleInit + an awaited resolveCostPerRequest() sibling closes the cold-start window. Concurrent list() calls are coalesced through an inflight promise.
  5. Frontend CostCell distinguishes 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

  • Backend unit tests (4487 total; 100% line coverage on cost-calculator.ts and opencode-go-catalog.service.ts).
  • Frontend unit tests (2817 total; existing $0.00 for subscription tests updated to the new per-request contract).
  • tsc --noEmit clean (backend + frontend).
  • npm run lint (no new errors).
  • npm run build clean.
  • Dry-ran the parser against the live go.mdx — all 12 OpenCode Go models resolve to plausible per-request costs (GLM-5.1 $0.013636, DeepSeek V4 Flash $0.000379, etc).
  • Live end-to-end smoke test with a real OpenCode Go subscription against a local Manifest server: a routed request through /v1/chat/completions (model opencode-go/glm-5.1) recorded cost_usd = 0.013636 in agent_messages and rendered as $0.01 in the Messages dashboard.
  • E2E tests not run locally (no Postgres in this worktree); CI will cover.

Out of scope

The reporter also asks for quota tracking against the $12 / 5h, $30 / week, $60 / month limits — that's a separate feature and not part of this fix.

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

codecov Bot commented May 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.41%. Comparing base (bcf8858) to head (7c98e48).

Additional details and impacted files

Impacted file tree graph

@@           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           
Flag Coverage Δ
frontend 99.46% <100.00%> (+<0.01%) ⬆️
shared 97.85% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@cubic-dev-ai cubic-dev-ai 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.

No issues found across 9 files

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).
@guillaumegay13

Copy link
Copy Markdown
Collaborator Author
Capture d’écran 2026-05-11 à 18 43 30

@guillaumegay13 guillaumegay13 requested a review from brunobuddy May 11, 2026 19:14
# 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.
@guillaumegay13 guillaumegay13 merged commit 6373274 into mnfst:main May 26, 2026
17 checks passed
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).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Opencode-Go Cost Tracking = $0.00

1 participant