modelcall and chatclient rework
This commit is contained in:
committed by
Christian Tzolov
parent
2abf10dbf9
commit
c4bfb5709b
@@ -51,11 +51,11 @@ public class AnthropicChatOptions implements ChatOptions, FunctionCallingOptions
|
||||
private @JsonProperty("top_k") Integer topK;
|
||||
|
||||
/**
|
||||
* Tool Function Callbacks to register with the ChatClient. For Prompt
|
||||
* Tool Function Callbacks to register with the ModelCall. For Prompt
|
||||
* Options the functionCallbacks are automatically enabled for the duration of the
|
||||
* prompt execution. For Default Options the functionCallbacks are registered but
|
||||
* disabled by default. Use the enableFunctions to set the functions from the registry
|
||||
* to be used by the ChatClient chat completion requests.
|
||||
* to be used by the ModelCall chat completion requests.
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
@JsonIgnore
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.ai.anthropic.api.AnthropicApi;
|
||||
@@ -38,7 +39,6 @@ import org.springframework.ai.anthropic.api.AnthropicApi.Role;
|
||||
import org.springframework.ai.anthropic.api.AnthropicApi.StreamResponse;
|
||||
import org.springframework.ai.anthropic.api.AnthropicApi.Usage;
|
||||
import org.springframework.ai.anthropic.metadata.AnthropicChatResponseMetadata;
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatClient;
|
||||
@@ -56,16 +56,16 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* The {@link ChatClient} implementation for the Anthropic service.
|
||||
* The {@link ModelCall} implementation for the Anthropic service.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class AnthropicChatClient extends
|
||||
public class AnthropicModelCall extends
|
||||
AbstractFunctionCallSupport<AnthropicApi.RequestMessage, AnthropicApi.ChatCompletionRequest, ResponseEntity<AnthropicApi.ChatCompletion>>
|
||||
implements ChatClient, StreamingChatClient {
|
||||
implements ModelCall, StreamingChatClient {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AnthropicChatClient.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(AnthropicModelCall.class);
|
||||
|
||||
public static final String DEFAULT_MODEL_NAME = AnthropicApi.ChatModel.CLAUDE_3_OPUS.getValue();
|
||||
|
||||
@@ -89,10 +89,10 @@ public class AnthropicChatClient extends
|
||||
public final RetryTemplate retryTemplate;
|
||||
|
||||
/**
|
||||
* Construct a new {@link AnthropicChatClient} instance.
|
||||
* Construct a new {@link AnthropicModelCall} instance.
|
||||
* @param anthropicApi the lower-level API for the Anthropic service.
|
||||
*/
|
||||
public AnthropicChatClient(AnthropicApi anthropicApi) {
|
||||
public AnthropicModelCall(AnthropicApi anthropicApi) {
|
||||
this(anthropicApi,
|
||||
AnthropicChatOptions.builder()
|
||||
.withModel(DEFAULT_MODEL_NAME)
|
||||
@@ -102,34 +102,34 @@ public class AnthropicChatClient extends
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@link AnthropicChatClient} instance.
|
||||
* Construct a new {@link AnthropicModelCall} instance.
|
||||
* @param anthropicApi the lower-level API for the Anthropic service.
|
||||
* @param defaultOptions the default options used for the chat completion requests.
|
||||
*/
|
||||
public AnthropicChatClient(AnthropicApi anthropicApi, AnthropicChatOptions defaultOptions) {
|
||||
public AnthropicModelCall(AnthropicApi anthropicApi, AnthropicChatOptions defaultOptions) {
|
||||
this(anthropicApi, defaultOptions, RetryUtils.DEFAULT_RETRY_TEMPLATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@link AnthropicChatClient} instance.
|
||||
* Construct a new {@link AnthropicModelCall} instance.
|
||||
* @param anthropicApi the lower-level API for the Anthropic service.
|
||||
* @param defaultOptions the default options used for the chat completion requests.
|
||||
* @param retryTemplate the retry template used to retry the Anthropic API calls.
|
||||
*/
|
||||
public AnthropicChatClient(AnthropicApi anthropicApi, AnthropicChatOptions defaultOptions,
|
||||
public AnthropicModelCall(AnthropicApi anthropicApi, AnthropicChatOptions defaultOptions,
|
||||
RetryTemplate retryTemplate) {
|
||||
this(anthropicApi, defaultOptions, retryTemplate, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@link AnthropicChatClient} instance.
|
||||
* Construct a new {@link AnthropicModelCall} instance.
|
||||
* @param anthropicApi the lower-level API for the Anthropic service.
|
||||
* @param defaultOptions the default options used for the chat completion requests.
|
||||
* @param retryTemplate the retry template used to retry the Anthropic API calls.
|
||||
* @param functionCallbackContext the function callback context used to store the
|
||||
* state of the function calls.
|
||||
*/
|
||||
public AnthropicChatClient(AnthropicApi anthropicApi, AnthropicChatOptions defaultOptions,
|
||||
public AnthropicModelCall(AnthropicApi anthropicApi, AnthropicChatOptions defaultOptions,
|
||||
RetryTemplate retryTemplate, FunctionCallbackContext functionCallbackContext) {
|
||||
|
||||
super(functionCallbackContext);
|
||||
@@ -29,7 +29,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.anthropic.api.AnthropicApi;
|
||||
import org.springframework.ai.anthropic.api.tool.MockWeatherService;
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatClient;
|
||||
@@ -56,12 +56,12 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest(classes = AnthropicTestConfiguration.class, properties = "spring.ai.retry.on-http-codes=429")
|
||||
@EnabledIfEnvironmentVariable(named = "ANTHROPIC_API_KEY", matches = ".+")
|
||||
class AnthropicChatClientIT {
|
||||
class AnthropicModelCallIT {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AnthropicChatClientIT.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(AnthropicModelCallIT.class);
|
||||
|
||||
@Autowired
|
||||
protected ChatClient chatClient;
|
||||
protected ModelCall modelCall;
|
||||
|
||||
@Autowired
|
||||
protected StreamingChatClient streamingChatClient;
|
||||
@@ -76,7 +76,7 @@ class AnthropicChatClientIT {
|
||||
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);
|
||||
Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", "Bob", "voice", "pirate"));
|
||||
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
|
||||
ChatResponse response = chatClient.call(prompt);
|
||||
ChatResponse response = modelCall.call(prompt);
|
||||
assertThat(response.getResults()).hasSize(1);
|
||||
assertThat(response.getMetadata().getUsage().getGenerationTokens()).isGreaterThan(0);
|
||||
assertThat(response.getMetadata().getUsage().getPromptTokens()).isGreaterThan(0);
|
||||
@@ -102,7 +102,7 @@ class AnthropicChatClientIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template,
|
||||
Map.of("subject", "ice cream flavors", "format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = this.chatClient.call(prompt).getResult();
|
||||
Generation generation = this.modelCall.call(prompt).getResult();
|
||||
|
||||
List<String> list = listOutputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(list).hasSize(5);
|
||||
@@ -120,7 +120,7 @@ class AnthropicChatClientIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template,
|
||||
Map.of("subject", "an array of numbers from 1 to 9 under they key name 'numbers'", "format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = chatClient.call(prompt).getResult();
|
||||
Generation generation = modelCall.call(prompt).getResult();
|
||||
|
||||
Map<String, Object> result = mapOutputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(result.get("numbers")).isEqualTo(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
|
||||
@@ -142,7 +142,7 @@ class AnthropicChatClientIT {
|
||||
""";
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = chatClient.call(prompt).getResult();
|
||||
Generation generation = modelCall.call(prompt).getResult();
|
||||
|
||||
ActorsFilmsRecord actorsFilms = beanOutputConverter.convert(generation.getOutput().getContent());
|
||||
logger.info("" + actorsFilms);
|
||||
@@ -187,7 +187,7 @@ class AnthropicChatClientIT {
|
||||
var userMessage = new UserMessage("Explain what do you see on this picture?",
|
||||
List.of(new Media(MimeTypeUtils.IMAGE_PNG, imageData)));
|
||||
|
||||
var response = chatClient.call(new Prompt(List.of(userMessage)));
|
||||
var response = modelCall.call(new Prompt(List.of(userMessage)));
|
||||
|
||||
logger.info(response.getResult().getOutput().getContent());
|
||||
assertThat(response.getResult().getOutput().getContent()).contains("bananas", "apple", "basket");
|
||||
@@ -209,7 +209,7 @@ class AnthropicChatClientIT {
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
ChatResponse response = chatClient.call(new Prompt(messages, promptOptions));
|
||||
ChatResponse response = modelCall.call(new Prompt(messages, promptOptions));
|
||||
|
||||
logger.info("Response: {}", response);
|
||||
|
||||
@@ -38,8 +38,8 @@ public class AnthropicTestConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AnthropicChatClient openAiChatClient(AnthropicApi api) {
|
||||
AnthropicChatClient anthropicChatClient = new AnthropicChatClient(api);
|
||||
public AnthropicModelCall openAiChatClient(AnthropicApi api) {
|
||||
AnthropicModelCall anthropicChatClient = new AnthropicModelCall(api);
|
||||
return anthropicChatClient;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ public class ChatCompletionRequestTests {
|
||||
@Test
|
||||
public void createRequestWithChatOptions() {
|
||||
|
||||
var client = new AnthropicChatClient(new AnthropicApi("TEST"),
|
||||
var client = new AnthropicModelCall(new AnthropicApi("TEST"),
|
||||
AnthropicChatOptions.builder().withModel("DEFAULT_MODEL").withTemperature(66.6f).build());
|
||||
|
||||
var request = client.createRequest(new Prompt("Test message content"), false);
|
||||
|
||||
@@ -127,11 +127,11 @@ public class AzureOpenAiChatOptions implements FunctionCallingOptions, ChatOptio
|
||||
private String deploymentName;
|
||||
|
||||
/**
|
||||
* OpenAI Tool Function Callbacks to register with the ChatClient. For Prompt Options
|
||||
* OpenAI Tool Function Callbacks to register with the ModelCall. For Prompt Options
|
||||
* the functionCallbacks are automatically enabled for the duration of the prompt
|
||||
* execution. For Default Options the functionCallbacks are registered but disabled by
|
||||
* default. Use the enableFunctions to set the functions from the registry to be used
|
||||
* by the ChatClient chat completion requests.
|
||||
* by the ModelCall chat completion requests.
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
@JsonIgnore
|
||||
|
||||
@@ -38,7 +38,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.azure.openai.metadata.AzureOpenAiChatResponseMetadata;
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatClient;
|
||||
@@ -63,7 +63,7 @@ import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* {@link ChatClient} implementation for {@literal Microsoft Azure AI} backed by
|
||||
* {@link ModelCall} implementation for {@literal Microsoft Azure AI} backed by
|
||||
* {@link OpenAIClient}.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
@@ -71,12 +71,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
* @author John Blum
|
||||
* @author Christian Tzolov
|
||||
* @author Grogdunn
|
||||
* @see ChatClient
|
||||
* @see ModelCall
|
||||
* @see com.azure.ai.openai.OpenAIClient
|
||||
*/
|
||||
public class AzureOpenAiChatClient
|
||||
public class AzureOpenAiModelCall
|
||||
extends AbstractFunctionCallSupport<ChatRequestMessage, ChatCompletionsOptions, ChatCompletions>
|
||||
implements ChatClient, StreamingChatClient {
|
||||
implements ModelCall, StreamingChatClient {
|
||||
|
||||
private static final String DEFAULT_DEPLOYMENT_NAME = "gpt-35-turbo";
|
||||
|
||||
@@ -94,7 +94,7 @@ public class AzureOpenAiChatClient
|
||||
*/
|
||||
private final OpenAIClient openAIClient;
|
||||
|
||||
public AzureOpenAiChatClient(OpenAIClient microsoftOpenAiClient) {
|
||||
public AzureOpenAiModelCall(OpenAIClient microsoftOpenAiClient) {
|
||||
this(microsoftOpenAiClient,
|
||||
AzureOpenAiChatOptions.builder()
|
||||
.withDeploymentName(DEFAULT_DEPLOYMENT_NAME)
|
||||
@@ -102,11 +102,11 @@ public class AzureOpenAiChatClient
|
||||
.build());
|
||||
}
|
||||
|
||||
public AzureOpenAiChatClient(OpenAIClient microsoftOpenAiClient, AzureOpenAiChatOptions options) {
|
||||
public AzureOpenAiModelCall(OpenAIClient microsoftOpenAiClient, AzureOpenAiChatOptions options) {
|
||||
this(microsoftOpenAiClient, options, null);
|
||||
}
|
||||
|
||||
public AzureOpenAiChatClient(OpenAIClient microsoftOpenAiClient, AzureOpenAiChatOptions options,
|
||||
public AzureOpenAiModelCall(OpenAIClient microsoftOpenAiClient, AzureOpenAiChatOptions options,
|
||||
FunctionCallbackContext functionCallbackContext) {
|
||||
super(functionCallbackContext);
|
||||
Assert.notNull(microsoftOpenAiClient, "com.azure.ai.openai.OpenAIClient must not be null");
|
||||
@@ -117,10 +117,10 @@ public class AzureOpenAiChatClient
|
||||
|
||||
/**
|
||||
* @deprecated since 0.8.0, use
|
||||
* {@link #AzureOpenAiChatClient(OpenAIClient, AzureOpenAiChatOptions)} instead.
|
||||
* {@link #AzureOpenAiModelCall(OpenAIClient, AzureOpenAiChatOptions)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "0.8.0")
|
||||
public AzureOpenAiChatClient withDefaultOptions(AzureOpenAiChatOptions defaultOptions) {
|
||||
public AzureOpenAiModelCall withDefaultOptions(AzureOpenAiChatOptions defaultOptions) {
|
||||
Assert.notNull(defaultOptions, "DefaultOptions must not be null");
|
||||
this.defaultOptions = defaultOptions;
|
||||
return this;
|
||||
@@ -53,7 +53,7 @@ public class AzureChatCompletionsOptionsTests {
|
||||
.withUser("user")
|
||||
.build();
|
||||
|
||||
var client = new AzureOpenAiChatClient(mockClient, defaultOptions);
|
||||
var client = new AzureOpenAiModelCall(mockClient, defaultOptions);
|
||||
|
||||
var requestOptions = client.toAzureChatCompletionsOptions(new Prompt("Test message content"));
|
||||
|
||||
|
||||
@@ -46,13 +46,13 @@ import org.springframework.core.convert.support.DefaultConversionService;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest(classes = AzureOpenAiChatClientIT.TestConfiguration.class)
|
||||
@SpringBootTest(classes = AzureOpenAiModelCallIT.TestConfiguration.class)
|
||||
@EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_API_KEY", matches = ".+")
|
||||
@EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_ENDPOINT", matches = ".+")
|
||||
class AzureOpenAiChatClientIT {
|
||||
class AzureOpenAiModelCallIT {
|
||||
|
||||
@Autowired
|
||||
private AzureOpenAiChatClient chatClient;
|
||||
private AzureOpenAiModelCall chatClient;
|
||||
|
||||
record ActorsFilms(String actor, List<String> movies) {
|
||||
}
|
||||
@@ -194,8 +194,8 @@ class AzureOpenAiChatClientIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AzureOpenAiChatClient azureOpenAiChatClient(OpenAIClient openAIClient) {
|
||||
return new AzureOpenAiChatClient(openAIClient,
|
||||
public AzureOpenAiModelCall azureOpenAiChatClient(OpenAIClient openAIClient) {
|
||||
return new AzureOpenAiModelCall(openAIClient,
|
||||
AzureOpenAiChatOptions.builder().withDeploymentName("gpt-35-turbo").withMaxTokens(200).build());
|
||||
|
||||
}
|
||||
@@ -59,8 +59,8 @@ public class MockAzureOpenAiTestConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
AzureOpenAiChatClient azureOpenAiChatClient(OpenAIClient microsoftAzureOpenAiClient) {
|
||||
return new AzureOpenAiChatClient(microsoftAzureOpenAiClient);
|
||||
AzureOpenAiModelCall azureOpenAiChatClient(OpenAIClient microsoftAzureOpenAiClient) {
|
||||
return new AzureOpenAiModelCall(microsoftAzureOpenAiClient);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.azure.openai.AzureOpenAiChatClient;
|
||||
import org.springframework.ai.azure.openai.AzureOpenAiModelCall;
|
||||
import org.springframework.ai.azure.openai.AzureOpenAiChatOptions;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
@@ -46,18 +46,18 @@ import reactor.core.publisher.Flux;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest(classes = AzureOpenAiChatClientFunctionCallIT.TestConfiguration.class)
|
||||
@SpringBootTest(classes = AzureOpenAiModelCallFunctionCallIT.TestConfiguration.class)
|
||||
@EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_API_KEY", matches = ".+")
|
||||
@EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_ENDPOINT", matches = ".+")
|
||||
class AzureOpenAiChatClientFunctionCallIT {
|
||||
class AzureOpenAiModelCallFunctionCallIT {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AzureOpenAiChatClientFunctionCallIT.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(AzureOpenAiModelCallFunctionCallIT.class);
|
||||
|
||||
@Autowired
|
||||
private String selectedModel;
|
||||
|
||||
@Autowired
|
||||
private AzureOpenAiChatClient chatClient;
|
||||
private AzureOpenAiModelCall chatClient;
|
||||
|
||||
@Test
|
||||
void functionCallTest() {
|
||||
@@ -129,8 +129,8 @@ class AzureOpenAiChatClientFunctionCallIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AzureOpenAiChatClient azureOpenAiChatClient(OpenAIClient openAIClient, String selectedModel) {
|
||||
return new AzureOpenAiChatClient(openAIClient,
|
||||
public AzureOpenAiModelCall azureOpenAiChatClient(OpenAIClient openAIClient, String selectedModel) {
|
||||
return new AzureOpenAiModelCall(openAIClient,
|
||||
AzureOpenAiChatOptions.builder().withDeploymentName(selectedModel).withMaxTokens(500).build());
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import com.azure.ai.openai.models.ContentFilterResultsForChoice;
|
||||
import com.azure.ai.openai.models.ContentFilterSeverity;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.ai.azure.openai.AzureOpenAiChatClient;
|
||||
import org.springframework.ai.azure.openai.AzureOpenAiModelCall;
|
||||
import org.springframework.ai.azure.openai.MockAzureOpenAiTestConfiguration;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
@@ -55,7 +55,7 @@ import org.springframework.web.context.request.WebRequest;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Unit Tests for {@link AzureOpenAiChatClient} asserting AI metadata.
|
||||
* Unit Tests for {@link AzureOpenAiModelCall} asserting AI metadata.
|
||||
*
|
||||
* @author John Blum
|
||||
* @author Christian Tzolov
|
||||
@@ -63,12 +63,12 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
@SpringBootTest
|
||||
@ActiveProfiles("spring-ai-azure-openai-mocks")
|
||||
@ContextConfiguration(classes = AzureOpenAiChatClientMetadataTests.TestConfiguration.class)
|
||||
@ContextConfiguration(classes = AzureOpenAiModelCallMetadataTests.TestConfiguration.class)
|
||||
@SuppressWarnings("unused")
|
||||
class AzureOpenAiChatClientMetadataTests {
|
||||
class AzureOpenAiModelCallMetadataTests {
|
||||
|
||||
@Autowired
|
||||
private AzureOpenAiChatClient aiClient;
|
||||
private AzureOpenAiModelCall aiClient;
|
||||
|
||||
@Test
|
||||
void azureOpenAiMetadataCapturedDuringGeneration() {
|
||||
@@ -17,7 +17,7 @@ package org.springframework.ai.bedrock.anthropic;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
|
||||
@@ -33,19 +33,19 @@ import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.model.ModelOptionsUtils;
|
||||
|
||||
/**
|
||||
* Java {@link ChatClient} and {@link StreamingChatClient} for the Bedrock Anthropic chat
|
||||
* Java {@link ModelCall} and {@link StreamingChatClient} for the Bedrock Anthropic chat
|
||||
* generative.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
* @since 0.8.0
|
||||
*/
|
||||
public class BedrockAnthropicChatClient implements ChatClient, StreamingChatClient {
|
||||
public class BedrockAnthropicModelCall implements ModelCall, StreamingChatClient {
|
||||
|
||||
private final AnthropicChatBedrockApi anthropicChatApi;
|
||||
|
||||
private final AnthropicChatOptions defaultOptions;
|
||||
|
||||
public BedrockAnthropicChatClient(AnthropicChatBedrockApi chatApi) {
|
||||
public BedrockAnthropicModelCall(AnthropicChatBedrockApi chatApi) {
|
||||
this(chatApi,
|
||||
AnthropicChatOptions.builder()
|
||||
.withTemperature(0.8f)
|
||||
@@ -55,7 +55,7 @@ public class BedrockAnthropicChatClient implements ChatClient, StreamingChatClie
|
||||
.build());
|
||||
}
|
||||
|
||||
public BedrockAnthropicChatClient(AnthropicChatBedrockApi chatApi, AnthropicChatOptions options) {
|
||||
public BedrockAnthropicModelCall(AnthropicChatBedrockApi chatApi, AnthropicChatOptions options) {
|
||||
this.anthropicChatApi = chatApi;
|
||||
this.defaultOptions = options;
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import org.springframework.ai.bedrock.anthropic3.api.Anthropic3ChatBedrockApi.An
|
||||
import org.springframework.ai.bedrock.anthropic3.api.Anthropic3ChatBedrockApi.MediaContent;
|
||||
import org.springframework.ai.bedrock.anthropic3.api.Anthropic3ChatBedrockApi.ChatCompletionMessage;
|
||||
import org.springframework.ai.bedrock.anthropic3.api.Anthropic3ChatBedrockApi.ChatCompletionMessage.Role;
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatClient;
|
||||
@@ -43,20 +43,20 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Java {@link ChatClient} and {@link StreamingChatClient} for the Bedrock Anthropic chat
|
||||
* Java {@link ModelCall} and {@link StreamingChatClient} for the Bedrock Anthropic chat
|
||||
* generative.
|
||||
*
|
||||
* @author Ben Middleton
|
||||
* @author Christian Tzolov
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class BedrockAnthropic3ChatClient implements ChatClient, StreamingChatClient {
|
||||
public class BedrockAnthropic3ModelCall implements ModelCall, StreamingChatClient {
|
||||
|
||||
private final Anthropic3ChatBedrockApi anthropicChatApi;
|
||||
|
||||
private final Anthropic3ChatOptions defaultOptions;
|
||||
|
||||
public BedrockAnthropic3ChatClient(Anthropic3ChatBedrockApi chatApi) {
|
||||
public BedrockAnthropic3ModelCall(Anthropic3ChatBedrockApi chatApi) {
|
||||
this(chatApi,
|
||||
Anthropic3ChatOptions.builder()
|
||||
.withTemperature(0.8f)
|
||||
@@ -66,7 +66,7 @@ public class BedrockAnthropic3ChatClient implements ChatClient, StreamingChatCli
|
||||
.build());
|
||||
}
|
||||
|
||||
public BedrockAnthropic3ChatClient(Anthropic3ChatBedrockApi chatApi, Anthropic3ChatOptions options) {
|
||||
public BedrockAnthropic3ModelCall(Anthropic3ChatBedrockApi chatApi, Anthropic3ChatOptions options) {
|
||||
this.anthropicChatApi = chatApi;
|
||||
this.defaultOptions = options;
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import org.springframework.ai.bedrock.MessageToPromptConverter;
|
||||
import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi;
|
||||
import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi.CohereChatRequest;
|
||||
import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi.CohereChatResponse;
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
@@ -39,17 +39,17 @@ import org.springframework.util.Assert;
|
||||
* @author Christian Tzolov
|
||||
* @since 0.8.0
|
||||
*/
|
||||
public class BedrockCohereChatClient implements ChatClient, StreamingChatClient {
|
||||
public class BedrockCohereModelCall implements ModelCall, StreamingChatClient {
|
||||
|
||||
private final CohereChatBedrockApi chatApi;
|
||||
|
||||
private final BedrockCohereChatOptions defaultOptions;
|
||||
|
||||
public BedrockCohereChatClient(CohereChatBedrockApi chatApi) {
|
||||
public BedrockCohereModelCall(CohereChatBedrockApi chatApi) {
|
||||
this(chatApi, BedrockCohereChatOptions.builder().build());
|
||||
}
|
||||
|
||||
public BedrockCohereChatClient(CohereChatBedrockApi chatApi, BedrockCohereChatOptions options) {
|
||||
public BedrockCohereModelCall(CohereChatBedrockApi chatApi, BedrockCohereChatOptions options) {
|
||||
Assert.notNull(chatApi, "CohereChatBedrockApi must not be null");
|
||||
Assert.notNull(options, "BedrockCohereChatOptions must not be null");
|
||||
|
||||
@@ -19,7 +19,7 @@ package org.springframework.ai.bedrock.jurassic2;
|
||||
import org.springframework.ai.bedrock.MessageToPromptConverter;
|
||||
import org.springframework.ai.bedrock.jurassic2.api.Ai21Jurassic2ChatBedrockApi;
|
||||
import org.springframework.ai.bedrock.jurassic2.api.Ai21Jurassic2ChatBedrockApi.Ai21Jurassic2ChatRequest;
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
|
||||
@@ -29,19 +29,18 @@ import org.springframework.ai.model.ModelOptionsUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Java {@link ChatClient} for the Bedrock Jurassic2 chat generative model.
|
||||
* Java {@link ModelCall} for the Bedrock Jurassic2 chat generative model.
|
||||
*
|
||||
* @author Ahmed Yousri
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class BedrockAi21Jurassic2ChatClient implements ChatClient {
|
||||
public class BedrockAi21Jurassic2ModelCall implements ModelCall {
|
||||
|
||||
private final Ai21Jurassic2ChatBedrockApi chatApi;
|
||||
|
||||
private final BedrockAi21Jurassic2ChatOptions defaultOptions;
|
||||
|
||||
public BedrockAi21Jurassic2ChatClient(Ai21Jurassic2ChatBedrockApi chatApi,
|
||||
BedrockAi21Jurassic2ChatOptions options) {
|
||||
public BedrockAi21Jurassic2ModelCall(Ai21Jurassic2ChatBedrockApi chatApi, BedrockAi21Jurassic2ChatOptions options) {
|
||||
Assert.notNull(chatApi, "Ai21Jurassic2ChatBedrockApi must not be null");
|
||||
Assert.notNull(options, "BedrockAi21Jurassic2ChatOptions must not be null");
|
||||
|
||||
@@ -49,7 +48,7 @@ public class BedrockAi21Jurassic2ChatClient implements ChatClient {
|
||||
this.defaultOptions = options;
|
||||
}
|
||||
|
||||
public BedrockAi21Jurassic2ChatClient(Ai21Jurassic2ChatBedrockApi chatApi) {
|
||||
public BedrockAi21Jurassic2ModelCall(Ai21Jurassic2ChatBedrockApi chatApi) {
|
||||
this(chatApi,
|
||||
BedrockAi21Jurassic2ChatOptions.builder()
|
||||
.withTemperature(0.8f)
|
||||
@@ -114,8 +113,8 @@ public class BedrockAi21Jurassic2ChatClient implements ChatClient {
|
||||
return this;
|
||||
}
|
||||
|
||||
public BedrockAi21Jurassic2ChatClient build() {
|
||||
return new BedrockAi21Jurassic2ChatClient(chatApi,
|
||||
public BedrockAi21Jurassic2ModelCall build() {
|
||||
return new BedrockAi21Jurassic2ModelCall(chatApi,
|
||||
options != null ? options : BedrockAi21Jurassic2ChatOptions.builder().build());
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import org.springframework.ai.bedrock.MessageToPromptConverter;
|
||||
import org.springframework.ai.bedrock.llama.api.LlamaChatBedrockApi;
|
||||
import org.springframework.ai.bedrock.llama.api.LlamaChatBedrockApi.LlamaChatRequest;
|
||||
import org.springframework.ai.bedrock.llama.api.LlamaChatBedrockApi.LlamaChatResponse;
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
@@ -35,25 +35,25 @@ import org.springframework.ai.model.ModelOptionsUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Java {@link ChatClient} and {@link StreamingChatClient} for the Bedrock Llama chat
|
||||
* Java {@link ModelCall} and {@link StreamingChatClient} for the Bedrock Llama chat
|
||||
* generative.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
* @author Wei Jiang
|
||||
* @since 0.8.0
|
||||
*/
|
||||
public class BedrockLlamaChatClient implements ChatClient, StreamingChatClient {
|
||||
public class BedrockLlamaModelCall implements ModelCall, StreamingChatClient {
|
||||
|
||||
private final LlamaChatBedrockApi chatApi;
|
||||
|
||||
private final BedrockLlamaChatOptions defaultOptions;
|
||||
|
||||
public BedrockLlamaChatClient(LlamaChatBedrockApi chatApi) {
|
||||
public BedrockLlamaModelCall(LlamaChatBedrockApi chatApi) {
|
||||
this(chatApi,
|
||||
BedrockLlamaChatOptions.builder().withTemperature(0.8f).withTopP(0.9f).withMaxGenLen(100).build());
|
||||
}
|
||||
|
||||
public BedrockLlamaChatClient(LlamaChatBedrockApi chatApi, BedrockLlamaChatOptions options) {
|
||||
public BedrockLlamaModelCall(LlamaChatBedrockApi chatApi, BedrockLlamaChatOptions options) {
|
||||
Assert.notNull(chatApi, "LlamaChatBedrockApi must not be null");
|
||||
Assert.notNull(options, "BedrockLlamaChatOptions must not be null");
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.springframework.ai.bedrock.titan.api.TitanChatBedrockApi;
|
||||
import org.springframework.ai.bedrock.titan.api.TitanChatBedrockApi.TitanChatRequest;
|
||||
import org.springframework.ai.bedrock.titan.api.TitanChatBedrockApi.TitanChatResponse;
|
||||
import org.springframework.ai.bedrock.titan.api.TitanChatBedrockApi.TitanChatResponseChunk;
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
@@ -39,17 +39,17 @@ import org.springframework.util.Assert;
|
||||
* @author Christian Tzolov
|
||||
* @since 0.8.0
|
||||
*/
|
||||
public class BedrockTitanChatClient implements ChatClient, StreamingChatClient {
|
||||
public class BedrockTitanModelCall implements ModelCall, StreamingChatClient {
|
||||
|
||||
private final TitanChatBedrockApi chatApi;
|
||||
|
||||
private final BedrockTitanChatOptions defaultOptions;
|
||||
|
||||
public BedrockTitanChatClient(TitanChatBedrockApi chatApi) {
|
||||
public BedrockTitanModelCall(TitanChatBedrockApi chatApi) {
|
||||
this(chatApi, BedrockTitanChatOptions.builder().withTemperature(0.8f).build());
|
||||
}
|
||||
|
||||
public BedrockTitanChatClient(TitanChatBedrockApi chatApi, BedrockTitanChatOptions defaultOptions) {
|
||||
public BedrockTitanModelCall(TitanChatBedrockApi chatApi, BedrockTitanChatOptions defaultOptions) {
|
||||
Assert.notNull(chatApi, "ChatApi must not be null");
|
||||
Assert.notNull(defaultOptions, "DefaultOptions must not be null");
|
||||
this.chatApi = chatApi;
|
||||
@@ -38,7 +38,7 @@ public class BedrockAnthropicCreateRequestTests {
|
||||
@Test
|
||||
public void createRequestWithChatOptions() {
|
||||
|
||||
var client = new BedrockAnthropicChatClient(anthropicChatApi,
|
||||
var client = new BedrockAnthropicModelCall(anthropicChatApi,
|
||||
AnthropicChatOptions.builder()
|
||||
.withTemperature(66.6f)
|
||||
.withTopK(66)
|
||||
|
||||
@@ -55,12 +55,12 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@SpringBootTest
|
||||
@EnabledIfEnvironmentVariable(named = "AWS_ACCESS_KEY_ID", matches = ".*")
|
||||
@EnabledIfEnvironmentVariable(named = "AWS_SECRET_ACCESS_KEY", matches = ".*")
|
||||
class BedrockAnthropicChatClientIT {
|
||||
class BedrockAnthropicModelCallIT {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BedrockAnthropicChatClientIT.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(BedrockAnthropicModelCallIT.class);
|
||||
|
||||
@Autowired
|
||||
private BedrockAnthropicChatClient client;
|
||||
private BedrockAnthropicModelCall client;
|
||||
|
||||
@Value("classpath:/prompts/system-message.st")
|
||||
private Resource systemResource;
|
||||
@@ -209,8 +209,8 @@ class BedrockAnthropicChatClientIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BedrockAnthropicChatClient anthropicChatClient(AnthropicChatBedrockApi anthropicApi) {
|
||||
return new BedrockAnthropicChatClient(anthropicApi);
|
||||
public BedrockAnthropicModelCall anthropicChatClient(AnthropicChatBedrockApi anthropicApi) {
|
||||
return new BedrockAnthropicModelCall(anthropicApi);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -37,7 +37,7 @@ public class BedrockAnthropic3CreateRequestTests {
|
||||
@Test
|
||||
public void createRequestWithChatOptions() {
|
||||
|
||||
var client = new BedrockAnthropic3ChatClient(anthropicChatApi,
|
||||
var client = new BedrockAnthropic3ModelCall(anthropicChatApi,
|
||||
Anthropic3ChatOptions.builder()
|
||||
.withTemperature(66.6f)
|
||||
.withTopK(66)
|
||||
|
||||
@@ -59,12 +59,12 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@SpringBootTest
|
||||
@EnabledIfEnvironmentVariable(named = "AWS_ACCESS_KEY_ID", matches = ".*")
|
||||
@EnabledIfEnvironmentVariable(named = "AWS_SECRET_ACCESS_KEY", matches = ".*")
|
||||
class BedrockAnthropic3ChatClientIT {
|
||||
class BedrockAnthropic3ModelCallIT {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BedrockAnthropic3ChatClientIT.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(BedrockAnthropic3ModelCallIT.class);
|
||||
|
||||
@Autowired
|
||||
private BedrockAnthropic3ChatClient client;
|
||||
private BedrockAnthropic3ModelCall client;
|
||||
|
||||
@Value("classpath:/prompts/system-message.st")
|
||||
private Resource systemResource;
|
||||
@@ -228,8 +228,8 @@ class BedrockAnthropic3ChatClientIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BedrockAnthropic3ChatClient anthropicChatClient(Anthropic3ChatBedrockApi anthropicApi) {
|
||||
return new BedrockAnthropic3ChatClient(anthropicApi);
|
||||
public BedrockAnthropic3ModelCall anthropicChatClient(Anthropic3ChatBedrockApi anthropicApi) {
|
||||
return new BedrockAnthropic3ModelCall(anthropicApi);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -45,7 +45,7 @@ public class BedrockCohereChatCreateRequestTests {
|
||||
@Test
|
||||
public void createRequestWithChatOptions() {
|
||||
|
||||
var client = new BedrockCohereChatClient(chatApi,
|
||||
var client = new BedrockCohereModelCall(chatApi,
|
||||
BedrockCohereChatOptions.builder()
|
||||
.withTemperature(66.6f)
|
||||
.withTopK(66)
|
||||
|
||||
@@ -54,10 +54,10 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@SpringBootTest
|
||||
@EnabledIfEnvironmentVariable(named = "AWS_ACCESS_KEY_ID", matches = ".*")
|
||||
@EnabledIfEnvironmentVariable(named = "AWS_SECRET_ACCESS_KEY", matches = ".*")
|
||||
class BedrockCohereChatClientIT {
|
||||
class BedrockCohereModelCallIT {
|
||||
|
||||
@Autowired
|
||||
private BedrockCohereChatClient client;
|
||||
private BedrockCohereModelCall client;
|
||||
|
||||
@Value("classpath:/prompts/system-message.st")
|
||||
private Resource systemResource;
|
||||
@@ -205,8 +205,8 @@ class BedrockCohereChatClientIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BedrockCohereChatClient cohereChatClient(CohereChatBedrockApi cohereApi) {
|
||||
return new BedrockCohereChatClient(cohereApi);
|
||||
public BedrockCohereModelCall cohereChatClient(CohereChatBedrockApi cohereApi) {
|
||||
return new BedrockCohereModelCall(cohereApi);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -49,10 +49,10 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@SpringBootTest
|
||||
@EnabledIfEnvironmentVariable(named = "AWS_ACCESS_KEY_ID", matches = ".*")
|
||||
@EnabledIfEnvironmentVariable(named = "AWS_SECRET_ACCESS_KEY", matches = ".*")
|
||||
class BedrockAi21Jurassic2ChatClientIT {
|
||||
class BedrockAi21Jurassic2ModelCallIT {
|
||||
|
||||
@Autowired
|
||||
private BedrockAi21Jurassic2ChatClient client;
|
||||
private BedrockAi21Jurassic2ModelCall client;
|
||||
|
||||
@Value("classpath:/prompts/system-message.st")
|
||||
private Resource systemResource;
|
||||
@@ -152,9 +152,9 @@ class BedrockAi21Jurassic2ChatClientIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BedrockAi21Jurassic2ChatClient bedrockAi21Jurassic2ChatClient(
|
||||
public BedrockAi21Jurassic2ModelCall bedrockAi21Jurassic2ChatClient(
|
||||
Ai21Jurassic2ChatBedrockApi jurassic2ChatBedrockApi) {
|
||||
return new BedrockAi21Jurassic2ChatClient(jurassic2ChatBedrockApi,
|
||||
return new BedrockAi21Jurassic2ModelCall(jurassic2ChatBedrockApi,
|
||||
BedrockAi21Jurassic2ChatOptions.builder()
|
||||
.withTemperature(0.5f)
|
||||
.withMaxTokens(100)
|
||||
@@ -45,7 +45,7 @@ public class BedrockLlamaCreateRequestTests {
|
||||
@Test
|
||||
public void createRequestWithChatOptions() {
|
||||
|
||||
var client = new BedrockLlamaChatClient(api,
|
||||
var client = new BedrockLlamaModelCall(api,
|
||||
BedrockLlamaChatOptions.builder().withTemperature(66.6f).withMaxGenLen(666).withTopP(0.66f).build());
|
||||
|
||||
var request = client.createRequest(new Prompt("Test message content"));
|
||||
|
||||
@@ -54,10 +54,10 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@SpringBootTest
|
||||
@EnabledIfEnvironmentVariable(named = "AWS_ACCESS_KEY_ID", matches = ".*")
|
||||
@EnabledIfEnvironmentVariable(named = "AWS_SECRET_ACCESS_KEY", matches = ".*")
|
||||
class BedrockLlamaChatClientIT {
|
||||
class BedrockLlamaModelCallIT {
|
||||
|
||||
@Autowired
|
||||
private BedrockLlamaChatClient client;
|
||||
private BedrockLlamaModelCall client;
|
||||
|
||||
@Value("classpath:/prompts/system-message.st")
|
||||
private Resource systemResource;
|
||||
@@ -206,8 +206,8 @@ class BedrockLlamaChatClientIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BedrockLlamaChatClient llamaChatClient(LlamaChatBedrockApi llamaApi) {
|
||||
return new BedrockLlamaChatClient(llamaApi,
|
||||
public BedrockLlamaModelCall llamaChatClient(LlamaChatBedrockApi llamaApi) {
|
||||
return new BedrockLlamaModelCall(llamaApi,
|
||||
BedrockLlamaChatOptions.builder().withTemperature(0.5f).withMaxGenLen(100).withTopP(0.9f).build());
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public class BedrockTitanChatCreateRequestTests {
|
||||
@Test
|
||||
public void createRequestWithChatOptions() {
|
||||
|
||||
var client = new BedrockTitanChatClient(api,
|
||||
var client = new BedrockTitanModelCall(api,
|
||||
BedrockTitanChatOptions.builder()
|
||||
.withTemperature(66.6f)
|
||||
.withTopP(0.66f)
|
||||
|
||||
@@ -55,10 +55,10 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@SpringBootTest
|
||||
@EnabledIfEnvironmentVariable(named = "AWS_ACCESS_KEY_ID", matches = ".*")
|
||||
@EnabledIfEnvironmentVariable(named = "AWS_SECRET_ACCESS_KEY", matches = ".*")
|
||||
class BedrockTitanChatClientIT {
|
||||
class BedrockTitanModelCallIT {
|
||||
|
||||
@Autowired
|
||||
private BedrockTitanChatClient client;
|
||||
private BedrockTitanModelCall client;
|
||||
|
||||
@Value("classpath:/prompts/system-message.st")
|
||||
private Resource systemResource;
|
||||
@@ -211,8 +211,8 @@ class BedrockTitanChatClientIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BedrockTitanChatClient titanChatClient(TitanChatBedrockApi titanApi) {
|
||||
return new BedrockTitanChatClient(titanApi);
|
||||
public BedrockTitanModelCall titanChatClient(TitanChatBedrockApi titanApi) {
|
||||
return new BedrockTitanModelCall(titanApi);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import java.util.Map;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.huggingface.api.TextGenerationInferenceApi;
|
||||
@@ -34,12 +34,12 @@ import org.springframework.ai.huggingface.model.GenerateResponse;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ChatClient} that interfaces with HuggingFace Inference
|
||||
* An implementation of {@link ModelCall} that interfaces with HuggingFace Inference
|
||||
* Endpoints for text generation.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
*/
|
||||
public class HuggingfaceChatClient implements ChatClient {
|
||||
public class HuggingfaceModelCall implements ModelCall {
|
||||
|
||||
/**
|
||||
* Token required for authenticating with the HuggingFace Inference API.
|
||||
@@ -68,11 +68,11 @@ public class HuggingfaceChatClient implements ChatClient {
|
||||
private int maxNewTokens = 1000;
|
||||
|
||||
/**
|
||||
* Constructs a new HuggingfaceChatClient with the specified API token and base path.
|
||||
* Constructs a new HuggingfaceModelCall with the specified API token and base path.
|
||||
* @param apiToken The API token for HuggingFace.
|
||||
* @param basePath The base path for API requests.
|
||||
*/
|
||||
public HuggingfaceChatClient(final String apiToken, String basePath) {
|
||||
public HuggingfaceModelCall(final String apiToken, String basePath) {
|
||||
this.apiToken = apiToken;
|
||||
this.apiClient.setBasePath(basePath);
|
||||
this.apiClient.addDefaultHeader("Authorization", "Bearer " + this.apiToken);
|
||||
@@ -23,7 +23,7 @@ import org.springframework.util.StringUtils;
|
||||
public class HuggingfaceTestConfiguration {
|
||||
|
||||
@Bean
|
||||
public HuggingfaceChatClient huggingfaceChatClient() {
|
||||
public HuggingfaceModelCall huggingfaceChatClient() {
|
||||
String apiKey = System.getenv("HUGGINGFACE_API_KEY");
|
||||
if (!StringUtils.hasText(apiKey)) {
|
||||
throw new IllegalArgumentException(
|
||||
@@ -31,7 +31,7 @@ public class HuggingfaceTestConfiguration {
|
||||
}
|
||||
// Created aws-mistral-7b-instruct-v0-1-805 via
|
||||
// https://ui.endpoints.huggingface.co/
|
||||
HuggingfaceChatClient huggingfaceChatClient = new HuggingfaceChatClient(apiKey,
|
||||
HuggingfaceModelCall huggingfaceChatClient = new HuggingfaceModelCall(apiKey,
|
||||
"https://f6hg7b3cvlmntp5i.us-east-1.aws.endpoints.huggingface.cloud");
|
||||
return huggingfaceChatClient;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
|
||||
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.huggingface.HuggingfaceChatClient;
|
||||
import org.springframework.ai.huggingface.HuggingfaceModelCall;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
@@ -33,7 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
public class ClientIT {
|
||||
|
||||
@Autowired
|
||||
protected HuggingfaceChatClient huggingfaceChatClient;
|
||||
protected HuggingfaceModelCall huggingfaceChatClient;
|
||||
|
||||
@Test
|
||||
void helloWorldCompletion() {
|
||||
|
||||
@@ -101,11 +101,11 @@ public class MistralAiChatOptions implements FunctionCallingOptions, ChatOptions
|
||||
private @JsonProperty("tool_choice") ToolChoice toolChoice;
|
||||
|
||||
/**
|
||||
* MistralAI Tool Function Callbacks to register with the ChatClient. For Prompt
|
||||
* MistralAI Tool Function Callbacks to register with the ModelCall. For Prompt
|
||||
* Options the functionCallbacks are automatically enabled for the duration of the
|
||||
* prompt execution. For Default Options the functionCallbacks are registered but
|
||||
* disabled by default. Use the enableFunctions to set the functions from the registry
|
||||
* to be used by the ChatClient chat completion requests.
|
||||
* to be used by the ModelCall chat completion requests.
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
@JsonIgnore
|
||||
|
||||
@@ -17,7 +17,7 @@ package org.springframework.ai.mistralai;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatClient;
|
||||
@@ -55,9 +55,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
* @author Grogdunn
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public class MistralAiChatClient extends
|
||||
public class MistralAiModelCall extends
|
||||
AbstractFunctionCallSupport<MistralAiApi.ChatCompletionMessage, MistralAiApi.ChatCompletionRequest, ResponseEntity<MistralAiApi.ChatCompletion>>
|
||||
implements ChatClient, StreamingChatClient {
|
||||
implements ModelCall, StreamingChatClient {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@@ -73,7 +73,7 @@ public class MistralAiChatClient extends
|
||||
|
||||
private final RetryTemplate retryTemplate;
|
||||
|
||||
public MistralAiChatClient(MistralAiApi mistralAiApi) {
|
||||
public MistralAiModelCall(MistralAiApi mistralAiApi) {
|
||||
this(mistralAiApi,
|
||||
MistralAiChatOptions.builder()
|
||||
.withTemperature(0.7f)
|
||||
@@ -83,11 +83,11 @@ public class MistralAiChatClient extends
|
||||
.build());
|
||||
}
|
||||
|
||||
public MistralAiChatClient(MistralAiApi mistralAiApi, MistralAiChatOptions options) {
|
||||
public MistralAiModelCall(MistralAiApi mistralAiApi, MistralAiChatOptions options) {
|
||||
this(mistralAiApi, options, null, RetryUtils.DEFAULT_RETRY_TEMPLATE);
|
||||
}
|
||||
|
||||
public MistralAiChatClient(MistralAiApi mistralAiApi, MistralAiChatOptions options,
|
||||
public MistralAiModelCall(MistralAiApi mistralAiApi, MistralAiChatOptions options,
|
||||
FunctionCallbackContext functionCallbackContext, RetryTemplate retryTemplate) {
|
||||
super(functionCallbackContext);
|
||||
Assert.notNull(mistralAiApi, "MistralAiApi must not be null");
|
||||
@@ -32,7 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@EnabledIfEnvironmentVariable(named = "MISTRAL_AI_API_KEY", matches = ".+")
|
||||
public class MistralAiChatCompletionRequestTest {
|
||||
|
||||
MistralAiChatClient chatClient = new MistralAiChatClient(new MistralAiApi("test"));
|
||||
MistralAiModelCall chatClient = new MistralAiModelCall(new MistralAiApi("test"));
|
||||
|
||||
@Test
|
||||
void chatCompletionDefaultRequestTest() {
|
||||
|
||||
@@ -27,7 +27,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatClient;
|
||||
@@ -56,12 +56,12 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
@SpringBootTest(classes = MistralAiTestConfiguration.class)
|
||||
@EnabledIfEnvironmentVariable(named = "MISTRAL_AI_API_KEY", matches = ".+")
|
||||
class MistralAiChatClientIT {
|
||||
class MistralAiModelCallIT {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MistralAiChatClientIT.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(MistralAiModelCallIT.class);
|
||||
|
||||
@Autowired
|
||||
protected ChatClient chatClient;
|
||||
protected ModelCall modelCall;
|
||||
|
||||
@Autowired
|
||||
protected StreamingChatClient streamingChatClient;
|
||||
@@ -90,7 +90,7 @@ class MistralAiChatClientIT {
|
||||
// NOTE: Mistral expects the system message to be before the user message or will
|
||||
// fail with 400 error.
|
||||
Prompt prompt = new Prompt(List.of(systemMessage, userMessage));
|
||||
ChatResponse response = chatClient.call(prompt);
|
||||
ChatResponse response = modelCall.call(prompt);
|
||||
assertThat(response.getResults()).hasSize(1);
|
||||
assertThat(response.getResults().get(0).getOutput().getContent()).contains("Blackbeard");
|
||||
}
|
||||
@@ -108,7 +108,7 @@ class MistralAiChatClientIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template,
|
||||
Map.of("subject", "ice cream flavors", "format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = this.chatClient.call(prompt).getResult();
|
||||
Generation generation = this.modelCall.call(prompt).getResult();
|
||||
|
||||
List<String> list = outputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(list).hasSize(5);
|
||||
@@ -126,7 +126,7 @@ class MistralAiChatClientIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template,
|
||||
Map.of("subject", "an array of numbers from 1 to 9 under they key name 'numbers'", "format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = chatClient.call(prompt).getResult();
|
||||
Generation generation = modelCall.call(prompt).getResult();
|
||||
|
||||
Map<String, Object> result = outputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(result.get("numbers")).isEqualTo(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
|
||||
@@ -148,7 +148,7 @@ class MistralAiChatClientIT {
|
||||
""";
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = chatClient.call(prompt).getResult();
|
||||
Generation generation = modelCall.call(prompt).getResult();
|
||||
|
||||
ActorsFilmsRecord actorsFilms = outputConverter.convert(generation.getOutput().getContent());
|
||||
logger.info("" + actorsFilms);
|
||||
@@ -201,7 +201,7 @@ class MistralAiChatClientIT {
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
ChatResponse response = chatClient.call(new Prompt(messages, promptOptions));
|
||||
ChatResponse response = modelCall.call(new Prompt(messages, promptOptions));
|
||||
|
||||
logger.info("Response: {}", response);
|
||||
|
||||
@@ -82,7 +82,7 @@ public class MistralAiRetryTests {
|
||||
|
||||
private @Mock MistralAiApi mistralAiApi;
|
||||
|
||||
private MistralAiChatClient chatClient;
|
||||
private MistralAiModelCall chatClient;
|
||||
|
||||
private MistralAiEmbeddingClient embeddingClient;
|
||||
|
||||
@@ -92,7 +92,7 @@ public class MistralAiRetryTests {
|
||||
retryListener = new TestRetryListener();
|
||||
retryTemplate.registerListener(retryListener);
|
||||
|
||||
chatClient = new MistralAiChatClient(mistralAiApi,
|
||||
chatClient = new MistralAiModelCall(mistralAiApi,
|
||||
MistralAiChatOptions.builder()
|
||||
.withTemperature(0.7f)
|
||||
.withTopP(1f)
|
||||
|
||||
@@ -41,8 +41,8 @@ public class MistralAiTestConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MistralAiChatClient mistralAiChatClient(MistralAiApi mistralAiApi) {
|
||||
return new MistralAiChatClient(mistralAiApi,
|
||||
public MistralAiModelCall mistralAiChatClient(MistralAiApi mistralAiApi) {
|
||||
return new MistralAiModelCall(mistralAiApi,
|
||||
MistralAiChatOptions.builder().withModel(MistralAiApi.ChatModel.MIXTRAL.getValue()).build());
|
||||
}
|
||||
|
||||
|
||||
@@ -18,10 +18,10 @@ package org.springframework.ai.ollama;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.ollama.metadata.OllamaChatResponseMetadata;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatClient;
|
||||
@@ -39,7 +39,7 @@ import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link ChatClient} implementation for {@literal Ollama}.
|
||||
* {@link ModelCall} implementation for {@literal Ollama}.
|
||||
*
|
||||
* Ollama allows developers to run large language models and generate embeddings locally.
|
||||
* It supports open-source models available on [Ollama AI
|
||||
@@ -52,7 +52,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Christian Tzolov
|
||||
* @since 0.8.0
|
||||
*/
|
||||
public class OllamaChatClient implements ChatClient, StreamingChatClient {
|
||||
public class OllamaModelCall implements ModelCall, StreamingChatClient {
|
||||
|
||||
/**
|
||||
* Low-level Ollama API library.
|
||||
@@ -64,11 +64,11 @@ public class OllamaChatClient implements ChatClient, StreamingChatClient {
|
||||
*/
|
||||
private OllamaOptions defaultOptions;
|
||||
|
||||
public OllamaChatClient(OllamaApi chatApi) {
|
||||
public OllamaModelCall(OllamaApi chatApi) {
|
||||
this(chatApi, OllamaOptions.create().withModel(OllamaOptions.DEFAULT_MODEL));
|
||||
}
|
||||
|
||||
public OllamaChatClient(OllamaApi chatApi, OllamaOptions defaultOptions) {
|
||||
public OllamaModelCall(OllamaApi chatApi, OllamaOptions defaultOptions) {
|
||||
Assert.notNull(chatApi, "OllamaApi must not be null");
|
||||
Assert.notNull(defaultOptions, "DefaultOptions must not be null");
|
||||
this.chatApi = chatApi;
|
||||
@@ -79,7 +79,7 @@ public class OllamaChatClient implements ChatClient, StreamingChatClient {
|
||||
* @deprecated Use {@link OllamaOptions#setModel} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public OllamaChatClient withModel(String model) {
|
||||
public OllamaModelCall withModel(String model) {
|
||||
this.defaultOptions.setModel(model);
|
||||
return this;
|
||||
}
|
||||
@@ -88,7 +88,7 @@ public class OllamaChatClient implements ChatClient, StreamingChatClient {
|
||||
* @deprecated Use {@link OllamaOptions} constructor instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public OllamaChatClient withDefaultOptions(OllamaOptions options) {
|
||||
public OllamaModelCall withDefaultOptions(OllamaOptions options) {
|
||||
this.defaultOptions = options;
|
||||
return this;
|
||||
}
|
||||
@@ -30,7 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
public class OllamaChatRequestTests {
|
||||
|
||||
OllamaChatClient client = new OllamaChatClient(new OllamaApi(),
|
||||
OllamaModelCall client = new OllamaModelCall(new OllamaApi(),
|
||||
new OllamaOptions().withModel("MODEL_NAME").withTopK(99).withTemperature(66.6f).withNumGPU(1));
|
||||
|
||||
@Test
|
||||
@@ -105,7 +105,7 @@ public class OllamaChatRequestTests {
|
||||
@Test
|
||||
public void createRequestWithDefaultOptionsModelOverride() {
|
||||
|
||||
OllamaChatClient client2 = new OllamaChatClient(new OllamaApi(),
|
||||
OllamaModelCall client2 = new OllamaModelCall(new OllamaApi(),
|
||||
new OllamaOptions().withModel("DEFAULT_OPTIONS_MODEL"));
|
||||
|
||||
var request = client2.ollamaChatRequest(new Prompt("Test message content"), true);
|
||||
|
||||
@@ -56,11 +56,11 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@SpringBootTest
|
||||
@Testcontainers
|
||||
@Disabled("For manual smoke testing only.")
|
||||
class OllamaChatClientIT {
|
||||
class OllamaModelCallIT {
|
||||
|
||||
private static String MODEL = "mistral";
|
||||
|
||||
private static final Log logger = LogFactory.getLog(OllamaChatClientIT.class);
|
||||
private static final Log logger = LogFactory.getLog(OllamaModelCallIT.class);
|
||||
|
||||
@Container
|
||||
static OllamaContainer ollamaContainer = new OllamaContainer("ollama/ollama:0.1.32");
|
||||
@@ -77,7 +77,7 @@ class OllamaChatClientIT {
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private OllamaChatClient client;
|
||||
private OllamaModelCall client;
|
||||
|
||||
@Test
|
||||
void roleTest() {
|
||||
@@ -219,8 +219,8 @@ class OllamaChatClientIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OllamaChatClient ollamaChat(OllamaApi ollamaApi) {
|
||||
return new OllamaChatClient(ollamaApi, OllamaOptions.create().withModel(MODEL).withTemperature(0.9f));
|
||||
public OllamaModelCall ollamaChat(OllamaApi ollamaApi) {
|
||||
return new OllamaModelCall(ollamaApi, OllamaOptions.create().withModel(MODEL).withTemperature(0.9f));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -44,11 +44,11 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@SpringBootTest
|
||||
@Testcontainers
|
||||
@Disabled("For manual smoke testing only.")
|
||||
class OllamaChatClientMultimodalIT {
|
||||
class OllamaModelCallMultimodalIT {
|
||||
|
||||
private static String MODEL = "llava";
|
||||
|
||||
private static final Log logger = LogFactory.getLog(OllamaChatClientIT.class);
|
||||
private static final Log logger = LogFactory.getLog(OllamaModelCallIT.class);
|
||||
|
||||
@Container
|
||||
static OllamaContainer ollamaContainer = new OllamaContainer("ollama/ollama:0.1.32");
|
||||
@@ -65,7 +65,7 @@ class OllamaChatClientMultimodalIT {
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private OllamaChatClient client;
|
||||
private OllamaModelCall client;
|
||||
|
||||
@Test
|
||||
void multiModalityTest() throws IOException {
|
||||
@@ -90,8 +90,8 @@ class OllamaChatClientMultimodalIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OllamaChatClient ollamaChat(OllamaApi ollamaApi) {
|
||||
return new OllamaChatClient(ollamaApi, OllamaOptions.create().withModel(MODEL).withTemperature(0.9f));
|
||||
public OllamaModelCall ollamaChat(OllamaApi ollamaApi) {
|
||||
return new OllamaModelCall(ollamaApi, OllamaOptions.create().withModel(MODEL).withTemperature(0.9f));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -134,10 +134,10 @@ public class OpenAiChatOptions implements FunctionCallingOptions, ChatOptions {
|
||||
private @JsonProperty("user") String user;
|
||||
|
||||
/**
|
||||
* OpenAI Tool Function Callbacks to register with the ChatClient.
|
||||
* OpenAI Tool Function Callbacks to register with the ModelCall.
|
||||
* For Prompt Options the functionCallbacks are automatically enabled for the duration of the prompt execution.
|
||||
* For Default Options the functionCallbacks are registered but disabled by default. Use the enableFunctions to set the functions
|
||||
* from the registry to be used by the ChatClient chat completion requests.
|
||||
* from the registry to be used by the ModelCall chat completion requests.
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
@JsonIgnore
|
||||
|
||||
@@ -17,7 +17,7 @@ package org.springframework.ai.openai;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatClient;
|
||||
@@ -58,7 +58,7 @@ import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* {@link ChatClient} and {@link StreamingChatClient} implementation for {@literal OpenAI}
|
||||
* {@link ModelCall} and {@link StreamingChatClient} implementation for {@literal OpenAI}
|
||||
* backed by {@link OpenAiApi}.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
@@ -68,15 +68,15 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
* @author Josh Long
|
||||
* @author Jemin Huh
|
||||
* @author Grogdunn
|
||||
* @see ChatClient
|
||||
* @see ModelCall
|
||||
* @see StreamingChatClient
|
||||
* @see OpenAiApi
|
||||
*/
|
||||
public class OpenAiChatClient extends
|
||||
public class OpenAiModelCall extends
|
||||
AbstractFunctionCallSupport<ChatCompletionMessage, OpenAiApi.ChatCompletionRequest, ResponseEntity<ChatCompletion>>
|
||||
implements ChatClient, StreamingChatClient {
|
||||
implements ModelCall, StreamingChatClient {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(OpenAiChatClient.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(OpenAiModelCall.class);
|
||||
|
||||
/**
|
||||
* The default options used for the chat completion requests.
|
||||
@@ -94,35 +94,35 @@ public class OpenAiChatClient extends
|
||||
private final OpenAiApi openAiApi;
|
||||
|
||||
/**
|
||||
* Creates an instance of the OpenAiChatClient.
|
||||
* Creates an instance of the OpenAiModelCall.
|
||||
* @param openAiApi The OpenAiApi instance to be used for interacting with the OpenAI
|
||||
* Chat API.
|
||||
* @throws IllegalArgumentException if openAiApi is null
|
||||
*/
|
||||
public OpenAiChatClient(OpenAiApi openAiApi) {
|
||||
public OpenAiModelCall(OpenAiApi openAiApi) {
|
||||
this(openAiApi,
|
||||
OpenAiChatOptions.builder().withModel(OpenAiApi.DEFAULT_CHAT_MODEL).withTemperature(0.7f).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes an instance of the OpenAiChatClient.
|
||||
* Initializes an instance of the OpenAiModelCall.
|
||||
* @param openAiApi The OpenAiApi instance to be used for interacting with the OpenAI
|
||||
* Chat API.
|
||||
* @param options The OpenAiChatOptions to configure the chat client.
|
||||
*/
|
||||
public OpenAiChatClient(OpenAiApi openAiApi, OpenAiChatOptions options) {
|
||||
public OpenAiModelCall(OpenAiApi openAiApi, OpenAiChatOptions options) {
|
||||
this(openAiApi, options, null, RetryUtils.DEFAULT_RETRY_TEMPLATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the OpenAiChatClient.
|
||||
* Initializes a new instance of the OpenAiModelCall.
|
||||
* @param openAiApi The OpenAiApi instance to be used for interacting with the OpenAI
|
||||
* Chat API.
|
||||
* @param options The OpenAiChatOptions to configure the chat client.
|
||||
* @param functionCallbackContext The function callback context.
|
||||
* @param retryTemplate The retry template.
|
||||
*/
|
||||
public OpenAiChatClient(OpenAiApi openAiApi, OpenAiChatOptions options,
|
||||
public OpenAiModelCall(OpenAiApi openAiApi, OpenAiChatOptions options,
|
||||
FunctionCallbackContext functionCallbackContext, RetryTemplate retryTemplate) {
|
||||
super(functionCallbackContext);
|
||||
Assert.notNull(openAiApi, "OpenAiApi must not be null");
|
||||
@@ -34,7 +34,7 @@ public class ChatCompletionRequestTests {
|
||||
@Test
|
||||
public void createRequestWithChatOptions() {
|
||||
|
||||
var client = new OpenAiChatClient(new OpenAiApi("TEST"),
|
||||
var client = new OpenAiModelCall(new OpenAiApi("TEST"),
|
||||
OpenAiChatOptions.builder().withModel("DEFAULT_MODEL").withTemperature(66.6f).build());
|
||||
|
||||
var request = client.createRequest(new Prompt("Test message content"), false);
|
||||
@@ -60,7 +60,7 @@ public class ChatCompletionRequestTests {
|
||||
|
||||
final String TOOL_FUNCTION_NAME = "CurrentWeather";
|
||||
|
||||
var client = new OpenAiChatClient(new OpenAiApi("TEST"),
|
||||
var client = new OpenAiModelCall(new OpenAiApi("TEST"),
|
||||
OpenAiChatOptions.builder().withModel("DEFAULT_MODEL").build());
|
||||
|
||||
var request = client.createRequest(new Prompt("Test message content",
|
||||
@@ -90,7 +90,7 @@ public class ChatCompletionRequestTests {
|
||||
|
||||
final String TOOL_FUNCTION_NAME = "CurrentWeather";
|
||||
|
||||
var client = new OpenAiChatClient(new OpenAiApi("TEST"),
|
||||
var client = new OpenAiModelCall(new OpenAiApi("TEST"),
|
||||
OpenAiChatOptions.builder()
|
||||
.withModel("DEFAULT_MODEL")
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
|
||||
@@ -51,8 +51,8 @@ public class OpenAiTestConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiChatClient openAiChatClient(OpenAiApi api) {
|
||||
OpenAiChatClient openAiChatClient = new OpenAiChatClient(api);
|
||||
public OpenAiModelCall openAiChatClient(OpenAiApi api) {
|
||||
OpenAiModelCall openAiChatClient = new OpenAiModelCall(api);
|
||||
return openAiChatClient;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,8 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.openai.OpenAiModelCall;
|
||||
import org.springframework.ai.openai.OpenAiTestConfiguration;
|
||||
import org.springframework.ai.openai.OpenAiChatClient;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingClient;
|
||||
import org.springframework.ai.openai.testutils.AbstractIT;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
@@ -61,7 +61,7 @@ public class AcmeIT extends AbstractIT {
|
||||
private OpenAiEmbeddingClient embeddingClient;
|
||||
|
||||
@Autowired
|
||||
private OpenAiChatClient chatClient;
|
||||
private OpenAiModelCall chatClient;
|
||||
|
||||
@Test
|
||||
void beanTest() {
|
||||
|
||||
@@ -27,7 +27,7 @@ import org.springframework.ai.chat.metadata.PromptMetadata;
|
||||
import org.springframework.ai.chat.metadata.RateLimit;
|
||||
import org.springframework.ai.chat.metadata.Usage;
|
||||
import org.springframework.ai.openai.api.OpenAiApi;
|
||||
import org.springframework.ai.openai.OpenAiChatClient;
|
||||
import org.springframework.ai.openai.OpenAiModelCall;
|
||||
import org.springframework.ai.openai.metadata.support.OpenAiApiResponseHeaders;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -57,7 +57,7 @@ public class OpenAiChatClientWithChatResponseMetadataTests {
|
||||
private static String TEST_API_KEY = "sk-1234567890";
|
||||
|
||||
@Autowired
|
||||
private OpenAiChatClient openAiChatClient;
|
||||
private OpenAiModelCall openAiChatClient;
|
||||
|
||||
@Autowired
|
||||
private MockRestServiceServer server;
|
||||
@@ -171,8 +171,8 @@ public class OpenAiChatClientWithChatResponseMetadataTests {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiChatClient openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiChatClient(openAiApi);
|
||||
public OpenAiModelCall openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiModelCall(openAiApi);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.openai.OpenAiChatClient;
|
||||
import org.springframework.ai.openai.OpenAiModelCall;
|
||||
import org.springframework.ai.openai.OpenAiChatOptions;
|
||||
import org.springframework.ai.openai.api.OpenAiApi;
|
||||
import org.springframework.ai.openai.api.OpenAiApi.ChatCompletionRequest;
|
||||
@@ -41,14 +41,14 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
@SpringBootTest(classes = OpenAiChatClient2IT.Config.class)
|
||||
@SpringBootTest(classes = OpenAiModelCall2IT.Config.class)
|
||||
@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+")
|
||||
public class OpenAiChatClient2IT {
|
||||
public class OpenAiModelCall2IT {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Autowired
|
||||
private OpenAiChatClient openAiChatClient;
|
||||
private OpenAiModelCall openAiChatClient;
|
||||
|
||||
@Test
|
||||
void responseFormatTest() throws JsonMappingException, JsonProcessingException {
|
||||
@@ -99,8 +99,8 @@ public class OpenAiChatClient2IT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiChatClient openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiChatClient(openAiApi);
|
||||
public OpenAiModelCall openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiModelCall(openAiApi);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -60,9 +60,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest(classes = OpenAiTestConfiguration.class)
|
||||
@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+")
|
||||
class OpenAiChatClientIT extends AbstractIT {
|
||||
class OpenAiModelCallIT extends AbstractIT {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(OpenAiChatClientIT.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(OpenAiModelCallIT.class);
|
||||
|
||||
@Value("classpath:/prompts/system-message.st")
|
||||
private Resource systemResource;
|
||||
@@ -74,7 +74,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);
|
||||
Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", "Bob", "voice", "pirate"));
|
||||
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
|
||||
ChatResponse response = chatClient.call(prompt);
|
||||
ChatResponse response = modelCall.call(prompt);
|
||||
assertThat(response.getResults()).hasSize(1);
|
||||
assertThat(response.getResults().get(0).getOutput().getContent()).contains("Blackbeard");
|
||||
// needs fine tuning... evaluateQuestionAndAnswer(request, response, false);
|
||||
@@ -93,7 +93,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template,
|
||||
Map.of("subject", "ice cream flavors", "format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = this.chatClient.call(prompt).getResult();
|
||||
Generation generation = this.modelCall.call(prompt).getResult();
|
||||
|
||||
List<String> list = outputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(list).hasSize(5);
|
||||
@@ -112,7 +112,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template,
|
||||
Map.of("subject", "an array of numbers from 1 to 9 under they key name 'numbers'", "format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = chatClient.call(prompt).getResult();
|
||||
Generation generation = modelCall.call(prompt).getResult();
|
||||
|
||||
Map<String, Object> result = outputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(result.get("numbers")).isEqualTo(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
|
||||
@@ -131,7 +131,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
""";
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = chatClient.call(prompt).getResult();
|
||||
Generation generation = modelCall.call(prompt).getResult();
|
||||
|
||||
ActorsFilms actorsFilms = outputConverter.convert(generation.getOutput().getContent());
|
||||
}
|
||||
@@ -151,7 +151,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
""";
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = chatClient.call(prompt).getResult();
|
||||
Generation generation = modelCall.call(prompt).getResult();
|
||||
|
||||
ActorsFilmsRecord actorsFilms = outputConverter.convert(generation.getOutput().getContent());
|
||||
logger.info("" + actorsFilms);
|
||||
@@ -204,7 +204,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
ChatResponse response = chatClient.call(new Prompt(messages, promptOptions));
|
||||
ChatResponse response = modelCall.call(new Prompt(messages, promptOptions));
|
||||
|
||||
logger.info("Response: {}", response);
|
||||
|
||||
@@ -255,7 +255,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
var userMessage = new UserMessage("Explain what do you see on this picture?",
|
||||
List.of(new Media(MimeTypeUtils.IMAGE_PNG, imageData)));
|
||||
|
||||
var response = chatClient
|
||||
var response = modelCall
|
||||
.call(new Prompt(List.of(userMessage), OpenAiChatOptions.builder().withModel(modelName).build()));
|
||||
|
||||
logger.info(response.getResult().getOutput().getContent());
|
||||
@@ -271,7 +271,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
.of(new Media(MimeTypeUtils.IMAGE_PNG,
|
||||
new URL("https://docs.spring.io/spring-ai/reference/1.0-SNAPSHOT/_images/multimodal.test.png"))));
|
||||
|
||||
ChatResponse response = chatClient
|
||||
ChatResponse response = modelCall
|
||||
.call(new Prompt(List.of(userMessage), OpenAiChatOptions.builder().withModel(modelName).build()));
|
||||
|
||||
logger.info(response.getResult().getOutput().getContent());
|
||||
@@ -39,10 +39,10 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest(classes = OpenAiTestConfiguration.class)
|
||||
@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+")
|
||||
class OpenAiChatClientTypeReferenceBeanOutputConverterIT extends AbstractIT {
|
||||
class OpenAiModelCallTypeReferenceBeanOutputConverterIT extends AbstractIT {
|
||||
|
||||
private static final Logger logger = LoggerFactory
|
||||
.getLogger(OpenAiChatClientTypeReferenceBeanOutputConverterIT.class);
|
||||
.getLogger(OpenAiModelCallTypeReferenceBeanOutputConverterIT.class);
|
||||
|
||||
record ActorsFilmsRecord(String actor, List<String> movies) {
|
||||
}
|
||||
@@ -61,7 +61,7 @@ class OpenAiChatClientTypeReferenceBeanOutputConverterIT extends AbstractIT {
|
||||
""";
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = chatClient.call(prompt).getResult();
|
||||
Generation generation = modelCall.call(prompt).getResult();
|
||||
|
||||
List<ActorsFilmsRecord> actorsFilms = outputConverter.convert(generation.getOutput().getContent());
|
||||
logger.info("" + actorsFilms);
|
||||
@@ -31,7 +31,7 @@ import org.springframework.ai.image.ImageMessage;
|
||||
import org.springframework.ai.image.ImagePrompt;
|
||||
import org.springframework.ai.openai.OpenAiAudioTranscriptionClient;
|
||||
import org.springframework.ai.openai.OpenAiAudioTranscriptionOptions;
|
||||
import org.springframework.ai.openai.OpenAiChatClient;
|
||||
import org.springframework.ai.openai.OpenAiModelCall;
|
||||
import org.springframework.ai.openai.OpenAiChatOptions;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingClient;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingOptions;
|
||||
@@ -107,7 +107,7 @@ public class OpenAiRetryTests {
|
||||
|
||||
private @Mock OpenAiImageApi openAiImageApi;
|
||||
|
||||
private OpenAiChatClient chatClient;
|
||||
private OpenAiModelCall chatClient;
|
||||
|
||||
private OpenAiEmbeddingClient embeddingClient;
|
||||
|
||||
@@ -121,7 +121,7 @@ public class OpenAiRetryTests {
|
||||
retryListener = new TestRetryListener();
|
||||
retryTemplate.registerListener(retryListener);
|
||||
|
||||
chatClient = new OpenAiChatClient(openAiApi, OpenAiChatOptions.builder().build(), null, retryTemplate);
|
||||
chatClient = new OpenAiModelCall(openAiApi, OpenAiChatOptions.builder().build(), null, retryTemplate);
|
||||
embeddingClient = new OpenAiEmbeddingClient(openAiApi, MetadataMode.EMBED,
|
||||
OpenAiEmbeddingOptions.builder().build(), retryTemplate);
|
||||
audioTranscriptionClient = new OpenAiAudioTranscriptionClient(openAiAudioApi,
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.springframework.ai.chat.memory.SystemPromptChatMemoryAugmentor;
|
||||
import org.springframework.ai.embedding.EmbeddingClient;
|
||||
import org.springframework.ai.evaluation.BaseMemoryTest;
|
||||
import org.springframework.ai.evaluation.RelevancyEvaluator;
|
||||
import org.springframework.ai.openai.OpenAiChatClient;
|
||||
import org.springframework.ai.openai.OpenAiModelCall;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingClient;
|
||||
import org.springframework.ai.openai.api.OpenAiApi;
|
||||
import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator;
|
||||
@@ -75,8 +75,8 @@ public class ChatMemoryLongTermSystemPromptIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiChatClient openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiChatClient(openAiApi);
|
||||
public OpenAiModelCall openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiModelCall(openAiApi);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -98,7 +98,7 @@ public class ChatMemoryLongTermSystemPromptIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ChatService memoryChatService(OpenAiChatClient chatClient, VectorStore vectorStore,
|
||||
public ChatService memoryChatService(OpenAiModelCall chatClient, VectorStore vectorStore,
|
||||
TokenCountEstimator tokenCountEstimator) {
|
||||
|
||||
return PromptTransformingChatService.builder(chatClient)
|
||||
@@ -110,7 +110,7 @@ public class ChatMemoryLongTermSystemPromptIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public StreamingChatService memoryStreamingChatService(OpenAiChatClient streamingChatClient,
|
||||
public StreamingChatService memoryStreamingChatService(OpenAiModelCall streamingChatClient,
|
||||
VectorStore vectorStore, TokenCountEstimator tokenCountEstimator) {
|
||||
|
||||
return StreamingPromptTransformingChatService.builder(streamingChatClient)
|
||||
@@ -122,7 +122,7 @@ public class ChatMemoryLongTermSystemPromptIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RelevancyEvaluator relevancyEvaluator(OpenAiChatClient chatClient) {
|
||||
public RelevancyEvaluator relevancyEvaluator(OpenAiModelCall chatClient) {
|
||||
return new RelevancyEvaluator(chatClient);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import org.springframework.ai.chat.memory.LastMaxTokenSizeContentTransformer;
|
||||
import org.springframework.ai.chat.memory.MessageChatMemoryAugmentor;
|
||||
import org.springframework.ai.evaluation.BaseMemoryTest;
|
||||
import org.springframework.ai.evaluation.RelevancyEvaluator;
|
||||
import org.springframework.ai.openai.OpenAiChatClient;
|
||||
import org.springframework.ai.openai.OpenAiModelCall;
|
||||
import org.springframework.ai.openai.api.OpenAiApi;
|
||||
import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator;
|
||||
import org.springframework.ai.tokenizer.TokenCountEstimator;
|
||||
@@ -59,8 +59,8 @@ public class ChatMemoryShortTermMessageListIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiChatClient openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiChatClient(openAiApi);
|
||||
public OpenAiModelCall openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiModelCall(openAiApi);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -74,7 +74,7 @@ public class ChatMemoryShortTermMessageListIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ChatService memoryChatService(OpenAiChatClient chatClient, ChatMemory chatHistory,
|
||||
public ChatService memoryChatService(OpenAiModelCall chatClient, ChatMemory chatHistory,
|
||||
TokenCountEstimator tokenCountEstimator) {
|
||||
|
||||
return PromptTransformingChatService.builder(chatClient)
|
||||
@@ -86,7 +86,7 @@ public class ChatMemoryShortTermMessageListIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public StreamingChatService memoryStreamingChatService(OpenAiChatClient streamingChatClient,
|
||||
public StreamingChatService memoryStreamingChatService(OpenAiModelCall streamingChatClient,
|
||||
ChatMemory chatHistory, TokenCountEstimator tokenCountEstimator) {
|
||||
|
||||
return StreamingPromptTransformingChatService.builder(streamingChatClient)
|
||||
@@ -98,7 +98,7 @@ public class ChatMemoryShortTermMessageListIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RelevancyEvaluator relevancyEvaluator(OpenAiChatClient chatClient) {
|
||||
public RelevancyEvaluator relevancyEvaluator(OpenAiModelCall chatClient) {
|
||||
return new RelevancyEvaluator(chatClient);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.springframework.ai.chat.memory.LastMaxTokenSizeContentTransformer;
|
||||
import org.springframework.ai.chat.memory.SystemPromptChatMemoryAugmentor;
|
||||
import org.springframework.ai.evaluation.BaseMemoryTest;
|
||||
import org.springframework.ai.evaluation.RelevancyEvaluator;
|
||||
import org.springframework.ai.openai.OpenAiChatClient;
|
||||
import org.springframework.ai.openai.OpenAiModelCall;
|
||||
import org.springframework.ai.openai.api.OpenAiApi;
|
||||
import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator;
|
||||
import org.springframework.ai.tokenizer.TokenCountEstimator;
|
||||
@@ -60,8 +60,8 @@ public class ChatMemoryShortTermSystemPromptIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiChatClient openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiChatClient(openAiApi);
|
||||
public OpenAiModelCall openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiModelCall(openAiApi);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -75,7 +75,7 @@ public class ChatMemoryShortTermSystemPromptIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ChatService memoryChatService(OpenAiChatClient chatClient, ChatMemory chatHistory,
|
||||
public ChatService memoryChatService(OpenAiModelCall chatClient, ChatMemory chatHistory,
|
||||
TokenCountEstimator tokenCountEstimator) {
|
||||
|
||||
return PromptTransformingChatService.builder(chatClient)
|
||||
@@ -87,7 +87,7 @@ public class ChatMemoryShortTermSystemPromptIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public StreamingChatService memoryStreamingChatService(OpenAiChatClient streamingChatClient,
|
||||
public StreamingChatService memoryStreamingChatService(OpenAiModelCall streamingChatClient,
|
||||
ChatMemory chatHistory, TokenCountEstimator tokenCountEstimator) {
|
||||
|
||||
return StreamingPromptTransformingChatService.builder(streamingChatClient)
|
||||
@@ -99,7 +99,7 @@ public class ChatMemoryShortTermSystemPromptIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RelevancyEvaluator relevancyEvaluator(OpenAiChatClient chatClient) {
|
||||
public RelevancyEvaluator relevancyEvaluator(OpenAiModelCall chatClient) {
|
||||
return new RelevancyEvaluator(chatClient);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.ai.chat.prompt.transformer.ChatServiceContext;
|
||||
import org.springframework.ai.chat.service.ChatService;
|
||||
import org.springframework.ai.chat.service.PromptTransformingChatService;
|
||||
import org.springframework.ai.openai.OpenAiChatOptions;
|
||||
import org.springframework.ai.openai.OpenAiModelCall;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import org.testcontainers.qdrant.QdrantContainer;
|
||||
@@ -50,10 +51,8 @@ import org.springframework.ai.chat.prompt.transformer.VectorStoreRetriever;
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.document.DocumentTransformer;
|
||||
import org.springframework.ai.embedding.EmbeddingClient;
|
||||
import org.springframework.ai.evaluation.EvaluationRequest;
|
||||
import org.springframework.ai.evaluation.EvaluationResponse;
|
||||
import org.springframework.ai.evaluation.RelevancyEvaluator;
|
||||
import org.springframework.ai.openai.OpenAiChatClient;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingClient;
|
||||
import org.springframework.ai.openai.api.OpenAiApi;
|
||||
import org.springframework.ai.reader.JsonReader;
|
||||
@@ -164,8 +163,8 @@ public class LongShortTermChatMemoryWithRagIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiChatClient openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiChatClient(openAiApi);
|
||||
public OpenAiModelCall openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiModelCall(openAiApi);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -187,7 +186,7 @@ public class LongShortTermChatMemoryWithRagIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ChatService memoryChatService(OpenAiChatClient chatClient, VectorStore vectorStore,
|
||||
public ChatService memoryChatService(OpenAiModelCall chatClient, VectorStore vectorStore,
|
||||
TokenCountEstimator tokenCountEstimator, ChatMemory chatHistory) {
|
||||
|
||||
return PromptTransformingChatService.builder(chatClient)
|
||||
@@ -224,7 +223,7 @@ public class LongShortTermChatMemoryWithRagIT {
|
||||
}
|
||||
|
||||
// @Bean
|
||||
// public StreamingChatService memoryStreamingChatAgent(OpenAiChatClient
|
||||
// public StreamingChatService memoryStreamingChatAgent(OpenAiModelCall
|
||||
// streamingChatClient,
|
||||
// VectorStore vectorStore, TokenCountEstimator tokenCountEstimator, ChatHistory
|
||||
// chatHistory) {
|
||||
@@ -241,7 +240,7 @@ public class LongShortTermChatMemoryWithRagIT {
|
||||
// }
|
||||
|
||||
@Bean
|
||||
public RelevancyEvaluator relevancyEvaluator(OpenAiChatClient chatClient) {
|
||||
public RelevancyEvaluator relevancyEvaluator(OpenAiModelCall chatClient) {
|
||||
// Use GPT 4 as a better model for determining relevancy. gpt 3.5 makes basic
|
||||
// mistakes
|
||||
OpenAiChatOptions openAiChatOptions = OpenAiChatOptions.builder()
|
||||
|
||||
@@ -23,6 +23,7 @@ import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.chat.service.ChatService;
|
||||
import org.springframework.ai.chat.prompt.transformer.TransformerContentType;
|
||||
import org.springframework.ai.document.Document;
|
||||
@@ -31,7 +32,6 @@ import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import org.testcontainers.qdrant.QdrantContainer;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.service.PromptTransformingChatService;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
@@ -39,10 +39,9 @@ import org.springframework.ai.chat.prompt.transformer.ChatServiceContext;
|
||||
import org.springframework.ai.chat.prompt.transformer.QuestionContextAugmentor;
|
||||
import org.springframework.ai.chat.prompt.transformer.VectorStoreRetriever;
|
||||
import org.springframework.ai.embedding.EmbeddingClient;
|
||||
import org.springframework.ai.evaluation.EvaluationRequest;
|
||||
import org.springframework.ai.evaluation.EvaluationResponse;
|
||||
import org.springframework.ai.evaluation.RelevancyEvaluator;
|
||||
import org.springframework.ai.openai.OpenAiChatClient;
|
||||
import org.springframework.ai.openai.OpenAiModelCall;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingClient;
|
||||
import org.springframework.ai.openai.api.OpenAiApi;
|
||||
import org.springframework.ai.reader.JsonReader;
|
||||
@@ -72,7 +71,7 @@ public class OpenAiPromptTransformingChatServiceIT {
|
||||
@Container
|
||||
static QdrantContainer qdrantContainer = new QdrantContainer("qdrant/qdrant:v1.9.2");
|
||||
|
||||
private final ChatClient chatClient;
|
||||
private final ModelCall modelCall;
|
||||
|
||||
private final VectorStore vectorStore;
|
||||
|
||||
@@ -82,9 +81,9 @@ public class OpenAiPromptTransformingChatServiceIT {
|
||||
private ChatService chatService;
|
||||
|
||||
@Autowired
|
||||
public OpenAiPromptTransformingChatServiceIT(ChatClient chatClient, ChatService chatService,
|
||||
public OpenAiPromptTransformingChatServiceIT(ModelCall modelCall, ChatService chatService,
|
||||
VectorStore vectorStore) {
|
||||
this.chatClient = chatClient;
|
||||
this.modelCall = modelCall;
|
||||
this.chatService = chatService;
|
||||
this.vectorStore = vectorStore;
|
||||
}
|
||||
@@ -103,7 +102,7 @@ public class OpenAiPromptTransformingChatServiceIT {
|
||||
OpenAiChatOptions openAiChatOptions = OpenAiChatOptions.builder()
|
||||
.withModel(GPT_4_TURBO_PREVIEW.getValue())
|
||||
.build();
|
||||
var relevancyEvaluator = new RelevancyEvaluator(this.chatClient, openAiChatOptions);
|
||||
var relevancyEvaluator = new RelevancyEvaluator(this.modelCall, openAiChatOptions);
|
||||
|
||||
EvaluationResponse evaluationResponse = relevancyEvaluator.evaluate(chatServiceResponse.toEvaluationRequest());
|
||||
assertTrue(evaluationResponse.isPass(), "Response is not relevant to the question");
|
||||
@@ -146,8 +145,8 @@ public class OpenAiPromptTransformingChatServiceIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ChatClient openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiChatClient(openAiApi);
|
||||
public ModelCall openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiModelCall(openAiApi);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -164,8 +163,8 @@ public class OpenAiPromptTransformingChatServiceIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ChatService chatService(ChatClient chatClient, VectorStore vectorStore) {
|
||||
return PromptTransformingChatService.builder(chatClient)
|
||||
public ChatService chatService(ModelCall modelCall, VectorStore vectorStore) {
|
||||
return PromptTransformingChatService.builder(modelCall)
|
||||
.withRetrievers(List.of(new VectorStoreRetriever(vectorStore, SearchRequest.defaults())))
|
||||
.withAugmentors(List.of(new QuestionContextAugmentor()))
|
||||
.build();
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.StreamingChatClient;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
@@ -43,7 +43,7 @@ public abstract class AbstractIT {
|
||||
private static final Logger logger = LoggerFactory.getLogger(AbstractIT.class);
|
||||
|
||||
@Autowired
|
||||
protected ChatClient chatClient;
|
||||
protected ModelCall modelCall;
|
||||
|
||||
@Autowired
|
||||
protected StreamingChatClient streamingChatClient;
|
||||
@@ -85,12 +85,12 @@ public abstract class AbstractIT {
|
||||
}
|
||||
Message userMessage = userPromptTemplate.createMessage();
|
||||
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
|
||||
String yesOrNo = chatClient.call(prompt).getResult().getOutput().getContent();
|
||||
String yesOrNo = modelCall.call(prompt).getResult().getOutput().getContent();
|
||||
logger.info("Is Answer related to question: " + yesOrNo);
|
||||
if (yesOrNo.equalsIgnoreCase("no")) {
|
||||
SystemMessage notRelatedSystemMessage = new SystemMessage(qaEvaluatorNotRelatedResource);
|
||||
prompt = new Prompt(List.of(userMessage, notRelatedSystemMessage));
|
||||
String reasonForFailure = chatClient.call(prompt).getResult().getOutput().getContent();
|
||||
String reasonForFailure = modelCall.call(prompt).getResult().getOutput().getContent();
|
||||
fail(reasonForFailure);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -24,8 +24,8 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.ai.document.DefaultContentFormatter;
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.openai.OpenAiModelCall;
|
||||
import org.springframework.ai.openai.api.OpenAiApi;
|
||||
import org.springframework.ai.openai.OpenAiChatClient;
|
||||
import org.springframework.ai.transformer.ContentFormatTransformer;
|
||||
import org.springframework.ai.transformer.KeywordMetadataEnricher;
|
||||
import org.springframework.ai.transformer.SummaryMetadataEnricher;
|
||||
@@ -163,18 +163,18 @@ public class MetadataTransformerIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiChatClient openAiChatClient(OpenAiApi openAiApi) {
|
||||
OpenAiChatClient openAiChatClient = new OpenAiChatClient(openAiApi);
|
||||
public OpenAiModelCall openAiChatClient(OpenAiApi openAiApi) {
|
||||
OpenAiModelCall openAiChatClient = new OpenAiModelCall(openAiApi);
|
||||
return openAiChatClient;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public KeywordMetadataEnricher keywordMetadata(OpenAiChatClient aiClient) {
|
||||
public KeywordMetadataEnricher keywordMetadata(OpenAiModelCall aiClient) {
|
||||
return new KeywordMetadataEnricher(aiClient, 5);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SummaryMetadataEnricher summaryMetadata(OpenAiChatClient aiClient) {
|
||||
public SummaryMetadataEnricher summaryMetadata(OpenAiModelCall aiClient) {
|
||||
return new SummaryMetadataEnricher(aiClient,
|
||||
List.of(SummaryType.PREVIOUS, SummaryType.CURRENT, SummaryType.NEXT));
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.model.function.FunctionCallback;
|
||||
import org.springframework.ai.model.function.FunctionCallingOptions;
|
||||
import org.springframework.ai.vertexai.gemini.VertexAiGeminiChatClient.ChatModel;
|
||||
import org.springframework.ai.vertexai.gemini.VertexAiGeminiModelCall.ChatModel;
|
||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -78,10 +78,10 @@ public class VertexAiGeminiChatOptions implements FunctionCallingOptions, ChatOp
|
||||
private @JsonProperty("modelName") String model;
|
||||
|
||||
/**
|
||||
* Tool Function Callbacks to register with the ChatClient.
|
||||
* Tool Function Callbacks to register with the ModelCall.
|
||||
* For Prompt Options the functionCallbacks are automatically enabled for the duration of the prompt execution.
|
||||
* For Default Options the functionCallbacks are registered but disabled by default. Use the enableFunctions to set the functions
|
||||
* from the registry to be used by the ChatClient chat completion requests.
|
||||
* from the registry to be used by the ModelCall chat completion requests.
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
@JsonIgnore
|
||||
|
||||
@@ -32,7 +32,7 @@ import com.google.cloud.vertexai.generativeai.PartMaker;
|
||||
import com.google.cloud.vertexai.generativeai.ResponseStream;
|
||||
import com.google.protobuf.Struct;
|
||||
import com.google.protobuf.util.JsonFormat;
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatClient;
|
||||
@@ -65,9 +65,9 @@ import java.util.stream.Collectors;
|
||||
* @author Grogdunn
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public class VertexAiGeminiChatClient
|
||||
extends AbstractFunctionCallSupport<Content, VertexAiGeminiChatClient.GeminiRequest, GenerateContentResponse>
|
||||
implements ChatClient, StreamingChatClient, DisposableBean {
|
||||
public class VertexAiGeminiModelCall
|
||||
extends AbstractFunctionCallSupport<Content, VertexAiGeminiModelCall.GeminiRequest, GenerateContentResponse>
|
||||
implements ModelCall, StreamingChatClient, DisposableBean {
|
||||
|
||||
private final static boolean IS_RUNTIME_CALL = true;
|
||||
|
||||
@@ -117,7 +117,7 @@ public class VertexAiGeminiChatClient
|
||||
|
||||
}
|
||||
|
||||
public VertexAiGeminiChatClient(VertexAI vertexAI) {
|
||||
public VertexAiGeminiModelCall(VertexAI vertexAI) {
|
||||
this(vertexAI,
|
||||
VertexAiGeminiChatOptions.builder()
|
||||
.withModel(ChatModel.GEMINI_PRO_VISION)
|
||||
@@ -125,11 +125,11 @@ public class VertexAiGeminiChatClient
|
||||
.build());
|
||||
}
|
||||
|
||||
public VertexAiGeminiChatClient(VertexAI vertexAI, VertexAiGeminiChatOptions options) {
|
||||
public VertexAiGeminiModelCall(VertexAI vertexAI, VertexAiGeminiChatOptions options) {
|
||||
this(vertexAI, options, null);
|
||||
}
|
||||
|
||||
public VertexAiGeminiChatClient(VertexAI vertexAI, VertexAiGeminiChatOptions options,
|
||||
public VertexAiGeminiModelCall(VertexAI vertexAI, VertexAiGeminiChatOptions options,
|
||||
FunctionCallbackContext functionCallbackContext) {
|
||||
|
||||
super(functionCallbackContext);
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.ai.vertexai.gemini.aot;
|
||||
|
||||
import org.springframework.ai.vertexai.gemini.VertexAiGeminiChatClient;
|
||||
import org.springframework.ai.vertexai.gemini.VertexAiGeminiModelCall;
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
@@ -34,7 +34,7 @@ public class VertexAiGeminiRuntimeHints implements RuntimeHintsRegistrar {
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
var mcs = MemberCategory.values();
|
||||
for (var tr : findJsonAnnotatedClassesInPackage(VertexAiGeminiChatClient.class))
|
||||
for (var tr : findJsonAnnotatedClassesInPackage(VertexAiGeminiModelCall.class))
|
||||
hints.reflection().registerType(tr, mcs);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,10 +53,10 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@SpringBootTest
|
||||
@EnabledIfEnvironmentVariable(named = "VERTEX_AI_GEMINI_PROJECT_ID", matches = ".*")
|
||||
@EnabledIfEnvironmentVariable(named = "VERTEX_AI_GEMINI_LOCATION", matches = ".*")
|
||||
class VertexAiGeminiChatClientIT {
|
||||
class VertexAiGeminiModelCallIT {
|
||||
|
||||
@Autowired
|
||||
private VertexAiGeminiChatClient client;
|
||||
private VertexAiGeminiModelCall client;
|
||||
|
||||
@Value("classpath:/prompts/system-message.st")
|
||||
private Resource systemResource;
|
||||
@@ -231,10 +231,10 @@ class VertexAiGeminiChatClientIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VertexAiGeminiChatClient vertexAiEmbedding(VertexAI vertexAi) {
|
||||
return new VertexAiGeminiChatClient(vertexAi,
|
||||
public VertexAiGeminiModelCall vertexAiEmbedding(VertexAI vertexAi) {
|
||||
return new VertexAiGeminiModelCall(vertexAi,
|
||||
VertexAiGeminiChatOptions.builder()
|
||||
.withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO_VISION)
|
||||
.withModel(VertexAiGeminiModelCall.ChatModel.GEMINI_PRO_VISION)
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.ai.vertexai.gemini.VertexAiGeminiChatClient;
|
||||
import org.springframework.ai.vertexai.gemini.VertexAiGeminiModelCall;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
|
||||
@@ -38,7 +38,7 @@ class VertexAiGeminiRuntimeHintsTests {
|
||||
RuntimeHints runtimeHints = new RuntimeHints();
|
||||
VertexAiGeminiRuntimeHints vertexAiGeminiRuntimeHints = new VertexAiGeminiRuntimeHints();
|
||||
vertexAiGeminiRuntimeHints.registerHints(runtimeHints, null);
|
||||
Set<TypeReference> jsonAnnotatedClasses = findJsonAnnotatedClassesInPackage(VertexAiGeminiChatClient.class);
|
||||
Set<TypeReference> jsonAnnotatedClasses = findJsonAnnotatedClassesInPackage(VertexAiGeminiModelCall.class);
|
||||
for (TypeReference jsonAnnotatedClass : jsonAnnotatedClasses) {
|
||||
assertThat(runtimeHints).matches(reflection().onType(jsonAnnotatedClass));
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ 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.vertexai.gemini.VertexAiGeminiModelCall;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
@@ -38,7 +39,6 @@ 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.model.function.FunctionCallbackWrapper.Builder.SchemaType;
|
||||
import org.springframework.ai.vertexai.gemini.VertexAiGeminiChatClient;
|
||||
import org.springframework.ai.vertexai.gemini.VertexAiGeminiChatOptions;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
@@ -50,12 +50,12 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@SpringBootTest
|
||||
@EnabledIfEnvironmentVariable(named = "VERTEX_AI_GEMINI_PROJECT_ID", matches = ".*")
|
||||
@EnabledIfEnvironmentVariable(named = "VERTEX_AI_GEMINI_LOCATION", matches = ".*")
|
||||
public class VertexAiGeminiChatClientFunctionCallingIT {
|
||||
public class VertexAiGeminiModelCallFunctionCallingIT {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Autowired
|
||||
private VertexAiGeminiChatClient vertexGeminiClient;
|
||||
private VertexAiGeminiModelCall vertexGeminiClient;
|
||||
|
||||
@AfterEach
|
||||
public void afterEach() {
|
||||
@@ -98,8 +98,8 @@ public class VertexAiGeminiChatClientFunctionCallingIT {
|
||||
""";
|
||||
|
||||
var promptOptions = VertexAiGeminiChatOptions.builder()
|
||||
.withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO)
|
||||
// .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO_1_5_PRO)
|
||||
.withModel(VertexAiGeminiModelCall.ChatModel.GEMINI_PRO)
|
||||
// .withModel(VertexAiGeminiModelCall.ChatModel.GEMINI_PRO_1_5_PRO)
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
.withName("get_current_weather")
|
||||
.withDescription("Get the current weather in a given location")
|
||||
@@ -126,8 +126,8 @@ public class VertexAiGeminiChatClientFunctionCallingIT {
|
||||
List<Message> messages = new ArrayList<>(List.of(userMessage));
|
||||
|
||||
var promptOptions = VertexAiGeminiChatOptions.builder()
|
||||
// .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO_1_5_PRO)
|
||||
.withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO.getValue())
|
||||
// .withModel(VertexAiGeminiModelCall.ChatModel.GEMINI_PRO_1_5_PRO)
|
||||
.withModel(VertexAiGeminiModelCall.ChatModel.GEMINI_PRO.getValue())
|
||||
.withFunctionCallbacks(List.of(
|
||||
FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
.withSchemaType(SchemaType.OPEN_API_SCHEMA)
|
||||
@@ -168,7 +168,7 @@ public class VertexAiGeminiChatClientFunctionCallingIT {
|
||||
List<Message> messages = new ArrayList<>(List.of(userMessage));
|
||||
|
||||
var promptOptions = VertexAiGeminiChatOptions.builder()
|
||||
.withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO)
|
||||
.withModel(VertexAiGeminiModelCall.ChatModel.GEMINI_PRO)
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
.withSchemaType(SchemaType.OPEN_API_SCHEMA)
|
||||
.withName("getCurrentWeather")
|
||||
@@ -224,10 +224,10 @@ public class VertexAiGeminiChatClientFunctionCallingIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VertexAiGeminiChatClient vertexAiEmbedding(VertexAI vertexAi) {
|
||||
return new VertexAiGeminiChatClient(vertexAi,
|
||||
public VertexAiGeminiModelCall vertexAiEmbedding(VertexAI vertexAi) {
|
||||
return new VertexAiGeminiModelCall(vertexAi,
|
||||
VertexAiGeminiChatOptions.builder()
|
||||
.withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO)
|
||||
.withModel(VertexAiGeminiModelCall.ChatModel.GEMINI_PRO)
|
||||
.withTemperature(0.9f)
|
||||
.build());
|
||||
}
|
||||
@@ -18,7 +18,7 @@ package org.springframework.ai.vertexai.palm2;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
@@ -35,18 +35,18 @@ import org.springframework.util.CollectionUtils;
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
public class VertexAiPaLm2ChatClient implements ChatClient {
|
||||
public class VertexAiPaLm2ModelCall implements ModelCall {
|
||||
|
||||
private final VertexAiPaLm2Api vertexAiApi;
|
||||
|
||||
private final VertexAiPaLm2ChatOptions defaultOptions;
|
||||
|
||||
public VertexAiPaLm2ChatClient(VertexAiPaLm2Api vertexAiApi) {
|
||||
public VertexAiPaLm2ModelCall(VertexAiPaLm2Api vertexAiApi) {
|
||||
this(vertexAiApi,
|
||||
VertexAiPaLm2ChatOptions.builder().withTemperature(0.7f).withCandidateCount(1).withTopK(20).build());
|
||||
}
|
||||
|
||||
public VertexAiPaLm2ChatClient(VertexAiPaLm2Api vertexAiApi, VertexAiPaLm2ChatOptions defaultOptions) {
|
||||
public VertexAiPaLm2ModelCall(VertexAiPaLm2Api vertexAiApi, VertexAiPaLm2ChatOptions defaultOptions) {
|
||||
Assert.notNull(defaultOptions, "Default options must not be null!");
|
||||
Assert.notNull(vertexAiApi, "VertexAiPaLm2Api must not be null!");
|
||||
|
||||
@@ -48,7 +48,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class VertexAiPaLm2ChatGenerationClientIT {
|
||||
|
||||
@Autowired
|
||||
private VertexAiPaLm2ChatClient client;
|
||||
private VertexAiPaLm2ModelCall client;
|
||||
|
||||
@Value("classpath:/prompts/system-message.st")
|
||||
private Resource systemResource;
|
||||
@@ -136,8 +136,8 @@ class VertexAiPaLm2ChatGenerationClientIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VertexAiPaLm2ChatClient vertexAiEmbedding(VertexAiPaLm2Api vertexAiApi) {
|
||||
return new VertexAiPaLm2ChatClient(vertexAiApi);
|
||||
public VertexAiPaLm2ModelCall vertexAiEmbedding(VertexAiPaLm2Api vertexAiApi) {
|
||||
return new VertexAiPaLm2ModelCall(vertexAiApi);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
public class VertexAiPaLm2ChatRequestTests {
|
||||
|
||||
VertexAiPaLm2ChatClient client = new VertexAiPaLm2ChatClient(new VertexAiPaLm2Api("bla"));
|
||||
VertexAiPaLm2ModelCall client = new VertexAiPaLm2ModelCall(new VertexAiPaLm2Api("bla"));
|
||||
|
||||
@Test
|
||||
public void createRequestWithDefaultOptions() {
|
||||
|
||||
@@ -18,9 +18,9 @@ package org.springframework.ai.watsonx;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatClient;
|
||||
@@ -35,7 +35,7 @@ import org.springframework.ai.watsonx.utils.MessageToPromptConverter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link ChatClient} implementation for {@literal watsonx.ai}.
|
||||
* {@link ModelCall} implementation for {@literal watsonx.ai}.
|
||||
*
|
||||
* watsonx.ai allows developers to use large language models within a SaaS service. It
|
||||
* supports multiple open-source models as well as IBM created models
|
||||
@@ -48,13 +48,13 @@ import org.springframework.util.Assert;
|
||||
* @author Christian Tzolov
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class WatsonxAiChatClient implements ChatClient, StreamingChatClient {
|
||||
public class WatsonxAiModelCall implements ModelCall, StreamingChatClient {
|
||||
|
||||
private final WatsonxAiApi watsonxAiApi;
|
||||
|
||||
private final WatsonxAiChatOptions defaultOptions;
|
||||
|
||||
public WatsonxAiChatClient(WatsonxAiApi watsonxAiApi) {
|
||||
public WatsonxAiModelCall(WatsonxAiApi watsonxAiApi) {
|
||||
this(watsonxAiApi,
|
||||
WatsonxAiChatOptions.builder()
|
||||
.withTemperature(0.7f)
|
||||
@@ -68,7 +68,7 @@ public class WatsonxAiChatClient implements ChatClient, StreamingChatClient {
|
||||
.build());
|
||||
}
|
||||
|
||||
public WatsonxAiChatClient(WatsonxAiApi watsonxAiApi, WatsonxAiChatOptions defaultOptions) {
|
||||
public WatsonxAiModelCall(WatsonxAiApi watsonxAiApi, WatsonxAiChatOptions defaultOptions) {
|
||||
Assert.notNull(watsonxAiApi, "watsonxAiApi cannot be null");
|
||||
Assert.notNull(defaultOptions, "defaultOptions cannot be null");
|
||||
this.watsonxAiApi = watsonxAiApi;
|
||||
@@ -46,9 +46,9 @@ import static org.mockito.Mockito.when;
|
||||
* @author Pablo Sanchidrian Herrera
|
||||
* @author John Jairo Moreno Rojas
|
||||
*/
|
||||
public class WatsonxAiChatClientTest {
|
||||
public class WatsonxAiModelCallTest {
|
||||
|
||||
WatsonxAiChatClient chatClient = new WatsonxAiChatClient(mock(WatsonxAiApi.class));
|
||||
WatsonxAiModelCall chatClient = new WatsonxAiModelCall(mock(WatsonxAiApi.class));
|
||||
|
||||
@Test
|
||||
public void testCreateRequestWithNoModelId() {
|
||||
@@ -157,7 +157,7 @@ public class WatsonxAiChatClientTest {
|
||||
@Test
|
||||
public void testCallMethod() {
|
||||
WatsonxAiApi mockChatApi = mock(WatsonxAiApi.class);
|
||||
WatsonxAiChatClient client = new WatsonxAiChatClient(mockChatApi);
|
||||
WatsonxAiModelCall client = new WatsonxAiModelCall(mockChatApi);
|
||||
|
||||
Prompt prompt = new Prompt(List.of(new SystemMessage("Your prompt here")),
|
||||
WatsonxAiChatOptions.builder().withModel("google/flan-ul2").build());
|
||||
@@ -186,7 +186,7 @@ public class WatsonxAiChatClientTest {
|
||||
@Test
|
||||
public void testStreamMethod() {
|
||||
WatsonxAiApi mockChatApi = mock(WatsonxAiApi.class);
|
||||
WatsonxAiChatClient client = new WatsonxAiChatClient(mockChatApi);
|
||||
WatsonxAiModelCall client = new WatsonxAiModelCall(mockChatApi);
|
||||
|
||||
Prompt prompt = new Prompt(List.of(new SystemMessage("Your prompt here")),
|
||||
WatsonxAiChatOptions.builder().withModel("google/flan-ul2").build());
|
||||
@@ -15,30 +15,426 @@
|
||||
*/
|
||||
package org.springframework.ai.chat;
|
||||
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.ai.chat.messages.Media;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.SystemMessage;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.model.ModelClient;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.chat.prompt.PromptTemplate;
|
||||
import org.springframework.ai.converter.BeanOutputConverter;
|
||||
import org.springframework.ai.model.function.FunctionCallback;
|
||||
import org.springframework.ai.model.function.FunctionCallbackWrapper;
|
||||
import org.springframework.ai.model.function.FunctionCallingOptionsBuilder;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.StringUtils;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ChatClient extends ModelClient<Prompt, ChatResponse> {
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
default String call(String message) {
|
||||
Prompt prompt = new Prompt(new UserMessage(message));
|
||||
Generation generation = call(prompt).getResult();
|
||||
return (generation != null) ? generation.getOutput().getContent() : "";
|
||||
// todo support plugging in a outputConverter at runtime
|
||||
// todo figure out stream and list methods
|
||||
|
||||
/*
|
||||
* @author Mark Pollack
|
||||
* @author Christian Tzolov
|
||||
* @author Josh Long
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public interface ChatClient {
|
||||
|
||||
static ChatClientBuilder builder(ModelCall connector) {
|
||||
return new ChatClientBuilder(connector);
|
||||
}
|
||||
|
||||
default String call(Message... messages) {
|
||||
Prompt prompt = new Prompt(Arrays.asList(messages));
|
||||
Generation generation = call(prompt).getResult();
|
||||
return (generation != null) ? generation.getOutput().getContent() : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
ChatResponse call(Prompt prompt);
|
||||
|
||||
ChatClientRequest call();
|
||||
|
||||
interface PromptSpec<T> {
|
||||
|
||||
T text(String text);
|
||||
|
||||
T text(Resource text, Charset charset);
|
||||
|
||||
T text(Resource text);
|
||||
|
||||
T params(Map<String, Object> p);
|
||||
|
||||
T param(String k, String v);
|
||||
|
||||
}
|
||||
|
||||
abstract class AbstractPromptSpec<T extends AbstractPromptSpec<T>> implements PromptSpec<T> {
|
||||
|
||||
private String text = "";
|
||||
|
||||
private final Map<String, Object> params = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public T text(String text) {
|
||||
this.text = text;
|
||||
return self();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T text(Resource text, Charset charset) {
|
||||
try {
|
||||
this.text(text.getContentAsString(charset));
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return self();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T text(Resource text) {
|
||||
this.text(text, Charset.defaultCharset());
|
||||
return self();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T param(String k, String v) {
|
||||
this.params.put(k, v);
|
||||
return self();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T params(Map<String, Object> p) {
|
||||
this.params.putAll(p);
|
||||
return self();
|
||||
}
|
||||
|
||||
protected abstract T self();
|
||||
|
||||
protected String text() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
protected Map<String, Object> params() {
|
||||
return this.params;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class UserSpec extends AbstractPromptSpec<UserSpec> implements PromptSpec<UserSpec> {
|
||||
|
||||
private final List<Media> media = new ArrayList<>();
|
||||
|
||||
public UserSpec media(Media... media) {
|
||||
this.media.addAll(Arrays.asList(media));
|
||||
return self();
|
||||
}
|
||||
|
||||
public UserSpec media(MimeType mimeType, URL url) {
|
||||
this.media.add(new Media(mimeType, url));
|
||||
return self();
|
||||
}
|
||||
|
||||
public UserSpec media(MimeType mimeType, Resource resource) {
|
||||
this.media.add(new Media(mimeType, resource));
|
||||
return self();
|
||||
}
|
||||
|
||||
protected List<Media> media() {
|
||||
return this.media;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UserSpec self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SystemSpec extends AbstractPromptSpec<SystemSpec> implements PromptSpec<SystemSpec> {
|
||||
|
||||
@Override
|
||||
protected SystemSpec self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ChatClientRequest {
|
||||
|
||||
private final ModelCall connector;
|
||||
|
||||
private String userText = "";
|
||||
|
||||
private String systemText = "";
|
||||
|
||||
private ChatOptions chatOptions;
|
||||
|
||||
private final List<Media> media = new ArrayList<>();
|
||||
|
||||
private final Set<String> functionNames = new HashSet<>();
|
||||
|
||||
private final List<FunctionCallback> functionCallbacks = new ArrayList<>();
|
||||
|
||||
private final Map<String, Object> userParams = new HashMap<>();
|
||||
|
||||
private final List<Message> messages = new ArrayList<>();
|
||||
|
||||
private final Map<String, Object> systemParams = new HashMap<>();
|
||||
|
||||
public ChatClientRequest(ModelCall connector, String userText, String systemText, List<String> functionNames,
|
||||
List<Media> media, ChatOptions chatOptions) {
|
||||
this.userText = userText;
|
||||
this.systemText = systemText;
|
||||
this.connector = connector;
|
||||
this.functionNames.addAll(functionNames);
|
||||
this.media.addAll(media);
|
||||
this.chatOptions = chatOptions;
|
||||
}
|
||||
|
||||
public ChatClientRequest messages(Message... messages) {
|
||||
this.messages.addAll(List.of(messages));
|
||||
return this;
|
||||
}
|
||||
|
||||
public <T extends ChatOptions> ChatClientRequest options(T options) {
|
||||
this.chatOptions = options;
|
||||
return this;
|
||||
}
|
||||
|
||||
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();
|
||||
this.functionCallbacks.add(fcw);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChatClientRequest functions(String... functions) {
|
||||
this.functionNames.addAll(List.of(functions));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChatClientRequest system(Consumer<SystemSpec> consumer) {
|
||||
var ss = new SystemSpec();
|
||||
consumer.accept(ss);
|
||||
this.systemText = ss.text();
|
||||
this.systemParams.putAll(ss.params());
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChatClientRequest user(Consumer<UserSpec> consumer) {
|
||||
var us = new UserSpec();
|
||||
consumer.accept(us);
|
||||
this.userText = us.text();
|
||||
this.userParams.putAll(us.params());
|
||||
this.media.addAll(us.media());
|
||||
return this;
|
||||
}
|
||||
|
||||
public static class ChatResponseSpec {
|
||||
|
||||
private final ChatClientRequest request;
|
||||
|
||||
private final ModelCall modelCall;
|
||||
|
||||
public ChatResponseSpec(ModelCall modelCall, ChatClientRequest request) {
|
||||
this.modelCall = modelCall;
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
public <T> T single(ParameterizedTypeReference<T> t) {
|
||||
return doSingleWithBeanOutputConverter(new BeanOutputConverter<T>(new ParameterizedTypeReference<>() {
|
||||
}));
|
||||
}
|
||||
|
||||
private <T> T doSingleWithBeanOutputConverter(BeanOutputConverter<T> boc) {
|
||||
var processedUserText = this.request.userText + System.lineSeparator() + System.lineSeparator()
|
||||
+ boc.getFormat();
|
||||
var chatResponse = doGetChatResponse(processedUserText);
|
||||
var stringResponse = chatResponse.getResult().getOutput().getContent();
|
||||
return boc.convert(stringResponse);
|
||||
}
|
||||
|
||||
public <T> T single(Class<T> clzz) {
|
||||
Assert.notNull(clzz, "the class must be non-null");
|
||||
var boc = new BeanOutputConverter<T>(clzz);
|
||||
return doSingleWithBeanOutputConverter(boc);
|
||||
}
|
||||
|
||||
private ChatResponse doGetChatResponse(String processedUserText) {
|
||||
|
||||
var messages = new ArrayList<Message>();
|
||||
var textsAreValid = (StringUtils.hasText(processedUserText)
|
||||
|| StringUtils.hasText(this.request.systemText));
|
||||
var messagesAreValid = !this.request.messages.isEmpty();
|
||||
|
||||
Assert.state(!(messagesAreValid && textsAreValid), "you must specify either " + Message.class.getName()
|
||||
+ " instances or user/system texts, but not both");
|
||||
|
||||
if (textsAreValid) {
|
||||
|
||||
var userMessage = new UserMessage(
|
||||
new PromptTemplate(processedUserText, this.request.userParams).render(),
|
||||
this.request.media);
|
||||
|
||||
var systemMessage = new SystemMessage(
|
||||
new PromptTemplate(this.request.systemText, this.request.systemParams).render());
|
||||
|
||||
messages.add(systemMessage);
|
||||
messages.add(userMessage);
|
||||
|
||||
}
|
||||
else {
|
||||
messages.addAll(this.request.messages);
|
||||
}
|
||||
if (this.request.chatOptions instanceof FunctionCallingOptionsBuilder.PortableFunctionCallingOptions functionCallingOptions) {
|
||||
if (!this.request.functionNames.isEmpty()) {
|
||||
functionCallingOptions.setFunctions(this.request.functionNames);
|
||||
}
|
||||
if (!this.request.functionCallbacks.isEmpty()) {
|
||||
functionCallingOptions.setFunctionCallbacks(this.request.functionCallbacks);
|
||||
}
|
||||
}
|
||||
var prompt = new Prompt(messages, this.request.chatOptions);
|
||||
return this.modelCall.call(prompt);
|
||||
}
|
||||
|
||||
public ChatResponse chatResponse() {
|
||||
return doGetChatResponse(this.request.userText);
|
||||
}
|
||||
|
||||
public <T> Flux<T> stream(Class<T> t) {
|
||||
notSupported();
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> Flux<T> stream(ParameterizedTypeReference<T> t) {
|
||||
notSupported();
|
||||
return Flux.empty();
|
||||
}
|
||||
|
||||
public String content() {
|
||||
return doGetChatResponse(this.request.userText).getResult().getOutput().getContent();
|
||||
}
|
||||
|
||||
public <T> Collection<T> list(Class<T> clzz) {
|
||||
// todo move to the new ParameterizedTypeReference ready
|
||||
// BeanOutputConverter
|
||||
notSupported();
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> Collection<T> list(ParameterizedTypeReference<Collection<T>> ptr) {
|
||||
notSupported();
|
||||
return List.of();
|
||||
}
|
||||
|
||||
private static void notSupported() {
|
||||
throw new RuntimeException("this operation is not supported");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ChatResponseSpec chat() {
|
||||
return new ChatResponseSpec(this.connector, this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ChatClientBuilder {
|
||||
|
||||
private final ModelCall modelCall;
|
||||
|
||||
private final List<Media> defaultMedia = new ArrayList<>();
|
||||
|
||||
private final List<String> defaultFunctionsNames = new ArrayList<>();
|
||||
|
||||
private final List<FunctionCallback> defaultFunctionCallbacks = new ArrayList<>();
|
||||
|
||||
private String defaultSystem;
|
||||
|
||||
private String defaultUser;
|
||||
|
||||
ChatClientBuilder(ModelCall modelCall) {
|
||||
Assert.notNull(modelCall, "the " + ModelCall.class.getName() + " must be non-null");
|
||||
this.modelCall = modelCall;
|
||||
}
|
||||
|
||||
public ChatClient build() {
|
||||
return new DefaultChatClient(this.modelCall, this.defaultSystem, this.defaultUser,
|
||||
this.defaultFunctionsNames, this.defaultMedia);
|
||||
}
|
||||
|
||||
public ChatClientBuilder defaultSystem(Resource resource) {
|
||||
return this.defaultSystem(resource, Charset.defaultCharset());
|
||||
}
|
||||
|
||||
public ChatClientBuilder defaultSystem(Resource resource, Charset charset) {
|
||||
try {
|
||||
this.defaultSystem = resource.getContentAsString(charset);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChatClientBuilder defaultSystem(String systemText) {
|
||||
this.defaultSystem = systemText;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChatClientBuilder defaultFunctions(String... functionNames) {
|
||||
this.defaultFunctionsNames.addAll(List.of(functionNames));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChatClientBuilder defaultFunctions(List<FunctionCallback> functions) {
|
||||
this.defaultFunctionCallbacks.addAll(functions);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChatClientBuilder defaultUser(Resource userText) {
|
||||
return this.defaultUser(userText, Charset.defaultCharset());
|
||||
}
|
||||
|
||||
public ChatClientBuilder defaultUser(Resource userText, Charset charset) {
|
||||
try {
|
||||
this.defaultUser = userText.getContentAsString(charset);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChatClientBuilder defaultUser(String userText) {
|
||||
this.defaultUser = userText;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Deprecated(since = "1.0.0 M1", forRemoval = true)
|
||||
default String call(String message) {
|
||||
var prompt = new Prompt(new UserMessage(message));
|
||||
var generation = call(prompt).getResult();
|
||||
return (generation != null) ? generation.getOutput().getContent() : "";
|
||||
}
|
||||
|
||||
@Deprecated(since = "1.0.0 M1", forRemoval = true)
|
||||
default String call(Message... messages) {
|
||||
var prompt = new Prompt(Arrays.asList(messages));
|
||||
var generation = call(prompt).getResult();
|
||||
return (generation != null) ? generation.getOutput().getContent() : "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.springframework.ai.chat;
|
||||
|
||||
import org.springframework.ai.chat.messages.Media;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Mark Pollack
|
||||
* @author Christian Tzolov
|
||||
* @author Josh Long
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
class DefaultChatClient implements ChatClient {
|
||||
|
||||
private final ModelCall modelCall;
|
||||
|
||||
private final String userText, systemText;
|
||||
|
||||
private final List<String> functionNames;
|
||||
|
||||
private final List<Media> media;
|
||||
|
||||
public DefaultChatClient(ModelCall modelCall, String defaultSystemPrompt, String defaultUserPrompt,
|
||||
List<String> defaultFunctions, List<Media> defaultMedia) {
|
||||
this.modelCall = modelCall;
|
||||
this.userText = defaultUserPrompt;
|
||||
this.systemText = defaultSystemPrompt;
|
||||
this.functionNames = defaultFunctions;
|
||||
this.media = defaultMedia;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatClientRequest call() {
|
||||
return new ChatClientRequest(this.modelCall, this.userText, this.systemText, this.functionNames, this.media,
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
* use the new fluid DSL starting in {@link #call()}
|
||||
* @param prompt the {@link Prompt prompt} object
|
||||
* @return a {@link ChatResponse chat response}
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0 M1")
|
||||
@Override
|
||||
public ChatResponse call(Prompt prompt) {
|
||||
return this.modelCall.call(prompt);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.chat;
|
||||
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.model.ModelClient;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ModelCall extends ModelClient<Prompt, ChatResponse> {
|
||||
|
||||
default String call(String message) {
|
||||
Prompt prompt = new Prompt(new UserMessage(message));
|
||||
Generation generation = call(prompt).getResult();
|
||||
return (generation != null) ? generation.getOutput().getContent() : "";
|
||||
}
|
||||
|
||||
default String call(Message... messages) {
|
||||
Prompt prompt = new Prompt(Arrays.asList(messages));
|
||||
Generation generation = call(prompt).getResult();
|
||||
return (generation != null) ? generation.getOutput().getContent() : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
ChatResponse call(Prompt prompt);
|
||||
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.ai.chat.service;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.transformer.ChatServiceContext;
|
||||
import org.springframework.ai.chat.prompt.transformer.PromptTransformer;
|
||||
@@ -35,7 +35,7 @@ import java.util.Objects;
|
||||
*/
|
||||
public class PromptTransformingChatService implements ChatService {
|
||||
|
||||
private ChatClient chatClient;
|
||||
private ModelCall modelCall;
|
||||
|
||||
private List<PromptTransformer> retrievers;
|
||||
|
||||
@@ -45,19 +45,19 @@ public class PromptTransformingChatService implements ChatService {
|
||||
|
||||
private List<ChatServiceListener> chatServiceListeners;
|
||||
|
||||
public PromptTransformingChatService(ChatClient chatClient, List<PromptTransformer> retrievers,
|
||||
public PromptTransformingChatService(ModelCall modelCall, List<PromptTransformer> retrievers,
|
||||
List<PromptTransformer> documentPostProcessors, List<PromptTransformer> augmentors,
|
||||
List<ChatServiceListener> chatServiceListeners) {
|
||||
Objects.requireNonNull(chatClient, "chatClient must not be null");
|
||||
this.chatClient = chatClient;
|
||||
Objects.requireNonNull(modelCall, "modelCall must not be null");
|
||||
this.modelCall = modelCall;
|
||||
this.retrievers = retrievers;
|
||||
this.documentPostProcessors = documentPostProcessors;
|
||||
this.augmentors = augmentors;
|
||||
this.chatServiceListeners = chatServiceListeners;
|
||||
}
|
||||
|
||||
public static Builder builder(ChatClient chatClient) {
|
||||
return new Builder().withChatClient(chatClient);
|
||||
public static Builder builder(ModelCall modelCall) {
|
||||
return new Builder().withChatClient(modelCall);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -86,7 +86,7 @@ public class PromptTransformingChatService implements ChatService {
|
||||
}
|
||||
|
||||
// Perform generation
|
||||
ChatResponse chatResponse = this.chatClient.call(chatServiceContext.getPrompt());
|
||||
ChatResponse chatResponse = this.modelCall.call(chatServiceContext.getPrompt());
|
||||
|
||||
// Invoke Listeners onComplete
|
||||
ChatServiceResponse chatServiceResponse = new ChatServiceResponse(chatServiceContext, chatResponse);
|
||||
@@ -98,7 +98,7 @@ public class PromptTransformingChatService implements ChatService {
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private ChatClient chatClient;
|
||||
private ModelCall modelCall;
|
||||
|
||||
private List<PromptTransformer> retrievers = new ArrayList<>();
|
||||
|
||||
@@ -108,8 +108,8 @@ public class PromptTransformingChatService implements ChatService {
|
||||
|
||||
private List<ChatServiceListener> chatServiceListeners = new ArrayList<>();
|
||||
|
||||
public Builder withChatClient(ChatClient chatClient) {
|
||||
this.chatClient = chatClient;
|
||||
public Builder withChatClient(ModelCall modelCall) {
|
||||
this.modelCall = modelCall;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ public class PromptTransformingChatService implements ChatService {
|
||||
}
|
||||
|
||||
public PromptTransformingChatService build() {
|
||||
return new PromptTransformingChatService(chatClient, retrievers, documentPostProcessors, augmentors,
|
||||
return new PromptTransformingChatService(modelCall, retrievers, documentPostProcessors, augmentors,
|
||||
chatServiceListeners);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package org.springframework.ai.evaluation;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.MessageType;
|
||||
@@ -31,14 +31,14 @@ public class RelevancyEvaluator implements Evaluator {
|
||||
|
||||
private final ChatOptions chatOptions;
|
||||
|
||||
private ChatClient chatClient;
|
||||
private ModelCall modelCall;
|
||||
|
||||
public RelevancyEvaluator(ChatClient chatClient) {
|
||||
this(chatClient, ChatOptionsBuilder.builder().build());
|
||||
public RelevancyEvaluator(ModelCall modelCall) {
|
||||
this(modelCall, ChatOptionsBuilder.builder().build());
|
||||
}
|
||||
|
||||
public RelevancyEvaluator(ChatClient chatClient, ChatOptions chatOptions) {
|
||||
this.chatClient = chatClient;
|
||||
public RelevancyEvaluator(ModelCall modelCall, ChatOptions chatOptions) {
|
||||
this.modelCall = modelCall;
|
||||
this.chatOptions = chatOptions;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public class RelevancyEvaluator implements Evaluator {
|
||||
Message message = promptTemplate
|
||||
.createMessage(Map.of("query", query, "response", response, "context", context));
|
||||
|
||||
ChatResponse chatResponse = this.chatClient.call(new Prompt(message, this.chatOptions));
|
||||
ChatResponse chatResponse = this.modelCall.call(new Prompt(message, this.chatOptions));
|
||||
|
||||
var evaluationResponse = chatResponse.getResult().getOutput().getContent();
|
||||
boolean passing = false;
|
||||
|
||||
@@ -54,7 +54,7 @@ abstract class AbstractFunctionCallback<I, O> implements Function<I, O>, Functio
|
||||
/**
|
||||
* Constructs a new {@link AbstractFunctionCallback} with the given name, description,
|
||||
* input type and default object mapper.
|
||||
* @param name Function name. Should be unique within the ChatClient's function
|
||||
* @param name Function name. Should be unique within the ModelCall's function
|
||||
* registry.
|
||||
* @param description Function description. Used as a "system prompt" by the model to
|
||||
* decide if the function should be called.
|
||||
|
||||
@@ -24,32 +24,32 @@ import java.util.Set;
|
||||
public interface FunctionCallingOptions {
|
||||
|
||||
/**
|
||||
* Function Callbacks to be registered with the ChatClient. For Prompt Options the
|
||||
* Function Callbacks to be registered with the ModelCall. For Prompt Options the
|
||||
* functionCallbacks are automatically enabled for the duration of the prompt
|
||||
* execution. For Default Options the FunctionCallbacks are registered but disabled by
|
||||
* default. You have to use "functions" property to list the function names from the
|
||||
* ChatClient registry to be used in the chat completion requests.
|
||||
* @return Return the Function Callbacks to be registered with the ChatClient.
|
||||
* ModelCall registry to be used in the chat completion requests.
|
||||
* @return Return the Function Callbacks to be registered with the ModelCall.
|
||||
*/
|
||||
List<FunctionCallback> getFunctionCallbacks();
|
||||
|
||||
/**
|
||||
* Set the Function Callbacks to be registered with the ChatClient.
|
||||
* Set the Function Callbacks to be registered with the ModelCall.
|
||||
* @param functionCallbacks the Function Callbacks to be registered with the
|
||||
* ChatClient.
|
||||
* ModelCall.
|
||||
*/
|
||||
void setFunctionCallbacks(List<FunctionCallback> functionCallbacks);
|
||||
|
||||
/**
|
||||
* @return List of function names from the ChatClient registry to be used in the next
|
||||
* @return List of function names from the ModelCall registry to be used in the next
|
||||
* chat completion requests.
|
||||
*/
|
||||
Set<String> getFunctions();
|
||||
|
||||
/**
|
||||
* Set the list of function names from the ChatClient registry to be used in the next
|
||||
* Set the list of function names from the ModelCall registry to be used in the next
|
||||
* chat completion requests.
|
||||
* @param functions the list of function names from the ChatClient registry to be used
|
||||
* @param functions the list of function names from the ModelCall registry to be used
|
||||
* in the next chat completion requests.
|
||||
*/
|
||||
void setFunctions(Set<String> functions);
|
||||
|
||||
@@ -18,7 +18,7 @@ package org.springframework.ai.transformer;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.document.DocumentTransformer;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
@@ -43,18 +43,18 @@ public class KeywordMetadataEnricher implements DocumentTransformer {
|
||||
/**
|
||||
* Model predictor
|
||||
*/
|
||||
private final ChatClient chatClient;
|
||||
private final ModelCall modelCall;
|
||||
|
||||
/**
|
||||
* The number of keywords to extract.
|
||||
*/
|
||||
private final int keywordCount;
|
||||
|
||||
public KeywordMetadataEnricher(ChatClient chatClient, int keywordCount) {
|
||||
Assert.notNull(chatClient, "ChatClient must not be null");
|
||||
public KeywordMetadataEnricher(ModelCall modelCall, int keywordCount) {
|
||||
Assert.notNull(modelCall, "ModelCall must not be null");
|
||||
Assert.isTrue(keywordCount >= 1, "Document count must be >= 1");
|
||||
|
||||
this.chatClient = chatClient;
|
||||
this.modelCall = modelCall;
|
||||
this.keywordCount = keywordCount;
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ public class KeywordMetadataEnricher implements DocumentTransformer {
|
||||
|
||||
var template = new PromptTemplate(String.format(KEYWORDS_TEMPLATE, keywordCount));
|
||||
Prompt prompt = template.create(Map.of(CONTEXT_STR_PLACEHOLDER, document.getContent()));
|
||||
String keywords = this.chatClient.call(prompt).getResult().getOutput().getContent();
|
||||
String keywords = this.modelCall.call(prompt).getResult().getOutput().getContent();
|
||||
document.getMetadata().putAll(Map.of(EXCERPT_KEYWORDS_METADATA_KEY, keywords));
|
||||
}
|
||||
return documents;
|
||||
|
||||
@@ -20,7 +20,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.document.DocumentTransformer;
|
||||
import org.springframework.ai.document.MetadataMode;
|
||||
@@ -62,7 +62,7 @@ public class SummaryMetadataEnricher implements DocumentTransformer {
|
||||
/**
|
||||
* AI client.
|
||||
*/
|
||||
private final ChatClient chatClient;
|
||||
private final ModelCall modelCall;
|
||||
|
||||
/**
|
||||
* Number of documents from front to use for title extraction.
|
||||
@@ -76,16 +76,16 @@ public class SummaryMetadataEnricher implements DocumentTransformer {
|
||||
*/
|
||||
private final String summaryTemplate;
|
||||
|
||||
public SummaryMetadataEnricher(ChatClient chatClient, List<SummaryType> summaryTypes) {
|
||||
this(chatClient, summaryTypes, DEFAULT_SUMMARY_EXTRACT_TEMPLATE, MetadataMode.ALL);
|
||||
public SummaryMetadataEnricher(ModelCall modelCall, List<SummaryType> summaryTypes) {
|
||||
this(modelCall, summaryTypes, DEFAULT_SUMMARY_EXTRACT_TEMPLATE, MetadataMode.ALL);
|
||||
}
|
||||
|
||||
public SummaryMetadataEnricher(ChatClient chatClient, List<SummaryType> summaryTypes, String summaryTemplate,
|
||||
public SummaryMetadataEnricher(ModelCall modelCall, List<SummaryType> summaryTypes, String summaryTemplate,
|
||||
MetadataMode metadataMode) {
|
||||
Assert.notNull(chatClient, "ChatClient must not be null");
|
||||
Assert.notNull(modelCall, "ModelCall must not be null");
|
||||
Assert.hasText(summaryTemplate, "Summary template must not be empty");
|
||||
|
||||
this.chatClient = chatClient;
|
||||
this.modelCall = modelCall;
|
||||
this.summaryTypes = CollectionUtils.isEmpty(summaryTypes) ? List.of(SummaryType.CURRENT) : summaryTypes;
|
||||
this.metadataMode = metadataMode;
|
||||
this.summaryTemplate = summaryTemplate;
|
||||
@@ -101,7 +101,7 @@ public class SummaryMetadataEnricher implements DocumentTransformer {
|
||||
|
||||
Prompt prompt = new PromptTemplate(this.summaryTemplate)
|
||||
.create(Map.of(CONTEXT_STR_PLACEHOLDER, documentContext));
|
||||
documentSummaries.add(this.chatClient.call(prompt).getResult().getOutput().getContent());
|
||||
documentSummaries.add(this.modelCall.call(prompt).getResult().getOutput().getContent());
|
||||
}
|
||||
|
||||
for (int i = 0; i < documentSummaries.size(); i++) {
|
||||
|
||||
@@ -34,12 +34,12 @@ import org.springframework.ai.chat.messages.AssistantMessage;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
|
||||
/**
|
||||
* Unit Tests for {@link ChatClient}.
|
||||
* Unit Tests for {@link ModelCall}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @since 0.2.0
|
||||
*/
|
||||
class ChatClientTests {
|
||||
class ModelCallTests {
|
||||
|
||||
@Test
|
||||
void generateWithStringCallsGenerateWithPromptAndReturnsResponseCorrectly() {
|
||||
@@ -47,7 +47,7 @@ class ChatClientTests {
|
||||
String userMessage = "Zero Wing";
|
||||
String responseMessage = "All your bases are belong to us";
|
||||
|
||||
ChatClient mockClient = Mockito.mock(ChatClient.class);
|
||||
ModelCall mockClient = Mockito.mock(ModelCall.class);
|
||||
|
||||
AssistantMessage mockAssistantMessage = Mockito.mock(AssistantMessage.class);
|
||||
when(mockAssistantMessage.getContent()).thenReturn(responseMessage);
|
||||
@@ -25,7 +25,7 @@ import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ModelCall;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatClient;
|
||||
@@ -48,7 +48,7 @@ import static org.mockito.Mockito.when;
|
||||
public class ChatMemoryTests {
|
||||
|
||||
@Mock
|
||||
ChatClient chatClient;
|
||||
ModelCall modelCall;
|
||||
|
||||
@Mock
|
||||
StreamingChatClient streamingChatClient;
|
||||
@@ -61,7 +61,7 @@ public class ChatMemoryTests {
|
||||
|
||||
ChatMemory chatHistory = new InMemoryChatMemory();
|
||||
|
||||
PromptTransformingChatService chatService = PromptTransformingChatService.builder(chatClient)
|
||||
PromptTransformingChatService chatService = PromptTransformingChatService.builder(modelCall)
|
||||
.withRetrievers(List.of(ChatMemoryRetriever.builder().withChatHistory(chatHistory).build()))
|
||||
.withContentPostProcessors(
|
||||
List.of(new LastMaxTokenSizeContentTransformer(new JTokkitTokenCountEstimator(), 10)))
|
||||
@@ -77,7 +77,7 @@ public class ChatMemoryTests {
|
||||
|
||||
ChatMemory chatHistory = new InMemoryChatMemory();
|
||||
|
||||
PromptTransformingChatService chatService = PromptTransformingChatService.builder(chatClient)
|
||||
PromptTransformingChatService chatService = PromptTransformingChatService.builder(modelCall)
|
||||
.withRetrievers(List.of(new ChatMemoryRetriever(chatHistory)))
|
||||
.withContentPostProcessors(
|
||||
List.of(new LastMaxTokenSizeContentTransformer(new JTokkitTokenCountEstimator(), 10)))
|
||||
@@ -90,7 +90,7 @@ public class ChatMemoryTests {
|
||||
|
||||
public void chatClientUserMessages(PromptTransformingChatService chatService, ChatMemory chatHistory) {
|
||||
|
||||
when(chatClient.call(promptCaptor.capture()))
|
||||
when(modelCall.call(promptCaptor.capture()))
|
||||
.thenReturn(new ChatResponse(List.of(new Generation("assistant:1"))))
|
||||
.thenReturn(new ChatResponse(List.of(new Generation("assistant:2"))))
|
||||
.thenReturn(new ChatResponse(List.of(new Generation("assistant:3"))));
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
link:https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html[Amazon Bedrock] is a managed service that provides foundation models from various AI providers, available through a unified API.
|
||||
|
||||
Spring AI supports https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids-arns.html[all the Chat and Embedding AI models] available through Amazon Bedrock by implementing the Spring interfaces `ChatClient`, `StreamingChatClient`, and `EmbeddingClient`.
|
||||
Spring AI supports https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids-arns.html[all the Chat and Embedding AI models] available through Amazon Bedrock by implementing the Spring interfaces `ModelCall`, `StreamingChatClient`, and `EmbeddingClient`.
|
||||
|
||||
Additionally, Spring AI provides Spring Auto-Configurations and Boot Starters for all clients, making it easy to bootstrap and configure for the Bedrock models.
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ TIP: In addition to the model specific https://github.com/spring-projects/spring
|
||||
|
||||
== Function Calling
|
||||
|
||||
You can register custom Java functions with the `AnthropicChatClient` and have the Anthropic Claude model intelligently choose to output a JSON object containing arguments to call one or many of the registered functions.
|
||||
You can register custom Java functions with the `AnthropicModelCall` and have the Anthropic Claude model intelligently choose to output a JSON object containing arguments to call one or many of the registered functions.
|
||||
This is a powerful technique to connect the LLM capabilities with external tools and APIs.
|
||||
Read more about xref:api/chat/functions/anthropic-chat-functions.adoc[Anthropic Function Calling].
|
||||
|
||||
@@ -194,7 +194,7 @@ spring.ai.anthropic.chat.options.max-tokens=450
|
||||
|
||||
TIP: replace the `api-key` with your Anthropic credentials.
|
||||
|
||||
This will create a `AnthropicChatClient` implementation that you can inject into your class.
|
||||
This will create a `AnthropicModelCall` implementation that you can inject into your class.
|
||||
Here is an example of a simple `@Controller` class that uses the chat client for text generations.
|
||||
|
||||
[source,java]
|
||||
@@ -224,7 +224,7 @@ public class ChatController {
|
||||
|
||||
== Manual Configuration
|
||||
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatClient.java[AnthropicChatClient] implements the `ChatClient` and `StreamingChatClient` and uses the <<low-level-api>> to connect to the Anthropic service.
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatClient.java[AnthropicChatClient] implements the `ModelCall` and `StreamingChatClient` and uses the <<low-level-api>> to connect to the Anthropic service.
|
||||
|
||||
Add the `spring-ai-anthropic` dependency to your project's Maven `pom.xml` file:
|
||||
|
||||
@@ -247,7 +247,7 @@ dependencies {
|
||||
|
||||
TIP: Refer to the xref:getting-started.adoc#dependency-management[Dependency Management] section to add the Spring AI BOM to your build file.
|
||||
|
||||
Next, create a `AnthropicChatClient` and use it for text generations:
|
||||
Next, create a `AnthropicModelCall` and use it for text generations:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
|
||||
@@ -88,7 +88,7 @@ The prefix `spring.ai.azure.openai` is the property prefix to configure the conn
|
||||
| spring.ai.azure.openai.endpoint | The endpoint from the Azure AI OpenAI `Keys and Endpoint` section under `Resource Management` | -
|
||||
|====
|
||||
|
||||
The prefix `spring.ai.azure.openai.chat` is the property prefix that configures the `ChatClient` implementation for Azure OpenAI.
|
||||
The prefix `spring.ai.azure.openai.chat` is the property prefix that configures the `ModelCall` implementation for Azure OpenAI.
|
||||
|
||||
[cols="3,5,3"]
|
||||
|====
|
||||
@@ -157,7 +157,7 @@ spring.ai.azure.openai.chat.options.temperature=0.7
|
||||
|
||||
TIP: replace the `api-key` and `endpoint` with your Azure OpenAI credentials.
|
||||
|
||||
This will create a `AzureOpenAiChatClient` implementation that you can inject into your class.
|
||||
This will create a `AzureOpenAiModelCall` implementation that you can inject into your class.
|
||||
Here is an example of a simple `@Controller` class that uses the chat client for text generations.
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ public class ChatController {
|
||||
|
||||
== Manual Configuration
|
||||
|
||||
The link:https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatClient.java[AzureOpenAiChatClient] implements the `ChatClient` and `StreamingChatClient` and uses the link:https://learn.microsoft.com/en-us/java/api/overview/azure/ai-openai-readme?view=azure-java-preview[Azure OpenAI Java Client].
|
||||
The link:https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatClient.java[AzureOpenAiChatClient] implements the `ModelCall` and `StreamingChatClient` and uses the link:https://learn.microsoft.com/en-us/java/api/overview/azure/ai-openai-readme?view=azure-java-preview[Azure OpenAI Java Client].
|
||||
|
||||
To enable it, add the `spring-ai-azure-openai` dependency to your project's Maven `pom.xml` file:
|
||||
[source, xml]
|
||||
@@ -210,9 +210,9 @@ dependencies {
|
||||
|
||||
TIP: Refer to the xref:getting-started.adoc#dependency-management[Dependency Management] section to add the Spring AI BOM to your build file.
|
||||
|
||||
TIP: The `spring-ai-azure-openai` dependency also provide the access to the `AzureOpenAiChatClient`. For more information about the `AzureOpenAiChatClient` refer to the link:../chat/azure-openai-chat.html[Azure OpenAI Chat] section.
|
||||
TIP: The `spring-ai-azure-openai` dependency also provide the access to the `AzureOpenAiModelCall`. For more information about the `AzureOpenAiModelCall` refer to the link:../chat/azure-openai-chat.html[Azure OpenAI Chat] section.
|
||||
|
||||
Next, create an `AzureOpenAiChatClient` instance and use it to generate text responses:
|
||||
Next, create an `AzureOpenAiModelCall` instance and use it to generate text responses:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
|
||||
@@ -138,7 +138,7 @@ spring.ai.bedrock.anthropic.chat.options.top-k=15
|
||||
|
||||
TIP: replace the `regions`, `access-key` and `secret-key` with your AWS credentials.
|
||||
|
||||
This will create a `BedrockAnthropicChatClient` implementation that you can inject into your class.
|
||||
This will create a `BedrockAnthropicModelCall` implementation that you can inject into your class.
|
||||
Here is an example of a simple `@Controller` class that uses the chat client for text generations.
|
||||
|
||||
[source,java]
|
||||
@@ -168,7 +168,7 @@ public class ChatController {
|
||||
|
||||
== Manual Configuration
|
||||
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/anthropic/BedrockAnthropicChatClient.java[BedrockAnthropicChatClient] implements the `ChatClient` and `StreamingChatClient` and uses the <<low-level-api>> to connect to the Bedrock Anthropic service.
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/anthropic/BedrockAnthropicChatClient.java[BedrockAnthropicChatClient] implements the `ModelCall` and `StreamingChatClient` and uses the <<low-level-api>> to connect to the Bedrock Anthropic service.
|
||||
|
||||
Add the `spring-ai-bedrock` dependency to your project's Maven `pom.xml` file:
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ spring.ai.bedrock.anthropic3.chat.options.top-k=15
|
||||
|
||||
TIP: replace the `regions`, `access-key` and `secret-key` with your AWS credentials.
|
||||
|
||||
This will create a `BedrockAnthropicChatClient` implementation that you can inject into your class.
|
||||
This will create a `BedrockAnthropicModelCall` implementation that you can inject into your class.
|
||||
Here is an example of a simple `@Controller` class that uses the chat client for text generations.
|
||||
|
||||
[source,java]
|
||||
@@ -209,7 +209,7 @@ public class ChatController {
|
||||
|
||||
== Manual Configuration
|
||||
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/anthropic3/BedrockAnthropic3ChatClient.java[BedrockAnthropic3ChatClient] implements the `ChatClient` and `StreamingChatClient` and uses the <<low-level-api>> to connect to the Bedrock Anthropic service.
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/anthropic3/BedrockAnthropic3ChatClient.java[BedrockAnthropic3ChatClient] implements the `ModelCall` and `StreamingChatClient` and uses the <<low-level-api>> to connect to the Bedrock Anthropic service.
|
||||
|
||||
Add the `spring-ai-bedrock` dependency to your project's Maven `pom.xml` file:
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ spring.ai.bedrock.cohere.chat.options.temperature=0.8
|
||||
|
||||
TIP: replace the `regions`, `access-key` and `secret-key` with your AWS credentials.
|
||||
|
||||
This will create a `BedrockCohereChatClient` implementation that you can inject into your class.
|
||||
This will create a `BedrockCohereModelCall` implementation that you can inject into your class.
|
||||
Here is an example of a simple `@Controller` class that uses the chat client for text generations.
|
||||
|
||||
[source,java]
|
||||
@@ -160,7 +160,7 @@ public class ChatController {
|
||||
|
||||
== Manual Configuration
|
||||
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/cohere/BedrockCohereChatClient.java[BedrockCohereChatClient] implements the `ChatClient` and `StreamingChatClient` and uses the <<low-level-api>> to connect to the Bedrock Cohere service.
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/cohere/BedrockCohereChatClient.java[BedrockCohereChatClient] implements the `ModelCall` and `StreamingChatClient` and uses the <<low-level-api>> to connect to the Bedrock Cohere service.
|
||||
|
||||
Add the `spring-ai-bedrock` dependency to your project's Maven `pom.xml` file:
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ spring.ai.bedrock.jurassic2.chat.options.temperature=0.8
|
||||
|
||||
TIP: replace the `regions`, `access-key` and `secret-key` with your AWS credentials.
|
||||
|
||||
This will create a `BedrockAi21Jurassic2ChatClient` implementation that you can inject into your class.
|
||||
This will create a `BedrockAi21Jurassic2ModelCall` implementation that you can inject into your class.
|
||||
Here is an example of a simple `@Controller` class that uses the chat client for text generations.
|
||||
|
||||
[source,java]
|
||||
@@ -148,7 +148,7 @@ public class ChatController {
|
||||
|
||||
== Manual Configuration
|
||||
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/jurassic2/BedrockAi21Jurassic2ChatClient.java[BedrockAi21Jurassic2ChatClient] implements the `ChatClient` uses the <<low-level-api>> to connect to the Bedrock Jurassic-2 service.
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/jurassic2/BedrockAi21Jurassic2ChatClient.java[BedrockAi21Jurassic2ChatClient] implements the `ModelCall` uses the <<low-level-api>> to connect to the Bedrock Jurassic-2 service.
|
||||
|
||||
Add the `spring-ai-bedrock` dependency to your project's Maven `pom.xml` file:
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ spring.ai.bedrock.llama.chat.options.temperature=0.8
|
||||
|
||||
TIP: replace the `regions`, `access-key` and `secret-key` with your AWS credentials.
|
||||
|
||||
This will create a `BedrockLlamaChatClient` implementation that you can inject into your class.
|
||||
This will create a `BedrockLlamaModelCall` implementation that you can inject into your class.
|
||||
Here is an example of a simple `@Controller` class that uses the chat client for text generations.
|
||||
|
||||
[source,java]
|
||||
@@ -158,7 +158,7 @@ public class ChatController {
|
||||
|
||||
== Manual Configuration
|
||||
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/llama/BedrockLlamaChatClient.java[BedrockLlamaChatClient] implements the `ChatClient` and `StreamingChatClient` and uses the <<low-level-api>> to connect to the Bedrock Anthropic service.
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/llama/BedrockLlamaChatClient.java[BedrockLlamaChatClient] implements the `ModelCall` and `StreamingChatClient` and uses the <<low-level-api>> to connect to the Bedrock Anthropic service.
|
||||
|
||||
Add the `spring-ai-bedrock` dependency to your project's Maven `pom.xml` file:
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ spring.ai.bedrock.titan.chat.options.temperature=0.8
|
||||
|
||||
TIP: replace the `regions`, `access-key` and `secret-key` with your AWS credentials.
|
||||
|
||||
This will create a `BedrockTitanChatClient` implementation that you can inject into your class.
|
||||
This will create a `BedrockTitanModelCall` implementation that you can inject into your class.
|
||||
Here is an example of a simple `@Controller` class that uses the chat client for text generations.
|
||||
|
||||
[source,java]
|
||||
@@ -156,7 +156,7 @@ public class ChatController {
|
||||
|
||||
== Manual Configuration
|
||||
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/titan/BedrockTitanChatClient.java[BedrockTitanChatClient] implements the `ChatClient` and `StreamingChatClient` and uses the <<low-level-api>> to connect to the Bedrock Titanic service.
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/titan/BedrockTitanChatClient.java[BedrockTitanChatClient] implements the `ModelCall` and `StreamingChatClient` and uses the <<low-level-api>> to connect to the Bedrock Titanic service.
|
||||
|
||||
Add the `spring-ai-bedrock` dependency to your project's Maven `pom.xml` file:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
= Anthropic Function Calling
|
||||
|
||||
You can register custom Java functions with the `AnthropicChatClient` and have the Anthropic models intelligently choose to output a JSON object containing arguments to call one or many of the registered functions.
|
||||
You can register custom Java functions with the `AnthropicModelCall` and have the Anthropic models intelligently choose to output a JSON object containing arguments to call one or many of the registered functions.
|
||||
This allows you to connect the LLM capabilities with external tools and APIs.
|
||||
The `claude-3-opus`, `claude-3-sonnet` and `claude-3-haiku` link:https://docs.anthropic.com/claude/docs/tool-use#tool-use-best-practices-and-limitations[models are trained to detect when a function should be called] and to respond with JSON that adheres to the function signature.
|
||||
|
||||
@@ -15,7 +15,7 @@ The `description` helps the model to understand when to call the function.
|
||||
As a developer, you need to implement a function that takes the function call arguments sent from the AI model, and respond with the result back to the model.
|
||||
Your function can in turn invoke other 3rd party services to provide the results.
|
||||
|
||||
Spring AI makes this as easy as defining a `@Bean` definition that returns a `java.util.Function` and supplying the bean name as an option when invoking the `ChatClient`.
|
||||
Spring AI makes this as easy as defining a `@Bean` definition that returns a `java.util.Function` and supplying the bean name as an option when invoking the `ModelCall`.
|
||||
|
||||
Under the hood, Spring wraps your POJO (the function) with the appropriate adapter code that enables interaction with the AI Model, saving you from writing tedious boilerplate code.
|
||||
The basis of the underlying infrastructure is the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/model/function/FunctionCallback.java[FunctionCallback.java] interface and the companion link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/model/function/FunctionCallbackWrapper.java[FunctionCallbackWrapper.java] utility class to simplify the implementation and registration of Java callback functions.
|
||||
@@ -70,7 +70,7 @@ We start with describing the most POJO friendly options.
|
||||
|
||||
In this approach you define `@Beans` in your application context as you would any other Spring managed object.
|
||||
|
||||
Internally, Spring AI `ChatClient` will create an instance of a `FunctionCallbackWrapper` wrapper that adds the logic for it being invoked via the AI model.
|
||||
Internally, Spring AI `ModelCall` will create an instance of a `FunctionCallbackWrapper` wrapper that adds the logic for it being invoked via the AI model.
|
||||
The name of the `@Bean` is passed as a `ChatOption`.
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ static class Config {
|
||||
}
|
||||
----
|
||||
|
||||
It wraps the 3rd party `MockWeatherService` function and registers it as a `CurrentWeather` function with the `AnthropicChatClient`.
|
||||
It wraps the 3rd party `MockWeatherService` function and registers it as a `CurrentWeather` function with the `AnthropicModelCall`.
|
||||
It also provides a description (2) and an optional response converter (3) to convert the response into a text as expected by the model.
|
||||
|
||||
NOTE: By default, the response converter does a JSON serialization of the Response object.
|
||||
@@ -159,7 +159,7 @@ ChatResponse response = chatClient.call(new Prompt(List.of(userMessage),
|
||||
logger.info("Response: {}", response);
|
||||
----
|
||||
|
||||
// NOTE: You can can have multiple functions registered in your `ChatClient` but only those enabled in the prompt request will be considered for the function calling.
|
||||
// NOTE: You can can have multiple functions registered in your `ModelCall` but only those enabled in the prompt request will be considered for the function calling.
|
||||
|
||||
Above user question will trigger 3 calls to `CurrentWeather` function (one for each city) and produce the final response.
|
||||
|
||||
@@ -187,5 +187,5 @@ NOTE: The in-prompt registered functions are enabled by default for the duration
|
||||
|
||||
This approach allows to dynamically chose different functions to be called based on the user input.
|
||||
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/anthropic/tool/FunctionCallWithPromptFunctionIT.java[FunctionCallWithPromptFunctionIT.java] integration test provides a complete example of how to register a function with the `AnthropicChatClient` and use it in a prompt request.
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/anthropic/tool/FunctionCallWithPromptFunctionIT.java[FunctionCallWithPromptFunctionIT.java] integration test provides a complete example of how to register a function with the `AnthropicModelCall` and use it in a prompt request.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Function calling lets developers create a description of a function in their code, then pass that description to a language model in a request. The response from the model includes the name of a function that matches the description and the arguments to call it with.
|
||||
|
||||
You can register custom Java functions with the `AzureOpenAiChatClient` and have the model intelligently choose to output a JSON object containing arguments to call one or many of the registered functions.
|
||||
You can register custom Java functions with the `AzureOpenAiModelCall` and have the model intelligently choose to output a JSON object containing arguments to call one or many of the registered functions.
|
||||
This allows you to connect the LLM capabilities with external tools and APIs.
|
||||
The Azure models are trained to detect when a function should be called and to respond with JSON that adheres to the function signature.
|
||||
|
||||
@@ -16,7 +16,7 @@ In general, the custom functions need to provide a function `name`, `description
|
||||
As a developer, you need to implement a function that takes the function call arguments sent from the AI model, and respond with the result back to the model.
|
||||
Your function can in turn invoke other 3rd party services to provide the results.
|
||||
|
||||
Spring AI makes this as easy as defining a `@Bean` definition that returns a `java.util.Function` and supplying the bean name as an option when invoking the `ChatClient`.
|
||||
Spring AI makes this as easy as defining a `@Bean` definition that returns a `java.util.Function` and supplying the bean name as an option when invoking the `ModelCall`.
|
||||
|
||||
Under the hood, Spring wraps your POJO (the function) with the appropriate adapter code that enables interaction with the AI Model, saving you from writing tedious boilerplate code.
|
||||
The basis of the underlying infrastructure is the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/model/function/FunctionCallback.java[FunctionCallback.java] interface and the companion link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/model/function/FunctionCallbackWrapper.java[FunctionCallbackWrapper.java] utility class to simplify the implementation and registration of Java callback functions.
|
||||
@@ -70,7 +70,7 @@ We start with describing the most POJO friendly options.
|
||||
|
||||
In this approach you define `@Beans` in your application context as you would any other Spring managed object.
|
||||
|
||||
Internally, Spring AI `ChatClient` will create an instance of a `FunctionCallbackWrapper` wrapper that adds the logic for it being invoked via the AI model.
|
||||
Internally, Spring AI `ModelCall` will create an instance of a `FunctionCallbackWrapper` wrapper that adds the logic for it being invoked via the AI model.
|
||||
The name of the `@Bean` is passed as a `ChatOption`.
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@ ChatResponse response = chatClient.call(new Prompt(List.of(userMessage),
|
||||
logger.info("Response: {}", response);
|
||||
----
|
||||
|
||||
// NOTE: You can can have multiple functions registered in your `ChatClient` but only those enabled in the prompt request will be considered for the function calling.
|
||||
// NOTE: You can can have multiple functions registered in your `ModelCall` but only those enabled in the prompt request will be considered for the function calling.
|
||||
|
||||
Above user question will trigger 3 calls to `CurrentWeather` function (one for each city) and the final response will be something like this:
|
||||
|
||||
@@ -194,5 +194,5 @@ NOTE: The in-prompt registered functions are enabled by default for the duration
|
||||
|
||||
This approach allows to dynamically chose different functions to be called based on the user input.
|
||||
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithPromptFunctionIT.java[FunctionCallWithPromptFunctionIT.java] integration test provides a complete example of how to register a function with the `AzureOpenAiChatClient` and use it in a prompt request.
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithPromptFunctionIT.java[FunctionCallWithPromptFunctionIT.java] integration test provides a complete example of how to register a function with the `AzureOpenAiModelCall` and use it in a prompt request.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
= Mistral AI Function Calling
|
||||
|
||||
You can register custom Java functions with the `MistralAiChatClient` and have the Mistral AI models intelligently choose to output a JSON object containing arguments to call one or many of the registered functions.
|
||||
You can register custom Java functions with the `MistralAiModelCall` and have the Mistral AI models intelligently choose to output a JSON object containing arguments to call one or many of the registered functions.
|
||||
This allows you to connect the LLM capabilities with external tools and APIs.
|
||||
The `open-mixtral-8x22b`, `mistral_small_latest`, and `mistral_large_latest` models are trained to detect when a function should be called and to respond with JSON that adheres to the function signature.
|
||||
|
||||
@@ -15,7 +15,7 @@ The `description` helps the model to understand when to call the function.
|
||||
As a developer, you need to implement a function that takes the function call arguments sent from the AI model, and respond with the result back to the model.
|
||||
Your function can in turn invoke other 3rd party services to provide the results.
|
||||
|
||||
Spring AI makes this as easy as defining a `@Bean` definition that returns a `java.util.Function` and supplying the bean name as an option when invoking the `ChatClient`.
|
||||
Spring AI makes this as easy as defining a `@Bean` definition that returns a `java.util.Function` and supplying the bean name as an option when invoking the `ModelCall`.
|
||||
|
||||
Under the hood, Spring wraps your POJO (the function) with the appropriate adapter code that enables interaction with the AI Model, saving you from writing tedious boilerplate code.
|
||||
The basis of the underlying infrastructure is the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/model/function/FunctionCallback.java[FunctionCallback.java] interface and the companion link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/model/function/FunctionCallbackWrapper.java[FunctionCallbackWrapper.java] utility class to simplify the implementation and registration of Java callback functions.
|
||||
@@ -70,7 +70,7 @@ We start with describing the most POJO friendly options.
|
||||
|
||||
In this approach you define `@Beans` in your application context as you would any other Spring managed object.
|
||||
|
||||
Internally, Spring AI `ChatClient` will create an instance of a `FunctionCallbackWrapper` wrapper that adds the logic for it being invoked via the AI model.
|
||||
Internally, Spring AI `ModelCall` will create an instance of a `FunctionCallbackWrapper` wrapper that adds the logic for it being invoked via the AI model.
|
||||
The name of the `@Bean` is passed as a `ChatOption`.
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ static class Config {
|
||||
}
|
||||
----
|
||||
|
||||
It wraps the 3rd party `MockWeatherService` function and registers it as a `CurrentWeather` function with the `MistralAiChatClient`.
|
||||
It wraps the 3rd party `MockWeatherService` function and registers it as a `CurrentWeather` function with the `MistralAiModelCall`.
|
||||
It also provides a description (2) and an optional response converter (3) to convert the response into a text as expected by the model.
|
||||
|
||||
NOTE: By default, the response converter does a JSON serialization of the Response object.
|
||||
@@ -162,7 +162,7 @@ ChatResponse response = chatClient.call(new Prompt(List.of(userMessage),
|
||||
logger.info("Response: {}", response);
|
||||
----
|
||||
|
||||
// NOTE: You can can have multiple functions registered in your `ChatClient` but only those enabled in the prompt request will be considered for the function calling.
|
||||
// NOTE: You can can have multiple functions registered in your `ModelCall` but only those enabled in the prompt request will be considered for the function calling.
|
||||
|
||||
Above user question will trigger 3 calls to `CurrentWeather` function (one for each city) and produce the final response.
|
||||
|
||||
@@ -190,7 +190,7 @@ NOTE: The in-prompt registered functions are enabled by default for the duration
|
||||
|
||||
This approach allows to dynamically chose different functions to be called based on the user input.
|
||||
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/PaymentStatusPromptIT.java[PaymentStatusPromptIT.java] integration test provides a complete example of how to register a function with the `MistralAiChatClient` and use it in a prompt request.
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/PaymentStatusPromptIT.java[PaymentStatusPromptIT.java] integration test provides a complete example of how to register a function with the `MistralAiModelCall` and use it in a prompt request.
|
||||
|
||||
|
||||
== Appendices
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
= Function Calling
|
||||
|
||||
You can register custom Java functions with the `OpenAiChatClient` and have the OpenAI model intelligently choose to output a JSON object containing arguments to call one or many of the registered functions.
|
||||
You can register custom Java functions with the `OpenAiModelCall` and have the OpenAI model intelligently choose to output a JSON object containing arguments to call one or many of the registered functions.
|
||||
This allows you to connect the LLM capabilities with external tools and APIs.
|
||||
The OpenAI models are trained to detect when a function should be called and to respond with JSON that adheres to the function signature.
|
||||
|
||||
@@ -11,12 +11,12 @@ In general, the custom functions need to provide a function `name`, `descriptio
|
||||
|
||||
As a developer, you need to implement a function that takes the function call arguments sent from the AI model, and respond with the result back to the model. Your function can in turn invoke other 3rd party services to provide the results.
|
||||
|
||||
Spring AI makes this as easy as defining a `@Bean` definition that returns a `java.util.Function` and supplying the bean name as an option when invoking the `ChatClient`.
|
||||
Spring AI makes this as easy as defining a `@Bean` definition that returns a `java.util.Function` and supplying the bean name as an option when invoking the `ModelCall`.
|
||||
|
||||
Under the hood, Spring wraps your POJO (the function) with the appropriate adapter code that enables interaction with the AI Model, saving you from writing tedious boilerplate code.
|
||||
The basis of the underlying infrastructure is the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/model/function/FunctionCallback.java[FunctionCallback.java] interface and the companion link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/model/function/FunctionCallbackWrapper.java[FunctionCallbackWrapper.java] utility class to simplify the implementation and registration of Java callback functions.
|
||||
|
||||
// Additionally, the Auto-Configuration provides a way to auto-register any Function<I, O> beans definition as function calling candidates in the `ChatClient`.
|
||||
// Additionally, the Auto-Configuration provides a way to auto-register any Function<I, O> beans definition as function calling candidates in the `ModelCall`.
|
||||
|
||||
|
||||
== How it works
|
||||
@@ -71,7 +71,7 @@ We start with describing the most POJO friendly options.
|
||||
|
||||
In this approach you define `@Beans` in your application context as you would any other Spring managed object.
|
||||
|
||||
Internally, Spring AI `ChatClient` will create an instance of a `FunctionCallbackWrapper` wrapper that adds the logic for it being invoked via the AI model.
|
||||
Internally, Spring AI `ModelCall` will create an instance of a `FunctionCallbackWrapper` wrapper that adds the logic for it being invoked via the AI model.
|
||||
The name of the `@Bean` is passed as a `ChatOption`.
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ static class Config {
|
||||
}
|
||||
----
|
||||
|
||||
It wraps the 3rd party `MockWeatherService` function and registers it as a `CurrentWeather` function with the `OpenAiChatClient`.
|
||||
It wraps the 3rd party `MockWeatherService` function and registers it as a `CurrentWeather` function with the `OpenAiModelCall`.
|
||||
It also provides a description (2) and an optional response converter (3) to convert the response into a text as expected by the model.
|
||||
|
||||
NOTE: By default, the response converter does a JSON serialization of the Response object.
|
||||
@@ -159,7 +159,7 @@ ChatResponse response = chatClient.call(new Prompt(List.of(userMessage),
|
||||
logger.info("Response: {}", response);
|
||||
----
|
||||
|
||||
// NOTE: You can can have multiple functions registered in your `ChatClient` but only those enabled in the prompt request will be considered for the function calling.
|
||||
// NOTE: You can can have multiple functions registered in your `ModelCall` but only those enabled in the prompt request will be considered for the function calling.
|
||||
|
||||
Above user question will trigger 3 calls to `CurrentWeather` function (one for each city) and the final response will be something like this:
|
||||
|
||||
@@ -197,11 +197,11 @@ NOTE: The in-prompt registered functions are enabled by default for the duration
|
||||
|
||||
This approach allows to dynamically chose different functions to be called based on the user input.
|
||||
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackInPromptIT.java[FunctionCallbackInPromptIT.java] integration test provides a complete example of how to register a function with the `OpenAiChatClient` and use it in a prompt request.
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackInPromptIT.java[FunctionCallbackInPromptIT.java] integration test provides a complete example of how to register a function with the `OpenAiModelCall` and use it in a prompt request.
|
||||
//
|
||||
// === Register Functions with Default Options
|
||||
//
|
||||
// You can programmatically register functions with the `OpenAiChatClient` using the `OpenAiChatOptions#withFunctionCallbacks`:
|
||||
// You can programmatically register functions with the `OpenAiModelCall` using the `OpenAiChatOptions#withFunctionCallbacks`:
|
||||
//
|
||||
// [source,java]
|
||||
// ----
|
||||
@@ -215,7 +215,7 @@ The https://github.com/spring-projects/spring-ai/blob/main/spring-ai-spring-boot
|
||||
// new MockWeatherService()))) // function code
|
||||
// .build();
|
||||
//
|
||||
// OpenAiChatClient chatClient = new OpenAiChatClient(openaiApi, defaultOptions);
|
||||
// OpenAiModelCall chatClient = new OpenAiModelCall(openaiApi, defaultOptions);
|
||||
//
|
||||
// UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
|
||||
//
|
||||
@@ -223,7 +223,7 @@ The https://github.com/spring-projects/spring-ai/blob/main/spring-ai-spring-boot
|
||||
// OpenAiChatOptions.builder().withFunction("CurrentWeather").build())); // Enable the function
|
||||
// ----
|
||||
//
|
||||
// NOTE: Functions are registered when OpenAiChatClient is created, by you must enable in the Prompt the functions to be used in the request.
|
||||
// NOTE: Functions are registered when OpenAiModelCall is created, by you must enable in the Prompt the functions to be used in the request.
|
||||
|
||||
|
||||
== Appendices:
|
||||
|
||||
@@ -6,7 +6,7 @@ The parallel function calling is gone as well.
|
||||
|
||||
Function calling lets developers create a description of a function in their code, then pass that description to a language model in a request. The response from the model includes the name of a function that matches the description and the arguments to call it with.
|
||||
|
||||
You can register custom Java functions with the `VertexAiGeminiChatClient` and have the Gemini Pro model intelligently choose to output a JSON object containing arguments to call one or many of the registered functions.
|
||||
You can register custom Java functions with the `VertexAiGeminiModelCall` and have the Gemini Pro model intelligently choose to output a JSON object containing arguments to call one or many of the registered functions.
|
||||
This allows you to connect the LLM capabilities with external tools and APIs.
|
||||
The VertexAI Gemini Pro model is trained to detect when a function should be called and to respond with JSON that adheres to the function signature.
|
||||
|
||||
@@ -18,12 +18,12 @@ In general, the custom functions need to provide a function `name`, `description
|
||||
As a developer, you need to implement a function that takes the function call arguments sent from the AI model, and respond with the result back to the model.
|
||||
Your function can in turn invoke other 3rd party services to provide the results.
|
||||
|
||||
Spring AI makes this as easy as defining a `@Bean` definition that returns a `java.util.Function` and supplying the bean name as an option when invoking the `ChatClient`.
|
||||
Spring AI makes this as easy as defining a `@Bean` definition that returns a `java.util.Function` and supplying the bean name as an option when invoking the `ModelCall`.
|
||||
|
||||
Under the hood, Spring wraps your POJO (the function) with the appropriate adapter code that enables interaction with the AI Model, saving you from writing tedious boilerplate code.
|
||||
The basis of the underlying infrastructure is the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/model/function/FunctionCallback.java[FunctionCallback.java] interface and the companion link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/model/function/FunctionCallbackWrapper.java[FunctionCallbackWrapper.java] utility class to simplify the implementation and registration of Java callback functions.
|
||||
|
||||
// Additionally, the Auto-Configuration provides a way to auto-register any Function<I, O> beans definition as function calling candidates in the `ChatClient`.
|
||||
// Additionally, the Auto-Configuration provides a way to auto-register any Function<I, O> beans definition as function calling candidates in the `ModelCall`.
|
||||
|
||||
== How it works
|
||||
|
||||
@@ -74,7 +74,7 @@ We start with describing the most POJO friendly options.
|
||||
|
||||
In this approach you define `@Beans` in your application context as you would any other Spring managed object.
|
||||
|
||||
Internally, Spring AI `ChatClient` will create an instance of a `FunctionCallbackWrapper` wrapper that adds the logic for it being invoked via the AI model.
|
||||
Internally, Spring AI `ModelCall` will create an instance of a `FunctionCallbackWrapper` wrapper that adds the logic for it being invoked via the AI model.
|
||||
The name of the `@Bean` is passed as a `ChatOption`.
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ static class Config {
|
||||
}
|
||||
----
|
||||
|
||||
It wraps the 3rd party `MockWeatherService` function and registers it as a `CurrentWeather` function with the `VertexAiGeminiChatClient`.
|
||||
It wraps the 3rd party `MockWeatherService` function and registers it as a `CurrentWeather` function with the `VertexAiGeminiModelCall`.
|
||||
It also provides a description (2) and sets the Schema type to Open API type (3).
|
||||
|
||||
NOTE: The default response converter does a JSON serialization of the Response object.
|
||||
@@ -162,7 +162,7 @@ ChatResponse response = chatClient.call(new Prompt(List.of(userMessage),
|
||||
logger.info("Response: {}", response);
|
||||
----
|
||||
|
||||
// NOTE: You can can have multiple functions registered in your `ChatClient` but only those enabled in the prompt request will be considered for the function calling.
|
||||
// NOTE: You can can have multiple functions registered in your `ModelCall` but only those enabled in the prompt request will be considered for the function calling.
|
||||
|
||||
Above user question will trigger 3 calls to `CurrentWeather` function (one for each city) and the final response will be something like this:
|
||||
|
||||
@@ -201,5 +201,5 @@ NOTE: The in-prompt registered functions are enabled by default for the duration
|
||||
|
||||
This approach allows to dynamically chose different functions to be called based on the user input.
|
||||
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/gemini/tool/FunctionCallWithPromptFunctionIT.java[FunctionCallWithPromptFunctionIT.java] integration test provides a complete example of how to register a function with the `VertexAiGeminiChatClient` and use it in a prompt request.
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/gemini/tool/FunctionCallWithPromptFunctionIT.java[FunctionCallWithPromptFunctionIT.java] integration test provides a complete example of how to register a function with the `VertexAiGeminiModelCall` and use it in a prompt request.
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ The prefix `spring.ai.mistralai.chat` is the property prefix that lets you confi
|
||||
| spring.ai.mistralai.chat.options.functionCallbacks | MistralAI Tool Function Callbacks to register with the ChatClient. | -
|
||||
|====
|
||||
|
||||
NOTE: You can override the common `spring.ai.mistralai.base-url` and `spring.ai.mistralai.api-key` for the `ChatClient` and `EmbeddingClient` implementations.
|
||||
NOTE: You can override the common `spring.ai.mistralai.base-url` and `spring.ai.mistralai.api-key` for the `ModelCall` and `EmbeddingClient` implementations.
|
||||
The `spring.ai.mistralai.chat.base-url` and `spring.ai.mistralai.chat.api-key` properties if set take precedence over the common properties.
|
||||
This is useful if you want to use different MistralAI accounts for different models and different model endpoints.
|
||||
|
||||
@@ -153,7 +153,7 @@ spring.ai.mistralai.chat.options.temperature=0.7
|
||||
|
||||
TIP: replace the `api-key` with your OpenAI credentials.
|
||||
|
||||
This will create a `MistralAiChatClient` implementation that you can inject into your class.
|
||||
This will create a `MistralAiModelCall` implementation that you can inject into your class.
|
||||
Here is an example of a simple `@Controller` class that uses the chat client for text generations.
|
||||
|
||||
[source,java]
|
||||
@@ -183,7 +183,7 @@ public class ChatController {
|
||||
|
||||
== Manual Configuration
|
||||
|
||||
The link:https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatClient.java[MistralAiChatClient] implements the `ChatClient` and `StreamingChatClient` and uses the <<low-level-api>> to connect to the MistralAI service.
|
||||
The link:https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatClient.java[MistralAiChatClient] implements the `ModelCall` and `StreamingChatClient` and uses the <<low-level-api>> to connect to the MistralAI service.
|
||||
|
||||
Add the `spring-ai-mistral-ai` dependency to your project's Maven `pom.xml` file:
|
||||
|
||||
@@ -206,7 +206,7 @@ dependencies {
|
||||
|
||||
TIP: Refer to the xref:getting-started.adoc#dependency-management[Dependency Management] section to add the Spring AI BOM to your build file.
|
||||
|
||||
Next, create a `MistralAiChatClient` and use it for text generations:
|
||||
Next, create a `MistralAiModelCall` and use it for text generations:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
= Ollama Chat
|
||||
|
||||
With https://ollama.ai/[Ollama] you can run various Large Language Models (LLMs) locally and generate text from them.
|
||||
Spring AI supports the Ollama text generation with `OllamaChatClient`.
|
||||
Spring AI supports the Ollama text generation with `OllamaModelCall`.
|
||||
|
||||
== Prerequisites
|
||||
|
||||
@@ -185,7 +185,7 @@ spring.ai.ollama.chat.options.temperature=0.7
|
||||
|
||||
TIP: replace the `base-url` with your Ollama server URL.
|
||||
|
||||
This will create a `OllamaChatClient` implementation that you can inject into your class.
|
||||
This will create a `OllamaModelCall` implementation that you can inject into your class.
|
||||
Here is an example of a simple `@Controller` class that uses the chat client for text generations.
|
||||
|
||||
[source,java]
|
||||
@@ -216,8 +216,8 @@ public class ChatController {
|
||||
|
||||
== Manual Configuration
|
||||
|
||||
If you don't want to use the Spring Boot auto-configuration, you can manually configure the `OllamaChatClient` in your application.
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaChatClient.java[OllamaChatClient] implements the `ChatClient` and `StreamingChatClient` and uses the <<low-level-api>> to connect to the Ollama service.
|
||||
If you don't want to use the Spring Boot auto-configuration, you can manually configure the `OllamaModelCall` in your application.
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaChatClient.java[OllamaChatClient] implements the `ModelCall` and `StreamingChatClient` and uses the <<low-level-api>> to connect to the Ollama service.
|
||||
|
||||
To use it add the `spring-ai-ollama` dependency to your project's Maven `pom.xml` file:
|
||||
|
||||
@@ -243,7 +243,7 @@ TIP: Refer to the xref:getting-started.adoc#dependency-management[Dependency Man
|
||||
TIP: The `spring-ai-ollama` dependency provides access also to the `OllamaEmbeddingClient`.
|
||||
For more information about the `OllamaEmbeddingClient` refer to the link:../embeddings/ollama-embeddings.html[Ollama Embedding Client] section.
|
||||
|
||||
Next, create an `OllamaChatClient` instance and use it to text generations requests:
|
||||
Next, create an `OllamaModelCall` instance and use it to text generations requests:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -274,7 +274,7 @@ image::ollama-chat-completion-api.jpg[OllamaApi Chat Completion API Diagram, 800
|
||||
|
||||
Here is a simple snippet showing how to use the API programmatically:
|
||||
|
||||
NOTE: The `OllamaApi` is low level api and is not recommended for direct use. Use the `OllamaChatClient` instead.
|
||||
NOTE: The `OllamaApi` is low level api and is not recommended for direct use. Use the `OllamaModelCall` instead.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
|
||||
@@ -107,7 +107,7 @@ The prefix `spring.ai.openai.chat` is the property prefix that lets you configur
|
||||
| spring.ai.openai.chat.options.functions | List of functions, identified by their names, to enable for function calling in a single prompt requests. Functions with those names must exist in the functionCallbacks registry. | -
|
||||
|====
|
||||
|
||||
NOTE: You can override the common `spring.ai.openai.base-url` and `spring.ai.openai.api-key` for the `ChatClient` and `EmbeddingClient` implementations.
|
||||
NOTE: You can override the common `spring.ai.openai.base-url` and `spring.ai.openai.api-key` for the `ModelCall` and `EmbeddingClient` implementations.
|
||||
The `spring.ai.openai.chat.base-url` and `spring.ai.openai.chat.api-key` properties if set take precedence over the common properties.
|
||||
This is useful if you want to use different OpenAI accounts for different models and different model endpoints.
|
||||
|
||||
@@ -209,7 +209,7 @@ spring.ai.openai.chat.options.temperature=0.7
|
||||
|
||||
TIP: replace the `api-key` with your OpenAI credentials.
|
||||
|
||||
This will create a `OpenAiChatClient` implementation that you can inject into your class.
|
||||
This will create a `OpenAiModelCall` implementation that you can inject into your class.
|
||||
Here is an example of a simple `@Controller` class that uses the chat client for text generations.
|
||||
|
||||
[source,java]
|
||||
@@ -239,7 +239,7 @@ public class ChatController {
|
||||
|
||||
== Manual Configuration
|
||||
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatClient.java[OpenAiChatClient] implements the `ChatClient` and `StreamingChatClient` and uses the <<low-level-api>> to connect to the OpenAI service.
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatClient.java[OpenAiChatClient] implements the `ModelCall` and `StreamingChatClient` and uses the <<low-level-api>> to connect to the OpenAI service.
|
||||
|
||||
Add the `spring-ai-openai` dependency to your project's Maven `pom.xml` file:
|
||||
|
||||
@@ -262,7 +262,7 @@ dependencies {
|
||||
|
||||
TIP: Refer to the xref:getting-started.adoc#dependency-management[Dependency Management] section to add the Spring AI BOM to your build file.
|
||||
|
||||
Next, create a `OpenAiChatClient` and use it for text generations:
|
||||
Next, create a `OpenAiModelCall` and use it for text generations:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
|
||||
@@ -151,7 +151,7 @@ spring.ai.vertex.ai.gemini.chat.options.temperature=0.5
|
||||
|
||||
TIP: replace the `api-key` with your VertexAI credentials.
|
||||
|
||||
This will create a `VertexAiGeminiChatClient` implementation that you can inject into your class.
|
||||
This will create a `VertexAiGeminiModelCall` implementation that you can inject into your class.
|
||||
Here is an example of a simple `@Controller` class that uses the chat client for text generations.
|
||||
|
||||
[source,java]
|
||||
@@ -181,7 +181,7 @@ public class ChatController {
|
||||
|
||||
== Manual Configuration
|
||||
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClient.java[VertexAiGeminiChatClient] implements the `ChatClient` and uses the `VertexAI` to connect to the Vertex AI Gemini service.
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClient.java[VertexAiGeminiChatClient] implements the `ModelCall` and uses the `VertexAI` to connect to the Vertex AI Gemini service.
|
||||
|
||||
Add the `spring-ai-vertex-ai-gemini` dependency to your project's Maven `pom.xml` file:
|
||||
|
||||
@@ -204,7 +204,7 @@ dependencies {
|
||||
|
||||
TIP: Refer to the xref:getting-started.adoc#dependency-management[Dependency Management] section to add the Spring AI BOM to your build file.
|
||||
|
||||
Next, create a `VertexAiGeminiChatClient` and use it for text generations:
|
||||
Next, create a `VertexAiGeminiModelCall` and use it for text generations:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
|
||||
@@ -114,7 +114,7 @@ spring.ai.vertex.ai.chat.options.temperature=0.5
|
||||
|
||||
TIP: replace the `api-key` with your VertexAI credentials.
|
||||
|
||||
This will create a `VertexAiPaLm2ChatClient` implementation that you can inject into your class.
|
||||
This will create a `VertexAiPaLm2ModelCall` implementation that you can inject into your class.
|
||||
Here is an example of a simple `@Controller` class that uses the chat client for text generations.
|
||||
|
||||
[source,java]
|
||||
@@ -144,7 +144,7 @@ public class ChatController {
|
||||
|
||||
== Manual Configuration
|
||||
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-openai/src/main/java/org/springframework/ai/vertexai/paml2/VertexAiPaLm2ChatClient.java[VertexAiPaLm2ChatClient] implements the `ChatClient` and uses the <<low-level-api>> to connect to the VertexAI service.
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-openai/src/main/java/org/springframework/ai/vertexai/paml2/VertexAiPaLm2ChatClient.java[VertexAiPaLm2ChatClient] implements the `ModelCall` and uses the <<low-level-api>> to connect to the VertexAI service.
|
||||
|
||||
Add the `spring-ai-vertex-ai-palm2` dependency to your project's Maven `pom.xml` file:
|
||||
|
||||
@@ -167,7 +167,7 @@ dependencies {
|
||||
|
||||
TIP: Refer to the xref:getting-started.adoc#dependency-management[Dependency Management] section to add the Spring AI BOM to your build file.
|
||||
|
||||
Next, create a `VertexAiPaLm2ChatClient` and use it for text generations:
|
||||
Next, create a `VertexAiPaLm2ModelCall` and use it for text generations:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user