diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/genai/GenAiAttributesExtractor.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/genai/GenAiAttributesExtractor.java index 263f9c99fcb5..091cd048276f 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/genai/GenAiAttributesExtractor.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/genai/GenAiAttributesExtractor.java @@ -95,10 +95,10 @@ public void onEnd( REQUEST request, @Nullable RESPONSE response, @Nullable Throwable error) { - internalSet( - attributes, - GEN_AI_RESPONSE_FINISH_REASONS, - getter.getResponseFinishReasons(request, response)); + List finishReasons = getter.getResponseFinishReasons(request, response); + if (finishReasons != null && !finishReasons.isEmpty()) { + attributes.put(GEN_AI_RESPONSE_FINISH_REASONS, finishReasons); + } internalSet(attributes, GEN_AI_RESPONSE_ID, getter.getResponseId(request, response)); internalSet(attributes, GEN_AI_RESPONSE_MODEL, getter.getResponseModel(request, response)); internalSet( diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/build.gradle.kts index ed221afb896f..6025d77ad53a 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/build.gradle.kts @@ -103,6 +103,11 @@ dependencies { library("software.amazon.awssdk:aws-core:2.2.0") library("software.amazon.awssdk:sqs:2.2.0") + // Don't use library to make sure base test is run with the floor version. + // bedrock runtime is tested separately in testBedrockRuntime. + // First release with Converse API + compileOnly("software.amazon.awssdk:bedrockruntime:2.25.63") + testImplementation(project(":instrumentation:aws-sdk:aws-sdk-2.2:testing")) testImplementation("io.opentelemetry.contrib:opentelemetry-aws-xray-propagator") diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v2_2/BedrockRuntimeInstrumentationModule.java b/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v2_2/BedrockRuntimeInstrumentationModule.java index 1abca42eaced..ad5c6a307bca 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v2_2/BedrockRuntimeInstrumentationModule.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v2_2/BedrockRuntimeInstrumentationModule.java @@ -11,7 +11,10 @@ import com.google.auto.service.AutoService; import io.opentelemetry.instrumentation.awssdk.v2_2.internal.BedrockRuntimeAdviceBridge; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import java.util.ArrayList; +import java.util.List; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatcher; @@ -27,6 +30,13 @@ public ElementMatcher.Junction classLoaderMatcher() { return hasClassesNamed("software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeClient"); } + @Override + public List typeInstrumentations() { + List instrumentations = new ArrayList<>(super.typeInstrumentations()); + instrumentations.add(new DefaultBedrockRuntimeAsyncClientBuilderInstrumentation()); + return instrumentations; + } + @Override public void doTransform(TypeTransformer transformer) { transformer.applyAdviceToMethod( diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v2_2/DefaultBedrockRuntimeAsyncClientBuilderInstrumentation.java b/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v2_2/DefaultBedrockRuntimeAsyncClientBuilderInstrumentation.java new file mode 100644 index 000000000000..638d308aace5 --- /dev/null +++ b/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v2_2/DefaultBedrockRuntimeAsyncClientBuilderInstrumentation.java @@ -0,0 +1,41 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.awssdk.v2_2; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +import io.opentelemetry.instrumentation.awssdk.v2_2.autoconfigure.AwsSdkSingletons; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeAsyncClient; + +public class DefaultBedrockRuntimeAsyncClientBuilderInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named( + "software.amazon.awssdk.services.bedrockruntime.DefaultBedrockRuntimeAsyncClientBuilder"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("buildClient"), this.getClass().getName() + "$BuildClientAdvice"); + } + + @SuppressWarnings("unused") + public static class BuildClientAdvice { + + @Advice.OnMethodExit(suppress = Throwable.class) + public static void methodExit( + @Advice.Return(readOnly = false) BedrockRuntimeAsyncClient client) { + client = AwsSdkSingletons.telemetry().wrapBedrockRuntimeClient(client); + } + } +} diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/testBedrockRuntime/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/Aws2BedrockRuntimeTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/testBedrockRuntime/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/Aws2BedrockRuntimeTest.java index e4c4ced070a6..31c0b3912a14 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/testBedrockRuntime/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/Aws2BedrockRuntimeTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/testBedrockRuntime/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/Aws2BedrockRuntimeTest.java @@ -10,6 +10,7 @@ import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import org.junit.jupiter.api.extension.RegisterExtension; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeAsyncClient; class Aws2BedrockRuntimeTest extends AbstractAws2BedrockRuntimeTest { @RegisterExtension @@ -24,4 +25,10 @@ protected InstrumentationExtension getTesting() { protected ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder() { return ClientOverrideConfiguration.builder(); } + + @Override + protected BedrockRuntimeAsyncClient configureBedrockRuntimeClient( + BedrockRuntimeAsyncClient client) { + return client; + } } diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkTelemetry.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkTelemetry.java index ef12069c928f..207b12508154 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkTelemetry.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkTelemetry.java @@ -10,6 +10,7 @@ import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.awssdk.v2_2.internal.AwsSdkInstrumenterFactory; +import io.opentelemetry.instrumentation.awssdk.v2_2.internal.BedrockRuntimeImpl; import io.opentelemetry.instrumentation.awssdk.v2_2.internal.Response; import io.opentelemetry.instrumentation.awssdk.v2_2.internal.SqsImpl; import io.opentelemetry.instrumentation.awssdk.v2_2.internal.SqsProcessRequest; @@ -21,6 +22,7 @@ import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeAsyncClient; import software.amazon.awssdk.services.sqs.SqsAsyncClient; import software.amazon.awssdk.services.sqs.SqsClient; @@ -29,6 +31,14 @@ * ExecutionInterceptor} returned by {@link #newExecutionInterceptor()} with an SDK client to have * all requests traced. * + *

Certain services additionally require wrapping the SDK client itself: + * + *

    + *
  • SQSClient - {@link #wrap(SqsClient)} + *
  • SQSAsyncClient - {@link #wrap(SqsAsyncClient)} + *
  • BedrockRuntimeAsyncClient - {@link #wrapBedrockRuntimeClient(BedrockRuntimeAsyncClient)} + *
+ * *
{@code
  * DynamoDbClient dynamoDb = DynamoDbClient.builder()
  *     .overrideConfiguration(ClientOverrideConfiguration.builder()
@@ -134,4 +144,14 @@ public SqsClient wrap(SqsClient sqsClient) {
   public SqsAsyncClient wrap(SqsAsyncClient sqsClient) {
     return SqsImpl.wrap(sqsClient);
   }
+
+  /**
+   * Construct a new tracing-enabled {@link BedrockRuntimeAsyncClient} using the provided {@link
+   * BedrockRuntimeAsyncClient} instance.
+   */
+  @NoMuzzle
+  public BedrockRuntimeAsyncClient wrapBedrockRuntimeClient(
+      BedrockRuntimeAsyncClient bedrockClient) {
+    return BedrockRuntimeImpl.wrap(bedrockClient);
+  }
 }
diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeAccess.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeAccess.java
index 6bf10ae48620..41e74a85a45f 100644
--- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeAccess.java
+++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeAccess.java
@@ -76,19 +76,19 @@ static List getStopSequences(SdkRequest request) {
 
   @Nullable
   @NoMuzzle
-  static String getStopReason(SdkResponse response) {
-    return enabled ? BedrockRuntimeImpl.getStopReason(response) : null;
+  static List getStopReasons(Response response) {
+    return enabled ? BedrockRuntimeImpl.getStopReasons(response) : null;
   }
 
   @Nullable
   @NoMuzzle
-  static Long getUsageInputTokens(SdkResponse response) {
+  static Long getUsageInputTokens(Response response) {
     return enabled ? BedrockRuntimeImpl.getUsageInputTokens(response) : null;
   }
 
   @Nullable
   @NoMuzzle
-  static Long getUsageOutputTokens(SdkResponse response) {
+  static Long getUsageOutputTokens(Response response) {
     return enabled ? BedrockRuntimeImpl.getUsageOutputTokens(response) : null;
   }
 
diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeAttributesGetter.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeAttributesGetter.java
index d91b5a47b009..e75c42db6cd5 100644
--- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeAttributesGetter.java
+++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeAttributesGetter.java
@@ -9,7 +9,7 @@
 import static java.util.Collections.emptyList;
 
 import io.opentelemetry.instrumentation.api.incubator.semconv.genai.GenAiAttributesGetter;
-import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import javax.annotation.Nullable;
 import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
@@ -38,6 +38,8 @@ public String getOperationName(ExecutionAttributes executionAttributes) {
     if (operation != null) {
       switch (operation) {
         case "Converse":
+        // fallthrough
+        case "ConverseStream":
           return GenAiOperationNameIncubatingValues.CHAT;
         default:
           return null;
@@ -120,11 +122,11 @@ public List getResponseFinishReasons(
     if (response == null) {
       return emptyList();
     }
-    String stopReason = BedrockRuntimeAccess.getStopReason(response.getSdkResponse());
-    if (stopReason == null) {
-      return emptyList();
+    List stopReasons = BedrockRuntimeAccess.getStopReasons(response);
+    if (stopReasons == null) {
+      return Collections.emptyList();
     }
-    return Arrays.asList(stopReason);
+    return stopReasons;
   }
 
   @Nullable
@@ -146,7 +148,7 @@ public Long getUsageInputTokens(
     if (response == null) {
       return null;
     }
-    return BedrockRuntimeAccess.getUsageInputTokens(response.getSdkResponse());
+    return BedrockRuntimeAccess.getUsageInputTokens(response);
   }
 
   @Nullable
@@ -156,6 +158,6 @@ public Long getUsageOutputTokens(
     if (response == null) {
       return null;
     }
-    return BedrockRuntimeAccess.getUsageOutputTokens(response.getSdkResponse());
+    return BedrockRuntimeAccess.getUsageOutputTokens(response);
   }
 }
diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeImpl.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeImpl.java
index e48825ecbc66..121e2ad11f47 100644
--- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeImpl.java
+++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeImpl.java
@@ -12,28 +12,47 @@
 import io.opentelemetry.api.logs.LogRecordBuilder;
 import io.opentelemetry.api.logs.Logger;
 import io.opentelemetry.context.Context;
+import io.opentelemetry.context.ContextKey;
+import io.opentelemetry.context.ImplicitContextKeyed;
+import io.opentelemetry.context.Scope;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import javax.annotation.Nullable;
 import software.amazon.awssdk.core.SdkRequest;
 import software.amazon.awssdk.core.SdkResponse;
+import software.amazon.awssdk.core.async.SdkPublisher;
 import software.amazon.awssdk.core.document.Document;
 import software.amazon.awssdk.protocols.json.SdkJsonGenerator;
+import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeAsyncClient;
 import software.amazon.awssdk.services.bedrockruntime.model.ContentBlock;
 import software.amazon.awssdk.services.bedrockruntime.model.ConverseRequest;
 import software.amazon.awssdk.services.bedrockruntime.model.ConverseResponse;
+import software.amazon.awssdk.services.bedrockruntime.model.ConverseStreamMetadataEvent;
+import software.amazon.awssdk.services.bedrockruntime.model.ConverseStreamOutput;
+import software.amazon.awssdk.services.bedrockruntime.model.ConverseStreamRequest;
+import software.amazon.awssdk.services.bedrockruntime.model.ConverseStreamResponse;
+import software.amazon.awssdk.services.bedrockruntime.model.ConverseStreamResponseHandler;
 import software.amazon.awssdk.services.bedrockruntime.model.InferenceConfiguration;
 import software.amazon.awssdk.services.bedrockruntime.model.Message;
+import software.amazon.awssdk.services.bedrockruntime.model.MessageStopEvent;
 import software.amazon.awssdk.services.bedrockruntime.model.StopReason;
 import software.amazon.awssdk.services.bedrockruntime.model.TokenUsage;
 import software.amazon.awssdk.services.bedrockruntime.model.ToolResultContentBlock;
 import software.amazon.awssdk.services.bedrockruntime.model.ToolUseBlock;
 import software.amazon.awssdk.thirdparty.jackson.core.JsonFactory;
 
-final class BedrockRuntimeImpl {
+/**
+ * This class is internal and is hence not for public use. Its APIs are unstable and can change at
+ * any time.
+ */
+public final class BedrockRuntimeImpl {
   private BedrockRuntimeImpl() {}
 
   private static final AttributeKey EVENT_NAME = stringKey("event.name");
@@ -45,6 +64,9 @@ static boolean isBedrockRuntimeRequest(SdkRequest request) {
     if (request instanceof ConverseRequest) {
       return true;
     }
+    if (request instanceof ConverseStreamRequest) {
+      return true;
+    }
     return false;
   }
 
@@ -59,84 +81,121 @@ static boolean isBedrockRuntimeResponse(SdkResponse request) {
   static String getModelId(SdkRequest request) {
     if (request instanceof ConverseRequest) {
       return ((ConverseRequest) request).modelId();
+    } else if (request instanceof ConverseStreamRequest) {
+      return ((ConverseStreamRequest) request).modelId();
     }
     return null;
   }
 
   @Nullable
   static Long getMaxTokens(SdkRequest request) {
+    InferenceConfiguration config = null;
     if (request instanceof ConverseRequest) {
-      InferenceConfiguration config = ((ConverseRequest) request).inferenceConfig();
-      if (config != null) {
-        return integerToLong(config.maxTokens());
-      }
+      config = ((ConverseRequest) request).inferenceConfig();
+    } else if (request instanceof ConverseStreamRequest) {
+      config = ((ConverseStreamRequest) request).inferenceConfig();
+    }
+    if (config != null) {
+      return integerToLong(config.maxTokens());
     }
     return null;
   }
 
   @Nullable
   static Double getTemperature(SdkRequest request) {
+    InferenceConfiguration config = null;
     if (request instanceof ConverseRequest) {
-      InferenceConfiguration config = ((ConverseRequest) request).inferenceConfig();
-      if (config != null) {
-        return floatToDouble(config.temperature());
-      }
+      config = ((ConverseRequest) request).inferenceConfig();
+    } else if (request instanceof ConverseStreamRequest) {
+      config = ((ConverseStreamRequest) request).inferenceConfig();
+    }
+    if (config != null) {
+      return floatToDouble(config.temperature());
     }
     return null;
   }
 
   @Nullable
   static Double getTopP(SdkRequest request) {
+    InferenceConfiguration config = null;
     if (request instanceof ConverseRequest) {
-      InferenceConfiguration config = ((ConverseRequest) request).inferenceConfig();
-      if (config != null) {
-        return floatToDouble(config.topP());
-      }
+      config = ((ConverseRequest) request).inferenceConfig();
+    } else if (request instanceof ConverseStreamRequest) {
+      config = ((ConverseStreamRequest) request).inferenceConfig();
+    }
+    if (config != null) {
+      return floatToDouble(config.topP());
     }
     return null;
   }
 
   @Nullable
   static List getStopSequences(SdkRequest request) {
+    InferenceConfiguration config = null;
     if (request instanceof ConverseRequest) {
-      InferenceConfiguration config = ((ConverseRequest) request).inferenceConfig();
-      if (config != null) {
-        return config.stopSequences();
-      }
+      config = ((ConverseRequest) request).inferenceConfig();
+    } else if (request instanceof ConverseStreamRequest) {
+      config = ((ConverseStreamRequest) request).inferenceConfig();
+    }
+    if (config != null) {
+      return config.stopSequences();
     }
     return null;
   }
 
   @Nullable
-  static String getStopReason(SdkResponse response) {
-    if (response instanceof ConverseResponse) {
-      StopReason reason = ((ConverseResponse) response).stopReason();
+  static List getStopReasons(Response response) {
+    SdkResponse sdkResponse = response.getSdkResponse();
+    if (sdkResponse instanceof ConverseResponse) {
+      StopReason reason = ((ConverseResponse) sdkResponse).stopReason();
       if (reason != null) {
-        return reason.toString();
+        return Collections.singletonList(reason.toString());
+      }
+    } else {
+      TracingConverseStreamResponseHandler streamHandler =
+          TracingConverseStreamResponseHandler.fromContext(response.otelContext());
+      if (streamHandler != null) {
+        return streamHandler.stopReasons;
       }
     }
     return null;
   }
 
   @Nullable
-  static Long getUsageInputTokens(SdkResponse response) {
-    if (response instanceof ConverseResponse) {
-      TokenUsage usage = ((ConverseResponse) response).usage();
-      if (usage != null) {
-        return integerToLong(usage.inputTokens());
+  static Long getUsageInputTokens(Response response) {
+    SdkResponse sdkResponse = response.getSdkResponse();
+    TokenUsage usage = null;
+    if (sdkResponse instanceof ConverseResponse) {
+      usage = ((ConverseResponse) sdkResponse).usage();
+    } else {
+      TracingConverseStreamResponseHandler streamHandler =
+          TracingConverseStreamResponseHandler.fromContext(response.otelContext());
+      if (streamHandler != null) {
+        usage = streamHandler.usage;
       }
     }
+    if (usage != null) {
+      return integerToLong(usage.inputTokens());
+    }
     return null;
   }
 
   @Nullable
-  static Long getUsageOutputTokens(SdkResponse response) {
-    if (response instanceof ConverseResponse) {
-      TokenUsage usage = ((ConverseResponse) response).usage();
-      if (usage != null) {
-        return integerToLong(usage.outputTokens());
+  static Long getUsageOutputTokens(Response response) {
+    SdkResponse sdkResponse = response.getSdkResponse();
+    TokenUsage usage = null;
+    if (sdkResponse instanceof ConverseResponse) {
+      usage = ((ConverseResponse) sdkResponse).usage();
+    } else {
+      TracingConverseStreamResponseHandler streamHandler =
+          TracingConverseStreamResponseHandler.fromContext(response.otelContext());
+      if (streamHandler != null) {
+        usage = streamHandler.usage;
       }
     }
+    if (usage != null) {
+      return integerToLong(usage.outputTokens());
+    }
     return null;
   }
 
@@ -211,6 +270,101 @@ private static Double floatToDouble(Float value) {
     return Double.valueOf(value);
   }
 
+  public static BedrockRuntimeAsyncClient wrap(BedrockRuntimeAsyncClient asyncClient) {
+    // proxy BedrockRuntimeAsyncClient so we can wrap the subscriber to converseStream to capture
+    // events.
+    return (BedrockRuntimeAsyncClient)
+        Proxy.newProxyInstance(
+            asyncClient.getClass().getClassLoader(),
+            new Class[] {BedrockRuntimeAsyncClient.class},
+            (proxy, method, args) -> {
+              if (method.getName().equals("converseStream")
+                  && args.length >= 2
+                  && args[1] instanceof ConverseStreamResponseHandler) {
+                TracingConverseStreamResponseHandler wrapped =
+                    new TracingConverseStreamResponseHandler(
+                        (ConverseStreamResponseHandler) args[1]);
+                args[1] = wrapped;
+                try (Scope ignored = wrapped.makeCurrent()) {
+                  return invokeProxyMethod(method, asyncClient, args);
+                }
+              }
+              return invokeProxyMethod(method, asyncClient, args);
+            });
+  }
+
+  private static Object invokeProxyMethod(Method method, Object target, Object[] args)
+      throws Throwable {
+    try {
+      return method.invoke(target, args);
+    } catch (InvocationTargetException exception) {
+      throw exception.getCause();
+    }
+  }
+
+  /**
+   * This class is internal and is hence not for public use. Its APIs are unstable and can change at
+   * any time.
+   */
+  public static class TracingConverseStreamResponseHandler
+      implements ConverseStreamResponseHandler, ImplicitContextKeyed {
+
+    @Nullable
+    public static TracingConverseStreamResponseHandler fromContext(Context context) {
+      return context.get(KEY);
+    }
+
+    private static final ContextKey KEY =
+        ContextKey.named("bedrock-runtime-converse-stream-response-handler");
+
+    private final ConverseStreamResponseHandler delegate;
+
+    List stopReasons;
+    TokenUsage usage;
+
+    TracingConverseStreamResponseHandler(ConverseStreamResponseHandler delegate) {
+      this.delegate = delegate;
+    }
+
+    @Override
+    public void responseReceived(ConverseStreamResponse converseStreamResponse) {
+      delegate.responseReceived(converseStreamResponse);
+    }
+
+    @Override
+    public void onEventStream(SdkPublisher sdkPublisher) {
+      delegate.onEventStream(
+          sdkPublisher.map(
+              event -> {
+                if (event instanceof MessageStopEvent) {
+                  if (stopReasons == null) {
+                    stopReasons = new ArrayList<>();
+                  }
+                  stopReasons.add(((MessageStopEvent) event).stopReasonAsString());
+                }
+                if (event instanceof ConverseStreamMetadataEvent) {
+                  usage = ((ConverseStreamMetadataEvent) event).usage();
+                }
+                return event;
+              }));
+    }
+
+    @Override
+    public void exceptionOccurred(Throwable throwable) {
+      delegate.exceptionOccurred(throwable);
+    }
+
+    @Override
+    public void complete() {
+      delegate.complete();
+    }
+
+    @Override
+    public Context storeInContext(Context context) {
+      return context.with(KEY, this);
+    }
+  }
+
   private static LogRecordBuilder newEvent(Context otelContext, Logger eventLogger) {
     return eventLogger
         .logRecordBuilder()
diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/Response.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/Response.java
index ed298e118c31..2b9c76aaf754 100644
--- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/Response.java
+++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/Response.java
@@ -5,6 +5,7 @@
 
 package io.opentelemetry.instrumentation.awssdk.v2_2.internal;
 
+import io.opentelemetry.context.Context;
 import software.amazon.awssdk.core.SdkResponse;
 import software.amazon.awssdk.http.SdkHttpResponse;
 
@@ -15,14 +16,20 @@
 public final class Response {
   private final SdkHttpResponse sdkHttpResponse;
   private final SdkResponse sdkResponse;
+  private final Context otelContext;
 
   Response(SdkHttpResponse sdkHttpResponse) {
     this(sdkHttpResponse, null);
   }
 
   Response(SdkHttpResponse sdkHttpResponse, SdkResponse sdkResponse) {
+    this(sdkHttpResponse, sdkResponse, null);
+  }
+
+  Response(SdkHttpResponse sdkHttpResponse, SdkResponse sdkResponse, Context otelContext) {
     this.sdkHttpResponse = sdkHttpResponse;
     this.sdkResponse = sdkResponse;
+    this.otelContext = otelContext;
   }
 
   public SdkHttpResponse getSdkHttpResponse() {
@@ -32,4 +39,8 @@ public SdkHttpResponse getSdkHttpResponse() {
   public SdkResponse getSdkResponse() {
     return sdkResponse;
   }
+
+  public Context otelContext() {
+    return otelContext;
+  }
 }
diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/TracingExecutionInterceptor.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/TracingExecutionInterceptor.java
index eabf2bf22874..416f3f76c112 100644
--- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/TracingExecutionInterceptor.java
+++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/TracingExecutionInterceptor.java
@@ -377,7 +377,10 @@ public void afterExecution(
           executionAttributes, otelContext, Span.fromContext(otelContext), httpResponse);
       RequestSpanFinisher finisher = executionAttributes.getAttribute(REQUEST_FINISHER_ATTRIBUTE);
       finisher.finish(
-          otelContext, executionAttributes, new Response(httpResponse, context.response()), null);
+          otelContext,
+          executionAttributes,
+          new Response(httpResponse, context.response(), otelContext),
+          null);
     }
     clearAttributes(executionAttributes);
   }
diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/testBedrockRuntime/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/Aws2BedrockRuntimeTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/testBedrockRuntime/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/Aws2BedrockRuntimeTest.java
index 65af3a7c1a85..31b6716aab52 100644
--- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/testBedrockRuntime/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/Aws2BedrockRuntimeTest.java
+++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/testBedrockRuntime/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/Aws2BedrockRuntimeTest.java
@@ -36,6 +36,7 @@
 import org.junit.jupiter.api.extension.RegisterExtension;
 import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
 import software.amazon.awssdk.core.document.Document;
+import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeAsyncClient;
 import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeClient;
 import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeClientBuilder;
 import software.amazon.awssdk.services.bedrockruntime.model.ContentBlock;
@@ -77,6 +78,12 @@ protected ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder
         .addExecutionInterceptor(telemetry.newExecutionInterceptor());
   }
 
+  @Override
+  protected BedrockRuntimeAsyncClient configureBedrockRuntimeClient(
+      BedrockRuntimeAsyncClient client) {
+    return telemetry.wrapBedrockRuntimeClient(client);
+  }
+
   @Test
   void testConverseToolCallNoMessageContent() {
     BedrockRuntimeClientBuilder builder = BedrockRuntimeClient.builder();
diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2BedrockRuntimeTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2BedrockRuntimeTest.java
index 5652c45d5e97..8d6d6aae56bc 100644
--- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2BedrockRuntimeTest.java
+++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2BedrockRuntimeTest.java
@@ -35,19 +35,25 @@
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.ExecutionException;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
 import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
+import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder;
 import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
 import software.amazon.awssdk.core.document.Document;
 import software.amazon.awssdk.regions.Region;
+import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeAsyncClient;
+import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeAsyncClientBuilder;
 import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeClient;
 import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeClientBuilder;
 import software.amazon.awssdk.services.bedrockruntime.model.ContentBlock;
 import software.amazon.awssdk.services.bedrockruntime.model.ConversationRole;
 import software.amazon.awssdk.services.bedrockruntime.model.ConverseRequest;
 import software.amazon.awssdk.services.bedrockruntime.model.ConverseResponse;
+import software.amazon.awssdk.services.bedrockruntime.model.ConverseStreamRequest;
+import software.amazon.awssdk.services.bedrockruntime.model.ConverseStreamResponseHandler;
 import software.amazon.awssdk.services.bedrockruntime.model.InferenceConfiguration;
 import software.amazon.awssdk.services.bedrockruntime.model.Message;
 import software.amazon.awssdk.services.bedrockruntime.model.Tool;
@@ -70,7 +76,10 @@ public abstract class AbstractAws2BedrockRuntimeTest {
 
   protected abstract ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder();
 
-  protected static void configureClient(BedrockRuntimeClientBuilder builder) {
+  protected abstract BedrockRuntimeAsyncClient configureBedrockRuntimeClient(
+      BedrockRuntimeAsyncClient client);
+
+  protected static void configureClient(AwsClientBuilder builder) {
     builder
         .region(Region.US_EAST_1)
         .endpointOverride(URI.create("http://localhost:" + recording.getPort()));
@@ -736,4 +745,195 @@ private static ToolConfiguration currentWeatherToolConfig() {
                 .build())
         .build();
   }
+
+  @Test
+  void testConverseStream() throws InterruptedException, ExecutionException {
+    BedrockRuntimeAsyncClientBuilder builder = BedrockRuntimeAsyncClient.builder();
+    builder.overrideConfiguration(createOverrideConfigurationBuilder().build());
+    configureClient(builder);
+    BedrockRuntimeAsyncClient client = configureBedrockRuntimeClient(builder.build());
+
+    String modelId = "amazon.titan-text-lite-v1";
+
+    List responseChunks = new ArrayList<>();
+
+    ConverseStreamResponseHandler responseHandler =
+        ConverseStreamResponseHandler.builder()
+            .subscriber(
+                ConverseStreamResponseHandler.Visitor.builder()
+                    .onContentBlockDelta(
+                        chunk -> {
+                          responseChunks.add(chunk.delta().text());
+                        })
+                    .build())
+            .build();
+
+    client
+        .converseStream(
+            ConverseStreamRequest.builder()
+                .modelId(modelId)
+                .messages(
+                    Message.builder()
+                        .role(ConversationRole.USER)
+                        .content(ContentBlock.fromText("Say this is a test"))
+                        .build())
+                .build(),
+            responseHandler)
+        .get();
+
+    assertThat(String.join("", responseChunks)).isEqualTo("\"Test, test\"");
+
+    getTesting()
+        .waitAndAssertTraces(
+            trace ->
+                trace.hasSpansSatisfyingExactly(
+                    span ->
+                        span.hasName("chat amazon.titan-text-lite-v1")
+                            .hasKind(SpanKind.CLIENT)
+                            .hasAttributesSatisfying(
+                                equalTo(
+                                    GEN_AI_SYSTEM,
+                                    GenAiIncubatingAttributes.GenAiSystemIncubatingValues
+                                        .AWS_BEDROCK),
+                                equalTo(
+                                    GEN_AI_OPERATION_NAME,
+                                    GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues
+                                        .CHAT),
+                                equalTo(GEN_AI_REQUEST_MODEL, modelId),
+                                equalTo(GEN_AI_USAGE_INPUT_TOKENS, 8),
+                                equalTo(GEN_AI_USAGE_OUTPUT_TOKENS, 10),
+                                equalTo(GEN_AI_RESPONSE_FINISH_REASONS, asList("end_turn")))));
+
+    getTesting()
+        .waitAndAssertMetrics(
+            INSTRUMENTATION_NAME,
+            metric ->
+                metric
+                    .hasName("gen_ai.client.token.usage")
+                    .hasUnit("{token}")
+                    .hasDescription("Measures number of input and output tokens used")
+                    .hasHistogramSatisfying(
+                        histogram ->
+                            histogram.hasPointsSatisfying(
+                                point ->
+                                    point
+                                        .hasSum(8)
+                                        .hasCount(1)
+                                        .hasAttributesSatisfyingExactly(
+                                            equalTo(GEN_AI_SYSTEM, AWS_BEDROCK),
+                                            equalTo(
+                                                GEN_AI_TOKEN_TYPE,
+                                                GenAiIncubatingAttributes
+                                                    .GenAiTokenTypeIncubatingValues.INPUT),
+                                            equalTo(
+                                                GEN_AI_OPERATION_NAME,
+                                                GenAiIncubatingAttributes
+                                                    .GenAiOperationNameIncubatingValues.CHAT),
+                                            equalTo(GEN_AI_REQUEST_MODEL, modelId)),
+                                point ->
+                                    point
+                                        .hasSum(10)
+                                        .hasCount(1)
+                                        .hasAttributesSatisfyingExactly(
+                                            equalTo(GEN_AI_SYSTEM, AWS_BEDROCK),
+                                            equalTo(
+                                                GEN_AI_TOKEN_TYPE,
+                                                GenAiIncubatingAttributes
+                                                    .GenAiTokenTypeIncubatingValues.COMPLETION),
+                                            equalTo(
+                                                GEN_AI_OPERATION_NAME,
+                                                GenAiIncubatingAttributes
+                                                    .GenAiOperationNameIncubatingValues.CHAT),
+                                            equalTo(GEN_AI_REQUEST_MODEL, modelId)))),
+            metric ->
+                metric
+                    .hasName("gen_ai.client.operation.duration")
+                    .hasUnit("s")
+                    .hasDescription("GenAI operation duration")
+                    .hasHistogramSatisfying(
+                        histogram ->
+                            histogram.hasPointsSatisfying(
+                                point ->
+                                    point
+                                        .hasSumGreaterThan(0.0)
+                                        .hasAttributesSatisfyingExactly(
+                                            equalTo(GEN_AI_SYSTEM, AWS_BEDROCK),
+                                            equalTo(
+                                                GEN_AI_OPERATION_NAME,
+                                                GenAiIncubatingAttributes
+                                                    .GenAiOperationNameIncubatingValues.CHAT),
+                                            equalTo(GEN_AI_REQUEST_MODEL, modelId)))));
+  }
+
+  @Test
+  void testConverseStreamOptions() throws InterruptedException, ExecutionException {
+    BedrockRuntimeAsyncClientBuilder builder = BedrockRuntimeAsyncClient.builder();
+    builder.overrideConfiguration(createOverrideConfigurationBuilder().build());
+    configureClient(builder);
+    BedrockRuntimeAsyncClient client = configureBedrockRuntimeClient(builder.build());
+
+    String modelId = "amazon.titan-text-lite-v1";
+
+    List responseChunks = new ArrayList<>();
+
+    ConverseStreamResponseHandler responseHandler =
+        ConverseStreamResponseHandler.builder()
+            .subscriber(
+                ConverseStreamResponseHandler.Visitor.builder()
+                    .onContentBlockDelta(
+                        chunk -> {
+                          responseChunks.add(chunk.delta().text());
+                        })
+                    .build())
+            .build();
+
+    client
+        .converseStream(
+            ConverseStreamRequest.builder()
+                .modelId(modelId)
+                .messages(
+                    Message.builder()
+                        .role(ConversationRole.USER)
+                        .content(ContentBlock.fromText("Say this is a test"))
+                        .build())
+                .inferenceConfig(
+                    InferenceConfiguration.builder()
+                        .maxTokens(5)
+                        .temperature(0.8f)
+                        .topP(1f)
+                        .stopSequences("|")
+                        .build())
+                .build(),
+            responseHandler)
+        .get();
+
+    assertThat(String.join("", responseChunks)).isEqualTo("This model");
+
+    getTesting()
+        .waitAndAssertTraces(
+            trace ->
+                trace.hasSpansSatisfyingExactly(
+                    span ->
+                        span.hasName("chat amazon.titan-text-lite-v1")
+                            .hasKind(SpanKind.CLIENT)
+                            .hasAttributesSatisfying(
+                                equalTo(
+                                    GEN_AI_SYSTEM,
+                                    GenAiIncubatingAttributes.GenAiSystemIncubatingValues
+                                        .AWS_BEDROCK),
+                                equalTo(
+                                    GEN_AI_OPERATION_NAME,
+                                    GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues
+                                        .CHAT),
+                                equalTo(GEN_AI_REQUEST_MODEL, modelId),
+                                equalTo(GEN_AI_REQUEST_MAX_TOKENS, 5),
+                                satisfies(
+                                    GEN_AI_REQUEST_TEMPERATURE,
+                                    temp -> temp.isCloseTo(0.8, within(0.0001))),
+                                equalTo(GEN_AI_REQUEST_TOP_P, 1.0),
+                                equalTo(GEN_AI_REQUEST_STOP_SEQUENCES, asList("|")),
+                                equalTo(GEN_AI_USAGE_INPUT_TOKENS, 8),
+                                equalTo(GEN_AI_USAGE_OUTPUT_TOKENS, 5),
+                                equalTo(GEN_AI_RESPONSE_FINISH_REASONS, asList("max_tokens")))));
+  }
 }
diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/FixedHostAwsV4AuthScheme.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/FixedHostAwsV4AuthScheme.java
index 681d5fe92ec6..032c01790d0a 100644
--- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/FixedHostAwsV4AuthScheme.java
+++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/FixedHostAwsV4AuthScheme.java
@@ -75,8 +75,24 @@ public SignedRequest sign(SignRequest request)
     @Override
     public CompletableFuture signAsync(
         AsyncSignRequest request) {
-      // TODO: Implement
-      return null;
+      SdkHttpRequest original = request.request();
+      AsyncSignRequest override =
+          request.toBuilder()
+              .request(
+                  request.request().toBuilder().port(443).protocol("https").host(apiUrl).build())
+              .build();
+      return DEFAULT
+          .signAsync(override)
+          .thenApply(
+              signed ->
+                  signed.toBuilder()
+                      .request(
+                          signed.request().toBuilder()
+                              .protocol(original.protocol())
+                              .host(original.host())
+                              .port(original.port())
+                              .build())
+                      .build());
     }
   }
 }
diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/resources/mappings/io.opentelemetry.instrumentation.awssdk.v2_2.abstractaws2bedrockruntimetest.testconversestream.yaml b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/resources/mappings/io.opentelemetry.instrumentation.awssdk.v2_2.abstractaws2bedrockruntimetest.testconversestream.yaml
new file mode 100644
index 000000000000..2ac6fd472e5c
--- /dev/null
+++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/resources/mappings/io.opentelemetry.instrumentation.awssdk.v2_2.abstractaws2bedrockruntimetest.testconversestream.yaml
@@ -0,0 +1,56 @@
+---
+id: 56110546-3b1d-4466-aebd-369edf2293a1
+name: model_amazontitan-text-lite-v1_converse-stream
+request:
+  url: /model/amazon.titan-text-lite-v1/converse-stream
+  method: POST
+  bodyPatterns:
+  - equalToJson: |-
+      {
+        "messages" : [ {
+          "role" : "user",
+          "content" : [ {
+            "text" : "Say this is a test"
+          } ]
+        } ]
+      }
+    ignoreArrayOrder: false
+    ignoreExtraElements: false
+response:
+  status: 200
+  base64Body: AAAArAAAAFJVkJ0mCzpldmVudC10eXBlBwAMbWVzc2FnZVN0YXJ0DTpjb250ZW50LXR5cGUHABBhcHBsaWNhdGlvbi9qc29uDTptZXNzYWdlLXR5cGUHAAVldmVudHsicCI6ImFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6QUJDREVGR0hJSktMTU5PUFFSU1RVIiwicm9sZSI6ImFzc2lzdGFudCJ9mBqgYwAAAPIAAABXotkYAws6ZXZlbnQtdHlwZQcAEWNvbnRlbnRCbG9ja0RlbHRhDTpjb250ZW50LXR5cGUHABBhcHBsaWNhdGlvbi9qc29uDTptZXNzYWdlLXR5cGUHAAVldmVudHsiY29udGVudEJsb2NrSW5kZXgiOjAsImRlbHRhIjp7InRleHQiOiJIZWxsbyEgSSdtIHNvcnJ5LCBidXQgSSdtIG5vdCBzdXJlIHdoYXQgeW91IG1lYW4uIENhbiB5b3UgcGxlYXNlIGNsYXJpZnkgeW91ciBxdWVzdGlvbj8ifSwicCI6ImFiIn149OkZAAAAsQAAAFbKjQoMCzpldmVudC10eXBlBwAQY29udGVudEJsb2NrU3RvcA06Y29udGVudC10eXBlBwAQYXBwbGljYXRpb24vanNvbg06bWVzc2FnZS10eXBlBwAFZXZlbnR7ImNvbnRlbnRCbG9ja0luZGV4IjowLCJwIjoiYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTIn1DzHT/AAAAhQAAAFEASIHpCzpldmVudC10eXBlBwALbWVzc2FnZVN0b3ANOmNvbnRlbnQtdHlwZQcAEGFwcGxpY2F0aW9uL2pzb24NOm1lc3NhZ2UtdHlwZQcABWV2ZW50eyJwIjoiYWJjZCIsInN0b3BSZWFzb24iOiJlbmRfdHVybiJ9HPrdBQAAAOUAAABOFHL7UQs6ZXZlbnQtdHlwZQcACG1ldGFkYXRhDTpjb250ZW50LXR5cGUHABBhcHBsaWNhdGlvbi9qc29uDTptZXNzYWdlLXR5cGUHAAVldmVudHsibWV0cmljcyI6eyJsYXRlbmN5TXMiOjEyNzF9LCJwIjoiYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKSyIsInVzYWdlIjp7ImlucHV0VG9rZW5zIjo4LCJvdXRwdXRUb2tlbnMiOjI2LCJ0b3RhbFRva2VucyI6MzR9fS4T3UI=
+  headers:
+    Date: "Thu, 27 Feb 2025 03:57:58 GMT"
+    Content-Type: application/vnd.amazon.eventstream
+    x-amzn-RequestId: afce5691-7115-4abb-87a0-8dff8ed025f1
+uuid: 56110546-3b1d-4466-aebd-369edf2293a1
+persistent: true
+insertionIndex: 6
+---
+id: 82987a12-fd54-4e35-a1d5-c19780fa69a5
+name: model_amazontitan-text-lite-v1_converse-stream
+request:
+  url: /model/amazon.titan-text-lite-v1/converse-stream
+  method: POST
+  bodyPatterns:
+  - equalToJson: |-
+      {
+        "messages" : [ {
+          "role" : "user",
+          "content" : [ {
+            "text" : "Say this is a test"
+          } ]
+        } ]
+      }
+    ignoreArrayOrder: false
+    ignoreExtraElements: false
+response:
+  status: 200
+  base64Body: AAAAiwAAAFImcW4yCzpldmVudC10eXBlBwAMbWVzc2FnZVN0YXJ0DTpjb250ZW50LXR5cGUHABBhcHBsaWNhdGlvbi9qc29uDTptZXNzYWdlLXR5cGUHAAVldmVudHsicCI6ImFiY2RlZmdoaWprbG1uIiwicm9sZSI6ImFzc2lzdGFudCJ9uwonDAAAAK4AAABXXzo6yQs6ZXZlbnQtdHlwZQcAEWNvbnRlbnRCbG9ja0RlbHRhDTpjb250ZW50LXR5cGUHABBhcHBsaWNhdGlvbi9qc29uDTptZXNzYWdlLXR5cGUHAAVldmVudHsiY29udGVudEJsb2NrSW5kZXgiOjAsImRlbHRhIjp7InRleHQiOiJcIlRlc3QsIHRlc3RcIiJ9LCJwIjoiYWJjZGVmZyJ9t6q0lwAAALUAAABWPw2szAs6ZXZlbnQtdHlwZQcAEGNvbnRlbnRCbG9ja1N0b3ANOmNvbnRlbnQtdHlwZQcAEGFwcGxpY2F0aW9uL2pzb24NOm1lc3NhZ2UtdHlwZQcABWV2ZW50eyJjb250ZW50QmxvY2tJbmRleCI6MCwicCI6ImFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6QUJDREVGR0hJSktMTU5PUFFSU1RVVlcifY0Un70AAACNAAAAUTA4yigLOmV2ZW50LXR5cGUHAAttZXNzYWdlU3RvcA06Y29udGVudC10eXBlBwAQYXBwbGljYXRpb24vanNvbg06bWVzc2FnZS10eXBlBwAFZXZlbnR7InAiOiJhYmNkZWZnaGlqa2wiLCJzdG9wUmVhc29uIjoiZW5kX3R1cm4ifS7bVN0AAADbAAAATgpj/bYLOmV2ZW50LXR5cGUHAAhtZXRhZGF0YQ06Y29udGVudC10eXBlBwAQYXBwbGljYXRpb24vanNvbg06bWVzc2FnZS10eXBlBwAFZXZlbnR7Im1ldHJpY3MiOnsibGF0ZW5jeU1zIjo2MjV9LCJwIjoiYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQiIsInVzYWdlIjp7ImlucHV0VG9rZW5zIjo4LCJvdXRwdXRUb2tlbnMiOjEwLCJ0b3RhbFRva2VucyI6MTh9fSqyy9o=
+  headers:
+    Date: "Thu, 27 Feb 2025 03:58:37 GMT"
+    Content-Type: application/vnd.amazon.eventstream
+    x-amzn-RequestId: 5308774f-01a7-40f3-bf88-fc1d00338db9
+uuid: 82987a12-fd54-4e35-a1d5-c19780fa69a5
+persistent: true
+insertionIndex: 8
diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/resources/mappings/io.opentelemetry.instrumentation.awssdk.v2_2.abstractaws2bedrockruntimetest.testconversestreamoptions.yaml b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/resources/mappings/io.opentelemetry.instrumentation.awssdk.v2_2.abstractaws2bedrockruntimetest.testconversestreamoptions.yaml
new file mode 100644
index 000000000000..3602c628d060
--- /dev/null
+++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/resources/mappings/io.opentelemetry.instrumentation.awssdk.v2_2.abstractaws2bedrockruntimetest.testconversestreamoptions.yaml
@@ -0,0 +1,34 @@
+---
+id: 172ed60a-ae31-47f3-a114-3e7e54e5bc91
+name: model_amazontitan-text-lite-v1_converse-stream
+request:
+  url: /model/amazon.titan-text-lite-v1/converse-stream
+  method: POST
+  bodyPatterns:
+  - equalToJson: |-
+      {
+        "messages" : [ {
+          "role" : "user",
+          "content" : [ {
+            "text" : "Say this is a test"
+          } ]
+        } ],
+        "inferenceConfig" : {
+          "maxTokens" : 5,
+          "temperature" : 0.8,
+          "topP" : 1,
+          "stopSequences" : [ "|" ]
+        }
+      }
+    ignoreArrayOrder: false
+    ignoreExtraElements: false
+response:
+  status: 200
+  base64Body: AAAAkwAAAFJ24bJxCzpldmVudC10eXBlBwAMbWVzc2FnZVN0YXJ0DTpjb250ZW50LXR5cGUHABBhcHBsaWNhdGlvbi9qc29uDTptZXNzYWdlLXR5cGUHAAVldmVudHsicCI6ImFiY2RlZmdoaWprbG1ub3BxcnN0dXYiLCJyb2xlIjoiYXNzaXN0YW50In2AaQZuAAAA3gAAAFem6NoGCzpldmVudC10eXBlBwARY29udGVudEJsb2NrRGVsdGENOmNvbnRlbnQtdHlwZQcAEGFwcGxpY2F0aW9uL2pzb24NOm1lc3NhZ2UtdHlwZQcABWV2ZW50eyJjb250ZW50QmxvY2tJbmRleCI6MCwiZGVsdGEiOnsidGV4dCI6IlRoaXMgbW9kZWwifSwicCI6ImFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU2In2KDkuVAAAAiQAAAFZb3PlLCzpldmVudC10eXBlBwAQY29udGVudEJsb2NrU3RvcA06Y29udGVudC10eXBlBwAQYXBwbGljYXRpb24vanNvbg06bWVzc2FnZS10eXBlBwAFZXZlbnR7ImNvbnRlbnRCbG9ja0luZGV4IjowLCJwIjoiYWJjZGUifecc8K8AAACyAAAAURNJ5X8LOmV2ZW50LXR5cGUHAAttZXNzYWdlU3RvcA06Y29udGVudC10eXBlBwAQYXBwbGljYXRpb24vanNvbg06bWVzc2FnZS10eXBlBwAFZXZlbnR7InAiOiJhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ekFCQ0RFRkdISUpLTE1OT1BRUlNUVSIsInN0b3BSZWFzb24iOiJtYXhfdG9rZW5zIn2F/hMnAAAAyAAAAE4tIxDkCzpldmVudC10eXBlBwAIbWV0YWRhdGENOmNvbnRlbnQtdHlwZQcAEGFwcGxpY2F0aW9uL2pzb24NOm1lc3NhZ2UtdHlwZQcABWV2ZW50eyJtZXRyaWNzIjp7ImxhdGVuY3lNcyI6NTE1fSwicCI6ImFiY2RlZmdoaWoiLCJ1c2FnZSI6eyJpbnB1dFRva2VucyI6OCwib3V0cHV0VG9rZW5zIjo1LCJ0b3RhbFRva2VucyI6MTN9fQQQoFU=
+  headers:
+    Date: "Thu, 27 Feb 2025 06:04:08 GMT"
+    Content-Type: application/vnd.amazon.eventstream
+    x-amzn-RequestId: 79e23cf6-ec23-4055-bf94-e6b8ca4555db
+uuid: 172ed60a-ae31-47f3-a114-3e7e54e5bc91
+persistent: true
+insertionIndex: 10