Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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 <a href="mailto:yuluo08290126@gmail.com">yuluo</a> Temporarily fixed the issue
* of Nacos log error during startup.
* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>
*/

public class PromptTemplateNacosConfigCondition implements Condition {
Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -28,12 +37,111 @@
* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>
*/

@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<Arguments> 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() {

Expand Down
Loading