diff --git a/apps/sim/app/api/function/execute/route.ts b/apps/sim/app/api/function/execute/route.ts index 434b2d54d..8a052bf1c 100644 --- a/apps/sim/app/api/function/execute/route.ts +++ b/apps/sim/app/api/function/execute/route.ts @@ -387,7 +387,12 @@ function resolveWorkflowVariables( if (type === 'number') { variableValue = Number(variableValue) } else if (type === 'boolean') { - variableValue = variableValue === 'true' || variableValue === true + if (typeof variableValue === 'boolean') { + // Already a boolean, keep as-is + } else { + const normalized = String(variableValue).toLowerCase().trim() + variableValue = normalized === 'true' || normalized === '1' + } } else if (type === 'json' && typeof variableValue === 'string') { try { variableValue = JSON.parse(variableValue) @@ -689,6 +694,10 @@ export async function POST(req: NextRequest) { for (const [k, v] of Object.entries(contextVariables)) { if (v === undefined) { prologue += `const ${k} = undefined;\n` + } else if (v === null) { + prologue += `const ${k} = null;\n` + } else if (typeof v === 'boolean' || typeof v === 'number') { + prologue += `const ${k} = ${v};\n` } else { prologue += `const ${k} = JSON.parse(${JSON.stringify(JSON.stringify(v))});\n` } @@ -764,6 +773,12 @@ export async function POST(req: NextRequest) { for (const [k, v] of Object.entries(contextVariables)) { if (v === undefined) { prologue += `${k} = None\n` + } else if (v === null) { + prologue += `${k} = None\n` + } else if (typeof v === 'boolean') { + prologue += `${k} = ${v ? 'True' : 'False'}\n` + } else if (typeof v === 'number') { + prologue += `${k} = ${v}\n` } else { prologue += `${k} = json.loads(${JSON.stringify(JSON.stringify(v))})\n` } diff --git a/apps/sim/executor/variables/resolver.ts b/apps/sim/executor/variables/resolver.ts index 980708931..fcb630707 100644 --- a/apps/sim/executor/variables/resolver.ts +++ b/apps/sim/executor/variables/resolver.ts @@ -157,7 +157,14 @@ export class VariableResolver { let replacementError: Error | null = null - // Use generic utility for smart variable reference replacement + const blockType = block?.metadata?.id + const language = + blockType === BlockType.FUNCTION + ? ((block?.config?.params as Record | undefined)?.language as + | string + | undefined) + : undefined + let result = replaceValidReferences(template, (match) => { if (replacementError) return match @@ -167,14 +174,18 @@ export class VariableResolver { return match } - const blockType = block?.metadata?.id const isInTemplateLiteral = blockType === BlockType.FUNCTION && template.includes('${') && template.includes('}') && template.includes('`') - return this.blockResolver.formatValueForBlock(resolved, blockType, isInTemplateLiteral) + return this.blockResolver.formatValueForBlock( + resolved, + blockType, + isInTemplateLiteral, + language + ) } catch (error) { replacementError = error instanceof Error ? error : new Error(String(error)) return match diff --git a/apps/sim/executor/variables/resolvers/block.ts b/apps/sim/executor/variables/resolvers/block.ts index 09d246e80..d741eaded 100644 --- a/apps/sim/executor/variables/resolvers/block.ts +++ b/apps/sim/executor/variables/resolvers/block.ts @@ -162,14 +162,15 @@ export class BlockResolver implements Resolver { public formatValueForBlock( value: any, blockType: string | undefined, - isInTemplateLiteral = false + isInTemplateLiteral = false, + language?: string ): string { if (blockType === 'condition') { return this.stringifyForCondition(value) } if (blockType === 'function') { - return this.formatValueForCodeContext(value, isInTemplateLiteral) + return this.formatValueForCodeContext(value, isInTemplateLiteral, language) } if (blockType === 'response') { @@ -210,7 +211,13 @@ export class BlockResolver implements Resolver { return String(value) } - private formatValueForCodeContext(value: any, isInTemplateLiteral: boolean): string { + private formatValueForCodeContext( + value: any, + isInTemplateLiteral: boolean, + language?: string + ): string { + const isPython = language === 'python' + if (isInTemplateLiteral) { if (typeof value === 'string') { return value @@ -218,6 +225,15 @@ export class BlockResolver implements Resolver { if (typeof value === 'object' && value !== null) { return JSON.stringify(value) } + if (typeof value === 'boolean') { + return isPython ? (value ? 'True' : 'False') : String(value) + } + if (value === undefined) { + return isPython ? 'None' : 'undefined' + } + if (value === null) { + return isPython ? 'None' : 'null' + } return String(value) } @@ -228,10 +244,13 @@ export class BlockResolver implements Resolver { return JSON.stringify(value) } if (value === undefined) { - return 'undefined' + return isPython ? 'None' : 'undefined' } if (value === null) { - return 'null' + return isPython ? 'None' : 'null' + } + if (typeof value === 'boolean') { + return isPython ? (value ? 'True' : 'False') : String(value) } return String(value) } diff --git a/apps/sim/lib/execution/isolated-vm-worker.cjs b/apps/sim/lib/execution/isolated-vm-worker.cjs index f6c587a15..932ef997d 100644 --- a/apps/sim/lib/execution/isolated-vm-worker.cjs +++ b/apps/sim/lib/execution/isolated-vm-worker.cjs @@ -132,6 +132,8 @@ async function executeCode(request) { for (const [key, value] of Object.entries(contextVariables)) { if (value === undefined) { await jail.set(key, undefined) + } else if (value === null) { + await jail.set(key, null) } else { await jail.set(key, new ivm.ExternalCopy(value).copyInto()) }