From f47b2726f89cb143702b6d084bb43d409dd60654 Mon Sep 17 00:00:00 2001 From: Sun Yuhan Date: Wed, 24 Sep 2025 09:26:46 +0800 Subject: [PATCH 1/2] feat: GH-4454 Added Builder pattern support for `ZhiPuAiAssistantMessage` and included corresponding unit tests. Signed-off-by: Sun Yuhan --- .../ai/zhipuai/ZhiPuAiAssistantMessage.java | 45 ++++++ .../ai/zhipuai/ZhiPuAiChatModel.java | 10 +- .../zhipuai/ZhiPuAiAssistantMessageTests.java | 135 ++++++++++++++++++ .../ai/chat/messages/AssistantMessage.java | 12 +- 4 files changed, 194 insertions(+), 8 deletions(-) create mode 100644 models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/ZhiPuAiAssistantMessageTests.java diff --git a/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiAssistantMessage.java b/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiAssistantMessage.java index b42f90ce912..7a0fc6d0b76 100644 --- a/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiAssistantMessage.java +++ b/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiAssistantMessage.java @@ -25,6 +25,7 @@ /** * @author YunKui Lu + * @author Sun Yuhan */ public class ZhiPuAiAssistantMessage extends AssistantMessage { @@ -78,4 +79,48 @@ public String toString() { + this.textContent + '\'' + '}'; } + public static Builder builder() { + return new Builder(); + } + + public static final class Builder extends AssistantMessage.Builder { + + private String reasoningContent; + + @Override + public Builder content(String content) { + this.content = content; + return this; + } + + @Override + public Builder properties(Map properties) { + this.properties = properties; + return this; + } + + @Override + public Builder toolCalls(List toolCalls) { + this.toolCalls = toolCalls; + return this; + } + + @Override + public Builder media(List media) { + this.media = media; + return this; + } + + public Builder reasoningContent(String reasoningContent) { + this.reasoningContent = reasoningContent; + return this; + } + + public ZhiPuAiAssistantMessage build() { + return new ZhiPuAiAssistantMessage(this.content, this.reasoningContent, this.properties, this.toolCalls, + this.media); + } + + } + } diff --git a/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiChatModel.java b/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiChatModel.java index 4e463301d52..a727fa9838b 100644 --- a/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiChatModel.java +++ b/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiChatModel.java @@ -230,8 +230,14 @@ private static Generation buildGeneration(Choice choice, Map met String textContent = choice.message().content(); String reasoningContent = choice.message().reasoningContent(); - var assistantMessage = new ZhiPuAiAssistantMessage(textContent, reasoningContent, metadata, toolCalls, - List.of()); + var assistantMessage = ZhiPuAiAssistantMessage.builder() + .content(textContent) + .reasoningContent(reasoningContent) + .properties(metadata) + .toolCalls(toolCalls) + .media(List.of()) + .build(); + String finishReason = (choice.finishReason() != null ? choice.finishReason().name() : ""); var generationMetadata = ChatGenerationMetadata.builder().finishReason(finishReason).build(); return new Generation(assistantMessage, generationMetadata); diff --git a/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/ZhiPuAiAssistantMessageTests.java b/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/ZhiPuAiAssistantMessageTests.java new file mode 100644 index 00000000000..04b5f968fe4 --- /dev/null +++ b/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/ZhiPuAiAssistantMessageTests.java @@ -0,0 +1,135 @@ +/* + * 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.zhipuai; + +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; + +/** + * Tests for {@link ZhiPuAiAssistantMessage}. + * + * @author Sun Yuhan + */ +class ZhiPuAiAssistantMessageTests { + + @Test + public void testConstructorWithContentOnly() { + String content = "Hello, world!"; + ZhiPuAiAssistantMessage message = new ZhiPuAiAssistantMessage(content); + + assertThat(message.getText()).isEqualTo(content); + assertThat(message.getReasoningContent()).isNull(); + } + + @Test + public void testConstructorWithAllParameters() { + String content = "Hello, world!"; + String reasoningContent = "This is my reasoning"; + Map properties = new HashMap<>(); + properties.put("key1", "value1"); + List toolCalls = List.of(new ToolCall("1", "function", "myFunction", "{}")); + List media = List.of(); + + ZhiPuAiAssistantMessage message = new ZhiPuAiAssistantMessage(content, reasoningContent, properties, toolCalls, + media); + + assertThat(message.getText()).isEqualTo(content); + assertThat(message.getReasoningContent()).isEqualTo(reasoningContent); + assertThat(message.getMetadata()).containsAllEntriesOf(properties); + assertThat(message.getToolCalls()).isEqualTo(toolCalls); + } + + @Test + public void testSettersAndGetters() { + ZhiPuAiAssistantMessage message = new ZhiPuAiAssistantMessage("test"); + + String reasoningContent = "New reasoning content"; + + message.setReasoningContent(reasoningContent); + + assertThat(message.getReasoningContent()).isEqualTo(reasoningContent); + } + + @Test + public void testEqualsAndHashCode() { + String content = "Hello, world!"; + String reasoningContent = "This is my reasoning"; + Map properties = new HashMap<>(); + properties.put("key1", "value1"); + List toolCalls = List.of(new ToolCall("1", "function", "myFunction", "{}")); + List media = List.of(); + + ZhiPuAiAssistantMessage message1 = new ZhiPuAiAssistantMessage(content, reasoningContent, properties, toolCalls, + media); + ZhiPuAiAssistantMessage message2 = new ZhiPuAiAssistantMessage(content, reasoningContent, properties, toolCalls, + media); + + assertThat(message1).isEqualTo(message2); + assertThat(message1.hashCode()).isEqualTo(message2.hashCode()); + + ZhiPuAiAssistantMessage message3 = new ZhiPuAiAssistantMessage(content, "different reasoning", properties, + toolCalls, media); + assertThat(message1).isNotEqualTo(message3); + } + + @Test + public void testToString() { + String content = "Hello, world!"; + String reasoningContent = "This is my reasoning"; + Map properties = new HashMap<>(); + properties.put("key1", "value1"); + List toolCalls = List.of(new ToolCall("1", "function", "myFunction", "{}")); + List media = List.of(); + + ZhiPuAiAssistantMessage message = new ZhiPuAiAssistantMessage(content, reasoningContent, properties, toolCalls, + media); + + assertThatNoException().isThrownBy(message::toString); + assertThat(message.toString()).contains(content, reasoningContent); + } + + @Test + public void testBuilderComplete() { + Map properties = Map.of("key", "value"); + List toolCalls = List.of(new ToolCall("1", "function", "testFunction", "{}")); + List media = List.of(); + + ZhiPuAiAssistantMessage message = ZhiPuAiAssistantMessage.builder() + .content("content") + .reasoningContent("reasoning") + .properties(properties) + .toolCalls(toolCalls) + .media(media) + .build(); + + assertThat(message.getText()).isEqualTo("content"); + assertThat(message.getReasoningContent()).isEqualTo("reasoning"); + assertThat(message.getMetadata()).containsAllEntriesOf(properties); + assertThat(message.getToolCalls()).isEqualTo(toolCalls); + assertThat(message.getMedia()).isEqualTo(media); + } + +} 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 8d6d18dd40b..7ebb22eafdc 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 @@ -121,17 +121,17 @@ public record ToolCall(String id, String type, String name, String arguments) { } - public static final class Builder { + public static class Builder { - private String content; + protected String content; - private Map properties = Map.of(); + protected Map properties = Map.of(); - private List toolCalls = List.of(); + protected List toolCalls = List.of(); - private List media = List.of(); + protected List media = List.of(); - private Builder() { + protected Builder() { } public Builder content(String content) { From 4a13b6d73306d69e4c3f372f61057a9c5f90c35d Mon Sep 17 00:00:00 2001 From: Sun Yuhan Date: Thu, 2 Oct 2025 23:20:19 +0800 Subject: [PATCH 2/2] fix: Temporarily remove the `builder()` method from `ZhiPuAiAssistantMessage` and modify related references to use `new` instead. Signed-off-by: Sun Yuhan --- .../ai/zhipuai/ZhiPuAiAssistantMessage.java | 16 ++++++++-------- .../ai/zhipuai/ZhiPuAiChatModel.java | 4 ++-- .../ai/zhipuai/ZhiPuAiAssistantMessageTests.java | 4 ++-- .../ai/chat/messages/AssistantMessage.java | 12 ++++++------ 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiAssistantMessage.java b/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiAssistantMessage.java index 7a0fc6d0b76..6dfad7e30f2 100644 --- a/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiAssistantMessage.java +++ b/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiAssistantMessage.java @@ -79,33 +79,33 @@ public String toString() { + this.textContent + '\'' + '}'; } - public static Builder builder() { - return new Builder(); - } + public static final class Builder { + + private String content; + + private Map properties = Map.of(); + + private List toolCalls = List.of(); - public static final class Builder extends AssistantMessage.Builder { + private List media = List.of(); private String reasoningContent; - @Override public Builder content(String content) { this.content = content; return this; } - @Override public Builder properties(Map properties) { this.properties = properties; return this; } - @Override public Builder toolCalls(List toolCalls) { this.toolCalls = toolCalls; return this; } - @Override public Builder media(List media) { this.media = media; return this; diff --git a/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiChatModel.java b/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiChatModel.java index a727fa9838b..2c9ff3e54ff 100644 --- a/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiChatModel.java +++ b/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiChatModel.java @@ -230,8 +230,8 @@ private static Generation buildGeneration(Choice choice, Map met String textContent = choice.message().content(); String reasoningContent = choice.message().reasoningContent(); - var assistantMessage = ZhiPuAiAssistantMessage.builder() - .content(textContent) + ZhiPuAiAssistantMessage.Builder builder = new ZhiPuAiAssistantMessage.Builder(); + var assistantMessage = builder.content(textContent) .reasoningContent(reasoningContent) .properties(metadata) .toolCalls(toolCalls) diff --git a/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/ZhiPuAiAssistantMessageTests.java b/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/ZhiPuAiAssistantMessageTests.java index 04b5f968fe4..b0c2bbfe60a 100644 --- a/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/ZhiPuAiAssistantMessageTests.java +++ b/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/ZhiPuAiAssistantMessageTests.java @@ -117,8 +117,8 @@ public void testBuilderComplete() { List toolCalls = List.of(new ToolCall("1", "function", "testFunction", "{}")); List media = List.of(); - ZhiPuAiAssistantMessage message = ZhiPuAiAssistantMessage.builder() - .content("content") + ZhiPuAiAssistantMessage.Builder builder = new ZhiPuAiAssistantMessage.Builder(); + ZhiPuAiAssistantMessage message = builder.content("content") .reasoningContent("reasoning") .properties(properties) .toolCalls(toolCalls) 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 7ebb22eafdc..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 @@ -121,17 +121,17 @@ public record ToolCall(String id, String type, String name, String arguments) { } - public static class Builder { + public static final class Builder { - protected String content; + private String content; - protected Map properties = Map.of(); + private Map properties = Map.of(); - protected List toolCalls = List.of(); + private List toolCalls = List.of(); - protected List media = List.of(); + private List media = List.of(); - protected Builder() { + private Builder() { } public Builder content(String content) {