From a3c0927c9bc69655da94914dfa87e7c6051901da Mon Sep 17 00:00:00 2001 From: GR Date: Thu, 15 Aug 2024 12:29:51 +0800 Subject: [PATCH] Add MiniMax chat option to mask sensitive information This commit introduces a new boolean option 'maskSensitiveInfo' for the MiniMax API model support in Spring AI. This feature allows users to control whether sensitive information in the output is masked. Relevant unit tests have been added to ensure proper functionality. Resolves: #1216 --- .../ai/minimax/MiniMaxChatOptions.java | 27 ++++++++++ .../ai/minimax/api/MiniMaxApi.java | 12 +++-- .../minimax/chat/MiniMaxChatOptionsTests.java | 49 +++++++++++++++++++ 3 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 models/spring-ai-minimax/src/test/java/org/springframework/ai/minimax/chat/MiniMaxChatOptionsTests.java diff --git a/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/MiniMaxChatOptions.java b/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/MiniMaxChatOptions.java index b924c0084..f1bf80773 100644 --- a/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/MiniMaxChatOptions.java +++ b/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/MiniMaxChatOptions.java @@ -99,6 +99,12 @@ public class MiniMaxChatOptions implements FunctionCallingOptions, ChatOptions { * probability mass are considered. We generally recommend altering this or temperature but not both. */ private @JsonProperty("top_p") Float topP; + /** + * Mask the text information in the output that is easy to involve privacy issues, + * including but not limited to email, domain name, link, ID number, home address, etc. + * The default is true, which means enabling masking. + */ + private @JsonProperty("mask_sensitive_info") Boolean maskSensitiveInfo; /** * 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 for. @@ -204,6 +210,11 @@ public class MiniMaxChatOptions implements FunctionCallingOptions, ChatOptions { return this; } + public Builder withMaskSensitiveInfo(Boolean maskSensitiveInfo) { + this.options.maskSensitiveInfo = maskSensitiveInfo; + return this; + } + public Builder withTools(List tools) { this.options.tools = tools; return this; @@ -334,6 +345,14 @@ public class MiniMaxChatOptions implements FunctionCallingOptions, ChatOptions { this.topP = topP; } + public Boolean getMaskSensitiveInfo() { + return maskSensitiveInfo; + } + + public void setMaskSensitiveInfo(Boolean maskSensitiveInfo) { + this.maskSensitiveInfo = maskSensitiveInfo; + } + public List getTools() { return this.tools; } @@ -389,6 +408,7 @@ public class MiniMaxChatOptions implements FunctionCallingOptions, ChatOptions { result = prime * result + ((stop == null) ? 0 : stop.hashCode()); result = prime * result + ((temperature == null) ? 0 : temperature.hashCode()); result = prime * result + ((topP == null) ? 0 : topP.hashCode()); + result = prime * result + ((maskSensitiveInfo == null) ? 0 : maskSensitiveInfo.hashCode()); result = prime * result + ((tools == null) ? 0 : tools.hashCode()); result = prime * result + ((toolChoice == null) ? 0 : toolChoice.hashCode()); return result; @@ -463,6 +483,12 @@ public class MiniMaxChatOptions implements FunctionCallingOptions, ChatOptions { } else if (!topP.equals(other.topP)) return false; + if (this.maskSensitiveInfo == null) { + if (other.maskSensitiveInfo != null) + return false; + } + else if (!maskSensitiveInfo.equals(other.maskSensitiveInfo)) + return false; if (this.tools == null) { if (other.tools != null) return false; @@ -494,6 +520,7 @@ public class MiniMaxChatOptions implements FunctionCallingOptions, ChatOptions { .withStop(fromOptions.getStop()) .withTemperature(fromOptions.getTemperature()) .withTopP(fromOptions.getTopP()) + .withMaskSensitiveInfo(fromOptions.getMaskSensitiveInfo()) .withTools(fromOptions.getTools()) .withToolChoice(fromOptions.getToolChoice()) .withFunctionCallbacks(fromOptions.getFunctionCallbacks()) diff --git a/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/api/MiniMaxApi.java b/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/api/MiniMaxApi.java index aedaed9d4..eb4923464 100644 --- a/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/api/MiniMaxApi.java +++ b/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/api/MiniMaxApi.java @@ -227,6 +227,9 @@ public class MiniMaxApi { * @param topP An alternative to sampling with temperature, called nucleus sampling, where the model considers the * results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% * probability mass are considered. We generally recommend altering this or temperature but not both. + * @param maskSensitiveInfo Mask the text information in the output that is easy to involve privacy issues, + * including but not limited to email, domain name, link, ID number, home address, etc. The default is true, + * which means enabling masking. * @param tools 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 for. * @param toolChoice Controls which (if any) function is called by the model. none means the model will not call a @@ -249,6 +252,7 @@ public class MiniMaxApi { @JsonProperty("stream") Boolean stream, @JsonProperty("temperature") Float temperature, @JsonProperty("top_p") Float topP, + @JsonProperty("mask_sensitive_info") Boolean maskSensitiveInfo, @JsonProperty("tools") List tools, @JsonProperty("tool_choice") Object toolChoice) { @@ -261,7 +265,7 @@ public class MiniMaxApi { */ public ChatCompletionRequest(List messages, String model, Float temperature) { this(messages, model, null, null, null, null, - null, null, null, false, temperature, null, + null, null, null, false, temperature, null,null, null, null); } @@ -276,7 +280,7 @@ public class MiniMaxApi { */ public ChatCompletionRequest(List messages, String model, Float temperature, boolean stream) { this(messages, model, null, null, null, null, - null, null, null, stream, temperature, null, + null, null, null, stream, temperature, null,null, null, null); } @@ -292,7 +296,7 @@ public class MiniMaxApi { public ChatCompletionRequest(List messages, String model, List tools, Object toolChoice) { this(messages, model, null, null, null, null, - null, null, null, false, 0.8f, null, + null, null, null, false, 0.8f, null,null, tools, toolChoice); } @@ -306,7 +310,7 @@ public class MiniMaxApi { */ public ChatCompletionRequest(List messages, Boolean stream) { this(messages, null, null, null, null, null, - null, null, null, stream, null, null, + null, null, null, stream, null, null,null, null, null); } diff --git a/models/spring-ai-minimax/src/test/java/org/springframework/ai/minimax/chat/MiniMaxChatOptionsTests.java b/models/spring-ai-minimax/src/test/java/org/springframework/ai/minimax/chat/MiniMaxChatOptionsTests.java new file mode 100644 index 000000000..35326fa6e --- /dev/null +++ b/models/spring-ai-minimax/src/test/java/org/springframework/ai/minimax/chat/MiniMaxChatOptionsTests.java @@ -0,0 +1,49 @@ +package org.springframework.ai.minimax.chat; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.springframework.ai.chat.messages.Message; +import org.springframework.ai.chat.messages.UserMessage; +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.minimax.MiniMaxChatModel; +import org.springframework.ai.minimax.MiniMaxChatOptions; +import org.springframework.ai.minimax.api.MiniMaxApi; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Geng Rong + */ +@EnabledIfEnvironmentVariable(named = "MINIMAX_API_KEY", matches = ".+") +public class MiniMaxChatOptionsTests { + + private final MiniMaxChatModel chatModel = new MiniMaxChatModel(new MiniMaxApi(System.getenv("MINIMAX_API_KEY"))); + + @Test + void testMarkSensitiveInfo() { + + UserMessage userMessage = new UserMessage( + "Please extract the phone number, the content: My name is Bob, and my phone number is 133-12345678"); + + List messages = new ArrayList<>(List.of(userMessage)); + + // markSensitiveInfo is enabled by default + ChatResponse response = chatModel.call(new Prompt(messages)); + String responseContent = response.getResult().getOutput().getContent(); + + assertThat(responseContent).contains("133-**"); + assertThat(responseContent).doesNotContain("133-12345678"); + + var chatOptions = MiniMaxChatOptions.builder().withMaskSensitiveInfo(false).build(); + + ChatResponse unmaskResponse = chatModel.call(new Prompt(messages, chatOptions)); + String unmaskResponseContent = unmaskResponse.getResult().getOutput().getContent(); + + assertThat(unmaskResponseContent).contains("133-12345678"); + } + +}