Skip to content

Commit e287b35

Browse files
Data Masking Convenience
1 parent a9be1ed commit e287b35

File tree

5 files changed

+111
-62
lines changed

5 files changed

+111
-62
lines changed

docs/guides/ORCHESTRATION_CHAT_COMPLETION.md

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -184,15 +184,8 @@ var result =
184184
Use the data masking module to anonymize personal information in the input:
185185

186186
```java
187-
var maskingProvider =
188-
MaskingProviderConfig.create()
189-
.type(MaskingProviderConfig.TypeEnum.SAP_DATA_PRIVACY_INTEGRATION)
190-
.method(MaskingProviderConfig.MethodEnum.ANONYMIZATION)
191-
.entities(
192-
DPIEntityConfig.create().type(DPIEntities.PHONE),
193-
DPIEntityConfig.create().type(DPIEntities.PERSON));
194-
var maskingConfig = MaskingModuleConfig.create().maskingProviders(maskingProvider);
195-
var configWithMasking = config.withMaskingConfig(maskingConfig);
187+
var maskingConfig = DpiMaskingConfig.pseudonymization().withEntities(DPIEntities.PERSON, DPIEntities.EMAIL);
188+
var configWithMasking = config.withDpiMaskingConfig(maskingConfig);
196189

197190
var systemMessage = ChatMessage.create()
198191
.role("system")
@@ -210,7 +203,7 @@ var result =
210203
new OrchestrationClient().chatCompletion(prompt, configWithMasking);
211204
```
212205

213-
In this example, the input will be masked before the call to the LLM. Note that data cannot be unmasked in the LLM output.
206+
In this example, the input will be masked before the call to the LLM and unmasked in the output.
214207

215208
### Set model parameters
216209

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package com.sap.ai.sdk.orchestration;
2+
3+
import static com.sap.ai.sdk.orchestration.client.model.DPIConfig.MethodEnum.ANONYMIZATION;
4+
import static com.sap.ai.sdk.orchestration.client.model.DPIConfig.MethodEnum.PSEUDONYMIZATION;
5+
import static com.sap.ai.sdk.orchestration.client.model.DPIConfig.TypeEnum.SAP_DATA_PRIVACY_INTEGRATION;
6+
7+
import com.sap.ai.sdk.orchestration.client.model.DPIConfig;
8+
import com.sap.ai.sdk.orchestration.client.model.DPIEntities;
9+
import com.sap.ai.sdk.orchestration.client.model.DPIEntityConfig;
10+
import com.sap.ai.sdk.orchestration.client.model.MaskingModuleConfig;
11+
import java.util.ArrayList;
12+
import java.util.Arrays;
13+
import java.util.List;
14+
import javax.annotation.Nonnull;
15+
import lombok.AccessLevel;
16+
import lombok.Getter;
17+
import lombok.RequiredArgsConstructor;
18+
import lombok.Value;
19+
import lombok.val;
20+
21+
/**
22+
* SAP Data Privacy Integration (DPI) can mask personally identifiable information using either
23+
* anonymization or pseudonymization.
24+
*/
25+
@Value
26+
@Getter(AccessLevel.PACKAGE)
27+
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
28+
public class DpiMaskingConfig {
29+
@Nonnull DPIConfig.MethodEnum maskingMethod;
30+
@Nonnull List<DPIEntities> entities;
31+
32+
/**
33+
* Build a configuration applying anonymization.
34+
*
35+
* @return A builder configured for anonymization
36+
*/
37+
@Nonnull
38+
public static Builder anonymization() {
39+
return new DpiMaskingConfig.Builder(ANONYMIZATION);
40+
}
41+
42+
/**
43+
* Build a configuration applying pseudonymization.
44+
*
45+
* @return A builder configured for pseudonymization
46+
*/
47+
@Nonnull
48+
public static Builder pseudonymization() {
49+
return new DpiMaskingConfig.Builder(PSEUDONYMIZATION);
50+
}
51+
52+
@Nonnull
53+
MaskingModuleConfig createConfig() {
54+
val entitiesDTO = entities.stream().map(it -> new DPIEntityConfig().type(it)).toList();
55+
return new MaskingModuleConfig()
56+
.addMaskingProvidersItem(
57+
new DPIConfig()
58+
.type(SAP_DATA_PRIVACY_INTEGRATION)
59+
.method(maskingMethod)
60+
.entities(entitiesDTO));
61+
}
62+
63+
/**
64+
* Builder for creating DPI masking configurations. Allows specifying which entity types should be
65+
* masked in the input text.
66+
*/
67+
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
68+
public static class Builder {
69+
private final DPIConfig.MethodEnum maskingMethod;
70+
71+
/**
72+
* Specifies which entities should be masked in the input text.
73+
*
74+
* @param entity An entity type to mask (required)
75+
* @param entities Additional entity types to mask (optional)
76+
* @return A configured {@link DpiMaskingConfig} instance
77+
* @see DPIEntities
78+
*/
79+
@Nonnull
80+
public DpiMaskingConfig withEntities(
81+
@Nonnull final DPIEntities entity, @Nonnull final DPIEntities... entities) {
82+
val entitiesList = new ArrayList<DPIEntities>();
83+
entitiesList.add(entity);
84+
entitiesList.addAll(Arrays.asList(entities));
85+
return new DpiMaskingConfig(maskingMethod, entitiesList);
86+
}
87+
}
88+
}

orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfig.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,17 @@ public class OrchestrationModuleConfig {
6262
public OrchestrationModuleConfig withLlmConfig(@Nonnull final OrchestrationAiModel aiModel) {
6363
return withLlmConfig(aiModel.createConfig());
6464
}
65+
66+
/**
67+
* Creates a new configuration with the given Data Masking configuration.
68+
*
69+
* @param dpiMasking The Data Masking configuration to use.
70+
* @return A new configuration with the given Data Masking configuration.
71+
*/
72+
@Tolerate
73+
@Nonnull
74+
public OrchestrationModuleConfig withDpiMaskingConfig(
75+
@Nonnull final DpiMaskingConfig dpiMasking) {
76+
return withMaskingConfig(dpiMasking.createConfig());
77+
}
6578
}

orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationUnitTest.java

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,15 @@
3434
import com.sap.ai.sdk.orchestration.client.model.AzureThreshold;
3535
import com.sap.ai.sdk.orchestration.client.model.ChatMessage;
3636
import com.sap.ai.sdk.orchestration.client.model.CompletionPostRequest;
37-
import com.sap.ai.sdk.orchestration.client.model.DPIConfig;
3837
import com.sap.ai.sdk.orchestration.client.model.DPIEntities;
39-
import com.sap.ai.sdk.orchestration.client.model.DPIEntityConfig;
4038
import com.sap.ai.sdk.orchestration.client.model.FilteringModuleConfig;
4139
import com.sap.ai.sdk.orchestration.client.model.GenericModuleResult;
4240
import com.sap.ai.sdk.orchestration.client.model.InputFilteringConfig;
4341
import com.sap.ai.sdk.orchestration.client.model.LLMModuleResultSynchronous;
44-
import com.sap.ai.sdk.orchestration.client.model.MaskingModuleConfig;
4542
import com.sap.ai.sdk.orchestration.client.model.OutputFilteringConfig;
4643
import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination;
4744
import java.io.IOException;
4845
import java.io.InputStream;
49-
import java.util.Arrays;
5046
import java.util.List;
5147
import java.util.Map;
5248
import java.util.Objects;
@@ -306,10 +302,9 @@ void maskingPseudonymization() throws IOException {
306302
.withBodyFile("maskingResponse.json")
307303
.withHeader("Content-Type", "application/json")));
308304

309-
final var maskingConfig =
310-
createMaskingConfig(DPIConfig.MethodEnum.PSEUDONYMIZATION, DPIEntities.PHONE);
305+
final var maskingConfig = DpiMaskingConfig.pseudonymization().withEntities(DPIEntities.PHONE);
311306

312-
final var result = client.chatCompletion(prompt, config.withMaskingConfig(maskingConfig));
307+
final var result = client.chatCompletion(prompt, config.withDpiMaskingConfig(maskingConfig));
313308
final var response = result.getOriginalResponse();
314309

315310
assertThat(response).isNotNull();
@@ -328,20 +323,6 @@ void maskingPseudonymization() throws IOException {
328323
}
329324
}
330325

331-
private static MaskingModuleConfig createMaskingConfig(
332-
@Nonnull final DPIConfig.MethodEnum method, @Nonnull final DPIEntities... entities) {
333-
334-
final var entityConfigs =
335-
Arrays.stream(entities).map(it -> new DPIEntityConfig().type(it)).toList();
336-
return new MaskingModuleConfig()
337-
.maskingProviders(
338-
List.of(
339-
new DPIConfig()
340-
.type(DPIConfig.TypeEnum.SAP_DATA_PRIVACY_INTEGRATION)
341-
.method(method)
342-
.entities(entityConfigs)));
343-
}
344-
345326
@Test
346327
void testErrorHandling() {
347328
stubFor(

sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/OrchestrationController.java

Lines changed: 5 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.GPT_35_TURBO;
44

5+
import com.sap.ai.sdk.orchestration.DpiMaskingConfig;
56
import com.sap.ai.sdk.orchestration.OrchestrationChatResponse;
67
import com.sap.ai.sdk.orchestration.OrchestrationClient;
78
import com.sap.ai.sdk.orchestration.OrchestrationModuleConfig;
@@ -10,15 +11,11 @@
1011
import com.sap.ai.sdk.orchestration.client.model.AzureContentSafetyFilterConfig;
1112
import com.sap.ai.sdk.orchestration.client.model.AzureThreshold;
1213
import com.sap.ai.sdk.orchestration.client.model.ChatMessage;
13-
import com.sap.ai.sdk.orchestration.client.model.DPIConfig;
1414
import com.sap.ai.sdk.orchestration.client.model.DPIEntities;
15-
import com.sap.ai.sdk.orchestration.client.model.DPIEntityConfig;
1615
import com.sap.ai.sdk.orchestration.client.model.FilteringModuleConfig;
1716
import com.sap.ai.sdk.orchestration.client.model.InputFilteringConfig;
18-
import com.sap.ai.sdk.orchestration.client.model.MaskingModuleConfig;
1917
import com.sap.ai.sdk.orchestration.client.model.OutputFilteringConfig;
2018
import com.sap.ai.sdk.orchestration.client.model.Template;
21-
import java.util.Arrays;
2219
import java.util.List;
2320
import java.util.Map;
2421
import javax.annotation.Nonnull;
@@ -158,9 +155,8 @@ public OrchestrationChatResponse maskingAnonymization() {
158155
""");
159156

160157
final var prompt = new OrchestrationPrompt(systemMessage, userMessage);
161-
final var maskingConfig =
162-
createMaskingConfig(DPIConfig.MethodEnum.ANONYMIZATION, DPIEntities.PERSON);
163-
final var configWithMasking = config.withMaskingConfig(maskingConfig);
158+
final var maskingConfig = DpiMaskingConfig.anonymization().withEntities(DPIEntities.PERSON);
159+
final var configWithMasking = config.withDpiMaskingConfig(maskingConfig);
164160

165161
return client.chatCompletion(prompt, configWithMasking);
166162
}
@@ -197,31 +193,9 @@ public OrchestrationChatResponse maskingPseudonymization() {
197193

198194
final var prompt = new OrchestrationPrompt(systemMessage, userMessage);
199195
final var maskingConfig =
200-
createMaskingConfig(
201-
DPIConfig.MethodEnum.PSEUDONYMIZATION, DPIEntities.PERSON, DPIEntities.EMAIL);
202-
final var configWithMasking = config.withMaskingConfig(maskingConfig);
196+
DpiMaskingConfig.pseudonymization().withEntities(DPIEntities.PERSON, DPIEntities.EMAIL);
197+
final var configWithMasking = config.withDpiMaskingConfig(maskingConfig);
203198

204199
return client.chatCompletion(prompt, configWithMasking);
205200
}
206-
207-
/**
208-
* Helper method to build masking configurations.
209-
*
210-
* @param method Either anonymization or pseudonymization.
211-
* @param entities The entities to mask.
212-
* @return A new masking configuration object.
213-
*/
214-
private static MaskingModuleConfig createMaskingConfig(
215-
@Nonnull final DPIConfig.MethodEnum method, @Nonnull final DPIEntities... entities) {
216-
217-
final var entityConfigs =
218-
Arrays.stream(entities).map(it -> new DPIEntityConfig().type(it)).toList();
219-
return new MaskingModuleConfig()
220-
.maskingProviders(
221-
List.of(
222-
new DPIConfig()
223-
.type(DPIConfig.TypeEnum.SAP_DATA_PRIVACY_INTEGRATION)
224-
.method(method)
225-
.entities(entityConfigs)));
226-
}
227201
}

0 commit comments

Comments
 (0)