From b9bbd8b807cbb7adfe81b7b0cb555b3b3e3a9412 Mon Sep 17 00:00:00 2001 From: Michael Kret Date: Thu, 20 Feb 2025 11:54:36 +0200 Subject: [PATCH 1/4] tools hints --- packages/editor-ui/src/utils/nodeViewUtils.ts | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/editor-ui/src/utils/nodeViewUtils.ts b/packages/editor-ui/src/utils/nodeViewUtils.ts index 80ec02b15df9e..9d2e8184fd781 100644 --- a/packages/editor-ui/src/utils/nodeViewUtils.ts +++ b/packages/editor-ui/src/utils/nodeViewUtils.ts @@ -15,7 +15,7 @@ import type { NodeHint, Workflow, } from 'n8n-workflow'; -import { NodeHelpers } from 'n8n-workflow'; +import { HTTP_REQUEST_TOOL_LANGCHAIN_NODE_TYPE, NodeHelpers } from 'n8n-workflow'; import type { RouteLocation } from 'vue-router'; /* @@ -248,6 +248,31 @@ export function getGenericHints({ }) { const nodeHints: NodeHint[] = []; + // tools hints + + if (node.type === HTTP_REQUEST_TOOL_LANGCHAIN_NODE_TYPE && hasNodeRun) { + const stringifiedParameters = JSON.stringify(workflowNode.parameters); + const placeholderRegex = /\{[a-zA-Z0-9_]+\}/; + if (!placeholderRegex.test(stringifiedParameters)) { + nodeHints.push({ + message: + 'No parameters are set up to be filled by AI. Add {placeholder_name} to the relevant parameter to do so.', + location: 'outputPane', + whenToDisplay: 'afterExecution', + }); + } + } else if (node.type.toLocaleLowerCase().includes('tool') && hasNodeRun) { + const stringifiedParameters = JSON.stringify(workflowNode.parameters); + if (!stringifiedParameters.includes('$fromAI')) { + nodeHints.push({ + message: + "No parameters are currently configured for AI input.
To enable AI-generated values, click the button on the left of the relevant parameter or manually add the following expression: {{ $fromAI('placeholder_name') }}.", + location: 'outputPane', + whenToDisplay: 'afterExecution', + }); + } + } + // add limit reached hint if (hasNodeRun && workflowNode.parameters.limit) { if (nodeOutputData.length === workflowNode.parameters.limit) { From 352fb6128b5054d4e804095d12976d9362deee17 Mon Sep 17 00:00:00 2001 From: Michael Kret Date: Thu, 6 Mar 2025 10:07:46 +0200 Subject: [PATCH 2/4] sendAndWait hint, tests --- .../editor-ui/src/utils/nodeViewUtils.test.ts | 52 ++++++++++++++++++- .../editor-ui/src/utils/nodeViewUtils.ts | 29 +++++------ 2 files changed, 64 insertions(+), 17 deletions(-) diff --git a/packages/frontend/editor-ui/src/utils/nodeViewUtils.test.ts b/packages/frontend/editor-ui/src/utils/nodeViewUtils.test.ts index 9e01b3449ce0e..a033555002370 100644 --- a/packages/frontend/editor-ui/src/utils/nodeViewUtils.test.ts +++ b/packages/frontend/editor-ui/src/utils/nodeViewUtils.test.ts @@ -1,7 +1,7 @@ import { generateOffsets, getGenericHints, getNewNodePosition } from './nodeViewUtils'; import type { INode, INodeTypeDescription, INodeExecutionData, Workflow } from 'n8n-workflow'; import type { INodeUi, XYPosition } from '@/Interface'; -import { NodeHelpers } from 'n8n-workflow'; +import { NodeHelpers, SEND_AND_WAIT_OPERATION } from 'n8n-workflow'; import { describe, it, expect, beforeEach } from 'vitest'; import { mock, type MockProxy } from 'vitest-mock-extended'; @@ -19,7 +19,7 @@ describe('getGenericHints', () => { beforeEach(() => { mockWorkflowNode = mock(); - mockNode = mock(); + mockNode = mock({ type: 'test' }); mockNodeType = mock(); mockNodeOutputData = []; mockWorkflow = mock(); @@ -138,6 +138,54 @@ describe('getGenericHints', () => { }, ]); }); + + it('should return an hint for tool nodes without AI expressions', () => { + mockNode.type = 'custom-tool-node'; + hasNodeRun = true; + mockWorkflowNode.parameters = { param1: 'staticValue' }; + + const hints = getGenericHints({ + workflowNode: mockWorkflowNode, + node: mockNode, + nodeType: mockNodeType, + nodeOutputData: mockNodeOutputData, + hasMultipleInputItems, + workflow: mockWorkflow, + hasNodeRun, + }); + + expect(hints).toEqual([ + { + message: + "No parameters are set up to be filled by AI. To enable AI-generated values, click the button on the left of the relevant parameter or add the expression: {{ $fromAI('placeholder_name') }}.", + location: 'outputPane', + whenToDisplay: 'afterExecution', + }, + ]); + }); + + it('should return a hint for sendAndWait operation with multiple input items', () => { + hasMultipleInputItems = true; + mockWorkflowNode.parameters.operation = SEND_AND_WAIT_OPERATION; + mockWorkflow.getNode.mockReturnValue({ executeOnce: false } as unknown as INode); + + const hints = getGenericHints({ + workflowNode: mockWorkflowNode, + node: mockNode, + nodeType: mockNodeType, + nodeOutputData: mockNodeOutputData, + hasMultipleInputItems, + workflow: mockWorkflow, + hasNodeRun, + }); + + expect(hints).toEqual([ + { + message: 'This action will run only once, for the first input item', + location: 'outputPane', + }, + ]); + }); }); describe('generateOffsets', () => { diff --git a/packages/frontend/editor-ui/src/utils/nodeViewUtils.ts b/packages/frontend/editor-ui/src/utils/nodeViewUtils.ts index 23b3b7e28567a..4ae66a3457360 100644 --- a/packages/frontend/editor-ui/src/utils/nodeViewUtils.ts +++ b/packages/frontend/editor-ui/src/utils/nodeViewUtils.ts @@ -15,7 +15,7 @@ import type { NodeHint, Workflow, } from 'n8n-workflow'; -import { HTTP_REQUEST_TOOL_LANGCHAIN_NODE_TYPE, NodeHelpers } from 'n8n-workflow'; +import { NodeHelpers, SEND_AND_WAIT_OPERATION } from 'n8n-workflow'; import type { RouteLocation } from 'vue-router'; /* @@ -281,24 +281,12 @@ export function getGenericHints({ const nodeHints: NodeHint[] = []; // tools hints - - if (node.type === HTTP_REQUEST_TOOL_LANGCHAIN_NODE_TYPE && hasNodeRun) { - const stringifiedParameters = JSON.stringify(workflowNode.parameters); - const placeholderRegex = /\{[a-zA-Z0-9_]+\}/; - if (!placeholderRegex.test(stringifiedParameters)) { - nodeHints.push({ - message: - 'No parameters are set up to be filled by AI. Add {placeholder_name} to the relevant parameter to do so.', - location: 'outputPane', - whenToDisplay: 'afterExecution', - }); - } - } else if (node.type.toLocaleLowerCase().includes('tool') && hasNodeRun) { + if (node?.type.toLocaleLowerCase().includes('tool') && hasNodeRun) { const stringifiedParameters = JSON.stringify(workflowNode.parameters); if (!stringifiedParameters.includes('$fromAI')) { nodeHints.push({ message: - "No parameters are currently configured for AI input.
To enable AI-generated values, click the button on the left of the relevant parameter or manually add the following expression: {{ $fromAI('placeholder_name') }}.", + "No parameters are set up to be filled by AI. To enable AI-generated values, click the button on the left of the relevant parameter or add the expression: {{ $fromAI('placeholder_name') }}.", location: 'outputPane', whenToDisplay: 'afterExecution', }); @@ -331,6 +319,17 @@ export function getGenericHints({ } } + // add sendAndWait hint + if (hasMultipleInputItems && workflowNode.parameters.operation === SEND_AND_WAIT_OPERATION) { + const executeOnce = workflow.getNode(node.name)?.executeOnce; + if (!executeOnce) { + nodeHints.push({ + message: 'This action will run only once, for the first input item', + location: 'outputPane', + }); + } + } + // add expression in field name hint for Set node if (node.type === SET_NODE_TYPE && node.parameters.mode === 'manual') { const rawParameters = NodeHelpers.getNodeParameters( From 59e09cc6cff2dc73b8faa024de27bbf5def5c93c Mon Sep 17 00:00:00 2001 From: Michael Kret Date: Tue, 11 Mar 2025 06:50:35 +0200 Subject: [PATCH 3/4] hint update --- packages/frontend/editor-ui/src/utils/nodeViewUtils.test.ts | 2 +- packages/frontend/editor-ui/src/utils/nodeViewUtils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/editor-ui/src/utils/nodeViewUtils.test.ts b/packages/frontend/editor-ui/src/utils/nodeViewUtils.test.ts index a033555002370..a1b5f16414675 100644 --- a/packages/frontend/editor-ui/src/utils/nodeViewUtils.test.ts +++ b/packages/frontend/editor-ui/src/utils/nodeViewUtils.test.ts @@ -157,7 +157,7 @@ describe('getGenericHints', () => { expect(hints).toEqual([ { message: - "No parameters are set up to be filled by AI. To enable AI-generated values, click the button on the left of the relevant parameter or add the expression: {{ $fromAI('placeholder_name') }}.", + "No parameters are set up to be filled by AI. To enable AI-generated values, click the button on the left of the relevant parameter or add the expression: {{ $fromAI('placeholder_name') }}.", location: 'outputPane', whenToDisplay: 'afterExecution', }, diff --git a/packages/frontend/editor-ui/src/utils/nodeViewUtils.ts b/packages/frontend/editor-ui/src/utils/nodeViewUtils.ts index 4ae66a3457360..a43ae2877be9f 100644 --- a/packages/frontend/editor-ui/src/utils/nodeViewUtils.ts +++ b/packages/frontend/editor-ui/src/utils/nodeViewUtils.ts @@ -286,7 +286,7 @@ export function getGenericHints({ if (!stringifiedParameters.includes('$fromAI')) { nodeHints.push({ message: - "No parameters are set up to be filled by AI. To enable AI-generated values, click the button on the left of the relevant parameter or add the expression: {{ $fromAI('placeholder_name') }}.", + "No parameters are set up to be filled by AI. To enable AI-generated values, click the button on the left of the relevant parameter or add the expression: {{ $fromAI('placeholder_name') }}.", location: 'outputPane', whenToDisplay: 'afterExecution', }); From 75f481073ac9344ed38af944298a6ca015120a8d Mon Sep 17 00:00:00 2001 From: Michael Kret Date: Wed, 12 Mar 2025 11:38:02 +0200 Subject: [PATCH 4/4] hint update --- packages/frontend/editor-ui/src/utils/nodeViewUtils.test.ts | 2 +- packages/frontend/editor-ui/src/utils/nodeViewUtils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/editor-ui/src/utils/nodeViewUtils.test.ts b/packages/frontend/editor-ui/src/utils/nodeViewUtils.test.ts index a1b5f16414675..911b26c09c45e 100644 --- a/packages/frontend/editor-ui/src/utils/nodeViewUtils.test.ts +++ b/packages/frontend/editor-ui/src/utils/nodeViewUtils.test.ts @@ -157,7 +157,7 @@ describe('getGenericHints', () => { expect(hints).toEqual([ { message: - "No parameters are set up to be filled by AI. To enable AI-generated values, click the button on the left of the relevant parameter or add the expression: {{ $fromAI('placeholder_name') }}.", + 'No parameters are set up to be filled by AI. Click on the ✨ button next to a parameter to allow AI to set its value.', location: 'outputPane', whenToDisplay: 'afterExecution', }, diff --git a/packages/frontend/editor-ui/src/utils/nodeViewUtils.ts b/packages/frontend/editor-ui/src/utils/nodeViewUtils.ts index a43ae2877be9f..0f31ff93d80f9 100644 --- a/packages/frontend/editor-ui/src/utils/nodeViewUtils.ts +++ b/packages/frontend/editor-ui/src/utils/nodeViewUtils.ts @@ -286,7 +286,7 @@ export function getGenericHints({ if (!stringifiedParameters.includes('$fromAI')) { nodeHints.push({ message: - "No parameters are set up to be filled by AI. To enable AI-generated values, click the button on the left of the relevant parameter or add the expression: {{ $fromAI('placeholder_name') }}.", + 'No parameters are set up to be filled by AI. Click on the ✨ button next to a parameter to allow AI to set its value.', location: 'outputPane', whenToDisplay: 'afterExecution', });