Repository navigation
fix(sse): estimate usage in passthrough stream even with include_usage - #12151
Merged
diegosouzapw merged 1 commit intoSep 1, 2026
Conversation
maxmad64bis
force-pushed
the
fix/stream-passthrough-usage-estimation
branch
from
August 30, 2026 19:22
271f8f6 to
0430001
Compare
maxmad64bis
force-pushed
the
fix/stream-passthrough-usage-estimation
branch
from
August 30, 2026 19:50
0430001 to
e29540b
Compare
diegosouzapw
merged commit Sep 1, 2026
5e6c9a9
into
cheaper-inference:release/v3.8.51
8 of 16 checks passed
This was referenced Sep 1, 2026
diegosouzapw
added a commit
to jonlwheat2-gif/OmniRoute
that referenced
this pull request
Sep 1, 2026
The helper was leftover from cheaper-inference#12151 and fails the absolute lint:json --max-warnings 0 gate on every PR that includes it. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
This was referenced Sep 1, 2026
6 tasks done
linhdmn
added a commit
to linhdmn/omniroute-INITIAL_PASSWORD-fix
that referenced
this pull request
Sep 1, 2026
…erscore Pre-existing cheaper-inference#12151 test file tripped the strict no-unused-vars gate (all three identifiers never referenced). Tests still pass.
maxmad64bis
pushed a commit
to maxmad64bis/OmniRouteFork
that referenced
this pull request
Sep 1, 2026
… + stream gates Docs cheaper-inference#12320 added radarPage.colLimits / trainsOnPrompts / trainsOnPromptsHelp as __MISSING__ in 42 locales but never added the EN source. That broke two gates: i18n-missing-placeholder-fallback (cheaper-inference#7258, "__MISSING__ after EN fallback") and settings-i18n-keys ("direct translation calls have English messages"). Add the three EN keys (values are the sentinels' English text). cheaper-inference#12151 changed passthrough to estimate usage on finish without usage when content exists and to drop the trailing choices:[] valid usage once estimated. tests/unit/stream-utils "forwards OpenAI usage-only empty choices chunks" still expected the old forward. Update it to assert estimated:true and no choices:[] (same tradeoff documented in stream-passthrough-usage-estimation). Also drops the dead collectSSE helper in stream-passthrough-usage- estimation.test.ts (3 unused identifiers) that reds No new ESLint warnings — same as already-merged fix/release-v3.8.51-eslint-unused- collectsse. Reproduced on pure tip 438db55: 4 RED (ESLint, i18n-missing-fallback, settings-i18n-keys, stream-utils ×2 via TIA + shard 4/4). After: 4 suites pass locally (i18n-missing, settings-i18n-keys, stream- passthrough, stream-utils 40/40).
diegosouzapw
added a commit
that referenced
this pull request
Sep 1, 2026
…e regression + radarPage i18n keys (#12327) * fix(sse): forward the upstream's real trailing usage in passthrough; estimate only at flush #12151 injected estimated usage into the finish chunk and dropped the real trailing usage block that genuine OpenAI upstreams send afterwards — metered clients got estimates instead of real token counts. The estimate now leaves via a canonical usage-only chunk at flush, only when the upstream stayed silent; a real trailing block is forwarded verbatim and wins. The tool_calls finish_reason normalization now materializes its own rewrite (it piggybacked on the removed finish-time rewrite), and the dead collectSSE helper goes with it (subsumes #12324). * fix(i18n): seed the radarPage limits/training keys the #12320 UI already consumes RadarCatalogTable.tsx references radarPage.colLimits / trainsOnPrompts / trainsOnPromptsHelp but #12320 never added them to en.json, so the EN fallback could not resolve the __MISSING__ markers across 42 locales. Real translations for pt-BR and vi; the rest resolve via the EN fallback. * fix(sse): carry the chat stream id into flush-time synthetic chunks The estimated usage-only chunk emitted at flush used passthroughResponsesId, which is only ever set on the Responses path — on the chat path the synthetic chunk shipped id: null, breaking the string-id invariant pinned by stream-numeric-ids.test.ts. Track the upstream chat-completion id in the passthrough loop and reuse it (falling back to the Responses id, then a generated one). Sibling sweep: 74 files importing utils/stream — 603/603.
diegosouzapw
added a commit
that referenced
this pull request
Sep 2, 2026
* feat(ui): enable React Compiler (#67) Enable reactCompiler: true in next.config.mjs (Next 16 + React 19.2.8). This automates memoization at build time, removing manual useCallback/useMemo debt (591 + 283 instances respectively) and preventing stale-closure bugs. Test results (pre-existing failures unchanged): vitest UI: 282/295 files pass (13 fail = missing router/ReactFlow mocks) vitest: 1805/1857 tests pass (52 fail = same pre-existing mock issues) node:test: api/services/db all pass (except platform-specific serviceSupervisorSpawnError — Windows spawn("ls") issue) No new failures introduced by the compiler transform. Optional cleanup: remove now-redundant useCallback/useMemo in hot components. * fix(build): add babel-plugin-react-compiler peer dependency (#67) React Compiler (reactCompiler: true in next.config.mjs) requires babel-plugin-react-compiler as an explicit peer dependency — Next.js declares it as optional ("*") and does not auto-install it. Installed babel-plugin-react-compiler@1.0.0 as a devDependency. Resolves correctly from both the project root and the next package context (Turbopack resolution path). * fix(ci): allowlist babel-plugin-react-compiler for React Compiler The React Compiler peer is a real npm package (facebook/react, MIT) required by Next 16 `reactCompiler: true`. Adding it to the anti-slopsquat allowlist unblocks check:deps and the 6A.8 unit-test gate. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(ci): drop unused collectSSE helper that trips ESLint The helper was leftover from #12151 and fails the absolute lint:json --max-warnings 0 gate on every PR that includes it. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: WebPerson <jonlwheat2-gif@users.noreply.github.com>
Merged
4 of 5 tasks
patrykkopycinski
added a commit
to patrykkopycinski/OmniRoute
that referenced
this pull request
Sep 6, 2026
cheaper-inference#12151 landed with a never-called collectSSE helper in stream-passthrough-usage-estimation.test.ts; its three unused identifiers (collectSSE / stream / writable) fail 'npm run lint:json -- --max-warnings 0', so the 'No new ESLint warnings' job reds every PR merge-ref cut from the current tip (first seen on PR cheaper-inference#11564's post-sync run). createSSEStream and parseSSEUsage are actually used and stay; only the dead closure goes. File suite still passes 6/6. (cherry picked from commit 3b1520b) Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
diegosouzapw
pushed a commit
that referenced
this pull request
Sep 10, 2026
…ream stays silent (#12828) Validado numa worktree combinada com a onda de streaming desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-api-typecheck OK (289), check-file-size OK após rebaseline, 88/88 nos testes focados. O #12151 cobriu só metade: passthrough emitia o chunk final de usage, translate calculava a estimativa **depois** de fechar o stream, então o número só chegava ao log do servidor e nunca ao cliente. Fechar essa metade é o que faz a feature existir de fato. Não emitir segundo chunk quando o upstream já mandou usage real é o detalhe que impede a correção de virar contagem dobrada.
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
cheaper-inference#12151) A passthrough stream could end with no usage even though the client asked for it via stream_options: {include_usage: true}, so providers that do not meter always showed 0 tokens. The fix estimates usage at the finish marker when the upstream stays silent (flagged estimated: true) and drops any duplicate trailing usage chunk so the client never sees two. open-sse/utils/stream.ts:1982,1749 · open-sse/utils/usageTracking.ts:651,664 Six cases: the predicate (finish without usage but with content, trailing valid, empty response, tool-only) plus two SSE harness cases through createSSEStream passthrough. Note on base: this branch forked 442 commits back and carried a base-red marker for cheaper-inference#12109, which is now closed — the release tip has no open base-red issue. It merged cleanly against the current tip regardless. Verified in a combined batch worktree: 174/174 focused tests across all 11 PRs of this batch (this PR's stream-passthrough-usage-estimation suite included), typecheck:core clean, check:cycles and check:docs-counts green. Thanks @maxmad64bis.
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…e regression + radarPage i18n keys (cheaper-inference#12327) * fix(sse): forward the upstream's real trailing usage in passthrough; estimate only at flush cheaper-inference#12151 injected estimated usage into the finish chunk and dropped the real trailing usage block that genuine OpenAI upstreams send afterwards — metered clients got estimates instead of real token counts. The estimate now leaves via a canonical usage-only chunk at flush, only when the upstream stayed silent; a real trailing block is forwarded verbatim and wins. The tool_calls finish_reason normalization now materializes its own rewrite (it piggybacked on the removed finish-time rewrite), and the dead collectSSE helper goes with it (subsumes cheaper-inference#12324). * fix(i18n): seed the radarPage limits/training keys the cheaper-inference#12320 UI already consumes RadarCatalogTable.tsx references radarPage.colLimits / trainsOnPrompts / trainsOnPromptsHelp but cheaper-inference#12320 never added them to en.json, so the EN fallback could not resolve the __MISSING__ markers across 42 locales. Real translations for pt-BR and vi; the rest resolve via the EN fallback. * fix(sse): carry the chat stream id into flush-time synthetic chunks The estimated usage-only chunk emitted at flush used passthroughResponsesId, which is only ever set on the Responses path — on the chat path the synthetic chunk shipped id: null, breaking the string-id invariant pinned by stream-numeric-ids.test.ts. Track the upstream chat-completion id in the passthrough loop and reuse it (falling back to the Responses id, then a generated one). Sibling sweep: 74 files importing utils/stream — 603/603.
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…r-inference#11783) * feat(ui): enable React Compiler (cheaper-inference#67) Enable reactCompiler: true in next.config.mjs (Next 16 + React 19.2.8). This automates memoization at build time, removing manual useCallback/useMemo debt (591 + 283 instances respectively) and preventing stale-closure bugs. Test results (pre-existing failures unchanged): vitest UI: 282/295 files pass (13 fail = missing router/ReactFlow mocks) vitest: 1805/1857 tests pass (52 fail = same pre-existing mock issues) node:test: api/services/db all pass (except platform-specific serviceSupervisorSpawnError — Windows spawn("ls") issue) No new failures introduced by the compiler transform. Optional cleanup: remove now-redundant useCallback/useMemo in hot components. * fix(build): add babel-plugin-react-compiler peer dependency (cheaper-inference#67) React Compiler (reactCompiler: true in next.config.mjs) requires babel-plugin-react-compiler as an explicit peer dependency — Next.js declares it as optional ("*") and does not auto-install it. Installed babel-plugin-react-compiler@1.0.0 as a devDependency. Resolves correctly from both the project root and the next package context (Turbopack resolution path). * fix(ci): allowlist babel-plugin-react-compiler for React Compiler The React Compiler peer is a real npm package (facebook/react, MIT) required by Next 16 `reactCompiler: true`. Adding it to the anti-slopsquat allowlist unblocks check:deps and the 6A.8 unit-test gate. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(ci): drop unused collectSSE helper that trips ESLint The helper was leftover from cheaper-inference#12151 and fails the absolute lint:json --max-warnings 0 gate on every PR that includes it. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: WebPerson <jonlwheat2-gif@users.noreply.github.com>
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…ream stays silent (cheaper-inference#12828) Validado numa worktree combinada com a onda de streaming desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-api-typecheck OK (289), check-file-size OK após rebaseline, 88/88 nos testes focados. O cheaper-inference#12151 cobriu só metade: passthrough emitia o chunk final de usage, translate calculava a estimativa **depois** de fechar o stream, então o número só chegava ao log do servidor e nunca ao cliente. Fechar essa metade é o que faz a feature existir de fato. Não emitir segundo chunk quando o upstream já mandou usage real é o detalhe que impede a correção de virar contagem dobrada.
This was referenced Oct 21, 2026
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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
Streams in passthrough mode could end with no usage even though the client asked for it, so some providers always showed
0 tokens. The fix estimates usage at the finish marker when the upstream stays silent, and drops any duplicate trailing usage chunk.Scenario: client sends
stream:truewithstream_options: {include_usage:true}, upstream closes withfinish_reason: stopbut nousage, and the proxy still returns usage to the client (flaggedestimated:true). If the upstream later sends a trailingchoices:[] usage:{...}it's ignored so the client doesn't see two usages.open-sse/utils/stream.ts:1982,1749·open-sse/utils/usageTracking.ts:651,664Related Issues
Related to silent passthrough usage on providers that don't meter.
Validation
npm run lint(exit 0;npm install es-abstract@1.23.9 --no-save --no-package-lockin worktree only — lockfile still pins 1.24.1, base reproduces the same load error)origin/release/v3.8.51at2acbfc6)Tests Added Or Updated
tests/unit/stream-passthrough-usage-estimation.test.ts— 6 cases: predicate (finish without usage but with content, trailing valid, empty response, tool-only) + 2 SSE harness cases viacreateSSEStreampassthrough (finish without usage emitsestimated:true, trailing valid after estimated is dropped).Coverage Notes
Touches
open-sse/utils/stream.ts,open-sse/utils/usageTracking.ts,open-sse/handlers/chatCore/clientUsageBuffer.tsthrough the new passthrough estimation branch and the sharedisEmptyUsage/hasValidUsagehelpers. Covered by the new test above and existingtests/unit/chatcore-client-usage-buffer.test.ts(10 cases).hasValidUsagenow handles GeminitotalTokenCountso a single-field Gemini usage isn't misclassified.Reviewer Notes
open-sse/utils/stream.ts:1749,1984uses a stream-scopedpassthroughForwardedUsageguard (per-lineinjectedUsageresets each line, so it can't prevent a trailingchoices:[] usagefrom forwarding a second time). If a finish without usage is already estimated, a valid trailing usage is dropped in favor of the estimate — wins formuse-spark(never sends trailing), pragmatic for OpenAI routes that do. Long-term alternative is deferring the finish, similar toopen-sse/translator/response/openai-to-claude.ts.tool_callsonly (no text) keepstotalContentLengthat 0, so no estimate — same as before, callers can revisit if needed.