Skip to content

Commit 5ca9c84

Browse files
gary-huangNayeem Kamal
andauthored
LLM Obs SDK Mapper (#8372)
* add APIs for llm obs * add llm message class to support llm spans * add llm message class to support llm spans * impl llmobs agent and llmobs apis * support llm messages with tool calls * handle default model name and provider * rm unneeded file * impl llmobs agent and llmobs apis * impl llmobs agent * working writer * add support for llm message and tool calls * cleaned up whitespace * resolve merge conflicts * remaining merge conflicts * fix bad method call * fixed llmobs intake creation if llmobs not enabled * removed print statements * added tests for llmobsspanmapper * fixed coverage for tags --------- Co-authored-by: Nayeem Kamal <[email protected]>
1 parent 993cdbe commit 5ca9c84

File tree

11 files changed

+547
-7
lines changed

11 files changed

+547
-7
lines changed
Submodule integrations-core updated 3586 files

dd-trace-api/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ excludedClassesCoverage += [
5151
'datadog.trace.context.NoopTraceScope',
5252
'datadog.trace.payloadtags.PayloadTagsData',
5353
'datadog.trace.payloadtags.PayloadTagsData.PathAndValue',
54+
'datadog.trace.api.llmobs.LLMObsTags',
5455
]
5556

5657
description = 'dd-trace-api'

dd-trace-api/src/main/java/datadog/trace/api/DDSpanTypes.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public class DDSpanTypes {
3636
public static final String PROTOBUF = "protobuf";
3737

3838
public static final String MULE = "mule";
39+
3940
public static final String VALKEY = "valkey";
4041
public static final String WEBSOCKET = "websocket";
4142

dd-trace-core/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ excludedClassesCoverage += [
3737
// Interface with an empty defender method
3838
'datadog.trace.core.propagation.HttpCodec.Extractor',
3939
'datadog.trace.core.flare.*',
40+
'datadog.trace.llmobs.writer.ddintake.LLMObsSpanMapper',
41+
'datadog.trace.llmobs.writer.ddintake.LLMObsSpanMapper.PayloadV1',
4042
// FIXME(DSM): test coverage needed
4143
'datadog.trace.core.datastreams.DataStreamContextInjector',
4244
'datadog.trace.common.sampling.TraceSamplingRules.RuleAdapter',

dd-trace-core/src/main/java/datadog/trace/common/writer/WriterFactory.java

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ public static Writer createWriter(
6868
if (!DD_AGENT_WRITER_TYPE.equals(configuredType)
6969
&& !DD_INTAKE_WRITER_TYPE.equals(configuredType)) {
7070
log.warn(
71-
"Writer type not configured correctly: Type {} not recognized. Ignoring", configuredType);
71+
"Writer type not configured correctly: Type {} not recognized. Ignoring ",
72+
configuredType);
7273
configuredType = datadog.trace.api.ConfigDefaults.DEFAULT_AGENT_WRITER_TYPE;
7374
}
7475

@@ -84,14 +85,22 @@ public static Writer createWriter(
8485

8586
// The AgentWriter doesn't support the CI Visibility protocol. If CI Visibility is
8687
// enabled, check if we can use the IntakeWriter instead.
87-
if (DD_AGENT_WRITER_TYPE.equals(configuredType) && config.isCiVisibilityEnabled()) {
88+
if (DD_AGENT_WRITER_TYPE.equals(configuredType) && (config.isCiVisibilityEnabled())) {
8889
if (featuresDiscovery.supportsEvpProxy() || config.isCiVisibilityAgentlessEnabled()) {
8990
configuredType = DD_INTAKE_WRITER_TYPE;
9091
} else {
9192
log.info(
9293
"CI Visibility functionality is limited. Please upgrade to Agent v6.40+ or v7.40+ or enable Agentless mode.");
9394
}
9495
}
96+
if (DD_AGENT_WRITER_TYPE.equals(configuredType) && (config.isLlmObsEnabled())) {
97+
if (featuresDiscovery.supportsEvpProxy() || config.isLlmObsAgentlessEnabled()) {
98+
configuredType = DD_INTAKE_WRITER_TYPE;
99+
} else {
100+
log.info("LLM Observability functionality is limited.");
101+
// TODO: add supported agent version to this log line for llm obs
102+
}
103+
}
95104

96105
RemoteWriter remoteWriter;
97106
if (DD_INTAKE_WRITER_TYPE.equals(configuredType)) {
@@ -117,7 +126,11 @@ public static Writer createWriter(
117126
createDDIntakeRemoteApi(config, commObjects, featuresDiscovery, TrackType.CITESTCOV);
118127
builder.addTrack(TrackType.CITESTCOV, coverageApi);
119128
}
120-
129+
if (config.isLlmObsEnabled()) {
130+
final RemoteApi llmobsApi =
131+
createDDIntakeRemoteApi(config, commObjects, featuresDiscovery, TrackType.LLMOBS);
132+
builder.addTrack(TrackType.LLMOBS, llmobsApi);
133+
}
121134
remoteWriter = builder.build();
122135

123136
} else { // configuredType == DDAgentWriter
@@ -173,20 +186,34 @@ private static RemoteApi createDDIntakeRemoteApi(
173186
SharedCommunicationObjects commObjects,
174187
DDAgentFeaturesDiscovery featuresDiscovery,
175188
TrackType trackType) {
176-
if (featuresDiscovery.supportsEvpProxy() && !config.isCiVisibilityAgentlessEnabled()) {
189+
boolean evpProxySupported = featuresDiscovery.supportsEvpProxy();
190+
boolean useProxyApi =
191+
(evpProxySupported && TrackType.LLMOBS == trackType && !config.isLlmObsAgentlessEnabled())
192+
|| (evpProxySupported
193+
&& (TrackType.CITESTCOV == trackType || TrackType.CITESTCYCLE == trackType)
194+
&& !config.isCiVisibilityAgentlessEnabled());
195+
196+
if (useProxyApi) {
177197
return DDEvpProxyApi.builder()
178198
.httpClient(commObjects.okHttpClient)
179199
.agentUrl(commObjects.agentUrl)
180200
.evpProxyEndpoint(featuresDiscovery.getEvpProxyEndpoint())
181201
.trackType(trackType)
182202
.compressionEnabled(featuresDiscovery.supportsContentEncodingHeadersWithEvpProxy())
183203
.build();
184-
185204
} else {
186205
HttpUrl hostUrl = null;
206+
String llmObsAgentlessUrl = config.getLlMObsAgentlessUrl();
207+
187208
if (config.getCiVisibilityAgentlessUrl() != null) {
188209
hostUrl = HttpUrl.get(config.getCiVisibilityAgentlessUrl());
189210
log.info("Using host URL '{}' to report CI Visibility traces in Agentless mode.", hostUrl);
211+
} else if (config.isLlmObsEnabled()
212+
&& config.isLlmObsAgentlessEnabled()
213+
&& llmObsAgentlessUrl != null
214+
&& !llmObsAgentlessUrl.isEmpty()) {
215+
hostUrl = HttpUrl.get(llmObsAgentlessUrl);
216+
log.info("Using host URL '{}' to report LLM Obs traces in Agentless mode.", hostUrl);
190217
}
191218
return DDIntakeApi.builder()
192219
.hostUrl(hostUrl)

dd-trace-core/src/main/java/datadog/trace/common/writer/ddintake/DDIntakeMapperDiscovery.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import datadog.trace.civisibility.writer.ddintake.CiTestCycleMapperV1;
77
import datadog.trace.common.writer.RemoteMapper;
88
import datadog.trace.common.writer.RemoteMapperDiscovery;
9+
import datadog.trace.llmobs.writer.ddintake.LLMObsSpanMapper;
910

1011
/**
1112
* Mapper discovery logic when a DDIntake is used. The mapper is discovered based on a backend
@@ -40,6 +41,8 @@ public void discover() {
4041
mapper = new CiTestCycleMapperV1(wellKnownTags, compressionEnabled);
4142
} else if (TrackType.CITESTCOV.equals(trackType)) {
4243
mapper = new CiTestCovMapperV2(compressionEnabled);
44+
} else if (TrackType.LLMOBS.equals(trackType)) {
45+
mapper = new LLMObsSpanMapper();
4346
} else {
4447
mapper = RemoteMapper.NO_OP;
4548
}

0 commit comments

Comments
 (0)