From 56b043264a5370ac041f8fde1adf928b5068eace Mon Sep 17 00:00:00 2001 From: vinodkiran Date: Fri, 22 Dec 2023 12:08:48 +0530 Subject: [PATCH] MMR Search for Pinecone, Weaviate, Zep and Supabase --- .../nodes/vectorstores/Pinecone/Pinecone.ts | 73 ++---------------- .../nodes/vectorstores/Supabase/Supabase.ts | 18 ++--- .../nodes/vectorstores/VectorStoreUtils.ts | 76 +++++++++++++++++++ .../nodes/vectorstores/Weaviate/Weaviate.ts | 18 ++--- .../components/nodes/vectorstores/Zep/Zep.ts | 22 ++---- 5 files changed, 98 insertions(+), 109 deletions(-) create mode 100644 packages/components/nodes/vectorstores/VectorStoreUtils.ts diff --git a/packages/components/nodes/vectorstores/Pinecone/Pinecone.ts b/packages/components/nodes/vectorstores/Pinecone/Pinecone.ts index 3f2e3ef1..4b91a9b5 100644 --- a/packages/components/nodes/vectorstores/Pinecone/Pinecone.ts +++ b/packages/components/nodes/vectorstores/Pinecone/Pinecone.ts @@ -5,6 +5,7 @@ import { Embeddings } from 'langchain/embeddings/base' import { Document } from 'langchain/document' import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface' import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' +import { addMMRInputParams, resolveVectorStoreOrRetriever } from '../VectorStoreUtils' class Pinecone_VectorStores implements INode { label: string @@ -23,11 +24,11 @@ class Pinecone_VectorStores implements INode { constructor() { this.label = 'Pinecone' this.name = 'pinecone' - this.version = 2.0 + this.version = 3.0 this.type = 'Pinecone' this.icon = 'pinecone.svg' this.category = 'Vector Stores' - this.description = `Upsert embedded data and perform search upon query using Pinecone, a leading fully managed hosted vector database` + this.description = `Upsert embedded data and perform similarity or mmr search using Pinecone, a leading fully managed hosted vector database` this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever'] this.badge = 'NEW' this.credential = { @@ -77,45 +78,9 @@ class Pinecone_VectorStores implements INode { type: 'number', additionalParams: true, optional: true - }, - { - label: 'Search Type', - name: 'searchType', - type: 'options', - default: 'similarity', - options: [ - { - label: 'Similarity', - name: 'similarity' - }, - { - label: 'Max Marginal Relevance', - name: 'mmr' - } - ], - additionalParams: true, - optional: true - }, - { - label: 'Fetch K (for MMR Search)', - name: 'fetchK', - description: 'Number of initial documents to fetch for MMR reranking. Default to 20. Used only when the search type is MMR', - placeholder: '20', - type: 'number', - additionalParams: true, - optional: true - }, - { - label: 'Lambda (for MMR Search)', - name: 'lambda', - description: - 'Number between 0 and 1 that determines the degree of diversity among the results, where 0 corresponds to maximum diversity and 1 to minimum diversity. Used only when the search type is MMR', - placeholder: '0.5', - type: 'number', - additionalParams: true, - optional: true } ] + addMMRInputParams(this.inputs) this.outputs = [ { label: 'Pinecone Retriever', @@ -177,10 +142,6 @@ class Pinecone_VectorStores implements INode { const pineconeMetadataFilter = nodeData.inputs?.pineconeMetadataFilter const docs = nodeData.inputs?.document as Document[] const embeddings = nodeData.inputs?.embeddings as Embeddings - const output = nodeData.outputs?.output as string - const searchType = nodeData.outputs?.searchType as string - const topK = nodeData.inputs?.topK as string - const k = topK ? parseFloat(topK) : 4 const credentialData = await getCredentialData(nodeData.credential ?? '', options) const pineconeApiKey = getCredentialParam('pineconeApiKey', credentialData, nodeData) @@ -213,31 +174,7 @@ class Pinecone_VectorStores implements INode { const vectorStore = await PineconeStore.fromExistingIndex(embeddings, obj) - if (output === 'retriever') { - if ('mmr' === searchType) { - const fetchK = nodeData.inputs?.fetchK as string - const lambda = nodeData.inputs?.lambda as string - const f = fetchK ? parseInt(fetchK) : 20 - const l = lambda ? parseFloat(lambda) : 0.5 - const retriever = vectorStore.asRetriever({ - searchType: 'mmr', - k: 5, - searchKwargs: { - fetchK: f, - lambda: l - } - }) - return retriever - } else { - // "searchType" is "similarity" - const retriever = vectorStore.asRetriever(k) - return retriever - } - } else if (output === 'vectorStore') { - ;(vectorStore as any).k = k - return vectorStore - } - return vectorStore + return resolveVectorStoreOrRetriever(nodeData, vectorStore) } } diff --git a/packages/components/nodes/vectorstores/Supabase/Supabase.ts b/packages/components/nodes/vectorstores/Supabase/Supabase.ts index 13840ab7..a5477914 100644 --- a/packages/components/nodes/vectorstores/Supabase/Supabase.ts +++ b/packages/components/nodes/vectorstores/Supabase/Supabase.ts @@ -5,6 +5,7 @@ import { Embeddings } from 'langchain/embeddings/base' import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface' import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' import { SupabaseLibArgs, SupabaseVectorStore } from 'langchain/vectorstores/supabase' +import { addMMRInputParams, resolveVectorStoreOrRetriever } from '../VectorStoreUtils' class Supabase_VectorStores implements INode { label: string @@ -23,11 +24,11 @@ class Supabase_VectorStores implements INode { constructor() { this.label = 'Supabase' this.name = 'supabase' - this.version = 1.0 + this.version = 2.0 this.type = 'Supabase' this.icon = 'supabase.svg' this.category = 'Vector Stores' - this.description = 'Upsert embedded data and perform similarity search upon query using Supabase via pgvector extension' + this.description = 'Upsert embedded data and perform similarity or mmr search upon query using Supabase via pgvector extension' this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever'] this.badge = 'NEW' this.credential = { @@ -81,6 +82,7 @@ class Supabase_VectorStores implements INode { optional: true } ] + addMMRInputParams(this.inputs) this.outputs = [ { label: 'Supabase Retriever', @@ -135,9 +137,6 @@ class Supabase_VectorStores implements INode { const queryName = nodeData.inputs?.queryName as string const embeddings = nodeData.inputs?.embeddings as Embeddings const supabaseMetadataFilter = nodeData.inputs?.supabaseMetadataFilter - const output = nodeData.outputs?.output as string - const topK = nodeData.inputs?.topK as string - const k = topK ? parseFloat(topK) : 4 const credentialData = await getCredentialData(nodeData.credential ?? '', options) const supabaseApiKey = getCredentialParam('supabaseApiKey', credentialData, nodeData) @@ -157,14 +156,7 @@ class Supabase_VectorStores implements INode { const vectorStore = await SupabaseVectorStore.fromExistingIndex(embeddings, obj) - if (output === 'retriever') { - const retriever = vectorStore.asRetriever(k) - return retriever - } else if (output === 'vectorStore') { - ;(vectorStore as any).k = k - return vectorStore - } - return vectorStore + return resolveVectorStoreOrRetriever(nodeData, vectorStore) } } diff --git a/packages/components/nodes/vectorstores/VectorStoreUtils.ts b/packages/components/nodes/vectorstores/VectorStoreUtils.ts new file mode 100644 index 00000000..b63a4121 --- /dev/null +++ b/packages/components/nodes/vectorstores/VectorStoreUtils.ts @@ -0,0 +1,76 @@ +import { INodeData } from '../../src' + +export const resolveVectorStoreOrRetriever = (nodeData: INodeData, vectorStore: any) => { + const output = nodeData.outputs?.output as string + const searchType = nodeData.outputs?.searchType as string + const topK = nodeData.inputs?.topK as string + const k = topK ? parseFloat(topK) : 4 + + if (output === 'retriever') { + if ('mmr' === searchType) { + const fetchK = nodeData.inputs?.fetchK as string + const lambda = nodeData.inputs?.lambda as string + const f = fetchK ? parseInt(fetchK) : 20 + const l = lambda ? parseFloat(lambda) : 0.5 + const retriever = vectorStore.asRetriever({ + searchType: 'mmr', + k: k, + searchKwargs: { + fetchK: f, + lambda: l + } + }) + return retriever + } else { + // "searchType" is "similarity" + return vectorStore.asRetriever(k) + } + } else if (output === 'vectorStore') { + ;(vectorStore as any).k = k + return vectorStore + } +} + +export const addMMRInputParams = (inputs: any[]) => { + const mmrInputParams = [ + { + label: 'Search Type', + name: 'searchType', + type: 'options', + default: 'similarity', + options: [ + { + label: 'Similarity', + name: 'similarity' + }, + { + label: 'Max Marginal Relevance', + name: 'mmr' + } + ], + additionalParams: true, + optional: true + }, + { + label: 'Fetch K (for MMR Search)', + name: 'fetchK', + description: 'Number of initial documents to fetch for MMR reranking. Default to 20. Used only when the search type is MMR', + placeholder: '20', + type: 'number', + additionalParams: true, + optional: true + }, + { + label: 'Lambda (for MMR Search)', + name: 'lambda', + description: + 'Number between 0 and 1 that determines the degree of diversity among the results, where 0 corresponds to maximum diversity and 1 to minimum diversity. Used only when the search type is MMR', + placeholder: '0.5', + type: 'number', + additionalParams: true, + optional: true + } + ] + + inputs.push(...mmrInputParams) +} diff --git a/packages/components/nodes/vectorstores/Weaviate/Weaviate.ts b/packages/components/nodes/vectorstores/Weaviate/Weaviate.ts index 5c31c737..0eface58 100644 --- a/packages/components/nodes/vectorstores/Weaviate/Weaviate.ts +++ b/packages/components/nodes/vectorstores/Weaviate/Weaviate.ts @@ -5,6 +5,7 @@ import { Document } from 'langchain/document' import { Embeddings } from 'langchain/embeddings/base' import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface' import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' +import { addMMRInputParams, resolveVectorStoreOrRetriever } from '../VectorStoreUtils' class Weaviate_VectorStores implements INode { label: string @@ -23,12 +24,12 @@ class Weaviate_VectorStores implements INode { constructor() { this.label = 'Weaviate' this.name = 'weaviate' - this.version = 1.0 + this.version = 2.0 this.type = 'Weaviate' this.icon = 'weaviate.png' this.category = 'Vector Stores' this.description = - 'Upsert embedded data and perform similarity search upon query using Weaviate, a scalable open-source vector database' + 'Upsert embedded data and perform similarity or mmr search using Weaviate, a scalable open-source vector database' this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever'] this.badge = 'NEW' this.credential = { @@ -107,6 +108,7 @@ class Weaviate_VectorStores implements INode { optional: true } ] + addMMRInputParams(this.inputs) this.outputs = [ { label: 'Weaviate Retriever', @@ -174,9 +176,6 @@ class Weaviate_VectorStores implements INode { const weaviateTextKey = nodeData.inputs?.weaviateTextKey as string const weaviateMetadataKeys = nodeData.inputs?.weaviateMetadataKeys as string const embeddings = nodeData.inputs?.embeddings as Embeddings - const output = nodeData.outputs?.output as string - const topK = nodeData.inputs?.topK as string - const k = topK ? parseFloat(topK) : 4 const credentialData = await getCredentialData(nodeData.credential ?? '', options) const weaviateApiKey = getCredentialParam('weaviateApiKey', credentialData, nodeData) @@ -199,14 +198,7 @@ class Weaviate_VectorStores implements INode { const vectorStore = await WeaviateStore.fromExistingIndex(embeddings, obj) - if (output === 'retriever') { - const retriever = vectorStore.asRetriever(k) - return retriever - } else if (output === 'vectorStore') { - ;(vectorStore as any).k = k - return vectorStore - } - return vectorStore + return resolveVectorStoreOrRetriever(nodeData, vectorStore) } } diff --git a/packages/components/nodes/vectorstores/Zep/Zep.ts b/packages/components/nodes/vectorstores/Zep/Zep.ts index ebb13c64..3d9f1978 100644 --- a/packages/components/nodes/vectorstores/Zep/Zep.ts +++ b/packages/components/nodes/vectorstores/Zep/Zep.ts @@ -5,6 +5,7 @@ import { Embeddings } from 'langchain/embeddings/base' import { Document } from 'langchain/document' import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface' import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' +import { addMMRInputParams, resolveVectorStoreOrRetriever } from '../VectorStoreUtils' class Zep_VectorStores implements INode { label: string @@ -23,12 +24,12 @@ class Zep_VectorStores implements INode { constructor() { this.label = 'Zep' this.name = 'zep' - this.version = 1.0 + this.version = 2.0 this.type = 'Zep' this.icon = 'zep.svg' this.category = 'Vector Stores' this.description = - 'Upsert embedded data and perform similarity search upon query using Zep, a fast and scalable building block for LLM apps' + 'Upsert embedded data and perform similarity or mmr search upon query using Zep, a fast and scalable building block for LLM apps' this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever'] this.badge = 'NEW' this.credential = { @@ -88,6 +89,7 @@ class Zep_VectorStores implements INode { optional: true } ] + addMMRInputParams(this.inputs) this.outputs = [ { label: 'Zep Retriever', @@ -144,9 +146,6 @@ class Zep_VectorStores implements INode { const zepMetadataFilter = nodeData.inputs?.zepMetadataFilter const dimension = nodeData.inputs?.dimension as number const embeddings = nodeData.inputs?.embeddings as Embeddings - const output = nodeData.outputs?.output as string - const topK = nodeData.inputs?.topK as string - const k = topK ? parseFloat(topK) : 4 const credentialData = await getCredentialData(nodeData.credential ?? '', options) const apiKey = getCredentialParam('apiKey', credentialData, nodeData) @@ -165,14 +164,7 @@ class Zep_VectorStores implements INode { const vectorStore = await ZepExistingVS.fromExistingIndex(embeddings, zepConfig) - if (output === 'retriever') { - const retriever = vectorStore.asRetriever(k) - return retriever - } else if (output === 'vectorStore') { - ;(vectorStore as any).k = k - return vectorStore - } - return vectorStore + return resolveVectorStoreOrRetriever(nodeData, vectorStore) } } @@ -210,7 +202,7 @@ class ZepExistingVS extends ZepVectorStore { this.args = args } - async initalizeCollection(args: IZepConfig & Partial) { + async initializeCollection(args: IZepConfig & Partial) { this.client = await ZepClient.init(args.apiUrl, args.apiKey) try { this.collection = await this.client.document.getCollection(args.collectionName) @@ -259,7 +251,7 @@ class ZepExistingVS extends ZepVectorStore { const newfilter = { where: { and: ANDFilters } } - await this.initalizeCollection(this.args!).catch((err) => { + await this.initializeCollection(this.args!).catch((err) => { console.error('Error initializing collection:', err) throw err })