Skip to content

feat(aws-sdk, llmobs): support Bedrock Converse and ConverseStream#8079

Open
PROFeNoM wants to merge 12 commits intoalex/llmobs-tool-definitionsfrom
alex/MLOB-3509_bedrock-converse-methods-support
Open

feat(aws-sdk, llmobs): support Bedrock Converse and ConverseStream#8079
PROFeNoM wants to merge 12 commits intoalex/llmobs-tool-definitionsfrom
alex/MLOB-3509_bedrock-converse-methods-support

Conversation

@PROFeNoM
Copy link
Copy Markdown
Contributor

@PROFeNoM PROFeNoM commented Apr 23, 2026

Adds LLMObs support for AWS Bedrock ConverseCommand and ConverseStreamCommand, bringing Node.js to parity with dd-trace-py (PRs DataDog/dd-trace-py#12560, DataDog/dd-trace-py#12873, DataDog/dd-trace-py#13756).

👉 Live Datadog UI traces — Python (existing integration) vs Node.js (this PR), same operations side-by-side

image

Sample comparison (Left=Python; right=NodeJS)

Depends on #8082 (feat(llmobs): add tool_definitions support to Tagger) — merge that first.

What gets emitted

One llm-kind LLMObs span per Converse call, named bedrock-runtime.command. Stream and non-stream produce the same span shape; the stream path assembles it from the ConverseStreamOutput event union (messageStartcontentBlockStartcontentBlockDeltacontentBlockStopmessageStopmetadata).

Example span for a Converse call with a system prompt + tool:

{
  "name": "bedrock-runtime.command",
  "meta": {
    "span.kind": "llm",
    "model_name": "anthropic.claude-3-haiku-20240307-v1:0",
    "model_provider": "amazon_bedrock",
    "input": {
      "messages": [
        { "role": "system", "content": "You are an expert swe..." },
        { "role": "user", "content": "Explain distributed tracing." }
      ]
    },
    "output": {
      "messages": [
        {
          "role": "assistant",
          "tool_calls": [{
            "name": "fetch_concept",
            "arguments": { "concept": "distributed tracing" },
            "tool_id": "tooluse_...",
            "type": "toolUse"
          }]
        }
      ]
    },
    "tool_definitions": [
      { "name": "fetch_concept", "description": "...", "schema": { "json": {...} } }
    ],
    "metadata": {
      "temperature": 0.5,
      "max_tokens": 512,
      "stop_reason": "tool_use"
    }
  },
  "metrics": {
    "input_tokens": 364,
    "output_tokens": 55,
    "total_tokens": 419,
    "cache_read_input_tokens": 0,
    "cache_write_input_tokens": 0
  }
}

Content in both input and output follows Bedrock's ContentBlock tagged union. Supported variants (text, toolUse, toolResult) are decoded into the shape above. Unsupported ones (image, document, video, reasoningContent, citationsContent, searchResult, cachePoint) become inline markers on non-stream responses only — [Unsupported content type: <type>] for top-level blocks (dd-trace-py utils.py:279) and [Unsupported content type(s): <keys>] inside tool results (dd-trace-py utils.py:266). The stream aggregator silently ignores unhandled ContentBlockDelta variants, matching dd-trace-py.

Known disparities with dd-trace-py not addressed in this PR

Verified side-by-side against dd-trace-py on a demo app running both tracers end-to-end against the Datadog UI. The differences below were identified and noted, but ignored.

Service name

Python hardcodes aws.bedrock-runtime (ignores DD_SERVICE; uses DD_BOTOCORE_SERVICE=aws + endpoint-name suffix). Node respects DD_SERVICE, as shown in the screenshot above.

Why not acted on: pre-existing divergence, unrelated to Converse. Changing either side is a service-naming policy call, and a breaking change; not something that belongs to that PR.

model_name / model_provider

Python splits via parse_model_id(anthropic, claude-3-haiku-20240307-v1:0); backend matches claude-3-haiku / anthropic. Node sends the full lowercased modelId + literal amazon_bedrock provider; the backend falls back to the raw string because amazon_bedrock isn't a recognized foundation-model provider.

Why not acted on: pre-existing Node behavior from PR #7952 (fix(llmobs): fix missing estimated cost on Bedrock LLM spans, commit 2bac2030d), unchanged by this PR.

stop_reason on ConverseStream

image

Python captures stopReason for non-stream Converse but drops it on the stream path (bedrock.py:304-308). Node emits stop_reason uniformly for both paths.

Why not acted on: Node is doing better parity than Python here tbh.

APM span name, resource case, tag prefix

image
Python Node
Span name bedrock-runtime.command aws.request
Resource ConverseStream (PascalCase) converseStream (camelCase)
Bedrock tags prefix bedrock.request.* aws.bedrock.request.*

Why not acted on: not introduced by this PR. Unchanged since the Bedrock plugin was introduced. A rename would be a breaking change.

Test plan

  • PLUGINS="aws-sdk" SPEC="bedrockruntime.spec.js" npm run test:plugins — green
  • LLMObs spec: packages/dd-trace/test/llmobs/plugins/aws-sdk/bedrockruntime.spec.js — green
  • npm run lint on touched files — clean
  • End-to-end verified on Datadog UI via companion demo app (demo-apps/bedrock-converse) — see live-traces link at the top of this description
  • Full CI

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 23, 2026

Overall package size

Self size: 5.56 MB
Deduped: 6.41 MB
No deduping: 6.41 MB

Dependency sizes | name | version | self size | total size | |------|---------|-----------|------------| | import-in-the-middle | 3.0.1 | 82.56 kB | 817.39 kB | | dc-polyfill | 0.1.10 | 26.73 kB | 26.73 kB |

🤖 This report was automatically generated by heaviest-objects-in-the-universe

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 23, 2026

Codecov Report

❌ Patch coverage is 85.85859% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.81%. Comparing base (1451e6c) to head (ad1cd0f).

Files with missing lines Patch % Lines
...lugin-aws-sdk/src/services/bedrockruntime/utils.js 82.05% 14 Missing ⚠️
Additional details and impacted files
@@                       Coverage Diff                        @@
##           alex/llmobs-tool-definitions    #8079      +/-   ##
================================================================
+ Coverage                         73.64%   73.81%   +0.17%     
================================================================
  Files                               782      782              
  Lines                             36371    36461      +90     
================================================================
+ Hits                              26785    26915     +130     
+ Misses                             9586     9546      -40     
Flag Coverage Δ
aiguard-macos 36.33% <ø> (ø)
aiguard-ubuntu 36.44% <ø> (ø)
aiguard-windows 36.24% <ø> (ø)
apm-capabilities-tracing-macos 47.94% <3.12%> (-0.17%) ⬇️
apm-capabilities-tracing-ubuntu-active 47.97% <3.12%> (-0.16%) ⬇️
apm-capabilities-tracing-ubuntu-latest 47.94% <3.12%> (-0.16%) ⬇️
apm-capabilities-tracing-ubuntu-maintenance 47.97% <3.12%> (-0.16%) ⬇️
apm-capabilities-tracing-ubuntu-oldest 47.96% <3.12%> (-0.16%) ⬇️
apm-capabilities-tracing-windows 47.77% <3.12%> (-0.16%) ⬇️
apm-integrations-child-process 35.99% <ø> (ø)
apm-integrations-couchbase-18 35.00% <ø> (ø)
apm-integrations-couchbase-eol 35.05% <ø> (ø)
apm-integrations-oracledb 35.05% <ø> (ø)
appsec-express 52.66% <ø> (+<0.01%) ⬆️
appsec-fastify 49.15% <ø> (ø)
appsec-graphql 49.51% <ø> (ø)
appsec-kafka 41.93% <ø> (-0.01%) ⬇️
appsec-ldapjs 41.26% <ø> (?)
appsec-lodash 41.29% <ø> (ø)
appsec-macos 56.72% <ø> (+<0.01%) ⬆️
appsec-mongodb-core 45.58% <ø> (ø)
appsec-mongoose 46.47% <ø> (ø)
appsec-mysql 48.63% <ø> (ø)
appsec-node-serialize 40.47% <ø> (ø)
appsec-passport 44.43% <ø> (ø)
appsec-postgres 48.34% <ø> (ø)
appsec-sourcing 39.96% <ø> (ø)
appsec-stripe 42.17% <ø> (ø)
appsec-template 40.63% <ø> (ø)
appsec-ubuntu 56.80% <ø> (ø)
appsec-windows 56.60% <ø> (ø)
instrumentations-instrumentation-bluebird 29.71% <ø> (ø)
instrumentations-instrumentation-body-parser 37.59% <ø> (ø)
instrumentations-instrumentation-child_process 35.36% <ø> (ø)
instrumentations-instrumentation-cookie-parser 31.63% <ø> (ø)
instrumentations-instrumentation-express 31.85% <ø> (ø)
instrumentations-instrumentation-express-mongo-sanitize 31.75% <ø> (ø)
instrumentations-instrumentation-express-session 37.22% <ø> (ø)
instrumentations-instrumentation-fs 29.38% <ø> (ø)
instrumentations-instrumentation-generic-pool 30.53% <ø> (ø)
instrumentations-instrumentation-http 36.82% <ø> (ø)
instrumentations-instrumentation-knex 29.68% <ø> (ø)
instrumentations-instrumentation-light-my-request 37.16% <ø> (ø)
instrumentations-instrumentation-mongoose 30.77% <ø> (-0.07%) ⬇️
instrumentations-instrumentation-multer 37.36% <ø> (ø)
instrumentations-instrumentation-mysql2 35.34% <ø> (ø)
instrumentations-instrumentation-passport 41.10% <ø> (ø)
instrumentations-instrumentation-passport-http 40.79% <ø> (ø)
instrumentations-instrumentation-passport-local 41.29% <ø> (ø)
instrumentations-instrumentation-pg 34.86% <ø> (ø)
instrumentations-instrumentation-promise 29.64% <ø> (ø)
instrumentations-instrumentation-promise-js 29.65% <ø> (ø)
instrumentations-instrumentation-q 29.68% <ø> (ø)
instrumentations-instrumentation-url 29.64% <ø> (ø)
instrumentations-instrumentation-when 29.66% <ø> (ø)
llmobs-ai 38.07% <0.00%> (-0.25%) ⬇️
llmobs-anthropic 37.77% <ø> (ø)
llmobs-bedrock 37.55% <85.85%> (+0.53%) ⬆️
llmobs-google-genai 37.45% <ø> (ø)
llmobs-langchain 36.71% <3.03%> (-0.22%) ⬇️
llmobs-openai 41.13% <ø> (ø)
llmobs-vertex-ai 37.63% <ø> (ø)
platform-core 30.03% <ø> (ø)
platform-esbuild 32.83% <ø> (ø)
platform-instrumentations-misc 40.01% <ø> (ø)
platform-shimmer 35.72% <ø> (ø)
platform-unit-guardrails 31.39% <ø> (ø)
platform-webpack 20.71% <0.00%> (-0.01%) ⬇️
plugins-azure-durable-functions 25.36% <ø> (ø)
plugins-azure-event-hubs 25.51% <ø> (ø)
plugins-azure-service-bus 24.92% <ø> (ø)
plugins-bullmq 40.51% <ø> (-0.12%) ⬇️
plugins-cassandra 35.19% <ø> (ø)
plugins-cookie 26.47% <ø> (ø)
plugins-cookie-parser 26.28% <ø> (ø)
plugins-crypto 25.70% <ø> (ø)
plugins-dd-trace-api 35.30% <ø> (ø)
plugins-express-mongo-sanitize 26.42% <ø> (ø)
plugins-express-session 26.24% <ø> (ø)
plugins-fastify 39.20% <ø> (ø)
plugins-fetch 35.70% <ø> (ø)
plugins-fs 35.59% <ø> (ø)
plugins-generic-pool 25.40% <ø> (ø)
plugins-google-cloud-pubsub 42.94% <ø> (ø)
plugins-grpc 37.97% <ø> (ø)
plugins-handlebars 26.46% <ø> (ø)
plugins-hapi 37.20% <ø> (ø)
plugins-hono 37.45% <ø> (ø)
plugins-ioredis 35.64% <ø> (ø)
plugins-knex 26.14% <ø> (ø)
plugins-langgraph 34.94% <ø> (ø)
plugins-ldapjs 24.02% <ø> (ø)
plugins-light-my-request 25.88% <ø> (ø)
plugins-limitd-client 29.94% <ø> (ø)
plugins-lodash 25.47% <ø> (ø)
plugins-mariadb 36.51% <ø> (ø)
plugins-memcached 35.29% <ø> (ø)
plugins-microgateway-core 36.29% <ø> (ø)
plugins-modelcontextprotocol-sdk 34.23% <ø> (ø)
plugins-moleculer 37.98% <ø> (ø)
plugins-mongodb 36.45% <ø> (ø)
plugins-mongodb-core 36.09% <ø> (ø)
plugins-mongoose 35.96% <ø> (-0.09%) ⬇️
plugins-multer 26.24% <ø> (ø)
plugins-mysql 36.37% <ø> (ø)
plugins-mysql2 36.36% <ø> (ø)
plugins-node-serialize 26.51% <ø> (ø)
plugins-opensearch 34.95% <ø> (ø)
plugins-passport-http 26.30% <ø> (ø)
plugins-pino 31.74% <ø> (ø)
plugins-postgres 34.35% <ø> (ø)
plugins-process 25.70% <ø> (ø)
plugins-pug 26.47% <ø> (ø)
plugins-redis 35.85% <ø> (ø)
plugins-router 39.85% <ø> (ø)
plugins-sequelize 25.18% <ø> (ø)
plugins-test-and-upstream-amqp10 35.61% <ø> (ø)
plugins-test-and-upstream-amqplib 40.78% <ø> (ø)
plugins-test-and-upstream-apollo 36.46% <ø> (ø)
plugins-test-and-upstream-avsc 35.37% <ø> (ø)
plugins-test-and-upstream-bunyan 31.08% <ø> (ø)
plugins-test-and-upstream-connect 37.79% <ø> (ø)
plugins-test-and-upstream-graphql 37.14% <ø> (ø)
plugins-test-and-upstream-koa 37.40% <ø> (ø)
plugins-test-and-upstream-protobufjs 35.59% <ø> (ø)
plugins-test-and-upstream-rhea 40.99% <ø> (+0.10%) ⬆️
plugins-undici 36.46% <ø> (ø)
plugins-url 25.70% <ø> (ø)
plugins-valkey 35.32% <ø> (ø)
plugins-vm 25.70% <ø> (ø)
plugins-winston 31.52% <ø> (ø)
plugins-ws 38.92% <ø> (ø)
profiling-macos 37.78% <ø> (ø)
profiling-ubuntu 38.40% <ø> (+0.45%) ⬆️
profiling-windows 39.31% <ø> (ø)
serverless-azure-functions-client 25.25% <ø> (ø)
serverless-azure-functions-eventhubs 25.25% <ø> (ø)
serverless-azure-functions-servicebus 25.25% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@datadog-datadog-prod-us1-2
Copy link
Copy Markdown

datadog-datadog-prod-us1-2 Bot commented Apr 23, 2026

Tests

🎉 All green!

❄️ No new flaky tests detected
🧪 All tests passed

🎯 Code Coverage (details)
Patch Coverage: 60.95%
Overall Coverage: 68.13% (-0.01%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: ad1cd0f | Docs | Datadog PR Page | Give us feedback!

@pr-commenter
Copy link
Copy Markdown

pr-commenter Bot commented Apr 23, 2026

Benchmarks

Benchmark execution time: 2026-04-27 13:30:56

Comparing candidate commit ad1cd0f in PR branch alex/MLOB-3509_bedrock-converse-methods-support with baseline commit 1451e6c in branch alex/llmobs-tool-definitions.

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

@PROFeNoM PROFeNoM force-pushed the alex/MLOB-3509_bedrock-converse-methods-support branch from 48ae827 to 7da447f Compare April 23, 2026 12:17
@PROFeNoM PROFeNoM changed the base branch from master to alex/llmobs-tool-definitions April 23, 2026 12:17
@PROFeNoM PROFeNoM force-pushed the alex/llmobs-tool-definitions branch from abcf58c to 6f9ab4d Compare April 23, 2026 12:23
@PROFeNoM PROFeNoM force-pushed the alex/MLOB-3509_bedrock-converse-methods-support branch from 7da447f to 6486db6 Compare April 23, 2026 12:24
@PROFeNoM PROFeNoM force-pushed the alex/llmobs-tool-definitions branch from 6f9ab4d to 21f766e Compare April 23, 2026 12:26
@PROFeNoM PROFeNoM force-pushed the alex/MLOB-3509_bedrock-converse-methods-support branch from 6486db6 to f22c2ab Compare April 23, 2026 12:27
@PROFeNoM PROFeNoM marked this pull request as ready for review April 23, 2026 14:17
@PROFeNoM PROFeNoM requested review from a team as code owners April 23, 2026 14:17
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a39f390575

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js Outdated
@PROFeNoM PROFeNoM force-pushed the alex/MLOB-3509_bedrock-converse-methods-support branch from cc5a8f8 to d6bf034 Compare April 23, 2026 14:41
Comment on lines +124 to +136
const tracesPromise = agent.assertFirstTraceSpan({
meta: {
'aws.operation': 'converseStream',
'aws.bedrock.request.model': converseRequest.modelId.split('.')[1],
'aws.bedrock.request.model_provider': converseRequest.provider.toLowerCase(),
},
})

const result = await bedrockRuntimeClient.send(command)
for await (const _event of result.stream) { // eslint-disable-line no-unused-vars
// drain
}
await tracesPromise
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is a very small risk for having an unhandled rejection in case the stream fails.

This is unlikely and the fix is not nice: the tracePromise needs a catch handler above to not trigger the unhandled rejection.

@PROFeNoM PROFeNoM force-pushed the alex/MLOB-3509_bedrock-converse-methods-support branch 2 times, most recently from 9654ef9 to 5c97b1b Compare April 27, 2026 12:23
Adds APM span tagging and LLMObs enrichment for ConverseCommand and
ConverseStreamCommand on the Bedrock runtime client.

- tracing: extend operation whitelist
- utils: Converse request/response parsers plus stream-event aggregator
  for text + toolUse content blocks
- instrumentation: wrap `result.stream` as well as `result.body` so
  ConverseStream events flow through the streamed-chunk channel
- llmobs: branch on Converse in setLLMObsTags, surface stop_reason,
  emit structured output messages with tool_calls
- tests + VCR cassettes for system + tool path against Claude 3 Haiku

Ref: MLOB-3509
@PROFeNoM PROFeNoM force-pushed the alex/llmobs-tool-definitions branch from 1cd9ac5 to 1451e6c Compare April 27, 2026 13:20
…ult variants

Emit placeholder markers instead of dropping data silently:
- buildToolResult: non-text/non-json content items (document, image,
  video, searchResult) now produce `[<type> content]` instead of ''.
- buildConverseStreamGeneration: ContentBlockDelta events that are
  neither text nor toolUse (citation, image, reasoningContent,
  toolResult) now append `[Unsupported delta: <type>]` at their
  contentBlockIndex, so streamed responses using these variants no
  longer tag as empty output.

Ref: MLOB-3509
When a ConverseCommand or ConverseStreamCommand request includes a
`toolConfig.tools` array, map each `toolSpec` to LLMObs'
`{ name, description, schema }` shape and attach it to the span via
the new `tagToolDefinitions` API (see parent PR #8082).

- utils.js: new `extractConverseToolDefinitions` helper.
- llmobs/plugins/bedrockruntime.js: call it on Converse requests.
- spec: assert on the `toolDefinitions` round-trip.

Brings Node parity with dd-trace-py's Bedrock Converse integration
for the "Available Tools" UI section.

Ref: MLOB-3509
Malformed Converse payloads (e.g. `content: [null]`, `tools: [null]`)
would throw in `extractMessagesFromConverseContent` and
`extractConverseToolDefinitions`. Plugin subscriber exceptions trigger
auto-disable via `this.configure(false)`, turning off Bedrock LLMObs
for the rest of the process.

Skip invalid entries instead of throwing, matching the defensive
optional-chaining style already used by this file's InvokeModel
extractors.

Flagged by Codex adversarial review.
Align `buildToolResult` fallback wording with dd-trace-py's
`[Unsupported content type(s): <keys>]` format (see
`dd-trace-py/ddtrace/llmobs/_integrations/utils.py:266`).
Dropping the `[Unsupported delta: <type>]` fallback: it spammed the
output on every delta event (thinking-mode responses stream
reasoningContent as dozens of small deltas) and had no parity in
dd-trace-py. Match Python's silent-drop behavior for unhandled
ContentBlockDelta variants; the non-stream `[Unsupported content
type: ...]` markers already cover the debuggability need.
- `buildToolCall`: extract `parseToolInput` helper so `args` is set
  once in a single expression (inputStr wins over input explicitly).
- `buildToolResult`: extract `resolveToolResultItem` helper so the
  three cases (text / json / unsupported) are a flat if-chain instead
  of a nested ternary inside `.map`.
- `extractConverseToolDefinitions`: rename `defs` -> `toolDefinitions`,
  `spec` -> `toolSpec` to match the AWS SDK field names.
- `extractTextAndResponseReasonConverse`: rename the raw-response-
  handle `message` -> `outputMessage` so it no longer collides with
  the `messages` LLMObs array built right after.
The original `setLLMObsTags` mixed InvokeModel and Converse flows,
branching on `isConverse` / `isStream` in three places (request
params, generation extraction, stop_reason emission). Split into
two specialized methods so each path reads top-to-bottom:

- `#tagConverseSpan` owns the Converse-only concerns:
  tool_definitions, stop_reason metadata, Converse request/response
  extractors.
- `#tagInvokeModelSpan` owns the per-provider InvokeModel extractors.
- `#tagCommon` handles the truly shared tagging (temperature,
  max_tokens, I/O, metrics) with no shape branching.

To make `#tagCommon` shape-agnostic, `Generation` now auto-derives
its structured `messages` field from `{message, role}` when the
caller doesn't pass `messages` explicitly. The plugin reads
`generation.messages` unconditionally; InvokeModel behavior is
unchanged (same `[{content, role}]` wrapper, just produced at the
class boundary instead of inline at the tag call site).

Also: `stop_reason` moves out of the shared metadata path into
`#tagConverseSpan`. It was leaking onto InvokeModel spans through
the shared code; this restores pre-PR behavior for InvokeModel.
…ctors

Stream events describe the same content-block structure as the non-
stream response, just chunked across start/delta events. Reassemble
the stream chunks into a normalized `ContentBlock[]` shape and reuse
the non-stream extractor via a shared `toOutputMessages` helper.

- `buildConverseStreamGeneration`: replaces the parallel
  `textByIdx` / `toolByIdx` maps (and the dedicated
  `assembleStreamMessage` helper, now deleted) with a single
  `blocksByIdx` Map of reassembled blocks. One code path for
  content-block -> output-message regardless of stream vs non-stream.
- `toOutputMessages`: new helper that wraps
  `extractMessagesFromConverseContent` with the "always emit at least
  one output message" fallback, shared by both paths.
- `extractMessagesFromConverseContent`: early-return when nothing
  extracted; end-of-function double-check replaced with a plain
  `return [message]`.
- `extractTextAndResponseReason` (InvokeModel Amazon path): replace
  the four explicit token fields with `...buildUsage(body.usage)`.
  Incidentally fixes a latent bug where `cacheRead/WriteInputTokenCount`
  were passed to a `Generation` constructor that only destructures
  `cacheRead/WriteTokens`, silently dropping those values.
The ContentBlockDelta union has 6 variants (text, toolUse, toolResult,
reasoningContent, citation, image). Only text and toolUse.input were
decoded; the rest are intended to be silently ignored per Python
parity.

The previous implementation leaked empty `{}` blocks into
`blocksByIdx` when a delta arrived at a new contentBlockIndex with no
matching handler (e.g. Claude 3.7 thinking mode emitting
reasoningContent deltas). Downstream,
`extractMessagesFromConverseContent` saw the empty block and emitted
`[Unsupported content type: unknown]`.

Fix: only write into `blocksByIdx` inside the branches that actually
populate a block. Unhandled deltas now leave the Map untouched.

Flagged by review on utils.js:647-651.
If `send` (or stream iteration) rejects before the test reaches
`await tracesPromise`, the trace-assertion promise stays pending and
later settles with no observer, triggering an unhandled-rejection
warning in some Node versions / CI configs.

Attach a no-op `.catch()` to the original `tracesPromise` right after
creation. This satisfies the "promise was handled" check without
mutating the source — the subsequent `await tracesPromise` still
sees the rejection and propagates it to the test runner.

Flagged on review.
…sent

`tagToolDefinitions` now logs a failure for non-array / empty input
(see #8082). The Bedrock plugin previously called it unconditionally
on every Converse request — `extractConverseToolDefinitions` returns
`[]` when there's no `toolConfig`, which would now produce noisy
`invalid_tool_definitions` logs on every tool-less Converse call.
Gate the call on `length > 0`.
@PROFeNoM PROFeNoM force-pushed the alex/MLOB-3509_bedrock-converse-methods-support branch from 5c97b1b to ad1cd0f Compare April 27, 2026 13:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants