Skip to content

Commit ad3b597

Browse files
committed
Adjust OpenAI Instrumentation to fit OTel 1.37.0
Change-Id: Ifb06d47236bd800cce3f8933e366fc6d69afc5cd
1 parent b945f9e commit ad3b597

File tree

17 files changed

+149
-44
lines changed

17 files changed

+149
-44
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.incubator.semconv.genai;
7+
8+
import com.google.auto.value.AutoValue;
9+
10+
@AutoValue
11+
public abstract class CaptureMessageOptions {
12+
13+
public abstract boolean captureMessageContent();
14+
15+
public abstract boolean emitExperimentalConventions();
16+
17+
public static CaptureMessageOptions create(
18+
boolean captureMessageContent, boolean emitExperimentalConventions) {
19+
return new AutoValue_CaptureMessageOptions(captureMessageContent, emitExperimentalConventions);
20+
}
21+
}
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Settings for the OpenAI instrumentation
22

3-
| System property | Type | Default | Description |
4-
|------------------------------------------------------|---------|---------|------------------------------------------|
5-
| `otel.instrumentation.genai.capture-message-content` | Boolean | `false` | Record content of user and LLM messages. |
3+
| System property | Type | Default | Description |
4+
|------------------------------------------------------|---------|---------|------------------------------------------------------------------------|
5+
| `otel.instrumentation.genai.capture-message-content` | Boolean | `false` | Record content of user and LLM messages. |
6+
| `otel.semconv-stability.opt-in` | Boolean | `` | Enable experimental features when set as `gen_ai_latest_experimental`. |

instrumentation/openai/openai-java-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/openai/v1_1/OpenAiSingletons.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ public final class OpenAiSingletons {
1515
.setCaptureMessageContent(
1616
AgentInstrumentationConfig.get()
1717
.getBoolean("otel.instrumentation.genai.capture-message-content", false))
18+
.setEmitExperimentalConventions(emitExperimentalConventions())
1819
.build();
1920

21+
private static boolean emitExperimentalConventions() {
22+
String config = AgentInstrumentationConfig.get().getString("otel.semconv-stability.opt-in", "");
23+
return config.contains("gen_ai_latest_experimental");
24+
}
25+
2026
private OpenAiSingletons() {}
2127
}

instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/ChatAttributesGetter.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import com.openai.models.chat.completions.ChatCompletion;
1212
import com.openai.models.chat.completions.ChatCompletionCreateParams;
13+
import com.openai.models.chat.completions.ChatCompletionCreateParams.ResponseFormat;
1314
import com.openai.models.completions.CompletionUsage;
1415
import io.opentelemetry.instrumentation.api.incubator.semconv.genai.GenAiAttributesGetter;
1516
import java.util.List;
@@ -106,6 +107,26 @@ public Double getRequestTopP(ChatCompletionCreateParams request) {
106107
return request.topP().orElse(null);
107108
}
108109

110+
@Nullable
111+
@Override
112+
public Long getChoiceCount(ChatCompletionCreateParams request) {
113+
return request.n().orElse(null);
114+
}
115+
116+
@Nullable
117+
@Override
118+
public String getOutputType(ChatCompletionCreateParams request) {
119+
if (request.responseFormat().isPresent()) {
120+
ResponseFormat responseFormat = request.responseFormat().get();
121+
if (responseFormat.isText()) {
122+
return GenAiAttributes.GenAiOutputTypeIncubatingValues.TEXT;
123+
} else if (responseFormat.isJsonSchema() || responseFormat.isJsonObject()) {
124+
return GenAiAttributes.GenAiOutputTypeIncubatingValues.JSON;
125+
}
126+
}
127+
return null;
128+
}
129+
109130
@Override
110131
public List<String> getResponseFinishReasons(
111132
ChatCompletionCreateParams request, @Nullable ChatCompletion response) {

instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/ChatCompletionEventsHelper.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import io.opentelemetry.api.logs.LogRecordBuilder;
2525
import io.opentelemetry.api.logs.Logger;
2626
import io.opentelemetry.context.Context;
27+
import io.opentelemetry.instrumentation.api.incubator.semconv.genai.CaptureMessageOptions;
2728
import java.lang.invoke.MethodHandle;
2829
import java.lang.invoke.MethodHandles;
2930
import java.lang.invoke.MethodType;
@@ -43,7 +44,13 @@ public static void emitPromptLogEvents(
4344
Context context,
4445
Logger eventLogger,
4546
ChatCompletionCreateParams request,
46-
boolean captureMessageContent) {
47+
CaptureMessageOptions captureMessageOptions) {
48+
if (captureMessageOptions.emitExperimentalConventions()) {
49+
// According to https://opentelemetry.io/docs/specs/semconv/gen-ai/openai/
50+
// skip if experimental conventions are not enabled
51+
return;
52+
}
53+
boolean captureMessageContent = captureMessageOptions.captureMessageContent();
4754
for (ChatCompletionMessageParam msg : request.messages()) {
4855
String eventType;
4956
Map<String, Value<?>> body = new HashMap<>();
@@ -170,7 +177,13 @@ public static void emitCompletionLogEvents(
170177
Context context,
171178
Logger eventLogger,
172179
ChatCompletion completion,
173-
boolean captureMessageContent) {
180+
CaptureMessageOptions captureMessageOptions) {
181+
if (captureMessageOptions.emitExperimentalConventions()) {
182+
// According to https://opentelemetry.io/docs/specs/semconv/gen-ai/openai/
183+
// skip if experimental conventions are not enabled
184+
return;
185+
}
186+
boolean captureMessageContent = captureMessageOptions.captureMessageContent();
174187
for (ChatCompletion.Choice choice : completion.choices()) {
175188
ChatCompletionMessage choiceMsg = choice.message();
176189
Map<String, Value<?>> message = new HashMap<>();

instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/EmbeddingAttributesGetter.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ public String getOperationTarget(EmbeddingCreateParams request) {
2828
return getRequestModel(request);
2929
}
3030

31-
3231
@Override
3332
public String getSystem(EmbeddingCreateParams request) {
3433
return GenAiAttributes.GenAiProviderNameIncubatingValues.OPENAI;

instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/GenAiAttributes.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,13 @@ static final class GenAiProviderNameIncubatingValues {
2626
private GenAiProviderNameIncubatingValues() {}
2727
}
2828

29+
static final class GenAiOutputTypeIncubatingValues {
30+
static final String TEXT = "text";
31+
32+
static final String JSON = "json";
33+
34+
private GenAiOutputTypeIncubatingValues() {}
35+
}
36+
2937
private GenAiAttributes() {}
3038
}

instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/InstrumentedChatCompletionService.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import io.opentelemetry.api.logs.Logger;
1515
import io.opentelemetry.context.Context;
1616
import io.opentelemetry.context.Scope;
17+
import io.opentelemetry.instrumentation.api.incubator.semconv.genai.CaptureMessageOptions;
1718
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
1819
import java.lang.reflect.Method;
1920

@@ -22,17 +23,17 @@ final class InstrumentedChatCompletionService
2223

2324
private final Instrumenter<ChatCompletionCreateParams, ChatCompletion> instrumenter;
2425
private final Logger eventLogger;
25-
private final boolean captureMessageContent;
26+
private final CaptureMessageOptions captureMessageOptions;
2627

2728
InstrumentedChatCompletionService(
2829
ChatCompletionService delegate,
2930
Instrumenter<ChatCompletionCreateParams, ChatCompletion> instrumenter,
3031
Logger eventLogger,
31-
boolean captureMessageContent) {
32+
CaptureMessageOptions captureMessageOptions) {
3233
super(delegate);
3334
this.instrumenter = instrumenter;
3435
this.eventLogger = eventLogger;
35-
this.captureMessageContent = captureMessageContent;
36+
this.captureMessageOptions = captureMessageOptions;
3637
}
3738

3839
@Override
@@ -96,10 +97,10 @@ private ChatCompletion createWithLogs(
9697
ChatCompletionCreateParams chatCompletionCreateParams,
9798
RequestOptions requestOptions) {
9899
ChatCompletionEventsHelper.emitPromptLogEvents(
99-
context, eventLogger, chatCompletionCreateParams, captureMessageContent);
100+
context, eventLogger, chatCompletionCreateParams, captureMessageOptions);
100101
ChatCompletion result = delegate.create(chatCompletionCreateParams, requestOptions);
101102
ChatCompletionEventsHelper.emitCompletionLogEvents(
102-
context, eventLogger, result, captureMessageContent);
103+
context, eventLogger, result, captureMessageOptions);
103104
return result;
104105
}
105106

@@ -126,7 +127,7 @@ private StreamResponse<ChatCompletionChunk> createStreamingWithLogs(
126127
RequestOptions requestOptions,
127128
boolean newSpan) {
128129
ChatCompletionEventsHelper.emitPromptLogEvents(
129-
context, eventLogger, chatCompletionCreateParams, captureMessageContent);
130+
context, eventLogger, chatCompletionCreateParams, captureMessageOptions);
130131
StreamResponse<ChatCompletionChunk> result =
131132
delegate.createStreaming(chatCompletionCreateParams, requestOptions);
132133
return new TracingStreamedResponse(
@@ -136,7 +137,7 @@ private StreamResponse<ChatCompletionChunk> createStreamingWithLogs(
136137
chatCompletionCreateParams,
137138
instrumenter,
138139
eventLogger,
139-
captureMessageContent,
140+
captureMessageOptions.captureMessageContent(),
140141
newSpan));
141142
}
142143
}

instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/InstrumentedChatCompletionServiceAsync.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import io.opentelemetry.api.logs.Logger;
1515
import io.opentelemetry.context.Context;
1616
import io.opentelemetry.context.Scope;
17+
import io.opentelemetry.instrumentation.api.incubator.semconv.genai.CaptureMessageOptions;
1718
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
1819
import java.lang.reflect.Method;
1920
import java.util.concurrent.CompletableFuture;
@@ -24,17 +25,17 @@ final class InstrumentedChatCompletionServiceAsync
2425

2526
private final Instrumenter<ChatCompletionCreateParams, ChatCompletion> instrumenter;
2627
private final Logger eventLogger;
27-
private final boolean captureMessageContent;
28+
private final CaptureMessageOptions captureMessageOptions;
2829

2930
InstrumentedChatCompletionServiceAsync(
3031
ChatCompletionServiceAsync delegate,
3132
Instrumenter<ChatCompletionCreateParams, ChatCompletion> instrumenter,
3233
Logger eventLogger,
33-
boolean captureMessageContent) {
34+
CaptureMessageOptions captureMessageOptions) {
3435
super(delegate);
3536
this.instrumenter = instrumenter;
3637
this.eventLogger = eventLogger;
37-
this.captureMessageContent = captureMessageContent;
38+
this.captureMessageOptions = captureMessageOptions;
3839
}
3940

4041
@Override
@@ -100,13 +101,13 @@ private CompletableFuture<ChatCompletion> createWithLogs(
100101
ChatCompletionCreateParams chatCompletionCreateParams,
101102
RequestOptions requestOptions) {
102103
ChatCompletionEventsHelper.emitPromptLogEvents(
103-
context, eventLogger, chatCompletionCreateParams, captureMessageContent);
104+
context, eventLogger, chatCompletionCreateParams, captureMessageOptions);
104105
CompletableFuture<ChatCompletion> future =
105106
delegate.create(chatCompletionCreateParams, requestOptions);
106107
future.thenAccept(
107108
r ->
108109
ChatCompletionEventsHelper.emitCompletionLogEvents(
109-
context, eventLogger, r, captureMessageContent));
110+
context, eventLogger, r, captureMessageOptions));
110111
return future;
111112
}
112113

@@ -133,7 +134,7 @@ private AsyncStreamResponse<ChatCompletionChunk> createStreamingWithLogs(
133134
RequestOptions requestOptions,
134135
boolean newSpan) {
135136
ChatCompletionEventsHelper.emitPromptLogEvents(
136-
context, eventLogger, chatCompletionCreateParams, captureMessageContent);
137+
context, eventLogger, chatCompletionCreateParams, captureMessageOptions);
137138
AsyncStreamResponse<ChatCompletionChunk> result =
138139
delegate.createStreaming(chatCompletionCreateParams, requestOptions);
139140
return new TracingAsyncStreamedResponse(
@@ -143,7 +144,7 @@ private AsyncStreamResponse<ChatCompletionChunk> createStreamingWithLogs(
143144
chatCompletionCreateParams,
144145
instrumenter,
145146
eventLogger,
146-
captureMessageContent,
147+
captureMessageOptions.captureMessageContent(),
147148
newSpan));
148149
}
149150
}

instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/InstrumentedChatService.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.openai.models.chat.completions.ChatCompletionCreateParams;
1010
import com.openai.services.blocking.ChatService;
1111
import io.opentelemetry.api.logs.Logger;
12+
import io.opentelemetry.instrumentation.api.incubator.semconv.genai.CaptureMessageOptions;
1213
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
1314
import java.lang.reflect.Method;
1415

@@ -17,17 +18,17 @@ final class InstrumentedChatService
1718

1819
private final Instrumenter<ChatCompletionCreateParams, ChatCompletion> instrumenter;
1920
private final Logger eventLogger;
20-
private final boolean captureMessageContent;
21+
private final CaptureMessageOptions captureMessageOptions;
2122

2223
InstrumentedChatService(
2324
ChatService delegate,
2425
Instrumenter<ChatCompletionCreateParams, ChatCompletion> instrumenter,
2526
Logger eventLogger,
26-
boolean captureMessageContent) {
27+
CaptureMessageOptions captureMessageOptions) {
2728
super(delegate);
2829
this.instrumenter = instrumenter;
2930
this.eventLogger = eventLogger;
30-
this.captureMessageContent = captureMessageContent;
31+
this.captureMessageOptions = captureMessageOptions;
3132
}
3233

3334
@Override
@@ -41,7 +42,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
4142
Class<?>[] parameterTypes = method.getParameterTypes();
4243
if (methodName.equals("completions") && parameterTypes.length == 0) {
4344
return new InstrumentedChatCompletionService(
44-
delegate.completions(), instrumenter, eventLogger, captureMessageContent)
45+
delegate.completions(), instrumenter, eventLogger, captureMessageOptions)
4546
.createProxy();
4647
}
4748
return super.invoke(proxy, method, args);

0 commit comments

Comments
 (0)