[google-adk-spring-ai] SpringAI beans are never auto-configured: @ConditionalOnBean(ChatModel) is evaluated before Spring AI model auto-configurations (missing ordering declaration)
- Lingua principale
- Java
- Stelle
- 1.7k
- Fork
- 420
- Merge medio
- 4g 12h
- PR unite (30g)
- 31
Descrizione
# Environment
| Component | Version |
|---|---|
| google-adk / google-adk-spring-ai | 1.9.0 |
| Spring AI (BOM) | 2.0.1 |
| Spring Boot | 4.0.2 |
| Java | 25 |
## Minimal reproduction
**pom.xml** (relevant parts):
```xml
org.springframework.boot
spring-boot-starter-parent
4.0.2
25
2.0.1
1.9.0
org.springframework.boot
spring-boot-starter
com.google.adk
google-adk
${adk.version}
com.google.adk
google-adk-spring-ai
${adk.version}
org.springframework.ai
spring-ai-starter-model-openai
org.springframework.ai
spring-ai-bom
${spring-ai.version}
pom
import
```
**Application.java:**
```java
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
LlmAgent agent(SpringAI springAI) {
return LlmAgent.builder()
.name("demo-agent")
.model(springAI)
.instruction("You are a helpful assistant.")
.build();
}
}
```
**application.yaml** (dummy values are enough — the failure happens at bean-definition processing, before any remote call):
```yaml
spring:
ai:
openai:
base-url: https://api.openai.com
api-key: dummy
chat:
model: gpt-4o-mini
```
Run: `mvn spring-boot:run`
## Actual behavior
Application fails to start:
```
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of method agent in com.example.demo.DemoApplication required a bean
of type 'com.google.adk.models.springai.SpringAI' that could not be found.
Action:
Consider defining a bean of type 'com.google.adk.models.springai.SpringAI' in your configuration.
```
Running with `--debug` shows the root cause — the OpenAI `ChatModel` bean definition **is** registered, but `SpringAIAutoConfiguration` is processed **before** it, so all three `SpringAI` `@Bean` methods are skipped:
```
OpenAiChatAutoConfiguration matched:
- @ConditionalOnProperty (spring.ai.model.chat=openai) matched
OpenAiChatAutoConfiguration#openAiChatModel matched:
- @ConditionalOnMissingBean ... did not find any beans (OnBeanCondition)
SpringAIAutoConfiguration#springAIWithBothModels:
Did not match:
- @ConditionalOnBean (types: ...ChatModel, ...StreamingChatModel) did not find any beans
SpringAIAutoConfiguration#springAIWithChatModel:
Did not match:
- @ConditionalOnBean (types: ...ChatModel) did not find any beans of type ...ChatModel (OnBeanCondition)
SpringAIAutoConfiguration#springAIWithStreamingModel:
Did not match:
- @ConditionalOnBean (types: ...StreamingChatModel) did not find any beans
```
Note `springAIEmbedding` has the same problem (`@ConditionalOnBean(EmbeddingModel.class)`) — it fails **silently** (no bean, no error), which makes it even harder to notice.
## Expected behavior
`SpringAIAutoConfiguration` registers a `SpringAI` bean whenever a `ChatModel` (and/or `StreamingChatModel`) is defined by Spring AI model auto-configurations, regardless of which provider starter is used.
## Root cause
`SpringAIAutoConfiguration` guards its `@Bean` methods with `@ConditionalOnBean(...)`, but declares no ordering relative to the auto-configurations that may provide those beans. Per the Spring Boot reference documentation, the result of `@ConditionalOnBean` depends on what has been processed so far, so an auto-configuration relying on it must be ordered after the configurations that register the matched beans.
`google-adk-spring-ai` 1.9.0 is built against Spring AI 2.x / Boot 4 (its POM depends on `spring-ai-model:2.0.1` and `spring-boot-autoconfigure:4.0.2`), and since it compiles only against `spring-ai-model` (no provider modules), the Class-based `after = ...` attribute is not usable here — the string-based `afterName` attribute exists exactly for this case (non-existent class names are silently ignored by the sorter).
## Suggested fix
Add an `afterName` list covering the Spring AI model auto-configurations, mirroring what Spring AI 1.x's own `ChatClientAutoConfiguration` did for the identical scenario (`@ConditionalOnBean(ChatModel)` with an unpredictable provider):
```diff
-@AutoConfiguration
+@AutoConfiguration(afterName = {
+ "org.springframework.ai.model.openai.autoconfigure.OpenAiChatAutoConfiguration",
+ "org.springframework.ai.model.openai.autoconfigure.OpenAiEmbeddingAutoConfiguration",
+ // remaining Spring AI 2.x model auto-configurations, e.g.:
+ // org.springframework.ai.model.anthropic.autoconfigure.AnthropicChatAutoConfiguration
+ // org.springframework.ai.model.ollama.autoconfigure.OllamaChatAutoConfiguration
+ // ... (package pattern: org.springframework.ai.model..autoconfigure.*)
+})
@ConditionalOnClass({SpringAI.class, ChatModel.class})
@ConditionalOnProperty(prefix = "adk.spring-ai.auto-configuration", name = "enabled",
havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties({SpringAIProperties.class})
public class SpringAIAutoConfiguration {
```
(Verified against Spring AI 2.0.1: `org.springframework.ai.model.openai.autoconfigure.OpenAiChatAutoConfiguration` / `OpenAiEmbeddingAutoConfiguration`. The other provider names above follow the 2.x package pattern and should be double-checked against the current source.)
An alternative design — the one Spring AI 2.0 itself adopted — is to drop the `@ConditionalOnBean` guards entirely and take `ChatModel`/`StreamingChatModel` as `@Bean` method parameters, which removes the ordering sensitivity altogether. The `afterName` fix is the smaller, backward-compatible change, though.
## Workaround (for users on 1.9.0)
Define the `SpringAI` bean in application code — user-defined beans are processed before auto-configurations, and ADK's `@ConditionalOnMissingBean(SpringAI.class)` then backs off:
```java
@Bean
SpringAI springAI(ChatModel chatModel) {
return new SpringAI(chatModel, "gpt-4o-mini");
}
```
This works with any provider (it consumes the `ChatModel` abstraction), but every project using ADK + Spring AI has to copy it, so it is not a substitute for the upstream fix.
## References
- Spring Boot reference — [Creating your own auto-configuration / condition annotations](https://docs.spring.io/spring-boot/reference/features/developing-auto-configuration.html): ordering matters for `@ConditionalOnBean`
- Spring AI 1.x `ChatClientAutoConfiguration`: same pattern solved with `afterName` over all provider auto-configurations
- Spring AI 2.0.1 `ChatClientAutoConfiguration`: same problem avoided via `@Bean` parameter injection instead of `@ConditionalOnBean`
Guida per i contributori
Apri la guida per i contributori
Valutazione
Questa issue non è ancora stata valutata.