Skip to content

fix(sse): accept first-party Claude empty end_turn in combo quality gate - #15598

Merged
diegosouzapw merged 2 commits into
cheaper-inference:release/v3.8.52from
insoln:fix/combo-empty-end-turn
Oct 6, 2026
Merged

diegosouzapw merged 2 commits into
cheaper-inference:release/v3.8.52from
insoln:fix/combo-empty-end-turn

Conversation

@insoln

@insoln insoln commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Problem

A complete Claude SSE response can legitimately end without any content blocks: message_start → message_delta with stop_reason: end_turn or stop_sequence → message_stop. Combo quality validation currently treats every zero-content Claude lifecycle as an invalid empty response and triggers failover. For an official Anthropic response, this sends an otherwise valid empty turn to other targets.

The exception must not be keyed by provider ID alone. An anthropic or claude connection can point to a custom, Claude-compatible base URL, and an unprefixed pinned alias can parse without a provider.

Change

Introduce isTrustedEmptyTurn(), a fail-closed check of the connection that actually served the response:

  • read X-OmniRoute-Selected-Connection-Id, which is set server-side for the served connection;
  • for a known claude/anthropic target only, fall back to its planned connection ID if the header is missing;
  • for an unresolved alias, require the selected-connection header rather than trusting a planned ID;
  • require the stored connection to be claude or anthropic, to match a known request provider, and to use the official api.anthropic.com host; missing, malformed, or unverifiable records are rejected.

validateResponseQuality() accepts an empty turn only if that trust check passes, message_stop is present, no content_block_* event was opened, and stop_reason is end_turn or stop_sequence. Validated responses retain byte-for-byte SSE replay. content_filter, incomplete lifecycles, empty opened text blocks, zero-byte bodies, third-party gateways, and unknown connections continue to fail over.

The trust result is wired through priority, round-robin, runtime-unit, and pinned-model paths. Regressions cover selected and fallback connection IDs, custom hosts, provider mismatches, aliases, and the preserved failure cases.

Fixes #15586

⚠️ base-red inherited: #15306

A complete Claude SSE lifecycle with zero content blocks and stop_reason
end_turn/stop_sequence is a legitimate empty answer from Anthropic, but the
combo quality gate treated it as an empty stream and failed over, replaying
the request on other targets.

The exemption is granted only when the connection that actually served the
response is a first-party Anthropic/Claude connection: the selected
connection (X-OmniRoute-Selected-Connection-Id, or the planned connection
for a known provider) must exist, belong to claude/anthropic and use the
official api.anthropic.com host. Unresolved aliases require the selected
header. Anything uncertain fails closed. content_filter, incomplete
lifecycles, empty opened text blocks and zero-byte bodies still fail over.

Refs cheaper-inference#15586
@insoln
insoln requested a review from diegosouzapw as a code owner October 5, 2026 22:21
@diegosouzapw
diegosouzapw merged commit 09a1624 into cheaper-inference:release/v3.8.52 Oct 6, 2026
43 of 51 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.

feat(backend): trusted first-party Claude empty end_turn stream is rejected by quality check and forced into failover

2 participants