Skip to content

Bedrock Runtime: event stream support (ConverseStream, InvokeModelWithResponseStream) - #2

Draft
arbus wants to merge 3 commits into
jan/bedrock-runtime-experimentfrom
eventstream-support
Draft

Bedrock Runtime: event stream support (ConverseStream, InvokeModelWithResponseStream)#2
arbus wants to merge 3 commits into
jan/bedrock-runtime-experimentfrom
eventstream-support

Conversation

@arbus

@arbus arbus commented Aug 4, 2026

Copy link
Copy Markdown

Summary

Adds streaming support for the Bedrock Converse API on top of jan/bedrock-runtime-experiment (which contains the generated amazonka-bedrock-runtime package and the hand-written Document type):

  • Amazonka.Data.EventStream (amazonka-core): a general decoder for the application/vnd.amazon.eventstream binary framing protocol — incremental attoparsec-based frame parsing with both CRC32 validations, typed header values, a FromEventStream class, and conduit-based decodeEvents/sinkEvents. In-stream exception/error messages are thrown as EventStreamError while consuming. Re-exported from the amazonka package so consumers don't need a direct amazonka-core dependency.
  • Response.receiveEventStream (amazonka-core): hands a still-streaming typed EventStream to the response, mirroring receiveBody semantics (consume within the request's ResourceT scope).
  • Hand-written ConverseStream and InvokeModelWithResponseStream operations and their event shapes in amazonka-bedrock-runtime — the generator cannot render eventstream operations (their union shapes reference exception shapes that are never rendered as type modules). HANDWRITTEN.md documents every hand-maintained file and how to restore the wiring after regeneration. InvokeModelWithBidirectionalStream remains out of scope (bidirectional streaming).
  • examples/src/BedrockRuntime.hs gains askStream, a token-by-token streaming variant of the Converse example.

Verification

  • Hand-written shapes cross-checked against upstream botocore bedrock-runtime/2023-09-30/service-2.json (current develop): all implemented members' JSON keys, header locationNames, URIs, and required/optional choices match exactly. Only deliberate omissions: ContentBlockStart.{toolResult,image} and ContentBlockDelta.{toolResult,image} (payload types absent from the generated snapshot; they degrade to all-Nothing records).
  • amazonka-core: 158 tests pass, including new eventstream golden vectors (independently generated with Python's zlib), property tests for arbitrary chunk boundaries, and all failure modes. amazonka-bedrock-runtime: 10 event-decoding tests pass.
  • Exercised live against Bedrock (us-east-1, Claude Haiku 4.5 / Sonnet 4.5): basic token streaming, tool-use input deltas, extended-thinking reasoning deltas (Sensitive-redacted in Show), pre-stream validation errors surfacing as ServiceError, early stream abandonment (clean connection close), and raw InvokeModelWithResponseStream with a native Anthropic body.
  • CRC32 is a deliberately simple bitwise implementation (~34 MB/s compiled): a 500k-token response costs ~0.3s CPU spread over the minutes the model takes to generate it.

🤖 Generated with Claude Code

https://claude.ai/code/session_014U3u9Twk5BYeWEvGmLN8HC

arbus and others added 3 commits August 4, 2026 15:19
Adds Amazonka.Data.EventStream, a general decoder for the
application/vnd.amazon.eventstream binary framing protocol used by AWS
streaming APIs (Bedrock ConverseStream, Kinesis SubscribeToShard,
Transcribe streaming, S3 SelectObjectContent, ...):

- Incremental, conduit-based decoding via attoparsec: frames may be
  split arbitrarily across network chunks.
- Both prelude and message CRC32s (standard IEEE CRC-32, per
  botocore/aws-sdk-go-v2) are validated; mismatches fail loudly.
- All nine header value types are supported.
- exception and error message types are surfaced distinctly from
  events, as EventStreamError thrown while the stream is consumed.
- A typed EventStream/FromEventStream layer plus a
  Response.receiveEventStream combinator mirroring receiveBody, so
  generated or hand-written operations can declare eventstream
  responses.

Golden test vectors are generated independently with Python's
struct/binascii.crc32 (zlib); the empty-message vector matches the
well-known AWS reference frame. Tests cover one-byte-at-a-time and
arbitrary chunk boundaries, corrupted CRCs, truncation, trailing
garbage, and event/exception/error dispatch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DU2ShNvakdkCjZPWjPujFT
Hand-written implementations of the two response-streaming Bedrock
Runtime operations, built on the new Amazonka.Data.EventStream decoder
in amazonka-core. The generator cannot render eventstream operations
(the union shapes reference exception shapes that are never rendered as
type modules), so these ops and their streaming-only shapes live in
src/ alongside the existing hand-written Document module; the annex
keeps them marked deprecated so regeneration does not emit broken
duplicates. HANDWRITTEN.md documents every hand-maintained file, the
design decisions, and the exact steps to restore the wiring after a
future regeneration.

- ConverseStream: full request surface (messages, system, tools,
  guardrails, prompt variables, performance/service tier, output
  config); response is a typed EventStream ConverseStreamOutput with
  sum-type events for messageStart, contentBlockStart (toolUse),
  contentBlockDelta (text / toolUse / reasoningContent / citation),
  contentBlockStop, messageStop, and metadata.
- InvokeModelWithResponseStream: raw-body request mirroring
  InvokeModel; response is a typed EventStream ResponseStream of
  PayloadPart chunks.
- In-stream service exceptions surface as EventStreamError thrown
  while consuming, mirroring how amazonka handles error shapes.
- InvokeModelWithBidirectionalStream remains dropped (bidirectional
  streaming is out of scope).
- Hand tests decode one event of every :event-type through the
  FromEventStream instances; examples gains askStream, a token-by-token
  streaming variant of the Converse example.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DU2ShNvakdkCjZPWjPujFT
Verified the hand-written ConverseStream / InvokeModelWithResponseStream
work against the upstream botocore service definition (all implemented
members, JSON keys, header locationNames, URIs and optionality match)
and exercised it live against Bedrock: text streaming, tool-use input
deltas, reasoning deltas (Sensitive-redacted), pre-stream validation
errors, early stream abandonment, and raw InvokeModelWithResponseStream
all behave correctly. Fixes from the review:

- Re-export Amazonka.Data.EventStream from the amazonka package, so
  consumers can call sinkEvents without adding a direct amazonka-core
  dependency; drop the now-unneeded amazonka-core dependency from
  examples, which existed only for this.
- Fix a -Wname-shadowing warning in the Converse example, introduced by
  the new event stream types exporting `text` fields.
- HANDWRITTEN.md: modelTimeoutException is a member of ResponseStream
  only, not ConverseStreamOutput.
- steps.md: update sections that still claimed amazonka has no
  eventstream decoder and that the streaming operations are absent by
  design; point at HANDWRITTEN.md for the hand-written wiring.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014U3u9Twk5BYeWEvGmLN8HC
@arbus
arbus changed the base branch from main to jan/bedrock-runtime-experiment August 4, 2026 08:42
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.

1 participant