diff --git a/auto-configurations/common/spring-ai-autoconfigure-retry/pom.xml b/auto-configurations/common/spring-ai-autoconfigure-retry/pom.xml
index 4fcb9371d82..f7b3d39d83f 100644
--- a/auto-configurations/common/spring-ai-autoconfigure-retry/pom.xml
+++ b/auto-configurations/common/spring-ai-autoconfigure-retry/pom.xml
@@ -62,6 +62,12 @@
test
+
+ org.springframework.boot
+ spring-boot-starter-restclient
+ test
+
+
org.mockito
mockito-core
diff --git a/auto-configurations/common/spring-ai-autoconfigure-retry/src/main/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfiguration.java b/auto-configurations/common/spring-ai-autoconfigure-retry/src/main/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfiguration.java
index 36d212c8d8a..8409ada52be 100644
--- a/auto-configurations/common/spring-ai-autoconfigure-retry/src/main/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfiguration.java
+++ b/auto-configurations/common/spring-ai-autoconfigure-retry/src/main/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfiguration.java
@@ -17,7 +17,9 @@
package org.springframework.ai.retry.autoconfigure;
import java.io.IOException;
+import java.net.URI;
import java.nio.charset.StandardCharsets;
+import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -30,12 +32,13 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
+import org.springframework.core.retry.RetryListener;
+import org.springframework.core.retry.RetryPolicy;
+import org.springframework.core.retry.RetryTemplate;
+import org.springframework.core.retry.Retryable;
+import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.NonNull;
-import org.springframework.retry.RetryCallback;
-import org.springframework.retry.RetryContext;
-import org.springframework.retry.RetryListener;
-import org.springframework.retry.support.RetryTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.ResponseErrorHandler;
@@ -58,21 +61,25 @@ public class SpringAiRetryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public RetryTemplate retryTemplate(SpringAiRetryProperties properties) {
- return RetryTemplate.builder()
+ RetryPolicy retryPolicy = RetryPolicy.builder()
.maxAttempts(properties.getMaxAttempts())
- .retryOn(TransientAiException.class)
- .exponentialBackoff(properties.getBackoff().getInitialInterval(), properties.getBackoff().getMultiplier(),
- properties.getBackoff().getMaxInterval())
- .withListener(new RetryListener() {
-
- @Override
- public void onError(RetryContext context, RetryCallback callback,
- Throwable throwable) {
- logger.warn("Retry error. Retry count: {}, Exception: {}", context.getRetryCount(),
- throwable.getMessage(), throwable);
- }
- })
+ .includes(TransientAiException.class)
+ .delay(properties.getBackoff().getInitialInterval())
+ .multiplier(properties.getBackoff().getMultiplier())
+ .maxDelay(properties.getBackoff().getMaxInterval())
.build();
+
+ RetryTemplate retryTemplate = new RetryTemplate(retryPolicy);
+ retryTemplate.setRetryListener(new RetryListener() {
+ private final AtomicInteger retryCount = new AtomicInteger(0);
+
+ @Override
+ public void onRetryFailure(RetryPolicy policy, Retryable> retryable, Throwable throwable) {
+ int currentRetries = retryCount.incrementAndGet();
+ logger.warn("Retry error. Retry count:{}", currentRetries, throwable);
+ }
+ });
+ return retryTemplate;
}
@Bean
@@ -87,7 +94,8 @@ public boolean hasError(@NonNull ClientHttpResponse response) throws IOException
}
@Override
- public void handleError(@NonNull ClientHttpResponse response) throws IOException {
+ public void handleError(URI url, HttpMethod method, @NonNull ClientHttpResponse response)
+ throws IOException {
if (!response.getStatusCode().isError()) {
return;
}
diff --git a/auto-configurations/common/spring-ai-autoconfigure-retry/src/test/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfigurationIT.java b/auto-configurations/common/spring-ai-autoconfigure-retry/src/test/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfigurationIT.java
index 4b712f0507e..dd4004dbb6b 100644
--- a/auto-configurations/common/spring-ai-autoconfigure-retry/src/test/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfigurationIT.java
+++ b/auto-configurations/common/spring-ai-autoconfigure-retry/src/test/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfigurationIT.java
@@ -19,9 +19,9 @@
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.web.client.ResponseErrorHandler;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-webflux/src/test/java/org/springframework/ai/mcp/server/autoconfigure/McpServerSseWebFluxAutoConfigurationTests.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-webflux/src/test/java/org/springframework/ai/mcp/server/autoconfigure/McpServerSseWebFluxAutoConfigurationTests.java
index 72fcafbcec6..b47b3e0984c 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-webflux/src/test/java/org/springframework/ai/mcp/server/autoconfigure/McpServerSseWebFluxAutoConfigurationTests.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-webflux/src/test/java/org/springframework/ai/mcp/server/autoconfigure/McpServerSseWebFluxAutoConfigurationTests.java
@@ -16,17 +16,17 @@
package org.springframework.ai.mcp.server.autoconfigure;
-import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.server.transport.WebFluxSseServerTransportProvider;
import org.junit.jupiter.api.Test;
import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerProperties;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
+import org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction;
+import tools.jackson.databind.ObjectMapper;
import static org.assertj.core.api.Assertions.assertThat;
@@ -46,8 +46,8 @@ void shouldConfigureWebFluxTransportWithCustomObjectMapper() {
ObjectMapper objectMapper = context.getBean(ObjectMapper.class);
// Verify that the ObjectMapper is configured to ignore unknown properties
- assertThat(objectMapper.getDeserializationConfig()
- .isEnabled(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)).isFalse();
+ assertThat(objectMapper.deserializationConfig()
+ .isEnabled(tools.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)).isFalse();
// Test with a JSON payload containing unknown fields
// CHECKSTYLE:OFF
diff --git a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-cassandra/pom.xml b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-cassandra/pom.xml
index 9f13c6f89e9..4dcbb0abc39 100644
--- a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-cassandra/pom.xml
+++ b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-cassandra/pom.xml
@@ -53,6 +53,11 @@
true
+
+ org.springframework.boot
+ spring-boot-cassandra
+
+
org.springframework.ai
diff --git a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-cassandra/src/main/java/org/springframework/ai/model/chat/memory/repository/cassandra/autoconfigure/CassandraChatMemoryRepositoryAutoConfiguration.java b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-cassandra/src/main/java/org/springframework/ai/model/chat/memory/repository/cassandra/autoconfigure/CassandraChatMemoryRepositoryAutoConfiguration.java
index e55bc82cea5..ff1964f83c2 100644
--- a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-cassandra/src/main/java/org/springframework/ai/model/chat/memory/repository/cassandra/autoconfigure/CassandraChatMemoryRepositoryAutoConfiguration.java
+++ b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-cassandra/src/main/java/org/springframework/ai/model/chat/memory/repository/cassandra/autoconfigure/CassandraChatMemoryRepositoryAutoConfiguration.java
@@ -22,7 +22,7 @@
import org.springframework.ai.chat.memory.repository.cassandra.CassandraChatMemoryRepositoryConfig;
import org.springframework.ai.model.chat.memory.autoconfigure.ChatMemoryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
+import org.springframework.boot.cassandra.autoconfigure.CassandraAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
diff --git a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-cassandra/src/test/java/org/springframework/ai/model/chat/memory/repository/cassandra/autoconfigure/CassandraChatMemoryRepositoryAutoConfigurationIT.java b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-cassandra/src/test/java/org/springframework/ai/model/chat/memory/repository/cassandra/autoconfigure/CassandraChatMemoryRepositoryAutoConfigurationIT.java
index 9b619ce9039..50d04eaa3ea 100644
--- a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-cassandra/src/test/java/org/springframework/ai/model/chat/memory/repository/cassandra/autoconfigure/CassandraChatMemoryRepositoryAutoConfigurationIT.java
+++ b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-cassandra/src/test/java/org/springframework/ai/model/chat/memory/repository/cassandra/autoconfigure/CassandraChatMemoryRepositoryAutoConfigurationIT.java
@@ -31,7 +31,7 @@
import org.springframework.ai.chat.messages.MessageType;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
+import org.springframework.boot.cassandra.autoconfigure.CassandraAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-cosmos-db/pom.xml b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-cosmos-db/pom.xml
index 535fd9f44ed..da7cb85c488 100644
--- a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-cosmos-db/pom.xml
+++ b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-cosmos-db/pom.xml
@@ -81,6 +81,11 @@
${azure-identity.version}
+
+ org.slf4j
+ jcl-over-slf4j
+
+
org.springframework.boot
diff --git a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/pom.xml b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/pom.xml
index 1ffcc2f324f..fbaf44ad6a1 100644
--- a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/pom.xml
+++ b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/pom.xml
@@ -53,6 +53,11 @@
true
+
+ org.springframework.boot
+ spring-boot-jdbc
+
+
org.springframework.boot
@@ -60,6 +65,12 @@
test
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+ test
+
+
org.postgresql
postgresql
diff --git a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/main/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryAutoConfiguration.java b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/main/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryAutoConfiguration.java
index 17bed069754..f6abfb91454 100644
--- a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/main/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryAutoConfiguration.java
+++ b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/main/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryAutoConfiguration.java
@@ -24,8 +24,8 @@
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
-import org.springframework.boot.autoconfigure.sql.init.OnDatabaseInitializationCondition;
+import org.springframework.boot.jdbc.autoconfigure.JdbcTemplateAutoConfiguration;
+import org.springframework.boot.sql.autoconfigure.init.OnDatabaseInitializationCondition;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
diff --git a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryHsqldbAutoConfigurationIT.java b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryHsqldbAutoConfigurationIT.java
index abfd6927a46..f9b10f48f5f 100644
--- a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryHsqldbAutoConfigurationIT.java
+++ b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryHsqldbAutoConfigurationIT.java
@@ -29,7 +29,6 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
-import org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
@@ -52,9 +51,8 @@
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY)
@ImportAutoConfiguration({ org.springframework.ai.model.chat.memory.autoconfigure.ChatMemoryAutoConfiguration.class,
JdbcChatMemoryRepositoryAutoConfiguration.class,
- org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration.class,
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration.class,
- SqlInitializationAutoConfiguration.class })
+ org.springframework.boot.jdbc.autoconfigure.JdbcTemplateAutoConfiguration.class,
+ org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration.class })
public class JdbcChatMemoryRepositoryHsqldbAutoConfigurationIT {
@Autowired
diff --git a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryPostgresqlAutoConfigurationIT.java b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryPostgresqlAutoConfigurationIT.java
index c6bde91fff4..2f7217c6710 100644
--- a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryPostgresqlAutoConfigurationIT.java
+++ b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryPostgresqlAutoConfigurationIT.java
@@ -28,8 +28,8 @@
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.model.chat.memory.autoconfigure.ChatMemoryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.JdbcTemplateAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositorySchemaInitializerPostgresqlTests.java b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositorySchemaInitializerPostgresqlTests.java
index 03542a87d98..7845ef2fa95 100644
--- a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositorySchemaInitializerPostgresqlTests.java
+++ b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositorySchemaInitializerPostgresqlTests.java
@@ -25,8 +25,8 @@
import org.testcontainers.utility.DockerImageName;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.JdbcTemplateAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositorySqlServerAutoConfigurationIT.java b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositorySqlServerAutoConfigurationIT.java
index a3bf69410ac..77095de4216 100644
--- a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositorySqlServerAutoConfigurationIT.java
+++ b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositorySqlServerAutoConfigurationIT.java
@@ -33,8 +33,8 @@
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.model.chat.memory.autoconfigure.ChatMemoryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.JdbcTemplateAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-neo4j/pom.xml b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-neo4j/pom.xml
index dc0e1ef9373..e4df7ded982 100644
--- a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-neo4j/pom.xml
+++ b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-neo4j/pom.xml
@@ -53,6 +53,11 @@
true
+
+ org.springframework.boot
+ spring-boot-neo4j
+
+
org.springframework.ai
diff --git a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-neo4j/src/main/java/org/springframework/ai/model/chat/memory/repository/neo4j/autoconfigure/Neo4jChatMemoryRepositoryAutoConfiguration.java b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-neo4j/src/main/java/org/springframework/ai/model/chat/memory/repository/neo4j/autoconfigure/Neo4jChatMemoryRepositoryAutoConfiguration.java
index 970cb6be91c..5371f4f7587 100644
--- a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-neo4j/src/main/java/org/springframework/ai/model/chat/memory/repository/neo4j/autoconfigure/Neo4jChatMemoryRepositoryAutoConfiguration.java
+++ b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-neo4j/src/main/java/org/springframework/ai/model/chat/memory/repository/neo4j/autoconfigure/Neo4jChatMemoryRepositoryAutoConfiguration.java
@@ -24,7 +24,7 @@
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
+import org.springframework.boot.neo4j.autoconfigure.Neo4jAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
diff --git a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-neo4j/src/test/java/org/springframework/ai/model/chat/memory/repository/neo4j/autoconfigure/Neo4jChatMemoryRepositoryAutoConfigurationIT.java b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-neo4j/src/test/java/org/springframework/ai/model/chat/memory/repository/neo4j/autoconfigure/Neo4jChatMemoryRepositoryAutoConfigurationIT.java
index f60f6f61320..0ba45f83161 100644
--- a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-neo4j/src/test/java/org/springframework/ai/model/chat/memory/repository/neo4j/autoconfigure/Neo4jChatMemoryRepositoryAutoConfigurationIT.java
+++ b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-neo4j/src/test/java/org/springframework/ai/model/chat/memory/repository/neo4j/autoconfigure/Neo4jChatMemoryRepositoryAutoConfigurationIT.java
@@ -39,7 +39,7 @@
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.content.Media;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
+import org.springframework.boot.neo4j.autoconfigure.Neo4jAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.util.MimeType;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-anthropic/pom.xml b/auto-configurations/models/spring-ai-autoconfigure-model-anthropic/pom.xml
index 3835531828c..f39662355aa 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-anthropic/pom.xml
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-anthropic/pom.xml
@@ -72,6 +72,18 @@
true
+
+ org.springframework.boot
+ spring-boot-restclient
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-webclient
+ true
+
+
org.springframework.ai
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 3233dd1eee1..17e2cc8edb6 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
@@ -33,12 +33,12 @@
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.restclient.autoconfigure.RestClientAutoConfiguration;
+import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.context.annotation.Import;
-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;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-anthropic/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/AnthropicPropertiesTests.java b/auto-configurations/models/spring-ai-autoconfigure-model-anthropic/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/AnthropicPropertiesTests.java
index f87eb430dfe..20af04ded1c 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-anthropic/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/AnthropicPropertiesTests.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-anthropic/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/AnthropicPropertiesTests.java
@@ -22,7 +22,7 @@
import org.springframework.ai.anthropic.api.AnthropicApi.ToolChoiceTool;
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-anthropic/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/BaseAnthropicIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-anthropic/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/BaseAnthropicIT.java
index c68d9188e10..44282b3614d 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-anthropic/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/BaseAnthropicIT.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-anthropic/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/BaseAnthropicIT.java
@@ -22,8 +22,8 @@
import org.springframework.ai.model.tool.autoconfigure.ToolCallingAutoConfiguration;
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
+import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
public abstract class BaseAnthropicIT {
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/pom.xml b/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/pom.xml
index 2f36a8c976e..e772e156453 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/pom.xml
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/pom.xml
@@ -72,6 +72,16 @@
true
+
+ org.springframework.boot
+ spring-boot-starter-restclient
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webclient
+
+
org.springframework.ai
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/main/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekChatAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/main/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekChatAutoConfiguration.java
index 98aae30a232..1c11d4b84ec 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/main/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekChatAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/main/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekChatAutoConfiguration.java
@@ -34,11 +34,11 @@
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.restclient.autoconfigure.RestClientAutoConfiguration;
+import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.client.ResponseErrorHandler;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/BaseDeepSeekIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/BaseDeepSeekIT.java
index 9423542a624..1ea7baf7292 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/BaseDeepSeekIT.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/BaseDeepSeekIT.java
@@ -22,8 +22,8 @@
import org.springframework.ai.model.tool.autoconfigure.ToolCallingAutoConfiguration;
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
+import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
/**
* Base utility class for DeepSeek integration tests.
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-elevenlabs/pom.xml b/auto-configurations/models/spring-ai-autoconfigure-model-elevenlabs/pom.xml
index bc09ef1f5b4..cb02bb344b4 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-elevenlabs/pom.xml
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-elevenlabs/pom.xml
@@ -66,6 +66,18 @@
true
+
+ org.springframework.boot
+ spring-boot-restclient
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-webclient
+ true
+
+
org.springframework.ai
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-elevenlabs/src/main/java/org/springframework/ai/model/elevenlabs/autoconfigure/ElevenLabsAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-elevenlabs/src/main/java/org/springframework/ai/model/elevenlabs/autoconfigure/ElevenLabsAutoConfiguration.java
index bbff2dcf71e..dd21a339914 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-elevenlabs/src/main/java/org/springframework/ai/model/elevenlabs/autoconfigure/ElevenLabsAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-elevenlabs/src/main/java/org/springframework/ai/model/elevenlabs/autoconfigure/ElevenLabsAutoConfiguration.java
@@ -24,11 +24,11 @@
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.restclient.autoconfigure.RestClientAutoConfiguration;
+import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-elevenlabs/src/test/java/org/springframework/ai/model/elevenlabs/autoconfigure/ElevenLabsITUtil.java b/auto-configurations/models/spring-ai-autoconfigure-model-elevenlabs/src/test/java/org/springframework/ai/model/elevenlabs/autoconfigure/ElevenLabsITUtil.java
index 9cd2b178856..86debc54233 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-elevenlabs/src/test/java/org/springframework/ai/model/elevenlabs/autoconfigure/ElevenLabsITUtil.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-elevenlabs/src/test/java/org/springframework/ai/model/elevenlabs/autoconfigure/ElevenLabsITUtil.java
@@ -18,8 +18,8 @@
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
+import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
/**
* Utility class for ElevenLabs integration tests.
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-google-genai/pom.xml b/auto-configurations/models/spring-ai-autoconfigure-model-google-genai/pom.xml
index 8bed6c0ea18..6b644a60b6e 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-google-genai/pom.xml
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-google-genai/pom.xml
@@ -101,6 +101,18 @@
test
+
+ org.springframework.boot
+ spring-boot-starter-restclient
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webclient
+ test
+
+
org.mockito
mockito-core
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-google-genai/src/main/java/org/springframework/ai/model/google/genai/autoconfigure/chat/GoogleGenAiChatAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-google-genai/src/main/java/org/springframework/ai/model/google/genai/autoconfigure/chat/GoogleGenAiChatAutoConfiguration.java
index e8c9b8ea2d1..664b1e3b9be 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-google-genai/src/main/java/org/springframework/ai/model/google/genai/autoconfigure/chat/GoogleGenAiChatAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-google-genai/src/main/java/org/springframework/ai/model/google/genai/autoconfigure/chat/GoogleGenAiChatAutoConfiguration.java
@@ -42,7 +42,7 @@
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-google-genai/src/main/java/org/springframework/ai/model/google/genai/autoconfigure/embedding/GoogleGenAiTextEmbeddingAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-google-genai/src/main/java/org/springframework/ai/model/google/genai/autoconfigure/embedding/GoogleGenAiTextEmbeddingAutoConfiguration.java
index 6fd62c663e4..d3825836171 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-google-genai/src/main/java/org/springframework/ai/model/google/genai/autoconfigure/embedding/GoogleGenAiTextEmbeddingAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-google-genai/src/main/java/org/springframework/ai/model/google/genai/autoconfigure/embedding/GoogleGenAiTextEmbeddingAutoConfiguration.java
@@ -31,7 +31,7 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
/**
* Auto-configuration for Google GenAI Text Embedding.
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-google-genai/src/test/java/org/springframework/ai/model/google/genai/autoconfigure/chat/tool/FunctionCallWithFunctionBeanIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-google-genai/src/test/java/org/springframework/ai/model/google/genai/autoconfigure/chat/tool/FunctionCallWithFunctionBeanIT.java
index 328e742fcfa..84d5bf71cc7 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-google-genai/src/test/java/org/springframework/ai/model/google/genai/autoconfigure/chat/tool/FunctionCallWithFunctionBeanIT.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-google-genai/src/test/java/org/springframework/ai/model/google/genai/autoconfigure/chat/tool/FunctionCallWithFunctionBeanIT.java
@@ -30,7 +30,7 @@
import org.springframework.ai.model.google.genai.autoconfigure.BaseGoogleGenAiIT;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.function.FunctionToolCallback;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/pom.xml b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/pom.xml
index 62eff8604f9..4233826e890 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/pom.xml
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/pom.xml
@@ -78,6 +78,16 @@
true
+
+ org.springframework.boot
+ spring-boot-restclient
+
+
+
+ org.springframework.boot
+ spring-boot-webclient
+
+
org.springframework.ai
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxChatAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxChatAutoConfiguration.java
index b51445491f5..6ab4d2bf11d 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxChatAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxChatAutoConfiguration.java
@@ -33,10 +33,10 @@
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.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.client.ResponseErrorHandler;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxEmbeddingAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxEmbeddingAutoConfiguration.java
index e7098339cc2..e87fd22a979 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxEmbeddingAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxEmbeddingAutoConfiguration.java
@@ -29,10 +29,10 @@
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.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.client.ResponseErrorHandler;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackInPromptIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackInPromptIT.java
index dcf772a0bf7..16b5f011ee5 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackInPromptIT.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackInPromptIT.java
@@ -36,7 +36,7 @@
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackWithPlainFunctionBeanIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackWithPlainFunctionBeanIT.java
index 0cb23c746a9..94f0678789c 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackWithPlainFunctionBeanIT.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackWithPlainFunctionBeanIT.java
@@ -37,7 +37,7 @@
import org.springframework.ai.model.tool.autoconfigure.ToolCallingAutoConfiguration;
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxAutoConfigurationIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxAutoConfigurationIT.java
index 543ad444578..723e800e0c2 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxAutoConfigurationIT.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxAutoConfigurationIT.java
@@ -34,7 +34,7 @@
import org.springframework.ai.model.tool.autoconfigure.ToolCallingAutoConfiguration;
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxFunctionCallbackIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxFunctionCallbackIT.java
index 8e41a0641dd..956c3dcedd1 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxFunctionCallbackIT.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxFunctionCallbackIT.java
@@ -36,7 +36,7 @@
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxPropertiesTests.java b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxPropertiesTests.java
index c0751d5e334..44df03c8b7a 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxPropertiesTests.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxPropertiesTests.java
@@ -27,7 +27,7 @@
import org.springframework.ai.model.tool.autoconfigure.ToolCallingAutoConfiguration;
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MinimaxModelConfigurationTests.java b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MinimaxModelConfigurationTests.java
index cf1b622d1ff..6fa8ac46c54 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MinimaxModelConfigurationTests.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MinimaxModelConfigurationTests.java
@@ -23,7 +23,7 @@
import org.springframework.ai.model.tool.autoconfigure.ToolCallingAutoConfiguration;
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/pom.xml b/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/pom.xml
index d4642a87308..c787ea91713 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/pom.xml
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/pom.xml
@@ -84,6 +84,16 @@
true
+
+ org.springframework.boot
+ spring-boot-restclient
+
+
+
+ org.springframework.boot
+ spring-boot-webclient
+
+
org.springframework.ai
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 9044e82cbbd..6732bbdafde 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
@@ -33,11 +33,11 @@
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.restclient.autoconfigure.RestClientAutoConfiguration;
+import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.client.ResponseErrorHandler;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiEmbeddingAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiEmbeddingAutoConfiguration.java
index ad8a8632ccd..3937029d39c 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiEmbeddingAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiEmbeddingAutoConfiguration.java
@@ -29,10 +29,10 @@
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.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.client.ResponseErrorHandler;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiModerationAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiModerationAutoConfiguration.java
index 778d267019e..932023787bc 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiModerationAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiModerationAutoConfiguration.java
@@ -27,11 +27,11 @@
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.restclient.autoconfigure.RestClientAutoConfiguration;
+import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.client.ResponseErrorHandler;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiOcrAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiOcrAutoConfiguration.java
index 8a247672507..49e26378f13 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiOcrAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiOcrAutoConfiguration.java
@@ -24,7 +24,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.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.util.Assert;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiOcrPropertiesTests.java b/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiOcrPropertiesTests.java
index 3b494c7f713..1a2ec9a34e5 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiOcrPropertiesTests.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiOcrPropertiesTests.java
@@ -21,7 +21,7 @@
import org.springframework.ai.mistralai.ocr.MistralOcrApi;
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-ollama/pom.xml b/auto-configurations/models/spring-ai-autoconfigure-model-ollama/pom.xml
index ecb49ce4c23..38c163ee13e 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-ollama/pom.xml
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-ollama/pom.xml
@@ -86,6 +86,18 @@
test
+
+
+ org.springframework.boot
+ spring-boot-starter-restclient
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webclient
+
+
org.springframework.boot
spring-boot-starter-test
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaApiAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaApiAutoConfiguration.java
index ed30d787021..3df440cdc3f 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaApiAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaApiAutoConfiguration.java
@@ -22,9 +22,9 @@
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-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.boot.restclient.autoconfigure.RestClientAutoConfiguration;
+import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaChatAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaChatAutoConfiguration.java
index 34b9ad58346..67bdae15714 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaChatAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaChatAutoConfiguration.java
@@ -36,7 +36,7 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
/**
* {@link AutoConfiguration Auto-configuration} for Ollama Chat model.
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/test/java/org/springframework/ai/model/ollama/autoconfigure/BaseOllamaIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/test/java/org/springframework/ai/model/ollama/autoconfigure/BaseOllamaIT.java
index 9a3bc2527f4..b4bd3fb8403 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/test/java/org/springframework/ai/model/ollama/autoconfigure/BaseOllamaIT.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/test/java/org/springframework/ai/model/ollama/autoconfigure/BaseOllamaIT.java
@@ -20,6 +20,7 @@
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.ollama.OllamaContainer;
@@ -30,7 +31,6 @@
import org.springframework.ai.ollama.management.PullModelStrategy;
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
import org.springframework.util.Assert;
@Testcontainers
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-openai/pom.xml b/auto-configurations/models/spring-ai-autoconfigure-model-openai/pom.xml
index ed37e511925..a388f7299bd 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-openai/pom.xml
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-openai/pom.xml
@@ -83,6 +83,16 @@
true
+
+ org.springframework.boot
+ spring-boot-restclient
+
+
+
+ org.springframework.boot
+ spring-boot-webclient
+
+
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAIAutoConfigurationUtil.java b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAIAutoConfigurationUtil.java
index 7eff1898fa4..398c4566184 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAIAutoConfigurationUtil.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAIAutoConfigurationUtil.java
@@ -20,10 +20,9 @@
import java.util.List;
import java.util.Map;
+import org.springframework.http.HttpHeaders;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
-import org.springframework.util.CollectionUtils;
-import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
public final class OpenAIAutoConfigurationUtil {
@@ -44,12 +43,12 @@ private OpenAIAutoConfigurationUtil() {
String organizationId = StringUtils.hasText(modelProperties.getOrganizationId())
? modelProperties.getOrganizationId() : commonProperties.getOrganizationId();
- Map> connectionHeaders = new HashMap<>();
+ HttpHeaders connectionHeaders = new HttpHeaders();
if (StringUtils.hasText(projectId)) {
- connectionHeaders.put("OpenAI-Project", List.of(projectId));
+ connectionHeaders.add("OpenAI-Project", projectId);
}
if (StringUtils.hasText(organizationId)) {
- connectionHeaders.put("OpenAI-Organization", List.of(organizationId));
+ connectionHeaders.add("OpenAI-Organization", organizationId);
}
Assert.hasText(baseUrl,
@@ -59,10 +58,10 @@ private OpenAIAutoConfigurationUtil() {
"OpenAI API key must be set. Use the connection property: spring.ai.openai.api-key or spring.ai.openai."
+ modelType + ".api-key property.");
- return new ResolvedConnectionProperties(baseUrl, apiKey, CollectionUtils.toMultiValueMap(connectionHeaders));
+ return new ResolvedConnectionProperties(baseUrl, apiKey, connectionHeaders);
}
- public record ResolvedConnectionProperties(String baseUrl, String apiKey, MultiValueMap headers) {
+ public record ResolvedConnectionProperties(String baseUrl, String apiKey, HttpHeaders headers) {
}
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAudioSpeechAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAudioSpeechAutoConfiguration.java
index 4d197904e5d..73b6228a19f 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAudioSpeechAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAudioSpeechAutoConfiguration.java
@@ -28,11 +28,11 @@
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.restclient.autoconfigure.RestClientAutoConfiguration;
+import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAudioTranscriptionAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAudioTranscriptionAutoConfiguration.java
index 7e9b67c70ca..f350e49536e 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAudioTranscriptionAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAudioTranscriptionAutoConfiguration.java
@@ -28,11 +28,11 @@
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.restclient.autoconfigure.RestClientAutoConfiguration;
+import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiChatAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiChatAutoConfiguration.java
index f1c1e4ea618..5fde6a55ff0 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiChatAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiChatAutoConfiguration.java
@@ -34,11 +34,11 @@
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.restclient.autoconfigure.RestClientAutoConfiguration;
+import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiEmbeddingAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiEmbeddingAutoConfiguration.java
index bf9b40e7a65..cefc31f2cbf 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiEmbeddingAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiEmbeddingAutoConfiguration.java
@@ -30,11 +30,11 @@
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.restclient.autoconfigure.RestClientAutoConfiguration;
+import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiImageAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiImageAutoConfiguration.java
index 07da6969a70..69522f8091a 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiImageAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiImageAutoConfiguration.java
@@ -31,11 +31,11 @@
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.restclient.autoconfigure.RestClientAutoConfiguration;
+import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiModerationAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiModerationAutoConfiguration.java
index bf74afe8fdc..f5e4a2ece45 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiModerationAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiModerationAutoConfiguration.java
@@ -28,11 +28,11 @@
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.restclient.autoconfigure.RestClientAutoConfiguration;
+import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/ChatClientAutoConfigurationIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/ChatClientAutoConfigurationIT.java
index 46dec5cb44c..89bd4f3e373 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/ChatClientAutoConfigurationIT.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/ChatClientAutoConfigurationIT.java
@@ -29,7 +29,7 @@
import org.springframework.ai.model.tool.autoconfigure.ToolCallingAutoConfiguration;
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAutoConfigurationIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAutoConfigurationIT.java
index 78e920f7f08..39f8fdcf86f 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAutoConfigurationIT.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAutoConfigurationIT.java
@@ -42,8 +42,8 @@
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
+import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiModelConfigurationTests.java b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiModelConfigurationTests.java
index b5797172932..bfd4b992a47 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiModelConfigurationTests.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiModelConfigurationTests.java
@@ -28,8 +28,8 @@
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
+import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiPropertiesTests.java b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiPropertiesTests.java
index 28378329628..cfcbf798ead 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiPropertiesTests.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiPropertiesTests.java
@@ -32,8 +32,8 @@
import org.springframework.ai.openai.api.OpenAiAudioApi;
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
+import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiResponseFormatPropertiesTests.java b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiResponseFormatPropertiesTests.java
index 04c2b5813f4..b8fb05b4f78 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiResponseFormatPropertiesTests.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiResponseFormatPropertiesTests.java
@@ -28,8 +28,8 @@
import org.springframework.ai.openai.api.ResponseFormat;
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
+import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackInPrompt2IT.java b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackInPrompt2IT.java
index d7b90d853ff..6797d39afa0 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackInPrompt2IT.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackInPrompt2IT.java
@@ -33,7 +33,7 @@
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackInPromptIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackInPromptIT.java
index 06510dfcdbe..60d47c834ce 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackInPromptIT.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackInPromptIT.java
@@ -38,7 +38,7 @@
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java
index 75debfb1069..fccf6708afd 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java
@@ -47,7 +47,7 @@
import org.springframework.ai.openai.api.OpenAiApi.ChatModel;
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/OpenAiFunctionCallback2IT.java b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/OpenAiFunctionCallback2IT.java
index a996c4c3e5a..4d7ed8a7459 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/OpenAiFunctionCallback2IT.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/OpenAiFunctionCallback2IT.java
@@ -32,7 +32,7 @@
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/OpenAiFunctionCallbackIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/OpenAiFunctionCallbackIT.java
index 9c917b25fdc..0d74ddc297b 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/OpenAiFunctionCallbackIT.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/OpenAiFunctionCallbackIT.java
@@ -39,7 +39,7 @@
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-postgresml-embedding/pom.xml b/auto-configurations/models/spring-ai-autoconfigure-model-postgresml-embedding/pom.xml
index 5ff82851c32..b56e490687b 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-postgresml-embedding/pom.xml
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-postgresml-embedding/pom.xml
@@ -66,6 +66,11 @@
true
+
+ org.springframework.boot
+ spring-boot-jdbc
+
+
org.springframework.ai
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-postgresml-embedding/src/main/java/org/springframework/ai/model/postgresml/autoconfigure/PostgresMlEmbeddingAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-postgresml-embedding/src/main/java/org/springframework/ai/model/postgresml/autoconfigure/PostgresMlEmbeddingAutoConfiguration.java
index 6fcbb4f2949..2daaa872552 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-postgresml-embedding/src/main/java/org/springframework/ai/model/postgresml/autoconfigure/PostgresMlEmbeddingAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-postgresml-embedding/src/main/java/org/springframework/ai/model/postgresml/autoconfigure/PostgresMlEmbeddingAutoConfiguration.java
@@ -23,7 +23,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.jdbc.JdbcTemplateAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.JdbcTemplateAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-stability-ai/pom.xml b/auto-configurations/models/spring-ai-autoconfigure-model-stability-ai/pom.xml
index 38f9f644b8c..479306a826a 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-stability-ai/pom.xml
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-stability-ai/pom.xml
@@ -66,6 +66,16 @@
true
+
+ org.springframework.boot
+ spring-boot-restclient
+
+
+
+ org.springframework.boot
+ spring-boot-webclient
+
+
org.springframework.ai
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-stability-ai/src/main/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiImageAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-stability-ai/src/main/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiImageAutoConfiguration.java
index b38df31325e..a082bb2cf3c 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-stability-ai/src/main/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiImageAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-stability-ai/src/main/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiImageAutoConfiguration.java
@@ -25,7 +25,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.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.util.Assert;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-vertex-ai/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/embedding/VertexAiTextEmbeddingAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-vertex-ai/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/embedding/VertexAiTextEmbeddingAutoConfiguration.java
index 56e253dc2d2..8c0370ec84d 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-vertex-ai/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/embedding/VertexAiTextEmbeddingAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-vertex-ai/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/embedding/VertexAiTextEmbeddingAutoConfiguration.java
@@ -31,7 +31,7 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
/**
* Auto-configuration for Vertex AI Gemini Chat.
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-vertex-ai/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/VertexAiGeminiChatAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-vertex-ai/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/VertexAiGeminiChatAutoConfiguration.java
index edb7057c1e6..79b4865ca8e 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-vertex-ai/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/VertexAiGeminiChatAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-vertex-ai/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/VertexAiGeminiChatAutoConfiguration.java
@@ -39,7 +39,7 @@
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/pom.xml b/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/pom.xml
index eafc77142fa..4b83577ff5b 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/pom.xml
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/pom.xml
@@ -84,6 +84,16 @@
true
+
+ org.springframework.boot
+ spring-boot-starter-restclient
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webclient
+
+
org.springframework.ai
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiChatAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiChatAutoConfiguration.java
index ba9468e5c4f..002236e5ecc 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiChatAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiChatAutoConfiguration.java
@@ -34,10 +34,10 @@
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.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.client.ResponseErrorHandler;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiEmbeddingAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiEmbeddingAutoConfiguration.java
index a80913cdd3d..b78c9139de9 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiEmbeddingAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiEmbeddingAutoConfiguration.java
@@ -30,10 +30,10 @@
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.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.client.ResponseErrorHandler;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiImageAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiImageAutoConfiguration.java
index ea89f6198cb..2ae307f4cb7 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiImageAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiImageAutoConfiguration.java
@@ -27,10 +27,10 @@
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.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.client.ResponseErrorHandler;
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/test/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiITUtil.java b/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/test/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiITUtil.java
index 6c4c0833602..4df8c382ac5 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/test/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiITUtil.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/test/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiITUtil.java
@@ -19,7 +19,7 @@
import org.springframework.ai.model.tool.autoconfigure.ToolCallingAutoConfiguration;
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
/**
* Utility class for ZhiPuAI integration tests.
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-cassandra/pom.xml b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-cassandra/pom.xml
index dfed881e4de..68690649c63 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-cassandra/pom.xml
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-cassandra/pom.xml
@@ -59,6 +59,12 @@
spring-boot-autoconfigure-processor
true
+
+
+ org.springframework.boot
+ spring-boot-cassandra
+
+
org.springframework.ai
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-cassandra/src/main/java/org/springframework/ai/vectorstore/cassandra/autoconfigure/CassandraVectorStoreAutoConfiguration.java b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-cassandra/src/main/java/org/springframework/ai/vectorstore/cassandra/autoconfigure/CassandraVectorStoreAutoConfiguration.java
index e58ef033ce9..0173b9afd80 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-cassandra/src/main/java/org/springframework/ai/vectorstore/cassandra/autoconfigure/CassandraVectorStoreAutoConfiguration.java
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-cassandra/src/main/java/org/springframework/ai/vectorstore/cassandra/autoconfigure/CassandraVectorStoreAutoConfiguration.java
@@ -30,8 +30,8 @@
import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
-import org.springframework.boot.autoconfigure.cassandra.DriverConfigLoaderBuilderCustomizer;
+import org.springframework.boot.cassandra.autoconfigure.CassandraAutoConfiguration;
+import org.springframework.boot.cassandra.autoconfigure.DriverConfigLoaderBuilderCustomizer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-cassandra/src/test/java/org/springframework/ai/vectorstore/cassandra/autoconfigure/CassandraVectorStoreAutoConfigurationIT.java b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-cassandra/src/test/java/org/springframework/ai/vectorstore/cassandra/autoconfigure/CassandraVectorStoreAutoConfigurationIT.java
index 0ad388a5b60..7669a6266b5 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-cassandra/src/test/java/org/springframework/ai/vectorstore/cassandra/autoconfigure/CassandraVectorStoreAutoConfigurationIT.java
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-cassandra/src/test/java/org/springframework/ai/vectorstore/cassandra/autoconfigure/CassandraVectorStoreAutoConfigurationIT.java
@@ -37,7 +37,7 @@
import org.springframework.ai.vectorstore.cassandra.CassandraVectorStore;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
+import org.springframework.boot.cassandra.autoconfigure.CassandraAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-couchbase/pom.xml b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-couchbase/pom.xml
index 11e93742d7e..74f8aa0e8d7 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-couchbase/pom.xml
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-couchbase/pom.xml
@@ -59,6 +59,11 @@
spring-boot-autoconfigure-processor
true
+
+
+ org.springframework.boot
+ spring-boot-couchbase
+
org.springframework.ai
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-couchbase/src/main/java/org/springframework/ai/vectorstore/couchbase/autoconfigure/CouchbaseSearchVectorStoreAutoConfiguration.java b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-couchbase/src/main/java/org/springframework/ai/vectorstore/couchbase/autoconfigure/CouchbaseSearchVectorStoreAutoConfiguration.java
index 25aa0617930..82bfaf8cfc0 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-couchbase/src/main/java/org/springframework/ai/vectorstore/couchbase/autoconfigure/CouchbaseSearchVectorStoreAutoConfiguration.java
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-couchbase/src/main/java/org/springframework/ai/vectorstore/couchbase/autoconfigure/CouchbaseSearchVectorStoreAutoConfiguration.java
@@ -23,7 +23,7 @@
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration;
+import org.springframework.boot.couchbase.autoconfigure.CouchbaseAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.context.annotation.Bean;
@@ -49,9 +49,9 @@ public CouchbaseSearchVectorStore vectorStore(CouchbaseSearchVectorStoreProperti
mapper.from(properties::getBucketName).whenHasText().to(builder::bucketName);
mapper.from(properties::getScopeName).whenHasText().to(builder::scopeName);
mapper.from(properties::getCollectionName).whenHasText().to(builder::collectionName);
- mapper.from(properties::getDimensions).whenNonNull().to(builder::dimensions);
- mapper.from(properties::getSimilarity).whenNonNull().to(builder::similarityFunction);
- mapper.from(properties::getOptimization).whenNonNull().to(builder::indexOptimization);
+ mapper.from(properties::getDimensions).to(builder::dimensions);
+ mapper.from(properties::getSimilarity).to(builder::similarityFunction);
+ mapper.from(properties::getOptimization).to(builder::indexOptimization);
return builder.initializeSchema(properties.isInitializeSchema()).build();
}
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-couchbase/src/test/java/org/springframework/ai/vectorstore/couchbase/autoconfigure/CouchbaseSearchVectorStoreAutoConfigurationIT.java b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-couchbase/src/test/java/org/springframework/ai/vectorstore/couchbase/autoconfigure/CouchbaseSearchVectorStoreAutoConfigurationIT.java
index bb8cc8b646c..279c58a498e 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-couchbase/src/test/java/org/springframework/ai/vectorstore/couchbase/autoconfigure/CouchbaseSearchVectorStoreAutoConfigurationIT.java
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-couchbase/src/test/java/org/springframework/ai/vectorstore/couchbase/autoconfigure/CouchbaseSearchVectorStoreAutoConfigurationIT.java
@@ -36,8 +36,8 @@
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.couchbase.autoconfigure.CouchbaseAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-elasticsearch/pom.xml b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-elasticsearch/pom.xml
index 30ab3d801c4..988b7a5f749 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-elasticsearch/pom.xml
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-elasticsearch/pom.xml
@@ -59,6 +59,11 @@
spring-boot-autoconfigure-processor
true
+
+
+ org.springframework.boot
+ spring-boot-elasticsearch
+
org.springframework.ai
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-elasticsearch/src/main/java/org/springframework/ai/vectorstore/elasticsearch/autoconfigure/ElasticsearchVectorStoreAutoConfiguration.java b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-elasticsearch/src/main/java/org/springframework/ai/vectorstore/elasticsearch/autoconfigure/ElasticsearchVectorStoreAutoConfiguration.java
index 2c780c94f54..8e59f814f8e 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-elasticsearch/src/main/java/org/springframework/ai/vectorstore/elasticsearch/autoconfigure/ElasticsearchVectorStoreAutoConfiguration.java
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-elasticsearch/src/main/java/org/springframework/ai/vectorstore/elasticsearch/autoconfigure/ElasticsearchVectorStoreAutoConfiguration.java
@@ -16,8 +16,8 @@
package org.springframework.ai.vectorstore.elasticsearch.autoconfigure;
+import co.elastic.clients.transport.rest5_client.low_level.Rest5Client;
import io.micrometer.observation.ObservationRegistry;
-import org.elasticsearch.client.RestClient;
import org.springframework.ai.embedding.BatchingStrategy;
import org.springframework.ai.embedding.EmbeddingModel;
@@ -31,7 +31,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.elasticsearch.ElasticsearchRestClientAutoConfiguration;
+import org.springframework.boot.elasticsearch.autoconfigure.ElasticsearchRestClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.context.annotation.Bean;
@@ -49,7 +49,7 @@
* @since 1.0.0
*/
@AutoConfiguration(after = ElasticsearchRestClientAutoConfiguration.class)
-@ConditionalOnClass({ ElasticsearchVectorStore.class, EmbeddingModel.class, RestClient.class })
+@ConditionalOnClass({ ElasticsearchVectorStore.class, EmbeddingModel.class, Rest5Client.class })
@EnableConfigurationProperties(ElasticsearchVectorStoreProperties.class)
@ConditionalOnProperty(name = SpringAIVectorStoreTypes.TYPE, havingValue = SpringAIVectorStoreTypes.ELASTICSEARCH,
matchIfMissing = true)
@@ -63,7 +63,7 @@ BatchingStrategy batchingStrategy() {
@Bean
@ConditionalOnMissingBean
- ElasticsearchVectorStore vectorStore(ElasticsearchVectorStoreProperties properties, RestClient restClient,
+ ElasticsearchVectorStore vectorStore(ElasticsearchVectorStoreProperties properties, Rest5Client restClient,
EmbeddingModel embeddingModel, ObjectProvider observationRegistry,
ObjectProvider customObservationConvention,
BatchingStrategy batchingStrategy) {
@@ -71,8 +71,8 @@ ElasticsearchVectorStore vectorStore(ElasticsearchVectorStoreProperties properti
PropertyMapper mapper = PropertyMapper.get();
mapper.from(properties::getIndexName).whenHasText().to(elasticsearchVectorStoreOptions::setIndexName);
- mapper.from(properties::getDimensions).whenNonNull().to(elasticsearchVectorStoreOptions::setDimensions);
- mapper.from(properties::getSimilarity).whenNonNull().to(elasticsearchVectorStoreOptions::setSimilarity);
+ mapper.from(properties::getDimensions).to(elasticsearchVectorStoreOptions::setDimensions);
+ mapper.from(properties::getSimilarity).to(elasticsearchVectorStoreOptions::setSimilarity);
mapper.from(properties::getEmbeddingFieldName)
.whenHasText()
.to(elasticsearchVectorStoreOptions::setEmbeddingFieldName);
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-elasticsearch/src/test/java/org/springframework/ai/vectorstore/elasticsearch/autoconfigure/ElasticsearchVectorStoreAutoConfigurationIT.java b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-elasticsearch/src/test/java/org/springframework/ai/vectorstore/elasticsearch/autoconfigure/ElasticsearchVectorStoreAutoConfigurationIT.java
index 48e5191a171..3066e503e9d 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-elasticsearch/src/test/java/org/springframework/ai/vectorstore/elasticsearch/autoconfigure/ElasticsearchVectorStoreAutoConfigurationIT.java
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-elasticsearch/src/test/java/org/springframework/ai/vectorstore/elasticsearch/autoconfigure/ElasticsearchVectorStoreAutoConfigurationIT.java
@@ -42,8 +42,8 @@
import org.springframework.ai.vectorstore.elasticsearch.SimilarityFunction;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.elasticsearch.autoconfigure.ElasticsearchRestClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mariadb/pom.xml b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mariadb/pom.xml
index d773cc1773f..8971473f7e0 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mariadb/pom.xml
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mariadb/pom.xml
@@ -59,6 +59,11 @@
spring-boot-autoconfigure-processor
true
+
+
+ org.springframework.boot
+ spring-boot-jdbc
+
org.springframework.ai
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mariadb/src/main/java/org/springframework/ai/vectorstore/mariadb/autoconfigure/MariaDbStoreAutoConfiguration.java b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mariadb/src/main/java/org/springframework/ai/vectorstore/mariadb/autoconfigure/MariaDbStoreAutoConfiguration.java
index 0c9606646f5..951b2c9e854 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mariadb/src/main/java/org/springframework/ai/vectorstore/mariadb/autoconfigure/MariaDbStoreAutoConfiguration.java
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mariadb/src/main/java/org/springframework/ai/vectorstore/mariadb/autoconfigure/MariaDbStoreAutoConfiguration.java
@@ -31,7 +31,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.jdbc.JdbcTemplateAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.JdbcTemplateAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mariadb/src/test/java/org/springframework/ai/vectorstore/mariadb/autoconfigure/MariaDbStoreAutoConfigurationIT.java b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mariadb/src/test/java/org/springframework/ai/vectorstore/mariadb/autoconfigure/MariaDbStoreAutoConfigurationIT.java
index 09fbfd6ad0b..b708eb559ac 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mariadb/src/test/java/org/springframework/ai/vectorstore/mariadb/autoconfigure/MariaDbStoreAutoConfigurationIT.java
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mariadb/src/test/java/org/springframework/ai/vectorstore/mariadb/autoconfigure/MariaDbStoreAutoConfigurationIT.java
@@ -39,8 +39,8 @@
import org.springframework.ai.vectorstore.mariadb.MariaDBVectorStore;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.JdbcTemplateAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mongodb-atlas/pom.xml b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mongodb-atlas/pom.xml
index 6d283e019ca..a6d6a55273f 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mongodb-atlas/pom.xml
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mongodb-atlas/pom.xml
@@ -126,5 +126,9 @@
${project.parent.version}
test
+
+ org.springframework.boot
+ spring-boot-starter-mongodb
+
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mongodb-atlas/src/test/java/org/springframework/ai/vectorstore/mongodb/autoconfigure/MongoDBAtlasVectorStoreAutoConfigurationIT.java b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mongodb-atlas/src/test/java/org/springframework/ai/vectorstore/mongodb/autoconfigure/MongoDBAtlasVectorStoreAutoConfigurationIT.java
index afd2723b452..ef630530496 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mongodb-atlas/src/test/java/org/springframework/ai/vectorstore/mongodb/autoconfigure/MongoDBAtlasVectorStoreAutoConfigurationIT.java
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mongodb-atlas/src/test/java/org/springframework/ai/vectorstore/mongodb/autoconfigure/MongoDBAtlasVectorStoreAutoConfigurationIT.java
@@ -38,9 +38,8 @@
import org.springframework.ai.vectorstore.mongodb.atlas.MongoDBAtlasVectorStore;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
-import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.mongodb.autoconfigure.MongoAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -63,7 +62,7 @@ class MongoDBAtlasVectorStoreAutoConfigurationIT {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withUserConfiguration(Config.class)
- .withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
+ .withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class,
MongoDBAtlasVectorStoreAutoConfiguration.class, RestClientAutoConfiguration.class,
SpringAiRetryAutoConfiguration.class, OpenAiEmbeddingAutoConfiguration.class))
.withPropertyValues("spring.data.mongodb.database=springaisample",
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-neo4j/pom.xml b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-neo4j/pom.xml
index 94654167324..1ca5ba63668 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-neo4j/pom.xml
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-neo4j/pom.xml
@@ -59,6 +59,10 @@
spring-boot-autoconfigure-processor
true
+
+ org.springframework.boot
+ spring-boot-neo4j
+
org.springframework.ai
@@ -71,6 +75,11 @@
spring-boot-starter-test
test
+
+ org.springframework.boot
+ spring-boot-starter-neo4j
+ test
+
org.springframework.boot
spring-boot-testcontainers
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-neo4j/src/main/java/org/springframework/ai/vectorstore/neo4j/autoconfigure/Neo4jVectorStoreAutoConfiguration.java b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-neo4j/src/main/java/org/springframework/ai/vectorstore/neo4j/autoconfigure/Neo4jVectorStoreAutoConfiguration.java
index 245a36e7deb..7cc2f3b86be 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-neo4j/src/main/java/org/springframework/ai/vectorstore/neo4j/autoconfigure/Neo4jVectorStoreAutoConfiguration.java
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-neo4j/src/main/java/org/springframework/ai/vectorstore/neo4j/autoconfigure/Neo4jVectorStoreAutoConfiguration.java
@@ -30,7 +30,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.neo4j.Neo4jAutoConfiguration;
+import org.springframework.boot.neo4j.autoconfigure.Neo4jAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-neo4j/src/test/java/org/springframework/ai/vectorstore/neo4j/autoconfigure/Neo4jVectorStoreAutoConfigurationIT.java b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-neo4j/src/test/java/org/springframework/ai/vectorstore/neo4j/autoconfigure/Neo4jVectorStoreAutoConfigurationIT.java
index fd9077d6c73..47ff53d9987 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-neo4j/src/test/java/org/springframework/ai/vectorstore/neo4j/autoconfigure/Neo4jVectorStoreAutoConfigurationIT.java
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-neo4j/src/test/java/org/springframework/ai/vectorstore/neo4j/autoconfigure/Neo4jVectorStoreAutoConfigurationIT.java
@@ -37,7 +37,7 @@
import org.springframework.ai.vectorstore.neo4j.Neo4jVectorStore;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
+import org.springframework.boot.neo4j.autoconfigure.Neo4jAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-oracle/pom.xml b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-oracle/pom.xml
index 66c6202bd82..c157df942ac 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-oracle/pom.xml
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-oracle/pom.xml
@@ -59,6 +59,10 @@
spring-boot-autoconfigure-processor
true
+
+ org.springframework.boot
+ spring-boot-jdbc
+
org.springframework.ai
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-oracle/src/main/java/org/springframework/ai/vectorstore/oracle/autoconfigure/OracleVectorStoreAutoConfiguration.java b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-oracle/src/main/java/org/springframework/ai/vectorstore/oracle/autoconfigure/OracleVectorStoreAutoConfiguration.java
index eb31a4dda94..f08715feda5 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-oracle/src/main/java/org/springframework/ai/vectorstore/oracle/autoconfigure/OracleVectorStoreAutoConfiguration.java
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-oracle/src/main/java/org/springframework/ai/vectorstore/oracle/autoconfigure/OracleVectorStoreAutoConfiguration.java
@@ -31,7 +31,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.jdbc.JdbcTemplateAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.JdbcTemplateAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-oracle/src/test/java/org/springframework/ai/vectorstore/oracle/autoconfigure/OracleVectorStoreAutoConfigurationIT.java b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-oracle/src/test/java/org/springframework/ai/vectorstore/oracle/autoconfigure/OracleVectorStoreAutoConfigurationIT.java
index 1e1261389bd..61ca7179ae8 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-oracle/src/test/java/org/springframework/ai/vectorstore/oracle/autoconfigure/OracleVectorStoreAutoConfigurationIT.java
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-oracle/src/test/java/org/springframework/ai/vectorstore/oracle/autoconfigure/OracleVectorStoreAutoConfigurationIT.java
@@ -38,8 +38,8 @@
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
import org.springframework.ai.vectorstore.oracle.OracleVectorStore;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.JdbcTemplateAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-pgvector/pom.xml b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-pgvector/pom.xml
index 9bc06e4b948..20c394f9a4c 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-pgvector/pom.xml
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-pgvector/pom.xml
@@ -59,6 +59,10 @@
spring-boot-autoconfigure-processor
true
+
+ org.springframework.boot
+ spring-boot-jdbc
+
org.springframework.ai
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-pgvector/src/main/java/org/springframework/ai/vectorstore/pgvector/autoconfigure/PgVectorStoreAutoConfiguration.java b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-pgvector/src/main/java/org/springframework/ai/vectorstore/pgvector/autoconfigure/PgVectorStoreAutoConfiguration.java
index 839d88a37e9..a79d3f9d54e 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-pgvector/src/main/java/org/springframework/ai/vectorstore/pgvector/autoconfigure/PgVectorStoreAutoConfiguration.java
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-pgvector/src/main/java/org/springframework/ai/vectorstore/pgvector/autoconfigure/PgVectorStoreAutoConfiguration.java
@@ -31,7 +31,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.jdbc.JdbcTemplateAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.JdbcTemplateAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-pgvector/src/test/java/org/springframework/ai/vectorstore/pgvector/autoconfigure/PgVectorStoreAutoConfigurationIT.java b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-pgvector/src/test/java/org/springframework/ai/vectorstore/pgvector/autoconfigure/PgVectorStoreAutoConfigurationIT.java
index 3958c109cdb..efca91bf3f7 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-pgvector/src/test/java/org/springframework/ai/vectorstore/pgvector/autoconfigure/PgVectorStoreAutoConfigurationIT.java
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-pgvector/src/test/java/org/springframework/ai/vectorstore/pgvector/autoconfigure/PgVectorStoreAutoConfigurationIT.java
@@ -39,8 +39,8 @@
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
import org.springframework.ai.vectorstore.pgvector.PgVectorStore;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.JdbcTemplateAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-redis/pom.xml b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-redis/pom.xml
index a01969180e1..de67582edd6 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-redis/pom.xml
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-redis/pom.xml
@@ -56,8 +56,7 @@
org.springframework.boot
- spring-boot-autoconfigure-processor
- true
+ spring-boot-data-redis
@@ -71,6 +70,11 @@
spring-boot-starter-test
test
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+ test
+
org.springframework.boot
spring-boot-testcontainers
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-redis/src/main/java/org/springframework/ai/vectorstore/redis/autoconfigure/RedisVectorStoreAutoConfiguration.java b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-redis/src/main/java/org/springframework/ai/vectorstore/redis/autoconfigure/RedisVectorStoreAutoConfiguration.java
index abb5b629bb4..b63dda5dd44 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-redis/src/main/java/org/springframework/ai/vectorstore/redis/autoconfigure/RedisVectorStoreAutoConfiguration.java
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-redis/src/main/java/org/springframework/ai/vectorstore/redis/autoconfigure/RedisVectorStoreAutoConfiguration.java
@@ -34,7 +34,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.data.redis.RedisAutoConfiguration;
+import org.springframework.boot.data.redis.autoconfigure.RedisAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
diff --git a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-redis/src/test/java/org/springframework/ai/vectorstore/redis/autoconfigure/RedisVectorStoreAutoConfigurationIT.java b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-redis/src/test/java/org/springframework/ai/vectorstore/redis/autoconfigure/RedisVectorStoreAutoConfigurationIT.java
index 40d3bce6e93..20dd28aa9be 100644
--- a/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-redis/src/test/java/org/springframework/ai/vectorstore/redis/autoconfigure/RedisVectorStoreAutoConfigurationIT.java
+++ b/auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-redis/src/test/java/org/springframework/ai/vectorstore/redis/autoconfigure/RedisVectorStoreAutoConfigurationIT.java
@@ -36,7 +36,7 @@
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
import org.springframework.ai.vectorstore.redis.RedisVectorStore;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+import org.springframework.boot.data.redis.autoconfigure.RedisAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
diff --git a/memory/repository/spring-ai-model-chat-memory-repository-cassandra/pom.xml b/memory/repository/spring-ai-model-chat-memory-repository-cassandra/pom.xml
index 99a26be2e85..f5a6dc9fc44 100644
--- a/memory/repository/spring-ai-model-chat-memory-repository-cassandra/pom.xml
+++ b/memory/repository/spring-ai-model-chat-memory-repository-cassandra/pom.xml
@@ -50,6 +50,12 @@
java-driver-query-builder
+
+ org.springframework.boot
+ spring-boot-jdbc
+ test
+
+
org.springframework.boot
spring-boot-starter-test
diff --git a/memory/repository/spring-ai-model-chat-memory-repository-cassandra/src/test/java/org/springframework/ai/chat/memory/repository/cassandra/CassandraChatMemoryRepositoryIT.java b/memory/repository/spring-ai-model-chat-memory-repository-cassandra/src/test/java/org/springframework/ai/chat/memory/repository/cassandra/CassandraChatMemoryRepositoryIT.java
index b16f28d31e0..c570f7d864c 100644
--- a/memory/repository/spring-ai-model-chat-memory-repository-cassandra/src/test/java/org/springframework/ai/chat/memory/repository/cassandra/CassandraChatMemoryRepositoryIT.java
+++ b/memory/repository/spring-ai-model-chat-memory-repository-cassandra/src/test/java/org/springframework/ai/chat/memory/repository/cassandra/CassandraChatMemoryRepositoryIT.java
@@ -39,7 +39,7 @@
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
diff --git a/memory/repository/spring-ai-model-chat-memory-repository-jdbc/pom.xml b/memory/repository/spring-ai-model-chat-memory-repository-jdbc/pom.xml
index e2271ea6dc0..e9e310ddee5 100644
--- a/memory/repository/spring-ai-model-chat-memory-repository-jdbc/pom.xml
+++ b/memory/repository/spring-ai-model-chat-memory-repository-jdbc/pom.xml
@@ -91,6 +91,12 @@
test
+
+ org.springframework.boot
+ spring-boot-jdbc
+ test
+
+
org.testcontainers
testcontainers
diff --git a/memory/repository/spring-ai-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/chat/memory/repository/jdbc/AbstractJdbcChatMemoryRepositoryIT.java b/memory/repository/spring-ai-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/chat/memory/repository/jdbc/AbstractJdbcChatMemoryRepositoryIT.java
index c26dd69049d..db58f060a17 100644
--- a/memory/repository/spring-ai-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/chat/memory/repository/jdbc/AbstractJdbcChatMemoryRepositoryIT.java
+++ b/memory/repository/spring-ai-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/chat/memory/repository/jdbc/AbstractJdbcChatMemoryRepositoryIT.java
@@ -35,8 +35,8 @@
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.JdbcTemplateAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
diff --git a/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatModel.java b/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatModel.java
index f8091eee212..bc547f65037 100644
--- a/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatModel.java
+++ b/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatModel.java
@@ -30,6 +30,8 @@
import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.core.retry.RetryException;
+import org.springframework.http.HttpHeaders;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
@@ -78,7 +80,7 @@
import org.springframework.ai.tool.definition.ToolDefinition;
import org.springframework.ai.util.json.JsonParser;
import org.springframework.http.ResponseEntity;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
@@ -192,8 +194,19 @@ public ChatResponse internalCall(Prompt prompt, ChatResponse previousChatRespons
this.observationRegistry)
.observe(() -> {
- ResponseEntity completionEntity = this.retryTemplate.execute(
- ctx -> this.anthropicApi.chatCompletionEntity(request, this.getAdditionalHttpHeaders(prompt)));
+ ResponseEntity completionEntity = null;
+ try {
+ completionEntity = this.retryTemplate.execute(() -> this.anthropicApi.chatCompletionEntity(request,
+ this.getAdditionalHttpHeaders(prompt)));
+ }
+ catch (RetryException e) {
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
+ }
+ else {
+ throw new RuntimeException(e.getCause());
+ }
+ }
AnthropicApi.ChatCompletionResponse completionResponse = completionEntity.getBody();
AnthropicApi.Usage usage = completionResponse.usage();
@@ -427,14 +440,15 @@ else if (mimeType.contains("pdf")) {
+ ". Supported types are: images (image/*) and PDF documents (application/pdf)");
}
- private MultiValueMap getAdditionalHttpHeaders(Prompt prompt) {
+ private HttpHeaders getAdditionalHttpHeaders(Prompt prompt) {
Map headers = new HashMap<>(this.defaultOptions.getHttpHeaders());
if (prompt.getOptions() != null && prompt.getOptions() instanceof AnthropicChatOptions chatOptions) {
headers.putAll(chatOptions.getHttpHeaders());
}
- return CollectionUtils.toMultiValueMap(
- headers.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> List.of(e.getValue()))));
+ HttpHeaders httpHeaders = new HttpHeaders();
+ headers.forEach(httpHeaders::add);
+ return httpHeaders;
}
Prompt buildRequestPrompt(Prompt prompt) {
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 cdd1a4fef51..e84aff29464 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
@@ -48,8 +48,6 @@
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
@@ -157,7 +155,7 @@ private AnthropicApi(String baseUrl, String completionsPath, ApiKey anthropicApi
* status code and headers.
*/
public ResponseEntity chatCompletionEntity(ChatCompletionRequest chatRequest) {
- return chatCompletionEntity(chatRequest, new LinkedMultiValueMap<>());
+ return chatCompletionEntity(chatRequest, new HttpHeaders());
}
/**
@@ -168,7 +166,7 @@ public ResponseEntity chatCompletionEntity(ChatCompletio
* status code and headers.
*/
public ResponseEntity chatCompletionEntity(ChatCompletionRequest chatRequest,
- MultiValueMap additionalHttpHeader) {
+ HttpHeaders additionalHttpHeader) {
Assert.notNull(chatRequest, "The request body can not be null.");
Assert.isTrue(!chatRequest.stream(), "Request must set the stream property to false.");
@@ -194,7 +192,7 @@ public ResponseEntity chatCompletionEntity(ChatCompletio
* @return Returns a {@link Flux} stream from chat completion chunks.
*/
public Flux chatCompletionStream(ChatCompletionRequest chatRequest) {
- return chatCompletionStream(chatRequest, new LinkedMultiValueMap<>());
+ return chatCompletionStream(chatRequest, new HttpHeaders());
}
/**
@@ -205,7 +203,7 @@ public Flux chatCompletionStream(ChatCompletionRequest c
* @return Returns a {@link Flux} stream from chat completion chunks.
*/
public Flux chatCompletionStream(ChatCompletionRequest chatRequest,
- MultiValueMap additionalHttpHeader) {
+ HttpHeaders additionalHttpHeader) {
Assert.notNull(chatRequest, "The request body can not be null.");
Assert.isTrue(chatRequest.stream(), "Request must set the stream property to true.");
@@ -258,7 +256,7 @@ public Flux chatCompletionStream(ChatCompletionRequest c
}
private void addDefaultHeadersIfMissing(HttpHeaders headers) {
- if (!headers.containsKey(HEADER_X_API_KEY)) {
+ if (!headers.containsHeader(HEADER_X_API_KEY)) {
String apiKeyValue = this.apiKey.getValue();
if (StringUtils.hasText(apiKeyValue)) {
headers.add(HEADER_X_API_KEY, apiKeyValue);
diff --git a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatModelObservationIT.java b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatModelObservationIT.java
index 910f572d208..382dbe6f64f 100644
--- a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatModelObservationIT.java
+++ b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatModelObservationIT.java
@@ -40,7 +40,7 @@
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 org.springframework.core.retry.RetryTemplate;
import static org.assertj.core.api.Assertions.assertThat;
@@ -171,7 +171,7 @@ public AnthropicApi anthropicApi() {
public AnthropicChatModel anthropicChatModel(AnthropicApi anthropicApi,
TestObservationRegistry observationRegistry) {
return new AnthropicChatModel(anthropicApi, AnthropicChatOptions.builder().build(),
- ToolCallingManager.builder().build(), RetryTemplate.defaultInstance(), observationRegistry);
+ ToolCallingManager.builder().build(), new RetryTemplate(), observationRegistry);
}
}
diff --git a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/api/AnthropicApiBuilderTests.java b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/api/AnthropicApiBuilderTests.java
index 8a89ea306c7..08eb75ffaca 100644
--- a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/api/AnthropicApiBuilderTests.java
+++ b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/api/AnthropicApiBuilderTests.java
@@ -229,7 +229,7 @@ void dynamicApiKeyRestClientWithAdditionalApiKeyHeader() throws InterruptedExcep
.temperature(0.8)
.messages(List.of(chatCompletionMessage))
.build();
- MultiValueMap additionalHeaders = new LinkedMultiValueMap<>();
+ var additionalHeaders = new HttpHeaders();
additionalHeaders.add("x-api-key", "additional-key");
ResponseEntity response = api.chatCompletionEntity(request,
additionalHeaders);
@@ -328,7 +328,7 @@ void dynamicApiKeyWebClientWithAdditionalApiKey() throws InterruptedException {
.messages(List.of(chatCompletionMessage))
.stream(true)
.build();
- MultiValueMap additionalHeaders = new LinkedMultiValueMap<>();
+ var additionalHeaders = new HttpHeaders();
additionalHeaders.add("x-api-key", "additional-key");
api.chatCompletionStream(request, additionalHeaders).collectList().block();
diff --git a/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekChatModel.java b/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekChatModel.java
index fba44ffd4ce..bd8581cde97 100644
--- a/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekChatModel.java
+++ b/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekChatModel.java
@@ -25,6 +25,8 @@
import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.core.retry.RetryException;
+
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
@@ -67,7 +69,7 @@
import org.springframework.ai.support.UsageCalculator;
import org.springframework.ai.tool.definition.ToolDefinition;
import org.springframework.http.ResponseEntity;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
@@ -165,8 +167,18 @@ public ChatResponse internalCall(Prompt prompt, ChatResponse previousChatRespons
this.observationRegistry)
.observe(() -> {
- ResponseEntity completionEntity = this.retryTemplate
- .execute(ctx -> this.deepSeekApi.chatCompletionEntity(request));
+ ResponseEntity completionEntity = null;
+ try {
+ completionEntity = this.retryTemplate.execute(() -> this.deepSeekApi.chatCompletionEntity(request));
+ }
+ catch (RetryException e) {
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
+ }
+ else {
+ throw new RuntimeException(e.getCause());
+ }
+ }
var chatCompletion = completionEntity.getBody();
diff --git a/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/api/DeepSeekApi.java b/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/api/DeepSeekApi.java
index 13415829854..d6b36d554f8 100644
--- a/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/api/DeepSeekApi.java
+++ b/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/api/DeepSeekApi.java
@@ -41,8 +41,6 @@
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
@@ -80,7 +78,7 @@ public class DeepSeekApi {
* @param webClientBuilder WebClient builder.
* @param responseErrorHandler Response error handler.
*/
- public DeepSeekApi(String baseUrl, ApiKey apiKey, MultiValueMap headers, String completionsPath,
+ public DeepSeekApi(String baseUrl, ApiKey apiKey, HttpHeaders headers, String completionsPath,
String betaPrefixPath, RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder,
ResponseErrorHandler responseErrorHandler) {
@@ -132,7 +130,7 @@ public ResponseEntity chatCompletionEntity(ChatCompletionRequest
* @return Returns a {@link Flux} stream from chat completion chunks.
*/
public Flux chatCompletionStream(ChatCompletionRequest chatRequest) {
- return chatCompletionStream(chatRequest, new LinkedMultiValueMap<>());
+ return chatCompletionStream(chatRequest, new HttpHeaders());
}
/**
@@ -144,7 +142,7 @@ public Flux chatCompletionStream(ChatCompletionRequest chat
* @return Returns a {@link Flux} stream from chat completion chunks.
*/
public Flux chatCompletionStream(ChatCompletionRequest chatRequest,
- MultiValueMap additionalHttpHeader) {
+ HttpHeaders additionalHttpHeader) {
Assert.notNull(chatRequest, "The request body can not be null.");
Assert.isTrue(chatRequest.stream(), "Request must set the stream property to true.");
@@ -911,7 +909,7 @@ public static final class Builder {
private ApiKey apiKey;
- private MultiValueMap headers = new LinkedMultiValueMap<>();
+ private HttpHeaders headers = new HttpHeaders();
private String completionsPath = org.springframework.ai.deepseek.api.common.DeepSeekConstants.DEFAULT_COMPLETIONS_PATH;
@@ -941,7 +939,7 @@ public Builder apiKey(String simpleApiKey) {
return this;
}
- public Builder headers(MultiValueMap headers) {
+ public Builder headers(HttpHeaders headers) {
Assert.notNull(headers, "headers cannot be null");
this.headers = headers;
return this;
diff --git a/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/DeepSeekRetryTests.java b/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/DeepSeekRetryTests.java
index 35f24eaadeb..895a393260e 100644
--- a/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/DeepSeekRetryTests.java
+++ b/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/DeepSeekRetryTests.java
@@ -35,10 +35,10 @@
import org.springframework.ai.retry.RetryUtils;
import org.springframework.ai.retry.TransientAiException;
import org.springframework.http.ResponseEntity;
-import org.springframework.retry.RetryCallback;
-import org.springframework.retry.RetryContext;
-import org.springframework.retry.RetryListener;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryListener;
+import org.springframework.core.retry.RetryPolicy;
+import org.springframework.core.retry.Retryable;
+import org.springframework.core.retry.RetryTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -62,7 +62,7 @@ public class DeepSeekRetryTests {
public void beforeEach() {
RetryTemplate retryTemplate = RetryUtils.SHORT_RETRY_TEMPLATE;
this.retryListener = new TestRetryListener();
- retryTemplate.registerListener(this.retryListener);
+ retryTemplate.setRetryListener(this.retryListener);
this.chatModel = DeepSeekChatModel.builder()
.deepSeekApi(this.deepSeekApi)
@@ -88,7 +88,7 @@ public void deepSeekChatTransientError() {
assertThat(result).isNotNull();
assertThat(result.getResult().getOutput().getText()).isSameAs("Response");
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
}
@@ -116,7 +116,7 @@ public void deepSeekChatStreamTransientError() {
assertThat(result).isNotNull();
assertThat(result.getResult().getOutput().getText()).isSameAs("Response");
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
}
@@ -134,14 +134,15 @@ private static class TestRetryListener implements RetryListener {
int onSuccessRetryCount = 0;
@Override
- public void onSuccess(RetryContext context, RetryCallback callback, T result) {
- this.onSuccessRetryCount = context.getRetryCount();
+ public void beforeRetry(final RetryPolicy retryPolicy, final Retryable> retryable) {
+ // Count each retry attempt
+ this.onErrorRetryCount++;
}
@Override
- public void onError(RetryContext context, RetryCallback callback,
- Throwable throwable) {
- this.onErrorRetryCount = context.getRetryCount();
+ public void onRetrySuccess(final RetryPolicy retryPolicy, final Retryable> retryable, final Object result) {
+ // Count successful retries - we increment when we succeed after a failure
+ this.onSuccessRetryCount++;
}
}
diff --git a/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/chat/DeepSeekChatModelObservationIT.java b/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/chat/DeepSeekChatModelObservationIT.java
index 00e8732dade..b2900e479cb 100644
--- a/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/chat/DeepSeekChatModelObservationIT.java
+++ b/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/chat/DeepSeekChatModelObservationIT.java
@@ -40,7 +40,7 @@
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 org.springframework.core.retry.RetryTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.ai.chat.observation.ChatModelObservationDocumentation.HighCardinalityKeyNames;
@@ -172,7 +172,7 @@ public DeepSeekApi deepSeekApi() {
public DeepSeekChatModel deepSeekChatModel(DeepSeekApi deepSeekApi,
TestObservationRegistry observationRegistry) {
return new DeepSeekChatModel(deepSeekApi, DeepSeekChatOptions.builder().build(),
- ToolCallingManager.builder().build(), RetryTemplate.defaultInstance(), observationRegistry);
+ ToolCallingManager.builder().build(), new RetryTemplate(), observationRegistry);
}
}
diff --git a/models/spring-ai-elevenlabs/src/main/java/org/springframework/ai/elevenlabs/ElevenLabsTextToSpeechModel.java b/models/spring-ai-elevenlabs/src/main/java/org/springframework/ai/elevenlabs/ElevenLabsTextToSpeechModel.java
index 68ed07568a8..7f4a5a98621 100644
--- a/models/spring-ai-elevenlabs/src/main/java/org/springframework/ai/elevenlabs/ElevenLabsTextToSpeechModel.java
+++ b/models/spring-ai-elevenlabs/src/main/java/org/springframework/ai/elevenlabs/ElevenLabsTextToSpeechModel.java
@@ -20,6 +20,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.core.retry.RetryException;
import reactor.core.publisher.Flux;
import org.springframework.ai.audio.tts.Speech;
@@ -29,7 +30,7 @@
import org.springframework.ai.audio.tts.TextToSpeechResponse;
import org.springframework.ai.elevenlabs.api.ElevenLabsApi;
import org.springframework.ai.retry.RetryUtils;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@@ -73,15 +74,26 @@ public static Builder builder() {
public TextToSpeechResponse call(TextToSpeechPrompt prompt) {
RequestContext requestContext = prepareRequest(prompt);
- byte[] audioData = this.retryTemplate.execute(context -> {
- var response = this.elevenLabsApi.textToSpeech(requestContext.request, requestContext.voiceId,
- requestContext.queryParameters);
- if (response.getBody() == null) {
- logger.warn("No speech response returned for request: {}", requestContext.request);
- return new byte[0];
+ byte[] audioData = null;
+ try {
+ audioData = this.retryTemplate.execute(() -> {
+ var response = this.elevenLabsApi.textToSpeech(requestContext.request, requestContext.voiceId,
+ requestContext.queryParameters);
+ if (response.getBody() == null) {
+ logger.warn("No speech response returned for request: {}", requestContext.request);
+ return new byte[0];
+ }
+ return response.getBody();
+ });
+ }
+ catch (RetryException e) {
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
+ }
+ else {
+ throw new RuntimeException(e.getCause());
}
- return response.getBody();
- });
+ }
return new TextToSpeechResponse(List.of(new Speech(audioData)));
}
@@ -90,9 +102,19 @@ public TextToSpeechResponse call(TextToSpeechPrompt prompt) {
public Flux stream(TextToSpeechPrompt prompt) {
RequestContext requestContext = prepareRequest(prompt);
- return this.retryTemplate.execute(context -> this.elevenLabsApi
- .textToSpeechStream(requestContext.request, requestContext.voiceId, requestContext.queryParameters)
- .map(entity -> new TextToSpeechResponse(List.of(new Speech(entity.getBody())))));
+ try {
+ return this.retryTemplate.execute(() -> this.elevenLabsApi
+ .textToSpeechStream(requestContext.request, requestContext.voiceId, requestContext.queryParameters)
+ .map(entity -> new TextToSpeechResponse(List.of(new Speech(entity.getBody())))));
+ }
+ catch (RetryException e) {
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
+ }
+ else {
+ throw new RuntimeException(e.getCause());
+ }
+ }
}
private RequestContext prepareRequest(TextToSpeechPrompt prompt) {
diff --git a/models/spring-ai-elevenlabs/src/main/java/org/springframework/ai/elevenlabs/api/ElevenLabsApi.java b/models/spring-ai-elevenlabs/src/main/java/org/springframework/ai/elevenlabs/api/ElevenLabsApi.java
index 10ce0349070..a6879b4f43b 100644
--- a/models/spring-ai-elevenlabs/src/main/java/org/springframework/ai/elevenlabs/api/ElevenLabsApi.java
+++ b/models/spring-ai-elevenlabs/src/main/java/org/springframework/ai/elevenlabs/api/ElevenLabsApi.java
@@ -33,7 +33,6 @@
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
-import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
@@ -62,9 +61,8 @@ public final class ElevenLabsApi {
* @param webClientBuilder A builder for the Spring WebClient.
* @param responseErrorHandler A custom error handler for API responses.
*/
- private ElevenLabsApi(String baseUrl, ApiKey apiKey, MultiValueMap headers,
- RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder,
- ResponseErrorHandler responseErrorHandler) {
+ private ElevenLabsApi(String baseUrl, ApiKey apiKey, HttpHeaders headers, RestClient.Builder restClientBuilder,
+ WebClient.Builder webClientBuilder, ResponseErrorHandler responseErrorHandler) {
Consumer jsonContentHeaders = h -> {
if (!(apiKey instanceof NoopApiKey)) {
@@ -331,7 +329,7 @@ public static final class Builder {
private ApiKey apiKey;
- private MultiValueMap headers = new LinkedMultiValueMap<>();
+ private HttpHeaders headers = new HttpHeaders();
private RestClient.Builder restClientBuilder = RestClient.builder();
@@ -357,7 +355,7 @@ public Builder apiKey(String simpleApiKey) {
return this;
}
- public Builder headers(MultiValueMap headers) {
+ public Builder headers(HttpHeaders headers) {
Assert.notNull(headers, "headers cannot be null");
this.headers = headers;
return this;
diff --git a/models/spring-ai-elevenlabs/src/main/java/org/springframework/ai/elevenlabs/api/ElevenLabsVoicesApi.java b/models/spring-ai-elevenlabs/src/main/java/org/springframework/ai/elevenlabs/api/ElevenLabsVoicesApi.java
index 3f6f6377937..e501ca98864 100644
--- a/models/spring-ai-elevenlabs/src/main/java/org/springframework/ai/elevenlabs/api/ElevenLabsVoicesApi.java
+++ b/models/spring-ai-elevenlabs/src/main/java/org/springframework/ai/elevenlabs/api/ElevenLabsVoicesApi.java
@@ -32,8 +32,6 @@
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
@@ -56,8 +54,8 @@ public class ElevenLabsVoicesApi {
* @param restClientBuilder A builder for the Spring RestClient.
* @param responseErrorHandler A custom error handler for API responses.
*/
- public ElevenLabsVoicesApi(String baseUrl, ApiKey apiKey, MultiValueMap headers,
- RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) {
+ public ElevenLabsVoicesApi(String baseUrl, ApiKey apiKey, HttpHeaders headers, RestClient.Builder restClientBuilder,
+ ResponseErrorHandler responseErrorHandler) {
Consumer jsonContentHeaders = h -> {
if (!(apiKey instanceof NoopApiKey)) {
h.set("xi-api-key", apiKey.getValue());
@@ -399,7 +397,7 @@ public static final class Builder {
private ApiKey apiKey;
- private MultiValueMap headers = new LinkedMultiValueMap<>();
+ private HttpHeaders headers = new HttpHeaders();
private RestClient.Builder restClientBuilder = RestClient.builder();
@@ -423,7 +421,7 @@ public Builder apiKey(String simpleApiKey) {
return this;
}
- public Builder headers(MultiValueMap headers) {
+ public Builder headers(HttpHeaders headers) {
Assert.notNull(headers, "headers cannot be null");
this.headers = headers;
return this;
diff --git a/models/spring-ai-google-genai-embedding/src/main/java/org/springframework/ai/google/genai/text/GoogleGenAiTextEmbeddingModel.java b/models/spring-ai-google-genai-embedding/src/main/java/org/springframework/ai/google/genai/text/GoogleGenAiTextEmbeddingModel.java
index 46c87cd6862..049720082bf 100644
--- a/models/spring-ai-google-genai-embedding/src/main/java/org/springframework/ai/google/genai/text/GoogleGenAiTextEmbeddingModel.java
+++ b/models/spring-ai-google-genai-embedding/src/main/java/org/springframework/ai/google/genai/text/GoogleGenAiTextEmbeddingModel.java
@@ -46,7 +46,8 @@
import org.springframework.ai.model.ModelOptionsUtils;
import org.springframework.ai.observation.conventions.AiProvider;
import org.springframework.ai.retry.RetryUtils;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryException;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -168,8 +169,19 @@ public EmbeddingResponse call(EmbeddingRequest request) {
}
// Call the embedding API with retry
- EmbedContentResponse embeddingResponse = this.retryTemplate
- .execute(context -> this.genAiClient.models.embedContent(modelName, validTexts, config));
+ EmbedContentResponse embeddingResponse = null;
+ try {
+ embeddingResponse = this.retryTemplate
+ .execute(() -> this.genAiClient.models.embedContent(modelName, validTexts, config));
+ }
+ catch (RetryException e) {
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
+ }
+ else {
+ throw new RuntimeException(e.getCause());
+ }
+ }
// Process the response
// Note: We need to handle the case where some texts were filtered out
diff --git a/models/spring-ai-google-genai-embedding/src/test/java/org/springframework/ai/google/genai/text/GoogleGenAiTextEmbeddingRetryTests.java b/models/spring-ai-google-genai-embedding/src/test/java/org/springframework/ai/google/genai/text/GoogleGenAiTextEmbeddingRetryTests.java
index 4dc9fce14c5..08ea9a8af07 100644
--- a/models/spring-ai-google-genai-embedding/src/test/java/org/springframework/ai/google/genai/text/GoogleGenAiTextEmbeddingRetryTests.java
+++ b/models/spring-ai-google-genai-embedding/src/test/java/org/springframework/ai/google/genai/text/GoogleGenAiTextEmbeddingRetryTests.java
@@ -36,10 +36,10 @@
import org.springframework.ai.google.genai.GoogleGenAiEmbeddingConnectionDetails;
import org.springframework.ai.retry.RetryUtils;
import org.springframework.ai.retry.TransientAiException;
-import org.springframework.retry.RetryCallback;
-import org.springframework.retry.RetryContext;
-import org.springframework.retry.RetryListener;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryListener;
+import org.springframework.core.retry.RetryPolicy;
+import org.springframework.core.retry.Retryable;
+import org.springframework.core.retry.RetryTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
@@ -75,7 +75,7 @@ public class GoogleGenAiTextEmbeddingRetryTests {
public void setUp() throws Exception {
this.retryTemplate = RetryUtils.SHORT_RETRY_TEMPLATE;
this.retryListener = new TestRetryListener();
- this.retryTemplate.registerListener(this.retryListener);
+ this.retryTemplate.setRetryListener(this.retryListener);
// Create a mock Client and use reflection to set the models field
this.mockGenAiClient = mock(Client.class);
@@ -114,7 +114,7 @@ public void vertexAiEmbeddingTransientError() {
assertThat(result).isNotNull();
assertThat(result.getResults()).hasSize(1);
assertThat(result.getResults().get(0).getOutput()).isEqualTo(new float[] { 9.9f, 8.8f });
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
verify(this.mockModels, times(3)).embedContent(anyString(), any(List.class), any(EmbedContentConfig.class));
@@ -143,14 +143,15 @@ private static class TestRetryListener implements RetryListener {
int onSuccessRetryCount = 0;
@Override
- public void onSuccess(RetryContext context, RetryCallback callback, T result) {
- this.onSuccessRetryCount = context.getRetryCount();
+ public void beforeRetry(final RetryPolicy retryPolicy, final Retryable> retryable) {
+ // Count each retry attempt
+ this.onErrorRetryCount++;
}
@Override
- public void onError(RetryContext context, RetryCallback callback,
- Throwable throwable) {
- this.onErrorRetryCount = context.getRetryCount();
+ public void onRetrySuccess(final RetryPolicy retryPolicy, final Retryable> retryable, final Object result) {
+ // Count successful retries - we increment when we succeed after a failure
+ this.onSuccessRetryCount++;
}
}
diff --git a/models/spring-ai-google-genai-embedding/src/test/java/org/springframework/ai/google/genai/text/TestGoogleGenAiTextEmbeddingModel.java b/models/spring-ai-google-genai-embedding/src/test/java/org/springframework/ai/google/genai/text/TestGoogleGenAiTextEmbeddingModel.java
index 44a06031afc..9836f63ec30 100644
--- a/models/spring-ai-google-genai-embedding/src/test/java/org/springframework/ai/google/genai/text/TestGoogleGenAiTextEmbeddingModel.java
+++ b/models/spring-ai-google-genai-embedding/src/test/java/org/springframework/ai/google/genai/text/TestGoogleGenAiTextEmbeddingModel.java
@@ -17,7 +17,7 @@
package org.springframework.ai.google.genai.text;
import org.springframework.ai.google.genai.GoogleGenAiEmbeddingConnectionDetails;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
/**
* Test implementation of GoogleGenAiTextEmbeddingModel that uses a mock connection for
diff --git a/models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiChatModel.java b/models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiChatModel.java
index 8e38008e859..7411d085c65 100644
--- a/models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiChatModel.java
+++ b/models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiChatModel.java
@@ -46,6 +46,7 @@
import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.core.retry.RetryException;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;
@@ -88,7 +89,7 @@
import org.springframework.ai.tool.definition.ToolDefinition;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.lang.NonNull;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
@@ -404,30 +405,41 @@ private ChatResponse internalCall(Prompt prompt, ChatResponse previousChatRespon
ChatResponse response = ChatModelObservationDocumentation.CHAT_MODEL_OPERATION
.observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,
this.observationRegistry)
- .observe(() -> this.retryTemplate.execute(context -> {
-
- var geminiRequest = createGeminiRequest(prompt);
-
- GenerateContentResponse generateContentResponse = this.getContentResponse(geminiRequest);
+ .observe(() -> {
+ try {
+ return this.retryTemplate.execute(() -> {
+
+ var geminiRequest = createGeminiRequest(prompt);
+
+ GenerateContentResponse generateContentResponse = this.getContentResponse(geminiRequest);
+
+ List generations = generateContentResponse.candidates()
+ .orElse(List.of())
+ .stream()
+ .map(this::responseCandidateToGeneration)
+ .flatMap(List::stream)
+ .toList();
+
+ var usage = generateContentResponse.usageMetadata();
+ GoogleGenAiChatOptions options = (GoogleGenAiChatOptions) prompt.getOptions();
+ Usage currentUsage = (usage.isPresent()) ? getDefaultUsage(usage.get(), options)
+ : getDefaultUsage(null, options);
+ Usage cumulativeUsage = UsageCalculator.getCumulativeUsage(currentUsage, previousChatResponse);
+ ChatResponse chatResponse = new ChatResponse(generations,
+ toChatResponseMetadata(cumulativeUsage, generateContentResponse.modelVersion().get()));
+
+ observationContext.setResponse(chatResponse);
+ return chatResponse;
+ });
+ }
+ catch (RetryException e) {
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
+ }
- List generations = generateContentResponse.candidates()
- .orElse(List.of())
- .stream()
- .map(this::responseCandidateToGeneration)
- .flatMap(List::stream)
- .toList();
-
- var usage = generateContentResponse.usageMetadata();
- GoogleGenAiChatOptions options = (GoogleGenAiChatOptions) prompt.getOptions();
- Usage currentUsage = (usage.isPresent()) ? getDefaultUsage(usage.get(), options)
- : getDefaultUsage(null, options);
- Usage cumulativeUsage = UsageCalculator.getCumulativeUsage(currentUsage, previousChatResponse);
- ChatResponse chatResponse = new ChatResponse(generations,
- toChatResponseMetadata(cumulativeUsage, generateContentResponse.modelVersion().get()));
-
- observationContext.setResponse(chatResponse);
- return chatResponse;
- }));
+ throw new RuntimeException(e);
+ }
+ });
if (this.toolExecutionEligibilityPredicate.isToolExecutionRequired(prompt.getOptions(), response)) {
var toolExecutionResult = this.toolCallingManager.executeToolCalls(prompt, response);
diff --git a/models/spring-ai-google-genai/src/test/java/org/springframework/ai/google/genai/GoogleGenAiChatModelCachedContentTests.java b/models/spring-ai-google-genai/src/test/java/org/springframework/ai/google/genai/GoogleGenAiChatModelCachedContentTests.java
index af4ea6679e0..f2340cc79d6 100644
--- a/models/spring-ai-google-genai/src/test/java/org/springframework/ai/google/genai/GoogleGenAiChatModelCachedContentTests.java
+++ b/models/spring-ai-google-genai/src/test/java/org/springframework/ai/google/genai/GoogleGenAiChatModelCachedContentTests.java
@@ -36,7 +36,7 @@
import org.springframework.ai.google.genai.cache.GoogleGenAiCachedContent;
import org.springframework.ai.google.genai.cache.GoogleGenAiCachedContentService;
import org.springframework.ai.retry.RetryUtils;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/models/spring-ai-google-genai/src/test/java/org/springframework/ai/google/genai/GoogleGenAiChatModelExtendedUsageTests.java b/models/spring-ai-google-genai/src/test/java/org/springframework/ai/google/genai/GoogleGenAiChatModelExtendedUsageTests.java
index 27e3ccf24a5..1b75b991a3a 100644
--- a/models/spring-ai-google-genai/src/test/java/org/springframework/ai/google/genai/GoogleGenAiChatModelExtendedUsageTests.java
+++ b/models/spring-ai-google-genai/src/test/java/org/springframework/ai/google/genai/GoogleGenAiChatModelExtendedUsageTests.java
@@ -41,7 +41,7 @@
import org.springframework.ai.google.genai.metadata.GoogleGenAiTrafficType;
import org.springframework.ai.google.genai.metadata.GoogleGenAiUsage;
import org.springframework.ai.retry.RetryUtils;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/models/spring-ai-google-genai/src/test/java/org/springframework/ai/google/genai/GoogleGenAiRetryTests.java b/models/spring-ai-google-genai/src/test/java/org/springframework/ai/google/genai/GoogleGenAiRetryTests.java
index 4170c992c64..85c570a7c3c 100644
--- a/models/spring-ai-google-genai/src/test/java/org/springframework/ai/google/genai/GoogleGenAiRetryTests.java
+++ b/models/spring-ai-google-genai/src/test/java/org/springframework/ai/google/genai/GoogleGenAiRetryTests.java
@@ -27,10 +27,10 @@
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.ai.retry.RetryUtils;
-import org.springframework.retry.RetryCallback;
-import org.springframework.retry.RetryContext;
-import org.springframework.retry.RetryListener;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryListener;
+import org.springframework.core.retry.RetryPolicy;
+import org.springframework.core.retry.Retryable;
+import org.springframework.core.retry.RetryTemplate;
/**
* @author Mark Pollack
@@ -55,7 +55,7 @@ public class GoogleGenAiRetryTests {
public void setUp() {
this.retryTemplate = RetryUtils.SHORT_RETRY_TEMPLATE;
this.retryListener = new TestRetryListener();
- this.retryTemplate.registerListener(this.retryListener);
+ this.retryTemplate.setRetryListener(this.retryListener);
this.chatModel = new org.springframework.ai.google.genai.TestGoogleGenAiGeminiChatModel(this.genAiClient,
GoogleGenAiChatOptions.builder()
@@ -95,14 +95,15 @@ private static class TestRetryListener implements RetryListener {
int onSuccessRetryCount = 0;
@Override
- public void onSuccess(RetryContext context, RetryCallback callback, T result) {
- this.onSuccessRetryCount = context.getRetryCount();
+ public void beforeRetry(final RetryPolicy retryPolicy, final Retryable> retryable) {
+ // Count each retry attempt
+ this.onErrorRetryCount++;
}
@Override
- public void onError(RetryContext context, RetryCallback callback,
- Throwable throwable) {
- this.onErrorRetryCount = context.getRetryCount();
+ public void onRetrySuccess(final RetryPolicy retryPolicy, final Retryable> retryable, final Object result) {
+ // Count successful retries - we increment when we succeed after a failure
+ this.onSuccessRetryCount++;
}
}
diff --git a/models/spring-ai-google-genai/src/test/java/org/springframework/ai/google/genai/TestGoogleGenAiGeminiChatModel.java b/models/spring-ai-google-genai/src/test/java/org/springframework/ai/google/genai/TestGoogleGenAiGeminiChatModel.java
index 6c63133fd1f..2730b73e501 100644
--- a/models/spring-ai-google-genai/src/test/java/org/springframework/ai/google/genai/TestGoogleGenAiGeminiChatModel.java
+++ b/models/spring-ai-google-genai/src/test/java/org/springframework/ai/google/genai/TestGoogleGenAiGeminiChatModel.java
@@ -20,7 +20,7 @@
import com.google.genai.types.GenerateContentResponse;
import org.springframework.ai.model.tool.ToolCallingManager;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
/**
* @author Mark Pollack
diff --git a/models/spring-ai-huggingface/pom.xml b/models/spring-ai-huggingface/pom.xml
index fae8ecbd802..9bb8329a1f3 100644
--- a/models/spring-ai-huggingface/pom.xml
+++ b/models/spring-ai-huggingface/pom.xml
@@ -90,7 +90,7 @@
io.swagger.codegen.v3
swagger-codegen-maven-plugin
- 3.0.64
+ 3.0.74
diff --git a/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/MiniMaxChatModel.java b/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/MiniMaxChatModel.java
index 5c771b2f5db..5e9af943676 100644
--- a/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/MiniMaxChatModel.java
+++ b/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/MiniMaxChatModel.java
@@ -26,6 +26,7 @@
import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.core.retry.RetryException;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
@@ -69,7 +70,7 @@
import org.springframework.ai.retry.RetryUtils;
import org.springframework.ai.tool.definition.ToolDefinition;
import org.springframework.http.ResponseEntity;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
@@ -253,8 +254,18 @@ public ChatResponse call(Prompt prompt) {
this.observationRegistry)
.observe(() -> {
- ResponseEntity completionEntity = this.retryTemplate
- .execute(ctx -> this.miniMaxApi.chatCompletionEntity(request));
+ ResponseEntity completionEntity = null;
+ try {
+ completionEntity = this.retryTemplate.execute(() -> this.miniMaxApi.chatCompletionEntity(request));
+ }
+ catch (RetryException e) {
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
+ }
+ else {
+ throw new RuntimeException(e.getCause());
+ }
+ }
var chatCompletion = completionEntity.getBody();
@@ -328,8 +339,18 @@ public Flux stream(Prompt prompt) {
return Flux.deferContextual(contextView -> {
ChatCompletionRequest request = createRequest(requestPrompt, true);
- Flux completionChunks = this.retryTemplate
- .execute(ctx -> this.miniMaxApi.chatCompletionStream(request));
+ Flux completionChunks = null;
+ try {
+ completionChunks = this.retryTemplate.execute(() -> this.miniMaxApi.chatCompletionStream(request));
+ }
+ catch (RetryException e) {
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
+ }
+ else {
+ throw new RuntimeException(e.getCause());
+ }
+ }
// For chunked responses, only the first chunk contains the choice role.
// The rest of the chunks with same ID share the same role.
diff --git a/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/MiniMaxEmbeddingModel.java b/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/MiniMaxEmbeddingModel.java
index 5dd0077fed3..944e7a40f04 100644
--- a/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/MiniMaxEmbeddingModel.java
+++ b/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/MiniMaxEmbeddingModel.java
@@ -22,6 +22,7 @@
import io.micrometer.observation.ObservationRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.core.retry.RetryException;
import org.springframework.ai.chat.metadata.DefaultUsage;
import org.springframework.ai.document.Document;
@@ -40,7 +41,7 @@
import org.springframework.ai.minimax.api.MiniMaxApiConstants;
import org.springframework.ai.model.ModelOptionsUtils;
import org.springframework.ai.retry.RetryUtils;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -165,8 +166,19 @@ public EmbeddingResponse call(EmbeddingRequest request) {
.observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,
this.observationRegistry)
.observe(() -> {
- MiniMaxApi.EmbeddingList apiEmbeddingResponse = this.retryTemplate
- .execute(ctx -> this.miniMaxApi.embeddings(apiRequest).getBody());
+ MiniMaxApi.EmbeddingList apiEmbeddingResponse = null;
+ try {
+ apiEmbeddingResponse = this.retryTemplate
+ .execute(() -> this.miniMaxApi.embeddings(apiRequest).getBody());
+ }
+ catch (RetryException e) {
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
+ }
+ else {
+ throw new RuntimeException(e.getCause());
+ }
+ }
if (apiEmbeddingResponse == null) {
logger.warn("No embeddings returned for request: {}", request);
diff --git a/models/spring-ai-minimax/src/test/java/org/springframework/ai/minimax/api/MiniMaxRetryTests.java b/models/spring-ai-minimax/src/test/java/org/springframework/ai/minimax/api/MiniMaxRetryTests.java
index 9f165e27c0d..db3acd39525 100644
--- a/models/spring-ai-minimax/src/test/java/org/springframework/ai/minimax/api/MiniMaxRetryTests.java
+++ b/models/spring-ai-minimax/src/test/java/org/springframework/ai/minimax/api/MiniMaxRetryTests.java
@@ -46,10 +46,10 @@
import org.springframework.ai.retry.RetryUtils;
import org.springframework.ai.retry.TransientAiException;
import org.springframework.http.ResponseEntity;
-import org.springframework.retry.RetryCallback;
-import org.springframework.retry.RetryContext;
-import org.springframework.retry.RetryListener;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryListener;
+import org.springframework.core.retry.RetryPolicy;
+import org.springframework.core.retry.RetryTemplate;
+import org.springframework.core.retry.Retryable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -78,7 +78,7 @@ public class MiniMaxRetryTests {
public void beforeEach() {
this.retryTemplate = RetryUtils.SHORT_RETRY_TEMPLATE;
this.retryListener = new TestRetryListener();
- this.retryTemplate.registerListener(this.retryListener);
+ this.retryTemplate.setRetryListener(this.retryListener);
this.chatModel = new MiniMaxChatModel(this.miniMaxApi, MiniMaxChatOptions.builder().build(),
ToolCallingManager.builder().build(), this.retryTemplate);
@@ -103,7 +103,7 @@ public void miniMaxChatTransientError() {
assertThat(result).isNotNull();
assertThat(result.getResult().getOutput().getText()).isSameAs("Response");
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
}
@@ -131,7 +131,7 @@ public void miniMaxChatStreamTransientError() {
assertThat(result).isNotNull();
assertThat(result.collectList().block().get(0).getResult().getOutput().getText()).isSameAs("Response");
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
}
@@ -158,7 +158,7 @@ public void miniMaxEmbeddingTransientError() {
assertThat(result).isNotNull();
assertThat(result.getResult().getOutput()).isEqualTo(new float[] { 9.9f, 8.8f });
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
}
@@ -171,21 +171,22 @@ public void miniMaxEmbeddingNonTransientError() {
.call(new org.springframework.ai.embedding.EmbeddingRequest(List.of("text1", "text2"), options)));
}
- private class TestRetryListener implements RetryListener {
+ private static class TestRetryListener implements RetryListener {
int onErrorRetryCount = 0;
int onSuccessRetryCount = 0;
@Override
- public void onSuccess(RetryContext context, RetryCallback callback, T result) {
- this.onSuccessRetryCount = context.getRetryCount();
+ public void beforeRetry(final RetryPolicy retryPolicy, final Retryable> retryable) {
+ // Count each retry attempt
+ this.onErrorRetryCount++;
}
@Override
- public void onError(RetryContext context, RetryCallback callback,
- Throwable throwable) {
- this.onErrorRetryCount = context.getRetryCount();
+ public void onRetrySuccess(final RetryPolicy retryPolicy, final Retryable> retryable, final Object result) {
+ // Count successful retries - we increment when we succeed after a failure
+ this.onSuccessRetryCount++;
}
}
diff --git a/models/spring-ai-minimax/src/test/java/org/springframework/ai/minimax/embedding/MiniMaxEmbeddingModelObservationIT.java b/models/spring-ai-minimax/src/test/java/org/springframework/ai/minimax/embedding/MiniMaxEmbeddingModelObservationIT.java
index 0b5df195dd3..b502515ae15 100644
--- a/models/spring-ai-minimax/src/test/java/org/springframework/ai/minimax/embedding/MiniMaxEmbeddingModelObservationIT.java
+++ b/models/spring-ai-minimax/src/test/java/org/springframework/ai/minimax/embedding/MiniMaxEmbeddingModelObservationIT.java
@@ -37,7 +37,7 @@
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 org.springframework.core.retry.RetryTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.ai.embedding.observation.EmbeddingModelObservationDocumentation.HighCardinalityKeyNames;
@@ -106,7 +106,7 @@ public MiniMaxApi minimaxApi() {
public MiniMaxEmbeddingModel minimaxEmbeddingModel(MiniMaxApi minimaxApi,
TestObservationRegistry observationRegistry) {
return new MiniMaxEmbeddingModel(minimaxApi, MetadataMode.EMBED, MiniMaxEmbeddingOptions.builder().build(),
- RetryTemplate.defaultInstance(), observationRegistry);
+ new RetryTemplate(), observationRegistry);
}
}
diff --git a/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatModel.java b/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatModel.java
index f7314603ec3..035630c6982 100644
--- a/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatModel.java
+++ b/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatModel.java
@@ -28,6 +28,8 @@
import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.core.retry.RetryException;
+
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
@@ -70,7 +72,7 @@
import org.springframework.ai.support.UsageCalculator;
import org.springframework.ai.tool.definition.ToolDefinition;
import org.springframework.http.ResponseEntity;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MimeType;
@@ -199,8 +201,19 @@ public ChatResponse internalCall(Prompt prompt, ChatResponse previousChatRespons
this.observationRegistry)
.observe(() -> {
- ResponseEntity completionEntity = this.retryTemplate
- .execute(ctx -> this.mistralAiApi.chatCompletionEntity(request));
+ ResponseEntity completionEntity = null;
+ try {
+ completionEntity = this.retryTemplate
+ .execute(() -> this.mistralAiApi.chatCompletionEntity(request));
+ }
+ catch (RetryException e) {
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
+ }
+ else {
+ throw new RuntimeException(e.getCause());
+ }
+ }
ChatCompletion chatCompletion = completionEntity.getBody();
@@ -272,8 +285,18 @@ public Flux internalStream(Prompt prompt, ChatResponse previousCha
observation.parentObservation(contextView.getOrDefault(ObservationThreadLocalAccessor.KEY, null)).start();
- Flux completionChunks = this.retryTemplate
- .execute(ctx -> this.mistralAiApi.chatCompletionStream(request));
+ Flux completionChunks = null;
+ try {
+ completionChunks = this.retryTemplate.execute(() -> this.mistralAiApi.chatCompletionStream(request));
+ }
+ catch (RetryException e) {
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
+ }
+ else {
+ throw new RuntimeException(e.getCause());
+ }
+ }
// For chunked responses, only the first chunk contains the choice role.
// The rest of the chunks with same ID share the same role.
diff --git a/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiEmbeddingModel.java b/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiEmbeddingModel.java
index b60c95d1bae..b5c93a8c951 100644
--- a/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiEmbeddingModel.java
+++ b/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiEmbeddingModel.java
@@ -21,6 +21,7 @@
import io.micrometer.observation.ObservationRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.core.retry.RetryException;
import org.springframework.ai.chat.metadata.DefaultUsage;
import org.springframework.ai.document.Document;
@@ -38,7 +39,7 @@
import org.springframework.ai.mistralai.api.MistralAiApi;
import org.springframework.ai.model.ModelOptionsUtils;
import org.springframework.ai.retry.RetryUtils;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
/**
@@ -129,8 +130,19 @@ public EmbeddingResponse call(EmbeddingRequest request) {
.observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,
this.observationRegistry)
.observe(() -> {
- var apiEmbeddingResponse = this.retryTemplate
- .execute(ctx -> this.mistralAiApi.embeddings(apiRequest).getBody());
+ MistralAiApi.EmbeddingList apiEmbeddingResponse = null;
+ try {
+ apiEmbeddingResponse = this.retryTemplate
+ .execute(() -> this.mistralAiApi.embeddings(apiRequest).getBody());
+ }
+ catch (RetryException e) {
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
+ }
+ else {
+ throw new RuntimeException(e.getCause());
+ }
+ }
if (apiEmbeddingResponse == null) {
logger.warn("No embeddings returned for request: {}", request);
diff --git a/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/moderation/MistralAiModerationModel.java b/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/moderation/MistralAiModerationModel.java
index 0717520d766..e0aaab16099 100644
--- a/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/moderation/MistralAiModerationModel.java
+++ b/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/moderation/MistralAiModerationModel.java
@@ -21,6 +21,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.core.retry.RetryException;
import org.springframework.ai.mistralai.api.MistralAiModerationApi;
import org.springframework.ai.model.ModelOptionsUtils;
@@ -35,7 +36,7 @@
import org.springframework.ai.moderation.ModerationResult;
import org.springframework.ai.retry.RetryUtils;
import org.springframework.http.ResponseEntity;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import static org.springframework.ai.mistralai.api.MistralAiModerationApi.MistralAiModerationRequest;
@@ -81,27 +82,39 @@ public MistralAiModerationModel(MistralAiModerationApi mistralAiModerationApi, R
@Override
public ModerationResponse call(ModerationPrompt moderationPrompt) {
- return this.retryTemplate.execute(ctx -> {
+ try {
+ return this.retryTemplate.execute(() -> {
- var instructions = moderationPrompt.getInstructions().getText();
+ var instructions = moderationPrompt.getInstructions().getText();
- var moderationRequest = new MistralAiModerationRequest(instructions);
+ var moderationRequest = new MistralAiModerationRequest(instructions);
- if (this.defaultOptions != null) {
- moderationRequest = ModelOptionsUtils.merge(this.defaultOptions, moderationRequest,
- MistralAiModerationRequest.class);
+ if (this.defaultOptions != null) {
+ moderationRequest = ModelOptionsUtils.merge(this.defaultOptions, moderationRequest,
+ MistralAiModerationRequest.class);
+ }
+ else {
+ // moderationPrompt.getOptions() never null but model can be empty,
+ // cause
+ // by ModerationPrompt constructor
+ moderationRequest = ModelOptionsUtils.merge(
+ toMistralAiModerationOptions(moderationPrompt.getOptions()), moderationRequest,
+ MistralAiModerationRequest.class);
+ }
+
+ var moderationResponseEntity = this.mistralAiModerationApi.moderate(moderationRequest);
+
+ return convertResponse(moderationResponseEntity, moderationRequest);
+ });
+ }
+ catch (RetryException e) {
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
}
else {
- // moderationPrompt.getOptions() never null but model can be empty, cause
- // by ModerationPrompt constructor
- moderationRequest = ModelOptionsUtils.merge(toMistralAiModerationOptions(moderationPrompt.getOptions()),
- moderationRequest, MistralAiModerationRequest.class);
+ throw new RuntimeException(e.getCause());
}
-
- var moderationResponseEntity = this.mistralAiModerationApi.moderate(moderationRequest);
-
- return convertResponse(moderationResponseEntity, moderationRequest);
- });
+ }
}
private ModerationResponse convertResponse(ResponseEntity moderationResponseEntity,
diff --git a/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatModelObservationIT.java b/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatModelObservationIT.java
index 5d8a30d309c..03ac09d993c 100644
--- a/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatModelObservationIT.java
+++ b/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatModelObservationIT.java
@@ -38,7 +38,7 @@
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 org.springframework.core.retry.RetryTemplate;
import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat;
@@ -190,7 +190,7 @@ public MistralAiChatModel mistralAiChatModel(MistralAiApi mistralAiApi,
return MistralAiChatModel.builder()
.mistralAiApi(mistralAiApi)
.defaultOptions(MistralAiChatOptions.builder().build())
- .retryTemplate(RetryTemplate.defaultInstance())
+ .retryTemplate(new RetryTemplate())
.observationRegistry(observationRegistry)
.build();
}
diff --git a/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiEmbeddingModelObservationIT.java b/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiEmbeddingModelObservationIT.java
index 8341c0fa22e..f3287c9fc3e 100644
--- a/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiEmbeddingModelObservationIT.java
+++ b/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiEmbeddingModelObservationIT.java
@@ -34,7 +34,7 @@
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 org.springframework.core.retry.RetryTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.ai.embedding.observation.EmbeddingModelObservationDocumentation.HighCardinalityKeyNames;
@@ -110,7 +110,7 @@ public MistralAiEmbeddingModel mistralAiEmbeddingModel(MistralAiApi mistralAiApi
return MistralAiEmbeddingModel.builder()
.mistralAiApi(mistralAiApi)
.options(MistralAiEmbeddingOptions.builder().build())
- .retryTemplate(RetryTemplate.defaultInstance())
+ .retryTemplate(new RetryTemplate())
.observationRegistry(observationRegistry)
.build();
}
diff --git a/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiRetryTests.java b/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiRetryTests.java
index 9709a3783ca..d608e1bab1a 100644
--- a/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiRetryTests.java
+++ b/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiRetryTests.java
@@ -41,10 +41,10 @@
import org.springframework.ai.retry.RetryUtils;
import org.springframework.ai.retry.TransientAiException;
import org.springframework.http.ResponseEntity;
-import org.springframework.retry.RetryCallback;
-import org.springframework.retry.RetryContext;
-import org.springframework.retry.RetryListener;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryListener;
+import org.springframework.core.retry.RetryPolicy;
+import org.springframework.core.retry.Retryable;
+import org.springframework.core.retry.RetryTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -75,7 +75,7 @@ public class MistralAiRetryTests {
public void beforeEach() {
this.retryTemplate = RetryUtils.SHORT_RETRY_TEMPLATE;
this.retryListener = new TestRetryListener();
- this.retryTemplate.registerListener(this.retryListener);
+ this.retryTemplate.setRetryListener(this.retryListener);
this.chatModel = MistralAiChatModel.builder()
.mistralAiApi(this.mistralAiApi)
@@ -110,7 +110,7 @@ public void mistralAiChatTransientError() {
assertThat(result).isNotNull();
assertThat(result.getResult().getOutput().getText()).isSameAs("Response");
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
}
@@ -139,7 +139,7 @@ public void mistralAiChatStreamTransientError() {
assertThat(result).isNotNull();
assertThat(result.collectList().block().get(0).getResult().getOutput().getText()).isSameAs("Response");
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
}
@@ -167,7 +167,7 @@ public void mistralAiEmbeddingTransientError() {
assertThat(result).isNotNull();
assertThat(result.getResult().getOutput()).isEqualTo(new float[] { 9.9f, 8.8f });
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
}
@@ -189,7 +189,7 @@ public void mistralAiChatMixedTransientAndNonTransientErrors() {
assertThrows(RuntimeException.class, () -> this.chatModel.call(new Prompt("text")));
// Should have 1 retry attempt before hitting non-transient error
- assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onErrorRetryCount).isEqualTo(1);
}
private static class TestRetryListener implements RetryListener {
@@ -199,14 +199,15 @@ private static class TestRetryListener implements RetryListener {
int onSuccessRetryCount = 0;
@Override
- public void onSuccess(RetryContext context, RetryCallback callback, T result) {
- this.onSuccessRetryCount = context.getRetryCount();
+ public void beforeRetry(final RetryPolicy retryPolicy, final Retryable> retryable) {
+ // Count each retry attempt
+ this.onErrorRetryCount++;
}
@Override
- public void onError(RetryContext context, RetryCallback callback,
- Throwable throwable) {
- this.onErrorRetryCount = context.getRetryCount();
+ public void onRetrySuccess(final RetryPolicy retryPolicy, final Retryable> retryable, final Object result) {
+ // Count successful retries - we increment when we succeed after a failure
+ this.onSuccessRetryCount++;
}
}
diff --git a/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaChatModel.java b/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaChatModel.java
index 7cb87eb8f3b..b17837e07c4 100644
--- a/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaChatModel.java
+++ b/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaChatModel.java
@@ -28,6 +28,7 @@
import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.core.retry.RetryException;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;
@@ -70,7 +71,7 @@
import org.springframework.ai.retry.RetryUtils;
import org.springframework.ai.tool.definition.ToolDefinition;
import org.springframework.ai.util.json.JsonParser;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
@@ -246,7 +247,18 @@ private ChatResponse internalCall(Prompt prompt, ChatResponse previousChatRespon
this.observationRegistry)
.observe(() -> {
- OllamaApi.ChatResponse ollamaResponse = this.retryTemplate.execute(ctx -> this.chatApi.chat(request));
+ OllamaApi.ChatResponse ollamaResponse = null;
+ try {
+ ollamaResponse = this.retryTemplate.execute(() -> this.chatApi.chat(request));
+ }
+ catch (RetryException e) {
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
+ }
+ else {
+ throw new RuntimeException(e.getCause());
+ }
+ }
List toolCalls = ollamaResponse.message().toolCalls() == null ? List.of()
: ollamaResponse.message()
diff --git a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelMultimodalIT.java b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelMultimodalIT.java
index 693f892e940..232269fa97f 100644
--- a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelMultimodalIT.java
+++ b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelMultimodalIT.java
@@ -35,10 +35,10 @@
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
-import org.springframework.retry.RetryCallback;
-import org.springframework.retry.RetryContext;
-import org.springframework.retry.RetryListener;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryListener;
+import org.springframework.core.retry.RetryPolicy;
+import org.springframework.core.retry.RetryTemplate;
+import org.springframework.core.retry.Retryable;
import org.springframework.util.MimeTypeUtils;
import static org.assertj.core.api.Assertions.assertThat;
@@ -93,19 +93,21 @@ public OllamaApi ollamaApi() {
@Bean
public OllamaChatModel ollamaChat(OllamaApi ollamaApi) {
- RetryTemplate retryTemplate = RetryTemplate.builder()
+ RetryPolicy retryPolicy = RetryPolicy.builder()
.maxAttempts(1)
- .retryOn(TransientAiException.class)
- .fixedBackoff(Duration.ofSeconds(1))
- .withListener(new RetryListener() {
-
- @Override
- public void onError(RetryContext context,
- RetryCallback callback, Throwable throwable) {
- logger.warn("Retry error. Retry count:" + context.getRetryCount(), throwable);
- }
- })
+ .includes(TransientAiException.class)
+ .delay(Duration.ofSeconds(1))
.build();
+
+ RetryTemplate retryTemplate = new RetryTemplate(retryPolicy);
+ retryTemplate.setRetryListener(new RetryListener() {
+
+ @Override
+ public void onRetryFailure(final RetryPolicy policy, final Retryable> retryable,
+ final Throwable throwable) {
+ logger.warn("Retry error. Retry count:" + (throwable.getSuppressed().length + 1), throwable);
+ }
+ });
return OllamaChatModel.builder()
.ollamaApi(ollamaApi)
.defaultOptions(OllamaChatOptions.builder().model(MODEL).temperature(0.9).build())
diff --git a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaRetryTests.java b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaRetryTests.java
index 323c969c6fa..a744aead9d1 100644
--- a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaRetryTests.java
+++ b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaRetryTests.java
@@ -35,10 +35,10 @@
import org.springframework.ai.retry.NonTransientAiException;
import org.springframework.ai.retry.RetryUtils;
import org.springframework.ai.retry.TransientAiException;
-import org.springframework.retry.RetryCallback;
-import org.springframework.retry.RetryContext;
-import org.springframework.retry.RetryListener;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryListener;
+import org.springframework.core.retry.RetryPolicy;
+import org.springframework.core.retry.RetryTemplate;
+import org.springframework.core.retry.Retryable;
import org.springframework.web.client.ResourceAccessException;
import static org.assertj.core.api.Assertions.assertThat;
@@ -71,7 +71,7 @@ class OllamaRetryTests {
public void beforeEach() {
this.retryTemplate = RetryUtils.SHORT_RETRY_TEMPLATE;
this.retryListener = new TestRetryListener();
- this.retryTemplate.registerListener(this.retryListener);
+ this.retryTemplate.setRetryListener(this.retryListener);
this.chatModel = OllamaChatModel.builder()
.ollamaApi(this.ollamaApi)
@@ -96,7 +96,7 @@ void ollamaChatTransientError() {
assertThat(result).isNotNull();
assertThat(result.getResult().getOutput().getText()).isSameAs("Response");
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
}
@@ -130,7 +130,7 @@ void ollamaChatNonTransientErrorShouldNotRetry() {
.hasMessage("Model not found");
assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(0);
- assertThat(this.retryListener.onErrorRetryCount).isEqualTo(1);
+ assertThat(this.retryListener.onErrorRetryCount).isEqualTo(0);
verify(this.ollamaApi, times(1)).chat(isA(OllamaApi.ChatRequest.class));
}
@@ -202,14 +202,15 @@ private static class TestRetryListener implements RetryListener {
int onSuccessRetryCount = 0;
@Override
- public void onSuccess(RetryContext context, RetryCallback callback, T result) {
- this.onSuccessRetryCount = context.getRetryCount();
+ public void beforeRetry(final RetryPolicy retryPolicy, final Retryable> retryable) {
+ // Count each retry attempt
+ this.onErrorRetryCount++;
}
@Override
- public void onError(RetryContext context, RetryCallback callback,
- Throwable throwable) {
- this.onErrorRetryCount = context.getRetryCount();
+ public void onRetrySuccess(final RetryPolicy retryPolicy, final Retryable> retryable, final Object result) {
+ // Count successful retries - we increment when we succeed after a failure
+ this.onSuccessRetryCount++;
}
}
diff --git a/models/spring-ai-openai/pom.xml b/models/spring-ai-openai/pom.xml
index 3f9adec528e..07558446367 100644
--- a/models/spring-ai-openai/pom.xml
+++ b/models/spring-ai-openai/pom.xml
@@ -87,6 +87,28 @@
test
+
+ org.springframework.boot
+ spring-boot-starter-jackson
+ test
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-restclient
+ ${spring-boot.version}
+ test
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webclient
+ ${spring-boot.version}
+ test
+
+
io.micrometer
micrometer-observation-test
diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiAudioSpeechModel.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiAudioSpeechModel.java
index f9fcd006cb6..a106ce5383b 100644
--- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiAudioSpeechModel.java
+++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiAudioSpeechModel.java
@@ -32,7 +32,7 @@
import org.springframework.ai.openai.metadata.support.OpenAiResponseHeaderExtractor;
import org.springframework.ai.retry.RetryUtils;
import org.springframework.http.ResponseEntity;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -127,8 +127,13 @@ public SpeechResponse call(SpeechPrompt speechPrompt) {
OpenAiAudioApi.SpeechRequest speechRequest = createRequest(speechPrompt);
- ResponseEntity speechEntity = this.retryTemplate
- .execute(ctx -> this.audioApi.createSpeech(speechRequest));
+ ResponseEntity speechEntity;
+ try {
+ speechEntity = this.retryTemplate.execute(() -> this.audioApi.createSpeech(speechRequest));
+ }
+ catch (Exception e) {
+ throw new RuntimeException("Error calling OpenAI audio speech API", e);
+ }
var speech = speechEntity.getBody();
@@ -154,8 +159,13 @@ public Flux stream(SpeechPrompt speechPrompt) {
OpenAiAudioApi.SpeechRequest speechRequest = createRequest(speechPrompt);
- Flux> speechEntity = this.retryTemplate
- .execute(ctx -> this.audioApi.stream(speechRequest));
+ Flux> speechEntity;
+ try {
+ speechEntity = this.retryTemplate.execute(() -> this.audioApi.stream(speechRequest));
+ }
+ catch (Exception e) {
+ throw new RuntimeException("Error calling OpenAI audio speech streaming API", e);
+ }
return speechEntity.map(entity -> new SpeechResponse(new Speech(entity.getBody()),
new OpenAiAudioSpeechResponseMetadata(OpenAiResponseHeaderExtractor.extractAiResponseHeaders(entity))));
diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiAudioTranscriptionModel.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiAudioTranscriptionModel.java
index 8fbd75d4d39..dd2395f4ecd 100644
--- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiAudioTranscriptionModel.java
+++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiAudioTranscriptionModel.java
@@ -31,7 +31,7 @@
import org.springframework.ai.retry.RetryUtils;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
/**
@@ -112,8 +112,14 @@ public AudioTranscriptionResponse call(AudioTranscriptionPrompt transcriptionPro
if (request.responseFormat().isJsonType()) {
- ResponseEntity transcriptionEntity = this.retryTemplate
- .execute(ctx -> this.audioApi.createTranscription(request, StructuredResponse.class));
+ ResponseEntity transcriptionEntity;
+ try {
+ transcriptionEntity = this.retryTemplate
+ .execute(() -> this.audioApi.createTranscription(request, StructuredResponse.class));
+ }
+ catch (Exception e) {
+ throw new RuntimeException("Error calling OpenAI transcription API", e);
+ }
var transcription = transcriptionEntity.getBody();
@@ -133,8 +139,14 @@ public AudioTranscriptionResponse call(AudioTranscriptionPrompt transcriptionPro
}
else {
- ResponseEntity transcriptionEntity = this.retryTemplate
- .execute(ctx -> this.audioApi.createTranscription(request, String.class));
+ ResponseEntity transcriptionEntity;
+ try {
+ transcriptionEntity = this.retryTemplate
+ .execute(() -> this.audioApi.createTranscription(request, String.class));
+ }
+ catch (Exception e) {
+ throw new RuntimeException("Error calling OpenAI transcription API", e);
+ }
var transcription = transcriptionEntity.getBody();
diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatModel.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatModel.java
index 246b7893c4a..b3dc2497954 100644
--- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatModel.java
+++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatModel.java
@@ -22,7 +22,6 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
@@ -78,13 +77,13 @@
import org.springframework.ai.tool.definition.ToolDefinition;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
+import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
-import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/**
@@ -196,8 +195,14 @@ public ChatResponse internalCall(Prompt prompt, ChatResponse previousChatRespons
this.observationRegistry)
.observe(() -> {
- ResponseEntity completionEntity = this.retryTemplate
- .execute(ctx -> this.openAiApi.chatCompletionEntity(request, getAdditionalHttpHeaders(prompt)));
+ ResponseEntity completionEntity;
+ try {
+ completionEntity = this.retryTemplate
+ .execute(() -> this.openAiApi.chatCompletionEntity(request, getAdditionalHttpHeaders(prompt)));
+ }
+ catch (Exception e) {
+ throw new RuntimeException("Error calling OpenAI chat completion API", e);
+ }
var chatCompletion = completionEntity.getBody();
@@ -402,14 +407,15 @@ public Flux internalStream(Prompt prompt, ChatResponse previousCha
});
}
- private MultiValueMap getAdditionalHttpHeaders(Prompt prompt) {
+ private HttpHeaders getAdditionalHttpHeaders(Prompt prompt) {
Map headers = new HashMap<>(this.defaultOptions.getHttpHeaders());
if (prompt.getOptions() != null && prompt.getOptions() instanceof OpenAiChatOptions chatOptions) {
headers.putAll(chatOptions.getHttpHeaders());
}
- return CollectionUtils.toMultiValueMap(
- headers.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> List.of(e.getValue()))));
+ HttpHeaders httpHeaders = new HttpHeaders();
+ headers.forEach(httpHeaders::add);
+ return httpHeaders;
}
private Generation buildGeneration(Choice choice, Map metadata, ChatCompletionRequest request) {
diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiEmbeddingModel.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiEmbeddingModel.java
index 47c06ac5a72..d5c3cb347d4 100644
--- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiEmbeddingModel.java
+++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiEmbeddingModel.java
@@ -42,7 +42,7 @@
import org.springframework.ai.openai.api.OpenAiApi.EmbeddingList;
import org.springframework.ai.openai.api.common.OpenAiApiConstants;
import org.springframework.ai.retry.RetryUtils;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
/**
@@ -164,8 +164,14 @@ public EmbeddingResponse call(EmbeddingRequest request) {
.observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,
this.observationRegistry)
.observe(() -> {
- EmbeddingList apiEmbeddingResponse = this.retryTemplate
- .execute(ctx -> this.openAiApi.embeddings(apiRequest).getBody());
+ EmbeddingList apiEmbeddingResponse;
+ try {
+ apiEmbeddingResponse = this.retryTemplate
+ .execute(() -> this.openAiApi.embeddings(apiRequest).getBody());
+ }
+ catch (Exception e) {
+ throw new RuntimeException("Error calling OpenAI embedding API", e);
+ }
if (apiEmbeddingResponse == null) {
logger.warn("No embeddings returned for request: {}", request);
diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiImageModel.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiImageModel.java
index 68354662548..c6d1a0cffe4 100644
--- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiImageModel.java
+++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiImageModel.java
@@ -39,7 +39,7 @@
import org.springframework.ai.openai.metadata.OpenAiImageGenerationMetadata;
import org.springframework.ai.retry.RetryUtils;
import org.springframework.http.ResponseEntity;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
/**
@@ -141,8 +141,14 @@ public ImageResponse call(ImagePrompt imagePrompt) {
.observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,
this.observationRegistry)
.observe(() -> {
- ResponseEntity imageResponseEntity = this.retryTemplate
- .execute(ctx -> this.openAiImageApi.createImage(imageRequest));
+ ResponseEntity imageResponseEntity;
+ try {
+ imageResponseEntity = this.retryTemplate
+ .execute(() -> this.openAiImageApi.createImage(imageRequest));
+ }
+ catch (Exception e) {
+ throw new RuntimeException("Error calling OpenAI image API", e);
+ }
ImageResponse imageResponse = convertResponse(imageResponseEntity, imageRequest);
diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiModerationModel.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiModerationModel.java
index 8e00c24b430..02e6f833bbf 100644
--- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiModerationModel.java
+++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiModerationModel.java
@@ -35,7 +35,7 @@
import org.springframework.ai.openai.api.OpenAiModerationApi;
import org.springframework.ai.retry.RetryUtils;
import org.springframework.http.ResponseEntity;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
/**
@@ -77,28 +77,34 @@ public OpenAiModerationModel withDefaultOptions(OpenAiModerationOptions defaultO
@Override
public ModerationResponse call(ModerationPrompt moderationPrompt) {
- return this.retryTemplate.execute(ctx -> {
+ try {
+ return this.retryTemplate.execute(() -> {
- String instructions = moderationPrompt.getInstructions().getText();
+ String instructions = moderationPrompt.getInstructions().getText();
- OpenAiModerationApi.OpenAiModerationRequest moderationRequest = new OpenAiModerationApi.OpenAiModerationRequest(
- instructions);
+ OpenAiModerationApi.OpenAiModerationRequest moderationRequest = new OpenAiModerationApi.OpenAiModerationRequest(
+ instructions);
- if (this.defaultOptions != null) {
- moderationRequest = ModelOptionsUtils.merge(this.defaultOptions, moderationRequest,
- OpenAiModerationApi.OpenAiModerationRequest.class);
- }
+ if (this.defaultOptions != null) {
+ moderationRequest = ModelOptionsUtils.merge(this.defaultOptions, moderationRequest,
+ OpenAiModerationApi.OpenAiModerationRequest.class);
+ }
- if (moderationPrompt.getOptions() != null) {
- moderationRequest = ModelOptionsUtils.merge(toOpenAiModerationOptions(moderationPrompt.getOptions()),
- moderationRequest, OpenAiModerationApi.OpenAiModerationRequest.class);
- }
+ if (moderationPrompt.getOptions() != null) {
+ moderationRequest = ModelOptionsUtils.merge(
+ toOpenAiModerationOptions(moderationPrompt.getOptions()), moderationRequest,
+ OpenAiModerationApi.OpenAiModerationRequest.class);
+ }
- ResponseEntity moderationResponseEntity = this.openAiModerationApi
- .createModeration(moderationRequest);
+ ResponseEntity moderationResponseEntity = this.openAiModerationApi
+ .createModeration(moderationRequest);
- return convertResponse(moderationResponseEntity, moderationRequest);
- });
+ return convertResponse(moderationResponseEntity, moderationRequest);
+ });
+ }
+ catch (Exception e) {
+ throw new RuntimeException("Error calling OpenAI moderation API", e);
+ }
}
private ModerationResponse convertResponse(
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 49d42b1e1aa..ba7ae46ac18 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
@@ -48,8 +48,6 @@
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
@@ -98,7 +96,7 @@ public static Builder builder() {
private final ApiKey apiKey;
- private final MultiValueMap headers;
+ private final HttpHeaders headers;
private final String completionsPath;
@@ -123,8 +121,8 @@ public static Builder builder() {
* @param webClientBuilder WebClient builder.
* @param responseErrorHandler Response error handler.
*/
- public OpenAiApi(String baseUrl, ApiKey apiKey, MultiValueMap headers, String completionsPath,
- String embeddingsPath, RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder,
+ public OpenAiApi(String baseUrl, ApiKey apiKey, HttpHeaders headers, String completionsPath, String embeddingsPath,
+ RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder,
ResponseErrorHandler responseErrorHandler) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
@@ -177,7 +175,7 @@ public static String getTextContent(List con
* and headers.
*/
public ResponseEntity chatCompletionEntity(ChatCompletionRequest chatRequest) {
- return chatCompletionEntity(chatRequest, new LinkedMultiValueMap<>());
+ return chatCompletionEntity(chatRequest, new HttpHeaders());
}
/**
@@ -189,7 +187,7 @@ public ResponseEntity chatCompletionEntity(ChatCompletionRequest
* and headers.
*/
public ResponseEntity chatCompletionEntity(ChatCompletionRequest chatRequest,
- MultiValueMap additionalHttpHeader) {
+ HttpHeaders additionalHttpHeader) {
Assert.notNull(chatRequest, REQUEST_BODY_NULL_MESSAGE);
Assert.isTrue(!chatRequest.stream(), STREAM_FALSE_MESSAGE);
@@ -215,7 +213,7 @@ public ResponseEntity chatCompletionEntity(ChatCompletionRequest
* @return Returns a {@link Flux} stream from chat completion chunks.
*/
public Flux chatCompletionStream(ChatCompletionRequest chatRequest) {
- return chatCompletionStream(chatRequest, new LinkedMultiValueMap<>());
+ return chatCompletionStream(chatRequest, new HttpHeaders());
}
/**
@@ -227,7 +225,7 @@ public Flux chatCompletionStream(ChatCompletionRequest chat
* @return Returns a {@link Flux} stream from chat completion chunks.
*/
public Flux chatCompletionStream(ChatCompletionRequest chatRequest,
- MultiValueMap additionalHttpHeader) {
+ HttpHeaders additionalHttpHeader) {
Assert.notNull(chatRequest, REQUEST_BODY_NULL_MESSAGE);
Assert.isTrue(chatRequest.stream(), "Request must set the stream property to true.");
@@ -323,7 +321,7 @@ public ResponseEntity> embeddings(EmbeddingRequest<
}
private void addDefaultHeadersIfMissing(HttpHeaders headers) {
- if (!headers.containsKey(HttpHeaders.AUTHORIZATION) && !(this.apiKey instanceof NoopApiKey)) {
+ if (headers.get(HttpHeaders.AUTHORIZATION) == null && !(this.apiKey instanceof NoopApiKey)) {
headers.setBearerAuth(this.apiKey.getValue());
}
}
@@ -337,7 +335,7 @@ ApiKey getApiKey() {
return this.apiKey;
}
- MultiValueMap getHeaders() {
+ HttpHeaders getHeaders() {
return this.headers;
}
@@ -1995,7 +1993,8 @@ public Builder() {
public Builder(OpenAiApi api) {
this.baseUrl = api.getBaseUrl();
this.apiKey = api.getApiKey();
- this.headers = new LinkedMultiValueMap<>(api.getHeaders());
+ this.headers = new HttpHeaders();
+ this.headers.addAll(api.getHeaders());
this.completionsPath = api.getCompletionsPath();
this.embeddingsPath = api.getEmbeddingsPath();
this.restClientBuilder = api.restClient != null ? api.restClient.mutate() : RestClient.builder();
@@ -2007,7 +2006,7 @@ public Builder(OpenAiApi api) {
private ApiKey apiKey;
- private MultiValueMap headers = new LinkedMultiValueMap<>();
+ private HttpHeaders headers = new HttpHeaders();
private String completionsPath = "/v1/chat/completions";
@@ -2036,7 +2035,7 @@ public Builder apiKey(String simpleApiKey) {
return this;
}
- public Builder headers(MultiValueMap headers) {
+ public Builder headers(HttpHeaders headers) {
Assert.notNull(headers, "headers cannot be null");
this.headers = headers;
return this;
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 cd5b3e89d8f..49026e96f2c 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
@@ -67,9 +67,8 @@ public class OpenAiAudioApi {
* @param webClientBuilder WebClient builder.
* @param responseErrorHandler Response error handler.
*/
- public OpenAiAudioApi(String baseUrl, ApiKey apiKey, MultiValueMap headers,
- RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder,
- ResponseErrorHandler responseErrorHandler) {
+ public OpenAiAudioApi(String baseUrl, ApiKey apiKey, HttpHeaders headers, RestClient.Builder restClientBuilder,
+ WebClient.Builder webClientBuilder, ResponseErrorHandler responseErrorHandler) {
Consumer authHeaders = h -> h.addAll(headers);
@@ -796,7 +795,7 @@ public static final class Builder {
private ApiKey apiKey;
- private MultiValueMap headers = new LinkedMultiValueMap<>();
+ private HttpHeaders headers = new HttpHeaders();
private RestClient.Builder restClientBuilder = RestClient.builder();
@@ -822,7 +821,7 @@ public Builder apiKey(String simpleApiKey) {
return this;
}
- public Builder headers(MultiValueMap headers) {
+ public Builder headers(HttpHeaders headers) {
Assert.notNull(headers, "headers cannot be null");
this.headers = headers;
return this;
diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiFileApi.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiFileApi.java
index 535b6a9a043..29ec58de969 100644
--- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiFileApi.java
+++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiFileApi.java
@@ -49,8 +49,8 @@ public class OpenAiFileApi {
private final RestClient restClient;
- public OpenAiFileApi(String baseUrl, ApiKey apiKey, MultiValueMap headers,
- RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) {
+ public OpenAiFileApi(String baseUrl, ApiKey apiKey, HttpHeaders headers, RestClient.Builder restClientBuilder,
+ ResponseErrorHandler responseErrorHandler) {
Consumer authHeaders = h -> h.addAll(headers);
this.restClient = restClientBuilder.clone()
@@ -360,7 +360,7 @@ public static final class Builder {
private ApiKey apiKey;
- private MultiValueMap headers = new LinkedMultiValueMap<>();
+ private HttpHeaders headers = new HttpHeaders();
private RestClient.Builder restClientBuilder = RestClient.builder();
@@ -384,7 +384,7 @@ public Builder apiKey(String simpleApiKey) {
return this;
}
- public Builder headers(MultiValueMap headers) {
+ public Builder headers(HttpHeaders headers) {
Assert.notNull(headers, "headers cannot be null");
this.headers = headers;
return this;
diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiImageApi.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiImageApi.java
index fe82e30e56b..bb20891ef80 100644
--- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiImageApi.java
+++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiImageApi.java
@@ -31,8 +31,6 @@
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
@@ -60,7 +58,7 @@ public class OpenAiImageApi {
* @param restClientBuilder the rest client builder to use.
* @param responseErrorHandler the response error handler to use.
*/
- public OpenAiImageApi(String baseUrl, ApiKey apiKey, MultiValueMap headers, String imagesPath,
+ public OpenAiImageApi(String baseUrl, ApiKey apiKey, HttpHeaders headers, String imagesPath,
RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) {
// @formatter:off
@@ -168,7 +166,7 @@ public static final class Builder {
private ApiKey apiKey;
- private MultiValueMap headers = new LinkedMultiValueMap<>();
+ private HttpHeaders headers = new HttpHeaders();
private RestClient.Builder restClientBuilder = RestClient.builder();
@@ -200,7 +198,7 @@ public Builder apiKey(String simpleApiKey) {
return this;
}
- public Builder headers(MultiValueMap headers) {
+ public Builder headers(HttpHeaders headers) {
Assert.notNull(headers, "headers cannot be null");
this.headers = headers;
return this;
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 fc05811c5bb..615392206e1 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
@@ -31,8 +31,6 @@
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
@@ -61,8 +59,8 @@ public class OpenAiModerationApi {
* @param apiKey OpenAI apiKey.
* @param restClientBuilder the rest client builder to use.
*/
- public OpenAiModerationApi(String baseUrl, ApiKey apiKey, MultiValueMap headers,
- RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) {
+ public OpenAiModerationApi(String baseUrl, ApiKey apiKey, HttpHeaders headers, RestClient.Builder restClientBuilder,
+ ResponseErrorHandler responseErrorHandler) {
this.objectMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
@@ -178,7 +176,7 @@ public static final class Builder {
private ApiKey apiKey;
- private MultiValueMap headers = new LinkedMultiValueMap<>();
+ private HttpHeaders headers = new HttpHeaders();
private RestClient.Builder restClientBuilder = RestClient.builder();
@@ -202,7 +200,7 @@ public Builder apiKey(String simpleApiKey) {
return this;
}
- public Builder headers(MultiValueMap headers) {
+ public Builder headers(HttpHeaders headers) {
Assert.notNull(headers, "headers cannot be null");
this.headers = headers;
return this;
diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/metadata/support/OpenAiResponseHeaderExtractor.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/metadata/support/OpenAiResponseHeaderExtractor.java
index 7a4d344755d..b7ad61960a3 100644
--- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/metadata/support/OpenAiResponseHeaderExtractor.java
+++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/metadata/support/OpenAiResponseHeaderExtractor.java
@@ -71,22 +71,18 @@ public static RateLimit extractAiResponseHeaders(ResponseEntity> response) {
private static Duration getHeaderAsDuration(ResponseEntity> response, String headerName) {
var headers = response.getHeaders();
- if (headers.containsKey(headerName)) {
- var values = headers.get(headerName);
- if (!CollectionUtils.isEmpty(values)) {
- return DurationFormatter.TIME_UNIT.parse(values.get(0));
- }
+ var values = headers.get(headerName);
+ if (!CollectionUtils.isEmpty(values)) {
+ return DurationFormatter.TIME_UNIT.parse(values.get(0));
}
return null;
}
private static Long getHeaderAsLong(ResponseEntity> response, String headerName) {
var headers = response.getHeaders();
- if (headers.containsKey(headerName)) {
- var values = headers.get(headerName);
- if (!CollectionUtils.isEmpty(values)) {
- return parseLong(headerName, values.get(0));
- }
+ var values = headers.get(headerName);
+ if (!CollectionUtils.isEmpty(values)) {
+ return parseLong(headerName, values.get(0));
}
return null;
}
diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiApiBuilderTests.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiApiBuilderTests.java
index e2858c9e46d..5697ff6f64e 100644
--- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiApiBuilderTests.java
+++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiApiBuilderTests.java
@@ -37,8 +37,6 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
@@ -66,7 +64,7 @@ void testMinimalBuilder() {
@Test
void testFullBuilder() {
- MultiValueMap headers = new LinkedMultiValueMap<>();
+ HttpHeaders headers = new HttpHeaders();
headers.add("Custom-Header", "test-value");
RestClient.Builder restClientBuilder = RestClient.builder();
WebClient.Builder webClientBuilder = WebClient.builder();
@@ -192,7 +190,7 @@ void testNullApiKeyValue() {
@Test
void testBuilderMethodChaining() {
- MultiValueMap headers = new LinkedMultiValueMap<>();
+ HttpHeaders headers = new HttpHeaders();
headers.add("Test-Header", "test-value");
OpenAiApi api = OpenAiApi.builder()
@@ -211,7 +209,7 @@ void testBuilderMethodChaining() {
@Test
void testCustomHeadersPreservation() {
- MultiValueMap customHeaders = new LinkedMultiValueMap<>();
+ HttpHeaders customHeaders = new HttpHeaders();
customHeaders.add("X-Custom-Header", "custom-value");
customHeaders.add("X-Organization", "org-123");
customHeaders.add("User-Agent", "Custom-Client/1.0");
@@ -223,7 +221,7 @@ void testCustomHeadersPreservation() {
@Test
void testComplexMultiValueHeaders() {
- MultiValueMap multiHeaders = new LinkedMultiValueMap<>();
+ HttpHeaders multiHeaders = new HttpHeaders();
multiHeaders.add("Accept", "application/json");
multiHeaders.add("Accept", "text/plain");
multiHeaders.add("Cache-Control", "no-cache");
@@ -381,7 +379,7 @@ void dynamicApiKeyRestClientWithAdditionalAuthorizationHeader() throws Interrupt
OpenAiApi.ChatCompletionRequest request = new OpenAiApi.ChatCompletionRequest(
List.of(chatCompletionMessage), "gpt-3.5-turbo", 0.8, false);
- MultiValueMap additionalHeaders = new LinkedMultiValueMap<>();
+ HttpHeaders additionalHeaders = new HttpHeaders();
additionalHeaders.add(HttpHeaders.AUTHORIZATION, "Bearer additional-key");
ResponseEntity response = api.chatCompletionEntity(request, additionalHeaders);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
@@ -478,7 +476,7 @@ void dynamicApiKeyWebClientWithAdditionalAuthorizationHeader() throws Interrupte
OpenAiApi.ChatCompletionMessage.Role.USER);
OpenAiApi.ChatCompletionRequest request = new OpenAiApi.ChatCompletionRequest(
List.of(chatCompletionMessage), "gpt-3.5-turbo", 0.8, true);
- MultiValueMap additionalHeaders = new LinkedMultiValueMap<>();
+ HttpHeaders additionalHeaders = new HttpHeaders();
additionalHeaders.add(HttpHeaders.AUTHORIZATION, "Bearer additional-key");
List response = api.chatCompletionStream(request, additionalHeaders)
.collectList()
diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiChatModelMutateTests.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiChatModelMutateTests.java
index 36ca255710e..cd549a9bebb 100644
--- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiChatModelMutateTests.java
+++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiChatModelMutateTests.java
@@ -20,7 +20,7 @@
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
-import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.http.HttpHeaders;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
@@ -88,12 +88,12 @@ void mutateDoesNotAffectOriginal() {
@Test
void mutateHeadersCreatesDistinctHeaders() {
- OpenAiApi mutatedApi = this.baseApi.mutate()
- .headers(new LinkedMultiValueMap<>(java.util.Map.of("X-Test", java.util.List.of("value"))))
- .build();
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("X-Test", "value");
+ OpenAiApi mutatedApi = this.baseApi.mutate().headers(headers).build();
- assertThat(mutatedApi.getHeaders()).containsKey("X-Test");
- assertThat(this.baseApi.getHeaders()).doesNotContainKey("X-Test");
+ assertThat(mutatedApi.getHeaders().get("X-Test")).isNotNull();
+ assertThat(this.baseApi.getHeaders().get("X-Test")).isNull();
}
@Test
@@ -129,7 +129,7 @@ void mutateAndCloneAreEquivalent() {
@Test
void testApiMutateWithComplexHeaders() {
- LinkedMultiValueMap complexHeaders = new LinkedMultiValueMap<>();
+ HttpHeaders complexHeaders = new HttpHeaders();
complexHeaders.add("Authorization", "Bearer custom-token");
complexHeaders.add("X-Custom-Header", "value1");
complexHeaders.add("X-Custom-Header", "value2");
@@ -137,9 +137,9 @@ void testApiMutateWithComplexHeaders() {
OpenAiApi mutatedApi = this.baseApi.mutate().headers(complexHeaders).build();
- assertThat(mutatedApi.getHeaders()).containsKey("Authorization");
- assertThat(mutatedApi.getHeaders()).containsKey("X-Custom-Header");
- assertThat(mutatedApi.getHeaders()).containsKey("User-Agent");
+ assertThat(mutatedApi.getHeaders().get("Authorization")).isNotNull();
+ assertThat(mutatedApi.getHeaders().get("X-Custom-Header")).isNotNull();
+ assertThat(mutatedApi.getHeaders().get("User-Agent")).isNotNull();
assertThat(mutatedApi.getHeaders().get("X-Custom-Header")).hasSize(2);
}
@@ -155,11 +155,11 @@ void testMutateWithEmptyOptions() {
@Test
void testApiMutateWithEmptyHeaders() {
- LinkedMultiValueMap emptyHeaders = new LinkedMultiValueMap<>();
+ HttpHeaders emptyHeaders = new HttpHeaders();
OpenAiApi mutatedApi = this.baseApi.mutate().headers(emptyHeaders).build();
- assertThat(mutatedApi.getHeaders()).isEmpty();
+ assertThat(mutatedApi.getHeaders().isEmpty()).isTrue();
}
@Test
diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiFileApiBuilderTests.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiFileApiBuilderTests.java
index 143fd9eaa68..0bcbf856db9 100644
--- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiFileApiBuilderTests.java
+++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiFileApiBuilderTests.java
@@ -36,8 +36,6 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
@@ -65,7 +63,7 @@ void testMinimalBuilder() {
@Test
void testFullBuilder() {
- MultiValueMap headers = new LinkedMultiValueMap<>();
+ HttpHeaders headers = new HttpHeaders();
headers.add("Custom-Header", "test-value");
RestClient.Builder restClientBuilder = RestClient.builder();
ResponseErrorHandler errorHandler = mock(ResponseErrorHandler.class);
diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/audio/api/OpenAiAudioApiBuilderTests.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/audio/api/OpenAiAudioApiBuilderTests.java
index ecd506277d3..2d567807abc 100644
--- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/audio/api/OpenAiAudioApiBuilderTests.java
+++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/audio/api/OpenAiAudioApiBuilderTests.java
@@ -37,8 +37,6 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
@@ -65,7 +63,7 @@ void testMinimalBuilder() {
@Test
void testFullBuilder() {
- MultiValueMap headers = new LinkedMultiValueMap<>();
+ HttpHeaders headers = new HttpHeaders();
headers.add("Custom-Header", "test-value");
RestClient.Builder restClientBuilder = RestClient.builder();
WebClient.Builder webClientBuilder = WebClient.builder();
diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/audio/transcription/OpenAiAudioTranscriptionModelTests.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/audio/transcription/OpenAiAudioTranscriptionModelTests.java
index ea9b3d930c3..51a4b52a53d 100644
--- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/audio/transcription/OpenAiAudioTranscriptionModelTests.java
+++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/audio/transcription/OpenAiAudioTranscriptionModelTests.java
@@ -32,10 +32,10 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
+import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.MockRestServiceServer;
-import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
@@ -123,9 +123,8 @@ static class Config {
@Bean
public OpenAiAudioApi openAiAudioApi(RestClient.Builder builder) {
- return new OpenAiAudioApi("https://api.openai.com", new SimpleApiKey("test-api-key"),
- new LinkedMultiValueMap<>(), builder, WebClient.builder(),
- RetryUtils.DEFAULT_RESPONSE_ERROR_HANDLER);
+ return new OpenAiAudioApi("https://api.openai.com", new SimpleApiKey("test-api-key"), new HttpHeaders(),
+ builder, WebClient.builder(), RetryUtils.DEFAULT_RESPONSE_ERROR_HANDLER);
}
@Bean
diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/MessageTypeContentTests.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/MessageTypeContentTests.java
index 0c949a15f71..3a176ffaef4 100644
--- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/MessageTypeContentTests.java
+++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/MessageTypeContentTests.java
@@ -41,10 +41,10 @@
import org.springframework.ai.openai.api.OpenAiApi.ChatCompletionChunk;
import org.springframework.ai.openai.api.OpenAiApi.ChatCompletionRequest;
import org.springframework.core.io.ByteArrayResource;
+import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
-import org.springframework.util.MultiValueMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@@ -66,7 +66,7 @@ public class MessageTypeContentTests {
ArgumentCaptor pomptCaptor;
@Captor
- ArgumentCaptor> headersCaptor;
+ ArgumentCaptor headersCaptor;
Flux fluxResponse = Flux.generate(
() -> new ChatCompletionChunk("id", List.of(), 0L, "model", null, "fp", "object", null), (state, sink) -> {
@@ -89,7 +89,7 @@ public void systemMessageSimpleContentType() {
this.chatModel.call(new Prompt(List.of(new SystemMessage("test message"))));
validateStringContent(this.pomptCaptor.getValue());
- assertThat(this.headersCaptor.getValue()).isEmpty();
+ assertThat(this.headersCaptor.getValue().isEmpty()).isTrue();
}
@Test
@@ -112,7 +112,7 @@ public void streamUserMessageSimpleContentType() {
this.chatModel.stream(new Prompt(List.of(new UserMessage("test message")))).subscribe();
validateStringContent(this.pomptCaptor.getValue());
- assertThat(this.headersCaptor.getValue()).isEmpty();
+ assertThat(this.headersCaptor.getValue().isEmpty()).isTrue();
}
private void validateStringContent(ChatCompletionRequest chatCompletionRequest) {
@@ -207,7 +207,7 @@ public void userMessageWithEmptyMediaList() {
.build())));
validateStringContent(this.pomptCaptor.getValue());
- assertThat(this.headersCaptor.getValue()).isEmpty();
+ assertThat(this.headersCaptor.getValue().isEmpty()).isTrue();
}
@Test
@@ -308,7 +308,7 @@ public void streamWithMultipleMessagesAndMedia() {
// User message should be complex
assertThat(request.messages().get(1).rawContent()).isInstanceOf(List.class);
- assertThat(this.headersCaptor.getValue()).isEmpty();
+ assertThat(this.headersCaptor.getValue().isEmpty()).isTrue();
}
// Helper method for testing different image formats
diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelObservationIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelObservationIT.java
index 3b73adf7f0b..fa2daa3962f 100644
--- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelObservationIT.java
+++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelObservationIT.java
@@ -40,7 +40,8 @@
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 org.springframework.core.retry.RetryPolicy;
+import org.springframework.core.retry.RetryTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.ai.chat.observation.ChatModelObservationDocumentation.HighCardinalityKeyNames;
@@ -175,7 +176,8 @@ public OpenAiApi openAiApi() {
@Bean
public OpenAiChatModel openAiChatModel(OpenAiApi openAiApi, TestObservationRegistry observationRegistry) {
return new OpenAiChatModel(openAiApi, OpenAiChatOptions.builder().build(),
- ToolCallingManager.builder().build(), RetryTemplate.defaultInstance(), observationRegistry);
+ ToolCallingManager.builder().build(), new RetryTemplate(RetryPolicy.withDefaults()),
+ observationRegistry);
}
}
diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelWithChatResponseMetadataTests.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelWithChatResponseMetadataTests.java
index 1e9815513c3..08aa5e07ff9 100644
--- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelWithChatResponseMetadataTests.java
+++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelWithChatResponseMetadataTests.java
@@ -35,7 +35,10 @@
import org.springframework.ai.openai.metadata.support.OpenAiApiResponseHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient;
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
+import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
@@ -55,7 +58,11 @@
* @author Christian Tzolov
* @since 0.7.0
*/
-@RestClientTest(OpenAiChatModelWithChatResponseMetadataTests.Config.class)
+@RestClientTest(value = OpenAiChatModelWithChatResponseMetadataTests.Config.class, properties = "debug=true")
+@AutoConfigureWebClient
+
+// TODO: Remove after Spring Boot 4 RC1
+@ImportAutoConfiguration(WebClientAutoConfiguration.class)
public class OpenAiChatModelWithChatResponseMetadataTests {
private static String TEST_API_KEY = "sk-1234567890";
diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiRetryTests.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiRetryTests.java
index e19e82640b2..4d70dc6e97e 100644
--- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiRetryTests.java
+++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiRetryTests.java
@@ -65,11 +65,11 @@
import org.springframework.ai.retry.RetryUtils;
import org.springframework.ai.retry.TransientAiException;
import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.retry.RetryListener;
+import org.springframework.core.retry.RetryPolicy;
+import org.springframework.core.retry.RetryTemplate;
+import org.springframework.core.retry.Retryable;
import org.springframework.http.ResponseEntity;
-import org.springframework.retry.RetryCallback;
-import org.springframework.retry.RetryContext;
-import org.springframework.retry.RetryListener;
-import org.springframework.retry.support.RetryTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -109,7 +109,7 @@ public class OpenAiRetryTests {
public void beforeEach() {
this.retryTemplate = RetryUtils.SHORT_RETRY_TEMPLATE;
this.retryListener = new TestRetryListener();
- this.retryTemplate.registerListener(this.retryListener);
+ this.retryTemplate.setRetryListener(this.retryListener);
this.chatModel = OpenAiChatModel.builder()
.openAiApi(this.openAiApi)
@@ -145,8 +145,8 @@ public void openAiChatTransientError() {
assertThat(result).isNotNull();
assertThat(result.getResult().getOutput().getText()).isSameAs("Response");
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
- assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
+ assertThat(this.retryListener.retryCount).isEqualTo(2);
}
@Test
@@ -174,8 +174,8 @@ public void openAiChatStreamTransientError() {
assertThat(result).isNotNull();
assertThat(result.collectList().block().get(0).getResult().getOutput().getText()).isSameAs("Response");
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
- assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
+ assertThat(this.retryListener.retryCount).isEqualTo(2);
}
@Test
@@ -202,8 +202,8 @@ public void openAiEmbeddingTransientError() {
assertThat(result).isNotNull();
assertThat(result.getResult().getOutput()).isEqualTo(new float[] { 9.9f, 8.8f });
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
- assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
+ assertThat(this.retryListener.retryCount).isEqualTo(2);
}
@Test
@@ -229,8 +229,8 @@ public void openAiAudioTranscriptionTransientError() {
assertThat(result).isNotNull();
assertThat(result.getResult().getOutput()).isEqualTo(expectedResponse.text());
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
- assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
+ assertThat(this.retryListener.retryCount).isEqualTo(2);
}
@Test
@@ -256,8 +256,8 @@ public void openAiImageTransientError() {
assertThat(result).isNotNull();
assertThat(result.getResult().getOutput().getUrl()).isEqualTo("url678");
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
- assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
+ assertThat(this.retryListener.retryCount).isEqualTo(2);
}
@Test
@@ -270,19 +270,19 @@ public void openAiImageNonTransientError() {
private static class TestRetryListener implements RetryListener {
- int onErrorRetryCount = 0;
+ int retryCount = 0;
int onSuccessRetryCount = 0;
@Override
- public void onSuccess(RetryContext context, RetryCallback callback, T result) {
- this.onSuccessRetryCount = context.getRetryCount();
+ public void onRetrySuccess(final RetryPolicy retryPolicy, final Retryable> retryable, final Object result) {
+ // Count successful retries - we increment when we succeed after a failure
+ this.onSuccessRetryCount++;
}
@Override
- public void onError(RetryContext context, RetryCallback callback,
- Throwable throwable) {
- this.onErrorRetryCount = context.getRetryCount();
+ public void beforeRetry(RetryPolicy retryPolicy, Retryable> retryable) {
+ retryCount++;
}
}
diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiStreamingFinishReasonTests.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiStreamingFinishReasonTests.java
index 3dc59444e82..d1f8dee4929 100644
--- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiStreamingFinishReasonTests.java
+++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiStreamingFinishReasonTests.java
@@ -40,7 +40,7 @@
import org.springframework.ai.openai.api.OpenAiApi.ChatCompletionMessage.Role;
import org.springframework.ai.openai.api.OpenAiApi.ChatCompletionRequest;
import org.springframework.ai.retry.RetryUtils;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/embedding/OpenAiEmbeddingModelObservationIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/embedding/OpenAiEmbeddingModelObservationIT.java
index aa76a67f7a5..5d3cd477653 100644
--- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/embedding/OpenAiEmbeddingModelObservationIT.java
+++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/embedding/OpenAiEmbeddingModelObservationIT.java
@@ -37,7 +37,8 @@
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 org.springframework.core.retry.RetryPolicy;
+import org.springframework.core.retry.RetryTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.ai.embedding.observation.EmbeddingModelObservationDocumentation.HighCardinalityKeyNames;
@@ -111,7 +112,7 @@ public OpenAiApi openAiApi() {
public OpenAiEmbeddingModel openAiEmbeddingModel(OpenAiApi openAiApi,
TestObservationRegistry observationRegistry) {
return new OpenAiEmbeddingModel(openAiApi, MetadataMode.EMBED, OpenAiEmbeddingOptions.builder().build(),
- RetryTemplate.defaultInstance(), observationRegistry);
+ new RetryTemplate(RetryPolicy.withDefaults()), observationRegistry);
}
}
diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/image/OpenAiImageModelNoOpApiKeysIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/image/OpenAiImageModelNoOpApiKeysIT.java
index 61160a08a1f..bbd3373f826 100644
--- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/image/OpenAiImageModelNoOpApiKeysIT.java
+++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/image/OpenAiImageModelNoOpApiKeysIT.java
@@ -31,7 +31,8 @@
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 org.springframework.core.retry.RetryPolicy;
+import org.springframework.core.retry.RetryTemplate;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
@@ -70,7 +71,7 @@ public OpenAiImageApi openAiImageApi() {
@Bean
public OpenAiImageModel openAiImageModel(OpenAiImageApi openAiImageApi) {
return new OpenAiImageModel(openAiImageApi, OpenAiImageOptions.builder().build(),
- RetryTemplate.defaultInstance(), TestObservationRegistry.create());
+ new RetryTemplate(RetryPolicy.withDefaults()), TestObservationRegistry.create());
}
}
diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/image/OpenAiImageModelObservationIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/image/OpenAiImageModelObservationIT.java
index 37dc7abcdba..8a6fb12b13b 100644
--- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/image/OpenAiImageModelObservationIT.java
+++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/image/OpenAiImageModelObservationIT.java
@@ -34,7 +34,8 @@
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 org.springframework.core.retry.RetryPolicy;
+import org.springframework.core.retry.RetryTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.ai.image.observation.ImageModelObservationDocumentation.HighCardinalityKeyNames;
@@ -105,7 +106,7 @@ public OpenAiImageApi openAiImageApi() {
public OpenAiImageModel openAiImageModel(OpenAiImageApi openAiImageApi,
TestObservationRegistry observationRegistry) {
return new OpenAiImageModel(openAiImageApi, OpenAiImageOptions.builder().build(),
- RetryTemplate.defaultInstance(), observationRegistry);
+ new RetryTemplate(RetryPolicy.withDefaults()), observationRegistry);
}
}
diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/image/api/OpenAiImageApiBuilderTests.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/image/api/OpenAiImageApiBuilderTests.java
index 50bfd71fef3..6121893973a 100644
--- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/image/api/OpenAiImageApiBuilderTests.java
+++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/image/api/OpenAiImageApiBuilderTests.java
@@ -37,8 +37,6 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
@@ -64,7 +62,7 @@ void testMinimalBuilder() {
@Test
void testFullBuilder() {
- MultiValueMap headers = new LinkedMultiValueMap<>();
+ HttpHeaders headers = new HttpHeaders();
headers.add("Custom-Header", "test-value");
RestClient.Builder restClientBuilder = RestClient.builder();
ResponseErrorHandler errorHandler = mock(ResponseErrorHandler.class);
diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/moderation/api/OpenAiModerationApiBuilderTests.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/moderation/api/OpenAiModerationApiBuilderTests.java
index 262eb21e05b..e7712e97d97 100644
--- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/moderation/api/OpenAiModerationApiBuilderTests.java
+++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/moderation/api/OpenAiModerationApiBuilderTests.java
@@ -37,8 +37,6 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
@@ -64,7 +62,7 @@ void testMinimalBuilder() {
@Test
void testFullBuilder() {
- MultiValueMap headers = new LinkedMultiValueMap<>();
+ HttpHeaders headers = new HttpHeaders();
headers.add("Custom-Header", "test-value");
RestClient.Builder restClientBuilder = RestClient.builder();
ResponseErrorHandler errorHandler = mock(ResponseErrorHandler.class);
diff --git a/models/spring-ai-vertex-ai-embedding/src/main/java/org/springframework/ai/vertexai/embedding/text/VertexAiTextEmbeddingModel.java b/models/spring-ai-vertex-ai-embedding/src/main/java/org/springframework/ai/vertexai/embedding/text/VertexAiTextEmbeddingModel.java
index 4bef9d1145b..380ed5035ae 100644
--- a/models/spring-ai-vertex-ai-embedding/src/main/java/org/springframework/ai/vertexai/embedding/text/VertexAiTextEmbeddingModel.java
+++ b/models/spring-ai-vertex-ai-embedding/src/main/java/org/springframework/ai/vertexai/embedding/text/VertexAiTextEmbeddingModel.java
@@ -50,7 +50,8 @@
import org.springframework.ai.vertexai.embedding.VertexAiEmbeddingUtils;
import org.springframework.ai.vertexai.embedding.VertexAiEmbeddingUtils.TextInstanceBuilder;
import org.springframework.ai.vertexai.embedding.VertexAiEmbeddingUtils.TextParametersBuilder;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryException;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -139,7 +140,7 @@ public EmbeddingResponse call(EmbeddingRequest request) {
(VertexAiTextEmbeddingOptions) options);
PredictResponse embeddingResponse = this.retryTemplate
- .execute(context -> getPredictResponse(client, predictRequestBuilder));
+ .execute(() -> getPredictResponse(client, predictRequestBuilder));
int index = 0;
int totalTokenCount = 0;
@@ -163,6 +164,14 @@ public EmbeddingResponse call(EmbeddingRequest request) {
return response;
}
+ catch (RetryException e) {
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
+ }
+ else {
+ throw new RuntimeException(e.getCause());
+ }
+ }
});
}
diff --git a/models/spring-ai-vertex-ai-embedding/src/test/java/org/springframework/ai/vertexai/embedding/text/TestVertexAiTextEmbeddingModel.java b/models/spring-ai-vertex-ai-embedding/src/test/java/org/springframework/ai/vertexai/embedding/text/TestVertexAiTextEmbeddingModel.java
index e8627f3d625..36853d0bca7 100644
--- a/models/spring-ai-vertex-ai-embedding/src/test/java/org/springframework/ai/vertexai/embedding/text/TestVertexAiTextEmbeddingModel.java
+++ b/models/spring-ai-vertex-ai-embedding/src/test/java/org/springframework/ai/vertexai/embedding/text/TestVertexAiTextEmbeddingModel.java
@@ -23,7 +23,7 @@
import org.springframework.ai.embedding.EmbeddingRequest;
import org.springframework.ai.vertexai.embedding.VertexAiEmbeddingConnectionDetails;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
public class TestVertexAiTextEmbeddingModel extends VertexAiTextEmbeddingModel {
diff --git a/models/spring-ai-vertex-ai-embedding/src/test/java/org/springframework/ai/vertexai/embedding/text/VertexAiTextEmbeddingRetryTests.java b/models/spring-ai-vertex-ai-embedding/src/test/java/org/springframework/ai/vertexai/embedding/text/VertexAiTextEmbeddingRetryTests.java
index 6d88ef6e958..08f4295dbcd 100644
--- a/models/spring-ai-vertex-ai-embedding/src/test/java/org/springframework/ai/vertexai/embedding/text/VertexAiTextEmbeddingRetryTests.java
+++ b/models/spring-ai-vertex-ai-embedding/src/test/java/org/springframework/ai/vertexai/embedding/text/VertexAiTextEmbeddingRetryTests.java
@@ -36,10 +36,10 @@
import org.springframework.ai.retry.RetryUtils;
import org.springframework.ai.retry.TransientAiException;
import org.springframework.ai.vertexai.embedding.VertexAiEmbeddingConnectionDetails;
-import org.springframework.retry.RetryCallback;
-import org.springframework.retry.RetryContext;
-import org.springframework.retry.RetryListener;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryListener;
+import org.springframework.core.retry.RetryPolicy;
+import org.springframework.core.retry.RetryTemplate;
+import org.springframework.core.retry.Retryable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
@@ -76,7 +76,7 @@ public class VertexAiTextEmbeddingRetryTests {
public void setUp() {
this.retryTemplate = RetryUtils.SHORT_RETRY_TEMPLATE;
this.retryListener = new TestRetryListener();
- this.retryTemplate.registerListener(this.retryListener);
+ this.retryTemplate.setRetryListener(this.retryListener);
this.embeddingModel = new TestVertexAiTextEmbeddingModel(this.mockConnectionDetails,
VertexAiTextEmbeddingOptions.builder().build(), this.retryTemplate);
@@ -123,7 +123,7 @@ public void vertexAiEmbeddingTransientError() {
assertThat(result).isNotNull();
assertThat(result.getResults()).hasSize(1);
assertThat(result.getResults().get(0).getOutput()).isEqualTo(new float[] { 9.9f, 8.8f });
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
verify(this.mockPredictRequestBuilder, times(3)).build();
@@ -163,14 +163,15 @@ private static class TestRetryListener implements RetryListener {
int onSuccessRetryCount = 0;
@Override
- public void onSuccess(RetryContext context, RetryCallback callback, T result) {
- this.onSuccessRetryCount = context.getRetryCount();
+ public void beforeRetry(final RetryPolicy retryPolicy, final Retryable> retryable) {
+ // Count each retry attempt
+ this.onErrorRetryCount++;
}
@Override
- public void onError(RetryContext context, RetryCallback callback,
- Throwable throwable) {
- this.onErrorRetryCount = context.getRetryCount();
+ public void onRetrySuccess(final RetryPolicy retryPolicy, final Retryable> retryable, final Object result) {
+ // Count successful retries - we increment when we succeed after a failure
+ this.onSuccessRetryCount++;
}
}
diff --git a/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatModel.java b/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatModel.java
index 3a55ee58611..957ff8cc959 100644
--- a/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatModel.java
+++ b/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatModel.java
@@ -49,6 +49,7 @@
import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.core.retry.RetryException;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;
@@ -92,7 +93,7 @@
import org.springframework.ai.vertexai.gemini.schema.VertexToolCallingManager;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.lang.NonNull;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
@@ -389,28 +390,45 @@ private ChatResponse internalCall(Prompt prompt, ChatResponse previousChatRespon
ChatResponse response = ChatModelObservationDocumentation.CHAT_MODEL_OPERATION
.observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,
this.observationRegistry)
- .observe(() -> this.retryTemplate.execute(context -> {
-
- var geminiRequest = createGeminiRequest(prompt);
-
- GenerateContentResponse generateContentResponse = this.getContentResponse(geminiRequest);
+ .observe(() -> {
+ try {
+ return this.retryTemplate.execute(() -> {
+
+ var geminiRequest = createGeminiRequest(prompt);
+
+ GenerateContentResponse generateContentResponse = this.getContentResponse(geminiRequest);
+
+ List generations = generateContentResponse.getCandidatesList()
+ .stream()
+ .map(this::responseCandidateToGeneration)
+ .flatMap(List::stream)
+ .toList();
+
+ GenerateContentResponse.UsageMetadata usage = generateContentResponse.getUsageMetadata();
+ Usage currentUsage = (usage != null)
+ ? new DefaultUsage(usage.getPromptTokenCount(), usage.getCandidatesTokenCount())
+ : new EmptyUsage();
+ Usage cumulativeUsage = UsageCalculator.getCumulativeUsage(currentUsage, previousChatResponse);
+ ChatResponse chatResponse = new ChatResponse(generations,
+ toChatResponseMetadata(cumulativeUsage));
+
+ observationContext.setResponse(chatResponse);
+ return chatResponse;
+ });
+ }
+ catch (RetryException e) {
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
+ }
- List generations = generateContentResponse.getCandidatesList()
- .stream()
- .map(this::responseCandidateToGeneration)
- .flatMap(List::stream)
- .toList();
-
- GenerateContentResponse.UsageMetadata usage = generateContentResponse.getUsageMetadata();
- Usage currentUsage = (usage != null)
- ? new DefaultUsage(usage.getPromptTokenCount(), usage.getCandidatesTokenCount())
- : new EmptyUsage();
- Usage cumulativeUsage = UsageCalculator.getCumulativeUsage(currentUsage, previousChatResponse);
- ChatResponse chatResponse = new ChatResponse(generations, toChatResponseMetadata(cumulativeUsage));
-
- observationContext.setResponse(chatResponse);
- return chatResponse;
- }));
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
+ }
+ else {
+ throw new RuntimeException(e.getCause());
+ }
+ }
+ });
if (this.toolExecutionEligibilityPredicate.isToolExecutionRequired(prompt.getOptions(), response)) {
var toolExecutionResult = this.toolCallingManager.executeToolCalls(prompt, response);
diff --git a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/TestVertexAiGeminiChatModel.java b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/TestVertexAiGeminiChatModel.java
index 33af68c57d2..e19e39fccb0 100644
--- a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/TestVertexAiGeminiChatModel.java
+++ b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/TestVertexAiGeminiChatModel.java
@@ -23,7 +23,7 @@
import com.google.cloud.vertexai.generativeai.GenerativeModel;
import org.springframework.ai.model.tool.ToolCallingManager;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
/**
* @author Mark Pollack
diff --git a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiRetryTests.java b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiRetryTests.java
index 79ac33982c3..5c5610c7524 100644
--- a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiRetryTests.java
+++ b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiRetryTests.java
@@ -35,10 +35,10 @@
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.retry.RetryUtils;
import org.springframework.ai.retry.TransientAiException;
-import org.springframework.retry.RetryCallback;
-import org.springframework.retry.RetryContext;
-import org.springframework.retry.RetryListener;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryListener;
+import org.springframework.core.retry.RetryPolicy;
+import org.springframework.core.retry.Retryable;
+import org.springframework.core.retry.RetryTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -68,7 +68,7 @@ public class VertexAiGeminiRetryTests {
public void setUp() {
this.retryTemplate = RetryUtils.SHORT_RETRY_TEMPLATE;
this.retryListener = new TestRetryListener();
- this.retryTemplate.registerListener(this.retryListener);
+ this.retryTemplate.setRetryListener(this.retryListener);
this.chatModel = new TestVertexAiGeminiChatModel(this.vertexAI,
VertexAiGeminiChatOptions.builder()
@@ -101,7 +101,7 @@ public void vertexAiGeminiChatTransientError() throws IOException {
// Assertions
assertThat(result).isNotNull();
assertThat(result.getResult().getOutput().getText()).isEqualTo("Response");
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
}
@@ -168,7 +168,6 @@ public void vertexAiGeminiChatMaxRetriesExceeded() throws Exception {
// Should throw the last TransientAiException after exhausting retries
assertThrows(TransientAiException.class, () -> this.chatModel.call(new Prompt("test prompt")));
-
// Verify retry attempts were made
assertThat(this.retryListener.onErrorRetryCount).isGreaterThan(0);
}
@@ -249,7 +248,7 @@ public void vertexAiGeminiChatAlternatingErrorsAndSuccess() throws Exception {
assertThat(result).isNotNull();
assertThat(result.getResult().getOutput().getText()).isEqualTo("Success after alternating errors");
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
}
@@ -260,14 +259,15 @@ private static class TestRetryListener implements RetryListener {
int onSuccessRetryCount = 0;
@Override
- public void onSuccess(RetryContext context, RetryCallback callback, T result) {
- this.onSuccessRetryCount = context.getRetryCount();
+ public void beforeRetry(final RetryPolicy retryPolicy, final Retryable> retryable) {
+ // Count each retry attempt
+ this.onErrorRetryCount++;
}
@Override
- public void onError(RetryContext context, RetryCallback callback,
- Throwable throwable) {
- this.onErrorRetryCount = context.getRetryCount();
+ public void onRetrySuccess(final RetryPolicy retryPolicy, final Retryable> retryable, final Object result) {
+ // Count successful retries - we increment when we succeed after a failure
+ this.onSuccessRetryCount++;
}
}
diff --git a/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiChatModel.java b/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiChatModel.java
index 2c9ff3e54ff..9090e73d912 100644
--- a/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiChatModel.java
+++ b/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiChatModel.java
@@ -27,6 +27,7 @@
import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.core.retry.RetryException;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
@@ -71,7 +72,7 @@
import org.springframework.ai.zhipuai.api.ZhiPuAiApi.ChatCompletionRequest;
import org.springframework.ai.zhipuai.api.ZhiPuApiConstants;
import org.springframework.http.ResponseEntity;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MimeType;
@@ -260,8 +261,18 @@ public ChatResponse call(Prompt prompt) {
this.observationRegistry)
.observe(() -> {
- ResponseEntity completionEntity = this.retryTemplate
- .execute(ctx -> this.zhiPuAiApi.chatCompletionEntity(request));
+ ResponseEntity completionEntity = null;
+ try {
+ completionEntity = this.retryTemplate.execute(() -> this.zhiPuAiApi.chatCompletionEntity(request));
+ }
+ catch (RetryException e) {
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
+ }
+ else {
+ throw new RuntimeException(e.getCause());
+ }
+ }
var chatCompletion = completionEntity.getBody();
@@ -319,8 +330,18 @@ public Flux stream(Prompt prompt) {
Prompt requestPrompt = buildRequestPrompt(prompt);
ChatCompletionRequest request = createRequest(requestPrompt, true);
- Flux completionChunks = this.retryTemplate
- .execute(ctx -> this.zhiPuAiApi.chatCompletionStream(request));
+ Flux completionChunks = null;
+ try {
+ completionChunks = this.retryTemplate.execute(() -> this.zhiPuAiApi.chatCompletionStream(request));
+ }
+ catch (RetryException e) {
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
+ }
+ else {
+ throw new RuntimeException(e.getCause());
+ }
+ }
// For chunked responses, only the first chunk contains the choice role.
// The rest of the chunks with same ID share the same role.
diff --git a/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiEmbeddingModel.java b/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiEmbeddingModel.java
index c1ec94262e1..2415ee34ae7 100644
--- a/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiEmbeddingModel.java
+++ b/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiEmbeddingModel.java
@@ -21,6 +21,7 @@
import io.micrometer.observation.ObservationRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.core.retry.RetryException;
import org.springframework.ai.chat.metadata.DefaultUsage;
import org.springframework.ai.chat.metadata.EmptyUsage;
@@ -41,7 +42,8 @@
import org.springframework.ai.retry.RetryUtils;
import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
import org.springframework.ai.zhipuai.api.ZhiPuApiConstants;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
+import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
@@ -165,8 +167,19 @@ public EmbeddingResponse call(EmbeddingRequest request) {
.observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,
this.observationRegistry)
.observe(() -> {
- var embeddingResponse = this.retryTemplate
- .execute(ctx -> this.zhiPuAiApi.embeddings(zhipuEmbeddingRequest));
+ ResponseEntity> embeddingResponse = null;
+ try {
+ embeddingResponse = this.retryTemplate
+ .execute(() -> this.zhiPuAiApi.embeddings(zhipuEmbeddingRequest));
+ }
+ catch (RetryException e) {
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
+ }
+ else {
+ throw new RuntimeException(e.getCause());
+ }
+ }
if (embeddingResponse == null || embeddingResponse.getBody() == null
|| CollectionUtils.isEmpty(embeddingResponse.getBody().data())) {
diff --git a/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiImageModel.java b/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiImageModel.java
index 406221e7d8a..ad1e13746af 100644
--- a/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiImageModel.java
+++ b/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiImageModel.java
@@ -20,6 +20,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.core.retry.RetryException;
import org.springframework.ai.image.Image;
import org.springframework.ai.image.ImageGeneration;
@@ -31,7 +32,7 @@
import org.springframework.ai.retry.RetryUtils;
import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi;
import org.springframework.http.ResponseEntity;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.Assert;
/**
@@ -71,30 +72,40 @@ public ZhiPuAiImageOptions getDefaultOptions() {
@Override
public ImageResponse call(ImagePrompt imagePrompt) {
- return this.retryTemplate.execute(ctx -> {
+ try {
+ return this.retryTemplate.execute(() -> {
- String instructions = imagePrompt.getInstructions().get(0).getText();
+ String instructions = imagePrompt.getInstructions().get(0).getText();
- ZhiPuAiImageApi.ZhiPuAiImageRequest imageRequest = new ZhiPuAiImageApi.ZhiPuAiImageRequest(instructions,
- ZhiPuAiImageApi.DEFAULT_IMAGE_MODEL);
+ ZhiPuAiImageApi.ZhiPuAiImageRequest imageRequest = new ZhiPuAiImageApi.ZhiPuAiImageRequest(instructions,
+ ZhiPuAiImageApi.DEFAULT_IMAGE_MODEL);
- if (this.defaultOptions != null) {
- imageRequest = ModelOptionsUtils.merge(this.defaultOptions, imageRequest,
- ZhiPuAiImageApi.ZhiPuAiImageRequest.class);
- }
+ if (this.defaultOptions != null) {
+ imageRequest = ModelOptionsUtils.merge(this.defaultOptions, imageRequest,
+ ZhiPuAiImageApi.ZhiPuAiImageRequest.class);
+ }
- if (imagePrompt.getOptions() != null) {
- imageRequest = ModelOptionsUtils.merge(toZhiPuAiImageOptions(imagePrompt.getOptions()), imageRequest,
- ZhiPuAiImageApi.ZhiPuAiImageRequest.class);
- }
+ if (imagePrompt.getOptions() != null) {
+ imageRequest = ModelOptionsUtils.merge(toZhiPuAiImageOptions(imagePrompt.getOptions()),
+ imageRequest, ZhiPuAiImageApi.ZhiPuAiImageRequest.class);
+ }
- // Make the request
- ResponseEntity imageResponseEntity = this.zhiPuAiImageApi
- .createImage(imageRequest);
+ // Make the request
+ ResponseEntity imageResponseEntity = this.zhiPuAiImageApi
+ .createImage(imageRequest);
- // Convert to org.springframework.ai.model derived ImageResponse data type
- return convertResponse(imageResponseEntity, imageRequest);
- });
+ // Convert to org.springframework.ai.model derived ImageResponse data type
+ return convertResponse(imageResponseEntity, imageRequest);
+ });
+ }
+ catch (RetryException e) {
+ if (e.getCause() instanceof RuntimeException r) {
+ throw r;
+ }
+ else {
+ throw new RuntimeException(e.getCause());
+ }
+ }
}
private ImageResponse convertResponse(ResponseEntity imageResponseEntity,
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 c5a99f99eb3..fb0d55d72c6 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
@@ -42,8 +42,6 @@
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
@@ -85,7 +83,7 @@ public static Builder builder() {
private final ApiKey apiKey;
- private final MultiValueMap headers;
+ private final HttpHeaders headers;
private final String completionsPath;
@@ -143,7 +141,7 @@ public ZhiPuAiApi(String baseUrl, String zhiPuAiToken, RestClient.Builder restCl
@Deprecated
public ZhiPuAiApi(String baseUrl, String zhiPuAiToken, RestClient.Builder restClientBuilder,
ResponseErrorHandler responseErrorHandler) {
- this(baseUrl, new SimpleApiKey(zhiPuAiToken), new LinkedMultiValueMap<>(), DEFAULT_COMPLETIONS_PATH,
+ this(baseUrl, new SimpleApiKey(zhiPuAiToken), new HttpHeaders(), DEFAULT_COMPLETIONS_PATH,
DEFAULT_EMBEDDINGS_PATH, restClientBuilder, WebClient.builder(), responseErrorHandler);
}
@@ -158,7 +156,7 @@ public ZhiPuAiApi(String baseUrl, String zhiPuAiToken, RestClient.Builder restCl
* @param webClientBuilder WebClient builder.
* @param responseErrorHandler Response error handler.
*/
- private ZhiPuAiApi(String baseUrl, ApiKey apiKey, MultiValueMap headers, String completionsPath,
+ private ZhiPuAiApi(String baseUrl, ApiKey apiKey, HttpHeaders headers, String completionsPath,
String embeddingsPath, RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder,
ResponseErrorHandler responseErrorHandler) {
Assert.hasText(completionsPath, "Completions Path must not be null");
@@ -204,7 +202,7 @@ public static String getTextContent(List con
* and headers.
*/
public ResponseEntity chatCompletionEntity(ChatCompletionRequest chatRequest) {
- return chatCompletionEntity(chatRequest, new LinkedMultiValueMap<>());
+ return chatCompletionEntity(chatRequest, new HttpHeaders());
}
/**
@@ -214,7 +212,7 @@ public ResponseEntity chatCompletionEntity(ChatCompletionRequest
* and headers.
*/
public ResponseEntity chatCompletionEntity(ChatCompletionRequest chatRequest,
- MultiValueMap additionalHttpHeader) {
+ HttpHeaders additionalHttpHeader) {
Assert.notNull(chatRequest, "The request body can not be null.");
Assert.isTrue(!chatRequest.stream(), "Request must set the stream property to false.");
@@ -239,7 +237,7 @@ public ResponseEntity chatCompletionEntity(ChatCompletionRequest
* @return Returns a {@link Flux} stream from chat completion chunks.
*/
public Flux chatCompletionStream(ChatCompletionRequest chatRequest) {
- return chatCompletionStream(chatRequest, new LinkedMultiValueMap<>());
+ return chatCompletionStream(chatRequest, new HttpHeaders());
}
/**
@@ -249,7 +247,7 @@ public Flux chatCompletionStream(ChatCompletionRequest chat
* @return Returns a {@link Flux} stream from chat completion chunks.
*/
public Flux chatCompletionStream(ChatCompletionRequest chatRequest,
- MultiValueMap additionalHttpHeader) {
+ HttpHeaders additionalHttpHeader) {
Assert.notNull(chatRequest, "The request body can not be null.");
Assert.isTrue(chatRequest.stream(), "Request must set the stream property to true.");
@@ -330,7 +328,7 @@ public ResponseEntity> embeddings(EmbeddingRequest<
}
private void addDefaultHeadersIfMissing(HttpHeaders headers) {
- if (!headers.containsKey(HttpHeaders.AUTHORIZATION) && !(this.apiKey instanceof NoopApiKey)) {
+ if (headers.get(HttpHeaders.AUTHORIZATION) == null && !(this.apiKey instanceof NoopApiKey)) {
headers.setBearerAuth(this.apiKey.getValue());
}
}
@@ -344,7 +342,7 @@ ApiKey getApiKey() {
return this.apiKey;
}
- MultiValueMap getHeaders() {
+ HttpHeaders getHeaders() {
return this.headers;
}
@@ -1215,7 +1213,8 @@ private Builder() {
public Builder(ZhiPuAiApi api) {
this.baseUrl = api.getBaseUrl();
this.apiKey = api.getApiKey();
- this.headers = new LinkedMultiValueMap<>(api.getHeaders());
+ this.headers = new HttpHeaders();
+ this.headers.addAll(api.getHeaders());
this.completionsPath = api.getCompletionsPath();
this.embeddingsPath = api.getEmbeddingsPath();
this.restClientBuilder = api.restClient != null ? api.restClient.mutate() : RestClient.builder();
@@ -1227,7 +1226,7 @@ public Builder(ZhiPuAiApi api) {
private ApiKey apiKey;
- private MultiValueMap headers = new LinkedMultiValueMap<>();
+ private HttpHeaders headers = new HttpHeaders();
private String completionsPath = DEFAULT_COMPLETIONS_PATH;
@@ -1256,7 +1255,7 @@ public Builder apiKey(String simpleApiKey) {
return this;
}
- public Builder headers(MultiValueMap headers) {
+ public Builder headers(HttpHeaders headers) {
Assert.notNull(headers, "headers cannot be null");
this.headers = headers;
return this;
diff --git a/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/api/ZhiPuAiApiBuilderTests.java b/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/api/ZhiPuAiApiBuilderTests.java
index a6409e70c20..de1684e1c6e 100644
--- a/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/api/ZhiPuAiApiBuilderTests.java
+++ b/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/api/ZhiPuAiApiBuilderTests.java
@@ -66,7 +66,7 @@ void testMinimalBuilder() {
@Test
void testFullBuilder() {
- MultiValueMap headers = new LinkedMultiValueMap<>();
+ var headers = new HttpHeaders();
headers.add("Custom-Header", "test-value");
RestClient.Builder restClientBuilder = RestClient.builder();
WebClient.Builder webClientBuilder = WebClient.builder();
@@ -232,7 +232,7 @@ void dynamicApiKeyRestClientWithAdditionalAuthorizationHeader() throws Interrupt
ZhiPuAiApi.ChatCompletionRequest request = new ZhiPuAiApi.ChatCompletionRequest(
List.of(chatCompletionMessage), "glm-4-flash", 0.8, false);
- MultiValueMap additionalHeaders = new LinkedMultiValueMap<>();
+ var additionalHeaders = new HttpHeaders();
additionalHeaders.add(HttpHeaders.AUTHORIZATION, "Bearer additional-key");
ResponseEntity response = api.chatCompletionEntity(request, additionalHeaders);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
@@ -289,7 +289,7 @@ void dynamicApiKeyWebClientWithAdditionalAuthorizationHeader() throws Interrupte
ZhiPuAiApi.ChatCompletionMessage.Role.USER);
ZhiPuAiApi.ChatCompletionRequest request = new ZhiPuAiApi.ChatCompletionRequest(
List.of(chatCompletionMessage), "glm-4-flash", 0.8, true);
- MultiValueMap additionalHeaders = new LinkedMultiValueMap<>();
+ var additionalHeaders = new HttpHeaders();
additionalHeaders.add(HttpHeaders.AUTHORIZATION, "Bearer additional-key");
List response = api.chatCompletionStream(request, additionalHeaders)
.collectList()
diff --git a/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/api/ZhiPuAiRetryTests.java b/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/api/ZhiPuAiRetryTests.java
index b1b37bd37ba..4fb387a5785 100644
--- a/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/api/ZhiPuAiRetryTests.java
+++ b/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/api/ZhiPuAiRetryTests.java
@@ -53,10 +53,10 @@
import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi.ZhiPuAiImageRequest;
import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi.ZhiPuAiImageResponse;
import org.springframework.http.ResponseEntity;
-import org.springframework.retry.RetryCallback;
-import org.springframework.retry.RetryContext;
-import org.springframework.retry.RetryListener;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryListener;
+import org.springframework.core.retry.RetryPolicy;
+import org.springframework.core.retry.Retryable;
+import org.springframework.core.retry.RetryTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -88,7 +88,7 @@ public class ZhiPuAiRetryTests {
public void beforeEach() {
this.retryTemplate = RetryUtils.SHORT_RETRY_TEMPLATE;
this.retryListener = new TestRetryListener();
- this.retryTemplate.registerListener(this.retryListener);
+ this.retryTemplate.setRetryListener(this.retryListener);
this.chatModel = new ZhiPuAiChatModel(this.zhiPuAiApi, ZhiPuAiChatOptions.builder().build(),
this.retryTemplate);
@@ -115,7 +115,7 @@ public void zhiPuAiChatTransientError() {
assertThat(result).isNotNull();
assertThat(result.getResult().getOutput().getText()).isSameAs("Response");
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
}
@@ -144,7 +144,7 @@ public void zhiPuAiChatStreamTransientError() {
assertThat(result).isNotNull();
assertThat(result.collectList().block().get(0).getResult().getOutput().getText()).isSameAs("Response");
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
}
@@ -174,7 +174,7 @@ public void zhiPuAiEmbeddingTransientError() {
assertThat(result).isNotNull();
// choose the first result
assertThat(result.getResult().getOutput()).isEqualTo(new float[] { 9.9f, 8.8f });
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
}
@@ -201,7 +201,7 @@ public void zhiPuAiImageTransientError() {
assertThat(result).isNotNull();
assertThat(result.getResult().getOutput().getUrl()).isEqualTo("url678");
- assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(2);
+ assertThat(this.retryListener.onSuccessRetryCount).isEqualTo(1);
assertThat(this.retryListener.onErrorRetryCount).isEqualTo(2);
}
@@ -213,21 +213,22 @@ public void zhiPuAiImageNonTransientError() {
() -> this.imageModel.call(new ImagePrompt(List.of(new ImageMessage("Image Message")))));
}
- private class TestRetryListener implements RetryListener {
+ private static class TestRetryListener implements RetryListener {
int onErrorRetryCount = 0;
int onSuccessRetryCount = 0;
@Override
- public void onSuccess(RetryContext context, RetryCallback callback, T result) {
- this.onSuccessRetryCount = context.getRetryCount();
+ public void beforeRetry(final RetryPolicy retryPolicy, final Retryable> retryable) {
+ // Count each retry attempt
+ this.onErrorRetryCount++;
}
@Override
- public void onError(RetryContext context, RetryCallback callback,
- Throwable throwable) {
- this.onErrorRetryCount = context.getRetryCount();
+ public void onRetrySuccess(final RetryPolicy retryPolicy, final Retryable> retryable, final Object result) {
+ // Count successful retries - we increment when we succeed after a failure
+ this.onSuccessRetryCount++;
}
}
diff --git a/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/chat/ZhiPuAiChatModelObservationIT.java b/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/chat/ZhiPuAiChatModelObservationIT.java
index 953c7c3bb4e..a01fc35ec0d 100644
--- a/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/chat/ZhiPuAiChatModelObservationIT.java
+++ b/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/chat/ZhiPuAiChatModelObservationIT.java
@@ -39,7 +39,7 @@
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 org.springframework.core.retry.RetryTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.ai.chat.observation.ChatModelObservationDocumentation.HighCardinalityKeyNames;
@@ -164,8 +164,8 @@ public ZhiPuAiApi zhiPuAiApi() {
@Bean
public ZhiPuAiChatModel zhiPuAiChatModel(ZhiPuAiApi zhiPuAiApi, TestObservationRegistry observationRegistry) {
- return new ZhiPuAiChatModel(zhiPuAiApi, ZhiPuAiChatOptions.builder().build(),
- RetryTemplate.defaultInstance(), observationRegistry);
+ return new ZhiPuAiChatModel(zhiPuAiApi, ZhiPuAiChatOptions.builder().build(), new RetryTemplate(),
+ observationRegistry);
}
}
diff --git a/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/embedding/ZhiPuAiEmbeddingModelObservationIT.java b/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/embedding/ZhiPuAiEmbeddingModelObservationIT.java
index f6a33037566..11f3bc70f75 100644
--- a/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/embedding/ZhiPuAiEmbeddingModelObservationIT.java
+++ b/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/embedding/ZhiPuAiEmbeddingModelObservationIT.java
@@ -37,7 +37,7 @@
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 org.springframework.core.retry.RetryTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.ai.embedding.observation.EmbeddingModelObservationDocumentation.HighCardinalityKeyNames;
@@ -107,7 +107,7 @@ public ZhiPuAiEmbeddingModel zhiPuAiEmbeddingModel(ZhiPuAiApi zhiPuAiApi,
TestObservationRegistry observationRegistry) {
return new ZhiPuAiEmbeddingModel(zhiPuAiApi, MetadataMode.EMBED,
ZhiPuAiEmbeddingOptions.builder().model(ZhiPuAiApi.DEFAULT_EMBEDDING_MODEL).build(),
- RetryTemplate.defaultInstance(), observationRegistry);
+ new RetryTemplate(), observationRegistry);
}
}
diff --git a/pom.xml b/pom.xml
index abb2bc543a3..3397cd66948 100644
--- a/pom.xml
+++ b/pom.xml
@@ -172,7 +172,7 @@
models/spring-ai-bedrock
models/spring-ai-bedrock-converse
models/spring-ai-elevenlabs
- models/spring-ai-huggingface
+
models/spring-ai-minimax
models/spring-ai-mistral-ai
models/spring-ai-oci-genai
@@ -269,12 +269,12 @@
${java.version}
- 3.5.6
+ 4.0.0-M3
4.3.4
1.0.0-beta.16
1.1.0
4.37.0
- 1.9.25
+ 2.2.20
2.31.65
@@ -355,12 +355,12 @@
1.0.0-alpha.5
0.0.4
3.5.0
- true
- true
+ false
+ false
9.3
3.2.8
- false
+ trje
@@ -380,51 +380,51 @@
-
- org.apache.maven.plugins
- maven-checkstyle-plugin
- ${maven-checkstyle-plugin.version}
-
-
- com.puppycrawl.tools
- checkstyle
- ${puppycrawl-tools-checkstyle.version}
-
-
- io.spring.javaformat
- spring-javaformat-checkstyle
- 0.0.43
-
-
-
-
- checkstyle-validation
- validate
- true
-
- ${disable.checks}
- src/checkstyle/checkstyle.xml
- src/checkstyle/checkstyle-header.txt
- true
-
- checkstyle.build.directory=${project.build.directory}
- checkstyle.suppressions.file=${project.basedir}/src/checkstyle/checkstyle-suppressions.xml
- checkstyle.additional.suppressions.file=${project.basedir}/src/checkstyle/checkstyle-suppressions.xml
- checkstyle.header.file=${project.basedir}/src/checkstyle/checkstyle-header.txt
-
- true
- ${maven-checkstyle-plugin.failsOnError}
-
-
- ${maven-checkstyle-plugin.failOnViolation}
-
-
-
- check
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
org.apache.maven.plugins
maven-site-plugin
@@ -1010,39 +1010,39 @@
-
-
-
- org.apache.maven.plugins
- maven-project-info-reports-plugin
- ${maven-project-info-reports-plugin.version}
-
- true
-
-
-
- org.apache.maven.plugins
- maven-checkstyle-plugin
- ${maven-checkstyle-plugin.version}
-
-
-
- checkstyle
-
-
-
-
- src/checkstyle/checkstyle.xml
- src/checkstyle/checkstyle-header.txt
-
- checkstyle.build.directory=${project.build.directory}
- checkstyle.suppressions.file=${project.basedir}/src/checkstyle/checkstyle-suppressions.xml
- checkstyle.additional.suppressions.file=${project.basedir}/src/checkstyle/checkstyle-suppressions.xml
- checkstyle.header.file=${project.basedir}/src/checkstyle/checkstyle-header.txt
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-ai-client-chat/src/main/kotlin/org/springframework/ai/chat/client/ChatClientExtensions.kt b/spring-ai-client-chat/src/main/kotlin/org/springframework/ai/chat/client/ChatClientExtensions.kt
index 40a4c6ffd84..7128603bbc7 100644
--- a/spring-ai-client-chat/src/main/kotlin/org/springframework/ai/chat/client/ChatClientExtensions.kt
+++ b/spring-ai-client-chat/src/main/kotlin/org/springframework/ai/chat/client/ChatClientExtensions.kt
@@ -25,8 +25,8 @@ import org.springframework.core.ParameterizedTypeReference
* @author Josh Long
*/
-inline fun ChatClient.CallResponseSpec.entity(): T =
+inline fun ChatClient.CallResponseSpec.entity(): T =
entity(object : ParameterizedTypeReference() {}) as T
-inline fun ChatClient.CallResponseSpec.responseEntity(): ResponseEntity =
+inline fun ChatClient.CallResponseSpec.responseEntity(): ResponseEntity =
responseEntity(object : ParameterizedTypeReference() {})
diff --git a/spring-ai-model/src/test/kotlin/org/springframework/ai/tool/resolution/TypeResolverHelperKotlinIT.kt b/spring-ai-model/src/test/kotlin/org/springframework/ai/tool/resolution/TypeResolverHelperKotlinIT.kt
index b7671a89b98..b2420bcfe4d 100644
--- a/spring-ai-model/src/test/kotlin/org/springframework/ai/tool/resolution/TypeResolverHelperKotlinIT.kt
+++ b/spring-ai-model/src/test/kotlin/org/springframework/ai/tool/resolution/TypeResolverHelperKotlinIT.kt
@@ -39,7 +39,7 @@ class TypeResolverHelperKotlinIT {
val functionType = TypeResolverHelper.resolveBeanType(this.applicationContext, beanName);
val functionInputClass = TypeResolverHelper.getFunctionArgumentType(functionType, 0).rawClass;
assertThat(functionInputClass).isNotNull();
- assertThat(functionInputClass.typeName).isEqualTo(WeatherRequest::class.java.getName());
+ assertThat(functionInputClass!!.typeName).isEqualTo(WeatherRequest::class.java.getName());
}
class Outer {
diff --git a/spring-ai-retry/pom.xml b/spring-ai-retry/pom.xml
index 20393c99ae0..1706fe61276 100644
--- a/spring-ai-retry/pom.xml
+++ b/spring-ai-retry/pom.xml
@@ -42,11 +42,6 @@
-
- org.springframework.retry
- spring-retry
-
-
org.springframework
spring-web
diff --git a/spring-ai-retry/src/main/java/org/springframework/ai/retry/NonTransientAiException.java b/spring-ai-retry/src/main/java/org/springframework/ai/retry/NonTransientAiException.java
index 44c405ca6d8..c82762f60e0 100644
--- a/spring-ai-retry/src/main/java/org/springframework/ai/retry/NonTransientAiException.java
+++ b/spring-ai-retry/src/main/java/org/springframework/ai/retry/NonTransientAiException.java
@@ -26,11 +26,20 @@
*/
public class NonTransientAiException extends RuntimeException {
- public NonTransientAiException(String message) {
+ /**
+ * Constructor with message.
+ * @param message the exception message
+ */
+ public NonTransientAiException(final String message) {
super(message);
}
- public NonTransientAiException(String message, Throwable cause) {
+ /**
+ * Constructor with message and cause.
+ * @param message the exception message
+ * @param cause the exception cause
+ */
+ public NonTransientAiException(final String message, final Throwable cause) {
super(message, cause);
}
diff --git a/spring-ai-retry/src/main/java/org/springframework/ai/retry/RetryUtils.java b/spring-ai-retry/src/main/java/org/springframework/ai/retry/RetryUtils.java
index 312a7aa9569..7a5d1665697 100644
--- a/spring-ai-retry/src/main/java/org/springframework/ai/retry/RetryUtils.java
+++ b/spring-ai-retry/src/main/java/org/springframework/ai/retry/RetryUtils.java
@@ -20,17 +20,18 @@
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
+import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.core.retry.RetryListener;
+import org.springframework.core.retry.RetryPolicy;
+import org.springframework.core.retry.RetryTemplate;
+import org.springframework.core.retry.Retryable;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.NonNull;
-import org.springframework.retry.RetryCallback;
-import org.springframework.retry.RetryContext;
-import org.springframework.retry.RetryListener;
-import org.springframework.retry.support.RetryTemplate;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.ResponseErrorHandler;
@@ -45,28 +46,44 @@
*/
public abstract class RetryUtils {
+ private static final int DEFAULT_MAX_ATTEMPTS = 10;
+
+ private static final long DEFAULT_INITIAL_INTERVAL = 2000;
+
+ private static final int DEFAULT_MULTIPLIER = 5;
+
+ private static final long DEFAULT_MAX_INTERVAL = 3 * 60000;
+
+ private static final long SHORT_INITIAL_INTERVAL = 100;
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(RetryUtils.class);
+
+ /**
+ * Default ResponseErrorHandler implementation.
+ */
public static final ResponseErrorHandler DEFAULT_RESPONSE_ERROR_HANDLER = new ResponseErrorHandler() {
@Override
- public boolean hasError(@NonNull ClientHttpResponse response) throws IOException {
+ public boolean hasError(final @NonNull ClientHttpResponse response) throws IOException {
return response.getStatusCode().isError();
}
@Override
- public void handleError(URI url, HttpMethod method, @NonNull ClientHttpResponse response) throws IOException {
+ public void handleError(final URI url, final HttpMethod method, final @NonNull ClientHttpResponse response)
+ throws IOException {
handleError(response);
}
@SuppressWarnings("removal")
- public void handleError(@NonNull ClientHttpResponse response) throws IOException {
+ public void handleError(final @NonNull ClientHttpResponse response) throws IOException {
if (response.getStatusCode().isError()) {
String error = StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8);
String message = String.format("%s - %s", response.getStatusCode().value(), error);
- /**
+ /*
* Thrown on 4xx client errors, such as 401 - Incorrect API key provided,
* 401 - You must be a member of an organization to use the API, 429 -
- * Rate limit reached for requests, 429 - You exceeded your current quota
- * , please check your plan and billing details.
+ * Rate limit reached for requests, 429 - You exceeded your current quota,
+ * please check your plan and billing details.
*/
if (response.getStatusCode().is4xxClientError()) {
throw new NonTransientAiException(message);
@@ -74,42 +91,68 @@ public void handleError(@NonNull ClientHttpResponse response) throws IOException
throw new TransientAiException(message);
}
}
+
};
- private static final Logger logger = LoggerFactory.getLogger(RetryUtils.class);
+ /**
+ * Default RetryTemplate with exponential backoff configuration.
+ */
+ public static final RetryTemplate DEFAULT_RETRY_TEMPLATE = createDefaultRetryTemplate();
+
+ /**
+ * Short RetryTemplate for testing scenarios.
+ */
+ public static final RetryTemplate SHORT_RETRY_TEMPLATE = createShortRetryTemplate();
- public static final RetryTemplate DEFAULT_RETRY_TEMPLATE = RetryTemplate.builder()
- .maxAttempts(10)
- .retryOn(TransientAiException.class)
- .retryOn(ResourceAccessException.class)
- .exponentialBackoff(Duration.ofMillis(2000), 5, Duration.ofMillis(3 * 60000))
- .withListener(new RetryListener() {
+ private static RetryTemplate createDefaultRetryTemplate() {
+ RetryPolicy retryPolicy = RetryPolicy.builder()
+ .maxAttempts(DEFAULT_MAX_ATTEMPTS)
+ .includes(TransientAiException.class)
+ .includes(ResourceAccessException.class)
+ .delay(Duration.ofMillis(DEFAULT_INITIAL_INTERVAL))
+ .multiplier(DEFAULT_MULTIPLIER)
+ .maxDelay(Duration.ofMillis(DEFAULT_MAX_INTERVAL))
+ .build();
+
+ RetryTemplate retryTemplate = new RetryTemplate(retryPolicy);
+ retryTemplate.setRetryListener(new RetryListener() {
+ private final AtomicInteger retryCount = new AtomicInteger(0);
@Override
- public void onError(RetryContext context,
- RetryCallback callback, Throwable throwable) {
- logger.warn("Retry error. Retry count:{}", context.getRetryCount(), throwable);
+ public void onRetryFailure(final RetryPolicy policy, final Retryable> retryable,
+ final Throwable throwable) {
+ int currentRetries = retryCount.incrementAndGet();
+ LOGGER.warn("Retry error. Retry count:{}", currentRetries, throwable);
}
- })
- .build();
+ });
+ return retryTemplate;
+ }
/**
- * Useful in testing scenarios where you don't want to wait long for retry and now
- * show stack trace
+ * Useful in testing scenarios where you don't want to wait long for retry and don't
+ * need to show stack trace.
+ * @return a RetryTemplate with short delays
*/
- public static final RetryTemplate SHORT_RETRY_TEMPLATE = RetryTemplate.builder()
- .maxAttempts(10)
- .retryOn(TransientAiException.class)
- .retryOn(ResourceAccessException.class)
- .fixedBackoff(Duration.ofMillis(100))
- .withListener(new RetryListener() {
+ private static RetryTemplate createShortRetryTemplate() {
+ RetryPolicy retryPolicy = RetryPolicy.builder()
+ .maxAttempts(DEFAULT_MAX_ATTEMPTS)
+ .includes(TransientAiException.class)
+ .includes(ResourceAccessException.class)
+ .delay(Duration.ofMillis(SHORT_INITIAL_INTERVAL))
+ .build();
+
+ RetryTemplate retryTemplate = new RetryTemplate(retryPolicy);
+ retryTemplate.setRetryListener(new RetryListener() {
+ private final AtomicInteger retryCount = new AtomicInteger(0);
@Override
- public void onError(RetryContext context,
- RetryCallback callback, Throwable throwable) {
- logger.warn("Retry error. Retry count:{}", context.getRetryCount());
+ public void onRetryFailure(final RetryPolicy policy, final Retryable> retryable,
+ final Throwable throwable) {
+ int currentRetries = retryCount.incrementAndGet();
+ LOGGER.warn("Retry error. Retry count:{}", currentRetries, throwable);
}
- })
- .build();
+ });
+ return retryTemplate;
+ }
}
diff --git a/spring-ai-retry/src/main/java/org/springframework/ai/retry/TransientAiException.java b/spring-ai-retry/src/main/java/org/springframework/ai/retry/TransientAiException.java
index 95b6e37f668..90d43fe0e5c 100644
--- a/spring-ai-retry/src/main/java/org/springframework/ai/retry/TransientAiException.java
+++ b/spring-ai-retry/src/main/java/org/springframework/ai/retry/TransientAiException.java
@@ -19,18 +19,27 @@
/**
* Root of the hierarchy of Model access exceptions that are considered transient - where
* a previously failed operation might be able to succeed when the operation is retried
- * without any intervention by application-level functionality.
+ * without any intervention.
*
* @author Christian Tzolov
* @since 0.8.1
*/
public class TransientAiException extends RuntimeException {
- public TransientAiException(String message) {
+ /**
+ * Constructor with message.
+ * @param message the exception message
+ */
+ public TransientAiException(final String message) {
super(message);
}
- public TransientAiException(String message, Throwable cause) {
+ /**
+ * Constructor with message and cause.
+ * @param message the exception message
+ * @param cause the exception cause
+ */
+ public TransientAiException(final String message, final Throwable cause) {
super(message, cause);
}
diff --git a/spring-ai-spring-boot-docker-compose/pom.xml b/spring-ai-spring-boot-docker-compose/pom.xml
index 642db247343..3e74acd2dd0 100644
--- a/spring-ai-spring-boot-docker-compose/pom.xml
+++ b/spring-ai-spring-boot-docker-compose/pom.xml
@@ -198,7 +198,14 @@
true
-
+
+ org.springframework.boot
+ spring-boot-starter-mongodb
+ true
+
+
+
+
org.springframework.ai
@@ -212,6 +219,8 @@
spring-boot-starter-web
test
+
+
org.springframework.boot
spring-boot-starter-test
diff --git a/spring-ai-spring-boot-docker-compose/src/main/java/org/springframework/ai/docker/compose/service/connection/mongo/MongoDbAtlasLocalDockerComposeConnectionDetailsFactory.java b/spring-ai-spring-boot-docker-compose/src/main/java/org/springframework/ai/docker/compose/service/connection/mongo/MongoDbAtlasLocalDockerComposeConnectionDetailsFactory.java
index 7c04ff256bf..9fccdf39d99 100644
--- a/spring-ai-spring-boot-docker-compose/src/main/java/org/springframework/ai/docker/compose/service/connection/mongo/MongoDbAtlasLocalDockerComposeConnectionDetailsFactory.java
+++ b/spring-ai-spring-boot-docker-compose/src/main/java/org/springframework/ai/docker/compose/service/connection/mongo/MongoDbAtlasLocalDockerComposeConnectionDetailsFactory.java
@@ -18,7 +18,7 @@
import com.mongodb.ConnectionString;
-import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails;
+import org.springframework.boot.mongodb.autoconfigure.MongoConnectionDetails;
import org.springframework.boot.docker.compose.core.RunningService;
import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionDetailsFactory;
import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionSource;
diff --git a/spring-ai-spring-boot-docker-compose/src/test/java/org/springframework/ai/docker/compose/service/connection/mongo/MongoDbAtlasLocalDockerComposeConnectionDetailsFactoryIT.java b/spring-ai-spring-boot-docker-compose/src/test/java/org/springframework/ai/docker/compose/service/connection/mongo/MongoDbAtlasLocalDockerComposeConnectionDetailsFactoryIT.java
index a85e7438c09..e8682145fc2 100644
--- a/spring-ai-spring-boot-docker-compose/src/test/java/org/springframework/ai/docker/compose/service/connection/mongo/MongoDbAtlasLocalDockerComposeConnectionDetailsFactoryIT.java
+++ b/spring-ai-spring-boot-docker-compose/src/test/java/org/springframework/ai/docker/compose/service/connection/mongo/MongoDbAtlasLocalDockerComposeConnectionDetailsFactoryIT.java
@@ -19,7 +19,7 @@
import org.junit.jupiter.api.Test;
import org.testcontainers.utility.DockerImageName;
-import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails;
+import org.springframework.boot.mongodb.autoconfigure.MongoConnectionDetails;
import org.springframework.boot.docker.compose.service.connection.test.AbstractDockerComposeIT;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/spring-ai-spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/test/AbstractDockerComposeIT.java b/spring-ai-spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/test/AbstractDockerComposeIT.java
index 26d26baccbb..66df65f4d41 100644
--- a/spring-ai-spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/test/AbstractDockerComposeIT.java
+++ b/spring-ai-spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/test/AbstractDockerComposeIT.java
@@ -32,7 +32,7 @@
import org.springframework.boot.SpringApplicationShutdownHandlers;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
-import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
+import org.springframework.boot.web.server.autoconfigure.servlet.ServletWebServerConfiguration;
import org.springframework.boot.testsupport.DisabledIfProcessUnavailable;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
@@ -97,7 +97,7 @@ private File transformedComposeFile(File composeFile, DockerImageName imageName)
}
@Configuration(proxyBeanMethods = false)
- @ImportAutoConfiguration(ServletWebServerFactoryAutoConfiguration.class)
+ @ImportAutoConfiguration(ServletWebServerConfiguration.class)
static class Config {
}
diff --git a/spring-ai-spring-boot-testcontainers/src/main/java/org/springframework/ai/testcontainers/service/connection/mongo/MongoDbAtlasLocalContainerConnectionDetailsFactory.java b/spring-ai-spring-boot-testcontainers/src/main/java/org/springframework/ai/testcontainers/service/connection/mongo/MongoDbAtlasLocalContainerConnectionDetailsFactory.java
index 32bd405326e..91a5c5a6cf1 100644
--- a/spring-ai-spring-boot-testcontainers/src/main/java/org/springframework/ai/testcontainers/service/connection/mongo/MongoDbAtlasLocalContainerConnectionDetailsFactory.java
+++ b/spring-ai-spring-boot-testcontainers/src/main/java/org/springframework/ai/testcontainers/service/connection/mongo/MongoDbAtlasLocalContainerConnectionDetailsFactory.java
@@ -22,7 +22,7 @@
import com.mongodb.ConnectionString;
import org.testcontainers.mongodb.MongoDBAtlasLocalContainer;
-import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails;
+import org.springframework.boot.mongodb.autoconfigure.MongoConnectionDetails;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
diff --git a/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/mongo/MongoDbAtlasLocalContainerConnectionDetailsFactoryIT.java b/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/mongo/MongoDbAtlasLocalContainerConnectionDetailsFactoryIT.java
index 6cd28c8982e..3da32032966 100644
--- a/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/mongo/MongoDbAtlasLocalContainerConnectionDetailsFactoryIT.java
+++ b/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/mongo/MongoDbAtlasLocalContainerConnectionDetailsFactoryIT.java
@@ -35,8 +35,7 @@
import org.springframework.ai.vectorstore.mongodb.autoconfigure.MongoDBAtlasVectorStoreAutoConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
-import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
-import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
+import org.springframework.boot.mongodb.autoconfigure.MongoAutoConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -47,10 +46,10 @@
@SpringJUnitConfig
@Testcontainers
-@TestPropertySource(properties = { "spring.data.mongodb.database=simpleaidb",
- "spring.ai.vectorstore.mongodb.initialize-schema=true",
- "spring.ai.vectorstore.mongodb.collection-name=test_collection",
- "spring.ai.vectorstore.mongodb.index-name=text_index" })
+@TestPropertySource(
+ properties = { "spring.mongodb.database=simpleaidb", "spring.ai.vectorstore.mongodb.initialize-schema=true",
+ "spring.ai.vectorstore.mongodb.collection-name=test_collection",
+ "spring.ai.vectorstore.mongodb.index-name=text_index" })
@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+")
class MongoDbAtlasLocalContainerConnectionDetailsFactoryIT {
@@ -94,8 +93,7 @@ public void addAndSearch() throws InterruptedException {
}
@Configuration(proxyBeanMethods = false)
- @ImportAutoConfiguration({ MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
- MongoDBAtlasVectorStoreAutoConfiguration.class })
+ @ImportAutoConfiguration({ MongoAutoConfiguration.class, MongoDBAtlasVectorStoreAutoConfiguration.class })
static class Config {
@Bean
diff --git a/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/ollama/OllamaContainerConnectionDetailsFactoryIT.java b/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/ollama/OllamaContainerConnectionDetailsFactoryIT.java
index d3217fc2c8c..6c9dc470f93 100644
--- a/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/ollama/OllamaContainerConnectionDetailsFactoryIT.java
+++ b/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/ollama/OllamaContainerConnectionDetailsFactoryIT.java
@@ -33,7 +33,7 @@
import org.springframework.ai.ollama.OllamaEmbeddingModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
+import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.TestPropertySource;
diff --git a/vector-stores/spring-ai-cassandra-store/pom.xml b/vector-stores/spring-ai-cassandra-store/pom.xml
index 84682622655..7de6c15a6f8 100644
--- a/vector-stores/spring-ai-cassandra-store/pom.xml
+++ b/vector-stores/spring-ai-cassandra-store/pom.xml
@@ -74,6 +74,12 @@
test
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+ test
+
+
org.testcontainers
junit-jupiter
diff --git a/vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/cassandra/CassandraRichSchemaVectorStoreIT.java b/vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/cassandra/CassandraRichSchemaVectorStoreIT.java
index 1db0398c1dd..48b0fa05b3b 100644
--- a/vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/cassandra/CassandraRichSchemaVectorStoreIT.java
+++ b/vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/cassandra/CassandraRichSchemaVectorStoreIT.java
@@ -50,7 +50,7 @@
import org.springframework.ai.vectorstore.cassandra.CassandraVectorStore.SchemaColumn;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
diff --git a/vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/cassandra/CassandraVectorStoreIT.java b/vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/cassandra/CassandraVectorStoreIT.java
index 040f222e723..cc33f0148c0 100644
--- a/vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/cassandra/CassandraVectorStoreIT.java
+++ b/vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/cassandra/CassandraVectorStoreIT.java
@@ -50,7 +50,7 @@
import org.springframework.ai.vectorstore.filter.Filter;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
diff --git a/vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/cassandra/WikiVectorStoreExample.java b/vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/cassandra/WikiVectorStoreExample.java
index 80d8b945fff..ff3803b16d9 100644
--- a/vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/cassandra/WikiVectorStoreExample.java
+++ b/vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/cassandra/WikiVectorStoreExample.java
@@ -32,7 +32,7 @@
import org.springframework.ai.vectorstore.cassandra.CassandraVectorStore.SchemaColumn;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
diff --git a/vector-stores/spring-ai-couchbase-store/pom.xml b/vector-stores/spring-ai-couchbase-store/pom.xml
index 50b583b7b68..5a70a6ba3b7 100644
--- a/vector-stores/spring-ai-couchbase-store/pom.xml
+++ b/vector-stores/spring-ai-couchbase-store/pom.xml
@@ -59,6 +59,11 @@
spring-boot-testcontainers
test
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+ test
+
org.testcontainers
couchbase
diff --git a/vector-stores/spring-ai-couchbase-store/src/test/java/org/springframework/ai/vectorstore/CouchbaseSearchVectorStoreIT.java b/vector-stores/spring-ai-couchbase-store/src/test/java/org/springframework/ai/vectorstore/CouchbaseSearchVectorStoreIT.java
index dba84ff4c9a..b6690292bac 100644
--- a/vector-stores/spring-ai-couchbase-store/src/test/java/org/springframework/ai/vectorstore/CouchbaseSearchVectorStoreIT.java
+++ b/vector-stores/spring-ai-couchbase-store/src/test/java/org/springframework/ai/vectorstore/CouchbaseSearchVectorStoreIT.java
@@ -45,7 +45,7 @@
import org.springframework.ai.vectorstore.testcontainer.CouchbaseContainerMetadata;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
diff --git a/vector-stores/spring-ai-elasticsearch-store/pom.xml b/vector-stores/spring-ai-elasticsearch-store/pom.xml
index 7c64cbb1c5a..fcd3cbb92e0 100644
--- a/vector-stores/spring-ai-elasticsearch-store/pom.xml
+++ b/vector-stores/spring-ai-elasticsearch-store/pom.xml
@@ -76,6 +76,12 @@
test
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+ test
+
+
org.testcontainers
elasticsearch
diff --git a/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStore.java b/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStore.java
index fc77e4f01f5..28446f75377 100644
--- a/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStore.java
+++ b/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStore.java
@@ -32,10 +32,10 @@
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.Version;
-import co.elastic.clients.transport.rest_client.RestClientTransport;
+import co.elastic.clients.transport.rest5_client.Rest5ClientTransport;
+import co.elastic.clients.transport.rest5_client.low_level.Rest5Client;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
-import org.elasticsearch.client.RestClient;
import org.springframework.ai.document.Document;
import org.springframework.ai.document.DocumentMetadata;
@@ -168,7 +168,7 @@ protected ElasticsearchVectorStore(Builder builder) {
this.filterExpressionConverter = builder.filterExpressionConverter;
String version = Version.VERSION == null ? "Unknown" : Version.VERSION.toString();
- this.elasticsearchClient = new ElasticsearchClient(new RestClientTransport(builder.restClient,
+ this.elasticsearchClient = new ElasticsearchClient(new Rest5ClientTransport(builder.restClient,
new JacksonJsonpMapper(
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false))))
.withTransportOptions(t -> t.addHeader("user-agent", "spring-ai elastic-java/" + version));
@@ -369,13 +369,13 @@ public Optional getNativeClient() {
* Creates a new builder instance for ElasticsearchVectorStore.
* @return a new ElasticsearchBuilder instance
*/
- public static Builder builder(RestClient restClient, EmbeddingModel embeddingModel) {
+ public static Builder builder(Rest5Client restClient, EmbeddingModel embeddingModel) {
return new Builder(restClient, embeddingModel);
}
public static class Builder extends AbstractVectorStoreBuilder {
- private final RestClient restClient;
+ private final Rest5Client restClient;
private ElasticsearchVectorStoreOptions options = new ElasticsearchVectorStoreOptions();
@@ -388,7 +388,7 @@ public static class Builder extends AbstractVectorStoreBuilder {
* @param restClient the Elasticsearch REST client
* @param embeddingModel the Embedding Model to be used
*/
- public Builder(RestClient restClient, EmbeddingModel embeddingModel) {
+ public Builder(Rest5Client restClient, EmbeddingModel embeddingModel) {
super(embeddingModel);
Assert.notNull(restClient, "RestClient must not be null");
this.restClient = restClient;
diff --git a/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStoreIT.java b/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStoreIT.java
index f1f706539df..3703dd4c24d 100644
--- a/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStoreIT.java
+++ b/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStoreIT.java
@@ -17,6 +17,7 @@
package org.springframework.ai.vectorstore.elasticsearch;
import java.io.IOException;
+import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.ZonedDateTime;
@@ -33,12 +34,13 @@
import co.elastic.clients.elasticsearch.cat.indices.IndicesRecord;
import co.elastic.clients.elasticsearch.indices.stats.IndicesStats;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
+import co.elastic.clients.transport.rest5_client.Rest5ClientTransport;
+import co.elastic.clients.transport.rest5_client.low_level.Rest5Client;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
-import org.apache.http.HttpHost;
+import org.apache.hc.core5.http.HttpHost;
import org.awaitility.Awaitility;
-import org.elasticsearch.client.RestClient;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -59,7 +61,7 @@
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.DefaultResourceLoader;
@@ -108,7 +110,7 @@ void cleanDatabase() {
getContextRunner().run(context -> {
// deleting indices and data before following tests
ElasticsearchClient elasticsearchClient = context.getBean(ElasticsearchClient.class);
- List indices = elasticsearchClient.cat().indices().valueBody().stream().map(IndicesRecord::index).toList();
+ var indices = elasticsearchClient.cat().indices().indices().stream().map(IndicesRecord::index).toList();
if (!indices.isEmpty()) {
elasticsearchClient.indices().delete(del -> del.index(indices));
}
@@ -478,12 +480,12 @@ public void getNativeClientTest() {
public static class TestApplication {
@Bean("vectorStore_cosine")
- public ElasticsearchVectorStore vectorStoreDefault(EmbeddingModel embeddingModel, RestClient restClient) {
+ public ElasticsearchVectorStore vectorStoreDefault(EmbeddingModel embeddingModel, Rest5Client restClient) {
return ElasticsearchVectorStore.builder(restClient, embeddingModel).initializeSchema(true).build();
}
@Bean("vectorStore_l2_norm")
- public ElasticsearchVectorStore vectorStoreL2(EmbeddingModel embeddingModel, RestClient restClient) {
+ public ElasticsearchVectorStore vectorStoreL2(EmbeddingModel embeddingModel, Rest5Client restClient) {
ElasticsearchVectorStoreOptions options = new ElasticsearchVectorStoreOptions();
options.setIndexName("index_l2");
options.setSimilarity(SimilarityFunction.l2_norm);
@@ -494,7 +496,7 @@ public ElasticsearchVectorStore vectorStoreL2(EmbeddingModel embeddingModel, Res
}
@Bean("vectorStore_dot_product")
- public ElasticsearchVectorStore vectorStoreDotProduct(EmbeddingModel embeddingModel, RestClient restClient) {
+ public ElasticsearchVectorStore vectorStoreDotProduct(EmbeddingModel embeddingModel, Rest5Client restClient) {
ElasticsearchVectorStoreOptions options = new ElasticsearchVectorStoreOptions();
options.setIndexName("index_dot_product");
options.setSimilarity(SimilarityFunction.dot_product);
@@ -505,7 +507,7 @@ public ElasticsearchVectorStore vectorStoreDotProduct(EmbeddingModel embeddingMo
}
@Bean("vectorStore_custom_embedding_field")
- public ElasticsearchVectorStore vectorStoreCustomField(EmbeddingModel embeddingModel, RestClient restClient) {
+ public ElasticsearchVectorStore vectorStoreCustomField(EmbeddingModel embeddingModel, Rest5Client restClient) {
ElasticsearchVectorStoreOptions options = new ElasticsearchVectorStoreOptions();
options.setEmbeddingFieldName("custom_embedding_field");
return ElasticsearchVectorStore.builder(restClient, embeddingModel)
@@ -520,13 +522,13 @@ public EmbeddingModel embeddingModel() {
}
@Bean
- RestClient restClient() {
- return RestClient.builder(HttpHost.create(elasticsearchContainer.getHttpHostAddress())).build();
+ Rest5Client restClient() throws URISyntaxException {
+ return Rest5Client.builder(HttpHost.create(elasticsearchContainer.getHttpHostAddress())).build();
}
@Bean
- ElasticsearchClient elasticsearchClient(RestClient restClient) {
- return new ElasticsearchClient(new RestClientTransport(restClient, new JacksonJsonpMapper(
+ ElasticsearchClient elasticsearchClient(Rest5Client restClient) {
+ return new ElasticsearchClient(new Rest5ClientTransport(restClient, new JacksonJsonpMapper(
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false))));
}
diff --git a/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStoreObservationIT.java b/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStoreObservationIT.java
index 5efeeb1dd28..7ec1bfca2f9 100644
--- a/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStoreObservationIT.java
+++ b/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStoreObservationIT.java
@@ -17,6 +17,7 @@
package org.springframework.ai.vectorstore.elasticsearch;
import java.io.IOException;
+import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.List;
@@ -26,15 +27,15 @@
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.cat.indices.IndicesRecord;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
-import co.elastic.clients.transport.rest_client.RestClientTransport;
+import co.elastic.clients.transport.rest5_client.Rest5ClientTransport;
+import co.elastic.clients.transport.rest5_client.low_level.Rest5Client;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.tck.TestObservationRegistry;
import io.micrometer.observation.tck.TestObservationRegistryAssert;
-import org.apache.http.HttpHost;
+import org.apache.hc.core5.http.HttpHost;
import org.awaitility.Awaitility;
-import org.elasticsearch.client.RestClient;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -58,7 +59,7 @@
import org.springframework.ai.vectorstore.observation.VectorStoreObservationDocumentation.LowCardinalityKeyNames;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.DefaultResourceLoader;
@@ -111,7 +112,7 @@ void cleanDatabase() {
getContextRunner().run(context -> {
// deleting indices and data before following tests
ElasticsearchClient elasticsearchClient = context.getBean(ElasticsearchClient.class);
- List indices = elasticsearchClient.cat().indices().valueBody().stream().map(IndicesRecord::index).toList();
+ var indices = elasticsearchClient.cat().indices().indices().stream().map(IndicesRecord::index).toList();
if (!indices.isEmpty()) {
elasticsearchClient.indices().delete(del -> del.index(indices));
}
@@ -207,7 +208,7 @@ public TestObservationRegistry observationRegistry() {
}
@Bean
- public ElasticsearchVectorStore vectorStoreDefault(EmbeddingModel embeddingModel, RestClient restClient,
+ public ElasticsearchVectorStore vectorStoreDefault(EmbeddingModel embeddingModel, Rest5Client restClient,
ObservationRegistry observationRegistry) {
return ElasticsearchVectorStore.builder(restClient, embeddingModel)
.initializeSchema(true)
@@ -224,13 +225,13 @@ public EmbeddingModel embeddingModel() {
}
@Bean
- RestClient restClient() {
- return RestClient.builder(HttpHost.create(elasticsearchContainer.getHttpHostAddress())).build();
+ Rest5Client restClient() throws URISyntaxException {
+ return Rest5Client.builder(HttpHost.create(elasticsearchContainer.getHttpHostAddress())).build();
}
@Bean
- ElasticsearchClient elasticsearchClient(RestClient restClient) {
- return new ElasticsearchClient(new RestClientTransport(restClient, new JacksonJsonpMapper(
+ ElasticsearchClient elasticsearchClient(Rest5Client restClient) {
+ return new ElasticsearchClient(new Rest5ClientTransport(restClient, new JacksonJsonpMapper(
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false))));
}
diff --git a/vector-stores/spring-ai-mariadb-store/pom.xml b/vector-stores/spring-ai-mariadb-store/pom.xml
index cb440f3561d..4f72fe546ab 100644
--- a/vector-stores/spring-ai-mariadb-store/pom.xml
+++ b/vector-stores/spring-ai-mariadb-store/pom.xml
@@ -80,6 +80,12 @@
test
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+ test
+
+
org.testcontainers
testcontainers
diff --git a/vector-stores/spring-ai-mariadb-store/src/test/java/org/springframework/ai/vectorstore/mariadb/MariaDBStoreCustomNamesIT.java b/vector-stores/spring-ai-mariadb-store/src/test/java/org/springframework/ai/vectorstore/mariadb/MariaDBStoreCustomNamesIT.java
index b69d945524f..3cd53141c6f 100644
--- a/vector-stores/spring-ai-mariadb-store/src/test/java/org/springframework/ai/vectorstore/mariadb/MariaDBStoreCustomNamesIT.java
+++ b/vector-stores/spring-ai-mariadb-store/src/test/java/org/springframework/ai/vectorstore/mariadb/MariaDBStoreCustomNamesIT.java
@@ -32,8 +32,8 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.ApplicationContext;
diff --git a/vector-stores/spring-ai-mariadb-store/src/test/java/org/springframework/ai/vectorstore/mariadb/MariaDBStoreIT.java b/vector-stores/spring-ai-mariadb-store/src/test/java/org/springframework/ai/vectorstore/mariadb/MariaDBStoreIT.java
index 96955dca876..0f26dae6161 100644
--- a/vector-stores/spring-ai-mariadb-store/src/test/java/org/springframework/ai/vectorstore/mariadb/MariaDBStoreIT.java
+++ b/vector-stores/spring-ai-mariadb-store/src/test/java/org/springframework/ai/vectorstore/mariadb/MariaDBStoreIT.java
@@ -53,8 +53,8 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.ApplicationContext;
diff --git a/vector-stores/spring-ai-mariadb-store/src/test/java/org/springframework/ai/vectorstore/mariadb/MariaDBStoreObservationIT.java b/vector-stores/spring-ai-mariadb-store/src/test/java/org/springframework/ai/vectorstore/mariadb/MariaDBStoreObservationIT.java
index 616078dae9f..2c42ee159df 100644
--- a/vector-stores/spring-ai-mariadb-store/src/test/java/org/springframework/ai/vectorstore/mariadb/MariaDBStoreObservationIT.java
+++ b/vector-stores/spring-ai-mariadb-store/src/test/java/org/springframework/ai/vectorstore/mariadb/MariaDBStoreObservationIT.java
@@ -47,8 +47,8 @@
import org.springframework.ai.vectorstore.observation.VectorStoreObservationDocumentation.LowCardinalityKeyNames;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
diff --git a/vector-stores/spring-ai-milvus-store/pom.xml b/vector-stores/spring-ai-milvus-store/pom.xml
index e5031cb6388..0fb49d16130 100644
--- a/vector-stores/spring-ai-milvus-store/pom.xml
+++ b/vector-stores/spring-ai-milvus-store/pom.xml
@@ -70,6 +70,11 @@
test
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+ test
+
org.springframework.boot
diff --git a/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/milvus/MilvusVectorStoreCustomFieldNamesIT.java b/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/milvus/MilvusVectorStoreCustomFieldNamesIT.java
index 14a8422441d..990bc982235 100644
--- a/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/milvus/MilvusVectorStoreCustomFieldNamesIT.java
+++ b/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/milvus/MilvusVectorStoreCustomFieldNamesIT.java
@@ -28,6 +28,7 @@
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.milvus.MilvusContainer;
@@ -42,7 +43,6 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.DefaultResourceLoader;
diff --git a/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/milvus/MilvusVectorStoreIT.java b/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/milvus/MilvusVectorStoreIT.java
index a367fa4068e..03d569ff268 100644
--- a/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/milvus/MilvusVectorStoreIT.java
+++ b/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/milvus/MilvusVectorStoreIT.java
@@ -40,6 +40,7 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.slf4j.LoggerFactory;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.milvus.MilvusContainer;
@@ -57,7 +58,6 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.DefaultResourceLoader;
diff --git a/vector-stores/spring-ai-neo4j-store/pom.xml b/vector-stores/spring-ai-neo4j-store/pom.xml
index 478f57f1793..06b65cd2f2f 100644
--- a/vector-stores/spring-ai-neo4j-store/pom.xml
+++ b/vector-stores/spring-ai-neo4j-store/pom.xml
@@ -84,6 +84,12 @@
test
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+ test
+
+
org.testcontainers
neo4j
diff --git a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/Neo4jVectorStoreIT.java b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/Neo4jVectorStoreIT.java
index b851269cb59..af751463fb5 100644
--- a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/Neo4jVectorStoreIT.java
+++ b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/Neo4jVectorStoreIT.java
@@ -46,7 +46,7 @@
import org.springframework.ai.vectorstore.filter.FilterExpressionTextParser;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
diff --git a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/Neo4jVectorStoreObservationIT.java b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/Neo4jVectorStoreObservationIT.java
index 8ce003d7272..ee3b3117422 100644
--- a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/Neo4jVectorStoreObservationIT.java
+++ b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/Neo4jVectorStoreObservationIT.java
@@ -50,7 +50,7 @@
import org.springframework.ai.vectorstore.observation.VectorStoreObservationDocumentation.LowCardinalityKeyNames;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.DefaultResourceLoader;
diff --git a/vector-stores/spring-ai-opensearch-store/pom.xml b/vector-stores/spring-ai-opensearch-store/pom.xml
index 84387f1a7ec..08d973c4a2e 100644
--- a/vector-stores/spring-ai-opensearch-store/pom.xml
+++ b/vector-stores/spring-ai-opensearch-store/pom.xml
@@ -106,7 +106,13 @@
micrometer-observation-test
test
-
+
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+ test
+
diff --git a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStoreIT.java b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStoreIT.java
index 380d434c63b..e56c7de3bb9 100644
--- a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStoreIT.java
+++ b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStoreIT.java
@@ -55,7 +55,7 @@
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.DefaultResourceLoader;
diff --git a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStoreObservationIT.java b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStoreObservationIT.java
index 13bf2eea1a3..5ef705c3a16 100644
--- a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStoreObservationIT.java
+++ b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStoreObservationIT.java
@@ -54,7 +54,7 @@
import org.springframework.ai.vectorstore.observation.VectorStoreObservationDocumentation.LowCardinalityKeyNames;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.DefaultResourceLoader;
diff --git a/vector-stores/spring-ai-oracle-store/pom.xml b/vector-stores/spring-ai-oracle-store/pom.xml
index 700e507407a..5f158b55106 100644
--- a/vector-stores/spring-ai-oracle-store/pom.xml
+++ b/vector-stores/spring-ai-oracle-store/pom.xml
@@ -95,6 +95,12 @@
test
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+ test
+
+
org.testcontainers
testcontainers
diff --git a/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/OracleVectorStoreIT.java b/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/OracleVectorStoreIT.java
index a9bca5631a7..e10bf28817c 100644
--- a/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/OracleVectorStoreIT.java
+++ b/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/OracleVectorStoreIT.java
@@ -52,8 +52,8 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.ApplicationContext;
diff --git a/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/OracleVectorStoreObservationIT.java b/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/OracleVectorStoreObservationIT.java
index 066a0295ff6..b729524367a 100644
--- a/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/OracleVectorStoreObservationIT.java
+++ b/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/OracleVectorStoreObservationIT.java
@@ -48,8 +48,8 @@
import org.springframework.ai.vectorstore.oracle.OracleVectorStore.OracleVectorStoreDistanceType;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.ApplicationContext;
diff --git a/vector-stores/spring-ai-pgvector-store/pom.xml b/vector-stores/spring-ai-pgvector-store/pom.xml
index 92251350a7d..c8a2504436d 100644
--- a/vector-stores/spring-ai-pgvector-store/pom.xml
+++ b/vector-stores/spring-ai-pgvector-store/pom.xml
@@ -98,6 +98,12 @@
test
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+ test
+
+
org.testcontainers
testcontainers
diff --git a/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/pgvector/PgVectorStoreAutoTruncationIT.java b/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/pgvector/PgVectorStoreAutoTruncationIT.java
index 819c72f294b..c5688dd9107 100644
--- a/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/pgvector/PgVectorStoreAutoTruncationIT.java
+++ b/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/pgvector/PgVectorStoreAutoTruncationIT.java
@@ -41,8 +41,8 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.ApplicationContext;
diff --git a/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/pgvector/PgVectorStoreCustomNamesIT.java b/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/pgvector/PgVectorStoreCustomNamesIT.java
index 56844743870..d1bf7186806 100644
--- a/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/pgvector/PgVectorStoreCustomNamesIT.java
+++ b/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/pgvector/PgVectorStoreCustomNamesIT.java
@@ -35,8 +35,8 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.ApplicationContext;
diff --git a/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/pgvector/PgVectorStoreIT.java b/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/pgvector/PgVectorStoreIT.java
index e8a75a00d2f..880cceaa675 100644
--- a/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/pgvector/PgVectorStoreIT.java
+++ b/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/pgvector/PgVectorStoreIT.java
@@ -56,8 +56,8 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.ApplicationContext;
diff --git a/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/pgvector/PgVectorStoreObservationIT.java b/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/pgvector/PgVectorStoreObservationIT.java
index 88cbe57520f..38b98e44d21 100644
--- a/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/pgvector/PgVectorStoreObservationIT.java
+++ b/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/pgvector/PgVectorStoreObservationIT.java
@@ -49,8 +49,8 @@
import org.springframework.ai.vectorstore.pgvector.PgVectorStore.PgIndexType;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
diff --git a/vector-stores/spring-ai-redis-store/pom.xml b/vector-stores/spring-ai-redis-store/pom.xml
index 6a536d76561..570e51e0ecb 100644
--- a/vector-stores/spring-ai-redis-store/pom.xml
+++ b/vector-stores/spring-ai-redis-store/pom.xml
@@ -75,6 +75,12 @@
test
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+ test
+
+
org.springframework.boot
spring-boot-starter-test
diff --git a/vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/redis/RedisVectorStoreIT.java b/vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/redis/RedisVectorStoreIT.java
index 80b2b304614..0f2051cb562 100644
--- a/vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/redis/RedisVectorStoreIT.java
+++ b/vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/redis/RedisVectorStoreIT.java
@@ -45,8 +45,7 @@
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.data.redis.autoconfigure.RedisAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.DefaultResourceLoader;
@@ -317,7 +316,6 @@ void getNativeClientTest() {
}
@SpringBootConfiguration
- @EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })
public static class TestApplication {
@Bean
diff --git a/vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/redis/RedisVectorStoreObservationIT.java b/vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/redis/RedisVectorStoreObservationIT.java
index 53e11eeb750..ffd8ade4fd5 100644
--- a/vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/redis/RedisVectorStoreObservationIT.java
+++ b/vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/redis/RedisVectorStoreObservationIT.java
@@ -47,7 +47,7 @@
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+import org.springframework.boot.data.redis.autoconfigure.RedisAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.DefaultResourceLoader;
diff --git a/vector-stores/spring-ai-typesense-store/pom.xml b/vector-stores/spring-ai-typesense-store/pom.xml
index 5b7c650efeb..c4f918dd395 100644
--- a/vector-stores/spring-ai-typesense-store/pom.xml
+++ b/vector-stores/spring-ai-typesense-store/pom.xml
@@ -95,6 +95,11 @@
test
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+ test
+
diff --git a/vector-stores/spring-ai-typesense-store/src/test/java/org/springframework/ai/vectorstore/typesense/TypesenseVectorStoreIT.java b/vector-stores/spring-ai-typesense-store/src/test/java/org/springframework/ai/vectorstore/typesense/TypesenseVectorStoreIT.java
index 863f72fa00a..34fea59c09a 100644
--- a/vector-stores/spring-ai-typesense-store/src/test/java/org/springframework/ai/vectorstore/typesense/TypesenseVectorStoreIT.java
+++ b/vector-stores/spring-ai-typesense-store/src/test/java/org/springframework/ai/vectorstore/typesense/TypesenseVectorStoreIT.java
@@ -46,7 +46,7 @@
import org.springframework.ai.vectorstore.filter.Filter;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.DefaultResourceLoader;