diff --git a/models/spring-ai-moonshot/src/main/java/org/springframework/ai/moonshot/MoonshotChatOptions.java b/models/spring-ai-moonshot/src/main/java/org/springframework/ai/moonshot/MoonshotChatOptions.java index 3efddaf89c3..35c9712db6a 100644 --- a/models/spring-ai-moonshot/src/main/java/org/springframework/ai/moonshot/MoonshotChatOptions.java +++ b/models/spring-ai-moonshot/src/main/java/org/springframework/ai/moonshot/MoonshotChatOptions.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. @@ -17,9 +17,11 @@ package org.springframework.ai.moonshot; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -141,7 +143,7 @@ public class MoonshotChatOptions implements FunctionCallingOptions { private Boolean proxyToolCalls; @JsonIgnore - private Map toolContext; + private Map toolContext = new HashMap<>(); public static Builder builder() { return new Builder(); @@ -281,6 +283,7 @@ public void setToolContext(Map toolContext) { } @Override + @SuppressWarnings("unchecked") public MoonshotChatOptions copy() { return builder().model(this.model) .maxTokens(this.maxTokens) @@ -289,130 +292,41 @@ public MoonshotChatOptions copy() { .N(this.n) .presencePenalty(this.presencePenalty) .frequencyPenalty(this.frequencyPenalty) - .stop(this.stop) + .stop(this.stop != null ? new ArrayList<>(this.stop) : null) .user(this.user) - .tools(this.tools) + .tools(this.tools != null ? new ArrayList<>(this.tools) : null) .toolChoice(this.toolChoice) - .functionCallbacks(this.functionCallbacks) - .functions(this.functions) + .functionCallbacks(this.functionCallbacks != null ? new ArrayList<>(this.functionCallbacks) : null) + .functions(this.functions != null ? new HashSet<>(this.functions) : null) .proxyToolCalls(this.proxyToolCalls) - .toolContext(this.toolContext) + .toolContext(this.toolContext != null ? new HashMap<>(this.toolContext) : null) .build(); } @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((this.model == null) ? 0 : this.model.hashCode()); - result = prime * result + ((this.frequencyPenalty == null) ? 0 : this.frequencyPenalty.hashCode()); - result = prime * result + ((this.maxTokens == null) ? 0 : this.maxTokens.hashCode()); - result = prime * result + ((this.n == null) ? 0 : this.n.hashCode()); - result = prime * result + ((this.presencePenalty == null) ? 0 : this.presencePenalty.hashCode()); - result = prime * result + ((this.stop == null) ? 0 : this.stop.hashCode()); - result = prime * result + ((this.temperature == null) ? 0 : this.temperature.hashCode()); - result = prime * result + ((this.topP == null) ? 0 : this.topP.hashCode()); - result = prime * result + ((this.user == null) ? 0 : this.user.hashCode()); - result = prime * result + ((this.proxyToolCalls == null) ? 0 : this.proxyToolCalls.hashCode()); - result = prime * result + ((this.toolContext == null) ? 0 : this.toolContext.hashCode()); - return result; + return Objects.hash(model, maxTokens, temperature, topP, n, presencePenalty, frequencyPenalty, stop, tools, + toolChoice, functionCallbacks, functions, user, proxyToolCalls, toolContext); } @Override - public boolean equals(Object obj) { - if (this == obj) { + public boolean equals(Object o) { + if (this == o) { return true; } - if (obj == null) { + if (!(o instanceof MoonshotChatOptions other)) { return false; } - if (getClass() != obj.getClass()) { - return false; - } - MoonshotChatOptions other = (MoonshotChatOptions) obj; - if (this.model == null) { - if (other.model != null) { - return false; - } - } - else if (!this.model.equals(other.model)) { - return false; - } - if (this.frequencyPenalty == null) { - if (other.frequencyPenalty != null) { - return false; - } - } - else if (!this.frequencyPenalty.equals(other.frequencyPenalty)) { - return false; - } - if (this.maxTokens == null) { - if (other.maxTokens != null) { - return false; - } - } - else if (!this.maxTokens.equals(other.maxTokens)) { - return false; - } - if (this.n == null) { - if (other.n != null) { - return false; - } - } - else if (!this.n.equals(other.n)) { - return false; - } - if (this.presencePenalty == null) { - if (other.presencePenalty != null) { - return false; - } - } - else if (!this.presencePenalty.equals(other.presencePenalty)) { - return false; - } - if (this.stop == null) { - if (other.stop != null) { - return false; - } - } - else if (!this.stop.equals(other.stop)) { - return false; - } - if (this.temperature == null) { - if (other.temperature != null) { - return false; - } - } - else if (!this.temperature.equals(other.temperature)) { - return false; - } - if (this.topP == null) { - if (other.topP != null) { - return false; - } - } - else if (!this.topP.equals(other.topP)) { - return false; - } - if (this.user == null) { - return other.user == null; - } - else if (!this.user.equals(other.user)) { - return false; - } - if (this.proxyToolCalls == null) { - return other.proxyToolCalls == null; - } - else if (!this.proxyToolCalls.equals(other.proxyToolCalls)) { - return false; - } - if (this.toolContext == null) { - return other.toolContext == null; - } - else if (!this.toolContext.equals(other.toolContext)) { - return false; - } - return true; + return Objects.equals(this.model, other.model) && Objects.equals(this.maxTokens, other.maxTokens) + && Objects.equals(this.temperature, other.temperature) && Objects.equals(this.topP, other.topP) + && Objects.equals(this.n, other.n) && Objects.equals(this.presencePenalty, other.presencePenalty) + && Objects.equals(this.frequencyPenalty, other.frequencyPenalty) + && Objects.equals(this.stop, other.stop) && Objects.equals(this.tools, other.tools) + && Objects.equals(this.toolChoice, other.toolChoice) + && Objects.equals(this.functionCallbacks, other.functionCallbacks) + && Objects.equals(this.functions, other.functions) && Objects.equals(this.user, other.user) + && Objects.equals(this.proxyToolCalls, other.proxyToolCalls) + && Objects.equals(this.toolContext, other.toolContext); } public static class Builder { diff --git a/models/spring-ai-moonshot/src/test/java/org/springframework/ai/moonshot/MoonshotChatOptionsTests.java b/models/spring-ai-moonshot/src/test/java/org/springframework/ai/moonshot/MoonshotChatOptionsTests.java new file mode 100644 index 00000000000..9486b65b3ba --- /dev/null +++ b/models/spring-ai-moonshot/src/test/java/org/springframework/ai/moonshot/MoonshotChatOptionsTests.java @@ -0,0 +1,126 @@ +/* + * 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.moonshot; + +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link MoonshotChatOptions}. + * + * @author Alexandros Pappas + */ +class MoonshotChatOptionsTests { + + @Test + void testBuilderWithAllFields() { + MoonshotChatOptions options = MoonshotChatOptions.builder() + .model("test-model") + .frequencyPenalty(0.5) + .maxTokens(10) + .N(1) + .presencePenalty(0.5) + .stop(List.of("test")) + .temperature(0.6) + .topP(0.6) + .toolChoice("test") + .proxyToolCalls(true) + .toolContext(Map.of("key1", "value1")) + .user("test-user") + .build(); + + assertThat(options) + .extracting("model", "frequencyPenalty", "maxTokens", "N", "presencePenalty", "stop", "temperature", "topP", + "toolChoice", "proxyToolCalls", "toolContext", "user") + .containsExactly("test-model", 0.5, 10, 1, 0.5, List.of("test"), 0.6, 0.6, "test", true, + Map.of("key1", "value1"), "test-user"); + } + + @Test + void testCopy() { + MoonshotChatOptions original = MoonshotChatOptions.builder() + .model("test-model") + .frequencyPenalty(0.5) + .maxTokens(10) + .N(1) + .presencePenalty(0.5) + .stop(List.of("test")) + .temperature(0.6) + .topP(0.6) + .toolChoice("test") + .proxyToolCalls(true) + .toolContext(Map.of("key1", "value1")) + .user("test-user") + .build(); + + MoonshotChatOptions copied = original.copy(); + + assertThat(copied).isNotSameAs(original).isEqualTo(original); + // Ensure deep copy + assertThat(copied.getStop()).isNotSameAs(original.getStop()); + assertThat(copied.getToolContext()).isNotSameAs(original.getToolContext()); + } + + @Test + void testSetters() { + MoonshotChatOptions options = new MoonshotChatOptions(); + options.setModel("test-model"); + options.setFrequencyPenalty(0.5); + options.setMaxTokens(10); + options.setN(1); + options.setPresencePenalty(0.5); + options.setUser("test-user"); + options.setStop(List.of("test")); + options.setTemperature(0.6); + options.setTopP(0.6); + options.setProxyToolCalls(true); + options.setToolContext(Map.of("key1", "value1")); + + assertThat(options.getModel()).isEqualTo("test-model"); + assertThat(options.getFrequencyPenalty()).isEqualTo(0.5); + assertThat(options.getMaxTokens()).isEqualTo(10); + assertThat(options.getN()).isEqualTo(1); + assertThat(options.getPresencePenalty()).isEqualTo(0.5); + assertThat(options.getUser()).isEqualTo("test-user"); + assertThat(options.getStopSequences()).isEqualTo(List.of("test")); + assertThat(options.getTemperature()).isEqualTo(0.6); + assertThat(options.getTopP()).isEqualTo(0.6); + assertThat(options.getProxyToolCalls()).isTrue(); + assertThat(options.getToolContext()).isEqualTo(Map.of("key1", "value1")); + } + + @Test + void testDefaultValues() { + MoonshotChatOptions options = new MoonshotChatOptions(); + assertThat(options.getModel()).isNull(); + assertThat(options.getFrequencyPenalty()).isNull(); + assertThat(options.getMaxTokens()).isNull(); + assertThat(options.getN()).isNull(); + assertThat(options.getPresencePenalty()).isNull(); + assertThat(options.getUser()).isNull(); + assertThat(options.getStopSequences()).isNull(); + assertThat(options.getTemperature()).isNull(); + assertThat(options.getTopP()).isNull(); + assertThat(options.getProxyToolCalls()).isNull(); + assertThat(options.getToolContext()).isEqualTo(new java.util.HashMap<>()); + } + +}