Remove the reactive advisor observability support
- Add Anthropic WebClient.Builder autoconfiguration to activate the OOTB WebClient obsercabilty support.
This commit is contained in:
committed by
Mark Pollack
parent
9dbb39ac4a
commit
99a4fec214
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user