8000 fix(ai-gateway): read Bedrock invoke-with-response-stream usage from base64 chunks by pcbeingused333 · Pull Request #4078 · fosrl/pangolin · GitHub
[go: up one dir, main page]

Skip to content

fix(ai-gateway): read Bedrock invoke-with-response-stream usage from base64 chunks - #4078

Merged
oschwartz10612 merged 4 commits into
fosrl:devfrom
pcbeingused333:fix/bedrock-stream-usage
Nov 9, 2026
Merged

oschwartz10612 merged 4 commits into
fosrl:devfrom
pcbeingused333:fix/bedrock-stream-usage

Conversation

@pcbeingused333
Copy link
Copy Markdown
Contributor

Community Contribution License Agreement

By creating this pull request, I grant the project maintainers an unlimited,
perpetual license to use, modify, and redistribute these contributions under any terms they
choose, including both the AGPLv3 and the Fossorial Commercial license terms. I
represent that I have the right to grant this license for all contributed content.

AI Disclosure

This change was prepared with AI assistance (Claude Code): code, test and description. It was reviewed before submission.

Description

invoke-with-response-stream has no x-amzn-bedrock-* token headers. Each model event arrives inside a binary event-stream frame whose payload is {"bytes":"<base64>","p":"..."}, so the usage fields (input_tokens, cache_read_input_tokens, amazon-bedrock-invocationMetrics, ...) are never present as clear text. extractBedrockModelInvoke searched the raw text, so extractUsage returned null and the pipeline fell back to estimateUsage over the base64 text: estimated tokens/cost and no cache counts. The comment there assumed a regex over the binary was enough, which is true for converse-stream (plain JSON) but not for this API.

The fix adds a small private helper that pulls each "bytes" value, decodes it with Buffer.from(..., "base64") and re-expresses the events as data: <json> lines. extractBedrockModelInvoke scans that text (falling back to the original text when no chunks are found), for both the Anthropic-style and the inputTokenCount/outputTokenCount shapes. Headers still take priority; extractBedrockConverse is untouched.

Note: no response from real AWS Bedrock was captured, and the behaviour has not been verified against a live endpoint. The frame format was checked offline: frames encoded with the AWS event-stream codec were accepted by @aws-sdk/client-bedrock-runtime as chunk events, and binary frames in that format, split at arbitrary byte boundaries and decoded with a streaming TextDecoder as the gateway does, yield the exact counts with this change. The unit tests use a simplified framing around the same {"bytes":...} payload.

How to test?

npx tsx server/lib/aiUsageExtraction.test.ts: the new testInvokeStreamAnthropicChunks and testInvokeStreamInvocationMetrics fail on main (extractor returns null) and pass with this change, together with the existing tests and a header-priority check.

oschwartz10612 and others added 4 commits November 4, 2026 14:48
Gemini reports the tokens of tool results (Google Search, URL context) in usageMetadata.toolUsePromptTokenCount, separate from promptTokenCount. The extractor ignored it, so usage and cost were under-counted.
…prompt-tokens

fix(ai): count Gemini toolUsePromptTokenCount as input tokens
…base64 chunks

invoke-with-response-stream wraps each model event as {"bytes":"<base64>"} inside event-stream frames, so the usage fields never appear in clear text. extractUsage returned null and the pipeline fell back to estimating tokens over the base64, losing real counts and cache tokens. Decode the chunks before scanning.
@oschwartz10612
oschwartz10612 changed the base branch from main to dev November 9, 2026 16:00
@oschwartz10612
oschwartz10612 merged commit 4af9e3a into fosrl:dev Nov 9, 2026
3 checks passed
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.

2 participants

0