Rename the ModelClient class hiearchy into Model
- Rename ModelClient into Model. Update all code and doc references. - Rename ChatClient to ChatModel. Update all ChatClient suffixes and chatClient fields and variables in code and doc. - Rename EmbeddingClient into EmbeddingModel. Update the XxxEmbeddingClient class and variable suffixes and embeddingClient variables and fields in code and docs. - Rename ImageClient into ImageModel. .... - Rename SpeechClient into SpeechModel .... - Rename TranscriptionClient into TranscriptionModel ... - Update all javadocs and antora pages. Update the related diagrams.
This commit is contained in:
@@ -26,7 +26,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ai.chat.ChatCaller;
|
||||
import org.springframework.ai.chat.ChatModel;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.ai.anthropic.api.AnthropicApi;
|
||||
@@ -41,7 +41,7 @@ import org.springframework.ai.anthropic.api.AnthropicApi.Usage;
|
||||
import org.springframework.ai.anthropic.metadata.AnthropicChatResponseMetadata;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatCaller;
|
||||
import org.springframework.ai.chat.StreamingChatModel;
|
||||
import org.springframework.ai.chat.messages.MessageType;
|
||||
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
@@ -56,16 +56,16 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* The {@link ChatCaller} implementation for the Anthropic service.
|
||||
* The {@link ChatModel} implementation for the Anthropic service.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class AnthropicModelCaller extends
|
||||
public class AnthropicChatModel extends
|
||||
AbstractFunctionCallSupport<AnthropicApi.RequestMessage, AnthropicApi.ChatCompletionRequest, ResponseEntity<AnthropicApi.ChatCompletion>>
|
||||
implements ChatCaller, StreamingChatCaller {
|
||||
implements ChatModel, StreamingChatModel {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AnthropicModelCaller.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(AnthropicChatModel.class);
|
||||
|
||||
public static final String DEFAULT_MODEL_NAME = AnthropicApi.ChatModel.CLAUDE_3_OPUS.getValue();
|
||||
|
||||
@@ -89,10 +89,10 @@ public class AnthropicModelCaller extends
|
||||
public final RetryTemplate retryTemplate;
|
||||
|
||||
/**
|
||||
* Construct a new {@link AnthropicModelCaller} instance.
|
||||
* Construct a new {@link AnthropicChatModel} instance.
|
||||
* @param anthropicApi the lower-level API for the Anthropic service.
|
||||
*/
|
||||
public AnthropicModelCaller(AnthropicApi anthropicApi) {
|
||||
public AnthropicChatModel(AnthropicApi anthropicApi) {
|
||||
this(anthropicApi,
|
||||
AnthropicChatOptions.builder()
|
||||
.withModel(DEFAULT_MODEL_NAME)
|
||||
@@ -102,34 +102,34 @@ public class AnthropicModelCaller extends
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@link AnthropicModelCaller} instance.
|
||||
* Construct a new {@link AnthropicChatModel} instance.
|
||||
* @param anthropicApi the lower-level API for the Anthropic service.
|
||||
* @param defaultOptions the default options used for the chat completion requests.
|
||||
*/
|
||||
public AnthropicModelCaller(AnthropicApi anthropicApi, AnthropicChatOptions defaultOptions) {
|
||||
public AnthropicChatModel(AnthropicApi anthropicApi, AnthropicChatOptions defaultOptions) {
|
||||
this(anthropicApi, defaultOptions, RetryUtils.DEFAULT_RETRY_TEMPLATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@link AnthropicModelCaller} instance.
|
||||
* Construct a new {@link AnthropicChatModel} 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 AnthropicModelCaller(AnthropicApi anthropicApi, AnthropicChatOptions defaultOptions,
|
||||
public AnthropicChatModel(AnthropicApi anthropicApi, AnthropicChatOptions defaultOptions,
|
||||
RetryTemplate retryTemplate) {
|
||||
this(anthropicApi, defaultOptions, retryTemplate, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@link AnthropicModelCaller} instance.
|
||||
* Construct a new {@link AnthropicChatModel} 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 AnthropicModelCaller(AnthropicApi anthropicApi, AnthropicChatOptions defaultOptions,
|
||||
public AnthropicChatModel(AnthropicApi anthropicApi, AnthropicChatOptions defaultOptions,
|
||||
RetryTemplate retryTemplate, FunctionCallbackContext functionCallbackContext) {
|
||||
|
||||
super(functionCallbackContext);
|
||||
@@ -51,11 +51,11 @@ public class AnthropicChatOptions implements ChatOptions, FunctionCallingOptions
|
||||
private @JsonProperty("top_k") Integer topK;
|
||||
|
||||
/**
|
||||
* Tool Function Callbacks to register with the ModelCall. For Prompt
|
||||
* Tool Function Callbacks to register with the ChatModel. 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 ModelCall chat completion requests.
|
||||
* to be used by the ChatModel chat completion requests.
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
@JsonIgnore
|
||||
|
||||
@@ -29,10 +29,10 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.anthropic.api.AnthropicApi;
|
||||
import org.springframework.ai.anthropic.api.tool.MockWeatherService;
|
||||
import org.springframework.ai.chat.ChatCaller;
|
||||
import org.springframework.ai.chat.ChatModel;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatCaller;
|
||||
import org.springframework.ai.chat.StreamingChatModel;
|
||||
import org.springframework.ai.chat.messages.AssistantMessage;
|
||||
import org.springframework.ai.chat.messages.Media;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
@@ -56,15 +56,15 @@ 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 AnthropicModelCallerIT {
|
||||
class AnthropicChatModelIT {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AnthropicModelCallerIT.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(AnthropicChatModelIT.class);
|
||||
|
||||
@Autowired
|
||||
protected ChatCaller modelCaller;
|
||||
protected ChatModel chatModel;
|
||||
|
||||
@Autowired
|
||||
protected StreamingChatCaller streamingChatClient;
|
||||
protected StreamingChatModel streamingChatModel;
|
||||
|
||||
@Value("classpath:/prompts/system-message.st")
|
||||
private Resource systemResource;
|
||||
@@ -76,7 +76,7 @@ class AnthropicModelCallerIT {
|
||||
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 = modelCaller.call(prompt);
|
||||
ChatResponse response = chatModel.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 AnthropicModelCallerIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template,
|
||||
Map.of("subject", "ice cream flavors", "format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = this.modelCaller.call(prompt).getResult();
|
||||
Generation generation = this.chatModel.call(prompt).getResult();
|
||||
|
||||
List<String> list = listOutputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(list).hasSize(5);
|
||||
@@ -120,7 +120,7 @@ class AnthropicModelCallerIT {
|
||||
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 = modelCaller.call(prompt).getResult();
|
||||
Generation generation = chatModel.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 AnthropicModelCallerIT {
|
||||
""";
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = modelCaller.call(prompt).getResult();
|
||||
Generation generation = chatModel.call(prompt).getResult();
|
||||
|
||||
ActorsFilmsRecord actorsFilms = beanOutputConverter.convert(generation.getOutput().getContent());
|
||||
logger.info("" + actorsFilms);
|
||||
@@ -163,7 +163,7 @@ class AnthropicModelCallerIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
|
||||
String generationTextFromStream = streamingChatClient.stream(prompt)
|
||||
String generationTextFromStream = streamingChatModel.stream(prompt)
|
||||
.collectList()
|
||||
.block()
|
||||
.stream()
|
||||
@@ -187,7 +187,7 @@ class AnthropicModelCallerIT {
|
||||
var userMessage = new UserMessage("Explain what do you see on this picture?",
|
||||
List.of(new Media(MimeTypeUtils.IMAGE_PNG, imageData)));
|
||||
|
||||
var response = modelCaller.call(new Prompt(List.of(userMessage)));
|
||||
var response = chatModel.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 AnthropicModelCallerIT {
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
ChatResponse response = modelCaller.call(new Prompt(messages, promptOptions));
|
||||
ChatResponse response = chatModel.call(new Prompt(messages, promptOptions));
|
||||
|
||||
logger.info("Response: {}", response);
|
||||
|
||||
@@ -38,9 +38,9 @@ public class AnthropicTestConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AnthropicModelCaller openAiChatClient(AnthropicApi api) {
|
||||
AnthropicModelCaller anthropicChatClient = new AnthropicModelCaller(api);
|
||||
return anthropicChatClient;
|
||||
public AnthropicChatModel openAiChatModel(AnthropicApi api) {
|
||||
AnthropicChatModel anthropicChatModel = new AnthropicChatModel(api);
|
||||
return anthropicChatModel;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public class ChatCompletionRequestTests {
|
||||
@Test
|
||||
public void createRequestWithChatOptions() {
|
||||
|
||||
var client = new AnthropicModelCaller(new AnthropicApi("TEST"),
|
||||
var client = new AnthropicChatModel(new AnthropicApi("TEST"),
|
||||
AnthropicChatOptions.builder().withModel("DEFAULT_MODEL").withTemperature(66.6f).build());
|
||||
|
||||
var request = client.createRequest(new Prompt("Test message content"), false);
|
||||
|
||||
@@ -38,10 +38,10 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.azure.openai.metadata.AzureOpenAiChatResponseMetadata;
|
||||
import org.springframework.ai.chat.ChatCaller;
|
||||
import org.springframework.ai.chat.ChatModel;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatCaller;
|
||||
import org.springframework.ai.chat.StreamingChatModel;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
|
||||
import org.springframework.ai.chat.metadata.PromptMetadata;
|
||||
@@ -63,7 +63,7 @@ import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* {@link ChatCaller} implementation for {@literal Microsoft Azure AI} backed by
|
||||
* {@link ChatModel} 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 ChatCaller
|
||||
* @see ChatModel
|
||||
* @see com.azure.ai.openai.OpenAIClient
|
||||
*/
|
||||
public class AzureOpenAiModelCaller
|
||||
public class AzureOpenAiChatModel
|
||||
extends AbstractFunctionCallSupport<ChatRequestMessage, ChatCompletionsOptions, ChatCompletions>
|
||||
implements ChatCaller, StreamingChatCaller {
|
||||
implements ChatModel, StreamingChatModel {
|
||||
|
||||
private static final String DEFAULT_DEPLOYMENT_NAME = "gpt-35-turbo";
|
||||
|
||||
@@ -94,7 +94,7 @@ public class AzureOpenAiModelCaller
|
||||
*/
|
||||
private final OpenAIClient openAIClient;
|
||||
|
||||
public AzureOpenAiModelCaller(OpenAIClient microsoftOpenAiClient) {
|
||||
public AzureOpenAiChatModel(OpenAIClient microsoftOpenAiClient) {
|
||||
this(microsoftOpenAiClient,
|
||||
AzureOpenAiChatOptions.builder()
|
||||
.withDeploymentName(DEFAULT_DEPLOYMENT_NAME)
|
||||
@@ -102,11 +102,11 @@ public class AzureOpenAiModelCaller
|
||||
.build());
|
||||
}
|
||||
|
||||
public AzureOpenAiModelCaller(OpenAIClient microsoftOpenAiClient, AzureOpenAiChatOptions options) {
|
||||
public AzureOpenAiChatModel(OpenAIClient microsoftOpenAiClient, AzureOpenAiChatOptions options) {
|
||||
this(microsoftOpenAiClient, options, null);
|
||||
}
|
||||
|
||||
public AzureOpenAiModelCaller(OpenAIClient microsoftOpenAiClient, AzureOpenAiChatOptions options,
|
||||
public AzureOpenAiChatModel(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 AzureOpenAiModelCaller
|
||||
|
||||
/**
|
||||
* @deprecated since 0.8.0, use
|
||||
* {@link #AzureOpenAiModelCall(OpenAIClient, AzureOpenAiChatOptions)} instead.
|
||||
* {@link #AzureOpenAiChatModel(OpenAIClient, AzureOpenAiChatOptions)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "0.8.0")
|
||||
public AzureOpenAiModelCaller withDefaultOptions(AzureOpenAiChatOptions defaultOptions) {
|
||||
public AzureOpenAiChatModel withDefaultOptions(AzureOpenAiChatOptions defaultOptions) {
|
||||
Assert.notNull(defaultOptions, "DefaultOptions must not be null");
|
||||
this.defaultOptions = defaultOptions;
|
||||
return this;
|
||||
@@ -127,11 +127,11 @@ public class AzureOpenAiChatOptions implements FunctionCallingOptions, ChatOptio
|
||||
private String deploymentName;
|
||||
|
||||
/**
|
||||
* OpenAI Tool Function Callbacks to register with the ModelCall. For Prompt Options
|
||||
* OpenAI Tool Function Callbacks to register with the ChatModel. 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 ModelCall chat completion requests.
|
||||
* by the ChatModel chat completion requests.
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
@JsonIgnore
|
||||
|
||||
@@ -28,7 +28,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.document.MetadataMode;
|
||||
import org.springframework.ai.embedding.AbstractEmbeddingClient;
|
||||
import org.springframework.ai.embedding.AbstractEmbeddingModel;
|
||||
import org.springframework.ai.embedding.Embedding;
|
||||
import org.springframework.ai.embedding.EmbeddingOptions;
|
||||
import org.springframework.ai.embedding.EmbeddingRequest;
|
||||
@@ -37,9 +37,9 @@ import org.springframework.ai.embedding.EmbeddingResponseMetadata;
|
||||
import org.springframework.ai.model.ModelOptionsUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
public class AzureOpenAiEmbeddingClient extends AbstractEmbeddingClient {
|
||||
public class AzureOpenAiEmbeddingModel extends AbstractEmbeddingModel {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AzureOpenAiEmbeddingClient.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(AzureOpenAiEmbeddingModel.class);
|
||||
|
||||
private final OpenAIClient azureOpenAiClient;
|
||||
|
||||
@@ -47,16 +47,16 @@ public class AzureOpenAiEmbeddingClient extends AbstractEmbeddingClient {
|
||||
|
||||
private final MetadataMode metadataMode;
|
||||
|
||||
public AzureOpenAiEmbeddingClient(OpenAIClient azureOpenAiClient) {
|
||||
public AzureOpenAiEmbeddingModel(OpenAIClient azureOpenAiClient) {
|
||||
this(azureOpenAiClient, MetadataMode.EMBED);
|
||||
}
|
||||
|
||||
public AzureOpenAiEmbeddingClient(OpenAIClient azureOpenAiClient, MetadataMode metadataMode) {
|
||||
public AzureOpenAiEmbeddingModel(OpenAIClient azureOpenAiClient, MetadataMode metadataMode) {
|
||||
this(azureOpenAiClient, metadataMode,
|
||||
AzureOpenAiEmbeddingOptions.builder().withDeploymentName("text-embedding-ada-002").build());
|
||||
}
|
||||
|
||||
public AzureOpenAiEmbeddingClient(OpenAIClient azureOpenAiClient, MetadataMode metadataMode,
|
||||
public AzureOpenAiEmbeddingModel(OpenAIClient azureOpenAiClient, MetadataMode metadataMode,
|
||||
AzureOpenAiEmbeddingOptions options) {
|
||||
Assert.notNull(azureOpenAiClient, "com.azure.ai.openai.OpenAIClient must not be null");
|
||||
Assert.notNull(metadataMode, "Metadata mode must not be null");
|
||||
@@ -53,7 +53,7 @@ public class AzureChatCompletionsOptionsTests {
|
||||
.withUser("user")
|
||||
.build();
|
||||
|
||||
var client = new AzureOpenAiModelCaller(mockClient, defaultOptions);
|
||||
var client = new AzureOpenAiChatModel(mockClient, defaultOptions);
|
||||
|
||||
var requestOptions = client.toAzureChatCompletionsOptions(new Prompt("Test message content"));
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ public class AzureEmbeddingsOptionsTests {
|
||||
public void createRequestWithChatOptions() {
|
||||
|
||||
OpenAIClient mockClient = Mockito.mock(OpenAIClient.class);
|
||||
var client = new AzureOpenAiEmbeddingClient(mockClient, MetadataMode.EMBED,
|
||||
var client = new AzureOpenAiEmbeddingModel(mockClient, MetadataMode.EMBED,
|
||||
AzureOpenAiEmbeddingOptions.builder()
|
||||
.withDeploymentName("DEFAULT_MODEL")
|
||||
.withUser("USER_TEST")
|
||||
|
||||
@@ -46,13 +46,13 @@ import org.springframework.core.convert.support.DefaultConversionService;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest(classes = AzureOpenAiModelCallIerT.TestConfiguration.class)
|
||||
@SpringBootTest(classes = AzureOpenAiChatModelIT.TestConfiguration.class)
|
||||
@EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_API_KEY", matches = ".+")
|
||||
@EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_ENDPOINT", matches = ".+")
|
||||
class AzureOpenAiModelCallIerT {
|
||||
class AzureOpenAiChatModelIT {
|
||||
|
||||
@Autowired
|
||||
private AzureOpenAiModelCaller chatClient;
|
||||
private AzureOpenAiChatModel chatModel;
|
||||
|
||||
record ActorsFilms(String actor, List<String> movies) {
|
||||
}
|
||||
@@ -69,7 +69,7 @@ class AzureOpenAiModelCallIerT {
|
||||
UserMessage userMessage = new UserMessage("Generate the names of 5 famous pirates.");
|
||||
|
||||
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
|
||||
ChatResponse response = chatClient.call(prompt);
|
||||
ChatResponse response = chatModel.call(prompt);
|
||||
assertThat(response.getResult().getOutput().getContent()).contains("Blackbeard");
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ class AzureOpenAiModelCallIerT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template,
|
||||
Map.of("subject", "ice cream flavors", "format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = chatClient.call(prompt).getResult();
|
||||
Generation generation = chatModel.call(prompt).getResult();
|
||||
|
||||
List<String> list = outputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(list).hasSize(5);
|
||||
@@ -105,7 +105,7 @@ class AzureOpenAiModelCallIerT {
|
||||
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 = chatModel.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));
|
||||
@@ -124,7 +124,7 @@ class AzureOpenAiModelCallIerT {
|
||||
""";
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = chatClient.call(prompt).getResult();
|
||||
Generation generation = chatModel.call(prompt).getResult();
|
||||
|
||||
ActorsFilms actorsFilms = outputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(actorsFilms.actor()).isNotNull();
|
||||
@@ -145,7 +145,7 @@ class AzureOpenAiModelCallIerT {
|
||||
""";
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = chatClient.call(prompt).getResult();
|
||||
Generation generation = chatModel.call(prompt).getResult();
|
||||
|
||||
ActorsFilmsRecord actorsFilms = outputConverter.convert(generation.getOutput().getContent());
|
||||
System.out.println(actorsFilms);
|
||||
@@ -166,7 +166,7 @@ class AzureOpenAiModelCallIerT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
|
||||
String generationTextFromStream = chatClient.stream(prompt)
|
||||
String generationTextFromStream = chatModel.stream(prompt)
|
||||
.collectList()
|
||||
.block()
|
||||
.stream()
|
||||
@@ -194,8 +194,8 @@ class AzureOpenAiModelCallIerT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AzureOpenAiModelCaller azureOpenAiChatClient(OpenAIClient openAIClient) {
|
||||
return new AzureOpenAiModelCaller(openAIClient,
|
||||
public AzureOpenAiChatModel azureOpenAiChatModel(OpenAIClient openAIClient) {
|
||||
return new AzureOpenAiChatModel(openAIClient,
|
||||
AzureOpenAiChatOptions.builder().withDeploymentName("gpt-35-turbo").withMaxTokens(200).build());
|
||||
|
||||
}
|
||||
@@ -34,25 +34,25 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@SpringBootTest
|
||||
@EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_API_KEY", matches = ".+")
|
||||
@EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_ENDPOINT", matches = ".+")
|
||||
class AzureOpenAiEmbeddingClientIT {
|
||||
class AzureOpenAiEmbeddingModelIT {
|
||||
|
||||
@Autowired
|
||||
private AzureOpenAiEmbeddingClient embeddingClient;
|
||||
private AzureOpenAiEmbeddingModel embeddingModel;
|
||||
|
||||
@Test
|
||||
void singleEmbedding() {
|
||||
assertThat(embeddingClient).isNotNull();
|
||||
EmbeddingResponse embeddingResponse = embeddingClient.embedForResponse(List.of("Hello World"));
|
||||
assertThat(embeddingModel).isNotNull();
|
||||
EmbeddingResponse embeddingResponse = embeddingModel.embedForResponse(List.of("Hello World"));
|
||||
assertThat(embeddingResponse.getResults()).hasSize(1);
|
||||
assertThat(embeddingResponse.getResults().get(0).getOutput()).isNotEmpty();
|
||||
System.out.println(embeddingClient.dimensions());
|
||||
assertThat(embeddingClient.dimensions()).isEqualTo(1536);
|
||||
System.out.println(embeddingModel.dimensions());
|
||||
assertThat(embeddingModel.dimensions()).isEqualTo(1536);
|
||||
}
|
||||
|
||||
@Test
|
||||
void batchEmbedding() {
|
||||
assertThat(embeddingClient).isNotNull();
|
||||
EmbeddingResponse embeddingResponse = embeddingClient
|
||||
assertThat(embeddingModel).isNotNull();
|
||||
EmbeddingResponse embeddingResponse = embeddingModel
|
||||
.embedForResponse(List.of("Hello World", "World is big and salvation is near"));
|
||||
assertThat(embeddingResponse.getResults()).hasSize(2);
|
||||
assertThat(embeddingResponse.getResults().get(0).getOutput()).isNotEmpty();
|
||||
@@ -60,7 +60,7 @@ class AzureOpenAiEmbeddingClientIT {
|
||||
assertThat(embeddingResponse.getResults().get(1).getOutput()).isNotEmpty();
|
||||
assertThat(embeddingResponse.getResults().get(1).getIndex()).isEqualTo(1);
|
||||
|
||||
assertThat(embeddingClient.dimensions()).isEqualTo(1536);
|
||||
assertThat(embeddingModel.dimensions()).isEqualTo(1536);
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
@@ -74,8 +74,8 @@ class AzureOpenAiEmbeddingClientIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AzureOpenAiEmbeddingClient azureEmbeddingClient(OpenAIClient openAIClient) {
|
||||
return new AzureOpenAiEmbeddingClient(openAIClient);
|
||||
public AzureOpenAiEmbeddingModel azureEmbeddingModel(OpenAIClient openAIClient) {
|
||||
return new AzureOpenAiEmbeddingModel(openAIClient);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -59,8 +59,8 @@ public class MockAzureOpenAiTestConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
AzureOpenAiModelCaller azureOpenAiChatClient(OpenAIClient microsoftAzureOpenAiClient) {
|
||||
return new AzureOpenAiModelCaller(microsoftAzureOpenAiClient);
|
||||
AzureOpenAiChatModel azureOpenAiChatModel(OpenAIClient microsoftAzureOpenAiClient) {
|
||||
return new AzureOpenAiChatModel(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.AzureOpenAiModelCaller;
|
||||
import org.springframework.ai.azure.openai.AzureOpenAiChatModel;
|
||||
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 = AzureOpenAiModelCallFunctionCallIT.TestConfiguration.class)
|
||||
@SpringBootTest(classes = AzureOpenAiChatModelFunctionCallIT.TestConfiguration.class)
|
||||
@EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_API_KEY", matches = ".+")
|
||||
@EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_ENDPOINT", matches = ".+")
|
||||
class AzureOpenAiModelCallFunctionCallIT {
|
||||
class AzureOpenAiChatModelFunctionCallIT {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AzureOpenAiModelCallFunctionCallIT.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(AzureOpenAiChatModelFunctionCallIT.class);
|
||||
|
||||
@Autowired
|
||||
private String selectedModel;
|
||||
|
||||
@Autowired
|
||||
private AzureOpenAiModelCaller chatClient;
|
||||
private AzureOpenAiChatModel chatModel;
|
||||
|
||||
@Test
|
||||
void functionCallTest() {
|
||||
@@ -75,7 +75,7 @@ class AzureOpenAiModelCallFunctionCallIT {
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
ChatResponse response = chatClient.call(new Prompt(messages, promptOptions));
|
||||
ChatResponse response = chatModel.call(new Prompt(messages, promptOptions));
|
||||
|
||||
logger.info("Response: {}", response);
|
||||
|
||||
@@ -99,7 +99,7 @@ class AzureOpenAiModelCallFunctionCallIT {
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
Flux<ChatResponse> response = chatClient.stream(new Prompt(messages, promptOptions));
|
||||
Flux<ChatResponse> response = chatModel.stream(new Prompt(messages, promptOptions));
|
||||
|
||||
final var counter = new AtomicInteger();
|
||||
String content = response.doOnEach(listSignal -> counter.getAndIncrement())
|
||||
@@ -129,8 +129,8 @@ class AzureOpenAiModelCallFunctionCallIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AzureOpenAiModelCaller azureOpenAiChatClient(OpenAIClient openAIClient, String selectedModel) {
|
||||
return new AzureOpenAiModelCaller(openAIClient,
|
||||
public AzureOpenAiChatModel azureOpenAiChatModel(OpenAIClient openAIClient, String selectedModel) {
|
||||
return new AzureOpenAiChatModel(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.AzureOpenAiModelCaller;
|
||||
import org.springframework.ai.azure.openai.AzureOpenAiChatModel;
|
||||
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 AzureOpenAiModelCaller} asserting AI metadata.
|
||||
* Unit Tests for {@link AzureOpenAiChatModel} 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 = AzureOpenAiModelCallMetadataTests.TestConfiguration.class)
|
||||
@ContextConfiguration(classes = AzureOpenAiChatModelMetadataTests.TestConfiguration.class)
|
||||
@SuppressWarnings("unused")
|
||||
class AzureOpenAiModelCallMetadataTests {
|
||||
class AzureOpenAiChatModelMetadataTests {
|
||||
|
||||
@Autowired
|
||||
private AzureOpenAiModelCaller aiClient;
|
||||
private AzureOpenAiChatModel aiClient;
|
||||
|
||||
@Test
|
||||
void azureOpenAiMetadataCapturedDuringGeneration() {
|
||||
@@ -17,7 +17,7 @@ package org.springframework.ai.bedrock.anthropic;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.ai.chat.ChatCaller;
|
||||
import org.springframework.ai.chat.ChatModel;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
|
||||
@@ -27,25 +27,25 @@ import org.springframework.ai.bedrock.MessageToPromptConverter;
|
||||
import org.springframework.ai.bedrock.anthropic.api.AnthropicChatBedrockApi;
|
||||
import org.springframework.ai.bedrock.anthropic.api.AnthropicChatBedrockApi.AnthropicChatRequest;
|
||||
import org.springframework.ai.bedrock.anthropic.api.AnthropicChatBedrockApi.AnthropicChatResponse;
|
||||
import org.springframework.ai.chat.StreamingChatCaller;
|
||||
import org.springframework.ai.chat.StreamingChatModel;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.model.ModelOptionsUtils;
|
||||
|
||||
/**
|
||||
* Java {@link ChatCaller} and {@link StreamingChatCaller} for the Bedrock Anthropic chat
|
||||
* Java {@link ChatModel} and {@link StreamingChatModel} for the Bedrock Anthropic chat
|
||||
* generative.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
* @since 0.8.0
|
||||
*/
|
||||
public class BedrockAnthropicModelCaller implements ChatCaller, StreamingChatCaller {
|
||||
public class BedrockAnthropicChatModel implements ChatModel, StreamingChatModel {
|
||||
|
||||
private final AnthropicChatBedrockApi anthropicChatApi;
|
||||
|
||||
private final AnthropicChatOptions defaultOptions;
|
||||
|
||||
public BedrockAnthropicModelCaller(AnthropicChatBedrockApi chatApi) {
|
||||
public BedrockAnthropicChatModel(AnthropicChatBedrockApi chatApi) {
|
||||
this(chatApi,
|
||||
AnthropicChatOptions.builder()
|
||||
.withTemperature(0.8f)
|
||||
@@ -55,7 +55,7 @@ public class BedrockAnthropicModelCaller implements ChatCaller, StreamingChatCal
|
||||
.build());
|
||||
}
|
||||
|
||||
public BedrockAnthropicModelCaller(AnthropicChatBedrockApi chatApi, AnthropicChatOptions options) {
|
||||
public BedrockAnthropicChatModel(AnthropicChatBedrockApi chatApi, AnthropicChatOptions options) {
|
||||
this.anthropicChatApi = chatApi;
|
||||
this.defaultOptions = options;
|
||||
}
|
||||
@@ -15,18 +15,25 @@
|
||||
*/
|
||||
package org.springframework.ai.bedrock.anthropic3;
|
||||
|
||||
import org.springframework.ai.bedrock.anthropic.AnthropicChatOptions;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.ai.bedrock.anthropic3.api.Anthropic3ChatBedrockApi;
|
||||
import org.springframework.ai.bedrock.anthropic3.api.Anthropic3ChatBedrockApi.AnthropicChatRequest;
|
||||
import org.springframework.ai.bedrock.anthropic3.api.Anthropic3ChatBedrockApi.AnthropicChatResponse;
|
||||
import org.springframework.ai.bedrock.anthropic3.api.Anthropic3ChatBedrockApi.AnthropicChatStreamingResponse.StreamingType;
|
||||
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.ChatCaller;
|
||||
import org.springframework.ai.bedrock.anthropic3.api.Anthropic3ChatBedrockApi.MediaContent;
|
||||
import org.springframework.ai.chat.ChatModel;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatCaller;
|
||||
import org.springframework.ai.chat.StreamingChatModel;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.MessageType;
|
||||
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
|
||||
@@ -35,29 +42,21 @@ import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.model.ModelOptionsUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Java {@link ChatCaller} and {@link StreamingChatCaller} for the Bedrock Anthropic chat
|
||||
* Java {@link ChatModel} and {@link StreamingChatModel} for the Bedrock Anthropic chat
|
||||
* generative.
|
||||
*
|
||||
* @author Ben Middleton
|
||||
* @author Christian Tzolov
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class BedrockAnthropic3ModelCaller implements ChatCaller, StreamingChatCaller {
|
||||
public class BedrockAnthropic3ChatModel implements ChatModel, StreamingChatModel {
|
||||
|
||||
private final Anthropic3ChatBedrockApi anthropicChatApi;
|
||||
|
||||
private final Anthropic3ChatOptions defaultOptions;
|
||||
|
||||
public BedrockAnthropic3ModelCaller(Anthropic3ChatBedrockApi chatApi) {
|
||||
public BedrockAnthropic3ChatModel(Anthropic3ChatBedrockApi chatApi) {
|
||||
this(chatApi,
|
||||
Anthropic3ChatOptions.builder()
|
||||
.withTemperature(0.8f)
|
||||
@@ -67,7 +66,7 @@ public class BedrockAnthropic3ModelCaller implements ChatCaller, StreamingChatCa
|
||||
.build());
|
||||
}
|
||||
|
||||
public BedrockAnthropic3ModelCaller(Anthropic3ChatBedrockApi chatApi, Anthropic3ChatOptions options) {
|
||||
public BedrockAnthropic3ChatModel(Anthropic3ChatBedrockApi chatApi, Anthropic3ChatOptions options) {
|
||||
this.anthropicChatApi = chatApi;
|
||||
this.defaultOptions = options;
|
||||
}
|
||||
@@ -24,11 +24,11 @@ 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.ChatCaller;
|
||||
import org.springframework.ai.chat.ChatModel;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatCaller;
|
||||
import org.springframework.ai.chat.StreamingChatModel;
|
||||
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
|
||||
import org.springframework.ai.chat.metadata.Usage;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
@@ -39,17 +39,17 @@ import org.springframework.util.Assert;
|
||||
* @author Christian Tzolov
|
||||
* @since 0.8.0
|
||||
*/
|
||||
public class BedrockCohereModelCaller implements ChatCaller, StreamingChatCaller {
|
||||
public class BedrockCohereChatModel implements ChatModel, StreamingChatModel {
|
||||
|
||||
private final CohereChatBedrockApi chatApi;
|
||||
|
||||
private final BedrockCohereChatOptions defaultOptions;
|
||||
|
||||
public BedrockCohereModelCaller(CohereChatBedrockApi chatApi) {
|
||||
public BedrockCohereChatModel(CohereChatBedrockApi chatApi) {
|
||||
this(chatApi, BedrockCohereChatOptions.builder().build());
|
||||
}
|
||||
|
||||
public BedrockCohereModelCaller(CohereChatBedrockApi chatApi, BedrockCohereChatOptions options) {
|
||||
public BedrockCohereChatModel(CohereChatBedrockApi chatApi, BedrockCohereChatOptions options) {
|
||||
Assert.notNull(chatApi, "CohereChatBedrockApi must not be null");
|
||||
Assert.notNull(options, "BedrockCohereChatOptions must not be null");
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.springframework.ai.bedrock.cohere.api.CohereEmbeddingBedrockApi;
|
||||
import org.springframework.ai.bedrock.cohere.api.CohereEmbeddingBedrockApi.CohereEmbeddingRequest;
|
||||
import org.springframework.ai.bedrock.cohere.api.CohereEmbeddingBedrockApi.CohereEmbeddingResponse;
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.embedding.AbstractEmbeddingClient;
|
||||
import org.springframework.ai.embedding.AbstractEmbeddingModel;
|
||||
import org.springframework.ai.embedding.Embedding;
|
||||
import org.springframework.ai.embedding.EmbeddingOptions;
|
||||
import org.springframework.ai.embedding.EmbeddingRequest;
|
||||
@@ -31,14 +31,14 @@ import org.springframework.ai.model.ModelOptionsUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.ai.embedding.EmbeddingClient} implementation that uses the
|
||||
* {@link org.springframework.ai.embedding.EmbeddingModel} implementation that uses the
|
||||
* Bedrock Cohere Embedding API. Note: The invocation metrics are not exposed by AWS for
|
||||
* this API. If this change in the future we will add it as metadata.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
* @since 0.8.0
|
||||
*/
|
||||
public class BedrockCohereEmbeddingClient extends AbstractEmbeddingClient {
|
||||
public class BedrockCohereEmbeddingModel extends AbstractEmbeddingModel {
|
||||
|
||||
private final CohereEmbeddingBedrockApi embeddingApi;
|
||||
|
||||
@@ -50,7 +50,7 @@ public class BedrockCohereEmbeddingClient extends AbstractEmbeddingClient {
|
||||
// private CohereEmbeddingRequest.Truncate truncate =
|
||||
// CohereEmbeddingRequest.Truncate.NONE;
|
||||
|
||||
public BedrockCohereEmbeddingClient(CohereEmbeddingBedrockApi cohereEmbeddingBedrockApi) {
|
||||
public BedrockCohereEmbeddingModel(CohereEmbeddingBedrockApi cohereEmbeddingBedrockApi) {
|
||||
this(cohereEmbeddingBedrockApi,
|
||||
BedrockCohereEmbeddingOptions.builder()
|
||||
.withInputType(CohereEmbeddingRequest.InputType.SEARCH_DOCUMENT)
|
||||
@@ -58,7 +58,7 @@ public class BedrockCohereEmbeddingClient extends AbstractEmbeddingClient {
|
||||
.build());
|
||||
}
|
||||
|
||||
public BedrockCohereEmbeddingClient(CohereEmbeddingBedrockApi cohereEmbeddingBedrockApi,
|
||||
public BedrockCohereEmbeddingModel(CohereEmbeddingBedrockApi cohereEmbeddingBedrockApi,
|
||||
BedrockCohereEmbeddingOptions options) {
|
||||
Assert.notNull(cohereEmbeddingBedrockApi, "CohereEmbeddingBedrockApi must not be null");
|
||||
Assert.notNull(options, "BedrockCohereEmbeddingOptions must not be null");
|
||||
@@ -71,7 +71,7 @@ public class BedrockCohereEmbeddingClient extends AbstractEmbeddingClient {
|
||||
// * @param inputType the input type to use.
|
||||
// * @return this client.
|
||||
// */
|
||||
// public BedrockCohereEmbeddingClient withInputType(CohereEmbeddingRequest.InputType
|
||||
// public BedrockCohereEmbeddingModel withInputType(CohereEmbeddingRequest.InputType
|
||||
// inputType) {
|
||||
// this.inputType = inputType;
|
||||
// return this;
|
||||
@@ -85,7 +85,7 @@ public class BedrockCohereEmbeddingClient extends AbstractEmbeddingClient {
|
||||
// * @param truncate the truncate option to use.
|
||||
// * @return this client.
|
||||
// */
|
||||
// public BedrockCohereEmbeddingClient withTruncate(CohereEmbeddingRequest.Truncate
|
||||
// public BedrockCohereEmbeddingModel withTruncate(CohereEmbeddingRequest.Truncate
|
||||
// truncate) {
|
||||
// this.truncate = truncate;
|
||||
// return this;
|
||||
@@ -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.ChatCaller;
|
||||
import org.springframework.ai.chat.ChatModel;
|
||||
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 ChatCaller} for the Bedrock Jurassic2 chat generative model.
|
||||
* Java {@link ChatModel} for the Bedrock Jurassic2 chat generative model.
|
||||
*
|
||||
* @author Ahmed Yousri
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class BedrockAi21Jurassic2ModelCaller implements ChatCaller {
|
||||
public class BedrockAi21Jurassic2ChatModel implements ChatModel {
|
||||
|
||||
private final Ai21Jurassic2ChatBedrockApi chatApi;
|
||||
|
||||
private final BedrockAi21Jurassic2ChatOptions defaultOptions;
|
||||
|
||||
public BedrockAi21Jurassic2ModelCaller(Ai21Jurassic2ChatBedrockApi chatApi,
|
||||
BedrockAi21Jurassic2ChatOptions options) {
|
||||
public BedrockAi21Jurassic2ChatModel(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 BedrockAi21Jurassic2ModelCaller implements ChatCaller {
|
||||
this.defaultOptions = options;
|
||||
}
|
||||
|
||||
public BedrockAi21Jurassic2ModelCaller(Ai21Jurassic2ChatBedrockApi chatApi) {
|
||||
public BedrockAi21Jurassic2ChatModel(Ai21Jurassic2ChatBedrockApi chatApi) {
|
||||
this(chatApi,
|
||||
BedrockAi21Jurassic2ChatOptions.builder()
|
||||
.withTemperature(0.8f)
|
||||
@@ -114,8 +113,8 @@ public class BedrockAi21Jurassic2ModelCaller implements ChatCaller {
|
||||
return this;
|
||||
}
|
||||
|
||||
public BedrockAi21Jurassic2ModelCaller build() {
|
||||
return new BedrockAi21Jurassic2ModelCaller(chatApi,
|
||||
public BedrockAi21Jurassic2ChatModel build() {
|
||||
return new BedrockAi21Jurassic2ChatModel(chatApi,
|
||||
options != null ? options : BedrockAi21Jurassic2ChatOptions.builder().build());
|
||||
}
|
||||
|
||||
@@ -23,11 +23,11 @@ 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.ChatCaller;
|
||||
import org.springframework.ai.chat.ChatModel;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatCaller;
|
||||
import org.springframework.ai.chat.StreamingChatModel;
|
||||
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
|
||||
import org.springframework.ai.chat.metadata.Usage;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
@@ -35,25 +35,25 @@ import org.springframework.ai.model.ModelOptionsUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Java {@link ChatCaller} and {@link StreamingChatCaller} for the Bedrock Llama chat
|
||||
* Java {@link ChatModel} and {@link StreamingChatModel} for the Bedrock Llama chat
|
||||
* generative.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
* @author Wei Jiang
|
||||
* @since 0.8.0
|
||||
*/
|
||||
public class BedrockLlamaModelCaller implements ChatCaller, StreamingChatCaller {
|
||||
public class BedrockLlamaChatModel implements ChatModel, StreamingChatModel {
|
||||
|
||||
private final LlamaChatBedrockApi chatApi;
|
||||
|
||||
private final BedrockLlamaChatOptions defaultOptions;
|
||||
|
||||
public BedrockLlamaModelCaller(LlamaChatBedrockApi chatApi) {
|
||||
public BedrockLlamaChatModel(LlamaChatBedrockApi chatApi) {
|
||||
this(chatApi,
|
||||
BedrockLlamaChatOptions.builder().withTemperature(0.8f).withTopP(0.9f).withMaxGenLen(100).build());
|
||||
}
|
||||
|
||||
public BedrockLlamaModelCaller(LlamaChatBedrockApi chatApi, BedrockLlamaChatOptions options) {
|
||||
public BedrockLlamaChatModel(LlamaChatBedrockApi chatApi, BedrockLlamaChatOptions options) {
|
||||
Assert.notNull(chatApi, "LlamaChatBedrockApi must not be null");
|
||||
Assert.notNull(options, "BedrockLlamaChatOptions must not be null");
|
||||
|
||||
@@ -24,11 +24,11 @@ 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.ChatCaller;
|
||||
import org.springframework.ai.chat.ChatModel;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatCaller;
|
||||
import org.springframework.ai.chat.StreamingChatModel;
|
||||
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
|
||||
import org.springframework.ai.chat.metadata.Usage;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
@@ -39,17 +39,17 @@ import org.springframework.util.Assert;
|
||||
* @author Christian Tzolov
|
||||
* @since 0.8.0
|
||||
*/
|
||||
public class BedrockTitanModelCaller implements ChatCaller, StreamingChatCaller {
|
||||
public class BedrockTitanChatModel implements ChatModel, StreamingChatModel {
|
||||
|
||||
private final TitanChatBedrockApi chatApi;
|
||||
|
||||
private final BedrockTitanChatOptions defaultOptions;
|
||||
|
||||
public BedrockTitanModelCaller(TitanChatBedrockApi chatApi) {
|
||||
public BedrockTitanChatModel(TitanChatBedrockApi chatApi) {
|
||||
this(chatApi, BedrockTitanChatOptions.builder().withTemperature(0.8f).build());
|
||||
}
|
||||
|
||||
public BedrockTitanModelCaller(TitanChatBedrockApi chatApi, BedrockTitanChatOptions defaultOptions) {
|
||||
public BedrockTitanChatModel(TitanChatBedrockApi chatApi, BedrockTitanChatOptions defaultOptions) {
|
||||
Assert.notNull(chatApi, "ChatApi must not be null");
|
||||
Assert.notNull(defaultOptions, "DefaultOptions must not be null");
|
||||
this.chatApi = chatApi;
|
||||
@@ -26,7 +26,7 @@ import org.springframework.ai.bedrock.titan.api.TitanEmbeddingBedrockApi;
|
||||
import org.springframework.ai.bedrock.titan.api.TitanEmbeddingBedrockApi.TitanEmbeddingRequest;
|
||||
import org.springframework.ai.bedrock.titan.api.TitanEmbeddingBedrockApi.TitanEmbeddingResponse;
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.embedding.AbstractEmbeddingClient;
|
||||
import org.springframework.ai.embedding.AbstractEmbeddingModel;
|
||||
import org.springframework.ai.embedding.Embedding;
|
||||
import org.springframework.ai.embedding.EmbeddingOptions;
|
||||
import org.springframework.ai.embedding.EmbeddingRequest;
|
||||
@@ -34,7 +34,7 @@ import org.springframework.ai.embedding.EmbeddingResponse;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.ai.embedding.EmbeddingClient} implementation that uses the
|
||||
* {@link org.springframework.ai.embedding.EmbeddingModel} implementation that uses the
|
||||
* Bedrock Titan Embedding API. Titan Embedding supports text and image (encoded in
|
||||
* base64) inputs.
|
||||
*
|
||||
@@ -44,7 +44,7 @@ import org.springframework.util.Assert;
|
||||
* @author Wei Jiang
|
||||
* @since 0.8.0
|
||||
*/
|
||||
public class BedrockTitanEmbeddingClient extends AbstractEmbeddingClient {
|
||||
public class BedrockTitanEmbeddingModel extends AbstractEmbeddingModel {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@@ -61,7 +61,7 @@ public class BedrockTitanEmbeddingClient extends AbstractEmbeddingClient {
|
||||
*/
|
||||
private InputType inputType = InputType.TEXT;
|
||||
|
||||
public BedrockTitanEmbeddingClient(TitanEmbeddingBedrockApi titanEmbeddingBedrockApi) {
|
||||
public BedrockTitanEmbeddingModel(TitanEmbeddingBedrockApi titanEmbeddingBedrockApi) {
|
||||
this.embeddingApi = titanEmbeddingBedrockApi;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public class BedrockTitanEmbeddingClient extends AbstractEmbeddingClient {
|
||||
* Titan Embedding API input types. Could be either text or image (encoded in base64).
|
||||
* @param inputType the input type to use.
|
||||
*/
|
||||
public BedrockTitanEmbeddingClient withInputType(InputType inputType) {
|
||||
public BedrockTitanEmbeddingModel withInputType(InputType inputType) {
|
||||
this.inputType = inputType;
|
||||
return this;
|
||||
}
|
||||
@@ -18,7 +18,7 @@ package org.springframework.ai.bedrock.titan;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
|
||||
import org.springframework.ai.bedrock.titan.BedrockTitanEmbeddingClient.InputType;
|
||||
import org.springframework.ai.bedrock.titan.BedrockTitanEmbeddingModel.InputType;
|
||||
import org.springframework.ai.embedding.EmbeddingOptions;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
@@ -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 BedrockAnthropicModelCallerIT {
|
||||
class BedrockAnthropicChatModelIT {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BedrockAnthropicModelCallerIT.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(BedrockAnthropicChatModelIT.class);
|
||||
|
||||
@Autowired
|
||||
private BedrockAnthropicModelCaller client;
|
||||
private BedrockAnthropicChatModel chatModel;
|
||||
|
||||
@Value("classpath:/prompts/system-message.st")
|
||||
private Resource systemResource;
|
||||
@@ -68,8 +68,8 @@ class BedrockAnthropicModelCallerIT {
|
||||
@Test
|
||||
void multipleStreamAttempts() {
|
||||
|
||||
Flux<ChatResponse> joke1Stream = client.stream(new Prompt(new UserMessage("Tell me a joke?")));
|
||||
Flux<ChatResponse> joke2Stream = client.stream(new Prompt(new UserMessage("Tell me a toy joke?")));
|
||||
Flux<ChatResponse> joke1Stream = chatModel.stream(new Prompt(new UserMessage("Tell me a joke?")));
|
||||
Flux<ChatResponse> joke2Stream = chatModel.stream(new Prompt(new UserMessage("Tell me a toy joke?")));
|
||||
|
||||
String joke1 = joke1Stream.collectList()
|
||||
.block()
|
||||
@@ -101,7 +101,7 @@ class BedrockAnthropicModelCallerIT {
|
||||
|
||||
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
|
||||
|
||||
ChatResponse response = client.call(prompt);
|
||||
ChatResponse response = chatModel.call(prompt);
|
||||
|
||||
assertThat(response.getResult().getOutput().getContent()).contains("Blackbeard");
|
||||
}
|
||||
@@ -119,7 +119,7 @@ class BedrockAnthropicModelCallerIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template,
|
||||
Map.of("subject", "ice cream flavors.", "format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = this.client.call(prompt).getResult();
|
||||
Generation generation = this.chatModel.call(prompt).getResult();
|
||||
|
||||
List<String> list = outputParser.convert(generation.getOutput().getContent());
|
||||
assertThat(list).hasSize(5);
|
||||
@@ -137,7 +137,7 @@ class BedrockAnthropicModelCallerIT {
|
||||
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 = client.call(prompt).getResult();
|
||||
Generation generation = chatModel.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));
|
||||
@@ -161,7 +161,7 @@ class BedrockAnthropicModelCallerIT {
|
||||
""";
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = client.call(prompt).getResult();
|
||||
Generation generation = chatModel.call(prompt).getResult();
|
||||
|
||||
ActorsFilmsRecord actorsFilms = outputConvert.convert(generation.getOutput().getContent());
|
||||
assertThat(actorsFilms.actor()).isEqualTo("Tom Hanks");
|
||||
@@ -182,7 +182,7 @@ class BedrockAnthropicModelCallerIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
|
||||
String generationTextFromStream = client.stream(prompt)
|
||||
String generationTextFromStream = chatModel.stream(prompt)
|
||||
.collectList()
|
||||
.block()
|
||||
.stream()
|
||||
@@ -209,8 +209,8 @@ class BedrockAnthropicModelCallerIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BedrockAnthropicModelCaller anthropicChatClient(AnthropicChatBedrockApi anthropicApi) {
|
||||
return new BedrockAnthropicModelCaller(anthropicApi);
|
||||
public BedrockAnthropicChatModel anthropicChatModel(AnthropicChatBedrockApi anthropicApi) {
|
||||
return new BedrockAnthropicChatModel(anthropicApi);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,7 +38,7 @@ public class BedrockAnthropicCreateRequestTests {
|
||||
@Test
|
||||
public void createRequestWithChatOptions() {
|
||||
|
||||
var client = new BedrockAnthropicModelCaller(anthropicChatApi,
|
||||
var client = new BedrockAnthropicChatModel(anthropicChatApi,
|
||||
AnthropicChatOptions.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 BedrockAnthropic3ModelCallerIT {
|
||||
class BedrockAnthropic3ChatModelIT {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BedrockAnthropic3ModelCallerIT.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(BedrockAnthropic3ChatModelIT.class);
|
||||
|
||||
@Autowired
|
||||
private BedrockAnthropic3ModelCaller client;
|
||||
private BedrockAnthropic3ChatModel chatModel;
|
||||
|
||||
@Value("classpath:/prompts/system-message.st")
|
||||
private Resource systemResource;
|
||||
@@ -72,8 +72,8 @@ class BedrockAnthropic3ModelCallerIT {
|
||||
@Test
|
||||
void multipleStreamAttempts() {
|
||||
|
||||
Flux<ChatResponse> joke1Stream = client.stream(new Prompt(new UserMessage("Tell me a joke?")));
|
||||
Flux<ChatResponse> joke2Stream = client.stream(new Prompt(new UserMessage("Tell me a toy joke?")));
|
||||
Flux<ChatResponse> joke1Stream = chatModel.stream(new Prompt(new UserMessage("Tell me a joke?")));
|
||||
Flux<ChatResponse> joke2Stream = chatModel.stream(new Prompt(new UserMessage("Tell me a toy joke?")));
|
||||
|
||||
String joke1 = joke1Stream.collectList()
|
||||
.block()
|
||||
@@ -105,7 +105,7 @@ class BedrockAnthropic3ModelCallerIT {
|
||||
|
||||
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
|
||||
|
||||
ChatResponse response = client.call(prompt);
|
||||
ChatResponse response = chatModel.call(prompt);
|
||||
|
||||
assertThat(response.getResult().getOutput().getContent()).contains("Blackbeard");
|
||||
}
|
||||
@@ -123,7 +123,7 @@ class BedrockAnthropic3ModelCallerIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template,
|
||||
Map.of("subject", "ice cream flavors.", "format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = this.client.call(prompt).getResult();
|
||||
Generation generation = this.chatModel.call(prompt).getResult();
|
||||
|
||||
List<String> list = outputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(list).hasSize(5);
|
||||
@@ -142,7 +142,7 @@ class BedrockAnthropic3ModelCallerIT {
|
||||
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 = client.call(prompt).getResult();
|
||||
Generation generation = chatModel.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));
|
||||
@@ -166,7 +166,7 @@ class BedrockAnthropic3ModelCallerIT {
|
||||
""";
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = client.call(prompt).getResult();
|
||||
Generation generation = chatModel.call(prompt).getResult();
|
||||
|
||||
ActorsFilmsRecord actorsFilms = outputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(actorsFilms.actor()).isEqualTo("Tom Hanks");
|
||||
@@ -187,7 +187,7 @@ class BedrockAnthropic3ModelCallerIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
|
||||
String generationTextFromStream = client.stream(prompt)
|
||||
String generationTextFromStream = chatModel.stream(prompt)
|
||||
.collectList()
|
||||
.block()
|
||||
.stream()
|
||||
@@ -211,7 +211,7 @@ class BedrockAnthropic3ModelCallerIT {
|
||||
var userMessage = new UserMessage("Explain what do you see o this picture?",
|
||||
List.of(new Media(MimeTypeUtils.IMAGE_PNG, imageData)));
|
||||
|
||||
var response = client.call(new Prompt(List.of(userMessage)));
|
||||
var response = chatModel.call(new Prompt(List.of(userMessage)));
|
||||
|
||||
logger.info(response.getResult().getOutput().getContent());
|
||||
assertThat(response.getResult().getOutput().getContent()).contains("bananas", "apple", "basket");
|
||||
@@ -228,8 +228,8 @@ class BedrockAnthropic3ModelCallerIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BedrockAnthropic3ModelCaller anthropicChatClient(Anthropic3ChatBedrockApi anthropicApi) {
|
||||
return new BedrockAnthropic3ModelCaller(anthropicApi);
|
||||
public BedrockAnthropic3ChatModel anthropicChatModel(Anthropic3ChatBedrockApi anthropicApi) {
|
||||
return new BedrockAnthropic3ChatModel(anthropicApi);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -37,7 +37,7 @@ public class BedrockAnthropic3CreateRequestTests {
|
||||
@Test
|
||||
public void createRequestWithChatOptions() {
|
||||
|
||||
var client = new BedrockAnthropic3ModelCaller(anthropicChatApi,
|
||||
var client = new BedrockAnthropic3ChatModel(anthropicChatApi,
|
||||
Anthropic3ChatOptions.builder()
|
||||
.withTemperature(66.6f)
|
||||
.withTopK(66)
|
||||
|
||||
@@ -45,7 +45,7 @@ public class BedrockCohereChatCreateRequestTests {
|
||||
@Test
|
||||
public void createRequestWithChatOptions() {
|
||||
|
||||
var client = new BedrockCohereModelCaller(chatApi,
|
||||
var client = new BedrockCohereChatModel(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 BedrockCohereModelCallerIT {
|
||||
class BedrockCohereChatModelIT {
|
||||
|
||||
@Autowired
|
||||
private BedrockCohereModelCaller client;
|
||||
private BedrockCohereChatModel chatModel;
|
||||
|
||||
@Value("classpath:/prompts/system-message.st")
|
||||
private Resource systemResource;
|
||||
@@ -65,8 +65,8 @@ class BedrockCohereModelCallerIT {
|
||||
@Test
|
||||
void multipleStreamAttempts() {
|
||||
|
||||
Flux<ChatResponse> joke1Stream = client.stream(new Prompt(new UserMessage("Tell me a joke?")));
|
||||
Flux<ChatResponse> joke2Stream = client.stream(new Prompt(new UserMessage("Tell me a toy joke?")));
|
||||
Flux<ChatResponse> joke1Stream = chatModel.stream(new Prompt(new UserMessage("Tell me a joke?")));
|
||||
Flux<ChatResponse> joke2Stream = chatModel.stream(new Prompt(new UserMessage("Tell me a toy joke?")));
|
||||
|
||||
String joke1 = joke1Stream.collectList()
|
||||
.block()
|
||||
@@ -98,7 +98,7 @@ class BedrockCohereModelCallerIT {
|
||||
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);
|
||||
Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice));
|
||||
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
|
||||
ChatResponse response = client.call(prompt);
|
||||
ChatResponse response = chatModel.call(prompt);
|
||||
assertThat(response.getResult().getOutput().getContent()).contains("Blackbeard");
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ class BedrockCohereModelCallerIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template,
|
||||
Map.of("subject", "ice cream flavors.", "format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = this.client.call(prompt).getResult();
|
||||
Generation generation = this.chatModel.call(prompt).getResult();
|
||||
|
||||
List<String> list = outputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(list).hasSize(5);
|
||||
@@ -134,7 +134,7 @@ class BedrockCohereModelCallerIT {
|
||||
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 = client.call(prompt).getResult();
|
||||
Generation generation = chatModel.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));
|
||||
@@ -157,7 +157,7 @@ class BedrockCohereModelCallerIT {
|
||||
""";
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = client.call(prompt).getResult();
|
||||
Generation generation = chatModel.call(prompt).getResult();
|
||||
|
||||
ActorsFilmsRecord actorsFilms = outputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(actorsFilms.actor()).isEqualTo("Tom Hanks");
|
||||
@@ -178,7 +178,7 @@ class BedrockCohereModelCallerIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
|
||||
String generationTextFromStream = client.stream(prompt)
|
||||
String generationTextFromStream = chatModel.stream(prompt)
|
||||
.collectList()
|
||||
.block()
|
||||
.stream()
|
||||
@@ -205,8 +205,8 @@ class BedrockCohereModelCallerIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BedrockCohereModelCaller cohereChatClient(CohereChatBedrockApi cohereApi) {
|
||||
return new BedrockCohereModelCaller(cohereApi);
|
||||
public BedrockCohereChatModel cohereChatModel(CohereChatBedrockApi cohereApi) {
|
||||
return new BedrockCohereChatModel(cohereApi);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -39,24 +39,24 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@SpringBootTest
|
||||
@EnabledIfEnvironmentVariable(named = "AWS_ACCESS_KEY_ID", matches = ".*")
|
||||
@EnabledIfEnvironmentVariable(named = "AWS_SECRET_ACCESS_KEY", matches = ".*")
|
||||
class BedrockCohereEmbeddingClientIT {
|
||||
class BedrockCohereEmbeddingModelIT {
|
||||
|
||||
@Autowired
|
||||
private BedrockCohereEmbeddingClient embeddingClient;
|
||||
private BedrockCohereEmbeddingModel embeddingModel;
|
||||
|
||||
@Test
|
||||
void singleEmbedding() {
|
||||
assertThat(embeddingClient).isNotNull();
|
||||
EmbeddingResponse embeddingResponse = embeddingClient.embedForResponse(List.of("Hello World"));
|
||||
assertThat(embeddingModel).isNotNull();
|
||||
EmbeddingResponse embeddingResponse = embeddingModel.embedForResponse(List.of("Hello World"));
|
||||
assertThat(embeddingResponse.getResults()).hasSize(1);
|
||||
assertThat(embeddingResponse.getResults().get(0).getOutput()).isNotEmpty();
|
||||
assertThat(embeddingClient.dimensions()).isEqualTo(1024);
|
||||
assertThat(embeddingModel.dimensions()).isEqualTo(1024);
|
||||
}
|
||||
|
||||
@Test
|
||||
void batchEmbedding() {
|
||||
assertThat(embeddingClient).isNotNull();
|
||||
EmbeddingResponse embeddingResponse = embeddingClient
|
||||
assertThat(embeddingModel).isNotNull();
|
||||
EmbeddingResponse embeddingResponse = embeddingModel
|
||||
.embedForResponse(List.of("Hello World", "World is big and salvation is near"));
|
||||
assertThat(embeddingResponse.getResults()).hasSize(2);
|
||||
assertThat(embeddingResponse.getResults().get(0).getOutput()).isNotEmpty();
|
||||
@@ -64,13 +64,13 @@ class BedrockCohereEmbeddingClientIT {
|
||||
assertThat(embeddingResponse.getResults().get(1).getOutput()).isNotEmpty();
|
||||
assertThat(embeddingResponse.getResults().get(1).getIndex()).isEqualTo(1);
|
||||
|
||||
assertThat(embeddingClient.dimensions()).isEqualTo(1024);
|
||||
assertThat(embeddingModel.dimensions()).isEqualTo(1024);
|
||||
}
|
||||
|
||||
@Test
|
||||
void embeddingWthOptions() {
|
||||
assertThat(embeddingClient).isNotNull();
|
||||
EmbeddingResponse embeddingResponse = embeddingClient
|
||||
assertThat(embeddingModel).isNotNull();
|
||||
EmbeddingResponse embeddingResponse = embeddingModel
|
||||
.call(new EmbeddingRequest(List.of("Hello World", "World is big and salvation is near"),
|
||||
BedrockCohereEmbeddingOptions.builder().withInputType(InputType.SEARCH_DOCUMENT).build()));
|
||||
assertThat(embeddingResponse.getResults()).hasSize(2);
|
||||
@@ -79,7 +79,7 @@ class BedrockCohereEmbeddingClientIT {
|
||||
assertThat(embeddingResponse.getResults().get(1).getOutput()).isNotEmpty();
|
||||
assertThat(embeddingResponse.getResults().get(1).getIndex()).isEqualTo(1);
|
||||
|
||||
assertThat(embeddingClient.dimensions()).isEqualTo(1024);
|
||||
assertThat(embeddingModel.dimensions()).isEqualTo(1024);
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
@@ -93,8 +93,8 @@ class BedrockCohereEmbeddingClientIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BedrockCohereEmbeddingClient cohereAiEmbedding(CohereEmbeddingBedrockApi cohereEmbeddingApi) {
|
||||
return new BedrockCohereEmbeddingClient(cohereEmbeddingApi);
|
||||
public BedrockCohereEmbeddingModel cohereAiEmbedding(CohereEmbeddingBedrockApi cohereEmbeddingApi) {
|
||||
return new BedrockCohereEmbeddingModel(cohereEmbeddingApi);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 BedrockAi21Jurassic2ModelCallerIT {
|
||||
class BedrockAi21Jurassic2ChatModelIT {
|
||||
|
||||
@Autowired
|
||||
private BedrockAi21Jurassic2ModelCaller client;
|
||||
private BedrockAi21Jurassic2ChatModel chatModel;
|
||||
|
||||
@Value("classpath:/prompts/system-message.st")
|
||||
private Resource systemResource;
|
||||
@@ -66,7 +66,7 @@ class BedrockAi21Jurassic2ModelCallerIT {
|
||||
|
||||
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
|
||||
|
||||
ChatResponse response = client.call(prompt);
|
||||
ChatResponse response = chatModel.call(prompt);
|
||||
|
||||
assertThat(response.getResult().getOutput().getContent()).contains("Blackbeard");
|
||||
}
|
||||
@@ -83,7 +83,7 @@ class BedrockAi21Jurassic2ModelCallerIT {
|
||||
UserMessage userMessage = new UserMessage("Can you express happiness using an emoji like 😄 ?");
|
||||
Prompt prompt = new Prompt(List.of(userMessage), options);
|
||||
|
||||
ChatResponse response = client.call(prompt);
|
||||
ChatResponse response = chatModel.call(prompt);
|
||||
|
||||
assertThat(response.getResult().getOutput().getContent()).matches(content -> content.contains("😄"));
|
||||
}
|
||||
@@ -103,7 +103,7 @@ class BedrockAi21Jurassic2ModelCallerIT {
|
||||
|
||||
Prompt prompt = new Prompt(List.of(userMessage, systemMessage), options);
|
||||
|
||||
ChatResponse response = client.call(prompt);
|
||||
ChatResponse response = chatModel.call(prompt);
|
||||
|
||||
assertThat(response.getResult().getOutput().getContent()).doesNotContain("😄");
|
||||
}
|
||||
@@ -120,7 +120,7 @@ class BedrockAi21Jurassic2ModelCallerIT {
|
||||
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 = client.call(prompt).getResult();
|
||||
Generation generation = chatModel.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));
|
||||
@@ -135,7 +135,7 @@ class BedrockAi21Jurassic2ModelCallerIT {
|
||||
|
||||
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
|
||||
|
||||
ChatResponse response = client.call(prompt);
|
||||
ChatResponse response = chatModel.call(prompt);
|
||||
|
||||
assertThat(response.getResult().getOutput().getContent()).contains("AI");
|
||||
}
|
||||
@@ -152,9 +152,9 @@ class BedrockAi21Jurassic2ModelCallerIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BedrockAi21Jurassic2ModelCaller bedrockAi21Jurassic2ChatClient(
|
||||
public BedrockAi21Jurassic2ChatModel bedrockAi21Jurassic2ChatModel(
|
||||
Ai21Jurassic2ChatBedrockApi jurassic2ChatBedrockApi) {
|
||||
return new BedrockAi21Jurassic2ModelCaller(jurassic2ChatBedrockApi,
|
||||
return new BedrockAi21Jurassic2ChatModel(jurassic2ChatBedrockApi,
|
||||
BedrockAi21Jurassic2ChatOptions.builder()
|
||||
.withTemperature(0.5f)
|
||||
.withMaxTokens(100)
|
||||
@@ -45,7 +45,7 @@ public class BedrockLlamaCreateRequestTests {
|
||||
@Test
|
||||
public void createRequestWithChatOptions() {
|
||||
|
||||
var client = new BedrockLlamaModelCaller(api,
|
||||
var client = new BedrockLlamaChatModel(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 BedrockLlamaModelCallerIT {
|
||||
class BedrockLlamaChatModelIT {
|
||||
|
||||
@Autowired
|
||||
private BedrockLlamaModelCaller client;
|
||||
private BedrockLlamaChatModel chatModel;
|
||||
|
||||
@Value("classpath:/prompts/system-message.st")
|
||||
private Resource systemResource;
|
||||
@@ -65,8 +65,8 @@ class BedrockLlamaModelCallerIT {
|
||||
@Test
|
||||
void multipleStreamAttempts() {
|
||||
|
||||
Flux<ChatResponse> joke2Stream = client.stream(new Prompt(new UserMessage("Tell me a Toy joke?")));
|
||||
Flux<ChatResponse> joke1Stream = client.stream(new Prompt(new UserMessage("Tell me a joke?")));
|
||||
Flux<ChatResponse> joke2Stream = chatModel.stream(new Prompt(new UserMessage("Tell me a Toy joke?")));
|
||||
Flux<ChatResponse> joke1Stream = chatModel.stream(new Prompt(new UserMessage("Tell me a joke?")));
|
||||
|
||||
String joke1 = joke1Stream.collectList()
|
||||
.block()
|
||||
@@ -98,7 +98,7 @@ class BedrockLlamaModelCallerIT {
|
||||
|
||||
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
|
||||
|
||||
ChatResponse response = client.call(prompt);
|
||||
ChatResponse response = chatModel.call(prompt);
|
||||
|
||||
assertThat(response.getResult().getOutput().getContent()).contains("Blackbeard");
|
||||
}
|
||||
@@ -116,7 +116,7 @@ class BedrockLlamaModelCallerIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template,
|
||||
Map.of("subject", "ice cream flavors.", "format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = this.client.call(prompt).getResult();
|
||||
Generation generation = this.chatModel.call(prompt).getResult();
|
||||
|
||||
List<String> list = outputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(list).hasSize(5);
|
||||
@@ -134,7 +134,7 @@ class BedrockLlamaModelCallerIT {
|
||||
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 = client.call(prompt).getResult();
|
||||
Generation generation = chatModel.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));
|
||||
@@ -158,7 +158,7 @@ class BedrockLlamaModelCallerIT {
|
||||
""";
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = client.call(prompt).getResult();
|
||||
Generation generation = chatModel.call(prompt).getResult();
|
||||
|
||||
ActorsFilmsRecord actorsFilms = outputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(actorsFilms.actor()).isEqualTo("Tom Hanks");
|
||||
@@ -179,7 +179,7 @@ class BedrockLlamaModelCallerIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
|
||||
String generationTextFromStream = client.stream(prompt)
|
||||
String generationTextFromStream = chatModel.stream(prompt)
|
||||
.collectList()
|
||||
.block()
|
||||
.stream()
|
||||
@@ -206,8 +206,8 @@ class BedrockLlamaModelCallerIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BedrockLlamaModelCaller llamaChatClient(LlamaChatBedrockApi llamaApi) {
|
||||
return new BedrockLlamaModelCaller(llamaApi,
|
||||
public BedrockLlamaChatModel llamaChatModel(LlamaChatBedrockApi llamaApi) {
|
||||
return new BedrockLlamaChatModel(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 BedrockTitanModelCaller(api,
|
||||
var client = new BedrockTitanChatModel(api,
|
||||
BedrockTitanChatOptions.builder()
|
||||
.withTemperature(66.6f)
|
||||
.withTopP(0.66f)
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
|
||||
import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
|
||||
import org.springframework.ai.bedrock.titan.BedrockTitanEmbeddingClient.InputType;
|
||||
import org.springframework.ai.bedrock.titan.BedrockTitanEmbeddingModel.InputType;
|
||||
import org.springframework.ai.bedrock.titan.api.TitanEmbeddingBedrockApi;
|
||||
import org.springframework.ai.bedrock.titan.api.TitanEmbeddingBedrockApi.TitanEmbeddingModel;
|
||||
import org.springframework.ai.embedding.EmbeddingRequest;
|
||||
@@ -44,19 +44,19 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@SpringBootTest
|
||||
@EnabledIfEnvironmentVariable(named = "AWS_ACCESS_KEY_ID", matches = ".*")
|
||||
@EnabledIfEnvironmentVariable(named = "AWS_SECRET_ACCESS_KEY", matches = ".*")
|
||||
class BedrockTitanEmbeddingClientIT {
|
||||
class BedrockTitanEmbeddingModelIT {
|
||||
|
||||
@Autowired
|
||||
private BedrockTitanEmbeddingClient embeddingClient;
|
||||
private BedrockTitanEmbeddingModel embeddingModel;
|
||||
|
||||
@Test
|
||||
void singleEmbedding() {
|
||||
assertThat(embeddingClient).isNotNull();
|
||||
EmbeddingResponse embeddingResponse = embeddingClient.call(new EmbeddingRequest(List.of("Hello World"),
|
||||
assertThat(embeddingModel).isNotNull();
|
||||
EmbeddingResponse embeddingResponse = embeddingModel.call(new EmbeddingRequest(List.of("Hello World"),
|
||||
BedrockTitanEmbeddingOptions.builder().withInputType(InputType.TEXT).build()));
|
||||
assertThat(embeddingResponse.getResults()).hasSize(1);
|
||||
assertThat(embeddingResponse.getResults().get(0).getOutput()).isNotEmpty();
|
||||
assertThat(embeddingClient.dimensions()).isEqualTo(1024);
|
||||
assertThat(embeddingModel.dimensions()).isEqualTo(1024);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -65,12 +65,12 @@ class BedrockTitanEmbeddingClientIT {
|
||||
byte[] image = new DefaultResourceLoader().getResource("classpath:/spring_framework.png")
|
||||
.getContentAsByteArray();
|
||||
|
||||
EmbeddingResponse embeddingResponse = embeddingClient
|
||||
EmbeddingResponse embeddingResponse = embeddingModel
|
||||
.call(new EmbeddingRequest(List.of(Base64.getEncoder().encodeToString(image)),
|
||||
BedrockTitanEmbeddingOptions.builder().withInputType(InputType.IMAGE).build()));
|
||||
assertThat(embeddingResponse.getResults()).hasSize(1);
|
||||
assertThat(embeddingResponse.getResults().get(0).getOutput()).isNotEmpty();
|
||||
assertThat(embeddingClient.dimensions()).isEqualTo(1024);
|
||||
assertThat(embeddingModel.dimensions()).isEqualTo(1024);
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
@@ -84,8 +84,8 @@ class BedrockTitanEmbeddingClientIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BedrockTitanEmbeddingClient titanEmbedding(TitanEmbeddingBedrockApi titanEmbeddingApi) {
|
||||
return new BedrockTitanEmbeddingClient(titanEmbeddingApi);
|
||||
public BedrockTitanEmbeddingModel titanEmbedding(TitanEmbeddingBedrockApi titanEmbeddingApi) {
|
||||
return new BedrockTitanEmbeddingModel(titanEmbeddingApi);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -58,7 +58,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class BedrockTitanModelCalerlIT {
|
||||
|
||||
@Autowired
|
||||
private BedrockTitanModelCaller client;
|
||||
private BedrockTitanChatModel chatModel;
|
||||
|
||||
@Value("classpath:/prompts/system-message.st")
|
||||
private Resource systemResource;
|
||||
@@ -66,8 +66,8 @@ class BedrockTitanModelCalerlIT {
|
||||
@Test
|
||||
void multipleStreamAttempts() {
|
||||
|
||||
Flux<ChatResponse> joke1Stream = client.stream(new Prompt(new UserMessage("Tell me a joke?")));
|
||||
Flux<ChatResponse> joke2Stream = client.stream(new Prompt(new UserMessage("Tell me a toy joke?")));
|
||||
Flux<ChatResponse> joke1Stream = chatModel.stream(new Prompt(new UserMessage("Tell me a joke?")));
|
||||
Flux<ChatResponse> joke2Stream = chatModel.stream(new Prompt(new UserMessage("Tell me a toy joke?")));
|
||||
|
||||
String joke1 = joke1Stream.collectList()
|
||||
.block()
|
||||
@@ -99,7 +99,7 @@ class BedrockTitanModelCalerlIT {
|
||||
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);
|
||||
Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice));
|
||||
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
|
||||
ChatResponse response = client.call(prompt);
|
||||
ChatResponse response = chatModel.call(prompt);
|
||||
assertThat(response.getResult().getOutput().getContent()).contains("Blackbeard");
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ class BedrockTitanModelCalerlIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template,
|
||||
Map.of("subject", "ice cream flavors.", "format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = this.client.call(prompt).getResult();
|
||||
Generation generation = this.chatModel.call(prompt).getResult();
|
||||
|
||||
List<String> list = outputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(list).hasSize(5);
|
||||
@@ -138,7 +138,7 @@ class BedrockTitanModelCalerlIT {
|
||||
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 = client.call(prompt).getResult();
|
||||
Generation generation = chatModel.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));
|
||||
@@ -162,7 +162,7 @@ class BedrockTitanModelCalerlIT {
|
||||
""";
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = client.call(prompt).getResult();
|
||||
Generation generation = chatModel.call(prompt).getResult();
|
||||
|
||||
ActorsFilmsRecord actorsFilms = outputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(actorsFilms.actor()).isEqualTo("Tom Hanks");
|
||||
@@ -184,7 +184,7 @@ class BedrockTitanModelCalerlIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
|
||||
String generationTextFromStream = client.stream(prompt)
|
||||
String generationTextFromStream = chatModel.stream(prompt)
|
||||
.collectList()
|
||||
.block()
|
||||
.stream()
|
||||
@@ -211,8 +211,8 @@ class BedrockTitanModelCalerlIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BedrockTitanModelCaller titanChatClient(TitanChatBedrockApi titanApi) {
|
||||
return new BedrockTitanModelCaller(titanApi);
|
||||
public BedrockTitanChatModel titanChatModel(TitanChatBedrockApi titanApi) {
|
||||
return new BedrockTitanChatModel(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.ChatCaller;
|
||||
import org.springframework.ai.chat.ChatModel;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.huggingface.api.TextGenerationInferenceApi;
|
||||
@@ -36,12 +36,12 @@ import org.springframework.ai.chat.prompt.ChatOptionsBuilder;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ChatCaller} that interfaces with HuggingFace Inference
|
||||
* An implementation of {@link ChatModel} that interfaces with HuggingFace Inference
|
||||
* Endpoints for text generation.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
*/
|
||||
public class HuggingfaceModelCaller implements ChatCaller {
|
||||
public class HuggingfaceChatModel implements ChatModel {
|
||||
|
||||
/**
|
||||
* Token required for authenticating with the HuggingFace Inference API.
|
||||
@@ -70,11 +70,11 @@ public class HuggingfaceModelCaller implements ChatCaller {
|
||||
private int maxNewTokens = 1000;
|
||||
|
||||
/**
|
||||
* Constructs a new HuggingfaceModelCall with the specified API token and base path.
|
||||
* Constructs a new HuggingfaceChatModel with the specified API token and base path.
|
||||
* @param apiToken The API token for HuggingFace.
|
||||
* @param basePath The base path for API requests.
|
||||
*/
|
||||
public HuggingfaceModelCaller(final String apiToken, String basePath) {
|
||||
public HuggingfaceChatModel(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 HuggingfaceModelCaller huggingfaceChatClient() {
|
||||
public HuggingfaceChatModel huggingfaceChatModel() {
|
||||
String apiKey = System.getenv("HUGGINGFACE_API_KEY");
|
||||
if (!StringUtils.hasText(apiKey)) {
|
||||
throw new IllegalArgumentException(
|
||||
@@ -31,9 +31,9 @@ public class HuggingfaceTestConfiguration {
|
||||
}
|
||||
// Created aws-mistral-7b-instruct-v0-1-805 via
|
||||
// https://ui.endpoints.huggingface.co/
|
||||
HuggingfaceModelCaller huggingfaceChatClient = new HuggingfaceModelCaller(apiKey,
|
||||
HuggingfaceChatModel huggingfaceChatModel = new HuggingfaceChatModel(apiKey,
|
||||
"https://f6hg7b3cvlmntp5i.us-east-1.aws.endpoints.huggingface.cloud");
|
||||
return huggingfaceChatClient;
|
||||
return huggingfaceChatModel;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.HuggingfaceModelCaller;
|
||||
import org.springframework.ai.huggingface.HuggingfaceChatModel;
|
||||
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 HuggingfaceModelCaller huggingfaceChatClient;
|
||||
protected HuggingfaceChatModel huggingfaceChatModel;
|
||||
|
||||
@Test
|
||||
void helloWorldCompletion() {
|
||||
@@ -46,7 +46,7 @@ public class ClientIT {
|
||||
[/INST]
|
||||
""";
|
||||
Prompt prompt = new Prompt(mistral7bInstruct);
|
||||
ChatResponse chatResponse = huggingfaceChatClient.call(prompt);
|
||||
ChatResponse chatResponse = huggingfaceChatModel.call(prompt);
|
||||
assertThat(chatResponse.getResult().getOutput().getContent()).isNotEmpty();
|
||||
String expectedResponse = """
|
||||
```json
|
||||
|
||||
@@ -15,14 +15,23 @@
|
||||
*/
|
||||
package org.springframework.ai.minimax;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.ai.chat.ChatCaller;
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ChatModel;
|
||||
import org.springframework.ai.chat.ChatModel;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatCaller;
|
||||
import org.springframework.ai.chat.StreamingChatModel;
|
||||
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
@@ -36,33 +45,22 @@ import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.MimeType;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* {@link ChatClient} and {@link StreamingChatCaller} implementation for
|
||||
* {@literal MiniMax} backed by {@link MiniMaxApi}.
|
||||
* {@link ChatModel} and {@link StreamingChatModel} implementation for {@literal MiniMax}
|
||||
* backed by {@link MiniMaxApi}.
|
||||
*
|
||||
* @author Geng Rong
|
||||
* @see ChatClient
|
||||
* @see StreamingChatCaller
|
||||
* @see ChatModel
|
||||
* @see StreamingChatModel
|
||||
* @see MiniMaxApi
|
||||
* @since 1.0.0 M1
|
||||
*/
|
||||
public class MiniMaxChatCaller extends
|
||||
public class MiniMaxChatModel extends
|
||||
AbstractFunctionCallSupport<MiniMaxApi.ChatCompletionMessage, MiniMaxApi.ChatCompletionRequest, ResponseEntity<MiniMaxApi.ChatCompletion>>
|
||||
implements ChatCaller, StreamingChatCaller {
|
||||
implements ChatModel, StreamingChatModel {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MiniMaxChatCaller.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(MiniMaxChatModel.class);
|
||||
|
||||
/**
|
||||
* The default options used for the chat completion requests.
|
||||
@@ -80,35 +78,35 @@ public class MiniMaxChatCaller extends
|
||||
private final MiniMaxApi miniMaxApi;
|
||||
|
||||
/**
|
||||
* Creates an instance of the MiniMaxChatClient.
|
||||
* Creates an instance of the MiniMaxChatModel.
|
||||
* @param miniMaxApi The MiniMaxApi instance to be used for interacting with the
|
||||
* MiniMax Chat API.
|
||||
* @throws IllegalArgumentException if MiniMaxApi is null
|
||||
*/
|
||||
public MiniMaxChatCaller(MiniMaxApi miniMaxApi) {
|
||||
public MiniMaxChatModel(MiniMaxApi miniMaxApi) {
|
||||
this(miniMaxApi,
|
||||
MiniMaxChatOptions.builder().withModel(MiniMaxApi.DEFAULT_CHAT_MODEL).withTemperature(0.7f).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes an instance of the MiniMaxChatClient.
|
||||
* Initializes an instance of the MiniMaxChatModel.
|
||||
* @param miniMaxApi The MiniMaxApi instance to be used for interacting with the
|
||||
* MiniMax Chat API.
|
||||
* @param options The MiniMaxChatOptions to configure the chat client.
|
||||
* @param options The MiniMaxChatOptions to configure the chat model.
|
||||
*/
|
||||
public MiniMaxChatCaller(MiniMaxApi miniMaxApi, MiniMaxChatOptions options) {
|
||||
public MiniMaxChatModel(MiniMaxApi miniMaxApi, MiniMaxChatOptions options) {
|
||||
this(miniMaxApi, options, null, RetryUtils.DEFAULT_RETRY_TEMPLATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the MiniMaxChatClient.
|
||||
* Initializes a new instance of the MiniMaxChatModel.
|
||||
* @param miniMaxApi The MiniMaxApi instance to be used for interacting with the
|
||||
* MiniMax Chat API.
|
||||
* @param options The MiniMaxChatOptions to configure the chat client.
|
||||
* @param options The MiniMaxChatOptions to configure the chat model.
|
||||
* @param functionCallbackContext The function callback context.
|
||||
* @param retryTemplate The retry template.
|
||||
*/
|
||||
public MiniMaxChatCaller(MiniMaxApi miniMaxApi, MiniMaxChatOptions options,
|
||||
public MiniMaxChatModel(MiniMaxApi miniMaxApi, MiniMaxChatOptions options,
|
||||
FunctionCallbackContext functionCallbackContext, RetryTemplate retryTemplate) {
|
||||
super(functionCallbackContext);
|
||||
Assert.notNull(miniMaxApi, "MiniMaxApi must not be null");
|
||||
@@ -114,10 +114,10 @@ public class MiniMaxChatOptions implements FunctionCallingOptions, ChatOptions {
|
||||
private @JsonProperty("tool_choice") String toolChoice;
|
||||
|
||||
/**
|
||||
* MiniMax Tool Function Callbacks to register with the ChatClient.
|
||||
* MiniMax Tool Function Callbacks to register with the ChatModel.
|
||||
* 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 ChatModel chat completion requests.
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
@JsonIgnore
|
||||
|
||||
@@ -19,7 +19,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.document.MetadataMode;
|
||||
import org.springframework.ai.embedding.AbstractEmbeddingClient;
|
||||
import org.springframework.ai.embedding.AbstractEmbeddingModel;
|
||||
import org.springframework.ai.embedding.Embedding;
|
||||
import org.springframework.ai.embedding.EmbeddingOptions;
|
||||
import org.springframework.ai.embedding.EmbeddingRequest;
|
||||
@@ -40,9 +40,9 @@ import java.util.List;
|
||||
* @author Geng Rong
|
||||
* @since 1.0.0 M1
|
||||
*/
|
||||
public class MiniMaxEmbeddingClient extends AbstractEmbeddingClient {
|
||||
public class MiniMaxEmbeddingModel extends AbstractEmbeddingModel {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MiniMaxEmbeddingClient.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(MiniMaxEmbeddingModel.class);
|
||||
|
||||
private final MiniMaxEmbeddingOptions defaultOptions;
|
||||
|
||||
@@ -53,43 +53,43 @@ public class MiniMaxEmbeddingClient extends AbstractEmbeddingClient {
|
||||
private final MetadataMode metadataMode;
|
||||
|
||||
/**
|
||||
* Constructor for the MiniMaxEmbeddingClient class.
|
||||
* Constructor for the MiniMaxEmbeddingModel class.
|
||||
* @param miniMaxApi The MiniMaxApi instance to use for making API requests.
|
||||
*/
|
||||
public MiniMaxEmbeddingClient(MiniMaxApi miniMaxApi) {
|
||||
public MiniMaxEmbeddingModel(MiniMaxApi miniMaxApi) {
|
||||
this(miniMaxApi, MetadataMode.EMBED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the MiniMaxEmbeddingClient class.
|
||||
* Initializes a new instance of the MiniMaxEmbeddingModel class.
|
||||
* @param miniMaxApi The MiniMaxApi instance to use for making API requests.
|
||||
* @param metadataMode The mode for generating metadata.
|
||||
*/
|
||||
public MiniMaxEmbeddingClient(MiniMaxApi miniMaxApi, MetadataMode metadataMode) {
|
||||
public MiniMaxEmbeddingModel(MiniMaxApi miniMaxApi, MetadataMode metadataMode) {
|
||||
this(miniMaxApi, metadataMode,
|
||||
MiniMaxEmbeddingOptions.builder().withModel(MiniMaxApi.DEFAULT_EMBEDDING_MODEL).build(),
|
||||
RetryUtils.DEFAULT_RETRY_TEMPLATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the MiniMaxEmbeddingClient class.
|
||||
* Initializes a new instance of the MiniMaxEmbeddingModel class.
|
||||
* @param miniMaxApi The MiniMaxApi instance to use for making API requests.
|
||||
* @param metadataMode The mode for generating metadata.
|
||||
* @param miniMaxEmbeddingOptions The options for MiniMax embedding.
|
||||
*/
|
||||
public MiniMaxEmbeddingClient(MiniMaxApi miniMaxApi, MetadataMode metadataMode,
|
||||
public MiniMaxEmbeddingModel(MiniMaxApi miniMaxApi, MetadataMode metadataMode,
|
||||
MiniMaxEmbeddingOptions miniMaxEmbeddingOptions) {
|
||||
this(miniMaxApi, metadataMode, miniMaxEmbeddingOptions, RetryUtils.DEFAULT_RETRY_TEMPLATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the MiniMaxEmbeddingClient class.
|
||||
* Initializes a new instance of the MiniMaxEmbeddingModel class.
|
||||
* @param miniMaxApi - The MiniMaxApi instance to use for making API requests.
|
||||
* @param metadataMode - The mode for generating metadata.
|
||||
* @param options - The options for MiniMax embedding.
|
||||
* @param retryTemplate - The RetryTemplate for retrying failed API requests.
|
||||
*/
|
||||
public MiniMaxEmbeddingClient(MiniMaxApi miniMaxApi, MetadataMode metadataMode, MiniMaxEmbeddingOptions options,
|
||||
public MiniMaxEmbeddingModel(MiniMaxApi miniMaxApi, MetadataMode metadataMode, MiniMaxEmbeddingOptions options,
|
||||
RetryTemplate retryTemplate) {
|
||||
Assert.notNull(miniMaxApi, "MiniMaxApi must not be null");
|
||||
Assert.notNull(metadataMode, "metadataMode must not be null");
|
||||
@@ -33,7 +33,7 @@ public class ChatCompletionRequestTests {
|
||||
@Test
|
||||
public void createRequestWithChatOptions() {
|
||||
|
||||
var client = new MiniMaxChatCaller(new MiniMaxApi("TEST"),
|
||||
var client = new MiniMaxChatModel(new MiniMaxApi("TEST"),
|
||||
MiniMaxChatOptions.builder().withModel("DEFAULT_MODEL").withTemperature(66.6f).build());
|
||||
|
||||
var request = client.createRequest(new Prompt("Test message content"), false);
|
||||
@@ -59,7 +59,7 @@ public class ChatCompletionRequestTests {
|
||||
|
||||
final String TOOL_FUNCTION_NAME = "CurrentWeather";
|
||||
|
||||
var client = new MiniMaxChatCaller(new MiniMaxApi("TEST"),
|
||||
var client = new MiniMaxChatModel(new MiniMaxApi("TEST"),
|
||||
MiniMaxChatOptions.builder().withModel("DEFAULT_MODEL").build());
|
||||
|
||||
var request = client.createRequest(new Prompt("Test message content",
|
||||
@@ -89,7 +89,7 @@ public class ChatCompletionRequestTests {
|
||||
|
||||
final String TOOL_FUNCTION_NAME = "CurrentWeather";
|
||||
|
||||
var client = new MiniMaxChatCaller(new MiniMaxApi("TEST"),
|
||||
var client = new MiniMaxChatModel(new MiniMaxApi("TEST"),
|
||||
MiniMaxChatOptions.builder()
|
||||
.withModel("DEFAULT_MODEL")
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.ai.minimax;
|
||||
|
||||
import org.springframework.ai.embedding.EmbeddingClient;
|
||||
import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.minimax.api.MiniMaxApi;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -42,13 +42,13 @@ public class MiniMaxTestConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MiniMaxChatCaller miniMaxChatClient(MiniMaxApi api) {
|
||||
return new MiniMaxChatCaller(api);
|
||||
public MiniMaxChatModel miniMaxChatModel(MiniMaxApi api) {
|
||||
return new MiniMaxChatModel(api);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EmbeddingClient miniMaxEmbeddingClient(MiniMaxApi api) {
|
||||
return new MiniMaxEmbeddingClient(api);
|
||||
public EmbeddingModel miniMaxEmbeddingModel(MiniMaxApi api) {
|
||||
return new MiniMaxEmbeddingModel(api);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,9 +22,9 @@ import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.document.MetadataMode;
|
||||
import org.springframework.ai.minimax.MiniMaxChatCaller;
|
||||
import org.springframework.ai.minimax.MiniMaxChatModel;
|
||||
import org.springframework.ai.minimax.MiniMaxChatOptions;
|
||||
import org.springframework.ai.minimax.MiniMaxEmbeddingClient;
|
||||
import org.springframework.ai.minimax.MiniMaxEmbeddingModel;
|
||||
import org.springframework.ai.minimax.MiniMaxEmbeddingOptions;
|
||||
import org.springframework.ai.minimax.api.MiniMaxApi.ChatCompletion;
|
||||
import org.springframework.ai.minimax.api.MiniMaxApi.ChatCompletionChunk;
|
||||
@@ -83,9 +83,9 @@ public class MiniMaxRetryTests {
|
||||
|
||||
private @Mock MiniMaxApi miniMaxApi;
|
||||
|
||||
private MiniMaxChatCaller chatClient;
|
||||
private MiniMaxChatModel chatModel;
|
||||
|
||||
private MiniMaxEmbeddingClient embeddingClient;
|
||||
private MiniMaxEmbeddingModel embeddingModel;
|
||||
|
||||
@BeforeEach
|
||||
public void beforeEach() {
|
||||
@@ -93,8 +93,8 @@ public class MiniMaxRetryTests {
|
||||
retryListener = new TestRetryListener();
|
||||
retryTemplate.registerListener(retryListener);
|
||||
|
||||
chatClient = new MiniMaxChatCaller(miniMaxApi, MiniMaxChatOptions.builder().build(), null, retryTemplate);
|
||||
embeddingClient = new MiniMaxEmbeddingClient(miniMaxApi, MetadataMode.EMBED,
|
||||
chatModel = new MiniMaxChatModel(miniMaxApi, MiniMaxChatOptions.builder().build(), null, retryTemplate);
|
||||
embeddingModel = new MiniMaxEmbeddingModel(miniMaxApi, MetadataMode.EMBED,
|
||||
MiniMaxEmbeddingOptions.builder().build(), retryTemplate);
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ public class MiniMaxRetryTests {
|
||||
.thenThrow(new TransientAiException("Transient Error 2"))
|
||||
.thenReturn(ResponseEntity.of(Optional.of(expectedChatCompletion)));
|
||||
|
||||
var result = chatClient.call(new Prompt("text"));
|
||||
var result = chatModel.call(new Prompt("text"));
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getResult().getOutput().getContent()).isSameAs("Response");
|
||||
@@ -123,7 +123,7 @@ public class MiniMaxRetryTests {
|
||||
public void miniMaxChatNonTransientError() {
|
||||
when(miniMaxApi.chatCompletionEntity(isA(ChatCompletionRequest.class)))
|
||||
.thenThrow(new RuntimeException("Non Transient Error"));
|
||||
assertThrows(RuntimeException.class, () -> chatClient.call(new Prompt("text")));
|
||||
assertThrows(RuntimeException.class, () -> chatModel.call(new Prompt("text")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -139,7 +139,7 @@ public class MiniMaxRetryTests {
|
||||
.thenThrow(new TransientAiException("Transient Error 2"))
|
||||
.thenReturn(Flux.just(expectedChatCompletion));
|
||||
|
||||
var result = chatClient.stream(new Prompt("text"));
|
||||
var result = chatModel.stream(new Prompt("text"));
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.collectList().block().get(0).getResult().getOutput().getContent()).isSameAs("Response");
|
||||
@@ -151,7 +151,7 @@ public class MiniMaxRetryTests {
|
||||
public void miniMaxChatStreamNonTransientError() {
|
||||
when(miniMaxApi.chatCompletionStream(isA(ChatCompletionRequest.class)))
|
||||
.thenThrow(new RuntimeException("Non Transient Error"));
|
||||
assertThrows(RuntimeException.class, () -> chatClient.stream(new Prompt("text")));
|
||||
assertThrows(RuntimeException.class, () -> chatModel.stream(new Prompt("text")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -164,7 +164,7 @@ public class MiniMaxRetryTests {
|
||||
.thenThrow(new TransientAiException("Transient Error 2"))
|
||||
.thenReturn(ResponseEntity.of(Optional.of(expectedEmbeddings)));
|
||||
|
||||
var result = embeddingClient
|
||||
var result = embeddingModel
|
||||
.call(new org.springframework.ai.embedding.EmbeddingRequest(List.of("text1", "text2"), null));
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
@@ -177,7 +177,7 @@ public class MiniMaxRetryTests {
|
||||
public void miniMaxEmbeddingNonTransientError() {
|
||||
when(miniMaxApi.embeddings(isA(EmbeddingRequest.class)))
|
||||
.thenThrow(new RuntimeException("Non Transient Error"));
|
||||
assertThrows(RuntimeException.class, () -> embeddingClient
|
||||
assertThrows(RuntimeException.class, () -> embeddingModel
|
||||
.call(new org.springframework.ai.embedding.EmbeddingRequest(List.of("text1", "text2"), null)));
|
||||
}
|
||||
|
||||
|
||||
@@ -17,10 +17,10 @@ package org.springframework.ai.mistralai;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ai.chat.ChatCaller;
|
||||
import org.springframework.ai.chat.ChatModel;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatCaller;
|
||||
import org.springframework.ai.chat.StreamingChatModel;
|
||||
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
@@ -55,9 +55,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
* @author Grogdunn
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public class MistralAiModelCaller extends
|
||||
public class MistralAiChatModel extends
|
||||
AbstractFunctionCallSupport<MistralAiApi.ChatCompletionMessage, MistralAiApi.ChatCompletionRequest, ResponseEntity<MistralAiApi.ChatCompletion>>
|
||||
implements ChatCaller, StreamingChatCaller {
|
||||
implements ChatModel, StreamingChatModel {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@@ -73,7 +73,7 @@ public class MistralAiModelCaller extends
|
||||
|
||||
private final RetryTemplate retryTemplate;
|
||||
|
||||
public MistralAiModelCaller(MistralAiApi mistralAiApi) {
|
||||
public MistralAiChatModel(MistralAiApi mistralAiApi) {
|
||||
this(mistralAiApi,
|
||||
MistralAiChatOptions.builder()
|
||||
.withTemperature(0.7f)
|
||||
@@ -83,11 +83,11 @@ public class MistralAiModelCaller extends
|
||||
.build());
|
||||
}
|
||||
|
||||
public MistralAiModelCaller(MistralAiApi mistralAiApi, MistralAiChatOptions options) {
|
||||
public MistralAiChatModel(MistralAiApi mistralAiApi, MistralAiChatOptions options) {
|
||||
this(mistralAiApi, options, null, RetryUtils.DEFAULT_RETRY_TEMPLATE);
|
||||
}
|
||||
|
||||
public MistralAiModelCaller(MistralAiApi mistralAiApi, MistralAiChatOptions options,
|
||||
public MistralAiChatModel(MistralAiApi mistralAiApi, MistralAiChatOptions options,
|
||||
FunctionCallbackContext functionCallbackContext, RetryTemplate retryTemplate) {
|
||||
super(functionCallbackContext);
|
||||
Assert.notNull(mistralAiApi, "MistralAiApi must not be null");
|
||||
@@ -101,11 +101,11 @@ public class MistralAiChatOptions implements FunctionCallingOptions, ChatOptions
|
||||
private @JsonProperty("tool_choice") ToolChoice toolChoice;
|
||||
|
||||
/**
|
||||
* MistralAI Tool Function Callbacks to register with the ModelCall. For Prompt
|
||||
* MistralAI Tool Function Callbacks to register with the ChatModel. 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 ModelCall chat completion requests.
|
||||
* to be used by the ChatModel chat completion requests.
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
@JsonIgnore
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.document.MetadataMode;
|
||||
import org.springframework.ai.embedding.AbstractEmbeddingClient;
|
||||
import org.springframework.ai.embedding.AbstractEmbeddingModel;
|
||||
import org.springframework.ai.embedding.Embedding;
|
||||
import org.springframework.ai.embedding.EmbeddingOptions;
|
||||
import org.springframework.ai.embedding.EmbeddingRequest;
|
||||
@@ -38,7 +38,7 @@ import org.springframework.util.Assert;
|
||||
* @author Ricken Bazolo
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public class MistralAiEmbeddingClient extends AbstractEmbeddingClient {
|
||||
public class MistralAiEmbeddingModel extends AbstractEmbeddingModel {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@@ -50,21 +50,21 @@ public class MistralAiEmbeddingClient extends AbstractEmbeddingClient {
|
||||
|
||||
private final RetryTemplate retryTemplate;
|
||||
|
||||
public MistralAiEmbeddingClient(MistralAiApi mistralAiApi) {
|
||||
public MistralAiEmbeddingModel(MistralAiApi mistralAiApi) {
|
||||
this(mistralAiApi, MetadataMode.EMBED);
|
||||
}
|
||||
|
||||
public MistralAiEmbeddingClient(MistralAiApi mistralAiApi, MetadataMode metadataMode) {
|
||||
public MistralAiEmbeddingModel(MistralAiApi mistralAiApi, MetadataMode metadataMode) {
|
||||
this(mistralAiApi, metadataMode,
|
||||
MistralAiEmbeddingOptions.builder().withModel(MistralAiApi.EmbeddingModel.EMBED.getValue()).build(),
|
||||
RetryUtils.DEFAULT_RETRY_TEMPLATE);
|
||||
}
|
||||
|
||||
public MistralAiEmbeddingClient(MistralAiApi mistralAiApi, MistralAiEmbeddingOptions options) {
|
||||
public MistralAiEmbeddingModel(MistralAiApi mistralAiApi, MistralAiEmbeddingOptions options) {
|
||||
this(mistralAiApi, MetadataMode.EMBED, options, RetryUtils.DEFAULT_RETRY_TEMPLATE);
|
||||
}
|
||||
|
||||
public MistralAiEmbeddingClient(MistralAiApi mistralAiApi, MetadataMode metadataMode,
|
||||
public MistralAiEmbeddingModel(MistralAiApi mistralAiApi, MetadataMode metadataMode,
|
||||
MistralAiEmbeddingOptions options, RetryTemplate retryTemplate) {
|
||||
Assert.notNull(mistralAiApi, "MistralAiApi must not be null");
|
||||
Assert.notNull(metadataMode, "metadataMode must not be null");
|
||||
@@ -32,12 +32,12 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@EnabledIfEnvironmentVariable(named = "MISTRAL_AI_API_KEY", matches = ".+")
|
||||
public class MistralAiChatCompletionRequestTest {
|
||||
|
||||
MistralAiModelCaller chatClient = new MistralAiModelCaller(new MistralAiApi("test"));
|
||||
MistralAiChatModel chatModel = new MistralAiChatModel(new MistralAiApi("test"));
|
||||
|
||||
@Test
|
||||
void chatCompletionDefaultRequestTest() {
|
||||
|
||||
var request = chatClient.createRequest(new Prompt("test content"), false);
|
||||
var request = chatModel.createRequest(new Prompt("test content"), false);
|
||||
|
||||
assertThat(request.messages()).hasSize(1);
|
||||
assertThat(request.topP()).isEqualTo(1);
|
||||
@@ -52,7 +52,7 @@ public class MistralAiChatCompletionRequestTest {
|
||||
|
||||
var options = MistralAiChatOptions.builder().withTemperature(0.5f).withTopP(0.8f).build();
|
||||
|
||||
var request = chatClient.createRequest(new Prompt("test content", options), true);
|
||||
var request = chatModel.createRequest(new Prompt("test content", options), true);
|
||||
|
||||
assertThat(request.messages().size()).isEqualTo(1);
|
||||
assertThat(request.topP()).isEqualTo(0.8f);
|
||||
|
||||
@@ -27,10 +27,10 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.ai.chat.ChatCaller;
|
||||
import org.springframework.ai.chat.ChatModel;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatCaller;
|
||||
import org.springframework.ai.chat.StreamingChatModel;
|
||||
import org.springframework.ai.chat.messages.AssistantMessage;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
@@ -56,15 +56,15 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
@SpringBootTest(classes = MistralAiTestConfiguration.class)
|
||||
@EnabledIfEnvironmentVariable(named = "MISTRAL_AI_API_KEY", matches = ".+")
|
||||
class MistralAiModelCallerIT {
|
||||
class MistralAiChatModelIT {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MistralAiModelCallerIT.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(MistralAiChatModelIT.class);
|
||||
|
||||
@Autowired
|
||||
protected ChatCaller modelCaller;
|
||||
protected ChatModel chatModel;
|
||||
|
||||
@Autowired
|
||||
protected StreamingChatCaller streamingChatClient;
|
||||
protected StreamingChatModel streamingChatModel;
|
||||
|
||||
@Value("classpath:/prompts/system-message.st")
|
||||
private Resource systemResource;
|
||||
@@ -90,7 +90,7 @@ class MistralAiModelCallerIT {
|
||||
// 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 = modelCaller.call(prompt);
|
||||
ChatResponse response = chatModel.call(prompt);
|
||||
assertThat(response.getResults()).hasSize(1);
|
||||
assertThat(response.getResults().get(0).getOutput().getContent()).contains("Blackbeard");
|
||||
}
|
||||
@@ -108,7 +108,7 @@ class MistralAiModelCallerIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template,
|
||||
Map.of("subject", "ice cream flavors", "format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = this.modelCaller.call(prompt).getResult();
|
||||
Generation generation = this.chatModel.call(prompt).getResult();
|
||||
|
||||
List<String> list = outputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(list).hasSize(5);
|
||||
@@ -126,7 +126,7 @@ class MistralAiModelCallerIT {
|
||||
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 = modelCaller.call(prompt).getResult();
|
||||
Generation generation = chatModel.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 MistralAiModelCallerIT {
|
||||
""";
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = modelCaller.call(prompt).getResult();
|
||||
Generation generation = chatModel.call(prompt).getResult();
|
||||
|
||||
ActorsFilmsRecord actorsFilms = outputConverter.convert(generation.getOutput().getContent());
|
||||
logger.info("" + actorsFilms);
|
||||
@@ -169,7 +169,7 @@ class MistralAiModelCallerIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
|
||||
String generationTextFromStream = streamingChatClient.stream(prompt)
|
||||
String generationTextFromStream = streamingChatModel.stream(prompt)
|
||||
.collectList()
|
||||
.block()
|
||||
.stream()
|
||||
@@ -201,7 +201,7 @@ class MistralAiModelCallerIT {
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
ChatResponse response = modelCaller.call(new Prompt(messages, promptOptions));
|
||||
ChatResponse response = chatModel.call(new Prompt(messages, promptOptions));
|
||||
|
||||
logger.info("Response: {}", response);
|
||||
|
||||
@@ -224,7 +224,7 @@ class MistralAiModelCallerIT {
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
Flux<ChatResponse> response = streamingChatClient.stream(new Prompt(messages, promptOptions));
|
||||
Flux<ChatResponse> response = streamingChatModel.stream(new Prompt(messages, promptOptions));
|
||||
|
||||
String content = response.collectList()
|
||||
.block()
|
||||
@@ -31,25 +31,25 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class MistralAiEmbeddingIT {
|
||||
|
||||
@Autowired
|
||||
private MistralAiEmbeddingClient mistralAiEmbeddingClient;
|
||||
private MistralAiEmbeddingModel mistralAiEmbeddingModel;
|
||||
|
||||
@Test
|
||||
void defaultEmbedding() {
|
||||
assertThat(mistralAiEmbeddingClient).isNotNull();
|
||||
var embeddingResponse = mistralAiEmbeddingClient.embedForResponse(List.of("Hello World"));
|
||||
assertThat(mistralAiEmbeddingModel).isNotNull();
|
||||
var embeddingResponse = mistralAiEmbeddingModel.embedForResponse(List.of("Hello World"));
|
||||
assertThat(embeddingResponse.getResults()).hasSize(1);
|
||||
assertThat(embeddingResponse.getResults().get(0)).isNotNull();
|
||||
assertThat(embeddingResponse.getResults().get(0).getOutput()).hasSize(1024);
|
||||
assertThat(embeddingResponse.getMetadata()).containsEntry("model", "mistral-embed");
|
||||
assertThat(embeddingResponse.getMetadata()).containsEntry("total-tokens", 4);
|
||||
assertThat(embeddingResponse.getMetadata()).containsEntry("prompt-tokens", 4);
|
||||
assertThat(mistralAiEmbeddingClient.dimensions()).isEqualTo(1024);
|
||||
assertThat(mistralAiEmbeddingModel.dimensions()).isEqualTo(1024);
|
||||
}
|
||||
|
||||
@Test
|
||||
void embeddingTest() {
|
||||
assertThat(mistralAiEmbeddingClient).isNotNull();
|
||||
var embeddingResponse = mistralAiEmbeddingClient.call(new EmbeddingRequest(
|
||||
assertThat(mistralAiEmbeddingModel).isNotNull();
|
||||
var embeddingResponse = mistralAiEmbeddingModel.call(new EmbeddingRequest(
|
||||
List.of("Hello World", "World is big"),
|
||||
MistralAiEmbeddingOptions.builder().withModel("mistral-embed").withEncodingFormat("float").build()));
|
||||
assertThat(embeddingResponse.getResults()).hasSize(2);
|
||||
@@ -58,7 +58,7 @@ class MistralAiEmbeddingIT {
|
||||
assertThat(embeddingResponse.getMetadata()).containsEntry("model", "mistral-embed");
|
||||
assertThat(embeddingResponse.getMetadata()).containsEntry("total-tokens", 9);
|
||||
assertThat(embeddingResponse.getMetadata()).containsEntry("prompt-tokens", 9);
|
||||
assertThat(mistralAiEmbeddingClient.dimensions()).isEqualTo(1024);
|
||||
assertThat(mistralAiEmbeddingModel.dimensions()).isEqualTo(1024);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -82,9 +82,9 @@ public class MistralAiRetryTests {
|
||||
|
||||
private @Mock MistralAiApi mistralAiApi;
|
||||
|
||||
private MistralAiModelCaller chatClient;
|
||||
private MistralAiChatModel chatModel;
|
||||
|
||||
private MistralAiEmbeddingClient embeddingClient;
|
||||
private MistralAiEmbeddingModel embeddingModel;
|
||||
|
||||
@BeforeEach
|
||||
public void beforeEach() {
|
||||
@@ -92,7 +92,7 @@ public class MistralAiRetryTests {
|
||||
retryListener = new TestRetryListener();
|
||||
retryTemplate.registerListener(retryListener);
|
||||
|
||||
chatClient = new MistralAiModelCaller(mistralAiApi,
|
||||
chatModel = new MistralAiChatModel(mistralAiApi,
|
||||
MistralAiChatOptions.builder()
|
||||
.withTemperature(0.7f)
|
||||
.withTopP(1f)
|
||||
@@ -100,7 +100,7 @@ public class MistralAiRetryTests {
|
||||
.withModel(MistralAiApi.ChatModel.TINY.getValue())
|
||||
.build(),
|
||||
null, retryTemplate);
|
||||
embeddingClient = new MistralAiEmbeddingClient(mistralAiApi, MetadataMode.EMBED,
|
||||
embeddingModel = new MistralAiEmbeddingModel(mistralAiApi, MetadataMode.EMBED,
|
||||
MistralAiEmbeddingOptions.builder().withModel(MistralAiApi.EmbeddingModel.EMBED.getValue()).build(),
|
||||
retryTemplate);
|
||||
}
|
||||
@@ -118,7 +118,7 @@ public class MistralAiRetryTests {
|
||||
.thenThrow(new TransientAiException("Transient Error 2"))
|
||||
.thenReturn(ResponseEntity.of(Optional.of(expectedChatCompletion)));
|
||||
|
||||
var result = chatClient.call(new Prompt("text"));
|
||||
var result = chatModel.call(new Prompt("text"));
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getResult().getOutput().getContent()).isSameAs("Response");
|
||||
@@ -130,7 +130,7 @@ public class MistralAiRetryTests {
|
||||
public void mistralAiChatNonTransientError() {
|
||||
when(mistralAiApi.chatCompletionEntity(isA(ChatCompletionRequest.class)))
|
||||
.thenThrow(new RuntimeException("Non Transient Error"));
|
||||
assertThrows(RuntimeException.class, () -> chatClient.call(new Prompt("text")));
|
||||
assertThrows(RuntimeException.class, () -> chatModel.call(new Prompt("text")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -146,7 +146,7 @@ public class MistralAiRetryTests {
|
||||
.thenThrow(new TransientAiException("Transient Error 2"))
|
||||
.thenReturn(Flux.just(expectedChatCompletion));
|
||||
|
||||
var result = chatClient.stream(new Prompt("text"));
|
||||
var result = chatModel.stream(new Prompt("text"));
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.collectList().block().get(0).getResult().getOutput().getContent()).isSameAs("Response");
|
||||
@@ -158,7 +158,7 @@ public class MistralAiRetryTests {
|
||||
public void mistralAiChatStreamNonTransientError() {
|
||||
when(mistralAiApi.chatCompletionStream(isA(ChatCompletionRequest.class)))
|
||||
.thenThrow(new RuntimeException("Non Transient Error"));
|
||||
assertThrows(RuntimeException.class, () -> chatClient.stream(new Prompt("text")));
|
||||
assertThrows(RuntimeException.class, () -> chatModel.stream(new Prompt("text")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -172,7 +172,7 @@ public class MistralAiRetryTests {
|
||||
.thenThrow(new TransientAiException("Transient Error 2"))
|
||||
.thenReturn(ResponseEntity.of(Optional.of(expectedEmbeddings)));
|
||||
|
||||
var result = embeddingClient
|
||||
var result = embeddingModel
|
||||
.call(new org.springframework.ai.embedding.EmbeddingRequest(List.of("text1", "text2"), null));
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
@@ -185,7 +185,7 @@ public class MistralAiRetryTests {
|
||||
public void mistralAiEmbeddingNonTransientError() {
|
||||
when(mistralAiApi.embeddings(isA(EmbeddingRequest.class)))
|
||||
.thenThrow(new RuntimeException("Non Transient Error"));
|
||||
assertThrows(RuntimeException.class, () -> embeddingClient
|
||||
assertThrows(RuntimeException.class, () -> embeddingModel
|
||||
.call(new org.springframework.ai.embedding.EmbeddingRequest(List.of("text1", "text2"), null)));
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.ai.mistralai;
|
||||
|
||||
import org.springframework.ai.embedding.EmbeddingClient;
|
||||
import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.mistralai.api.MistralAiApi;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -35,14 +35,14 @@ public class MistralAiTestConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EmbeddingClient mistralAiEmbeddingClient(MistralAiApi api) {
|
||||
return new MistralAiEmbeddingClient(api,
|
||||
public EmbeddingModel mistralAiEmbeddingModel(MistralAiApi api) {
|
||||
return new MistralAiEmbeddingModel(api,
|
||||
MistralAiEmbeddingOptions.builder().withModel(MistralAiApi.EmbeddingModel.EMBED.getValue()).build());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MistralAiModelCaller mistralAiChatClient(MistralAiApi mistralAiApi) {
|
||||
return new MistralAiModelCaller(mistralAiApi,
|
||||
public MistralAiChatModel mistralAiChatModel(MistralAiApi mistralAiApi) {
|
||||
return new MistralAiChatModel(mistralAiApi,
|
||||
MistralAiChatOptions.builder().withModel(MistralAiApi.ChatModel.MIXTRAL.getValue()).build());
|
||||
}
|
||||
|
||||
|
||||
@@ -18,13 +18,13 @@ package org.springframework.ai.ollama;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.ai.chat.ChatCaller;
|
||||
import org.springframework.ai.chat.ChatModel;
|
||||
import org.springframework.ai.ollama.metadata.OllamaChatResponseMetadata;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatCaller;
|
||||
import org.springframework.ai.chat.StreamingChatModel;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.MessageType;
|
||||
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
|
||||
@@ -39,7 +39,7 @@ import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link ChatCaller} implementation for {@literal Ollama}.
|
||||
* {@link ChatModel} 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 OllamaModelCaller implements ChatCaller, StreamingChatCaller {
|
||||
public class OllamaChatModel implements ChatModel, StreamingChatModel {
|
||||
|
||||
/**
|
||||
* Low-level Ollama API library.
|
||||
@@ -64,11 +64,11 @@ public class OllamaModelCaller implements ChatCaller, StreamingChatCaller {
|
||||
*/
|
||||
private OllamaOptions defaultOptions;
|
||||
|
||||
public OllamaModelCaller(OllamaApi chatApi) {
|
||||
public OllamaChatModel(OllamaApi chatApi) {
|
||||
this(chatApi, OllamaOptions.create().withModel(OllamaOptions.DEFAULT_MODEL));
|
||||
}
|
||||
|
||||
public OllamaModelCaller(OllamaApi chatApi, OllamaOptions defaultOptions) {
|
||||
public OllamaChatModel(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 OllamaModelCaller implements ChatCaller, StreamingChatCaller {
|
||||
* @deprecated Use {@link OllamaOptions#setModel} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public OllamaModelCaller withModel(String model) {
|
||||
public OllamaChatModel withModel(String model) {
|
||||
this.defaultOptions.setModel(model);
|
||||
return this;
|
||||
}
|
||||
@@ -88,7 +88,7 @@ public class OllamaModelCaller implements ChatCaller, StreamingChatCaller {
|
||||
* @deprecated Use {@link OllamaOptions} constructor instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public OllamaModelCaller withDefaultOptions(OllamaOptions options) {
|
||||
public OllamaChatModel withDefaultOptions(OllamaOptions options) {
|
||||
this.defaultOptions = options;
|
||||
return this;
|
||||
}
|
||||
@@ -23,9 +23,9 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.embedding.AbstractEmbeddingClient;
|
||||
import org.springframework.ai.embedding.AbstractEmbeddingModel;
|
||||
import org.springframework.ai.embedding.Embedding;
|
||||
import org.springframework.ai.embedding.EmbeddingClient;
|
||||
import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.embedding.EmbeddingOptions;
|
||||
import org.springframework.ai.embedding.EmbeddingResponse;
|
||||
import org.springframework.ai.model.ModelOptionsUtils;
|
||||
@@ -36,7 +36,7 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link EmbeddingClient} implementation for {@literal Ollama}.
|
||||
* {@link EmbeddingModel} 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
|
||||
@@ -51,7 +51,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Christian Tzolov
|
||||
* @since 0.8.0
|
||||
*/
|
||||
public class OllamaEmbeddingClient extends AbstractEmbeddingClient {
|
||||
public class OllamaEmbeddingModel extends AbstractEmbeddingModel {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@@ -62,11 +62,11 @@ public class OllamaEmbeddingClient extends AbstractEmbeddingClient {
|
||||
*/
|
||||
private OllamaOptions defaultOptions = OllamaOptions.create().withModel(OllamaOptions.DEFAULT_MODEL);
|
||||
|
||||
public OllamaEmbeddingClient(OllamaApi ollamaApi) {
|
||||
public OllamaEmbeddingModel(OllamaApi ollamaApi) {
|
||||
this.ollamaApi = ollamaApi;
|
||||
}
|
||||
|
||||
public OllamaEmbeddingClient(OllamaApi ollamaApi, OllamaOptions defaultOptions) {
|
||||
public OllamaEmbeddingModel(OllamaApi ollamaApi, OllamaOptions defaultOptions) {
|
||||
this.ollamaApi = ollamaApi;
|
||||
this.defaultOptions = defaultOptions;
|
||||
}
|
||||
@@ -75,7 +75,7 @@ public class OllamaEmbeddingClient extends AbstractEmbeddingClient {
|
||||
* @deprecated Use {@link OllamaOptions#setModel} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public OllamaEmbeddingClient withModel(String model) {
|
||||
public OllamaEmbeddingModel withModel(String model) {
|
||||
this.defaultOptions.setModel(model);
|
||||
return this;
|
||||
}
|
||||
@@ -84,7 +84,7 @@ public class OllamaEmbeddingClient extends AbstractEmbeddingClient {
|
||||
* @deprecated Use {@link OllamaOptions} constructor instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public OllamaEmbeddingClient withDefaultOptions(OllamaOptions options) {
|
||||
public OllamaEmbeddingModel withDefaultOptions(OllamaOptions options) {
|
||||
this.defaultOptions = options;
|
||||
return this;
|
||||
}
|
||||
@@ -56,11 +56,11 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@SpringBootTest
|
||||
@Testcontainers
|
||||
@Disabled("For manual smoke testing only.")
|
||||
class OllamaModelCallerIT {
|
||||
class OllamaChatModelIT {
|
||||
|
||||
private static String MODEL = "mistral";
|
||||
|
||||
private static final Log logger = LogFactory.getLog(OllamaModelCallerIT.class);
|
||||
private static final Log logger = LogFactory.getLog(OllamaChatModelIT.class);
|
||||
|
||||
@Container
|
||||
static OllamaContainer ollamaContainer = new OllamaContainer("ollama/ollama:0.1.32");
|
||||
@@ -77,7 +77,7 @@ class OllamaModelCallerIT {
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private OllamaModelCaller client;
|
||||
private OllamaChatModel chatModel;
|
||||
|
||||
@Test
|
||||
void roleTest() {
|
||||
@@ -95,13 +95,13 @@ class OllamaModelCallerIT {
|
||||
|
||||
Prompt prompt = new Prompt(List.of(userMessage, systemMessage), portableOptions);
|
||||
|
||||
ChatResponse response = client.call(prompt);
|
||||
ChatResponse response = chatModel.call(prompt);
|
||||
assertThat(response.getResult().getOutput().getContent()).contains("Blackbeard");
|
||||
|
||||
// ollama specific options
|
||||
var ollamaOptions = new OllamaOptions().withLowVRAM(true);
|
||||
|
||||
response = client.call(new Prompt(List.of(userMessage, systemMessage), ollamaOptions));
|
||||
response = chatModel.call(new Prompt(List.of(userMessage, systemMessage), ollamaOptions));
|
||||
assertThat(response.getResult().getOutput().getContent()).contains("Blackbeard");
|
||||
|
||||
}
|
||||
@@ -109,7 +109,7 @@ class OllamaModelCallerIT {
|
||||
@Test
|
||||
void usageTest() {
|
||||
Prompt prompt = new Prompt("Tell me a joke");
|
||||
ChatResponse response = client.call(prompt);
|
||||
ChatResponse response = chatModel.call(prompt);
|
||||
Usage usage = response.getMetadata().getUsage();
|
||||
|
||||
assertThat(usage).isNotNull();
|
||||
@@ -131,7 +131,7 @@ class OllamaModelCallerIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template,
|
||||
Map.of("subject", "ice cream flavors.", "format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = this.client.call(prompt).getResult();
|
||||
Generation generation = this.chatModel.call(prompt).getResult();
|
||||
|
||||
List<String> list = outputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(list).hasSize(5);
|
||||
@@ -151,7 +151,7 @@ class OllamaModelCallerIT {
|
||||
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 = client.call(prompt).getResult();
|
||||
Generation generation = chatModel.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));
|
||||
@@ -173,7 +173,7 @@ class OllamaModelCallerIT {
|
||||
""";
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = client.call(prompt).getResult();
|
||||
Generation generation = chatModel.call(prompt).getResult();
|
||||
|
||||
ActorsFilmsRecord actorsFilms = outputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(actorsFilms.actor()).isEqualTo("Tom Hanks");
|
||||
@@ -194,7 +194,7 @@ class OllamaModelCallerIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
|
||||
String generationTextFromStream = client.stream(prompt)
|
||||
String generationTextFromStream = chatModel.stream(prompt)
|
||||
.collectList()
|
||||
.block()
|
||||
.stream()
|
||||
@@ -219,8 +219,8 @@ class OllamaModelCallerIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OllamaModelCaller ollamaChat(OllamaApi ollamaApi) {
|
||||
return new OllamaModelCaller(ollamaApi, OllamaOptions.create().withModel(MODEL).withTemperature(0.9f));
|
||||
public OllamaChatModel ollamaChat(OllamaApi ollamaApi) {
|
||||
return new OllamaChatModel(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 OllamaModelCallerMultimodalIT {
|
||||
class OllamaChatModelMultimodalIT {
|
||||
|
||||
private static String MODEL = "llava";
|
||||
|
||||
private static final Log logger = LogFactory.getLog(OllamaModelCallerIT.class);
|
||||
private static final Log logger = LogFactory.getLog(OllamaChatModelIT.class);
|
||||
|
||||
@Container
|
||||
static OllamaContainer ollamaContainer = new OllamaContainer("ollama/ollama:0.1.32");
|
||||
@@ -65,7 +65,7 @@ class OllamaModelCallerMultimodalIT {
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private OllamaModelCaller client;
|
||||
private OllamaChatModel chatModel;
|
||||
|
||||
@Test
|
||||
void multiModalityTest() throws IOException {
|
||||
@@ -75,7 +75,7 @@ class OllamaModelCallerMultimodalIT {
|
||||
var userMessage = new UserMessage("Explain what do you see on this picture?",
|
||||
List.of(new Media(MimeTypeUtils.IMAGE_PNG, imageData)));
|
||||
|
||||
var response = client.call(new Prompt(List.of(userMessage)));
|
||||
var response = chatModel.call(new Prompt(List.of(userMessage)));
|
||||
|
||||
logger.info(response.getResult().getOutput().getContent());
|
||||
assertThat(response.getResult().getOutput().getContent()).contains("bananas", "apple", "basket");
|
||||
@@ -90,8 +90,8 @@ class OllamaModelCallerMultimodalIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OllamaModelCaller ollamaChat(OllamaApi ollamaApi) {
|
||||
return new OllamaModelCaller(ollamaApi, OllamaOptions.create().withModel(MODEL).withTemperature(0.9f));
|
||||
public OllamaChatModel ollamaChat(OllamaApi ollamaApi) {
|
||||
return new OllamaChatModel(ollamaApi, OllamaOptions.create().withModel(MODEL).withTemperature(0.9f));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,13 +30,13 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
public class OllamaChatRequestTests {
|
||||
|
||||
OllamaModelCaller client = new OllamaModelCaller(new OllamaApi(),
|
||||
OllamaChatModel chatModel = new OllamaChatModel(new OllamaApi(),
|
||||
new OllamaOptions().withModel("MODEL_NAME").withTopK(99).withTemperature(66.6f).withNumGPU(1));
|
||||
|
||||
@Test
|
||||
public void createRequestWithDefaultOptions() {
|
||||
|
||||
var request = client.ollamaChatRequest(new Prompt("Test message content"), false);
|
||||
var request = chatModel.ollamaChatRequest(new Prompt("Test message content"), false);
|
||||
|
||||
assertThat(request.messages()).hasSize(1);
|
||||
assertThat(request.stream()).isFalse();
|
||||
@@ -54,7 +54,7 @@ public class OllamaChatRequestTests {
|
||||
// Runtime options should override the default options.
|
||||
OllamaOptions promptOptions = new OllamaOptions().withTemperature(0.8f).withTopP(0.5f).withNumGPU(2);
|
||||
|
||||
var request = client.ollamaChatRequest(new Prompt("Test message content", promptOptions), true);
|
||||
var request = chatModel.ollamaChatRequest(new Prompt("Test message content", promptOptions), true);
|
||||
|
||||
assertThat(request.messages()).hasSize(1);
|
||||
assertThat(request.stream()).isTrue();
|
||||
@@ -79,7 +79,7 @@ public class OllamaChatRequestTests {
|
||||
.withTopP(0.6f)
|
||||
.build();
|
||||
|
||||
var request = client.ollamaChatRequest(new Prompt("Test message content", portablePromptOptions), true);
|
||||
var request = chatModel.ollamaChatRequest(new Prompt("Test message content", portablePromptOptions), true);
|
||||
|
||||
assertThat(request.messages()).hasSize(1);
|
||||
assertThat(request.stream()).isTrue();
|
||||
@@ -97,7 +97,7 @@ public class OllamaChatRequestTests {
|
||||
// Ollama runtime options.
|
||||
OllamaOptions promptOptions = new OllamaOptions().withModel("PROMPT_MODEL");
|
||||
|
||||
var request = client.ollamaChatRequest(new Prompt("Test message content", promptOptions), true);
|
||||
var request = chatModel.ollamaChatRequest(new Prompt("Test message content", promptOptions), true);
|
||||
|
||||
assertThat(request.model()).isEqualTo("PROMPT_MODEL");
|
||||
}
|
||||
@@ -105,17 +105,17 @@ public class OllamaChatRequestTests {
|
||||
@Test
|
||||
public void createRequestWithDefaultOptionsModelOverride() {
|
||||
|
||||
OllamaModelCaller client2 = new OllamaModelCaller(new OllamaApi(),
|
||||
OllamaChatModel chatModel = new OllamaChatModel(new OllamaApi(),
|
||||
new OllamaOptions().withModel("DEFAULT_OPTIONS_MODEL"));
|
||||
|
||||
var request = client2.ollamaChatRequest(new Prompt("Test message content"), true);
|
||||
var request = chatModel.ollamaChatRequest(new Prompt("Test message content"), true);
|
||||
|
||||
assertThat(request.model()).isEqualTo("DEFAULT_OPTIONS_MODEL");
|
||||
|
||||
// Prompt options should override the default options.
|
||||
OllamaOptions promptOptions = new OllamaOptions().withModel("PROMPT_MODEL");
|
||||
|
||||
request = client2.ollamaChatRequest(new Prompt("Test message content", promptOptions), true);
|
||||
request = chatModel.ollamaChatRequest(new Prompt("Test message content", promptOptions), true);
|
||||
|
||||
assertThat(request.model()).isEqualTo("PROMPT_MODEL");
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import org.testcontainers.ollama.OllamaContainer;
|
||||
|
||||
import org.springframework.ai.embedding.EmbeddingResponse;
|
||||
import org.springframework.ai.ollama.api.OllamaApi;
|
||||
@@ -34,14 +34,13 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.testcontainers.ollama.OllamaContainer;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest
|
||||
@Disabled("For manual smoke testing only.")
|
||||
@Testcontainers
|
||||
class OllamaEmbeddingClientIT {
|
||||
class OllamaEmbeddingModelIT {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(OllamaApiIT.class);
|
||||
|
||||
@@ -60,15 +59,15 @@ class OllamaEmbeddingClientIT {
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private OllamaEmbeddingClient embeddingClient;
|
||||
private OllamaEmbeddingModel embeddingModel;
|
||||
|
||||
@Test
|
||||
void singleEmbedding() {
|
||||
assertThat(embeddingClient).isNotNull();
|
||||
EmbeddingResponse embeddingResponse = embeddingClient.embedForResponse(List.of("Hello World"));
|
||||
assertThat(embeddingModel).isNotNull();
|
||||
EmbeddingResponse embeddingResponse = embeddingModel.embedForResponse(List.of("Hello World"));
|
||||
assertThat(embeddingResponse.getResults()).hasSize(1);
|
||||
assertThat(embeddingResponse.getResults().get(0).getOutput()).isNotEmpty();
|
||||
assertThat(embeddingClient.dimensions()).isEqualTo(3200);
|
||||
assertThat(embeddingModel.dimensions()).isEqualTo(3200);
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
@@ -80,8 +79,8 @@ class OllamaEmbeddingClientIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OllamaEmbeddingClient ollamaEmbedding(OllamaApi ollamaApi) {
|
||||
return new OllamaEmbeddingClient(ollamaApi).withModel("orca-mini");
|
||||
public OllamaEmbeddingModel ollamaEmbedding(OllamaApi ollamaApi) {
|
||||
return new OllamaEmbeddingModel(ollamaApi).withModel("orca-mini");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,13 +28,13 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
public class OllamaEmbeddingRequestTests {
|
||||
|
||||
OllamaEmbeddingClient client = new OllamaEmbeddingClient(new OllamaApi()).withDefaultOptions(
|
||||
OllamaEmbeddingModel chatModel = new OllamaEmbeddingModel(new OllamaApi()).withDefaultOptions(
|
||||
new OllamaOptions().withModel("DEFAULT_MODEL").withMainGPU(11).withUseMMap(true).withNumGPU(1));
|
||||
|
||||
@Test
|
||||
public void ollamaEmbeddingRequestDefaultOptions() {
|
||||
|
||||
var request = client.ollamaEmbeddingRequest("Hello", null);
|
||||
var request = chatModel.ollamaEmbeddingRequest("Hello", null);
|
||||
|
||||
assertThat(request.model()).isEqualTo("DEFAULT_MODEL");
|
||||
assertThat(request.options().get("num_gpu")).isEqualTo(1);
|
||||
@@ -51,7 +51,7 @@ public class OllamaEmbeddingRequestTests {
|
||||
.withUseMMap(true)
|
||||
.withNumGPU(2);
|
||||
|
||||
var request = client.ollamaEmbeddingRequest("Hello", promptOptions);
|
||||
var request = chatModel.ollamaEmbeddingRequest("Hello", promptOptions);
|
||||
|
||||
assertThat(request.model()).isEqualTo("PROMPT_MODEL");
|
||||
assertThat(request.options().get("num_gpu")).isEqualTo(2);
|
||||
|
||||
@@ -24,10 +24,10 @@ import org.springframework.ai.openai.api.OpenAiAudioApi;
|
||||
import org.springframework.ai.openai.api.OpenAiAudioApi.SpeechRequest.AudioResponseFormat;
|
||||
import org.springframework.ai.openai.api.common.OpenAiApiException;
|
||||
import org.springframework.ai.openai.audio.speech.Speech;
|
||||
import org.springframework.ai.openai.audio.speech.SpeechClient;
|
||||
import org.springframework.ai.openai.audio.speech.SpeechModel;
|
||||
import org.springframework.ai.openai.audio.speech.SpeechPrompt;
|
||||
import org.springframework.ai.openai.audio.speech.SpeechResponse;
|
||||
import org.springframework.ai.openai.audio.speech.StreamingSpeechClient;
|
||||
import org.springframework.ai.openai.audio.speech.StreamingSpeechModel;
|
||||
import org.springframework.ai.openai.metadata.audio.OpenAiAudioSpeechResponseMetadata;
|
||||
import org.springframework.ai.openai.metadata.support.OpenAiResponseHeaderExtractor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -44,7 +44,7 @@ import java.time.Duration;
|
||||
* @see OpenAiAudioApi
|
||||
* @since 1.0.0-M1
|
||||
*/
|
||||
public class OpenAiAudioSpeechClient implements SpeechClient, StreamingSpeechClient {
|
||||
public class OpenAiAudioSpeechModel implements SpeechModel, StreamingSpeechModel {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@@ -61,12 +61,12 @@ public class OpenAiAudioSpeechClient implements SpeechClient, StreamingSpeechCli
|
||||
private final OpenAiAudioApi audioApi;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the OpenAiAudioSpeechClient class with the provided
|
||||
* Initializes a new instance of the OpenAiAudioSpeechModel class with the provided
|
||||
* OpenAiAudioApi. It uses the model tts-1, response format mp3, voice alloy, and the
|
||||
* default speed of 1.0.
|
||||
* @param audioApi The OpenAiAudioApi to use for speech synthesis.
|
||||
*/
|
||||
public OpenAiAudioSpeechClient(OpenAiAudioApi audioApi) {
|
||||
public OpenAiAudioSpeechModel(OpenAiAudioApi audioApi) {
|
||||
this(audioApi,
|
||||
OpenAiAudioSpeechOptions.builder()
|
||||
.withModel(OpenAiAudioApi.TtsModel.TTS_1.getValue())
|
||||
@@ -77,13 +77,13 @@ public class OpenAiAudioSpeechClient implements SpeechClient, StreamingSpeechCli
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the OpenAiAudioSpeechClient class with the provided
|
||||
* Initializes a new instance of the OpenAiAudioSpeechModel class with the provided
|
||||
* OpenAiAudioApi and options.
|
||||
* @param audioApi The OpenAiAudioApi to use for speech synthesis.
|
||||
* @param options The OpenAiAudioSpeechOptions containing the speech synthesis
|
||||
* options.
|
||||
*/
|
||||
public OpenAiAudioSpeechClient(OpenAiAudioApi audioApi, OpenAiAudioSpeechOptions options) {
|
||||
public OpenAiAudioSpeechModel(OpenAiAudioApi audioApi, OpenAiAudioSpeechOptions options) {
|
||||
Assert.notNull(audioApi, "OpenAiAudioApi must not be null");
|
||||
Assert.notNull(options, "OpenAiSpeechOptions must not be null");
|
||||
this.audioApi = audioApi;
|
||||
@@ -35,7 +35,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.chat.metadata.RateLimit;
|
||||
import org.springframework.ai.model.ModelClient;
|
||||
import org.springframework.ai.model.Model;
|
||||
import org.springframework.ai.openai.api.OpenAiAudioApi;
|
||||
import org.springframework.ai.openai.api.OpenAiAudioApi.StructuredResponse;
|
||||
import org.springframework.ai.openai.audio.transcription.AudioTranscription;
|
||||
@@ -59,8 +59,7 @@ import org.springframework.util.Assert;
|
||||
* @see OpenAiAudioApi
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public class OpenAiAudioTranscriptionClient
|
||||
implements ModelClient<AudioTranscriptionPrompt, AudioTranscriptionResponse> {
|
||||
public class OpenAiAudioTranscriptionModel implements Model<AudioTranscriptionPrompt, AudioTranscriptionResponse> {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@@ -71,11 +70,11 @@ public class OpenAiAudioTranscriptionClient
|
||||
private final OpenAiAudioApi audioApi;
|
||||
|
||||
/**
|
||||
* OpenAiAudioTranscriptionClient is a client class used to interact with the OpenAI
|
||||
* OpenAiAudioTranscriptionModel is a client class used to interact with the OpenAI
|
||||
* Audio Transcription API.
|
||||
* @param audioApi The OpenAiAudioApi instance to be used for making API calls.
|
||||
*/
|
||||
public OpenAiAudioTranscriptionClient(OpenAiAudioApi audioApi) {
|
||||
public OpenAiAudioTranscriptionModel(OpenAiAudioApi audioApi) {
|
||||
this(audioApi,
|
||||
OpenAiAudioTranscriptionOptions.builder()
|
||||
.withModel(OpenAiAudioApi.WhisperModel.WHISPER_1.getValue())
|
||||
@@ -86,25 +85,25 @@ public class OpenAiAudioTranscriptionClient
|
||||
}
|
||||
|
||||
/**
|
||||
* OpenAiAudioTranscriptionClient is a client class used to interact with the OpenAI
|
||||
* OpenAiAudioTranscriptionModel is a client class used to interact with the OpenAI
|
||||
* Audio Transcription API.
|
||||
* @param audioApi The OpenAiAudioApi instance to be used for making API calls.
|
||||
* @param options The OpenAiAudioTranscriptionOptions instance for configuring the
|
||||
* audio transcription.
|
||||
*/
|
||||
public OpenAiAudioTranscriptionClient(OpenAiAudioApi audioApi, OpenAiAudioTranscriptionOptions options) {
|
||||
public OpenAiAudioTranscriptionModel(OpenAiAudioApi audioApi, OpenAiAudioTranscriptionOptions options) {
|
||||
this(audioApi, options, RetryUtils.DEFAULT_RETRY_TEMPLATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* OpenAiAudioTranscriptionClient is a client class used to interact with the OpenAI
|
||||
* OpenAiAudioTranscriptionModel is a client class used to interact with the OpenAI
|
||||
* Audio Transcription API.
|
||||
* @param audioApi The OpenAiAudioApi instance to be used for making API calls.
|
||||
* @param options The OpenAiAudioTranscriptionOptions instance for configuring the
|
||||
* audio transcription.
|
||||
* @param retryTemplate The RetryTemplate instance for retrying failed API calls.
|
||||
*/
|
||||
public OpenAiAudioTranscriptionClient(OpenAiAudioApi audioApi, OpenAiAudioTranscriptionOptions options,
|
||||
public OpenAiAudioTranscriptionModel(OpenAiAudioApi audioApi, OpenAiAudioTranscriptionOptions options,
|
||||
RetryTemplate retryTemplate) {
|
||||
Assert.notNull(audioApi, "OpenAiAudioApi must not be null");
|
||||
Assert.notNull(options, "OpenAiTranscriptionOptions must not be null");
|
||||
@@ -17,10 +17,10 @@ package org.springframework.ai.openai;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ai.chat.ChatCaller;
|
||||
import org.springframework.ai.chat.ChatModel;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.StreamingChatCaller;
|
||||
import org.springframework.ai.chat.StreamingChatModel;
|
||||
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
|
||||
import org.springframework.ai.chat.metadata.RateLimit;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
@@ -58,7 +58,7 @@ import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* {@link ChatCaller} and {@link StreamingChatCaller} implementation for {@literal OpenAI}
|
||||
* {@link ChatModel} and {@link StreamingChatModel} 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 ChatCaller
|
||||
* @see StreamingChatCaller
|
||||
* @see ChatModel
|
||||
* @see StreamingChatModel
|
||||
* @see OpenAiApi
|
||||
*/
|
||||
public class OpenAiModelCaller extends
|
||||
public class OpenAiChatModel extends
|
||||
AbstractFunctionCallSupport<ChatCompletionMessage, OpenAiApi.ChatCompletionRequest, ResponseEntity<ChatCompletion>>
|
||||
implements ChatCaller, StreamingChatCaller {
|
||||
implements ChatModel, StreamingChatModel {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(OpenAiModelCaller.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(OpenAiChatModel.class);
|
||||
|
||||
/**
|
||||
* The default options used for the chat completion requests.
|
||||
@@ -94,35 +94,35 @@ public class OpenAiModelCaller extends
|
||||
private final OpenAiApi openAiApi;
|
||||
|
||||
/**
|
||||
* Creates an instance of the OpenAiModelCall.
|
||||
* Creates an instance of the OpenAiChatModel.
|
||||
* @param openAiApi The OpenAiApi instance to be used for interacting with the OpenAI
|
||||
* Chat API.
|
||||
* @throws IllegalArgumentException if openAiApi is null
|
||||
*/
|
||||
public OpenAiModelCaller(OpenAiApi openAiApi) {
|
||||
public OpenAiChatModel(OpenAiApi openAiApi) {
|
||||
this(openAiApi,
|
||||
OpenAiChatOptions.builder().withModel(OpenAiApi.DEFAULT_CHAT_MODEL).withTemperature(0.7f).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes an instance of the OpenAiModelCall.
|
||||
* Initializes an instance of the OpenAiChatModel.
|
||||
* @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 options The OpenAiChatOptions to configure the chat model.
|
||||
*/
|
||||
public OpenAiModelCaller(OpenAiApi openAiApi, OpenAiChatOptions options) {
|
||||
public OpenAiChatModel(OpenAiApi openAiApi, OpenAiChatOptions options) {
|
||||
this(openAiApi, options, null, RetryUtils.DEFAULT_RETRY_TEMPLATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the OpenAiModelCall.
|
||||
* Initializes a new instance of the OpenAiChatModel.
|
||||
* @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 options The OpenAiChatOptions to configure the chat model.
|
||||
* @param functionCallbackContext The function callback context.
|
||||
* @param retryTemplate The retry template.
|
||||
*/
|
||||
public OpenAiModelCaller(OpenAiApi openAiApi, OpenAiChatOptions options,
|
||||
public OpenAiChatModel(OpenAiApi openAiApi, OpenAiChatOptions options,
|
||||
FunctionCallbackContext functionCallbackContext, RetryTemplate retryTemplate) {
|
||||
super(functionCallbackContext);
|
||||
Assert.notNull(openAiApi, "OpenAiApi must not be null");
|
||||
@@ -134,10 +134,10 @@ public class OpenAiChatOptions implements FunctionCallingOptions, ChatOptions {
|
||||
private @JsonProperty("user") String user;
|
||||
|
||||
/**
|
||||
* OpenAI Tool Function Callbacks to register with the ModelCall.
|
||||
* OpenAI Tool Function Callbacks to register with the ChatModel.
|
||||
* 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 ModelCall chat completion requests.
|
||||
* from the registry to be used by the ChatModel chat completion requests.
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
@JsonIgnore
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.document.MetadataMode;
|
||||
import org.springframework.ai.embedding.AbstractEmbeddingClient;
|
||||
import org.springframework.ai.embedding.AbstractEmbeddingModel;
|
||||
import org.springframework.ai.embedding.Embedding;
|
||||
import org.springframework.ai.embedding.EmbeddingOptions;
|
||||
import org.springframework.ai.embedding.EmbeddingRequest;
|
||||
@@ -41,9 +41,9 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
public class OpenAiEmbeddingClient extends AbstractEmbeddingClient {
|
||||
public class OpenAiEmbeddingModel extends AbstractEmbeddingModel {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(OpenAiEmbeddingClient.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(OpenAiEmbeddingModel.class);
|
||||
|
||||
private final OpenAiEmbeddingOptions defaultOptions;
|
||||
|
||||
@@ -54,43 +54,43 @@ public class OpenAiEmbeddingClient extends AbstractEmbeddingClient {
|
||||
private final MetadataMode metadataMode;
|
||||
|
||||
/**
|
||||
* Constructor for the OpenAiEmbeddingClient class.
|
||||
* Constructor for the OpenAiEmbeddingModel class.
|
||||
* @param openAiApi The OpenAiApi instance to use for making API requests.
|
||||
*/
|
||||
public OpenAiEmbeddingClient(OpenAiApi openAiApi) {
|
||||
public OpenAiEmbeddingModel(OpenAiApi openAiApi) {
|
||||
this(openAiApi, MetadataMode.EMBED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the OpenAiEmbeddingClient class.
|
||||
* Initializes a new instance of the OpenAiEmbeddingModel class.
|
||||
* @param openAiApi The OpenAiApi instance to use for making API requests.
|
||||
* @param metadataMode The mode for generating metadata.
|
||||
*/
|
||||
public OpenAiEmbeddingClient(OpenAiApi openAiApi, MetadataMode metadataMode) {
|
||||
public OpenAiEmbeddingModel(OpenAiApi openAiApi, MetadataMode metadataMode) {
|
||||
this(openAiApi, metadataMode,
|
||||
OpenAiEmbeddingOptions.builder().withModel(OpenAiApi.DEFAULT_EMBEDDING_MODEL).build(),
|
||||
RetryUtils.DEFAULT_RETRY_TEMPLATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the OpenAiEmbeddingClient class.
|
||||
* Initializes a new instance of the OpenAiEmbeddingModel class.
|
||||
* @param openAiApi The OpenAiApi instance to use for making API requests.
|
||||
* @param metadataMode The mode for generating metadata.
|
||||
* @param openAiEmbeddingOptions The options for OpenAi embedding.
|
||||
*/
|
||||
public OpenAiEmbeddingClient(OpenAiApi openAiApi, MetadataMode metadataMode,
|
||||
public OpenAiEmbeddingModel(OpenAiApi openAiApi, MetadataMode metadataMode,
|
||||
OpenAiEmbeddingOptions openAiEmbeddingOptions) {
|
||||
this(openAiApi, metadataMode, openAiEmbeddingOptions, RetryUtils.DEFAULT_RETRY_TEMPLATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the OpenAiEmbeddingClient class.
|
||||
* Initializes a new instance of the OpenAiEmbeddingModel class.
|
||||
* @param openAiApi - The OpenAiApi instance to use for making API requests.
|
||||
* @param metadataMode - The mode for generating metadata.
|
||||
* @param options - The options for OpenAI embedding.
|
||||
* @param retryTemplate - The RetryTemplate for retrying failed API requests.
|
||||
*/
|
||||
public OpenAiEmbeddingClient(OpenAiApi openAiApi, MetadataMode metadataMode, OpenAiEmbeddingOptions options,
|
||||
public OpenAiEmbeddingModel(OpenAiApi openAiApi, MetadataMode metadataMode, OpenAiEmbeddingOptions options,
|
||||
RetryTemplate retryTemplate) {
|
||||
Assert.notNull(openAiApi, "OpenAiService must not be null");
|
||||
Assert.notNull(metadataMode, "metadataMode must not be null");
|
||||
@@ -21,7 +21,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.image.Image;
|
||||
import org.springframework.ai.image.ImageClient;
|
||||
import org.springframework.ai.image.ImageModel;
|
||||
import org.springframework.ai.image.ImageGeneration;
|
||||
import org.springframework.ai.image.ImageOptions;
|
||||
import org.springframework.ai.image.ImagePrompt;
|
||||
@@ -37,16 +37,16 @@ import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* OpenAiImageClient is a class that implements the ImageClient interface. It provides a
|
||||
* OpenAiImageModel is a class that implements the ImageModel interface. It provides a
|
||||
* client for calling the OpenAI image generation API.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author Christian Tzolov
|
||||
* @since 0.8.0
|
||||
*/
|
||||
public class OpenAiImageClient implements ImageClient {
|
||||
public class OpenAiImageModel implements ImageModel {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(OpenAiImageClient.class);
|
||||
private final static Logger logger = LoggerFactory.getLogger(OpenAiImageModel.class);
|
||||
|
||||
private OpenAiImageOptions defaultOptions;
|
||||
|
||||
@@ -54,11 +54,11 @@ public class OpenAiImageClient implements ImageClient {
|
||||
|
||||
public final RetryTemplate retryTemplate;
|
||||
|
||||
public OpenAiImageClient(OpenAiImageApi openAiImageApi) {
|
||||
public OpenAiImageModel(OpenAiImageApi openAiImageApi) {
|
||||
this(openAiImageApi, OpenAiImageOptions.builder().build(), RetryUtils.DEFAULT_RETRY_TEMPLATE);
|
||||
}
|
||||
|
||||
public OpenAiImageClient(OpenAiImageApi openAiImageApi, OpenAiImageOptions defaultOptions,
|
||||
public OpenAiImageModel(OpenAiImageApi openAiImageApi, OpenAiImageOptions defaultOptions,
|
||||
RetryTemplate retryTemplate) {
|
||||
Assert.notNull(openAiImageApi, "OpenAiImageApi must not be null");
|
||||
Assert.notNull(defaultOptions, "defaultOptions must not be null");
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
package org.springframework.ai.openai.audio.speech;
|
||||
|
||||
import org.springframework.ai.model.ModelClient;
|
||||
import org.springframework.ai.model.Model;
|
||||
|
||||
/**
|
||||
* The {@link SpeechClient} interface provides a way to interact with the OpenAI
|
||||
* The {@link SpeechModel} interface provides a way to interact with the OpenAI
|
||||
* Text-to-Speech (TTS) API. It allows you to convert text input into lifelike spoken
|
||||
* audio.
|
||||
*
|
||||
@@ -27,7 +27,7 @@ import org.springframework.ai.model.ModelClient;
|
||||
* @since 1.0.0-M1
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SpeechClient extends ModelClient<SpeechPrompt, SpeechResponse> {
|
||||
public interface SpeechModel extends Model<SpeechPrompt, SpeechResponse> {
|
||||
|
||||
/**
|
||||
* Generates spoken audio from the provided text message.
|
||||
@@ -16,11 +16,11 @@
|
||||
|
||||
package org.springframework.ai.openai.audio.speech;
|
||||
|
||||
import org.springframework.ai.model.StreamingModelClient;
|
||||
import org.springframework.ai.model.StreamingModel;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* The {@link StreamingSpeechClient} interface provides a way to interact with the OpenAI
|
||||
* The {@link StreamingSpeechModel} interface provides a way to interact with the OpenAI
|
||||
* Text-to-Speech (TTS) API using a streaming approach, allowing you to receive the
|
||||
* generated audio in a real-time fashion.
|
||||
*
|
||||
@@ -28,7 +28,7 @@ import reactor.core.publisher.Flux;
|
||||
* @since 1.0.0-M1
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface StreamingSpeechClient extends StreamingModelClient<SpeechPrompt, SpeechResponse> {
|
||||
public interface StreamingSpeechModel extends StreamingModel<SpeechPrompt, SpeechResponse> {
|
||||
|
||||
/**
|
||||
* Generates a stream of audio bytes from the provided text message.
|
||||
@@ -34,7 +34,7 @@ public class ChatCompletionRequestTests {
|
||||
@Test
|
||||
public void createRequestWithChatOptions() {
|
||||
|
||||
var client = new OpenAiModelCaller(new OpenAiApi("TEST"),
|
||||
var client = new OpenAiChatModel(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 OpenAiModelCaller(new OpenAiApi("TEST"),
|
||||
var client = new OpenAiChatModel(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 OpenAiModelCaller(new OpenAiApi("TEST"),
|
||||
var client = new OpenAiChatModel(new OpenAiApi("TEST"),
|
||||
OpenAiChatOptions.builder()
|
||||
.withModel("DEFAULT_MODEL")
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.ai.openai;
|
||||
|
||||
import org.springframework.ai.embedding.EmbeddingClient;
|
||||
import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.openai.api.OpenAiApi;
|
||||
import org.springframework.ai.openai.api.OpenAiAudioApi;
|
||||
import org.springframework.ai.openai.api.OpenAiImageApi;
|
||||
@@ -51,33 +51,33 @@ public class OpenAiTestConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiModelCaller openAiChatClient(OpenAiApi api) {
|
||||
OpenAiModelCaller openAiChatClient = new OpenAiModelCaller(api);
|
||||
return openAiChatClient;
|
||||
public OpenAiChatModel openAiChatModel(OpenAiApi api) {
|
||||
OpenAiChatModel openAiChatModel = new OpenAiChatModel(api);
|
||||
return openAiChatModel;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiAudioTranscriptionClient openAiTranscriptionClient(OpenAiAudioApi api) {
|
||||
OpenAiAudioTranscriptionClient openAiTranscriptionClient = new OpenAiAudioTranscriptionClient(api);
|
||||
return openAiTranscriptionClient;
|
||||
public OpenAiAudioTranscriptionModel openAiTranscriptionModel(OpenAiAudioApi api) {
|
||||
OpenAiAudioTranscriptionModel openAiTranscriptionModel = new OpenAiAudioTranscriptionModel(api);
|
||||
return openAiTranscriptionModel;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiAudioSpeechClient openAiAudioSpeechClient(OpenAiAudioApi api) {
|
||||
OpenAiAudioSpeechClient openAiAudioSpeechClient = new OpenAiAudioSpeechClient(api);
|
||||
return openAiAudioSpeechClient;
|
||||
public OpenAiAudioSpeechModel openAiAudioSpeechModel(OpenAiAudioApi api) {
|
||||
OpenAiAudioSpeechModel openAiAudioSpeechModel = new OpenAiAudioSpeechModel(api);
|
||||
return openAiAudioSpeechModel;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiImageClient openAiImageClient(OpenAiImageApi imageApi) {
|
||||
OpenAiImageClient openAiImageClient = new OpenAiImageClient(imageApi);
|
||||
// openAiImageClient.setModel("foobar");
|
||||
return openAiImageClient;
|
||||
public OpenAiImageModel openAiImageModel(OpenAiImageApi imageApi) {
|
||||
OpenAiImageModel openAiImageModel = new OpenAiImageModel(imageApi);
|
||||
// openAiImageModel.setModel("foobar");
|
||||
return openAiImageModel;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EmbeddingClient openAiEmbeddingClient(OpenAiApi api) {
|
||||
return new OpenAiEmbeddingClient(api);
|
||||
public EmbeddingModel openAiEmbeddingModel(OpenAiApi api) {
|
||||
return new OpenAiEmbeddingModel(api);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public class TranscriptionRequestTests {
|
||||
@Test
|
||||
public void defaultOptions() {
|
||||
|
||||
var client = new OpenAiAudioTranscriptionClient(new OpenAiAudioApi("TEST"),
|
||||
var client = new OpenAiAudioTranscriptionModel(new OpenAiAudioApi("TEST"),
|
||||
OpenAiAudioTranscriptionOptions.builder()
|
||||
.withModel("DEFAULT_MODEL")
|
||||
.withResponseFormat(TranscriptResponseFormat.TEXT)
|
||||
@@ -58,7 +58,7 @@ public class TranscriptionRequestTests {
|
||||
@Test
|
||||
public void runtimeOptions() {
|
||||
|
||||
var client = new OpenAiAudioTranscriptionClient(new OpenAiAudioApi("TEST"),
|
||||
var client = new OpenAiAudioTranscriptionModel(new OpenAiAudioApi("TEST"),
|
||||
OpenAiAudioTranscriptionOptions.builder()
|
||||
.withModel("DEFAULT_MODEL")
|
||||
.withResponseFormat(TranscriptResponseFormat.TEXT)
|
||||
|
||||
@@ -26,9 +26,9 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.openai.OpenAiModelCaller;
|
||||
import org.springframework.ai.openai.OpenAiChatModel;
|
||||
import org.springframework.ai.openai.OpenAiTestConfiguration;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingClient;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingModel;
|
||||
import org.springframework.ai.openai.testutils.AbstractIT;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
|
||||
@@ -58,16 +58,16 @@ public class AcmeIT extends AbstractIT {
|
||||
private Resource systemBikePrompt;
|
||||
|
||||
@Autowired
|
||||
private OpenAiEmbeddingClient embeddingClient;
|
||||
private OpenAiEmbeddingModel embeddingModel;
|
||||
|
||||
@Autowired
|
||||
private OpenAiModelCaller chatClient;
|
||||
private OpenAiChatModel chatModel;
|
||||
|
||||
@Test
|
||||
void beanTest() {
|
||||
assertThat(bikesResource).isNotNull();
|
||||
assertThat(embeddingClient).isNotNull();
|
||||
assertThat(chatClient).isNotNull();
|
||||
assertThat(embeddingModel).isNotNull();
|
||||
assertThat(chatModel).isNotNull();
|
||||
}
|
||||
|
||||
// @Test
|
||||
@@ -81,7 +81,7 @@ public class AcmeIT extends AbstractIT {
|
||||
// Step 2 - Create embeddings and save to vector store
|
||||
|
||||
logger.info("Creating Embeddings...");
|
||||
VectorStore vectorStore = new SimpleVectorStore(embeddingClient);
|
||||
VectorStore vectorStore = new SimpleVectorStore(embeddingModel);
|
||||
|
||||
vectorStore.accept(textSplitter.apply(jsonReader.get()));
|
||||
|
||||
@@ -108,7 +108,7 @@ public class AcmeIT extends AbstractIT {
|
||||
logger.info("Asking AI generative to reply to question.");
|
||||
Prompt prompt = new Prompt(List.of(systemMessage, userMessage));
|
||||
logger.info("AI responded.");
|
||||
ChatResponse response = chatClient.call(prompt);
|
||||
ChatResponse response = chatModel.call(prompt);
|
||||
|
||||
evaluateQuestionAndAnswer(userQuery, response, true);
|
||||
}
|
||||
|
||||
@@ -32,13 +32,13 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest(classes = OpenAiTestConfiguration.class)
|
||||
@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+")
|
||||
class OpenAiSpeechClientIT extends AbstractIT {
|
||||
class OpenAiSpeechModelIT extends AbstractIT {
|
||||
|
||||
private static final Float SPEED = 1.0f;
|
||||
|
||||
@Test
|
||||
void shouldSuccessfullyStreamAudioBytesForEmptyMessage() {
|
||||
Flux<byte[]> response = speechClient.stream("Today is a wonderful day to build something people love!");
|
||||
Flux<byte[]> response = speechModel.stream("Today is a wonderful day to build something people love!");
|
||||
assertThat(response).isNotNull();
|
||||
assertThat(response.collectList().block()).isNotNull();
|
||||
System.out.println(response.collectList().block());
|
||||
@@ -46,7 +46,7 @@ class OpenAiSpeechClientIT extends AbstractIT {
|
||||
|
||||
@Test
|
||||
void shouldProduceAudioBytesDirectlyFromMessage() {
|
||||
byte[] audioBytes = speechClient.call("Today is a wonderful day to build something people love!");
|
||||
byte[] audioBytes = speechModel.call("Today is a wonderful day to build something people love!");
|
||||
assertThat(audioBytes).hasSizeGreaterThan(0);
|
||||
|
||||
}
|
||||
@@ -61,7 +61,7 @@ class OpenAiSpeechClientIT extends AbstractIT {
|
||||
.build();
|
||||
SpeechPrompt speechPrompt = new SpeechPrompt("Today is a wonderful day to build something people love!",
|
||||
speechOptions);
|
||||
SpeechResponse response = speechClient.call(speechPrompt);
|
||||
SpeechResponse response = speechModel.call(speechPrompt);
|
||||
byte[] audioBytes = response.getResult().getOutput();
|
||||
assertThat(response.getResults()).hasSize(1);
|
||||
assertThat(response.getResults().get(0).getOutput()).isNotEmpty();
|
||||
@@ -79,7 +79,7 @@ class OpenAiSpeechClientIT extends AbstractIT {
|
||||
.build();
|
||||
SpeechPrompt speechPrompt = new SpeechPrompt("Today is a wonderful day to build something people love!",
|
||||
speechOptions);
|
||||
SpeechResponse response = speechClient.call(speechPrompt);
|
||||
SpeechResponse response = speechModel.call(speechPrompt);
|
||||
OpenAiAudioSpeechResponseMetadata metadata = response.getMetadata();
|
||||
assertThat(metadata).isNotNull();
|
||||
assertThat(metadata.getRateLimit()).isNotNull();
|
||||
@@ -100,7 +100,7 @@ class OpenAiSpeechClientIT extends AbstractIT {
|
||||
|
||||
SpeechPrompt speechPrompt = new SpeechPrompt("Today is a wonderful day to build something people love!",
|
||||
speechOptions);
|
||||
Flux<SpeechResponse> responseFlux = speechClient.stream(speechPrompt);
|
||||
Flux<SpeechResponse> responseFlux = speechModel.stream(speechPrompt);
|
||||
assertThat(responseFlux).isNotNull();
|
||||
List<SpeechResponse> responses = responseFlux.collectList().block();
|
||||
assertThat(responses).isNotNull();
|
||||
@@ -18,7 +18,7 @@ package org.springframework.ai.openai.audio.speech;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ai.openai.OpenAiAudioSpeechClient;
|
||||
import org.springframework.ai.openai.OpenAiAudioSpeechModel;
|
||||
import org.springframework.ai.openai.OpenAiAudioSpeechOptions;
|
||||
import org.springframework.ai.openai.api.OpenAiAudioApi;
|
||||
import org.springframework.ai.openai.metadata.audio.OpenAiAudioSpeechResponseMetadata;
|
||||
@@ -45,15 +45,15 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat
|
||||
/**
|
||||
* @author Ahmed Yousri
|
||||
*/
|
||||
@RestClientTest(OpenAiSpeechClientWithSpeechResponseMetadataTests.Config.class)
|
||||
public class OpenAiSpeechClientWithSpeechResponseMetadataTests {
|
||||
@RestClientTest(OpenAiSpeechModelWithSpeechResponseMetadataTests.Config.class)
|
||||
public class OpenAiSpeechModelWithSpeechResponseMetadataTests {
|
||||
|
||||
private static String TEST_API_KEY = "sk-1234567890";
|
||||
|
||||
private static final Float SPEED = 1.0f;
|
||||
|
||||
@Autowired
|
||||
private OpenAiAudioSpeechClient openAiSpeechClient;
|
||||
private OpenAiAudioSpeechModel openAiSpeechClient;
|
||||
|
||||
@Autowired
|
||||
private MockRestServiceServer server;
|
||||
@@ -121,8 +121,8 @@ public class OpenAiSpeechClientWithSpeechResponseMetadataTests {
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public OpenAiAudioSpeechClient openAiAudioSpeechClient(OpenAiAudioApi openAiAudioApi) {
|
||||
return new OpenAiAudioSpeechClient(openAiAudioApi);
|
||||
public OpenAiAudioSpeechModel openAiAudioSpeechClient(OpenAiAudioApi openAiAudioApi) {
|
||||
return new OpenAiAudioSpeechModel(openAiAudioApi);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -31,7 +31,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest(classes = OpenAiTestConfiguration.class)
|
||||
@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+")
|
||||
class OpenAiTranscriptionClientIT extends AbstractIT {
|
||||
class OpenAiTranscriptionModelIT extends AbstractIT {
|
||||
|
||||
@Value("classpath:/speech/jfk.flac")
|
||||
private Resource audioFile;
|
||||
@@ -43,7 +43,7 @@ class OpenAiTranscriptionClientIT extends AbstractIT {
|
||||
.withTemperature(0f)
|
||||
.build();
|
||||
AudioTranscriptionPrompt transcriptionRequest = new AudioTranscriptionPrompt(audioFile, transcriptionOptions);
|
||||
AudioTranscriptionResponse response = transcriptionClient.call(transcriptionRequest);
|
||||
AudioTranscriptionResponse response = transcriptionModel.call(transcriptionRequest);
|
||||
assertThat(response.getResults()).hasSize(1);
|
||||
assertThat(response.getResults().get(0).getOutput().toLowerCase().contains("fellow")).isTrue();
|
||||
}
|
||||
@@ -59,7 +59,7 @@ class OpenAiTranscriptionClientIT extends AbstractIT {
|
||||
.withResponseFormat(responseFormat)
|
||||
.build();
|
||||
AudioTranscriptionPrompt transcriptionRequest = new AudioTranscriptionPrompt(audioFile, transcriptionOptions);
|
||||
AudioTranscriptionResponse response = transcriptionClient.call(transcriptionRequest);
|
||||
AudioTranscriptionResponse response = transcriptionModel.call(transcriptionRequest);
|
||||
assertThat(response.getResults()).hasSize(1);
|
||||
assertThat(response.getResults().get(0).getOutput().toLowerCase().contains("fellow")).isTrue();
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.ai.chat.metadata.RateLimit;
|
||||
import org.springframework.ai.openai.OpenAiAudioTranscriptionClient;
|
||||
import org.springframework.ai.openai.OpenAiAudioTranscriptionModel;
|
||||
import org.springframework.ai.openai.api.OpenAiAudioApi;
|
||||
import org.springframework.ai.openai.metadata.audio.OpenAiAudioTranscriptionMetadata;
|
||||
import org.springframework.ai.openai.metadata.audio.OpenAiAudioTranscriptionResponseMetadata;
|
||||
@@ -48,13 +48,13 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat
|
||||
/**
|
||||
* @author Michael Lavelle
|
||||
*/
|
||||
@RestClientTest(OpenAiTranscriptionClientWithTranscriptionResponseMetadataTests.Config.class)
|
||||
public class OpenAiTranscriptionClientWithTranscriptionResponseMetadataTests {
|
||||
@RestClientTest(OpenAiTranscriptionModelWithTranscriptionResponseMetadataTests.Config.class)
|
||||
public class OpenAiTranscriptionModelWithTranscriptionResponseMetadataTests {
|
||||
|
||||
private static String TEST_API_KEY = "sk-1234567890";
|
||||
|
||||
@Autowired
|
||||
private OpenAiAudioTranscriptionClient openAiTranscriptionClient;
|
||||
private OpenAiAudioTranscriptionModel openAiTranscriptionClient;
|
||||
|
||||
@Autowired
|
||||
private MockRestServiceServer server;
|
||||
@@ -156,8 +156,8 @@ public class OpenAiTranscriptionClientWithTranscriptionResponseMetadataTests {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiAudioTranscriptionClient openAiClient(OpenAiAudioApi openAiAudioApi) {
|
||||
return new OpenAiAudioTranscriptionClient(openAiAudioApi);
|
||||
public OpenAiAudioTranscriptionModel openAiClient(OpenAiAudioApi openAiAudioApi) {
|
||||
return new OpenAiAudioTranscriptionModel(openAiAudioApi);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,7 +18,7 @@ package org.springframework.ai.openai.audio.transcription;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.ai.openai.OpenAiAudioTranscriptionClient;
|
||||
import org.springframework.ai.openai.OpenAiAudioTranscriptionModel;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -33,18 +33,18 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Unit Tests for {@link TranscriptionClient}.
|
||||
* Unit Tests for {@link TranscriptionModel}.
|
||||
*
|
||||
* @author Michael Lavelle
|
||||
*/
|
||||
class TranscriptionClientTests {
|
||||
class TranscriptionModelTests {
|
||||
|
||||
@Test
|
||||
void transcrbeRequestReturnsResponseCorrectly() {
|
||||
|
||||
Resource mockAudioFile = Mockito.mock(Resource.class);
|
||||
|
||||
OpenAiAudioTranscriptionClient mockClient = Mockito.mock(OpenAiAudioTranscriptionClient.class);
|
||||
OpenAiAudioTranscriptionModel mockClient = Mockito.mock(OpenAiAudioTranscriptionModel.class);
|
||||
|
||||
String mockTranscription = "All your bases are belong to us";
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.slf4j.LoggerFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ChatModel;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.converter.BeanOutputConverter;
|
||||
import org.springframework.ai.openai.OpenAiChatOptions;
|
||||
@@ -52,7 +53,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+")
|
||||
class OpenAiChatClientIT extends AbstractIT {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(OpenAiModelCallerIT.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(OpenAiChatClientIT.class);
|
||||
|
||||
@Value("classpath:/prompts/system-message.st")
|
||||
private Resource systemTextResource;
|
||||
@@ -61,7 +62,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
void roleTest() {
|
||||
|
||||
// @formatter:off
|
||||
ChatResponse response = ChatClient.builder(modelCaller).build().prompt()
|
||||
ChatResponse response = ChatClient.builder(chatModel).build().prompt()
|
||||
.system(s -> s.text(systemTextResource)
|
||||
.param("name", "Bob")
|
||||
.param("voice", "pirate"))
|
||||
@@ -78,7 +79,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
@Test
|
||||
void listOutputConverter() {
|
||||
// @formatter:off
|
||||
Collection<String> collection = ChatClient.builder(modelCaller).build().prompt()
|
||||
Collection<String> collection = ChatClient.builder(chatModel).build().prompt()
|
||||
.user(u -> u.text("List five {subject}")
|
||||
.param("subject", "ice cream flavors"))
|
||||
.call()
|
||||
@@ -92,7 +93,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
void listOutputConverter2() {
|
||||
|
||||
// @formatter:off
|
||||
List<ActorsFilmsRecord> actorsFilms = ChatClient.builder(modelCaller).build().prompt()
|
||||
List<ActorsFilmsRecord> actorsFilms = ChatClient.builder(chatModel).build().prompt()
|
||||
.user("Generate the filmography of 5 movies for Tom Hanks and Bill Murray.")
|
||||
.call()
|
||||
.single(new ParameterizedTypeReference<List<ActorsFilmsRecord>>() {
|
||||
@@ -108,7 +109,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
void listOutputConverter3() {
|
||||
|
||||
// @formatter:off
|
||||
Collection<ActorsFilmsRecord> actorsFilms = ChatClient.builder(modelCaller).build().prompt()
|
||||
Collection<ActorsFilmsRecord> actorsFilms = ChatClient.builder(chatModel).build().prompt()
|
||||
.user("Generate the filmography of 5 movies for Tom Hanks and Bill Murray.")
|
||||
.call()
|
||||
.list(ActorsFilmsRecord.class);
|
||||
@@ -122,7 +123,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
@Test
|
||||
void mapOutputConverter() {
|
||||
// @formatter:off
|
||||
Map<String, Object> result = ChatClient.builder(modelCaller).build().prompt()
|
||||
Map<String, Object> result = ChatClient.builder(chatModel).build().prompt()
|
||||
.user(u -> u.text("Provide me a List of {subject}")
|
||||
.param("subject", "an array of numbers from 1 to 9 under they key name 'numbers'"))
|
||||
.call()
|
||||
@@ -137,7 +138,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
void beanOutputConverter() {
|
||||
|
||||
// @formatter:off
|
||||
ActorsFilms actorsFilms = ChatClient.builder(modelCaller).build().prompt()
|
||||
ActorsFilms actorsFilms = ChatClient.builder(chatModel).build().prompt()
|
||||
.user("Generate the filmography for a random actor.")
|
||||
.call()
|
||||
.single(ActorsFilms.class);
|
||||
@@ -154,7 +155,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
void beanOutputConverterRecords() {
|
||||
|
||||
// @formatter:off
|
||||
ActorsFilmsRecord actorsFilms = ChatClient.builder(modelCaller).build().prompt()
|
||||
ActorsFilmsRecord actorsFilms = ChatClient.builder(chatModel).build().prompt()
|
||||
.user("Generate the filmography of 5 movies for Tom Hanks.")
|
||||
.call()
|
||||
.single(ActorsFilmsRecord.class);
|
||||
@@ -171,7 +172,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
BeanOutputConverter<ActorsFilmsRecord> outputConverter = new BeanOutputConverter<>(ActorsFilmsRecord.class);
|
||||
|
||||
// @formatter:off
|
||||
Flux<String> chatResponse = ChatClient.builder(modelCaller)
|
||||
Flux<String> chatResponse = ChatClient.builder(chatModel)
|
||||
.build()
|
||||
.prompt()
|
||||
.user(u -> u
|
||||
@@ -198,7 +199,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
void functionCallTest() {
|
||||
|
||||
// @formatter:off
|
||||
String response = ChatClient.builder(modelCaller).build().prompt()
|
||||
String response = ChatClient.builder(chatModel).build().prompt()
|
||||
.user(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris?"))
|
||||
.function("getCurrentWeather", "Get the weather in location", new MockWeatherService())
|
||||
.call()
|
||||
@@ -216,7 +217,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
void streamFunctionCallTest() {
|
||||
|
||||
// @formatter:off
|
||||
Flux<String> response = ChatClient.builder(modelCaller).build().prompt()
|
||||
Flux<String> response = ChatClient.builder(chatModel).build().prompt()
|
||||
.user("What's the weather like in San Francisco, Tokyo, and Paris?")
|
||||
.function("getCurrentWeather", "Get the weather in location", new MockWeatherService())
|
||||
.stream()
|
||||
@@ -236,7 +237,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
void multiModalityEmbeddedImage(String modelName) throws IOException {
|
||||
|
||||
// @formatter:off
|
||||
String response = ChatClient.builder(modelCaller).build().prompt()
|
||||
String response = ChatClient.builder(chatModel).build().prompt()
|
||||
// TODO consider adding model(...) method to ChatClient as a shortcut to
|
||||
// OpenAiChatOptions.builder().withModel(modelName).build()
|
||||
.options(OpenAiChatOptions.builder().withModel(modelName).build())
|
||||
@@ -259,7 +260,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
URL url = new URL("https://docs.spring.io/spring-ai/reference/1.0-SNAPSHOT/_images/multimodal.test.png");
|
||||
|
||||
// @formatter:off
|
||||
String response = ChatClient.builder(modelCaller).build().prompt()
|
||||
String response = ChatClient.builder(chatModel).build().prompt()
|
||||
// TODO consider adding model(...) method to ChatClient as a shortcut to
|
||||
// OpenAiChatOptions.builder().withModel(modelName).build()
|
||||
.options(OpenAiChatOptions.builder().withModel(modelName).build())
|
||||
@@ -280,7 +281,7 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
URL url = new URL("https://docs.spring.io/spring-ai/reference/1.0-SNAPSHOT/_images/multimodal.test.png");
|
||||
|
||||
// @formatter:off
|
||||
Flux<String> response = ChatClient.builder(modelCaller).build().prompt()
|
||||
Flux<String> response = ChatClient.builder(chatModel).build().prompt()
|
||||
.options(OpenAiChatOptions.builder().withModel(OpenAiApi.ChatModel.GPT_4_VISION_PREVIEW.getValue())
|
||||
.build())
|
||||
.user(u -> u.text("Explain what do you see on this picture?")
|
||||
|
||||
@@ -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.OpenAiModelCaller;
|
||||
import org.springframework.ai.openai.OpenAiChatModel;
|
||||
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 = OpenAiModelCaller2IT.Config.class)
|
||||
@SpringBootTest(classes = OpenAiChatModel2IT.Config.class)
|
||||
@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+")
|
||||
public class OpenAiModelCaller2IT {
|
||||
public class OpenAiChatModel2IT {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Autowired
|
||||
private OpenAiModelCaller openAiChatClient;
|
||||
private OpenAiChatModel openAiChatModel;
|
||||
|
||||
@Test
|
||||
void responseFormatTest() throws JsonMappingException, JsonProcessingException {
|
||||
@@ -67,7 +67,7 @@ public class OpenAiModelCaller2IT {
|
||||
.withResponseFormat(new ChatCompletionRequest.ResponseFormat("json_object"))
|
||||
.build());
|
||||
|
||||
ChatResponse response = this.openAiChatClient.call(prompt);
|
||||
ChatResponse response = this.openAiChatModel.call(prompt);
|
||||
|
||||
assertThat(response).isNotNull();
|
||||
|
||||
@@ -99,8 +99,8 @@ public class OpenAiModelCaller2IT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiModelCaller openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiModelCaller(openAiApi);
|
||||
public OpenAiChatModel openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiChatModel(openAiApi);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -31,7 +31,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
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.messages.AssistantMessage;
|
||||
@@ -61,9 +60,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest(classes = OpenAiTestConfiguration.class)
|
||||
@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+")
|
||||
class OpenAiModelCallerIT extends AbstractIT {
|
||||
class OpenAiChatModelIT extends AbstractIT {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(OpenAiModelCallerIT.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(OpenAiChatModelIT.class);
|
||||
|
||||
@Value("classpath:/prompts/system-message.st")
|
||||
private Resource systemResource;
|
||||
@@ -75,7 +74,7 @@ class OpenAiModelCallerIT 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 = modelCaller.call(prompt);
|
||||
ChatResponse response = chatModel.call(prompt);
|
||||
assertThat(response.getResults()).hasSize(1);
|
||||
assertThat(response.getResults().get(0).getOutput().getContent()).contains("Blackbeard");
|
||||
// needs fine tuning... evaluateQuestionAndAnswer(request, response, false);
|
||||
@@ -94,7 +93,7 @@ class OpenAiModelCallerIT extends AbstractIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template,
|
||||
Map.of("subject", "ice cream flavors", "format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = this.modelCaller.call(prompt).getResult();
|
||||
Generation generation = this.chatModel.call(prompt).getResult();
|
||||
|
||||
List<String> list = outputConverter.convert(generation.getOutput().getContent());
|
||||
assertThat(list).hasSize(5);
|
||||
@@ -113,7 +112,7 @@ class OpenAiModelCallerIT 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 = modelCaller.call(prompt).getResult();
|
||||
Generation generation = chatModel.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));
|
||||
@@ -132,7 +131,7 @@ class OpenAiModelCallerIT extends AbstractIT {
|
||||
""";
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = modelCaller.call(prompt).getResult();
|
||||
Generation generation = chatModel.call(prompt).getResult();
|
||||
|
||||
ActorsFilms actorsFilms = outputConverter.convert(generation.getOutput().getContent());
|
||||
}
|
||||
@@ -152,7 +151,7 @@ class OpenAiModelCallerIT extends AbstractIT {
|
||||
""";
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = modelCaller.call(prompt).getResult();
|
||||
Generation generation = chatModel.call(prompt).getResult();
|
||||
|
||||
ActorsFilmsRecord actorsFilms = outputConverter.convert(generation.getOutput().getContent());
|
||||
logger.info("" + actorsFilms);
|
||||
@@ -173,7 +172,7 @@ class OpenAiModelCallerIT extends AbstractIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
|
||||
String generationTextFromStream = streamingChatClient.stream(prompt)
|
||||
String generationTextFromStream = streamingChatModel.stream(prompt)
|
||||
.collectList()
|
||||
.block()
|
||||
.stream()
|
||||
@@ -205,7 +204,7 @@ class OpenAiModelCallerIT extends AbstractIT {
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
ChatResponse response = modelCaller.call(new Prompt(messages, promptOptions));
|
||||
ChatResponse response = chatModel.call(new Prompt(messages, promptOptions));
|
||||
|
||||
logger.info("Response: {}", response);
|
||||
|
||||
@@ -230,7 +229,7 @@ class OpenAiModelCallerIT extends AbstractIT {
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
Flux<ChatResponse> response = streamingChatClient.stream(new Prompt(messages, promptOptions));
|
||||
Flux<ChatResponse> response = streamingChatModel.stream(new Prompt(messages, promptOptions));
|
||||
|
||||
String content = response.collectList()
|
||||
.block()
|
||||
@@ -256,7 +255,7 @@ class OpenAiModelCallerIT extends AbstractIT {
|
||||
var userMessage = new UserMessage("Explain what do you see on this picture?",
|
||||
List.of(new Media(MimeTypeUtils.IMAGE_PNG, imageData)));
|
||||
|
||||
var response = modelCaller
|
||||
var response = chatModel
|
||||
.call(new Prompt(List.of(userMessage), OpenAiChatOptions.builder().withModel(modelName).build()));
|
||||
|
||||
logger.info(response.getResult().getOutput().getContent());
|
||||
@@ -272,7 +271,7 @@ class OpenAiModelCallerIT 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 = modelCaller
|
||||
ChatResponse response = chatModel
|
||||
.call(new Prompt(List.of(userMessage), OpenAiChatOptions.builder().withModel(modelName).build()));
|
||||
|
||||
logger.info(response.getResult().getOutput().getContent());
|
||||
@@ -287,7 +286,7 @@ class OpenAiModelCallerIT extends AbstractIT {
|
||||
.of(new Media(MimeTypeUtils.IMAGE_PNG,
|
||||
new URL("https://docs.spring.io/spring-ai/reference/1.0-SNAPSHOT/_images/multimodal.test.png"))));
|
||||
|
||||
Flux<ChatResponse> response = streamingChatClient.stream(new Prompt(List.of(userMessage),
|
||||
Flux<ChatResponse> response = streamingChatModel.stream(new Prompt(List.of(userMessage),
|
||||
OpenAiChatOptions.builder().withModel(OpenAiApi.ChatModel.GPT_4_VISION_PREVIEW.getValue()).build()));
|
||||
|
||||
String content = response.collectList()
|
||||
@@ -39,10 +39,10 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest(classes = OpenAiTestConfiguration.class)
|
||||
@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+")
|
||||
class OpenAiModelCallerTypeReferenceBeanOutputConverterIT extends AbstractIT {
|
||||
class OpenAiChatModelTypeReferenceBeanOutputConverterIT extends AbstractIT {
|
||||
|
||||
private static final Logger logger = LoggerFactory
|
||||
.getLogger(OpenAiModelCallerTypeReferenceBeanOutputConverterIT.class);
|
||||
.getLogger(OpenAiChatModelTypeReferenceBeanOutputConverterIT.class);
|
||||
|
||||
record ActorsFilmsRecord(String actor, List<String> movies) {
|
||||
}
|
||||
@@ -61,7 +61,7 @@ class OpenAiModelCallerTypeReferenceBeanOutputConverterIT extends AbstractIT {
|
||||
""";
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
Generation generation = modelCaller.call(prompt).getResult();
|
||||
Generation generation = chatModel.call(prompt).getResult();
|
||||
|
||||
List<ActorsFilmsRecord> actorsFilms = outputConverter.convert(generation.getOutput().getContent());
|
||||
logger.info("" + actorsFilms);
|
||||
@@ -87,7 +87,7 @@ class OpenAiModelCallerTypeReferenceBeanOutputConverterIT extends AbstractIT {
|
||||
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
|
||||
Prompt prompt = new Prompt(promptTemplate.createMessage());
|
||||
|
||||
String generationTextFromStream = streamingChatClient.stream(prompt)
|
||||
String generationTextFromStream = streamingChatModel.stream(prompt)
|
||||
.collectList()
|
||||
.block()
|
||||
.stream()
|
||||
@@ -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.OpenAiModelCaller;
|
||||
import org.springframework.ai.openai.OpenAiChatModel;
|
||||
import org.springframework.ai.openai.metadata.support.OpenAiApiResponseHeaders;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -51,13 +51,13 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat
|
||||
* @author Christian Tzolov
|
||||
* @since 0.7.0
|
||||
*/
|
||||
@RestClientTest(OpenAiChatClientWithChatResponseMetadataTests.Config.class)
|
||||
public class OpenAiChatClientWithChatResponseMetadataTests {
|
||||
@RestClientTest(OpenAiChatModelWithChatResponseMetadataTests.Config.class)
|
||||
public class OpenAiChatModelWithChatResponseMetadataTests {
|
||||
|
||||
private static String TEST_API_KEY = "sk-1234567890";
|
||||
|
||||
@Autowired
|
||||
private OpenAiModelCaller openAiChatClient;
|
||||
private OpenAiChatModel openAiChatClient;
|
||||
|
||||
@Autowired
|
||||
private MockRestServiceServer server;
|
||||
@@ -171,8 +171,8 @@ public class OpenAiChatClientWithChatResponseMetadataTests {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiModelCaller openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiModelCaller(openAiApi);
|
||||
public OpenAiChatModel openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiChatModel(openAiApi);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,13 +29,13 @@ import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.document.MetadataMode;
|
||||
import org.springframework.ai.image.ImageMessage;
|
||||
import org.springframework.ai.image.ImagePrompt;
|
||||
import org.springframework.ai.openai.OpenAiAudioTranscriptionClient;
|
||||
import org.springframework.ai.openai.OpenAiAudioTranscriptionModel;
|
||||
import org.springframework.ai.openai.OpenAiAudioTranscriptionOptions;
|
||||
import org.springframework.ai.openai.OpenAiModelCaller;
|
||||
import org.springframework.ai.openai.OpenAiChatModel;
|
||||
import org.springframework.ai.openai.OpenAiChatOptions;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingClient;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingModel;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingOptions;
|
||||
import org.springframework.ai.openai.OpenAiImageClient;
|
||||
import org.springframework.ai.openai.OpenAiImageModel;
|
||||
import org.springframework.ai.openai.OpenAiImageOptions;
|
||||
import org.springframework.ai.openai.api.OpenAiApi;
|
||||
import org.springframework.ai.openai.api.OpenAiApi.ChatCompletion;
|
||||
@@ -107,13 +107,13 @@ public class OpenAiRetryTests {
|
||||
|
||||
private @Mock OpenAiImageApi openAiImageApi;
|
||||
|
||||
private OpenAiModelCaller chatClient;
|
||||
private OpenAiChatModel chatModel;
|
||||
|
||||
private OpenAiEmbeddingClient embeddingClient;
|
||||
private OpenAiEmbeddingModel embeddingModel;
|
||||
|
||||
private OpenAiAudioTranscriptionClient audioTranscriptionClient;
|
||||
private OpenAiAudioTranscriptionModel audioTranscriptionModel;
|
||||
|
||||
private OpenAiImageClient imageClient;
|
||||
private OpenAiImageModel imageModel;
|
||||
|
||||
@BeforeEach
|
||||
public void beforeEach() {
|
||||
@@ -121,16 +121,16 @@ public class OpenAiRetryTests {
|
||||
retryListener = new TestRetryListener();
|
||||
retryTemplate.registerListener(retryListener);
|
||||
|
||||
chatClient = new OpenAiModelCaller(openAiApi, OpenAiChatOptions.builder().build(), null, retryTemplate);
|
||||
embeddingClient = new OpenAiEmbeddingClient(openAiApi, MetadataMode.EMBED,
|
||||
chatModel = new OpenAiChatModel(openAiApi, OpenAiChatOptions.builder().build(), null, retryTemplate);
|
||||
embeddingModel = new OpenAiEmbeddingModel(openAiApi, MetadataMode.EMBED,
|
||||
OpenAiEmbeddingOptions.builder().build(), retryTemplate);
|
||||
audioTranscriptionClient = new OpenAiAudioTranscriptionClient(openAiAudioApi,
|
||||
audioTranscriptionModel = new OpenAiAudioTranscriptionModel(openAiAudioApi,
|
||||
OpenAiAudioTranscriptionOptions.builder()
|
||||
.withModel("model")
|
||||
.withResponseFormat(TranscriptResponseFormat.JSON)
|
||||
.build(),
|
||||
retryTemplate);
|
||||
imageClient = new OpenAiImageClient(openAiImageApi, OpenAiImageOptions.builder().build(), retryTemplate);
|
||||
imageModel = new OpenAiImageModel(openAiImageApi, OpenAiImageOptions.builder().build(), retryTemplate);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -146,7 +146,7 @@ public class OpenAiRetryTests {
|
||||
.thenThrow(new TransientAiException("Transient Error 2"))
|
||||
.thenReturn(ResponseEntity.of(Optional.of(expectedChatCompletion)));
|
||||
|
||||
var result = chatClient.call(new Prompt("text"));
|
||||
var result = chatModel.call(new Prompt("text"));
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getResult().getOutput().getContent()).isSameAs("Response");
|
||||
@@ -158,7 +158,7 @@ public class OpenAiRetryTests {
|
||||
public void openAiChatNonTransientError() {
|
||||
when(openAiApi.chatCompletionEntity(isA(ChatCompletionRequest.class)))
|
||||
.thenThrow(new RuntimeException("Non Transient Error"));
|
||||
assertThrows(RuntimeException.class, () -> chatClient.call(new Prompt("text")));
|
||||
assertThrows(RuntimeException.class, () -> chatModel.call(new Prompt("text")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -174,7 +174,7 @@ public class OpenAiRetryTests {
|
||||
.thenThrow(new TransientAiException("Transient Error 2"))
|
||||
.thenReturn(Flux.just(expectedChatCompletion));
|
||||
|
||||
var result = chatClient.stream(new Prompt("text"));
|
||||
var result = chatModel.stream(new Prompt("text"));
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.collectList().block().get(0).getResult().getOutput().getContent()).isSameAs("Response");
|
||||
@@ -186,7 +186,7 @@ public class OpenAiRetryTests {
|
||||
public void openAiChatStreamNonTransientError() {
|
||||
when(openAiApi.chatCompletionStream(isA(ChatCompletionRequest.class)))
|
||||
.thenThrow(new RuntimeException("Non Transient Error"));
|
||||
assertThrows(RuntimeException.class, () -> chatClient.stream(new Prompt("text")));
|
||||
assertThrows(RuntimeException.class, () -> chatModel.stream(new Prompt("text")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -199,7 +199,7 @@ public class OpenAiRetryTests {
|
||||
.thenThrow(new TransientAiException("Transient Error 2"))
|
||||
.thenReturn(ResponseEntity.of(Optional.of(expectedEmbeddings)));
|
||||
|
||||
var result = embeddingClient
|
||||
var result = embeddingModel
|
||||
.call(new org.springframework.ai.embedding.EmbeddingRequest(List.of("text1", "text2"), null));
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
@@ -212,7 +212,7 @@ public class OpenAiRetryTests {
|
||||
public void openAiEmbeddingNonTransientError() {
|
||||
when(openAiApi.embeddings(isA(EmbeddingRequest.class)))
|
||||
.thenThrow(new RuntimeException("Non Transient Error"));
|
||||
assertThrows(RuntimeException.class, () -> embeddingClient
|
||||
assertThrows(RuntimeException.class, () -> embeddingModel
|
||||
.call(new org.springframework.ai.embedding.EmbeddingRequest(List.of("text1", "text2"), null)));
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ public class OpenAiRetryTests {
|
||||
.thenThrow(new TransientAiException("Transient Error 2"))
|
||||
.thenReturn(ResponseEntity.of(Optional.of(expectedResponse)));
|
||||
|
||||
AudioTranscriptionResponse result = audioTranscriptionClient
|
||||
AudioTranscriptionResponse result = audioTranscriptionModel
|
||||
.call(new AudioTranscriptionPrompt(new ClassPathResource("speech/jfk.flac")));
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
@@ -239,7 +239,7 @@ public class OpenAiRetryTests {
|
||||
public void openAiAudioTranscriptionNonTransientError() {
|
||||
when(openAiAudioApi.createTranscription(isA(TranscriptionRequest.class), isA(Class.class)))
|
||||
.thenThrow(new RuntimeException("Transient Error 1"));
|
||||
assertThrows(RuntimeException.class, () -> audioTranscriptionClient
|
||||
assertThrows(RuntimeException.class, () -> audioTranscriptionModel
|
||||
.call(new AudioTranscriptionPrompt(new ClassPathResource("speech/jfk.flac"))));
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ public class OpenAiRetryTests {
|
||||
.thenThrow(new TransientAiException("Transient Error 2"))
|
||||
.thenReturn(ResponseEntity.of(Optional.of(expectedResponse)));
|
||||
|
||||
var result = imageClient.call(new ImagePrompt(List.of(new ImageMessage("Image Message"))));
|
||||
var result = imageModel.call(new ImagePrompt(List.of(new ImageMessage("Image Message"))));
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getResult().getOutput().getUrl()).isEqualTo("url678");
|
||||
@@ -266,7 +266,7 @@ public class OpenAiRetryTests {
|
||||
when(openAiImageApi.createImage(isA(OpenAiImageRequest.class)))
|
||||
.thenThrow(new RuntimeException("Transient Error 1"));
|
||||
assertThrows(RuntimeException.class,
|
||||
() -> imageClient.call(new ImagePrompt(List.of(new ImageMessage("Image Message")))));
|
||||
() -> imageModel.call(new ImagePrompt(List.of(new ImageMessage("Image Message")))));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,11 +33,11 @@ import org.springframework.ai.chat.memory.VectorStoreChatMemoryChatServiceListen
|
||||
import org.springframework.ai.chat.memory.VectorStoreChatMemoryRetriever;
|
||||
import org.springframework.ai.chat.memory.LastMaxTokenSizeContentTransformer;
|
||||
import org.springframework.ai.chat.memory.SystemPromptChatMemoryAugmentor;
|
||||
import org.springframework.ai.embedding.EmbeddingClient;
|
||||
import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.evaluation.BaseMemoryTest;
|
||||
import org.springframework.ai.evaluation.RelevancyEvaluator;
|
||||
import org.springframework.ai.openai.OpenAiModelCaller;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingClient;
|
||||
import org.springframework.ai.openai.OpenAiChatModel;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingModel;
|
||||
import org.springframework.ai.openai.api.OpenAiApi;
|
||||
import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator;
|
||||
import org.springframework.ai.tokenizer.TokenCountEstimator;
|
||||
@@ -75,21 +75,21 @@ public class ChatMemoryLongTermSystemPromptIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiModelCaller openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiModelCaller(openAiApi);
|
||||
public OpenAiChatModel openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiChatModel(openAiApi);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EmbeddingClient embeddingClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiEmbeddingClient(openAiApi);
|
||||
public EmbeddingModel embeddingModel(OpenAiApi openAiApi) {
|
||||
return new OpenAiEmbeddingModel(openAiApi);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VectorStore qdrantVectorStore(EmbeddingClient embeddingClient) {
|
||||
public VectorStore qdrantVectorStore(EmbeddingModel embeddingModel) {
|
||||
QdrantClient qdrantClient = new QdrantClient(QdrantGrpcClient
|
||||
.newBuilder(qdrantContainer.getHost(), qdrantContainer.getMappedPort(QDRANT_GRPC_PORT), false)
|
||||
.build());
|
||||
return new QdrantVectorStore(qdrantClient, COLLECTION_NAME, embeddingClient);
|
||||
return new QdrantVectorStore(qdrantClient, COLLECTION_NAME, embeddingModel);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -98,10 +98,10 @@ public class ChatMemoryLongTermSystemPromptIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ChatService memoryChatService(OpenAiModelCaller chatClient, VectorStore vectorStore,
|
||||
public ChatService memoryChatService(OpenAiChatModel chatModel, VectorStore vectorStore,
|
||||
TokenCountEstimator tokenCountEstimator) {
|
||||
|
||||
return PromptTransformingChatService.builder(chatClient)
|
||||
return PromptTransformingChatService.builder(chatModel)
|
||||
.withRetrievers(List.of(new VectorStoreChatMemoryRetriever(vectorStore, 10)))
|
||||
.withContentPostProcessors(List.of(new LastMaxTokenSizeContentTransformer(tokenCountEstimator, 1000)))
|
||||
.withAugmentors(List.of(new SystemPromptChatMemoryAugmentor()))
|
||||
@@ -110,10 +110,10 @@ public class ChatMemoryLongTermSystemPromptIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public StreamingChatService memoryStreamingChatService(OpenAiModelCaller streamingChatClient,
|
||||
public StreamingChatService memoryStreamingChatService(OpenAiChatModel streamingChatModel,
|
||||
VectorStore vectorStore, TokenCountEstimator tokenCountEstimator) {
|
||||
|
||||
return StreamingPromptTransformingChatService.builder(streamingChatClient)
|
||||
return StreamingPromptTransformingChatService.builder(streamingChatModel)
|
||||
.withRetrievers(List.of(new VectorStoreChatMemoryRetriever(vectorStore, 10)))
|
||||
.withDocumentPostProcessors(List.of(new LastMaxTokenSizeContentTransformer(tokenCountEstimator, 1000)))
|
||||
.withAugmentors(List.of(new SystemPromptChatMemoryAugmentor()))
|
||||
@@ -122,8 +122,8 @@ public class ChatMemoryLongTermSystemPromptIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RelevancyEvaluator relevancyEvaluator(OpenAiModelCaller chatClient) {
|
||||
return new RelevancyEvaluator(chatClient);
|
||||
public RelevancyEvaluator relevancyEvaluator(OpenAiChatModel chatModel) {
|
||||
return new RelevancyEvaluator(chatModel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.OpenAiModelCaller;
|
||||
import org.springframework.ai.openai.OpenAiChatModel;
|
||||
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 OpenAiModelCaller openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiModelCaller(openAiApi);
|
||||
public OpenAiChatModel openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiChatModel(openAiApi);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -74,10 +74,10 @@ public class ChatMemoryShortTermMessageListIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ChatService memoryChatService(OpenAiModelCaller chatClient, ChatMemory chatHistory,
|
||||
public ChatService memoryChatService(OpenAiChatModel chatModel, ChatMemory chatHistory,
|
||||
TokenCountEstimator tokenCountEstimator) {
|
||||
|
||||
return PromptTransformingChatService.builder(chatClient)
|
||||
return PromptTransformingChatService.builder(chatModel)
|
||||
.withRetrievers(List.of(new ChatMemoryRetriever(chatHistory)))
|
||||
.withContentPostProcessors(List.of(new LastMaxTokenSizeContentTransformer(tokenCountEstimator, 1000)))
|
||||
.withAugmentors(List.of(new MessageChatMemoryAugmentor()))
|
||||
@@ -86,10 +86,10 @@ public class ChatMemoryShortTermMessageListIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public StreamingChatService memoryStreamingChatService(OpenAiModelCaller streamingChatClient,
|
||||
public StreamingChatService memoryStreamingChatService(OpenAiChatModel streamingChatModel,
|
||||
ChatMemory chatHistory, TokenCountEstimator tokenCountEstimator) {
|
||||
|
||||
return StreamingPromptTransformingChatService.builder(streamingChatClient)
|
||||
return StreamingPromptTransformingChatService.builder(streamingChatModel)
|
||||
.withRetrievers(List.of(new ChatMemoryRetriever(chatHistory)))
|
||||
.withDocumentPostProcessors(List.of(new LastMaxTokenSizeContentTransformer(tokenCountEstimator, 1000)))
|
||||
.withAugmentors(List.of(new MessageChatMemoryAugmentor()))
|
||||
@@ -98,8 +98,8 @@ public class ChatMemoryShortTermMessageListIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RelevancyEvaluator relevancyEvaluator(OpenAiModelCaller chatClient) {
|
||||
return new RelevancyEvaluator(chatClient);
|
||||
public RelevancyEvaluator relevancyEvaluator(OpenAiChatModel chatModel) {
|
||||
return new RelevancyEvaluator(chatModel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.OpenAiModelCaller;
|
||||
import org.springframework.ai.openai.OpenAiChatModel;
|
||||
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 OpenAiModelCaller openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiModelCaller(openAiApi);
|
||||
public OpenAiChatModel openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiChatModel(openAiApi);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -75,10 +75,10 @@ public class ChatMemoryShortTermSystemPromptIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ChatService memoryChatService(OpenAiModelCaller chatClient, ChatMemory chatHistory,
|
||||
public ChatService memoryChatService(OpenAiChatModel chatModel, ChatMemory chatHistory,
|
||||
TokenCountEstimator tokenCountEstimator) {
|
||||
|
||||
return PromptTransformingChatService.builder(chatClient)
|
||||
return PromptTransformingChatService.builder(chatModel)
|
||||
.withRetrievers(List.of(new ChatMemoryRetriever(chatHistory)))
|
||||
.withContentPostProcessors(List.of(new LastMaxTokenSizeContentTransformer(tokenCountEstimator, 1000)))
|
||||
.withAugmentors(List.of(new SystemPromptChatMemoryAugmentor()))
|
||||
@@ -87,10 +87,10 @@ public class ChatMemoryShortTermSystemPromptIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public StreamingChatService memoryStreamingChatService(OpenAiModelCaller streamingChatClient,
|
||||
public StreamingChatService memoryStreamingChatService(OpenAiChatModel streamingChatModel,
|
||||
ChatMemory chatHistory, TokenCountEstimator tokenCountEstimator) {
|
||||
|
||||
return StreamingPromptTransformingChatService.builder(streamingChatClient)
|
||||
return StreamingPromptTransformingChatService.builder(streamingChatModel)
|
||||
.withRetrievers(List.of(new ChatMemoryRetriever(chatHistory)))
|
||||
.withDocumentPostProcessors(List.of(new LastMaxTokenSizeContentTransformer(tokenCountEstimator, 1000)))
|
||||
.withAugmentors(List.of(new SystemPromptChatMemoryAugmentor()))
|
||||
@@ -99,8 +99,8 @@ public class ChatMemoryShortTermSystemPromptIT extends BaseMemoryTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RelevancyEvaluator relevancyEvaluator(OpenAiModelCaller chatClient) {
|
||||
return new RelevancyEvaluator(chatClient);
|
||||
public RelevancyEvaluator relevancyEvaluator(OpenAiChatModel chatModel) {
|
||||
return new RelevancyEvaluator(chatModel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,7 +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.OpenAiModelCaller;
|
||||
import org.springframework.ai.openai.OpenAiChatModel;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import org.testcontainers.qdrant.QdrantContainer;
|
||||
@@ -50,10 +50,10 @@ import org.springframework.ai.chat.prompt.transformer.TransformerContentType;
|
||||
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.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.evaluation.EvaluationResponse;
|
||||
import org.springframework.ai.evaluation.RelevancyEvaluator;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingClient;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingModel;
|
||||
import org.springframework.ai.openai.api.OpenAiApi;
|
||||
import org.springframework.ai.reader.JsonReader;
|
||||
import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator;
|
||||
@@ -163,21 +163,21 @@ public class LongShortTermChatMemoryWithRagIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiModelCaller openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiModelCaller(openAiApi);
|
||||
public OpenAiChatModel openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiChatModel(openAiApi);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiEmbeddingClient embeddingClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiEmbeddingClient(openAiApi);
|
||||
public OpenAiEmbeddingModel embeddingModel(OpenAiApi openAiApi) {
|
||||
return new OpenAiEmbeddingModel(openAiApi);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VectorStore qdrantVectorStore(EmbeddingClient embeddingClient) {
|
||||
public VectorStore qdrantVectorStore(EmbeddingModel embeddingModel) {
|
||||
QdrantClient qdrantClient = new QdrantClient(QdrantGrpcClient
|
||||
.newBuilder(qdrantContainer.getHost(), qdrantContainer.getMappedPort(QDRANT_GRPC_PORT), false)
|
||||
.build());
|
||||
return new QdrantVectorStore(qdrantClient, COLLECTION_NAME, embeddingClient);
|
||||
return new QdrantVectorStore(qdrantClient, COLLECTION_NAME, embeddingModel);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -186,10 +186,10 @@ public class LongShortTermChatMemoryWithRagIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ChatService memoryChatService(OpenAiModelCaller chatClient, VectorStore vectorStore,
|
||||
public ChatService memoryChatService(OpenAiChatModel chatModel, VectorStore vectorStore,
|
||||
TokenCountEstimator tokenCountEstimator, ChatMemory chatHistory) {
|
||||
|
||||
return PromptTransformingChatService.builder(chatClient)
|
||||
return PromptTransformingChatService.builder(chatModel)
|
||||
.withRetrievers(List.of(new VectorStoreRetriever(vectorStore, SearchRequest.defaults()),
|
||||
ChatMemoryRetriever.builder()
|
||||
.withChatHistory(chatHistory)
|
||||
@@ -223,12 +223,12 @@ public class LongShortTermChatMemoryWithRagIT {
|
||||
}
|
||||
|
||||
// @Bean
|
||||
// public StreamingChatService memoryStreamingChatAgent(OpenAiModelCall
|
||||
// streamingChatClient,
|
||||
// public StreamingChatService memoryStreamingChatAgent(OpenAiChatModel
|
||||
// streamingChatModel,
|
||||
// VectorStore vectorStore, TokenCountEstimator tokenCountEstimator, ChatHistory
|
||||
// chatHistory) {
|
||||
|
||||
// return StreamingPromptTransformingChatService.builder(streamingChatClient)
|
||||
// return StreamingPromptTransformingChatService.builder(streamingChatModel)
|
||||
// .withRetrievers(List.of(new ChatHistoryRetriever(chatHistory), new
|
||||
// DocumentChatHistoryRetriever(vectorStore, 10)))
|
||||
// .withDocumentPostProcessors(List.of(new
|
||||
@@ -240,13 +240,13 @@ public class LongShortTermChatMemoryWithRagIT {
|
||||
// }
|
||||
|
||||
@Bean
|
||||
public RelevancyEvaluator relevancyEvaluator(OpenAiModelCaller chatClient) {
|
||||
public RelevancyEvaluator relevancyEvaluator(OpenAiChatModel chatModel) {
|
||||
// Use GPT 4 as a better model for determining relevancy. gpt 3.5 makes basic
|
||||
// mistakes
|
||||
OpenAiChatOptions openAiChatOptions = OpenAiChatOptions.builder()
|
||||
.withModel(GPT_4_TURBO_PREVIEW.getValue())
|
||||
.build();
|
||||
return new RelevancyEvaluator(chatClient, openAiChatOptions);
|
||||
return new RelevancyEvaluator(chatModel, openAiChatOptions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +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.ChatCaller;
|
||||
import org.springframework.ai.chat.ChatModel;
|
||||
import org.springframework.ai.chat.service.ChatService;
|
||||
import org.springframework.ai.chat.prompt.transformer.TransformerContentType;
|
||||
import org.springframework.ai.document.Document;
|
||||
@@ -38,11 +38,11 @@ import org.springframework.ai.chat.prompt.Prompt;
|
||||
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.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.evaluation.EvaluationResponse;
|
||||
import org.springframework.ai.evaluation.RelevancyEvaluator;
|
||||
import org.springframework.ai.openai.OpenAiModelCaller;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingClient;
|
||||
import org.springframework.ai.openai.OpenAiChatModel;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingModel;
|
||||
import org.springframework.ai.openai.api.OpenAiApi;
|
||||
import org.springframework.ai.reader.JsonReader;
|
||||
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
|
||||
@@ -71,7 +71,7 @@ public class OpenAiPromptTransformingChatServiceIT {
|
||||
@Container
|
||||
static QdrantContainer qdrantContainer = new QdrantContainer("qdrant/qdrant:v1.9.2");
|
||||
|
||||
private final ChatCaller modelCaller;
|
||||
private final ChatModel chatModel;
|
||||
|
||||
private final VectorStore vectorStore;
|
||||
|
||||
@@ -81,9 +81,9 @@ public class OpenAiPromptTransformingChatServiceIT {
|
||||
private ChatService chatService;
|
||||
|
||||
@Autowired
|
||||
public OpenAiPromptTransformingChatServiceIT(ChatCaller modelCaller, ChatService chatService,
|
||||
public OpenAiPromptTransformingChatServiceIT(ChatModel chatModel, ChatService chatService,
|
||||
VectorStore vectorStore) {
|
||||
this.modelCaller = modelCaller;
|
||||
this.chatModel = chatModel;
|
||||
this.chatService = chatService;
|
||||
this.vectorStore = vectorStore;
|
||||
}
|
||||
@@ -102,7 +102,7 @@ public class OpenAiPromptTransformingChatServiceIT {
|
||||
OpenAiChatOptions openAiChatOptions = OpenAiChatOptions.builder()
|
||||
.withModel(GPT_4_TURBO_PREVIEW.getValue())
|
||||
.build();
|
||||
var relevancyEvaluator = new RelevancyEvaluator(this.modelCaller, openAiChatOptions);
|
||||
var relevancyEvaluator = new RelevancyEvaluator(this.chatModel, openAiChatOptions);
|
||||
|
||||
EvaluationResponse evaluationResponse = relevancyEvaluator.evaluate(chatServiceResponse.toEvaluationRequest());
|
||||
assertTrue(evaluationResponse.isPass(), "Response is not relevant to the question");
|
||||
@@ -145,26 +145,26 @@ public class OpenAiPromptTransformingChatServiceIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ChatCaller openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiModelCaller(openAiApi);
|
||||
public ChatModel openAiClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiChatModel(openAiApi);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EmbeddingClient embeddingClient(OpenAiApi openAiApi) {
|
||||
return new OpenAiEmbeddingClient(openAiApi);
|
||||
public EmbeddingModel embeddingModel(OpenAiApi openAiApi) {
|
||||
return new OpenAiEmbeddingModel(openAiApi);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VectorStore qdrantVectorStore(EmbeddingClient embeddingClient) {
|
||||
public VectorStore qdrantVectorStore(EmbeddingModel embeddingModel) {
|
||||
QdrantClient qdrantClient = new QdrantClient(QdrantGrpcClient
|
||||
.newBuilder(qdrantContainer.getHost(), qdrantContainer.getMappedPort(QDRANT_GRPC_PORT), false)
|
||||
.build());
|
||||
return new QdrantVectorStore(qdrantClient, COLLECTION_NAME, embeddingClient);
|
||||
return new QdrantVectorStore(qdrantClient, COLLECTION_NAME, embeddingModel);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ChatService chatService(ChatCaller modelCaller, VectorStore vectorStore) {
|
||||
return PromptTransformingChatService.builder(modelCaller)
|
||||
public ChatService chatService(ChatModel chatModel, VectorStore vectorStore) {
|
||||
return PromptTransformingChatService.builder(chatModel)
|
||||
.withRetrievers(List.of(new VectorStoreRetriever(vectorStore, SearchRequest.defaults())))
|
||||
.withAugmentors(List.of(new QuestionContextAugmentor()))
|
||||
.build();
|
||||
|
||||
@@ -19,7 +19,7 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.ai.embedding.EmbeddingRequest;
|
||||
import org.springframework.ai.embedding.EmbeddingResponse;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingClient;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingModel;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingOptions;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
@@ -32,13 +32,13 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class EmbeddingIT {
|
||||
|
||||
@Autowired
|
||||
private OpenAiEmbeddingClient embeddingClient;
|
||||
private OpenAiEmbeddingModel embeddingModel;
|
||||
|
||||
@Test
|
||||
void defaultEmbedding() {
|
||||
assertThat(embeddingClient).isNotNull();
|
||||
assertThat(embeddingModel).isNotNull();
|
||||
|
||||
EmbeddingResponse embeddingResponse = embeddingClient.embedForResponse(List.of("Hello World"));
|
||||
EmbeddingResponse embeddingResponse = embeddingModel.embedForResponse(List.of("Hello World"));
|
||||
assertThat(embeddingResponse.getResults()).hasSize(1);
|
||||
assertThat(embeddingResponse.getResults().get(0)).isNotNull();
|
||||
assertThat(embeddingResponse.getResults().get(0).getOutput()).hasSize(1536);
|
||||
@@ -46,13 +46,13 @@ class EmbeddingIT {
|
||||
assertThat(embeddingResponse.getMetadata()).containsEntry("total-tokens", 2);
|
||||
assertThat(embeddingResponse.getMetadata()).containsEntry("prompt-tokens", 2);
|
||||
|
||||
assertThat(embeddingClient.dimensions()).isEqualTo(1536);
|
||||
assertThat(embeddingModel.dimensions()).isEqualTo(1536);
|
||||
}
|
||||
|
||||
@Test
|
||||
void embedding3Large() {
|
||||
|
||||
EmbeddingResponse embeddingResponse = embeddingClient.call(new EmbeddingRequest(List.of("Hello World"),
|
||||
EmbeddingResponse embeddingResponse = embeddingModel.call(new EmbeddingRequest(List.of("Hello World"),
|
||||
OpenAiEmbeddingOptions.builder().withModel("text-embedding-3-large").build()));
|
||||
assertThat(embeddingResponse.getResults()).hasSize(1);
|
||||
assertThat(embeddingResponse.getResults().get(0)).isNotNull();
|
||||
@@ -61,13 +61,13 @@ class EmbeddingIT {
|
||||
assertThat(embeddingResponse.getMetadata()).containsEntry("total-tokens", 2);
|
||||
assertThat(embeddingResponse.getMetadata()).containsEntry("prompt-tokens", 2);
|
||||
|
||||
// assertThat(embeddingClient.dimensions()).isEqualTo(3072);
|
||||
// assertThat(embeddingModel.dimensions()).isEqualTo(3072);
|
||||
}
|
||||
|
||||
@Test
|
||||
void textEmbeddingAda002() {
|
||||
|
||||
EmbeddingResponse embeddingResponse = embeddingClient.call(new EmbeddingRequest(List.of("Hello World"),
|
||||
EmbeddingResponse embeddingResponse = embeddingModel.call(new EmbeddingRequest(List.of("Hello World"),
|
||||
OpenAiEmbeddingOptions.builder().withModel("text-embedding-3-small").build()));
|
||||
assertThat(embeddingResponse.getResults()).hasSize(1);
|
||||
assertThat(embeddingResponse.getResults().get(0)).isNotNull();
|
||||
@@ -77,7 +77,7 @@ class EmbeddingIT {
|
||||
assertThat(embeddingResponse.getMetadata()).containsEntry("total-tokens", 2);
|
||||
assertThat(embeddingResponse.getMetadata()).containsEntry("prompt-tokens", 2);
|
||||
|
||||
// assertThat(embeddingClient.dimensions()).isEqualTo(3072);
|
||||
// assertThat(embeddingModel.dimensions()).isEqualTo(3072);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest(classes = OpenAiTestConfiguration.class)
|
||||
@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+")
|
||||
public class OpenAiImageClientIT extends AbstractIT {
|
||||
public class OpenAiImageModelIT extends AbstractIT {
|
||||
|
||||
@Test
|
||||
void imageAsUrlTest() {
|
||||
@@ -44,7 +44,7 @@ public class OpenAiImageClientIT extends AbstractIT {
|
||||
|
||||
ImagePrompt imagePrompt = new ImagePrompt(instructions, options);
|
||||
|
||||
ImageResponse imageResponse = imageClient.call(imagePrompt);
|
||||
ImageResponse imageResponse = imageModel.call(imagePrompt);
|
||||
|
||||
assertThat(imageResponse.getResults()).hasSize(1);
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.springframework.ai.image.ImageGeneration;
|
||||
import org.springframework.ai.image.ImagePrompt;
|
||||
import org.springframework.ai.image.ImageResponse;
|
||||
import org.springframework.ai.image.ImageResponseMetadata;
|
||||
import org.springframework.ai.openai.OpenAiImageClient;
|
||||
import org.springframework.ai.openai.OpenAiImageModel;
|
||||
import org.springframework.ai.openai.api.OpenAiImageApi;
|
||||
import org.springframework.ai.openai.metadata.support.OpenAiApiResponseHeaders;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -47,13 +47,13 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat
|
||||
* @author Christian Tzolov
|
||||
* @since 0.7.0
|
||||
*/
|
||||
@RestClientTest(OpenAiImageClientWithImageResponseMetadataTests.Config.class)
|
||||
public class OpenAiImageClientWithImageResponseMetadataTests {
|
||||
@RestClientTest(OpenAiImageModelWithImageResponseMetadataTests.Config.class)
|
||||
public class OpenAiImageModelWithImageResponseMetadataTests {
|
||||
|
||||
private static String TEST_API_KEY = "sk-1234567890";
|
||||
|
||||
@Autowired
|
||||
private OpenAiImageClient openAiImageClient;
|
||||
private OpenAiImageModel openAiImageModel;
|
||||
|
||||
@Autowired
|
||||
private MockRestServiceServer server;
|
||||
@@ -70,7 +70,7 @@ public class OpenAiImageClientWithImageResponseMetadataTests {
|
||||
|
||||
ImagePrompt prompt = new ImagePrompt("Create an image of a mini golden doodle dog.");
|
||||
|
||||
ImageResponse response = this.openAiImageClient.call(prompt);
|
||||
ImageResponse response = this.openAiImageModel.call(prompt);
|
||||
|
||||
assertThat(response).isNotNull();
|
||||
List<ImageGeneration> imageGenerations = response.getResults();
|
||||
@@ -134,8 +134,8 @@ public class OpenAiImageClientWithImageResponseMetadataTests {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiImageClient openAiImageClient(OpenAiImageApi openAiImageApi) {
|
||||
return new OpenAiImageClient(openAiImageApi);
|
||||
public OpenAiImageModel openAiImageModel(OpenAiImageApi openAiImageApi) {
|
||||
return new OpenAiImageModel(openAiImageApi);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,16 +21,16 @@ import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.chat.ChatCaller;
|
||||
import org.springframework.ai.chat.ChatModel;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.StreamingChatCaller;
|
||||
import org.springframework.ai.chat.StreamingChatModel;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.chat.prompt.PromptTemplate;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.SystemMessage;
|
||||
import org.springframework.ai.image.ImageClient;
|
||||
import org.springframework.ai.openai.OpenAiAudioSpeechClient;
|
||||
import org.springframework.ai.openai.OpenAiAudioTranscriptionClient;
|
||||
import org.springframework.ai.image.ImageModel;
|
||||
import org.springframework.ai.openai.OpenAiAudioSpeechModel;
|
||||
import org.springframework.ai.openai.OpenAiAudioTranscriptionModel;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -43,19 +43,19 @@ public abstract class AbstractIT {
|
||||
private static final Logger logger = LoggerFactory.getLogger(AbstractIT.class);
|
||||
|
||||
@Autowired
|
||||
protected ChatCaller modelCaller;
|
||||
protected ChatModel chatModel;
|
||||
|
||||
@Autowired
|
||||
protected StreamingChatCaller streamingChatClient;
|
||||
protected StreamingChatModel streamingChatModel;
|
||||
|
||||
@Autowired
|
||||
protected OpenAiAudioTranscriptionClient transcriptionClient;
|
||||
protected OpenAiAudioTranscriptionModel transcriptionModel;
|
||||
|
||||
@Autowired
|
||||
protected OpenAiAudioSpeechClient speechClient;
|
||||
protected OpenAiAudioSpeechModel speechModel;
|
||||
|
||||
@Autowired
|
||||
protected ImageClient imageClient;
|
||||
protected ImageModel imageModel;
|
||||
|
||||
@Value("classpath:/prompts/eval/qa-evaluator-accurate-answer.st")
|
||||
protected Resource qaEvaluatorAccurateAnswerResource;
|
||||
@@ -85,12 +85,12 @@ public abstract class AbstractIT {
|
||||
}
|
||||
Message userMessage = userPromptTemplate.createMessage();
|
||||
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
|
||||
String yesOrNo = modelCaller.call(prompt).getResult().getOutput().getContent();
|
||||
String yesOrNo = chatModel.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 = modelCaller.call(prompt).getResult().getOutput().getContent();
|
||||
String reasonForFailure = chatModel.call(prompt).getResult().getOutput().getContent();
|
||||
fail(reasonForFailure);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.ai.document.DefaultContentFormatter;
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.openai.OpenAiModelCaller;
|
||||
import org.springframework.ai.openai.OpenAiChatModel;
|
||||
import org.springframework.ai.openai.api.OpenAiApi;
|
||||
import org.springframework.ai.transformer.ContentFormatTransformer;
|
||||
import org.springframework.ai.transformer.KeywordMetadataEnricher;
|
||||
@@ -163,18 +163,18 @@ public class MetadataTransformerIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiModelCaller openAiChatClient(OpenAiApi openAiApi) {
|
||||
OpenAiModelCaller openAiChatClient = new OpenAiModelCaller(openAiApi);
|
||||
return openAiChatClient;
|
||||
public OpenAiChatModel openAiChatModel(OpenAiApi openAiApi) {
|
||||
OpenAiChatModel openAiChatModel = new OpenAiChatModel(openAiApi);
|
||||
return openAiChatModel;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public KeywordMetadataEnricher keywordMetadata(OpenAiModelCaller aiClient) {
|
||||
public KeywordMetadataEnricher keywordMetadata(OpenAiChatModel aiClient) {
|
||||
return new KeywordMetadataEnricher(aiClient, 5);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SummaryMetadataEnricher summaryMetadata(OpenAiModelCaller aiClient) {
|
||||
public SummaryMetadataEnricher summaryMetadata(OpenAiChatModel aiClient) {
|
||||
return new SummaryMetadataEnricher(aiClient,
|
||||
List.of(SummaryType.PREVIOUS, SummaryType.CURRENT, SummaryType.NEXT));
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.CleanupMode;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.embedding.EmbeddingClient;
|
||||
import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.reader.JsonReader;
|
||||
import org.springframework.ai.vectorstore.SimpleVectorStore;
|
||||
import org.springframework.ai.reader.JsonMetadataGenerator;
|
||||
@@ -42,21 +42,21 @@ public class SimplePersistentVectorStoreIT {
|
||||
private Resource bikesJsonResource;
|
||||
|
||||
@Autowired
|
||||
private EmbeddingClient embeddingClient;
|
||||
private EmbeddingModel embeddingModel;
|
||||
|
||||
@Test
|
||||
void persist(@TempDir(cleanup = CleanupMode.ON_SUCCESS) Path workingDir) {
|
||||
JsonReader jsonReader = new JsonReader(bikesJsonResource, new ProductMetadataGenerator(), "price", "name",
|
||||
"shortDescription", "description", "tags");
|
||||
List<Document> documents = jsonReader.get();
|
||||
SimpleVectorStore vectorStore = new SimpleVectorStore(this.embeddingClient);
|
||||
SimpleVectorStore vectorStore = new SimpleVectorStore(this.embeddingModel);
|
||||
vectorStore.add(documents);
|
||||
|
||||
File tempFile = new File(workingDir.toFile(), "temp.txt");
|
||||
vectorStore.save(tempFile);
|
||||
assertThat(tempFile).isNotEmpty();
|
||||
assertThat(tempFile).content().contains("Velo 99 XR1 AXS");
|
||||
SimpleVectorStore vectorStore2 = new SimpleVectorStore(this.embeddingClient);
|
||||
SimpleVectorStore vectorStore2 = new SimpleVectorStore(this.embeddingModel);
|
||||
|
||||
vectorStore2.load(tempFile);
|
||||
List<Document> similaritySearch = vectorStore2.similaritySearch("Velo 99 XR1 AXS");
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.document.MetadataMode;
|
||||
import org.springframework.ai.embedding.AbstractEmbeddingClient;
|
||||
import org.springframework.ai.embedding.AbstractEmbeddingModel;
|
||||
import org.springframework.ai.embedding.Embedding;
|
||||
import org.springframework.ai.embedding.EmbeddingOptions;
|
||||
import org.springframework.ai.embedding.EmbeddingRequest;
|
||||
@@ -39,12 +39,12 @@ import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* <a href="https://postgresml.org">PostgresML</a> EmbeddingClient
|
||||
* <a href="https://postgresml.org">PostgresML</a> EmbeddingModel
|
||||
*
|
||||
* @author Toshiaki Maki
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
public class PostgresMlEmbeddingClient extends AbstractEmbeddingClient implements InitializingBean {
|
||||
public class PostgresMlEmbeddingModel extends AbstractEmbeddingModel implements InitializingBean {
|
||||
|
||||
public static final String DEFAULT_TRANSFORMER_MODEL = "distilbert-base-uncased";
|
||||
|
||||
@@ -83,16 +83,16 @@ public class PostgresMlEmbeddingClient extends AbstractEmbeddingClient implement
|
||||
* a constructor
|
||||
* @param jdbcTemplate JdbcTemplate
|
||||
*/
|
||||
public PostgresMlEmbeddingClient(JdbcTemplate jdbcTemplate) {
|
||||
public PostgresMlEmbeddingModel(JdbcTemplate jdbcTemplate) {
|
||||
this(jdbcTemplate, PostgresMlEmbeddingOptions.builder().build());
|
||||
}
|
||||
|
||||
/**
|
||||
* a PostgresMlEmbeddingClient constructor
|
||||
* a PostgresMlEmbeddingModel constructor
|
||||
* @param jdbcTemplate JdbcTemplate to use to interact with the database.
|
||||
* @param options PostgresMlEmbeddingOptions to configure the client.
|
||||
*/
|
||||
public PostgresMlEmbeddingClient(JdbcTemplate jdbcTemplate, PostgresMlEmbeddingOptions options) {
|
||||
public PostgresMlEmbeddingModel(JdbcTemplate jdbcTemplate, PostgresMlEmbeddingOptions options) {
|
||||
Assert.notNull(jdbcTemplate, "jdbc template must not be null.");
|
||||
Assert.notNull(options, "options must not be null.");
|
||||
Assert.notNull(options.getTransformer(), "transformer must not be null.");
|
||||
@@ -110,7 +110,7 @@ public class PostgresMlEmbeddingClient extends AbstractEmbeddingClient implement
|
||||
* @param transformer huggingface sentence-transformer name
|
||||
*/
|
||||
@Deprecated(since = "0.8.0", forRemoval = true)
|
||||
public PostgresMlEmbeddingClient(JdbcTemplate jdbcTemplate, String transformer) {
|
||||
public PostgresMlEmbeddingModel(JdbcTemplate jdbcTemplate, String transformer) {
|
||||
this(jdbcTemplate, transformer, VectorType.PG_ARRAY);
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ public class PostgresMlEmbeddingClient extends AbstractEmbeddingClient implement
|
||||
* @param vectorType vector type in PostgreSQL
|
||||
*/
|
||||
@Deprecated(since = "0.8.0", forRemoval = true)
|
||||
public PostgresMlEmbeddingClient(JdbcTemplate jdbcTemplate, String transformer, VectorType vectorType) {
|
||||
public PostgresMlEmbeddingModel(JdbcTemplate jdbcTemplate, String transformer, VectorType vectorType) {
|
||||
this(jdbcTemplate, transformer, vectorType, Map.of(), MetadataMode.EMBED);
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ public class PostgresMlEmbeddingClient extends AbstractEmbeddingClient implement
|
||||
* @param kwargs optional arguments
|
||||
*/
|
||||
@Deprecated(since = "0.8.0", forRemoval = true)
|
||||
public PostgresMlEmbeddingClient(JdbcTemplate jdbcTemplate, String transformer, VectorType vectorType,
|
||||
public PostgresMlEmbeddingModel(JdbcTemplate jdbcTemplate, String transformer, VectorType vectorType,
|
||||
Map<String, Object> kwargs, MetadataMode metadataMode) {
|
||||
Assert.notNull(jdbcTemplate, "jdbc template must not be null.");
|
||||
Assert.notNull(transformer, "transformer must not be null.");
|
||||
@@ -24,7 +24,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.springframework.ai.document.MetadataMode;
|
||||
import org.springframework.ai.embedding.EmbeddingOptions;
|
||||
import org.springframework.ai.model.ModelOptionsUtils;
|
||||
import org.springframework.ai.postgresml.PostgresMlEmbeddingClient.VectorType;
|
||||
import org.springframework.ai.postgresml.PostgresMlEmbeddingModel.VectorType;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
@@ -36,7 +36,7 @@ public class PostgresMlEmbeddingOptions implements EmbeddingOptions {
|
||||
/**
|
||||
* The Huggingface transformer model to use for the embedding.
|
||||
*/
|
||||
private @JsonProperty("transformer") String transformer = PostgresMlEmbeddingClient.DEFAULT_TRANSFORMER_MODEL;
|
||||
private @JsonProperty("transformer") String transformer = PostgresMlEmbeddingModel.DEFAULT_TRANSFORMER_MODEL;
|
||||
|
||||
/**
|
||||
* PostgresML vector type to use for the embedding.
|
||||
|
||||
@@ -30,7 +30,7 @@ import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.springframework.ai.embedding.EmbeddingOptions;
|
||||
import org.springframework.ai.embedding.EmbeddingRequest;
|
||||
import org.springframework.ai.embedding.EmbeddingResponse;
|
||||
import org.springframework.ai.postgresml.PostgresMlEmbeddingClient.VectorType;
|
||||
import org.springframework.ai.postgresml.PostgresMlEmbeddingModel.VectorType;
|
||||
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
|
||||
@@ -56,7 +56,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
|
||||
@Testcontainers
|
||||
@Disabled("Disabled from automatic execution, as it requires an excessive amount of memory (over 9GB)!")
|
||||
class PostgresMlEmbeddingClientIT {
|
||||
class PostgresMlEmbeddingModelIT {
|
||||
|
||||
@Container
|
||||
@ServiceConnection
|
||||
@@ -80,51 +80,51 @@ class PostgresMlEmbeddingClientIT {
|
||||
|
||||
@Test
|
||||
void embed() {
|
||||
PostgresMlEmbeddingClient embeddingClient = new PostgresMlEmbeddingClient(this.jdbcTemplate);
|
||||
embeddingClient.afterPropertiesSet();
|
||||
PostgresMlEmbeddingModel embeddingModel = new PostgresMlEmbeddingModel(this.jdbcTemplate);
|
||||
embeddingModel.afterPropertiesSet();
|
||||
|
||||
List<Double> embed = embeddingClient.embed("Hello World!");
|
||||
List<Double> embed = embeddingModel.embed("Hello World!");
|
||||
|
||||
assertThat(embed).hasSize(768);
|
||||
}
|
||||
|
||||
@Test
|
||||
void embedWithPgVector() {
|
||||
PostgresMlEmbeddingClient embeddingClient = new PostgresMlEmbeddingClient(this.jdbcTemplate,
|
||||
PostgresMlEmbeddingModel embeddingModel = new PostgresMlEmbeddingModel(this.jdbcTemplate,
|
||||
PostgresMlEmbeddingOptions.builder()
|
||||
.withTransformer("distilbert-base-uncased")
|
||||
.withVectorType(PostgresMlEmbeddingClient.VectorType.PG_VECTOR)
|
||||
.withVectorType(PostgresMlEmbeddingModel.VectorType.PG_VECTOR)
|
||||
.build());
|
||||
embeddingClient.afterPropertiesSet();
|
||||
embeddingModel.afterPropertiesSet();
|
||||
|
||||
List<Double> embed = embeddingClient.embed(new Document("Hello World!"));
|
||||
List<Double> embed = embeddingModel.embed(new Document("Hello World!"));
|
||||
|
||||
assertThat(embed).hasSize(768);
|
||||
}
|
||||
|
||||
@Test
|
||||
void embedWithDifferentModel() {
|
||||
PostgresMlEmbeddingClient embeddingClient = new PostgresMlEmbeddingClient(this.jdbcTemplate,
|
||||
PostgresMlEmbeddingModel embeddingModel = new PostgresMlEmbeddingModel(this.jdbcTemplate,
|
||||
PostgresMlEmbeddingOptions.builder().withTransformer("intfloat/e5-small").build());
|
||||
embeddingClient.afterPropertiesSet();
|
||||
embeddingModel.afterPropertiesSet();
|
||||
|
||||
List<Double> embed = embeddingClient.embed(new Document("Hello World!"));
|
||||
List<Double> embed = embeddingModel.embed(new Document("Hello World!"));
|
||||
|
||||
assertThat(embed).hasSize(384);
|
||||
}
|
||||
|
||||
@Test
|
||||
void embedWithKwargs() {
|
||||
PostgresMlEmbeddingClient embeddingClient = new PostgresMlEmbeddingClient(this.jdbcTemplate,
|
||||
PostgresMlEmbeddingModel embeddingModel = new PostgresMlEmbeddingModel(this.jdbcTemplate,
|
||||
PostgresMlEmbeddingOptions.builder()
|
||||
.withTransformer("distilbert-base-uncased")
|
||||
.withVectorType(PostgresMlEmbeddingClient.VectorType.PG_ARRAY)
|
||||
.withVectorType(PostgresMlEmbeddingModel.VectorType.PG_ARRAY)
|
||||
.withKwargs(Map.of("device", "cpu"))
|
||||
.withMetadataMode(MetadataMode.EMBED)
|
||||
.build());
|
||||
embeddingClient.afterPropertiesSet();
|
||||
embeddingModel.afterPropertiesSet();
|
||||
|
||||
List<Double> embed = embeddingClient.embed(new Document("Hello World!"));
|
||||
List<Double> embed = embeddingModel.embed(new Document("Hello World!"));
|
||||
|
||||
assertThat(embed).hasSize(768);
|
||||
}
|
||||
@@ -132,14 +132,14 @@ class PostgresMlEmbeddingClientIT {
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "PG_ARRAY", "PG_VECTOR" })
|
||||
void embedForResponse(String vectorType) {
|
||||
PostgresMlEmbeddingClient embeddingClient = new PostgresMlEmbeddingClient(this.jdbcTemplate,
|
||||
PostgresMlEmbeddingModel embeddingModel = new PostgresMlEmbeddingModel(this.jdbcTemplate,
|
||||
PostgresMlEmbeddingOptions.builder()
|
||||
.withTransformer("distilbert-base-uncased")
|
||||
.withVectorType(VectorType.valueOf(vectorType))
|
||||
.build());
|
||||
embeddingClient.afterPropertiesSet();
|
||||
embeddingModel.afterPropertiesSet();
|
||||
|
||||
EmbeddingResponse embeddingResponse = embeddingClient
|
||||
EmbeddingResponse embeddingResponse = embeddingModel
|
||||
.embedForResponse(List.of("Hello World!", "Spring AI!", "LLM!"));
|
||||
|
||||
assertThat(embeddingResponse).isNotNull();
|
||||
@@ -157,16 +157,16 @@ class PostgresMlEmbeddingClientIT {
|
||||
@Test
|
||||
void embedCallWithRequestOptionsOverride() {
|
||||
|
||||
PostgresMlEmbeddingClient embeddingClient = new PostgresMlEmbeddingClient(this.jdbcTemplate,
|
||||
PostgresMlEmbeddingModel embeddingModel = new PostgresMlEmbeddingModel(this.jdbcTemplate,
|
||||
PostgresMlEmbeddingOptions.builder()
|
||||
.withTransformer("distilbert-base-uncased")
|
||||
.withVectorType(VectorType.PG_VECTOR)
|
||||
.build());
|
||||
embeddingClient.afterPropertiesSet();
|
||||
embeddingModel.afterPropertiesSet();
|
||||
|
||||
var request1 = new EmbeddingRequest(List.of("Hello World!", "Spring AI!", "LLM!"), EmbeddingOptions.EMPTY);
|
||||
|
||||
EmbeddingResponse embeddingResponse = embeddingClient.call(request1);
|
||||
EmbeddingResponse embeddingResponse = embeddingModel.call(request1);
|
||||
|
||||
assertThat(embeddingResponse).isNotNull();
|
||||
assertThat(embeddingResponse.getResults()).hasSize(3);
|
||||
@@ -188,7 +188,7 @@ class PostgresMlEmbeddingClientIT {
|
||||
.withKwargs(Map.of("device", "cpu"))
|
||||
.build());
|
||||
|
||||
embeddingResponse = embeddingClient.call(request2);
|
||||
embeddingResponse = embeddingModel.call(request2);
|
||||
|
||||
assertThat(embeddingResponse).isNotNull();
|
||||
assertThat(embeddingResponse.getResults()).hasSize(3);
|
||||
@@ -205,11 +205,11 @@ class PostgresMlEmbeddingClientIT {
|
||||
|
||||
@Test
|
||||
void dimensions() {
|
||||
PostgresMlEmbeddingClient embeddingClient = new PostgresMlEmbeddingClient(this.jdbcTemplate);
|
||||
embeddingClient.afterPropertiesSet();
|
||||
Assertions.assertThat(embeddingClient.dimensions()).isEqualTo(768);
|
||||
PostgresMlEmbeddingModel embeddingModel = new PostgresMlEmbeddingModel(this.jdbcTemplate);
|
||||
embeddingModel.afterPropertiesSet();
|
||||
Assertions.assertThat(embeddingModel.dimensions()).isEqualTo(768);
|
||||
// cached
|
||||
Assertions.assertThat(embeddingClient.dimensions()).isEqualTo(768);
|
||||
Assertions.assertThat(embeddingModel.dimensions()).isEqualTo(768);
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@@ -34,8 +34,8 @@ public class PostgresMlEmbeddingOptionsTests {
|
||||
public void defaultOptions() {
|
||||
PostgresMlEmbeddingOptions options = PostgresMlEmbeddingOptions.builder().build();
|
||||
|
||||
assertThat(options.getTransformer()).isEqualTo(PostgresMlEmbeddingClient.DEFAULT_TRANSFORMER_MODEL);
|
||||
assertThat(options.getVectorType()).isEqualTo(PostgresMlEmbeddingClient.VectorType.PG_ARRAY);
|
||||
assertThat(options.getTransformer()).isEqualTo(PostgresMlEmbeddingModel.DEFAULT_TRANSFORMER_MODEL);
|
||||
assertThat(options.getVectorType()).isEqualTo(PostgresMlEmbeddingModel.VectorType.PG_ARRAY);
|
||||
assertThat(options.getKwargs()).isEqualTo(Map.of());
|
||||
assertThat(options.getMetadataMode()).isEqualTo(org.springframework.ai.document.MetadataMode.EMBED);
|
||||
}
|
||||
@@ -44,13 +44,13 @@ public class PostgresMlEmbeddingOptionsTests {
|
||||
public void newOptions() {
|
||||
PostgresMlEmbeddingOptions options = PostgresMlEmbeddingOptions.builder()
|
||||
.withTransformer("intfloat/e5-small")
|
||||
.withVectorType(PostgresMlEmbeddingClient.VectorType.PG_VECTOR)
|
||||
.withVectorType(PostgresMlEmbeddingModel.VectorType.PG_VECTOR)
|
||||
.withMetadataMode(org.springframework.ai.document.MetadataMode.ALL)
|
||||
.withKwargs(Map.of("device", "cpu"))
|
||||
.build();
|
||||
|
||||
assertThat(options.getTransformer()).isEqualTo("intfloat/e5-small");
|
||||
assertThat(options.getVectorType()).isEqualTo(PostgresMlEmbeddingClient.VectorType.PG_VECTOR);
|
||||
assertThat(options.getVectorType()).isEqualTo(PostgresMlEmbeddingModel.VectorType.PG_VECTOR);
|
||||
assertThat(options.getKwargs()).isEqualTo(Map.of("device", "cpu"));
|
||||
assertThat(options.getMetadataMode()).isEqualTo(org.springframework.ai.document.MetadataMode.ALL);
|
||||
}
|
||||
@@ -59,37 +59,37 @@ public class PostgresMlEmbeddingOptionsTests {
|
||||
public void mergeOptions() {
|
||||
|
||||
var jdbcTemplate = Mockito.mock(JdbcTemplate.class);
|
||||
PostgresMlEmbeddingClient embeddingClient = new PostgresMlEmbeddingClient(jdbcTemplate);
|
||||
PostgresMlEmbeddingModel embeddingModel = new PostgresMlEmbeddingModel(jdbcTemplate);
|
||||
|
||||
PostgresMlEmbeddingOptions options = embeddingClient.mergeOptions(EmbeddingOptions.EMPTY);
|
||||
PostgresMlEmbeddingOptions options = embeddingModel.mergeOptions(EmbeddingOptions.EMPTY);
|
||||
|
||||
// Default options
|
||||
assertThat(options.getTransformer()).isEqualTo(PostgresMlEmbeddingClient.DEFAULT_TRANSFORMER_MODEL);
|
||||
assertThat(options.getVectorType()).isEqualTo(PostgresMlEmbeddingClient.VectorType.PG_ARRAY);
|
||||
assertThat(options.getTransformer()).isEqualTo(PostgresMlEmbeddingModel.DEFAULT_TRANSFORMER_MODEL);
|
||||
assertThat(options.getVectorType()).isEqualTo(PostgresMlEmbeddingModel.VectorType.PG_ARRAY);
|
||||
assertThat(options.getKwargs()).isEqualTo(Map.of());
|
||||
assertThat(options.getMetadataMode()).isEqualTo(org.springframework.ai.document.MetadataMode.EMBED);
|
||||
|
||||
// Partial override
|
||||
options = embeddingClient.mergeOptions(PostgresMlEmbeddingOptions.builder()
|
||||
options = embeddingModel.mergeOptions(PostgresMlEmbeddingOptions.builder()
|
||||
.withTransformer("intfloat/e5-small")
|
||||
.withKwargs(Map.of("device", "cpu"))
|
||||
.build());
|
||||
|
||||
assertThat(options.getTransformer()).isEqualTo("intfloat/e5-small");
|
||||
assertThat(options.getVectorType()).isEqualTo(PostgresMlEmbeddingClient.VectorType.PG_ARRAY); // Default
|
||||
assertThat(options.getVectorType()).isEqualTo(PostgresMlEmbeddingModel.VectorType.PG_ARRAY); // Default
|
||||
assertThat(options.getKwargs()).isEqualTo(Map.of("device", "cpu"));
|
||||
assertThat(options.getMetadataMode()).isEqualTo(org.springframework.ai.document.MetadataMode.EMBED); // Default
|
||||
|
||||
// Complete override
|
||||
options = embeddingClient.mergeOptions(PostgresMlEmbeddingOptions.builder()
|
||||
options = embeddingModel.mergeOptions(PostgresMlEmbeddingOptions.builder()
|
||||
.withTransformer("intfloat/e5-small")
|
||||
.withVectorType(PostgresMlEmbeddingClient.VectorType.PG_VECTOR)
|
||||
.withVectorType(PostgresMlEmbeddingModel.VectorType.PG_VECTOR)
|
||||
.withMetadataMode(org.springframework.ai.document.MetadataMode.ALL)
|
||||
.withKwargs(Map.of("device", "cpu"))
|
||||
.build());
|
||||
|
||||
assertThat(options.getTransformer()).isEqualTo("intfloat/e5-small");
|
||||
assertThat(options.getVectorType()).isEqualTo(PostgresMlEmbeddingClient.VectorType.PG_VECTOR);
|
||||
assertThat(options.getVectorType()).isEqualTo(PostgresMlEmbeddingModel.VectorType.PG_VECTOR);
|
||||
assertThat(options.getKwargs()).isEqualTo(Map.of("device", "cpu"));
|
||||
assertThat(options.getMetadataMode()).isEqualTo(org.springframework.ai.document.MetadataMode.ALL);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.image.Image;
|
||||
import org.springframework.ai.image.ImageClient;
|
||||
import org.springframework.ai.image.ImageModel;
|
||||
import org.springframework.ai.image.ImageGeneration;
|
||||
import org.springframework.ai.image.ImageOptions;
|
||||
import org.springframework.ai.image.ImagePrompt;
|
||||
@@ -34,10 +34,10 @@ import org.springframework.ai.stabilityai.api.StabilityAiImageOptions;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* StabilityAiImageClient is a class that implements the ImageClient interface. It
|
||||
* provides a client for calling the StabilityAI image generation API.
|
||||
* StabilityAiImageModel is a class that implements the ImageModel interface. It provides
|
||||
* a client for calling the StabilityAI image generation API.
|
||||
*/
|
||||
public class StabilityAiImageClient implements ImageClient {
|
||||
public class StabilityAiImageModel implements ImageModel {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@@ -45,11 +45,11 @@ public class StabilityAiImageClient implements ImageClient {
|
||||
|
||||
private final StabilityAiApi stabilityAiApi;
|
||||
|
||||
public StabilityAiImageClient(StabilityAiApi stabilityAiApi) {
|
||||
public StabilityAiImageModel(StabilityAiApi stabilityAiApi) {
|
||||
this(stabilityAiApi, StabilityAiImageOptions.builder().build());
|
||||
}
|
||||
|
||||
public StabilityAiImageClient(StabilityAiApi stabilityAiApi, StabilityAiImageOptions options) {
|
||||
public StabilityAiImageModel(StabilityAiApi stabilityAiApi, StabilityAiImageOptions options) {
|
||||
Assert.notNull(stabilityAiApi, "StabilityAiApi must not be null");
|
||||
Assert.notNull(options, "StabilityAiImageOptions must not be null");
|
||||
this.stabilityAiApi = stabilityAiApi;
|
||||
@@ -61,20 +61,20 @@ public class StabilityAiImageClient implements ImageClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the StabilityAiImageClient with the given StabilityAiImagePrompt and returns
|
||||
* Calls the StabilityAiImageModel with the given StabilityAiImagePrompt and returns
|
||||
* the ImageResponse. This overloaded call method lets you pass the full set of Prompt
|
||||
* instructions that StabilityAI supports.
|
||||
* @param imagePrompt the StabilityAiImagePrompt containing the prompt and image model
|
||||
* options
|
||||
* @return the ImageResponse generated by the StabilityAiImageClient
|
||||
* @return the ImageResponse generated by the StabilityAiImageModel
|
||||
*/
|
||||
public ImageResponse call(ImagePrompt imagePrompt) {
|
||||
|
||||
ImageOptions runtimeOptions = imagePrompt.getOptions();
|
||||
|
||||
// Merge the runtime options passed via the prompt with the StabilityAiImageClient
|
||||
// Merge the runtime options passed via the prompt with the StabilityAiImageModel
|
||||
// options configured via Autoconfiguration.
|
||||
// Runtime options overwrite StabilityAiImageClient options
|
||||
// Runtime options overwrite StabilityAiImageModel options
|
||||
StabilityAiImageOptions optionsToUse = ModelOptionsUtils.merge(runtimeOptions, this.options,
|
||||
StabilityAiImageOptions.class);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user