dtamind-components
Version:
DTAmindai Components
272 lines (269 loc) • 11.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("../../../src/utils");
const validator_1 = require("../../../src/validator");
const messages_1 = require("@langchain/core/messages");
const uuid_1 = require("uuid");
class ExecuteFlow_SeqAgents {
constructor() {
//@ts-ignore
this.loadMethods = {
async listFlows(_, options) {
const returnData = [];
const appDataSource = options.appDataSource;
const databaseEntities = options.databaseEntities;
if (appDataSource === undefined || !appDataSource) {
return returnData;
}
const searchOptions = options.searchOptions || {};
const chatflows = await appDataSource.getRepository(databaseEntities['ChatFlow']).findBy(searchOptions);
for (let i = 0; i < chatflows.length; i += 1) {
const data = {
label: chatflows[i].name,
name: chatflows[i].id
};
returnData.push(data);
}
return returnData;
}
};
this.label = 'Execute Flow';
this.name = 'seqExecuteFlow';
this.version = 1.0;
this.type = 'ExecuteFlow';
this.icon = 'executeflow.svg';
this.category = 'Sequential Agents';
this.description = `Execute chatflow/agentflow and return final response`;
this.baseClasses = [this.type];
this.credential = {
label: 'Connect Credential',
name: 'credential',
type: 'credential',
credentialNames: ['chatflowApi'],
optional: true
};
this.inputs = [
{
label: 'Sequential Node',
name: 'sequentialNode',
type: 'Start | Agent | Condition | LLMNode | ToolNode | CustomFunction | ExecuteFlow',
description: 'Can be connected to one of the following nodes: Start, Agent, Condition, LLM Node, Tool Node, Custom Function, Execute Flow',
list: true
},
{
label: 'Name',
name: 'seqExecuteFlowName',
type: 'string'
},
{
label: 'Select Flow',
name: 'selectedFlow',
type: 'asyncOptions',
loadMethod: 'listFlows'
},
{
label: 'Input',
name: 'seqExecuteFlowInput',
type: 'options',
description: 'Select one of the following or enter custom input',
freeSolo: true,
loadPreviousNodes: true,
options: [
{
label: '{{ question }}',
name: 'userQuestion',
description: 'Use the user question from the chat as input.'
}
]
},
{
label: 'Override Config',
name: 'overrideConfig',
description: 'Override the config passed to the flow.',
type: 'json',
optional: true,
additionalParams: true
},
{
label: 'Base URL',
name: 'baseURL',
type: 'string',
description: 'Base URL to Flowise. By default, it is the URL of the incoming request. Useful when you need to execute flow through an alternative route.',
placeholder: 'http://localhost:3000',
optional: true,
additionalParams: true
},
{
label: 'Start new session per message',
name: 'startNewSession',
type: 'boolean',
description: 'Whether to continue the session or start a new one with each interaction. Useful for flows with memory if you want to avoid it.',
default: false,
optional: true,
additionalParams: true
},
{
label: 'Return Value As',
name: 'returnValueAs',
type: 'options',
options: [
{ label: 'AI Message', name: 'aiMessage' },
{ label: 'Human Message', name: 'humanMessage' },
{
label: 'State Object',
name: 'stateObj',
description: "Return as state object, ex: { foo: bar }. This will update the custom state 'foo' to 'bar'"
}
],
default: 'aiMessage'
}
];
}
async init(nodeData, input, options) {
const selectedFlowId = nodeData.inputs?.selectedFlow;
const _seqExecuteFlowName = nodeData.inputs?.seqExecuteFlowName;
if (!_seqExecuteFlowName)
throw new Error('Execute Flow node name is required!');
const seqExecuteFlowName = _seqExecuteFlowName.toLowerCase().replace(/\s/g, '_').trim();
const startNewSession = nodeData.inputs?.startNewSession;
const appDataSource = options.appDataSource;
const databaseEntities = options.databaseEntities;
const sequentialNodes = nodeData.inputs?.sequentialNode;
const seqExecuteFlowInput = nodeData.inputs?.seqExecuteFlowInput;
const overrideConfig = typeof nodeData.inputs?.overrideConfig === 'string' &&
nodeData.inputs.overrideConfig.startsWith('{') &&
nodeData.inputs.overrideConfig.endsWith('}')
? JSON.parse(nodeData.inputs.overrideConfig)
: nodeData.inputs?.overrideConfig;
if (!sequentialNodes || !sequentialNodes.length)
throw new Error('Execute Flow must have a predecessor!');
const baseURL = nodeData.inputs?.baseURL || options.baseURL;
const returnValueAs = nodeData.inputs?.returnValueAs;
// Validate selectedFlowId is a valid UUID
if (!selectedFlowId || !(0, validator_1.isValidUUID)(selectedFlowId)) {
throw new Error('Invalid flow ID: must be a valid UUID');
}
// Validate baseURL is a valid URL
if (!baseURL || !(0, validator_1.isValidURL)(baseURL)) {
throw new Error('Invalid base URL: must be a valid URL');
}
const credentialData = await (0, utils_1.getCredentialData)(nodeData.credential ?? '', options);
const chatflowApiKey = (0, utils_1.getCredentialParam)('chatflowApiKey', credentialData, nodeData);
if (selectedFlowId === options.chatflowid)
throw new Error('Cannot call the same agentflow!');
let headers = {};
if (chatflowApiKey)
headers = { Authorization: `Bearer ${chatflowApiKey}` };
const chatflowId = options.chatflowid;
const sessionId = options.sessionId;
const chatId = options.chatId;
const executeFunc = async (state) => {
const variables = await (0, utils_1.getVars)(appDataSource, databaseEntities, nodeData, options);
let flowInput = '';
if (seqExecuteFlowInput === 'userQuestion') {
flowInput = input;
}
else if (seqExecuteFlowInput && seqExecuteFlowInput.startsWith('{{') && seqExecuteFlowInput.endsWith('}}')) {
const nodeId = seqExecuteFlowInput.replace('{{', '').replace('}}', '').replace('$', '').trim();
const messageOutputs = (state.messages ?? []).filter((message) => message.additional_kwargs && message.additional_kwargs?.nodeId === nodeId);
const messageOutput = messageOutputs[messageOutputs.length - 1];
if (messageOutput) {
flowInput = JSON.stringify(messageOutput.content);
}
}
const flow = {
chatflowId,
sessionId,
chatId,
input: flowInput,
state
};
const body = {
question: flowInput,
chatId: startNewSession ? (0, uuid_1.v4)() : chatId,
overrideConfig: {
sessionId: startNewSession ? (0, uuid_1.v4)() : sessionId,
...(overrideConfig ?? {})
}
};
const callOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...headers
},
body: JSON.stringify(body)
};
// Create additional sandbox variables
const additionalSandbox = {
$callOptions: callOptions,
$callBody: body
};
const sandbox = (0, utils_1.createCodeExecutionSandbox)(flowInput, variables, flow, additionalSandbox);
const code = `
const fetch = require('node-fetch');
const url = "${baseURL}/api/v1/prediction/${selectedFlowId}";
const body = $callBody;
const options = $callOptions;
try {
const response = await fetch(url, options);
const resp = await response.json();
return resp.text;
} catch (error) {
console.error(error);
return '';
}
`;
try {
let response = await (0, utils_1.executeJavaScriptCode)(code, sandbox, {
useSandbox: false,
timeout: 10000
});
if (typeof response === 'object') {
response = JSON.stringify(response);
}
if (returnValueAs === 'humanMessage') {
return {
messages: [
new messages_1.HumanMessage({
content: response,
additional_kwargs: {
nodeId: nodeData.id
}
})
]
};
}
return {
messages: [
new messages_1.AIMessage({
content: response,
additional_kwargs: {
nodeId: nodeData.id
}
})
]
};
}
catch (e) {
throw new Error(e);
}
};
const startLLM = sequentialNodes[0].startLLM;
const returnOutput = {
id: nodeData.id,
node: executeFunc,
name: seqExecuteFlowName,
label: _seqExecuteFlowName,
type: 'utilities',
output: 'ExecuteFlow',
llm: startLLM,
startLLM,
multiModalMessageContent: sequentialNodes[0]?.multiModalMessageContent,
predecessorAgents: sequentialNodes
};
return returnOutput;
}
}
module.exports = { nodeClass: ExecuteFlow_SeqAgents };
//# sourceMappingURL=ExecuteFlow.js.map