diff --git a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureOpenAiChatClientIT.java b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureOpenAiChatClientIT.java index a7c25f8b2db..2cb952b05f9 100644 --- a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureOpenAiChatClientIT.java +++ b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureOpenAiChatClientIT.java @@ -66,7 +66,7 @@ void roleTest() { You should reply to the user's request with your name and also in the style of a {voice}. """).createMessage(Map.of("name", "Bob", "voice", "pirate")); - UserMessage userMessage = new UserMessage("Generate the names of 5 famous pirates."); + UserMessage userMessage = UserMessage.builder().withContent("Generate the names of 5 famous pirates.").build(); Prompt prompt = new Prompt(List.of(userMessage, systemMessage)); ChatResponse response = chatClient.call(prompt); diff --git a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/AzureOpenAiChatClientFunctionCallIT.java b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/AzureOpenAiChatClientFunctionCallIT.java index db86ee501ba..04f5936fb18 100644 --- a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/AzureOpenAiChatClientFunctionCallIT.java +++ b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/AzureOpenAiChatClientFunctionCallIT.java @@ -53,7 +53,9 @@ class AzureOpenAiChatClientFunctionCallIT { @Test void functionCallTest() { - UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, in Tokyo, and in Paris?"); + UserMessage userMessage = UserMessage.builder() + .withContent("What's the weather like in San Francisco, in Tokyo, and in Paris?") + .build(); List messages = new ArrayList<>(List.of(userMessage)); diff --git a/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/anthropic/BedrockAnthropicChatClientIT.java b/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/anthropic/BedrockAnthropicChatClientIT.java index 23d853aea20..b15889a6336 100644 --- a/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/anthropic/BedrockAnthropicChatClientIT.java +++ b/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/anthropic/BedrockAnthropicChatClientIT.java @@ -66,8 +66,9 @@ class BedrockAnthropicChatClientIT { @Test void roleTest() { - UserMessage userMessage = new UserMessage( - "Tell me about 3 famous pirates from the Golden Age of Piracy and why they did."); + UserMessage userMessage = UserMessage.builder() + .withContent("Tell me about 3 famous pirates from the Golden Age of Piracy and why they did.") + .build(); SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource); Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", "Bob", "voice", "pirate")); diff --git a/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/cohere/BedrockCohereChatClientIT.java b/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/cohere/BedrockCohereChatClientIT.java index 12512af50cf..4b2cdccaf18 100644 --- a/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/cohere/BedrockCohereChatClientIT.java +++ b/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/cohere/BedrockCohereChatClientIT.java @@ -65,7 +65,7 @@ void roleTest() { String request = "Tell me about 3 famous pirates from the Golden Age of Piracy and why they did."; String name = "Bob"; String voice = "pirate"; - UserMessage userMessage = new UserMessage(request); + UserMessage userMessage = UserMessage.builder().withContent(request).build(); SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource); Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice)); Prompt prompt = new Prompt(List.of(userMessage, systemMessage)); diff --git a/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/llama2/BedrockLlama2ChatClientIT.java b/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/llama2/BedrockLlama2ChatClientIT.java index c73a3e9264a..ae16a487d10 100644 --- a/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/llama2/BedrockLlama2ChatClientIT.java +++ b/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/llama2/BedrockLlama2ChatClientIT.java @@ -63,8 +63,9 @@ class BedrockLlama2ChatClientIT { @Test void roleTest() { - UserMessage userMessage = new UserMessage( - "Tell me about 3 famous pirates from the Golden Age of Piracy and why they did."); + UserMessage userMessage = UserMessage.builder() + .withContent("Tell me about 3 famous pirates from the Golden Age of Piracy and why they did.") + .build(); SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource); Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", "Bob", "voice", "pirate")); diff --git a/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/titan/BedrockTitanChatClientIT.java b/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/titan/BedrockTitanChatClientIT.java index 51fae542c0e..a69287c0a04 100644 --- a/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/titan/BedrockTitanChatClientIT.java +++ b/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/titan/BedrockTitanChatClientIT.java @@ -66,7 +66,7 @@ void roleTest() { String request = "Tell me about 3 famous pirates from the Golden Age of Piracy and why they did."; String name = "Bob"; String voice = "pirate"; - UserMessage userMessage = new UserMessage(request); + UserMessage userMessage = UserMessage.builder().withContent(request).build(); SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource); Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice)); Prompt prompt = new Prompt(List.of(userMessage, systemMessage)); diff --git a/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatClientIT.java b/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatClientIT.java index 00cbf697394..b3aba30022f 100644 --- a/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatClientIT.java +++ b/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatClientIT.java @@ -83,8 +83,9 @@ class MistralAiChatClientIT { @Test void roleTest() { - UserMessage userMessage = new UserMessage( - "Tell me about 3 famous pirates from the Golden Age of Piracy and why they did."); + UserMessage userMessage = UserMessage.builder() + .withContent("Tell me about 3 famous pirates from the Golden Age of Piracy and why they did.") + .build(); SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource); Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", "Bob", "voice", "pirate")); // NOTE: Mistral expects the system message to be before the user message or will @@ -188,7 +189,9 @@ void beanStreamOutputParserRecords() { @Test void functionCallTest() { - UserMessage userMessage = new UserMessage("What's the weather like in San Francisco?"); + UserMessage userMessage = UserMessage.builder() + .withContent("What's the weather like in San Francisco?") + .build(); List messages = new ArrayList<>(List.of(userMessage)); @@ -211,7 +214,7 @@ void functionCallTest() { @Test void streamFunctionCallTest() { - UserMessage userMessage = new UserMessage("What's the weather like in Tokyo, Japan?"); + UserMessage userMessage = UserMessage.builder().withContent("What's the weather like in Tokyo, Japan?").build(); List messages = new ArrayList<>(List.of(userMessage)); diff --git a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatClientIT.java b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatClientIT.java index 72941eff8a9..79c3679f3f1 100644 --- a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatClientIT.java +++ b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatClientIT.java @@ -87,7 +87,9 @@ void roleTest() { You should reply to the user's request with your name and also in the style of a {voice}. """).createMessage(Map.of("name", "Bob", "voice", "pirate")); - UserMessage userMessage = new UserMessage("Tell me about 5 famous pirates from the Golden Age of Piracy."); + UserMessage userMessage = UserMessage.builder() + .withContent("Tell me about 5 famous pirates from the Golden Age of Piracy.") + .build(); // portable/generic options var portableOptions = ChatOptionsBuilder.builder().withTemperature(0.7f).build(); diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/acme/AcmeIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/acme/AcmeIT.java index e38595b90e2..9fca967ecfd 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/acme/AcmeIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/acme/AcmeIT.java @@ -101,7 +101,7 @@ void acmeChain() { // be relevant. Message systemMessage = getSystemMessage(similarDocuments); - UserMessage userMessage = new UserMessage(userQuery); + UserMessage userMessage = UserMessage.builder().withContent(userQuery).build(); // Create the prompt ad-hoc for now, need to put in system message and user // message via ChatPromptTemplate or some other message building mechanic; diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientIT.java index b23af540b34..d8ab06e7621 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientIT.java @@ -62,8 +62,9 @@ class OpenAiChatClientIT extends AbstractIT { @Test void roleTest() { - UserMessage userMessage = new UserMessage( - "Tell me about 3 famous pirates from the Golden Age of Piracy and why they did."); + UserMessage userMessage = UserMessage.builder() + .withContent("Tell me about 3 famous pirates from the Golden Age of Piracy and why they did.") + .build(); SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource); Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", "Bob", "voice", "pirate")); Prompt prompt = new Prompt(List.of(userMessage, systemMessage)); @@ -184,7 +185,9 @@ void beanStreamOutputParserRecords() { @Test void functionCallTest() { - UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?"); + UserMessage userMessage = UserMessage.builder() + .withContent("What's the weather like in San Francisco, Tokyo, and Paris?") + .build(); List messages = new ArrayList<>(List.of(userMessage)); @@ -209,7 +212,9 @@ void functionCallTest() { @Test void streamFunctionCallTest() { - UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?"); + UserMessage userMessage = UserMessage.builder() + .withContent("What's the weather like in San Francisco, Tokyo, and Paris?") + .build(); List messages = new ArrayList<>(List.of(userMessage)); diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/testutils/AbstractIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/testutils/AbstractIT.java index b8e1c86a27e..8070323fd33 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/testutils/AbstractIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/testutils/AbstractIT.java @@ -74,17 +74,19 @@ protected void evaluateQuestionAndAnswer(String question, ChatResponse response, Map.of("question", question, "answer", answer)); SystemMessage systemMessage; if (factBased) { - systemMessage = new SystemMessage(qaEvalutaorFactBasedAnswerResource); + systemMessage = SystemMessage.builder().withResource(qaEvalutaorFactBasedAnswerResource).build(); } else { - systemMessage = new SystemMessage(qaEvaluatorAccurateAnswerResource); + systemMessage = SystemMessage.builder().withResource(qaEvaluatorAccurateAnswerResource).build(); } Message userMessage = userPromptTemplate.createMessage(); Prompt prompt = new Prompt(List.of(userMessage, systemMessage)); String yesOrNo = openAiChatClient.call(prompt).getResult().getOutput().getContent(); logger.info("Is Answer related to question: " + yesOrNo); if (yesOrNo.equalsIgnoreCase("no")) { - SystemMessage notRelatedSystemMessage = new SystemMessage(qaEvaluatorNotRelatedResource); + SystemMessage notRelatedSystemMessage = SystemMessage.builder() + .withResource(qaEvaluatorNotRelatedResource) + .build(); prompt = new Prompt(List.of(userMessage, notRelatedSystemMessage)); String reasonForFailure = openAiChatClient.call(prompt).getResult().getOutput().getContent(); fail(reasonForFailure); diff --git a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClientIT.java b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClientIT.java index 8860dc9f9bb..6e5655473f7 100644 --- a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClientIT.java +++ b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClientIT.java @@ -66,7 +66,7 @@ void roleTest() { String request = "Tell me about 3 famous pirates from the Golden Age of Piracy and why they did."; String name = "Bob"; String voice = "pirate"; - UserMessage userMessage = new UserMessage(request); + UserMessage userMessage = UserMessage.builder().withContent(request).build(); SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource); Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice)); Prompt prompt = new Prompt(List.of(userMessage, systemMessage)); @@ -188,8 +188,10 @@ void multiModalityTest() throws IOException { byte[] data = new ClassPathResource("/vertex.test.png").getContentAsByteArray(); - var userMessage = new UserMessage("Explain what do you see o this picture?", - List.of(new Media(MimeTypeUtils.IMAGE_PNG, data))); + var userMessage = UserMessage.builder() + .withContent("Explain what do you see o this picture?") + .withMedia(List.of(new Media(MimeTypeUtils.IMAGE_PNG, data))) + .build(); ChatResponse response = client.call(new Prompt(List.of(userMessage))); diff --git a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/function/VertexAiGeminiChatClientFunctionCallingIT.java b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/function/VertexAiGeminiChatClientFunctionCallingIT.java index 5d7083d790e..6d5825e8558 100644 --- a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/function/VertexAiGeminiChatClientFunctionCallingIT.java +++ b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/function/VertexAiGeminiChatClientFunctionCallingIT.java @@ -68,8 +68,10 @@ public void afterEach() { @Test public void functionCallExplicitOpenApiSchema() { - UserMessage userMessage = new UserMessage( - "What's the weather like in San Francisco, in Paris and in Tokyo, Japan? Use Multi-turn function calling. Provide answer for all requested locations."); + UserMessage userMessage = UserMessage.builder() + .withContent( + "What's the weather like in San Francisco, in Paris and in Tokyo, Japan? Use Multi-turn function calling. Provide answer for all requested locations.") + .build(); List messages = new ArrayList<>(List.of(userMessage)); @@ -116,7 +118,7 @@ public void functionCallTestInferredOpenApiSchema() { // UserMessage userMessage = new UserMessage("What's the weather like in San // Francisco, Paris and Tokyo?"); - UserMessage userMessage = new UserMessage("What's the weather like in Paris?"); + UserMessage userMessage = UserMessage.builder().withContent("What's the weather like in Paris?").build(); List messages = new ArrayList<>(List.of(userMessage)); @@ -145,8 +147,10 @@ public void functionCallTestInferredOpenApiSchema() { @Test public void functionCallTestInferredOpenApiSchemaStream() { - UserMessage userMessage = new UserMessage( - "What's the weather like in San Francisco, in Paris and in Tokyo? Use Multi-turn function calling."); + UserMessage userMessage = UserMessage.builder() + .withContent( + "What's the weather like in San Francisco, in Paris and in Tokyo? Use Multi-turn function calling.") + .build(); List messages = new ArrayList<>(List.of(userMessage)); diff --git a/models/spring-ai-vertex-ai-palm2/src/test/java/org/springframework/ai/vertexai/palm2/VertexAiPaLm2ChatGenerationClientIT.java b/models/spring-ai-vertex-ai-palm2/src/test/java/org/springframework/ai/vertexai/palm2/VertexAiPaLm2ChatGenerationClientIT.java index b661332679d..dc5accbe733 100644 --- a/models/spring-ai-vertex-ai-palm2/src/test/java/org/springframework/ai/vertexai/palm2/VertexAiPaLm2ChatGenerationClientIT.java +++ b/models/spring-ai-vertex-ai-palm2/src/test/java/org/springframework/ai/vertexai/palm2/VertexAiPaLm2ChatGenerationClientIT.java @@ -58,7 +58,7 @@ void roleTest() { String request = "Tell me about 3 famous pirates from the Golden Age of Piracy and why they did."; String name = "Bob"; String voice = "pirate"; - UserMessage userMessage = new UserMessage(request); + UserMessage userMessage = UserMessage.builder().withContent(request).build(); SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource); Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice)); Prompt prompt = new Prompt(List.of(userMessage, systemMessage)); diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatClient.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatClient.java index 6925c16eedb..8eb5fd4ab84 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatClient.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatClient.java @@ -23,7 +23,7 @@ public interface ChatClient extends ModelClient { default String call(String message) { - Prompt prompt = new Prompt(new UserMessage(message)); + Prompt prompt = new Prompt(UserMessage.builder().withContent(message).build()); Generation generation = call(prompt).getResult(); return (generation != null) ? generation.getOutput().getContent() : ""; } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/Generation.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/Generation.java index d3c42750b29..80a317e1e32 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/Generation.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/Generation.java @@ -33,11 +33,11 @@ public class Generation implements ModelResult { private ChatGenerationMetadata chatGenerationMetadata; public Generation(String text) { - this.assistantMessage = new AssistantMessage(text); + this.assistantMessage = AssistantMessage.builder().withContent(text).build(); } public Generation(String text, Map properties) { - this.assistantMessage = new AssistantMessage(text, properties); + this.assistantMessage = AssistantMessage.builder().withContent(text).withProperties(properties).build(); } @Override diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/AbstractMessage.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/AbstractMessage.java index 77b54afeae8..bdf0cb0023e 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/AbstractMessage.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/AbstractMessage.java @@ -15,17 +15,10 @@ */ package org.springframework.ai.chat.messages; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; -import org.springframework.core.io.Resource; import org.springframework.util.Assert; -import org.springframework.util.StreamUtils; /** * The AbstractMessage class is an abstract implementation of the Message interface. It @@ -33,6 +26,7 @@ * message type. * * @see Message + * @author youngmon */ public abstract class AbstractMessage implements Message { @@ -40,61 +34,23 @@ public abstract class AbstractMessage implements Message { protected final String textContent; - protected final List mediaData; + protected final List media; /** * Additional options for the message to influence the response, not a generative map. */ protected final Map properties; - protected AbstractMessage(MessageType messageType, String content) { - this(messageType, content, Map.of()); - } - - protected AbstractMessage(MessageType messageType, String content, Map messageProperties) { - Assert.notNull(messageType, "Message type must not be null"); - this.messageType = messageType; - this.textContent = content; - this.mediaData = new ArrayList<>(); - this.properties = messageProperties; - } - - protected AbstractMessage(MessageType messageType, String textContent, List mediaData) { - this(messageType, textContent, mediaData, Map.of()); - } - - protected AbstractMessage(MessageType messageType, String textContent, List mediaData, - Map messageProperties) { + protected AbstractMessage(final MessageType messageType, final String textContent, final List media, + final Map messageProperties) { Assert.notNull(messageType, "Message type must not be null"); Assert.notNull(textContent, "Content must not be null"); - Assert.notNull(mediaData, "media data must not be null"); this.messageType = messageType; this.textContent = textContent; - this.mediaData = new ArrayList<>(mediaData); - this.properties = messageProperties; - } - - protected AbstractMessage(MessageType messageType, Resource resource) { - this(messageType, resource, Collections.emptyMap()); - } - - @SuppressWarnings("null") - protected AbstractMessage(MessageType messageType, Resource resource, Map messageProperties) { - Assert.notNull(messageType, "Message type must not be null"); - Assert.notNull(resource, "Resource must not be null"); - - this.messageType = messageType; + this.media = media; this.properties = messageProperties; - this.mediaData = new ArrayList<>(); - - try (InputStream inputStream = resource.getInputStream()) { - this.textContent = StreamUtils.copyToString(inputStream, Charset.defaultCharset()); - } - catch (IOException ex) { - throw new RuntimeException("Failed to read resource", ex); - } } @Override @@ -104,7 +60,7 @@ public String getContent() { @Override public List getMedia() { - return this.mediaData; + return this.media; } @Override @@ -121,7 +77,7 @@ public MessageType getMessageType() { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((mediaData == null) ? 0 : mediaData.hashCode()); + result = prime * result + ((media == null) ? 0 : media.hashCode()); result = prime * result + ((properties == null) ? 0 : properties.hashCode()); result = prime * result + ((messageType == null) ? 0 : messageType.hashCode()); return result; @@ -136,11 +92,11 @@ public boolean equals(Object obj) { if (getClass() != obj.getClass()) return false; AbstractMessage other = (AbstractMessage) obj; - if (mediaData == null) { - if (other.mediaData != null) + if (media == null) { + if (other.media != null) return false; } - else if (!mediaData.equals(other.mediaData)) + else if (!media.equals(other.media)) return false; if (properties == null) { if (other.properties != null) @@ -148,9 +104,13 @@ else if (!mediaData.equals(other.mediaData)) } else if (!properties.equals(other.properties)) return false; - if (messageType != other.messageType) - return false; - return true; + return messageType == other.messageType; + } + + @Override + public String toString() { + return String.format("%s{content='%s', properties=%s, messageType=%s}", getClass().getSimpleName(), + getContent(), getProperties().toString(), getMessageType()); } } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/AbstractMessageBuilder.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/AbstractMessageBuilder.java new file mode 100644 index 00000000000..5b7f5f6d321 --- /dev/null +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/AbstractMessageBuilder.java @@ -0,0 +1,73 @@ +/* + * Copyright 2023 - 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ai.chat.messages; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This abstract builder class provides a generic way to construct message objects of any + * type. It follows the builder pattern to facilitate the step-by-step construction of + * message objects while keeping the constructed object immutable. + * + * @param the type of the concrete builder that extends this abstract builder, used to + * enable method chaining with the correct return type. + * @since 0.8.1 + * @author youngmon + */ +public abstract class AbstractMessageBuilder> { + + protected MessageType messageType; + + protected String textContent; + + protected List media = new ArrayList<>(); + + /** + * Additional options for the message to influence the response, not a generative map. + */ + protected Map properties = new HashMap<>(); + + protected AbstractMessageBuilder() { + } + + protected AbstractMessageBuilder(final MessageType type) { + this.messageType = type; + } + + protected T withMessageType(final MessageType type) { + this.messageType = type; + return (T) this; + } + + protected T withContent(final String content) { + this.textContent = content; + return (T) this; + } + + protected T withProperties(final Map properties) { + this.properties.putAll(properties); + return (T) this; + } + + protected T withMedia(final List media) { + this.media.addAll(media); + return (T) this; + } + +} diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/AssistantMessage.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/AssistantMessage.java index c5f6831abbc..f62318e5c22 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/AssistantMessage.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/AssistantMessage.java @@ -15,6 +15,7 @@ */ package org.springframework.ai.chat.messages; +import java.util.List; import java.util.Map; /** @@ -25,18 +26,52 @@ */ public class AssistantMessage extends AbstractMessage { - public AssistantMessage(String content) { - super(MessageType.ASSISTANT, content); + private AssistantMessage(final MessageType type, final String textContent, final List media, + final Map properties) { + super(type, textContent, media, properties); } - public AssistantMessage(String content, Map properties) { - super(MessageType.ASSISTANT, content, properties); + /** + * Creates a new {@link AssistantMessageBuilder} instance. + * @return A new instance of AssistantMessageBuilder. + */ + public static AssistantMessageBuilder builder() { + return new AssistantMessageBuilder(); } - @Override - public String toString() { - return "AssistantMessage{" + "content='" + getContent() + '\'' + ", properties=" + properties + ", messageType=" - + messageType + '}'; + /** + * Initializes a new {@link AssistantMessageBuilder} with settings from an existing + * {@link AssistantMessage} object. + * @param message The AssistantMessage object whose settings are to be used. + * @return A AssistantMessageBuilder instance initialized with the provided + * AssistantMessage settings. + */ + public static AssistantMessageBuilder builder(final AssistantMessage message) { + return builder().withContent(message.getContent()).withProperties(message.getProperties()); + } + + /** + * Builder for {@link AssistantMessage}. This builder creates assistant message + * object. + */ + public static class AssistantMessageBuilder extends AbstractMessageBuilder { + + private AssistantMessageBuilder() { + super(MessageType.ASSISTANT); + } + + public AssistantMessageBuilder withContent(final String content) { + return super.withContent(content); + } + + public AssistantMessageBuilder withProperties(final Map properties) { + return super.withProperties(properties); + } + + public AssistantMessage build() { + return new AssistantMessage(this.messageType, this.textContent, this.media, this.properties); + } + } } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/ChatMessage.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/ChatMessage.java index ea4803a5943..d212082ff34 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/ChatMessage.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/ChatMessage.java @@ -15,6 +15,7 @@ */ package org.springframework.ai.chat.messages; +import java.util.List; import java.util.Map; /** @@ -23,20 +24,95 @@ */ public class ChatMessage extends AbstractMessage { - public ChatMessage(String role, String content) { - super(MessageType.valueOf(role), content); + private ChatMessage(final MessageType type, final String textContent, final List media, + final Map properties) { + super(type, textContent, media, properties); } - public ChatMessage(String role, String content, Map properties) { - super(MessageType.valueOf(role), content, properties); + /** + * Creates a new {@link ChatMessageBuilder} instance. + * @return A new instance of ChatMessageBuilder. + */ + public static ChatMessageBuilder builder() { + return new ChatMessageBuilder(); } - public ChatMessage(MessageType messageType, String content) { - super(messageType, content); + /** + * Initializes a new {@link ChatMessageBuilder} with settings from an existing + * {@link ChatMessage} object. + * @param message The ChatMessage object whose settings are to be used. + * @return A ChatMessageBuilder instance initialized with the provided ChatMessage + * settings. + */ + public static ChatMessageBuilder builder(final ChatMessage message) { + return builder().withMessageType(message.getMessageType()) + .withContent(message.getContent()) + .withProperties(message.getProperties()); } - public ChatMessage(MessageType messageType, String content, Map properties) { - super(messageType, content, properties); + /** + * Initializes a {@link ChatMessageBuilder} with a message type determined by the + * provided role. This method is a convenient shortcut to start building a + * {@link ChatMessage} when the role is known. It internally calls {@link #builder()} + * to create a new builder instance and sets the message type by converting the role + * to a {@link MessageType} using {@link MessageType#valueOf(String)}. + * + * See {@link ChatMessageBuilder#withRole(String)} for more details on role handling. + * @param role a string that should match one of the {@link MessageType} enum + * constants, indicating the desired message type + * @return a {@link ChatMessageBuilder} instance with the initial message type set + * @throws IllegalArgumentException if the role does not correspond to any + * {@link MessageType} enum constants + */ + public static ChatMessageBuilder builder(final String role) { + return builder().withMessageType(MessageType.valueOf(role)); + } + + /** + * Builder for {@link ChatMessage}. This builder creates chat message object. + */ + public static class ChatMessageBuilder extends AbstractMessageBuilder { + + private ChatMessageBuilder() { + super(); + } + + /** + * Sets the message type based on the provided role. The role argument is expected + * to correspond to a valid enum value of {@link MessageType}. This method allows + * for the dynamic assignment of the message type, facilitating different + * behaviors or processing logic based on the message's role in the conversation. + * + * Note: This method internally converts the role string to a {@link MessageType} + * enum. It's important to ensure that the provided role string exactly matches + * one of the enum's constants. If the match is unsuccessful, an + * IllegalArgumentException will be thrown by {@link MessageType#valueOf(String)}. + * @param role a string representation of the message type, corresponding to the + * {@link MessageType} enum + * @return this builder instance to allow for method chaining + * @throws IllegalArgumentException if the provided role does not match any + * {@link MessageType} enum constants + */ + public ChatMessageBuilder withRole(final String role) { + return super.withMessageType(MessageType.valueOf(role)); + } + + public ChatMessageBuilder withMessageType(final MessageType type) { + return super.withMessageType(type); + } + + public ChatMessageBuilder withContent(final String content) { + return super.withContent(content); + } + + public ChatMessageBuilder withProperties(final Map properties) { + return super.withProperties(properties); + } + + public ChatMessage build() { + return new ChatMessage(this.messageType, this.textContent, this.media, this.properties); + } + } } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/FunctionMessage.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/FunctionMessage.java index 06485ac57d6..be7e8cab3b6 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/FunctionMessage.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/FunctionMessage.java @@ -15,6 +15,7 @@ */ package org.springframework.ai.chat.messages; +import java.util.List; import java.util.Map; /** @@ -23,18 +24,51 @@ */ public class FunctionMessage extends AbstractMessage { - public FunctionMessage(String content) { - super(MessageType.FUNCTION, content); + private FunctionMessage(final MessageType type, final String textContent, final List media, + final Map properties) { + super(type, textContent, media, properties); } - public FunctionMessage(String content, Map properties) { - super(MessageType.FUNCTION, content, properties); + /** + * Creates a new {@link FunctionMessageBuilder} instance. + * @return A new instance of FunctionMessageBuilder. + */ + public static FunctionMessageBuilder builder() { + return new FunctionMessageBuilder(); } - @Override - public String toString() { - return "FunctionMessage{" + "content='" + getContent() + '\'' + ", properties=" + properties + ", messageType=" - + messageType + '}'; + /** + * Initializes a new {@link FunctionMessageBuilder} with settings from an existing + * {@link FunctionMessage} object. + * @param message The FunctionMessage object whose settings are to be used. + * @return A FunctionMessageBuilder instance initialized with the provided + * FunctionMessage settings. + */ + public static FunctionMessageBuilder builder(FunctionMessage message) { + return builder().withContent(message.getContent()).withProperties(message.getProperties()); + } + + /** + * Builder for {@link FunctionMessage}. This builder creates function message object. + */ + public static class FunctionMessageBuilder extends AbstractMessageBuilder { + + private FunctionMessageBuilder() { + super(MessageType.FUNCTION); + } + + public FunctionMessageBuilder withContent(final String content) { + return super.withContent(content); + } + + public FunctionMessageBuilder withProperties(final Map properties) { + return super.withProperties(properties); + } + + public FunctionMessage build() { + return new FunctionMessage(this.messageType, this.textContent, this.media, this.properties); + } + } } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/SystemMessage.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/SystemMessage.java index 0c0a20e0422..f17209066ef 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/SystemMessage.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/SystemMessage.java @@ -15,7 +15,13 @@ */ package org.springframework.ai.chat.messages; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.List; +import java.util.Map; import org.springframework.core.io.Resource; +import org.springframework.util.StreamUtils; /** * A message of the type 'system' passed as input. The system message gives high level @@ -26,18 +32,65 @@ */ public class SystemMessage extends AbstractMessage { - public SystemMessage(String content) { - super(MessageType.SYSTEM, content); + private SystemMessage(final MessageType type, final String textContent, final List media, + final Map properties) { + super(type, textContent, media, properties); } - public SystemMessage(Resource resource) { - super(MessageType.SYSTEM, resource); + /** + * Creates a new {@link SystemMessageBuilder} instance. + * @return A new instance of SystemMessageBuilder. + */ + public static SystemMessageBuilder builder() { + return new SystemMessageBuilder(); } - @Override - public String toString() { - return "SystemMessage{" + "content='" + getContent() + '\'' + ", properties=" + properties + ", messageType=" - + messageType + '}'; + /** + * Initializes a new {@link SystemMessageBuilder} with settings from an existing + * {@link SystemMessage} object. + * @param message The SystemMessage object whose settings are to be used. + * @return A SystemMessageBuilder instance initialized with the provided SystemMessage + * settings. + */ + public static SystemMessageBuilder builder(final SystemMessage message) { + return builder().withContent(message.getContent()); + } + + /** + * Builder for {@link SystemMessage}. This builder creates system message object. + */ + public static class SystemMessageBuilder extends AbstractMessageBuilder { + + private SystemMessageBuilder() { + super(MessageType.SYSTEM); + } + + public SystemMessageBuilder withContent(final String content) { + return super.withContent(content); + } + + /** + * Loads the content from the given resource and sets it as the content of the + * message being built. This method is useful for setting the content of a message + * to the contents of a file or other resource. + * @param resource the Spring Resource object representing the source of the + * content + * @return this builder instance to allow for method chaining + * @throws RuntimeException if an I/O error occurs reading from the resource + */ + public SystemMessageBuilder withResource(final Resource resource) { + try (InputStream inputStream = resource.getInputStream()) { + return super.withContent(StreamUtils.copyToString(inputStream, Charset.defaultCharset())); + } + catch (IOException ex) { + throw new RuntimeException("Failed to read resource", ex); + } + } + + public SystemMessage build() { + return new SystemMessage(this.messageType, this.textContent, this.media, this.properties); + } + } } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/UserMessage.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/UserMessage.java index d981a67edec..495aa7cb49e 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/UserMessage.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/UserMessage.java @@ -15,9 +15,14 @@ */ package org.springframework.ai.chat.messages; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; import java.util.List; +import java.util.Map; import org.springframework.core.io.Resource; +import org.springframework.util.StreamUtils; /** * A message of the type 'user' passed as input Messages with the user role are from the @@ -26,22 +31,71 @@ */ public class UserMessage extends AbstractMessage { - public UserMessage(String message) { - super(MessageType.USER, message); + private UserMessage(final MessageType type, final String textContent, final List media, + final Map properties) { + super(type, textContent, media, properties); } - public UserMessage(Resource resource) { - super(MessageType.USER, resource); + /** + * Creates a new {@link UserMessageBuilder} instance. + * @return A new instance of UserMessageBuilder. + */ + public static UserMessageBuilder builder() { + return new UserMessageBuilder(); } - public UserMessage(String textContent, List mediaList) { - super(MessageType.USER, textContent, mediaList); + /** + * Initializes a new {@link UserMessageBuilder} with settings from an existing + * {@link UserMessage} object. + * @param message The UserMessage object whose settings are to be used. + * @return A UserMessageBuilder instance initialized with the provided UserMessage + * settings. + */ + public static UserMessageBuilder builder(final UserMessage message) { + return builder().withContent(message.getContent()) + .withMedia(message.getMedia()) + .withProperties(message.getProperties()); } - @Override - public String toString() { - return "UserMessage{" + "content='" + getContent() + '\'' + ", properties=" + properties + ", messageType=" - + messageType + '}'; + /** + * Builder for {@link UserMessage}. This builder creates system message object. + */ + public static class UserMessageBuilder extends AbstractMessageBuilder { + + private UserMessageBuilder() { + super(MessageType.USER); + } + + public UserMessageBuilder withContent(final String content) { + return super.withContent(content); + } + + /** + * Loads the content from the given resource and sets it as the content of the + * message being built. This method is useful for setting the content of a message + * to the contents of a file or other resource. + * @param resource the Spring Resource object representing the source of the + * content + * @return this builder instance to allow for method chaining + * @throws RuntimeException if an I/O error occurs reading from the resource + */ + public UserMessageBuilder withResource(final Resource resource) { + try (InputStream inputStream = resource.getInputStream()) { + return super.withContent(StreamUtils.copyToString(inputStream, Charset.defaultCharset())); + } + catch (IOException ex) { + throw new RuntimeException("Failed to read resource", ex); + } + } + + public UserMessageBuilder withMedia(final List mediaData) { + return super.withMedia(mediaData); + } + + public UserMessage build() { + return new UserMessage(this.messageType, this.textContent, this.media, this.properties); + } + } } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/AssistantPromptTemplate.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/AssistantPromptTemplate.java index 8355bc98b4d..374c4f31b89 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/AssistantPromptTemplate.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/AssistantPromptTemplate.java @@ -33,22 +33,22 @@ public AssistantPromptTemplate(Resource resource) { @Override public Prompt create() { - return new Prompt(new AssistantMessage(render())); + return new Prompt(AssistantMessage.builder().withContent(render()).build()); } @Override public Prompt create(Map model) { - return new Prompt(new AssistantMessage(render(model))); + return new Prompt(AssistantMessage.builder().withContent(render(model)).build()); } @Override public Message createMessage() { - return new AssistantMessage(render()); + return AssistantMessage.builder().withContent(render()).build(); } @Override public Message createMessage(Map model) { - return new AssistantMessage(render(model)); + return AssistantMessage.builder().withContent(render(model)).build(); } } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/Prompt.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/Prompt.java index 86aab947fad..d1181c7d7fb 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/Prompt.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/Prompt.java @@ -31,7 +31,7 @@ public class Prompt implements ModelRequest> { private ChatOptions modelOptions; public Prompt(String contents) { - this(new UserMessage(contents)); + this(UserMessage.builder().withContent(contents).build()); } public Prompt(Message message) { @@ -43,7 +43,7 @@ public Prompt(List messages) { } public Prompt(String contents, ChatOptions modelOptions) { - this(new UserMessage(contents), modelOptions); + this(UserMessage.builder().withContent(contents).build(), modelOptions); } public Prompt(Message message, ChatOptions modelOptions) { diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/PromptTemplate.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/PromptTemplate.java index db74d9aaaad..aaf72006b12 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/PromptTemplate.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/PromptTemplate.java @@ -170,12 +170,12 @@ private String renderResource(Resource resource) { @Override public Message createMessage() { - return new UserMessage(render()); + return UserMessage.builder().withContent(render()).build(); } @Override public Message createMessage(Map model) { - return new UserMessage(render(model)); + return UserMessage.builder().withContent(render(model)).build(); } @Override diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/SystemPromptTemplate.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/SystemPromptTemplate.java index 8ac1aa85e42..292c241e97a 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/SystemPromptTemplate.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/SystemPromptTemplate.java @@ -33,22 +33,22 @@ public SystemPromptTemplate(Resource resource) { @Override public Message createMessage() { - return new SystemMessage(render()); + return SystemMessage.builder().withContent(render()).build(); } @Override public Message createMessage(Map model) { - return new SystemMessage(render(model)); + return SystemMessage.builder().withContent(render(model)).build(); } @Override public Prompt create() { - return new Prompt(new SystemMessage(render())); + return new Prompt(SystemMessage.builder().withContent(render()).build()); } @Override public Prompt create(Map model) { - return new Prompt(new SystemMessage(render(model))); + return new Prompt(SystemMessage.builder().withContent(render(model)).build()); } } diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/AzureOpenAiAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/AzureOpenAiAutoConfigurationIT.java index 0ecb7357e04..c6c5b92ef53 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/AzureOpenAiAutoConfigurationIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/AzureOpenAiAutoConfigurationIT.java @@ -71,8 +71,9 @@ public class AzureOpenAiAutoConfigurationIT { You should reply to the user's request with your name and also in the style of a {voice}. """).createMessage(Map.of("name", "Bob", "voice", "pirate")); - private final UserMessage userMessage = new UserMessage( - "Tell me about 3 famous pirates from the Golden Age of Piracy and why they did."); + private final UserMessage userMessage = UserMessage.builder() + .withContent("Tell me about 3 famous pirates from the Golden Age of Piracy and why they did.") + .build(); @Test public void chatCompletion() { diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithFunctionBeanIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithFunctionBeanIT.java index 4fd729fb8b9..cd82bf96c9e 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithFunctionBeanIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithFunctionBeanIT.java @@ -59,8 +59,10 @@ void functionCallTest() { ChatClient chatClient = context.getBean(AzureOpenAiChatClient.class); - UserMessage userMessage = new UserMessage( - "What's the weather like in San Francisco, Paris and in Tokyo? Use Multi-turn function calling."); + UserMessage userMessage = UserMessage.builder() + .withContent( + "What's the weather like in San Francisco, Paris and in Tokyo? Use Multi-turn function calling.") + .build(); ChatResponse response = chatClient.call(new Prompt(List.of(userMessage), AzureOpenAiChatOptions.builder().withFunction("weatherFunction").build())); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithFunctionWrapperIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithFunctionWrapperIT.java index ff070a957f2..efaa967c752 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithFunctionWrapperIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithFunctionWrapperIT.java @@ -58,8 +58,9 @@ void functionCallTest() { AzureOpenAiChatClient chatClient = context.getBean(AzureOpenAiChatClient.class); - UserMessage userMessage = new UserMessage( - "What's the weather like in San Francisco, Paris and in Tokyo?"); + UserMessage userMessage = UserMessage.builder() + .withContent("What's the weather like in San Francisco, Paris and in Tokyo?") + .build(); ChatResponse response = chatClient.call(new Prompt(List.of(userMessage), AzureOpenAiChatOptions.builder().withFunction("WeatherInfo").build())); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithPromptFunctionIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithPromptFunctionIT.java index 898eefe801a..124ef969adb 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithPromptFunctionIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithPromptFunctionIT.java @@ -54,8 +54,10 @@ void functionCallTest() { AzureOpenAiChatClient chatClient = context.getBean(AzureOpenAiChatClient.class); - UserMessage userMessage = new UserMessage( - "What's the weather like in San Francisco, in Paris and in Tokyo? Use Multi-turn function calling."); + UserMessage userMessage = UserMessage.builder() + .withContent( + "What's the weather like in San Francisco, in Paris and in Tokyo? Use Multi-turn function calling.") + .build(); var promptOptions = AzureOpenAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService()) diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/anthropic/BedrockAnthropicChatAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/anthropic/BedrockAnthropicChatAutoConfigurationIT.java index f8aca8b9a44..4dd1812f65d 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/anthropic/BedrockAnthropicChatAutoConfigurationIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/anthropic/BedrockAnthropicChatAutoConfigurationIT.java @@ -63,8 +63,9 @@ public class BedrockAnthropicChatAutoConfigurationIT { You should reply to the user's request with your name and also in the style of a {voice}. """).createMessage(Map.of("name", "Bob", "voice", "pirate")); - private final UserMessage userMessage = new UserMessage( - "Tell me about 3 famous pirates from the Golden Age of Piracy and why they did."); + private final UserMessage userMessage = UserMessage.builder() + .withContent("Tell me about 3 famous pirates from the Golden Age of Piracy and why they did.") + .build(); @Test public void chatCompletion() { diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/cohere/BedrockCohereChatAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/cohere/BedrockCohereChatAutoConfigurationIT.java index e515f6afde5..2cf91cb2c94 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/cohere/BedrockCohereChatAutoConfigurationIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/cohere/BedrockCohereChatAutoConfigurationIT.java @@ -66,8 +66,9 @@ public class BedrockCohereChatAutoConfigurationIT { You should reply to the user's request with your name and also in the style of a {voice}. """).createMessage(Map.of("name", "Bob", "voice", "pirate")); - private final UserMessage userMessage = new UserMessage( - "Tell me about 3 famous pirates from the Golden Age of Piracy and why they did."); + private final UserMessage userMessage = UserMessage.builder() + .withContent("Tell me about 3 famous pirates from the Golden Age of Piracy and why they did.") + .build(); @Test public void chatCompletion() { diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/llama2/BedrockLlama2ChatAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/llama2/BedrockLlama2ChatAutoConfigurationIT.java index 3c55877bcaf..65db0c1dcbf 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/llama2/BedrockLlama2ChatAutoConfigurationIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/llama2/BedrockLlama2ChatAutoConfigurationIT.java @@ -64,8 +64,9 @@ public class BedrockLlama2ChatAutoConfigurationIT { You should reply to the user's request with your name and also in the style of a {voice}. """).createMessage(Map.of("name", "Bob", "voice", "pirate")); - private final UserMessage userMessage = new UserMessage( - "Tell me about 3 famous pirates from the Golden Age of Piracy and why they did."); + private final UserMessage userMessage = UserMessage.builder() + .withContent("Tell me about 3 famous pirates from the Golden Age of Piracy and why they did.") + .build(); @Test public void chatCompletion() { diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/titan/BedrockTitanChatAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/titan/BedrockTitanChatAutoConfigurationIT.java index 93c6541787c..f9b780bfd4b 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/titan/BedrockTitanChatAutoConfigurationIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/titan/BedrockTitanChatAutoConfigurationIT.java @@ -64,8 +64,9 @@ public class BedrockTitanChatAutoConfigurationIT { You should reply to the user's request with your name and also in the style of a {voice}. """).createMessage(Map.of("name", "Bob", "voice", "pirate")); - private final UserMessage userMessage = new UserMessage( - "Tell me about 3 famous pirates from the Golden Age of Piracy and why they did."); + private final UserMessage userMessage = UserMessage.builder() + .withContent("Tell me about 3 famous pirates from the Golden Age of Piracy and why they did.") + .build(); @Test public void chatCompletion() { diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/MistralAiAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/MistralAiAutoConfigurationIT.java index 25816e8c0e4..9fd345b4f33 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/MistralAiAutoConfigurationIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/MistralAiAutoConfigurationIT.java @@ -65,7 +65,8 @@ void generate() { void generateStreaming() { contextRunner.run(context -> { MistralAiChatClient client = context.getBean(MistralAiChatClient.class); - Flux responseFlux = client.stream(new Prompt(new UserMessage("Hello"))); + Flux responseFlux = client + .stream(new Prompt(UserMessage.builder().withContent("Hello").build())); String response = responseFlux.collectList().block().stream().map(chatResponse -> { return chatResponse.getResults().get(0).getOutput().getContent(); }).collect(Collectors.joining()); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/PaymentStatusBeanIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/PaymentStatusBeanIT.java index d4259f3ee38..e864aa0adf0 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/PaymentStatusBeanIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/PaymentStatusBeanIT.java @@ -62,12 +62,14 @@ void functionCallTest() { MistralAiChatClient chatClient = context.getBean(MistralAiChatClient.class); - ChatResponse response = chatClient - .call(new Prompt(List.of(new UserMessage("What's the status of my transaction with id T1001?")), - MistralAiChatOptions.builder() - .withFunction("retrievePaymentStatus") - .withFunction("retrievePaymentDate") - .build())); + ChatResponse response = chatClient.call(new Prompt( + List.of(UserMessage.builder() + .withContent("What's the status of my transaction with id T1001?") + .build()), + MistralAiChatOptions.builder() + .withFunction("retrievePaymentStatus") + .withFunction("retrievePaymentDate") + .build())); logger.info("Response: {}", response); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/PaymentStatusBeanOpenAiIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/PaymentStatusBeanOpenAiIT.java index 15796f921b2..81a9fa7fe4e 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/PaymentStatusBeanOpenAiIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/PaymentStatusBeanOpenAiIT.java @@ -43,8 +43,8 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * Same test as {@link PaymentStatusBeanIT.java} but using {@link OpenAiChatClient} for - * Mistral AI Function Calling implementation. + * Same test as {@link PaymentStatusBeanIT} but using {@link OpenAiChatClient} for Mistral + * AI Function Calling implementation. * * @author Christian Tzolov */ @@ -69,12 +69,14 @@ void functionCallTest() { OpenAiChatClient chatClient = context.getBean(OpenAiChatClient.class); - ChatResponse response = chatClient - .call(new Prompt(List.of(new UserMessage("What's the status of my transaction with id T1001?")), - OpenAiChatOptions.builder() - .withFunction("retrievePaymentStatus") - .withFunction("retrievePaymentDate") - .build())); + ChatResponse response = chatClient.call(new Prompt( + List.of(UserMessage.builder() + .withContent("What's the status of my transaction with id T1001?") + .build()), + OpenAiChatOptions.builder() + .withFunction("retrievePaymentStatus") + .withFunction("retrievePaymentDate") + .build())); logger.info("Response: {}", response); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/PaymentStatusPromptIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/PaymentStatusPromptIT.java index 58bd9e84cc6..0dde099e913 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/PaymentStatusPromptIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/PaymentStatusPromptIT.java @@ -73,7 +73,9 @@ void functionCallTest() { MistralAiChatClient chatClient = context.getBean(MistralAiChatClient.class); - UserMessage userMessage = new UserMessage("What's the status of my transaction with id T1001?"); + UserMessage userMessage = UserMessage.builder() + .withContent("What's the status of my transaction with id T1001?") + .build(); var promptOptions = MistralAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new Function() { diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/WeatherServicePromptIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/WeatherServicePromptIT.java index 6bf052f5697..2c79291c437 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/WeatherServicePromptIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/WeatherServicePromptIT.java @@ -66,7 +66,9 @@ void promptFunctionCall() { MistralAiChatClient chatClient = context.getBean(MistralAiChatClient.class); - UserMessage userMessage = new UserMessage("What's the weather like in Paris?"); + UserMessage userMessage = UserMessage.builder() + .withContent("What's the weather like in Paris?") + .build(); // UserMessage userMessage = new UserMessage("What's the weather like in // San Francisco, Tokyo, and // Paris?"); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/ollama/OllamaChatAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/ollama/OllamaChatAutoConfigurationIT.java index affa261979f..920a1186bac 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/ollama/OllamaChatAutoConfigurationIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/ollama/OllamaChatAutoConfigurationIT.java @@ -98,8 +98,9 @@ public static void beforeAll() throws IOException, InterruptedException { You should reply to the user's request with your name and also in the style of a {voice}. """).createMessage(Map.of("name", "Bob", "voice", "pirate")); - private final UserMessage userMessage = new UserMessage( - "Tell me about 3 famous pirates from the Golden Age of Piracy and why they did."); + private final UserMessage userMessage = UserMessage.builder() + .withContent("Tell me about 3 famous pirates from the Golden Age of Piracy and why they did.") + .build(); @Test public void chatCompletion() { diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/OpenAiAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/OpenAiAutoConfigurationIT.java index 41bbfb8c7b1..54356caee6d 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/OpenAiAutoConfigurationIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/OpenAiAutoConfigurationIT.java @@ -78,7 +78,8 @@ void transcribe() { void generateStreaming() { contextRunner.run(context -> { OpenAiChatClient client = context.getBean(OpenAiChatClient.class); - Flux responseFlux = client.stream(new Prompt(new UserMessage("Hello"))); + Flux responseFlux = client + .stream(new Prompt(UserMessage.builder().withContent("Hello").build())); String response = responseFlux.collectList().block().stream().map(chatResponse -> { return chatResponse.getResults().get(0).getOutput().getContent(); }).collect(Collectors.joining()); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackInPromptIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackInPromptIT.java index 67770f6af82..d40111696b6 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackInPromptIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackInPromptIT.java @@ -56,7 +56,9 @@ void functionCallTest() { OpenAiChatClient chatClient = context.getBean(OpenAiChatClient.class); - UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?"); + UserMessage userMessage = UserMessage.builder() + .withContent("What's the weather like in San Francisco, Tokyo, and Paris?") + .build(); var promptOptions = OpenAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService()) @@ -81,7 +83,9 @@ void streamingFunctionCallTest() { OpenAiChatClient chatClient = context.getBean(OpenAiChatClient.class); - UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?"); + UserMessage userMessage = UserMessage.builder() + .withContent("What's the weather like in San Francisco, Tokyo, and Paris?") + .build(); var promptOptions = OpenAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService()) diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackWithPlainFunctionBeanIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackWithPlainFunctionBeanIT.java index d5437a62fdf..00d033f29c8 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackWithPlainFunctionBeanIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackWithPlainFunctionBeanIT.java @@ -63,7 +63,9 @@ void functionCallTest() { OpenAiChatClient chatClient = context.getBean(OpenAiChatClient.class); // Test weatherFunction - UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?"); + UserMessage userMessage = UserMessage.builder() + .withContent("What's the weather like in San Francisco, Tokyo, and Paris?") + .build(); ChatResponse response = chatClient.call(new Prompt(List.of(userMessage), OpenAiChatOptions.builder().withFunction("weatherFunction").build())); @@ -90,7 +92,9 @@ void functionCallWithPortableFunctionCallingOptions() { OpenAiChatClient chatClient = context.getBean(OpenAiChatClient.class); // Test weatherFunction - UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?"); + UserMessage userMessage = UserMessage.builder() + .withContent("What's the weather like in San Francisco, Tokyo, and Paris?") + .build(); PortableFunctionCallingOptions functionOptions = FunctionCallingOptions.builder() .withFunction("weatherFunction") @@ -109,7 +113,9 @@ void streamFunctionCallTest() { OpenAiChatClient chatClient = context.getBean(OpenAiChatClient.class); // Test weatherFunction - UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?"); + UserMessage userMessage = UserMessage.builder() + .withContent("What's the weather like in San Francisco, Tokyo, and Paris?") + .build(); Flux response = chatClient.stream(new Prompt(List.of(userMessage), OpenAiChatOptions.builder().withFunction("weatherFunction").build())); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackWrapperIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackWrapperIT.java index 44e2c3af740..47082079f77 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackWrapperIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackWrapperIT.java @@ -60,7 +60,9 @@ void functionCallTest() { OpenAiChatClient chatClient = context.getBean(OpenAiChatClient.class); - UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?"); + UserMessage userMessage = UserMessage.builder() + .withContent("What's the weather like in San Francisco, Tokyo, and Paris?") + .build(); ChatResponse response = chatClient.call( new Prompt(List.of(userMessage), OpenAiChatOptions.builder().withFunction("WeatherInfo").build())); @@ -78,7 +80,9 @@ void streamFunctionCallTest() { OpenAiChatClient chatClient = context.getBean(OpenAiChatClient.class); - UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?"); + UserMessage userMessage = UserMessage.builder() + .withContent("What's the weather like in San Francisco, Tokyo, and Paris?") + .build(); Flux response = chatClient.stream( new Prompt(List.of(userMessage), OpenAiChatOptions.builder().withFunction("WeatherInfo").build())); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/VertexAiGeminiAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/VertexAiGeminiAutoConfigurationIT.java index 5fd24735290..f098c0740f4 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/VertexAiGeminiAutoConfigurationIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/VertexAiGeminiAutoConfigurationIT.java @@ -59,7 +59,8 @@ void generate() { void generateStreaming() { contextRunner.run(context -> { VertexAiGeminiChatClient client = context.getBean(VertexAiGeminiChatClient.class); - Flux responseFlux = client.stream(new Prompt(new UserMessage("Hello"))); + Flux responseFlux = client + .stream(new Prompt(UserMessage.builder().withContent("Hello").build())); String response = responseFlux.collectList().block().stream().map(chatResponse -> { return chatResponse.getResults().get(0).getOutput().getContent(); }).collect(Collectors.joining()); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithFunctionBeanIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithFunctionBeanIT.java index cc259066745..260c2beab4a 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithFunctionBeanIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithFunctionBeanIT.java @@ -61,13 +61,14 @@ void functionCallTest() { VertexAiGeminiChatClient chatClient = context.getBean(VertexAiGeminiChatClient.class); - var systemMessage = new SystemMessage(""" + var systemMessage = SystemMessage.builder().withContent(""" Use Multi-turn function calling. Answer for all listed locations. If the information was not fetched call the function again. Repeat at most 3 times. - """); - var userMessage = new UserMessage( - "What's the weather like in San Francisco, Paris and in Tokyo (Japan)?"); + """).build(); + var userMessage = UserMessage.builder() + .withContent("What's the weather like in San Francisco, Paris and in Tokyo (Japan)?") + .build(); ChatResponse response = chatClient.call(new Prompt(List.of(systemMessage, userMessage), VertexAiGeminiChatOptions.builder().withFunction("weatherFunction").build())); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithFunctionWrapperIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithFunctionWrapperIT.java index cbad663c9c2..cc0d9455f8d 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithFunctionWrapperIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithFunctionWrapperIT.java @@ -60,12 +60,14 @@ void functionCallTest() { VertexAiGeminiChatClient chatClient = context.getBean(VertexAiGeminiChatClient.class); - var systemMessage = new SystemMessage(""" + var systemMessage = SystemMessage.builder().withContent(""" Use Multi-turn function calling. Answer for all listed locations. If the information was not fetched call the function again. Repeat at most 3 times. - """); - var userMessage = new UserMessage("What's the weather like in San Francisco, Paris and in Tokyo?"); + """).build(); + var userMessage = UserMessage.builder() + .withContent("What's the weather like in San Francisco, Paris and in Tokyo?") + .build(); ChatResponse response = chatClient.call(new Prompt(List.of(systemMessage, userMessage), VertexAiGeminiChatOptions.builder().withFunction("WeatherInfo").build())); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithPromptFunctionIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithPromptFunctionIT.java index a2dc42154e3..f911ef70b3b 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithPromptFunctionIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithPromptFunctionIT.java @@ -56,13 +56,14 @@ void functionCallTest() { VertexAiGeminiChatClient chatClient = context.getBean(VertexAiGeminiChatClient.class); - var systemMessage = new SystemMessage(""" + var systemMessage = SystemMessage.builder().withContent(""" Use Multi-turn function calling. Answer for all listed locations. If the information was not fetched call the function again. Repeat at most 3 times. - """); - UserMessage userMessage = new UserMessage( - "What's the weather like in San Francisco, in Paris and in Tokyo?"); + """).build(); + UserMessage userMessage = UserMessage.builder() + .withContent("What's the weather like in San Francisco, in Paris and in Tokyo?") + .build(); var promptOptions = VertexAiGeminiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService()) diff --git a/spring-ai-test/src/main/java/org/springframework/ai/evaluation/BasicEvaluationTest.java b/spring-ai-test/src/main/java/org/springframework/ai/evaluation/BasicEvaluationTest.java index bdc0750bd68..a0c66dd7118 100644 --- a/spring-ai-test/src/main/java/org/springframework/ai/evaluation/BasicEvaluationTest.java +++ b/spring-ai-test/src/main/java/org/springframework/ai/evaluation/BasicEvaluationTest.java @@ -61,17 +61,19 @@ protected void evaluateQuestionAndAnswer(String question, ChatResponse response, Map.of("question", question, "answer", answer)); SystemMessage systemMessage; if (factBased) { - systemMessage = new SystemMessage(qaEvaluatorFactBasedAnswerResource); + systemMessage = SystemMessage.builder().withResource(qaEvaluatorFactBasedAnswerResource).build(); } else { - systemMessage = new SystemMessage(qaEvaluatorAccurateAnswerResource); + systemMessage = SystemMessage.builder().withResource(qaEvaluatorAccurateAnswerResource).build(); } Message userMessage = userPromptTemplate.createMessage(); Prompt prompt = new Prompt(List.of(userMessage, systemMessage)); String yesOrNo = openAiChatClient.call(prompt).getResult().getOutput().getContent(); logger.info("Is Answer related to question: " + yesOrNo); if (yesOrNo.equalsIgnoreCase("no")) { - SystemMessage notRelatedSystemMessage = new SystemMessage(qaEvaluatorNotRelatedResource); + SystemMessage notRelatedSystemMessage = SystemMessage.builder() + .withResource(qaEvaluatorNotRelatedResource) + .build(); prompt = new Prompt(List.of(userMessage, notRelatedSystemMessage)); String reasonForFailure = openAiChatClient.call(prompt).getResult().getOutput().getContent(); fail(reasonForFailure);