fix(variables): Fix resolution on double < (#2016)

* Fix variable <>

* Ling

* Clean
This commit is contained in:
Siddharth Ganesan
2025-11-15 15:09:01 -08:00
committed by GitHub
parent fca92a7499
commit 949f9287cf
7 changed files with 90 additions and 36 deletions

View File

@@ -5,6 +5,7 @@ import type { LoopScope } from '@/executor/execution/state'
import type { BlockStateController } from '@/executor/execution/types'
import type { ExecutionContext, NormalizedBlockOutput } from '@/executor/types'
import type { LoopConfigWithNodes } from '@/executor/types/loop'
import { replaceValidReferences } from '@/executor/utils/reference-validation'
import {
buildSentinelEndId,
buildSentinelStartId,
@@ -271,16 +272,14 @@ export class LoopOrchestrator {
}
try {
const referencePattern = /<([^>]+)>/g
let evaluatedCondition = condition
logger.info('Evaluating loop condition', {
originalCondition: condition,
iteration: scope.iteration,
workflowVariables: ctx.workflowVariables,
})
evaluatedCondition = evaluatedCondition.replace(referencePattern, (match) => {
// Use generic utility for smart variable reference replacement
const evaluatedCondition = replaceValidReferences(condition, (match) => {
const resolved = this.resolver.resolveSingleReference(ctx, '', match, scope)
logger.info('Resolved variable reference in loop condition', {
reference: match,

View File

@@ -0,0 +1,49 @@
import { isLikelyReferenceSegment } from '@/lib/workflows/references'
import { REFERENCE } from '@/executor/consts'
/**
* Creates a regex pattern for matching variable references.
* Uses [^<>]+ to prevent matching across nested brackets (e.g., "<3 <real.ref>" matches separately).
*/
export function createReferencePattern(): RegExp {
return new RegExp(
`${REFERENCE.START}([^${REFERENCE.START}${REFERENCE.END}]+)${REFERENCE.END}`,
'g'
)
}
/**
* Creates a regex pattern for matching environment variables {{variable}}
*/
export function createEnvVarPattern(): RegExp {
return new RegExp(`\\${REFERENCE.ENV_VAR_START}([^}]+)\\${REFERENCE.ENV_VAR_END}`, 'g')
}
/**
* Combined pattern matching both <reference> and {{env_var}}
*/
export function createCombinedPattern(): RegExp {
return new RegExp(
`${REFERENCE.START}[^${REFERENCE.START}${REFERENCE.END}]+${REFERENCE.END}|` +
`\\${REFERENCE.ENV_VAR_START}[^}]+\\${REFERENCE.ENV_VAR_END}`,
'g'
)
}
/**
* Replaces variable references with smart validation.
* Distinguishes < operator from < bracket using isLikelyReferenceSegment.
*/
export function replaceValidReferences(
template: string,
replacer: (match: string) => string
): string {
const pattern = createReferencePattern()
return template.replace(pattern, (match) => {
if (!isLikelyReferenceSegment(match)) {
return match
}
return replacer(match)
})
}

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@/lib/logs/console/logger'
import { BlockType, REFERENCE } from '@/executor/consts'
import type { ExecutionState, LoopScope } from '@/executor/execution/state'
import type { ExecutionContext } from '@/executor/types'
import { replaceValidReferences } from '@/executor/utils/reference-validation'
import { BlockResolver } from '@/executor/variables/resolvers/block'
import { EnvResolver } from '@/executor/variables/resolvers/env'
import { LoopResolver } from '@/executor/variables/resolvers/loop'
@@ -147,21 +148,17 @@ export class VariableResolver {
loopScope?: LoopScope,
block?: SerializedBlock
): string {
let result = template
const resolutionContext: ResolutionContext = {
executionContext: ctx,
executionState: this.state,
currentNodeId,
loopScope,
}
const referenceRegex = new RegExp(
`${REFERENCE.START}([^${REFERENCE.END}]+)${REFERENCE.END}`,
'g'
)
let replacementError: Error | null = null
result = result.replace(referenceRegex, (match) => {
// Use generic utility for smart variable reference replacement
let result = replaceValidReferences(template, (match) => {
if (replacementError) return match
try {
@@ -202,21 +199,17 @@ export class VariableResolver {
template: string,
loopScope?: LoopScope
): string {
let result = template
const resolutionContext: ResolutionContext = {
executionContext: ctx,
executionState: this.state,
currentNodeId,
loopScope,
}
const referenceRegex = new RegExp(
`${REFERENCE.START}([^${REFERENCE.END}]+)${REFERENCE.END}`,
'g'
)
let replacementError: Error | null = null
result = result.replace(referenceRegex, (match) => {
// Use generic utility for smart variable reference replacement
let result = replaceValidReferences(template, (match) => {
if (replacementError) return match
try {