-
Notifications
You must be signed in to change notification settings - Fork 1
Merge from paulbakker:feature/boot4 #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feature/spring-boot-4-preparations
Are you sure you want to change the base?
Merge from paulbakker:feature/boot4 #1
Conversation
Signed-off-by: Paul Bakker <[email protected]>
Signed-off-by: Paul Bakker <[email protected]>
…spring-boot-4-preparations-joined-efforts # Conflicts: # auto-configurations/common/spring-ai-autoconfigure-retry/src/main/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfiguration.java # auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-webflux/src/test/java/org/springframework/ai/mcp/server/autoconfigure/McpServerSseWebFluxAutoConfigurationTests.java # 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 # 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 # 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 # 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 # auto-configurations/models/spring-ai-autoconfigure-model-anthropic/pom.xml # auto-configurations/models/spring-ai-autoconfigure-model-anthropic/src/main/java/org/springframework/ai/model/anthropic/autoconfigure/AnthropicChatAutoConfiguration.java # auto-configurations/models/spring-ai-autoconfigure-model-deepseek/pom.xml # auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/main/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekChatAutoConfiguration.java # auto-configurations/models/spring-ai-autoconfigure-model-elevenlabs/pom.xml # auto-configurations/models/spring-ai-autoconfigure-model-elevenlabs/src/main/java/org/springframework/ai/model/elevenlabs/autoconfigure/ElevenLabsAutoConfiguration.java # auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxChatAutoConfiguration.java # auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxEmbeddingAutoConfiguration.java # auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiChatAutoConfiguration.java # auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiEmbeddingAutoConfiguration.java # auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiModerationAutoConfiguration.java # auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiOcrAutoConfiguration.java # auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/test/java/org/springframework/ai/model/ollama/autoconfigure/BaseOllamaIT.java # auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAudioSpeechAutoConfiguration.java # auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAudioTranscriptionAutoConfiguration.java # auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiChatAutoConfiguration.java # auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiEmbeddingAutoConfiguration.java # auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiImageAutoConfiguration.java # auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiModerationAutoConfiguration.java # auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAutoConfigurationIT.java # auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiModelConfigurationTests.java # auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiPropertiesTests.java # auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiResponseFormatPropertiesTests.java # auto-configurations/models/spring-ai-autoconfigure-model-postgresml-embedding/src/main/java/org/springframework/ai/model/postgresml/autoconfigure/PostgresMlEmbeddingAutoConfiguration.java # auto-configurations/models/spring-ai-autoconfigure-model-stability-ai/src/main/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiImageAutoConfiguration.java # auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/pom.xml # auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiChatAutoConfiguration.java # auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiEmbeddingAutoConfiguration.java # auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiImageAutoConfiguration.java # auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-cassandra/src/main/java/org/springframework/ai/vectorstore/cassandra/autoconfigure/CassandraVectorStoreAutoConfiguration.java # auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-couchbase/src/main/java/org/springframework/ai/vectorstore/couchbase/autoconfigure/CouchbaseSearchVectorStoreAutoConfiguration.java # auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-couchbase/src/test/java/org/springframework/ai/vectorstore/couchbase/autoconfigure/CouchbaseSearchVectorStoreAutoConfigurationIT.java # auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-elasticsearch/src/main/java/org/springframework/ai/vectorstore/elasticsearch/autoconfigure/ElasticsearchVectorStoreAutoConfiguration.java # auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-elasticsearch/src/test/java/org/springframework/ai/vectorstore/elasticsearch/autoconfigure/ElasticsearchVectorStoreAutoConfigurationIT.java # auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mariadb/src/main/java/org/springframework/ai/vectorstore/mariadb/autoconfigure/MariaDbStoreAutoConfiguration.java # auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-mongodb-atlas/src/test/java/org/springframework/ai/vectorstore/mongodb/autoconfigure/MongoDBAtlasVectorStoreAutoConfigurationIT.java # auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-neo4j/src/main/java/org/springframework/ai/vectorstore/neo4j/autoconfigure/Neo4jVectorStoreAutoConfiguration.java # auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-oracle/src/main/java/org/springframework/ai/vectorstore/oracle/autoconfigure/OracleVectorStoreAutoConfiguration.java # auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-pgvector/src/main/java/org/springframework/ai/vectorstore/pgvector/autoconfigure/PgVectorStoreAutoConfiguration.java # auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-redis/src/main/java/org/springframework/ai/vectorstore/redis/autoconfigure/RedisVectorStoreAutoConfiguration.java # auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-redis/src/test/java/org/springframework/ai/vectorstore/redis/autoconfigure/RedisVectorStoreAutoConfigurationIT.java # memory/repository/spring-ai-model-chat-memory-repository-cassandra/src/test/java/org/springframework/ai/chat/memory/repository/cassandra/CassandraChatMemoryRepositoryIT.java # models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/api/AnthropicApi.java # models/spring-ai-huggingface/pom.xml # models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java # models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiFileApi.java # models/spring-ai-openai/src/main/java/org/springframework/ai/openai/metadata/support/OpenAiResponseHeaderExtractor.java # models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelWithChatResponseMetadataTests.java # models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/api/ZhiPuAiApi.java # pom.xml # spring-ai-model/src/test/kotlin/org/springframework/ai/tool/resolution/TypeResolverHelperKotlinIT.kt # spring-ai-retry/pom.xml # spring-ai-retry/src/main/java/org/springframework/ai/retry/RetryUtils.java # spring-ai-spring-boot-docker-compose/src/main/java/org/springframework/ai/docker/compose/service/connection/mongo/MongoDbAtlasLocalDockerComposeConnectionDetailsFactory.java # spring-ai-spring-boot-docker-compose/src/test/java/org/springframework/ai/docker/compose/service/connection/mongo/MongoDbAtlasLocalDockerComposeConnectionDetailsFactoryIT.java # spring-ai-spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/test/AbstractDockerComposeIT.java # spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/mongo/MongoDbAtlasLocalContainerConnectionDetailsFactoryIT.java # vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/cassandra/CassandraRichSchemaVectorStoreIT.java # vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/cassandra/CassandraVectorStoreIT.java # vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/cassandra/WikiVectorStoreExample.java # vector-stores/spring-ai-couchbase-store/src/test/java/org/springframework/ai/vectorstore/CouchbaseSearchVectorStoreIT.java # vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStoreIT.java # vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStoreObservationIT.java # vector-stores/spring-ai-mariadb-store/src/test/java/org/springframework/ai/vectorstore/mariadb/MariaDBStoreCustomNamesIT.java # vector-stores/spring-ai-mariadb-store/src/test/java/org/springframework/ai/vectorstore/mariadb/MariaDBStoreIT.java # vector-stores/spring-ai-mariadb-store/src/test/java/org/springframework/ai/vectorstore/mariadb/MariaDBStoreObservationIT.java # vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/milvus/MilvusVectorStoreCustomFieldNamesIT.java # vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/milvus/MilvusVectorStoreIT.java # vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/Neo4jVectorStoreIT.java # vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/Neo4jVectorStoreObservationIT.java # vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStoreIT.java # vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStoreObservationIT.java # vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/OracleVectorStoreIT.java # vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/OracleVectorStoreObservationIT.java # vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/pgvector/PgVectorStoreAutoTruncationIT.java # vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/pgvector/PgVectorStoreCustomNamesIT.java # vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/pgvector/PgVectorStoreIT.java # vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/pgvector/PgVectorStoreObservationIT.java # vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/redis/RedisVectorStoreIT.java # vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/redis/RedisVectorStoreObservationIT.java # vector-stores/spring-ai-typesense-store/src/test/java/org/springframework/ai/vectorstore/typesense/TypesenseVectorStoreIT.java
Signed-off-by: Dmitry Bedrin <[email protected]>
Signed-off-by: Dmitry Bedrin <[email protected]>
Signed-off-by: Dmitry Bedrin <[email protected]>
Signed-off-by: Dmitry Bedrin <[email protected]>
…ueMap Signed-off-by: Dmitry Bedrin <[email protected]>
Signed-off-by: Dmitry Bedrin <[email protected]>
Signed-off-by: Dmitry Bedrin <[email protected]>
Signed-off-by: Dmitry Bedrin <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR migrates from Spring Retry library to Spring Framework 7's built-in retry functionality. The changes remove the external spring-retry dependency and update all retry-related code to use the new Spring Framework core retry APIs.
Key changes:
- Removed spring-retry dependency and migrated to
org.springframework.core.retrypackage - Updated RetryTemplate instantiation from builder pattern to constructor-based with RetryPolicy
- Changed RetryListener callback methods to match new API (
beforeRetry,onRetrySuccess,onRetryFailure) - Replaced
MultiValueMap<String, String>withHttpHeadersfor API header management - Added try-catch blocks to handle
RetryExceptionin retry template execute calls
Reviewed Changes
Copilot reviewed 145 out of 145 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| spring-ai-retry/pom.xml | Removed spring-retry dependency |
| spring-ai-retry/src/main/java/org/springframework/ai/retry/*.java | Updated retry classes to use Spring Framework 7 core retry APIs |
| models//src/main/java/**/.java | Updated model classes to use new RetryTemplate API and handle RetryException |
| models/*/src/test/java/**/*Tests.java | Updated test retry listeners to use new callback methods |
| auto-configurations/**/pom.xml | Added Spring Boot starter dependencies for JDBC, Redis, MongoDB, etc. |
| Various API classes | Replaced MultiValueMap with HttpHeaders for better type safety |
Comments suppressed due to low confidence (1)
models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiChatModel.java:682
- Variable usageMetadata may be null at this access because of this null argument.
Variable usageMetadata may be null at this access because of this null argument.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| 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 <T extends Object, E extends Throwable> void onError(RetryContext context, | ||
| RetryCallback<T, E> 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 = this.retryCount.incrementAndGet(); | ||
| LOGGER.warn("Retry error. Retry count:{}", currentRetries, throwable); | ||
| } | ||
| }) | ||
| .build(); | ||
| }); | ||
| return retryTemplate; | ||
| } |
Copilot
AI
Oct 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The AtomicInteger retryCount in the RetryListener is instance-specific but the RetryTemplate is static and shared. This counter will accumulate across all retry operations globally, not per retry operation. Consider using RetryContext or removing the counter if the new API provides retry count differently.
| 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 <T extends Object, E extends Throwable> void onError(RetryContext context, | ||
| RetryCallback<T, E> 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 = this.retryCount.incrementAndGet(); | ||
| LOGGER.warn("Retry error. Retry count:{}", currentRetries, throwable); | ||
| } | ||
| }) | ||
| .build(); | ||
| }); | ||
| return retryTemplate; | ||
| } |
Copilot
AI
Oct 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same issue as createDefaultRetryTemplate - the AtomicInteger retryCount will accumulate globally across all retry operations since SHORT_RETRY_TEMPLATE is a shared static field.
| catch (RetryException e) { | ||
| if (e.getCause() instanceof RuntimeException r) { | ||
| throw r; | ||
| } | ||
|
|
||
| List<Generation> 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()); | ||
| } | ||
| } |
Copilot
AI
Oct 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicate conditional check - the first if (e.getCause() instanceof RuntimeException r) block is redundant and unreachable code follows it.
auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-redis/pom.xml
Show resolved
Hide resolved
Signed-off-by: Dmitry Bedrin <[email protected]>
Signed-off-by: Dmitry Bedrin <[email protected]>
| } | ||
| } | ||
|
|
||
| AnthropicApi.ChatCompletionResponse completionResponse = completionEntity.getBody(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it the same behaviour as it was previously?
If yes, we need to make sure that we're handling RetryException's in all places where we use RetryTemplate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This mimics the old behavior, only if the cause is not a RuntimeException, the behavior would technically be different.
Signed-off-by: Dmitry Bedrin <[email protected]>
Signed-off-by: Dmitry Bedrin <[email protected]>
…emory-repository-cosmos-db Signed-off-by: Dmitry Bedrin <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
No description provided.