diff --git a/spring-ai-template-st/src/main/java/org/springframework/ai/template/st/StTemplateRenderer.java b/spring-ai-template-st/src/main/java/org/springframework/ai/template/st/StTemplateRenderer.java index 91730a651..a8914ead0 100644 --- a/spring-ai-template-st/src/main/java/org/springframework/ai/template/st/StTemplateRenderer.java +++ b/spring-ai-template-st/src/main/java/org/springframework/ai/template/st/StTemplateRenderer.java @@ -24,6 +24,7 @@ import org.springframework.ai.template.TemplateRenderer; import org.springframework.ai.template.ValidationMode; import org.springframework.util.Assert; import org.stringtemplate.v4.ST; +import org.stringtemplate.v4.compiler.Compiler; import org.stringtemplate.v4.compiler.STLexer; import java.util.HashSet; @@ -48,17 +49,22 @@ public class StTemplateRenderer implements TemplateRenderer { private static final ValidationMode DEFAULT_VALIDATION_MODE = ValidationMode.THROW; + private static final boolean DEFAULT_SKIP_BUILT_IN_FUNCTIONS_VALIDATION = false; + private final char startDelimiterToken; private final char endDelimiterToken; private final ValidationMode validationMode; - StTemplateRenderer(char startDelimiterToken, char endDelimiterToken, ValidationMode validationMode) { + private final boolean skipBuiltInFunctionsValidation; + + StTemplateRenderer(char startDelimiterToken, char endDelimiterToken, ValidationMode validationMode, boolean skipBuiltInFunctionsValidation) { Assert.notNull(validationMode, "validationMode cannot be null"); this.startDelimiterToken = startDelimiterToken; this.endDelimiterToken = endDelimiterToken; this.validationMode = validationMode; + this.skipBuiltInFunctionsValidation = skipBuiltInFunctionsValidation; } @Override @@ -113,15 +119,20 @@ public class StTemplateRenderer implements TemplateRenderer { if (token.getType() == STLexer.LDELIM && i + 1 < tokens.size() && tokens.get(i + 1).getType() == STLexer.ID) { if (i + 2 < tokens.size() && tokens.get(i + 2).getType() == STLexer.COLON) { - inputVariables.add(tokens.get(i + 1).getText()); - isInsideList = true; + String text = tokens.get(i + 1).getText(); + if (!Compiler.funcs.containsKey(text) || !skipBuiltInFunctionsValidation) { + inputVariables.add(text); + isInsideList = true; + } } } else if (token.getType() == STLexer.RDELIM) { isInsideList = false; } else if (!isInsideList && token.getType() == STLexer.ID) { - inputVariables.add(token.getText()); + if (!Compiler.funcs.containsKey(token.getText()) || !skipBuiltInFunctionsValidation) { + inputVariables.add(token.getText()); + } } } @@ -140,6 +151,8 @@ public class StTemplateRenderer implements TemplateRenderer { private ValidationMode validationMode = DEFAULT_VALIDATION_MODE; + private boolean skipBuiltInFunctionsValidation = DEFAULT_SKIP_BUILT_IN_FUNCTIONS_VALIDATION; + private Builder() { } @@ -158,10 +171,15 @@ public class StTemplateRenderer implements TemplateRenderer { return this; } + public Builder skipBuiltInFunctionsValidation() { + this.skipBuiltInFunctionsValidation = true; + return this; + } + public StTemplateRenderer build() { - return new StTemplateRenderer(startDelimiterToken, endDelimiterToken, validationMode); + return new StTemplateRenderer(startDelimiterToken, endDelimiterToken, validationMode, skipBuiltInFunctionsValidation); } } -} \ No newline at end of file +} diff --git a/spring-ai-template-st/src/test/java/org/springframework/ai/template/st/StTemplateRendererTests.java b/spring-ai-template-st/src/test/java/org/springframework/ai/template/st/StTemplateRendererTests.java index b9f2316ea..de228e0a9 100644 --- a/spring-ai-template-st/src/test/java/org/springframework/ai/template/st/StTemplateRendererTests.java +++ b/spring-ai-template-st/src/test/java/org/springframework/ai/template/st/StTemplateRendererTests.java @@ -280,4 +280,20 @@ class StTemplateRendererTests { assertThat(result).isEqualTo("Person: John, Age: 30"); } -} \ No newline at end of file + /** + * Test whether StringTemplate can correctly render a template containing built-in functions + * when {@code skipBuiltInFunctionsValidation()} is enabled. It should render properly. + */ + @Test + void shouldRenderTemplateWithSkipBuiltInFunctionsValidation() { + StTemplateRenderer renderer = StTemplateRenderer.builder().skipBuiltInFunctionsValidation().build(); + Map variables = new HashMap<>(); + variables.put("memory", "you are a helpful assistant"); + String template = "{if(strlen(memory))}Hello!{endif}"; + + String result = renderer.apply(template, variables); + + assertThat(result).isEqualTo("Hello!"); + } + +}