feat: Add equals, hashCode, deep copy, and tests to OCICohereChatOptions (#2361)

This commit enhances OCICohereChatOptions 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 OCICohereChatOptionsTests to verify copy(), builders, setters, and default values.

Signed-off-by: Alexandros Pappas <apappascs@gmail.com>
This commit is contained in:
Alexandros Pappas
2025-04-28 18:03:53 +02:00
committed by GitHub
parent e5faad5ea4
commit dc5922fe9a
2 changed files with 167 additions and 4 deletions

View File

@@ -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.
@@ -16,7 +16,9 @@
package org.springframework.ai.oci.cohere;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -29,6 +31,7 @@ import org.springframework.ai.chat.prompt.ChatOptions;
*
* @author Anders Swanson
* @author Ilayaperumal Gopinathan
* @author Alexnadros Pappas
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class OCICohereChatOptions implements ChatOptions {
@@ -124,11 +127,11 @@ public class OCICohereChatOptions implements ChatOptions {
.temperature(fromOptions.temperature)
.topP(fromOptions.topP)
.topK(fromOptions.topK)
.stop(fromOptions.stop)
.stop(fromOptions.stop != null ? new ArrayList<>(fromOptions.stop) : null)
.frequencyPenalty(fromOptions.frequencyPenalty)
.presencePenalty(fromOptions.presencePenalty)
.documents(fromOptions.documents)
.tools(fromOptions.tools)
.documents(fromOptions.documents != null ? new ArrayList<>(fromOptions.documents) : null)
.tools(fromOptions.tools != null ? new ArrayList<>(fromOptions.tools) : null)
.build();
}
@@ -257,10 +260,37 @@ public class OCICohereChatOptions implements ChatOptions {
}
@Override
@SuppressWarnings("unchecked")
public ChatOptions copy() {
return fromOptions(this);
}
@Override
public int hashCode() {
return Objects.hash(model, maxTokens, compartment, servingMode, preambleOverride, temperature, topP, topK, stop,
frequencyPenalty, presencePenalty, documents, tools);
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
OCICohereChatOptions that = (OCICohereChatOptions) o;
return Objects.equals(this.model, that.model) && Objects.equals(this.maxTokens, that.maxTokens)
&& Objects.equals(this.compartment, that.compartment)
&& Objects.equals(this.servingMode, that.servingMode)
&& Objects.equals(this.preambleOverride, that.preambleOverride)
&& Objects.equals(this.temperature, that.temperature) && Objects.equals(this.topP, that.topP)
&& Objects.equals(this.topK, that.topK) && Objects.equals(this.stop, that.stop)
&& Objects.equals(this.frequencyPenalty, that.frequencyPenalty)
&& Objects.equals(this.presencePenalty, that.presencePenalty)
&& Objects.equals(this.documents, that.documents) && Objects.equals(this.tools, that.tools);
}
public static class Builder {
protected OCICohereChatOptions chatOptions;

View File

@@ -0,0 +1,133 @@
/*
* 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.oci.cohere;
import com.oracle.bmc.generativeaiinference.model.CohereTool;
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 OCICohereChatOptions}.
*
* @author Alexandros Pappas
*/
class OCICohereChatOptionsTests {
@Test
void testBuilderWithAllFields() {
OCICohereChatOptions options = OCICohereChatOptions.builder()
.model("test-model")
.maxTokens(10)
.compartment("test-compartment")
.servingMode("test-servingMode")
.preambleOverride("test-preambleOverride")
.temperature(0.6)
.topP(0.6)
.topK(50)
.stop(List.of("test"))
.frequencyPenalty(0.5)
.presencePenalty(0.5)
.documents(List.of("doc1", "doc2"))
.build();
assertThat(options)
.extracting("model", "maxTokens", "compartment", "servingMode", "preambleOverride", "temperature", "topP",
"topK", "stop", "frequencyPenalty", "presencePenalty", "documents")
.containsExactly("test-model", 10, "test-compartment", "test-servingMode", "test-preambleOverride", 0.6,
0.6, 50, List.of("test"), 0.5, 0.5, List.of("doc1", "doc2"));
}
@Test
void testCopy() {
OCICohereChatOptions original = OCICohereChatOptions.builder()
.model("test-model")
.maxTokens(10)
.compartment("test-compartment")
.servingMode("test-servingMode")
.preambleOverride("test-preambleOverride")
.temperature(0.6)
.topP(0.6)
.topK(50)
.stop(List.of("test"))
.frequencyPenalty(0.5)
.presencePenalty(0.5)
.documents(List.of("doc1", "doc2"))
.tools(List.of(new CohereTool("test-tool", "test-context", Map.of())))
.build();
OCICohereChatOptions copied = (OCICohereChatOptions) original.copy();
assertThat(copied).isNotSameAs(original).isEqualTo(original);
// Ensure deep copy
assertThat(copied.getStop()).isNotSameAs(original.getStop());
assertThat(copied.getDocuments()).isNotSameAs(original.getDocuments());
assertThat(copied.getTools()).isNotSameAs(original.getTools());
}
@Test
void testSetters() {
OCICohereChatOptions options = new OCICohereChatOptions();
options.setModel("test-model");
options.setMaxTokens(10);
options.setCompartment("test-compartment");
options.setServingMode("test-servingMode");
options.setPreambleOverride("test-preambleOverride");
options.setTemperature(0.6);
options.setTopP(0.6);
options.setTopK(50);
options.setStop(List.of("test"));
options.setFrequencyPenalty(0.5);
options.setPresencePenalty(0.5);
options.setDocuments(List.of("doc1", "doc2"));
assertThat(options.getModel()).isEqualTo("test-model");
assertThat(options.getMaxTokens()).isEqualTo(10);
assertThat(options.getCompartment()).isEqualTo("test-compartment");
assertThat(options.getServingMode()).isEqualTo("test-servingMode");
assertThat(options.getPreambleOverride()).isEqualTo("test-preambleOverride");
assertThat(options.getTemperature()).isEqualTo(0.6);
assertThat(options.getTopP()).isEqualTo(0.6);
assertThat(options.getTopK()).isEqualTo(50);
assertThat(options.getStop()).isEqualTo(List.of("test"));
assertThat(options.getFrequencyPenalty()).isEqualTo(0.5);
assertThat(options.getPresencePenalty()).isEqualTo(0.5);
assertThat(options.getDocuments()).isEqualTo(List.of("doc1", "doc2"));
}
@Test
void testDefaultValues() {
OCICohereChatOptions options = new OCICohereChatOptions();
assertThat(options.getModel()).isNull();
assertThat(options.getMaxTokens()).isNull();
assertThat(options.getCompartment()).isNull();
assertThat(options.getServingMode()).isNull();
assertThat(options.getPreambleOverride()).isNull();
assertThat(options.getTemperature()).isNull();
assertThat(options.getTopP()).isNull();
assertThat(options.getTopK()).isNull();
assertThat(options.getStop()).isNull();
assertThat(options.getFrequencyPenalty()).isNull();
assertThat(options.getPresencePenalty()).isNull();
assertThat(options.getDocuments()).isNull();
assertThat(options.getTools()).isNull();
}
}