Renaming of AiClient and related classes and packagenames

* Rename AiClient to ChatClient
* Rename AiResponse to ChatResponse
* Rename AiStreamClient to StreamingChatClient
* Rename package org.sf.ai.client to org.sf.ai.chat
* Update readme to indicate breaking changes
This commit is contained in:
Mark Pollack
2023-12-19 11:20:08 -05:00
parent 200be26d2e
commit 017d2a9bfc
34 changed files with 212 additions and 196 deletions

View File

@@ -12,6 +12,19 @@ Let's make your `@Beans` intelligent!
:warning:
### Breaking Changes
December 19, 2023 Update
Renaming of AiClient and related classes and packagenames
* Rename AiClient to ChatClient
* Rename AiResponse to ChatResponse
* Rename AiStreamClient to StreamingChatClient
* Rename package org.sf.ai.client to org.sf.ai.chat
:warning:
December 1, 2023
We are transitioning the project's Group ID:
* **FROM**: `org.springframework.experimental.ai`

View File

@@ -31,9 +31,9 @@ import com.azure.ai.openai.models.PromptFilterResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.azure.openai.metadata.AzureOpenAiGenerationMetadata;
import org.springframework.ai.client.AiClient;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.Generation;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.metadata.ChoiceMetadata;
import org.springframework.ai.metadata.PromptMetadata;
import org.springframework.ai.metadata.PromptMetadata.PromptFilterMetadata;
@@ -42,16 +42,16 @@ import org.springframework.ai.prompt.messages.Message;
import org.springframework.util.Assert;
/**
* {@link AiClient} implementation for {@literal Microsoft Azure AI} backed by
* {@link ChatClient} implementation for {@literal Microsoft Azure AI} backed by
* {@link OpenAIClient}.
*
* @author Mark Pollack
* @author Ueibin Kim
* @author John Blum
* @see org.springframework.ai.client.AiClient
* @see ChatClient
* @see com.azure.ai.openai.OpenAIClient
*/
public class AzureOpenAiClient implements AiClient {
public class AzureOpenAiClient implements ChatClient {
private Double temperature = 0.7;
@@ -108,7 +108,7 @@ public class AzureOpenAiClient implements AiClient {
}
@Override
public AiResponse generate(Prompt prompt) {
public ChatResponse generate(Prompt prompt) {
List<Message> messages = prompt.getMessages();
List<ChatMessage> azureMessages = new ArrayList<>();
@@ -136,7 +136,7 @@ public class AzureOpenAiClient implements AiClient {
generations.add(generation);
}
return new AiResponse(generations, AzureOpenAiGenerationMetadata.from(chatCompletions))
return new ChatResponse(generations, AzureOpenAiGenerationMetadata.from(chatCompletions))
.withPromptMetadata(generatePromptMetadata(chatCompletions));
}

View File

@@ -27,8 +27,8 @@ import com.azure.ai.openai.models.ContentFilterSeverity;
import org.junit.jupiter.api.Test;
import org.springframework.ai.azure.openai.MockAzureOpenAiTestConfiguration;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.Generation;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.metadata.ChoiceMetadata;
import org.springframework.ai.metadata.GenerationMetadata;
import org.springframework.ai.metadata.PromptMetadata;
@@ -73,7 +73,7 @@ class AzureOpenAiClientMetadataTests {
Prompt prompt = new Prompt("Can I fly like a bird?");
AiResponse response = this.aiClient.generate(prompt);
ChatResponse response = this.aiClient.generate(prompt);
assertThat(response).isNotNull();
@@ -88,7 +88,7 @@ class AzureOpenAiClientMetadataTests {
assertChoiceMetadata(generation);
}
private void assertPromptMetadata(AiResponse response) {
private void assertPromptMetadata(ChatResponse response) {
PromptMetadata promptMetadata = response.getPromptMetadata();
@@ -101,7 +101,7 @@ class AzureOpenAiClientMetadataTests {
assertContentFilterResults(promptFilterMetadata.getContentFilterMetadata(), ContentFilterSeverity.HIGH);
}
private void assertGenerationMetadata(AiResponse response) {
private void assertGenerationMetadata(ChatResponse response) {
GenerationMetadata generationMetadata = response.getGenerationMetadata();

View File

@@ -18,26 +18,27 @@ package org.springframework.ai.bedrock.anthropic;
import java.util.List;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.ChatResponse;
import reactor.core.publisher.Flux;
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.client.AiClient;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.AiStreamClient;
import org.springframework.ai.client.Generation;
import org.springframework.ai.chat.StreamingChatClient;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.metadata.ChoiceMetadata;
import org.springframework.ai.prompt.Prompt;
/**
* Java {@link AiClient} and {@link AiStreamClient} for the Bedrock Anthropic chat model.
* Java {@link ChatClient} and {@link StreamingChatClient} for the Bedrock Anthropic chat
* model.
*
* @author Christian Tzolov
* @since 0.8.0
*/
public class BedrockAnthropicChatClient implements AiClient, AiStreamClient {
public class BedrockAnthropicChatClient implements ChatClient, StreamingChatClient {
private final AnthropicChatBedrockApi anthropicChatApi;
@@ -88,7 +89,7 @@ public class BedrockAnthropicChatClient implements AiClient, AiStreamClient {
}
@Override
public AiResponse generate(Prompt prompt) {
public ChatResponse generate(Prompt prompt) {
final String promptValue = MessageToPromptConverter.create().toPrompt(prompt.getMessages());
AnthropicChatRequest request = AnthropicChatRequest.builder(promptValue)
@@ -102,11 +103,11 @@ public class BedrockAnthropicChatClient implements AiClient, AiStreamClient {
AnthropicChatResponse response = this.anthropicChatApi.chatCompletion(request);
return new AiResponse(List.of(new Generation(response.completion())));
return new ChatResponse(List.of(new Generation(response.completion())));
}
@Override
public Flux<AiResponse> generateStream(Prompt prompt) {
public Flux<ChatResponse> generateStream(Prompt prompt) {
final String promptValue = MessageToPromptConverter.create().toPrompt(prompt.getMessages());
@@ -128,7 +129,7 @@ public class BedrockAnthropicChatClient implements AiClient, AiStreamClient {
generation = generation
.withChoiceMetadata(ChoiceMetadata.from(stopReason, response.amazonBedrockInvocationMetrics()));
}
return new AiResponse(List.of(generation));
return new ChatResponse(List.of(generation));
});
}

View File

@@ -18,6 +18,7 @@ package org.springframework.ai.bedrock.cohere;
import java.util.List;
import org.springframework.ai.chat.ChatResponse;
import reactor.core.publisher.Flux;
import org.springframework.ai.bedrock.BedrockUsage;
@@ -28,10 +29,9 @@ import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi.CohereChat
import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi.CohereChatRequest.LogitBias;
import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi.CohereChatRequest.ReturnLikelihoods;
import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi.CohereChatRequest.Truncate;
import org.springframework.ai.client.AiClient;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.AiStreamClient;
import org.springframework.ai.client.Generation;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.StreamingChatClient;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.metadata.ChoiceMetadata;
import org.springframework.ai.metadata.Usage;
import org.springframework.ai.prompt.Prompt;
@@ -40,7 +40,7 @@ import org.springframework.ai.prompt.Prompt;
* @author Christian Tzolov
* @since 0.8.0
*/
public class BedrockCohereChatClient implements AiClient, AiStreamClient {
public class BedrockCohereChatClient implements ChatClient, StreamingChatClient {
private final CohereChatBedrockApi chatApi;
@@ -112,25 +112,25 @@ public class BedrockCohereChatClient implements AiClient, AiStreamClient {
}
@Override
public AiResponse generate(Prompt prompt) {
public ChatResponse generate(Prompt prompt) {
CohereChatResponse response = this.chatApi.chatCompletion(this.createRequest(prompt, false));
List<Generation> generations = response.generations().stream().map(g -> {
return new Generation(g.text());
}).toList();
return new AiResponse(generations);
return new ChatResponse(generations);
}
@Override
public Flux<AiResponse> generateStream(Prompt prompt) {
public Flux<ChatResponse> generateStream(Prompt prompt) {
return this.chatApi.chatCompletionStream(this.createRequest(prompt, true)).map(g -> {
if (g.isFinished()) {
String finishReason = g.finishReason().name();
Usage usage = BedrockUsage.from(g.amazonBedrockInvocationMetrics());
return new AiResponse(
return new ChatResponse(
List.of(new Generation("").withChoiceMetadata(ChoiceMetadata.from(finishReason, usage))));
}
return new AiResponse(List.of(new Generation(g.text())));
return new ChatResponse(List.of(new Generation(g.text())));
});
}

View File

@@ -18,27 +18,28 @@ package org.springframework.ai.bedrock.llama2;
import java.util.List;
import org.springframework.ai.chat.ChatResponse;
import reactor.core.publisher.Flux;
import org.springframework.ai.bedrock.MessageToPromptConverter;
import org.springframework.ai.bedrock.llama2.api.Llama2ChatBedrockApi;
import org.springframework.ai.bedrock.llama2.api.Llama2ChatBedrockApi.Llama2ChatRequest;
import org.springframework.ai.bedrock.llama2.api.Llama2ChatBedrockApi.Llama2ChatResponse;
import org.springframework.ai.client.AiClient;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.AiStreamClient;
import org.springframework.ai.client.Generation;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.StreamingChatClient;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.metadata.ChoiceMetadata;
import org.springframework.ai.metadata.Usage;
import org.springframework.ai.prompt.Prompt;
/**
* Java {@link AiClient} and {@link AiStreamClient} for the Bedrock Llama2 chat model.
* Java {@link ChatClient} and {@link StreamingChatClient} for the Bedrock Llama2 chat
* model.
*
* @author Christian Tzolov
* @since 0.8.0
*/
public class BedrockLlama2ChatClient implements AiClient, AiStreamClient {
public class BedrockLlama2ChatClient implements ChatClient, StreamingChatClient {
private final Llama2ChatBedrockApi chatApi;
@@ -68,7 +69,7 @@ public class BedrockLlama2ChatClient implements AiClient, AiStreamClient {
}
@Override
public AiResponse generate(Prompt prompt) {
public ChatResponse generate(Prompt prompt) {
final String promptValue = MessageToPromptConverter.create().toPrompt(prompt.getMessages());
var request = Llama2ChatRequest.builder(promptValue)
@@ -79,12 +80,12 @@ public class BedrockLlama2ChatClient implements AiClient, AiStreamClient {
Llama2ChatResponse response = this.chatApi.chatCompletion(request);
return new AiResponse(List.of(new Generation(response.generation())
return new ChatResponse(List.of(new Generation(response.generation())
.withChoiceMetadata(ChoiceMetadata.from(response.stopReason().name(), extractUsage(response)))));
}
@Override
public Flux<AiResponse> generateStream(Prompt prompt) {
public Flux<ChatResponse> generateStream(Prompt prompt) {
final String promptValue = MessageToPromptConverter.create().toPrompt(prompt.getMessages());
@@ -98,7 +99,7 @@ public class BedrockLlama2ChatClient implements AiClient, AiStreamClient {
return fluxResponse.map(response -> {
String stopReason = response.stopReason() != null ? response.stopReason().name() : null;
return new AiResponse(List.of(new Generation(response.generation())
return new ChatResponse(List.of(new Generation(response.generation())
.withChoiceMetadata(ChoiceMetadata.from(stopReason, extractUsage(response)))));
});
}

View File

@@ -18,6 +18,7 @@ package org.springframework.ai.bedrock.titan;
import java.util.List;
import org.springframework.ai.chat.ChatClient;
import reactor.core.publisher.Flux;
import org.springframework.ai.bedrock.MessageToPromptConverter;
@@ -25,10 +26,9 @@ 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.client.AiClient;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.AiStreamClient;
import org.springframework.ai.client.Generation;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.StreamingChatClient;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.metadata.ChoiceMetadata;
import org.springframework.ai.metadata.Usage;
import org.springframework.ai.prompt.Prompt;
@@ -37,7 +37,7 @@ import org.springframework.ai.prompt.Prompt;
* @author Christian Tzolov
* @since 0.8.0
*/
public class BedrockTitanChatClient implements AiClient, AiStreamClient {
public class BedrockTitanChatClient implements ChatClient, StreamingChatClient {
private final TitanChatBedrockApi chatApi;
@@ -74,17 +74,17 @@ public class BedrockTitanChatClient implements AiClient, AiStreamClient {
}
@Override
public AiResponse generate(Prompt prompt) {
public ChatResponse generate(Prompt prompt) {
TitanChatResponse response = this.chatApi.chatCompletion(this.createRequest(prompt, false));
List<Generation> generations = response.results().stream().map(result -> {
return new Generation(result.outputText());
}).toList();
return new AiResponse(generations);
return new ChatResponse(generations);
}
@Override
public Flux<AiResponse> generateStream(Prompt prompt) {
public Flux<ChatResponse> generateStream(Prompt prompt) {
return this.chatApi.chatCompletionStream(this.createRequest(prompt, true)).map(chunk -> {
Generation generation = new Generation(chunk.outputText());
@@ -99,7 +99,7 @@ public class BedrockTitanChatClient implements AiClient, AiStreamClient {
generation = generation.withChoiceMetadata(ChoiceMetadata.from(completionReason, extractUsage(chunk)));
}
return new AiResponse(List.of(generation));
return new ChatResponse(List.of(generation));
});
}

View File

@@ -8,12 +8,12 @@ import java.util.stream.Collectors;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.springframework.ai.chat.ChatResponse;
import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import org.springframework.ai.bedrock.anthropic.api.AnthropicChatBedrockApi;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.Generation;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.parser.BeanOutputParser;
import org.springframework.ai.parser.ListOutputParser;
import org.springframework.ai.parser.MapOutputParser;
@@ -52,7 +52,7 @@ class BedrockAnthropicChatClientIT {
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
AiResponse response = client.generate(prompt);
ChatResponse response = client.generate(prompt);
assertThat(response.getGeneration().getContent()).contains("Blackbeard");
}
@@ -137,7 +137,7 @@ class BedrockAnthropicChatClientIT {
.collectList()
.block()
.stream()
.map(AiResponse::getGenerations)
.map(ChatResponse::getGenerations)
.flatMap(List::stream)
.map(Generation::getContent)
.collect(Collectors.joining());

View File

@@ -13,8 +13,8 @@ import software.amazon.awssdk.regions.Region;
import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi;
import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi.CohereChatModel;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.Generation;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.parser.BeanOutputParser;
import org.springframework.ai.parser.ListOutputParser;
import org.springframework.ai.parser.MapOutputParser;
@@ -53,7 +53,7 @@ class BedrockCohereChatClientIT {
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);
Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice));
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
AiResponse response = client.generate(prompt);
ChatResponse response = client.generate(prompt);
assertThat(response.getGeneration().getContent()).contains("Blackbeard");
}
@@ -137,7 +137,7 @@ class BedrockCohereChatClientIT {
.collectList()
.block()
.stream()
.map(AiResponse::getGenerations)
.map(ChatResponse::getGenerations)
.flatMap(List::stream)
.map(Generation::getContent)
.collect(Collectors.joining());

View File

@@ -9,13 +9,13 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.springframework.ai.chat.ChatResponse;
import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import org.springframework.ai.bedrock.llama2.api.Llama2ChatBedrockApi;
import org.springframework.ai.bedrock.llama2.api.Llama2ChatBedrockApi.Llama2ChatCompletionModel;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.Generation;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.parser.BeanOutputParser;
import org.springframework.ai.parser.ListOutputParser;
import org.springframework.ai.parser.MapOutputParser;
@@ -54,7 +54,7 @@ class BedrockLlama2ChatClientIT {
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
AiResponse response = client.generate(prompt);
ChatResponse response = client.generate(prompt);
assertThat(response.getGeneration().getContent()).contains("Blackbeard");
}
@@ -142,7 +142,7 @@ class BedrockLlama2ChatClientIT {
.collectList()
.block()
.stream()
.map(AiResponse::getGenerations)
.map(ChatResponse::getGenerations)
.flatMap(List::stream)
.map(Generation::getContent)
.collect(Collectors.joining());

View File

@@ -9,13 +9,13 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.springframework.ai.chat.ChatResponse;
import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import org.springframework.ai.bedrock.titan.api.TitanChatBedrockApi;
import org.springframework.ai.bedrock.titan.api.TitanChatBedrockApi.TitanChatModel;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.Generation;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.parser.BeanOutputParser;
import org.springframework.ai.parser.ListOutputParser;
import org.springframework.ai.parser.MapOutputParser;
@@ -54,7 +54,7 @@ class BedrockTitanChatClientIT {
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);
Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice));
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
AiResponse response = client.generate(prompt);
ChatResponse response = client.generate(prompt);
assertThat(response.getGeneration().getContent()).contains("Blackbeard");
}
@@ -143,7 +143,7 @@ class BedrockTitanChatClientIT {
.collectList()
.block()
.stream()
.map(AiResponse::getGenerations)
.map(ChatResponse::getGenerations)
.flatMap(List::stream)
.map(Generation::getContent)
.collect(Collectors.joining());

View File

@@ -14,19 +14,19 @@
* limitations under the License.
*/
package org.springframework.ai.client;
package org.springframework.ai.chat;
import org.springframework.ai.prompt.Prompt;
import org.springframework.ai.prompt.messages.UserMessage;
@FunctionalInterface
public interface AiClient {
public interface ChatClient {
default String generate(String message) {
Prompt prompt = new Prompt(new UserMessage(message));
return generate(prompt).getGeneration().getContent();
}
AiResponse generate(Prompt prompt);
ChatResponse generate(Prompt prompt);
}

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ai.client;
package org.springframework.ai.chat;
import java.util.List;
@@ -24,7 +24,7 @@ import org.springframework.lang.Nullable;
/**
* The chat completion (e.g. generation) response returned by an AI provider.
*/
public class AiResponse {
public class ChatResponse {
private final GenerationMetadata metadata;
@@ -36,22 +36,22 @@ public class AiResponse {
private PromptMetadata promptMetadata;
/**
* Construct a new {@link AiResponse} instance without metadata.
* Construct a new {@link ChatResponse} instance without metadata.
* @param generations the {@link List} of {@link Generation} returned by the AI
* provider.
*/
public AiResponse(List<Generation> generations) {
public ChatResponse(List<Generation> generations) {
this(generations, GenerationMetadata.NULL);
}
/**
* Construct a new {@link AiResponse} instance.
* Construct a new {@link ChatResponse} instance.
* @param generations the {@link List} of {@link Generation} returned by the AI
* provider.
* @param metadata {@link GenerationMetadata} containing information about the use of
* the AI provider's API.
*/
public AiResponse(List<Generation> generations, GenerationMetadata metadata) {
public ChatResponse(List<Generation> generations, GenerationMetadata metadata) {
this.metadata = metadata;
this.generations = List.copyOf(generations);
}
@@ -96,10 +96,10 @@ public class AiResponse {
* when processing the prompt.
* @param promptMetadata {@link PromptMetadata} returned by the AI in the response
* when processing the prompt.
* @return this {@link AiResponse}.
* @return this {@link ChatResponse}.
* @see #getPromptMetadata()
*/
public AiResponse withPromptMetadata(@Nullable PromptMetadata promptMetadata) {
public ChatResponse withPromptMetadata(@Nullable PromptMetadata promptMetadata) {
this.promptMetadata = promptMetadata;
return this;
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.ai.client;
package org.springframework.ai.chat;
import java.util.Collections;
import java.util.Map;

View File

@@ -14,15 +14,15 @@
* limitations under the License.
*/
package org.springframework.ai.client;
package org.springframework.ai.chat;
import reactor.core.publisher.Flux;
import org.springframework.ai.prompt.Prompt;
@FunctionalInterface
public interface AiStreamClient {
public interface StreamingChatClient {
public Flux<AiResponse> generateStream(Prompt prompt);
public Flux<ChatResponse> generateStream(Prompt prompt);
}

View File

@@ -19,7 +19,7 @@ package org.springframework.ai.transformer;
import java.util.List;
import java.util.Map;
import org.springframework.ai.client.AiClient;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.document.Document;
import org.springframework.ai.document.DocumentTransformer;
import org.springframework.ai.prompt.Prompt;
@@ -44,18 +44,18 @@ public class KeywordMetadataEnricher implements DocumentTransformer {
/**
* Model predictor
*/
private final AiClient aiClient;
private final ChatClient chatClient;
/**
* The number of keywords to extract.
*/
private final int keywordCount;
public KeywordMetadataEnricher(AiClient aiClient, int keywordCount) {
Assert.notNull(aiClient, "AiClient must not be null");
public KeywordMetadataEnricher(ChatClient chatClient, int keywordCount) {
Assert.notNull(chatClient, "ChatClient must not be null");
Assert.isTrue(keywordCount >= 1, "Document count must be >= 1");
this.aiClient = aiClient;
this.chatClient = chatClient;
this.keywordCount = keywordCount;
}
@@ -65,7 +65,7 @@ public class KeywordMetadataEnricher implements DocumentTransformer {
var template = new PromptTemplate(String.format(KEYWORDS_TEMPLATE, keywordCount));
Prompt prompt = template.create(Map.of(CONTEXT_STR_PLACEHOLDER, document.getContent()));
String keywords = this.aiClient.generate(prompt).getGeneration().getContent();
String keywords = this.chatClient.generate(prompt).getGeneration().getContent();
document.getMetadata().putAll(Map.of(EXCERPT_KEYWORDS_METADATA_KEY, keywords));
}
return documents;

View File

@@ -21,7 +21,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.ai.client.AiClient;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.document.Document;
import org.springframework.ai.document.DocumentTransformer;
import org.springframework.ai.document.MetadataMode;
@@ -63,7 +63,7 @@ public class SummaryMetadataEnricher implements DocumentTransformer {
/**
* AI client.
*/
private final AiClient aiClient;
private final ChatClient chatClient;
/**
* Number of documents from front to use for title extraction.
@@ -77,16 +77,16 @@ public class SummaryMetadataEnricher implements DocumentTransformer {
*/
private final String summaryTemplate;
public SummaryMetadataEnricher(AiClient aiClient, List<SummaryType> summaryTypes) {
this(aiClient, summaryTypes, DEFAULT_SUMMARY_EXTRACT_TEMPLATE, MetadataMode.ALL);
public SummaryMetadataEnricher(ChatClient chatClient, List<SummaryType> summaryTypes) {
this(chatClient, summaryTypes, DEFAULT_SUMMARY_EXTRACT_TEMPLATE, MetadataMode.ALL);
}
public SummaryMetadataEnricher(AiClient aiClient, List<SummaryType> summaryTypes, String summaryTemplate,
public SummaryMetadataEnricher(ChatClient chatClient, List<SummaryType> summaryTypes, String summaryTemplate,
MetadataMode metadataMode) {
Assert.notNull(aiClient, "AiClient must not be null");
Assert.notNull(chatClient, "ChatClient must not be null");
Assert.hasText(summaryTemplate, "Summary template must not be empty");
this.aiClient = aiClient;
this.chatClient = chatClient;
this.summaryTypes = CollectionUtils.isEmpty(summaryTypes) ? List.of(SummaryType.CURRENT) : summaryTypes;
this.metadataMode = metadataMode;
this.summaryTemplate = summaryTemplate;
@@ -102,7 +102,7 @@ public class SummaryMetadataEnricher implements DocumentTransformer {
Prompt prompt = new PromptTemplate(this.summaryTemplate)
.create(Map.of(CONTEXT_STR_PLACEHOLDER, documentContext));
documentSummaries.add(this.aiClient.generate(prompt).getGeneration().getContent());
documentSummaries.add(this.chatClient.generate(prompt).getGeneration().getContent());
}
for (int i = 0; i < documentSummaries.size(); i++) {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.ai.client;
package org.springframework.ai.chat;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -33,10 +33,11 @@ import java.util.Collections;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.ai.prompt.Prompt;
/**
* Unit Tests for {@link AiClient}.
* Unit Tests for {@link ChatClient}.
*
* @author John Blum
* @since 0.2.0
@@ -49,9 +50,9 @@ class AiClientTests {
String userMessage = "Zero Wing";
String responseMessage = "All your bases are belong to us";
AiClient mockClient = mock(AiClient.class);
ChatClient mockClient = Mockito.mock(ChatClient.class);
Generation generation = spy(new Generation(responseMessage));
AiResponse response = spy(new AiResponse(Collections.singletonList(generation)));
ChatResponse response = spy(new ChatResponse(Collections.singletonList(generation)));
doCallRealMethod().when(mockClient).generate(anyString());

View File

@@ -23,9 +23,9 @@ import java.util.Map;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.ai.client.AiClient;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.Generation;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.huggingface.api.TextGenerationInferenceApi;
import org.springframework.ai.huggingface.invoker.ApiClient;
import org.springframework.ai.huggingface.model.AllOfGenerateResponseDetails;
@@ -35,12 +35,12 @@ import org.springframework.ai.huggingface.model.GenerateResponse;
import org.springframework.ai.prompt.Prompt;
/**
* An implementation of {@link AiClient} that interfaces with HuggingFace Inference
* An implementation of {@link ChatClient} that interfaces with HuggingFace Inference
* Endpoints for text generation.
*
* @author Mark Pollack
*/
public class HuggingfaceAiClient implements AiClient {
public class HuggingfaceAiClient implements ChatClient {
/**
* Token required for authenticating with the HuggingFace Inference API.
@@ -83,10 +83,10 @@ public class HuggingfaceAiClient implements AiClient {
/**
* Generate text based on the provided prompt.
* @param prompt The input prompt based on which text is to be generated.
* @return AiResponse containing the generated text and other related details.
* @return ChatResponse containing the generated text and other related details.
*/
@Override
public AiResponse generate(Prompt prompt) {
public ChatResponse generate(Prompt prompt) {
GenerateRequest generateRequest = new GenerateRequest();
generateRequest.setInputs(prompt.getContents());
GenerateParameters generateParameters = new GenerateParameters();
@@ -102,7 +102,7 @@ public class HuggingfaceAiClient implements AiClient {
});
Generation generation = new Generation(generatedText, detailsMap);
generations.add(generation);
return new AiResponse(generations);
return new ChatResponse(generations);
}
/**

View File

@@ -20,7 +20,7 @@ import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.prompt.Prompt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@@ -46,8 +46,8 @@ public class ClientIT {
[/INST]
""";
Prompt prompt = new Prompt(mistral7bInstruct);
AiResponse aiResponse = huggingfaceAiClient.generate(prompt);
assertThat(aiResponse.getGeneration().getContent()).isNotEmpty();
ChatResponse chatResponse = huggingfaceAiClient.generate(prompt);
assertThat(chatResponse.getGeneration().getContent()).isNotEmpty();
String expectedResponse = """
```json
{
@@ -56,9 +56,9 @@ public class ClientIT {
"address": "#1 Samuel St."
}
```""";
assertThat(aiResponse.getGeneration().getContent()).isEqualTo(expectedResponse);
assertThat(aiResponse.getGeneration().getProperties()).containsKey("generated_tokens");
assertThat(aiResponse.getGeneration().getProperties()).containsEntry("generated_tokens", 39);
assertThat(chatResponse.getGeneration().getContent()).isEqualTo(expectedResponse);
assertThat(chatResponse.getGeneration().getProperties()).containsKey("generated_tokens");
assertThat(chatResponse.getGeneration().getProperties()).containsEntry("generated_tokens", 39);
}

View File

@@ -4,9 +4,9 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.client.AiClient;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.Generation;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.prompt.Prompt;
import org.springframework.util.CollectionUtils;
@@ -30,7 +30,7 @@ import java.util.stream.Collectors;
*
* @author nullptr
*/
public class OllamaClient implements AiClient {
public class OllamaClient implements ChatClient {
/** Logger for logging the events and messages. */
private static final Logger log = LoggerFactory.getLogger(OllamaClient.class);
@@ -72,7 +72,7 @@ public class OllamaClient implements AiClient {
}
@Override
public AiResponse generate(Prompt prompt) {
public ChatResponse generate(Prompt prompt) {
validatePrompt(prompt);
HttpRequest request = buildHttpRequest(prompt);
@@ -196,11 +196,11 @@ public class OllamaClient implements AiClient {
}
/**
* Converts the list of OllamaGenerateResult into a structured AiResponse.
* Converts the list of OllamaGenerateResult into a structured ChatResponse.
* @param results List of OllamaGenerateResult.
* @return Formulated AiResponse.
* @return Formulated ChatResponse.
*/
protected AiResponse getAiResponse(List<OllamaGenerateResult> results) {
protected ChatResponse getAiResponse(List<OllamaGenerateResult> results) {
var ollamaResponse = results.stream()
.filter(Objects::nonNull)
.filter(it -> it.getResponse() != null && !it.getResponse().isBlank())
@@ -211,7 +211,7 @@ public class OllamaClient implements AiClient {
var generation = new Generation(ollamaResponse);
// TODO investigate mapping of additional metadata/runtime info to the response.
return new AiResponse(Collections.singletonList(generation));
return new ChatResponse(Collections.singletonList(generation));
}
/**

View File

@@ -3,7 +3,7 @@ package org.springframework.ai.ollama.client;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.prompt.Prompt;
import org.springframework.util.CollectionUtils;
@@ -17,11 +17,11 @@ public class OllamaClientTests {
OllamaClient ollama2 = getOllamaClient();
Prompt prompt = new Prompt("Hello");
AiResponse aiResponse = ollama2.generate(prompt);
Assertions.assertNotNull(aiResponse);
Assertions.assertFalse(CollectionUtils.isEmpty(aiResponse.getGenerations()));
Assertions.assertNotNull(aiResponse.getGeneration());
Assertions.assertNotNull(aiResponse.getGeneration().getContent());
ChatResponse chatResponse = ollama2.generate(prompt);
Assertions.assertNotNull(chatResponse);
Assertions.assertFalse(CollectionUtils.isEmpty(chatResponse.getGenerations()));
Assertions.assertNotNull(chatResponse.getGeneration());
Assertions.assertNotNull(chatResponse.getGeneration().getContent());
}
private static OllamaClient getOllamaClient() {

View File

@@ -22,12 +22,12 @@ import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.ChatResponse;
import reactor.core.publisher.Flux;
import org.springframework.ai.client.AiClient;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.AiStreamClient;
import org.springframework.ai.client.Generation;
import org.springframework.ai.chat.StreamingChatClient;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.metadata.ChoiceMetadata;
import org.springframework.ai.metadata.RateLimit;
import org.springframework.ai.openai.api.OpenAiApi;
@@ -43,7 +43,7 @@ import org.springframework.retry.support.RetryTemplate;
import org.springframework.util.Assert;
/**
* {@link AiClient} implementation for {@literal OpenAI} backed by {@link OpenAiApi}.
* {@link ChatClient} implementation for {@literal OpenAI} backed by {@link OpenAiApi}.
*
* @author Mark Pollack
* @author Christian Tzolov
@@ -51,11 +51,11 @@ import org.springframework.util.Assert;
* @author John Blum
* @author Josh Long
* @author Jemin Huh
* @see org.springframework.ai.client.AiClient
* @see org.springframework.ai.client.AiStreamClient
* @see ChatClient
* @see StreamingChatClient
* @see OpenAiApi
*/
public class OpenAiClient implements AiClient, AiStreamClient {
public class OpenAiClient implements ChatClient, StreamingChatClient {
private Double temperature = 0.7;
@@ -93,7 +93,7 @@ public class OpenAiClient implements AiClient, AiStreamClient {
}
@Override
public AiResponse generate(Prompt prompt) {
public ChatResponse generate(Prompt prompt) {
return this.retryTemplate.execute(ctx -> {
List<Message> messages = prompt.getMessages();
@@ -110,7 +110,7 @@ public class OpenAiClient implements AiClient, AiStreamClient {
var chatCompletion = completionEntity.getBody();
if (chatCompletion == null) {
logger.warn("No chat completion returned for request: {}", chatCompletionMessages);
return new AiResponse(List.of());
return new ChatResponse(List.of());
}
RateLimit rateLimits = OpenAiResponseHeaderExtractor.extractAiResponseHeaders(completionEntity);
@@ -120,13 +120,13 @@ public class OpenAiClient implements AiClient, AiStreamClient {
.withChoiceMetadata(ChoiceMetadata.from(choice.finishReason().name(), null));
}).toList();
return new AiResponse(generations,
return new ChatResponse(generations,
OpenAiGenerationMetadata.from(completionEntity.getBody()).withRateLimit(rateLimits));
});
}
@Override
public Flux<AiResponse> generateStream(Prompt prompt) {
public Flux<ChatResponse> generateStream(Prompt prompt) {
return this.retryTemplate.execute(ctx -> {
List<Message> messages = prompt.getMessages();
@@ -144,7 +144,7 @@ public class OpenAiClient implements AiClient, AiStreamClient {
ConcurrentHashMap<String, String> roleMap = new ConcurrentHashMap<>();
// An alternative implementation that returns Flux<Generation> instead of
// Flux<AiResponse>.
// Flux<ChatResponse>.
// Flux<Generation> generationFlux = completionChunks.map(chunk -> {
// String chunkId = chunk.id();
// return chunk.choices().stream()
@@ -172,7 +172,7 @@ public class OpenAiClient implements AiClient, AiStreamClient {
}
return generation;
}).toList();
return new AiResponse(generations);
return new ChatResponse(generations);
});
});
}

View File

@@ -9,7 +9,7 @@ import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.document.Document;
import org.springframework.ai.openai.OpenAiTestConfiguration;
import org.springframework.ai.openai.client.OpenAiClient;
@@ -96,7 +96,7 @@ public class AcmeIT extends AbstractIT {
logger.info("Asking AI model to reply to question.");
Prompt prompt = new Prompt(List.of(systemMessage, userMessage));
logger.info("AI responded.");
AiResponse response = aiClient.generate(prompt);
ChatResponse response = aiClient.generate(prompt);
evaluateQuestionAndAnswer(userQuery, response, true);
}

View File

@@ -8,8 +8,8 @@ import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.Generation;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.openai.OpenAiTestConfiguration;
import org.springframework.ai.openai.testutils.AbstractIT;
import org.springframework.ai.parser.BeanOutputParser;
@@ -41,7 +41,7 @@ class OpenAiClientIT 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));
AiResponse response = openAiClient.generate(prompt);
ChatResponse response = openAiClient.generate(prompt);
assertThat(response.getGenerations()).hasSize(1);
assertThat(response.getGenerations().get(0).getContent()).contains("Blackbeard");
// needs fine tuning... evaluateQuestionAndAnswer(request, response, false);
@@ -139,11 +139,11 @@ class OpenAiClientIT extends AbstractIT {
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
Prompt prompt = new Prompt(promptTemplate.createMessage());
String generationTextFromStream = openAiStreamClient.generateStream(prompt)
String generationTextFromStream = openStreamingChatClient.generateStream(prompt)
.collectList()
.block()
.stream()
.map(AiResponse::getGenerations)
.map(ChatResponse::getGenerations)
.flatMap(List::stream)
.map(Generation::getContent)
.collect(Collectors.joining());

View File

@@ -21,7 +21,7 @@ import java.time.Duration;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.metadata.ChoiceMetadata;
import org.springframework.ai.metadata.GenerationMetadata;
import org.springframework.ai.metadata.PromptMetadata;
@@ -74,7 +74,7 @@ public class OpenAiClientWithGenerationMetadata2Tests {
Prompt prompt = new Prompt("Reach for the sky.");
AiResponse response = this.openAiClient.generate(prompt);
ChatResponse response = this.openAiClient.generate(prompt);
assertThat(response).isNotNull();

View File

@@ -6,9 +6,9 @@ import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.client.AiClient;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.AiStreamClient;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.StreamingChatClient;
import org.springframework.ai.prompt.Prompt;
import org.springframework.ai.prompt.PromptTemplate;
import org.springframework.ai.prompt.messages.Message;
@@ -25,10 +25,10 @@ public abstract class AbstractIT {
private static final Logger logger = LoggerFactory.getLogger(AbstractIT.class);
@Autowired
protected AiClient openAiClient;
protected ChatClient openAiClient;
@Autowired
protected AiStreamClient openAiStreamClient;
protected StreamingChatClient openStreamingChatClient;
@Value("classpath:/prompts/eval/qa-evaluator-accurate-answer.st")
protected Resource qaEvaluatorAccurateAnswerResource;
@@ -42,7 +42,7 @@ public abstract class AbstractIT {
@Value("classpath:/prompts/eval/user-evaluator-message.st")
protected Resource userEvaluatorResource;
protected void evaluateQuestionAndAnswer(String question, AiResponse response, boolean factBased) {
protected void evaluateQuestionAndAnswer(String question, ChatResponse response, boolean factBased) {
assertThat(response).isNotNull();
String answer = response.getGeneration().getContent();
logger.info("Question: " + question);

View File

@@ -28,8 +28,8 @@ import software.amazon.awssdk.regions.Region;
import org.springframework.ai.autoconfigure.bedrock.BedrockAwsConnectionProperties;
import org.springframework.ai.bedrock.anthropic.BedrockAnthropicChatClient;
import org.springframework.ai.bedrock.anthropic.api.AnthropicChatBedrockApi.AnthropicChatModel;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.Generation;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.prompt.Prompt;
import org.springframework.ai.prompt.SystemPromptTemplate;
import org.springframework.ai.prompt.messages.Message;
@@ -70,7 +70,7 @@ public class BedrockAnthropicChatAutoConfigurationIT {
public void chatCompletion() {
contextRunner.run(context -> {
BedrockAnthropicChatClient anthropicChatClient = context.getBean(BedrockAnthropicChatClient.class);
AiResponse response = anthropicChatClient.generate(new Prompt(List.of(userMessage, systemMessage)));
ChatResponse response = anthropicChatClient.generate(new Prompt(List.of(userMessage, systemMessage)));
assertThat(response.getGeneration().getContent()).contains("Blackbeard");
});
}
@@ -81,14 +81,14 @@ public class BedrockAnthropicChatAutoConfigurationIT {
BedrockAnthropicChatClient anthropicChatClient = context.getBean(BedrockAnthropicChatClient.class);
Flux<AiResponse> response = anthropicChatClient
Flux<ChatResponse> response = anthropicChatClient
.generateStream(new Prompt(List.of(userMessage, systemMessage)));
List<AiResponse> responses = response.collectList().block();
List<ChatResponse> responses = response.collectList().block();
assertThat(responses.size()).isGreaterThan(2);
String stitchedResponseContent = responses.stream()
.map(AiResponse::getGenerations)
.map(ChatResponse::getGenerations)
.flatMap(List::stream)
.map(Generation::getContent)
.collect(Collectors.joining());

View File

@@ -22,6 +22,7 @@ import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.springframework.ai.chat.ChatResponse;
import reactor.core.publisher.Flux;
import software.amazon.awssdk.regions.Region;
@@ -30,8 +31,7 @@ import org.springframework.ai.bedrock.cohere.BedrockCohereChatClient;
import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi.CohereChatModel;
import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi.CohereChatRequest.ReturnLikelihoods;
import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi.CohereChatRequest.Truncate;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.Generation;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.prompt.Prompt;
import org.springframework.ai.prompt.SystemPromptTemplate;
import org.springframework.ai.prompt.messages.Message;
@@ -72,7 +72,7 @@ public class BedrockCohereChatAutoConfigurationIT {
public void chatCompletion() {
contextRunner.run(context -> {
BedrockCohereChatClient cohereChatClient = context.getBean(BedrockCohereChatClient.class);
AiResponse response = cohereChatClient.generate(new Prompt(List.of(userMessage, systemMessage)));
ChatResponse response = cohereChatClient.generate(new Prompt(List.of(userMessage, systemMessage)));
assertThat(response.getGeneration().getContent()).contains("Blackbeard");
});
}
@@ -83,14 +83,14 @@ public class BedrockCohereChatAutoConfigurationIT {
BedrockCohereChatClient cohereChatClient = context.getBean(BedrockCohereChatClient.class);
Flux<AiResponse> response = cohereChatClient
Flux<ChatResponse> response = cohereChatClient
.generateStream(new Prompt(List.of(userMessage, systemMessage)));
List<AiResponse> responses = response.collectList().block();
List<ChatResponse> responses = response.collectList().block();
assertThat(responses.size()).isGreaterThan(2);
String stitchedResponseContent = responses.stream()
.map(AiResponse::getGenerations)
.map(ChatResponse::getGenerations)
.flatMap(List::stream)
.map(Generation::getContent)
.collect(Collectors.joining());

View File

@@ -22,14 +22,14 @@ import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.springframework.ai.chat.ChatResponse;
import reactor.core.publisher.Flux;
import software.amazon.awssdk.regions.Region;
import org.springframework.ai.autoconfigure.bedrock.BedrockAwsConnectionProperties;
import org.springframework.ai.bedrock.llama2.BedrockLlama2ChatClient;
import org.springframework.ai.bedrock.llama2.api.Llama2ChatBedrockApi.Llama2ChatCompletionModel;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.Generation;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.prompt.Prompt;
import org.springframework.ai.prompt.SystemPromptTemplate;
import org.springframework.ai.prompt.messages.Message;
@@ -70,7 +70,7 @@ public class BedrockLlama2ChatAutoConfigurationIT {
public void chatCompletion() {
contextRunner.run(context -> {
BedrockLlama2ChatClient llama2ChatClient = context.getBean(BedrockLlama2ChatClient.class);
AiResponse response = llama2ChatClient.generate(new Prompt(List.of(userMessage, systemMessage)));
ChatResponse response = llama2ChatClient.generate(new Prompt(List.of(userMessage, systemMessage)));
assertThat(response.getGeneration().getContent()).contains("Blackbeard");
});
}
@@ -81,14 +81,14 @@ public class BedrockLlama2ChatAutoConfigurationIT {
BedrockLlama2ChatClient llama2ChatClient = context.getBean(BedrockLlama2ChatClient.class);
Flux<AiResponse> response = llama2ChatClient
Flux<ChatResponse> response = llama2ChatClient
.generateStream(new Prompt(List.of(userMessage, systemMessage)));
List<AiResponse> responses = response.collectList().block();
List<ChatResponse> responses = response.collectList().block();
assertThat(responses.size()).isGreaterThan(2);
String stitchedResponseContent = responses.stream()
.map(AiResponse::getGenerations)
.map(ChatResponse::getGenerations)
.flatMap(List::stream)
.map(Generation::getContent)
.collect(Collectors.joining());

View File

@@ -22,14 +22,14 @@ import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.springframework.ai.chat.ChatResponse;
import reactor.core.publisher.Flux;
import software.amazon.awssdk.regions.Region;
import org.springframework.ai.autoconfigure.bedrock.BedrockAwsConnectionProperties;
import org.springframework.ai.bedrock.titan.BedrockTitanChatClient;
import org.springframework.ai.bedrock.titan.api.TitanChatBedrockApi.TitanChatModel;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.Generation;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.prompt.Prompt;
import org.springframework.ai.prompt.SystemPromptTemplate;
import org.springframework.ai.prompt.messages.Message;
@@ -70,7 +70,7 @@ public class BedrockTitanChatAutoConfigurationIT {
public void chatCompletion() {
contextRunner.run(context -> {
BedrockTitanChatClient chatClient = context.getBean(BedrockTitanChatClient.class);
AiResponse response = chatClient.generate(new Prompt(List.of(userMessage, systemMessage)));
ChatResponse response = chatClient.generate(new Prompt(List.of(userMessage, systemMessage)));
assertThat(response.getGeneration().getContent()).contains("Blackbeard");
});
}
@@ -81,13 +81,13 @@ public class BedrockTitanChatAutoConfigurationIT {
BedrockTitanChatClient chatClient = context.getBean(BedrockTitanChatClient.class);
Flux<AiResponse> response = chatClient.generateStream(new Prompt(List.of(userMessage, systemMessage)));
Flux<ChatResponse> response = chatClient.generateStream(new Prompt(List.of(userMessage, systemMessage)));
List<AiResponse> responses = response.collectList().block();
List<ChatResponse> responses = response.collectList().block();
assertThat(responses.size()).isGreaterThan(1);
String stitchedResponseContent = responses.stream()
.map(AiResponse::getGenerations)
.map(ChatResponse::getGenerations)
.flatMap(List::stream)
.map(Generation::getContent)
.collect(Collectors.joining());

View File

@@ -18,8 +18,8 @@ package org.springframework.ai.evaluation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.client.AiClient;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.prompt.Prompt;
import org.springframework.ai.prompt.PromptTemplate;
import org.springframework.ai.prompt.messages.Message;
@@ -39,7 +39,7 @@ public class BasicEvaluationTest {
private static final Logger logger = LoggerFactory.getLogger(BasicEvaluationTest.class);
@Autowired
protected AiClient openAiClient;
protected ChatClient openAiClient;
@Value("classpath:/prompts/spring/test/evaluation/qa-evaluator-accurate-answer.st")
protected Resource qaEvaluatorAccurateAnswerResource;
@@ -53,7 +53,7 @@ public class BasicEvaluationTest {
@Value("classpath:/prompts/spring/test/evaluation/user-evaluator-message.st")
protected Resource userEvaluatorResource;
protected void evaluateQuestionAndAnswer(String question, AiResponse response, boolean factBased) {
protected void evaluateQuestionAndAnswer(String question, ChatResponse response, boolean factBased) {
assertThat(response).isNotNull();
String answer = response.getGeneration().getContent();
logger.info("Question: " + question);

View File

@@ -19,9 +19,9 @@ package org.springframework.ai.vertex.generation;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.ai.client.AiClient;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.Generation;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.prompt.Prompt;
import org.springframework.ai.prompt.messages.MessageType;
import org.springframework.ai.vertex.api.VertexAiApi;
@@ -34,7 +34,7 @@ import org.springframework.util.CollectionUtils;
/**
* @author Christian Tzolov
*/
public class VertexAiChatClient implements AiClient {
public class VertexAiChatClient implements ChatClient {
private final VertexAiApi vertexAiApi;
@@ -71,7 +71,7 @@ public class VertexAiChatClient implements AiClient {
}
@Override
public AiResponse generate(Prompt prompt) {
public ChatResponse generate(Prompt prompt) {
String vertexContext = prompt.getMessages()
.stream()
@@ -99,7 +99,7 @@ public class VertexAiChatClient implements AiClient {
.map(vmsg -> new Generation(vmsg.content()))
.toList();
return new AiResponse(generations);
return new ChatResponse(generations);
}
}

View File

@@ -7,8 +7,8 @@ import java.util.Map;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.Generation;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.parser.BeanOutputParser;
import org.springframework.ai.parser.ListOutputParser;
import org.springframework.ai.parser.MapOutputParser;
@@ -47,7 +47,7 @@ class VertexAiChatGenerationClientIT {
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);
Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice));
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
AiResponse response = client.generate(prompt);
ChatResponse response = client.generate(prompt);
assertThat(response.getGeneration().getContent()).contains("Bartholomew");
}