mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
* fix(stream): emit terminal SSE frames on mid-stream upstream failure (#7699) On /v1/messages (Anthropic format), when the upstream SSE stream fails mid-flight after bytes have been forwarded to the client, OmniRoute used to silently close the connection with no terminal event. Anthropic SDK and Claude Code report "Connection closed mid-response. The response above may be incomplete." Two fixes in open-sse/utils/streamHandler.ts: 1. buildStreamErrorChunks (Claude format) now emits event:message_stop after event:error — the Anthropic stream terminator that clients expect. Previously only event:error was sent, leaving the client hanging. 2. createDisconnectAwareStream pull() now detects upstream "done" without a client-visible terminal marker ([DONE] / response.completed / message_stop) and emits a synthetic terminal error frame instead of silently closing. This covers the case where the upstream drops the connection mid-stream without sending an error chunk. Adds tests/unit/silent-sse-close-7699.test.ts covering both code paths across Claude, OpenAI Chat, and OpenAI Responses formats. Refs: diegosouzapw/OmniRoute#7699 * fix(stream): scope terminal-marker missing detection to known formats with forwarded bytes Gate the done-path synthetic 502 error on bytesWereForwarded AND a known clientResponseFormat. Without this gate, any stream that closes cleanly without a terminal marker (including raw passthrough streams and non-API transforms) is incorrectly treated as a mid-stream drop. - Add bytesWereForwarded flag set on first Uint8Array chunk - Require clientResponseFormat to be set before injecting 502 - Fixes 3 broken stream-handler tests (pipes transformed bytes, slow upstream stall watchdog, normal completion watchdog) - Preserves #7699 fix: Claude-format streams that forwarded content but missed message_stop still get the synthetic terminal frame * fix(stream): scope terminal-marker heuristic to Claude only, add non-SSE regression #7699 is scoped to /v1/messages (Anthropic): Claude clients treat a stream that ends without message_stop as an error, and Anthropic's SSE spec explicitly permits a mid-stream event: error. The issue's own suggested fix says the current OpenAI silent-close "remains reasonable" — so the done-without-terminal-marker synthetic-502 heuristic must not fire for any other clientResponseFormat (gemini/codex/kiro/cursor/openai/openai-responses etc.), where a done stream with no [DONE]/response.completed/message_stop equivalent is not necessarily a silent drop. Narrows the gate from "any truthy clientResponseFormat" to "clientResponseFormat === FORMATS.CLAUDE" specifically. Adds a regression test asserting a plain non-SSE OpenAI-format completion (bytes forwarded, no terminal marker) is NOT mutated with a synthetic error frame. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * test(stream): trim new regression test to clear the 800-line test-file cap tests/unit/stream-handler.test.ts was 772 lines pre-#7816 (not in the frozen file-size baseline, so it's evaluated as new-file-cap 800). The added regression test pushed it to 807; trim boilerplate to land at 796. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>