Skip to content

Add initial instrumentation of OpenAI client #14221

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Jul 17, 2025

Conversation

anuraaga
Copy link
Contributor

@anuraaga anuraaga commented Jul 10, 2025

This is initial instrumentation for OpenAI SDK. It is very similar in concept to the previous Bedrock instrumentation.

Future PRs will add support for streaming, async client, embeddings, and responses.

This is adapted from the instrumentation released in elastic's EDOT

/cc @codefromthecrypt

@anuraaga anuraaga requested a review from a team as a code owner July 10, 2025 04:54
@@ -5,6 +5,7 @@ plugins {
}

dependencies {
compileOnly("org.scala-lang:scala-library:2.11.12")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

IntelliJ was having trouble syncing the project without this. It seemed harmless so hopefully it's ok.

@otelbot-java-instrumentation
Copy link
Contributor

🔧 The result from spotlessApply was committed to the PR branch.

@SuppressWarnings("IdentifierName") // Want to match library's convention
public class OpenAIInstrumentationModule extends InstrumentationModule {
public OpenAIInstrumentationModule() {
super("openai", "openai-java", "openai-java-1.1");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got this lint error though it looks matching to me

Expected to find super\(\n? *"openai-java",\n? *"openai-java-1.1" in instrumentation/openai/openai-java-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/openai/v1_1/OpenAIInstrumentationModule.java

Copy link
Contributor

Choose a reason for hiding this comment

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

I'd guess that it wants to see openai-java as the first instrumentation name to match with the directory name.

…metry-java-instrumentation into openai-instrumentation
@anuraaga anuraaga force-pushed the openai-instrumentation branch from a302aef to 1a238e0 Compare July 11, 2025 01:32
Copy link
Contributor Author

@anuraaga anuraaga left a comment

Choose a reason for hiding this comment

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

Was a bit scared but could somehow get the shading working

exclude(dependency("org.junit.platform:junit-platform-commons"))
exclude(dependency("org.ow2.asm:asm"))
// Exclude dependencies bundled during Armeria shading
exclude(dependency("com.fasterxml.jackson.core:jackson-annotations"))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a bit weird, I wonder if instead of x-shaded-for-testing we should have a single shaded-test-deps project combining them? For this PR keeping changes to existing at a minimal though

Copy link
Contributor

Choose a reason for hiding this comment

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

i agree that having a single shaded artifact might be better


// Ensures tests are not affected by wiremock dependencies. Wiremock itself is
// safe with its original name.
relocate("com.google.common", "io.opentelemetry.testing.internal.guava")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It was tedious to find all these, but I couldn't find a better way. I thought it could be nice to just relocate everything, but I couldn't see any opt-out in that case to not relocate slf4j / junit. So opted-in with a big list instead.

Choose a reason for hiding this comment

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

Any notes to add on regenerate/check assumptions on this when libs update?

Copy link

@codefromthecrypt codefromthecrypt left a comment

Choose a reason for hiding this comment

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

Worth citing where this is coming from (albeit adapted?) Can help build confidence vs bedrock which was built from scratch and not formerly released elsewhere.

} else if (content.isArrayOfContentParts()) {
return joinContentParts(content.asArrayOfContentParts());
} else {
throw new IllegalStateException("Unhandled content type for " + content);

Choose a reason for hiding this comment

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

I assume this wont crash the actual request..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Made it safer

throw new IllegalStateException("Unhandled content type for " + content);
}
}

Choose a reason for hiding this comment

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

Guessing we aren't lucky enough to have base types to address the duplication that way.. and as the client is generated typical issue I suppose


// Ensures tests are not affected by wiremock dependencies. Wiremock itself is
// safe with its original name.
relocate("com.google.common", "io.opentelemetry.testing.internal.guava")

Choose a reason for hiding this comment

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

Any notes to add on regenerate/check assumptions on this when libs update?

@laurit
Copy link
Contributor

laurit commented Jul 14, 2025

closing and reopening to re-trigger checks

@laurit laurit closed this Jul 14, 2025
@laurit laurit reopened this Jul 14, 2025
exclude(dependency("org.junit.platform:junit-platform-commons"))
exclude(dependency("org.ow2.asm:asm"))
// Exclude dependencies bundled during Armeria shading
exclude(dependency("com.fasterxml.jackson.core:jackson-annotations"))
Copy link
Contributor

Choose a reason for hiding this comment

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

i agree that having a single shaded artifact might be better

Copy link
Contributor Author

@anuraaga anuraaga left a comment

Choose a reason for hiding this comment

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

Thanks @laurit applied the comments

OpenAITelemetry.builder(GlobalOpenTelemetry.get())
.setCaptureMessageContent(
AgentInstrumentationConfig.get()
.getBoolean("otel.instrumentation.genai.capture-message-content", false))
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should document this similarly to

| `otel.instrumentation.genai.capture-message-content` | Boolean | `false` | v2 only, record content of user and LLM messages when using Bedrock. |
An alternative would be to document in on the opentelemetry.io website.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, added a readme

@trask trask added this to the v2.18.0 milestone Jul 16, 2025
@trask trask merged commit 9fc8b20 into open-telemetry:main Jul 17, 2025
88 of 89 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants