Added a "skipBuiltInFunctionsValidation" option to StTemplateRenderer, which defaults to false. When enabled, it skips validation of built-in functions in the template, preventing these built-in functions from being validated as variables to be replaced.

Signed-off-by: Sun Yuhan <1085481446@qq.com>
This commit is contained in:
Sun Yuhan
2025-04-30 23:35:52 +08:00
committed by Mark Pollack
parent b39805af30
commit af29a795b0
2 changed files with 41 additions and 7 deletions

View File

@@ -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);
}
}
}
}

View File

@@ -280,4 +280,20 @@ class StTemplateRendererTests {
assertThat(result).isEqualTo("Person: John, Age: 30");
}
}
/**
* 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<String, Object> 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!");
}
}