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);
|
||||
|
||||
Reference in New Issue
Block a user