Restructure OpenAI and AzureOpenAI ChatClientITs
- Use List<ChatResponse> to better examine the full response
This commit is contained in:
@@ -20,13 +20,18 @@ import static com.azure.core.http.policy.HttpLogDetailLevel.BODY_AND_HEADERS;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
|
||||
|
||||
import org.springframework.ai.chat.client.ChatClient;
|
||||
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.converter.BeanOutputConverter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -35,18 +40,77 @@ import com.azure.ai.openai.OpenAIClientBuilder;
|
||||
import com.azure.ai.openai.OpenAIServiceVersion;
|
||||
import com.azure.core.credential.AzureKeyCredential;
|
||||
import com.azure.core.http.policy.HttpLogOptions;
|
||||
import org.springframework.core.io.Resource;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Soby Chacko
|
||||
*/
|
||||
@SpringBootTest(classes = AzureOpenAiChatClientTest.TestConfiguration.class)
|
||||
@SpringBootTest(classes = AzureOpenAiChatClientIT.TestConfiguration.class)
|
||||
@EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_API_KEY", matches = ".+")
|
||||
@EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_ENDPOINT", matches = ".+")
|
||||
public class AzureOpenAiChatClientTest {
|
||||
public class AzureOpenAiChatClientIT {
|
||||
|
||||
@Autowired
|
||||
private ChatClient chatClient;
|
||||
|
||||
@Value("classpath:/prompts/system-message.st")
|
||||
private Resource systemTextResource;
|
||||
|
||||
record ActorsFilms(String actor, List<String> movies) {
|
||||
}
|
||||
|
||||
@Test
|
||||
void call() {
|
||||
|
||||
// @formatter:off
|
||||
ChatResponse response = chatClient.prompt()
|
||||
.advisors(new SimpleLoggerAdvisor())
|
||||
.system(s -> s.text(systemTextResource)
|
||||
.param("name", "Bob")
|
||||
.param("voice", "pirate"))
|
||||
.user("Tell me about 3 famous pirates from the Golden Age of Piracy and what they did")
|
||||
.call()
|
||||
.chatResponse();
|
||||
// @formatter:on
|
||||
|
||||
assertThat(response.getResults()).hasSize(1);
|
||||
assertThat(response.getResults().get(0).getOutput().getContent()).contains("Blackbeard");
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanStreamOutputConverterRecords() {
|
||||
|
||||
BeanOutputConverter<ActorsFilms> outputConverter = new BeanOutputConverter<>(ActorsFilms.class);
|
||||
|
||||
// @formatter:off
|
||||
Flux<ChatResponse> chatResponse = chatClient
|
||||
.prompt()
|
||||
.advisors(new SimpleLoggerAdvisor())
|
||||
.user(u -> u
|
||||
.text("Generate the filmography of 5 movies for Tom Hanks. " + System.lineSeparator()
|
||||
+ "{format}")
|
||||
.param("format", outputConverter.getFormat()))
|
||||
.stream()
|
||||
.chatResponse();
|
||||
|
||||
List<ChatResponse> chatResponses = chatResponse.collectList()
|
||||
.block()
|
||||
.stream()
|
||||
.toList();
|
||||
|
||||
String generationTextFromStream = chatResponses
|
||||
.stream()
|
||||
.map(cr -> cr.getResult().getOutput().getContent())
|
||||
.collect(Collectors.joining());
|
||||
// @formatter:on
|
||||
|
||||
ActorsFilms actorsFilms = outputConverter.convert(generationTextFromStream);
|
||||
|
||||
assertThat(actorsFilms.actor()).isEqualTo("Tom Hanks");
|
||||
assertThat(actorsFilms.movies()).hasSize(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
void streamingAndImperativeResponsesContainIdenticalRelevantResults() {
|
||||
String prompt = "Name all states in the USA and their capitals, add a space followed by a hyphen, then another space between the two. "
|
||||
@@ -0,0 +1,4 @@
|
||||
"You are a helpful AI assistant. Your name is {name}.
|
||||
You are an AI assistant that helps people find information.
|
||||
Your name is {name}
|
||||
You should reply to the user's request with your name and also in the style of a {voice}.
|
||||
@@ -212,19 +212,25 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
BeanOutputConverter<ActorsFilms> outputConverter = new BeanOutputConverter<>(ActorsFilms.class);
|
||||
|
||||
// @formatter:off
|
||||
Flux<String> chatResponse = ChatClient.create(chatModel)
|
||||
Flux<ChatResponse> chatResponse = ChatClient.create(chatModel)
|
||||
.prompt()
|
||||
.options(OpenAiChatOptions.builder().withStreamUsage(true).build())
|
||||
.advisors(new SimpleLoggerAdvisor())
|
||||
.user(u -> u
|
||||
.text("Generate the filmography of 5 movies for Tom Hanks. " + System.lineSeparator()
|
||||
+ "{format}")
|
||||
.param("format", outputConverter.getFormat()))
|
||||
.stream()
|
||||
.content();
|
||||
.chatResponse();
|
||||
|
||||
String generationTextFromStream = chatResponse.collectList()
|
||||
List<ChatResponse> chatResponses = chatResponse.collectList()
|
||||
.block()
|
||||
.stream()
|
||||
.toList();
|
||||
|
||||
String generationTextFromStream = chatResponses
|
||||
.stream()
|
||||
.map(cr -> cr.getResult().getOutput().getContent())
|
||||
.collect(Collectors.joining());
|
||||
// @formatter:on
|
||||
|
||||
|
||||
@@ -34,9 +34,12 @@ import org.springframework.ai.chat.memory.InMemoryChatMemory;
|
||||
import org.springframework.ai.chat.messages.AssistantMessage;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.MessageType;
|
||||
import org.springframework.ai.chat.metadata.ChatResponseMetadata;
|
||||
import org.springframework.ai.chat.metadata.EmptyUsage;
|
||||
import org.springframework.ai.chat.model.ChatModel;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.model.Generation;
|
||||
import org.springframework.ai.chat.model.MessageAggregator;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
@@ -60,9 +63,19 @@ public class ChatClientAdvisorTests {
|
||||
@Test
|
||||
public void promptChatMemory() {
|
||||
|
||||
var builder = ChatResponseMetadata.builder()
|
||||
.withId("124")
|
||||
.withUsage(new MessageAggregator.DefaultUsage(1, 2, 3))
|
||||
.withModel("gpt4o")
|
||||
.withKeyValue("created", 0L)
|
||||
.withKeyValue("system-fingerprint", "john doe");
|
||||
ChatResponseMetadata chatResponseMetadata = builder.build();
|
||||
|
||||
when(chatModel.call(promptCaptor.capture()))
|
||||
.thenReturn(new ChatResponse(List.of(new Generation(new AssistantMessage("Hello John")))))
|
||||
.thenReturn(new ChatResponse(List.of(new Generation(new AssistantMessage("Your name is John")))));
|
||||
.thenReturn(
|
||||
new ChatResponse(List.of(new Generation(new AssistantMessage("Hello John"))), chatResponseMetadata))
|
||||
.thenReturn(new ChatResponse(List.of(new Generation(new AssistantMessage("Your name is John"))),
|
||||
chatResponseMetadata));
|
||||
|
||||
ChatMemory chatMemory = new InMemoryChatMemory();
|
||||
|
||||
@@ -71,8 +84,9 @@ public class ChatClientAdvisorTests {
|
||||
.defaultAdvisors(new PromptChatMemoryAdvisor(chatMemory))
|
||||
.build();
|
||||
|
||||
var content = chatClient.prompt().user("my name is John").call().content();
|
||||
ChatResponse chatResponse = chatClient.prompt().user("my name is John").call().chatResponse();
|
||||
|
||||
String content = chatResponse.getResult().getOutput().getContent();
|
||||
assertThat(content).isEqualTo("Hello John");
|
||||
|
||||
Message systemMessage = promptCaptor.getValue().getInstructions().get(0);
|
||||
|
||||
Reference in New Issue
Block a user