(
reactFlowNodeData: INodeData,
question: string,
form: Record<string, any>,
flowConfig: IFlowConfig | undefined,
availableVariables: Variable[],
variableOverrides: IVariableOverride[],
uploadedFilesContent: string,
chatHistory: IMessage[],
componentNodes: IComponentNodes,
agentFlowExecutedData?: IAgentflowExecutedData[],
iterationContext?: ICommonObject,
loopCounts?: Map<string, number>,
webhook?: Record<string, any>
)
| 239 | } |
| 240 | |
| 241 | export const resolveVariables = async ( |
| 242 | reactFlowNodeData: INodeData, |
| 243 | question: string, |
| 244 | form: Record<string, any>, |
| 245 | flowConfig: IFlowConfig | undefined, |
| 246 | availableVariables: Variable[], |
| 247 | variableOverrides: IVariableOverride[], |
| 248 | uploadedFilesContent: string, |
| 249 | chatHistory: IMessage[], |
| 250 | componentNodes: IComponentNodes, |
| 251 | agentFlowExecutedData?: IAgentflowExecutedData[], |
| 252 | iterationContext?: ICommonObject, |
| 253 | loopCounts?: Map<string, number>, |
| 254 | webhook?: Record<string, any> |
| 255 | ): Promise<INodeData> => { |
| 256 | let flowNodeData = cloneDeep(reactFlowNodeData) |
| 257 | const types = 'inputs' |
| 258 | |
| 259 | const resolveNodeReference = async (value: any): Promise<any> => { |
| 260 | // If value is an array, process each element |
| 261 | if (Array.isArray(value)) { |
| 262 | return Promise.all(value.map((item) => resolveNodeReference(item))) |
| 263 | } |
| 264 | |
| 265 | // If value is an object, process each property |
| 266 | if (typeof value === 'object' && value !== null) { |
| 267 | const resolvedObj: any = {} |
| 268 | for (const [key, val] of Object.entries(value)) { |
| 269 | resolvedObj[key] = await resolveNodeReference(val) |
| 270 | } |
| 271 | return resolvedObj |
| 272 | } |
| 273 | |
| 274 | // If value is not a string, return as is |
| 275 | if (typeof value !== 'string') return value |
| 276 | |
| 277 | // Convert legacy HTML content to markdown, preserving any markdown syntax within. |
| 278 | // Legacy content from old getHTML() starts with a TipTap block tag (e.g. <p>text</p>). |
| 279 | // Anchor with ^ to avoid matching intentional HTML/XML tags in user prompts |
| 280 | // (e.g. <instruction><div>...</div></instruction>). |
| 281 | if (/^\s*<(?:p|div|h[1-6]|ul|ol|blockquote|pre|table)\b/i.test(value)) { |
| 282 | const turndownService = new TurndownService() |
| 283 | // Disable escaping so markdown characters (e.g. ###, -, *) inside HTML are preserved as-is |
| 284 | turndownService.escape = (str: string) => str |
| 285 | value = turndownService.turndown(value) |
| 286 | } |
| 287 | |
| 288 | const matches = value.match(/{{(.*?)}}/g) |
| 289 | |
| 290 | if (!matches) return value |
| 291 | |
| 292 | let resolvedValue = value |
| 293 | for (const match of matches) { |
| 294 | // Remove {{ }} and trim whitespace |
| 295 | const reference = match.replace(/[{}]/g, '').trim() |
| 296 | const variableFullPath = reference |
| 297 | |
| 298 | if (variableFullPath === QUESTION_VAR_PREFIX) { |
no test coverage detected