Skip to content

fix(usage): read Gemini usageMetadata out of the antigravity response envelope - #10430

Merged
diegosouzapw merged 13 commits into
cheaper-inference:release/v3.8.50from
rqzbeh:fix/antigravity-usage-envelope
Aug 16, 2026
Merged

diegosouzapw merged 13 commits into
cheaper-inference:release/v3.8.50from
rqzbeh:fix/antigravity-usage-envelope

Conversation

@rqzbeh

@rqzbeh rqzbeh commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Non-streaming Antigravity / gemini-cli requests wrapped their payloads in
{ response: { ... } }, but extractUsageFromResponse only tested the
top-level usageMetadata — so every non-streaming antigravity request
logged IN 0 | OUT 0 and zeroed rows in the usage dashboard. This is the
port of upstream decolua/9router#59d858b ("fix(usage): read Gemini
usageMetadata out of the antigravity response envelope").

The fix reads responseBody.usageMetadata || responseBody.response?.usageMetadata
(the same envelope read the streaming path and usageTracking already use);
top-level metadata keeps priority and the OpenAI / Claude branches are
untouched.

Related Issues

Validation

  • Change type: fix (usage accounting)
  • Focused tests and category gates from the golden path
  • npm run lint — no issues in changed files (repo-wide baseline noise documented)
  • Reconciled with the current active release base (branch from release/v3.8.50 tip)
  • PR is mergeable / CI is running

Tests Added Or Updated

  • tests/unit/usage-extractor.test.ts: two new tests —
    • reads Gemini usageMetadata from the antigravity { response: {...} } envelope
    • prefers top-level usageMetadata over the envelope when both are present
  • Ran: usage-extractor, token-accounting-input-fix, xai-exact-cost-2453 suites — 49/49 pass.

Coverage Notes

  • Streaming usage (usageTracking.ts) already unwrapped the envelope; this
    closes the non-streaming gap (chatCore.ts:4367 + semantic-cache path).

Reviewer Notes

  • Same port shipped upstream in 9router v0.5.55 (2026-08-14) — verified
    against the OmniRoute codebase before implementing.

… envelope

Port decolua/9router#59d858b: antigravity/gemini-cli wrap non-streaming
payloads in { response: {...} }, so extractUsageFromResponse only saw the
top-level usageMetadata and every non-streaming antigravity request logged
zero usage (IN 0 | OUT 0) and zeroed usage-dashboard rows. Top-level
metadata keeps priority; OpenAI/Claude branches untouched.
@rqzbeh
rqzbeh requested a review from diegosouzapw as a code owner August 15, 2026 00:09
@diegosouzapw

Copy link
Copy Markdown
Collaborator

Thanks for the port, @rqzbeh — this is a clean, surgical fix. Verified locally against release/v3.8.50: node --import tsx/esm --test tests/unit/usage-extractor.test.ts passes 28/28 including your two new cases, and eslint on the touched files is clean. Good catch matching this up with the envelope-unwrap the streaming path already does at usageTracking.ts:675.

No changes requested — this looks merge-ready as-is. One small thought for a possible follow-up (not blocking this PR): the Gemini branch of extractUsageFromResponse still doesn't surface cachedContentTokenCount into a cached_tokens field the way the OpenAI/Claude/Responses branches do — your test fixture even includes it. Might be worth a separate PR if Gemini cache-hit accounting turns out to matter for cost tracking. Not something this fix needs to solve.

Nice work on tracing this all the way to the upstream issue and matching the existing envelope-read pattern.

@rqzbeh

rqzbeh commented Aug 15, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @diegosouzapw — good call on that gap. I've opened the follow-up as a separate PR: #10465 (fix(usage): surface Gemini cachedContentTokenCount as cached_tokens).

It adds cached_tokens: usageMetadata.cachedContentTokenCount || 0 to the Gemini branch of extractUsageFromResponse, matching the streaming path you referenced (usageTracking.ts:684), and the envelope test fixture now actually asserts the 7 it was carrying. Also added a dedicated regression test for the top-level case; usage-extractor runs 29/29 with the companion suites green and eslint clean.

One note: the branch is stacked on the #10430 branch, so the PR diff currently includes the envelope fix too — once #10430 merges, #10465 reduces to just its own delta. Happy to rebase onto release/v3.8.50 if you'd prefer it independent.

@diegosouzapw
diegosouzapw merged commit df226e5 into cheaper-inference:release/v3.8.50 Aug 16, 2026
16 checks passed
@diegosouzapw

Copy link
Copy Markdown
Collaborator

Validated in local merge-train .claude/worktrees/merge-train-20260816-000002-suite.log on devbox-vm-06-dev002 @ 8be62df65f61cc4cf29cdc3e6bc38173ffafa4e3 (FAST gates green: static + changed tests + vitest; daily full-suite run still required). Merged under the batch merge-train protocol (merge-gates §7). ⚠️ base-red inherited: #10523 (ServiceSupervisor order-dependent test, not touched by this batch).

diegosouzapw pushed a commit that referenced this pull request Aug 17, 2026
…10465)

* fix(usage): read Gemini usageMetadata out of the antigravity response envelope

Port decolua/9router#59d858b: antigravity/gemini-cli wrap non-streaming
payloads in { response: {...} }, so extractUsageFromResponse only saw the
top-level usageMetadata and every non-streaming antigravity request logged
zero usage (IN 0 | OUT 0) and zeroed usage-dashboard rows. Top-level
metadata keeps priority; OpenAI/Claude branches untouched.

* chore(changelog): fragment for #10430 antigravity usage envelope

* fix(usage): surface Gemini cachedContentTokenCount as cached_tokens

Review follow-up on #10430: the Gemini branch of extractUsageFromResponse
ignored cachedContentTokenCount, so non-streaming cache-hit tokens never
reached the cached_tokens field the OpenAI/Claude/Responses branches
already populate (and the streaming path surfaces at usageTracking.ts:684).

Adds cached_tokens: usageMetadata.cachedContentTokenCount || 0, updates
the three Gemini assertions (envelope fixture already carried
cachedContentTokenCount: 7), and adds a dedicated regression test.

* chore(changelog): fragment for #10465 Gemini cached_tokens surfacing

* test(fix): refresh expired alibaba quota sample validity and onnxruntime pin for v3.8.50 base

- alibaba-free-tier-quota-fetcher.test.ts: sample quotaValidityPeriod
  (2026-08-16 16:00 UTC) is in the past, making every quota entry classify
  as expired/not_capable; bump to 2028-01-01 UTC so the text/merge
  classification tests exercise the intended path again.
- optional-transformers-dependency.test.ts: onnxruntime-node pin assertion
  updated from ~1.24.3 to ~1.27.0 to match package.json (bumped by #10403);
  the regular-not-optional intent is unchanged.

---------

Co-authored-by: Rouzbeh <rqzbeh@users.noreply.github.com>
tkgo11 pushed a commit to tkgo11/OmniRoute that referenced this pull request Sep 23, 2026
… envelope (cheaper-inference#10430)

* fix(usage): read Gemini usageMetadata out of the antigravity response envelope

Port decolua/9router#59d858b: antigravity/gemini-cli wrap non-streaming
payloads in { response: {...} }, so extractUsageFromResponse only saw the
top-level usageMetadata and every non-streaming antigravity request logged
zero usage (IN 0 | OUT 0) and zeroed usage-dashboard rows. Top-level
metadata keeps priority; OpenAI/Claude branches untouched.

* chore(changelog): fragment for cheaper-inference#10430 antigravity usage envelope

* ci: re-run dast-smoke (Build CLI bundle runner timeout flake)

---------

Co-authored-by: Rouzbeh <rqzbeh@users.noreply.github.com>
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
… envelope (cheaper-inference#10430)

* fix(usage): read Gemini usageMetadata out of the antigravity response envelope

Port decolua/9router#59d858b: antigravity/gemini-cli wrap non-streaming
payloads in { response: {...} }, so extractUsageFromResponse only saw the
top-level usageMetadata and every non-streaming antigravity request logged
zero usage (IN 0 | OUT 0) and zeroed usage-dashboard rows. Top-level
metadata keeps priority; OpenAI/Claude branches untouched.

* chore(changelog): fragment for cheaper-inference#10430 antigravity usage envelope

* ci: re-run dast-smoke (Build CLI bundle runner timeout flake)

---------

Co-authored-by: Rouzbeh <rqzbeh@users.noreply.github.com>
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…heaper-inference#10465)

* fix(usage): read Gemini usageMetadata out of the antigravity response envelope

Port decolua/9router#59d858b: antigravity/gemini-cli wrap non-streaming
payloads in { response: {...} }, so extractUsageFromResponse only saw the
top-level usageMetadata and every non-streaming antigravity request logged
zero usage (IN 0 | OUT 0) and zeroed usage-dashboard rows. Top-level
metadata keeps priority; OpenAI/Claude branches untouched.

* chore(changelog): fragment for cheaper-inference#10430 antigravity usage envelope

* fix(usage): surface Gemini cachedContentTokenCount as cached_tokens

Review follow-up on cheaper-inference#10430: the Gemini branch of extractUsageFromResponse
ignored cachedContentTokenCount, so non-streaming cache-hit tokens never
reached the cached_tokens field the OpenAI/Claude/Responses branches
already populate (and the streaming path surfaces at usageTracking.ts:684).

Adds cached_tokens: usageMetadata.cachedContentTokenCount || 0, updates
the three Gemini assertions (envelope fixture already carried
cachedContentTokenCount: 7), and adds a dedicated regression test.

* chore(changelog): fragment for cheaper-inference#10465 Gemini cached_tokens surfacing

* test(fix): refresh expired alibaba quota sample validity and onnxruntime pin for v3.8.50 base

- alibaba-free-tier-quota-fetcher.test.ts: sample quotaValidityPeriod
  (2026-08-16 16:00 UTC) is in the past, making every quota entry classify
  as expired/not_capable; bump to 2028-01-01 UTC so the text/merge
  classification tests exercise the intended path again.
- optional-transformers-dependency.test.ts: onnxruntime-node pin assertion
  updated from ~1.24.3 to ~1.27.0 to match package.json (bumped by cheaper-inference#10403);
  the regular-not-optional intent is unchanged.

---------

Co-authored-by: Rouzbeh <rqzbeh@users.noreply.github.com>
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.

Title: Antigravity: usage always shows IN 0 · OUT 0 despite successful DONE (v0.5.50)

2 participants