diff --git a/packages/components/credentials/VoyageAIApi.credential.ts b/packages/components/credentials/VoyageAIApi.credential.ts
new file mode 100644
index 00000000000..5965ce5998a
--- /dev/null
+++ b/packages/components/credentials/VoyageAIApi.credential.ts
@@ -0,0 +1,32 @@
+import { INodeParams, INodeCredential } from '../src/Interface'
+
+class VoyageAIApi implements INodeCredential {
+ label: string
+ name: string
+ version: number
+ description: string
+ inputs: INodeParams[]
+
+ constructor() {
+ this.label = 'Voyage AI API'
+ this.name = 'voyageAIApi'
+ this.version = 1.0
+ this.description =
+ 'Refer to official guide on how to get an API Key'
+ this.inputs = [
+ {
+ label: 'Voyage AI Endpoint',
+ name: 'endpoint',
+ type: 'string',
+ default: 'https://api.voyageai.com/v1/embeddings'
+ },
+ {
+ label: 'Voyage AI API Key',
+ name: 'apiKey',
+ type: 'password'
+ }
+ ]
+ }
+}
+
+module.exports = { credClass: VoyageAIApi }
diff --git a/packages/components/nodes/embeddings/VoyageAIEmbedding/VoyageAIEmbedding.ts b/packages/components/nodes/embeddings/VoyageAIEmbedding/VoyageAIEmbedding.ts
new file mode 100644
index 00000000000..82cfcecdc5e
--- /dev/null
+++ b/packages/components/nodes/embeddings/VoyageAIEmbedding/VoyageAIEmbedding.ts
@@ -0,0 +1,84 @@
+import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface'
+import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
+import { VoyageEmbeddings, VoyageEmbeddingsParams } from 'langchain/embeddings/voyage'
+
+class VoyageAIEmbedding_Embeddings implements INode {
+ label: string
+ name: string
+ version: number
+ type: string
+ icon: string
+ category: string
+ description: string
+ baseClasses: string[]
+ credential: INodeParams
+ inputs: INodeParams[]
+
+ constructor() {
+ this.label = 'VoyageAI Embeddings'
+ this.name = 'voyageAIEmbeddings'
+ this.version = 1.0
+ this.type = 'VoyageAIEmbeddings'
+ this.icon = 'voyageai.png'
+ this.category = 'Embeddings'
+ this.description = 'Voyage AI API to generate embeddings for a given text'
+ this.baseClasses = [this.type, ...getBaseClasses(VoyageEmbeddings)]
+ this.credential = {
+ label: 'Connect Credential',
+ name: 'credential',
+ type: 'credential',
+ credentialNames: ['voyageAIApi']
+ }
+ this.inputs = [
+ {
+ label: 'Model Name',
+ name: 'modelName',
+ type: 'options',
+ options: [
+ {
+ label: 'voyage-2',
+ name: 'voyage-2',
+ description: 'Base generalist embedding model optimized for both latency and quality'
+ },
+ {
+ label: 'voyage-code-2',
+ name: 'voyage-code-2',
+ description: 'Optimized for code retrieval'
+ },
+ {
+ label: 'voyage-large-2',
+ name: 'voyage-large-2',
+ description: 'Powerful generalist embedding model'
+ },
+ {
+ label: 'voyage-lite-02-instruct',
+ name: 'voyage-lite-02-instruct',
+ description: 'Instruction-tuned for classification, clustering, and sentence textual similarity tasks'
+ }
+ ],
+ default: 'voyage-2',
+ optional: true
+ }
+ ]
+ }
+
+ async init(nodeData: INodeData, _: string, options: ICommonObject): Promise {
+ const modelName = nodeData.inputs?.modelName as string
+
+ const credentialData = await getCredentialData(nodeData.credential ?? '', options)
+ const voyageAiApiKey = getCredentialParam('apiKey', credentialData, nodeData)
+ const voyageAiEndpoint = getCredentialParam('endpoint', credentialData, nodeData)
+
+ const obj: Partial & { apiKey?: string } = {
+ apiKey: voyageAiApiKey
+ }
+
+ if (modelName) obj.modelName = modelName
+
+ const model = new VoyageEmbeddings(obj)
+ if (voyageAiEndpoint) model.apiUrl = voyageAiEndpoint
+ return model
+ }
+}
+
+module.exports = { nodeClass: VoyageAIEmbedding_Embeddings }
diff --git a/packages/components/nodes/embeddings/VoyageAIEmbedding/voyageai.png b/packages/components/nodes/embeddings/VoyageAIEmbedding/voyageai.png
new file mode 100644
index 00000000000..83ee56018f0
Binary files /dev/null and b/packages/components/nodes/embeddings/VoyageAIEmbedding/voyageai.png differ
diff --git a/packages/components/nodes/retrievers/VoyageAIRetriever/VoyageAIRerank.ts b/packages/components/nodes/retrievers/VoyageAIRetriever/VoyageAIRerank.ts
new file mode 100644
index 00000000000..fd43c0b656b
--- /dev/null
+++ b/packages/components/nodes/retrievers/VoyageAIRetriever/VoyageAIRerank.ts
@@ -0,0 +1,52 @@
+import axios from 'axios'
+import { Callbacks } from '@langchain/core/callbacks/manager'
+import { Document } from '@langchain/core/documents'
+import { BaseDocumentCompressor } from 'langchain/retrievers/document_compressors'
+
+export class VoyageAIRerank extends BaseDocumentCompressor {
+ private voyageAIAPIKey: any
+ private readonly VOYAGEAI_RERANK_API_URL = 'https://api.voyageai.com/v1/rerank'
+ private model: string = 'rerank-lite-1'
+ private readonly k: number
+
+ constructor(voyageAIAPIKey: string, model: string, k: number) {
+ super()
+ this.voyageAIAPIKey = voyageAIAPIKey
+ this.model = model
+ this.k = k
+ }
+ async compressDocuments(
+ documents: Document>[],
+ query: string,
+ _?: Callbacks | undefined
+ ): Promise>[]> {
+ // avoid empty api call
+ if (documents.length === 0) {
+ return []
+ }
+ const config = {
+ headers: {
+ Authorization: `Bearer ${this.voyageAIAPIKey}`,
+ 'Content-Type': 'application/json',
+ Accept: 'application/json'
+ }
+ }
+ const data = {
+ model: this.model,
+ query: query,
+ documents: documents.map((doc) => doc.pageContent)
+ }
+ try {
+ let returnedDocs = await axios.post(this.VOYAGEAI_RERANK_API_URL, data, config)
+ const finalResults: Document>[] = []
+ returnedDocs.data.results.forEach((result: any) => {
+ const doc = documents[result.index]
+ doc.metadata.relevance_score = result.relevance_score
+ finalResults.push(doc)
+ })
+ return finalResults.splice(0, this.k)
+ } catch (error) {
+ return documents
+ }
+ }
+}
diff --git a/packages/components/nodes/retrievers/VoyageAIRetriever/VoyageAIRerankRetriever.ts b/packages/components/nodes/retrievers/VoyageAIRetriever/VoyageAIRerankRetriever.ts
new file mode 100644
index 00000000000..b5687b4a809
--- /dev/null
+++ b/packages/components/nodes/retrievers/VoyageAIRetriever/VoyageAIRerankRetriever.ts
@@ -0,0 +1,129 @@
+import { BaseRetriever } from '@langchain/core/retrievers'
+import { VectorStoreRetriever } from '@langchain/core/vectorstores'
+import { ContextualCompressionRetriever } from 'langchain/retrievers/contextual_compression'
+import { VoyageAIRerank } from './VoyageAIRerank'
+import { getCredentialData, getCredentialParam, handleEscapeCharacters } from '../../../src'
+import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
+
+class VoyageAIRerankRetriever_Retrievers implements INode {
+ label: string
+ name: string
+ version: number
+ description: string
+ type: string
+ icon: string
+ category: string
+ baseClasses: string[]
+ inputs: INodeParams[]
+ credential: INodeParams
+ badge: string
+ outputs: INodeOutputsValue[]
+
+ constructor() {
+ this.label = 'Voyage AI Rerank Retriever'
+ this.name = 'voyageAIRerankRetriever'
+ this.version = 1.0
+ this.type = 'VoyageAIRerankRetriever'
+ this.icon = 'voyageai.png'
+ this.category = 'Retrievers'
+ this.badge = 'NEW'
+ this.description = 'Voyage AI Rerank indexes the documents from most to least semantically relevant to the query.'
+ this.baseClasses = [this.type, 'BaseRetriever']
+ this.credential = {
+ label: 'Connect Credential',
+ name: 'credential',
+ type: 'credential',
+ credentialNames: ['voyageAIApi']
+ }
+ this.inputs = [
+ {
+ label: 'Vector Store Retriever',
+ name: 'baseRetriever',
+ type: 'VectorStoreRetriever'
+ },
+ {
+ label: 'Model Name',
+ name: 'model',
+ type: 'options',
+ options: [
+ {
+ label: 'rerank-lite-1',
+ name: 'rerank-lite-1'
+ }
+ ],
+ default: 'rerank-lite-1',
+ optional: true
+ },
+ {
+ label: 'Query',
+ name: 'query',
+ type: 'string',
+ description: 'Query to retrieve documents from retriever. If not specified, user question will be used',
+ optional: true,
+ acceptVariable: true
+ },
+ {
+ label: 'Top K',
+ name: 'topK',
+ description: 'Number of top results to fetch. Default to the TopK of the Base Retriever',
+ placeholder: '4',
+ type: 'number',
+ additionalParams: true,
+ optional: true
+ }
+ ]
+ this.outputs = [
+ {
+ label: 'Voyage AI Rerank Retriever',
+ name: 'retriever',
+ baseClasses: this.baseClasses
+ },
+ {
+ label: 'Document',
+ name: 'document',
+ description: 'Array of document objects containing metadata and pageContent',
+ baseClasses: ['Document', 'json']
+ },
+ {
+ label: 'Text',
+ name: 'text',
+ description: 'Concatenated string from pageContent of documents',
+ baseClasses: ['string', 'json']
+ }
+ ]
+ }
+
+ async init(nodeData: INodeData, input: string, options: ICommonObject): Promise {
+ const baseRetriever = nodeData.inputs?.baseRetriever as BaseRetriever
+ const model = nodeData.inputs?.model as string
+ const query = nodeData.inputs?.query as string
+ const credentialData = await getCredentialData(nodeData.credential ?? '', options)
+ const voyageAiApiKey = getCredentialParam('apiKey', credentialData, nodeData)
+ const topK = nodeData.inputs?.topK as string
+ const k = topK ? parseFloat(topK) : (baseRetriever as VectorStoreRetriever).k ?? 4
+ const output = nodeData.outputs?.output as string
+
+ const voyageAICompressor = new VoyageAIRerank(voyageAiApiKey, model, k)
+
+ const retriever = new ContextualCompressionRetriever({
+ baseCompressor: voyageAICompressor,
+ baseRetriever: baseRetriever
+ })
+
+ if (output === 'retriever') return retriever
+ else if (output === 'document') return await retriever.getRelevantDocuments(query ? query : input)
+ else if (output === 'text') {
+ let finaltext = ''
+
+ const docs = await retriever.getRelevantDocuments(query ? query : input)
+
+ for (const doc of docs) finaltext += `${doc.pageContent}\n`
+
+ return handleEscapeCharacters(finaltext, false)
+ }
+
+ return retriever
+ }
+}
+
+module.exports = { nodeClass: VoyageAIRerankRetriever_Retrievers }
diff --git a/packages/components/nodes/retrievers/VoyageAIRetriever/voyageai.png b/packages/components/nodes/retrievers/VoyageAIRetriever/voyageai.png
new file mode 100644
index 00000000000..83ee56018f0
Binary files /dev/null and b/packages/components/nodes/retrievers/VoyageAIRetriever/voyageai.png differ