5
5
import datadog .communication .serialization .Writable ;
6
6
import datadog .trace .api .DDTags ;
7
7
import datadog .trace .api .intake .TrackType ;
8
+ import datadog .trace .api .llmobs .LLMObs ;
8
9
import datadog .trace .api .llmobs .LLMObsTags ;
9
10
import datadog .trace .bootstrap .instrumentation .api .InternalSpanTypes ;
10
11
import datadog .trace .bootstrap .instrumentation .api .Tags ;
@@ -63,6 +64,17 @@ public class LLMObsSpanMapper implements RemoteMapper {
63
64
private static final byte [] METRICS = "metrics" .getBytes (StandardCharsets .UTF_8 );
64
65
private static final byte [] TAGS = "tags" .getBytes (StandardCharsets .UTF_8 );
65
66
67
+ private static final byte [] LLM_MESSAGE_ROLE = "role" .getBytes (StandardCharsets .UTF_8 );
68
+ private static final byte [] LLM_MESSAGE_CONTENT = "content" .getBytes (StandardCharsets .UTF_8 );
69
+ private static final byte [] LLM_MESSAGE_TOOL_CALLS =
70
+ "tool_calls" .getBytes (StandardCharsets .UTF_8 );
71
+
72
+ private static final byte [] LLM_TOOL_CALL_NAME = "name" .getBytes (StandardCharsets .UTF_8 );
73
+ private static final byte [] LLM_TOOL_CALL_TYPE = "type" .getBytes (StandardCharsets .UTF_8 );
74
+ private static final byte [] LLM_TOOL_CALL_TOOL_ID = "tool_id" .getBytes (StandardCharsets .UTF_8 );
75
+ private static final byte [] LLM_TOOL_CALL_ARGUMENTS =
76
+ "arguments" .getBytes (StandardCharsets .UTF_8 );
77
+
66
78
private final LLMObsSpanMapper .MetaWriter metaWriter = new MetaWriter ();
67
79
private final int size ;
68
80
@@ -242,8 +254,7 @@ public void accept(Metadata metadata) {
242
254
for (Map .Entry <String , Object > tag : metadata .getTags ().entrySet ()) {
243
255
String key = tag .getKey ();
244
256
Object value = tag .getValue ();
245
- if (!tagsToRemapToMeta .containsKey (key )
246
- && key .startsWith (LLMOBS_TAG_PREFIX )) {
257
+ if (!tagsToRemapToMeta .containsKey (key ) && key .startsWith (LLMOBS_TAG_PREFIX )) {
247
258
writable .writeObject (key .substring (LLMOBS_TAG_PREFIX .length ()) + ":" + value , null );
248
259
}
249
260
}
@@ -266,8 +277,52 @@ public void accept(Metadata metadata) {
266
277
if (key .equals (INPUT ) || key .equals (OUTPUT )) {
267
278
if (!spanKind .equals (Tags .LLMOBS_LLM_SPAN_KIND )) {
268
279
key += ".value" ;
280
+ writable .writeString (key , null );
281
+ writable .writeObject (val , null );
269
282
} else {
283
+ if (!(val instanceof List )) {
284
+ LOGGER .warn (
285
+ "unexpectedly found incorrect type for LLM span IO {}, expecting list" ,
286
+ val .getClass ().getName ());
287
+ continue ;
288
+ }
289
+ // llm span kind must have llm objects
290
+ List <LLMObs .LLMMessage > messages = (List <LLMObs .LLMMessage >) val ;
270
291
key += ".messages" ;
292
+ writable .writeString (key , null );
293
+ writable .startArray (messages .size ());
294
+ for (LLMObs .LLMMessage message : messages ) {
295
+ List <LLMObs .ToolCall > toolCalls = message .getToolCalls ();
296
+ boolean hasToolCalls = null != toolCalls && !toolCalls .isEmpty ();
297
+ writable .startMap (hasToolCalls ? 3 : 2 );
298
+ writable .writeUTF8 (LLM_MESSAGE_ROLE );
299
+ writable .writeString (message .getRole (), null );
300
+ writable .writeUTF8 (LLM_MESSAGE_CONTENT );
301
+ writable .writeString (message .getContent (), null );
302
+ if (hasToolCalls ) {
303
+ writable .writeUTF8 (LLM_MESSAGE_TOOL_CALLS );
304
+ writable .startArray (toolCalls .size ());
305
+ for (LLMObs .ToolCall toolCall : toolCalls ) {
306
+ Map <String , Object > arguments = toolCall .getArguments ();
307
+ boolean hasArguments = null != arguments && !arguments .isEmpty ();
308
+ writable .startMap (hasArguments ? 4 : 3 );
309
+ writable .writeUTF8 (LLM_TOOL_CALL_NAME );
310
+ writable .writeString (toolCall .getName (), null );
311
+ writable .writeUTF8 (LLM_TOOL_CALL_TYPE );
312
+ writable .writeString (toolCall .getType (), null );
313
+ writable .writeUTF8 (LLM_TOOL_CALL_TOOL_ID );
314
+ writable .writeString (toolCall .getToolID (), null );
315
+ if (hasArguments ) {
316
+ writable .writeUTF8 (LLM_TOOL_CALL_ARGUMENTS );
317
+ writable .startMap (arguments .size ());
318
+ for (Map .Entry <String , Object > argument : arguments .entrySet ()) {
319
+ writable .writeString (argument .getKey (), null );
320
+ writable .writeObject (argument .getValue (), null );
321
+ }
322
+ }
323
+ }
324
+ }
325
+ }
271
326
}
272
327
} else if (key .equals (LLMObsTags .METADATA ) && val instanceof Map ) {
273
328
Map <String , Object > metadataMap = (Map ) val ;
@@ -277,10 +332,10 @@ public void accept(Metadata metadata) {
277
332
writable .writeString (entry .getKey (), null );
278
333
writable .writeObject (entry .getValue (), null );
279
334
}
280
- continue ;
335
+ } else {
336
+ writable .writeString (key , null );
337
+ writable .writeObject (val , null );
281
338
}
282
- writable .writeString (key , null );
283
- writable .writeObject (val , null );
284
339
}
285
340
}
286
341
}
0 commit comments