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")); + } +}