From ae09200114d704e24f9409824613f5969bbc1297 Mon Sep 17 00:00:00 2001 From: Sun Yuhan Date: Wed, 27 Aug 2025 20:56:51 +0800 Subject: [PATCH 1/3] feat: GH-4251 Add a `getRequiredVariables` method to `TemplateRenderer` to parse the required variables from the template. Signed-off-by: Sun Yuhan --- .../openai/chat/OpenAiCompatibleChatModelIT.java | 2 +- .../ai/template/NoOpTemplateRenderer.java | 6 ++++++ .../ai/template/TemplateRenderer.java | 4 ++++ .../ai/chat/prompt/PromptTemplateTests.java | 6 ++++++ .../ai/chat/prompt/SystemPromptTemplateTests.java | 6 ++++++ .../ai/template/st/StTemplateRenderer.java | 6 ++++++ .../ai/template/st/StTemplateRendererTests.java | 14 ++++++++++++++ 7 files changed, 43 insertions(+), 1 deletion(-) diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiCompatibleChatModelIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiCompatibleChatModelIT.java index b83db6387b6..7b11baf0db4 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiCompatibleChatModelIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiCompatibleChatModelIT.java @@ -68,7 +68,7 @@ static Stream openAiCompatibleApis() { .openAiApi(OpenAiApi.builder() .baseUrl("https://api.groq.com/openai") .apiKey(System.getenv("GROQ_API_KEY")) - .build()) + .build()) .defaultOptions(forModelName("llama3-8b-8192")) .build()); } diff --git a/spring-ai-commons/src/main/java/org/springframework/ai/template/NoOpTemplateRenderer.java b/spring-ai-commons/src/main/java/org/springframework/ai/template/NoOpTemplateRenderer.java index d4685201cf7..2af72b087ec 100644 --- a/spring-ai-commons/src/main/java/org/springframework/ai/template/NoOpTemplateRenderer.java +++ b/spring-ai-commons/src/main/java/org/springframework/ai/template/NoOpTemplateRenderer.java @@ -17,6 +17,7 @@ package org.springframework.ai.template; import java.util.Map; +import java.util.Set; import org.springframework.util.Assert; @@ -36,4 +37,9 @@ public String apply(String template, Map variables) { return template; } + @Override + public Set getRequiredVariables(String template) { + return Set.of(); + } + } diff --git a/spring-ai-commons/src/main/java/org/springframework/ai/template/TemplateRenderer.java b/spring-ai-commons/src/main/java/org/springframework/ai/template/TemplateRenderer.java index 96b199af43a..2d5a6302f73 100644 --- a/spring-ai-commons/src/main/java/org/springframework/ai/template/TemplateRenderer.java +++ b/spring-ai-commons/src/main/java/org/springframework/ai/template/TemplateRenderer.java @@ -17,12 +17,14 @@ package org.springframework.ai.template; import java.util.Map; +import java.util.Set; import java.util.function.BiFunction; /** * Renders a template using a given strategy. * * @author Thomas Vitale + * @author Sun Yuhan * @since 1.0.0 */ public interface TemplateRenderer extends BiFunction, String> { @@ -30,4 +32,6 @@ public interface TemplateRenderer extends BiFunction @Override String apply(String template, Map variables); + Set getRequiredVariables(String template); + } diff --git a/spring-ai-model/src/test/java/org/springframework/ai/chat/prompt/PromptTemplateTests.java b/spring-ai-model/src/test/java/org/springframework/ai/chat/prompt/PromptTemplateTests.java index ecd33e0317d..749b44e1122 100644 --- a/spring-ai-model/src/test/java/org/springframework/ai/chat/prompt/PromptTemplateTests.java +++ b/spring-ai-model/src/test/java/org/springframework/ai/chat/prompt/PromptTemplateTests.java @@ -18,6 +18,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Set; import org.junit.jupiter.api.Test; @@ -316,6 +317,11 @@ public String apply(String template, Map model) { return template + " (Rendered by Custom)"; } + @Override + public Set getRequiredVariables(String template) { + return Set.of(); + } + } } diff --git a/spring-ai-model/src/test/java/org/springframework/ai/chat/prompt/SystemPromptTemplateTests.java b/spring-ai-model/src/test/java/org/springframework/ai/chat/prompt/SystemPromptTemplateTests.java index d5b08064d12..0ff41511933 100644 --- a/spring-ai-model/src/test/java/org/springframework/ai/chat/prompt/SystemPromptTemplateTests.java +++ b/spring-ai-model/src/test/java/org/springframework/ai/chat/prompt/SystemPromptTemplateTests.java @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -324,6 +325,11 @@ public String apply(String template, Map model) { return template + " (Rendered by Custom)"; } + @Override + public Set getRequiredVariables(String template) { + return Set.of(); + } + } } 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 3780b948a09..33f102e03c5 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 @@ -49,6 +49,7 @@ * is shared between threads. * * @author Thomas Vitale + * @author Sun Yuhan * @since 1.0.0 */ public class StTemplateRenderer implements TemplateRenderer { @@ -110,6 +111,11 @@ public String apply(String template, Map variables) { return st.render(); } + @Override + public Set getRequiredVariables(String template) { + return getInputVariables(createST(template)); + } + private ST createST(String template) { try { return new ST(template, this.startDelimiterToken, this.endDelimiterToken); 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 1dd548c5c0e..1a8f646d3dd 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 @@ -18,6 +18,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Set; import org.junit.jupiter.api.Test; @@ -31,6 +32,7 @@ * Unit tests for {@link StTemplateRenderer}. * * @author Thomas Vitale + * @author Sun Yuhan */ class StTemplateRendererTests { @@ -297,4 +299,16 @@ void shouldRenderTemplateWithBuiltInFunctions() { assertThat(result).isEqualTo("Hello!"); } + /** + * Test whether the required variables can be correctly extracted from the template. + */ + @Test + void shouldCorrectlyExtractedRequiredVariables() { + StTemplateRenderer renderer = StTemplateRenderer.builder().build(); + String template = "Person: {name}, Age: {age}"; + Set requiredVariables = renderer.getRequiredVariables(template); + + assertThat(requiredVariables).contains("name", "age"); + } + } From 04312df2fb220f232916ce21dad958842b8c02ed Mon Sep 17 00:00:00 2001 From: Sun Yuhan Date: Thu, 28 Aug 2025 09:39:58 +0800 Subject: [PATCH 2/3] fix: Remove the redundant imports Signed-off-by: Sun Yuhan --- .../ai/chat/prompt/SystemPromptTemplateTests.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/spring-ai-model/src/test/java/org/springframework/ai/chat/prompt/SystemPromptTemplateTests.java b/spring-ai-model/src/test/java/org/springframework/ai/chat/prompt/SystemPromptTemplateTests.java index 9acb85a88d8..505ee833075 100644 --- a/spring-ai-model/src/test/java/org/springframework/ai/chat/prompt/SystemPromptTemplateTests.java +++ b/spring-ai-model/src/test/java/org/springframework/ai/chat/prompt/SystemPromptTemplateTests.java @@ -28,8 +28,6 @@ import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.Resource; -import java.util.HashMap; -import java.util.Map; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; From 9d60992f6b2f2d9943126c3a99bc69fa1829509c Mon Sep 17 00:00:00 2001 From: Sun Yuhan Date: Thu, 28 Aug 2025 10:38:02 +0800 Subject: [PATCH 3/3] fix: Fix the import order. Signed-off-by: Sun Yuhan --- .../ai/chat/prompt/SystemPromptTemplateTests.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spring-ai-model/src/test/java/org/springframework/ai/chat/prompt/SystemPromptTemplateTests.java b/spring-ai-model/src/test/java/org/springframework/ai/chat/prompt/SystemPromptTemplateTests.java index 505ee833075..359b4e6c684 100644 --- a/spring-ai-model/src/test/java/org/springframework/ai/chat/prompt/SystemPromptTemplateTests.java +++ b/spring-ai-model/src/test/java/org/springframework/ai/chat/prompt/SystemPromptTemplateTests.java @@ -18,6 +18,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Set; import org.junit.jupiter.api.Test; @@ -28,8 +29,6 @@ import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.Resource; -import java.util.Set; - import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy;