Skip to content

Commit 9c14f5f

Browse files
author
Vikhyath Mondreti
committed
fix(func var resolution): variable ref codepath triggered
1 parent d50db1d commit 9c14f5f

File tree

4 files changed

+79
-5
lines changed

4 files changed

+79
-5
lines changed

apps/sim/app/api/function/execute/route.ts

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ const logger = createLogger('FunctionExecuteAPI')
1919
function resolveCodeVariables(
2020
code: string,
2121
params: Record<string, any>,
22-
envVars: Record<string, string> = {}
22+
envVars: Record<string, string> = {},
23+
blockData: Record<string, any> = {},
24+
blockNameMapping: Record<string, string> = {}
2325
): { resolvedCode: string; contextVariables: Record<string, any> } {
2426
let resolvedCode = code
2527
const contextVariables: Record<string, any> = {}
@@ -41,11 +43,54 @@ function resolveCodeVariables(
4143

4244
// Resolve tags with <tag_name> syntax (including nested paths like <block.response.data>)
4345
const tagMatches = resolvedCode.match(/<([a-zA-Z_][a-zA-Z0-9_.]*[a-zA-Z0-9_])>/g) || []
46+
47+
4448
for (const match of tagMatches) {
4549
const tagName = match.slice(1, -1).trim()
4650

47-
// Handle nested paths like "getrecord.response.data"
48-
const tagValue = getNestedValue(params, tagName) || ''
51+
52+
// Handle nested paths like "getrecord.response.data" or "function1.response.result"
53+
// First try params, then blockData directly, then try with block name mapping
54+
let tagValue = getNestedValue(params, tagName) || getNestedValue(blockData, tagName) || ''
55+
56+
// If not found and the path starts with a block name, try mapping the block name to ID
57+
if (!tagValue && tagName.includes('.')) {
58+
const pathParts = tagName.split('.')
59+
const normalizedBlockName = pathParts[0] // This should already be normalized like "function1"
60+
61+
// Find the block ID by looking for a block name that normalizes to this value
62+
let blockId = null
63+
let matchedBlockName = null
64+
65+
for (const [blockName, id] of Object.entries(blockNameMapping)) {
66+
// Apply the same normalization logic as the UI: remove spaces and lowercase
67+
const normalizedName = blockName.replace(/\s+/g, '').toLowerCase()
68+
if (normalizedName === normalizedBlockName) {
69+
blockId = id
70+
matchedBlockName = blockName
71+
break
72+
}
73+
}
74+
75+
if (blockId) {
76+
const remainingPath = pathParts.slice(1).join('.')
77+
const fullPath = `${blockId}.${remainingPath}`
78+
tagValue = getNestedValue(blockData, fullPath) || ''
79+
80+
}
81+
}
82+
83+
84+
85+
// If the value is a stringified JSON, parse it back to object
86+
if (typeof tagValue === 'string' && tagValue.length > 100 &&
87+
(tagValue.startsWith('{') || tagValue.startsWith('['))) {
88+
try {
89+
tagValue = JSON.parse(tagValue)
90+
} catch (e) {
91+
// Keep as string if parsing fails
92+
}
93+
}
4994

5095
// Instead of injecting large JSON directly, create a variable reference
5196
const safeVarName = `__tag_${tagName.replace(/[^a-zA-Z0-9_]/g, '_')}`
@@ -89,6 +134,8 @@ export async function POST(req: NextRequest) {
89134
params = {},
90135
timeout = 5000,
91136
envVars = {},
137+
blockData = {},
138+
blockNameMapping = {},
92139
workflowId,
93140
isCustomTool = false,
94141
} = body
@@ -106,7 +153,14 @@ export async function POST(req: NextRequest) {
106153
})
107154

108155
// Resolve variables in the code with workflow environment variables
109-
const { resolvedCode, contextVariables } = resolveCodeVariables(code, executionParams, envVars)
156+
logger.info(`[${requestId}] Original code:`, code.substring(0, 200))
157+
logger.info(`[${requestId}] Execution params keys:`, Object.keys(executionParams))
158+
159+
160+
const { resolvedCode, contextVariables } = resolveCodeVariables(code, executionParams, envVars, blockData, blockNameMapping)
161+
162+
logger.info(`[${requestId}] Resolved code:`, resolvedCode.substring(0, 200))
163+
logger.info(`[${requestId}] Context variables keys:`, Object.keys(contextVariables))
110164

111165
const executionMethod = 'vm' // Default execution method
112166

apps/sim/executor/handlers/function/function-handler.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,29 @@ export class FunctionBlockHandler implements BlockHandler {
2323
? inputs.code.map((c: { content: string }) => c.content).join('\n')
2424
: inputs.code
2525

26+
// Extract block data for variable resolution
27+
const blockData: Record<string, any> = {}
28+
const blockNameMapping: Record<string, string> = {}
29+
30+
for (const [blockId, blockState] of context.blockStates.entries()) {
31+
if (blockState.output) {
32+
blockData[blockId] = blockState.output
33+
34+
// Try to find the block name from the workflow
35+
const workflowBlock = context.workflow?.blocks?.find(b => b.id === blockId)
36+
if (workflowBlock?.metadata?.name) {
37+
blockNameMapping[workflowBlock.metadata.name] = blockId
38+
}
39+
}
40+
}
41+
2642
// Directly use the function_execute tool which calls the API route
27-
logger.info(`Executing function block via API route: ${block.id}`)
2843
const result = await executeTool('function_execute', {
2944
code: codeContent,
3045
timeout: inputs.timeout || 5000,
3146
envVars: context.environmentVariables || {},
47+
blockData: blockData, // Pass block data for variable resolution
48+
blockNameMapping: blockNameMapping, // Pass block name to ID mapping
3249
_context: { workflowId: context.workflowId },
3350
})
3451

apps/sim/executor/resolver.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,7 @@ export class InputResolver {
593593
isInTemplateLiteral
594594
)
595595
} else {
596+
// The function execution API will handle variable resolution within code strings
596597
formattedValue =
597598
typeof replacementValue === 'object'
598599
? JSON.stringify(replacementValue)

apps/sim/tools/function/execute.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ export const functionExecuteTool: ToolConfig<CodeExecutionInput, CodeExecutionOu
4545
code: codeContent,
4646
timeout: params.timeout || DEFAULT_TIMEOUT,
4747
envVars: params.envVars || {},
48+
blockData: params.blockData || {},
49+
blockNameMapping: params.blockNameMapping || {},
4850
workflowId: params._context?.workflowId,
4951
isCustomTool: params.isCustomTool || false,
5052
}

0 commit comments

Comments
 (0)