2323import static com .bytechef .component .ai .llm .constant .LLMConstants .ROLE ;
2424import static com .bytechef .component .ai .llm .constant .LLMConstants .TEMPERATURE_PROPERTY ;
2525import static com .bytechef .component .ai .universal .text .constant .AiTextConstants .CUSTOM_PATTERNS ;
26+ import static com .bytechef .component .ai .universal .text .constant .AiTextConstants .MASK_MAP ;
2627import static com .bytechef .component .ai .universal .text .constant .AiTextConstants .MODEL_NO_OPTIONS_PROPERTY ;
2728import static com .bytechef .component .ai .universal .text .constant .AiTextConstants .MODEL_OPTIONS_PROPERTY ;
2829import static com .bytechef .component .ai .universal .text .constant .AiTextConstants .MODEL_URL_PROPERTY ;
3233import static com .bytechef .component .ai .universal .text .constant .AiTextConstants .TEXT ;
3334import static com .bytechef .component .definition .ComponentDsl .action ;
3435import static com .bytechef .component .definition .ComponentDsl .array ;
36+ import static com .bytechef .component .definition .ComponentDsl .object ;
3537import static com .bytechef .component .definition .ComponentDsl .outputSchema ;
3638import static com .bytechef .component .definition .ComponentDsl .sampleOutput ;
3739import static com .bytechef .component .definition .ComponentDsl .string ;
3840
41+ import com .bytechef .component .ai .llm .ChatModel ;
3942import com .bytechef .component .ai .universal .text .action .definition .AiTextActionDefinition ;
4043import com .bytechef .component .ai .universal .text .constant .AiTextConstants ;
4144import com .bytechef .component .definition .ComponentDsl ;
@@ -86,8 +89,18 @@ public static AiTextActionDefinition of(
8689 MAX_TOKENS_PROPERTY ,
8790 TEMPERATURE_PROPERTY )
8891 .output (
89- outputSchema (string ().description ("The text with sensitive content redacted." )),
90- sampleOutput ("Hello, my name is [REDACTED] and my email is [EMAIL]." )),
92+ outputSchema (
93+ object ()
94+ .properties (
95+ string (TEXT )
96+ .description ("The text with sensitive content redacted." ),
97+ object (MASK_MAP )
98+ .description ("Mapping of mask tokens to their original values." )
99+ .additionalProperties (string ()))),
100+ sampleOutput (
101+ Map .of (
102+ TEXT , "Hello, my name is [REDACTED_1] and my email is [EMAIL_1]." ,
103+ MASK_MAP , Map .of ("[REDACTED_1]" , "John Doe" , "[EMAIL_1]" , "john@example.com" )))),
91104 provider , new MaskAction (), propertyService );
92105 }
93106
@@ -108,7 +121,9 @@ public Parameters createParameters(Parameters inputParameters) {
108121 Map <String , Object > modelInputParametersMap = new HashMap <>();
109122
110123 String systemPrompt =
111- "You are a content redaction specialist. Detect and replace sensitive information in the given text with mask tokens. Return only the redacted text with no additional commentary." ;
124+ "You are a content redaction specialist. Detect and replace sensitive information in the given text with mask tokens. "
125+ + "Increment the number suffix (_1, _2, ...) for each unique occurrence of the same type so every masking is unique. "
126+ + "Respond with a JSON object with two fields: \" text\" (the redacted text) and \" maskMap\" (an object mapping each mask token to the original value it replaced)." ;
112127
113128 StringBuilder userBuilder = new StringBuilder ();
114129
@@ -119,7 +134,7 @@ public Parameters createParameters(Parameters inputParameters) {
119134 List <String > keywords = inputParameters .getList (SENSITIVE_KEYWORDS , String .class , List .of ());
120135
121136 if (!keywords .isEmpty ()) {
122- userBuilder .append ("- Replace each of the following sensitive keywords with [REDACTED ]: " )
137+ userBuilder .append ("- Replace each of the following sensitive keywords with [REDACTED_N ]: " )
123138 .append (String .join (", " , keywords ))
124139 .append ("\n " );
125140 }
@@ -132,23 +147,46 @@ public Parameters createParameters(Parameters inputParameters) {
132147 .replace ("_" , " " ))
133148 .append (" values with [" )
134149 .append (piiType )
135- .append ("]\n " );
150+ .append ("_N ]\n " );
136151 }
137152
138153 List <String > customPatterns = inputParameters .getList (CUSTOM_PATTERNS , String .class , List .of ());
139154
140155 if (!customPatterns .isEmpty ()) {
141- userBuilder .append ("- Replace all matches of the following patterns with [REDACTED ]: " )
156+ userBuilder .append ("- Replace all matches of the following patterns with [CUSTOM_N ]: " )
142157 .append (String .join (", " , customPatterns ))
143158 .append ("\n " );
144159 }
145160
161+ String responseSchema = """
162+ {
163+ "type": "object",
164+ "properties": {
165+ "text": {
166+ "type": "string"
167+ },
168+ "maskMap": {
169+ "type": "object",
170+ "additionalProperties": {
171+ "type": "string"
172+ }
173+ }
174+ },
175+ "required": ["text", "maskMap"]
176+ }
177+ """ ;
178+
146179 modelInputParametersMap .put (
147180 "messages" ,
148181 List .of (
149182 Map .of ("content" , systemPrompt , ROLE , SYSTEM .name ()),
150183 Map .of ("content" , userBuilder .toString (), ROLE , USER .name ())));
151184 modelInputParametersMap .put ("model" , inputParameters .getString (MODEL ));
185+ modelInputParametersMap .put (
186+ "response" ,
187+ Map .of (
188+ "responseFormat" , ChatModel .ResponseFormat .JSON ,
189+ "responseSchema" , responseSchema ));
152190
153191 return ParametersFactory .create (modelInputParametersMap );
154192 }
0 commit comments