diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatClient.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatClient.java index 12749b60d..51d2ddb08 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatClient.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatClient.java @@ -24,10 +24,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; -import java.util.regex.Pattern; -import java.util.stream.Collectors; import reactor.core.publisher.Flux; @@ -60,21 +57,10 @@ import org.springframework.util.StringUtils; */ public interface ChatClient { - Pattern PLACEHOLDER_EXTRACTION_PATTER = Pattern.compile("\\{(.*?)\\}"); - - private static List extractPlaceholders(String text) { - var placeholders = new ArrayList(); - var matcher = PLACEHOLDER_EXTRACTION_PATTER.matcher(text); - while (matcher.find()) { - placeholders.add(matcher.group(1)); - } - return placeholders; + static ChatClient create(ChatModel chatModel) { + return builder(chatModel).build(); } - // static ChatClient create(ChatModel chatModel) { - // return builder(chatModel).build(); - // } - static ChatClientBuilder builder(ChatModel chatModel) { return new ChatClientBuilder(chatModel); } @@ -228,9 +214,9 @@ public interface ChatClient { private final List messages = new ArrayList<>(); - private final Map userParams = new ConcurrentHashMap<>(); + private final Map userParams = new HashMap<>(); - private final Map systemParams = new ConcurrentHashMap<>(); + private final Map systemParams = new HashMap<>(); /* copy constructor */ ChatClientRequest(ChatClientRequest ccr) { @@ -286,11 +272,6 @@ public interface ChatClient { return this; } - public ChatClientRequest chatOptions(ChatOptions chatOptions) { - this.chatOptions = chatOptions; - return this; - } - public ChatClientRequest system(String text) { this.systemText = text; return this; @@ -410,19 +391,6 @@ public interface ChatClient { } - // Hack: Prune any trailing parameters not used in the system text. - // Later will cause the ST string template to fail. - private static Map pruneTrailingParams(String text, Map params) { - if (CollectionUtils.isEmpty(params)) { - return params; - } - List paramNames = extractPlaceholders(text); - return params.entrySet() - .stream() - .filter(e -> paramNames.contains(e.getKey())) - .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())); - } - public static class CallResponseSpec { private final ChatClientRequest request; @@ -471,19 +439,15 @@ public interface ChatClient { if (textsAreValid) { UserMessage userMessage = null; if (!CollectionUtils.isEmpty(userParams)) { - userMessage = new UserMessage( - new PromptTemplate(processedUserText, - pruneTrailingParams(processedUserText, userParams)) - .render(), + userMessage = new UserMessage(new PromptTemplate(processedUserText, userParams).render(), this.request.media); } else { userMessage = new UserMessage(processedUserText, this.request.media); } if (StringUtils.hasText(this.request.systemText) || !this.request.systemParams.isEmpty()) { - var systemMessage = new SystemMessage(new PromptTemplate(this.request.systemText, - pruneTrailingParams(this.request.systemText, this.request.systemParams)) - .render()); + var systemMessage = new SystemMessage( + new PromptTemplate(this.request.systemText, this.request.systemParams).render()); messages.add(systemMessage); } messages.add(userMessage); @@ -514,13 +478,6 @@ public interface ChatClient { return doGetChatResponse(this.request.userText).getResult().getOutput().getContent(); } - public List contents() { - return doGetChatResponse(this.request.userText).getResults() - .stream() - .map(r -> r.getOutput().getContent()) - .toList(); - } - } public static class StreamResponseSpec { @@ -546,19 +503,15 @@ public interface ChatClient { if (textsAreValid) { UserMessage userMessage = null; if (!CollectionUtils.isEmpty(userParams)) { - userMessage = new UserMessage( - new PromptTemplate(processedUserText, - pruneTrailingParams(processedUserText, userParams)) - .render(), + userMessage = new UserMessage(new PromptTemplate(processedUserText, userParams).render(), this.request.media); } else { userMessage = new UserMessage(processedUserText, this.request.media); } if (StringUtils.hasText(this.request.systemText) || !this.request.systemParams.isEmpty()) { - var systemMessage = new SystemMessage(new PromptTemplate(this.request.systemText, - pruneTrailingParams(this.request.systemText, this.request.systemParams)) - .render()); + var systemMessage = new SystemMessage( + new PromptTemplate(this.request.systemText, this.request.systemParams).render()); messages.add(systemMessage); } messages.add(userMessage); @@ -625,7 +578,7 @@ public interface ChatClient { } public ChatClientBuilder defaultOptions(ChatOptions chatOptions) { - this.defaultRequest.chatOptions(chatOptions); + this.defaultRequest.options(chatOptions); return this; } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/PromptTemplate.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/PromptTemplate.java index 86990a089..f4c1a641b 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/PromptTemplate.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/PromptTemplate.java @@ -23,7 +23,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Objects; import java.util.Set; import org.antlr.runtime.Token; @@ -34,8 +33,6 @@ import org.stringtemplate.v4.compiler.STLexer; import org.springframework.ai.chat.messages.Media; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.UserMessage; -import org.springframework.ai.converter.StructuredOutputConverter; -import org.springframework.ai.parser.OutputParser; import org.springframework.core.io.Resource; import org.springframework.util.StreamUtils; @@ -49,10 +46,6 @@ public class PromptTemplate implements PromptTemplateActions, PromptTemplateMess protected TemplateFormat templateFormat = TemplateFormat.ST; - private OutputParser outputParser; - - private StructuredOutputConverter structuredOutputConverter; - public PromptTemplate(Resource resource) { try (InputStream inputStream = resource.getInputStream()) { this.template = StreamUtils.copyToString(inputStream, Charset.defaultCharset()); @@ -86,7 +79,6 @@ public class PromptTemplate implements PromptTemplateActions, PromptTemplateMess this.st = new ST(this.template, '{', '}'); for (Entry entry : model.entrySet()) { add(entry.getKey(), entry.getValue()); - dynamicModel.put(entry.getKey(), entry.getValue()); } } catch (Exception ex) { @@ -105,8 +97,7 @@ public class PromptTemplate implements PromptTemplateActions, PromptTemplateMess try { this.st = new ST(this.template, '{', '}'); for (Entry entry : model.entrySet()) { - add(entry.getKey(), entry.getValue()); - dynamicModel.put(entry.getKey(), entry.getValue()); + this.add(entry.getKey(), entry.getValue()); } } catch (Exception ex) { @@ -114,30 +105,6 @@ public class PromptTemplate implements PromptTemplateActions, PromptTemplateMess } } - /** - * @deprecated Use {@link #getOutputConverter()} instead. - */ - public OutputParser getOutputParser() { - return this.outputParser; - } - - /** - * @deprecated Use {@link #setOutputConverter(StructuredOutputConverter)} instead. - */ - public void setOutputParser(OutputParser outputParser) { - Objects.requireNonNull(outputParser, "Output Parser can not be null"); - this.outputParser = outputParser; - } - - public StructuredOutputConverter getOutputConverter() { - return this.structuredOutputConverter; - } - - public void setOutputConverter(StructuredOutputConverter structuredOutputConverter) { - Objects.requireNonNull(structuredOutputConverter, "Structured Output Converter can not be null"); - this.structuredOutputConverter = structuredOutputConverter; - } - public void add(String name, Object value) { this.st.add(name, value); this.dynamicModel.put(name, value); @@ -162,18 +129,18 @@ public class PromptTemplate implements PromptTemplateActions, PromptTemplateMess public String render(Map model) { validate(model); for (Entry entry : model.entrySet()) { - if (st.getAttribute(entry.getKey()) != null) { - st.remove(entry.getKey()); + if (this.st.getAttribute(entry.getKey()) != null) { + this.st.remove(entry.getKey()); } if (entry.getValue() instanceof Resource) { - st.add(entry.getKey(), renderResource((Resource) entry.getValue())); + this.st.add(entry.getKey(), renderResource((Resource) entry.getValue())); } else { - st.add(entry.getKey(), entry.getValue()); + this.st.add(entry.getKey(), entry.getValue()); } } - return st.render(); + return this.st.render(); } private String renderResource(Resource resource) { @@ -252,14 +219,7 @@ public class PromptTemplate implements PromptTemplateActions, PromptTemplateMess if (!modelKeys.containsAll(templateTokens)) { templateTokens.removeAll(modelKeys); throw new IllegalStateException( - "All template variables were not replaced. Missing variable names are " + templateTokens); - } - - // Check if the template references any keys not provided by the model - if (!templateTokens.containsAll(modelKeys)) { - modelKeys.removeAll(templateTokens); - throw new IllegalStateException( - "All model variables were not replaced. Missing variable names are " + modelKeys); + "Not all template variables were replaced. Missing variable names are " + templateTokens); } } diff --git a/spring-ai-core/src/test/java/org/springframework/ai/chat/ChatClientTest.java b/spring-ai-core/src/test/java/org/springframework/ai/chat/ChatClientTest.java index 02c927769..093ccbde6 100644 --- a/spring-ai-core/src/test/java/org/springframework/ai/chat/ChatClientTest.java +++ b/spring-ai-core/src/test/java/org/springframework/ai/chat/ChatClientTest.java @@ -161,6 +161,7 @@ public class ChatClientTest { assertThat(systemMessage.getContent()).isEqualTo("Default system text value1New, value2"); assertThat(systemMessage.getMessageType()).isEqualTo(MessageType.SYSTEM); + // streaming content = join(chatClient.prompt() .system(s -> s.param("param1", "value1New")) .stream().content());