Make FunctionCallingOptions collections immutable

Prevent external modification of internal state by returning unmodifiable
collections and adding defensive copies in setters and builders.

Co-authored-by: youngmoneee@users.noreply.github.com
This commit is contained in:
Mark Pollack
2024-10-22 16:52:55 -04:00
committed by Christian Tzolov
parent c544d0c0a9
commit cf75640c86

View File

@@ -16,6 +16,7 @@
package org.springframework.ai.model.function; package org.springframework.ai.model.function;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@@ -111,14 +112,18 @@ public class FunctionCallingOptionsBuilder {
public FunctionCallingOptionsBuilder withToolContext(Map<String, Object> context) { public FunctionCallingOptionsBuilder withToolContext(Map<String, Object> context) {
Assert.notNull(context, "Tool context must not be null"); Assert.notNull(context, "Tool context must not be null");
this.options.getToolContext().putAll(context); Map<String, Object> newContext = new HashMap<>(this.options.getToolContext());
newContext.putAll(context);
this.options.setToolContext(newContext);
return this; return this;
} }
public FunctionCallingOptionsBuilder withToolContext(String key, Object value) { public FunctionCallingOptionsBuilder withToolContext(String key, Object value) {
Assert.notNull(key, "Key must not be null"); Assert.notNull(key, "Key must not be null");
Assert.notNull(value, "Value must not be null"); Assert.notNull(value, "Value must not be null");
this.options.getToolContext().put(key, value); Map<String, Object> newContext = new HashMap<>(this.options.getToolContext());
newContext.put(key, value);
this.options.setToolContext(newContext);
return this; return this;
} }
@@ -158,22 +163,22 @@ public class FunctionCallingOptionsBuilder {
@Override @Override
public List<FunctionCallback> getFunctionCallbacks() { public List<FunctionCallback> getFunctionCallbacks() {
return this.functionCallbacks; return Collections.unmodifiableList(this.functionCallbacks);
} }
public void setFunctionCallbacks(List<FunctionCallback> functionCallbacks) { public void setFunctionCallbacks(List<FunctionCallback> functionCallbacks) {
Assert.notNull(functionCallbacks, "FunctionCallbacks must not be null"); Assert.notNull(functionCallbacks, "FunctionCallbacks must not be null");
this.functionCallbacks = functionCallbacks; this.functionCallbacks = new ArrayList<>(functionCallbacks);
} }
@Override @Override
public Set<String> getFunctions() { public Set<String> getFunctions() {
return this.functions; return Collections.unmodifiableSet(this.functions);
} }
public void setFunctions(Set<String> functions) { public void setFunctions(Set<String> functions) {
Assert.notNull(functions, "Functions must not be null"); Assert.notNull(functions, "Functions must not be null");
this.functions = functions; this.functions = new HashSet<>(functions);
} }
@Override @Override
@@ -258,11 +263,12 @@ public class FunctionCallingOptionsBuilder {
} }
public Map<String, Object> getToolContext() { public Map<String, Object> getToolContext() {
return context; return Collections.unmodifiableMap(this.context);
} }
public void setToolContext(Map<String, Object> context) { public void setToolContext(Map<String, Object> context) {
this.context = context; Assert.notNull(context, "Context must not be null");
this.context = new HashMap<>(context);
} }
@Override @Override
@@ -271,14 +277,14 @@ public class FunctionCallingOptionsBuilder {
.withFrequencyPenalty(this.frequencyPenalty) .withFrequencyPenalty(this.frequencyPenalty)
.withMaxTokens(this.maxTokens) .withMaxTokens(this.maxTokens)
.withPresencePenalty(this.presencePenalty) .withPresencePenalty(this.presencePenalty)
.withStopSequences(this.stopSequences) .withStopSequences(this.stopSequences != null ? new ArrayList<>(this.stopSequences) : null)
.withTemperature(this.temperature) .withTemperature(this.temperature)
.withTopK(this.topK) .withTopK(this.topK)
.withTopP(this.topP) .withTopP(this.topP)
.withFunctions(this.functions) .withFunctions(new HashSet<>(this.functions))
.withFunctionCallbacks(this.functionCallbacks) .withFunctionCallbacks(new ArrayList<>(this.functionCallbacks))
.withProxyToolCalls(this.proxyToolCalls) .withProxyToolCalls(this.proxyToolCalls)
.withToolContext(this.getToolContext()) .withToolContext(new HashMap<>(this.getToolContext()))
.build(); .build();
} }