diff --git a/packages/server/src/Interface.ts b/packages/server/src/Interface.ts
index fbf4ea1299e..40589ff50e0 100644
--- a/packages/server/src/Interface.ts
+++ b/packages/server/src/Interface.ts
@@ -354,3 +354,15 @@ export interface IVariableOverride {
// DocumentStore related
export * from './Interface.DocumentStore'
+
+export enum RedactionRuleType {
+ REMOVE = 'remove',
+ REPLACE = 'replace',
+ ASTERISK = 'asterisk'
+}
+export interface RedactionRule {
+ phrase: string
+ isRegexp: boolean
+ type: RedactionRuleType
+ replacement: string
+}
diff --git a/packages/server/src/utils/buildChatflow.ts b/packages/server/src/utils/buildChatflow.ts
index f73f6044398..86d5dbd73c6 100644
--- a/packages/server/src/utils/buildChatflow.ts
+++ b/packages/server/src/utils/buildChatflow.ts
@@ -32,7 +32,9 @@ import {
IVariable,
INodeOverrides,
IVariableOverride,
- MODE
+ MODE,
+ RedactionRule,
+ RedactionRuleType
} from '../Interface'
import { InternalFlowiseError } from '../errors/internalFlowiseError'
import { databaseEntities } from '.'
@@ -653,26 +655,62 @@ export const executeFlow = async ({
resultText = result.text
/* Check for post-processing settings */
if (chatflowConfig?.postProcessing?.enabled === true) {
+ let moderatedResponse = resultText
try {
- const postProcessingFunction = JSON.parse(chatflowConfig?.postProcessing?.customFunction)
- const nodeInstanceFilePath = componentNodes['customFunction'].filePath as string
- const nodeModule = await import(nodeInstanceFilePath)
- const nodeData = {
- inputs: { javascriptFunction: postProcessingFunction },
- outputs: { output: 'output' }
+ //first run redaction rules
+ if (chatflowConfig.postProcessing.redactionRules) {
+ const redactionRules: RedactionRule[] = JSON.parse(chatflowConfig.postProcessing.redactionRules)
+ for (const rule of redactionRules) {
+ switch (rule.type) {
+ case RedactionRuleType.REMOVE:
+ if (rule.isRegexp) {
+ const regex = new RegExp(rule.phrase, 'g')
+ moderatedResponse = moderatedResponse.replace(regex, '')
+ } else {
+ moderatedResponse = moderatedResponse.replace(rule.phrase, '')
+ }
+ break
+ case RedactionRuleType.REPLACE:
+ if (rule.isRegexp) {
+ const regex = new RegExp(rule.phrase, 'g')
+ moderatedResponse = moderatedResponse.replace(regex, rule.replacement)
+ } else {
+ moderatedResponse = moderatedResponse.replace(rule.phrase, rule.replacement)
+ }
+ break
+ case RedactionRuleType.ASTERISK:
+ if (rule.isRegexp) {
+ const regex = new RegExp(rule.phrase, 'g')
+ moderatedResponse = moderatedResponse.replace(regex, '***')
+ } else {
+ moderatedResponse = moderatedResponse.replace(rule.phrase, '***')
+ }
+ break
+ }
+ }
}
- const options: ICommonObject = {
- chatflowid: chatflow.id,
- sessionId,
- chatId,
- input: question,
- rawOutput: resultText,
- appDataSource,
- databaseEntities,
- logger
+ //second - apply custom function
+ if (chatflowConfig?.postProcessing?.customFunction) {
+ const postProcessingFunction = JSON.parse(chatflowConfig?.postProcessing?.customFunction)
+ const nodeInstanceFilePath = componentNodes['customFunction'].filePath as string
+ const nodeModule = await import(nodeInstanceFilePath)
+ const nodeData = {
+ inputs: { javascriptFunction: postProcessingFunction },
+ outputs: { output: 'output' }
+ }
+ const options: ICommonObject = {
+ chatflowid: chatflow.id,
+ sessionId,
+ chatId,
+ input: question,
+ rawOutput: moderatedResponse,
+ appDataSource,
+ databaseEntities,
+ logger
+ }
+ const customFuncNodeInstance = new nodeModule.nodeClass()
+ moderatedResponse = await customFuncNodeInstance.init(nodeData, question, options)
}
- const customFuncNodeInstance = new nodeModule.nodeClass()
- let moderatedResponse = await customFuncNodeInstance.init(nodeData, question, options)
result.text = moderatedResponse
resultText = result.text
} catch (e) {
diff --git a/packages/ui/src/ui-component/extended/PostProcessing.jsx b/packages/ui/src/ui-component/extended/PostProcessing.jsx
index fd56a3eb683..62b144c9c3e 100644
--- a/packages/ui/src/ui-component/extended/PostProcessing.jsx
+++ b/packages/ui/src/ui-component/extended/PostProcessing.jsx
@@ -4,13 +4,14 @@ import PropTypes from 'prop-types'
import { useSelector } from 'react-redux'
// material-ui
-import { IconButton, Button, Box, Typography } from '@mui/material'
-import { IconArrowsMaximize, IconBulb, IconX } from '@tabler/icons-react'
+import { IconButton, Button, Box, Typography, Tabs, Tab, Stack, OutlinedInput, Divider, Chip, Breadcrumbs } from '@mui/material'
+import { IconArrowRight, IconArrowsMaximize, IconBulb, IconX } from '@tabler/icons-react'
import { useTheme } from '@mui/material/styles'
// Project import
import { StyledButton } from '@/ui-component/button/StyledButton'
import { SwitchInput } from '@/ui-component/switch/Switch'
+import { TabPanel } from '@/ui-component/tabs/TabPanel'
import { CodeEditor } from '@/ui-component/editor/CodeEditor'
import ExpandTextDialog from '@/ui-component/dialog/ExpandTextDialog'
@@ -20,8 +21,48 @@ import useNotifier from '@/utils/useNotifier'
// API
import chatflowsApi from '@/api/chatflows'
+import { Dropdown } from '@/ui-component/dropdown/Dropdown'
const sampleFunction = `return $flow.rawOutput + " This is a post processed response!";`
+const redactionTypes = [
+ {
+ label: 'Remove matching text',
+ name: 'remove'
+ },
+ {
+ label: 'Replace with custom text',
+ name: 'replace'
+ },
+ {
+ label: 'Replace with ***',
+ name: 'asterisk'
+ }
+]
+
+function HorizontalStepper() {
+ const breadcrumbs = [
+
$flow.rawOutput, $flow.input, $flow.chatflowId, $flow.sessionId, $flow.chatId- +
$flow.rawOutput, $flow.input, $flow.chatflowId, $flow.sessionId, $flow.chatId+ +