Skip to content

feat(opencode): surface turn-level cache and spend telemetry - #649

Open
yzxcj797 wants to merge 4 commits into
yc-software:mainfrom
yzxcj797:feat/opencode-turn-usage-586g
Open

feat(opencode): surface turn-level cache and spend telemetry#649
yzxcj797 wants to merge 4 commits into
yc-software:mainfrom
yzxcj797:feat/opencode-turn-usage-586g

Conversation

@yzxcj797

@yzxcj797 yzxcj797 commented Aug 21, 2026

Copy link
Copy Markdown

Summary

Completes the harness coverage started in #645/#646: opencode's usage only existed on the per-capture session_llm_requests rows flushed after the turn returned, so opencode turns landed in turn_metrics with no cache or spend telemetry.

Implementation

The flush was already awaited in finally before the turn's promise resolved, so the happy path now runs it before building the result — no user-visible timing change, it only lets the result observe what the flush recorded. Each capture's usageFromInfo is summed into a turn accumulator (child sessions keep their own; misaligned captures record without usage and contribute nothing). The result maps the totals through the shared usageToTurnTelemetry (moved to harness.ts in #646). Unlike codex, opencode's provider computes cost, so costUsage carries a real number. The finally flush stays for every non-happy path and is a no-op after the splice.

Stacked on #646 (which is stacked on #645) — merge order: #645#646 → this.

Testing

  • The existing "records real usage, cost, and timings" test now asserts the turn result carries cacheUsage {cacheRead: 50, cacheWrite: 10, uncachedInput: 100} and costUsage {outputTokens: 20, costUsd: 0.0353} from the same fixture that feeds the llm row. Passes where the fake-sidecar harness tests run; on this Windows host those tests fail identically with and without the change (spawn of a shebang script), so the local differential for them is inconclusive — tsc --noEmit is clean and the accumulation is pure data flow.

Follow-up to #586


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

…/metrics

The budget now debits the LlmCallUsage the harnesses meter, but an
operator planning which models to run qm on still had to join spend out
of session_llm_requests by hand: turn_metrics carried the cache columns
and no cost, no output tokens.

Harness turn results gain costUsage {outputTokens, costUsd} alongside
cacheUsage -- pi sums its per-call stats, claude reports the SDK's
running cost total with the fallback branch flagging unknown cost, and
the mock harness reports its deterministic usage. The orchestrator
lands both on TurnMetricSample, turn_metrics grows output_tokens and
cost_usd (ALTER TABLE ADD COLUMN IF NOT EXISTS, so existing deployments
migrate in place), and /v1/admin/metrics returns a spend block:
samples, turnsWithKnownCost, costUsdTotal, outputTokensTotal.

Follow-up to yc-software#586
codex accumulated full per-thread usage live but its turn result
carried none of it, so codex turns landed in turn_metrics without the
cache or spend telemetry pi and claude already report.

The turn result now maps the same thread totals the recordLlmRequest
flush uses, through one pure usageToTurnTelemetry helper: cacheUsage
(cacheRead/cacheWrite/uncachedInput) and costUsage (outputTokens,
costUsd). The codex SDK reports no cost, so costUsd stays 0 -- the same
value already persisted to session_llm_requests for these calls.

opencode still reports no turn telemetry: its usage is only fetched in
the post-return flush, so wiring it needs a restructure noted for
follow-up.

Follow-up to yc-software#586
The mapping is harness-agnostic; the next harness to report turn
telemetry imports it from the module that owns the result shape.
opencode's usage only existed on the per-capture llm rows flushed after
the turn returned, so opencode turns landed in turn_metrics without the
cache or spend telemetry every other reporting harness carries.

The flush already awaited before the turn's promise resolved, so the
happy path now runs it before building the result and sums each
capture's usage into a turn accumulator (children keep their own); the
result maps it through the shared usageToTurnTelemetry. Unlike codex,
opencode's provider computes cost, so costUsage carries a real number.
The finally-block flush stays for every non-happy path and is a no-op
after the splice.

Follow-up to yc-software#586
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.

1 participant