Improve PrompTemplate paramters handling

This commit is contained in:
Christian Tzolov
2024-05-23 14:55:29 +02:00
parent b3445b45f4
commit 6c9c766da6
3 changed files with 19 additions and 105 deletions

View File

@@ -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<String> extractPlaceholders(String text) {
var placeholders = new ArrayList<String>();
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<Message> messages = new ArrayList<>();
private final Map<String, Object> userParams = new ConcurrentHashMap<>();
private final Map<String, Object> userParams = new HashMap<>();
private final Map<String, Object> systemParams = new ConcurrentHashMap<>();
private final Map<String, Object> 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<String, Object> pruneTrailingParams(String text, Map<String, Object> params) {
if (CollectionUtils.isEmpty(params)) {
return params;
}
List<String> 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<String> 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;
}

View File

@@ -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<String, Object> 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<String, Object> 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<String, Object> model) {
validate(model);
for (Entry<String, Object> 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);
}
}

View File

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