mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 17:01:00 +03:00
Refactor/Update code execution sandbox implementation across components (#4904)
refactor: Update code execution sandbox implementation across components - Replaced NodeVM usage with a new createCodeExecutionSandbox function for improved sandbox management. - Enhanced JavaScript code execution with executeJavaScriptCode function, allowing for better handling of libraries and output streaming. - Updated multiple components to utilize the new sandboxing approach, ensuring consistent execution environment. - Added validation for UUIDs and URLs in various tools to enhance input safety. - Refactored input handling in CustomFunction and IfElseFunction to streamline variable management.
This commit is contained in:
@@ -8,8 +8,7 @@ import {
|
||||
INodeParams,
|
||||
IServerSideEventStreamer
|
||||
} from '../../../src/Interface'
|
||||
import { availableDependencies, defaultAllowBuiltInDep, getVars, prepareSandboxVars } from '../../../src/utils'
|
||||
import { NodeVM } from '@flowiseai/nodevm'
|
||||
import { getVars, executeJavaScriptCode, createCodeExecutionSandbox } from '../../../src/utils'
|
||||
import { updateFlowState } from '../utils'
|
||||
|
||||
interface ICustomFunctionInputVariables {
|
||||
@@ -19,9 +18,9 @@ interface ICustomFunctionInputVariables {
|
||||
|
||||
const exampleFunc = `/*
|
||||
* You can use any libraries imported in Flowise
|
||||
* You can use properties specified in Input Schema as variables. Ex: Property = userid, Variable = $userid
|
||||
* You can use properties specified in Input Variables with the prefix $. For example: $foo
|
||||
* You can get default flow config: $flow.sessionId, $flow.chatId, $flow.chatflowId, $flow.input, $flow.state
|
||||
* You can get custom variables: $vars.<variable-name>
|
||||
* You can get global variables: $vars.<variable-name>
|
||||
* Must return a string value at the end of function
|
||||
*/
|
||||
|
||||
@@ -161,55 +160,36 @@ class CustomFunction_Agentflow implements INode {
|
||||
state: newState
|
||||
}
|
||||
|
||||
let sandbox: any = {
|
||||
$input: input,
|
||||
util: undefined,
|
||||
Symbol: undefined,
|
||||
child_process: undefined,
|
||||
fs: undefined,
|
||||
process: undefined
|
||||
}
|
||||
sandbox['$vars'] = prepareSandboxVars(variables)
|
||||
sandbox['$flow'] = flow
|
||||
|
||||
// Create additional sandbox variables for custom function inputs
|
||||
const additionalSandbox: ICommonObject = {}
|
||||
for (const item of functionInputVariables) {
|
||||
const variableName = item.variableName
|
||||
const variableValue = item.variableValue
|
||||
sandbox[`$${variableName}`] = variableValue
|
||||
additionalSandbox[`$${variableName}`] = variableValue
|
||||
}
|
||||
|
||||
const builtinDeps = process.env.TOOL_FUNCTION_BUILTIN_DEP
|
||||
? defaultAllowBuiltInDep.concat(process.env.TOOL_FUNCTION_BUILTIN_DEP.split(','))
|
||||
: defaultAllowBuiltInDep
|
||||
const externalDeps = process.env.TOOL_FUNCTION_EXTERNAL_DEP ? process.env.TOOL_FUNCTION_EXTERNAL_DEP.split(',') : []
|
||||
const deps = availableDependencies.concat(externalDeps)
|
||||
const sandbox = createCodeExecutionSandbox(input, variables, flow, additionalSandbox)
|
||||
|
||||
const nodeVMOptions = {
|
||||
console: 'inherit',
|
||||
sandbox,
|
||||
require: {
|
||||
external: { modules: deps },
|
||||
builtin: builtinDeps
|
||||
},
|
||||
eval: false,
|
||||
wasm: false,
|
||||
timeout: 10000
|
||||
} as any
|
||||
// Setup streaming function if needed
|
||||
const streamOutput = isStreamable
|
||||
? (output: string) => {
|
||||
const sseStreamer: IServerSideEventStreamer = options.sseStreamer
|
||||
sseStreamer.streamTokenEvent(chatId, output)
|
||||
}
|
||||
: undefined
|
||||
|
||||
const vm = new NodeVM(nodeVMOptions)
|
||||
try {
|
||||
const response = await vm.run(`module.exports = async function() {${javascriptFunction}}()`, __dirname)
|
||||
const response = await executeJavaScriptCode(javascriptFunction, sandbox, {
|
||||
libraries: ['axios'],
|
||||
streamOutput,
|
||||
timeout: 10000
|
||||
})
|
||||
|
||||
let finalOutput = response
|
||||
if (typeof response === 'object') {
|
||||
finalOutput = JSON.stringify(response, null, 2)
|
||||
}
|
||||
|
||||
if (isStreamable) {
|
||||
const sseStreamer: IServerSideEventStreamer = options.sseStreamer
|
||||
sseStreamer.streamTokenEvent(chatId, finalOutput)
|
||||
}
|
||||
|
||||
// Process template variables in state
|
||||
if (newState && Object.keys(newState).length > 0) {
|
||||
for (const key in newState) {
|
||||
|
||||
+10
-32
@@ -1,7 +1,6 @@
|
||||
import { ICommonObject, IDatabaseEntity, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
|
||||
import { NodeVM } from '@flowiseai/nodevm'
|
||||
import { DataSource } from 'typeorm'
|
||||
import { availableDependencies, defaultAllowBuiltInDep, getVars, handleEscapeCharacters, prepareSandboxVars } from '../../../src/utils'
|
||||
import { getVars, handleEscapeCharacters, executeJavaScriptCode, createCodeExecutionSandbox } from '../../../src/utils'
|
||||
|
||||
class CustomDocumentLoader_DocumentLoaders implements INode {
|
||||
label: string
|
||||
@@ -106,44 +105,23 @@ class CustomDocumentLoader_DocumentLoaders implements INode {
|
||||
}
|
||||
}
|
||||
|
||||
let sandbox: any = {
|
||||
$input: input,
|
||||
util: undefined,
|
||||
Symbol: undefined,
|
||||
child_process: undefined,
|
||||
fs: undefined,
|
||||
process: undefined
|
||||
}
|
||||
sandbox['$vars'] = prepareSandboxVars(variables)
|
||||
sandbox['$flow'] = flow
|
||||
// Create additional sandbox variables
|
||||
const additionalSandbox: ICommonObject = {}
|
||||
|
||||
// Add input variables to sandbox
|
||||
if (Object.keys(inputVars).length) {
|
||||
for (const item in inputVars) {
|
||||
sandbox[`$${item}`] = inputVars[item]
|
||||
additionalSandbox[`$${item}`] = inputVars[item]
|
||||
}
|
||||
}
|
||||
|
||||
const builtinDeps = process.env.TOOL_FUNCTION_BUILTIN_DEP
|
||||
? defaultAllowBuiltInDep.concat(process.env.TOOL_FUNCTION_BUILTIN_DEP.split(','))
|
||||
: defaultAllowBuiltInDep
|
||||
const externalDeps = process.env.TOOL_FUNCTION_EXTERNAL_DEP ? process.env.TOOL_FUNCTION_EXTERNAL_DEP.split(',') : []
|
||||
const deps = availableDependencies.concat(externalDeps)
|
||||
const sandbox = createCodeExecutionSandbox(input, variables, flow, additionalSandbox)
|
||||
|
||||
const nodeVMOptions = {
|
||||
console: 'inherit',
|
||||
sandbox,
|
||||
require: {
|
||||
external: { modules: deps },
|
||||
builtin: builtinDeps
|
||||
},
|
||||
eval: false,
|
||||
wasm: false,
|
||||
timeout: 10000
|
||||
} as any
|
||||
|
||||
const vm = new NodeVM(nodeVMOptions)
|
||||
try {
|
||||
const response = await vm.run(`module.exports = async function() {${javascriptFunction}}()`, __dirname)
|
||||
const response = await executeJavaScriptCode(javascriptFunction, sandbox, {
|
||||
libraries: ['axios'],
|
||||
timeout: 10000
|
||||
})
|
||||
|
||||
if (output === 'document' && Array.isArray(response)) {
|
||||
if (response.length === 0) return response
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { ICommonObject, IDatabaseEntity, INode, INodeData, INodeParams } from '../../../src/Interface'
|
||||
import { getBaseClasses, transformBracesWithColon } from '../../../src/utils'
|
||||
import { getBaseClasses, transformBracesWithColon, getVars, executeJavaScriptCode, createCodeExecutionSandbox } from '../../../src/utils'
|
||||
import { ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate } from '@langchain/core/prompts'
|
||||
import { getVM } from '../../sequentialagents/commonUtils'
|
||||
import { DataSource } from 'typeorm'
|
||||
const defaultFunc = `const { AIMessage, HumanMessage, ToolMessage } = require('@langchain/core/messages');
|
||||
|
||||
@@ -120,13 +119,29 @@ class ChatPromptTemplate_Prompts implements INode {
|
||||
) {
|
||||
const appDataSource = options.appDataSource as DataSource
|
||||
const databaseEntities = options.databaseEntities as IDatabaseEntity
|
||||
const vm = await getVM(appDataSource, databaseEntities, nodeData, options, {})
|
||||
const variables = await getVars(appDataSource, databaseEntities, nodeData, options)
|
||||
const flow = {
|
||||
chatflowId: options.chatflowid,
|
||||
sessionId: options.sessionId,
|
||||
chatId: options.chatId
|
||||
}
|
||||
|
||||
const sandbox = createCodeExecutionSandbox('', variables, flow)
|
||||
|
||||
try {
|
||||
const response = await vm.run(`module.exports = async function() {${messageHistoryCode}}()`, __dirname)
|
||||
if (!Array.isArray(response)) throw new Error('Returned message history must be an array')
|
||||
const response = await executeJavaScriptCode(messageHistoryCode, sandbox, {
|
||||
libraries: ['axios', '@langchain/core'],
|
||||
timeout: 10000
|
||||
})
|
||||
|
||||
const parsedResponse = JSON.parse(response)
|
||||
|
||||
if (!Array.isArray(parsedResponse)) {
|
||||
throw new Error('Returned message history must be an array')
|
||||
}
|
||||
prompt = ChatPromptTemplate.fromMessages([
|
||||
SystemMessagePromptTemplate.fromTemplate(systemMessagePrompt),
|
||||
...response,
|
||||
...parsedResponse,
|
||||
HumanMessagePromptTemplate.fromTemplate(humanMessagePrompt)
|
||||
])
|
||||
} catch (e) {
|
||||
|
||||
@@ -36,11 +36,12 @@ import {
|
||||
handleEscapeCharacters,
|
||||
prepareSandboxVars,
|
||||
removeInvalidImageMarkdown,
|
||||
transformBracesWithColon
|
||||
transformBracesWithColon,
|
||||
executeJavaScriptCode,
|
||||
createCodeExecutionSandbox
|
||||
} from '../../../src/utils'
|
||||
import {
|
||||
customGet,
|
||||
getVM,
|
||||
processImageMessage,
|
||||
transformObjectPropertyToFunction,
|
||||
filterConversationHistory,
|
||||
@@ -936,9 +937,13 @@ const getReturnOutput = async (nodeData: INodeData, input: string, options: ICom
|
||||
throw new Error(e)
|
||||
}
|
||||
} else if (selectedTab === 'updateStateMemoryCode' && updateStateMemoryCode) {
|
||||
const vm = await getVM(appDataSource, databaseEntities, nodeData, options, flow)
|
||||
const sandbox = createCodeExecutionSandbox(input, variables, flow)
|
||||
|
||||
try {
|
||||
const response = await vm.run(`module.exports = async function() {${updateStateMemoryCode}}()`, __dirname)
|
||||
const response = await executeJavaScriptCode(updateStateMemoryCode, sandbox, {
|
||||
timeout: 10000
|
||||
})
|
||||
|
||||
if (typeof response !== 'object') throw new Error('Return output must be an object')
|
||||
return response
|
||||
} catch (e) {
|
||||
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
ISeqAgentNode,
|
||||
ISeqAgentsState
|
||||
} from '../../../src/Interface'
|
||||
import { checkCondition, customGet, getVM } from '../commonUtils'
|
||||
import { getVars, prepareSandboxVars } from '../../../src/utils'
|
||||
import { checkCondition, customGet } from '../commonUtils'
|
||||
import { getVars, prepareSandboxVars, executeJavaScriptCode, createCodeExecutionSandbox } from '../../../src/utils'
|
||||
|
||||
const howToUseCode = `
|
||||
1. Must return a string value at the end of function. For example:
|
||||
@@ -279,9 +279,13 @@ const runCondition = async (nodeData: INodeData, input: string, options: ICommon
|
||||
}
|
||||
|
||||
if (selectedTab === 'conditionFunction' && conditionFunction) {
|
||||
const vm = await getVM(appDataSource, databaseEntities, nodeData, options, flow)
|
||||
const sandbox = createCodeExecutionSandbox(input, variables, flow)
|
||||
|
||||
try {
|
||||
const response = await vm.run(`module.exports = async function() {${conditionFunction}}()`, __dirname)
|
||||
const response = await executeJavaScriptCode(conditionFunction, sandbox, {
|
||||
timeout: 10000
|
||||
})
|
||||
|
||||
if (typeof response !== 'string') throw new Error('Condition function must return a string')
|
||||
return response
|
||||
} catch (e) {
|
||||
|
||||
@@ -16,12 +16,19 @@ import {
|
||||
ISeqAgentNode,
|
||||
ISeqAgentsState
|
||||
} from '../../../src/Interface'
|
||||
import { getInputVariables, getVars, handleEscapeCharacters, prepareSandboxVars, transformBracesWithColon } from '../../../src/utils'
|
||||
import {
|
||||
getInputVariables,
|
||||
getVars,
|
||||
handleEscapeCharacters,
|
||||
prepareSandboxVars,
|
||||
transformBracesWithColon,
|
||||
executeJavaScriptCode,
|
||||
createCodeExecutionSandbox
|
||||
} from '../../../src/utils'
|
||||
import {
|
||||
checkCondition,
|
||||
convertStructuredSchemaToZod,
|
||||
customGet,
|
||||
getVM,
|
||||
transformObjectPropertyToFunction,
|
||||
filterConversationHistory,
|
||||
restructureMessages
|
||||
@@ -539,9 +546,13 @@ const runCondition = async (
|
||||
}
|
||||
|
||||
if (selectedTab === 'conditionFunction' && conditionFunction) {
|
||||
const vm = await getVM(appDataSource, databaseEntities, nodeData, options, flow)
|
||||
const sandbox = createCodeExecutionSandbox(input, variables, flow)
|
||||
|
||||
try {
|
||||
const response = await vm.run(`module.exports = async function() {${conditionFunction}}()`, __dirname)
|
||||
const response = await executeJavaScriptCode(conditionFunction, sandbox, {
|
||||
timeout: 10000
|
||||
})
|
||||
|
||||
if (typeof response !== 'string') throw new Error('Condition function must return a string')
|
||||
return response
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { NodeVM } from '@flowiseai/nodevm'
|
||||
import { DataSource } from 'typeorm'
|
||||
import { availableDependencies, defaultAllowBuiltInDep, getVars, handleEscapeCharacters, prepareSandboxVars } from '../../../src/utils'
|
||||
import { getVars, handleEscapeCharacters, executeJavaScriptCode, createCodeExecutionSandbox } from '../../../src/utils'
|
||||
import { ICommonObject, IDatabaseEntity, INode, INodeData, INodeParams, ISeqAgentNode, ISeqAgentsState } from '../../../src/Interface'
|
||||
import { AIMessage, BaseMessage, HumanMessage } from '@langchain/core/messages'
|
||||
import { customGet } from '../commonUtils'
|
||||
@@ -154,44 +153,22 @@ class CustomFunction_SeqAgents implements INode {
|
||||
}
|
||||
}
|
||||
|
||||
let sandbox: any = {
|
||||
$input: input,
|
||||
util: undefined,
|
||||
Symbol: undefined,
|
||||
child_process: undefined,
|
||||
fs: undefined,
|
||||
process: undefined
|
||||
}
|
||||
sandbox['$vars'] = prepareSandboxVars(variables)
|
||||
sandbox['$flow'] = flow
|
||||
// Create additional sandbox variables
|
||||
const additionalSandbox: ICommonObject = {}
|
||||
|
||||
// Add input variables to sandbox
|
||||
if (Object.keys(inputVars).length) {
|
||||
for (const item in inputVars) {
|
||||
sandbox[`$${item}`] = inputVars[item]
|
||||
additionalSandbox[`$${item}`] = inputVars[item]
|
||||
}
|
||||
}
|
||||
|
||||
const builtinDeps = process.env.TOOL_FUNCTION_BUILTIN_DEP
|
||||
? defaultAllowBuiltInDep.concat(process.env.TOOL_FUNCTION_BUILTIN_DEP.split(','))
|
||||
: defaultAllowBuiltInDep
|
||||
const externalDeps = process.env.TOOL_FUNCTION_EXTERNAL_DEP ? process.env.TOOL_FUNCTION_EXTERNAL_DEP.split(',') : []
|
||||
const deps = availableDependencies.concat(externalDeps)
|
||||
const sandbox = createCodeExecutionSandbox(input, variables, flow, additionalSandbox)
|
||||
|
||||
const nodeVMOptions = {
|
||||
console: 'inherit',
|
||||
sandbox,
|
||||
require: {
|
||||
external: { modules: deps },
|
||||
builtin: builtinDeps
|
||||
},
|
||||
eval: false,
|
||||
wasm: false,
|
||||
timeout: 10000
|
||||
} as any
|
||||
|
||||
const vm = new NodeVM(nodeVMOptions)
|
||||
try {
|
||||
const response = await vm.run(`module.exports = async function() {${javascriptFunction}}()`, __dirname)
|
||||
const response = await executeJavaScriptCode(javascriptFunction, sandbox, {
|
||||
timeout: 10000
|
||||
})
|
||||
|
||||
if (returnValueAs === 'stateObj') {
|
||||
if (typeof response !== 'object') {
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import { NodeVM } from '@flowiseai/nodevm'
|
||||
import { DataSource } from 'typeorm'
|
||||
import {
|
||||
availableDependencies,
|
||||
defaultAllowBuiltInDep,
|
||||
getCredentialData,
|
||||
getCredentialParam,
|
||||
getVars,
|
||||
prepareSandboxVars
|
||||
} from '../../../src/utils'
|
||||
import { getCredentialData, getCredentialParam, getVars, executeJavaScriptCode, createCodeExecutionSandbox } from '../../../src/utils'
|
||||
import { isValidUUID, isValidURL } from '../../../src/validator'
|
||||
import {
|
||||
ICommonObject,
|
||||
IDatabaseEntity,
|
||||
@@ -177,6 +170,16 @@ class ExecuteFlow_SeqAgents implements INode {
|
||||
const baseURL = (nodeData.inputs?.baseURL as string) || (options.baseURL as string)
|
||||
const returnValueAs = nodeData.inputs?.returnValueAs as string
|
||||
|
||||
// Validate selectedFlowId is a valid UUID
|
||||
if (!selectedFlowId || !isValidUUID(selectedFlowId)) {
|
||||
throw new Error('Invalid flow ID: must be a valid UUID')
|
||||
}
|
||||
|
||||
// Validate baseURL is a valid URL
|
||||
if (!baseURL || !isValidURL(baseURL)) {
|
||||
throw new Error('Invalid base URL: must be a valid URL')
|
||||
}
|
||||
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
const chatflowApiKey = getCredentialParam('chatflowApiKey', credentialData, nodeData)
|
||||
|
||||
@@ -233,18 +236,13 @@ class ExecuteFlow_SeqAgents implements INode {
|
||||
body: JSON.stringify(body)
|
||||
}
|
||||
|
||||
let sandbox: ICommonObject = {
|
||||
$input: flowInput,
|
||||
// Create additional sandbox variables
|
||||
const additionalSandbox: ICommonObject = {
|
||||
$callOptions: callOptions,
|
||||
$callBody: body,
|
||||
util: undefined,
|
||||
Symbol: undefined,
|
||||
child_process: undefined,
|
||||
fs: undefined,
|
||||
process: undefined
|
||||
$callBody: body
|
||||
}
|
||||
sandbox['$vars'] = prepareSandboxVars(variables)
|
||||
sandbox['$flow'] = flow
|
||||
|
||||
const sandbox = createCodeExecutionSandbox(flowInput, variables, flow, additionalSandbox)
|
||||
|
||||
const code = `
|
||||
const fetch = require('node-fetch');
|
||||
@@ -264,27 +262,11 @@ class ExecuteFlow_SeqAgents implements INode {
|
||||
}
|
||||
`
|
||||
|
||||
const builtinDeps = process.env.TOOL_FUNCTION_BUILTIN_DEP
|
||||
? defaultAllowBuiltInDep.concat(process.env.TOOL_FUNCTION_BUILTIN_DEP.split(','))
|
||||
: defaultAllowBuiltInDep
|
||||
const externalDeps = process.env.TOOL_FUNCTION_EXTERNAL_DEP ? process.env.TOOL_FUNCTION_EXTERNAL_DEP.split(',') : []
|
||||
const deps = availableDependencies.concat(externalDeps)
|
||||
|
||||
const nodeVMOptions = {
|
||||
console: 'inherit',
|
||||
sandbox,
|
||||
require: {
|
||||
external: { modules: deps },
|
||||
builtin: builtinDeps
|
||||
},
|
||||
eval: false,
|
||||
wasm: false,
|
||||
timeout: 10000
|
||||
} as any
|
||||
|
||||
const vm = new NodeVM(nodeVMOptions)
|
||||
try {
|
||||
let response = await vm.run(`module.exports = async function() {${code}}()`, __dirname)
|
||||
let response = await executeJavaScriptCode(code, sandbox, {
|
||||
useSandbox: false,
|
||||
timeout: 10000
|
||||
})
|
||||
|
||||
if (typeof response === 'object') {
|
||||
response = JSON.stringify(response)
|
||||
|
||||
@@ -24,12 +24,13 @@ import {
|
||||
getVars,
|
||||
handleEscapeCharacters,
|
||||
prepareSandboxVars,
|
||||
transformBracesWithColon
|
||||
transformBracesWithColon,
|
||||
executeJavaScriptCode,
|
||||
createCodeExecutionSandbox
|
||||
} from '../../../src/utils'
|
||||
import {
|
||||
convertStructuredSchemaToZod,
|
||||
customGet,
|
||||
getVM,
|
||||
processImageMessage,
|
||||
transformObjectPropertyToFunction,
|
||||
filterConversationHistory,
|
||||
@@ -708,9 +709,13 @@ const getReturnOutput = async (nodeData: INodeData, input: string, options: ICom
|
||||
throw new Error(e)
|
||||
}
|
||||
} else if (selectedTab === 'updateStateMemoryCode' && updateStateMemoryCode) {
|
||||
const vm = await getVM(appDataSource, databaseEntities, nodeData, options, flow)
|
||||
const sandbox = createCodeExecutionSandbox(input, variables, flow)
|
||||
|
||||
try {
|
||||
const response = await vm.run(`module.exports = async function() {${updateStateMemoryCode}}()`, __dirname)
|
||||
const response = await executeJavaScriptCode(updateStateMemoryCode, sandbox, {
|
||||
timeout: 10000
|
||||
})
|
||||
|
||||
if (typeof response !== 'object') throw new Error('Return output must be an object')
|
||||
return response
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { START } from '@langchain/langgraph'
|
||||
import { NodeVM } from '@flowiseai/nodevm'
|
||||
import { DataSource } from 'typeorm'
|
||||
import { ICommonObject, IDatabaseEntity, INode, INodeData, INodeParams, ISeqAgentNode } from '../../../src/Interface'
|
||||
import { availableDependencies, defaultAllowBuiltInDep, getVars, prepareSandboxVars } from '../../../src/utils'
|
||||
import { getVars, executeJavaScriptCode, createCodeExecutionSandbox } from '../../../src/utils'
|
||||
|
||||
const defaultFunc = `{
|
||||
aggregate: {
|
||||
value: (x, y) => x.concat(y), // here we append the new message to the existing messages
|
||||
default: () => []
|
||||
},
|
||||
replacedValue: {
|
||||
value: (x, y) => y ?? x,
|
||||
default: () => null
|
||||
}
|
||||
}`
|
||||
|
||||
@@ -198,37 +201,13 @@ class State_SeqAgents implements INode {
|
||||
input
|
||||
}
|
||||
|
||||
let sandbox: any = {
|
||||
util: undefined,
|
||||
Symbol: undefined,
|
||||
child_process: undefined,
|
||||
fs: undefined,
|
||||
process: undefined
|
||||
}
|
||||
sandbox['$vars'] = prepareSandboxVars(variables)
|
||||
sandbox['$flow'] = flow
|
||||
const sandbox = createCodeExecutionSandbox('', variables, flow)
|
||||
|
||||
const builtinDeps = process.env.TOOL_FUNCTION_BUILTIN_DEP
|
||||
? defaultAllowBuiltInDep.concat(process.env.TOOL_FUNCTION_BUILTIN_DEP.split(','))
|
||||
: defaultAllowBuiltInDep
|
||||
const externalDeps = process.env.TOOL_FUNCTION_EXTERNAL_DEP ? process.env.TOOL_FUNCTION_EXTERNAL_DEP.split(',') : []
|
||||
const deps = availableDependencies.concat(externalDeps)
|
||||
|
||||
const nodeVMOptions = {
|
||||
console: 'inherit',
|
||||
sandbox,
|
||||
require: {
|
||||
external: { modules: deps },
|
||||
builtin: builtinDeps
|
||||
},
|
||||
eval: false,
|
||||
wasm: false,
|
||||
timeout: 10000
|
||||
} as any
|
||||
|
||||
const vm = new NodeVM(nodeVMOptions)
|
||||
try {
|
||||
const response = await vm.run(`module.exports = async function() {return ${stateMemoryCode}}()`, __dirname)
|
||||
const response = await executeJavaScriptCode(`return ${stateMemoryCode}`, sandbox, {
|
||||
timeout: 10000
|
||||
})
|
||||
|
||||
if (typeof response !== 'object') throw new Error('State must be an object')
|
||||
const returnOutput: ISeqAgentNode = {
|
||||
id: nodeData.id,
|
||||
|
||||
@@ -15,8 +15,8 @@ import { RunnableConfig } from '@langchain/core/runnables'
|
||||
import { ARTIFACTS_PREFIX, SOURCE_DOCUMENTS_PREFIX, TOOL_ARGS_PREFIX } from '../../../src/agents'
|
||||
import { Document } from '@langchain/core/documents'
|
||||
import { DataSource } from 'typeorm'
|
||||
import { MessagesState, RunnableCallable, customGet, getVM } from '../commonUtils'
|
||||
import { getVars, prepareSandboxVars } from '../../../src/utils'
|
||||
import { MessagesState, RunnableCallable, customGet } from '../commonUtils'
|
||||
import { getVars, prepareSandboxVars, executeJavaScriptCode, createCodeExecutionSandbox } from '../../../src/utils'
|
||||
import { ChatPromptTemplate } from '@langchain/core/prompts'
|
||||
|
||||
const defaultApprovalPrompt = `You are about to execute tool: {tools}. Ask if user want to proceed`
|
||||
@@ -572,9 +572,13 @@ const getReturnOutput = async (
|
||||
throw new Error(e)
|
||||
}
|
||||
} else if (selectedTab === 'updateStateMemoryCode' && updateStateMemoryCode) {
|
||||
const vm = await getVM(appDataSource, databaseEntities, nodeData, options, flow)
|
||||
const sandbox = createCodeExecutionSandbox(input, variables, flow)
|
||||
|
||||
try {
|
||||
const response = await vm.run(`module.exports = async function() {${updateStateMemoryCode}}()`, __dirname)
|
||||
const response = await executeJavaScriptCode(updateStateMemoryCode, sandbox, {
|
||||
timeout: 10000
|
||||
})
|
||||
|
||||
if (typeof response !== 'object') throw new Error('Return output must be an object')
|
||||
return response
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { get } from 'lodash'
|
||||
import { z } from 'zod'
|
||||
import { DataSource } from 'typeorm'
|
||||
import { NodeVM } from '@flowiseai/nodevm'
|
||||
import { StructuredTool } from '@langchain/core/tools'
|
||||
import { ChatMistralAI } from '@langchain/mistralai'
|
||||
import { ChatAnthropic } from '@langchain/anthropic'
|
||||
@@ -17,7 +16,7 @@ import {
|
||||
IVisionChatModal,
|
||||
ConversationHistorySelection
|
||||
} from '../../src/Interface'
|
||||
import { availableDependencies, defaultAllowBuiltInDep, getVars, prepareSandboxVars } from '../../src/utils'
|
||||
import { getVars, executeJavaScriptCode, createCodeExecutionSandbox } from '../../src/utils'
|
||||
import { ChatPromptTemplate, BaseMessagePromptTemplateLike } from '@langchain/core/prompts'
|
||||
|
||||
export const checkCondition = (input: string | number | undefined, condition: string, value: string | number = ''): boolean => {
|
||||
@@ -150,46 +149,6 @@ export const processImageMessage = async (llm: BaseChatModel, nodeData: INodeDat
|
||||
return multiModalMessageContent
|
||||
}
|
||||
|
||||
export const getVM = async (
|
||||
appDataSource: DataSource,
|
||||
databaseEntities: IDatabaseEntity,
|
||||
nodeData: INodeData,
|
||||
options: ICommonObject,
|
||||
flow: ICommonObject
|
||||
) => {
|
||||
const variables = await getVars(appDataSource, databaseEntities, nodeData, options)
|
||||
|
||||
let sandbox: any = {
|
||||
util: undefined,
|
||||
Symbol: undefined,
|
||||
child_process: undefined,
|
||||
fs: undefined,
|
||||
process: undefined
|
||||
}
|
||||
sandbox['$vars'] = prepareSandboxVars(variables)
|
||||
sandbox['$flow'] = flow
|
||||
|
||||
const builtinDeps = process.env.TOOL_FUNCTION_BUILTIN_DEP
|
||||
? defaultAllowBuiltInDep.concat(process.env.TOOL_FUNCTION_BUILTIN_DEP.split(','))
|
||||
: defaultAllowBuiltInDep
|
||||
const externalDeps = process.env.TOOL_FUNCTION_EXTERNAL_DEP ? process.env.TOOL_FUNCTION_EXTERNAL_DEP.split(',') : []
|
||||
const deps = availableDependencies.concat(externalDeps)
|
||||
|
||||
const nodeVMOptions = {
|
||||
console: 'inherit',
|
||||
sandbox,
|
||||
require: {
|
||||
external: { modules: deps },
|
||||
builtin: builtinDeps
|
||||
},
|
||||
eval: false,
|
||||
wasm: false,
|
||||
timeout: 10000
|
||||
} as any
|
||||
|
||||
return new NodeVM(nodeVMOptions)
|
||||
}
|
||||
|
||||
export const customGet = (obj: any, path: string) => {
|
||||
if (path.includes('[-1]')) {
|
||||
const parts = path.split('.')
|
||||
@@ -426,9 +385,21 @@ export const checkMessageHistory = async (
|
||||
if (messageHistory) {
|
||||
const appDataSource = options.appDataSource as DataSource
|
||||
const databaseEntities = options.databaseEntities as IDatabaseEntity
|
||||
const vm = await getVM(appDataSource, databaseEntities, nodeData, options, {})
|
||||
|
||||
const variables = await getVars(appDataSource, databaseEntities, nodeData, options)
|
||||
const flow = {
|
||||
chatflowId: options.chatflowid,
|
||||
sessionId: options.sessionId,
|
||||
chatId: options.chatId
|
||||
}
|
||||
|
||||
const sandbox = createCodeExecutionSandbox('', variables, flow)
|
||||
|
||||
try {
|
||||
const response = await vm.run(`module.exports = async function() {${messageHistory}}()`, __dirname)
|
||||
const response = await executeJavaScriptCode(messageHistory, sandbox, {
|
||||
timeout: 10000
|
||||
})
|
||||
|
||||
if (!Array.isArray(response)) throw new Error('Returned message history must be an array')
|
||||
if (sysPrompt) {
|
||||
// insert at index 1
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { DataSource } from 'typeorm'
|
||||
import { z } from 'zod'
|
||||
import { NodeVM } from '@flowiseai/nodevm'
|
||||
import { RunnableConfig } from '@langchain/core/runnables'
|
||||
import { CallbackManagerForToolRun, Callbacks, CallbackManager, parseCallbackConfigArg } from '@langchain/core/callbacks/manager'
|
||||
import { StructuredTool } from '@langchain/core/tools'
|
||||
import { ICommonObject, IDatabaseEntity, INode, INodeData, INodeOptionsValue, INodeParams } from '../../../src/Interface'
|
||||
import { availableDependencies, defaultAllowBuiltInDep, getCredentialData, getCredentialParam } from '../../../src/utils'
|
||||
import { getCredentialData, getCredentialParam, executeJavaScriptCode, createCodeExecutionSandbox } from '../../../src/utils'
|
||||
import { isValidUUID, isValidURL } from '../../../src/validator'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
class AgentAsTool_Tools implements INode {
|
||||
@@ -160,6 +160,16 @@ class AgentAsTool_Tools implements INode {
|
||||
|
||||
const baseURL = (nodeData.inputs?.baseURL as string) || (options.baseURL as string)
|
||||
|
||||
// Validate agentflowid is a valid UUID
|
||||
if (!selectedAgentflowId || !isValidUUID(selectedAgentflowId)) {
|
||||
throw new Error('Invalid agentflow ID: must be a valid UUID')
|
||||
}
|
||||
|
||||
// Validate baseURL is a valid URL
|
||||
if (!baseURL || !isValidURL(baseURL)) {
|
||||
throw new Error('Invalid base URL: must be a valid URL')
|
||||
}
|
||||
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
const agentflowApiKey = getCredentialParam('agentflowApiKey', credentialData, nodeData)
|
||||
|
||||
@@ -326,16 +336,6 @@ class AgentflowTool extends StructuredTool {
|
||||
body: JSON.stringify(body)
|
||||
}
|
||||
|
||||
let sandbox = {
|
||||
$callOptions: options,
|
||||
$callBody: body,
|
||||
util: undefined,
|
||||
Symbol: undefined,
|
||||
child_process: undefined,
|
||||
fs: undefined,
|
||||
process: undefined
|
||||
}
|
||||
|
||||
const code = `
|
||||
const fetch = require('node-fetch');
|
||||
const url = "${this.baseURL}/api/v1/prediction/${this.agentflowid}";
|
||||
@@ -353,26 +353,19 @@ try {
|
||||
return '';
|
||||
}
|
||||
`
|
||||
const builtinDeps = process.env.TOOL_FUNCTION_BUILTIN_DEP
|
||||
? defaultAllowBuiltInDep.concat(process.env.TOOL_FUNCTION_BUILTIN_DEP.split(','))
|
||||
: defaultAllowBuiltInDep
|
||||
const externalDeps = process.env.TOOL_FUNCTION_EXTERNAL_DEP ? process.env.TOOL_FUNCTION_EXTERNAL_DEP.split(',') : []
|
||||
const deps = availableDependencies.concat(externalDeps)
|
||||
|
||||
const vmOptions = {
|
||||
console: 'inherit',
|
||||
sandbox,
|
||||
require: {
|
||||
external: { modules: deps },
|
||||
builtin: builtinDeps
|
||||
},
|
||||
eval: false,
|
||||
wasm: false,
|
||||
// Create additional sandbox variables
|
||||
const additionalSandbox: ICommonObject = {
|
||||
$callOptions: options,
|
||||
$callBody: body
|
||||
}
|
||||
|
||||
const sandbox = createCodeExecutionSandbox('', [], {}, additionalSandbox)
|
||||
|
||||
const response = await executeJavaScriptCode(code, sandbox, {
|
||||
useSandbox: false,
|
||||
timeout: 10000
|
||||
} as any
|
||||
|
||||
const vm = new NodeVM(vmOptions)
|
||||
const response = await vm.run(`module.exports = async function() {${code}}()`, __dirname)
|
||||
})
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { DataSource } from 'typeorm'
|
||||
import { z } from 'zod'
|
||||
import { NodeVM } from '@flowiseai/nodevm'
|
||||
import { RunnableConfig } from '@langchain/core/runnables'
|
||||
import { CallbackManagerForToolRun, Callbacks, CallbackManager, parseCallbackConfigArg } from '@langchain/core/callbacks/manager'
|
||||
import { StructuredTool } from '@langchain/core/tools'
|
||||
import { ICommonObject, IDatabaseEntity, INode, INodeData, INodeOptionsValue, INodeParams } from '../../../src/Interface'
|
||||
import { availableDependencies, defaultAllowBuiltInDep, getCredentialData, getCredentialParam } from '../../../src/utils'
|
||||
import { getCredentialData, getCredentialParam, executeJavaScriptCode, createCodeExecutionSandbox } from '../../../src/utils'
|
||||
import { isValidUUID, isValidURL } from '../../../src/validator'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
class ChatflowTool_Tools implements INode {
|
||||
@@ -168,6 +168,16 @@ class ChatflowTool_Tools implements INode {
|
||||
|
||||
const baseURL = (nodeData.inputs?.baseURL as string) || (options.baseURL as string)
|
||||
|
||||
// Validate selectedChatflowId is a valid UUID
|
||||
if (!selectedChatflowId || !isValidUUID(selectedChatflowId)) {
|
||||
throw new Error('Invalid chatflow ID: must be a valid UUID')
|
||||
}
|
||||
|
||||
// Validate baseURL is a valid URL
|
||||
if (!baseURL || !isValidURL(baseURL)) {
|
||||
throw new Error('Invalid base URL: must be a valid URL')
|
||||
}
|
||||
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
const chatflowApiKey = getCredentialParam('chatflowApiKey', credentialData, nodeData)
|
||||
|
||||
@@ -334,16 +344,6 @@ class ChatflowTool extends StructuredTool {
|
||||
body: JSON.stringify(body)
|
||||
}
|
||||
|
||||
let sandbox = {
|
||||
$callOptions: options,
|
||||
$callBody: body,
|
||||
util: undefined,
|
||||
Symbol: undefined,
|
||||
child_process: undefined,
|
||||
fs: undefined,
|
||||
process: undefined
|
||||
}
|
||||
|
||||
const code = `
|
||||
const fetch = require('node-fetch');
|
||||
const url = "${this.baseURL}/api/v1/prediction/${this.chatflowid}";
|
||||
@@ -361,26 +361,19 @@ try {
|
||||
return '';
|
||||
}
|
||||
`
|
||||
const builtinDeps = process.env.TOOL_FUNCTION_BUILTIN_DEP
|
||||
? defaultAllowBuiltInDep.concat(process.env.TOOL_FUNCTION_BUILTIN_DEP.split(','))
|
||||
: defaultAllowBuiltInDep
|
||||
const externalDeps = process.env.TOOL_FUNCTION_EXTERNAL_DEP ? process.env.TOOL_FUNCTION_EXTERNAL_DEP.split(',') : []
|
||||
const deps = availableDependencies.concat(externalDeps)
|
||||
|
||||
const vmOptions = {
|
||||
console: 'inherit',
|
||||
sandbox,
|
||||
require: {
|
||||
external: { modules: deps },
|
||||
builtin: builtinDeps
|
||||
},
|
||||
eval: false,
|
||||
wasm: false,
|
||||
// Create additional sandbox variables
|
||||
const additionalSandbox: ICommonObject = {
|
||||
$callOptions: options,
|
||||
$callBody: body
|
||||
}
|
||||
|
||||
const sandbox = createCodeExecutionSandbox('', [], {}, additionalSandbox)
|
||||
|
||||
const response = await executeJavaScriptCode(code, sandbox, {
|
||||
useSandbox: false,
|
||||
timeout: 10000
|
||||
} as any
|
||||
|
||||
const vm = new NodeVM(vmOptions)
|
||||
const response = await vm.run(`module.exports = async function() {${code}}()`, __dirname)
|
||||
})
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface'
|
||||
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
|
||||
import { StructuredTool, ToolInputParsingException, ToolParams } from '@langchain/core/tools'
|
||||
import { CodeInterpreter } from '@e2b/code-interpreter'
|
||||
import { Sandbox } from '@e2b/code-interpreter'
|
||||
import { z } from 'zod'
|
||||
import { addSingleFileToStorage } from '../../../src/storageUtils'
|
||||
import { CallbackManager, CallbackManagerForToolRun, Callbacks, parseCallbackConfigArg } from '@langchain/core/callbacks/manager'
|
||||
@@ -107,7 +107,7 @@ export class E2BTool extends StructuredTool {
|
||||
|
||||
description = DESC
|
||||
|
||||
instance: CodeInterpreter
|
||||
instance: Sandbox
|
||||
|
||||
apiKey: string
|
||||
|
||||
@@ -204,8 +204,8 @@ export class E2BTool extends StructuredTool {
|
||||
flowConfig = { ...this.flowObj, ...flowConfig }
|
||||
try {
|
||||
if ('input' in arg) {
|
||||
this.instance = await CodeInterpreter.create({ apiKey: this.apiKey })
|
||||
const execution = await this.instance.notebook.execCell(arg?.input)
|
||||
this.instance = await Sandbox.create({ apiKey: this.apiKey })
|
||||
const execution = await this.instance.runCode(arg?.input, { language: 'python' })
|
||||
|
||||
const artifacts = []
|
||||
for (const result of execution.results) {
|
||||
@@ -251,8 +251,6 @@ export class E2BTool extends StructuredTool {
|
||||
}
|
||||
}
|
||||
|
||||
this.instance.close()
|
||||
|
||||
let output = ''
|
||||
|
||||
if (execution.text) output = execution.text
|
||||
@@ -267,7 +265,7 @@ export class E2BTool extends StructuredTool {
|
||||
return 'No input provided'
|
||||
}
|
||||
} catch (e) {
|
||||
if (this.instance) this.instance.close()
|
||||
if (this.instance) this.instance.kill()
|
||||
return typeof e === 'string' ? e : JSON.stringify(e, null, 2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { z } from 'zod'
|
||||
import { NodeVM } from '@flowiseai/nodevm'
|
||||
import { RunnableConfig } from '@langchain/core/runnables'
|
||||
import { StructuredTool, ToolParams } from '@langchain/core/tools'
|
||||
import { CallbackManagerForToolRun, Callbacks, CallbackManager, parseCallbackConfigArg } from '@langchain/core/callbacks/manager'
|
||||
import { availableDependencies, defaultAllowBuiltInDep, prepareSandboxVars } from '../../../src/utils'
|
||||
import { executeJavaScriptCode, createCodeExecutionSandbox } from '../../../src/utils'
|
||||
import { ICommonObject } from '../../../src/Interface'
|
||||
|
||||
class ToolInputParsingException extends Error {
|
||||
@@ -111,46 +110,23 @@ export class DynamicStructuredTool<
|
||||
_?: CallbackManagerForToolRun,
|
||||
flowConfig?: { sessionId?: string; chatId?: string; input?: string; state?: ICommonObject }
|
||||
): Promise<string> {
|
||||
let sandbox: any = {
|
||||
util: undefined,
|
||||
Symbol: undefined,
|
||||
child_process: undefined,
|
||||
fs: undefined,
|
||||
process: undefined
|
||||
}
|
||||
// Create additional sandbox variables for tool arguments
|
||||
const additionalSandbox: ICommonObject = {}
|
||||
|
||||
if (typeof arg === 'object' && Object.keys(arg).length) {
|
||||
for (const item in arg) {
|
||||
sandbox[`$${item}`] = arg[item]
|
||||
additionalSandbox[`$${item}`] = arg[item]
|
||||
}
|
||||
}
|
||||
|
||||
sandbox['$vars'] = prepareSandboxVars(this.variables)
|
||||
// Prepare flow object for sandbox
|
||||
const flow = this.flowObj ? { ...this.flowObj, ...flowConfig } : {}
|
||||
|
||||
// inject flow properties
|
||||
if (this.flowObj) {
|
||||
sandbox['$flow'] = { ...this.flowObj, ...flowConfig }
|
||||
}
|
||||
const sandbox = createCodeExecutionSandbox('', this.variables || [], flow, additionalSandbox)
|
||||
|
||||
const builtinDeps = process.env.TOOL_FUNCTION_BUILTIN_DEP
|
||||
? defaultAllowBuiltInDep.concat(process.env.TOOL_FUNCTION_BUILTIN_DEP.split(','))
|
||||
: defaultAllowBuiltInDep
|
||||
const externalDeps = process.env.TOOL_FUNCTION_EXTERNAL_DEP ? process.env.TOOL_FUNCTION_EXTERNAL_DEP.split(',') : []
|
||||
const deps = availableDependencies.concat(externalDeps)
|
||||
|
||||
const options = {
|
||||
console: 'inherit',
|
||||
sandbox,
|
||||
require: {
|
||||
external: { modules: deps },
|
||||
builtin: builtinDeps
|
||||
},
|
||||
eval: false,
|
||||
wasm: false,
|
||||
const response = await executeJavaScriptCode(this.code, sandbox, {
|
||||
timeout: 10000
|
||||
} as any
|
||||
|
||||
const vm = new NodeVM(options)
|
||||
const response = await vm.run(`module.exports = async function() {${this.code}}()`, __dirname)
|
||||
})
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { z } from 'zod'
|
||||
import { RequestInit } from 'node-fetch'
|
||||
import { NodeVM } from '@flowiseai/nodevm'
|
||||
import { RunnableConfig } from '@langchain/core/runnables'
|
||||
import { StructuredTool, ToolParams } from '@langchain/core/tools'
|
||||
import { CallbackManagerForToolRun, Callbacks, CallbackManager, parseCallbackConfigArg } from '@langchain/core/callbacks/manager'
|
||||
import { availableDependencies, defaultAllowBuiltInDep, prepareSandboxVars } from '../../../src/utils'
|
||||
import { executeJavaScriptCode, createCodeExecutionSandbox } from '../../../src/utils'
|
||||
import { ICommonObject } from '../../../src/Interface'
|
||||
|
||||
const removeNulls = (obj: Record<string, any>) => {
|
||||
@@ -217,32 +216,22 @@ export class DynamicStructuredTool<
|
||||
_?: CallbackManagerForToolRun,
|
||||
flowConfig?: { sessionId?: string; chatId?: string; input?: string; state?: ICommonObject }
|
||||
): Promise<string> {
|
||||
let sandbox: any = {
|
||||
util: undefined,
|
||||
Symbol: undefined,
|
||||
child_process: undefined,
|
||||
fs: undefined,
|
||||
process: undefined
|
||||
}
|
||||
let processedArg = { ...arg }
|
||||
|
||||
if (this.removeNulls && typeof processedArg === 'object' && processedArg !== null) {
|
||||
processedArg = removeNulls(processedArg)
|
||||
}
|
||||
|
||||
// Create additional sandbox variables for tool arguments
|
||||
const additionalSandbox: ICommonObject = {}
|
||||
|
||||
if (typeof processedArg === 'object' && Object.keys(processedArg).length) {
|
||||
for (const item in processedArg) {
|
||||
sandbox[`$${item}`] = processedArg[item]
|
||||
additionalSandbox[`$${item}`] = processedArg[item]
|
||||
}
|
||||
}
|
||||
|
||||
sandbox['$vars'] = prepareSandboxVars(this.variables)
|
||||
|
||||
// inject flow properties
|
||||
if (this.flowObj) {
|
||||
sandbox['$flow'] = { ...this.flowObj, ...flowConfig }
|
||||
}
|
||||
|
||||
// Prepare HTTP request options
|
||||
const callOptions: RequestInit = {
|
||||
method: this.method,
|
||||
headers: {
|
||||
@@ -253,31 +242,20 @@ export class DynamicStructuredTool<
|
||||
if (arg.RequestBody && this.method.toUpperCase() !== 'GET') {
|
||||
callOptions.body = JSON.stringify(arg.RequestBody)
|
||||
}
|
||||
sandbox['$options'] = callOptions
|
||||
additionalSandbox['$options'] = callOptions
|
||||
|
||||
// Generate complete URL
|
||||
const completeUrl = getUrl(this.baseUrl, arg)
|
||||
sandbox['$url'] = completeUrl
|
||||
additionalSandbox['$url'] = completeUrl
|
||||
|
||||
const builtinDeps = process.env.TOOL_FUNCTION_BUILTIN_DEP
|
||||
? defaultAllowBuiltInDep.concat(process.env.TOOL_FUNCTION_BUILTIN_DEP.split(','))
|
||||
: defaultAllowBuiltInDep
|
||||
const externalDeps = process.env.TOOL_FUNCTION_EXTERNAL_DEP ? process.env.TOOL_FUNCTION_EXTERNAL_DEP.split(',') : []
|
||||
const deps = availableDependencies.concat(externalDeps)
|
||||
// Prepare flow object for sandbox
|
||||
const flow = this.flowObj ? { ...this.flowObj, ...flowConfig } : {}
|
||||
|
||||
const options = {
|
||||
console: 'inherit',
|
||||
sandbox,
|
||||
require: {
|
||||
external: { modules: deps },
|
||||
builtin: builtinDeps
|
||||
},
|
||||
eval: false,
|
||||
wasm: false,
|
||||
const sandbox = createCodeExecutionSandbox('', this.variables || [], flow, additionalSandbox)
|
||||
|
||||
const response = await executeJavaScriptCode(this.customCode || defaultCode, sandbox, {
|
||||
timeout: 10000
|
||||
} as any
|
||||
|
||||
const vm = new NodeVM(options)
|
||||
const response = await vm.run(`module.exports = async function() {${this.customCode || defaultCode}}()`, __dirname)
|
||||
})
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { flatten } from 'lodash'
|
||||
import { type StructuredTool } from '@langchain/core/tools'
|
||||
import { NodeVM } from '@flowiseai/nodevm'
|
||||
import { DataSource } from 'typeorm'
|
||||
import { availableDependencies, defaultAllowBuiltInDep, getVars, handleEscapeCharacters, prepareSandboxVars } from '../../../src/utils'
|
||||
import { getVars, handleEscapeCharacters, executeJavaScriptCode, createCodeExecutionSandbox } from '../../../src/utils'
|
||||
import { ICommonObject, IDatabaseEntity, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
|
||||
|
||||
class CustomFunction_Utilities implements INode {
|
||||
@@ -118,45 +117,24 @@ class CustomFunction_Utilities implements INode {
|
||||
}
|
||||
}
|
||||
|
||||
let sandbox: any = {
|
||||
$input: input,
|
||||
util: undefined,
|
||||
Symbol: undefined,
|
||||
child_process: undefined,
|
||||
fs: undefined,
|
||||
process: undefined
|
||||
// Create additional sandbox variables
|
||||
const additionalSandbox: ICommonObject = {
|
||||
$tools: tools
|
||||
}
|
||||
sandbox['$vars'] = prepareSandboxVars(variables)
|
||||
sandbox['$flow'] = flow
|
||||
sandbox['$tools'] = tools
|
||||
|
||||
// Add input variables to sandbox
|
||||
if (Object.keys(inputVars).length) {
|
||||
for (const item in inputVars) {
|
||||
sandbox[`$${item}`] = inputVars[item]
|
||||
additionalSandbox[`$${item}`] = inputVars[item]
|
||||
}
|
||||
}
|
||||
|
||||
const builtinDeps = process.env.TOOL_FUNCTION_BUILTIN_DEP
|
||||
? defaultAllowBuiltInDep.concat(process.env.TOOL_FUNCTION_BUILTIN_DEP.split(','))
|
||||
: defaultAllowBuiltInDep
|
||||
const externalDeps = process.env.TOOL_FUNCTION_EXTERNAL_DEP ? process.env.TOOL_FUNCTION_EXTERNAL_DEP.split(',') : []
|
||||
const deps = availableDependencies.concat(externalDeps)
|
||||
const sandbox = createCodeExecutionSandbox(input, variables, flow, additionalSandbox)
|
||||
|
||||
const nodeVMOptions = {
|
||||
console: 'inherit',
|
||||
sandbox,
|
||||
require: {
|
||||
external: { modules: deps },
|
||||
builtin: builtinDeps
|
||||
},
|
||||
eval: false,
|
||||
wasm: false,
|
||||
timeout: 10000
|
||||
} as any
|
||||
|
||||
const vm = new NodeVM(nodeVMOptions)
|
||||
try {
|
||||
const response = await vm.run(`module.exports = async function() {${javascriptFunction}}()`, __dirname)
|
||||
const response = await executeJavaScriptCode(javascriptFunction, sandbox, {
|
||||
timeout: 10000
|
||||
})
|
||||
|
||||
if (typeof response === 'string' && !isEndingNode) {
|
||||
return handleEscapeCharacters(response, false)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { NodeVM } from '@flowiseai/nodevm'
|
||||
import { DataSource } from 'typeorm'
|
||||
import { availableDependencies, defaultAllowBuiltInDep, getVars, handleEscapeCharacters, prepareSandboxVars } from '../../../src/utils'
|
||||
import { getVars, handleEscapeCharacters, executeJavaScriptCode, createCodeExecutionSandbox } from '../../../src/utils'
|
||||
import { ICommonObject, IDatabaseEntity, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
|
||||
|
||||
class IfElseFunction_Utilities implements INode {
|
||||
@@ -119,48 +118,30 @@ class IfElseFunction_Utilities implements INode {
|
||||
}
|
||||
}
|
||||
|
||||
let sandbox: any = {
|
||||
$input: input,
|
||||
util: undefined,
|
||||
Symbol: undefined,
|
||||
child_process: undefined,
|
||||
fs: undefined,
|
||||
process: undefined
|
||||
}
|
||||
sandbox['$vars'] = prepareSandboxVars(variables)
|
||||
sandbox['$flow'] = flow
|
||||
// Create additional sandbox variables
|
||||
const additionalSandbox: ICommonObject = {}
|
||||
|
||||
// Add input variables to sandbox
|
||||
if (Object.keys(inputVars).length) {
|
||||
for (const item in inputVars) {
|
||||
sandbox[`$${item}`] = inputVars[item]
|
||||
additionalSandbox[`$${item}`] = inputVars[item]
|
||||
}
|
||||
}
|
||||
|
||||
const builtinDeps = process.env.TOOL_FUNCTION_BUILTIN_DEP
|
||||
? defaultAllowBuiltInDep.concat(process.env.TOOL_FUNCTION_BUILTIN_DEP.split(','))
|
||||
: defaultAllowBuiltInDep
|
||||
const externalDeps = process.env.TOOL_FUNCTION_EXTERNAL_DEP ? process.env.TOOL_FUNCTION_EXTERNAL_DEP.split(',') : []
|
||||
const deps = availableDependencies.concat(externalDeps)
|
||||
const sandbox = createCodeExecutionSandbox(input, variables, flow, additionalSandbox)
|
||||
|
||||
const nodeVMOptions = {
|
||||
console: 'inherit',
|
||||
sandbox,
|
||||
require: {
|
||||
external: { modules: deps },
|
||||
builtin: builtinDeps
|
||||
},
|
||||
eval: false,
|
||||
wasm: false,
|
||||
timeout: 10000
|
||||
} as any
|
||||
|
||||
const vm = new NodeVM(nodeVMOptions)
|
||||
try {
|
||||
const responseTrue = await vm.run(`module.exports = async function() {${ifFunction}}()`, __dirname)
|
||||
const responseTrue = await executeJavaScriptCode(ifFunction, sandbox, {
|
||||
timeout: 10000
|
||||
})
|
||||
|
||||
if (responseTrue)
|
||||
return { output: typeof responseTrue === 'string' ? handleEscapeCharacters(responseTrue, false) : responseTrue, type: true }
|
||||
|
||||
const responseFalse = await vm.run(`module.exports = async function() {${elseFunction}}()`, __dirname)
|
||||
const responseFalse = await executeJavaScriptCode(elseFunction, sandbox, {
|
||||
timeout: 10000
|
||||
})
|
||||
|
||||
return { output: typeof responseFalse === 'string' ? handleEscapeCharacters(responseFalse, false) : responseFalse, type: false }
|
||||
} catch (e) {
|
||||
throw new Error(e)
|
||||
|
||||
Reference in New Issue
Block a user