From 07e6c7e13f1ddac9abaa484f7ee502cd049d49ef Mon Sep 17 00:00:00 2001 From: oleg Date: Tue, 6 May 2025 09:31:43 +0200 Subject: [PATCH] feat(core): Improve nodeNameToToolName special characters normalization (#15126) --- .../@n8n/nodes-langchain/utils/helpers.ts | 6 +- .../utils/tests/helpers.test.ts | 57 ++++++++++++++++++- .../utils/create-node-as-tool.ts | 7 ++- 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/packages/@n8n/nodes-langchain/utils/helpers.ts b/packages/@n8n/nodes-langchain/utils/helpers.ts index 994f247fe9..0b61b7946b 100644 --- a/packages/@n8n/nodes-langchain/utils/helpers.ts +++ b/packages/@n8n/nodes-langchain/utils/helpers.ts @@ -251,6 +251,10 @@ export function unwrapNestedOutput(output: Record): Record\/\\'"^%$]/g, '_').replace(/_+/g, '_'); } diff --git a/packages/@n8n/nodes-langchain/utils/tests/helpers.test.ts b/packages/@n8n/nodes-langchain/utils/tests/helpers.test.ts index d9de5b1d3b..a7419c9fc8 100644 --- a/packages/@n8n/nodes-langchain/utils/tests/helpers.test.ts +++ b/packages/@n8n/nodes-langchain/utils/tests/helpers.test.ts @@ -5,9 +5,62 @@ import { NodeOperationError } from 'n8n-workflow'; import type { ISupplyDataFunctions, IExecuteFunctions, INode } from 'n8n-workflow'; import { z } from 'zod'; -import { escapeSingleCurlyBrackets, getConnectedTools, unwrapNestedOutput } from '../helpers'; +import { + escapeSingleCurlyBrackets, + getConnectedTools, + nodeNameToToolName, + unwrapNestedOutput, +} from '../helpers'; import { N8nTool } from '../N8nTool'; +describe('nodeNameToToolName', () => { + const getNodeWithName = (name: string): INode => ({ + id: 'test-node', + name, + type: 'test', + typeVersion: 1, + position: [0, 0] as [number, number], + parameters: {}, + }); + it('should replace spaces with underscores', () => { + expect(nodeNameToToolName(getNodeWithName('Test Node'))).toBe('Test_Node'); + }); + + it('should replace dots with underscores', () => { + expect(nodeNameToToolName(getNodeWithName('Test.Node'))).toBe('Test_Node'); + }); + + it('should replace question marks with underscores', () => { + expect(nodeNameToToolName(getNodeWithName('Test?Node'))).toBe('Test_Node'); + }); + + it('should replace exclamation marks with underscores', () => { + expect(nodeNameToToolName(getNodeWithName('Test!Node'))).toBe('Test_Node'); + }); + + it('should replace equals signs with underscores', () => { + expect(nodeNameToToolName(getNodeWithName('Test=Node'))).toBe('Test_Node'); + }); + + it('should replace multiple special characters with underscores', () => { + expect(nodeNameToToolName(getNodeWithName('Test.Node?With!Special=Chars'))).toBe( + 'Test_Node_With_Special_Chars', + ); + }); + + it('should handle names that already have underscores', () => { + expect(nodeNameToToolName(getNodeWithName('Test_Node'))).toBe('Test_Node'); + }); + + it('should handle names with consecutive special characters', () => { + expect(nodeNameToToolName(getNodeWithName('Test..!!??==Node'))).toBe('Test_Node'); + }); + + it('should replace various special characters with underscores', () => { + expect(nodeNameToToolName(getNodeWithName('Test#+*()[]{}:;,<>/\\\'"%$Node'))).toBe('Test_Node'); + }); +}); + describe('escapeSingleCurlyBrackets', () => { it('should return undefined when input is undefined', () => { expect(escapeSingleCurlyBrackets(undefined)).toBeUndefined(); @@ -166,7 +219,7 @@ describe('getConnectedTools', () => { name: 'Test Node', type: 'test', typeVersion: 1, - position: [0, 0], + position: [0, 0] as [number, number], parameters: {}, }; diff --git a/packages/core/src/execution-engine/node-execution-context/utils/create-node-as-tool.ts b/packages/core/src/execution-engine/node-execution-context/utils/create-node-as-tool.ts index 8f785698c4..d71b18626f 100644 --- a/packages/core/src/execution-engine/node-execution-context/utils/create-node-as-tool.ts +++ b/packages/core/src/execution-engine/node-execution-context/utils/create-node-as-tool.ts @@ -108,8 +108,13 @@ function makeDescription(node: INode, nodeType: INodeType): string { return (node.parameters.toolDescription as string) ?? nodeType.description.description; } +/** + * Converts a node name to a valid tool name by replacing special characters with underscores + * and collapsing consecutive underscores into a single one. + * This method is copied from `packages/@n8n/nodes-langchain/utils/helpers.ts`. + */ export function nodeNameToToolName(node: INode): string { - return node.name.replace(/ /g, '_'); + return node.name.replace(/[\s.?!=+#@&*()[\]{}:;,<>\/\\'"^%$]/g, '_').replace(/_+/g, '_'); } /**