diff --git a/spring-ai-model/src/main/java/org/springframework/ai/chat/messages/AssistantMessage.java b/spring-ai-model/src/main/java/org/springframework/ai/chat/messages/AssistantMessage.java index b092de2d6da..8d6d18dd40b 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/chat/messages/AssistantMessage.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/chat/messages/AssistantMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 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. @@ -33,6 +33,7 @@ * * @author Mark Pollack * @author Christian Tzolov + * @author Thomas Vitale * @since 1.0.0 */ public class AssistantMessage extends AbstractMessage implements MediaContent { @@ -45,14 +46,26 @@ public AssistantMessage(String content) { this(content, Map.of()); } + /** + * @deprecated in favor of {@link AssistantMessage#builder()}. + */ + @Deprecated public AssistantMessage(String content, Map properties) { this(content, properties, List.of()); } + /** + * @deprecated in favor of {@link AssistantMessage#builder()}. + */ + @Deprecated public AssistantMessage(String content, Map properties, List toolCalls) { this(content, properties, toolCalls, List.of()); } + /** + * @deprecated in favor of {@link AssistantMessage#builder()}. + */ + @Deprecated public AssistantMessage(String content, Map properties, List toolCalls, List media) { super(MessageType.ASSISTANT, content, properties); @@ -100,8 +113,51 @@ public String toString() { + this.textContent + ", metadata=" + this.metadata + "]"; } + public static Builder builder() { + return new Builder(); + } + public record ToolCall(String id, String type, String name, String arguments) { } + public static final class Builder { + + private String content; + + private Map properties = Map.of(); + + private List toolCalls = List.of(); + + private List media = List.of(); + + private Builder() { + } + + public Builder content(String content) { + this.content = content; + return this; + } + + public Builder properties(Map properties) { + this.properties = properties; + return this; + } + + public Builder toolCalls(List toolCalls) { + this.toolCalls = toolCalls; + return this; + } + + public Builder media(List media) { + this.media = media; + return this; + } + + public AssistantMessage build() { + return new AssistantMessage(this.content, this.properties, this.toolCalls, this.media); + } + + } + } diff --git a/spring-ai-model/src/test/java/org/springframework/ai/chat/messages/AssistantMessageTests.java b/spring-ai-model/src/test/java/org/springframework/ai/chat/messages/AssistantMessageTests.java new file mode 100644 index 00000000000..31efb5163ad --- /dev/null +++ b/spring-ai-model/src/test/java/org/springframework/ai/chat/messages/AssistantMessageTests.java @@ -0,0 +1,51 @@ +/* + * Copyright 2023-2025 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 org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * Unit tests for {@link AssistantMessage}. + * + * @author Thomas Vitale + */ +class AssistantMessageTests { + + @Test + void whenMediaIsNullThenThrow() { + assertThatThrownBy(() -> AssistantMessage.builder().media(null).build()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Media must not be null"); + } + + @Test + void whenMetadataIsNullThenThrow() { + assertThatThrownBy(() -> AssistantMessage.builder().properties(null).build()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Metadata must not be null"); + } + + @Test + void whenToolCallsIsNullThenThrow() { + assertThatThrownBy(() -> AssistantMessage.builder().toolCalls(null).build()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Tool calls must not be null"); + } + +}