Skip to content

Commit 29cdc0b

Browse files
committed
Simplify and remove duplicate logic in AI model observation components.
Closes #1653
1 parent 0d2d4b7 commit 29cdc0b

File tree

7 files changed

+379
-149
lines changed

7 files changed

+379
-149
lines changed

spring-ai-core/src/main/java/org/springframework/ai/chat/observation/ChatModelCompletionObservationFilter.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,26 +22,28 @@
2222
import org.springframework.ai.observation.tracing.TracingHelper;
2323

2424
/**
25-
* An {@link ObservationFilter} to include the chat completion content in the observation.
25+
* {@link ObservationFilter} used to include chat completion content in the
26+
* {@link Observation}.
2627
*
2728
* @author Thomas Vitale
29+
* @author John Blum
2830
* @since 1.0.0
2931
*/
3032
public class ChatModelCompletionObservationFilter implements ObservationFilter {
3133

3234
@Override
3335
public Observation.Context map(Observation.Context context) {
34-
if (!(context instanceof ChatModelObservationContext chatModelObservationContext)) {
35-
return context;
36-
}
3736

38-
var completions = ChatModelObservationContentProcessor.completion(chatModelObservationContext);
37+
if (context instanceof ChatModelObservationContext chatModelObservationContext) {
38+
39+
var completions = ChatModelObservationContentProcessor.completion(chatModelObservationContext);
3940

40-
chatModelObservationContext
41-
.addHighCardinalityKeyValue(ChatModelObservationDocumentation.HighCardinalityKeyNames.COMPLETION
42-
.withValue(TracingHelper.concatenateStrings(completions)));
41+
context = chatModelObservationContext
42+
.addHighCardinalityKeyValue(ChatModelObservationDocumentation.HighCardinalityKeyNames.COMPLETION
43+
.withValue(TracingHelper.concatenateStrings(completions)));
44+
}
4345

44-
return chatModelObservationContext;
46+
return context;
4547
}
4648

4749
}

spring-ai-core/src/main/java/org/springframework/ai/chat/observation/ChatModelMeterObservationHandler.java

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,25 @@
1616

1717
package org.springframework.ai.chat.observation;
1818

19+
import java.util.Optional;
20+
1921
import io.micrometer.core.instrument.MeterRegistry;
2022
import io.micrometer.observation.Observation;
2123
import io.micrometer.observation.ObservationHandler;
2224

25+
import org.springframework.ai.chat.metadata.ChatResponseMetadata;
26+
import org.springframework.ai.chat.metadata.Usage;
27+
import org.springframework.ai.chat.model.ChatResponse;
2328
import org.springframework.ai.model.observation.ModelUsageMetricsGenerator;
29+
import org.springframework.lang.Nullable;
2430

2531
/**
26-
* Handler for generating metrics from chat model observations.
32+
* {@link ObservationHandler} used to generate metrics from chat model observations.
2733
*
2834
* @author Thomas Vitale
35+
* @author John Blum
36+
* @see ChatModelObservationContext
37+
* @see ObservationHandler
2938
* @since 1.0.0
3039
*/
3140
public class ChatModelMeterObservationHandler implements ObservationHandler<ChatModelObservationContext> {
@@ -38,11 +47,16 @@ public ChatModelMeterObservationHandler(MeterRegistry meterRegistry) {
3847

3948
@Override
4049
public void onStop(ChatModelObservationContext context) {
41-
if (context.getResponse() != null && context.getResponse().getMetadata() != null
42-
&& context.getResponse().getMetadata().getUsage() != null) {
43-
ModelUsageMetricsGenerator.generate(context.getResponse().getMetadata().getUsage(), context,
44-
this.meterRegistry);
45-
}
50+
resolveUsage(context)
51+
.ifPresent(usage -> ModelUsageMetricsGenerator.generate(usage, context, this.meterRegistry));
52+
}
53+
54+
private Optional<Usage> resolveUsage(@Nullable ChatModelObservationContext context) {
55+
56+
return Optional.ofNullable(context)
57+
.map(ChatModelObservationContext::getResponse)
58+
.map(ChatResponse::getMetadata)
59+
.map(ChatResponseMetadata::getUsage);
4660
}
4761

4862
@Override

spring-ai-core/src/main/java/org/springframework/ai/chat/observation/ChatModelObservationContentProcessor.java

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,46 +16,45 @@
1616

1717
package org.springframework.ai.chat.observation;
1818

19+
import java.util.Collections;
1920
import java.util.List;
21+
import java.util.Optional;
2022

23+
import org.springframework.ai.chat.messages.Message;
24+
import org.springframework.ai.chat.model.ChatResponse;
25+
import org.springframework.ai.chat.model.Generation;
2126
import org.springframework.ai.model.Content;
27+
import org.springframework.lang.Nullable;
2228
import org.springframework.util.CollectionUtils;
2329
import org.springframework.util.StringUtils;
2430

2531
/**
2632
* Utilities to process the prompt and completion content in observations for chat models.
2733
*
2834
* @author Thomas Vitale
35+
* @author John Blum
36+
* @since 1.0.0
2937
*/
30-
public final class ChatModelObservationContentProcessor {
31-
32-
private ChatModelObservationContentProcessor() {
33-
}
38+
public abstract class ChatModelObservationContentProcessor {
3439

3540
public static List<String> prompt(ChatModelObservationContext context) {
36-
if (CollectionUtils.isEmpty(context.getRequest().getInstructions())) {
37-
return List.of();
38-
}
3941

40-
return context.getRequest().getInstructions().stream().map(Content::getContent).toList();
41-
}
42+
List<Message> instructions = context.getRequest().getInstructions();
4243

43-
public static List<String> completion(ChatModelObservationContext context) {
44-
if (context == null || context.getResponse() == null || context.getResponse().getResults() == null
45-
|| CollectionUtils.isEmpty(context.getResponse().getResults())) {
46-
return List.of();
47-
}
44+
return CollectionUtils.isEmpty(instructions) ? Collections.emptyList()
45+
: instructions.stream().map(Content::getContent).toList();
46+
}
4847

49-
if (!StringUtils.hasText(context.getResponse().getResult().getOutput().getContent())) {
50-
return List.of();
51-
}
48+
public static List<String> completion(@Nullable ChatModelObservationContext context) {
5249

53-
return context.getResponse()
54-
.getResults()
50+
return Optional.ofNullable(context)
51+
.map(ChatModelObservationContext::getResponse)
52+
.map(ChatResponse::getResults)
53+
.orElseGet(Collections::emptyList)
5554
.stream()
56-
.filter(generation -> generation.getOutput() != null
57-
&& StringUtils.hasText(generation.getOutput().getContent()))
58-
.map(generation -> generation.getOutput().getContent())
55+
.map(Generation::getOutput)
56+
.map(Message::getContent)
57+
.filter(StringUtils::hasText)
5958
.toList();
6059
}
6160

spring-ai-core/src/main/java/org/springframework/ai/chat/observation/ChatModelPromptContentObservationFilter.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,24 @@
2525
* An {@link ObservationFilter} to include the chat prompt content in the observation.
2626
*
2727
* @author Thomas Vitale
28+
* @author John Blum
2829
* @since 1.0.0
2930
*/
3031
public class ChatModelPromptContentObservationFilter implements ObservationFilter {
3132

3233
@Override
3334
public Observation.Context map(Observation.Context context) {
34-
if (!(context instanceof ChatModelObservationContext chatModelObservationContext)) {
35-
return context;
36-
}
3735

38-
var prompts = ChatModelObservationContentProcessor.prompt(chatModelObservationContext);
36+
if (context instanceof ChatModelObservationContext chatModelObservationContext) {
37+
38+
var prompts = ChatModelObservationContentProcessor.prompt(chatModelObservationContext);
3939

40-
chatModelObservationContext
41-
.addHighCardinalityKeyValue(ChatModelObservationDocumentation.HighCardinalityKeyNames.PROMPT
42-
.withValue(TracingHelper.concatenateStrings(prompts)));
40+
context = chatModelObservationContext
41+
.addHighCardinalityKeyValue(ChatModelObservationDocumentation.HighCardinalityKeyNames.PROMPT
42+
.withValue(TracingHelper.concatenateStrings(prompts)));
43+
}
4344

44-
return chatModelObservationContext;
45+
return context;
4546
}
4647

4748
}

0 commit comments

Comments
 (0)