Skip to content

Commit 2f7b59f

Browse files
authored
Add support for ConverseStream to bedrock instrumentation (#13410)
1 parent fd76910 commit 2f7b59f

File tree

16 files changed

+616
-50
lines changed

16 files changed

+616
-50
lines changed

instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/genai/GenAiAttributesExtractor.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,10 @@ public void onEnd(
9595
REQUEST request,
9696
@Nullable RESPONSE response,
9797
@Nullable Throwable error) {
98-
internalSet(
99-
attributes,
100-
GEN_AI_RESPONSE_FINISH_REASONS,
101-
getter.getResponseFinishReasons(request, response));
98+
List<String> finishReasons = getter.getResponseFinishReasons(request, response);
99+
if (finishReasons != null && !finishReasons.isEmpty()) {
100+
attributes.put(GEN_AI_RESPONSE_FINISH_REASONS, finishReasons);
101+
}
102102
internalSet(attributes, GEN_AI_RESPONSE_ID, getter.getResponseId(request, response));
103103
internalSet(attributes, GEN_AI_RESPONSE_MODEL, getter.getResponseModel(request, response));
104104
internalSet(

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

+5
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

+10
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

+7
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

+20
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import io.opentelemetry.context.propagation.TextMapPropagator;
1111
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
1212
import io.opentelemetry.instrumentation.awssdk.v2_2.internal.AwsSdkInstrumenterFactory;
13+
import io.opentelemetry.instrumentation.awssdk.v2_2.internal.BedrockRuntimeImpl;
1314
import io.opentelemetry.instrumentation.awssdk.v2_2.internal.Response;
1415
import io.opentelemetry.instrumentation.awssdk.v2_2.internal.SqsImpl;
1516
import io.opentelemetry.instrumentation.awssdk.v2_2.internal.SqsProcessRequest;
@@ -21,6 +22,7 @@
2122
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
2223
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
2324
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
25+
import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeAsyncClient;
2426
import software.amazon.awssdk.services.sqs.SqsAsyncClient;
2527
import software.amazon.awssdk.services.sqs.SqsClient;
2628

@@ -29,6 +31,14 @@
2931
* ExecutionInterceptor} returned by {@link #newExecutionInterceptor()} with an SDK client to have
3032
* all requests traced.
3133
*
34+
* <p>Certain services additionally require wrapping the SDK client itself:
35+
*
36+
* <ul>
37+
* <li>SQSClient - {@link #wrap(SqsClient)}
38+
* <li>SQSAsyncClient - {@link #wrap(SqsAsyncClient)}
39+
* <li>BedrockRuntimeAsyncClient - {@link #wrapBedrockRuntimeClient(BedrockRuntimeAsyncClient)}
40+
* </ul>
41+
*
3242
* <pre>{@code
3343
* DynamoDbClient dynamoDb = DynamoDbClient.builder()
3444
* .overrideConfiguration(ClientOverrideConfiguration.builder()
@@ -134,4 +144,14 @@ public SqsClient wrap(SqsClient sqsClient) {
134144
public SqsAsyncClient wrap(SqsAsyncClient sqsClient) {
135145
return SqsImpl.wrap(sqsClient);
136146
}
147+
148+
/**
149+
* Construct a new tracing-enabled {@link BedrockRuntimeAsyncClient} using the provided {@link
150+
* BedrockRuntimeAsyncClient} instance.
151+
*/
152+
@NoMuzzle
153+
public BedrockRuntimeAsyncClient wrapBedrockRuntimeClient(
154+
BedrockRuntimeAsyncClient bedrockClient) {
155+
return BedrockRuntimeImpl.wrap(bedrockClient);
156+
}
137157
}

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -76,19 +76,19 @@ static List<String> getStopSequences(SdkRequest request) {
7676

7777
@Nullable
7878
@NoMuzzle
79-
static String getStopReason(SdkResponse response) {
80-
return enabled ? BedrockRuntimeImpl.getStopReason(response) : null;
79+
static List<String> getStopReasons(Response response) {
80+
return enabled ? BedrockRuntimeImpl.getStopReasons(response) : null;
8181
}
8282

8383
@Nullable
8484
@NoMuzzle
85-
static Long getUsageInputTokens(SdkResponse response) {
85+
static Long getUsageInputTokens(Response response) {
8686
return enabled ? BedrockRuntimeImpl.getUsageInputTokens(response) : null;
8787
}
8888

8989
@Nullable
9090
@NoMuzzle
91-
static Long getUsageOutputTokens(SdkResponse response) {
91+
static Long getUsageOutputTokens(Response response) {
9292
return enabled ? BedrockRuntimeImpl.getUsageOutputTokens(response) : null;
9393
}
9494

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

+9-7
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import static java.util.Collections.emptyList;
1010

1111
import io.opentelemetry.instrumentation.api.incubator.semconv.genai.GenAiAttributesGetter;
12-
import java.util.Arrays;
12+
import java.util.Collections;
1313
import java.util.List;
1414
import javax.annotation.Nullable;
1515
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
@@ -38,6 +38,8 @@ public String getOperationName(ExecutionAttributes executionAttributes) {
3838
if (operation != null) {
3939
switch (operation) {
4040
case "Converse":
41+
// fallthrough
42+
case "ConverseStream":
4143
return GenAiOperationNameIncubatingValues.CHAT;
4244
default:
4345
return null;
@@ -120,11 +122,11 @@ public List<String> getResponseFinishReasons(
120122
if (response == null) {
121123
return emptyList();
122124
}
123-
String stopReason = BedrockRuntimeAccess.getStopReason(response.getSdkResponse());
124-
if (stopReason == null) {
125-
return emptyList();
125+
List<String> stopReasons = BedrockRuntimeAccess.getStopReasons(response);
126+
if (stopReasons == null) {
127+
return Collections.emptyList();
126128
}
127-
return Arrays.asList(stopReason);
129+
return stopReasons;
128130
}
129131

130132
@Nullable
@@ -146,7 +148,7 @@ public Long getUsageInputTokens(
146148
if (response == null) {
147149
return null;
148150
}
149-
return BedrockRuntimeAccess.getUsageInputTokens(response.getSdkResponse());
151+
return BedrockRuntimeAccess.getUsageInputTokens(response);
150152
}
151153

152154
@Nullable
@@ -156,6 +158,6 @@ public Long getUsageOutputTokens(
156158
if (response == null) {
157159
return null;
158160
}
159-
return BedrockRuntimeAccess.getUsageOutputTokens(response.getSdkResponse());
161+
return BedrockRuntimeAccess.getUsageOutputTokens(response);
160162
}
161163
}

0 commit comments

Comments
 (0)