55import datadog .communication .serialization .Writable ;
66import datadog .trace .api .DDTags ;
77import datadog .trace .api .intake .TrackType ;
8+ import datadog .trace .api .llmobs .LLMObs ;
89import datadog .trace .api .llmobs .LLMObsTags ;
910import datadog .trace .bootstrap .instrumentation .api .InternalSpanTypes ;
1011import datadog .trace .bootstrap .instrumentation .api .Tags ;
@@ -63,6 +64,17 @@ public class LLMObsSpanMapper implements RemoteMapper {
6364 private static final byte [] METRICS = "metrics" .getBytes (StandardCharsets .UTF_8 );
6465 private static final byte [] TAGS = "tags" .getBytes (StandardCharsets .UTF_8 );
6566
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+
6678 private final LLMObsSpanMapper .MetaWriter metaWriter = new MetaWriter ();
6779 private final int size ;
6880
@@ -242,8 +254,7 @@ public void accept(Metadata metadata) {
242254 for (Map .Entry <String , Object > tag : metadata .getTags ().entrySet ()) {
243255 String key = tag .getKey ();
244256 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 )) {
247258 writable .writeObject (key .substring (LLMOBS_TAG_PREFIX .length ()) + ":" + value , null );
248259 }
249260 }
@@ -266,8 +277,52 @@ public void accept(Metadata metadata) {
266277 if (key .equals (INPUT ) || key .equals (OUTPUT )) {
267278 if (!spanKind .equals (Tags .LLMOBS_LLM_SPAN_KIND )) {
268279 key += ".value" ;
280+ writable .writeString (key , null );
281+ writable .writeObject (val , null );
269282 } 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 ;
270291 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+ }
271326 }
272327 } else if (key .equals (LLMObsTags .METADATA ) && val instanceof Map ) {
273328 Map <String , Object > metadataMap = (Map ) val ;
@@ -277,10 +332,10 @@ public void accept(Metadata metadata) {
277332 writable .writeString (entry .getKey (), null );
278333 writable .writeObject (entry .getValue (), null );
279334 }
280- continue ;
335+ } else {
336+ writable .writeString (key , null );
337+ writable .writeObject (val , null );
281338 }
282- writable .writeString (key , null );
283- writable .writeObject (val , null );
284339 }
285340 }
286341 }
0 commit comments