mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-19 02:51:14 +00:00
feat(core): Implement wrapping of regular nodes as AI Tools (#10641)
Co-authored-by: JP van Oosten <jp@n8n.io>
This commit is contained in:
@@ -13,11 +13,12 @@ import {
|
||||
} from 'n8n-core';
|
||||
import type {
|
||||
KnownNodesAndCredentials,
|
||||
INodeTypeBaseDescription,
|
||||
INodeTypeDescription,
|
||||
INodeTypeData,
|
||||
ICredentialTypeData,
|
||||
} from 'n8n-workflow';
|
||||
import { ApplicationError, ErrorReporterProxy as ErrorReporter } from 'n8n-workflow';
|
||||
import { NodeHelpers, ApplicationError, ErrorReporterProxy as ErrorReporter } from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
CUSTOM_API_CALL_KEY,
|
||||
@@ -38,8 +39,11 @@ interface LoadedNodesAndCredentials {
|
||||
export class LoadNodesAndCredentials {
|
||||
private known: KnownNodesAndCredentials = { nodes: {}, credentials: {} };
|
||||
|
||||
// This contains the actually loaded objects, and their source paths
|
||||
loaded: LoadedNodesAndCredentials = { nodes: {}, credentials: {} };
|
||||
|
||||
// For nodes, this only contains the descriptions, loaded from either the
|
||||
// actual file, or the lazy loaded json
|
||||
types: Types = { nodes: [], credentials: [] };
|
||||
|
||||
loaders: Record<string, DirectoryLoader> = {};
|
||||
@@ -260,6 +264,34 @@ export class LoadNodesAndCredentials {
|
||||
return loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates all AI Agent tools by duplicating the node descriptions for
|
||||
* all nodes that are marked as `usableAsTool`. It basically modifies the
|
||||
* description. The actual wrapping happens in the langchain code for getting
|
||||
* the connected tools.
|
||||
*/
|
||||
createAiTools() {
|
||||
const usableNodes: Array<INodeTypeBaseDescription | INodeTypeDescription> =
|
||||
this.types.nodes.filter((nodetype) => nodetype.usableAsTool === true);
|
||||
|
||||
for (const usableNode of usableNodes) {
|
||||
const description: INodeTypeBaseDescription | INodeTypeDescription =
|
||||
structuredClone(usableNode);
|
||||
const wrapped = NodeHelpers.convertNodeToAiTool({ description }).description;
|
||||
|
||||
this.types.nodes.push(wrapped);
|
||||
this.known.nodes[wrapped.name] = structuredClone(this.known.nodes[usableNode.name]);
|
||||
|
||||
const credentialNames = Object.entries(this.known.credentials)
|
||||
.filter(([_, credential]) => credential?.supportedNodes?.includes(usableNode.name))
|
||||
.map(([credentialName]) => credentialName);
|
||||
|
||||
credentialNames.forEach((name) =>
|
||||
this.known.credentials[name]?.supportedNodes?.push(wrapped.name),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async postProcessLoaders() {
|
||||
this.known = { nodes: {}, credentials: {} };
|
||||
this.loaded = { nodes: {}, credentials: {} };
|
||||
@@ -307,6 +339,8 @@ export class LoadNodesAndCredentials {
|
||||
}
|
||||
}
|
||||
|
||||
this.createAiTools();
|
||||
|
||||
this.injectCustomApiCallOptions();
|
||||
|
||||
for (const postProcessor of this.postProcessors) {
|
||||
|
||||
Reference in New Issue
Block a user