Skip to content

Conversation

lievan
Copy link
Contributor

@lievan lievan commented Jun 12, 2025

Fixes an issue where modifying chunks returned from a bedrock stream impacted our tracing of those chunks. Instead, our tracing should reflect the original response returned regardless of whether or not it was modified.

This is relevant when libraries like langchain delete data from the raw streamed response (e.g. popping usageMetadata).

The fix uses an approach where we immediately process stream chunks as they are iterated over, instead of waiting until the entire stream has finished.

The data flow is like this:

  1. a streamed chunk is read from TracedBotocoreConverseStream
  2. we send that chunk to _output_stream_processor, which reads all the relevant data from that chunk and builds the final output messages, token usage, metadata. This should block until we reach the next yield, at which point we've read all the data we needed for this chunk. The parsing logic is unchanged from the previous helper we used to parse the stream stream, except this method is now a generator.
  3. we yield the chunk back to the user

In terms of manual testing, i've verified that it fixes the langchain x bedrock converse streaming issue
image

if this logic looks good, it may be a pattern we will want to implement for our other integrations as well

Checklist

  • PR author has checked that all the criteria below are met
  • The PR description includes an overview of the change
  • The PR description articulates the motivation for the change
  • The change includes tests OR the PR description describes a testing strategy
  • The PR description notes risks associated with the change, if any
  • Newly-added code is easy to change
  • The change follows the library release note guidelines
  • The change includes or references documentation updates if necessary
  • Backport labels are set (if applicable)

Reviewer Checklist

  • Reviewer has checked that all the criteria below are met
  • Title is accurate
  • All changes are related to the pull request's stated goal
  • Avoids breaking API changes
  • Testing strategy adequately addresses listed risks
  • Newly-added code is easy to change
  • Release note makes sense to a user of the library
  • If necessary, author has acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment
  • Backport labels are set in a manner that is consistent with the release branch maintenance policy

Copy link
Contributor

github-actions bot commented Jun 12, 2025

CODEOWNERS have been resolved as:

releasenotes/notes/fix-bedrock-stream-0bd52763872967a5.yaml             @DataDog/apm-python
ddtrace/contrib/internal/botocore/services/bedrock.py                   @DataDog/ml-observability
ddtrace/llmobs/_integrations/bedrock.py                                 @DataDog/ml-observability
tests/contrib/botocore/test_bedrock_llmobs.py                           @DataDog/ml-observability

Copy link
Contributor

github-actions bot commented Jun 12, 2025

Bootstrap import analysis

Comparison of import times between this PR and base.

Summary

The average import time from this PR is: 285 ± 8 ms.

The average import time from base is: 278 ± 3 ms.

The import time difference between this PR and base is: 7.1 ± 0.3 ms.

Import time breakdown

The following import paths have grown:

ddtrace.auto 3.849 ms (1.35%)
ddtrace 2.097 ms (0.74%)
ddtrace._logger 1.217 ms (0.43%)
ddtrace.internal.telemetry 1.217 ms (0.43%)
ddtrace.internal.telemetry.writer 0.654 ms (0.23%)
http.client 0.417 ms (0.15%)
email.parser 0.194 ms (0.07%)
email.feedparser 0.194 ms (0.07%)
email._policybase 0.194 ms (0.07%)
email.header 0.167 ms (0.06%)
email.charset 0.167 ms (0.06%)
email.utils 0.026 ms (0.01%)
random 0.026 ms (0.01%)
ssl 0.173 ms (0.06%)
_ssl 0.076 ms (0.03%)
ddtrace.settings._telemetry 0.050 ms (0.02%)
ddtrace.internal.telemetry.data 0.048 ms (0.02%)
ddtrace.internal.packages 0.048 ms (0.02%)
_sysconfigdata__linux_x86_64-linux-gnu 0.048 ms (0.02%)
ddtrace.internal.telemetry.metrics_namespaces 0.047 ms (0.02%)
ddtrace.internal.runtime 0.042 ms (0.01%)
uuid 0.042 ms (0.01%)
platform 0.042 ms (0.01%)
ddtrace.settings._agent 0.518 ms (0.18%)
ddtrace.settings 0.320 ms (0.11%)
ddtrace.settings.http 0.214 ms (0.07%)
ddtrace.internal.utils.cache 0.214 ms (0.07%)
inspect 0.214 ms (0.07%)
ddtrace.settings.integration 0.106 ms (0.04%)
ddtrace.vendor.debtcollector 0.106 ms (0.04%)
ddtrace.vendor.debtcollector.removals 0.037 ms (0.01%)
ddtrace.vendor.debtcollector.moves 0.035 ms (0.01%)
ddtrace.vendor 0.034 ms (0.01%)
ddtrace.internal.module 0.034 ms (0.01%)
socket 0.140 ms (0.05%)
_socket 0.058 ms (0.02%)
ddtrace.settings._core 0.059 ms (0.02%)
envier 0.059 ms (0.02%)
envier.env 0.059 ms (0.02%)
ddtrace.trace 0.395 ms (0.14%)
ddtrace._trace.filters 0.188 ms (0.07%)
ddtrace._trace.processor 0.188 ms (0.07%)
ddtrace._trace.sampler 0.066 ms (0.02%)
ddtrace._trace.span 0.066 ms (0.02%)
ddtrace.internal._rand 0.043 ms (0.02%)
ddtrace._trace._span_pointer 0.023 ms (0.01%)
ddtrace.internal.dogstatsd 0.051 ms (0.02%)
ddtrace.vendor.dogstatsd 0.051 ms (0.02%)
ddtrace.vendor.dogstatsd.base 0.051 ms (0.02%)
ddtrace.internal.writer 0.033 ms (0.01%)
ddtrace.internal.writer.writer 0.033 ms (0.01%)
gzip 0.033 ms (0.01%)
ddtrace._trace.context 0.070 ms (0.02%)
ddtrace._trace._span_link 0.039 ms (0.01%)
ddtrace._trace.tracer 0.044 ms (0.02%)
ddtrace.settings._config 0.352 ms (0.12%)
ddtrace.internal.gitmetadata 0.290 ms (0.10%)
ddtrace.ext.ci 0.290 ms (0.10%)
ddtrace.ext.git 0.252 ms (0.09%)
tempfile 0.205 ms (0.07%)
shutil 0.046 ms (0.02%)
ddtrace.internal._file_queue 0.062 ms (0.02%)
secrets 0.042 ms (0.01%)
hmac 0.042 ms (0.01%)
_hashlib 0.042 ms (0.01%)
ddtrace._monkey 0.134 ms (0.05%)
ddtrace.appsec._listeners 0.059 ms (0.02%)
ddtrace.internal.core 0.059 ms (0.02%)
ddtrace.internal.core.event_hub 0.059 ms (0.02%)
ddtrace.settings.asm 0.039 ms (0.01%)
ddtrace.bootstrap.sitecustomize 1.752 ms (0.61%)
ddtrace.bootstrap.preload 1.179 ms (0.41%)
ddtrace.internal.products 0.399 ms (0.14%)
importlib.metadata 0.375 ms (0.13%)
zipfile 0.268 ms (0.09%)
zipfile._path 0.216 ms (0.08%)
csv 0.035 ms (0.01%)
_csv 0.035 ms (0.01%)
ddtrace.settings.profiling 0.235 ms (0.08%)
ddtrace.vendor.psutil 0.117 ms (0.04%)
ddtrace.vendor.psutil._pslinux 0.117 ms (0.04%)
glob 0.023 ms (0.01%)
ddtrace.internal.datadog.profiling.ddup 0.049 ms (0.02%)
ddtrace.internal.datadog.profiling.ddup._ddup 0.049 ms (0.02%)
multiprocessing.sharedctypes 0.234 ms (0.08%)
multiprocessing.heap 0.234 ms (0.08%)
mmap 0.213 ms (0.07%)
ddtrace.internal.remoteconfig._connectors 0.116 ms (0.04%)
ctypes 0.080 ms (0.03%)
_ctypes 0.038 ms (0.01%)
multiprocessing 0.057 ms (0.02%)
multiprocessing.context 0.057 ms (0.02%)
multiprocessing.reduction 0.057 ms (0.02%)
pickle 0.057 ms (0.02%)
ddtrace.debugging._import 0.039 ms (0.01%)
ddtrace.debugging._function.discovery 0.039 ms (0.01%)
ddtrace.internal.remoteconfig.worker 0.039 ms (0.01%)
ddtrace.settings.crashtracker 0.030 ms (0.01%)
ddtrace.internal.runtime.runtime_metrics 0.029 ms (0.01%)
ddtrace.appsec._common_module_patches 0.197 ms (0.07%)
ddtrace.appsec._asm_request_context 0.197 ms (0.07%)
ddtrace.appsec._utils 0.197 ms (0.07%)
ddtrace._trace.trace_handlers 0.154 ms (0.05%)
ddtrace._trace._inferred_proxy 0.081 ms (0.03%)
ddtrace.propagation.http 0.081 ms (0.03%)

The following import paths have shrunk:

ddtrace.auto 1.086 ms (0.38%)
ddtrace 0.650 ms (0.23%)
ddtrace.internal._unpatched 0.023 ms (0.01%)
json 0.023 ms (0.01%)
json.decoder 0.023 ms (0.01%)
re 0.023 ms (0.01%)
enum 0.023 ms (0.01%)
types 0.023 ms (0.01%)
ddtrace.bootstrap.sitecustomize 0.436 ms (0.15%)
ddtrace.bootstrap.preload 0.436 ms (0.15%)
ddtrace.internal.remoteconfig.client 0.436 ms (0.15%)

@pr-commenter
Copy link

pr-commenter bot commented Jun 12, 2025

Benchmarks

Benchmark execution time: 2025-06-17 19:04:24

Comparing candidate commit c8bce8e in PR branch evan.li/fix-bedrock-streaming with baseline commit 0192912 in branch main.

Found 3 performance improvements and 0 performance regressions! Performance is the same for 520 metrics, 3 unstable metrics.

scenario:iastdjangostartup-appsec

  • 🟩 execution_time [-1.994s; -1.831s] or [-91.285%; -83.853%]

scenario:iastdjangostartup-iast

  • 🟩 execution_time [-2.202s; -2.012s] or [-85.451%; -78.100%]

scenario:iastdjangostartup-tracer

  • 🟩 execution_time [-1.630s; -1.434s] or [-81.498%; -71.736%]

@lievan lievan marked this pull request as ready for review June 12, 2025 18:08
@lievan lievan requested review from a team as code owners June 12, 2025 18:08
@lievan lievan requested review from gnufede and ZStriker19 June 12, 2025 18:08
@lievan lievan changed the title fix(llmobs): handle mid stream modifications for bedrock fix(llmobs): bedrock converse tracing is resistant to modifying the stream Jun 12, 2025
@lievan lievan enabled auto-merge (squash) June 17, 2025 03:28
@lievan lievan changed the title fix(llmobs): bedrock converse tracing is resistant to modifying the stream fix(llmobs): bedrock converse tracing is resistant to modifying the stream Jun 17, 2025
lievan and others added 2 commits June 17, 2025 10:18
@lievan lievan force-pushed the evan.li/fix-bedrock-streaming branch 2 times, most recently from 13986c6 to c8bce8e Compare June 17, 2025 18:15
@lievan lievan merged commit 5592908 into main Jun 17, 2025
357 of 358 checks passed
@lievan lievan deleted the evan.li/fix-bedrock-streaming branch June 17, 2025 19:05
Copy link
Contributor

The backport to 2.21 failed:

The process '/usr/bin/git' failed with exit code 1

To backport manually, run these commands in your terminal:

# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add .worktrees/backport-2.21 2.21
# Navigate to the new working tree
cd .worktrees/backport-2.21
# Create a new branch
git switch --create backport-13659-to-2.21
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 5592908cfe4af4aa4dffe12f6c30191bbac361c2
# Push it to GitHub
git push --set-upstream origin backport-13659-to-2.21
# Go back to the original working tree
cd ../..
# Delete the working tree
git worktree remove .worktrees/backport-2.21

Then, create a pull request where the base branch is 2.21 and the compare/head branch is backport-13659-to-2.21.

sydney-tung pushed a commit that referenced this pull request Jun 24, 2025
…tream (#13659)

Fixes an issue where modifying chunks returned from a bedrock stream
impacted our tracing of those chunks. Instead, our tracing should
reflect the original response returned regardless of whether or not it
was modified.

This is relevant when libraries like langchain delete data from the raw
streamed response (e.g.
[popping](https://github.com/langchain-ai/langchain-aws/blob/40abb584979a349019d89bbf1cba7d8c56d23664/libs/aws/langchain_aws/chat_models/bedrock_converse.py#L995)
`usageMetadata`).

The fix uses an approach where we immediately process stream chunks as
they are iterated over, instead of waiting until the entire stream has
finished.

The data flow is like this:
1. a streamed chunk is read from `TracedBotocoreConverseStream`
2. we send that chunk to `_output_stream_processor`, which reads all the
relevant data from that chunk and builds the final output messages,
token usage, metadata. This should block until we reach the next yield,
at which point we've read all the data we needed for this chunk. The
parsing logic is **unchanged** from the previous helper we used to parse
the stream stream, except this method is now a generator.
4. we yield the chunk back to the user

In terms of manual testing, i've verified that it fixes the langchain x
bedrock converse streaming issue
<img width="908" alt="image"
src="https://github.com/user-attachments/assets/e4d1e4e5-3d1c-4f2d-a822-f7c7cdb169a6"
/>

if this logic looks good, it may be a pattern we will want to implement
for our other integrations as well

## Checklist
- [ ] PR author has checked that all the criteria below are met
- The PR description includes an overview of the change
- The PR description articulates the motivation for the change
- The change includes tests OR the PR description describes a testing
strategy
- The PR description notes risks associated with the change, if any
- Newly-added code is easy to change
- The change follows the [library release note
guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html)
- The change includes or references documentation updates if necessary
- Backport labels are set (if
[applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting))

## Reviewer Checklist
- [ ] Reviewer has checked that all the criteria below are met 
- Title is accurate
- All changes are related to the pull request's stated goal
- Avoids breaking
[API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces)
changes
- Testing strategy adequately addresses listed risks
- Newly-added code is easy to change
- Release note makes sense to a user of the library
- If necessary, author has acknowledged and discussed the performance
implications of this PR as reported in the benchmarks PR comment
- Backport labels are set in a manner that is consistent with the
[release branch maintenance
policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)

---------

Co-authored-by: lievan <[email protected]>
happynancee pushed a commit that referenced this pull request Jul 7, 2025
…tream (#13659)

Fixes an issue where modifying chunks returned from a bedrock stream
impacted our tracing of those chunks. Instead, our tracing should
reflect the original response returned regardless of whether or not it
was modified.

This is relevant when libraries like langchain delete data from the raw
streamed response (e.g.
[popping](https://github.com/langchain-ai/langchain-aws/blob/40abb584979a349019d89bbf1cba7d8c56d23664/libs/aws/langchain_aws/chat_models/bedrock_converse.py#L995)
`usageMetadata`).

The fix uses an approach where we immediately process stream chunks as
they are iterated over, instead of waiting until the entire stream has
finished.

The data flow is like this:
1. a streamed chunk is read from `TracedBotocoreConverseStream`
2. we send that chunk to `_output_stream_processor`, which reads all the
relevant data from that chunk and builds the final output messages,
token usage, metadata. This should block until we reach the next yield,
at which point we've read all the data we needed for this chunk. The
parsing logic is **unchanged** from the previous helper we used to parse
the stream stream, except this method is now a generator.
4. we yield the chunk back to the user

In terms of manual testing, i've verified that it fixes the langchain x
bedrock converse streaming issue
<img width="908" alt="image"
src="https://github.com/user-attachments/assets/e4d1e4e5-3d1c-4f2d-a822-f7c7cdb169a6"
/>

if this logic looks good, it may be a pattern we will want to implement
for our other integrations as well

## Checklist
- [ ] PR author has checked that all the criteria below are met
- The PR description includes an overview of the change
- The PR description articulates the motivation for the change
- The change includes tests OR the PR description describes a testing
strategy
- The PR description notes risks associated with the change, if any
- Newly-added code is easy to change
- The change follows the [library release note
guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html)
- The change includes or references documentation updates if necessary
- Backport labels are set (if
[applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting))

## Reviewer Checklist
- [ ] Reviewer has checked that all the criteria below are met 
- Title is accurate
- All changes are related to the pull request's stated goal
- Avoids breaking
[API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces)
changes
- Testing strategy adequately addresses listed risks
- Newly-added code is easy to change
- Release note makes sense to a user of the library
- If necessary, author has acknowledged and discussed the performance
implications of this PR as reported in the benchmarks PR comment
- Backport labels are set in a manner that is consistent with the
[release branch maintenance
policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)

---------

Co-authored-by: lievan <[email protected]>
alyshawang pushed a commit that referenced this pull request Jul 25, 2025
…tream (#13659)

Fixes an issue where modifying chunks returned from a bedrock stream
impacted our tracing of those chunks. Instead, our tracing should
reflect the original response returned regardless of whether or not it
was modified.

This is relevant when libraries like langchain delete data from the raw
streamed response (e.g.
[popping](https://github.com/langchain-ai/langchain-aws/blob/40abb584979a349019d89bbf1cba7d8c56d23664/libs/aws/langchain_aws/chat_models/bedrock_converse.py#L995)
`usageMetadata`).

The fix uses an approach where we immediately process stream chunks as
they are iterated over, instead of waiting until the entire stream has
finished.

The data flow is like this:
1. a streamed chunk is read from `TracedBotocoreConverseStream`
2. we send that chunk to `_output_stream_processor`, which reads all the
relevant data from that chunk and builds the final output messages,
token usage, metadata. This should block until we reach the next yield,
at which point we've read all the data we needed for this chunk. The
parsing logic is **unchanged** from the previous helper we used to parse
the stream stream, except this method is now a generator.
4. we yield the chunk back to the user

In terms of manual testing, i've verified that it fixes the langchain x
bedrock converse streaming issue
<img width="908" alt="image"
src="https://github.com/user-attachments/assets/e4d1e4e5-3d1c-4f2d-a822-f7c7cdb169a6"
/>

if this logic looks good, it may be a pattern we will want to implement
for our other integrations as well

## Checklist
- [ ] PR author has checked that all the criteria below are met
- The PR description includes an overview of the change
- The PR description articulates the motivation for the change
- The change includes tests OR the PR description describes a testing
strategy
- The PR description notes risks associated with the change, if any
- Newly-added code is easy to change
- The change follows the [library release note
guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html)
- The change includes or references documentation updates if necessary
- Backport labels are set (if
[applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting))

## Reviewer Checklist
- [ ] Reviewer has checked that all the criteria below are met 
- Title is accurate
- All changes are related to the pull request's stated goal
- Avoids breaking
[API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces)
changes
- Testing strategy adequately addresses listed risks
- Newly-added code is easy to change
- Release note makes sense to a user of the library
- If necessary, author has acknowledged and discussed the performance
implications of this PR as reported in the benchmarks PR comment
- Backport labels are set in a manner that is consistent with the
[release branch maintenance
policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)

---------

Co-authored-by: lievan <[email protected]>
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.

5 participants