From 28e54dc3c92e2d23379133afd276b9076a7312c2 Mon Sep 17 00:00:00 2001 From: "logic.wu0" <605524858@qq.com> Date: Thu, 29 Oct 2026 20:31:40 +0800 Subject: [PATCH] fix(prompt): preserve global Nacos Config setting Keep the prompt condition scoped to prompt beans and cover switch combinations, context isolation, and custom beans. Document the migration from the previous global side effect. Fixes agentic-ai-java/argi-extensions#68 --- .../argi-autoconfigure-nacos-prompt/README.md | 24 ++++ .../PromptTemplateNacosConfigCondition.java | 12 +- .../PromptTemplateAutoConfigurationTests.java | 112 +++++++++++++++++- 3 files changed, 139 insertions(+), 9 deletions(-) create mode 100644 auto-configurations/prompt/argi-autoconfigure-nacos-prompt/README.md diff --git a/auto-configurations/prompt/argi-autoconfigure-nacos-prompt/README.md b/auto-configurations/prompt/argi-autoconfigure-nacos-prompt/README.md new file mode 100644 index 000000000..4f7d048b5 --- /dev/null +++ b/auto-configurations/prompt/argi-autoconfigure-nacos-prompt/README.md @@ -0,0 +1,24 @@ +# Nacos Prompt auto-configuration + +`argi.nacos.prompt.template.enabled` controls the prompt template factory and its +builder configuration. It defaults to `false`. Set it to `true` to enable these +beans; application-provided beans still take precedence. + +`spring.nacos.config.enabled` independently controls the Nacos Config integration. +Evaluating the prompt condition does not change this property or any JVM system +property. Disabling Prompt does not disable Nacos Config used elsewhere in the +application. + +## Migration + +Previously, leaving Prompt disabled implicitly set the JVM system property +`spring.nacos.config.enabled=false`, including when the application explicitly +enabled Nacos Config. This also affected other application contexts in the JVM. + +Applications that relied on that side effect and do not need Nacos Config should +now explicitly configure `spring.nacos.config.enabled=false`. Applications that +use Nacos Config should keep their own connection configuration; disabling Prompt +must not hide Nacos Config configuration or connection failures. + +The regression tests cover the Prompt auto-configuration offline. They do not +verify connections to a live Nacos server. diff --git a/auto-configurations/prompt/argi-autoconfigure-nacos-prompt/src/main/java/io/github/agentic/ai/autoconfigure/prompt/PromptTemplateNacosConfigCondition.java b/auto-configurations/prompt/argi-autoconfigure-nacos-prompt/src/main/java/io/github/agentic/ai/autoconfigure/prompt/PromptTemplateNacosConfigCondition.java index 7eefc38fb..0a5f4decb 100644 --- a/auto-configurations/prompt/argi-autoconfigure-nacos-prompt/src/main/java/io/github/agentic/ai/autoconfigure/prompt/PromptTemplateNacosConfigCondition.java +++ b/auto-configurations/prompt/argi-autoconfigure-nacos-prompt/src/main/java/io/github/agentic/ai/autoconfigure/prompt/PromptTemplateNacosConfigCondition.java @@ -24,9 +24,12 @@ import org.springframework.core.type.AnnotatedTypeMetadata; /** + * Controls only Nacos prompt template auto-configuration. The independent + * {@code spring.nacos.config.enabled} setting is owned by the application and must not + * be changed while evaluating this condition. + * * @author yuluo - * @author yuluo Temporarily fixed the issue - * of Nacos log error during startup. + * @author yuluo */ public class PromptTemplateNacosConfigCondition implements Condition { @@ -45,11 +48,6 @@ public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) // The default value is false, means that the nacos prompt template is not enabled. Boolean enabled = context.getEnvironment().getProperty(tmplPrefix, Boolean.class, false); - // Setting NacosAutoConfiguration#enabled=false, avoid spring alibaba nacos related bean auto config. - // Causes an error to be reported in the ARGI Nacos startup log - if (!enabled) { - System.setProperty("spring.nacos.config.enabled", "false"); - } logger.debug("PromptTemplateNacosConfigCondition matches enabled: {}",enabled); // @formatter:on diff --git a/auto-configurations/prompt/argi-autoconfigure-nacos-prompt/src/test/java/io/github/agentic/ai/autoconfigure/prompt/PromptTemplateAutoConfigurationTests.java b/auto-configurations/prompt/argi-autoconfigure-nacos-prompt/src/test/java/io/github/agentic/ai/autoconfigure/prompt/PromptTemplateAutoConfigurationTests.java index a0378dbfb..f85d45da0 100644 --- a/auto-configurations/prompt/argi-autoconfigure-nacos-prompt/src/test/java/io/github/agentic/ai/autoconfigure/prompt/PromptTemplateAutoConfigurationTests.java +++ b/auto-configurations/prompt/argi-autoconfigure-nacos-prompt/src/test/java/io/github/agentic/ai/autoconfigure/prompt/PromptTemplateAutoConfigurationTests.java @@ -15,10 +15,19 @@ */ package io.github.agentic.ai.autoconfigure.prompt; +import java.util.stream.Stream; + import io.github.agentic.ai.prompt.ConfigurablePromptTemplateFactory; +import io.github.agentic.ai.prompt.PromptTemplateBuilderConfigure; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.ResourceLock; +import org.junit.jupiter.api.parallel.Resources; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; @@ -28,12 +37,111 @@ * @author yuluo */ -@SpringBootTest(classes = PromptTemplateAutoConfiguration.class) +@ResourceLock(Resources.SYSTEM_PROPERTIES) class PromptTemplateAutoConfigurationTests { + private static final String GLOBAL_ENABLED = "spring.nacos.config.enabled"; + + private static final String PROMPT_ENABLED = "argi.nacos.prompt.template.enabled"; + + private String originalGlobalEnabled; + + private String originalPromptEnabled; + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withUserConfiguration(PromptTemplateAutoConfiguration.class); + @BeforeEach + void saveSystemProperties() { + originalGlobalEnabled = System.getProperty(GLOBAL_ENABLED); + originalPromptEnabled = System.getProperty(PROMPT_ENABLED); + System.clearProperty(PROMPT_ENABLED); + } + + @AfterEach + void restoreSystemProperties() { + setSystemProperty(GLOBAL_ENABLED, originalGlobalEnabled); + setSystemProperty(PROMPT_ENABLED, originalPromptEnabled); + } + + private static void setSystemProperty(String name, String value) { + if (value == null) { + System.clearProperty(name); + } + else { + System.setProperty(name, value); + } + } + + static Stream switchCombinations() { + return Stream.of("true", "false", null) + .flatMap(prompt -> Stream.of("true", "false", null).map(global -> Arguments.of(prompt, global))); + } + + @ParameterizedTest + @MethodSource("switchCombinations") + void promptSwitchDoesNotChangeGlobalNacosConfig(String prompt, String global) { + setSystemProperty(GLOBAL_ENABLED, global); + ApplicationContextRunner runner = this.contextRunner; + if (prompt != null) { + runner = runner.withPropertyValues(PROMPT_ENABLED + "=" + prompt); + } + runner.run(context -> { + assertThat(context).hasNotFailed(); + if ("true".equals(prompt)) { + assertThat(context).hasSingleBean(ConfigurablePromptTemplateFactory.class); + assertThat(context).hasSingleBean(PromptTemplateBuilderConfigure.class); + } + else { + assertThat(context).doesNotHaveBean(ConfigurablePromptTemplateFactory.class); + assertThat(context).doesNotHaveBean(PromptTemplateBuilderConfigure.class); + } + assertThat(System.getProperty(GLOBAL_ENABLED)).isEqualTo(global); + }); + assertThat(System.getProperty(GLOBAL_ENABLED)).isEqualTo(global); + } + + @Test + void contextsWithDifferentPromptSettingsAndRecreationRemainIndependent() { + System.setProperty(GLOBAL_ENABLED, "true"); + this.contextRunner.withPropertyValues(PROMPT_ENABLED + "=true").run(enabled -> { + ConfigurablePromptTemplateFactory factory = enabled.getBean(ConfigurablePromptTemplateFactory.class); + this.contextRunner.withPropertyValues(PROMPT_ENABLED + "=false").run(disabled -> { + assertThat(disabled).doesNotHaveBean(ConfigurablePromptTemplateFactory.class); + assertThat(enabled.getBean(ConfigurablePromptTemplateFactory.class)).isSameAs(factory); + assertThat(System.getProperty(GLOBAL_ENABLED)).isEqualTo("true"); + }); + this.contextRunner.run(recreated -> { + assertThat(recreated).doesNotHaveBean(ConfigurablePromptTemplateFactory.class); + assertThat(System.getProperty(GLOBAL_ENABLED)).isEqualTo("true"); + }); + }); + this.contextRunner.withPropertyValues(PROMPT_ENABLED + "=true").run(recreated -> { + assertThat(recreated).hasSingleBean(ConfigurablePromptTemplateFactory.class); + assertThat(System.getProperty(GLOBAL_ENABLED)).isEqualTo("true"); + }); + } + + @Test + void customBeansArePreservedWithoutChangingGlobalConfig() { + System.setProperty(GLOBAL_ENABLED, "true"); + PromptTemplateBuilderConfigure configure = new PromptTemplateBuilderConfigure(); + ConfigurablePromptTemplateFactory factory = new ConfigurablePromptTemplateFactory(configure); + for (boolean enabled : new boolean[] { true, false }) { + this.contextRunner.withPropertyValues(PROMPT_ENABLED + "=" + enabled) + .withBean(PromptTemplateBuilderConfigure.class, () -> configure) + .withBean(ConfigurablePromptTemplateFactory.class, () -> factory) + .run(context -> { + assertThat(context).hasNotFailed(); + assertThat(context).hasSingleBean(PromptTemplateBuilderConfigure.class); + assertThat(context).hasSingleBean(ConfigurablePromptTemplateFactory.class); + assertThat(context.getBean(ConfigurablePromptTemplateFactory.class)).isSameAs(factory); + assertThat(context.getBean(PromptTemplateBuilderConfigure.class)).isSameAs(configure); + assertThat(System.getProperty(GLOBAL_ENABLED)).isEqualTo("true"); + }); + } + } + @Test void whenEnabledPropertyIsTrue_thenBeanShouldBeCreated() {