Skip to content

fix(usage): preserve provider-billed zero totals#101177

Merged
steipete merged 6 commits into
openclaw:mainfrom
snowzlmbot:fix/usage-cost-provenance-zero-total
Jul 7, 2026
Merged

fix(usage): preserve provider-billed zero totals#101177
steipete merged 6 commits into
openclaw:mainfrom
snowzlmbot:fix/usage-cost-provenance-zero-total

Conversation

@snowzlmbot

@snowzlmbot snowzlmbot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

Persisted usage.cost.total: 0 is ambiguous: it can be an estimated placeholder that should be recomputed, or an authoritative billed zero from a free provider route. Without provenance, transcript summaries can replace a legitimate free-route total with catalog pricing.

This follows up #97047 and supersedes the closed #97134 and #101163 attempts.

Why This Change Was Made

OpenRouter includes detailed usage, including cost, in the final streaming chunk. The first maintainer rewrite tried to reconcile cost through OpenRouter's generation-metadata endpoint, but credentialed live proof exposed an asynchronous materialization race: the completed stream could still be followed by repeated generation lookup 404s.

The final implementation consumes the provider-reported final-chunk cost directly in both OpenAI-compatible stream parsers. A shared validator accepts only finite, non-negative numeric totals and marks them provider-billed; missing or malformed costs keep the existing catalog estimate. This removes the extra network request, retry policy, response wrapper, and provider/model-specific core logic.

Official contracts checked:

User Impact

  • Provider-billed zero totals remain zero in transcripts, summaries, logs, and durable usage caches.
  • Unproven placeholder zeros keep current behavior: known-priced rows are recomputed and unknown-priced rows remain missing-cost entries.
  • OpenRouter completion no longer waits for or depends on a post-stream metadata request.

What Changed

  • Add the closed optional usage.cost.totalOrigin: "provider-billed" marker to the canonical usage type.
  • Apply validated provider-reported totals in both the reusable package parser and the embedded-agent managed transport parser.
  • Preserve the marker through replay-history normalization.
  • Teach session aggregation and cache reconstruction to retain authoritative billed zeros.
  • Bump the durable usage-cost cache version from 6 to 7.
  • Remove the OpenRouter generation lookup/retry/wrapper path after live proof showed its timing race.

Evidence

Exact head: 9b20ff17137f1326ded9e7c2a56f8c8b8616ffd8.

  • Blacksmith Testbox focused proof: run 28841560054
    • 523 tests passed across the package parser, embedded-agent transport, OpenRouter provider, replay sanitizer, and session accounting.
    • Targeted formatting and Oxlint passed.
  • Whole-branch autoreview: clean after its first pass found the separate embedded-agent parser; the accepted finding was fixed and both local-diff and whole-branch reviews were rerun clean.
  • Final exact-head PR CI: run 28843221762.
  • Final exact-head real behavior proof: run 28843220470.
  • Credentialed OpenRouter extension live proof: run 28842089462.
    • extensions/openrouter/openrouter.live.test.ts passed its live free-route billed-zero assertion.
    • The shared advisory shard also recorded an unrelated OpenCode live-auth failure; the OpenRouter target itself passed.
  • Credentialed real-Gateway OpenRouter profile proof: run 28842813349.
    • The workflow selected functional code head 0b9758d93d5 and pinned the catalog-backed openrouter/openrouter/auto model through a temporary harness branch. Final head 9b20ff17137 differs only by removing the release-owned root changelog entry required by the native prepare guard.
    • Prompt, file-read tool, exec+read tool, and image probes passed end to end.
    • The advisory file's only failure was an unrelated stale Google priority assertion; the live runs meaningful prompts across models with available keys target passed (109/110 tests overall).
  • git diff --check: passed.

The earlier live attempts are useful negative proof: generation-metadata polling failed to observe the completed free-route generation reliably, which prompted the direct final-stream contract rewrite now under test.

Risk / Compatibility

The marker is optional and additive. Existing transcript rows remain valid. Only a finite, non-negative provider-reported numeric total becomes authoritative; absent, string, negative, or non-finite values retain catalog estimation. Cache version 7 rebuilds older summaries under the new semantics.

No config, CLI, protocol, dependency, lockfile, secret, or UI surface changes. Screenshots are not applicable.

@snowzlmbot

Copy link
Copy Markdown
Contributor Author

Addressed the review direction from #101163 by replacing the provider/model-specific core rule with an explicit provenance contract:

  • Added usage.cost.totalOrigin?: "provider-billed" | "estimated".
  • OpenRouter now marks successful generation-metadata totals as provider-billed.
  • Core preserves usage.cost.total === 0 only when that total is explicitly provider-billed, or under the existing nonzero-component reconciliation guard.
  • Unproven known-priced zero totals keep current main behavior and still recompute.
  • No DeepSeek/OpenRouter model ids are hard-coded in core.

Validation so far:

  • Local targeted:
    • git diff --check -- src/agents/usage.ts packages/llm-core/src/types.ts src/infra/session-cost-usage.types.ts src/infra/session-cost-usage.ts src/infra/session-cost-usage.test.ts extensions/openrouter/stream.ts extensions/openrouter/index.test.ts
    • CI=1 NO_COLOR=1 node scripts/run-vitest.mjs run src/infra/session-cost-usage.test.ts extensions/openrouter/index.test.ts
      • src/infra/session-cost-usage.test.ts: 66 passed
      • extensions/openrouter/index.test.ts: 46 passed
  • GitHub Actions:
    • Real behavior proof: pass
    • build-artifacts: pass
    • check-prod-types: pass
    • check-lint: pass
    • check-test-types: pass
    • node compact shards: pass
    • security/high/path/label/dependency guards: pass

Known CI blocker not introduced by this diff:

  • QA Smoke CI fails before any scenario runs with:
    • Selected QA scenarios require multiple channels (telegram, matrix, slack, whatsapp); split the run by channel.
  • The same pre-scenario smoke-ci profile split failure appeared on fix(usage): preserve provider-billed zero costs #101163 as well. This patch does not touch QA profile/channel workflow code.

@clawsweeper review-pr

@clawsweeper

clawsweeper Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@RomneyDa
RomneyDa force-pushed the fix/usage-cost-provenance-zero-total branch from 6bcc50c to d94a866 Compare July 6, 2026 21:00
@clawsweeper

clawsweeper Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Codex review: stale review; fresh review needed.

Summary
The latest durable ClawSweeper review was for head 0b9758d93d5a7bdd20c24b753e172a715f6142a3, but the PR head is now 9b20ff17137f1326ded9e7c2a56f8c8b8616ffd8. Its old verdict and PR readiness labels are no longer current.

Next step
Run or wait for a fresh ClawSweeper review on the current PR head.

Review history (3 earlier review cycles)
  • reviewed 2026-07-06T21:10:57.386Z sha d94a866 :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-07T02:26:36.860Z sha d94a866 :: needs maintainer review before merge. :: none
  • reviewed 2026-07-07T04:35:04.593Z sha 1a0e6c5 :: found issues before merge. :: [P3] Remove the release-owned changelog edit

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jul 6, 2026
@snowzlmbot

Copy link
Copy Markdown
Contributor Author

Updated the PR body with current-head evidence for d94a866db584795872e27fae0b2b72dd4baac7fc without changing the code patch.

Added proof covers the requested provider-billed zero-total path through a redacted transcript summary, durable cache, and session-log spend path on a test server, plus exact-head GitHub Actions links.

@clawsweeper review-pr

@clawsweeper

clawsweeper Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 7, 2026
@steipete steipete self-assigned this Jul 7, 2026
@clawsweeper clawsweeper Bot removed proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jul 7, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. and removed status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jul 7, 2026
@steipete

steipete commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Maintainer rewrite and final review complete on exact head 0b9758d93d5a7bdd20c24b753e172a715f6142a3.

What changed:

  • Replaced the post-stream generation-metadata lookup after credentialed proof exposed its 404 materialization race.
  • Consume validated final-chunk provider cost in both OpenAI-compatible stream parsers.
  • Preserve the closed provider-billed provenance marker through replay, aggregation, logs, and durable cache rebuilds.
  • Keep existing placeholder-zero recomputation unchanged.
  • Added the changelog credit for @snowzlmbot.

Proof:

  • Blacksmith Testbox run 28841560054: 523 focused tests passed; targeted oxfmt and Oxlint passed.
  • Exact-head CI run 28842085117: passed.
  • Exact-head real-behavior run 28842090141: passed.
  • Credentialed OpenRouter extension run 28842089462: live free-route billed-zero assertion passed.
  • Credentialed Gateway run 28842813349: selected the exact PR head and openrouter/openrouter/auto; prompt, file-read, exec+read, and image probes passed. The advisory file's only failure was an unrelated stale Google priority assertion; the live model target passed (109/110 tests overall).
  • Fresh local-diff and whole-branch autoreviews: clean. The first review found the separate production embedded-agent parser; that accepted finding was fixed before both final reruns.
  • git diff --check: passed.

No UI changed, so before/after screenshots are not applicable. Review artifacts validate with zero findings. Ready for the repository-native prepare/merge flow.

@clawsweeper clawsweeper Bot removed proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jul 7, 2026
@steipete

steipete commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Final-head proof refresh: 9b20ff17137f1326ded9e7c2a56f8c8b8616ffd8 differs from the fully exercised implementation head only by removing the release-owned root changelog entry required by the native prepare guard.

  • Exact-head PR CI run 28843221762: passed.
  • Exact-head real-behavior run 28843220470: passed.
  • Native review artifacts validate with zero findings and no proof gaps.

The focused Testbox, credentialed OpenRouter extension, and real-Gateway prompt/tool/image proofs remain documented in the PR body. Ready for the native prepare/merge lane.

@steipete
steipete merged commit 53580e1 into openclaw:main Jul 7, 2026
113 of 118 checks passed
@steipete

steipete commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 7, 2026
* fix(usage): preserve provider-billed zero totals

* fix(usage): harden provider-billed cost provenance

* fix(openrouter): retry delayed generation metadata

* fix(openrouter): satisfy retry lint

* refactor(openrouter): consume streamed billed cost

* chore: keep release notes out of contributor PR

---------

Co-authored-by: snowzlmbot <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
sheyanmin pushed a commit to sheyanmin/openclaw that referenced this pull request Jul 8, 2026
* fix(usage): preserve provider-billed zero totals

* fix(usage): harden provider-billed cost provenance

* fix(openrouter): retry delayed generation metadata

* fix(openrouter): satisfy retry lint

* refactor(openrouter): consume streamed billed cost

* chore: keep release notes out of contributor PR

---------

Co-authored-by: snowzlmbot <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
giodl73-repo pushed a commit to giodl73-repo/openclaw that referenced this pull request Jul 8, 2026
* fix(usage): preserve provider-billed zero totals

* fix(usage): harden provider-billed cost provenance

* fix(openrouter): retry delayed generation metadata

* fix(openrouter): satisfy retry lint

* refactor(openrouter): consume streamed billed cost

* chore: keep release notes out of contributor PR

---------

Co-authored-by: snowzlmbot <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling extensions: openrouter P2 Normal backlog priority with limited blast radius. size: L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants