diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-anthropic/src/main/java/org/springframework/ai/model/anthropic/autoconfigure/AnthropicChatAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-anthropic/src/main/java/org/springframework/ai/model/anthropic/autoconfigure/AnthropicChatAutoConfiguration.java index 760f87afc..93e5c2640 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-anthropic/src/main/java/org/springframework/ai/model/anthropic/autoconfigure/AnthropicChatAutoConfiguration.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-anthropic/src/main/java/org/springframework/ai/model/anthropic/autoconfigure/AnthropicChatAutoConfiguration.java @@ -52,7 +52,7 @@ import org.springframework.web.reactive.function.client.WebClient; * @since 1.0.0 */ @AutoConfiguration(after = { RestClientAutoConfiguration.class, SpringAiRetryAutoConfiguration.class, - ToolCallingAutoConfiguration.class }) + ToolCallingAutoConfiguration.class, WebClientAutoConfiguration.class }) @EnableConfigurationProperties({ AnthropicChatProperties.class, AnthropicConnectionProperties.class }) @ConditionalOnClass(AnthropicApi.class) @ConditionalOnProperty(name = SpringAIModelProperties.CHAT_MODEL, havingValue = SpringAIModels.ANTHROPIC, diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiChatAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiChatAutoConfiguration.java index 3be4210a8..bd40285ea 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiChatAutoConfiguration.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiChatAutoConfiguration.java @@ -35,6 +35,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration; +import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.retry.support.RetryTemplate; @@ -42,6 +43,7 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.RestClient; +import org.springframework.web.reactive.function.client.WebClient; /** * Chat {@link AutoConfiguration Auto-configuration} for Mistral AI. @@ -52,28 +54,30 @@ import org.springframework.web.client.RestClient; * @author Ilayaperumal Gopinathan * @since 0.8.1 */ -@AutoConfiguration(after = { RestClientAutoConfiguration.class, SpringAiRetryAutoConfiguration.class, - ToolCallingAutoConfiguration.class }) +@AutoConfiguration(after = { RestClientAutoConfiguration.class, WebClientAutoConfiguration.class, + SpringAiRetryAutoConfiguration.class, ToolCallingAutoConfiguration.class }) @EnableConfigurationProperties({ MistralAiCommonProperties.class, MistralAiChatProperties.class }) @ConditionalOnProperty(name = SpringAIModelProperties.CHAT_MODEL, havingValue = SpringAIModels.MISTRAL, matchIfMissing = true) @ConditionalOnClass(MistralAiApi.class) @ImportAutoConfiguration(classes = { SpringAiRetryAutoConfiguration.class, RestClientAutoConfiguration.class, - ToolCallingAutoConfiguration.class }) + WebClientAutoConfiguration.class, ToolCallingAutoConfiguration.class }) public class MistralAiChatAutoConfiguration { @Bean @ConditionalOnMissingBean public MistralAiChatModel mistralAiChatModel(MistralAiCommonProperties commonProperties, MistralAiChatProperties chatProperties, ObjectProvider restClientBuilderProvider, - ToolCallingManager toolCallingManager, RetryTemplate retryTemplate, - ResponseErrorHandler responseErrorHandler, ObjectProvider observationRegistry, + ObjectProvider webClientBuilderProvider, ToolCallingManager toolCallingManager, + RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler, + ObjectProvider observationRegistry, ObjectProvider observationConvention, ObjectProvider mistralAiToolExecutionEligibilityPredicate) { var mistralAiApi = mistralAiApi(chatProperties.getApiKey(), commonProperties.getApiKey(), chatProperties.getBaseUrl(), commonProperties.getBaseUrl(), - restClientBuilderProvider.getIfAvailable(RestClient::builder), responseErrorHandler); + restClientBuilderProvider.getIfAvailable(RestClient::builder), + webClientBuilderProvider.getIfAvailable(WebClient::builder), responseErrorHandler); var chatModel = MistralAiChatModel.builder() .mistralAiApi(mistralAiApi) @@ -91,7 +95,8 @@ public class MistralAiChatAutoConfiguration { } private MistralAiApi mistralAiApi(String apiKey, String commonApiKey, String baseUrl, String commonBaseUrl, - RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) { + RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder, + ResponseErrorHandler responseErrorHandler) { var resolvedApiKey = StringUtils.hasText(apiKey) ? apiKey : commonApiKey; var resoledBaseUrl = StringUtils.hasText(baseUrl) ? baseUrl : commonBaseUrl; @@ -99,7 +104,8 @@ public class MistralAiChatAutoConfiguration { Assert.hasText(resolvedApiKey, "Mistral API key must be set"); Assert.hasText(resoledBaseUrl, "Mistral base URL must be set"); - return new MistralAiApi(resoledBaseUrl, resolvedApiKey, restClientBuilder, responseErrorHandler); + return new MistralAiApi(resoledBaseUrl, resolvedApiKey, restClientBuilder, webClientBuilder, + responseErrorHandler); } } diff --git a/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/api/AnthropicApi.java b/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/api/AnthropicApi.java index a5aa780a0..d7c1aacd2 100644 --- a/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/api/AnthropicApi.java +++ b/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/api/AnthropicApi.java @@ -119,12 +119,14 @@ public class AnthropicApi { this.completionsPath = completionsPath; - this.restClient = restClientBuilder.baseUrl(baseUrl) + this.restClient = restClientBuilder.clone() + .baseUrl(baseUrl) .defaultHeaders(jsonContentHeaders) .defaultStatusHandler(responseErrorHandler) .build(); - this.webClient = webClientBuilder.baseUrl(baseUrl) + this.webClient = webClientBuilder.clone() + .baseUrl(baseUrl) .defaultHeaders(jsonContentHeaders) .defaultStatusHandler(HttpStatusCode::isError, resp -> resp.bodyToMono(String.class) diff --git a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/api/AnthropicApiIT.java b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/api/AnthropicApiIT.java index 67f0b61ed..fe80f72a3 100644 --- a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/api/AnthropicApiIT.java +++ b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/api/AnthropicApiIT.java @@ -110,7 +110,7 @@ public class AnthropicApiIT { void chatCompletionStreamError() { AnthropicMessage chatCompletionMessage = new AnthropicMessage(List.of(new ContentBlock("Tell me a Joke?")), Role.USER); - AnthropicApi api = AnthropicApi.builder().baseUrl("FAKE_KEY_FOR_ERROR_RESPONSE").build(); + AnthropicApi api = AnthropicApi.builder().apiKey("FAKE_KEY_FOR_ERROR_RESPONSE").build(); Flux response = api.chatCompletionStream(new ChatCompletionRequest( AnthropicApi.ChatModel.CLAUDE_3_OPUS.getValue(), List.of(chatCompletionMessage), null, 100, 0.8, true)); diff --git a/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/api/MiniMaxApi.java b/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/api/MiniMaxApi.java index 0d6dabaa8..eeb6c859a 100644 --- a/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/api/MiniMaxApi.java +++ b/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/api/MiniMaxApi.java @@ -115,7 +115,7 @@ public class MiniMaxApi { .defaultStatusHandler(responseErrorHandler) .build(); - this.webClient = WebClient.builder() + this.webClient = WebClient.builder() // FIXME: use a bean instead .baseUrl(baseUrl) .defaultHeaders(authHeaders) .build(); diff --git a/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/api/MistralAiApi.java b/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/api/MistralAiApi.java index 7b32a5911..172961c8e 100644 --- a/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/api/MistralAiApi.java +++ b/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/api/MistralAiApi.java @@ -101,18 +101,31 @@ public class MistralAiApi { */ public MistralAiApi(String baseUrl, String mistralAiApiKey, RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) { + this(baseUrl, mistralAiApiKey, restClientBuilder, WebClient.builder(), responseErrorHandler); + } + + /** + * Create a new client api. + * @param baseUrl api base URL. + * @param mistralAiApiKey Mistral api Key. + * @param restClientBuilder RestClient builder. + * @param responseErrorHandler Response error handler. + */ + public MistralAiApi(String baseUrl, String mistralAiApiKey, RestClient.Builder restClientBuilder, + WebClient.Builder webClientBuilder, ResponseErrorHandler responseErrorHandler) { Consumer jsonContentHeaders = headers -> { headers.setBearerAuth(mistralAiApiKey); headers.setContentType(MediaType.APPLICATION_JSON); }; - this.restClient = restClientBuilder.baseUrl(baseUrl) + this.restClient = restClientBuilder.clone() + .baseUrl(baseUrl) .defaultHeaders(jsonContentHeaders) .defaultStatusHandler(responseErrorHandler) .build(); - this.webClient = WebClient.builder().baseUrl(baseUrl).defaultHeaders(jsonContentHeaders).build(); + this.webClient = webClientBuilder.clone().baseUrl(baseUrl).defaultHeaders(jsonContentHeaders).build(); } /** diff --git a/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/api/OllamaApi.java b/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/api/OllamaApi.java index 02f1e8816..0a440f9de 100644 --- a/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/api/OllamaApi.java +++ b/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/api/OllamaApi.java @@ -79,12 +79,15 @@ public class OllamaApi { headers.setAccept(List.of(MediaType.APPLICATION_JSON)); }; - this.restClient = restClientBuilder.baseUrl(baseUrl) + this.restClient = restClientBuilder + .clone() + .baseUrl(baseUrl) .defaultHeaders(defaultHeaders) .defaultStatusHandler(responseErrorHandler) .build(); this.webClient = webClientBuilder + .clone() .baseUrl(baseUrl) .defaultHeaders(defaultHeaders) .build(); diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java index 3cc5d5b48..a0da1221d 100644 --- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java +++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java @@ -115,12 +115,13 @@ public class OpenAiApi { h.setContentType(MediaType.APPLICATION_JSON); h.addAll(headers); }; - this.restClient = restClientBuilder.baseUrl(baseUrl) + this.restClient = restClientBuilder.clone() + .baseUrl(baseUrl) .defaultHeaders(finalHeaders) .defaultStatusHandler(responseErrorHandler) .build(); - this.webClient = webClientBuilder + this.webClient = webClientBuilder.clone() .baseUrl(baseUrl) .defaultHeaders(finalHeaders) .build(); // @formatter:on diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiAudioApi.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiAudioApi.java index 8974081b2..21775d5e7 100644 --- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiAudioApi.java +++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiAudioApi.java @@ -77,12 +77,13 @@ public class OpenAiAudioApi { // h.setContentType(MediaType.APPLICATION_JSON); }; - this.restClient = restClientBuilder.baseUrl(baseUrl) + this.restClient = restClientBuilder.clone() + .baseUrl(baseUrl) .defaultHeaders(authHeaders) .defaultStatusHandler(responseErrorHandler) .build(); - this.webClient = webClientBuilder.baseUrl(baseUrl).defaultHeaders(authHeaders).build(); + this.webClient = webClientBuilder.clone().baseUrl(baseUrl).defaultHeaders(authHeaders).build(); } public static Builder builder() { diff --git a/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/api/ZhiPuAiApi.java b/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/api/ZhiPuAiApi.java index eee867661..98f215dbf 100644 --- a/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/api/ZhiPuAiApi.java +++ b/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/api/ZhiPuAiApi.java @@ -116,7 +116,7 @@ public class ZhiPuAiApi { .defaultStatusHandler(responseErrorHandler) .build(); - this.webClient = WebClient.builder() + this.webClient = WebClient.builder() // FIXME: use a builder instead .baseUrl(baseUrl) .defaultHeaders(authHeaders) .build(); diff --git a/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/chroma/vectorstore/ChromaApi.java b/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/chroma/vectorstore/ChromaApi.java index 359a6da0b..72495566d 100644 --- a/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/chroma/vectorstore/ChromaApi.java +++ b/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/chroma/vectorstore/ChromaApi.java @@ -113,7 +113,6 @@ public class ChromaApi { return result; } - @Nullable public void createTenant(String tenantName) { this.restClient.post() @@ -144,7 +143,6 @@ public class ChromaApi { } } - @Nullable public void createDatabase(String tenantName, String databaseName) { this.restClient.post()