From 4c434a6f59c6f3245f0b5d693a82634d6d3670e7 Mon Sep 17 00:00:00 2001
From: kongxubihai <1769699920@qq.com>
Date: Fri, 11 Sep 2026 05:35:35 +0000
Subject: [PATCH] fix(spring-ai): order SpringAIAutoConfiguration after Spring
AI model auto-configurations
---
contrib/spring-ai/pom.xml | 5 ++
.../SpringAIAutoConfiguration.java | 23 ++++++-
...SpringAIAutoConfigurationOrderingTest.java | 60 +++++++++++++++++++
3 files changed, 87 insertions(+), 1 deletion(-)
create mode 100644 contrib/spring-ai/src/test/java/com/google/adk/models/springai/autoconfigure/SpringAIAutoConfigurationOrderingTest.java
diff --git a/contrib/spring-ai/pom.xml b/contrib/spring-ai/pom.xml
index 2a6b1143b..1632bee24 100644
--- a/contrib/spring-ai/pom.xml
+++ b/contrib/spring-ai/pom.xml
@@ -137,6 +137,11 @@
spring-ai-ollama
test
+
+ org.springframework.ai
+ spring-ai-starter-model-openai
+ test
+
diff --git a/contrib/spring-ai/src/main/java/com/google/adk/models/springai/autoconfigure/SpringAIAutoConfiguration.java b/contrib/spring-ai/src/main/java/com/google/adk/models/springai/autoconfigure/SpringAIAutoConfiguration.java
index 7a312ca88..62e947efa 100644
--- a/contrib/spring-ai/src/main/java/com/google/adk/models/springai/autoconfigure/SpringAIAutoConfiguration.java
+++ b/contrib/spring-ai/src/main/java/com/google/adk/models/springai/autoconfigure/SpringAIAutoConfiguration.java
@@ -58,7 +58,28 @@
* adk.spring-ai.validation.enabled=true
*
*/
-@AutoConfiguration
+@AutoConfiguration(
+ afterName = {
+ // --- Spring AI 2.0.1 chat model auto-configurations (verified against published jars) ---
+ "org.springframework.ai.model.anthropic.autoconfigure.AnthropicChatAutoConfiguration",
+ "org.springframework.ai.model.bedrock.converse.autoconfigure.BedrockConverseProxyChatAutoConfiguration",
+ "org.springframework.ai.model.deepseek.autoconfigure.DeepSeekChatAutoConfiguration",
+ "org.springframework.ai.model.google.genai.autoconfigure.chat.GoogleGenAiChatAutoConfiguration",
+ "org.springframework.ai.model.mistralai.autoconfigure.MistralAiChatAutoConfiguration",
+ "org.springframework.ai.model.ollama.autoconfigure.OllamaChatAutoConfiguration",
+ "org.springframework.ai.model.openai.autoconfigure.OpenAiChatAutoConfiguration",
+ // --- embedding model auto-configurations (required for springAIEmbedding,
+ // which silently fails to register without these) ---
+ "org.springframework.ai.model.bedrock.cohere.autoconfigure.BedrockCohereEmbeddingAutoConfiguration",
+ "org.springframework.ai.model.bedrock.titan.autoconfigure.BedrockTitanEmbeddingAutoConfiguration",
+ "org.springframework.ai.model.google.genai.autoconfigure.embedding.GoogleGenAiTextEmbeddingAutoConfiguration",
+ "org.springframework.ai.model.mistralai.autoconfigure.MistralAiEmbeddingAutoConfiguration",
+ "org.springframework.ai.model.ollama.autoconfigure.OllamaEmbeddingAutoConfiguration",
+ "org.springframework.ai.model.openai.autoconfigure.OpenAiEmbeddingAutoConfiguration",
+ "org.springframework.ai.model.postgresml.autoconfigure.PostgresMlEmbeddingAutoConfiguration",
+ "org.springframework.ai.model.transformers.autoconfigure.TransformersEmbeddingModelAutoConfiguration",
+ "org.springframework.ai.model.vertexai.autoconfigure.embedding.VertexAiTextEmbeddingAutoConfiguration"
+ })
@ConditionalOnClass({SpringAI.class, ChatModel.class})
@ConditionalOnProperty(
prefix = "adk.spring-ai.auto-configuration",
diff --git a/contrib/spring-ai/src/test/java/com/google/adk/models/springai/autoconfigure/SpringAIAutoConfigurationOrderingTest.java b/contrib/spring-ai/src/test/java/com/google/adk/models/springai/autoconfigure/SpringAIAutoConfigurationOrderingTest.java
new file mode 100644
index 000000000..001e98b8f
--- /dev/null
+++ b/contrib/spring-ai/src/test/java/com/google/adk/models/springai/autoconfigure/SpringAIAutoConfigurationOrderingTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2025 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.adk.models.springai.autoconfigure;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.model.openai.autoconfigure.OpenAiChatAutoConfiguration;
+import org.springframework.ai.model.openai.autoconfigure.OpenAiEmbeddingAutoConfiguration;
+import org.springframework.ai.model.tool.autoconfigure.ToolCallingAutoConfiguration;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+
+/**
+ * Regression tests: SpringAIAutoConfiguration guards its beans with {@code @ConditionalOnBean},
+ * which is order-sensitive. It must therefore declare itself ordered after the Spring AI model
+ * auto-configurations, otherwise the conditions never match and no SpringAI/SpringAIEmbedding bean
+ * is registered.
+ */
+class SpringAIAutoConfigurationOrderingTest {
+
+ // OpenAI auto-configurations fail at context refresh without a non-blank api key.
+ private static final String[] PROPERTIES = {"spring.ai.openai.api-key=dummy"};
+
+ // ToolCallingAutoConfiguration provides the ToolCallingManager that
+ // OpenAiChatAutoConfiguration requires. A real application imports it
+ // automatically; ApplicationContextRunner only processes what is declared.
+ private final ApplicationContextRunner runner =
+ new ApplicationContextRunner()
+ .withPropertyValues(PROPERTIES)
+ .withConfiguration(
+ AutoConfigurations.of(
+ SpringAIAutoConfiguration.class, // listed first on purpose
+ ToolCallingAutoConfiguration.class,
+ OpenAiChatAutoConfiguration.class,
+ OpenAiEmbeddingAutoConfiguration.class));
+
+ @Test
+ void registersSpringAI_whenProviderAutoConfigurationsAreProcessedFirst() {
+ runner.run(context -> assertThat(context).hasBean("springAIWithBothModels"));
+ }
+
+ @Test
+ void registersSpringAIEmbedding_whenEmbeddingAutoConfigurationIsProcessedFirst() {
+ runner.run(context -> assertThat(context).hasBean("springAIEmbedding"));
+ }
+}