stop the pointless copying of data around and put everything in one single well-known, mutable, carrier object.

This commit is contained in:
Josh Long
2024-05-19 11:01:05 +02:00
committed by Christian Tzolov
parent c4bfb5709b
commit 9ffc315e1f
2 changed files with 45 additions and 101 deletions

View File

@@ -31,7 +31,6 @@ import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.MimeType;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import java.io.IOException;
import java.net.URL;
@@ -180,20 +179,25 @@ public interface ChatClient {
private final List<FunctionCallback> functionCallbacks = new ArrayList<>();
private final Map<String, Object> userParams = new HashMap<>();
private final List<Message> messages = new ArrayList<>();
private final Map<String, Object> userParams = new HashMap<>();
private final Map<String, Object> systemParams = new HashMap<>();
public ChatClientRequest(ModelCall connector, String userText, String systemText, List<String> functionNames,
List<Media> media, ChatOptions chatOptions) {
public ChatClientRequest(ModelCall connector, String userText, String systemText,
List<FunctionCallback> functionCallbacks, List<String> functionNames, List<Media> media,
ChatOptions chatOptions) {
this.connector = connector;
this.chatOptions = chatOptions;
this.userText = userText;
this.systemText = systemText;
this.connector = connector;
this.functionNames.addAll(functionNames);
this.functionCallbacks.addAll(functionCallbacks);
this.media.addAll(media);
this.chatOptions = chatOptions;
}
public ChatClientRequest messages(Message... messages) {
@@ -222,6 +226,11 @@ public interface ChatClient {
return this;
}
public ChatClientRequest chatOptions(ChatOptions chatOptions) {
this.chatOptions = chatOptions;
return this;
}
public ChatClientRequest system(Consumer<SystemSpec> consumer) {
var ss = new SystemSpec();
consumer.accept(ss);
@@ -270,27 +279,20 @@ public interface ChatClient {
}
private ChatResponse doGetChatResponse(String processedUserText) {
var messages = new ArrayList<Message>();
var textsAreValid = (StringUtils.hasText(processedUserText)
|| StringUtils.hasText(this.request.systemText));
var messagesAreValid = !this.request.messages.isEmpty();
Assert.state(!(messagesAreValid && textsAreValid), "you must specify either " + Message.class.getName()
+ " instances or user/system texts, but not both");
if (textsAreValid) {
var userMessage = new UserMessage(
new PromptTemplate(processedUserText, this.request.userParams).render(),
this.request.media);
var systemMessage = new SystemMessage(
new PromptTemplate(this.request.systemText, this.request.systemParams).render());
messages.add(systemMessage);
messages.add(userMessage);
}
else {
messages.addAll(this.request.messages);
@@ -311,34 +313,18 @@ public interface ChatClient {
return doGetChatResponse(this.request.userText);
}
public <T> Flux<T> stream(Class<T> t) {
notSupported();
return null;
}
public <T> Flux<T> stream(ParameterizedTypeReference<T> t) {
notSupported();
return Flux.empty();
}
public String content() {
return doGetChatResponse(this.request.userText).getResult().getOutput().getContent();
}
@SuppressWarnings("unused")
public <T> Collection<T> list(Class<T> clzz) {
// todo move to the new ParameterizedTypeReference ready
// BeanOutputConverter
notSupported();
return null;
return single(new ParameterizedTypeReference<List<T>>() {
});
}
public <T> Collection<T> list(ParameterizedTypeReference<Collection<T>> ptr) {
notSupported();
return List.of();
}
private static void notSupported() {
throw new RuntimeException("this operation is not supported");
public <T> Collection<T> list(ParameterizedTypeReference<List<T>> ptr) {
return single(ptr);
}
}
@@ -353,71 +339,42 @@ public interface ChatClient {
private final ModelCall modelCall;
private final List<Media> defaultMedia = new ArrayList<>();
private final List<String> defaultFunctionsNames = new ArrayList<>();
private final List<FunctionCallback> defaultFunctionCallbacks = new ArrayList<>();
private String defaultSystem;
private String defaultUser;
private final ChatClientRequest defaultRequest;
ChatClientBuilder(ModelCall modelCall) {
Assert.notNull(modelCall, "the " + ModelCall.class.getName() + " must be non-null");
this.modelCall = modelCall;
this.defaultRequest = new ChatClientRequest(modelCall, "", "", List.of(), List.of(), List.of(), null);
}
public ChatClient build() {
return new DefaultChatClient(this.modelCall, this.defaultSystem, this.defaultUser,
this.defaultFunctionsNames, this.defaultMedia);
return new DefaultChatClient(this.modelCall, this.defaultRequest);
}
public ChatClientBuilder defaultSystem(Resource resource) {
return this.defaultSystem(resource, Charset.defaultCharset());
}
public ChatClientBuilder defaultSystem(Resource resource, Charset charset) {
try {
this.defaultSystem = resource.getContentAsString(charset);
}
catch (IOException e) {
throw new RuntimeException(e);
}
public ChatClientBuilder defaultChatOptions(ChatOptions chatOptions) {
this.defaultRequest.chatOptions(chatOptions);
return this;
}
public ChatClientBuilder defaultSystem(String systemText) {
this.defaultSystem = systemText;
public ChatClientBuilder defaultUser(Consumer<UserSpec> userSpecConsumer) {
this.defaultRequest.user(userSpecConsumer);
return this;
}
public ChatClientBuilder defaultSystem(Consumer<SystemSpec> systemSpecConsumer) {
this.defaultRequest.system(systemSpecConsumer);
return this;
}
public <I, O> ChatClientBuilder defaultFunctionWrappers(String name, String description,
java.util.function.Function<I, O> function) {
this.defaultRequest.function(name, description, function);
return this;
}
public ChatClientBuilder defaultFunctions(String... functionNames) {
this.defaultFunctionsNames.addAll(List.of(functionNames));
return this;
}
public ChatClientBuilder defaultFunctions(List<FunctionCallback> functions) {
this.defaultFunctionCallbacks.addAll(functions);
return this;
}
public ChatClientBuilder defaultUser(Resource userText) {
return this.defaultUser(userText, Charset.defaultCharset());
}
public ChatClientBuilder defaultUser(Resource userText, Charset charset) {
try {
this.defaultUser = userText.getContentAsString(charset);
}
catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}
public ChatClientBuilder defaultUser(String userText) {
this.defaultUser = userText;
this.defaultRequest.functions(functionNames);
return this;
}

View File

@@ -1,10 +1,7 @@
package org.springframework.ai.chat;
import org.springframework.ai.chat.messages.Media;
import org.springframework.ai.chat.prompt.Prompt;
import java.util.List;
/**
* @author Mark Pollack
* @author Christian Tzolov
@@ -15,26 +12,16 @@ class DefaultChatClient implements ChatClient {
private final ModelCall modelCall;
private final String userText, systemText;
private final ChatClientRequest defaultChatClientRequest;
private final List<String> functionNames;
private final List<Media> media;
public DefaultChatClient(ModelCall modelCall, String defaultSystemPrompt, String defaultUserPrompt,
List<String> defaultFunctions, List<Media> defaultMedia) {
public DefaultChatClient(ModelCall modelCall, ChatClientRequest defaultChatClientRequest) {
this.modelCall = modelCall;
this.userText = defaultUserPrompt;
this.systemText = defaultSystemPrompt;
this.functionNames = defaultFunctions;
this.media = defaultMedia;
this.defaultChatClientRequest = defaultChatClientRequest;
}
@Override
public ChatClientRequest call() {
return new ChatClientRequest(this.modelCall, this.userText, this.systemText, this.functionNames, this.media,
null);
return this.defaultChatClientRequest;
}
/**