Skip to content

fix(stream): inject stream_options.include_usage for OpenAI-compatible streaming - #3081

Closed
thatssoheil wants to merge 2 commits into
decolua:masterfrom
thatssoheil:fix/3017-stream-usage
Closed

thatssoheil wants to merge 2 commits into
decolua:masterfrom
thatssoheil:fix/3017-stream-usage

Conversation

@thatssoheil

@thatssoheil thatssoheil commented Aug 6, 2026 •

Copy link
Copy Markdown

What

Injects stream_options: { include_usage: true } into upstream OpenAI-compatible requests when streaming, so usage chunks are actually returned by upstreams that require the opt-in (fixes streaming usage showing 0).

Closes #3017

Streaming requests to OpenAI-compatible providers (e.g. opencode combo ->
deepseek-free) recorded IN 0 OUT 0 because the upstream never sent a usage
chunk: the client's stream request lacked stream_options.include_usage.
Ask for usage in the final chunk (same approach as the iflow executor) so
usageHistory and the dashboard reflect real token counts for streams.

Closes #3017
Copilot AI lite review requested due to automatic review settings August 6, 2026 13:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

afandiaziz pushed a commit to afandiaziz/9router that referenced this pull request Aug 8, 2026
Cherry-picked from open upstream PRs (none merged upstream as of 2026-08-09):
  decolua#3078 /api/pxpipe -> LOCAL_ONLY_PATHS (defense in depth)
  decolua#3085 enforce requireApiKey on GET /v1/models
  decolua#3063 SSRF guard on search baseUrl + block default-password remote login
  decolua#3081 inject stream_options.include_usage for OpenAI-compatible upstreams
  decolua#3083 read cached_tokens from nested prompt_tokens_details

Verified: no test regressions vs v0.5.50 baseline (88 pre-existing
failures unchanged); +21 new passing tests.
afandiaziz pushed a commit to afandiaziz/9router that referenced this pull request Aug 9, 2026
29 PR upstream di-cherry-pick (semua masih open upstream per 2026-08-09).
Rincian lengkap + link per PR ada di FORK-CHANGES.md.

P1 skala 2475 koneksi : decolua#2798 decolua#410 decolua#2879 decolua#879 decolua#2997
P2 akurasi token/usage: decolua#2422 decolua#2658 decolua#2762 decolua#2453 decolua#2668 decolua#2361
P3 provider & combo   : decolua#2526 decolua#3125 decolua#1434 decolua#2689 decolua#2439 decolua#2724 decolua#2647 decolua#1805
                        decolua#2909 decolua#2853 decolua#2508 decolua#2928 decolua#2345 decolua#2112 decolua#2786
P4 keamanan           : decolua#1666 decolua#2776

Revert decolua#664: menambah transformRequest kedua di DefaultExecutor sehingga
menimpa yang pertama dan mematikan stream_options/text.format/
injectReasoningContent/stripUnsupportedParams — termasuk PR decolua#3081 yang
sudah dipakai produksi.

Test: 88 gagal / 1783 lulus — nol regresi vs baseline v0.5.50 (88/1656).
chisewaguri added a commit to chisewaguri/9router that referenced this pull request Aug 12, 2026
Port of DurinDoor decolua#401: the generic stream_options.include_usage injection (from decolua#3081) leaked into Claude Messages requests, which reject the OpenAI-only field with 400 'stream_options: Extra inputs are not permitted'. Gate on the effective transport format being OpenAI (runtimeTransport.format, falling back to provider config), stripping the -apikey suffix.
chisewaguri added a commit to chisewaguri/9router that referenced this pull request Aug 12, 2026
The decolua#3081 injection added stream_options to the responses->chat translated body, but the translation doesn't carry the stream field. OpenAI-compatible upstreams (deepseek) reject with 'stream_options should be set along with stream = true'. Set stream:true since the executor streams upstream regardless.
@chisewaguri

chisewaguri commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

heads up, on the responses to chat path the translated body has no stream field, so just injecting stream_options makes openai compatible upstreams (deepseek) 400 with "stream_options should be set along with stream = true". we set transformed.stream = true alongside the injection, probably worth doing here too so responses clients dont break.

Review feedback (#3081): the executor-level stream flag can be true while
the translated body omits stream (Responses->chat conversion, Accept:
text/event-stream clients). Injecting stream_options into such a body
makes strict OpenAI-compatible upstreams (deepseek) 400 with
"stream_options should be set along with stream = true".

Gate injection on the body's own stream === true, matching what the
upstream actually receives.
@thatssoheil

Copy link
Copy Markdown
Author

Good catch, thanks. The executor-level stream flag is derived from body.stream !== false (chatCore.js), so streaming-capable clients that omit stream (Responses->chat path, Accept: text/event-stream) can reach the executor with stream=true while the translated body has no stream field.

Fixed in becd573: injection is now gated on transformed.stream === true, so stream_options only goes out with bodies that actually declare stream: true. Added a test covering the Responses->chat case (body without stream, executor flag true) - 4/4 pass.

@thatssoheil thatssoheil closed this by deleting the head repository Oct 2, 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.

Usage/token tracking shows 0 for streamed responses (STREAM mode), works fine for JSON/non-stream

3 participants