Improve PrompTemplate paramters handling
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user