2727import datadog .trace .bootstrap .instrumentation .api .Tags ;
2828import datadog .trace .bootstrap .instrumentation .api .UTF8BytesString ;
2929import datadog .trace .bootstrap .instrumentation .decorator .ClientDecorator ;
30+ import java .util .ArrayList ;
3031import java .util .Collections ;
3132import java .util .HashMap ;
3233import java .util .List ;
@@ -192,7 +193,7 @@ public void decorateWithClientOptions(AgentSpan span, ClientOptions clientOption
192193 // clientOptions.queryParams().values("api-version")
193194 }
194195
195- public void decorateChatCompletion (AgentSpan span , ChatCompletionCreateParams params ) {
196+ public void decorateChatCompletion (AgentSpan span , ChatCompletionCreateParams params , boolean stream ) {
196197 span .setResourceName (CHAT_COMPLETIONS_CREATE );
197198 span .setTag ("openai.request.endpoint" , "v1/chat/completions" );
198199 span .setTag ("openai.request.method" , "POST" );
@@ -206,9 +207,17 @@ public void decorateChatCompletion(AgentSpan span, ChatCompletionCreateParams pa
206207 params .messages ().stream ().map (OpenAiDecorator ::llmMessage ).collect (Collectors .toList ()));
207208
208209 Map <String , Object > metadata = new HashMap <>();
210+ // maxTokens is deprecated but integration tests missing to provide maxCompletionTokens
209211 params .maxTokens ().ifPresent (v -> metadata .put ("max_tokens" , v ));
210- // params.maxCompletionTokens().ifPresent(v -> metadata.put("max_tokens", v));
211212 params .temperature ().ifPresent (v -> metadata .put ("temperature" , v ));
213+ if (stream ) {
214+ metadata .put ("stream" , true );
215+ }
216+ params .streamOptions ().ifPresent (v -> {
217+ if (v .includeUsage ().orElse (false )) {
218+ metadata .put ("stream_options" , Collections .singletonMap ("include_usage" , true ));
219+ }
220+ });
212221 span .setTag ("_ml_obs_tag.metadata" , metadata );
213222 }
214223
@@ -270,11 +279,50 @@ private static LLMObs.LLMMessage llmMessage(ChatCompletion.Choice choice) {
270279 }
271280
272281 public void decorateWithChatCompletionChunks (AgentSpan span , List <ChatCompletionChunk > chunks ) {
273- if (! chunks .isEmpty ()) {
274- span . setTag ( RESPONSE_MODEL , chunks . get ( 0 ). model ()) ;
282+ if (chunks .isEmpty ()) {
283+ return ;
275284 }
285+ ChatCompletionChunk firstChunk = chunks .get (0 );
286+ String modelName = firstChunk .model ();
287+ span .setTag (RESPONSE_MODEL , modelName );
276288
277- // TODO set LLMObs tags
289+ span .setTag ("_ml_obs_tag.model_name" , modelName );
290+ span .setTag ("_ml_obs_tag.model_provider" , "openai" );
291+ // span.setTag("_ml_obs_tag.model_version", ); // TODO split and set version, e.g.
292+ // gpt-3.5-turbo-instruct:20230824-v2
293+
294+ // assume that number of choices is the same for each chunk
295+ final int choiceNum = firstChunk .choices ().size ();
296+ // collect roles by choices by the first chunk
297+ String [] roles = new String [choiceNum ];
298+ for (int i =0 ; i < choiceNum ; i ++) {
299+ ChatCompletionChunk .Choice choice = firstChunk .choices ().get (i );
300+ Optional <String > role = choice .delta ().role ().flatMap (r -> r ._value ().asString ());
301+ if (role .isPresent ()) {
302+ roles [i ] = role .get ();
303+ }
304+ }
305+ // collect content by choices for all chunks
306+ StringBuilder [] contents = new StringBuilder [choiceNum ];
307+ for (int i =0 ; i < choiceNum ; i ++) {
308+ contents [i ] = new StringBuilder (128 );
309+ }
310+ for (ChatCompletionChunk chunk : chunks ) {
311+ // choices can be empty for the last chunk
312+ List <ChatCompletionChunk .Choice > choices = chunk .choices ();
313+ for (int i =0 ; i < choiceNum && i < choices .size (); i ++) {
314+ ChatCompletionChunk .Choice choice = choices .get (i );
315+ ChatCompletionChunk .Choice .Delta delta = choice .delta ();
316+ delta .content ().ifPresent (contents [i ]::append );
317+ }
318+ chunk .usage ().ifPresent (usage -> OpenAiDecorator .annotateWithCompletionUsage (span , usage ));
319+ }
320+ // build LLMMessages
321+ List <LLMObs .LLMMessage > llmMessages = new ArrayList <>(choiceNum );
322+ for (int i =0 ; i < choiceNum ; i ++) {
323+ llmMessages .add (LLMObs .LLMMessage .from (roles [i ], contents [i ].toString ()));
324+ }
325+ span .setTag ("_ml_obs_tag.output" , llmMessages );
278326 }
279327
280328 public void decorateEmbedding (AgentSpan span , EmbeddingCreateParams params ) {
0 commit comments