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'),
}))
vi.mock('@/lib/execution/isolated-vm', () => ({
executeInIsolatedVM: vi.fn(),
vi.mock('@/tools', () => ({
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,
contextVariables: Record<string, unknown>
): { result: unknown; stdout: string; error?: { message: string; name: string } } {
/**
* Simulates what the function_execute tool does when evaluating condition code
*/
function simulateConditionExecution(code: string): {
success: boolean
output?: { result: unknown }
error?: string
} {
try {
const fn = new Function(...Object.keys(contextVariables), code)
const result = fn(...Object.values(contextVariables))
return { result, stdout: '' }
// The code is in format: "const context = {...};\nreturn Boolean(...)"
// We need to execute it and return the result
const fn = new Function(code)
const result = fn()
return { success: true, output: { result } }
} catch (error: any) {
return {
result: null,
stdout: '',
error: { message: error.message, name: error.name || 'Error' },
success: false,
error: error.message,
}
}
}
@@ -143,8 +148,8 @@ describe('ConditionBlockHandler', () => {
vi.clearAllMocks()
mockExecuteInIsolatedVM.mockImplementation(async ({ code, contextVariables }) => {
return simulateIsolatedVMExecution(code, contextVariables)
mockExecuteTool.mockImplementation(async (_toolId: string, params: { code: string }) => {
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 type { BlockOutput } from '@/blocks/types'
import { BlockType, CONDITION, DEFAULTS, EDGE } from '@/executor/constants'
import type { BlockHandler, ExecutionContext } from '@/executor/types'
import type { SerializedBlock } from '@/serializer/types'
import { executeTool } from '@/tools'
const logger = createLogger('ConditionBlockHandler')
@@ -39,32 +38,38 @@ export async function evaluateConditionExpression(
}
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({
code,
params: {},
envVars: {},
contextVariables: { context: evalContext },
timeoutMs: CONDITION_TIMEOUT_MS,
requestId,
})
if (result.error) {
logger.error(`Failed to evaluate condition: ${result.error.message}`, {
if (!result.success) {
logger.error(`Failed to evaluate condition: ${result.error}`, {
originalCondition: conditionExpression,
resolvedCondition: resolvedConditionValue,
evalContext,
error: result.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) {
logger.error(`Failed to evaluate condition: ${evalError.message}`, {
originalCondition: conditionExpression,