Skip to content

Stream Anthropic /messages responses in E2E fake handlers#1868

Merged
stephentoub merged 2 commits into
mainfrom
stephentoub-glowing-guide
Jul 1, 2026
Merged

Stream Anthropic /messages responses in E2E fake handlers#1868
stephentoub merged 2 commits into
mainfrom
stephentoub-glowing-guide

Conversation

@stephentoub

Copy link
Copy Markdown
Collaborator

Why

The C# SDK leg in copilot-agent-runtime CI started failing with:

Failed to finalize Anthropic message stream: stream ended without producing a Message with role=assistant

The runtime recently ported Anthropic model orchestration to a native Rust transport. That transport sends /messages requests with stream: true and unconditionally drains any 2xx response as an SSE stream, then finalizes it to a Message. Our E2E fake handlers returned buffered application/json for /messages, which has no SSE events, so the newer CLI raised MissingFinalMessage.

This is a test-fidelity gap, not a production bug: real Anthropic returns text/event-stream for streaming requests. The SDK's own CI still passes because it runs against pinned CLI 1.0.67 (before the transport change), while the runtime leg builds the CLI from main.

What

Update all six SDK E2E fake request handlers so the /messages endpoint returns a proper Anthropic Messages SSE sequence (message_start -> content_block_start -> content_block_delta -> content_block_stop -> message_delta -> message_stop) when the request body requests streaming, while keeping the buffered JSON response for non-streaming requests.

Handlers updated:

  • dotnet/test/E2E/CopilotRequestE2EProvider.cs
  • nodejs/test/e2e/session_config.e2e.test.ts
  • python/e2e/_copilot_request_helpers.py
  • go/internal/e2e/copilot_request_helpers_test.go
  • rust/tests/e2e/session_config.rs
  • java/src/test/java/com/github/copilot/CopilotRequestTestSupport.java

The change is backward compatible: with the pinned CLI (which does not stream Anthropic), the new branch is inert; with the newer CLI it satisfies the SSE expectation. Only C# was failing today, but the other five share the same stub and would break the moment the pinned CLI is bumped past the transport change, so all six are fixed together.

Verification

  • C#: dotnet build passes
  • Node: tsc --noEmit clean
  • Python: py_compile clean
  • Java: test-compile BUILD SUCCESS, plus spotless:apply
  • Go and Rust could not be compiled in this environment (no go toolchain; no clang for Rust's ring build). Both edits were reviewed by hand against the existing streaming patterns in their files.

Generated by Copilot

The runtime's native Anthropic transport sends /messages requests with
stream:true and drains any 2xx response as an SSE stream, then finalizes
it to a Message. The E2E fake handlers returned buffered application/json
for /messages, so the newer CLI raised 'stream ended without producing a
Message with role=assistant' (MissingFinalMessage).

Update all six SDK E2E fake handlers to return a proper Anthropic Messages
SSE sequence (message_start through message_stop) when the request body
requests streaming, keeping buffered JSON for non-streaming requests. This
fixes the C# leg in copilot-agent-runtime CI and prevents the equivalent
break in the other five SDKs when the pinned CLI is bumped.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 1, 2026 15:03
@stephentoub stephentoub requested a review from a team as a code owner July 1, 2026 15:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the SDK E2E “fake upstream” request handlers across all language legs to correctly emulate Anthropic /messages streaming by returning a text/event-stream (SSE) sequence when the request body sets stream: true. This aligns the test stubs with the newer CLI/runtime behavior that always drains successful /messages responses as an SSE stream.

Changes:

  • Add an Anthropic Messages SSE “message_start … message_stop” response path for streaming /messages requests.
  • Preserve the existing buffered application/json response for non-streaming /messages requests.
  • Thread request bodies into inference response builders where needed (e.g., Rust/Node/Java) to detect streaming requests.
Show a summary per file
File Description
rust/tests/e2e/session_config.rs Adds SSE response helpers and returns Anthropic /messages SSE when the request asks for streaming.
python/e2e/_copilot_request_helpers.py Returns Anthropic /messages SSE body for streaming requests; keeps JSON for non-streaming.
nodejs/test/e2e/session_config.e2e.test.ts Adds SSE builder + Anthropic stream body, switching based on "stream": true in the request body.
java/src/test/java/com/github/copilot/CopilotRequestTestSupport.java Introduces anthropicMessageSseBody and returns SSE for streaming /messages requests.
go/internal/e2e/copilot_request_helpers_test.go Adds buildAnthropicMessageSSEBody and uses it for streaming /messages responses.
dotnet/test/E2E/CopilotRequestE2EProvider.cs Switches /messages handler to return Anthropic SSE sequence when stream: true is detected.

Review details

  • Files reviewed: 6/6 changed files
  • Comments generated: 0
  • Review effort level: Low

@github-actions

This comment has been minimized.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Cross-SDK Consistency Review ✅

This PR does an excellent job of maintaining consistency across all six SDK implementations. A quick audit:

Verified consistent across all 6 SDKs (Node.js, Python, Go, .NET, Java, Rust)

Check Result
All 6 SDKs updated in the same PR
Identical SSE event sequence (message_startcontent_block_startcontent_block_deltacontent_block_stopmessage_deltamessage_stop)
Identical JSON payloads for each event
Same content-type header (text/event-stream)
Same SSE wire format (event: {name}\ndata: {json}\n\n)
Same synthetic response text ("OK from the synthetic stream.")
Stream detection keyed on "stream": true in request body

Minor style note (no action required)

Five SDKs extract the synthetic text into a named constant (SYNTHETIC_TEXT, syntheticResponseText, SyntheticText, etc.). The Node.js test uses the same string inline as a literal. Functionally identical — just a style nit if you want to harmonize.

No cross-SDK consistency gaps introduced by this PR.

Generated by SDK Consistency Review Agent for issue #1868 · sonnet46 1.1M ·

@stephentoub stephentoub merged commit 0ce76f0 into main Jul 1, 2026
41 checks passed
@stephentoub stephentoub deleted the stephentoub-glowing-guide branch July 1, 2026 16:17
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.

2 participants