fix(condition): async execution isolated vm error (#2446)

* fix(condition): async execution isolated vm error

* fix tests
This commit is contained in:
Vikhyath Mondreti
2025-12-18 11:02:01 -08:00
committed by GitHub
parent 7ef1150383
commit 78b7643e65
2 changed files with 43 additions and 33 deletions

View File

@@ -17,27 +17,32 @@ vi.mock('@/lib/core/utils/request', () => ({
generateRequestId: vi.fn(() => 'test-request-id'), generateRequestId: vi.fn(() => 'test-request-id'),
})) }))
vi.mock('@/lib/execution/isolated-vm', () => ({ vi.mock('@/tools', () => ({
executeInIsolatedVM: vi.fn(), executeTool: vi.fn(),
})) }))
import { executeInIsolatedVM } from '@/lib/execution/isolated-vm' import { executeTool } from '@/tools'
const mockExecuteInIsolatedVM = executeInIsolatedVM as ReturnType<typeof vi.fn> const mockExecuteTool = executeTool as ReturnType<typeof vi.fn>
function simulateIsolatedVMExecution( /**
code: string, * Simulates what the function_execute tool does when evaluating condition code
contextVariables: Record<string, unknown> */
): { result: unknown; stdout: string; error?: { message: string; name: string } } { function simulateConditionExecution(code: string): {
success: boolean
output?: { result: unknown }
error?: string
} {
try { try {
const fn = new Function(...Object.keys(contextVariables), code) // The code is in format: "const context = {...};\nreturn Boolean(...)"
const result = fn(...Object.values(contextVariables)) // We need to execute it and return the result
return { result, stdout: '' } const fn = new Function(code)
const result = fn()
return { success: true, output: { result } }
} catch (error: any) { } catch (error: any) {
return { return {
result: null, success: false,
stdout: '', error: error.message,
error: { message: error.message, name: error.name || 'Error' },
} }
} }
} }
@@ -143,8 +148,8 @@ describe('ConditionBlockHandler', () => {
vi.clearAllMocks() vi.clearAllMocks()
mockExecuteInIsolatedVM.mockImplementation(async ({ code, contextVariables }) => { mockExecuteTool.mockImplementation(async (_toolId: string, params: { code: string }) => {
return simulateIsolatedVMExecution(code, contextVariables) return simulateConditionExecution(params.code)
}) })
}) })

View File

@@ -1,10 +1,9 @@
import { generateRequestId } from '@/lib/core/utils/request'
import { executeInIsolatedVM } from '@/lib/execution/isolated-vm'
import { createLogger } from '@/lib/logs/console/logger' import { createLogger } from '@/lib/logs/console/logger'
import type { BlockOutput } from '@/blocks/types' import type { BlockOutput } from '@/blocks/types'
import { BlockType, CONDITION, DEFAULTS, EDGE } from '@/executor/constants' import { BlockType, CONDITION, DEFAULTS, EDGE } from '@/executor/constants'
import type { BlockHandler, ExecutionContext } from '@/executor/types' import type { BlockHandler, ExecutionContext } from '@/executor/types'
import type { SerializedBlock } from '@/serializer/types' import type { SerializedBlock } from '@/serializer/types'
import { executeTool } from '@/tools'
const logger = createLogger('ConditionBlockHandler') const logger = createLogger('ConditionBlockHandler')
@@ -39,32 +38,38 @@ export async function evaluateConditionExpression(
} }
try { try {
const requestId = generateRequestId() const contextSetup = `const context = ${JSON.stringify(evalContext)};`
const code = `${contextSetup}\nreturn Boolean(${resolvedConditionValue})`
const code = `return Boolean(${resolvedConditionValue})` const result = await executeTool(
'function_execute',
{
code,
timeout: CONDITION_TIMEOUT_MS,
envVars: {},
_context: {
workflowId: ctx.workflowId,
workspaceId: ctx.workspaceId,
},
},
false,
false,
ctx
)
const result = await executeInIsolatedVM({ if (!result.success) {
code, logger.error(`Failed to evaluate condition: ${result.error}`, {
params: {},
envVars: {},
contextVariables: { context: evalContext },
timeoutMs: CONDITION_TIMEOUT_MS,
requestId,
})
if (result.error) {
logger.error(`Failed to evaluate condition: ${result.error.message}`, {
originalCondition: conditionExpression, originalCondition: conditionExpression,
resolvedCondition: resolvedConditionValue, resolvedCondition: resolvedConditionValue,
evalContext, evalContext,
error: result.error, error: result.error,
}) })
throw new Error( throw new Error(
`Evaluation error in condition: ${result.error.message}. (Resolved: ${resolvedConditionValue})` `Evaluation error in condition: ${result.error}. (Resolved: ${resolvedConditionValue})`
) )
} }
return Boolean(result.result) return Boolean(result.output?.result)
} catch (evalError: any) { } catch (evalError: any) {
logger.error(`Failed to evaluate condition: ${evalError.message}`, { logger.error(`Failed to evaluate condition: ${evalError.message}`, {
originalCondition: conditionExpression, originalCondition: conditionExpression,