diff --git a/models/spring-ai-bedrock/pom.xml b/models/spring-ai-bedrock/pom.xml
index e3b79d30bda..51c458c6514 100644
--- a/models/spring-ai-bedrock/pom.xml
+++ b/models/spring-ai-bedrock/pom.xml
@@ -29,12 +29,6 @@
${project.parent.version}
-
- org.springframework
- spring-web
- ${spring-framework.version}
-
-
org.springframework
spring-webflux
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 d631f25355a..c254d07d6d0 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
@@ -15,30 +15,34 @@
*/
package org.springframework.ai.minimax.api;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonInclude.Include;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonValue;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
import org.springframework.ai.model.ChatModelDescription;
import org.springframework.ai.model.ModelOptionsUtils;
import org.springframework.ai.retry.RetryUtils;
-import org.springframework.ai.util.api.ApiUtils;
import org.springframework.boot.context.properties.bind.ConstructorBinding;
import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonValue;
+
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Predicate;
-
// @formatter:off
/**
* Single class implementation of the MiniMax Chat Completion API and
@@ -98,15 +102,20 @@ public MiniMaxApi(String baseUrl, String miniMaxToken, RestClient.Builder restCl
*/
public MiniMaxApi(String baseUrl, String miniMaxToken, RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) {
+ Consumer authHeaders = (headers) -> {
+ headers.setBearerAuth(miniMaxToken);
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ };
+
this.restClient = restClientBuilder
.baseUrl(baseUrl)
- .defaultHeaders(ApiUtils.getJsonContentHeaders(miniMaxToken))
+ .defaultHeaders(authHeaders)
.defaultStatusHandler(responseErrorHandler)
.build();
this.webClient = WebClient.builder()
.baseUrl(baseUrl)
- .defaultHeaders(ApiUtils.getJsonContentHeaders(miniMaxToken))
+ .defaultHeaders(authHeaders)
.build();
}
diff --git a/models/spring-ai-oci-genai/pom.xml b/models/spring-ai-oci-genai/pom.xml
index fc007e9f9b3..b4ab01cfea2 100644
--- a/models/spring-ai-oci-genai/pom.xml
+++ b/models/spring-ai-oci-genai/pom.xml
@@ -41,12 +41,6 @@
${oci-sdk-version}
-
-
- org.springframework.boot
- spring-boot
-
-
org.springframework
spring-context-support
diff --git a/models/spring-ai-ollama/pom.xml b/models/spring-ai-ollama/pom.xml
index d950b86121f..69f64f8c9c8 100644
--- a/models/spring-ai-ollama/pom.xml
+++ b/models/spring-ai-ollama/pom.xml
@@ -22,11 +22,6 @@
-
- org.springframework.boot
- spring-boot
-
-
org.springframework.ai
spring-ai-core
@@ -34,14 +29,9 @@
- org.springframework.ai
- spring-ai-retry
- ${project.parent.version}
-
-
-
- org.springframework
- spring-webflux
+ org.springframework.ai
+ spring-ai-retry
+ ${project.parent.version}
diff --git a/models/spring-ai-openai/pom.xml b/models/spring-ai-openai/pom.xml
index cfb45f5862c..1019f915a55 100644
--- a/models/spring-ai-openai/pom.xml
+++ b/models/spring-ai-openai/pom.xml
@@ -21,7 +21,7 @@
-
+
org.springframework.ai
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 144c54f715f..31418268269 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
@@ -18,6 +18,7 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
import java.util.function.Predicate;
import org.springframework.ai.model.ChatModelDescription;
@@ -26,6 +27,7 @@
import org.springframework.ai.retry.RetryUtils;
import org.springframework.boot.context.properties.bind.ConstructorBinding;
import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
@@ -154,22 +156,19 @@ public OpenAiApi(String baseUrl, String apiKey, MultiValueMap he
this.completionsPath = completionsPath;
this.embeddingsPath = embeddingsPath;
// @formatter:off
+ Consumer finalHeaders = h -> {
+ h.setBearerAuth(apiKey);
+ h.setContentType(MediaType.APPLICATION_JSON);
+ h.addAll(headers);
+ };
this.restClient = restClientBuilder.baseUrl(baseUrl)
- .defaultHeaders(h -> {
- h.setBearerAuth(apiKey);
- h.setContentType(MediaType.APPLICATION_JSON);
- h.addAll(headers);
- })
+ .defaultHeaders(finalHeaders)
.defaultStatusHandler(responseErrorHandler)
.build();
this.webClient = webClientBuilder
.baseUrl(baseUrl)
- .defaultHeaders(h -> {
- h.setBearerAuth(apiKey);
- h.setContentType(MediaType.APPLICATION_JSON);
- h.addAll(headers);
- })
+ .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 83dee8850be..72161328ffe 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
@@ -17,14 +17,10 @@
import java.util.List;
import java.util.Map;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonInclude.Include;
-import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.function.Consumer;
import org.springframework.ai.openai.api.common.OpenAiApiConstants;
import org.springframework.ai.retry.RetryUtils;
-import org.springframework.ai.util.api.ApiUtils;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
@@ -36,6 +32,11 @@
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -71,13 +72,14 @@ public OpenAiAudioApi(String openAiToken) {
public OpenAiAudioApi(String baseUrl, String openAiToken, RestClient.Builder restClientBuilder,
ResponseErrorHandler responseErrorHandler) {
- this.restClient = restClientBuilder.baseUrl(baseUrl).defaultHeaders(headers -> {
- headers.setBearerAuth(openAiToken);
- }).defaultStatusHandler(responseErrorHandler).build();
+ Consumer authHeaders = h -> h.setBearerAuth(openAiToken);
- this.webClient = WebClient.builder().baseUrl(baseUrl).defaultHeaders(headers -> {
- headers.setBearerAuth(openAiToken);
- }).defaultHeaders(ApiUtils.getJsonContentHeaders(openAiToken)).build();
+ this.restClient = restClientBuilder.baseUrl(baseUrl)
+ .defaultHeaders(authHeaders)
+ .defaultStatusHandler(responseErrorHandler)
+ .build();
+
+ this.webClient = WebClient.builder().baseUrl(baseUrl).defaultHeaders(authHeaders).build();
}
/**
@@ -108,23 +110,18 @@ public OpenAiAudioApi(String baseUrl, String apiKey, MultiValueMap {
- h.setBearerAuth(apiKey);
- h.addAll(headers);
- })
- .defaultStatusHandler(responseErrorHandler).build();
-
- this.webClient = webClientBuilder
- .baseUrl(baseUrl)
- .defaultHeaders(h -> {
- h.setBearerAuth(apiKey);
- h.addAll(headers);
- })
- .defaultHeaders(ApiUtils.getJsonContentHeaders(apiKey)).build();
- // @formatter:on
+ Consumer authHeaders = h -> {
+ h.setBearerAuth(apiKey);
+ h.addAll(headers);
+ // h.setContentType(MediaType.APPLICATION_JSON);
+ };
+
+ this.restClient = restClientBuilder.baseUrl(baseUrl)
+ .defaultHeaders(authHeaders)
+ .defaultStatusHandler(responseErrorHandler)
+ .build();
+
+ this.webClient = webClientBuilder.baseUrl(baseUrl).defaultHeaders(authHeaders).build();
}
/**
diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiModerationApi.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiModerationApi.java
index bb9465e9820..092cc62bc96 100644
--- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiModerationApi.java
+++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiModerationApi.java
@@ -15,21 +15,17 @@
*/
package org.springframework.ai.openai.api;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.ai.retry.RetryUtils;
-import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
-import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.Assert;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
-import java.io.IOException;
-import java.util.function.Consumer;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
/**
* OpenAI Moderation API.
@@ -61,15 +57,10 @@ public OpenAiModerationApi(String baseUrl, String openAiToken, RestClient.Builde
this.objectMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
- Consumer jsonContentHeaders = headers -> {
- headers.setBearerAuth(openAiToken);
- headers.setContentType(MediaType.APPLICATION_JSON);
- };
-
- this.restClient = restClientBuilder.baseUrl(baseUrl)
- .defaultHeaders(jsonContentHeaders)
- .defaultStatusHandler(responseErrorHandler)
- .build();
+ this.restClient = restClientBuilder.baseUrl(baseUrl).defaultHeaders(h -> {
+ h.setBearerAuth(openAiToken);
+ h.setContentType(MediaType.APPLICATION_JSON);
+ }).defaultStatusHandler(responseErrorHandler).build();
}
// @formatter:off
diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/audio/transcription/OpenAiTranscriptionModelWithTranscriptionResponseMetadataTests.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/audio/transcription/OpenAiTranscriptionModelWithTranscriptionResponseMetadataTests.java
index 58a398a5af7..a7749a8f021 100644
--- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/audio/transcription/OpenAiTranscriptionModelWithTranscriptionResponseMetadataTests.java
+++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/audio/transcription/OpenAiTranscriptionModelWithTranscriptionResponseMetadataTests.java
@@ -15,6 +15,14 @@
*/
package org.springframework.ai.openai.audio.transcription;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.header;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
+
+import java.time.Duration;
+
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.ai.audio.transcription.AudioTranscriptionMetadata;
@@ -38,12 +46,6 @@
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.client.RestClient;
-import java.time.Duration;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;
-import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
-
/**
* @author Michael Lavelle
*/
@@ -53,7 +55,7 @@ public class OpenAiTranscriptionModelWithTranscriptionResponseMetadataTests {
private static String TEST_API_KEY = "sk-1234567890";
@Autowired
- private OpenAiAudioTranscriptionModel openAiTranscriptionClient;
+ private OpenAiAudioTranscriptionModel openAiTranscriptionModel;
@Autowired
private MockRestServiceServer server;
@@ -72,7 +74,7 @@ void aiResponseContainsAiMetadata() {
AudioTranscriptionPrompt transcriptionRequest = new AudioTranscriptionPrompt(audioFile);
- AudioTranscriptionResponse response = this.openAiTranscriptionClient.call(transcriptionRequest);
+ AudioTranscriptionResponse response = this.openAiTranscriptionModel.call(transcriptionRequest);
assertThat(response).isNotNull();
diff --git a/models/spring-ai-vertex-ai-embedding/pom.xml b/models/spring-ai-vertex-ai-embedding/pom.xml
index 5988fcf4476..b94de96bd87 100644
--- a/models/spring-ai-vertex-ai-embedding/pom.xml
+++ b/models/spring-ai-vertex-ai-embedding/pom.xml
@@ -58,13 +58,6 @@
${project.parent.version}
-
- org.springframework
- spring-web
- ${spring-framework.version}
-
-
-
org.springframework
diff --git a/models/spring-ai-vertex-ai-gemini/pom.xml b/models/spring-ai-vertex-ai-gemini/pom.xml
index da9eb0beb6c..57f5dcd5ce7 100644
--- a/models/spring-ai-vertex-ai-gemini/pom.xml
+++ b/models/spring-ai-vertex-ai-gemini/pom.xml
@@ -58,12 +58,6 @@
${project.parent.version}
-
- org.springframework
- spring-web
- ${spring-framework.version}
-
-
diff --git a/models/spring-ai-vertex-ai-palm2/pom.xml b/models/spring-ai-vertex-ai-palm2/pom.xml
index 448a3143fab..2c5123b3c2f 100644
--- a/models/spring-ai-vertex-ai-palm2/pom.xml
+++ b/models/spring-ai-vertex-ai-palm2/pom.xml
@@ -31,8 +31,7 @@
org.springframework
- spring-web
- ${spring-framework.version}
+ spring-web
diff --git a/models/spring-ai-watsonx-ai/pom.xml b/models/spring-ai-watsonx-ai/pom.xml
index 00ab9cb712d..e5fc7df00d8 100644
--- a/models/spring-ai-watsonx-ai/pom.xml
+++ b/models/spring-ai-watsonx-ai/pom.xml
@@ -21,6 +21,7 @@
+
org.springframework.boot
spring-boot
@@ -38,11 +39,6 @@
${project.parent.version}
-
- org.springframework
- spring-webflux
-
-
org.springframework.boot
spring-boot-starter-logging
diff --git a/models/spring-ai-zhipuai/pom.xml b/models/spring-ai-zhipuai/pom.xml
index c0e5128ec48..4c2c6e1791c 100644
--- a/models/spring-ai-zhipuai/pom.xml
+++ b/models/spring-ai-zhipuai/pom.xml
@@ -21,7 +21,6 @@
-
org.springframework.ai
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 363937cc3e0..0758d2e8f89 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
@@ -15,31 +15,35 @@
*/
package org.springframework.ai.zhipuai.api;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonInclude.Include;
-import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
import org.springframework.ai.model.ChatModelDescription;
import org.springframework.ai.model.ModelOptionsUtils;
import org.springframework.ai.retry.RetryUtils;
-import org.springframework.ai.util.api.ApiUtils;
import org.springframework.boot.context.properties.bind.ConstructorBinding;
import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Predicate;
-
// @formatter:off
/**
* Single class implementation of the ZhiPuAI Chat Completion API and
@@ -99,15 +103,20 @@ public ZhiPuAiApi(String baseUrl, String zhiPuAiToken, RestClient.Builder restCl
*/
public ZhiPuAiApi(String baseUrl, String zhiPuAiToken, RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) {
+ Consumer authHeaders = h -> {
+ h.setBearerAuth(zhiPuAiToken);
+ h.setContentType(MediaType.APPLICATION_JSON);
+ };
+
this.restClient = restClientBuilder
.baseUrl(baseUrl)
- .defaultHeaders(ApiUtils.getJsonContentHeaders(zhiPuAiToken))
+ .defaultHeaders(authHeaders)
.defaultStatusHandler(responseErrorHandler)
.build();
this.webClient = WebClient.builder()
.baseUrl(baseUrl)
- .defaultHeaders(ApiUtils.getJsonContentHeaders(zhiPuAiToken))
+ .defaultHeaders(authHeaders)
.build();
}
diff --git a/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/api/ZhiPuAiImageApi.java b/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/api/ZhiPuAiImageApi.java
index b64e4964b08..58fa938bb82 100644
--- a/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/api/ZhiPuAiImageApi.java
+++ b/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/api/ZhiPuAiImageApi.java
@@ -15,16 +15,18 @@
*/
package org.springframework.ai.zhipuai.api;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.List;
+import java.util.function.Consumer;
+
import org.springframework.ai.retry.RetryUtils;
-import org.springframework.ai.util.api.ApiUtils;
+import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
-import java.util.List;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
/**
* ZhiPuAI Image API.
@@ -68,8 +70,13 @@ public ZhiPuAiImageApi(String baseUrl, String zhiPuAiToken, RestClient.Builder r
public ZhiPuAiImageApi(String baseUrl, String zhiPuAiToken, RestClient.Builder restClientBuilder,
ResponseErrorHandler responseErrorHandler) {
+ Consumer authHeaders = h -> {
+ h.setBearerAuth(zhiPuAiToken);
+ // h.setContentType(MediaType.APPLICATION_JSON);
+ };
+
this.restClient = restClientBuilder.baseUrl(baseUrl)
- .defaultHeaders(ApiUtils.getJsonContentHeaders(zhiPuAiToken))
+ .defaultHeaders(authHeaders)
.defaultStatusHandler(responseErrorHandler)
.build();
}
diff --git a/pom.xml b/pom.xml
index edbe843c820..b3af8247d15 100644
--- a/pom.xml
+++ b/pom.xml
@@ -148,7 +148,6 @@
4.1.3
3.3.4
- 6.1.4
4.3.4
1.0.0-beta.10
1.1.0
@@ -163,21 +162,21 @@
0.30.0
1.19.2
3.46.1
- 26.41.0
+ 26.48.0
1.9.1
- 2.0.5
+ 2.0.9
9.20.0
4.35.0
2.2.20
- 2.0.2
+ 2.0.3
3.25.2
4.18.1
- 3.0.1
- 0.1.4
+ 3.0.3
+ 0.1.6
2.20.11
23.4.0.24.05
42.7.2
diff --git a/spring-ai-core/pom.xml b/spring-ai-core/pom.xml
index 10bfcbb4c4e..8a25edad098 100644
--- a/spring-ai-core/pom.xml
+++ b/spring-ai-core/pom.xml
@@ -74,11 +74,6 @@
spring-messaging
-
- org.springframework
- spring-web
-
-
io.micrometer
micrometer-core
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/util/api/ApiUtils.java b/spring-ai-core/src/main/java/org/springframework/ai/util/api/ApiUtils.java
deleted file mode 100644
index 3f1f98750e1..00000000000
--- a/spring-ai-core/src/main/java/org/springframework/ai/util/api/ApiUtils.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2023 - 2024 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.ai.util.api;
-
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.MediaType;
-
-import java.util.function.Consumer;
-
-/**
- * The ApiUtils class provides utility methods for working with API requests and
- * responses.
- *
- * @author Geng Rong
- * @author Christian Tzolov
- * @author Piotr Olaszewski
- * @since 1.0.0 M1
- */
-public class ApiUtils {
-
- public static Consumer getJsonContentHeaders(String apiKey) {
- return (headers) -> {
- headers.setBearerAuth(apiKey);
- headers.setContentType(MediaType.APPLICATION_JSON);
- };
- }
-
-}
diff --git a/spring-ai-retry/pom.xml b/spring-ai-retry/pom.xml
index 75834506deb..c35268567f0 100644
--- a/spring-ai-retry/pom.xml
+++ b/spring-ai-retry/pom.xml
@@ -34,6 +34,7 @@
${spring-retry.version}
+
org.springframework
spring-webflux
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/anthropic/AnthropicAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/anthropic/AnthropicAutoConfiguration.java
index 35c50730057..ff311b7cab4 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/anthropic/AnthropicAutoConfiguration.java
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/anthropic/AnthropicAutoConfiguration.java
@@ -17,7 +17,6 @@
import java.util.List;
-import io.micrometer.observation.ObservationRegistry;
import org.springframework.ai.anthropic.AnthropicChatModel;
import org.springframework.ai.anthropic.api.AnthropicApi;
import org.springframework.ai.autoconfigure.retry.SpringAiRetryAutoConfiguration;
@@ -40,6 +39,8 @@
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
+import io.micrometer.observation.ObservationRegistry;
+
/**
* @author Christian Tzolov
* @author Thomas Vitale
@@ -57,11 +58,12 @@ public class AnthropicAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public AnthropicApi anthropicApi(AnthropicConnectionProperties connectionProperties,
- RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder,
- ResponseErrorHandler responseErrorHandler) {
+ ObjectProvider restClientBuilderProvider,
+ ObjectProvider webClientBuilderProvider, ResponseErrorHandler responseErrorHandler) {
return new AnthropicApi(connectionProperties.getBaseUrl(), connectionProperties.getApiKey(),
- connectionProperties.getVersion(), restClientBuilder, webClientBuilder, responseErrorHandler,
+ connectionProperties.getVersion(), restClientBuilderProvider.getIfAvailable(RestClient::builder),
+ webClientBuilderProvider.getIfAvailable(WebClient::builder), responseErrorHandler,
connectionProperties.getBetaVersion());
}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/minimax/MiniMaxAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/minimax/MiniMaxAutoConfiguration.java
index 43ea7928b84..e4c549140f0 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/minimax/MiniMaxAutoConfiguration.java
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/minimax/MiniMaxAutoConfiguration.java
@@ -15,7 +15,8 @@
*/
package org.springframework.ai.autoconfigure.minimax;
-import io.micrometer.observation.ObservationRegistry;
+import java.util.List;
+
import org.springframework.ai.autoconfigure.retry.SpringAiRetryAutoConfiguration;
import org.springframework.ai.chat.observation.ChatModelObservationConvention;
import org.springframework.ai.embedding.observation.EmbeddingModelObservationConvention;
@@ -35,15 +36,11 @@
import org.springframework.context.annotation.Bean;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.util.Assert;
-import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
-import java.util.stream.Collectors;
+import io.micrometer.observation.ObservationRegistry;
/**
* @author Geng Rong
@@ -59,14 +56,15 @@ public class MiniMaxAutoConfiguration {
@ConditionalOnProperty(prefix = MiniMaxChatProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
public MiniMaxChatModel miniMaxChatModel(MiniMaxConnectionProperties commonProperties,
- MiniMaxChatProperties chatProperties, RestClient.Builder restClientBuilder,
+ MiniMaxChatProperties chatProperties, ObjectProvider restClientBuilderProvider,
List toolFunctionCallbacks, FunctionCallbackContext functionCallbackContext,
RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler,
ObjectProvider observationRegistry,
ObjectProvider observationConvention) {
var miniMaxApi = miniMaxApi(chatProperties.getBaseUrl(), commonProperties.getBaseUrl(),
- chatProperties.getApiKey(), commonProperties.getApiKey(), restClientBuilder, responseErrorHandler);
+ chatProperties.getApiKey(), commonProperties.getApiKey(),
+ restClientBuilderProvider.getIfAvailable(RestClient::builder), responseErrorHandler);
var chatModel = new MiniMaxChatModel(miniMaxApi, chatProperties.getOptions(), functionCallbackContext,
toolFunctionCallbacks, retryTemplate, observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP));
@@ -80,13 +78,14 @@ public MiniMaxChatModel miniMaxChatModel(MiniMaxConnectionProperties commonPrope
@ConditionalOnProperty(prefix = MiniMaxEmbeddingProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
public MiniMaxEmbeddingModel miniMaxEmbeddingModel(MiniMaxConnectionProperties commonProperties,
- MiniMaxEmbeddingProperties embeddingProperties, RestClient.Builder restClientBuilder,
- RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler,
- ObjectProvider observationRegistry,
+ MiniMaxEmbeddingProperties embeddingProperties,
+ ObjectProvider restClientBuilderProvider, RetryTemplate retryTemplate,
+ ResponseErrorHandler responseErrorHandler, ObjectProvider observationRegistry,
ObjectProvider observationConvention) {
var miniMaxApi = miniMaxApi(embeddingProperties.getBaseUrl(), commonProperties.getBaseUrl(),
- embeddingProperties.getApiKey(), commonProperties.getApiKey(), restClientBuilder, responseErrorHandler);
+ embeddingProperties.getApiKey(), commonProperties.getApiKey(),
+ restClientBuilderProvider.getIfAvailable(RestClient::builder), responseErrorHandler);
var embeddingModel = new MiniMaxEmbeddingModel(miniMaxApi, embeddingProperties.getMetadataMode(),
embeddingProperties.getOptions(), retryTemplate,
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mistralai/MistralAiAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mistralai/MistralAiAutoConfiguration.java
index 6e40c5eaf3b..be572d81919 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mistralai/MistralAiAutoConfiguration.java
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mistralai/MistralAiAutoConfiguration.java
@@ -17,7 +17,6 @@
import java.util.List;
-import io.micrometer.observation.ObservationRegistry;
import org.springframework.ai.autoconfigure.retry.SpringAiRetryAutoConfiguration;
import org.springframework.ai.chat.observation.ChatModelObservationConvention;
import org.springframework.ai.embedding.observation.EmbeddingModelObservationConvention;
@@ -43,6 +42,8 @@
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
+import io.micrometer.observation.ObservationRegistry;
+
/**
* @author Ricken Bazolo
* @author Christian Tzolov
@@ -62,14 +63,14 @@ public class MistralAiAutoConfiguration {
@ConditionalOnProperty(prefix = MistralAiEmbeddingProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
public MistralAiEmbeddingModel mistralAiEmbeddingModel(MistralAiCommonProperties commonProperties,
- MistralAiEmbeddingProperties embeddingProperties, RestClient.Builder restClientBuilder,
- RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler,
- ObjectProvider observationRegistry,
+ MistralAiEmbeddingProperties embeddingProperties,
+ ObjectProvider restClientBuilderProvider, RetryTemplate retryTemplate,
+ ResponseErrorHandler responseErrorHandler, ObjectProvider observationRegistry,
ObjectProvider observationConvention) {
var mistralAiApi = mistralAiApi(embeddingProperties.getApiKey(), commonProperties.getApiKey(),
- embeddingProperties.getBaseUrl(), commonProperties.getBaseUrl(), restClientBuilder,
- responseErrorHandler);
+ embeddingProperties.getBaseUrl(), commonProperties.getBaseUrl(),
+ restClientBuilderProvider.getIfAvailable(RestClient::builder), responseErrorHandler);
var embeddingModel = new MistralAiEmbeddingModel(mistralAiApi, embeddingProperties.getMetadataMode(),
embeddingProperties.getOptions(), retryTemplate,
@@ -85,14 +86,15 @@ public MistralAiEmbeddingModel mistralAiEmbeddingModel(MistralAiCommonProperties
@ConditionalOnProperty(prefix = MistralAiChatProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
public MistralAiChatModel mistralAiChatModel(MistralAiCommonProperties commonProperties,
- MistralAiChatProperties chatProperties, RestClient.Builder restClientBuilder,
+ MistralAiChatProperties chatProperties, ObjectProvider restClientBuilderProvider,
List toolFunctionCallbacks, FunctionCallbackContext functionCallbackContext,
RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler,
ObjectProvider observationRegistry,
ObjectProvider observationConvention) {
var mistralAiApi = mistralAiApi(chatProperties.getApiKey(), commonProperties.getApiKey(),
- chatProperties.getBaseUrl(), commonProperties.getBaseUrl(), restClientBuilder, responseErrorHandler);
+ chatProperties.getBaseUrl(), commonProperties.getBaseUrl(),
+ restClientBuilderProvider.getIfAvailable(RestClient::builder), responseErrorHandler);
var chatModel = new MistralAiChatModel(mistralAiApi, chatProperties.getOptions(), functionCallbackContext,
toolFunctionCallbacks, retryTemplate, observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP));
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/moonshot/MoonshotAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/moonshot/MoonshotAutoConfiguration.java
index 7517705b1b3..3bd4223f416 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/moonshot/MoonshotAutoConfiguration.java
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/moonshot/MoonshotAutoConfiguration.java
@@ -15,7 +15,8 @@
*/
package org.springframework.ai.autoconfigure.moonshot;
-import io.micrometer.observation.ObservationRegistry;
+import java.util.List;
+
import org.springframework.ai.autoconfigure.retry.SpringAiRetryAutoConfiguration;
import org.springframework.ai.chat.observation.ChatModelObservationConvention;
import org.springframework.ai.model.function.FunctionCallback;
@@ -37,7 +38,7 @@
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
-import java.util.List;
+import io.micrometer.observation.ObservationRegistry;
/**
* @author Geng Rong
@@ -52,14 +53,15 @@ public class MoonshotAutoConfiguration {
@ConditionalOnProperty(prefix = MoonshotChatProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
public MoonshotChatModel moonshotChatModel(MoonshotCommonProperties commonProperties,
- MoonshotChatProperties chatProperties, RestClient.Builder restClientBuilder,
+ MoonshotChatProperties chatProperties, ObjectProvider restClientBuilderProvider,
List toolFunctionCallbacks, FunctionCallbackContext functionCallbackContext,
RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler,
ObjectProvider observationRegistry,
ObjectProvider observationConvention) {
var moonshotApi = moonshotApi(chatProperties.getApiKey(), commonProperties.getApiKey(),
- chatProperties.getBaseUrl(), commonProperties.getBaseUrl(), restClientBuilder, responseErrorHandler);
+ chatProperties.getBaseUrl(), commonProperties.getBaseUrl(),
+ restClientBuilderProvider.getIfAvailable(RestClient::builder), responseErrorHandler);
var chatModel = new MoonshotChatModel(moonshotApi, chatProperties.getOptions(), functionCallbackContext,
toolFunctionCallbacks, retryTemplate, observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP));
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/ollama/OllamaAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/ollama/OllamaAutoConfiguration.java
index 106f04c3e51..0e6bccb4816 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/ollama/OllamaAutoConfiguration.java
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/ollama/OllamaAutoConfiguration.java
@@ -17,7 +17,6 @@
import java.util.List;
-import io.micrometer.observation.ObservationRegistry;
import org.springframework.ai.chat.observation.ChatModelObservationConvention;
import org.springframework.ai.embedding.observation.EmbeddingModelObservationConvention;
import org.springframework.ai.model.function.FunctionCallback;
@@ -39,6 +38,8 @@
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
+import io.micrometer.observation.ObservationRegistry;
+
/**
* {@link AutoConfiguration Auto-configuration} for Ollama Chat Client.
*
@@ -62,9 +63,12 @@ public PropertiesOllamaConnectionDetails ollamaConnectionDetails(OllamaConnectio
@Bean
@ConditionalOnMissingBean
- public OllamaApi ollamaApi(OllamaConnectionDetails connectionDetails, RestClient.Builder restClientBuilder,
- WebClient.Builder webClientBuilder) {
- return new OllamaApi(connectionDetails.getBaseUrl(), restClientBuilder, webClientBuilder);
+ public OllamaApi ollamaApi(OllamaConnectionDetails connectionDetails,
+ ObjectProvider restClientBuilderProvider,
+ ObjectProvider webClientBuilderProvider) {
+ return new OllamaApi(connectionDetails.getBaseUrl(),
+ restClientBuilderProvider.getIfAvailable(RestClient::builder),
+ webClientBuilderProvider.getIfAvailable(WebClient::builder));
}
@Bean
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/openai/OpenAiAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/openai/OpenAiAutoConfiguration.java
index e0586b50e20..d594460c5a2 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/openai/OpenAiAutoConfiguration.java
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/openai/OpenAiAutoConfiguration.java
@@ -78,14 +78,15 @@ public class OpenAiAutoConfiguration {
@ConditionalOnProperty(prefix = OpenAiChatProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
public OpenAiChatModel openAiChatModel(OpenAiConnectionProperties commonProperties,
- OpenAiChatProperties chatProperties, RestClient.Builder restClientBuilder,
- WebClient.Builder webClientBuilder, List toolFunctionCallbacks,
+ OpenAiChatProperties chatProperties, ObjectProvider restClientBuilderProvider,
+ ObjectProvider webClientBuilderProvider, List toolFunctionCallbacks,
FunctionCallbackContext functionCallbackContext, RetryTemplate retryTemplate,
ResponseErrorHandler responseErrorHandler, ObjectProvider observationRegistry,
ObjectProvider observationConvention) {
- var openAiApi = openAiApi(chatProperties, commonProperties, restClientBuilder, webClientBuilder,
- responseErrorHandler, "chat");
+ var openAiApi = openAiApi(chatProperties, commonProperties,
+ restClientBuilderProvider.getIfAvailable(RestClient::builder),
+ webClientBuilderProvider.getIfAvailable(WebClient::builder), responseErrorHandler, "chat");
var chatModel = new OpenAiChatModel(openAiApi, chatProperties.getOptions(), functionCallbackContext,
toolFunctionCallbacks, retryTemplate, observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP));
@@ -100,13 +101,14 @@ public OpenAiChatModel openAiChatModel(OpenAiConnectionProperties commonProperti
@ConditionalOnProperty(prefix = OpenAiEmbeddingProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
public OpenAiEmbeddingModel openAiEmbeddingModel(OpenAiConnectionProperties commonProperties,
- OpenAiEmbeddingProperties embeddingProperties, RestClient.Builder restClientBuilder,
- WebClient.Builder webClientBuilder, RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler,
- ObjectProvider observationRegistry,
+ OpenAiEmbeddingProperties embeddingProperties, ObjectProvider restClientBuilderProvider,
+ ObjectProvider webClientBuilderProvider, RetryTemplate retryTemplate,
+ ResponseErrorHandler responseErrorHandler, ObjectProvider observationRegistry,
ObjectProvider observationConvention) {
- var openAiApi = openAiApi(embeddingProperties, commonProperties, restClientBuilder, webClientBuilder,
- responseErrorHandler, "embedding");
+ var openAiApi = openAiApi(embeddingProperties, commonProperties,
+ restClientBuilderProvider.getIfAvailable(RestClient::builder),
+ webClientBuilderProvider.getIfAvailable(WebClient::builder), responseErrorHandler, "embedding");
var embeddingModel = new OpenAiEmbeddingModel(openAiApi, embeddingProperties.getMetadataMode(),
embeddingProperties.getOptions(), retryTemplate,
@@ -146,14 +148,15 @@ private OpenAiApi openAiApi(OpenAiEmbeddingProperties embeddingProperties,
@ConditionalOnProperty(prefix = OpenAiImageProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
public OpenAiImageModel openAiImageModel(OpenAiConnectionProperties commonProperties,
- OpenAiImageProperties imageProperties, RestClient.Builder restClientBuilder, RetryTemplate retryTemplate,
- ResponseErrorHandler responseErrorHandler, ObjectProvider observationRegistry,
+ OpenAiImageProperties imageProperties, ObjectProvider restClientBuilderProvider,
+ RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler,
+ ObjectProvider observationRegistry,
ObjectProvider observationConvention) {
ResolvedConnectionProperties resolved = resolveConnectionProperties(commonProperties, imageProperties, "image");
var openAiImageApi = new OpenAiImageApi(resolved.baseUrl(), resolved.apiKey(), resolved.headers(),
- restClientBuilder, responseErrorHandler);
+ restClientBuilderProvider.getIfAvailable(RestClient::builder), responseErrorHandler);
var imageModel = new OpenAiImageModel(openAiImageApi, imageProperties.getOptions(), retryTemplate,
observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP));
@@ -169,14 +172,15 @@ public OpenAiImageModel openAiImageModel(OpenAiConnectionProperties commonProper
havingValue = "true", matchIfMissing = true)
public OpenAiAudioTranscriptionModel openAiAudioTranscriptionModel(OpenAiConnectionProperties commonProperties,
OpenAiAudioTranscriptionProperties transcriptionProperties, RetryTemplate retryTemplate,
- RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder,
- ResponseErrorHandler responseErrorHandler) {
+ ObjectProvider restClientBuilderProvider,
+ ObjectProvider webClientBuilderProvider, ResponseErrorHandler responseErrorHandler) {
ResolvedConnectionProperties resolved = resolveConnectionProperties(commonProperties, transcriptionProperties,
"transcription");
var openAiAudioApi = new OpenAiAudioApi(resolved.baseUrl(), resolved.apiKey(), resolved.headers(),
- restClientBuilder, webClientBuilder, responseErrorHandler);
+ restClientBuilderProvider.getIfAvailable(RestClient::builder),
+ webClientBuilderProvider.getIfAvailable(WebClient::builder), responseErrorHandler);
return new OpenAiAudioTranscriptionModel(openAiAudioApi, transcriptionProperties.getOptions(), retryTemplate);
@@ -186,13 +190,13 @@ public OpenAiAudioTranscriptionModel openAiAudioTranscriptionModel(OpenAiConnect
@ConditionalOnMissingBean
public OpenAiModerationModel openAiModerationClient(OpenAiConnectionProperties commonProperties,
OpenAiModerationProperties moderationProperties, RetryTemplate retryTemplate,
- RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) {
+ ObjectProvider restClientBuilderProvider, ResponseErrorHandler responseErrorHandler) {
ResolvedConnectionProperties resolved = resolveConnectionProperties(commonProperties, moderationProperties,
"moderation");
- var openAiModerationApi = new OpenAiModerationApi(resolved.baseUrl, resolved.apiKey(), restClientBuilder,
- responseErrorHandler);
+ var openAiModerationApi = new OpenAiModerationApi(resolved.baseUrl, resolved.apiKey(),
+ restClientBuilderProvider.getIfAvailable(RestClient::builder), responseErrorHandler);
return new OpenAiModerationModel(openAiModerationApi, retryTemplate)
.withDefaultOptions(moderationProperties.getOptions());
@@ -204,14 +208,15 @@ public OpenAiModerationModel openAiModerationClient(OpenAiConnectionProperties c
matchIfMissing = true)
public OpenAiAudioSpeechModel openAiAudioSpeechClient(OpenAiConnectionProperties commonProperties,
OpenAiAudioSpeechProperties speechProperties, RetryTemplate retryTemplate,
- RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder,
- ResponseErrorHandler responseErrorHandler) {
+ ObjectProvider restClientBuilderProvider,
+ ObjectProvider webClientBuilderProvider, ResponseErrorHandler responseErrorHandler) {
ResolvedConnectionProperties resolved = resolveConnectionProperties(commonProperties, speechProperties,
"speach");
var openAiAudioApi = new OpenAiAudioApi(resolved.baseUrl(), resolved.apiKey(), resolved.headers(),
- restClientBuilder, webClientBuilder, responseErrorHandler);
+ restClientBuilderProvider.getIfAvailable(RestClient::builder),
+ webClientBuilderProvider.getIfAvailable(WebClient::builder), responseErrorHandler);
return new OpenAiAudioSpeechModel(openAiAudioApi, speechProperties.getOptions());
}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/qianfan/QianFanAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/qianfan/QianFanAutoConfiguration.java
index 17e65f2a074..5a2efccae0f 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/qianfan/QianFanAutoConfiguration.java
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/qianfan/QianFanAutoConfiguration.java
@@ -15,7 +15,6 @@
*/
package org.springframework.ai.autoconfigure.qianfan;
-import io.micrometer.observation.ObservationRegistry;
import org.springframework.ai.autoconfigure.retry.SpringAiRetryAutoConfiguration;
import org.springframework.ai.chat.observation.ChatModelObservationConvention;
import org.springframework.ai.embedding.observation.EmbeddingModelObservationConvention;
@@ -41,6 +40,8 @@
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
+import io.micrometer.observation.ObservationRegistry;
+
/**
* @author Geng Rong
*/
@@ -55,13 +56,15 @@ public class QianFanAutoConfiguration {
@ConditionalOnProperty(prefix = QianFanChatProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
public QianFanChatModel qianFanChatModel(QianFanConnectionProperties commonProperties,
- QianFanChatProperties chatProperties, RestClient.Builder restClientBuilder, RetryTemplate retryTemplate,
- ResponseErrorHandler responseErrorHandler, ObjectProvider observationRegistry,
+ QianFanChatProperties chatProperties, ObjectProvider restClientBuilderProvider,
+ RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler,
+ ObjectProvider observationRegistry,
ObjectProvider observationConvention) {
var qianFanApi = qianFanApi(chatProperties.getBaseUrl(), commonProperties.getBaseUrl(),
chatProperties.getApiKey(), commonProperties.getApiKey(), chatProperties.getSecretKey(),
- commonProperties.getSecretKey(), restClientBuilder, responseErrorHandler);
+ commonProperties.getSecretKey(), restClientBuilderProvider.getIfAvailable(RestClient::builder),
+ responseErrorHandler);
var chatModel = new QianFanChatModel(qianFanApi, chatProperties.getOptions(), retryTemplate,
observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP));
@@ -76,14 +79,15 @@ public QianFanChatModel qianFanChatModel(QianFanConnectionProperties commonPrope
@ConditionalOnProperty(prefix = QianFanEmbeddingProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
public QianFanEmbeddingModel qianFanEmbeddingModel(QianFanConnectionProperties commonProperties,
- QianFanEmbeddingProperties embeddingProperties, RestClient.Builder restClientBuilder,
- RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler,
- ObjectProvider observationRegistry,
+ QianFanEmbeddingProperties embeddingProperties,
+ ObjectProvider restClientBuilderProvider, RetryTemplate retryTemplate,
+ ResponseErrorHandler responseErrorHandler, ObjectProvider observationRegistry,
ObjectProvider observationConvention) {
var qianFanApi = qianFanApi(embeddingProperties.getBaseUrl(), commonProperties.getBaseUrl(),
embeddingProperties.getApiKey(), commonProperties.getApiKey(), embeddingProperties.getSecretKey(),
- commonProperties.getSecretKey(), restClientBuilder, responseErrorHandler);
+ commonProperties.getSecretKey(), restClientBuilderProvider.getIfAvailable(RestClient::builder),
+ responseErrorHandler);
var embeddingModel = new QianFanEmbeddingModel(qianFanApi, embeddingProperties.getMetadataMode(),
embeddingProperties.getOptions(), retryTemplate,
@@ -99,8 +103,9 @@ public QianFanEmbeddingModel qianFanEmbeddingModel(QianFanConnectionProperties c
@ConditionalOnProperty(prefix = QianFanImageProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
public QianFanImageModel qianFanImageModel(QianFanConnectionProperties commonProperties,
- QianFanImageProperties imageProperties, RestClient.Builder restClientBuilder, RetryTemplate retryTemplate,
- ResponseErrorHandler responseErrorHandler, ObjectProvider observationRegistry,
+ QianFanImageProperties imageProperties, ObjectProvider restClientBuilderProvider,
+ RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler,
+ ObjectProvider observationRegistry,
ObjectProvider observationConvention) {
String apiKey = StringUtils.hasText(imageProperties.getApiKey()) ? imageProperties.getApiKey()
@@ -116,7 +121,8 @@ public QianFanImageModel qianFanImageModel(QianFanConnectionProperties commonPro
Assert.hasText(secretKey, "QianFan secret key must be set. Use the property: spring.ai.qianfan.secret-key");
Assert.hasText(baseUrl, "QianFan base URL must be set. Use the property: spring.ai.qianfan.base-url");
- var qianFanImageApi = new QianFanImageApi(baseUrl, apiKey, secretKey, restClientBuilder, responseErrorHandler);
+ var qianFanImageApi = new QianFanImageApi(baseUrl, apiKey, secretKey,
+ restClientBuilderProvider.getIfAvailable(RestClient::builder), responseErrorHandler);
var imageModel = new QianFanImageModel(qianFanImageApi, imageProperties.getOptions(), retryTemplate,
observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP));
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/stabilityai/StabilityAiImageAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/stabilityai/StabilityAiImageAutoConfiguration.java
index 5983499f2cf..0a594ff0bc1 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/stabilityai/StabilityAiImageAutoConfiguration.java
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/stabilityai/StabilityAiImageAutoConfiguration.java
@@ -17,6 +17,7 @@
import org.springframework.ai.stabilityai.StabilityAiImageModel;
import org.springframework.ai.stabilityai.api.StabilityAiApi;
+import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -26,6 +27,7 @@
import org.springframework.context.annotation.Bean;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
+import org.springframework.web.client.RestClient;
/**
* @author Mark Pollack
@@ -40,7 +42,7 @@ public class StabilityAiImageAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public StabilityAiApi stabilityAiApi(StabilityAiConnectionProperties commonProperties,
- StabilityAiImageProperties imageProperties) {
+ StabilityAiImageProperties imageProperties, ObjectProvider restClientBuilderProvider) {
String apiKey = StringUtils.hasText(imageProperties.getApiKey()) ? imageProperties.getApiKey()
: commonProperties.getApiKey();
@@ -51,7 +53,8 @@ public StabilityAiApi stabilityAiApi(StabilityAiConnectionProperties commonPrope
Assert.hasText(apiKey, "StabilityAI API key must be set");
Assert.hasText(baseUrl, "StabilityAI base URL must be set");
- return new StabilityAiApi(apiKey, imageProperties.getOptions().getModel(), baseUrl);
+ return new StabilityAiApi(apiKey, imageProperties.getOptions().getModel(), baseUrl,
+ restClientBuilderProvider.getIfAvailable(RestClient::builder));
}
@Bean
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/chroma/ChromaVectorStoreAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/chroma/ChromaVectorStoreAutoConfiguration.java
index 14b3324853b..c6d031ec1bb 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/chroma/ChromaVectorStoreAutoConfiguration.java
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/chroma/ChromaVectorStoreAutoConfiguration.java
@@ -28,7 +28,6 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestClient;
@@ -54,18 +53,13 @@ PropertiesChromaConnectionDetails chromaConnectionDetails(ChromaApiProperties pr
@Bean
@ConditionalOnMissingBean
- public RestClient.Builder builder() {
- return RestClient.builder().requestFactory(new SimpleClientHttpRequestFactory());
- }
-
- @Bean
- @ConditionalOnMissingBean
- public ChromaApi chromaApi(ChromaApiProperties apiProperties, RestClient.Builder restClientBuilder,
- ChromaConnectionDetails connectionDetails) {
+ public ChromaApi chromaApi(ChromaApiProperties apiProperties,
+ ObjectProvider restClientBuilderProvider, ChromaConnectionDetails connectionDetails) {
String chromaUrl = String.format("%s:%s", connectionDetails.getHost(), connectionDetails.getPort());
- var chromaApi = new ChromaApi(chromaUrl, restClientBuilder, new ObjectMapper());
+ var chromaApi = new ChromaApi(chromaUrl, restClientBuilderProvider.getIfAvailable(RestClient::builder),
+ new ObjectMapper());
if (StringUtils.hasText(connectionDetails.getKeyToken())) {
chromaApi.withKeyToken(connectionDetails.getKeyToken());
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vertexai/gemini/VertexAiGeminiAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vertexai/gemini/VertexAiGeminiAutoConfiguration.java
index 7f6496700f9..a6c83100a89 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vertexai/gemini/VertexAiGeminiAutoConfiguration.java
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vertexai/gemini/VertexAiGeminiAutoConfiguration.java
@@ -40,6 +40,7 @@
import com.google.auth.oauth2.GoogleCredentials;
import com.google.cloud.vertexai.VertexAI;
+
import io.micrometer.observation.ObservationRegistry;
/**
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vertexai/palm2/VertexAiPalm2AutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vertexai/palm2/VertexAiPalm2AutoConfiguration.java
index 4899f7a526f..e2ac02ed66a 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vertexai/palm2/VertexAiPalm2AutoConfiguration.java
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vertexai/palm2/VertexAiPalm2AutoConfiguration.java
@@ -19,6 +19,7 @@
import org.springframework.ai.vertexai.palm2.VertexAiPaLm2ChatModel;
import org.springframework.ai.vertexai.palm2.VertexAiPaLm2EmbeddingModel;
import org.springframework.ai.vertexai.palm2.api.VertexAiPaLm2Api;
+import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -42,10 +43,11 @@ public class VertexAiPalm2AutoConfiguration {
@ConditionalOnMissingBean
public VertexAiPaLm2Api vertexAiApi(VertexAiPalm2ConnectionProperties connectionProperties,
VertexAiPalm2EmbeddingProperties embeddingAiProperties, VertexAiPlam2ChatProperties chatProperties,
- RestClient.Builder restClientBuilder) {
+ ObjectProvider restClientBuilderProvider) {
return new VertexAiPaLm2Api(connectionProperties.getBaseUrl(), connectionProperties.getApiKey(),
- chatProperties.getModel(), embeddingAiProperties.getModel(), restClientBuilder);
+ chatProperties.getModel(), embeddingAiProperties.getModel(),
+ restClientBuilderProvider.getIfAvailable(RestClient::builder));
}
@Bean
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/watsonxai/WatsonxAiAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/watsonxai/WatsonxAiAutoConfiguration.java
index 8cb42373786..4d7f9f43637 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/watsonxai/WatsonxAiAutoConfiguration.java
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/watsonxai/WatsonxAiAutoConfiguration.java
@@ -18,6 +18,7 @@
import org.springframework.ai.watsonx.WatsonxAiChatModel;
import org.springframework.ai.watsonx.WatsonxAiEmbeddingModel;
import org.springframework.ai.watsonx.api.WatsonxAiApi;
+import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -45,10 +46,11 @@ public class WatsonxAiAutoConfiguration {
@Bean
@ConditionalOnMissingBean
- public WatsonxAiApi watsonxApi(WatsonxAiConnectionProperties properties, RestClient.Builder restClientBuilder) {
+ public WatsonxAiApi watsonxApi(WatsonxAiConnectionProperties properties,
+ ObjectProvider restClientBuilderProvider) {
return new WatsonxAiApi(properties.getBaseUrl(), properties.getStreamEndpoint(), properties.getTextEndpoint(),
properties.getEmbeddingEndpoint(), properties.getProjectId(), properties.getIAMToken(),
- restClientBuilder);
+ restClientBuilderProvider.getIfAvailable(RestClient::builder));
}
@Bean
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/zhipuai/ZhiPuAiAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/zhipuai/ZhiPuAiAutoConfiguration.java
index ca7d0724582..98afeaf79fe 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/zhipuai/ZhiPuAiAutoConfiguration.java
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/zhipuai/ZhiPuAiAutoConfiguration.java
@@ -15,7 +15,8 @@
*/
package org.springframework.ai.autoconfigure.zhipuai;
-import io.micrometer.observation.ObservationRegistry;
+import java.util.List;
+
import org.springframework.ai.autoconfigure.retry.SpringAiRetryAutoConfiguration;
import org.springframework.ai.chat.observation.ChatModelObservationConvention;
import org.springframework.ai.embedding.observation.EmbeddingModelObservationConvention;
@@ -41,7 +42,7 @@
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
-import java.util.List;
+import io.micrometer.observation.ObservationRegistry;
/**
* @author Geng Rong
@@ -57,14 +58,15 @@ public class ZhiPuAiAutoConfiguration {
@ConditionalOnProperty(prefix = ZhiPuAiChatProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
public ZhiPuAiChatModel zhiPuAiChatModel(ZhiPuAiConnectionProperties commonProperties,
- ZhiPuAiChatProperties chatProperties, RestClient.Builder restClientBuilder,
+ ZhiPuAiChatProperties chatProperties, ObjectProvider restClientBuilderProvider,
List toolFunctionCallbacks, FunctionCallbackContext functionCallbackContext,
RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler,
ObjectProvider observationRegistry,
ObjectProvider observationConvention) {
var zhiPuAiApi = zhiPuAiApi(chatProperties.getBaseUrl(), commonProperties.getBaseUrl(),
- chatProperties.getApiKey(), commonProperties.getApiKey(), restClientBuilder, responseErrorHandler);
+ chatProperties.getApiKey(), commonProperties.getApiKey(),
+ restClientBuilderProvider.getIfAvailable(RestClient::builder), responseErrorHandler);
var chatModel = new ZhiPuAiChatModel(zhiPuAiApi, chatProperties.getOptions(), functionCallbackContext,
toolFunctionCallbacks, retryTemplate, observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP));
diff --git a/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/mongo/MongoDbAtlasLocalContainerConnectionDetailsFactoryTest.java b/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/mongo/MongoDbAtlasLocalContainerConnectionDetailsFactoryIt.java
similarity index 98%
rename from spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/mongo/MongoDbAtlasLocalContainerConnectionDetailsFactoryTest.java
rename to spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/mongo/MongoDbAtlasLocalContainerConnectionDetailsFactoryIt.java
index b99f107b2d3..34b748de47e 100644
--- a/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/mongo/MongoDbAtlasLocalContainerConnectionDetailsFactoryTest.java
+++ b/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/mongo/MongoDbAtlasLocalContainerConnectionDetailsFactoryIt.java
@@ -48,7 +48,7 @@
"spring.ai.vectorstore.mongodb.initialize-schema=true",
"spring.ai.vectorstore.mongodb.collection-name=test_collection",
"spring.ai.vectorstore.mongodb.index-name=text_index" })
-class MongoDbAtlasLocalContainerConnectionDetailsFactoryTest {
+class MongoDbAtlasLocalContainerConnectionDetailsFactoryIT {
@Container
@ServiceConnection
diff --git a/vector-stores/spring-ai-chroma-store/pom.xml b/vector-stores/spring-ai-chroma-store/pom.xml
index 84ef6383db2..c057ea33967 100644
--- a/vector-stores/spring-ai-chroma-store/pom.xml
+++ b/vector-stores/spring-ai-chroma-store/pom.xml
@@ -30,7 +30,7 @@
org.springframework
- spring-web
+ spring-webflux
diff --git a/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/chroma/ChromaApi.java b/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/chroma/ChromaApi.java
index 9932b1fe897..2d5c09ef3e8 100644
--- a/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/chroma/ChromaApi.java
+++ b/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/chroma/ChromaApi.java
@@ -69,10 +69,10 @@ public ChromaApi(String baseUrl, RestClient.Builder restClientBuilder) {
}
public ChromaApi(String baseUrl, RestClient.Builder restClientBuilder, ObjectMapper objectMapper) {
- Consumer defaultHeaders = headers -> {
- headers.setContentType(MediaType.APPLICATION_JSON);
- };
- this.restClient = restClientBuilder.baseUrl(baseUrl).defaultHeaders(defaultHeaders).build();
+
+ this.restClient = restClientBuilder.baseUrl(baseUrl)
+ .defaultHeaders(h -> h.setContentType(MediaType.APPLICATION_JSON))
+ .build();
this.objectMapper = objectMapper;
}