Add line and Server-Sent Events readers for streaming responses#18
Merged
Conversation
Add two IAsyncEnumerable members on ISimpleStreamResponse (implemented in SimpleStreamResponse), so callers can mock them: - ReadLinesAsync: reads the body as text lines (CR/LF/CRLF), encoding from the Content-Type charset (UTF-8 default), cancellation observed mid-read. - ReadServerSentEventsAsync: parses the text/event-stream wire format per the WHATWG SSE spec (blank-line dispatch, multi-data join, comment/keep-alive skipping, event/id/retry fields, id carries forward), returning ServerSentEvent frames. Both are generic to the SSE standard - application conventions (OpenAI's [DONE] sentinel, deserializing the data payload) are deliberately left to the caller, keeping SimpleHttpClient general-purpose. Made these interface methods (not extension methods) so they can be mocked; a test asserts mockability. IAsyncEnumerable works on netstandard2.0 via the Microsoft.Bcl.AsyncInterfaces dependency System.Text.Json already brings in; added <LangVersion>latest</LangVersion> so the netstandard2.0 target (C# 7.3 by default) can compile async streams. Adds ServerSentEvent model, 14 tests (parser units + WireMock end-to-end + mockability), and README docs. net10.0 and net48: 110 tests each. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The intro sentence and NuGet <Description> predated the streaming support, so they described only the buffered request/response model. Mention the line/SSE streaming helpers, add the SSE subsection to the README table of contents, and fix grammar in the package description. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make the three benefits parallel noun phrases joined by a single 'and' in both the README intro and the NuGet description, fixing the awkward 'No extension methods, included interfaces allow for...' reading. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds two streaming-consumption methods on
ISimpleStreamResponse, so consumers don't have to hand-roll SSE framing on top of the rawBodystream:ReadLinesAsync(ct)— reads the body as text lines (CR/LF/CRLF), encoding from the Content-Type charset (UTF-8 default), cancellation observed mid-read.ReadServerSentEventsAsync(ct)— parses thetext/event-streamwire format per the WHATWG SSE spec: blank-line dispatch, multipledata:lines joined with newlines, comment/keep-alive (:) lines skipped,event/id/retryfields, andidcarrying forward. ReturnsServerSentEventframes (Data,EventType,Id,Retry).Design
ISimpleStreamResponse, implemented inSimpleStreamResponse, so callers can mock them (a test asserts this).EventSourceconsumes), so it belongs in the library. Application-specific conventions are deliberately left to the caller:[DONE]special-casing — it passes through as ordinaryData(OpenAI's convention; Anthropic usesevent: message_stop). The caller'sifdecides.Datais a string; the caller deserializes with whatever serializer/type it wants.Notes
IAsyncEnumerableworks onnetstandard2.0viaMicrosoft.Bcl.AsyncInterfaces, already pulled in transitively bySystem.Text.Json— no new dependency. Added<LangVersion>latest</LangVersion>so the netstandard2.0 target (C# 7.3 by default) can compile async streams.CancellationAwareStream, so cancellation is honored mid-read.Tests
14 new tests (parser units via in-memory streams, a WireMock end-to-end through
MakeStreamRequest, and a mockability test): line splitting, CRLF, multi-datajoin, no-space-after-colon, comment skipping, event/id/retry, id-carries-forward, reset-between-events,[DONE]pass-through, event-without-data not dispatched, incomplete trailing event not dispatched, pre-cancelled token throws, mockable interface. net10.0 and net48: 110 each.🤖 Generated with Claude Code