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> {
|
||||
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 state = options.agentflowRuntime?.state as ICommonObject
|
||||
|
|
@ -147,11 +147,17 @@ class CustomFunction_Agentflow implements INode {
|
|||
|
||||
const variables = await getVars(appDataSource, databaseEntities, nodeData, options)
|
||||
const flow = {
|
||||
input,
|
||||
state,
|
||||
chatflowId: options.chatflowid,
|
||||
sessionId: options.sessionId,
|
||||
chatId: options.chatId,
|
||||
input,
|
||||
state
|
||||
rawOutput: options.postProcessing?.rawOutput || '',
|
||||
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
|
||||
|
|
|
|||
|
|
@ -84,11 +84,16 @@ class CustomFunction_Utilities implements INode {
|
|||
|
||||
const variables = await getVars(appDataSource, databaseEntities, nodeData, options)
|
||||
const flow = {
|
||||
input,
|
||||
chatflowId: options.chatflowid,
|
||||
sessionId: options.sessionId,
|
||||
chatId: options.chatId,
|
||||
rawOutput: options.rawOutput || '',
|
||||
input
|
||||
rawOutput: options.postProcessing?.rawOutput || '',
|
||||
chatHistory: options.postProcessing?.chatHistory || [],
|
||||
sourceDocuments: options.postProcessing?.sourceDocuments,
|
||||
usedTools: options.postProcessing?.usedTools,
|
||||
artifacts: options.postProcessing?.artifacts,
|
||||
fileAnnotations: options.postProcessing?.fileAnnotations
|
||||
}
|
||||
|
||||
let inputVars: ICommonObject = {}
|
||||
|
|
|
|||
|
|
@ -2122,7 +2122,62 @@ export const executeAgentFlow = async ({
|
|||
|
||||
// check if last agentFlowExecutedData.data.output contains the key "content"
|
||||
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
|
||||
agentFlowExecutedData = agentFlowExecutedData.map((data) => _removeCredentialId(data))
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { Request } from 'express'
|
|||
import * as path from 'path'
|
||||
import { DataSource } from 'typeorm'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { omit } from 'lodash'
|
||||
import { omit, cloneDeep } from 'lodash'
|
||||
import {
|
||||
IFileUpload,
|
||||
convertSpeechToText,
|
||||
|
|
@ -817,7 +817,14 @@ export const executeFlow = async ({
|
|||
sessionId,
|
||||
chatId,
|
||||
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,
|
||||
databaseEntities,
|
||||
workspaceId,
|
||||
|
|
|
|||
|
|
@ -53,8 +53,7 @@ const CHATFLOW_CONFIGURATION_TABS = [
|
|||
},
|
||||
{
|
||||
label: 'Post Processing',
|
||||
id: 'postProcessing',
|
||||
hideInAgentFlow: true
|
||||
id: 'postProcessing'
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,25 @@ 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,
|
||||
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'
|
||||
|
||||
// Project import
|
||||
|
|
@ -21,7 +38,11 @@ import useNotifier from '@/utils/useNotifier'
|
|||
// API
|
||||
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 dispatch = useDispatch()
|
||||
|
|
@ -175,31 +196,105 @@ const PostProcessing = ({ dialogProps }) => {
|
|||
/>
|
||||
</div>
|
||||
</Box>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
borderRadius: 10,
|
||||
background: '#d8f3dc',
|
||||
padding: 10,
|
||||
marginTop: 10
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingTop: 10
|
||||
<Card sx={{ borderColor: theme.palette.primary[200] + 75, mt: 2, mb: 2 }} variant='outlined'>
|
||||
<Accordion
|
||||
disableGutters
|
||||
sx={{
|
||||
'&:before': {
|
||||
display: 'none'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<IconBulb size={30} color='#2d6a4f' />
|
||||
<span style={{ color: '#2d6a4f', marginLeft: 10, fontWeight: 500 }}>
|
||||
The following variables are available to use in the custom function:{' '}
|
||||
<pre>$flow.rawOutput, $flow.input, $flow.chatflowId, $flow.sessionId, $flow.chatId</pre>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography>Available Variables</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails sx={{ p: 0 }}>
|
||||
<TableContainer component={Paper}>
|
||||
<Table aria-label='available variables table'>
|
||||
<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
|
||||
style={{ marginBottom: 10, marginTop: 10 }}
|
||||
variant='contained'
|
||||
|
|
|
|||
Loading…
Reference in New Issue