fix(anthropic): accept done stream terminator#136
Conversation
Signed-off-by: Bhuvan Agrawal <11240550+bhuvan002@users.noreply.github.com>
WalkthroughAnthropic native SSE streaming now treats ChangesAnthropic SSE termination
Estimated code review effort: 2 (Simple) | ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
crates/switchyard-components/tests/adversarial_native_backends.rs (1)
1043-1077: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winCover the unterminated
[DONE]frame.This test exercises only the normal SSE-frame path because
[DONE]is followed by\n\n. Add a case where the stream ends withdata: [DONE]without a final separator, so the buffered-frame handling atanthropic.rsLine 539 is regression-tested.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/switchyard-components/tests/adversarial_native_backends.rs` around lines 1043 - 1077, The test `anthropic_streaming_stops_at_done_marker` currently covers only a `[DONE]` frame terminated by a blank-line separator. Add a separate streaming test case whose SSE payload ends exactly with `data: [DONE]` without a trailing newline or separator, then collect the response and assert it emits only the preceding `message_start` event while exercising the buffered-frame handling in the Anthropic backend.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@crates/switchyard-components/tests/adversarial_native_backends.rs`:
- Around line 1043-1077: The test `anthropic_streaming_stops_at_done_marker`
currently covers only a `[DONE]` frame terminated by a blank-line separator. Add
a separate streaming test case whose SSE payload ends exactly with `data:
[DONE]` without a trailing newline or separator, then collect the response and
assert it emits only the preceding `message_start` event while exercising the
buffered-frame handling in the Anthropic backend.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: cd36e2e8-2a0b-40b0-a899-473640bd05e7
📒 Files selected for processing (2)
crates/switchyard-components/src/backends/anthropic.rscrates/switchyard-components/tests/adversarial_native_backends.rs
What
Teach the native Anthropic streaming backend to recognize the OpenAI-style
[DONE]SSE sentinel and stop the stream when it appears.Add a regression test that verifies events after the sentinel are not emitted.
Existing coverage still verifies normal Anthropic streams that end without
[DONE].Why
OpenRouter's Anthropic-compatible endpoint can append
data: [DONE]afterotherwise valid Anthropic SSE events. Switchyard previously tried to parse that
sentinel as JSON, turning a successful response into a mid-stream parse error.
Reproduction
With Switchyard running on port 4001 and
openrouter-opusconfigured as anAnthropic-format route to
anthropic/claude-opus-4.8on OpenRouter:Before this fix, the response contains the normal Anthropic events through
message_stop, then ends with:OpenRouter has appended
data: [DONE]; Switchyard tries to decode it as JSONand emits the error above. After this fix,
[DONE]terminates the streamcleanly and no error event is emitted.
How tested
uv run ruff check .cleanuv run mypy switchyardcleanuv run pytest tests/ -v -m 'not integration'— 1949 passed, 12 skippedcargo fmt --all --checkcleancargo clippy --workspace --all-targets -- -D warningscleancargo test --workspacegreen/api/v1/messagesChecklist
snake_caseof the primary class. (N/A: no classes added.)switchyard/__init__.py.__all__if intended for downstream use. (N/A: no public symbols added.)--helpupdated if customer-facing surface changed. (N/A: compatibility fix only.)Signed-off-by: Bhuvan Agrawal <11240550+bhuvan002@users.noreply.github.com>) per the DCO.Notes for reviewers
The full live E2E suite reached 17 passing tests, then hit its five-error limit
because the shared fixture on current
mainstill invokes the removedswitchyard passthroughcommand. That harness issue is unrelated to thistwo-file parser change; 19 live tests were therefore not run.
Summary by CodeRabbit
Bug Fixes
[DONE]marker.Tests
[DONE]are ignored.