Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,5 @@ hs_err_pid*

# Local environment
.env
*.local
*.local
.claude
70 changes: 0 additions & 70 deletions FIXES_SUMMARY.md

This file was deleted.

9 changes: 7 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ dependencies {
compileOnly 'com.openai:openai-java:2.8.1'
testImplementation 'com.openai:openai-java:2.8.1'
implementation "io.opentelemetry.instrumentation:opentelemetry-openai-java-1.1:2.19.0-alpha"

// Anthropic Instrumentation
compileOnly "com.anthropic:anthropic-java:2.8.1"
testImplementation "com.anthropic:anthropic-java:2.8.1"
}

/**
Expand Down Expand Up @@ -281,12 +285,13 @@ task validateJavaVersion {


// Task to test the jar by running it
task testJar(type: Exec) {
task testJar(type: JavaExec) {
description = 'Test the jar by running it and fail build if non-zero exit code'
group = 'verification'
dependsOn jar

commandLine 'java', '-jar', jar.archiveFile.get().asFile.absolutePath
classpath = files(jar.archiveFile)
javaLauncher = javaToolchains.launcherFor(java.toolchain)

doFirst {
// println "Testing jar: ${jar.archiveFile.get().asFile.absolutePath}"
Expand Down
16 changes: 16 additions & 0 deletions examples/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ dependencies {
implementation "io.opentelemetry:opentelemetry-exporter-otlp:${otelVersion}"
// to run OAI instrumentation examples
implementation 'com.openai:openai-java:2.8.1'
// to run anthropic examples
implementation "com.anthropic:anthropic-java:2.8.1"
}

application {
Expand Down Expand Up @@ -69,3 +71,17 @@ task runExperiment(type: JavaExec) {
suspend = false
}
}

task runAnthropicInstrumentation(type: JavaExec) {
group = 'Braintrust SDK Examples'
description = 'Run the Anthropic instrumentation example. NOTE: this requires ANTHROPIC_API_KEY to be exported and will make a small call to anthropic, using your tokens'
classpath = sourceSets.main.runtimeClasspath
mainClass = 'dev.braintrust.examples.AnthropicInstrumentationExample'
systemProperty 'org.slf4j.simpleLogger.log.dev.braintrust', braintrustLogLevel
debugOptions {
enabled = true
port = 5566
server = true
suspend = false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package dev.braintrust.examples;

import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
import dev.braintrust.config.BraintrustConfig;
import dev.braintrust.instrumentation.anthropic.BraintrustAnthropic;
import dev.braintrust.trace.BraintrustTracing;

/** Basic OTel + Anthropic instrumentation example */
public class AnthropicInstrumentationExample {
public static void main(String[] args) throws Exception {
if (null == System.getenv("ANTHROPIC_API_KEY")) {
System.err.println(
"\n"
+ "WARNING envar ANTHROPIC_API_KEY not found. This example will likely"
+ " fail.\n");
}

var braintrustConfig = BraintrustConfig.fromEnvironment();
var openTelemetry = BraintrustTracing.of(braintrustConfig, true);
var tracer = BraintrustTracing.getTracer(openTelemetry);

// Wrap Anthropic client with Braintrust instrumentation
AnthropicClient anthropicClient =
BraintrustAnthropic.wrap(openTelemetry, AnthropicOkHttpClient.fromEnv());

var rootSpan = tracer.spanBuilder("anthropic-java-instrumentation-example").startSpan();
try (var ignored = rootSpan.makeCurrent()) {
Thread.sleep(70); // just to make span look interesting

var request =
MessageCreateParams.builder()
.model(Model.CLAUDE_3_5_HAIKU_20241022)
.system("You are the world's greatest philosopher")
.addUserMessage("What's the meaning of life? Be very brief.")
.maxTokens(50)
.temperature(0.0)
.build();

var response = anthropicClient.messages().create(request);
System.out.println("~~~ GOT RESPONSE: " + response);

Thread.sleep(30); // not required, just to show span duration
} finally {
rootSpan.end();
}

var url =
braintrustConfig.fetchProjectURI()
+ "/logs?r=%s&s=%s"
.formatted(
rootSpan.getSpanContext().getTraceId(),
rootSpan.getSpanContext().getSpanId());

System.out.println("\n\n Example complete! View your data in Braintrust: " + url);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public static void main(String[] args) throws Exception {
braintrustConfig.fetchProjectURI()
+ "/logs?r=%s&s=%s"
.formatted(
span.getSpanContext().getSpanId(),
span.getSpanContext().getTraceId(),
span.getSpanContext().getSpanId());
System.out.println("\n\n Example complete! View your data in Braintrust: " + url);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static void main(String[] args) throws Exception {
var tracer = BraintrustTracing.getTracer(openTelemetry);
OpenAIClient openAIClient =
BraintrustOpenAI.wrapOpenAI(openTelemetry, OpenAIOkHttpClient.fromEnv());
var rootSpan = tracer.spanBuilder("java-braintrust-example").startSpan();
var rootSpan = tracer.spanBuilder("openai-java-instrumentation-example").startSpan();
try (var ignored = rootSpan.makeCurrent()) {
Thread.sleep(70); // Not required. This is just to make the span look interesting
var request =
Expand All @@ -41,7 +41,7 @@ public static void main(String[] args) throws Exception {
braintrustConfig.fetchProjectURI()
+ "/logs?r=%s&s=%s"
.formatted(
rootSpan.getSpanContext().getSpanId(),
rootSpan.getSpanContext().getTraceId(),
rootSpan.getSpanContext().getSpanId());
System.out.println("\n\n Example complete! View your data in Braintrust: " + url);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static void main(String[] args) throws Exception {
braintrustConfig.fetchProjectURI()
+ "/logs?r=%s&s=%s"
.formatted(
span.getSpanContext().getSpanId(),
span.getSpanContext().getTraceId(),
span.getSpanContext().getSpanId());
System.out.println("\n\n Example complete! View your data in Braintrust: " + url);
}
Expand Down
50 changes: 0 additions & 50 deletions run-with-env.sh

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package dev.braintrust.instrumentation.anthropic;

import com.anthropic.client.AnthropicClient;
import dev.braintrust.instrumentation.anthropic.otel.AnthropicTelemetry;
import io.opentelemetry.api.OpenTelemetry;

/** Braintrust Anthropic client instrumentation. */
public final class BraintrustAnthropic {

/** Instrument Anthropic client with braintrust traces */
public static AnthropicClient wrap(OpenTelemetry otel, AnthropicClient client) {
return AnthropicTelemetry.builder(otel).setCaptureMessageContent(true).build().wrap(client);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package dev.braintrust.instrumentation.anthropic.otel;

import com.anthropic.client.AnthropicClient;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.logs.Logger;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;

/** Entrypoint for instrumenting Anthropic clients. */
public final class AnthropicTelemetry {
/** Returns a new {@link AnthropicTelemetry} configured with the given {@link OpenTelemetry}. */
public static AnthropicTelemetry create(OpenTelemetry openTelemetry) {
return builder(openTelemetry).build();
}

/**
* Returns a new {@link AnthropicTelemetryBuilder} configured with the given {@link
* OpenTelemetry}.
*/
public static AnthropicTelemetryBuilder builder(OpenTelemetry openTelemetry) {
return new AnthropicTelemetryBuilder(openTelemetry);
}

private final Instrumenter<MessageCreateParams, Message> messageInstrumenter;

private final Logger eventLogger;

private final boolean captureMessageContent;

AnthropicTelemetry(
Instrumenter<MessageCreateParams, Message> messageInstrumenter,
Logger eventLogger,
boolean captureMessageContent) {
this.messageInstrumenter = messageInstrumenter;
this.eventLogger = eventLogger;
this.captureMessageContent = captureMessageContent;
}

/** Wraps the provided AnthropicClient, enabling telemetry for it. */
public AnthropicClient wrap(AnthropicClient client) {
return new InstrumentedAnthropicClient(
client, messageInstrumenter, eventLogger, captureMessageContent)
.createProxy();
}
}
Loading