n8n/packages/@n8n/ai-workflow-builder.ee/evaluations/support/load-nodes.ts
Mutasem Aldmour 9729c2a5da
feat(ai-builder): Add code-base workflow builder (#24535)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-10 12:24:27 +00:00

81 lines
2.7 KiB
TypeScript

import { readFileSync, existsSync } from 'fs';
import { jsonParse, type INodeTypeDescription } from 'n8n-workflow';
import { join } from 'path';
interface NodeWithVersion extends INodeTypeDescription {
version: number | number[];
defaultVersion?: number;
}
// These types are ignored because they tend to cause issues when generating workflows
// Same as in ai-workflow-builder-agent.service.ts
const IGNORED_TYPES = new Set([
'@n8n/n8n-nodes-langchain.toolVectorStore',
'@n8n/n8n-nodes-langchain.documentGithubLoader',
'@n8n/n8n-nodes-langchain.code',
]);
// Parse disabled nodes from environment variable (comma-separated)
function getDisabledNodes(): Set<string> {
const disabledNodesEnv = process.env.N8N_EVALS_DISABLED_NODES ?? '';
return new Set(
disabledNodesEnv
.split(',')
.map((s) => s.trim())
.filter(Boolean),
);
}
/**
* Filter node types similar to production service:
* - Remove ignored types (hardcoded)
* - Remove disabled types (from env var)
* - Remove hidden nodes (except DataTable)
* - Merge tool nodes with their non-tool counterparts
*/
function filterNodeTypes(
nodeTypes: INodeTypeDescription[],
disabledNodes: Set<string>,
): INodeTypeDescription[] {
const visibleNodeTypes = nodeTypes.filter(
(nodeType) =>
!IGNORED_TYPES.has(nodeType.name) &&
!disabledNodes.has(nodeType.name) &&
// Filter out hidden nodes, except for the Data Table node which has custom hiding logic
(nodeType.hidden !== true || nodeType.name === 'n8n-nodes-base.dataTable'),
);
return visibleNodeTypes.map((nodeType) => {
// If the node type is a tool, merge it with the corresponding non-tool node type
const isTool = nodeType.name.endsWith('Tool');
if (!isTool) return nodeType;
const nonToolNode = nodeTypes.find((nt) => nt.name === nodeType.name.replace('Tool', ''));
if (!nonToolNode) return nodeType;
return { ...nonToolNode, ...nodeType };
});
}
export function loadNodesFromFile(): INodeTypeDescription[] {
const preferredPath = join(__dirname, '..', '.data', 'nodes.json');
const legacyPath = join(__dirname, '..', 'nodes.json');
const nodesPath = existsSync(preferredPath) ? preferredPath : legacyPath;
if (!existsSync(nodesPath)) {
throw new Error(
`nodes.json not found at ${nodesPath}. ` +
'Run n8n and export node definitions to evaluations/.data/nodes.json',
);
}
const nodesData = readFileSync(nodesPath, 'utf-8');
const allNodes = jsonParse<NodeWithVersion[]>(nodesData);
// Keep all version entries instead of selecting only the latest version.
// Workflows may use older node versions (e.g., removeDuplicates v1.1), and
// discarding those entries causes "Node type not found" validation errors.
const disabledNodes = getDisabledNodes();
return filterNodeTypes(allNodes, disabledNodes);
}