Fix AzureOpenAI auto-configuration opt-in

- Move AzureOpenAiClientBuilderAutoConfiguration to a simple AzureOpenAiClientBuilderConfiguration
 - This will make sure that the Azure OpenAI client builder will only be imported when the underlying Azure OpenAI auto-configuration is enabled
 - Update tests

Signed-off-by: Ilayaperumal Gopinathan <ilayaperumal.gopinathan@broadcom.com>
This commit is contained in:
Ilayaperumal Gopinathan
2025-04-10 12:04:20 +01:00
committed by Mark Pollack
parent eef5f74b4c
commit 31afae9650
6 changed files with 27 additions and 8 deletions

View File

@@ -28,6 +28,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
/**
* {@link AutoConfiguration Auto-configuration} for Azure OpenAI.
@@ -42,7 +43,7 @@ import org.springframework.context.annotation.Bean;
@EnableConfigurationProperties(AzureOpenAiAudioTranscriptionProperties.class)
@ConditionalOnProperty(name = SpringAIModelProperties.AUDIO_TRANSCRIPTION_MODEL,
havingValue = SpringAIModels.AZURE_OPENAI, matchIfMissing = true)
@ImportAutoConfiguration(classes = AzureOpenAiClientBuilderAutoConfiguration.class)
@Import(AzureOpenAiClientBuilderConfiguration.class)
public class AzureOpenAiAudioTranscriptionAutoConfiguration {
@Bean

View File

@@ -38,6 +38,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
/**
* {@link AutoConfiguration Auto-configuration} for Azure OpenAI.
@@ -52,8 +53,8 @@ import org.springframework.context.annotation.Bean;
@EnableConfigurationProperties({ AzureOpenAiChatProperties.class })
@ConditionalOnProperty(name = SpringAIModelProperties.CHAT_MODEL, havingValue = SpringAIModels.AZURE_OPENAI,
matchIfMissing = true)
@ImportAutoConfiguration(
classes = { AzureOpenAiClientBuilderAutoConfiguration.class, ToolCallingAutoConfiguration.class })
@ImportAutoConfiguration(classes = { ToolCallingAutoConfiguration.class })
@Import(AzureOpenAiClientBuilderConfiguration.class)
public class AzureOpenAiChatAutoConfiguration {
@Bean

View File

@@ -38,17 +38,16 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* {@link AutoConfiguration Auto-configuration} for Azure OpenAI.
* Azure OpenAI Client Builder configuration.
*
* @author Piotr Olaszewski
* @author Soby Chacko
* @author Manuel Andreo Garcia
* @author Ilayaperumal Gopinathan
*/
@AutoConfiguration
@ConditionalOnClass({ OpenAIClientBuilder.class })
@EnableConfigurationProperties(AzureOpenAiConnectionProperties.class)
public class AzureOpenAiClientBuilderAutoConfiguration {
public class AzureOpenAiClientBuilderConfiguration {
private static final String APPLICATION_ID = "spring-ai";

View File

@@ -31,6 +31,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
/**
* {@link AutoConfiguration Auto-configuration} for Azure OpenAI.
@@ -45,7 +46,7 @@ import org.springframework.context.annotation.Bean;
@EnableConfigurationProperties({ AzureOpenAiEmbeddingProperties.class })
@ConditionalOnProperty(name = SpringAIModelProperties.EMBEDDING_MODEL, havingValue = SpringAIModels.AZURE_OPENAI,
matchIfMissing = true)
@ImportAutoConfiguration(classes = AzureOpenAiClientBuilderAutoConfiguration.class)
@Import(AzureOpenAiClientBuilderConfiguration.class)
public class AzureOpenAiEmbeddingAutoConfiguration {
@Bean

View File

@@ -28,6 +28,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
/**
* {@link AutoConfiguration Auto-configuration} for Azure OpenAI.
@@ -42,7 +43,7 @@ import org.springframework.context.annotation.Bean;
@ConditionalOnProperty(name = SpringAIModelProperties.IMAGE_MODEL, havingValue = SpringAIModels.AZURE_OPENAI,
matchIfMissing = true)
@EnableConfigurationProperties(AzureOpenAiImageOptionsProperties.class)
@ImportAutoConfiguration(classes = AzureOpenAiClientBuilderAutoConfiguration.class)
@Import(AzureOpenAiClientBuilderConfiguration.class)
public class AzureOpenAiImageAutoConfiguration {
@Bean

View File

@@ -16,6 +16,7 @@
package org.springframework.ai.model.azure.openai.autoconfigure;
import com.azure.ai.openai.OpenAIClientBuilder;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
@@ -45,6 +46,7 @@ public class AzureOpenAiModelConfigurationTests {
this.contextRunner.withConfiguration(AutoConfigurations.of(AzureOpenAiChatAutoConfiguration.class))
.run(context -> {
assertThat(context.getBeansOfType(AzureOpenAiChatModel.class)).isNotEmpty();
assertThat(context.getBeansOfType(OpenAIClientBuilder.class)).isNotEmpty();
assertThat(context.getBeansOfType(AzureOpenAiEmbeddingModel.class)).isEmpty();
assertThat(context.getBeansOfType(AzureOpenAiImageModel.class)).isEmpty();
assertThat(context.getBeansOfType(AzureOpenAiAudioTranscriptionModel.class)).isEmpty();
@@ -55,6 +57,7 @@ public class AzureOpenAiModelConfigurationTests {
.run(context -> {
assertThat(context.getBeansOfType(AzureOpenAiChatProperties.class)).isEmpty();
assertThat(context.getBeansOfType(AzureOpenAiChatModel.class)).isEmpty();
assertThat(context.getBeansOfType(OpenAIClientBuilder.class)).isEmpty();
});
this.contextRunner.withConfiguration(AutoConfigurations.of(AzureOpenAiChatAutoConfiguration.class))
@@ -70,6 +73,7 @@ public class AzureOpenAiModelConfigurationTests {
"spring.ai.model.audio.transcription=none", "spring.ai.model.moderation=none")
.run(context -> {
assertThat(context.getBeansOfType(AzureOpenAiChatModel.class)).isNotEmpty();
assertThat(context.getBeansOfType(OpenAIClientBuilder.class)).isNotEmpty();
assertThat(context.getBeansOfType(AzureOpenAiEmbeddingModel.class)).isEmpty();
assertThat(context.getBeansOfType(AzureOpenAiImageModel.class)).isEmpty();
assertThat(context.getBeansOfType(AzureOpenAiAudioTranscriptionModel.class)).isEmpty();
@@ -84,6 +88,7 @@ public class AzureOpenAiModelConfigurationTests {
assertThat(context.getBeansOfType(AzureOpenAiEmbeddingModel.class)).isNotEmpty();
assertThat(context.getBeansOfType(AzureOpenAiImageModel.class)).isEmpty();
assertThat(context.getBeansOfType(AzureOpenAiAudioTranscriptionModel.class)).isEmpty();
assertThat(context.getBeansOfType(OpenAIClientBuilder.class)).isNotEmpty();
});
this.contextRunner.withConfiguration(AutoConfigurations.of(AzureOpenAiEmbeddingAutoConfiguration.class))
@@ -91,6 +96,7 @@ public class AzureOpenAiModelConfigurationTests {
.run(context -> {
assertThat(context.getBeansOfType(AzureOpenAiEmbeddingProperties.class)).isEmpty();
assertThat(context.getBeansOfType(AzureOpenAiEmbeddingModel.class)).isEmpty();
assertThat(context.getBeansOfType(OpenAIClientBuilder.class)).isEmpty();
});
this.contextRunner.withConfiguration(AutoConfigurations.of(AzureOpenAiEmbeddingAutoConfiguration.class))
@@ -98,6 +104,7 @@ public class AzureOpenAiModelConfigurationTests {
.run(context -> {
assertThat(context.getBeansOfType(AzureOpenAiEmbeddingProperties.class)).isNotEmpty();
assertThat(context.getBeansOfType(AzureOpenAiEmbeddingModel.class)).isNotEmpty();
assertThat(context.getBeansOfType(OpenAIClientBuilder.class)).isNotEmpty();
});
this.contextRunner.withConfiguration(AutoConfigurations.of(AzureOpenAiEmbeddingAutoConfiguration.class))
@@ -107,6 +114,7 @@ public class AzureOpenAiModelConfigurationTests {
.run(context -> {
assertThat(context.getBeansOfType(AzureOpenAiChatModel.class)).isEmpty();
assertThat(context.getBeansOfType(AzureOpenAiEmbeddingModel.class)).isNotEmpty();
assertThat(context.getBeansOfType(OpenAIClientBuilder.class)).isNotEmpty();
assertThat(context.getBeansOfType(AzureOpenAiImageModel.class)).isEmpty();
assertThat(context.getBeansOfType(AzureOpenAiAudioTranscriptionModel.class)).isEmpty();
});
@@ -119,6 +127,7 @@ public class AzureOpenAiModelConfigurationTests {
assertThat(context.getBeansOfType(AzureOpenAiChatModel.class)).isEmpty();
assertThat(context.getBeansOfType(AzureOpenAiEmbeddingModel.class)).isEmpty();
assertThat(context.getBeansOfType(AzureOpenAiImageModel.class)).isNotEmpty();
assertThat(context.getBeansOfType(OpenAIClientBuilder.class)).isNotEmpty();
assertThat(context.getBeansOfType(AzureOpenAiAudioTranscriptionModel.class)).isEmpty();
});
@@ -127,6 +136,7 @@ public class AzureOpenAiModelConfigurationTests {
.run(context -> {
assertThat(context.getBeansOfType(AzureOpenAiImageOptionsProperties.class)).isEmpty();
assertThat(context.getBeansOfType(AzureOpenAiImageModel.class)).isEmpty();
assertThat(context.getBeansOfType(OpenAIClientBuilder.class)).isEmpty();
});
this.contextRunner.withConfiguration(AutoConfigurations.of(AzureOpenAiImageAutoConfiguration.class))
@@ -134,6 +144,7 @@ public class AzureOpenAiModelConfigurationTests {
.run(context -> {
assertThat(context.getBeansOfType(AzureOpenAiImageOptionsProperties.class)).isNotEmpty();
assertThat(context.getBeansOfType(AzureOpenAiImageModel.class)).isNotEmpty();
assertThat(context.getBeansOfType(OpenAIClientBuilder.class)).isNotEmpty();
});
this.contextRunner.withConfiguration(AutoConfigurations.of(AzureOpenAiImageAutoConfiguration.class))
@@ -144,6 +155,7 @@ public class AzureOpenAiModelConfigurationTests {
assertThat(context.getBeansOfType(AzureOpenAiChatModel.class)).isEmpty();
assertThat(context.getBeansOfType(AzureOpenAiEmbeddingModel.class)).isEmpty();
assertThat(context.getBeansOfType(AzureOpenAiImageModel.class)).isNotEmpty();
assertThat(context.getBeansOfType(OpenAIClientBuilder.class)).isNotEmpty();
assertThat(context.getBeansOfType(AzureOpenAiAudioTranscriptionModel.class)).isEmpty();
});
}
@@ -157,6 +169,7 @@ public class AzureOpenAiModelConfigurationTests {
assertThat(context.getBeansOfType(AzureOpenAiEmbeddingModel.class)).isEmpty();
assertThat(context.getBeansOfType(AzureOpenAiImageModel.class)).isEmpty();
assertThat(context.getBeansOfType(AzureOpenAiAudioTranscriptionModel.class)).isNotEmpty();
assertThat(context.getBeansOfType(OpenAIClientBuilder.class)).isNotEmpty();
});
this.contextRunner
@@ -165,6 +178,7 @@ public class AzureOpenAiModelConfigurationTests {
.run(context -> {
assertThat(context.getBeansOfType(AzureOpenAiAudioTranscriptionProperties.class)).isEmpty();
assertThat(context.getBeansOfType(AzureOpenAiAudioTranscriptionModel.class)).isEmpty();
assertThat(context.getBeansOfType(OpenAIClientBuilder.class)).isEmpty();
});
this.contextRunner
@@ -173,6 +187,7 @@ public class AzureOpenAiModelConfigurationTests {
.run(context -> {
assertThat(context.getBeansOfType(AzureOpenAiAudioTranscriptionProperties.class)).isNotEmpty();
assertThat(context.getBeansOfType(AzureOpenAiAudioTranscriptionModel.class)).isNotEmpty();
assertThat(context.getBeansOfType(OpenAIClientBuilder.class)).isNotEmpty();
});
this.contextRunner
@@ -185,6 +200,7 @@ public class AzureOpenAiModelConfigurationTests {
assertThat(context.getBeansOfType(AzureOpenAiEmbeddingModel.class)).isEmpty();
assertThat(context.getBeansOfType(AzureOpenAiImageModel.class)).isEmpty();
assertThat(context.getBeansOfType(AzureOpenAiAudioTranscriptionModel.class)).isNotEmpty();
assertThat(context.getBeansOfType(OpenAIClientBuilder.class)).isNotEmpty();
});
}