dtamind-components
Version:
Apps integration for Dtamind. Contain Nodes and Credentials.
197 lines (196 loc) • 7.87 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("../../../src/utils");
const nodevm_1 = require("@dtamindai/nodevm");
const utils_2 = require("../utils");
const exampleFunc = `/*
* You can use any libraries imported in Dtamind
* You can use properties specified in Input Schema as variables. Ex: Property = userid, Variable = $userid
* You can get default flow config: $flow.sessionId, $flow.chatId, $flow.chatflowId, $flow.input, $flow.state
* You can get custom variables: $vars.<variable-name>
* Must return a string value at the end of function
*/
const fetch = require('node-fetch');
const url = 'https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41¤t_weather=true';
const options = {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
};
try {
const response = await fetch(url, options);
const text = await response.text();
return text;
} catch (error) {
console.error(error);
return '';
}`;
class CustomFunction_Agentflow {
constructor() {
//@ts-ignore
this.loadMethods = {
async listRuntimeStateKeys(_, options) {
const previousNodes = options.previousNodes;
const startAgentflowNode = previousNodes.find((node) => node.name === 'startAgentflow');
const state = startAgentflowNode?.inputs?.startState;
return state.map((item) => ({ label: item.key, name: item.key }));
}
};
this.label = 'Custom Function';
this.name = 'customFunctionAgentflow';
this.version = 1.0;
this.type = 'CustomFunction';
this.category = 'Agent Flows';
this.description = 'Execute custom function';
this.baseClasses = [this.type];
this.color = '#E4B7FF';
this.inputs = [
{
label: 'Input Variables',
name: 'customFunctionInputVariables',
description: 'Input variables can be used in the function with prefix $. For example: $foo',
type: 'array',
optional: true,
acceptVariable: true,
array: [
{
label: 'Variable Name',
name: 'variableName',
type: 'string'
},
{
label: 'Variable Value',
name: 'variableValue',
type: 'string',
acceptVariable: true
}
]
},
{
label: 'Javascript Function',
name: 'customFunctionJavascriptFunction',
type: 'code',
codeExample: exampleFunc,
description: 'The function to execute. Must return a string or an object that can be converted to a string.'
},
{
label: 'Update Flow State',
name: 'customFunctionUpdateState',
description: 'Update runtime state during the execution of the workflow',
type: 'array',
optional: true,
acceptVariable: true,
array: [
{
label: 'Key',
name: 'key',
type: 'asyncOptions',
loadMethod: 'listRuntimeStateKeys',
freeSolo: true
},
{
label: 'Value',
name: 'value',
type: 'string',
acceptVariable: true,
acceptNodeOutputAsVariable: true
}
]
}
];
}
async run(nodeData, input, options) {
const javascriptFunction = nodeData.inputs?.customFunctionJavascriptFunction;
const functionInputVariables = nodeData.inputs?.customFunctionInputVariables;
const _customFunctionUpdateState = nodeData.inputs?.customFunctionUpdateState;
const state = options.agentflowRuntime?.state;
const chatId = options.chatId;
const isLastNode = options.isLastNode;
const isStreamable = isLastNode && options.sseStreamer !== undefined;
const appDataSource = options.appDataSource;
const databaseEntities = options.databaseEntities;
// Update flow state if needed
let newState = { ...state };
if (_customFunctionUpdateState && Array.isArray(_customFunctionUpdateState) && _customFunctionUpdateState.length > 0) {
newState = (0, utils_2.updateFlowState)(state, _customFunctionUpdateState);
}
const variables = await (0, utils_1.getVars)(appDataSource, databaseEntities, nodeData, options);
const flow = {
chatflowId: options.chatflowid,
sessionId: options.sessionId,
chatId: options.chatId,
input,
state: newState
};
let sandbox = {
$input: input,
util: undefined,
Symbol: undefined,
child_process: undefined,
fs: undefined,
process: undefined
};
sandbox['$vars'] = (0, utils_1.prepareSandboxVars)(variables);
sandbox['$flow'] = flow;
for (const item of functionInputVariables) {
const variableName = item.variableName;
const variableValue = item.variableValue;
sandbox[`$${variableName}`] = variableValue;
}
const builtinDeps = process.env.TOOL_FUNCTION_BUILTIN_DEP
? utils_1.defaultAllowBuiltInDep.concat(process.env.TOOL_FUNCTION_BUILTIN_DEP.split(','))
: utils_1.defaultAllowBuiltInDep;
const externalDeps = process.env.TOOL_FUNCTION_EXTERNAL_DEP ? process.env.TOOL_FUNCTION_EXTERNAL_DEP.split(',') : [];
const deps = utils_1.availableDependencies.concat(externalDeps);
const nodeVMOptions = {
console: 'inherit',
sandbox,
require: {
external: { modules: deps },
builtin: builtinDeps
},
eval: false,
wasm: false,
timeout: 10000
};
const vm = new nodevm_1.NodeVM(nodeVMOptions);
try {
const response = await vm.run(`module.exports = async function() {${javascriptFunction}}()`, __dirname);
let finalOutput = response;
if (typeof response === 'object') {
finalOutput = JSON.stringify(response, null, 2);
}
if (isStreamable) {
const sseStreamer = options.sseStreamer;
sseStreamer.streamTokenEvent(chatId, finalOutput);
}
// Process template variables in state
if (newState && Object.keys(newState).length > 0) {
for (const key in newState) {
if (newState[key].toString().includes('{{ output }}')) {
newState[key] = finalOutput;
}
}
}
const returnOutput = {
id: nodeData.id,
name: this.name,
input: {
inputVariables: functionInputVariables,
code: javascriptFunction
},
output: {
content: finalOutput
},
state: newState
};
return returnOutput;
}
catch (e) {
throw new Error(e);
}
}
}
module.exports = { nodeClass: CustomFunction_Agentflow };
//# sourceMappingURL=CustomFunction.js.map