mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 20:24:05 -08:00
feat(Qdrant Vector Store Node): Qdrant vector store support (#8080)
## Summary This PR intends to add [Qdrant](https://qdrant.tech/) as a supported vectorstore node to load and retrieve documents from in a workflow. ## Review / Merge checklist - [x] PR title and summary are descriptive. - [x] Node/credentials documentation to be updated in https://github.com/n8n-io/n8n-docs/pull/1796. --------- Co-authored-by: oleg <me@olegivaniv.com> Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
This commit is contained in:
parent
ef3a57719e
commit
66460f66b0
|
@ -0,0 +1,50 @@
|
|||
import type {
|
||||
IAuthenticateGeneric,
|
||||
ICredentialTestRequest,
|
||||
ICredentialType,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export class QdrantApi implements ICredentialType {
|
||||
name = 'qdrantApi';
|
||||
|
||||
displayName = 'QdrantApi';
|
||||
|
||||
documentationUrl = 'https://docs.n8n.io/integrations/builtin/credentials/qdrant/';
|
||||
|
||||
properties: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'API Key',
|
||||
name: 'apiKey',
|
||||
type: 'string',
|
||||
typeOptions: { password: true },
|
||||
required: true,
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Qdrant URL',
|
||||
name: 'qdrantUrl',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
},
|
||||
];
|
||||
|
||||
authenticate: IAuthenticateGeneric = {
|
||||
type: 'generic',
|
||||
properties: {
|
||||
headers: {
|
||||
'api-key': '={{$credentials.apiKey}}',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
test: ICredentialTestRequest = {
|
||||
request: {
|
||||
baseURL: '={{$credentials.qdrantUrl}}',
|
||||
headers: {
|
||||
accept: 'application/json; charset=utf-8',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
import { type INodeProperties } from 'n8n-workflow';
|
||||
import type { QdrantLibArgs } from 'langchain/vectorstores/qdrant';
|
||||
import { QdrantVectorStore } from 'langchain/vectorstores/qdrant';
|
||||
import type { Schemas as QdrantSchemas } from '@qdrant/js-client-rest';
|
||||
import { createVectorStoreNode } from '../shared/createVectorStoreNode';
|
||||
import { qdrantCollectionRLC } from '../shared/descriptions';
|
||||
import { qdrantCollectionsSearch } from '../shared/methods/listSearch';
|
||||
|
||||
const sharedFields: INodeProperties[] = [qdrantCollectionRLC];
|
||||
|
||||
const insertFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Option',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Collection Config',
|
||||
name: 'collectionConfig',
|
||||
type: 'json',
|
||||
default: '',
|
||||
description:
|
||||
'JSON options for creating a collection. <a href="https://qdrant.tech/documentation/concepts/collections">Learn more</a>.',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const VectorStoreQdrant = createVectorStoreNode({
|
||||
meta: {
|
||||
displayName: 'Qdrant Vector Store',
|
||||
name: 'vectorStoreQdrant',
|
||||
description: 'Work with your data in a Qdrant collection',
|
||||
icon: 'file:qdrant.svg',
|
||||
docsUrl:
|
||||
'https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.vectorstoreqdrant/',
|
||||
credentials: [
|
||||
{
|
||||
name: 'qdrantApi',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
methods: { listSearch: { qdrantCollectionsSearch } },
|
||||
insertFields,
|
||||
sharedFields,
|
||||
async getVectorStoreClient(context, filter, embeddings, itemIndex) {
|
||||
const collection = context.getNodeParameter('qdrantCollection', itemIndex, '', {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
const credentials = await context.getCredentials('qdrantApi');
|
||||
|
||||
const config: QdrantLibArgs = {
|
||||
url: credentials.qdrantUrl as string,
|
||||
apiKey: credentials.apiKey as string,
|
||||
collectionName: collection,
|
||||
};
|
||||
|
||||
return QdrantVectorStore.fromExistingCollection(embeddings, config);
|
||||
},
|
||||
async populateVectorStore(context, embeddings, documents, itemIndex) {
|
||||
const collectionName = context.getNodeParameter('qdrantCollection', itemIndex, '', {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
// If collection config is not provided, the collection will be created with default settings
|
||||
// i.e. with the size of the passed embeddings and "Cosine" distance metric
|
||||
const { collectionConfig } = context.getNodeParameter('options', itemIndex, {}) as {
|
||||
collectionConfig?: QdrantSchemas['CreateCollection'];
|
||||
};
|
||||
const credentials = await context.getCredentials('qdrantApi');
|
||||
|
||||
const config: QdrantLibArgs = {
|
||||
url: credentials.qdrantUrl as string,
|
||||
apiKey: credentials.apiKey as string,
|
||||
collectionName,
|
||||
collectionConfig,
|
||||
};
|
||||
|
||||
await QdrantVectorStore.fromDocuments(documents, embeddings, config);
|
||||
},
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="256px" height="296px" viewBox="0 0 256 296" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||
<title>qdrant</title>
|
||||
<defs>
|
||||
<linearGradient x1="81.5619048%" y1="44.8421053%" x2="-18.0857143%" y2="44.8421053%" id="linearGradient-1">
|
||||
<stop stop-color="#FF3364" offset="0%"></stop>
|
||||
<stop stop-color="#C91540" stop-opacity="0" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g>
|
||||
<polygon fill="#24386C" points="201.31705 271.722427 195.422715 109.21267 184.747781 66.3682368 256 73.9112545 256 270.492509 212.474757 295.612626"></polygon>
|
||||
<polygon fill="#7589BE" points="255.995151 73.8998107 212.469908 99.037384 122.649634 79.3346471 17.5160008 122.140288 1.1370484e-14 73.8998107 63.9883137 36.9499053 127.996024 0 191.986277 36.9499053"></polygon>
|
||||
<polyline fill="#B2BFE8" points="0.00310340412 73.8998107 43.5283462 99.037384 68.7590217 174.073816 153.949405 242.236209 128.001067 295.599243 63.9933568 258.647398 0.00310340412 221.697492 0.00310340412 73.897871"></polyline>
|
||||
<polyline fill="#24386C" points="156.856906 202.807459 128.001067 245.347371 128.001067 295.603122 168.946605 271.978458 190.043934 240.475027"></polyline>
|
||||
<polygon fill="#7589BE" points="128.018523 195.107138 87.0555287 124.184656 95.8787005 100.678309 129.42068 84.4156955 168.946411 124.185819"></polygon>
|
||||
<polyline fill="#B2BFE8" points="87.0555287 124.178837 128.001067 147.803501 128.001067 195.091621 90.131778 196.720927 67.2247763 167.471344 87.0555287 124.178856"></polyline>
|
||||
<polygon fill="#24386C" points="128.001067 147.799621 168.946605 124.176897 196.813234 170.576668 163.090869 198.439418 128.001067 195.089293"></polygon>
|
||||
<path d="M168.946605,271.974579 L212.471848,295.601182 L212.471848,99.0393237 L170.226759,74.658205 L128.001067,50.2770864 L85.7559782,74.658205 L43.5302858,99.0393237 L43.5302858,196.581255 L85.7559782,220.962373 L128.001067,245.345432 L168.946605,221.699432 L168.946605,271.974579 Z M168.946605,171.443681 L128.001067,195.087742 L87.0555287,171.443681 L87.0555287,124.174957 L128.001067,100.530897 L168.946605,124.174957 L168.946605,171.443681" fill="#DC244C"></path>
|
||||
<polygon fill="url(#linearGradient-1)" points="128.018523 245.362888 128.018523 195.099379 87.2863443 171.657041 87.2863443 221.837146"></polygon>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
|
@ -45,3 +45,26 @@ export const supabaseTableNameRLC: INodeProperties = {
|
|||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const qdrantCollectionRLC: INodeProperties = {
|
||||
displayName: 'Qdrant Collection',
|
||||
name: 'qdrantCollection',
|
||||
type: 'resourceLocator',
|
||||
default: { mode: 'list', value: '' },
|
||||
required: true,
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'qdrantCollectionsSearch',
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'ID',
|
||||
name: 'id',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { ApplicationError, type IDataObject, type ILoadOptionsFunctions } from 'n8n-workflow';
|
||||
import { Pinecone } from '@pinecone-database/pinecone';
|
||||
import { QdrantClient } from '@qdrant/js-client-rest';
|
||||
|
||||
export async function pineconeIndexSearch(this: ILoadOptionsFunctions) {
|
||||
const credentials = await this.getCredentials('pineconeApi');
|
||||
|
@ -49,3 +50,21 @@ export async function supabaseTableNameSearch(this: ILoadOptionsFunctions) {
|
|||
|
||||
return { results };
|
||||
}
|
||||
|
||||
export async function qdrantCollectionsSearch(this: ILoadOptionsFunctions) {
|
||||
const credentials = await this.getCredentials('qdrantApi');
|
||||
|
||||
const client = new QdrantClient({
|
||||
url: credentials.qdrantUrl as string,
|
||||
apiKey: credentials.apiKey as string,
|
||||
});
|
||||
|
||||
const response = await client.getCollections();
|
||||
|
||||
const results = response.collections.map((collection) => ({
|
||||
name: collection.name,
|
||||
value: collection.name,
|
||||
}));
|
||||
|
||||
return { results };
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
"dist/credentials/MotorheadApi.credentials.js",
|
||||
"dist/credentials/OllamaApi.credentials.js",
|
||||
"dist/credentials/PineconeApi.credentials.js",
|
||||
"dist/credentials/QdrantApi.credentials.js",
|
||||
"dist/credentials/SerpApi.credentials.js",
|
||||
"dist/credentials/WolframAlphaApi.credentials.js",
|
||||
"dist/credentials/XataApi.credentials.js",
|
||||
|
@ -93,6 +94,7 @@
|
|||
"dist/nodes/vector_store/VectorStorePinecone/VectorStorePinecone.node.js",
|
||||
"dist/nodes/vector_store/VectorStorePineconeInsert/VectorStorePineconeInsert.node.js",
|
||||
"dist/nodes/vector_store/VectorStorePineconeLoad/VectorStorePineconeLoad.node.js",
|
||||
"dist/nodes/vector_store/VectorStoreQdrant/VectorStoreQdrant.node.js",
|
||||
"dist/nodes/vector_store/VectorStoreSupabase/VectorStoreSupabase.node.js",
|
||||
"dist/nodes/vector_store/VectorStoreSupabaseInsert/VectorStoreSupabaseInsert.node.js",
|
||||
"dist/nodes/vector_store/VectorStoreSupabaseLoad/VectorStoreSupabaseLoad.node.js",
|
||||
|
@ -118,6 +120,7 @@
|
|||
"@huggingface/inference": "2.6.4",
|
||||
"@n8n/vm2": "3.9.20",
|
||||
"@pinecone-database/pinecone": "1.1.2",
|
||||
"@qdrant/js-client-rest": "1.7.0",
|
||||
"@supabase/supabase-js": "2.38.5",
|
||||
"@xata.io/client": "0.25.3",
|
||||
"cohere-ai": "6.2.2",
|
||||
|
|
|
@ -192,6 +192,9 @@ importers:
|
|||
'@pinecone-database/pinecone':
|
||||
specifier: 1.1.2
|
||||
version: 1.1.2
|
||||
'@qdrant/js-client-rest':
|
||||
specifier: 1.7.0
|
||||
version: 1.7.0(typescript@5.3.2)
|
||||
'@supabase/supabase-js':
|
||||
specifier: 2.38.5
|
||||
version: 2.38.5
|
||||
|
@ -215,7 +218,7 @@ importers:
|
|||
version: 1.2.0
|
||||
langchain:
|
||||
specifier: 0.0.198
|
||||
version: 0.0.198(@aws-sdk/client-bedrock-runtime@3.454.0)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@0.2.1)(@huggingface/inference@2.6.4)(@pinecone-database/pinecone@1.1.2)(@supabase/supabase-js@2.38.5)(@xata.io/client@0.25.3)(cohere-ai@6.2.2)(d3-dsv@2.0.0)(epub2@3.0.1)(html-to-text@9.0.5)(lodash@4.17.21)(mammoth@1.6.0)(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.11)(typeorm@0.3.17)
|
||||
version: 0.0.198(@aws-sdk/client-bedrock-runtime@3.454.0)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@0.2.1)(@huggingface/inference@2.6.4)(@pinecone-database/pinecone@1.1.2)(@qdrant/js-client-rest@1.7.0)(@supabase/supabase-js@2.38.5)(@xata.io/client@0.25.3)(cohere-ai@6.2.2)(d3-dsv@2.0.0)(epub2@3.0.1)(html-to-text@9.0.5)(lodash@4.17.21)(mammoth@1.6.0)(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.11)(typeorm@0.3.17)
|
||||
lodash:
|
||||
specifier: 4.17.21
|
||||
version: 4.17.21
|
||||
|
@ -6544,6 +6547,23 @@ packages:
|
|||
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
|
||||
dev: false
|
||||
|
||||
/@qdrant/js-client-rest@1.7.0(typescript@5.3.2):
|
||||
resolution: {integrity: sha512-16O0EQfrrybcPVipodxykr6dMUlBzKW7a63cSDUFVgc5a1AWESwERykwjuvW5KqvKdkPcxZ2NssrvgUO1W3MgA==}
|
||||
engines: {node: '>=18.0.0', pnpm: '>=8'}
|
||||
peerDependencies:
|
||||
typescript: ^5.3.0
|
||||
dependencies:
|
||||
'@qdrant/openapi-typescript-fetch': 1.2.1
|
||||
'@sevinf/maybe': 0.5.0
|
||||
typescript: 5.3.2
|
||||
undici: 5.26.4
|
||||
dev: false
|
||||
|
||||
/@qdrant/openapi-typescript-fetch@1.2.1:
|
||||
resolution: {integrity: sha512-oiBJRN1ME7orFZocgE25jrM3knIF/OKJfMsZPBbtMMKfgNVYfps0MokGvSJkBmecj6bf8QoLXWIGlIoaTM4Zmw==}
|
||||
engines: {node: '>=12.0.0', pnpm: '>=8'}
|
||||
dev: false
|
||||
|
||||
/@radix-ui/number@1.0.1:
|
||||
resolution: {integrity: sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==}
|
||||
dependencies:
|
||||
|
@ -7391,6 +7411,10 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@sevinf/maybe@0.5.0:
|
||||
resolution: {integrity: sha512-ARhyoYDnY1LES3vYI0fiG6e9esWfTNcXcO6+MPJJXcnyMV3bim4lnFt45VXouV7y82F4x3YH8nOQ6VztuvUiWg==}
|
||||
dev: false
|
||||
|
||||
/@sideway/address@4.1.4:
|
||||
resolution: {integrity: sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==}
|
||||
dependencies:
|
||||
|
@ -18585,7 +18609,7 @@ packages:
|
|||
resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==}
|
||||
dev: false
|
||||
|
||||
/langchain@0.0.198(@aws-sdk/client-bedrock-runtime@3.454.0)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@0.2.1)(@huggingface/inference@2.6.4)(@pinecone-database/pinecone@1.1.2)(@supabase/supabase-js@2.38.5)(@xata.io/client@0.25.3)(cohere-ai@6.2.2)(d3-dsv@2.0.0)(epub2@3.0.1)(html-to-text@9.0.5)(lodash@4.17.21)(mammoth@1.6.0)(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.11)(typeorm@0.3.17):
|
||||
/langchain@0.0.198(@aws-sdk/client-bedrock-runtime@3.454.0)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@0.2.1)(@huggingface/inference@2.6.4)(@pinecone-database/pinecone@1.1.2)(@qdrant/js-client-rest@1.7.0)(@supabase/supabase-js@2.38.5)(@xata.io/client@0.25.3)(cohere-ai@6.2.2)(d3-dsv@2.0.0)(epub2@3.0.1)(html-to-text@9.0.5)(lodash@4.17.21)(mammoth@1.6.0)(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.11)(typeorm@0.3.17):
|
||||
resolution: {integrity: sha512-YC0O1g8r61InCWyF5NmiQjdghdq6LKcgMrDZtqLbgDxAe4RoSldonm+5oNXS3yjCISG0j3s5Cty+yB7klqvUpg==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
|
@ -18898,6 +18922,7 @@ packages:
|
|||
'@huggingface/inference': 2.6.4
|
||||
'@langchain/core': 0.0.2
|
||||
'@pinecone-database/pinecone': 1.1.2
|
||||
'@qdrant/js-client-rest': 1.7.0(typescript@5.3.2)
|
||||
'@supabase/supabase-js': 2.38.5
|
||||
'@xata.io/client': 0.25.3(typescript@5.3.2)
|
||||
binary-extensions: 2.2.0
|
||||
|
|
Loading…
Reference in a new issue