Remove the reactive advisor observability support

-  Add Anthropic WebClient.Builder autoconfiguration to activate the OOTB WebClient obsercabilty support.
This commit is contained in:
Christian Tzolov
2024-08-23 12:36:06 +02:00
committed by Mark Pollack
parent 9dbb39ac4a
commit 99a4fec214
4 changed files with 28 additions and 43 deletions

View File

@@ -91,7 +91,7 @@ public class AnthropicApi {
* @param anthropicApiKey Anthropic api Key.
*/
public AnthropicApi(String baseUrl, String anthropicApiKey) {
this(baseUrl, anthropicApiKey, DEFAULT_ANTHROPIC_VERSION, RestClient.builder(),
this(baseUrl, anthropicApiKey, DEFAULT_ANTHROPIC_VERSION, RestClient.builder(), WebClient.builder(),
RetryUtils.DEFAULT_RESPONSE_ERROR_HANDLER);
}
@@ -103,8 +103,9 @@ public class AnthropicApi {
* @param responseErrorHandler Response error handler.
*/
public AnthropicApi(String baseUrl, String anthropicApiKey, String anthropicVersion,
RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) {
this(baseUrl, anthropicApiKey, anthropicVersion, restClientBuilder, responseErrorHandler,
RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder,
ResponseErrorHandler responseErrorHandler) {
this(baseUrl, anthropicApiKey, anthropicVersion, restClientBuilder, webClientBuilder, responseErrorHandler,
DEFAULT_ANTHROPIC_BETA_VERSION);
}
@@ -117,8 +118,8 @@ public class AnthropicApi {
* @param anthropicBetaFeatures Anthropic beta features.
*/
public AnthropicApi(String baseUrl, String anthropicApiKey, String anthropicVersion,
RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler,
String anthropicBetaFeatures) {
RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder,
ResponseErrorHandler responseErrorHandler, String anthropicBetaFeatures) {
Consumer<HttpHeaders> jsonContentHeaders = headers -> {
headers.add(HEADER_X_API_KEY, anthropicApiKey);
@@ -132,8 +133,7 @@ public class AnthropicApi {
.defaultStatusHandler(responseErrorHandler)
.build();
this.webClient = WebClient.builder()
.baseUrl(baseUrl)
this.webClient = webClientBuilder.baseUrl(baseUrl)
.defaultHeaders(jsonContentHeaders)
.defaultStatusHandler(HttpStatusCode::isError,
resp -> Mono.just(new RuntimeException("Response exception, Status: [" + resp.statusCode()

View File

@@ -15,15 +15,19 @@
*/
package org.springframework.ai.anthropic;
import io.micrometer.common.KeyValue;
import io.micrometer.observation.tck.TestObservationRegistry;
import io.micrometer.observation.tck.TestObservationRegistryAssert;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.springframework.ai.anthropic.api.AnthropicApi;
import org.springframework.ai.chat.metadata.ChatResponseMetadata;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.observation.ChatModelObservationDocumentation.HighCardinalityKeyNames;
import org.springframework.ai.chat.observation.ChatModelObservationDocumentation.LowCardinalityKeyNames;
import org.springframework.ai.chat.observation.DefaultChatModelObservationConvention;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.function.FunctionCallbackContext;
@@ -34,15 +38,12 @@ import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.retry.support.RetryTemplate;
import io.micrometer.common.KeyValue;
import io.micrometer.observation.tck.TestObservationRegistry;
import io.micrometer.observation.tck.TestObservationRegistryAssert;
import reactor.core.publisher.Flux;
import java.util.List;
import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.ai.chat.observation.ChatModelObservationDocumentation.HighCardinalityKeyNames;
import static org.springframework.ai.chat.observation.ChatModelObservationDocumentation.LowCardinalityKeyNames;
/**
* Integration tests for observation instrumentation in {@link AnthropicChatModel}.
*
@@ -67,7 +68,7 @@ public class AnthropicChatModelObservationIT {
@Test
void observationForChatOperation() {
var options = AnthropicChatOptions.builder()
.withModel(AnthropicApi.ChatModel.CLAUDE_3_5_SONNET.getValue())
.withModel(AnthropicApi.ChatModel.CLAUDE_3_HAIKU.getValue())
.withMaxTokens(2048)
.withStopSequences(List.of("this-is-the-end"))
.withTemperature(0.7f)
@@ -89,7 +90,7 @@ public class AnthropicChatModelObservationIT {
@Test
void observationForStreamingChatOperation() {
var options = AnthropicChatOptions.builder()
.withModel(AnthropicApi.ChatModel.CLAUDE_3_5_SONNET.getValue())
.withModel(AnthropicApi.ChatModel.CLAUDE_3_HAIKU.getValue())
.withMaxTokens(2048)
.withStopSequences(List.of("this-is-the-end"))
.withTemperature(0.7f)
@@ -125,12 +126,12 @@ public class AnthropicChatModelObservationIT {
.doesNotHaveAnyRemainingCurrentObservation()
.hasObservationWithNameEqualTo(DefaultChatModelObservationConvention.DEFAULT_NAME)
.that()
.hasContextualNameEqualTo("chat " + AnthropicApi.ChatModel.CLAUDE_3_5_SONNET.getValue())
.hasContextualNameEqualTo("chat " + AnthropicApi.ChatModel.CLAUDE_3_HAIKU.getValue())
.hasLowCardinalityKeyValue(LowCardinalityKeyNames.AI_OPERATION_TYPE.asString(),
AiOperationType.CHAT.value())
.hasLowCardinalityKeyValue(LowCardinalityKeyNames.AI_PROVIDER.asString(), AiProvider.ANTHROPIC.value())
.hasLowCardinalityKeyValue(LowCardinalityKeyNames.REQUEST_MODEL.asString(),
AnthropicApi.ChatModel.CLAUDE_3_5_SONNET.getValue())
AnthropicApi.ChatModel.CLAUDE_3_HAIKU.getValue())
.hasLowCardinalityKeyValue(LowCardinalityKeyNames.RESPONSE_MODEL.asString(), responseMetadata.getModel())
.hasHighCardinalityKeyValue(HighCardinalityKeyNames.REQUEST_FREQUENCY_PENALTY.asString(),
KeyValue.NONE_VALUE)

View File

@@ -23,9 +23,7 @@ import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;
import reactor.core.publisher.Flux;
/**
@@ -84,24 +82,8 @@ public class ObservableRequestResponseAdvisor implements RequestResponseAdvisor
@Override
public Flux<ChatResponse> adviseResponse(Flux<ChatResponse> fluxResponse, Map<String, Object> context) {
return Flux.deferContextual(contextView -> {
var observationContext = this.doCreateObservationContextBuilder(AdvisorObservationContext.Type.AFTER)
.withAdvisorResponseContext(context)
.build();
Observation observation = AdvisorObservationDocumentation.AI_ADVISOR.observation(
this.customObservationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,
this.observationRegistry);
observation.parentObservation(contextView.getOrDefault(ObservationThreadLocalAccessor.KEY, null)).start();
// @formatter:off
return this.targetAdvisor.adviseResponse(fluxResponse, context)
.doOnError(observation::error)
.doFinally(s -> observation.stop())
.contextWrite(ctx -> ctx.put(ObservationThreadLocalAccessor.KEY, observation));
// @formatter:on
});
// NOTE: The reactive observation support is not available yet.
return this.targetAdvisor.adviseResponse(fluxResponse, context);
}
/**

View File

@@ -38,6 +38,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
/**
* @author Christian Tzolov
@@ -56,10 +57,11 @@ public class AnthropicAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public AnthropicApi anthropicApi(AnthropicConnectionProperties connectionProperties,
RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) {
RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder,
ResponseErrorHandler responseErrorHandler) {
return new AnthropicApi(connectionProperties.getBaseUrl(), connectionProperties.getApiKey(),
connectionProperties.getVersion(), restClientBuilder, responseErrorHandler,
connectionProperties.getVersion(), restClientBuilder, webClientBuilder, responseErrorHandler,
connectionProperties.getBetaVersion());
}