Skip to content

Commit b02d108

Browse files
committed
Add support for ConverseStream to bedrock instrumentation
1 parent 5f41b7f commit b02d108

File tree

15 files changed

+560
-49
lines changed

15 files changed

+560
-49
lines changed

instrumentation/aws-sdk/aws-sdk-2.2/javaagent/build.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ dependencies {
103103
library("software.amazon.awssdk:aws-core:2.2.0")
104104
library("software.amazon.awssdk:sqs:2.2.0")
105105

106+
// Don't use library to make sure base test is run with the floor version.
107+
// bedrock runtime is tested separately in testBedrockRuntime.
108+
// First release with Converse API
109+
compileOnly("software.amazon.awssdk:bedrockruntime:2.25.63")
110+
106111
testImplementation(project(":instrumentation:aws-sdk:aws-sdk-2.2:testing"))
107112
testImplementation("io.opentelemetry.contrib:opentelemetry-aws-xray-propagator")
108113

instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v2_2/BedrockRuntimeInstrumentationModule.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
import com.google.auto.service.AutoService;
1212
import io.opentelemetry.instrumentation.awssdk.v2_2.internal.BedrockRuntimeAdviceBridge;
1313
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
14+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
1415
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
16+
import java.util.ArrayList;
17+
import java.util.List;
1518
import net.bytebuddy.asm.Advice;
1619
import net.bytebuddy.matcher.ElementMatcher;
1720

@@ -27,6 +30,13 @@ public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
2730
return hasClassesNamed("software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeClient");
2831
}
2932

33+
@Override
34+
public List<TypeInstrumentation> typeInstrumentations() {
35+
List<TypeInstrumentation> instrumentations = new ArrayList<>(super.typeInstrumentations());
36+
instrumentations.add(new DefaultBedrockRuntimeAsyncClientBuilderInstrumentation());
37+
return instrumentations;
38+
}
39+
3040
@Override
3141
public void doTransform(TypeTransformer transformer) {
3242
transformer.applyAdviceToMethod(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.awssdk.v2_2;
7+
8+
import static net.bytebuddy.matcher.ElementMatchers.named;
9+
10+
import io.opentelemetry.instrumentation.awssdk.v2_2.autoconfigure.AwsSdkSingletons;
11+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
12+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
13+
import net.bytebuddy.asm.Advice;
14+
import net.bytebuddy.description.type.TypeDescription;
15+
import net.bytebuddy.matcher.ElementMatcher;
16+
import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeAsyncClient;
17+
18+
public class DefaultBedrockRuntimeAsyncClientBuilderInstrumentation implements TypeInstrumentation {
19+
20+
@Override
21+
public ElementMatcher<TypeDescription> typeMatcher() {
22+
return named(
23+
"software.amazon.awssdk.services.bedrockruntime.DefaultBedrockRuntimeAsyncClientBuilder");
24+
}
25+
26+
@Override
27+
public void transform(TypeTransformer transformer) {
28+
transformer.applyAdviceToMethod(
29+
named("buildClient"), this.getClass().getName() + "$BuildClientAdvice");
30+
}
31+
32+
@SuppressWarnings("unused")
33+
public static class BuildClientAdvice {
34+
35+
@Advice.OnMethodExit(suppress = Throwable.class)
36+
public static void methodExit(
37+
@Advice.Return(readOnly = false) BedrockRuntimeAsyncClient client) {
38+
client = AwsSdkSingletons.telemetry().wrapBedrockRuntimeClient(client);
39+
}
40+
}
41+
}

instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/testBedrockRuntime/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/Aws2BedrockRuntimeTest.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
1111
import org.junit.jupiter.api.extension.RegisterExtension;
1212
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
13+
import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeAsyncClient;
1314

1415
class Aws2BedrockRuntimeTest extends AbstractAws2BedrockRuntimeTest {
1516
@RegisterExtension
@@ -24,4 +25,10 @@ protected InstrumentationExtension getTesting() {
2425
protected ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder() {
2526
return ClientOverrideConfiguration.builder();
2627
}
28+
29+
@Override
30+
protected BedrockRuntimeAsyncClient configureBedrockRuntimeClient(
31+
BedrockRuntimeAsyncClient client) {
32+
return client;
33+
}
2734
}

instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkTelemetry.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import io.opentelemetry.context.propagation.TextMapPropagator;
1010
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
1111
import io.opentelemetry.instrumentation.awssdk.v2_2.internal.AwsSdkInstrumenterFactory;
12+
import io.opentelemetry.instrumentation.awssdk.v2_2.internal.BedrockRuntimeImpl;
1213
import io.opentelemetry.instrumentation.awssdk.v2_2.internal.Response;
1314
import io.opentelemetry.instrumentation.awssdk.v2_2.internal.SqsImpl;
1415
import io.opentelemetry.instrumentation.awssdk.v2_2.internal.SqsProcessRequest;
@@ -20,6 +21,7 @@
2021
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
2122
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
2223
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
24+
import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeAsyncClient;
2325
import software.amazon.awssdk.services.sqs.SqsAsyncClient;
2426
import software.amazon.awssdk.services.sqs.SqsClient;
2527

@@ -28,6 +30,14 @@
2830
* ExecutionInterceptor} returned by {@link #newExecutionInterceptor()} with an SDK client to have
2931
* all requests traced.
3032
*
33+
* <p>Certain services additionally require wrapping the SDK client itself:
34+
*
35+
* <ul>
36+
* <li>SQSClient - {@link #wrap(SqsClient)}
37+
* <li>SQSAsyncClient - {@link #wrap(SqsAsyncClient)}
38+
* <li>BedrockRuntimeAsyncClient - {@link #wrapBedrockRuntimeClient(BedrockRuntimeAsyncClient)}
39+
* </ul>
40+
*
3141
* <pre>{@code
3242
* DynamoDbClient dynamoDb = DynamoDbClient.builder()
3343
* .overrideConfiguration(ClientOverrideConfiguration.builder()
@@ -126,4 +136,14 @@ public SqsClient wrap(SqsClient sqsClient) {
126136
public SqsAsyncClient wrap(SqsAsyncClient sqsClient) {
127137
return SqsImpl.wrap(sqsClient);
128138
}
139+
140+
/**
141+
* Construct a new tracing-enabled {@link BedrockRuntimeAsyncClient} using the provided {@link
142+
* BedrockRuntimeAsyncClient} instance.
143+
*/
144+
@NoMuzzle
145+
public BedrockRuntimeAsyncClient wrapBedrockRuntimeClient(
146+
BedrockRuntimeAsyncClient bedrockClient) {
147+
return BedrockRuntimeImpl.wrap(bedrockClient);
148+
}
129149
}

instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeAccess.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import java.util.List;
1010
import javax.annotation.Nullable;
1111
import software.amazon.awssdk.core.SdkRequest;
12-
import software.amazon.awssdk.core.SdkResponse;
1312

1413
final class BedrockRuntimeAccess {
1514
private BedrockRuntimeAccess() {}
@@ -69,19 +68,19 @@ static List<String> getStopSequences(SdkRequest request) {
6968

7069
@Nullable
7170
@NoMuzzle
72-
static String getStopReason(SdkResponse response) {
73-
return enabled ? BedrockRuntimeImpl.getStopReason(response) : null;
71+
static List<String> getStopReasons(Response response) {
72+
return enabled ? BedrockRuntimeImpl.getStopReasons(response) : null;
7473
}
7574

7675
@Nullable
7776
@NoMuzzle
78-
static Long getUsageInputTokens(SdkResponse response) {
77+
static Long getUsageInputTokens(Response response) {
7978
return enabled ? BedrockRuntimeImpl.getUsageInputTokens(response) : null;
8079
}
8180

8281
@Nullable
8382
@NoMuzzle
84-
static Long getUsageOutputTokens(SdkResponse response) {
83+
static Long getUsageOutputTokens(Response response) {
8584
return enabled ? BedrockRuntimeImpl.getUsageOutputTokens(response) : null;
8685
}
8786
}

instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeAttributesGetter.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import static io.opentelemetry.instrumentation.awssdk.v2_2.internal.TracingExecutionInterceptor.SDK_REQUEST_ATTRIBUTE;
99

1010
import io.opentelemetry.instrumentation.api.incubator.semconv.genai.GenAiAttributesGetter;
11-
import java.util.Arrays;
1211
import java.util.List;
1312
import javax.annotation.Nullable;
1413
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
@@ -37,6 +36,8 @@ public String getOperationName(ExecutionAttributes executionAttributes) {
3736
if (operation != null) {
3837
switch (operation) {
3938
case "Converse":
39+
// fallthrough
40+
case "ConverseStream":
4041
return GenAiOperationNameIncubatingValues.CHAT;
4142
default:
4243
return null;
@@ -117,11 +118,11 @@ public Double getRequestTopP(ExecutionAttributes executionAttributes) {
117118
@Override
118119
public List<String> getResponseFinishReasons(
119120
ExecutionAttributes executionAttributes, Response response) {
120-
String stopReason = BedrockRuntimeAccess.getStopReason(response.getSdkResponse());
121-
if (stopReason == null) {
121+
List<String> stopReasons = BedrockRuntimeAccess.getStopReasons(response);
122+
if (stopReasons == null) {
122123
return null;
123124
}
124-
return Arrays.asList(stopReason);
125+
return stopReasons;
125126
}
126127

127128
@Nullable
@@ -139,12 +140,12 @@ public String getResponseModel(ExecutionAttributes executionAttributes, Response
139140
@Nullable
140141
@Override
141142
public Long getUsageInputTokens(ExecutionAttributes executionAttributes, Response response) {
142-
return BedrockRuntimeAccess.getUsageInputTokens(response.getSdkResponse());
143+
return BedrockRuntimeAccess.getUsageInputTokens(response);
143144
}
144145

145146
@Nullable
146147
@Override
147148
public Long getUsageOutputTokens(ExecutionAttributes executionAttributes, Response response) {
148-
return BedrockRuntimeAccess.getUsageOutputTokens(response.getSdkResponse());
149+
return BedrockRuntimeAccess.getUsageOutputTokens(response);
149150
}
150151
}

0 commit comments

Comments
 (0)