Feature: Add access to chat history and other useful variables in post-processing (#5511)
* access chat history and other useful variables in post-processing * cloning data to prevent mutations in post-processing * Enhance post-processing capabilities by adding support for additional variables and improving the UI for available variables display. Update CustomFunction implementations to utilize post-processing options consistently across components. --------- Co-authored-by: Henry <hzj94@hotmail.com>
This commit is contained in:
parent
562370b8e2
commit
6a59af11e6
|
|
@ -134,7 +134,7 @@ class CustomFunction_Agentflow implements INode {
|
||||||
|
|
||||||
async run(nodeData: INodeData, input: string, options: ICommonObject): Promise<any> {
|
async run(nodeData: INodeData, input: string, options: ICommonObject): Promise<any> {
|
||||||
const javascriptFunction = nodeData.inputs?.customFunctionJavascriptFunction as string
|
const javascriptFunction = nodeData.inputs?.customFunctionJavascriptFunction as string
|
||||||
const functionInputVariables = nodeData.inputs?.customFunctionInputVariables as ICustomFunctionInputVariables[]
|
const functionInputVariables = (nodeData.inputs?.customFunctionInputVariables as ICustomFunctionInputVariables[]) ?? []
|
||||||
const _customFunctionUpdateState = nodeData.inputs?.customFunctionUpdateState
|
const _customFunctionUpdateState = nodeData.inputs?.customFunctionUpdateState
|
||||||
|
|
||||||
const state = options.agentflowRuntime?.state as ICommonObject
|
const state = options.agentflowRuntime?.state as ICommonObject
|
||||||
|
|
@ -147,11 +147,17 @@ class CustomFunction_Agentflow implements INode {
|
||||||
|
|
||||||
const variables = await getVars(appDataSource, databaseEntities, nodeData, options)
|
const variables = await getVars(appDataSource, databaseEntities, nodeData, options)
|
||||||
const flow = {
|
const flow = {
|
||||||
|
input,
|
||||||
|
state,
|
||||||
chatflowId: options.chatflowid,
|
chatflowId: options.chatflowid,
|
||||||
sessionId: options.sessionId,
|
sessionId: options.sessionId,
|
||||||
chatId: options.chatId,
|
chatId: options.chatId,
|
||||||
input,
|
rawOutput: options.postProcessing?.rawOutput || '',
|
||||||
state
|
chatHistory: options.postProcessing?.chatHistory || [],
|
||||||
|
sourceDocuments: options.postProcessing?.sourceDocuments,
|
||||||
|
usedTools: options.postProcessing?.usedTools,
|
||||||
|
artifacts: options.postProcessing?.artifacts,
|
||||||
|
fileAnnotations: options.postProcessing?.fileAnnotations
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create additional sandbox variables for custom function inputs
|
// Create additional sandbox variables for custom function inputs
|
||||||
|
|
|
||||||
|
|
@ -84,11 +84,16 @@ class CustomFunction_Utilities implements INode {
|
||||||
|
|
||||||
const variables = await getVars(appDataSource, databaseEntities, nodeData, options)
|
const variables = await getVars(appDataSource, databaseEntities, nodeData, options)
|
||||||
const flow = {
|
const flow = {
|
||||||
|
input,
|
||||||
chatflowId: options.chatflowid,
|
chatflowId: options.chatflowid,
|
||||||
sessionId: options.sessionId,
|
sessionId: options.sessionId,
|
||||||
chatId: options.chatId,
|
chatId: options.chatId,
|
||||||
rawOutput: options.rawOutput || '',
|
rawOutput: options.postProcessing?.rawOutput || '',
|
||||||
input
|
chatHistory: options.postProcessing?.chatHistory || [],
|
||||||
|
sourceDocuments: options.postProcessing?.sourceDocuments,
|
||||||
|
usedTools: options.postProcessing?.usedTools,
|
||||||
|
artifacts: options.postProcessing?.artifacts,
|
||||||
|
fileAnnotations: options.postProcessing?.fileAnnotations
|
||||||
}
|
}
|
||||||
|
|
||||||
let inputVars: ICommonObject = {}
|
let inputVars: ICommonObject = {}
|
||||||
|
|
|
||||||
|
|
@ -2122,7 +2122,62 @@ export const executeAgentFlow = async ({
|
||||||
|
|
||||||
// check if last agentFlowExecutedData.data.output contains the key "content"
|
// check if last agentFlowExecutedData.data.output contains the key "content"
|
||||||
const lastNodeOutput = agentFlowExecutedData[agentFlowExecutedData.length - 1].data?.output as ICommonObject | undefined
|
const lastNodeOutput = agentFlowExecutedData[agentFlowExecutedData.length - 1].data?.output as ICommonObject | undefined
|
||||||
const content = (lastNodeOutput?.content as string) ?? ' '
|
let content = (lastNodeOutput?.content as string) ?? ' '
|
||||||
|
|
||||||
|
/* Check for post-processing settings */
|
||||||
|
let chatflowConfig: ICommonObject = {}
|
||||||
|
try {
|
||||||
|
if (chatflow.chatbotConfig) {
|
||||||
|
chatflowConfig = typeof chatflow.chatbotConfig === 'string' ? JSON.parse(chatflow.chatbotConfig) : chatflow.chatbotConfig
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.error('[server]: Error parsing chatflow config:', e)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chatflowConfig?.postProcessing?.enabled === true && content) {
|
||||||
|
try {
|
||||||
|
const postProcessingFunction = JSON.parse(chatflowConfig?.postProcessing?.customFunction)
|
||||||
|
const nodeInstanceFilePath = componentNodes['customFunctionAgentflow'].filePath as string
|
||||||
|
const nodeModule = await import(nodeInstanceFilePath)
|
||||||
|
//set the outputs.output to EndingNode to prevent json escaping of content...
|
||||||
|
const nodeData = {
|
||||||
|
inputs: { customFunctionJavascriptFunction: postProcessingFunction }
|
||||||
|
}
|
||||||
|
const runtimeChatHistory = agentflowRuntime.chatHistory || []
|
||||||
|
const chatHistory = [...pastChatHistory, ...runtimeChatHistory]
|
||||||
|
const options: ICommonObject = {
|
||||||
|
chatflowid: chatflow.id,
|
||||||
|
sessionId,
|
||||||
|
chatId,
|
||||||
|
input: question || form,
|
||||||
|
postProcessing: {
|
||||||
|
rawOutput: content,
|
||||||
|
chatHistory: cloneDeep(chatHistory),
|
||||||
|
sourceDocuments: lastNodeOutput?.sourceDocuments ? cloneDeep(lastNodeOutput.sourceDocuments) : undefined,
|
||||||
|
usedTools: lastNodeOutput?.usedTools ? cloneDeep(lastNodeOutput.usedTools) : undefined,
|
||||||
|
artifacts: lastNodeOutput?.artifacts ? cloneDeep(lastNodeOutput.artifacts) : undefined,
|
||||||
|
fileAnnotations: lastNodeOutput?.fileAnnotations ? cloneDeep(lastNodeOutput.fileAnnotations) : undefined
|
||||||
|
},
|
||||||
|
appDataSource,
|
||||||
|
databaseEntities,
|
||||||
|
workspaceId,
|
||||||
|
orgId,
|
||||||
|
logger
|
||||||
|
}
|
||||||
|
const customFuncNodeInstance = new nodeModule.nodeClass()
|
||||||
|
const customFunctionResponse = await customFuncNodeInstance.run(nodeData, question || form, options)
|
||||||
|
const moderatedResponse = customFunctionResponse.output.content
|
||||||
|
if (typeof moderatedResponse === 'string') {
|
||||||
|
content = moderatedResponse
|
||||||
|
} else if (typeof moderatedResponse === 'object') {
|
||||||
|
content = '```json\n' + JSON.stringify(moderatedResponse, null, 2) + '\n```'
|
||||||
|
} else {
|
||||||
|
content = moderatedResponse
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.error('[server]: Post Processing Error:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// remove credentialId from agentFlowExecutedData
|
// remove credentialId from agentFlowExecutedData
|
||||||
agentFlowExecutedData = agentFlowExecutedData.map((data) => _removeCredentialId(data))
|
agentFlowExecutedData = agentFlowExecutedData.map((data) => _removeCredentialId(data))
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { Request } from 'express'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import { DataSource } from 'typeorm'
|
import { DataSource } from 'typeorm'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { omit } from 'lodash'
|
import { omit, cloneDeep } from 'lodash'
|
||||||
import {
|
import {
|
||||||
IFileUpload,
|
IFileUpload,
|
||||||
convertSpeechToText,
|
convertSpeechToText,
|
||||||
|
|
@ -817,7 +817,14 @@ export const executeFlow = async ({
|
||||||
sessionId,
|
sessionId,
|
||||||
chatId,
|
chatId,
|
||||||
input: question,
|
input: question,
|
||||||
rawOutput: resultText,
|
postProcessing: {
|
||||||
|
rawOutput: resultText,
|
||||||
|
chatHistory: cloneDeep(chatHistory),
|
||||||
|
sourceDocuments: result?.sourceDocuments ? cloneDeep(result.sourceDocuments) : undefined,
|
||||||
|
usedTools: result?.usedTools ? cloneDeep(result.usedTools) : undefined,
|
||||||
|
artifacts: result?.artifacts ? cloneDeep(result.artifacts) : undefined,
|
||||||
|
fileAnnotations: result?.fileAnnotations ? cloneDeep(result.fileAnnotations) : undefined
|
||||||
|
},
|
||||||
appDataSource,
|
appDataSource,
|
||||||
databaseEntities,
|
databaseEntities,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,7 @@ const CHATFLOW_CONFIGURATION_TABS = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Post Processing',
|
label: 'Post Processing',
|
||||||
id: 'postProcessing',
|
id: 'postProcessing'
|
||||||
hideInAgentFlow: true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,25 @@ import PropTypes from 'prop-types'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
// material-ui
|
// material-ui
|
||||||
import { IconButton, Button, Box, Typography } from '@mui/material'
|
import {
|
||||||
import { IconArrowsMaximize, IconBulb, IconX } from '@tabler/icons-react'
|
IconButton,
|
||||||
|
Button,
|
||||||
|
Box,
|
||||||
|
Typography,
|
||||||
|
TableContainer,
|
||||||
|
Table,
|
||||||
|
TableHead,
|
||||||
|
TableBody,
|
||||||
|
TableRow,
|
||||||
|
TableCell,
|
||||||
|
Paper,
|
||||||
|
Accordion,
|
||||||
|
AccordionSummary,
|
||||||
|
AccordionDetails,
|
||||||
|
Card
|
||||||
|
} from '@mui/material'
|
||||||
|
import { IconArrowsMaximize, IconX } from '@tabler/icons-react'
|
||||||
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
|
||||||
import { useTheme } from '@mui/material/styles'
|
import { useTheme } from '@mui/material/styles'
|
||||||
|
|
||||||
// Project import
|
// Project import
|
||||||
|
|
@ -21,7 +38,11 @@ import useNotifier from '@/utils/useNotifier'
|
||||||
// API
|
// API
|
||||||
import chatflowsApi from '@/api/chatflows'
|
import chatflowsApi from '@/api/chatflows'
|
||||||
|
|
||||||
const sampleFunction = `return $flow.rawOutput + " This is a post processed response!";`
|
const sampleFunction = `// Access chat history as a string
|
||||||
|
const chatHistory = JSON.stringify($flow.chatHistory, null, 2);
|
||||||
|
|
||||||
|
// Return a modified response
|
||||||
|
return $flow.rawOutput + " This is a post processed response!";`
|
||||||
|
|
||||||
const PostProcessing = ({ dialogProps }) => {
|
const PostProcessing = ({ dialogProps }) => {
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
|
|
@ -175,31 +196,105 @@ const PostProcessing = ({ dialogProps }) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Box>
|
</Box>
|
||||||
<div
|
<Card sx={{ borderColor: theme.palette.primary[200] + 75, mt: 2, mb: 2 }} variant='outlined'>
|
||||||
style={{
|
<Accordion
|
||||||
display: 'flex',
|
disableGutters
|
||||||
flexDirection: 'column',
|
sx={{
|
||||||
borderRadius: 10,
|
'&:before': {
|
||||||
background: '#d8f3dc',
|
display: 'none'
|
||||||
padding: 10,
|
}
|
||||||
marginTop: 10
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
paddingTop: 10
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<IconBulb size={30} color='#2d6a4f' />
|
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||||
<span style={{ color: '#2d6a4f', marginLeft: 10, fontWeight: 500 }}>
|
<Typography>Available Variables</Typography>
|
||||||
The following variables are available to use in the custom function:{' '}
|
</AccordionSummary>
|
||||||
<pre>$flow.rawOutput, $flow.input, $flow.chatflowId, $flow.sessionId, $flow.chatId</pre>
|
<AccordionDetails sx={{ p: 0 }}>
|
||||||
</span>
|
<TableContainer component={Paper}>
|
||||||
</div>
|
<Table aria-label='available variables table'>
|
||||||
</div>
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell sx={{ width: '30%' }}>Variable</TableCell>
|
||||||
|
<TableCell sx={{ width: '15%' }}>Type</TableCell>
|
||||||
|
<TableCell sx={{ width: '55%' }}>Description</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>
|
||||||
|
<code>$flow.rawOutput</code>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>string</TableCell>
|
||||||
|
<TableCell>The raw output response from the flow</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>
|
||||||
|
<code>$flow.input</code>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>string</TableCell>
|
||||||
|
<TableCell>The user input message</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>
|
||||||
|
<code>$flow.chatHistory</code>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>array</TableCell>
|
||||||
|
<TableCell>Array of previous messages in the conversation</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>
|
||||||
|
<code>$flow.chatflowId</code>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>string</TableCell>
|
||||||
|
<TableCell>Unique identifier for the chatflow</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>
|
||||||
|
<code>$flow.sessionId</code>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>string</TableCell>
|
||||||
|
<TableCell>Current session identifier</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>
|
||||||
|
<code>$flow.chatId</code>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>string</TableCell>
|
||||||
|
<TableCell>Current chat identifier</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>
|
||||||
|
<code>$flow.sourceDocuments</code>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>array</TableCell>
|
||||||
|
<TableCell>Source documents used in retrieval (if applicable)</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>
|
||||||
|
<code>$flow.usedTools</code>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>array</TableCell>
|
||||||
|
<TableCell>List of tools used during execution</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>
|
||||||
|
<code>$flow.artifacts</code>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>array</TableCell>
|
||||||
|
<TableCell>List of artifacts generated during execution</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell sx={{ borderBottom: 'none' }}>
|
||||||
|
<code>$flow.fileAnnotations</code>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell sx={{ borderBottom: 'none' }}>array</TableCell>
|
||||||
|
<TableCell sx={{ borderBottom: 'none' }}>File annotations associated with the response</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
</AccordionDetails>
|
||||||
|
</Accordion>
|
||||||
|
</Card>
|
||||||
<StyledButton
|
<StyledButton
|
||||||
style={{ marginBottom: 10, marginTop: 10 }}
|
style={{ marginBottom: 10, marginTop: 10 }}
|
||||||
variant='contained'
|
variant='contained'
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue