Skip to content

fix(usage): parse Antigravity SSE usage and count tool calls - #70

Open
mahdiwafy wants to merge 2 commits into
Vanszs:mainfrom
mahdiwafy:pr/usage-antigravity-tool-calls
Open

fix(usage): parse Antigravity SSE usage and count tool calls#70
mahdiwafy wants to merge 2 commits into
Vanszs:mainfrom
mahdiwafy:pr/usage-antigravity-tool-calls

Conversation

@mahdiwafy

Copy link
Copy Markdown

Summary

  • Parses Antigravity/Gemini SSE usage metadata from response.candidates / usageMetadata.
  • Counts streaming tool calls toward completion tokens when there is no text output.

Scope

Focused only on usage accounting fixes.

Verification

  • npm run lint:undef

…+ usageMetadata) in parseSSEToOpenAIResponse

The parseSSEToOpenAIResponse function only understood OpenAI SSE format
(choices[0].delta.content, root-level usage). Antigravity/Gemini SSE
chunks use a different schema:

  data: {"response":{"candidates":[{...}],"usageMetadata":{...}}}

- Text content from response.candidates[0].content.parts[].text
- Reasoning from .parts[].thought
- Finish reason from candidate.finishReason
- Usage from response.usageMetadata (promptTokenCount, candidatesTokenCount,
  thoughtsTokenCount, totalTokenCount)

Also moves the OpenAI usage extraction before the AG block so the
existing path is preserved for OpenAI-formatted chunks.
…age tracking

Tool-call-only turns (agentic coding clients like Claude Code/Kiro sending
100+ tools) stream their real output entirely through delta.tool_calls
(and delta.partial_json for Claude tool_use), with delta.content and
delta.reasoning_content empty. totalContentLength in createSSEStream()
only summed content/reasoning length, so these turns left it stuck at 0.

That starved both usage fallbacks:
- estimateUsage() (provider sent no/invalid usage at all)
- the new hasZeroCompletionWithContent()/fixZeroCompletionUsage() path
  (provider sent completion_tokens: 0 despite real output)

Net effect: usage logs showed "out=0" for most tool-heavy turns even
though the client received a large tool_calls payload — matching the
live claude-sonnet-5 report on VansRouter (a5a5b570 account/Shiteru).

Changes:
- stream.js: sum delta.tool_calls[].function.name/arguments length (both
  PASSTHROUGH and TRANSLATE modes) and delta.partial_json (Claude tool_use)
  into totalContentLength.
- usageTracking.js: add hasZeroCompletionWithContent() and
  fixZeroCompletionUsage() to patch a provider-reported zero completion
  count without discarding its (usually accurate) prompt_tokens.
- Wire the new fallback into all 4 usage-decision sites in stream.js.

Verified: reproduced the exact bug shape in a new integration test
(in=166912, completion_tokens: 0 finish chunk, pure tool_calls output) —
now estimates a non-zero completion count instead of logging 0. Deployed
the fix live to the running container and confirmed in production logs:
claude-sonnet-5 tool-heavy turns now log out=44/56/322/etc instead of a
long run of out=0.

Separately observed (NOT fixed by this change, needs its own
investigation): a distinct pattern of genuinely empty completions
(response.content === "[Empty streaming response]", no tool_calls
either) from the Shiteru-backed claude-sonnet-5 route once prompt size
exceeds ~130k tokens — confirmed via requestDetails table, not a usage-
counting artifact.
@mahdiwafy mahdiwafy closed this Jul 30, 2026
@mahdiwafy
mahdiwafy deleted the pr/usage-antigravity-tool-calls branch July 30, 2026 05:06
@mahdiwafy
mahdiwafy restored the pr/usage-antigravity-tool-calls branch July 30, 2026 05:15
@mahdiwafy mahdiwafy reopened this Jul 30, 2026
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