diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/main/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekChatAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/main/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekChatAutoConfiguration.java index 23515e416f2..98aae30a232 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/main/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekChatAutoConfiguration.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/main/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekChatAutoConfiguration.java @@ -31,7 +31,6 @@ import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -50,6 +49,7 @@ * {@link AutoConfiguration Auto-configuration} for DeepSeek Chat Model. * * @author Geng Rong + * @author Hyunsang Han */ @AutoConfiguration(after = { RestClientAutoConfiguration.class, WebClientAutoConfiguration.class, SpringAiRetryAutoConfiguration.class, ToolCallingAutoConfiguration.class }) @@ -57,8 +57,6 @@ @EnableConfigurationProperties({ DeepSeekConnectionProperties.class, DeepSeekChatProperties.class }) @ConditionalOnProperty(name = SpringAIModelProperties.CHAT_MODEL, havingValue = SpringAIModels.DEEPSEEK, matchIfMissing = true) -@ImportAutoConfiguration(classes = { SpringAiRetryAutoConfiguration.class, RestClientAutoConfiguration.class, - WebClientAutoConfiguration.class, ToolCallingAutoConfiguration.class }) public class DeepSeekChatAutoConfiguration { @Bean diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/BaseDeepSeekIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/BaseDeepSeekIT.java new file mode 100644 index 00000000000..b87b54136eb --- /dev/null +++ b/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/BaseDeepSeekIT.java @@ -0,0 +1,42 @@ +/* + * Copyright 2025-2025 the original author or authors. + * + * 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 + * + * https://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 org.springframework.ai.model.deepseek.autoconfigure; + +import java.util.Arrays; +import java.util.stream.Stream; + +import org.springframework.ai.model.tool.autoconfigure.ToolCallingAutoConfiguration; +import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration; +import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration; + +/** + * Base utility class for DeepSeek integration tests. + * + * @author Hyunsang Han + */ +public abstract class BaseDeepSeekIT { + + public static AutoConfigurations deepSeekAutoConfig(Class... additional) { + Class[] dependencies = { SpringAiRetryAutoConfiguration.class, ToolCallingAutoConfiguration.class, + RestClientAutoConfiguration.class, WebClientAutoConfiguration.class }; + Class[] all = Stream.concat(Arrays.stream(dependencies), Arrays.stream(additional)).toArray(Class[]::new); + return AutoConfigurations.of(all); + } + +} \ No newline at end of file diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekAutoConfigurationIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekAutoConfigurationIT.java index 22d0678e96b..0e0c49f6e8a 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekAutoConfigurationIT.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekAutoConfigurationIT.java @@ -29,25 +29,22 @@ import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.deepseek.DeepSeekChatModel; -import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration; -import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Geng Rong + * @author Hyunsang Han */ @EnabledIfEnvironmentVariable(named = "DEEPSEEK_API_KEY", matches = ".*") -public class DeepSeekAutoConfigurationIT { +public class DeepSeekAutoConfigurationIT extends BaseDeepSeekIT { private static final Log logger = LogFactory.getLog(DeepSeekAutoConfigurationIT.class); private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withPropertyValues("spring.ai.deepseek.apiKey=" + System.getenv("DEEPSEEK_API_KEY")) - .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class, - RestClientAutoConfiguration.class, DeepSeekChatAutoConfiguration.class)); + .withConfiguration(BaseDeepSeekIT.deepSeekAutoConfig(DeepSeekChatAutoConfiguration.class)); @Test void generate() { diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekPropertiesTests.java b/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekPropertiesTests.java index 8e1d1322242..6d88c4e2688 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekPropertiesTests.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekPropertiesTests.java @@ -19,17 +19,15 @@ import org.junit.jupiter.api.Test; import org.springframework.ai.deepseek.DeepSeekChatModel; -import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration; -import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Geng Rong + * @author Hyunsang Han */ -public class DeepSeekPropertiesTests { +public class DeepSeekPropertiesTests extends BaseDeepSeekIT { @Test public void chatProperties() { @@ -41,8 +39,7 @@ public void chatProperties() { "spring.ai.deepseek.chat.options.model=MODEL_XYZ", "spring.ai.deepseek.chat.options.temperature=0.55") // @formatter:on - .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class, - RestClientAutoConfiguration.class, DeepSeekChatAutoConfiguration.class)) + .withConfiguration(deepSeekAutoConfig(DeepSeekChatAutoConfiguration.class)) .run(context -> { var chatProperties = context.getBean(DeepSeekChatProperties.class); var connectionProperties = context.getBean(DeepSeekConnectionProperties.class); @@ -70,8 +67,7 @@ public void chatOverrideConnectionProperties() { "spring.ai.deepseek.chat.options.model=MODEL_XYZ", "spring.ai.deepseek.chat.options.temperature=0.55") // @formatter:on - .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class, - RestClientAutoConfiguration.class, DeepSeekChatAutoConfiguration.class)) + .withConfiguration(deepSeekAutoConfig(DeepSeekChatAutoConfiguration.class)) .run(context -> { var chatProperties = context.getBean(DeepSeekChatProperties.class); var connectionProperties = context.getBean(DeepSeekConnectionProperties.class); @@ -108,8 +104,7 @@ public void chatOptionsTest() { "spring.ai.deepseek.chat.options.user=userXYZ" ) // @formatter:on - .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class, - RestClientAutoConfiguration.class, DeepSeekChatAutoConfiguration.class)) + .withConfiguration(deepSeekAutoConfig(DeepSeekChatAutoConfiguration.class)) .run(context -> { var chatProperties = context.getBean(DeepSeekChatProperties.class); var connectionProperties = context.getBean(DeepSeekConnectionProperties.class); @@ -132,7 +127,7 @@ void chatActivation() { new ApplicationContextRunner() .withPropertyValues("spring.ai.deepseek.api-key=API_KEY", "spring.ai.deepseek.base-url=TEST_BASE_URL", "spring.ai.model.chat=none") - .withConfiguration(AutoConfigurations.of(DeepSeekChatAutoConfiguration.class)) + .withConfiguration(deepSeekAutoConfig(DeepSeekChatAutoConfiguration.class)) .run(context -> { assertThat(context.getBeansOfType(DeepSeekChatProperties.class)).isEmpty(); assertThat(context.getBeansOfType(DeepSeekChatModel.class)).isEmpty(); @@ -140,7 +135,7 @@ void chatActivation() { new ApplicationContextRunner() .withPropertyValues("spring.ai.deepseek.api-key=API_KEY", "spring.ai.deepseek.base-url=TEST_BASE_URL") - .withConfiguration(AutoConfigurations.of(DeepSeekChatAutoConfiguration.class)) + .withConfiguration(deepSeekAutoConfig(DeepSeekChatAutoConfiguration.class)) .run(context -> { assertThat(context.getBeansOfType(DeepSeekChatProperties.class)).isNotEmpty(); assertThat(context.getBeansOfType(DeepSeekChatModel.class)).isNotEmpty(); @@ -149,7 +144,7 @@ void chatActivation() { new ApplicationContextRunner() .withPropertyValues("spring.ai.deepseek.api-key=API_KEY", "spring.ai.deepseek.base-url=TEST_BASE_URL", "spring.ai.model.chat=deepseek") - .withConfiguration(AutoConfigurations.of(DeepSeekChatAutoConfiguration.class)) + .withConfiguration(deepSeekAutoConfig(DeepSeekChatAutoConfiguration.class)) .run(context -> { assertThat(context.getBeansOfType(DeepSeekChatProperties.class)).isNotEmpty(); assertThat(context.getBeansOfType(DeepSeekChatModel.class)).isNotEmpty(); diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/tool/DeepSeekFunctionCallbackIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/tool/DeepSeekFunctionCallbackIT.java index 2a5cb68a129..3d2e63c4f84 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/tool/DeepSeekFunctionCallbackIT.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/tool/DeepSeekFunctionCallbackIT.java @@ -33,12 +33,10 @@ import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.deepseek.DeepSeekChatModel; import org.springframework.ai.deepseek.DeepSeekChatOptions; +import org.springframework.ai.model.deepseek.autoconfigure.BaseDeepSeekIT; import org.springframework.ai.model.deepseek.autoconfigure.DeepSeekChatAutoConfiguration; -import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration; import org.springframework.ai.tool.ToolCallback; import org.springframework.ai.tool.function.FunctionToolCallback; -import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -47,18 +45,18 @@ /** * @author Geng Rong + * @author Hyunsang Han */ // @Disabled("the deepseek-chat model's Function Calling capability is unstable see: // https://api-docs.deepseek.com/guides/function_calling") @EnabledIfEnvironmentVariable(named = "DEEPSEEK_API_KEY", matches = ".*") -public class DeepSeekFunctionCallbackIT { +public class DeepSeekFunctionCallbackIT extends BaseDeepSeekIT { private final Logger logger = LoggerFactory.getLogger(DeepSeekFunctionCallbackIT.class); private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withPropertyValues("spring.ai.deepseek.apiKey=" + System.getenv("DEEPSEEK_API_KEY")) - .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class, - RestClientAutoConfiguration.class, DeepSeekChatAutoConfiguration.class)) + .withConfiguration(deepSeekAutoConfig(DeepSeekChatAutoConfiguration.class)) .withUserConfiguration(Config.class); @Test diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/tool/FunctionCallbackInPromptIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/tool/FunctionCallbackInPromptIT.java index f3accd5ffa0..4ae85f607e8 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/tool/FunctionCallbackInPromptIT.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/tool/FunctionCallbackInPromptIT.java @@ -32,26 +32,27 @@ import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.deepseek.DeepSeekChatModel; import org.springframework.ai.deepseek.DeepSeekChatOptions; +import org.springframework.ai.model.deepseek.autoconfigure.BaseDeepSeekIT; import org.springframework.ai.model.deepseek.autoconfigure.DeepSeekChatAutoConfiguration; import org.springframework.ai.tool.function.FunctionToolCallback; -import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Geng Rong + * @author Hyunsang Han */ // @Disabled("the deepseek-chat model's Function Calling capability is unstable see: // https://api-docs.deepseek.com/guides/function_calling") @EnabledIfEnvironmentVariable(named = "DEEPSEEK_API_KEY", matches = ".*") -public class FunctionCallbackInPromptIT { +public class FunctionCallbackInPromptIT extends BaseDeepSeekIT { private final Logger logger = LoggerFactory.getLogger(FunctionCallbackInPromptIT.class); private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withPropertyValues("spring.ai.deepseek.apiKey=" + System.getenv("DEEPSEEK_API_KEY")) - .withConfiguration(AutoConfigurations.of(DeepSeekChatAutoConfiguration.class)); + .withConfiguration(deepSeekAutoConfig(DeepSeekChatAutoConfiguration.class)); @Test void functionCallTest() { diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java index c6fb1d8527c..0d97934ae66 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java @@ -33,9 +33,9 @@ import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.deepseek.DeepSeekChatModel; import org.springframework.ai.deepseek.DeepSeekChatOptions; +import org.springframework.ai.model.deepseek.autoconfigure.BaseDeepSeekIT; import org.springframework.ai.model.deepseek.autoconfigure.DeepSeekChatAutoConfiguration; import org.springframework.ai.model.tool.ToolCallingChatOptions; -import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -45,17 +45,18 @@ /** * @author Geng Rong + * @author Hyunsang Han */ @EnabledIfEnvironmentVariable(named = "DEEPSEEK_API_KEY", matches = ".*") // @Disabled("the deepseek-chat model's Function Calling capability is unstable see: // https://api-docs.deepseek.com/guides/function_calling") -class FunctionCallbackWithPlainFunctionBeanIT { +class FunctionCallbackWithPlainFunctionBeanIT extends BaseDeepSeekIT { private final Logger logger = LoggerFactory.getLogger(FunctionCallbackWithPlainFunctionBeanIT.class); private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withPropertyValues("spring.ai.deepseek.apiKey=" + System.getenv("DEEPSEEK_API_KEY")) - .withConfiguration(AutoConfigurations.of(DeepSeekChatAutoConfiguration.class)) + .withConfiguration(deepSeekAutoConfig(DeepSeekChatAutoConfiguration.class)) .withUserConfiguration(Config.class); @Test