From c8dab32fcd5938728af9f17c1f65792c7d4be320 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Tue, 2 Jun 2026 14:35:07 +0200 Subject: [PATCH] feat: add LangChain4j 1.0 instrumentation module with three phase hooks --- .../langchain4j/langchain4j-1.0/build.gradle | 19 +++ .../langchain4j-1.0/gradle.lockfile | 137 ++++++++++++++++++ .../AiServicesInstrumentation.java | 40 +++++ .../langchain4j/ChatModelInstrumentation.java | 51 +++++++ .../LangChain4jProfilingModule.java | 23 +++ .../ToolExecutorInstrumentation.java | 51 +++++++ settings.gradle.kts | 1 + 7 files changed, 322 insertions(+) create mode 100644 dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/build.gradle create mode 100644 dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/gradle.lockfile create mode 100644 dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/src/main/java/datadog/trace/instrumentation/langchain4j/AiServicesInstrumentation.java create mode 100644 dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/src/main/java/datadog/trace/instrumentation/langchain4j/ChatModelInstrumentation.java create mode 100644 dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/src/main/java/datadog/trace/instrumentation/langchain4j/LangChain4jProfilingModule.java create mode 100644 dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/src/main/java/datadog/trace/instrumentation/langchain4j/ToolExecutorInstrumentation.java diff --git a/dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/build.gradle b/dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/build.gradle new file mode 100644 index 00000000000..a5a2793836c --- /dev/null +++ b/dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/build.gradle @@ -0,0 +1,19 @@ +apply from: "$rootDir/gradle/java.gradle" + +def minVer = '1.0.0' + +muzzle { + pass { + group = "dev.langchain4j" + module = "langchain4j-core" + versions = "[$minVer,)" + } +} + +addTestSuiteForDir('latestDepTest', 'test') + +dependencies { + compileOnly group: 'dev.langchain4j', name: 'langchain4j-core', version: minVer + testImplementation group: 'dev.langchain4j', name: 'langchain4j', version: minVer + latestDepTestImplementation group: 'dev.langchain4j', name: 'langchain4j', version: '+' +} diff --git a/dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/gradle.lockfile b/dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/gradle.lockfile new file mode 100644 index 00000000000..11f3fa44b1e --- /dev/null +++ b/dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/gradle.lockfile @@ -0,0 +1,137 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +cafe.cryptography:curve25519-elisabeth:0.1.0=latestDepTestRuntimeClasspath,testRuntimeClasspath +cafe.cryptography:ed25519-elisabeth:0.1.0=latestDepTestRuntimeClasspath,testRuntimeClasspath +ch.qos.logback:logback-classic:1.2.13=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +ch.qos.logback:logback-core:1.2.13=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.blogspot.mydailyjava:weak-lock-free:0.17=buildTimeInstrumentationPlugin,compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.datadoghq.okhttp3:okhttp:3.12.15=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.datadoghq.okio:okio:1.17.6=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.datadoghq:dd-instrument-java:0.0.3=buildTimeInstrumentationPlugin,compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.datadoghq:dd-javac-plugin-client:0.2.2=buildTimeInstrumentationPlugin,compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.datadoghq:java-dogstatsd-client:4.4.5=latestDepTestRuntimeClasspath,testRuntimeClasspath +com.datadoghq:sketches-java:0.8.3=latestDepTestRuntimeClasspath,testRuntimeClasspath +com.fasterxml.jackson.core:jackson-annotations:2.19.0=compileClasspath,testCompileClasspath,testRuntimeClasspath +com.fasterxml.jackson.core:jackson-annotations:2.21=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath +com.fasterxml.jackson.core:jackson-core:2.19.0=compileClasspath,testCompileClasspath,testRuntimeClasspath +com.fasterxml.jackson.core:jackson-core:2.21.3=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath +com.fasterxml.jackson.core:jackson-databind:2.19.0=compileClasspath,testCompileClasspath,testRuntimeClasspath +com.fasterxml.jackson.core:jackson-databind:2.21.3=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath +com.fasterxml.jackson:jackson-bom:2.19.0=compileClasspath,testCompileClasspath,testRuntimeClasspath +com.fasterxml.jackson:jackson-bom:2.21.3=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath +com.github.javaparser:javaparser-core:3.25.6=codenarc +com.github.jnr:jffi:1.3.14=latestDepTestRuntimeClasspath,testRuntimeClasspath +com.github.jnr:jnr-a64asm:1.0.0=latestDepTestRuntimeClasspath,testRuntimeClasspath +com.github.jnr:jnr-constants:0.10.4=latestDepTestRuntimeClasspath,testRuntimeClasspath +com.github.jnr:jnr-enxio:0.32.19=latestDepTestRuntimeClasspath,testRuntimeClasspath +com.github.jnr:jnr-ffi:2.2.18=latestDepTestRuntimeClasspath,testRuntimeClasspath +com.github.jnr:jnr-posix:3.1.21=latestDepTestRuntimeClasspath,testRuntimeClasspath +com.github.jnr:jnr-unixsocket:0.38.24=latestDepTestRuntimeClasspath,testRuntimeClasspath +com.github.jnr:jnr-x86asm:1.0.2=latestDepTestRuntimeClasspath,testRuntimeClasspath +com.github.spotbugs:spotbugs-annotations:4.9.8=compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath +com.github.spotbugs:spotbugs:4.9.8=spotbugs +com.github.stephenc.jcip:jcip-annotations:1.0-1=spotbugs +com.google.auto.service:auto-service-annotations:1.1.1=annotationProcessor,compileClasspath,latestDepTestAnnotationProcessor,latestDepTestCompileClasspath,testAnnotationProcessor,testCompileClasspath +com.google.auto.service:auto-service:1.1.1=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor +com.google.auto:auto-common:1.2.1=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor +com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,compileClasspath,latestDepTestAnnotationProcessor,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,spotbugs,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath +com.google.code.gson:gson:2.13.2=spotbugs +com.google.errorprone:error_prone_annotations:2.18.0=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor +com.google.errorprone:error_prone_annotations:2.41.0=spotbugs +com.google.guava:failureaccess:1.0.1=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor +com.google.guava:guava:20.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.guava:guava:32.0.1-jre=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor +com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor +com.google.j2objc:j2objc-annotations:2.8=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor +com.google.re2j:re2j:1.7=latestDepTestRuntimeClasspath,testRuntimeClasspath +com.squareup.moshi:moshi:1.11.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.squareup.okhttp3:logging-interceptor:3.12.12=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.squareup.okhttp3:okhttp:3.12.12=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.squareup.okio:okio:1.17.5=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.thoughtworks.qdox:qdox:1.12.1=codenarc +commons-fileupload:commons-fileupload:1.5=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +commons-io:commons-io:2.11.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +commons-io:commons-io:2.20.0=spotbugs +de.thetaphi:forbiddenapis:3.10=compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +dev.langchain4j:langchain4j-core:1.0.0=compileClasspath,testCompileClasspath,testRuntimeClasspath +dev.langchain4j:langchain4j-core:1.15.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath +dev.langchain4j:langchain4j:1.0.0=testCompileClasspath,testRuntimeClasspath +dev.langchain4j:langchain4j:1.15.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath +io.leangen.geantyref:geantyref:1.3.16=latestDepTestRuntimeClasspath,testRuntimeClasspath +io.sqreen:libsqreen:17.3.0=latestDepTestRuntimeClasspath,testRuntimeClasspath +javax.servlet:javax.servlet-api:3.1.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +jaxen:jaxen:2.0.0=spotbugs +junit:junit:4.13.2=latestDepTestRuntimeClasspath,testRuntimeClasspath +net.bytebuddy:byte-buddy-agent:1.18.8=buildTimeInstrumentationPlugin,compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +net.bytebuddy:byte-buddy:1.18.8=buildTimeInstrumentationPlugin,compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +net.java.dev.jna:jna-platform:5.8.0=latestDepTestRuntimeClasspath,testRuntimeClasspath +net.java.dev.jna:jna:5.8.0=latestDepTestRuntimeClasspath,testRuntimeClasspath +net.sf.saxon:Saxon-HE:12.9=spotbugs +org.apache.ant:ant-antlr:1.10.14=codenarc +org.apache.ant:ant-junit:1.10.14=codenarc +org.apache.bcel:bcel:6.11.0=spotbugs +org.apache.commons:commons-lang3:3.19.0=spotbugs +org.apache.commons:commons-text:1.14.0=spotbugs +org.apache.logging.log4j:log4j-api:2.25.2=spotbugs +org.apache.logging.log4j:log4j-core:2.25.2=spotbugs +org.apache.opennlp:opennlp-tools:2.5.4=testCompileClasspath,testRuntimeClasspath +org.apache.opennlp:opennlp-tools:2.5.9=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath +org.apiguardian:apiguardian-api:1.1.2=latestDepTestCompileClasspath,testCompileClasspath +org.checkerframework:checker-qual:3.33.0=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor +org.codehaus.groovy:groovy-ant:3.0.23=codenarc +org.codehaus.groovy:groovy-docgenerator:3.0.23=codenarc +org.codehaus.groovy:groovy-groovydoc:3.0.23=codenarc +org.codehaus.groovy:groovy-json:3.0.23=codenarc +org.codehaus.groovy:groovy-json:3.0.25=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-templates:3.0.23=codenarc +org.codehaus.groovy:groovy-xml:3.0.23=codenarc +org.codehaus.groovy:groovy:3.0.23=codenarc +org.codehaus.groovy:groovy:3.0.25=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.codenarc:CodeNarc:3.7.0=codenarc +org.dom4j:dom4j:2.2.0=spotbugs +org.gmetrics:GMetrics:2.1.0=codenarc +org.hamcrest:hamcrest-core:1.3=latestDepTestRuntimeClasspath,testRuntimeClasspath +org.hamcrest:hamcrest:3.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.jctools:jctools-core-jdk11:4.0.6=latestDepTestRuntimeClasspath,testRuntimeClasspath +org.jctools:jctools-core:4.0.6=latestDepTestRuntimeClasspath,testRuntimeClasspath +org.jspecify:jspecify:1.0.0=compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-api:5.14.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-engine:5.14.1=latestDepTestRuntimeClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-params:5.14.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter:5.14.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-commons:1.14.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-engine:1.14.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-launcher:1.14.1=latestDepTestRuntimeClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-runner:1.14.1=latestDepTestRuntimeClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-suite-api:1.14.1=latestDepTestRuntimeClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-suite-commons:1.14.1=latestDepTestRuntimeClasspath,testRuntimeClasspath +org.junit:junit-bom:5.14.0=spotbugs +org.junit:junit-bom:5.14.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.mockito:mockito-core:4.4.0=latestDepTestRuntimeClasspath,testRuntimeClasspath +org.objenesis:objenesis:3.3=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.opentest4j:opentest4j:1.3.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-analysis:9.7.1=latestDepTestRuntimeClasspath,testRuntimeClasspath +org.ow2.asm:asm-analysis:9.9=spotbugs +org.ow2.asm:asm-commons:9.9=spotbugs +org.ow2.asm:asm-commons:9.9.1=latestDepTestRuntimeClasspath,testRuntimeClasspath +org.ow2.asm:asm-tree:9.9=spotbugs +org.ow2.asm:asm-tree:9.9.1=latestDepTestRuntimeClasspath,testRuntimeClasspath +org.ow2.asm:asm-util:9.7.1=latestDepTestRuntimeClasspath,testRuntimeClasspath +org.ow2.asm:asm-util:9.9=spotbugs +org.ow2.asm:asm:9.9=spotbugs +org.ow2.asm:asm:9.9.1=buildTimeInstrumentationPlugin,compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.slf4j:jcl-over-slf4j:1.7.30=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.slf4j:jul-to-slf4j:1.7.30=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.slf4j:log4j-over-slf4j:1.7.30=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.slf4j:slf4j-api:1.7.30=buildTimeInstrumentationPlugin,muzzleBootstrap,muzzleTooling,runtimeClasspath +org.slf4j:slf4j-api:2.0.17=compileClasspath,spotbugs,spotbugsSlf4j,testCompileClasspath,testRuntimeClasspath +org.slf4j:slf4j-api:2.0.18=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath +org.slf4j:slf4j-simple:2.0.17=spotbugsSlf4j +org.snakeyaml:snakeyaml-engine:2.9=buildTimeInstrumentationPlugin,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testRuntimeClasspath +org.spockframework:spock-bom:2.4-groovy-3.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.spockframework:spock-core:2.4-groovy-3.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.tabletest:tabletest-junit:1.2.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.tabletest:tabletest-parser:1.2.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.xmlresolver:xmlresolver:5.3.3=spotbugs +empty=spotbugsPlugins diff --git a/dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/src/main/java/datadog/trace/instrumentation/langchain4j/AiServicesInstrumentation.java b/dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/src/main/java/datadog/trace/instrumentation/langchain4j/AiServicesInstrumentation.java new file mode 100644 index 00000000000..7033aa04cd6 --- /dev/null +++ b/dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/src/main/java/datadog/trace/instrumentation/langchain4j/AiServicesInstrumentation.java @@ -0,0 +1,40 @@ +package datadog.trace.instrumentation.langchain4j; + +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.api.profiling.Profiling; +import datadog.trace.api.profiling.ProfilingScope; +import net.bytebuddy.asm.Advice; + +public class AiServicesInstrumentation + implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice { + + @Override + public String instrumentedType() { + return "dev.langchain4j.service.DefaultAiServices$1"; + } + + @Override + public void methodAdvice(MethodTransformer transformer) { + transformer.applyAdvice( + isMethod().and(named("invoke")), + AiServicesInstrumentation.class.getName() + "$InvokeAdvice"); + } + + public static final class InvokeAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static ProfilingScope enter() { + ProfilingScope scope = Profiling.get().newScope(); + scope.setContextValue("llm.agent.phase", "context_build"); + return scope; + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + public static void exit(@Advice.Enter final ProfilingScope scope) { + scope.clearContextValue("llm.agent.phase"); + scope.close(); + } + } +} diff --git a/dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/src/main/java/datadog/trace/instrumentation/langchain4j/ChatModelInstrumentation.java b/dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/src/main/java/datadog/trace/instrumentation/langchain4j/ChatModelInstrumentation.java new file mode 100644 index 00000000000..20f286d6589 --- /dev/null +++ b/dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/src/main/java/datadog/trace/instrumentation/langchain4j/ChatModelInstrumentation.java @@ -0,0 +1,51 @@ +package datadog.trace.instrumentation.langchain4j; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.implementsInterface; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.api.profiling.Profiling; +import datadog.trace.api.profiling.ProfilingScope; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class ChatModelInstrumentation + implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice { + + @Override + public String hierarchyMarkerType() { + return "dev.langchain4j.model.chat.ChatModel"; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return implementsInterface(named("dev.langchain4j.model.chat.ChatModel")); + } + + @Override + public void methodAdvice(MethodTransformer transformer) { + transformer.applyAdvice( + isMethod() + .and(net.bytebuddy.matcher.ElementMatchers.named("chat")) + .and(takesArgument(0, named("dev.langchain4j.model.chat.request.ChatRequest"))), + ChatModelInstrumentation.class.getName() + "$ChatAdvice"); + } + + public static final class ChatAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static ProfilingScope enter() { + ProfilingScope scope = Profiling.get().newScope(); + scope.setContextValue("llm.agent.phase", "awaiting_inference"); + return scope; + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + public static void exit(@Advice.Enter final ProfilingScope scope) { + scope.clearContextValue("llm.agent.phase"); + scope.close(); + } + } +} diff --git a/dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/src/main/java/datadog/trace/instrumentation/langchain4j/LangChain4jProfilingModule.java b/dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/src/main/java/datadog/trace/instrumentation/langchain4j/LangChain4jProfilingModule.java new file mode 100644 index 00000000000..4a3575424a0 --- /dev/null +++ b/dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/src/main/java/datadog/trace/instrumentation/langchain4j/LangChain4jProfilingModule.java @@ -0,0 +1,23 @@ +package datadog.trace.instrumentation.langchain4j; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import java.util.Arrays; +import java.util.List; + +@AutoService(InstrumenterModule.class) +public class LangChain4jProfilingModule extends InstrumenterModule.Profiling { + + public LangChain4jProfilingModule() { + super("langchain4j"); + } + + @Override + public List typeInstrumentations() { + return Arrays.asList( + new ChatModelInstrumentation(), + new ToolExecutorInstrumentation(), + new AiServicesInstrumentation()); + } +} diff --git a/dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/src/main/java/datadog/trace/instrumentation/langchain4j/ToolExecutorInstrumentation.java b/dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/src/main/java/datadog/trace/instrumentation/langchain4j/ToolExecutorInstrumentation.java new file mode 100644 index 00000000000..2ac69441885 --- /dev/null +++ b/dd-java-agent/instrumentation/langchain4j/langchain4j-1.0/src/main/java/datadog/trace/instrumentation/langchain4j/ToolExecutorInstrumentation.java @@ -0,0 +1,51 @@ +package datadog.trace.instrumentation.langchain4j; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.implementsInterface; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.api.profiling.Profiling; +import datadog.trace.api.profiling.ProfilingScope; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class ToolExecutorInstrumentation + implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice { + + @Override + public String hierarchyMarkerType() { + return "dev.langchain4j.service.tool.ToolExecutor"; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return implementsInterface(named("dev.langchain4j.service.tool.ToolExecutor")); + } + + @Override + public void methodAdvice(MethodTransformer transformer) { + transformer.applyAdvice( + isMethod() + .and(net.bytebuddy.matcher.ElementMatchers.named("execute")) + .and(takesArguments(2)), + ToolExecutorInstrumentation.class.getName() + "$ExecuteAdvice"); + } + + public static final class ExecuteAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static ProfilingScope enter() { + ProfilingScope scope = Profiling.get().newScope(); + scope.setContextValue("llm.agent.phase", "tool_execution"); + return scope; + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + public static void exit(@Advice.Enter final ProfilingScope scope) { + scope.clearContextValue("llm.agent.phase"); + scope.close(); + } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 487f6275cf1..38bc5321bcb 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -501,6 +501,7 @@ include( ":dd-java-agent:instrumentation:okhttp:okhttp-2.2", ":dd-java-agent:instrumentation:okhttp:okhttp-3.0", ":dd-java-agent:instrumentation:openai-java:openai-java-3.0", + ":dd-java-agent:instrumentation:langchain4j:langchain4j-1.0", ":dd-java-agent:instrumentation:opensearch:opensearch-rest-1.0", ":dd-java-agent:instrumentation:opensearch:opensearch-transport-1.0", ":dd-java-agent:instrumentation:opensearch:opensearch-common",