Skip to content

Commit

Permalink
feat(Qdrant Vector Store Node): Qdrant vector store support (#8080)
Browse files Browse the repository at this point in the history
## 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
n8n-io/n8n-docs#1796.

---------

Co-authored-by: oleg <me@olegivaniv.com>
Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
  • Loading branch information
3 people authored Jan 3, 2024
1 parent ef3a577 commit 66460f6
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 2 deletions.
50 changes: 50 additions & 0 deletions packages/@n8n/nodes-langchain/credentials/QdrantApi.credentials.ts
Original file line number Diff line number Diff line change
@@ -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',
},
},
};
}
Original file line number Diff line number Diff line change
@@ -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);
},
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -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',
},
],
};
Original file line number Diff line number Diff line change
@@ -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');
Expand Down Expand Up @@ -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 };
}
3 changes: 3 additions & 0 deletions packages/@n8n/nodes-langchain/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand Down
29 changes: 27 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 66460f6

Please sign in to comment.