From 3109de6073b237ee3dcc93afb69345586f3b836d Mon Sep 17 00:00:00 2001 From: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com> Date: Mon, 6 Jan 2025 14:59:20 +0100 Subject: [PATCH] feat: Add load options to new tool mode for vector stores (#12462) --- .../createVectorStoreNode.test.ts.snap | 234 ++++++++++++++++++ .../shared/createVectorStoreNode.test.ts | 7 +- .../shared/createVectorStoreNode.ts | 12 +- 3 files changed, 249 insertions(+), 4 deletions(-) create mode 100644 packages/@n8n/nodes-langchain/nodes/vector_store/shared/__snapshots__/createVectorStoreNode.test.ts.snap diff --git a/packages/@n8n/nodes-langchain/nodes/vector_store/shared/__snapshots__/createVectorStoreNode.test.ts.snap b/packages/@n8n/nodes-langchain/nodes/vector_store/shared/__snapshots__/createVectorStoreNode.test.ts.snap new file mode 100644 index 0000000000..91da891842 --- /dev/null +++ b/packages/@n8n/nodes-langchain/nodes/vector_store/shared/__snapshots__/createVectorStoreNode.test.ts.snap @@ -0,0 +1,234 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`createVectorStoreNode retrieve mode supplies vector store as data 1`] = ` +{ + "codex": { + "categories": [ + "AI", + ], + "resources": { + "primaryDocumentation": [ + { + "url": undefined, + }, + ], + }, + "subcategories": { + "AI": [ + "Vector Stores", + "Tools", + "Root Nodes", + ], + "Tools": [ + "Other Tools", + ], + }, + }, + "credentials": undefined, + "defaults": { + "name": undefined, + }, + "description": undefined, + "displayName": undefined, + "group": [ + "transform", + ], + "icon": undefined, + "iconColor": undefined, + "inputs": "={{ + ((parameters) => { + const mode = parameters?.mode; + const inputs = [{ displayName: "Embedding", type: "ai_embedding", required: true, maxConnections: 1}] + + if (mode === 'retrieve-as-tool') { + return inputs; + } + + if (['insert', 'load', 'update'].includes(mode)) { + inputs.push({ displayName: "", type: "main"}) + } + + if (['insert'].includes(mode)) { + inputs.push({ displayName: "Document", type: "ai_document", required: true, maxConnections: 1}) + } + return inputs + })($parameter) + }}", + "name": "mockConstructor", + "outputs": "={{ + ((parameters) => { + const mode = parameters?.mode ?? 'retrieve'; + + if (mode === 'retrieve-as-tool') { + return [{ displayName: "Tool", type: "ai_tool"}] + } + + if (mode === 'retrieve') { + return [{ displayName: "Vector Store", type: "ai_vectorStore"}] + } + return [{ displayName: "", type: "main"}] + })($parameter) + }}", + "properties": [ + { + "default": "retrieve", + "displayName": "Operation Mode", + "name": "mode", + "noDataExpression": true, + "options": [ + { + "action": "Get ranked documents from vector store", + "description": "Get many ranked documents from vector store for query", + "name": "Get Many", + "value": "load", + }, + { + "action": "Add documents to vector store", + "description": "Insert documents into vector store", + "name": "Insert Documents", + "value": "insert", + }, + { + "action": "Retrieve documents for AI processing as Vector Store", + "description": "Retrieve documents from vector store to be used as vector store with AI nodes", + "name": "Retrieve Documents (As Vector Store for AI Agent)", + "outputConnectionType": "ai_vectorStore", + "value": "retrieve", + }, + { + "action": "Retrieve documents for AI processing as Tool", + "description": "Retrieve documents from vector store to be used as tool with AI nodes", + "name": "Retrieve Documents (As Tool for AI Agent)", + "outputConnectionType": "ai_tool", + "value": "retrieve-as-tool", + }, + ], + "type": "options", + }, + { + "default": "", + "displayName": "This node must be connected to a vector store retriever. Insert one", + "displayOptions": { + "show": { + "mode": [ + "retrieve", + ], + }, + }, + "name": "notice", + "type": "notice", + "typeOptions": { + "containerClass": "ndv-connection-hint-notice", + }, + }, + { + "default": "", + "description": "Name of the vector store", + "displayName": "Name", + "displayOptions": { + "show": { + "mode": [ + "retrieve-as-tool", + ], + }, + }, + "name": "toolName", + "placeholder": "e.g. company_knowledge_base", + "required": true, + "type": "string", + "validateType": "string-alphanumeric", + }, + { + "default": "", + "description": "Explain to the LLM what this tool does, a good, specific description would allow LLMs to produce expected results much more often", + "displayName": "Description", + "displayOptions": { + "show": { + "mode": [ + "retrieve-as-tool", + ], + }, + }, + "name": "toolDescription", + "placeholder": "e.g. undefined", + "required": true, + "type": "string", + "typeOptions": { + "rows": 2, + }, + }, + { + "default": "", + "description": "Search prompt to retrieve matching documents from the vector store using similarity-based ranking", + "displayName": "Prompt", + "displayOptions": { + "show": { + "mode": [ + "load", + ], + }, + }, + "name": "prompt", + "required": true, + "type": "string", + }, + { + "default": 4, + "description": "Number of top results to fetch from vector store", + "displayName": "Limit", + "displayOptions": { + "show": { + "mode": [ + "load", + "retrieve-as-tool", + ], + }, + }, + "name": "topK", + "type": "number", + }, + { + "default": true, + "description": "Whether or not to include document metadata", + "displayName": "Include Metadata", + "displayOptions": { + "show": { + "mode": [ + "load", + "retrieve-as-tool", + ], + }, + }, + "name": "includeDocumentMetadata", + "type": "boolean", + }, + { + "default": "", + "description": "ID of an embedding entry", + "displayName": "ID", + "displayOptions": { + "show": { + "mode": [ + "update", + ], + }, + }, + "name": "id", + "required": true, + "type": "string", + }, + { + "displayOptions": { + "show": { + "mode": [ + "load", + "retrieve-as-tool", + ], + }, + }, + "name": "loadField", + }, + ], + "version": 1, +} +`; diff --git a/packages/@n8n/nodes-langchain/nodes/vector_store/shared/createVectorStoreNode.test.ts b/packages/@n8n/nodes-langchain/nodes/vector_store/shared/createVectorStoreNode.test.ts index 26036dce80..28d15def1e 100644 --- a/packages/@n8n/nodes-langchain/nodes/vector_store/shared/createVectorStoreNode.test.ts +++ b/packages/@n8n/nodes-langchain/nodes/vector_store/shared/createVectorStoreNode.test.ts @@ -49,7 +49,11 @@ describe('createVectorStoreNode', () => { const vectorStoreNodeArgs = mock({ sharedFields: [], insertFields: [], - loadFields: [], + loadFields: [ + { + name: 'loadField', + }, + ], retrieveFields: [], updateFields: [], getVectorStoreClient: jest.fn().mockReturnValue(vectorStore), @@ -82,6 +86,7 @@ describe('createVectorStoreNode', () => { const wrappedVectorStore = (data.response as { logWrapped: VectorStore }).logWrapped; // ASSERT + expect(nodeType.description).toMatchSnapshot(); expect(wrappedVectorStore).toEqual(vectorStore); expect(vectorStoreNodeArgs.getVectorStoreClient).toHaveBeenCalled(); }); diff --git a/packages/@n8n/nodes-langchain/nodes/vector_store/shared/createVectorStoreNode.ts b/packages/@n8n/nodes-langchain/nodes/vector_store/shared/createVectorStoreNode.ts index b7c0de3922..f8e11cadf1 100644 --- a/packages/@n8n/nodes-langchain/nodes/vector_store/shared/createVectorStoreNode.ts +++ b/packages/@n8n/nodes-langchain/nodes/vector_store/shared/createVectorStoreNode.ts @@ -80,10 +80,13 @@ export interface VectorStoreNodeConstructorArgs { ) => Promise; } -function transformDescriptionForOperationMode(fields: INodeProperties[], mode: NodeOperationMode) { +function transformDescriptionForOperationMode( + fields: INodeProperties[], + mode: NodeOperationMode | NodeOperationMode[], +) { return fields.map((field) => ({ ...field, - displayOptions: { show: { mode: [mode] } }, + displayOptions: { show: { mode: Array.isArray(mode) ? mode : [mode] } }, })); } @@ -299,7 +302,10 @@ export const createVectorStoreNode = (args: VectorStoreNodeConstructorArgs) => }, }, }, - ...transformDescriptionForOperationMode(args.loadFields ?? [], 'load'), + ...transformDescriptionForOperationMode(args.loadFields ?? [], [ + 'load', + 'retrieve-as-tool', + ]), ...transformDescriptionForOperationMode(args.retrieveFields ?? [], 'retrieve'), ...transformDescriptionForOperationMode(args.updateFields ?? [], 'update'), ],