From b74e30816c30f73df938dacb344052261319618a Mon Sep 17 00:00:00 2001 From: Seunghyeon Ji <3aroa2@gmail.com> Date: Fri, 11 Apr 2025 21:42:40 +0900 Subject: [PATCH] Add three request body parameters to Mistral AI Chat Completion Add presence_penalty, frequency_penalty, and n parameters Following Mistral AI API specifications as referenced in https://docs.mistral.ai/api/#tag/chat Rename Builder method N() to n() Signed-off-by: Seunghyeon Ji <3aroa2@gmail.com> --- .../ai/mistralai/MistralAiChatOptions.java | 84 +++++++++++++++---- .../MistralAiChatModelObservationIT.java | 10 ++- 2 files changed, 78 insertions(+), 16 deletions(-) diff --git a/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatOptions.java b/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatOptions.java index 700e12dfe..2b392d517 100644 --- a/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatOptions.java +++ b/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatOptions.java @@ -100,6 +100,28 @@ public class MistralAiChatOptions implements ToolCallingChatOptions { */ private @JsonProperty("stop") List stop; + /** + * Number between -2.0 and 2.0. frequency_penalty penalizes the repetition of words + * based on their frequency in the generated text. A higher frequency penalty + * discourages the model from repeating words that have already appeared frequently in + * the output, promoting diversity and reducing repetition. + */ + private @JsonProperty("frequency_penalty") Double frequencyPenalty; + + /** + * Number between -2.0 and 2.0. presence_penalty determines how much the model + * penalizes the repetition of words or phrases. A higher presence penalty encourages + * the model to use a wider variety of words and phrases, making the output more + * diverse and creative. + */ + private @JsonProperty("presence_penalty") Double presencePenalty; + + /** + * Number of completions to return for each request, input tokens are only billed + * once. + */ + private @JsonProperty("n") Integer n; + /** * A list of tools the model may call. Currently, only functions are supported as a * tool. Use this to provide a list of functions the model may generate JSON inputs @@ -150,6 +172,9 @@ public class MistralAiChatOptions implements ToolCallingChatOptions { .topP(fromOptions.getTopP()) .responseFormat(fromOptions.getResponseFormat()) .stop(fromOptions.getStop()) + .frequencyPenalty(fromOptions.getFrequencyPenalty()) + .presencePenalty(fromOptions.getPresencePenalty()) + .n(fromOptions.getN()) .tools(fromOptions.getTools()) .toolChoice(fromOptions.getToolChoice()) .toolCallbacks(fromOptions.getToolCallbacks()) @@ -254,6 +279,32 @@ public class MistralAiChatOptions implements ToolCallingChatOptions { this.topP = topP; } + @Override + public Double getFrequencyPenalty() { + return this.frequencyPenalty; + } + + public void setFrequencyPenalty(Double frequencyPenalty) { + this.frequencyPenalty = frequencyPenalty; + } + + @Override + public Double getPresencePenalty() { + return this.presencePenalty; + } + + public void setPresencePenalty(Double presencePenalty) { + this.presencePenalty = presencePenalty; + } + + public Integer getN() { + return this.n; + } + + public void setN(Integer n) { + this.n = n; + } + @Override @JsonIgnore public List getToolCallbacks() { @@ -296,18 +347,6 @@ public class MistralAiChatOptions implements ToolCallingChatOptions { this.internalToolExecutionEnabled = internalToolExecutionEnabled; } - @Override - @JsonIgnore - public Double getFrequencyPenalty() { - return null; - } - - @Override - @JsonIgnore - public Double getPresencePenalty() { - return null; - } - @Override @JsonIgnore public Integer getTopK() { @@ -334,8 +373,8 @@ public class MistralAiChatOptions implements ToolCallingChatOptions { @Override public int hashCode() { return Objects.hash(this.model, this.temperature, this.topP, this.maxTokens, this.safePrompt, this.randomSeed, - this.responseFormat, this.stop, this.tools, this.toolChoice, this.toolCallbacks, this.tools, - this.internalToolExecutionEnabled, this.toolContext); + this.responseFormat, this.stop, this.frequencyPenalty, this.presencePenalty, this.n, this.tools, + this.toolChoice, this.toolCallbacks, this.tools, this.internalToolExecutionEnabled, this.toolContext); } @Override @@ -355,6 +394,8 @@ public class MistralAiChatOptions implements ToolCallingChatOptions { && Objects.equals(this.safePrompt, other.safePrompt) && Objects.equals(this.randomSeed, other.randomSeed) && Objects.equals(this.responseFormat, other.responseFormat) && Objects.equals(this.stop, other.stop) + && Objects.equals(this.frequencyPenalty, other.frequencyPenalty) + && Objects.equals(this.presencePenalty, other.presencePenalty) && Objects.equals(this.n, other.n) && Objects.equals(this.tools, other.tools) && Objects.equals(this.toolChoice, other.toolChoice) && Objects.equals(this.toolCallbacks, other.toolCallbacks) && Objects.equals(this.toolNames, other.toolNames) @@ -396,6 +437,21 @@ public class MistralAiChatOptions implements ToolCallingChatOptions { return this; } + public Builder frequencyPenalty(Double frequencyPenalty) { + this.options.frequencyPenalty = frequencyPenalty; + return this; + } + + public Builder presencePenalty(Double presencePenalty) { + this.options.presencePenalty = presencePenalty; + return this; + } + + public Builder n(Integer n) { + this.options.n = n; + return this; + } + public Builder temperature(Double temperature) { this.options.setTemperature(temperature); return this; diff --git a/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatModelObservationIT.java b/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatModelObservationIT.java index 31070144b..7e2aeed21 100644 --- a/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatModelObservationIT.java +++ b/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatModelObservationIT.java @@ -74,6 +74,9 @@ public class MistralAiChatModelObservationIT { .stop(List.of("this-is-the-end")) .temperature(0.7) .topP(1.0) + .presencePenalty(0.0) + .frequencyPenalty(0.0) + .n(2) .build(); Prompt prompt = new Prompt("Why does a raven look like a desk?", options); @@ -95,6 +98,9 @@ public class MistralAiChatModelObservationIT { .stop(List.of("this-is-the-end")) .temperature(0.7) .topP(1.0) + .presencePenalty(0.0) + .frequencyPenalty(0.0) + .n(2) .build(); Prompt prompt = new Prompt("Why does a raven look like a desk?", options); @@ -133,9 +139,9 @@ public class MistralAiChatModelObservationIT { .hasLowCardinalityKeyValue(LowCardinalityKeyNames.RESPONSE_MODEL.asString(), StringUtils.hasText(responseMetadata.getModel()) ? responseMetadata.getModel() : KeyValue.NONE_VALUE) - .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.REQUEST_FREQUENCY_PENALTY.asString()) + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.REQUEST_FREQUENCY_PENALTY.asString(), "0.0") + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.REQUEST_PRESENCE_PENALTY.asString(), "0.0") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.REQUEST_MAX_TOKENS.asString(), "2048") - .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.REQUEST_PRESENCE_PENALTY.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.REQUEST_STOP_SEQUENCES.asString(), "[\"this-is-the-end\"]") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.REQUEST_TEMPERATURE.asString(), "0.7")