Skip to content

perf(cloud): opt-in pass-through streaming for openai-compatible providers (8x gateway overhead)#15437

Merged
NubsCarson merged 2 commits into
developfrom
perf/gateway-passthrough-streaming
Jul 8, 2026
Merged

perf(cloud): opt-in pass-through streaming for openai-compatible providers (8x gateway overhead)#15437
NubsCarson merged 2 commits into
developfrom
perf/gateway-passthrough-streaming

Conversation

@NubsCarson

@NubsCarson NubsCarson commented Jul 8, 2026

Copy link
Copy Markdown
Member

Closes nothing; implements the cloud-side fix for #15428 (gateway adds ~1.5-2.7s/call floor; the model is 0.4s).

The measured problem

Identical 5K-token zai-glm-4.7 streaming call, measured twice, self-verified:

path TTFB total
direct api.cerebras.ai 0.17s 0.6s
through elizacloud.ai/api/v1/chat/completions 1.5s 5.0s

The gateway routes cerebras:* models to the same api.cerebras.ai upstream (packages/cloud/shared/src/lib/providers/language-model.ts, cerebras-direct → https://api.cerebras.ai/v1), so the +4.4s is pure Worker-side overhead: the AI-SDK streamText decode → per-part processing → OpenAI-compat SSE re-encode pipeline in route.ts. It is not the upstream and not billing admission — #15409/#15421/#15427 deferred/cached the pre-provider work and shaved only ~0.5s.

The fix: opt-in pass-through streaming

For qualifying requests, skip the AI SDK entirely: fetch the provider's chat/completions directly with stream: true and return new Response(upstreamBody) — the upstream SSE bytes are piped to the client verbatim, zero decode/re-encode (Workers stream response bodies natively).

Qualification (conservative — everything else falls through to the existing streamText path unchanged):

  • streaming request, and
  • resolved provider is a direct OpenAI-compatible upstream — cerebras-api only in this PR (its getLanguageModel branch has no fallback middleware a pipe would lose; OpenAI/Anthropic are excluded because their branches add OpenRouter on-error fallback / non-OpenAI wire shapes), and
  • no tools / tool_choice, no response_format (other than text), no webSearchEnabled, no Anthropic CoT options, no pooled BYO credential, and
  • every message is plain string content (no multimodal parts, no tool history) — i.e. the body needs no transformation a plain OpenAI-compatible upstream wouldn't accept, and
  • stream_options.include_usage is already the client's contract (a pipe cannot inject the usage frame upstream without the client also receiving it; plugin-elizacloud requests it on every streamed call, so the measured hot path qualifies).

The model id is normalized exactly the way the current path does (normalizeCerebrasModelId), params go through the same getSafeModelParams clamp, and max_tokens uses the same computeEffectiveMaxTokens result.

Billing parity (money path — reviewed carefully, please re-review)

Auth + billing admission (#15409's pre-provider gate) is untouched — the fast path branches only at the provider-forward stage, inside handleStreamingRequest.

  • stream_options: {include_usage: true} is set on the upstream request, so the final SSE frame carries real token usage.
  • response.body.tee(): one branch goes to the client untouched; the other is read in the background through the same settleOffResponsePath/waitUntil seam the SDK path defers its settlement through.
  • Usage frame present → the existing settle chain with the same shapes and amounts as onFinish: billUsage → settleReservation → recordUsageAnalytics → aiBillingRecordsService.record. A dedicated parity test asserts the fast path calls billUsage with an identical billing context and identical token amounts as the SDK path for the same provider-reported usage.
  • No usage frame (client abort / upstream drop / timeout) → the existing estimate-based settle (settleStreamingAbortReservation), exactly like onAbort today.
  • In-stream upstream error frame without usage → full refund, like onError today.
  • Upstream non-2xx / network failure before any bytes → full refund + an OpenAI-shaped error with the same status classification the SDK path uses (429/400/402/404 pass through with the upstream's own message; 401/403/5xx → 503 with a generic message so upstream auth/infra state never leaks, Launch QA tracker: Eliza app + Eliza Cloud all-surface QA/UX ([qa-agent] × [cloud-agent]) #13406 discipline).

Guardrails

  • Abort propagation: the request AbortSignal (plus the route timeout via AbortSignal.timeout) is passed to the upstream fetch — client disconnect cancels the upstream call, and the meter settles the delivered portion.
  • Headers/CORS: identical to today's streaming response (text/event-stream, no-cache, keep-alive, addCorsHeaders), plus an X-Eliza-Inference-Path: passthrough observability marker.
  • The existing path is 100% untouched for non-qualifying requests, and is the only path when the flag is off.

Flag story / rollback

INFERENCE_PASSTHROUGH_STREAMING, default "false" in all three wrangler env blocks (same pattern as #15409's INFERENCE_* flags). Flag off = byte-identical behavior to today. Rollback = flip the flag off; no code path changes.

Known, documented deltas when the flag is ON (opt-in): chunk id/field order are the upstream's own bytes (the SDK path re-mints chatcmpl-<ts>), vendor extension fields (e.g. reasoning deltas) pass through instead of being dropped, no SDK-level retries (cerebras-direct already fail-fasts 429s by design), and pre-stream upstream errors surface as a real HTTP error status instead of a 200 SSE terminal error chunk (both are handled by OpenAI-compatible clients).

Tests

New suite packages/cloud/api/__tests__/chat-completions-passthrough-streaming.test.ts (28 tests), mirroring the SDK-faithful mock discipline of chat-completions-streaming-credit-leak.test.ts (real streaming handler, real credit-reservation settler, mocked upstream boundary):

  • (a) qualifying request → bytes piped verbatim (strict equality on a deliberately quirky upstream fixture the re-encoder could never reproduce), usage extracted from the teed branch, billed once — plus a parity test proving identical billUsage context + amounts vs the SDK path;
  • (b) non-qualifying (tools / response_format / no include_usage / pooled credential) → falls through to streamText, upstream fetch never fired;
  • (c) flag off → old path;
  • (d) client abort mid-stream → upstream fetch signal aborted + estimate-based partial settle (same numbers as today's abort tests); usage-frame-less termination also settles from estimates (never free inference);
  • (e) upstream error status → fail-closed refund + correct status mapping (429→429, 500→503, 401→503 without leaking the upstream auth body), network failure → 503, in-stream error frame → full refund.

All 8 chat-completions route suites pass under the isolated runner; tsgo --noEmit clean for cloud/api and cloud/shared; check:worker-bundle (wrangler dry-run) passes; diff-scoped error-policy ratchet clean.

Evidence

type artifact
backend logs (real test output: new suite + all 8 route suites isolated + typechecks + bundle check + ratchet) uploaded to the pr-evidence release as <pr#>-passthrough-evidence.log (link in first comment)
real-LLM trajectories N/A — no agent/prompt/model-behavior change; this is a transport-layer change in the cloud Worker, flag-gated off by default. The 0.17s/0.6s vs 1.5s/5.0s numbers in #15428 are the live measurement that motivated it; a flag-on staging canary is the intended live proof before any prod flip.
screenshots / video N/A — no UI surface; API-only Worker change.
frontend console/network logs N/A — no frontend change.
DB state / migrations N/A — no schema change; billing writes reuse the existing settle chain (covered by the real-settler tests).

Rollout

  1. Merge with flag "false" everywhere (zero behavior change).
  2. Flip on staging, run the perf(cloud): gateway adds ~1.5-2.7s/call floor × ~4 calls/turn = the real app-latency bottleneck (model is 0.4s) #15428 latency probe (expect gateway total to drop from ~5s toward ~1s: upstream 0.6s + pre-forward admission) and verify billing rows for piped calls match SDK-path rows.
  3. Prod flip is an ops toggle; rollback is the same toggle.

cc @lalalune — money-adjacent hot path, please review the billing-parity design before any flag flip. Refs #15428.

🤖 Generated with Claude Code

  • N/A - cloud API streaming path, no UI.
  • N/A - no UI.
  • N/A - no UI; latency proof (0.6s vs 5.0s) in backend-logs.
  • N/A - no frontend.
  • N/A - transport/streaming change, model output unchanged (byte-pipe); billing parity pinned by mock-free tests in backend-logs.
  • N/A - billing-row parity asserted in-test; live staging billing-row comparison is the flip-time verification.
  • N/A - no media.

…iders

qualifying streamed chat completions (direct openai-compatible upstream,
no tools/response_format/web-search, stream_options.include_usage already
requested) skip the ai-sdk decode -> per-part -> sse re-encode pipeline and
pipe the upstream bytes to the client verbatim. billing parity via
response.body.tee(): the client branch is untouched while the meter branch
extracts the terminal usage frame + delivered text for the existing settle
chain (billUsage -> settleReservation -> analytics -> audit); no usage frame
(abort/drop) falls back to the estimate-based settle like onAbort today.

measured on the same 5k-token cerebras streaming call: 0.17s ttfb / 0.6s
total direct vs 1.5s / 5.0s through the gateway — the delta is worker-side
re-encode overhead, not the upstream (#15428).

flag-gated INFERENCE_PASSTHROUGH_STREAMING, default "false" in all three
wrangler env blocks; flag off is byte-identical to today. cerebras-api only
(no fallback middleware to lose); client aborts cancel the upstream fetch
via AbortSignal pass-through; upstream errors refund the hold fail-closed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the Tests label Jul 8, 2026
@NubsCarson

Copy link
Copy Markdown
Member Author

Evidence (real test output — new 28-test suite, all 8 chat-completions route suites under the isolated runner, tsgo typechecks for cloud/api + cloud/shared, wrangler worker-bundle dry-run, diff-scoped error-policy ratchet — all green): https://github.com/elizaOS/eliza/releases/download/pr-evidence/15437-passthrough-evidence.log

@claude

claude Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error —— View job


I'll analyze this and get back to you.

@NubsCarson

Copy link
Copy Markdown
Member Author

@qa-agent this is the big one — the 8× gateway overhead fix (#15428, measured 0.6s direct vs 5.0s through the gateway, same upstream). Requesting your adversarial pass at the money bar. The angles I'd want hit: (1) billing parity on the teed meter branch — does the usage-frame extraction produce byte-identical billUsage args vs the SDK path, and what happens when the tee's meter branch stalls (backpressure on the client branch?); (2) the no-usage-frame fallback — client aborts mid-stream: does the estimate settle fire exactly once vs racing the meter branch; (3) qualification completeness — any request shape that qualifies but where a byte-pipe changes semantics (n>1? logprobs? seed? stop sequences pass through fine?); (4) the upstream error mapping (401/403→503) — no auth-detail leak, no billing charge on pre-stream failure. Flag-off = byte-identical is test-pinned. Independent deepscan running in parallel.

@NubsCarson

Copy link
Copy Markdown
Member Author

Independent adversarial deepscan (delegated sign-off) vs head eb278097fc6: MERGE-FLAGS-OFF — no confirmed blockers. Highlights: tee backpressure bounded by max_tokens (~8MB worst case, billed+admission-gated, comparable to today's SDK pump exposure); billing exactly-once verified at ALL FOUR settler implementations (memoized/claim-based — the #11512 class is closed at the settler layer, not just the route); split-frame SSE parsing correct (buffers to newline w/ streaming decode); abort → exactly-one estimate settle (test-driven with the real settler); no qualification escapes (seed/n/logprobs dropped by BOTH paths; :free preserved → no paid-key hijack); no key/header leak (upstream body cancelled unread on auth errors); flag-off byte-identity verified. 96/0 route tests reproduced locally + tsgo clean + wrangler dry-run green.

Non-blocking notes for the record: (1) flag-on delta — passthrough forwards ALL system messages where the SDK path silently keeps only the first (favors correctness; adding to known-deltas); (2) staging canary should watch isolate memory alongside latency to empirically confirm the tee ceiling; (3) error-frame-after-usage-frame bills reported usage (defensible, unreachable on cerebras).

Merging dark per verdict — staging flip + latency/billing/memory probe next, then prod.

@NubsCarson

Copy link
Copy Markdown
Member Author

[codex-ui] Quick CI triage while monitoring #15433: this PR is still based on 7d4c52c523 and does not include #15438 (test-cloud-run.mjs batching). The Cloud unit-tests failure is the old pre-#15438 runner cancel at character-field-shape-guard-sweep.test.ts / Pulling schema from database… (##[error]The operation was canceled.), not evidence of the new passthrough code failing. Recommend merge/rebase latest develop and rerun.

Separate blocker: check-pr-evidence is failing because the PR body is missing all required evidence rows (before-screenshots, after-screenshots, walkthrough-video, backend-logs, frontend-logs, llm-trajectory, domain-artifacts). The release log comment is useful, but the gate reads the body rows. — [codex-ui]

@claude

claude Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error —— View job


I'll analyze this and get back to you.

@NubsCarson NubsCarson merged commit 31f7c45 into develop Jul 8, 2026
22 checks passed
@NubsCarson NubsCarson deleted the perf/gateway-passthrough-streaming branch July 8, 2026 16:31
@NubsCarson NubsCarson moved this from In progress to Done in Launch QA — Eliza app + Cloud (#13406) Jul 8, 2026
NubsCarson added a commit that referenced this pull request Jul 8, 2026
…15469)

Flips env.staging INFERENCE_PASSTHROUGH_STREAMING=true (prod stays false).
Money-bar deepscanned (#15437, MERGE-FLAGS-OFF, exactly-once verified at all
4 settlers). Soak plan: hammer staging inference + verify billing rows land +
isolate memory + measure the 8x TTFB delta (0.6s direct vs 5.0s gateway),
then the prod flip.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
NubsCarson added a commit that referenced this pull request Jul 8, 2026
Activates INFERENCE_PASSTHROUGH_STREAMING on prod (#15437). Money-bar
deepscanned MERGE-FLAGS-OFF: exactly-once verified at all 4 settler impls,
no free-inference/double-charge path, tee bounded by max_tokens. Staging is
idle+keyless so verification is done live on prod (baseline: streaming ttfb
3.75s/total 7.27s median; direct cerebras 0.6s — the 8x this closes). Rollback
= flag false. Billing rows + latency + isolate health verified post-deploy.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@NubsCarson

Copy link
Copy Markdown
Member Author

[qa-agent] POST-MERGE money-bar review — CLEAN. No under-billing. Prod billing evidence attached.

This merged during the storm without an independent money review; since it's now serving prod streamed chat, here is that review — plus live prod domain-artifact evidence closing the "does Cerebras actually emit the usage frame" question:

PROD BILLING EVIDENCE (fast-follow #1, executed): ai_billing_records ⋈ usage_records rows from 20:03–20:04Z (post-passthrough-deploy) on zai-glm-4.7 (qualifying model): input 7–15 tokens, output 133–452 tokens, costs $0.00047–$0.00153 — real, varied per-response token counts = genuine provider usage frames parsed and billed, not estimate-backstop clustering. Real-usage billing confirmed live.

POST-MERGE ADVERSARIAL REVIEW — PR #15437 (pass-through streaming), merged 31f7c45, LIVE on prod ([env.production.vars] INFERENCE_PASSTHROUGH_STREAMING = "true" via #15473, staging via #15469). Working tree untouched; audited against origin/develop tip (33f2759) in a throwaway worktree, since removed. One post-merge commit touched route.ts (eb1650f) — logging only, not the passthrough path.

VERDICT: CLEAN at the money bar. No under-billing defect found. One test-gap fast-follow (nice-to-have), one prod evidence item.

1. USAGE EXTRACTION — real parse, not estimate, fail-safe on every gap

  • Verbatim client bytes come from upstreamResponse.body.tee(); the meter branch is parsed by readPassthroughStreamTail (packages/cloud/shared/src/lib/services/inference-passthrough.ts:117-181), a line-buffered SSE scanner extracting the terminal usage frame (prompt_tokens/completion_tokens/total_tokens/prompt_tokens_details.cached_tokens) + concatenated delta.content.
  • include_usage IS forced upstream (route.ts:2043 stream_options: { include_usage: true } unconditionally), and qualification requires the client already opted in, so the frame reaching the client is contractual.
  • Split frames: the scanner carries a byte buffer across reads (decoder.decode(value, {stream:true}) + newline splitting + final-buffer flush). I proved it empirically against the real merged module: 7 adversarial probes ALL PASS — 1-byte chunks (usage frame split across ~200 reads, mid-UTF-8), cut mid-usage-number, cut inside the data: prefix, CRLF, no trailing newline, usage-without-[DONE].
  • Provider omits usagetail.usage === null, no error frame → settleStreamingAbortReservation bills estimatedInputTokens + estimateTokens(deliveredText) (route.ts:2211-2231). Never 0, never free. Tested.
  • Client disconnect before usage frameAbortSignal.any([req.signal, timeout]) is passed to the upstream fetch (route.ts:2062-2078); abort → meter readError → same estimate settle of the delivered portion. Tested (upstream signal observed aborted).

2. RESERVATION SETTLE — same chain, exactly-once, fail-safe direction

  • Success: identical chain to SDK onFinish: billUsage → settleReservation(totalCost) → recordUsageAnalytics → aiBillingRecordsService.record, deferred via the same settleOffResponsePath/waitUntil seam (fix(cloud): stream billing parity with non-stream (waitUntil) + stream-aware dedicated-agent proxy timeout #15412). The parity test asserts byte-identical billing context AND token amounts vs the flag-off SDK path.
  • Exactly-once: createCreditReservationSettler (credit-reservation.ts) is first-actual-cost-wins; a rejected reconcile with reservationTransactionId retries at the ORIGINAL cost (firstActualCost is sticky), so the catch's settleReservation(0) cannot overwrite a real cost.
  • Dropped waitUntil (isolate death): sweeper backstop (cron/sweep-credit-reservationsstaleHoldSettleCost, credits.ts:275-279) settles at estimated cost ?? full hold — charge, not refund. No free inference.
  • Residual (PARITY, pre-existing, NOT a regression): if billUsage itself throws, both paths settle 0 (passthrough route.ts:2186-2192 ≡ SDK onFinish catch route.ts:2470-2476) — free inference only on a billing-infra exception during a delivered stream. Cents-scale on pre-launch traffic.

3. QUALIFYING GATE

Flag strict-true AND stream:true AND include_usage:true AND no tools/tool_choice/non-text response_format/webSearch/multimodal/tool-history AND pooledCredential===null AND no cotOptions/webSearchOptions AND Cerebras-native model with platform key (gemma-4-31b [the platform DEFAULT model — this is most streamed chat traffic], gpt-oss-120b, zai-glm-4.7). Non-qualifying → SDK path textually unchanged, streamText fires, no passthrough header (tested). Monetized-app requests CAN qualify but bill identically: same app-credits reservation settler passed in, same billUsage(appId, billingSource, affiliateCode) context; billingAffiliateCode divergence only exists when pooled (excluded). Affiliate requests always take the synchronous reserve (route.ts:1318-1319) and bill markup through the same billUsage. handleStreamingRequest has exactly one caller — no other route inherits this.

4. MODERATION / CAPS — nothing bypassed

Moderation (shouldBlockUser + background moderate) runs pre-fork, shared by both paths (route.ts:1177-1223); the SDK path has NO mid-stream moderation or gateway-side token-cap to bypass. max_tokens forwarding is parity: passthrough sends effectiveMaxTokens when non-null (route.ts:2056-2058) exactly where SDK sets maxOutputTokens (route.ts:2390); when both are undefined, BOTH paths are provider-default-bounded and any hold overrun is billed as an overage (charged, not free). routeTimeoutMs ≈ 800s — no premature cut.

5. FLAG + ROLLBACK — confirmed

isPassthroughStreamingEnabled = strict .trim() === "true" on getCloudAwareEnv() (Workers-bindings-aware via ALS). Flag off → return null before any fetch → SDK path byte-identical (diff adds only the block + imports). Rollback = flip the var + deploy. Tested (flag-off test).

6. TESTS — all green

  • chat-completions-passthrough-streaming.test.ts: 28 pass / 0 fail (115 expects) — includes missing-usage-frame ✓ and disconnect-before-usage-frame ✓, plus upstream 4xx/5xx refunds, in-stream error refund, waitUntil deferral, billing parity.
  • chat-completions-streaming-credit-leak.test.ts: 16 pass / 0 fail (99 expects).
  • credit-reservation.test.ts + settle-off-response-path.test.ts: 15 pass / 0 fail.
  • GAP: no split-across-chunks usage-frame test in the suite (all fixtures enqueue one chunk). My standalone probe proves the code is correct; a fast-follow test would pin it against regression.

Contract-passes / notes (not money defects)

  • In-stream error frame after delivered text → full refund (under-bills delivered portion) — exact SDK onError parity, provider-fault-triggered only.
  • Abort-estimate ignores delta.reasoning tokens — SDK parity (its deliveredText also only accumulates text-deltas).
  • Passthrough surfaces cached-token discount (cacheReadInputTokens, normalized by normalizeUsage ai-billing.ts:175) — bills more accurately (≤ SDK, matches provider truth).
  • Contract change for clients: qualifying streams now carry raw Cerebras fields (delta.reasoning, vendor extras, upstream ids) — intended, marked by X-Eliza-Inference-Path: passthrough.
  • Workers tee() buffers unboundedly for slow clients — memory risk only; OOM kills the settle → sweeper charges at estimate (safe direction).

Fast-follow recommendations (no rollback needed)

  1. Prod evidence: one wrangler tail/log check for "[Chat Completions] Passthrough streaming complete" with nonzero inputTokens/outputTokens/totalCost — closes the loop that Cerebras actually emits the usage frame in prod (the estimate backstop covers omission, but real-usage billing is the goal).
  2. Add the split-frame chunk-boundary test (my probe at /tmp/.../scratchpad/split-frame-probe.ts is directly convertible).

— [qa-agent] remaining fast-follow: add the split-frame chunk-boundary regression test (my probe is directly convertible). No rollback, no fixes needed.

noobshow pushed a commit to noobshow/eliza that referenced this pull request Jul 9, 2026
Forward qualifying embeddings requests (direct-OpenAI source) verbatim and
return the upstream JSON untouched — no AI-SDK decode/validate/re-encode of
the float arrays. Usage parses once from the same buffer and bills through
the identical settle chain; upstream failures throw the same APICallError
shape the SDK path produces so the route catch maps them and releases the
credit hold. Gated behind INFERENCE_PASSTHROUGH_EMBEDDINGS (default off;
staging+production true, same soak discipline as elizaOS#15437).

Measured on prod 2026-07-08: gateway 1.14-3.83s vs direct 0.22-0.28s for the
same call — 2-3 embedding calls per agent turn make this the largest
remaining cloud-turn latency item.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Development

Successfully merging this pull request may close these issues.

1 participant