88import com .openai .models .chat .completions .ChatCompletion ;
99import com .openai .models .chat .completions .ChatCompletionChunk ;
1010import com .openai .models .chat .completions .ChatCompletionCreateParams ;
11+ import com .openai .models .chat .completions .ChatCompletionMessage ;
12+ import com .openai .models .chat .completions .ChatCompletionMessageParam ;
1113import com .openai .models .completions .Completion ;
1214import com .openai .models .completions .CompletionCreateParams ;
15+ import com .openai .models .completions .CompletionUsage ;
1316import com .openai .models .embeddings .CreateEmbeddingResponse ;
1417import com .openai .models .embeddings .EmbeddingCreateParams ;
1518import com .openai .models .responses .Response ;
@@ -127,22 +130,13 @@ public void decorateWithCompletion(AgentSpan span, Completion completion) {
127130 .collect (Collectors .toList ());
128131 span .setTag ("_ml_obs_tag.output" , output );
129132
130- completion
131- .usage ()
132- .ifPresent (
133- u -> {
134- span .setTag ("_ml_obs_metric.input_tokens" , u .promptTokens ());
135- span .setTag ("_ml_obs_metric.output_tokens" , u .completionTokens ());
136- span .setTag ("_ml_obs_metric.total_tokens" , u .totalTokens ());
137- });
133+ completion .usage ().ifPresent (usage -> OpenAiDecorator .annotateWithCompletionUsage (span , usage ));
138134 }
139135
140136 public void decorateWithCompletions (AgentSpan span , List <Completion > completions ) {
141137 if (!completions .isEmpty ()) {
142138 decorateWithCompletion (span , completions .get (0 ));
143139 }
144-
145- // TODO set LLMObs tags (not visible to APM)
146140 }
147141
148142 public void decorateWithHttpResponse (AgentSpan span , HttpResponse response ) {
@@ -206,20 +200,81 @@ public void decorateChatCompletion(AgentSpan span, ChatCompletionCreateParams pa
206200 return ;
207201 }
208202 span .setTag (REQUEST_MODEL , params .model ().asString ()); // TODO extract model, might not be set
203+
204+ span .setTag (
205+ "_ml_obs_tag.input" ,
206+ params .messages ().stream ().map (OpenAiDecorator ::llmMessage ).collect (Collectors .toList ()));
207+
208+ Map <String , Object > metadata = new HashMap <>();
209+ params .maxTokens ().ifPresent (v -> metadata .put ("max_tokens" , v ));
210+ // params.maxCompletionTokens().ifPresent(v -> metadata.put("max_tokens", v));
211+ params .temperature ().ifPresent (v -> metadata .put ("temperature" , v ));
212+ span .setTag ("_ml_obs_tag.metadata" , metadata );
213+ }
214+
215+ private static LLMObs .LLMMessage llmMessage (ChatCompletionMessageParam m ) {
216+ String role = "unknown" ;
217+ String content = null ;
218+ if (m .isAssistant ()) {
219+ role = "assistant" ;
220+ content = m .asAssistant ().content ().map (v -> v .text ().orElse (null )).orElse (null );
221+ } else if (m .isDeveloper ()) {
222+ role = "developer" ;
223+ content = m .asDeveloper ().content ().text ().orElse (null );
224+ } else if (m .isSystem ()) {
225+ role = "system" ;
226+ content = m .asSystem ().content ().text ().orElse (null );
227+ } else if (m .isTool ()) {
228+ role = "tool" ;
229+ content = m .asTool ().content ().text ().orElse (null );
230+ } else if (m .isUser ()) {
231+ role = "user" ;
232+ content = m .asUser ().content ().text ().orElse (null );
233+ }
234+ return LLMObs .LLMMessage .from (role , content );
209235 }
210236
211237 public void decorateWithChatCompletion (AgentSpan span , ChatCompletion completion ) {
212- span .setTag (RESPONSE_MODEL , completion .model ());
238+ String modelName = completion .model ();
239+ span .setTag (RESPONSE_MODEL , modelName );
213240
214- // TODO set LLMObs tags (not visible to APM)
241+ span .setTag ("_ml_obs_tag.model_name" , modelName );
242+ span .setTag ("_ml_obs_tag.model_provider" , "openai" );
243+ // span.setTag("_ml_obs_tag.model_version", ); // TODO split and set version, e.g.
244+ // gpt-3.5-turbo-instruct:20230824-v2c
245+
246+ List <LLMObs .LLMMessage > output =
247+ completion .choices ().stream ()
248+ .map (OpenAiDecorator ::llmMessage )
249+ .collect (Collectors .toList ());
250+ span .setTag ("_ml_obs_tag.output" , output );
251+
252+ completion .usage ().ifPresent (usage -> OpenAiDecorator .annotateWithCompletionUsage (span , usage ));
253+ }
254+
255+ private static void annotateWithCompletionUsage (AgentSpan span , CompletionUsage usage ) {
256+ span .setTag ("_ml_obs_metric.input_tokens" , usage .promptTokens ());
257+ span .setTag ("_ml_obs_metric.output_tokens" , usage .completionTokens ());
258+ span .setTag ("_ml_obs_metric.total_tokens" , usage .totalTokens ());
259+ }
260+
261+ private static LLMObs .LLMMessage llmMessage (ChatCompletion .Choice choice ) {
262+ ChatCompletionMessage msg = choice .message ();
263+ Optional <?> roleOpt = msg ._role ().asString ();
264+ String role = "unknown" ;
265+ if (roleOpt .isPresent ()) {
266+ role = String .valueOf (roleOpt .get ());
267+ }
268+ String content = msg .content ().orElse (null );
269+ return LLMObs .LLMMessage .from (role , content );
215270 }
216271
217272 public void decorateWithChatCompletionChunks (AgentSpan span , List <ChatCompletionChunk > chunks ) {
218273 if (!chunks .isEmpty ()) {
219274 span .setTag (RESPONSE_MODEL , chunks .get (0 ).model ());
220275 }
221276
222- // TODO set LLMObs tags (not visible to APM)
277+ // TODO set LLMObs tags
223278 }
224279
225280 public void decorateEmbedding (AgentSpan span , EmbeddingCreateParams params ) {
@@ -231,13 +286,13 @@ public void decorateEmbedding(AgentSpan span, EmbeddingCreateParams params) {
231286 }
232287 span .setTag (REQUEST_MODEL , params .model ().asString ()); // TODO extract model, might not be set
233288
234- // TODO set LLMObs tags (not visible to APM)
289+ // TODO set LLMObs tags
235290 }
236291
237292 public void decorateWithEmbedding (AgentSpan span , CreateEmbeddingResponse response ) {
238293 span .setTag (RESPONSE_MODEL , response .model ());
239294
240- // TODO set LLMObs tags (not visible to APM)
295+ // TODO set LLMObs tags
241296 }
242297
243298 public void decorateResponse (AgentSpan span , ResponseCreateParams params ) {
@@ -256,7 +311,7 @@ public void decorateResponse(AgentSpan span, ResponseCreateParams params) {
256311 public void decorateWithResponse (AgentSpan span , Response response ) {
257312 span .setTag (RESPONSE_MODEL , extractResponseModel (response ._model ()));
258313
259- // TODO set LLMObs tags (not visible to APM)
314+ // TODO set LLMObs tags
260315 }
261316
262317 private String extractResponseModel (JsonField <ResponsesModel > model ) {
@@ -292,6 +347,6 @@ public void decorateWithResponseStreamEvent(AgentSpan span, List<ResponseStreamE
292347 // span.setTag(RESPONSE_MODEL, responseStreamEvent.res()); // TODO there is no model
293348 }
294349
295- // TODO set LLMObs tags (not visible to APM)
350+ // TODO set LLMObs tags
296351 }
297352}
0 commit comments