Add ChatCaller#getDefultOptions() breaking the funcitonal contract.
- Implement fromOptions copty factory for eavery chatoptions implementation. - Extend ChatClient to use the caller default options if not provided explicitely. - Fix system/user text overdidign default system/user texts. Only non empty user/system text can overrid defult system/user text. - Rename chat() method to collect(). - Add OpenAI FunctionCallbackWrapper2IT auto-config tests.
This commit is contained in:
@@ -223,4 +223,17 @@ public class AnthropicChatOptions implements ChatOptions, FunctionCallingOptions
|
||||
this.functions = functions;
|
||||
}
|
||||
|
||||
public static AnthropicChatOptions fromOptions(AnthropicChatOptions fromOptions) {
|
||||
return builder().withModel(fromOptions.getModel())
|
||||
.withMaxTokens(fromOptions.getMaxTokens())
|
||||
.withMetadata(fromOptions.getMetadata())
|
||||
.withStopSequences(fromOptions.getStopSequences())
|
||||
.withTemperature(fromOptions.getTemperature())
|
||||
.withTopP(fromOptions.getTopP())
|
||||
.withTopK(fromOptions.getTopK())
|
||||
.withFunctionCallbacks(fromOptions.getFunctionCallbacks())
|
||||
.withFunctions(fromOptions.getFunctions())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -457,4 +457,9 @@ public class AnthropicModelCaller extends
|
||||
"Streaming (stream=true) is not yet supported. We plan to add streaming support in a future beta version.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return AnthropicChatOptions.fromOptions(this.defaultOptions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -356,4 +356,22 @@ public class AzureOpenAiChatOptions implements FunctionCallingOptions, ChatOptio
|
||||
this.functions = functions;
|
||||
}
|
||||
|
||||
public static AzureOpenAiChatOptions fromOptions(AzureOpenAiChatOptions fromOptions) {
|
||||
return builder().withDeploymentName(fromOptions.getDeploymentName())
|
||||
.withFrequencyPenalty(
|
||||
fromOptions.getFrequencyPenalty() != null ? fromOptions.getFrequencyPenalty().floatValue() : null)
|
||||
.withLogitBias(fromOptions.getLogitBias())
|
||||
.withMaxTokens(fromOptions.getMaxTokens())
|
||||
.withN(fromOptions.getN())
|
||||
.withPresencePenalty(
|
||||
fromOptions.getPresencePenalty() != null ? fromOptions.getPresencePenalty().floatValue() : null)
|
||||
.withStop(fromOptions.getStop())
|
||||
.withTemperature(fromOptions.getTemperature())
|
||||
.withTopP(fromOptions.getTopP())
|
||||
.withUser(fromOptions.getUser())
|
||||
.withFunctionCallbacks(fromOptions.getFunctionCallbacks())
|
||||
.withFunctions(fromOptions.getFunctions())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ public class AzureOpenAiModelCaller
|
||||
}
|
||||
|
||||
public AzureOpenAiChatOptions getDefaultOptions() {
|
||||
return this.defaultOptions;
|
||||
return AzureOpenAiChatOptions.fromOptions(this.defaultOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -164,4 +164,14 @@ public class AnthropicChatOptions implements ChatOptions {
|
||||
this.anthropicVersion = anthropicVersion;
|
||||
}
|
||||
|
||||
public static AnthropicChatOptions fromOptions(AnthropicChatOptions fromOptions) {
|
||||
return builder().withTemperature(fromOptions.getTemperature())
|
||||
.withMaxTokensToSample(fromOptions.getMaxTokensToSample())
|
||||
.withTopK(fromOptions.getTopK())
|
||||
.withTopP(fromOptions.getTopP())
|
||||
.withStopSequences(fromOptions.getStopSequences())
|
||||
.withAnthropicVersion(fromOptions.getAnthropicVersion())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -117,4 +117,9 @@ public class BedrockAnthropicModelCaller implements ChatCaller, StreamingChatCal
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return AnthropicChatOptions.fromOptions(this.defaultOptions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -163,4 +163,14 @@ public class Anthropic3ChatOptions implements ChatOptions {
|
||||
this.anthropicVersion = anthropicVersion;
|
||||
}
|
||||
|
||||
public static Anthropic3ChatOptions fromOptions(Anthropic3ChatOptions fromOptions) {
|
||||
return builder().withTemperature(fromOptions.getTemperature())
|
||||
.withMaxTokens(fromOptions.getMaxTokens())
|
||||
.withTopK(fromOptions.getTopK())
|
||||
.withTopP(fromOptions.getTopP())
|
||||
.withStopSequences(fromOptions.getStopSequences())
|
||||
.withAnthropicVersion(fromOptions.getAnthropicVersion())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.ai.bedrock.anthropic3;
|
||||
|
||||
import org.springframework.ai.bedrock.anthropic.AnthropicChatOptions;
|
||||
import org.springframework.ai.bedrock.anthropic3.api.Anthropic3ChatBedrockApi;
|
||||
import org.springframework.ai.bedrock.anthropic3.api.Anthropic3ChatBedrockApi.AnthropicChatRequest;
|
||||
import org.springframework.ai.bedrock.anthropic3.api.Anthropic3ChatBedrockApi.AnthropicChatResponse;
|
||||
@@ -187,4 +188,9 @@ public class BedrockAnthropic3ModelCaller implements ChatCaller, StreamingChatCa
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return Anthropic3ChatOptions.fromOptions(this.defaultOptions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -213,4 +213,17 @@ public class BedrockCohereChatOptions implements ChatOptions {
|
||||
this.truncate = truncate;
|
||||
}
|
||||
|
||||
public static BedrockCohereChatOptions fromOptions(BedrockCohereChatOptions fromOptions) {
|
||||
return builder().withTemperature(fromOptions.getTemperature())
|
||||
.withTopP(fromOptions.getTopP())
|
||||
.withTopK(fromOptions.getTopK())
|
||||
.withMaxTokens(fromOptions.getMaxTokens())
|
||||
.withStopSequences(fromOptions.getStopSequences())
|
||||
.withReturnLikelihoods(fromOptions.getReturnLikelihoods())
|
||||
.withNumGenerations(fromOptions.getNumGenerations())
|
||||
.withLogitBias(fromOptions.getLogitBias())
|
||||
.withTruncate(fromOptions.getTruncate())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -114,4 +114,9 @@ public class BedrockCohereModelCaller implements ChatCaller, StreamingChatCaller
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return BedrockCohereChatOptions.fromOptions(this.defaultOptions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -413,4 +413,19 @@ public class BedrockAi21Jurassic2ChatOptions implements ChatOptions {
|
||||
}
|
||||
}
|
||||
|
||||
public static BedrockAi21Jurassic2ChatOptions fromOptions(BedrockAi21Jurassic2ChatOptions fromOptions) {
|
||||
return builder().withPrompt(fromOptions.getPrompt())
|
||||
.withNumResults(fromOptions.getNumResults())
|
||||
.withMaxTokens(fromOptions.getMaxTokens())
|
||||
.withMinTokens(fromOptions.getMinTokens())
|
||||
.withTemperature(fromOptions.getTemperature())
|
||||
.withTopP(fromOptions.getTopP())
|
||||
.withTopK(fromOptions.getTopK())
|
||||
.withStopSequences(fromOptions.getStopSequences())
|
||||
.withFrequencyPenalty(fromOptions.getFrequencyPenalty())
|
||||
.withPresencePenalty(fromOptions.getPresencePenalty())
|
||||
.withCountPenalty(fromOptions.getCountPenalty())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -121,4 +121,9 @@ public class BedrockAi21Jurassic2ModelCaller implements ChatCaller {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return BedrockAi21Jurassic2ChatOptions.fromOptions(this.defaultOptions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -109,4 +109,11 @@ public class BedrockLlamaChatOptions implements ChatOptions {
|
||||
throw new UnsupportedOperationException("Unsupported option: 'TopK'");
|
||||
}
|
||||
|
||||
public static BedrockLlamaChatOptions fromOptions(BedrockLlamaChatOptions fromOptions) {
|
||||
return builder().withTemperature(fromOptions.getTemperature())
|
||||
.withTopP(fromOptions.getTopP())
|
||||
.withMaxGenLen(fromOptions.getMaxGenLen())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -130,4 +130,9 @@ public class BedrockLlamaModelCaller implements ChatCaller, StreamingChatCaller
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return BedrockLlamaChatOptions.fromOptions(this.defaultOptions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -128,4 +128,12 @@ public class BedrockTitanChatOptions implements ChatOptions {
|
||||
throw new UnsupportedOperationException("Bedrock Titan Chat does not support the 'TopK' option.'");
|
||||
}
|
||||
|
||||
public static BedrockTitanChatOptions fromOptions(BedrockTitanChatOptions fromOptions) {
|
||||
return builder().withTemperature(fromOptions.getTemperature())
|
||||
.withTopP(fromOptions.getTopP())
|
||||
.withMaxTokenCount(fromOptions.getMaxTokenCount())
|
||||
.withStopSequences(fromOptions.getStopSequences())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -146,4 +146,9 @@ public class BedrockTitanModelCaller implements ChatCaller, StreamingChatCaller
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return BedrockTitanChatOptions.fromOptions(this.defaultOptions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ import org.springframework.ai.huggingface.model.AllOfGenerateResponseDetails;
|
||||
import org.springframework.ai.huggingface.model.GenerateParameters;
|
||||
import org.springframework.ai.huggingface.model.GenerateRequest;
|
||||
import org.springframework.ai.huggingface.model.GenerateResponse;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.prompt.ChatOptionsBuilder;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
|
||||
/**
|
||||
@@ -120,4 +122,9 @@ public class HuggingfaceModelCaller implements ChatCaller {
|
||||
this.maxNewTokens = maxNewTokens;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return ChatOptionsBuilder.builder().build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -58,11 +58,11 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
* @see MiniMaxApi
|
||||
* @since 1.0.0 M1
|
||||
*/
|
||||
public class MiniMaxChatClient extends
|
||||
public class MiniMaxChatCaller extends
|
||||
AbstractFunctionCallSupport<MiniMaxApi.ChatCompletionMessage, MiniMaxApi.ChatCompletionRequest, ResponseEntity<MiniMaxApi.ChatCompletion>>
|
||||
implements ChatCaller, StreamingChatCaller {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MiniMaxChatClient.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(MiniMaxChatCaller.class);
|
||||
|
||||
/**
|
||||
* The default options used for the chat completion requests.
|
||||
@@ -85,7 +85,7 @@ public class MiniMaxChatClient extends
|
||||
* MiniMax Chat API.
|
||||
* @throws IllegalArgumentException if MiniMaxApi is null
|
||||
*/
|
||||
public MiniMaxChatClient(MiniMaxApi miniMaxApi) {
|
||||
public MiniMaxChatCaller(MiniMaxApi miniMaxApi) {
|
||||
this(miniMaxApi,
|
||||
MiniMaxChatOptions.builder().withModel(MiniMaxApi.DEFAULT_CHAT_MODEL).withTemperature(0.7f).build());
|
||||
}
|
||||
@@ -96,7 +96,7 @@ public class MiniMaxChatClient extends
|
||||
* MiniMax Chat API.
|
||||
* @param options The MiniMaxChatOptions to configure the chat client.
|
||||
*/
|
||||
public MiniMaxChatClient(MiniMaxApi miniMaxApi, MiniMaxChatOptions options) {
|
||||
public MiniMaxChatCaller(MiniMaxApi miniMaxApi, MiniMaxChatOptions options) {
|
||||
this(miniMaxApi, options, null, RetryUtils.DEFAULT_RETRY_TEMPLATE);
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ public class MiniMaxChatClient extends
|
||||
* @param functionCallbackContext The function callback context.
|
||||
* @param retryTemplate The retry template.
|
||||
*/
|
||||
public MiniMaxChatClient(MiniMaxApi miniMaxApi, MiniMaxChatOptions options,
|
||||
public MiniMaxChatCaller(MiniMaxApi miniMaxApi, MiniMaxChatOptions options,
|
||||
FunctionCallbackContext functionCallbackContext, RetryTemplate retryTemplate) {
|
||||
super(functionCallbackContext);
|
||||
Assert.notNull(miniMaxApi, "MiniMaxApi must not be null");
|
||||
@@ -281,22 +281,6 @@ public class MiniMaxChatClient extends
|
||||
return request;
|
||||
}
|
||||
|
||||
private String fromMediaData(MimeType mimeType, Object mediaContentData) {
|
||||
if (mediaContentData instanceof byte[] bytes) {
|
||||
// Assume the bytes are an image. So, convert the bytes to a base64 encoded
|
||||
// following the prefix pattern.
|
||||
return String.format("data:%s;base64,%s", mimeType.toString(), Base64.getEncoder().encodeToString(bytes));
|
||||
}
|
||||
else if (mediaContentData instanceof String text) {
|
||||
// Assume the text is a URLs or a base64 encoded image prefixed by the user.
|
||||
return text;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(
|
||||
"Unsupported media data type: " + mediaContentData.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
private List<MiniMaxApi.FunctionTool> getFunctionTools(Set<String> functionNames) {
|
||||
return this.resolveFunctionCallbacks(functionNames).stream().map(functionCallback -> {
|
||||
var function = new MiniMaxApi.FunctionTool.Function(functionCallback.getDescription(),
|
||||
@@ -376,4 +360,9 @@ public class MiniMaxChatClient extends
|
||||
&& choice.finishReason() == MiniMaxApi.ChatCompletionFinishReason.TOOL_CALLS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return MiniMaxChatOptions.fromOptions(this.defaultOptions);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -467,4 +467,22 @@ public class MiniMaxChatOptions implements FunctionCallingOptions, ChatOptions {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static MiniMaxChatOptions fromOptions(MiniMaxChatOptions fromOptions) {
|
||||
return builder().withModel(fromOptions.getModel())
|
||||
.withFrequencyPenalty(fromOptions.getFrequencyPenalty())
|
||||
.withMaxTokens(fromOptions.getMaxTokens())
|
||||
.withN(fromOptions.getN())
|
||||
.withPresencePenalty(fromOptions.getPresencePenalty())
|
||||
.withResponseFormat(fromOptions.getResponseFormat())
|
||||
.withSeed(fromOptions.getSeed())
|
||||
.withStop(fromOptions.getStop())
|
||||
.withTemperature(fromOptions.getTemperature())
|
||||
.withTopP(fromOptions.getTopP())
|
||||
.withTools(fromOptions.getTools())
|
||||
.withToolChoice(fromOptions.getToolChoice())
|
||||
.withFunctionCallbacks(fromOptions.getFunctionCallbacks())
|
||||
.withFunctions(fromOptions.getFunctions())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public class ChatCompletionRequestTests {
|
||||
@Test
|
||||
public void createRequestWithChatOptions() {
|
||||
|
||||
var client = new MiniMaxChatClient(new MiniMaxApi("TEST"),
|
||||
var client = new MiniMaxChatCaller(new MiniMaxApi("TEST"),
|
||||
MiniMaxChatOptions.builder().withModel("DEFAULT_MODEL").withTemperature(66.6f).build());
|
||||
|
||||
var request = client.createRequest(new Prompt("Test message content"), false);
|
||||
@@ -59,7 +59,7 @@ public class ChatCompletionRequestTests {
|
||||
|
||||
final String TOOL_FUNCTION_NAME = "CurrentWeather";
|
||||
|
||||
var client = new MiniMaxChatClient(new MiniMaxApi("TEST"),
|
||||
var client = new MiniMaxChatCaller(new MiniMaxApi("TEST"),
|
||||
MiniMaxChatOptions.builder().withModel("DEFAULT_MODEL").build());
|
||||
|
||||
var request = client.createRequest(new Prompt("Test message content",
|
||||
@@ -89,7 +89,7 @@ public class ChatCompletionRequestTests {
|
||||
|
||||
final String TOOL_FUNCTION_NAME = "CurrentWeather";
|
||||
|
||||
var client = new MiniMaxChatClient(new MiniMaxApi("TEST"),
|
||||
var client = new MiniMaxChatCaller(new MiniMaxApi("TEST"),
|
||||
MiniMaxChatOptions.builder()
|
||||
.withModel("DEFAULT_MODEL")
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
|
||||
@@ -42,8 +42,8 @@ public class MiniMaxTestConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MiniMaxChatClient miniMaxChatClient(MiniMaxApi api) {
|
||||
return new MiniMaxChatClient(api);
|
||||
public MiniMaxChatCaller miniMaxChatClient(MiniMaxApi api) {
|
||||
return new MiniMaxChatCaller(api);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.document.MetadataMode;
|
||||
import org.springframework.ai.minimax.MiniMaxChatClient;
|
||||
import org.springframework.ai.minimax.MiniMaxChatCaller;
|
||||
import org.springframework.ai.minimax.MiniMaxChatOptions;
|
||||
import org.springframework.ai.minimax.MiniMaxEmbeddingClient;
|
||||
import org.springframework.ai.minimax.MiniMaxEmbeddingOptions;
|
||||
@@ -83,7 +83,7 @@ public class MiniMaxRetryTests {
|
||||
|
||||
private @Mock MiniMaxApi miniMaxApi;
|
||||
|
||||
private MiniMaxChatClient chatClient;
|
||||
private MiniMaxChatCaller chatClient;
|
||||
|
||||
private MiniMaxEmbeddingClient embeddingClient;
|
||||
|
||||
@@ -93,7 +93,7 @@ public class MiniMaxRetryTests {
|
||||
retryListener = new TestRetryListener();
|
||||
retryTemplate.registerListener(retryListener);
|
||||
|
||||
chatClient = new MiniMaxChatClient(miniMaxApi, MiniMaxChatOptions.builder().build(), null, retryTemplate);
|
||||
chatClient = new MiniMaxChatCaller(miniMaxApi, MiniMaxChatOptions.builder().build(), null, retryTemplate);
|
||||
embeddingClient = new MiniMaxEmbeddingClient(miniMaxApi, MetadataMode.EMBED,
|
||||
MiniMaxEmbeddingOptions.builder().build(), retryTemplate);
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ public class MistralAiChatOptions implements FunctionCallingOptions, ChatOptions
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withMaxToken(Integer maxTokens) {
|
||||
public Builder withMaxTokens(Integer maxTokens) {
|
||||
this.options.setMaxTokens(maxTokens);
|
||||
return this;
|
||||
}
|
||||
@@ -309,4 +309,19 @@ public class MistralAiChatOptions implements FunctionCallingOptions, ChatOptions
|
||||
this.functions = functions;
|
||||
}
|
||||
|
||||
public static MistralAiChatOptions fromOptions(MistralAiChatOptions fromOptions) {
|
||||
return builder().withModel(fromOptions.getModel())
|
||||
.withMaxTokens(fromOptions.getMaxTokens())
|
||||
.withSafePrompt(fromOptions.getSafePrompt())
|
||||
.withRandomSeed(fromOptions.getRandomSeed())
|
||||
.withTemperature(fromOptions.getTemperature())
|
||||
.withTopP(fromOptions.getTopP())
|
||||
.withResponseFormat(fromOptions.getResponseFormat())
|
||||
.withTools(fromOptions.getTools())
|
||||
.withToolChoice(fromOptions.getToolChoice())
|
||||
.withFunctionCallbacks(fromOptions.getFunctionCallbacks())
|
||||
.withFunctions(fromOptions.getFunctions())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -324,4 +324,9 @@ public class MistralAiModelCaller extends
|
||||
return !CollectionUtils.isEmpty(choices.get(0).message().toolCalls());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return MistralAiChatOptions.fromOptions(this.defaultOptions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -205,4 +205,9 @@ public class OllamaModelCaller implements ChatCaller, StreamingChatCaller {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return OllamaOptions.fromOptions(this.defaultOptions);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -714,6 +714,43 @@ public class OllamaOptions implements ChatOptions, EmbeddingOptions {
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
}
|
||||
|
||||
public static OllamaOptions fromOptions(OllamaOptions fromOptions) {
|
||||
return new OllamaOptions()
|
||||
.withModel(fromOptions.getModel())
|
||||
.withFormat(fromOptions.getFormat())
|
||||
.withKeepAlive(fromOptions.getKeepAlive())
|
||||
.withUseNUMA(fromOptions.getUseNUMA())
|
||||
.withNumCtx(fromOptions.getNumCtx())
|
||||
.withNumBatch(fromOptions.getNumBatch())
|
||||
.withNumGQA(fromOptions.getNumGQA())
|
||||
.withNumGPU(fromOptions.getNumGPU())
|
||||
.withMainGPU(fromOptions.getMainGPU())
|
||||
.withLowVRAM(fromOptions.getLowVRAM())
|
||||
.withF16KV(fromOptions.getF16KV())
|
||||
.withLogitsAll(fromOptions.getLogitsAll())
|
||||
.withVocabOnly(fromOptions.getVocabOnly())
|
||||
.withUseMMap(fromOptions.getUseMMap())
|
||||
.withUseMLock(fromOptions.getUseMLock())
|
||||
.withNumThread(fromOptions.getNumThread())
|
||||
.withNumKeep(fromOptions.getNumKeep())
|
||||
.withSeed(fromOptions.getSeed())
|
||||
.withNumPredict(fromOptions.getNumPredict())
|
||||
.withTopK(fromOptions.getTopK())
|
||||
.withTopP(fromOptions.getTopP())
|
||||
.withTfsZ(fromOptions.getTfsZ())
|
||||
.withTypicalP(fromOptions.getTypicalP())
|
||||
.withRepeatLastN(fromOptions.getRepeatLastN())
|
||||
.withTemperature(fromOptions.getTemperature())
|
||||
.withRepeatPenalty(fromOptions.getRepeatPenalty())
|
||||
.withPresencePenalty(fromOptions.getPresencePenalty())
|
||||
.withFrequencyPenalty(fromOptions.getFrequencyPenalty())
|
||||
.withMirostat(fromOptions.getMirostat())
|
||||
.withMirostatTau(fromOptions.getMirostatTau())
|
||||
.withMirostatEta(fromOptions.getMirostatEta())
|
||||
.withPenalizeNewline(fromOptions.getPenalizeNewline())
|
||||
.withStop(fromOptions.getStop());
|
||||
}
|
||||
|
||||
|
||||
// @formatter:on
|
||||
|
||||
|
||||
@@ -567,4 +567,27 @@ public class OpenAiChatOptions implements FunctionCallingOptions, ChatOptions {
|
||||
throw new UnsupportedOperationException("Unimplemented method 'setTopK'");
|
||||
}
|
||||
|
||||
public static OpenAiChatOptions fromOptions(OpenAiChatOptions fromOptions) {
|
||||
return OpenAiChatOptions.builder()
|
||||
.withModel(fromOptions.getModel())
|
||||
.withFrequencyPenalty(fromOptions.getFrequencyPenalty())
|
||||
.withLogitBias(fromOptions.getLogitBias())
|
||||
.withLogprobs(fromOptions.getLogprobs())
|
||||
.withTopLogprobs(fromOptions.getTopLogprobs())
|
||||
.withMaxTokens(fromOptions.getMaxTokens())
|
||||
.withN(fromOptions.getN())
|
||||
.withPresencePenalty(fromOptions.getPresencePenalty())
|
||||
.withResponseFormat(fromOptions.getResponseFormat())
|
||||
.withSeed(fromOptions.getSeed())
|
||||
.withStop(fromOptions.getStop())
|
||||
.withTemperature(fromOptions.getTemperature())
|
||||
.withTopP(fromOptions.getTopP())
|
||||
.withTools(fromOptions.getTools())
|
||||
.withToolChoice(fromOptions.getToolChoice())
|
||||
.withUser(fromOptions.getUser())
|
||||
.withFunctionCallbacks(fromOptions.getFunctionCallbacks())
|
||||
.withFunctions(fromOptions.getFunctions())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -394,4 +394,9 @@ public class OpenAiModelCaller extends
|
||||
&& choice.finishReason() == ChatCompletionFinishReason.TOOL_CALLS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return OpenAiChatOptions.fromOptions(this.defaultOptions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -62,10 +62,13 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
@Test
|
||||
void roleTest() {
|
||||
|
||||
ChatResponse response = ChatClient.builder(modelCaller).build().call()
|
||||
.system(s -> s.text(systemTextResource).param("name", "Bob").param("voice", "pirate"))
|
||||
.user(u -> u.text("Tell me about 3 famous pirates from the Golden Age of Piracy and what they did"))
|
||||
.chat().chatResponse();
|
||||
ChatResponse response = ChatClient.builder(modelCaller)
|
||||
.build()
|
||||
.call()
|
||||
.system(s -> s.text(systemTextResource).param("name", "Bob").param("voice", "pirate"))
|
||||
.user(u -> u.text("Tell me about 3 famous pirates from the Golden Age of Piracy and what they did"))
|
||||
.collect()
|
||||
.chatResponse();
|
||||
|
||||
System.out.println(response);
|
||||
// UserMessage userMessage = new UserMessage(
|
||||
@@ -86,9 +89,12 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
void listOutputConverter() {
|
||||
|
||||
// TODO: there is a problem here.
|
||||
Collection<String> list = ChatClient.builder(modelCaller).build().call()
|
||||
.user(u -> u.text("List five {subject}").param("subject", "ice cream flavors"))
|
||||
.chat().list(String.class);
|
||||
Collection<String> list = ChatClient.builder(modelCaller)
|
||||
.build()
|
||||
.call()
|
||||
.user(u -> u.text("List five {subject}").param("subject", "ice cream flavors"))
|
||||
.collect()
|
||||
.list(String.class);
|
||||
|
||||
// DefaultConversionService conversionService = new DefaultConversionService();
|
||||
// ListOutputConverter outputConverter = new
|
||||
@@ -113,11 +119,14 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
@Test
|
||||
void mapOutputConverter() {
|
||||
|
||||
Map<String, Object> result = ChatClient.builder(modelCaller).build().call()
|
||||
.user(u -> u.text("Provide me a List of {subject}")
|
||||
.param("subject", "an array of numbers from 1 to 9 under they key name 'numbers'"))
|
||||
.chat().single(new ParameterizedTypeReference<Map<String, Object>>() {
|
||||
});
|
||||
Map<String, Object> result = ChatClient.builder(modelCaller)
|
||||
.build()
|
||||
.call()
|
||||
.user(u -> u.text("Provide me a List of {subject}")
|
||||
.param("subject", "an array of numbers from 1 to 9 under they key name 'numbers'"))
|
||||
.collect()
|
||||
.single(new ParameterizedTypeReference<Map<String, Object>>() {
|
||||
});
|
||||
|
||||
// MapOutputConverter outputConverter = new MapOutputConverter();
|
||||
|
||||
@@ -140,9 +149,12 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
@Test
|
||||
void beanOutputConverter() {
|
||||
|
||||
ActorsFilms actorsFilms = ChatClient.builder(modelCaller).build().call()
|
||||
.user(u -> u.text("Generate the filmography for a random actor."))
|
||||
.chat().single(ActorsFilms.class);
|
||||
ActorsFilms actorsFilms = ChatClient.builder(modelCaller)
|
||||
.build()
|
||||
.call()
|
||||
.user(u -> u.text("Generate the filmography for a random actor."))
|
||||
.collect()
|
||||
.single(ActorsFilms.class);
|
||||
|
||||
// BeanOutputConverter<ActorsFilms> outputConverter = new
|
||||
// BeanOutputConverter<>(ActorsFilms.class);
|
||||
@@ -170,9 +182,11 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
void beanOutputConverterRecords() {
|
||||
|
||||
ActorsFilmsRecord actorsFilms = ChatClient.builder(modelCaller)
|
||||
.build().call()
|
||||
.user(u -> u.text("Generate the filmography of 5 movies for Tom Hanks."))
|
||||
.chat().single(ActorsFilmsRecord.class);
|
||||
.build()
|
||||
.call()
|
||||
.user(u -> u.text("Generate the filmography of 5 movies for Tom Hanks."))
|
||||
.collect()
|
||||
.single(ActorsFilmsRecord.class);
|
||||
|
||||
// BeanOutputConverter<ActorsFilmsRecord> outputConverter = new
|
||||
// BeanOutputConverter<>(ActorsFilmsRecord.class);
|
||||
@@ -199,31 +213,38 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
|
||||
BeanOutputConverter<ActorsFilmsRecord> outputConverter = new BeanOutputConverter<>(ActorsFilmsRecord.class);
|
||||
|
||||
Flux<ChatResponse> chatResponse = ChatClient.builder(modelCaller).build()
|
||||
.call().user(u -> u.text("Generate the filmography of 5 movies for Tom Hanks. " + System.lineSeparator() + "{format}")
|
||||
.param("format", outputConverter.getFormat()))
|
||||
.stream().chatResponse();
|
||||
Flux<ChatResponse> chatResponse = ChatClient.builder(modelCaller)
|
||||
.build()
|
||||
.call()
|
||||
.user(u -> u
|
||||
.text("Generate the filmography of 5 movies for Tom Hanks. " + System.lineSeparator() + "{format}")
|
||||
.param("format", outputConverter.getFormat()))
|
||||
.stream()
|
||||
.chatResponse();
|
||||
|
||||
String generationTextFromStream = chatResponse.collectList().block()
|
||||
.stream()
|
||||
.map(ChatResponse::getResult)
|
||||
.map(Generation::getOutput)
|
||||
.map(AssistantMessage::getContent)
|
||||
.collect(Collectors.joining());
|
||||
String generationTextFromStream = chatResponse.collectList()
|
||||
.block()
|
||||
.stream()
|
||||
.map(ChatResponse::getResult)
|
||||
.map(Generation::getOutput)
|
||||
.map(AssistantMessage::getContent)
|
||||
.collect(Collectors.joining());
|
||||
|
||||
// String generationTextFromStream = chatResponse.collectList()
|
||||
// .block()
|
||||
// .stream()
|
||||
// .collect(Collectors.joining());
|
||||
|
||||
// BeanOutputConverter<ActorsFilmsRecord> outputConverter = new BeanOutputConverter<>(ActorsFilmsRecord.class);
|
||||
// BeanOutputConverter<ActorsFilmsRecord> outputConverter = new
|
||||
// BeanOutputConverter<>(ActorsFilmsRecord.class);
|
||||
|
||||
// String format = outputConverter.getFormat();
|
||||
// String template = """
|
||||
// Generate the filmography of 5 movies for Tom Hanks.
|
||||
// {format}
|
||||
// """;
|
||||
// PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
// PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format",
|
||||
// format));
|
||||
// Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
|
||||
// String generationTextFromStream = streamingChatClient.stream(prompt)
|
||||
@@ -245,13 +266,13 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
@Test
|
||||
void functionCallTest() {
|
||||
|
||||
ChatResponse response = ChatClient.builder(modelCaller).build().call()
|
||||
.user(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris?"))
|
||||
// TODO how to use the protable function calling options internally.
|
||||
// Perhaps the ModelCaller a emptyOptions() method needs to be provided.
|
||||
.options(OpenAiChatOptions.builder().build())
|
||||
.function("getCurrentWeather", "Get the weather in location", new MockWeatherService())
|
||||
.chat().chatResponse();
|
||||
ChatResponse response = ChatClient.builder(modelCaller)
|
||||
.build()
|
||||
.call()
|
||||
.user(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris?"))
|
||||
.function("getCurrentWeather", "Get the weather in location", new MockWeatherService())
|
||||
.collect()
|
||||
.chatResponse();
|
||||
|
||||
// UserMessage userMessage = new UserMessage("What's the weather like in San
|
||||
// Francisco, Tokyo, and Paris?");
|
||||
@@ -280,37 +301,40 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
@Test
|
||||
void streamFunctionCallTest() {
|
||||
|
||||
Flux<ChatResponse> response = ChatClient.builder(modelCaller).build().call()
|
||||
.user(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris?"))
|
||||
// TODO how to use the protable function calling options internally.
|
||||
// Perhaps the ModelCaller a emptyOptions() method needs to be provided.
|
||||
.options(OpenAiChatOptions.builder().build())
|
||||
.function("getCurrentWeather", "Get the weather in location", new MockWeatherService())
|
||||
.stream().chatResponse();
|
||||
Flux<ChatResponse> response = ChatClient.builder(modelCaller)
|
||||
.build()
|
||||
.call()
|
||||
.user(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris?"))
|
||||
.function("getCurrentWeather", "Get the weather in location", new MockWeatherService())
|
||||
.stream()
|
||||
.chatResponse();
|
||||
|
||||
// UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
|
||||
// UserMessage userMessage = new UserMessage("What's the weather like in San
|
||||
// Francisco, Tokyo, and Paris?");
|
||||
|
||||
// List<Message> messages = new ArrayList<>(List.of(userMessage));
|
||||
|
||||
// var promptOptions = OpenAiChatOptions.builder()
|
||||
// // .withModel(OpenAiApi.ChatModel.GPT_4_TURBO_PREVIEW.getValue())
|
||||
// .withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
// .withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new
|
||||
// MockWeatherService())
|
||||
// .withName("getCurrentWeather")
|
||||
// .withDescription("Get the weather in location")
|
||||
// .withResponseConverter((response) -> "" + response.temp() + response.unit())
|
||||
// .build()))
|
||||
// .build();
|
||||
|
||||
// Flux<ChatResponse> response = streamingChatClient.stream(new Prompt(messages, promptOptions));
|
||||
// Flux<ChatResponse> response = streamingChatClient.stream(new Prompt(messages,
|
||||
// promptOptions));
|
||||
|
||||
String content = response.collectList()
|
||||
.block()
|
||||
.stream()
|
||||
.map(ChatResponse::getResults)
|
||||
.flatMap(List::stream)
|
||||
.map(Generation::getOutput)
|
||||
.map(AssistantMessage::getContent)
|
||||
.collect(Collectors.joining());
|
||||
.block()
|
||||
.stream()
|
||||
.map(ChatResponse::getResults)
|
||||
.flatMap(List::stream)
|
||||
.map(Generation::getOutput)
|
||||
.map(AssistantMessage::getContent)
|
||||
.collect(Collectors.joining());
|
||||
logger.info("Response: {}", content);
|
||||
|
||||
assertThat(content).containsAnyOf("30.0", "30");
|
||||
@@ -323,15 +347,15 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
void multiModalityEmbeddedImage(String modelName) throws IOException {
|
||||
|
||||
ChatResponse response = ChatClient.builder(modelCaller)
|
||||
.build()
|
||||
.call()
|
||||
// TODO consider adding model(...) method to ChatClient as a shortcut to
|
||||
// OpenAiChatOptions.builder().withModel(modelName).build()
|
||||
.options(OpenAiChatOptions.builder().withModel(modelName).build())
|
||||
.user(u -> u.text("Explain what do you see on this picture?")
|
||||
.media(MimeTypeUtils.IMAGE_PNG, new ClassPathResource("/test.png")))
|
||||
.chat()
|
||||
.chatResponse();
|
||||
.build()
|
||||
.call()
|
||||
// TODO consider adding model(...) method to ChatClient as a shortcut to
|
||||
// OpenAiChatOptions.builder().withModel(modelName).build()
|
||||
.options(OpenAiChatOptions.builder().withModel(modelName).build())
|
||||
.user(u -> u.text("Explain what do you see on this picture?")
|
||||
.media(MimeTypeUtils.IMAGE_PNG, new ClassPathResource("/test.png")))
|
||||
.collect()
|
||||
.chatResponse();
|
||||
|
||||
// var imageData = new ClassPathResource("/test.png");
|
||||
|
||||
@@ -354,13 +378,15 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
// TODO: add url method that wrapps the checked exception.
|
||||
URL url = new URL("https://docs.spring.io/spring-ai/reference/1.0-SNAPSHOT/_images/multimodal.test.png");
|
||||
|
||||
ChatResponse response = ChatClient.builder(modelCaller).build().call()
|
||||
// TODO consider adding model(...) method to ChatClient as a shortcut to
|
||||
// OpenAiChatOptions.builder().withModel(modelName).build()
|
||||
.options(OpenAiChatOptions.builder().withModel(modelName).build())
|
||||
.user(u -> u.text("Explain what do you see on this picture?")
|
||||
.media(MimeTypeUtils.IMAGE_PNG, url))
|
||||
.chat().chatResponse();
|
||||
ChatResponse response = ChatClient.builder(modelCaller)
|
||||
.build()
|
||||
.call()
|
||||
// TODO consider adding model(...) method to ChatClient as a shortcut to
|
||||
// OpenAiChatOptions.builder().withModel(modelName).build()
|
||||
.options(OpenAiChatOptions.builder().withModel(modelName).build())
|
||||
.user(u -> u.text("Explain what do you see on this picture?").media(MimeTypeUtils.IMAGE_PNG, url))
|
||||
.collect()
|
||||
.chatResponse();
|
||||
|
||||
// var userMessage = new UserMessage("Explain what do you see on this picture?",
|
||||
// List
|
||||
@@ -383,30 +409,34 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
// TODO: add url method that wrapps the checked exception.
|
||||
URL url = new URL("https://docs.spring.io/spring-ai/reference/1.0-SNAPSHOT/_images/multimodal.test.png");
|
||||
|
||||
Flux<ChatResponse> response = ChatClient.builder(modelCaller).build().call()
|
||||
// TODO consider adding model(...) method to ChatClient as a shortcut to
|
||||
// OpenAiChatOptions.builder().withModel(modelName).build()
|
||||
.options(OpenAiChatOptions.builder().withModel(OpenAiApi.ChatModel.GPT_4_VISION_PREVIEW.getValue())
|
||||
.build())
|
||||
.user(u -> u.text("Explain what do you see on this picture?")
|
||||
.media(MimeTypeUtils.IMAGE_PNG, url))
|
||||
.stream().chatResponse();
|
||||
Flux<ChatResponse> response = ChatClient.builder(modelCaller)
|
||||
.build()
|
||||
.call()
|
||||
// TODO consider adding model(...) method to ChatClient as a shortcut to
|
||||
// OpenAiChatOptions.builder().withModel(modelName).build()
|
||||
.options(OpenAiChatOptions.builder().withModel(OpenAiApi.ChatModel.GPT_4_VISION_PREVIEW.getValue()).build())
|
||||
.user(u -> u.text("Explain what do you see on this picture?").media(MimeTypeUtils.IMAGE_PNG, url))
|
||||
.stream()
|
||||
.chatResponse();
|
||||
|
||||
// var userMessage = new UserMessage("Explain what do you see on this picture?", List
|
||||
// var userMessage = new UserMessage("Explain what do you see on this picture?",
|
||||
// List
|
||||
// .of(new Media(MimeTypeUtils.IMAGE_PNG,
|
||||
// new URL("https://docs.spring.io/spring-ai/reference/1.0-SNAPSHOT/_images/multimodal.test.png"))));
|
||||
// new
|
||||
// URL("https://docs.spring.io/spring-ai/reference/1.0-SNAPSHOT/_images/multimodal.test.png"))));
|
||||
|
||||
// Flux<ChatResponse> response = streamingChatClient.stream(new Prompt(List.of(userMessage),
|
||||
// Flux<ChatResponse> response = streamingChatClient.stream(new
|
||||
// Prompt(List.of(userMessage),
|
||||
// OpenAiChatOptions.builder().withModel(OpenAiApi.ChatModel.GPT_4_VISION_PREVIEW.getValue()).build()));
|
||||
|
||||
String content = response.collectList()
|
||||
.block()
|
||||
.stream()
|
||||
.map(ChatResponse::getResults)
|
||||
.flatMap(List::stream)
|
||||
.map(Generation::getOutput)
|
||||
.map(AssistantMessage::getContent)
|
||||
.collect(Collectors.joining());
|
||||
.block()
|
||||
.stream()
|
||||
.map(ChatResponse::getResults)
|
||||
.flatMap(List::stream)
|
||||
.map(Generation::getOutput)
|
||||
.map(AssistantMessage::getContent)
|
||||
.collect(Collectors.joining());
|
||||
logger.info("Response: {}", content);
|
||||
assertThat(content).contains("bananas", "apple");
|
||||
assertThat(content).containsAnyOf("bowl", "basket");
|
||||
|
||||
@@ -336,4 +336,18 @@ public class VertexAiGeminiChatOptions implements FunctionCallingOptions, ChatOp
|
||||
return true;
|
||||
}
|
||||
|
||||
public static VertexAiGeminiChatOptions fromOptions(VertexAiGeminiChatOptions fromOptions) {
|
||||
VertexAiGeminiChatOptions options = new VertexAiGeminiChatOptions();
|
||||
options.setStopSequences(fromOptions.getStopSequences());
|
||||
options.setTemperature(fromOptions.getTemperature());
|
||||
options.setTopP(fromOptions.getTopP());
|
||||
options.setTopK(fromOptions.getTopK());
|
||||
options.setCandidateCount(fromOptions.getCandidateCount());
|
||||
options.setMaxOutputTokens(fromOptions.getMaxOutputTokens());
|
||||
options.setModel(fromOptions.getModel());
|
||||
options.setFunctionCallbacks(fromOptions.getFunctionCallbacks());
|
||||
options.setFunctions(fromOptions.getFunctions());
|
||||
return options;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -478,4 +478,9 @@ public class VertexAiGeminiModelCaller
|
||||
return response.getCandidatesList().get(0).getContent().getPartsList().get(0).hasFunctionCall();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return VertexAiGeminiChatOptions.fromOptions(this.defaultOptions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -127,4 +127,13 @@ public class VertexAiPaLm2ChatOptions implements ChatOptions {
|
||||
this.topK = topK;
|
||||
}
|
||||
|
||||
public static VertexAiPaLm2ChatOptions fromOptions(VertexAiPaLm2ChatOptions fromOptions) {
|
||||
return VertexAiPaLm2ChatOptions.builder()
|
||||
.withTemperature(fromOptions.getTemperature())
|
||||
.withCandidateCount(fromOptions.getCandidateCount())
|
||||
.withTopP(fromOptions.getTopP())
|
||||
.withTopK(fromOptions.getTopK())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -111,4 +111,9 @@ public class VertexAiPaLm2ModelCaller implements ChatCaller {
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return VertexAiPaLm2ChatOptions.fromOptions(this.defaultOptions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -324,5 +324,21 @@ public class WatsonxAiChatOptions implements ChatOptions {
|
||||
return input != null ? input.replaceAll("([a-z])([A-Z]+)", "$1_$2").toLowerCase() : null;
|
||||
}
|
||||
|
||||
public static WatsonxAiChatOptions fromOptions(WatsonxAiChatOptions fromOptions) {
|
||||
return WatsonxAiChatOptions.builder()
|
||||
.withTemperature(fromOptions.getTemperature())
|
||||
.withTopP(fromOptions.getTopP())
|
||||
.withTopK(fromOptions.getTopK())
|
||||
.withDecodingMethod(fromOptions.getDecodingMethod())
|
||||
.withMaxNewTokens(fromOptions.getMaxNewTokens())
|
||||
.withMinNewTokens(fromOptions.getMinNewTokens())
|
||||
.withStopSequences(fromOptions.getStopSequences())
|
||||
.withRepetitionPenalty(fromOptions.getRepetitionPenalty())
|
||||
.withRandomSeed(fromOptions.getRandomSeed())
|
||||
.withModel(fromOptions.getModel())
|
||||
.withAdditionalProperties(fromOptions.getAdditionalProperties())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
// @formatter:on
|
||||
@@ -140,4 +140,9 @@ public class WatsonxAiModelCaller implements ChatCaller, StreamingChatCaller {
|
||||
return WatsonxAiRequest.builder(convertedPrompt).withParameters(parameters).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return WatsonxAiChatOptions.fromOptions(this.defaultOptions);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -66,11 +66,11 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
* @see ZhiPuAiApi
|
||||
* @since 1.0.0 M1
|
||||
*/
|
||||
public class ZhiPuAiChatClient extends
|
||||
public class ZhiPuAiChatCaller extends
|
||||
AbstractFunctionCallSupport<ChatCompletionMessage, ZhiPuAiApi.ChatCompletionRequest, ResponseEntity<ChatCompletion>>
|
||||
implements ChatCaller, StreamingChatCaller {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ZhiPuAiChatClient.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(ZhiPuAiChatCaller.class);
|
||||
|
||||
/**
|
||||
* The default options used for the chat completion requests.
|
||||
@@ -93,7 +93,7 @@ public class ZhiPuAiChatClient extends
|
||||
* ZhiPuAI Chat API.
|
||||
* @throws IllegalArgumentException if zhiPuAiApi is null
|
||||
*/
|
||||
public ZhiPuAiChatClient(ZhiPuAiApi zhiPuAiApi) {
|
||||
public ZhiPuAiChatCaller(ZhiPuAiApi zhiPuAiApi) {
|
||||
this(zhiPuAiApi,
|
||||
ZhiPuAiChatOptions.builder().withModel(ZhiPuAiApi.DEFAULT_CHAT_MODEL).withTemperature(0.7f).build());
|
||||
}
|
||||
@@ -104,7 +104,7 @@ public class ZhiPuAiChatClient extends
|
||||
* ZhiPuAI Chat API.
|
||||
* @param options The ZhiPuAiChatOptions to configure the chat client.
|
||||
*/
|
||||
public ZhiPuAiChatClient(ZhiPuAiApi zhiPuAiApi, ZhiPuAiChatOptions options) {
|
||||
public ZhiPuAiChatCaller(ZhiPuAiApi zhiPuAiApi, ZhiPuAiChatOptions options) {
|
||||
this(zhiPuAiApi, options, null, RetryUtils.DEFAULT_RETRY_TEMPLATE);
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ public class ZhiPuAiChatClient extends
|
||||
* @param functionCallbackContext The function callback context.
|
||||
* @param retryTemplate The retry template.
|
||||
*/
|
||||
public ZhiPuAiChatClient(ZhiPuAiApi zhiPuAiApi, ZhiPuAiChatOptions options,
|
||||
public ZhiPuAiChatCaller(ZhiPuAiApi zhiPuAiApi, ZhiPuAiChatOptions options,
|
||||
FunctionCallbackContext functionCallbackContext, RetryTemplate retryTemplate) {
|
||||
super(functionCallbackContext);
|
||||
Assert.notNull(zhiPuAiApi, "ZhiPuAiApi must not be null");
|
||||
@@ -381,4 +381,9 @@ public class ZhiPuAiChatClient extends
|
||||
&& choice.finishReason() == ChatCompletionFinishReason.TOOL_CALLS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return ZhiPuAiChatOptions.fromOptions(this.defaultOptions);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -490,4 +490,24 @@ public class ZhiPuAiChatOptions implements FunctionCallingOptions, ChatOptions {
|
||||
throw new UnsupportedOperationException("Unimplemented method 'setTopK'");
|
||||
}
|
||||
|
||||
public static ZhiPuAiChatOptions fromOptions(ZhiPuAiChatOptions fromOptions) {
|
||||
return ZhiPuAiChatOptions.builder()
|
||||
.withModel(fromOptions.getModel())
|
||||
.withFrequencyPenalty(fromOptions.getFrequencyPenalty())
|
||||
.withMaxTokens(fromOptions.getMaxTokens())
|
||||
.withN(fromOptions.getN())
|
||||
.withPresencePenalty(fromOptions.getPresencePenalty())
|
||||
.withResponseFormat(fromOptions.getResponseFormat())
|
||||
.withSeed(fromOptions.getSeed())
|
||||
.withStop(fromOptions.getStop())
|
||||
.withTemperature(fromOptions.getTemperature())
|
||||
.withTopP(fromOptions.getTopP())
|
||||
.withTools(fromOptions.getTools())
|
||||
.withToolChoice(fromOptions.getToolChoice())
|
||||
.withUser(fromOptions.getUser())
|
||||
.withFunctionCallbacks(fromOptions.getFunctionCallbacks())
|
||||
.withFunctions(fromOptions.getFunctions())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public class ChatCompletionRequestTests {
|
||||
@Test
|
||||
public void createRequestWithChatOptions() {
|
||||
|
||||
var client = new ZhiPuAiChatClient(new ZhiPuAiApi("TEST"),
|
||||
var client = new ZhiPuAiChatCaller(new ZhiPuAiApi("TEST"),
|
||||
ZhiPuAiChatOptions.builder().withModel("DEFAULT_MODEL").withTemperature(66.6f).build());
|
||||
|
||||
var request = client.createRequest(new Prompt("Test message content"), false);
|
||||
@@ -59,7 +59,7 @@ public class ChatCompletionRequestTests {
|
||||
|
||||
final String TOOL_FUNCTION_NAME = "CurrentWeather";
|
||||
|
||||
var client = new ZhiPuAiChatClient(new ZhiPuAiApi("TEST"),
|
||||
var client = new ZhiPuAiChatCaller(new ZhiPuAiApi("TEST"),
|
||||
ZhiPuAiChatOptions.builder().withModel("DEFAULT_MODEL").build());
|
||||
|
||||
var request = client.createRequest(new Prompt("Test message content",
|
||||
@@ -89,7 +89,7 @@ public class ChatCompletionRequestTests {
|
||||
|
||||
final String TOOL_FUNCTION_NAME = "CurrentWeather";
|
||||
|
||||
var client = new ZhiPuAiChatClient(new ZhiPuAiApi("TEST"),
|
||||
var client = new ZhiPuAiChatCaller(new ZhiPuAiApi("TEST"),
|
||||
ZhiPuAiChatOptions.builder()
|
||||
.withModel("DEFAULT_MODEL")
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
|
||||
@@ -48,8 +48,8 @@ public class ZhiPuAiTestConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ZhiPuAiChatClient zhiPuAiChatClient(ZhiPuAiApi api) {
|
||||
return new ZhiPuAiChatClient(api);
|
||||
public ZhiPuAiChatCaller zhiPuAiChatClient(ZhiPuAiApi api) {
|
||||
return new ZhiPuAiChatCaller(api);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.springframework.ai.image.ImageMessage;
|
||||
import org.springframework.ai.image.ImagePrompt;
|
||||
import org.springframework.ai.retry.RetryUtils;
|
||||
import org.springframework.ai.retry.TransientAiException;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatClient;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatCaller;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatOptions;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiEmbeddingClient;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiEmbeddingOptions;
|
||||
@@ -93,7 +93,7 @@ public class ZhiPuAiRetryTests {
|
||||
|
||||
private @Mock ZhiPuAiImageApi zhiPuAiImageApi;
|
||||
|
||||
private ZhiPuAiChatClient chatClient;
|
||||
private ZhiPuAiChatCaller chatClient;
|
||||
|
||||
private ZhiPuAiEmbeddingClient embeddingClient;
|
||||
|
||||
@@ -105,7 +105,7 @@ public class ZhiPuAiRetryTests {
|
||||
retryListener = new TestRetryListener();
|
||||
retryTemplate.registerListener(retryListener);
|
||||
|
||||
chatClient = new ZhiPuAiChatClient(zhiPuAiApi, ZhiPuAiChatOptions.builder().build(), null, retryTemplate);
|
||||
chatClient = new ZhiPuAiChatCaller(zhiPuAiApi, ZhiPuAiChatOptions.builder().build(), null, retryTemplate);
|
||||
embeddingClient = new ZhiPuAiEmbeddingClient(zhiPuAiApi, MetadataMode.EMBED,
|
||||
ZhiPuAiEmbeddingOptions.builder().build(), retryTemplate);
|
||||
imageClient = new ZhiPuAiImageClient(zhiPuAiImageApi, ZhiPuAiImageOptions.builder().build(), retryTemplate);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.ai.chat;
|
||||
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -23,7 +24,7 @@ import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.model.ModelClient;
|
||||
|
||||
@FunctionalInterface
|
||||
// @FunctionalInterface
|
||||
public interface ChatCaller extends ModelClient<Prompt, ChatResponse> {
|
||||
|
||||
default String call(String message) {
|
||||
@@ -41,4 +42,6 @@ public interface ChatCaller extends ModelClient<Prompt, ChatResponse> {
|
||||
@Override
|
||||
ChatResponse call(Prompt prompt);
|
||||
|
||||
ChatOptions getDefaultOptions();
|
||||
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import java.util.function.Consumer;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.ai.chat.messages.AssistantMessage;
|
||||
import org.springframework.ai.chat.messages.Media;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.SystemMessage;
|
||||
@@ -51,7 +50,7 @@ import org.springframework.util.StringUtils;
|
||||
// todo support plugging in a outputConverter at runtime
|
||||
// todo figure out stream and list methods
|
||||
|
||||
/*
|
||||
/**
|
||||
* @author Mark Pollack
|
||||
* @author Christian Tzolov
|
||||
* @author Josh Long
|
||||
@@ -206,7 +205,7 @@ public interface ChatClient {
|
||||
ChatOptions chatOptions) {
|
||||
|
||||
this.caller = caller;
|
||||
this.chatOptions = chatOptions;
|
||||
this.chatOptions = chatOptions != null ? chatOptions : caller.getDefaultOptions();
|
||||
|
||||
this.userText = userText;
|
||||
this.systemText = systemText;
|
||||
@@ -229,10 +228,10 @@ public interface ChatClient {
|
||||
public <I, O> ChatClientRequest function(String name, String description,
|
||||
java.util.function.Function<I, O> function) {
|
||||
var fcw = FunctionCallbackWrapper.builder(function)
|
||||
.withDescription(description)
|
||||
.withName(name)
|
||||
.withResponseConverter(Object::toString)
|
||||
.build();
|
||||
.withDescription(description)
|
||||
.withName(name)
|
||||
.withResponseConverter(Object::toString)
|
||||
.build();
|
||||
this.functionCallbacks.add(fcw);
|
||||
return this;
|
||||
}
|
||||
@@ -250,27 +249,32 @@ public interface ChatClient {
|
||||
public ChatClientRequest system(Consumer<SystemSpec> consumer) {
|
||||
var ss = new SystemSpec();
|
||||
consumer.accept(ss);
|
||||
this.systemText = ss.text();
|
||||
this.systemText = StringUtils.hasText(ss.text()) ? ss.text() : this.systemText;
|
||||
this.systemParams.putAll(ss.params());
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChatClientRequest user(String text) {
|
||||
this.userText = text;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChatClientRequest user(Consumer<UserSpec> consumer) {
|
||||
var us = new UserSpec();
|
||||
consumer.accept(us);
|
||||
this.userText = us.text();
|
||||
this.userText = StringUtils.hasText(us.text()) ? us.text() : this.userText;
|
||||
this.userParams.putAll(us.params());
|
||||
this.media.addAll(us.media());
|
||||
return this;
|
||||
}
|
||||
|
||||
public static class ChatResponseSpec {
|
||||
public static class CollectResponseSpec {
|
||||
|
||||
private final ChatClientRequest request;
|
||||
|
||||
private final ChatCaller modelCaller;
|
||||
|
||||
public ChatResponseSpec(ChatCaller modelCaller, ChatClientRequest request) {
|
||||
public CollectResponseSpec(ChatCaller modelCaller, ChatClientRequest request) {
|
||||
this.modelCaller = modelCaller;
|
||||
this.request = request;
|
||||
}
|
||||
@@ -353,8 +357,10 @@ public interface ChatClient {
|
||||
}
|
||||
|
||||
public List<String> contents() {
|
||||
return doGetChatResponse(this.request.userText).getResults().stream()
|
||||
.map(r -> r.getOutput().getContent()).toList();
|
||||
return doGetChatResponse(this.request.userText).getResults()
|
||||
.stream()
|
||||
.map(r -> r.getOutput().getContent())
|
||||
.toList();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@@ -369,24 +375,27 @@ public interface ChatClient {
|
||||
|
||||
}
|
||||
|
||||
public static class ChatStreamResponseSpec {
|
||||
public static class StreamResponseSpec {
|
||||
|
||||
private final ChatClientRequest request;
|
||||
|
||||
private final StreamingChatCaller modelCaller;
|
||||
|
||||
public ChatStreamResponseSpec(StreamingChatCaller modelCaller, ChatClientRequest request) {
|
||||
public StreamResponseSpec(StreamingChatCaller modelCaller, ChatClientRequest request) {
|
||||
this.modelCaller = modelCaller;
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
// public <T> Flux<T> single(ParameterizedTypeReference<T> t) {
|
||||
// return doSingleWithBeanOutputConverter(new BeanOutputConverter<T>(new ParameterizedTypeReference<>() {
|
||||
// return doSingleWithBeanOutputConverter(new BeanOutputConverter<T>(new
|
||||
// ParameterizedTypeReference<>() {
|
||||
// }));
|
||||
// }
|
||||
|
||||
// private <T> Flux<T> doSingleWithBeanOutputConverter(BeanOutputConverter<T> boc) {
|
||||
// var processedUserText = this.request.userText + System.lineSeparator() + System.lineSeparator()
|
||||
// private <T> Flux<T> doSingleWithBeanOutputConverter(BeanOutputConverter<T>
|
||||
// boc) {
|
||||
// var processedUserText = this.request.userText + System.lineSeparator() +
|
||||
// System.lineSeparator()
|
||||
// + "{format}";
|
||||
// var chatResponse = doGetChatResponse(processedUserText, boc.getFormat());
|
||||
// var stringResponse = chatResponse.getResult().getOutput().getContent();
|
||||
@@ -399,15 +408,13 @@ public interface ChatClient {
|
||||
// return doSingleWithBeanOutputConverter(boc);
|
||||
// }
|
||||
|
||||
private Flux<ChatResponse> doGetChatResponse(String processedUserText) {
|
||||
return this.doGetChatResponse(processedUserText, "");
|
||||
}
|
||||
// private Flux<ChatResponse> doGetFluxChatResponse(String processedUserText)
|
||||
// {
|
||||
// return this.doGetFluxChatResponse(processedUserText, "");
|
||||
// }
|
||||
|
||||
private Flux<ChatResponse> doGetChatResponse(String processedUserText, String formatParam) {
|
||||
private Flux<ChatResponse> doGetFluxChatResponse(String processedUserText) {
|
||||
Map<String, Object> userParams = new HashMap<>(this.request.userParams);
|
||||
if (StringUtils.hasText(formatParam)) {
|
||||
userParams.put("format", formatParam);
|
||||
}
|
||||
|
||||
var messages = new ArrayList<Message>();
|
||||
var textsAreValid = (StringUtils.hasText(processedUserText)
|
||||
@@ -450,21 +457,22 @@ public interface ChatClient {
|
||||
}
|
||||
|
||||
public Flux<ChatResponse> chatResponse() {
|
||||
return doGetChatResponse(this.request.userText);
|
||||
return doGetFluxChatResponse(this.request.userText);
|
||||
}
|
||||
|
||||
public Flux<String> content() {
|
||||
return doGetChatResponse(this.request.userText)
|
||||
.map(ChatResponse::getResult)
|
||||
.map(Generation::getOutput)
|
||||
.map(AssistantMessage::getContent);
|
||||
// .map(r -> r.getResult().getOutput().getContent())
|
||||
// .filter(v -> StringUtils.hasText(v));
|
||||
}
|
||||
|
||||
public Flux<List<String>> contents() {
|
||||
return doGetChatResponse(this.request.userText).map(r -> r.getResults().stream()
|
||||
.map(rr -> rr.getOutput().getContent()).toList());
|
||||
return doGetFluxChatResponse(this.request.userText)
|
||||
// .map(ChatResponse::getResult)
|
||||
// .map(Generation::getOutput)
|
||||
// .map(AssistantMessage::getContent);
|
||||
.map(r -> {
|
||||
if (r.getResult() == null || r.getResult().getOutput() == null
|
||||
|| r.getResult().getOutput().getContent() == null) {
|
||||
return "";
|
||||
}
|
||||
return r.getResult().getOutput().getContent();
|
||||
})
|
||||
.filter(v -> StringUtils.hasText(v));
|
||||
}
|
||||
|
||||
// @SuppressWarnings("unused")
|
||||
@@ -479,12 +487,12 @@ public interface ChatClient {
|
||||
|
||||
}
|
||||
|
||||
public ChatResponseSpec chat() {
|
||||
return new ChatResponseSpec(this.caller, this);
|
||||
public CollectResponseSpec collect() {
|
||||
return new CollectResponseSpec(this.caller, this);
|
||||
}
|
||||
|
||||
public ChatStreamResponseSpec stream() {
|
||||
return new ChatStreamResponseSpec((StreamingChatCaller) this.caller, this);
|
||||
public StreamResponseSpec stream() {
|
||||
return new StreamResponseSpec((StreamingChatCaller) this.caller, this);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -505,7 +513,7 @@ public interface ChatClient {
|
||||
return new DefaultChatClient(this.modelCaller, this.defaultRequest);
|
||||
}
|
||||
|
||||
public ChatClientBuilder defaultChatOptions(ChatOptions chatOptions) {
|
||||
public ChatClientBuilder defaultRuntimeOptions(ChatOptions chatOptions) {
|
||||
this.defaultRequest.chatOptions(chatOptions);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.ai.chat;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -25,8 +24,6 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
/**
|
||||
@@ -51,7 +48,7 @@ public class Main {
|
||||
|
||||
String response = client.call()
|
||||
.user(u -> u.text("User text {music}").param("music", "Rock").media(MimeTypeUtils.IMAGE_PNG, url))
|
||||
.chat()
|
||||
.collect()
|
||||
.single(String.class);
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
package org.springframework.ai.autoconfigure.minimax;
|
||||
|
||||
import org.springframework.ai.autoconfigure.retry.SpringAiRetryAutoConfiguration;
|
||||
import org.springframework.ai.minimax.MiniMaxChatClient;
|
||||
import org.springframework.ai.minimax.MiniMaxChatCaller;
|
||||
import org.springframework.ai.minimax.MiniMaxEmbeddingClient;
|
||||
import org.springframework.ai.minimax.api.MiniMaxApi;
|
||||
import org.springframework.ai.model.function.FunctionCallback;
|
||||
@@ -51,7 +51,7 @@ public class MiniMaxAutoConfiguration {
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(prefix = MiniMaxChatProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
|
||||
matchIfMissing = true)
|
||||
public MiniMaxChatClient miniMaxChatClient(MiniMaxConnectionProperties commonProperties,
|
||||
public MiniMaxChatCaller miniMaxChatClient(MiniMaxConnectionProperties commonProperties,
|
||||
MiniMaxChatProperties chatProperties, RestClient.Builder restClientBuilder,
|
||||
List<FunctionCallback> toolFunctionCallbacks, FunctionCallbackContext functionCallbackContext,
|
||||
RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler) {
|
||||
@@ -63,7 +63,7 @@ public class MiniMaxAutoConfiguration {
|
||||
chatProperties.getOptions().getFunctionCallbacks().addAll(toolFunctionCallbacks);
|
||||
}
|
||||
|
||||
return new MiniMaxChatClient(miniMaxApi, chatProperties.getOptions(), functionCallbackContext, retryTemplate);
|
||||
return new MiniMaxChatCaller(miniMaxApi, chatProperties.getOptions(), functionCallbackContext, retryTemplate);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -18,7 +18,7 @@ package org.springframework.ai.autoconfigure.zhipuai;
|
||||
import org.springframework.ai.autoconfigure.retry.SpringAiRetryAutoConfiguration;
|
||||
import org.springframework.ai.model.function.FunctionCallback;
|
||||
import org.springframework.ai.model.function.FunctionCallbackContext;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatClient;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatCaller;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiEmbeddingClient;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiImageClient;
|
||||
import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
|
||||
@@ -53,7 +53,7 @@ public class ZhiPuAiAutoConfiguration {
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(prefix = ZhiPuAiChatProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
|
||||
matchIfMissing = true)
|
||||
public ZhiPuAiChatClient zhiPuAiChatClient(ZhiPuAiConnectionProperties commonProperties,
|
||||
public ZhiPuAiChatCaller zhiPuAiChatClient(ZhiPuAiConnectionProperties commonProperties,
|
||||
ZhiPuAiChatProperties chatProperties, RestClient.Builder restClientBuilder,
|
||||
List<FunctionCallback> toolFunctionCallbacks, FunctionCallbackContext functionCallbackContext,
|
||||
RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler) {
|
||||
@@ -65,7 +65,7 @@ public class ZhiPuAiAutoConfiguration {
|
||||
chatProperties.getOptions().getFunctionCallbacks().addAll(toolFunctionCallbacks);
|
||||
}
|
||||
|
||||
return new ZhiPuAiChatClient(zhiPuAiApi, chatProperties.getOptions(), functionCallbackContext, retryTemplate);
|
||||
return new ZhiPuAiChatCaller(zhiPuAiApi, chatProperties.getOptions(), functionCallbackContext, retryTemplate);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.messages.AssistantMessage;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.minimax.MiniMaxChatClient;
|
||||
import org.springframework.ai.minimax.MiniMaxChatCaller;
|
||||
import org.springframework.ai.minimax.MiniMaxChatOptions;
|
||||
import org.springframework.ai.model.function.FunctionCallbackWrapper;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
@@ -55,7 +55,7 @@ public class FunctionCallbackInPromptIT {
|
||||
void functionCallTest() {
|
||||
contextRunner.withPropertyValues("spring.ai.minimax.chat.options.model=abab6-chat").run(context -> {
|
||||
|
||||
MiniMaxChatClient chatClient = context.getBean(MiniMaxChatClient.class);
|
||||
MiniMaxChatCaller chatClient = context.getBean(MiniMaxChatCaller.class);
|
||||
|
||||
UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
|
||||
|
||||
@@ -80,7 +80,7 @@ public class FunctionCallbackInPromptIT {
|
||||
|
||||
contextRunner.withPropertyValues("spring.ai.minimax.chat.options.model=abab6-chat").run(context -> {
|
||||
|
||||
MiniMaxChatClient chatClient = context.getBean(MiniMaxChatClient.class);
|
||||
MiniMaxChatCaller chatClient = context.getBean(MiniMaxChatCaller.class);
|
||||
|
||||
UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.messages.AssistantMessage;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.minimax.MiniMaxChatClient;
|
||||
import org.springframework.ai.minimax.MiniMaxChatCaller;
|
||||
import org.springframework.ai.minimax.MiniMaxChatOptions;
|
||||
import org.springframework.ai.model.function.FunctionCallingOptions;
|
||||
import org.springframework.ai.model.function.FunctionCallingOptionsBuilder.PortableFunctionCallingOptions;
|
||||
@@ -61,7 +61,7 @@ class FunctionCallbackWithPlainFunctionBeanIT {
|
||||
void functionCallTest() {
|
||||
contextRunner.withPropertyValues("spring.ai.minimax.chat.options.model=abab6-chat").run(context -> {
|
||||
|
||||
MiniMaxChatClient chatClient = context.getBean(MiniMaxChatClient.class);
|
||||
MiniMaxChatCaller chatClient = context.getBean(MiniMaxChatCaller.class);
|
||||
|
||||
// Test weatherFunction
|
||||
UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
|
||||
@@ -88,7 +88,7 @@ class FunctionCallbackWithPlainFunctionBeanIT {
|
||||
void functionCallWithPortableFunctionCallingOptions() {
|
||||
contextRunner.withPropertyValues("spring.ai.minimax.chat.options.model=abab6-chat").run(context -> {
|
||||
|
||||
MiniMaxChatClient chatClient = context.getBean(MiniMaxChatClient.class);
|
||||
MiniMaxChatCaller chatClient = context.getBean(MiniMaxChatCaller.class);
|
||||
|
||||
// Test weatherFunction
|
||||
UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
|
||||
@@ -107,7 +107,7 @@ class FunctionCallbackWithPlainFunctionBeanIT {
|
||||
void streamFunctionCallTest() {
|
||||
contextRunner.withPropertyValues("spring.ai.minimax.chat.options.model=abab6-chat").run(context -> {
|
||||
|
||||
MiniMaxChatClient chatClient = context.getBean(MiniMaxChatClient.class);
|
||||
MiniMaxChatCaller chatClient = context.getBean(MiniMaxChatCaller.class);
|
||||
|
||||
// Test weatherFunction
|
||||
UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.messages.AssistantMessage;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.minimax.MiniMaxChatClient;
|
||||
import org.springframework.ai.minimax.MiniMaxChatCaller;
|
||||
import org.springframework.ai.minimax.MiniMaxChatOptions;
|
||||
import org.springframework.ai.model.function.FunctionCallback;
|
||||
import org.springframework.ai.model.function.FunctionCallbackWrapper;
|
||||
@@ -59,7 +59,7 @@ public class FunctionCallbackWrapperIT {
|
||||
void functionCallTest() {
|
||||
contextRunner.withPropertyValues("spring.ai.minimax.chat.options.model=abab6-chat").run(context -> {
|
||||
|
||||
MiniMaxChatClient chatClient = context.getBean(MiniMaxChatClient.class);
|
||||
MiniMaxChatCaller chatClient = context.getBean(MiniMaxChatCaller.class);
|
||||
|
||||
UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
|
||||
|
||||
@@ -77,7 +77,7 @@ public class FunctionCallbackWrapperIT {
|
||||
void streamFunctionCallTest() {
|
||||
contextRunner.withPropertyValues("spring.ai.minimax.chat.options.model=abab6-chat").run(context -> {
|
||||
|
||||
MiniMaxChatClient chatClient = context.getBean(MiniMaxChatClient.class);
|
||||
MiniMaxChatCaller chatClient = context.getBean(MiniMaxChatCaller.class);
|
||||
|
||||
UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.embedding.EmbeddingResponse;
|
||||
import org.springframework.ai.minimax.MiniMaxChatClient;
|
||||
import org.springframework.ai.minimax.MiniMaxChatCaller;
|
||||
import org.springframework.ai.minimax.MiniMaxEmbeddingClient;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
|
||||
@@ -52,7 +52,7 @@ public class MiniMaxAutoConfigurationIT {
|
||||
@Test
|
||||
void generate() {
|
||||
contextRunner.run(context -> {
|
||||
MiniMaxChatClient client = context.getBean(MiniMaxChatClient.class);
|
||||
MiniMaxChatCaller client = context.getBean(MiniMaxChatCaller.class);
|
||||
String response = client.call("Hello");
|
||||
assertThat(response).isNotEmpty();
|
||||
logger.info("Response: " + response);
|
||||
@@ -62,7 +62,7 @@ public class MiniMaxAutoConfigurationIT {
|
||||
@Test
|
||||
void generateStreaming() {
|
||||
contextRunner.run(context -> {
|
||||
MiniMaxChatClient client = context.getBean(MiniMaxChatClient.class);
|
||||
MiniMaxChatCaller client = context.getBean(MiniMaxChatCaller.class);
|
||||
Flux<ChatResponse> responseFlux = client.stream(new Prompt(new UserMessage("Hello")));
|
||||
String response = responseFlux.collectList().block().stream().map(chatResponse -> {
|
||||
return chatResponse.getResults().get(0).getOutput().getContent();
|
||||
|
||||
@@ -19,7 +19,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import org.skyscreamer.jsonassert.JSONCompareMode;
|
||||
import org.springframework.ai.autoconfigure.retry.SpringAiRetryAutoConfiguration;
|
||||
import org.springframework.ai.minimax.MiniMaxChatClient;
|
||||
import org.springframework.ai.minimax.MiniMaxChatCaller;
|
||||
import org.springframework.ai.minimax.MiniMaxEmbeddingClient;
|
||||
import org.springframework.ai.minimax.api.MiniMaxApi;
|
||||
import org.springframework.ai.model.ModelOptionsUtils;
|
||||
@@ -302,7 +302,7 @@ public class MiniMaxPropertiesTests {
|
||||
RestClientAutoConfiguration.class, MiniMaxAutoConfiguration.class))
|
||||
.run(context -> {
|
||||
assertThat(context.getBeansOfType(MiniMaxChatProperties.class)).isNotEmpty();
|
||||
assertThat(context.getBeansOfType(MiniMaxChatClient.class)).isEmpty();
|
||||
assertThat(context.getBeansOfType(MiniMaxChatCaller.class)).isEmpty();
|
||||
});
|
||||
|
||||
new ApplicationContextRunner()
|
||||
@@ -311,7 +311,7 @@ public class MiniMaxPropertiesTests {
|
||||
RestClientAutoConfiguration.class, MiniMaxAutoConfiguration.class))
|
||||
.run(context -> {
|
||||
assertThat(context.getBeansOfType(MiniMaxChatProperties.class)).isNotEmpty();
|
||||
assertThat(context.getBeansOfType(MiniMaxChatClient.class)).isNotEmpty();
|
||||
assertThat(context.getBeansOfType(MiniMaxChatCaller.class)).isNotEmpty();
|
||||
});
|
||||
|
||||
new ApplicationContextRunner()
|
||||
@@ -321,7 +321,7 @@ public class MiniMaxPropertiesTests {
|
||||
RestClientAutoConfiguration.class, MiniMaxAutoConfiguration.class))
|
||||
.run(context -> {
|
||||
assertThat(context.getBeansOfType(MiniMaxChatProperties.class)).isNotEmpty();
|
||||
assertThat(context.getBeansOfType(MiniMaxChatClient.class)).isNotEmpty();
|
||||
assertThat(context.getBeansOfType(MiniMaxChatCaller.class)).isNotEmpty();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ai.autoconfigure.openai.tool;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration;
|
||||
import org.springframework.ai.autoconfigure.retry.SpringAiRetryAutoConfiguration;
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.model.function.FunctionCallback;
|
||||
import org.springframework.ai.model.function.FunctionCallbackWrapper;
|
||||
import org.springframework.ai.openai.OpenAiModelCaller;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".*")
|
||||
public class FunctionCallbackWrapper2IT {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(FunctionCallbackWrapperIT.class);
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withPropertyValues("spring.ai.openai.apiKey=" + System.getenv("OPENAI_API_KEY"))
|
||||
.withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
|
||||
RestClientAutoConfiguration.class, OpenAiAutoConfiguration.class))
|
||||
.withUserConfiguration(Config.class);
|
||||
|
||||
@Test
|
||||
void functionCallTest() {
|
||||
contextRunner.withPropertyValues("spring.ai.openai.chat.options.model=gpt-4-turbo-preview").run(context -> {
|
||||
|
||||
OpenAiModelCaller caller = context.getBean(OpenAiModelCaller.class);
|
||||
|
||||
ChatClient chatClient = ChatClient.builder(caller)
|
||||
.defaultFunctions("WeatherInfo")
|
||||
.defaultUser(u -> u.text("What's the weather like in {cities}?"))
|
||||
.build();
|
||||
|
||||
String content = chatClient.call()
|
||||
.user(u -> u.param("cities", "San Francisco, Tokyo, Paris"))
|
||||
.collect()
|
||||
.content();
|
||||
|
||||
logger.info("Response: {}", content);
|
||||
|
||||
assertThat(content).containsAnyOf("30.0", "30");
|
||||
assertThat(content).containsAnyOf("15.0", "15");
|
||||
assertThat(content).containsAnyOf("10", "10");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void streamFunctionCallTest() {
|
||||
contextRunner.withPropertyValues("spring.ai.openai.chat.options.model=gpt-4-turbo-preview").run(context -> {
|
||||
|
||||
OpenAiModelCaller caller = context.getBean(OpenAiModelCaller.class);
|
||||
|
||||
String content = ChatClient.builder(caller)
|
||||
.build()
|
||||
.call()
|
||||
.functions("WeatherInfo")
|
||||
.user(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris?"))
|
||||
.stream()
|
||||
.content()
|
||||
.collectList()
|
||||
.block()
|
||||
.stream()
|
||||
.collect(Collectors.joining());
|
||||
|
||||
logger.info("Response: {}", content);
|
||||
|
||||
assertThat(content).containsAnyOf("30.0", "30");
|
||||
assertThat(content).containsAnyOf("10.0", "10");
|
||||
assertThat(content).containsAnyOf("15.0", "15");
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public FunctionCallback weatherFunctionInfo() {
|
||||
|
||||
return FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
.withName("WeatherInfo")
|
||||
.withDescription("Get the weather in location")
|
||||
.withResponseConverter((response) -> "" + response.temp() + response.unit())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -58,11 +58,11 @@ public class FunctionCallbackWrapperIT {
|
||||
void functionCallTest() {
|
||||
contextRunner.withPropertyValues("spring.ai.openai.chat.options.model=gpt-4-turbo-preview").run(context -> {
|
||||
|
||||
OpenAiModelCaller chatClient = context.getBean(OpenAiModelCaller.class);
|
||||
OpenAiModelCaller caller = context.getBean(OpenAiModelCaller.class);
|
||||
|
||||
UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
|
||||
|
||||
ChatResponse response = chatClient.call(
|
||||
ChatResponse response = caller.call(
|
||||
new Prompt(List.of(userMessage), OpenAiChatOptions.builder().withFunction("WeatherInfo").build()));
|
||||
|
||||
logger.info("Response: {}", response);
|
||||
@@ -76,11 +76,11 @@ public class FunctionCallbackWrapperIT {
|
||||
void streamFunctionCallTest() {
|
||||
contextRunner.withPropertyValues("spring.ai.openai.chat.options.model=gpt-4-turbo-preview").run(context -> {
|
||||
|
||||
OpenAiModelCaller chatClient = context.getBean(OpenAiModelCaller.class);
|
||||
OpenAiModelCaller caller = context.getBean(OpenAiModelCaller.class);
|
||||
|
||||
UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
|
||||
|
||||
Flux<ChatResponse> response = chatClient.stream(
|
||||
Flux<ChatResponse> response = caller.stream(
|
||||
new Prompt(List.of(userMessage), OpenAiChatOptions.builder().withFunction("WeatherInfo").build()));
|
||||
|
||||
String content = response.collectList()
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.embedding.EmbeddingResponse;
|
||||
import org.springframework.ai.image.ImagePrompt;
|
||||
import org.springframework.ai.image.ImageResponse;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatClient;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatCaller;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiEmbeddingClient;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiImageClient;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
@@ -55,7 +55,7 @@ public class ZhiPuAiAutoConfigurationIT {
|
||||
@Test
|
||||
void generate() {
|
||||
contextRunner.run(context -> {
|
||||
ZhiPuAiChatClient client = context.getBean(ZhiPuAiChatClient.class);
|
||||
ZhiPuAiChatCaller client = context.getBean(ZhiPuAiChatCaller.class);
|
||||
String response = client.call("Hello");
|
||||
assertThat(response).isNotEmpty();
|
||||
logger.info("Response: " + response);
|
||||
@@ -65,7 +65,7 @@ public class ZhiPuAiAutoConfigurationIT {
|
||||
@Test
|
||||
void generateStreaming() {
|
||||
contextRunner.run(context -> {
|
||||
ZhiPuAiChatClient client = context.getBean(ZhiPuAiChatClient.class);
|
||||
ZhiPuAiChatCaller client = context.getBean(ZhiPuAiChatCaller.class);
|
||||
Flux<ChatResponse> responseFlux = client.stream(new Prompt(new UserMessage("Hello")));
|
||||
String response = responseFlux.collectList().block().stream().map(chatResponse -> {
|
||||
return chatResponse.getResults().get(0).getOutput().getContent();
|
||||
|
||||
@@ -20,7 +20,7 @@ import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import org.skyscreamer.jsonassert.JSONCompareMode;
|
||||
import org.springframework.ai.autoconfigure.retry.SpringAiRetryAutoConfiguration;
|
||||
import org.springframework.ai.model.ModelOptionsUtils;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatClient;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatCaller;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiEmbeddingClient;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiImageClient;
|
||||
import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
|
||||
@@ -378,7 +378,7 @@ public class ZhiPuAiPropertiesTests {
|
||||
RestClientAutoConfiguration.class, ZhiPuAiAutoConfiguration.class))
|
||||
.run(context -> {
|
||||
assertThat(context.getBeansOfType(ZhiPuAiChatProperties.class)).isNotEmpty();
|
||||
assertThat(context.getBeansOfType(ZhiPuAiChatClient.class)).isEmpty();
|
||||
assertThat(context.getBeansOfType(ZhiPuAiChatCaller.class)).isEmpty();
|
||||
});
|
||||
|
||||
new ApplicationContextRunner()
|
||||
@@ -387,7 +387,7 @@ public class ZhiPuAiPropertiesTests {
|
||||
RestClientAutoConfiguration.class, ZhiPuAiAutoConfiguration.class))
|
||||
.run(context -> {
|
||||
assertThat(context.getBeansOfType(ZhiPuAiChatProperties.class)).isNotEmpty();
|
||||
assertThat(context.getBeansOfType(ZhiPuAiChatClient.class)).isNotEmpty();
|
||||
assertThat(context.getBeansOfType(ZhiPuAiChatCaller.class)).isNotEmpty();
|
||||
});
|
||||
|
||||
new ApplicationContextRunner()
|
||||
@@ -397,7 +397,7 @@ public class ZhiPuAiPropertiesTests {
|
||||
RestClientAutoConfiguration.class, ZhiPuAiAutoConfiguration.class))
|
||||
.run(context -> {
|
||||
assertThat(context.getBeansOfType(ZhiPuAiChatProperties.class)).isNotEmpty();
|
||||
assertThat(context.getBeansOfType(ZhiPuAiChatClient.class)).isNotEmpty();
|
||||
assertThat(context.getBeansOfType(ZhiPuAiChatCaller.class)).isNotEmpty();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ import org.springframework.ai.chat.messages.AssistantMessage;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.model.function.FunctionCallbackWrapper;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatClient;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatCaller;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatOptions;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
|
||||
@@ -56,7 +56,7 @@ public class FunctionCallbackInPromptIT {
|
||||
void functionCallTest() {
|
||||
contextRunner.withPropertyValues("spring.ai.zhipuai.chat.options.model=glm-4").run(context -> {
|
||||
|
||||
ZhiPuAiChatClient chatClient = context.getBean(ZhiPuAiChatClient.class);
|
||||
ZhiPuAiChatCaller chatClient = context.getBean(ZhiPuAiChatCaller.class);
|
||||
|
||||
UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
|
||||
|
||||
@@ -81,7 +81,7 @@ public class FunctionCallbackInPromptIT {
|
||||
|
||||
contextRunner.withPropertyValues("spring.ai.zhipuai.chat.options.model=glm-4").run(context -> {
|
||||
|
||||
ZhiPuAiChatClient chatClient = context.getBean(ZhiPuAiChatClient.class);
|
||||
ZhiPuAiChatCaller chatClient = context.getBean(ZhiPuAiChatCaller.class);
|
||||
|
||||
UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.model.function.FunctionCallingOptions;
|
||||
import org.springframework.ai.model.function.FunctionCallingOptionsBuilder.PortableFunctionCallingOptions;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatClient;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatCaller;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatOptions;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
|
||||
@@ -62,7 +62,7 @@ class FunctionCallbackWithPlainFunctionBeanIT {
|
||||
void functionCallTest() {
|
||||
contextRunner.withPropertyValues("spring.ai.zhipuai.chat.options.model=glm-4").run(context -> {
|
||||
|
||||
ZhiPuAiChatClient chatClient = context.getBean(ZhiPuAiChatClient.class);
|
||||
ZhiPuAiChatCaller chatClient = context.getBean(ZhiPuAiChatCaller.class);
|
||||
|
||||
// Test weatherFunction
|
||||
UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
|
||||
@@ -89,7 +89,7 @@ class FunctionCallbackWithPlainFunctionBeanIT {
|
||||
void functionCallWithPortableFunctionCallingOptions() {
|
||||
contextRunner.withPropertyValues("spring.ai.zhipuai.chat.options.model=glm-4").run(context -> {
|
||||
|
||||
ZhiPuAiChatClient chatClient = context.getBean(ZhiPuAiChatClient.class);
|
||||
ZhiPuAiChatCaller chatClient = context.getBean(ZhiPuAiChatCaller.class);
|
||||
|
||||
// Test weatherFunction
|
||||
UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
|
||||
@@ -108,7 +108,7 @@ class FunctionCallbackWithPlainFunctionBeanIT {
|
||||
void streamFunctionCallTest() {
|
||||
contextRunner.withPropertyValues("spring.ai.zhipuai.chat.options.model=glm-4").run(context -> {
|
||||
|
||||
ZhiPuAiChatClient chatClient = context.getBean(ZhiPuAiChatClient.class);
|
||||
ZhiPuAiChatCaller chatClient = context.getBean(ZhiPuAiChatCaller.class);
|
||||
|
||||
// Test weatherFunction
|
||||
UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
|
||||
|
||||
@@ -28,7 +28,7 @@ import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.model.function.FunctionCallback;
|
||||
import org.springframework.ai.model.function.FunctionCallbackWrapper;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatClient;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatCaller;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatOptions;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
|
||||
@@ -60,7 +60,7 @@ public class FunctionCallbackWrapperIT {
|
||||
void functionCallTest() {
|
||||
contextRunner.withPropertyValues("spring.ai.zhipuai.chat.options.model=glm-4").run(context -> {
|
||||
|
||||
ZhiPuAiChatClient chatClient = context.getBean(ZhiPuAiChatClient.class);
|
||||
ZhiPuAiChatCaller chatClient = context.getBean(ZhiPuAiChatCaller.class);
|
||||
|
||||
UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
|
||||
|
||||
@@ -78,7 +78,7 @@ public class FunctionCallbackWrapperIT {
|
||||
void streamFunctionCallTest() {
|
||||
contextRunner.withPropertyValues("spring.ai.zhipuai.chat.options.model=glm-4").run(context -> {
|
||||
|
||||
ZhiPuAiChatClient chatClient = context.getBean(ZhiPuAiChatClient.class);
|
||||
ZhiPuAiChatCaller chatClient = context.getBean(ZhiPuAiChatCaller.class);
|
||||
|
||||
UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user