diff --git a/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekAssistantMessage.java b/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekAssistantMessage.java index 6159d9beadb..353a4d8a60a 100644 --- a/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekAssistantMessage.java +++ b/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekAssistantMessage.java @@ -23,6 +23,11 @@ import org.springframework.ai.chat.messages.AssistantMessage; import org.springframework.ai.content.Media; +/** + * @author Mark Pollack + * @author Soby Chacko + * @author Sun Yuhan + */ public class DeepSeekAssistantMessage extends AssistantMessage { private Boolean prefix; @@ -57,6 +62,13 @@ public DeepSeekAssistantMessage(String content, String reasoningContent, Map properties, List toolCalls, List media) { + super(content, properties, toolCalls, media); + this.reasoningContent = reasoningContent; + this.prefix = prefix; + } + public static DeepSeekAssistantMessage prefixAssistantMessage(String context) { return prefixAssistantMessage(context, null); } @@ -102,9 +114,60 @@ public int hashCode() { @Override public String toString() { - return "AssistantMessage [messageType=" + this.messageType + ", toolCalls=" + super.getToolCalls() + return "DeepSeekAssistantMessage [messageType=" + this.messageType + ", toolCalls=" + super.getToolCalls() + ", textContent=" + this.textContent + ", reasoningContent=" + this.reasoningContent + ", prefix=" + this.prefix + ", metadata=" + this.metadata + "]"; } + public static final class Builder { + + private String content; + + private Map properties = Map.of(); + + private List toolCalls = List.of(); + + private List media = List.of(); + + private Boolean prefix; + + private String reasoningContent; + + 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 Builder prefix(Boolean prefix) { + this.prefix = prefix; + return this; + } + + public Builder reasoningContent(String reasoningContent) { + this.reasoningContent = reasoningContent; + return this; + } + + public DeepSeekAssistantMessage build() { + return new DeepSeekAssistantMessage(this.content, this.reasoningContent, this.prefix, this.properties, + this.toolCalls, this.media); + } + + } + } diff --git a/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekChatModel.java b/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekChatModel.java index b9ecd7325f0..fba44ffd4ce 100644 --- a/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekChatModel.java +++ b/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekChatModel.java @@ -340,8 +340,13 @@ private Generation buildGeneration(Choice choice, Map metadata) String textContent = choice.message().content(); String reasoningContent = choice.message().reasoningContent(); - DeepSeekAssistantMessage assistantMessage = new DeepSeekAssistantMessage(textContent, reasoningContent, - metadata, toolCalls); + DeepSeekAssistantMessage.Builder builder = new DeepSeekAssistantMessage.Builder(); + DeepSeekAssistantMessage assistantMessage = builder.content(textContent) + .reasoningContent(reasoningContent) + .properties(metadata) + .toolCalls(toolCalls) + .build(); + return new Generation(assistantMessage, generationMetadataBuilder.build()); } diff --git a/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/DeepSeekAssistantMessageTests.java b/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/DeepSeekAssistantMessageTests.java new file mode 100644 index 00000000000..74d8ea86a73 --- /dev/null +++ b/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/DeepSeekAssistantMessageTests.java @@ -0,0 +1,190 @@ +/* + * Copyright 2025-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.deepseek; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import org.springframework.ai.chat.messages.AssistantMessage.ToolCall; +import org.springframework.ai.content.Media; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; + +/** + * Unit tests for {@link DeepSeekAssistantMessage}. + * + * @author Sun Yuhan + */ +class DeepSeekAssistantMessageTests { + + @Test + public void testConstructorWithContentOnly() { + String content = "Hello, world!"; + DeepSeekAssistantMessage message = new DeepSeekAssistantMessage(content); + + assertThat(message.getText()).isEqualTo(content); + assertThat(message.getReasoningContent()).isNull(); + assertThat(message.getPrefix()).isNull(); + } + + @Test + public void testConstructorWithContentAndReasoningContent() { + String content = "Hello, world!"; + String reasoningContent = "This is my reasoning"; + DeepSeekAssistantMessage message = new DeepSeekAssistantMessage(content, reasoningContent); + + assertThat(message.getText()).isEqualTo(content); + assertThat(message.getReasoningContent()).isEqualTo(reasoningContent); + assertThat(message.getPrefix()).isNull(); + } + + @Test + public void testConstructorWithContentAndProperties() { + String content = "Hello, world!"; + Map properties = new HashMap<>(); + properties.put("key1", "value1"); + properties.put("key2", 123); + + DeepSeekAssistantMessage message = new DeepSeekAssistantMessage(content, properties); + + assertThat(message.getText()).isEqualTo(content); + assertThat(message.getMetadata()).containsAllEntriesOf(properties); + assertThat(message.getReasoningContent()).isNull(); + assertThat(message.getPrefix()).isNull(); + } + + @Test + public void testConstructorWithContentPropertiesAndToolCalls() { + String content = "Hello, world!"; + Map properties = new HashMap<>(); + properties.put("key1", "value1"); + + List toolCalls = List.of(new ToolCall("1", "function", "myFunction", "{}")); + + DeepSeekAssistantMessage message = new DeepSeekAssistantMessage(content, properties, toolCalls); + + assertThat(message.getText()).isEqualTo(content); + assertThat(message.getMetadata()).containsAllEntriesOf(properties); + assertThat(message.getToolCalls()).isEqualTo(toolCalls); + assertThat(message.getReasoningContent()).isNull(); + assertThat(message.getPrefix()).isNull(); + } + + @Test + public void testConstructorWithAllParameters() { + String content = "Hello, world!"; + String reasoningContent = "This is my reasoning"; + Boolean prefix = true; + Map properties = new HashMap<>(); + properties.put("key1", "value1"); + List toolCalls = List.of(new ToolCall("1", "function", "myFunction", "{}")); + + DeepSeekAssistantMessage message = new DeepSeekAssistantMessage(content, reasoningContent, prefix, properties, + toolCalls, List.of()); + + assertThat(message.getText()).isEqualTo(content); + assertThat(message.getReasoningContent()).isEqualTo(reasoningContent); + assertThat(message.getPrefix()).isEqualTo(prefix); + assertThat(message.getMetadata()).containsAllEntriesOf(properties); + assertThat(message.getToolCalls()).isEqualTo(toolCalls); + } + + @Test + public void testPrefixAssistantMessageFactoryMethod() { + String content = "Hello, world!"; + DeepSeekAssistantMessage message = DeepSeekAssistantMessage.prefixAssistantMessage(content); + + assertThat(message.getText()).isEqualTo(content); + assertThat(message.getReasoningContent()).isNull(); + } + + @Test + public void testPrefixAssistantMessageFactoryMethodWithReasoning() { + String content = "Hello, world!"; + String reasoningContent = "This is my reasoning"; + DeepSeekAssistantMessage message = DeepSeekAssistantMessage.prefixAssistantMessage(content, reasoningContent); + + assertThat(message.getText()).isEqualTo(content); + assertThat(message.getReasoningContent()).isEqualTo(reasoningContent); + } + + @Test + public void testSettersAndGetters() { + DeepSeekAssistantMessage message = new DeepSeekAssistantMessage("test"); + + String reasoningContent = "New reasoning content"; + Boolean prefix = false; + + message.setReasoningContent(reasoningContent); + message.setPrefix(prefix); + + assertThat(message.getReasoningContent()).isEqualTo(reasoningContent); + assertThat(message.getPrefix()).isEqualTo(prefix); + } + + @Test + public void testEqualsAndHashCode() { + DeepSeekAssistantMessage message1 = new DeepSeekAssistantMessage("content", "reasoning", true, Map.of(), + List.of(), List.of()); + DeepSeekAssistantMessage message2 = new DeepSeekAssistantMessage("content", "reasoning", true, Map.of(), + List.of(), List.of()); + + assertThat(message1).isEqualTo(message2); + assertThat(message1.hashCode()).isEqualTo(message2.hashCode()); + + DeepSeekAssistantMessage message3 = new DeepSeekAssistantMessage("content", "different reasoning", true, + Map.of(), List.of(), List.of()); + assertThat(message1).isNotEqualTo(message3); + } + + @Test + public void testToString() { + DeepSeekAssistantMessage message = new DeepSeekAssistantMessage("content", "reasoning"); + message.setPrefix(true); + + assertThatNoException().isThrownBy(message::toString); + assertThat(message.toString()).contains("content", "reasoning", "true"); + } + + @Test + public void testBuilderComplete() { + Map properties = Map.of("key", "value"); + List toolCalls = List.of(new ToolCall("1", "function", "testFunction", "{}")); + List media = List.of(); + + DeepSeekAssistantMessage.Builder builder = new DeepSeekAssistantMessage.Builder(); + DeepSeekAssistantMessage message = builder.content("content") + .reasoningContent("reasoning") + .prefix(true) + .properties(properties) + .toolCalls(toolCalls) + .media(media) + .build(); + + assertThat(message.getText()).isEqualTo("content"); + assertThat(message.getReasoningContent()).isEqualTo("reasoning"); + assertThat(message.getPrefix()).isEqualTo(true); + assertThat(message.getMetadata()).containsAllEntriesOf(properties); + assertThat(message.getToolCalls()).isEqualTo(toolCalls); + assertThat(message.getMedia()).isEqualTo(media); + } + +}