Repository navigation
Conversation
…a usage Anthropic-compatible providers can report input_tokens as 0 in message_start and send the real count in message_delta. The stream parser only read input_tokens from message_start, so the final usage chunk, and the request log built from it, recorded prompt_tokens=0 with total_tokens equal to output tokens alone. Use message_delta's input_tokens when it is non-zero.
|
I traced the streaming usage path end to end, from the provider stream through the gateway's request logging to the backend ingestion. On current I did find one real gap along the way. The Anthropic stream parser only read @strzhao, could you share which provider and |
|
Here's our setup and a live capture that confirms your second commit covers our case exactly. Deployment: a custom provider (named Production evidence (agentcc_request_log, our pilot stack): all 5 streaming rows for this provider have Raw SSE from the upstream (one minimal streaming call just now, usage fields verbatim): So BigModel reports the real input count only in |
|
Thanks @strzhao, that SSE capture confirms it. BigModel only reports input_tokens in message_delta, which is exactly what the second commit handles. I'd appreciate your re-check once it lands on dev. Since the reporter has confirmed the root cause and the fix, could a maintainer label #3446 as accepted so this can go through review? |

Summary
Streaming responses through the gateway saved only
total_tokensin request logs;prompt_tokensandcompletion_tokensstayed empty. The streaming path already reads usage from the final chunk, butmodels.Usageonly decoded OpenAI field names, so providers reportinginput_tokens/output_tokens(Anthropic-style) lost the split. This adds a fallback decoder so the split is recorded for those providers, which keeps cost, budget, and analytics numbers correct for streamed requests.Linked issues
Closes #3446
Linear: n/a
AI use
AI use: AI-assisted coding, reviewed and tested by me
Type of change
E2E coverage
E2E: exempt (gateway-internal)
1) What changes were done
A. Usage decoding accepts input/output token names
agentcc-gateway/internal/models/response.go: newUsage.UnmarshalJSON. It readsprompt_tokens/completion_tokensas before, falls back toinput_tokens/output_tokenswhen those are absent, and derivestotal_tokenswhen it is missing or 0.B. Regression tests
agentcc-gateway/internal/models/usage_split_test.go: covers the alias decoding, unchanged OpenAI behavior, and total derivation.2) Why the changes were done
Usagedecoder repairs every path that decodes provider usage (streaming chunks included) without touching each provider adapter.3) Tests written + scenarios each covers
agentcc-gateway/internal/models/usage_split_test.go— usage decoding across provider field namesTestStreamChunkUsage_InputOutputAliases— a stream chunk withinput_tokens/output_tokensfills prompt/completion (failed before the fix: prompt=0 completion=0 total=300)TestUsage_OpenAIFieldsUnchanged— OpenAI-format usage decodes exactly as beforeTestUsage_TotalDerivedWhenMissing— total is computed from the split when omitted4) How to run / test
Gateway tests
Backend (
bin/test), frontend, and contract checks are not affected; this is a Go gateway-only change.5) Screenshots / recordings
Test output
See the test output in section 3.
UI testing
N/A (gateway-internal).
6) Edge cases & considerations
total_tokens: never overwritten.TestExtractRunUsagestill passes).7) Pre-existing issues (NOT introduced by this PR)
go vetreports an existing warning aterrors_test.go:109.TestOrgProviderBuildFailureIsA502ServerError/unresolvable_hostcan fail due to a DNS timeout in some environments; unrelated to this change.8) Architectural / important decisions
Usage.UnmarshalJSONinstead of per-provider stream handlers, so every caller gets the fix at once.Checklist
bin/testpasses locally (backend) — N/A, Go gateway onlyyarn test:runpasses locally (frontend) — N/Ayarn contracts:checkpasses if API surface changed — N/A, no API change