You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[AISAGE-756] fix(ai-gateway): force a truthy stream to true - #8554
A request could set stream to a truthy value that is not a boolean, such as "true" or 1, and skip usage recording. The proxy added stream_options.include_usage for any truthy stream, so an upstream could stream the response. Usage accounting reads the response as SSE only when stream === true, so it parsed the SSE body as JSON. That parse threw, no usage row was written, and the request was served free.
handleLlmProxyRequest now sets any truthy stream to true right after parsing the body, for Chat Completions, Responses and Messages. The upstream request and usage accounting then agree that the response is streamed.
Verification
Unit tests: a truthy non-boolean stream on each API reaches the upstream as true and is accounted as streaming.
No manual test against an upstream; the change only normalizes the request body.
chrarnoldus
changed the title
[AISAGE-756] fix(ai-gateway): reject non-boolean stream, estimate lost usage
[AISAGE-756] fix(ai-gateway): force a truthy stream to true
Oct 22, 2026
The two changed files were reviewed: handleLlmProxyRequest now coerces any truthy non-boolean stream to true before the upstream call and before usageContext.isStreaming is derived, so usage accounting and the upstream request agree on streaming. The new it.each case exercises Chat Completions, Responses and Messages and asserts both the forwarded body and isStreaming, and the intermediate commit's estimate/fallback code is fully reverted with no dangling references.
Assumptions:[INFERENCE] the fix relies on upstreams treating any truthy stream as streaming (e.g. a client sending stream: "false" now becomes streaming); if an upstream instead rejects or ignores it, usage handling behaves exactly as before. [INFERENCE] the residual failure mode — an upstream returning a non-SSE body to a request the proxy treats as streaming — still has no fallback in countAndStoreUsage and would silently lose usage, but that path is pre-existing and outside this diff.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A request could set
streamto a truthy value that is not a boolean, such as"true"or1, and skip usage recording. The proxy addedstream_options.include_usagefor any truthystream, so an upstream could stream the response. Usage accounting reads the response as SSE only whenstream === true, so it parsed the SSE body as JSON. That parse threw, no usage row was written, and the request was served free.handleLlmProxyRequestnow sets any truthystreamtotrueright after parsing the body, for Chat Completions, Responses and Messages. The upstream request and usage accounting then agree that the response is streamed.Verification
streamon each API reaches the upstream astrueand is accounted as streaming.Visual Changes
N/A