Skip to content

feat(boto3): Instrument Bedrock Runtime streaming calls as gen_ai spans - #8189

Draft
sentry-junior[bot] wants to merge 2 commits into
junior/boto3-bedrock-invoke-modelfrom
junior/boto3-bedrock-streaming
Draft

sentry-junior[bot] wants to merge 2 commits into
junior/boto3-bedrock-invoke-modelfrom
junior/boto3-bedrock-streaming

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Description

This PR (3 of 3, stacked on #8187) records ConverseStream (chat) and InvokeModelWithResponseStream (generate_content) as gen_ai spans with gen_ai.response.streaming: true. The finish reason, token usage, and output messages (text, reasoning, and tool calls with streamed JSON arguments) are built from the events the caller receives. The span finishes once, when the stream is:

  • exhausted
  • close()d, read or unread
  • stopped early, e.g. break (the iterator's GeneratorExit)
  • failed, e.g. a modelStreamErrorException event or a read error. The error is re-raised unchanged and the span is marked as errored.

Decisions the diff doesn't show:

  • botocore.eventstream.EventStream.__iter__ and close are patched globally in setup_once. Python looks up __iter__ on the class, so it can't be patched per instance. The patch only acts on streams that carry a _sentry_hooks attribute, which this sets on instrumented Bedrock responses. Other event streams, e.g. S3 Select, go through yield from unchanged. This is the same approach as the Anthropic integration's Stream._iterator patch. The response object and the events the caller gets are untouched.
  • InvokeModelWithResponseStream chunks are parsed per model family, like JS. Nova chunks reuse the ConverseStream parser, since they share its format. Usage falls back to Bedrock's amazon-bedrock-invocationMetrics in the last chunk. Claude output tokens come from message_delta rather than message_start, which JS uses and which reports only the first token.
  • Output content is buffered only when data_collection.gen_ai.outputs (or, without data_collection, send_default_pii with include_prompts) allows recording it. That check, made when the stream starts, is the only gate on streamed outputs. Input messages are gated by data_collection.gen_ai.inputs when the request is made, as for the non-streaming calls.
  • Malformed InvokeModelWithResponseStream chunks are skipped quietly. The caller still gets them unchanged.

Changelog note

ConverseStream and InvokeModelWithResponseStream spans change from http.client / auto.http.boto3 to gen_ai.chat and gen_ai.generate_content / auto.ai.boto3, and their names change from aws.bedrock-runtime.ConverseStream and aws.bedrock-runtime.InvokeModelWithResponseStream to chat <modelId> and generate_content <modelId>, which affects ignore_spans rules that match on the name. Boto3Integration is auto-enabled, so users with send_default_pii=True start sending streamed prompts and responses unless they set Boto3Integration(include_prompts=False) or configure data_collection.

Issues

via @dcramer.

--

View Session [Sentry]

@github-actions

github-actions Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Coverage Results 📊

✅ 139674 passed | ⏭️ 7497 skipped | Total: 147171 | Pass Rate: 94.91% | Execution Time: 466m 57s

📊 Comparison with Base Branch

Metric Change
Total Tests 📈 +2501
Passed Tests 📈 +2460
Failed Tests —
Skipped Tests 📈 +41

All tests are passing successfully.

✅ Patch coverage is 94.83% (220 of 232 changed executable lines covered; target 80%).
Project statement coverage is 90.44% (up 0.11 percentage points from base (b8c2634) to head (81e76ad)).

Changed files with executable lines (3)
File Patch coverage Changed executable lines
sentry_sdk/integrations/boto3/_services/bedrock_runtime.py 95.63% 175/183 covered; missed: 261, 274, 809, 811, 850, 851, 888, 896; partial branches: 253, 260, 273, 802, 804, 806, 835, 848, 887, 895, 987, 989
sentry_sdk/integrations/boto3/_instrumentation.py 91.49% 43/47 covered; missed: 469, 470, 471, 472; partial branches: 474
sentry_sdk/integrations/boto3/init.py 100.00% 2/2 covered
Coverage diff
@@            Coverage Diff             @@
##        master     #8189       +/-##
==========================================
+ Coverage    90.33%    90.44%    +0.11%
==========================================
  Files          203       204        +1
  Tracked lines     26905     27554      +649
  Branches     10048     10386      +338
==========================================
+ Hits         24302     24919      +617
- Misses        2603      2635       +32
- Partials      1516      1561       +45

Generated by Coverage Action

@sentry-junior
sentry-junior Bot force-pushed the junior/boto3-bedrock-streaming branch 2 times, most recently from febb4e2 to dc0d1ab Compare November 4, 2026 23:50
@sentry-junior
sentry-junior Bot force-pushed the junior/boto3-bedrock-streaming branch from dc0d1ab to d740ea6 Compare November 5, 2026 00:13
@sentry-junior
sentry-junior Bot force-pushed the junior/boto3-bedrock-streaming branch 2 times, most recently from 7007b79 to 34c9160 Compare November 5, 2026 01:08
@sentry-junior
sentry-junior Bot force-pushed the junior/boto3-bedrock-streaming branch from 34c9160 to 5a2015e Compare November 5, 2026 01:16
sentry-junior Bot and others added 2 commits November 5, 2026 01:29
Record ConverseStream and InvokeModelWithResponseStream calls as gen_ai
spans that finish once the caller has received the stream: when it's
exhausted, closed, iteration stops early, or it fails. The finish reason,
token usage, and output messages are accumulated from the events the
caller receives, which are unchanged.

`EventStream.__iter__` and `close` are patched globally, since the iterator
is a method; only streams of instrumented calls are recorded.

Refs #5470

Co-Authored-By: Sergiy Dybskiy <3760543+sergical@users.noreply.github.com>
…uts once

Malformed InvokeModelWithResponseStream chunks are skipped quietly instead
of reporting an SDK internal error, like the non-streamed response path.

The buffered stream content is already gated on recording outputs, so
serialize it directly rather than checking the gate a second time. That
makes the buffering gate the only gate, so tests cover it.

Co-Authored-By: Sergiy Dybskiy <3760543+sergical@users.noreply.github.com>
@sentry-junior
sentry-junior Bot force-pushed the junior/boto3-bedrock-streaming branch from 5a2015e to 81e76ad Compare November 5, 2026 01:41

This branch has not been deployed

No deployments
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