Skip to content

Commit 15f1fa7

Browse files
committed
feat: equals, hashCode, deep copy, and tests to MiniMaxChatOptions
This commit enhances MiniMaxChatOptions by: - Updating `equals` and `hashCode` methods for proper object comparison. - Updating `copy()` method, creating new instances of mutable collections (List, Set, Map, Metadata) to prevent shared state. - Adding `MiniMaxChatOptionsTests` to verify `copy()`, builders, setters, and default values. Signed-off-by: Alexandros Pappas <[email protected]>
1 parent 092bbae commit 15f1fa7

File tree

2 files changed

+167
-154
lines changed

2 files changed

+167
-154
lines changed

models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/MiniMaxChatOptions.java

Lines changed: 26 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
package org.springframework.ai.minimax;
1818

1919
import java.util.ArrayList;
20+
import java.util.HashMap;
2021
import java.util.HashSet;
2122
import java.util.List;
2223
import java.util.Map;
24+
import java.util.Objects;
2325
import java.util.Set;
2426

2527
import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -43,6 +45,7 @@
4345
* @author Geng Rong
4446
* @author Thomas Vitale
4547
* @author Ilayaperumal Gopinathan
48+
* @author Alexandros Pappas
4649
* @since 1.0.0 M1
4750
*/
4851
@JsonInclude(Include.NON_NULL)
@@ -146,7 +149,7 @@ public class MiniMaxChatOptions implements FunctionCallingOptions {
146149
private Boolean proxyToolCalls;
147150

148151
@JsonIgnore
149-
private Map<String, Object> toolContext;
152+
private Map<String, Object> toolContext = new HashMap<>();
150153

151154
// @formatter:on
152155

@@ -162,16 +165,17 @@ public static MiniMaxChatOptions fromOptions(MiniMaxChatOptions fromOptions) {
162165
.presencePenalty(fromOptions.getPresencePenalty())
163166
.responseFormat(fromOptions.getResponseFormat())
164167
.seed(fromOptions.getSeed())
165-
.stop(fromOptions.getStop())
168+
.stop(fromOptions.getStop() != null ? new ArrayList<>(fromOptions.getStop()) : null)
166169
.temperature(fromOptions.getTemperature())
167170
.topP(fromOptions.getTopP())
168171
.maskSensitiveInfo(fromOptions.getMaskSensitiveInfo())
169-
.tools(fromOptions.getTools())
172+
.tools(fromOptions.getTools() != null ? new ArrayList<>(fromOptions.getTools()) : null)
170173
.toolChoice(fromOptions.getToolChoice())
171-
.functionCallbacks(fromOptions.getFunctionCallbacks())
172-
.functions(fromOptions.getFunctions())
174+
.functionCallbacks(fromOptions.getFunctionCallbacks() != null
175+
? new ArrayList<>(fromOptions.getFunctionCallbacks()) : null)
176+
.functions(fromOptions.getFunctions() != null ? new HashSet<>(fromOptions.getFunctions()) : null)
173177
.proxyToolCalls(fromOptions.getProxyToolCalls())
174-
.toolContext(fromOptions.getToolContext())
178+
.toolContext(fromOptions.getToolContext() != null ? new HashMap<>(fromOptions.getToolContext()) : null)
175179
.build();
176180
}
177181

@@ -342,164 +346,32 @@ public void setToolContext(Map<String, Object> toolContext) {
342346

343347
@Override
344348
public int hashCode() {
345-
final int prime = 31;
346-
int result = 1;
347-
result = prime * result + ((this.model == null) ? 0 : this.model.hashCode());
348-
result = prime * result + ((this.frequencyPenalty == null) ? 0 : this.frequencyPenalty.hashCode());
349-
result = prime * result + ((this.maxTokens == null) ? 0 : this.maxTokens.hashCode());
350-
result = prime * result + ((this.n == null) ? 0 : this.n.hashCode());
351-
result = prime * result + ((this.presencePenalty == null) ? 0 : this.presencePenalty.hashCode());
352-
result = prime * result + ((this.responseFormat == null) ? 0 : this.responseFormat.hashCode());
353-
result = prime * result + ((this.seed == null) ? 0 : this.seed.hashCode());
354-
result = prime * result + ((this.stop == null) ? 0 : this.stop.hashCode());
355-
result = prime * result + ((this.temperature == null) ? 0 : this.temperature.hashCode());
356-
result = prime * result + ((this.topP == null) ? 0 : this.topP.hashCode());
357-
result = prime * result + ((this.maskSensitiveInfo == null) ? 0 : this.maskSensitiveInfo.hashCode());
358-
result = prime * result + ((this.tools == null) ? 0 : this.tools.hashCode());
359-
result = prime * result + ((this.toolChoice == null) ? 0 : this.toolChoice.hashCode());
360-
result = prime * result + ((this.proxyToolCalls == null) ? 0 : this.proxyToolCalls.hashCode());
361-
result = prime * result + ((this.toolContext == null) ? 0 : this.toolContext.hashCode());
362-
return result;
349+
return Objects.hash(model, frequencyPenalty, maxTokens, n, presencePenalty, responseFormat, seed, stop,
350+
temperature, topP, maskSensitiveInfo, tools, toolChoice, proxyToolCalls, toolContext);
363351
}
364352

365353
@Override
366-
public boolean equals(Object obj) {
367-
if (this == obj) {
354+
public boolean equals(Object o) {
355+
if (this == o) {
368356
return true;
369357
}
370-
if (obj == null) {
358+
if (!(o instanceof MiniMaxChatOptions that)) {
371359
return false;
372360
}
373-
if (getClass() != obj.getClass()) {
374-
return false;
375-
}
376-
MiniMaxChatOptions other = (MiniMaxChatOptions) obj;
377-
if (this.model == null) {
378-
if (other.model != null) {
379-
return false;
380-
}
381-
}
382-
else if (!this.model.equals(other.model)) {
383-
return false;
384-
}
385-
if (this.frequencyPenalty == null) {
386-
if (other.frequencyPenalty != null) {
387-
return false;
388-
}
389-
}
390-
else if (!this.frequencyPenalty.equals(other.frequencyPenalty)) {
391-
return false;
392-
}
393-
if (this.maxTokens == null) {
394-
if (other.maxTokens != null) {
395-
return false;
396-
}
397-
}
398-
else if (!this.maxTokens.equals(other.maxTokens)) {
399-
return false;
400-
}
401-
if (this.n == null) {
402-
if (other.n != null) {
403-
return false;
404-
}
405-
}
406-
else if (!this.n.equals(other.n)) {
407-
return false;
408-
}
409-
if (this.presencePenalty == null) {
410-
if (other.presencePenalty != null) {
411-
return false;
412-
}
413-
}
414-
else if (!this.presencePenalty.equals(other.presencePenalty)) {
415-
return false;
416-
}
417-
if (this.responseFormat == null) {
418-
if (other.responseFormat != null) {
419-
return false;
420-
}
421-
}
422-
else if (!this.responseFormat.equals(other.responseFormat)) {
423-
return false;
424-
}
425-
if (this.seed == null) {
426-
if (other.seed != null) {
427-
return false;
428-
}
429-
}
430-
else if (!this.seed.equals(other.seed)) {
431-
return false;
432-
}
433-
if (this.stop == null) {
434-
if (other.stop != null) {
435-
return false;
436-
}
437-
}
438-
else if (!this.stop.equals(other.stop)) {
439-
return false;
440-
}
441-
if (this.temperature == null) {
442-
if (other.temperature != null) {
443-
return false;
444-
}
445-
}
446-
else if (!this.temperature.equals(other.temperature)) {
447-
return false;
448-
}
449-
if (this.topP == null) {
450-
if (other.topP != null) {
451-
return false;
452-
}
453-
}
454-
else if (!this.topP.equals(other.topP)) {
455-
return false;
456-
}
457-
if (this.maskSensitiveInfo == null) {
458-
if (other.maskSensitiveInfo != null) {
459-
return false;
460-
}
461-
}
462-
else if (!this.maskSensitiveInfo.equals(other.maskSensitiveInfo)) {
463-
return false;
464-
}
465-
if (this.tools == null) {
466-
if (other.tools != null) {
467-
return false;
468-
}
469-
}
470-
else if (!this.tools.equals(other.tools)) {
471-
return false;
472-
}
473-
if (this.toolChoice == null) {
474-
if (other.toolChoice != null) {
475-
return false;
476-
}
477-
}
478-
else if (!this.toolChoice.equals(other.toolChoice)) {
479-
return false;
480-
}
481-
if (this.proxyToolCalls == null) {
482-
if (other.proxyToolCalls != null) {
483-
return false;
484-
}
485-
}
486-
else if (!this.proxyToolCalls.equals(other.proxyToolCalls)) {
487-
return false;
488-
}
489-
490-
if (this.toolContext == null) {
491-
if (other.toolContext != null) {
492-
return false;
493-
}
494-
}
495-
else if (!this.toolContext.equals(other.toolContext)) {
496-
return false;
497-
}
498-
499-
return true;
361+
return Objects.equals(this.model, that.model) && Objects.equals(this.frequencyPenalty, that.frequencyPenalty)
362+
&& Objects.equals(this.maxTokens, that.maxTokens) && Objects.equals(this.n, that.n)
363+
&& Objects.equals(this.presencePenalty, that.presencePenalty)
364+
&& Objects.equals(this.responseFormat, that.responseFormat) && Objects.equals(this.seed, that.seed)
365+
&& Objects.equals(this.stop, that.stop) && Objects.equals(this.temperature, that.temperature)
366+
&& Objects.equals(this.topP, that.topP)
367+
&& Objects.equals(this.maskSensitiveInfo, that.maskSensitiveInfo)
368+
&& Objects.equals(this.tools, that.tools) && Objects.equals(this.toolChoice, that.toolChoice)
369+
&& Objects.equals(this.proxyToolCalls, that.proxyToolCalls)
370+
&& Objects.equals(this.toolContext, that.toolContext);
500371
}
501372

502373
@Override
374+
@SuppressWarnings("unchecked")
503375
public MiniMaxChatOptions copy() {
504376
return fromOptions(this);
505377
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Copyright 2025-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ai.minimax;
18+
19+
import org.junit.jupiter.api.Test;
20+
import org.springframework.ai.minimax.api.MiniMaxApi;
21+
22+
import java.util.List;
23+
import java.util.Map;
24+
25+
import static org.assertj.core.api.Assertions.assertThat;
26+
27+
/**
28+
* Tests for {@link MiniMaxChatOptions}.
29+
*
30+
* @author Alexandros Pappas
31+
*/
32+
class MiniMaxChatOptionsTests {
33+
34+
@Test
35+
void testBuilderWithAllFields() {
36+
MiniMaxChatOptions options = MiniMaxChatOptions.builder()
37+
.model("test-model")
38+
.frequencyPenalty(0.5)
39+
.maxTokens(10)
40+
.N(1)
41+
.presencePenalty(0.5)
42+
.responseFormat(new MiniMaxApi.ChatCompletionRequest.ResponseFormat("text"))
43+
.seed(1)
44+
.stop(List.of("test"))
45+
.temperature(0.6)
46+
.topP(0.6)
47+
.maskSensitiveInfo(false)
48+
.toolChoice("test")
49+
.proxyToolCalls(true)
50+
.toolContext(Map.of("key1", "value1"))
51+
.build();
52+
53+
assertThat(options)
54+
.extracting("model", "frequencyPenalty", "maxTokens", "N", "presencePenalty", "responseFormat", "seed",
55+
"stopSequences", "temperature", "topP", "maskSensitiveInfo", "toolChoice", "proxyToolCalls",
56+
"toolContext")
57+
.containsExactly("test-model", 0.5, 10, 1, 0.5, new MiniMaxApi.ChatCompletionRequest.ResponseFormat("text"),
58+
1, List.of("test"), 0.6, 0.6, false, "test", true, Map.of("key1", "value1"));
59+
}
60+
61+
@Test
62+
void testCopy() {
63+
MiniMaxChatOptions original = MiniMaxChatOptions.builder()
64+
.model("test-model")
65+
.frequencyPenalty(0.5)
66+
.maxTokens(10)
67+
.N(1)
68+
.presencePenalty(0.5)
69+
.responseFormat(new MiniMaxApi.ChatCompletionRequest.ResponseFormat("text"))
70+
.seed(1)
71+
.stop(List.of("test"))
72+
.temperature(0.6)
73+
.topP(0.6)
74+
.maskSensitiveInfo(false)
75+
.toolChoice("test")
76+
.proxyToolCalls(true)
77+
.toolContext(Map.of("key1", "value1"))
78+
.build();
79+
80+
MiniMaxChatOptions copied = original.copy();
81+
82+
assertThat(copied).isNotSameAs(original).isEqualTo(original);
83+
// Ensure deep copy
84+
assertThat(copied.getStop()).isNotSameAs(original.getStop());
85+
assertThat(copied.getToolContext()).isNotSameAs(original.getToolContext());
86+
}
87+
88+
@Test
89+
void testSetters() {
90+
MiniMaxChatOptions options = new MiniMaxChatOptions();
91+
options.setModel("test-model");
92+
options.setFrequencyPenalty(0.5);
93+
options.setMaxTokens(10);
94+
options.setN(1);
95+
options.setPresencePenalty(0.5);
96+
options.setResponseFormat(new MiniMaxApi.ChatCompletionRequest.ResponseFormat("text"));
97+
options.setSeed(1);
98+
options.setStopSequences(List.of("test"));
99+
options.setTemperature(0.6);
100+
options.setTopP(0.6);
101+
options.setMaskSensitiveInfo(false);
102+
options.setToolChoice("test");
103+
options.setProxyToolCalls(true);
104+
options.setToolContext(Map.of("key1", "value1"));
105+
106+
assertThat(options.getModel()).isEqualTo("test-model");
107+
assertThat(options.getFrequencyPenalty()).isEqualTo(0.5);
108+
assertThat(options.getMaxTokens()).isEqualTo(10);
109+
assertThat(options.getN()).isEqualTo(1);
110+
assertThat(options.getPresencePenalty()).isEqualTo(0.5);
111+
assertThat(options.getResponseFormat()).isEqualTo(new MiniMaxApi.ChatCompletionRequest.ResponseFormat("text"));
112+
assertThat(options.getSeed()).isEqualTo(1);
113+
assertThat(options.getStopSequences()).isEqualTo(List.of("test"));
114+
assertThat(options.getTemperature()).isEqualTo(0.6);
115+
assertThat(options.getTopP()).isEqualTo(0.6);
116+
assertThat(options.getMaskSensitiveInfo()).isEqualTo(false);
117+
assertThat(options.getToolChoice()).isEqualTo("test");
118+
assertThat(options.getProxyToolCalls()).isEqualTo(true);
119+
assertThat(options.getToolContext()).isEqualTo(Map.of("key1", "value1"));
120+
}
121+
122+
@Test
123+
void testDefaultValues() {
124+
MiniMaxChatOptions options = new MiniMaxChatOptions();
125+
assertThat(options.getModel()).isNull();
126+
assertThat(options.getFrequencyPenalty()).isNull();
127+
assertThat(options.getMaxTokens()).isNull();
128+
assertThat(options.getN()).isNull();
129+
assertThat(options.getPresencePenalty()).isNull();
130+
assertThat(options.getResponseFormat()).isNull();
131+
assertThat(options.getSeed()).isNull();
132+
assertThat(options.getStopSequences()).isNull();
133+
assertThat(options.getTemperature()).isNull();
134+
assertThat(options.getTopP()).isNull();
135+
assertThat(options.getMaskSensitiveInfo()).isNull();
136+
assertThat(options.getToolChoice()).isNull();
137+
assertThat(options.getProxyToolCalls()).isNull();
138+
assertThat(options.getToolContext()).isEqualTo(new java.util.HashMap<>());
139+
}
140+
141+
}

0 commit comments

Comments
 (0)