diff --git a/packages/components/README-ZH.md b/packages/components/README-ZH.md
index 0605812f..12cb240b 100644
--- a/packages/components/README-ZH.md
+++ b/packages/components/README-ZH.md
@@ -16,4 +16,4 @@ npm i flowise-components
## 许可证
-此存储库中的源代码在[MIT许可证](https://github.com/FlowiseAI/Flowise/blob/master/LICENSE.md)下提供。
\ No newline at end of file
+此存储库中的源代码在[MIT许可证](https://github.com/FlowiseAI/Flowise/blob/master/LICENSE.md)下提供。
diff --git a/packages/components/credentials/MilvusAuth.credential.ts b/packages/components/credentials/MilvusAuth.credential.ts
new file mode 100644
index 00000000..b94e1fc8
--- /dev/null
+++ b/packages/components/credentials/MilvusAuth.credential.ts
@@ -0,0 +1,31 @@
+import { INodeParams, INodeCredential } from '../src/Interface'
+
+class MilvusCredential implements INodeCredential {
+ label: string
+ name: string
+ version: number
+ description: string
+ inputs: INodeParams[]
+
+ constructor() {
+ this.label = 'Milvus Auth'
+ this.name = 'milvusAuth'
+ this.version = 1.0
+ this.description =
+ 'You can find the Milvus Authentication from here page.'
+ this.inputs = [
+ {
+ label: 'Milvus User',
+ name: 'milvusUser',
+ type: 'string'
+ },
+ {
+ label: 'Milvus Password',
+ name: 'milvusPassword',
+ type: 'password'
+ }
+ ]
+ }
+}
+
+module.exports = { credClass: MilvusCredential }
diff --git a/packages/components/nodes/chains/ApiChain/apichain.svg b/packages/components/nodes/chains/ApiChain/apichain.svg
index ef62e168..3b86b905 100644
--- a/packages/components/nodes/chains/ApiChain/apichain.svg
+++ b/packages/components/nodes/chains/ApiChain/apichain.svg
@@ -1,3 +1,3 @@
-
\ No newline at end of file
diff --git a/packages/components/nodes/chains/ConversationChain/ConversationChain.ts b/packages/components/nodes/chains/ConversationChain/ConversationChain.ts
index cd42a670..08663395 100644
--- a/packages/components/nodes/chains/ConversationChain/ConversationChain.ts
+++ b/packages/components/nodes/chains/ConversationChain/ConversationChain.ts
@@ -45,7 +45,8 @@ class ConversationChain_Chains implements INode {
label: 'Document',
name: 'document',
type: 'Document',
- description: 'Include whole document into the context window',
+ description:
+ 'Include whole document into the context window, if you get maximum context length error, please use model with higher context window like Claude 100k, or gpt4 32k',
optional: true,
list: true
},
diff --git a/packages/components/nodes/chatmodels/ChatOpenAI/ChatOpenAI.ts b/packages/components/nodes/chatmodels/ChatOpenAI/ChatOpenAI.ts
index 9512da66..ca081ff4 100644
--- a/packages/components/nodes/chatmodels/ChatOpenAI/ChatOpenAI.ts
+++ b/packages/components/nodes/chatmodels/ChatOpenAI/ChatOpenAI.ts
@@ -125,6 +125,13 @@ class ChatOpenAI_ChatModels implements INode {
type: 'string',
optional: true,
additionalParams: true
+ },
+ {
+ label: 'BaseOptions',
+ name: 'baseOptions',
+ type: 'json',
+ optional: true,
+ additionalParams: true
}
]
}
@@ -139,6 +146,7 @@ class ChatOpenAI_ChatModels implements INode {
const timeout = nodeData.inputs?.timeout as string
const streaming = nodeData.inputs?.streaming as boolean
const basePath = nodeData.inputs?.basepath as string
+ const baseOptions = nodeData.inputs?.baseOptions
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const openAIApiKey = getCredentialParam('openAIApiKey', credentialData, nodeData)
@@ -156,8 +164,18 @@ class ChatOpenAI_ChatModels implements INode {
if (presencePenalty) obj.presencePenalty = parseFloat(presencePenalty)
if (timeout) obj.timeout = parseInt(timeout, 10)
+ let parsedBaseOptions: any | undefined = undefined
+
+ if (baseOptions) {
+ try {
+ parsedBaseOptions = typeof baseOptions === 'object' ? baseOptions : JSON.parse(baseOptions)
+ } catch (exception) {
+ throw new Error("Invalid JSON in the ChatOpenAI's BaseOptions: " + exception)
+ }
+ }
const model = new ChatOpenAI(obj, {
- basePath
+ basePath,
+ baseOptions: parsedBaseOptions
})
return model
}
diff --git a/packages/components/nodes/documentloaders/Cheerio/Cheerio.ts b/packages/components/nodes/documentloaders/Cheerio/Cheerio.ts
index 310aa9e6..1c21c1ea 100644
--- a/packages/components/nodes/documentloaders/Cheerio/Cheerio.ts
+++ b/packages/components/nodes/documentloaders/Cheerio/Cheerio.ts
@@ -64,7 +64,7 @@ class Cheerio_DocumentLoaders implements INode {
additionalParams: true,
description:
'Only used when "Get Relative Links Method" is selected. Set 0 to retrieve all relative links, default limit is 10.',
- warning: `Retreiving all links might take long time, and all links will be upserted again if the flow's state changed (eg: different URL, chunk size, etc)`
+ warning: `Retrieving all links might take long time, and all links will be upserted again if the flow's state changed (eg: different URL, chunk size, etc)`
},
{
label: 'Metadata',
diff --git a/packages/components/nodes/documentloaders/Playwright/Playwright.ts b/packages/components/nodes/documentloaders/Playwright/Playwright.ts
index 3399574d..eb246045 100644
--- a/packages/components/nodes/documentloaders/Playwright/Playwright.ts
+++ b/packages/components/nodes/documentloaders/Playwright/Playwright.ts
@@ -1,6 +1,6 @@
import { INode, INodeData, INodeParams } from '../../../src/Interface'
import { TextSplitter } from 'langchain/text_splitter'
-import { PlaywrightWebBaseLoader } from 'langchain/document_loaders/web/playwright'
+import { Browser, Page, PlaywrightWebBaseLoader, PlaywrightWebBaseLoaderOptions } from 'langchain/document_loaders/web/playwright'
import { test } from 'linkifyjs'
import { webCrawl, xmlScrape } from '../../../src'
@@ -64,7 +64,45 @@ class Playwright_DocumentLoaders implements INode {
additionalParams: true,
description:
'Only used when "Get Relative Links Method" is selected. Set 0 to retrieve all relative links, default limit is 10.',
- warning: `Retreiving all links might take long time, and all links will be upserted again if the flow's state changed (eg: different URL, chunk size, etc)`
+ warning: `Retrieving all links might take long time, and all links will be upserted again if the flow's state changed (eg: different URL, chunk size, etc)`
+ },
+ {
+ label: 'Wait Until',
+ name: 'waitUntilGoToOption',
+ type: 'options',
+ description: 'Select a go to wait until option',
+ options: [
+ {
+ label: 'Load',
+ name: 'load',
+ description: 'Consider operation to be finished when the load event is fired.'
+ },
+ {
+ label: 'DOM Content Loaded',
+ name: 'domcontentloaded',
+ description: 'Consider operation to be finished when the DOMContentLoaded event is fired.'
+ },
+ {
+ label: 'Network Idle',
+ name: 'networkidle',
+ description: 'Navigation is finished when there are no more connections for at least 500 ms.'
+ },
+ {
+ label: 'Commit',
+ name: 'commit',
+ description: 'Consider operation to be finished when network response is received and the document started loading.'
+ }
+ ],
+ optional: true,
+ additionalParams: true
+ },
+ {
+ label: 'Wait for selector to load',
+ name: 'waitForSelector',
+ type: 'string',
+ optional: true,
+ additionalParams: true,
+ description: 'CSS selectors like .div or #div'
},
{
label: 'Metadata',
@@ -81,6 +119,8 @@ class Playwright_DocumentLoaders implements INode {
const metadata = nodeData.inputs?.metadata
const relativeLinksMethod = nodeData.inputs?.relativeLinksMethod as string
let limit = nodeData.inputs?.limit as string
+ let waitUntilGoToOption = nodeData.inputs?.waitUntilGoToOption as 'load' | 'domcontentloaded' | 'networkidle' | 'commit' | undefined
+ let waitForSelector = nodeData.inputs?.waitForSelector as string
let url = nodeData.inputs?.url as string
url = url.trim()
@@ -91,7 +131,26 @@ class Playwright_DocumentLoaders implements INode {
async function playwrightLoader(url: string): Promise {
try {
let docs = []
- const loader = new PlaywrightWebBaseLoader(url)
+ const config: PlaywrightWebBaseLoaderOptions = {
+ launchOptions: {
+ args: ['--no-sandbox'],
+ headless: true
+ }
+ }
+ if (waitUntilGoToOption) {
+ config['gotoOptions'] = {
+ waitUntil: waitUntilGoToOption
+ }
+ }
+ if (waitForSelector) {
+ config['evaluate'] = async (page: Page, _: Browser): Promise => {
+ await page.waitForSelector(waitForSelector)
+
+ const result = await page.evaluate(() => document.body.innerHTML)
+ return result
+ }
+ }
+ const loader = new PlaywrightWebBaseLoader(url, config)
if (textSplitter) {
docs = await loader.loadAndSplit(textSplitter)
} else {
diff --git a/packages/components/nodes/documentloaders/Puppeteer/Puppeteer.ts b/packages/components/nodes/documentloaders/Puppeteer/Puppeteer.ts
index ea6280db..4691eb94 100644
--- a/packages/components/nodes/documentloaders/Puppeteer/Puppeteer.ts
+++ b/packages/components/nodes/documentloaders/Puppeteer/Puppeteer.ts
@@ -1,8 +1,9 @@
import { INode, INodeData, INodeParams } from '../../../src/Interface'
import { TextSplitter } from 'langchain/text_splitter'
-import { PuppeteerWebBaseLoader } from 'langchain/document_loaders/web/puppeteer'
+import { Browser, Page, PuppeteerWebBaseLoader, PuppeteerWebBaseLoaderOptions } from 'langchain/document_loaders/web/puppeteer'
import { test } from 'linkifyjs'
import { webCrawl, xmlScrape } from '../../../src'
+import { PuppeteerLifeCycleEvent } from 'puppeteer'
class Puppeteer_DocumentLoaders implements INode {
label: string
@@ -64,7 +65,45 @@ class Puppeteer_DocumentLoaders implements INode {
additionalParams: true,
description:
'Only used when "Get Relative Links Method" is selected. Set 0 to retrieve all relative links, default limit is 10.',
- warning: `Retreiving all links might take long time, and all links will be upserted again if the flow's state changed (eg: different URL, chunk size, etc)`
+ warning: `Retrieving all links might take long time, and all links will be upserted again if the flow's state changed (eg: different URL, chunk size, etc)`
+ },
+ {
+ label: 'Wait Until',
+ name: 'waitUntilGoToOption',
+ type: 'options',
+ description: 'Select a go to wait until option',
+ options: [
+ {
+ label: 'Load',
+ name: 'load',
+ description: `When the initial HTML document's DOM has been loaded and parsed`
+ },
+ {
+ label: 'DOM Content Loaded',
+ name: 'domcontentloaded',
+ description: `When the complete HTML document's DOM has been loaded and parsed`
+ },
+ {
+ label: 'Network Idle 0',
+ name: 'networkidle0',
+ description: 'Navigation is finished when there are no more than 0 network connections for at least 500 ms'
+ },
+ {
+ label: 'Network Idle 2',
+ name: 'networkidle2',
+ description: 'Navigation is finished when there are no more than 2 network connections for at least 500 ms'
+ }
+ ],
+ optional: true,
+ additionalParams: true
+ },
+ {
+ label: 'Wait for selector to load',
+ name: 'waitForSelector',
+ type: 'string',
+ optional: true,
+ additionalParams: true,
+ description: 'CSS selectors like .div or #div'
},
{
label: 'Metadata',
@@ -81,6 +120,8 @@ class Puppeteer_DocumentLoaders implements INode {
const metadata = nodeData.inputs?.metadata
const relativeLinksMethod = nodeData.inputs?.relativeLinksMethod as string
let limit = nodeData.inputs?.limit as string
+ let waitUntilGoToOption = nodeData.inputs?.waitUntilGoToOption as PuppeteerLifeCycleEvent
+ let waitForSelector = nodeData.inputs?.waitForSelector as string
let url = nodeData.inputs?.url as string
url = url.trim()
@@ -91,12 +132,26 @@ class Puppeteer_DocumentLoaders implements INode {
async function puppeteerLoader(url: string): Promise {
try {
let docs = []
- const loader = new PuppeteerWebBaseLoader(url, {
+ const config: PuppeteerWebBaseLoaderOptions = {
launchOptions: {
args: ['--no-sandbox'],
headless: 'new'
}
- })
+ }
+ if (waitUntilGoToOption) {
+ config['gotoOptions'] = {
+ waitUntil: waitUntilGoToOption
+ }
+ }
+ if (waitForSelector) {
+ config['evaluate'] = async (page: Page, _: Browser): Promise => {
+ await page.waitForSelector(waitForSelector)
+
+ const result = await page.evaluate(() => document.body.innerHTML)
+ return result
+ }
+ }
+ const loader = new PuppeteerWebBaseLoader(url, config)
if (textSplitter) {
docs = await loader.loadAndSplit(textSplitter)
} else {
diff --git a/packages/components/nodes/documentloaders/VectorStoreToDocument/VectorStoreToDocument.ts b/packages/components/nodes/documentloaders/VectorStoreToDocument/VectorStoreToDocument.ts
new file mode 100644
index 00000000..b3f320ce
--- /dev/null
+++ b/packages/components/nodes/documentloaders/VectorStoreToDocument/VectorStoreToDocument.ts
@@ -0,0 +1,87 @@
+import { VectorStore } from 'langchain/vectorstores/base'
+import { INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
+import { handleEscapeCharacters } from '../../../src/utils'
+
+class VectorStoreToDocument_DocumentLoaders implements INode {
+ label: string
+ name: string
+ version: number
+ description: string
+ type: string
+ icon: string
+ category: string
+ baseClasses: string[]
+ inputs: INodeParams[]
+ outputs: INodeOutputsValue[]
+
+ constructor() {
+ this.label = 'VectorStore To Document'
+ this.name = 'vectorStoreToDocument'
+ this.version = 1.0
+ this.type = 'Document'
+ this.icon = 'vectorretriever.svg'
+ this.category = 'Document Loaders'
+ this.description = 'Search documents with scores from vector store'
+ this.baseClasses = [this.type]
+ this.inputs = [
+ {
+ label: 'Vector Store',
+ name: 'vectorStore',
+ type: 'VectorStore'
+ },
+ {
+ label: 'Minimum Score (%)',
+ name: 'minScore',
+ type: 'number',
+ optional: true,
+ placeholder: '75',
+ step: 1,
+ description: 'Minumum score for embeddings documents to be included'
+ }
+ ]
+ this.outputs = [
+ {
+ label: 'Document',
+ name: 'document',
+ baseClasses: this.baseClasses
+ },
+ {
+ label: 'Text',
+ name: 'text',
+ baseClasses: ['string', 'json']
+ }
+ ]
+ }
+
+ async init(nodeData: INodeData, input: string): Promise {
+ const vectorStore = nodeData.inputs?.vectorStore as VectorStore
+ const minScore = nodeData.inputs?.minScore as number
+ const output = nodeData.outputs?.output as string
+
+ const topK = (vectorStore as any)?.k ?? 4
+
+ const docs = await vectorStore.similaritySearchWithScore(input, topK)
+ // eslint-disable-next-line no-console
+ console.log('\x1b[94m\x1b[1m\n*****VectorStore Documents*****\n\x1b[0m\x1b[0m')
+ // eslint-disable-next-line no-console
+ console.log(docs)
+
+ if (output === 'document') {
+ let finaldocs = []
+ for (const doc of docs) {
+ if (minScore && doc[1] < minScore / 100) continue
+ finaldocs.push(doc[0])
+ }
+ return finaldocs
+ } else {
+ let finaltext = ''
+ for (const doc of docs) {
+ if (minScore && doc[1] < minScore / 100) continue
+ finaltext += `${doc[0].pageContent}\n`
+ }
+ return handleEscapeCharacters(finaltext, false)
+ }
+ }
+}
+
+module.exports = { nodeClass: VectorStoreToDocument_DocumentLoaders }
diff --git a/packages/components/nodes/documentloaders/VectorStoreToDocument/vectorretriever.svg b/packages/components/nodes/documentloaders/VectorStoreToDocument/vectorretriever.svg
new file mode 100644
index 00000000..208a59f1
--- /dev/null
+++ b/packages/components/nodes/documentloaders/VectorStoreToDocument/vectorretriever.svg
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/packages/components/nodes/memory/ZepMemory/ZepMemory.ts b/packages/components/nodes/memory/ZepMemory/ZepMemory.ts
index f2a47852..0c05563a 100644
--- a/packages/components/nodes/memory/ZepMemory/ZepMemory.ts
+++ b/packages/components/nodes/memory/ZepMemory/ZepMemory.ts
@@ -120,7 +120,8 @@ class ZepMemory_Memory implements INode {
zep.loadMemoryVariables = async (values) => {
let data = await tmpFunc.bind(zep, values)()
if (autoSummary && zep.returnMessages && data[zep.memoryKey] && data[zep.memoryKey].length) {
- const memory = await zep.zepClient.getMemory(zep.sessionId, parseInt(k, 10) ?? 10)
+ const zepClient = await zep.zepClientPromise
+ const memory = await zepClient.memory.getMemory(zep.sessionId, parseInt(k, 10) ?? 10)
if (memory?.summary) {
let summary = autoSummaryTemplate.replace(/{summary}/g, memory.summary.content)
// eslint-disable-next-line no-console
@@ -190,23 +191,6 @@ class ZepMemoryExtended extends ZepMemory {
super(fields)
this.isSessionIdUsingChatMessageId = fields.isSessionIdUsingChatMessageId
}
-
- async clear(): Promise {
- // Only clear when sessionId is using chatId
- // If sessionId is specified, clearing and inserting again will error because the sessionId has been soft deleted
- // If using chatId, it will not be a problem because the sessionId will always be the new chatId
- if (this.isSessionIdUsingChatMessageId) {
- try {
- await this.zepClient.deleteMemory(this.sessionId)
- } catch (error) {
- console.error('Error deleting session: ', error)
- }
-
- // Clear the superclass's chat history
- await super.clear()
- }
- await this.chatHistory.clear()
- }
}
module.exports = { nodeClass: ZepMemory_Memory }
diff --git a/packages/components/nodes/memory/ZepMemory/zep.png b/packages/components/nodes/memory/ZepMemory/zep.png
index 293be6f6..2fdb2382 100644
Binary files a/packages/components/nodes/memory/ZepMemory/zep.png and b/packages/components/nodes/memory/ZepMemory/zep.png differ
diff --git a/packages/components/nodes/vectorstores/Faiss_Existing/Faiss_Existing.ts b/packages/components/nodes/vectorstores/Faiss_Existing/Faiss_Existing.ts
index 8c8d03a8..15d476d8 100644
--- a/packages/components/nodes/vectorstores/Faiss_Existing/Faiss_Existing.ts
+++ b/packages/components/nodes/vectorstores/Faiss_Existing/Faiss_Existing.ts
@@ -2,6 +2,7 @@ import { INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/I
import { FaissStore } from 'langchain/vectorstores/faiss'
import { Embeddings } from 'langchain/embeddings/base'
import { getBaseClasses } from '../../../src/utils'
+import { Document } from 'langchain/document'
class Faiss_Existing_VectorStores implements INode {
label: string
@@ -70,6 +71,23 @@ class Faiss_Existing_VectorStores implements INode {
const vectorStore = await FaissStore.load(basePath, embeddings)
+ // Avoid illegal invocation error
+ vectorStore.similaritySearchVectorWithScore = async (query: number[], k: number) => {
+ const index = vectorStore.index
+
+ if (k > index.ntotal()) {
+ const total = index.ntotal()
+ console.warn(`k (${k}) is greater than the number of elements in the index (${total}), setting k to ${total}`)
+ k = total
+ }
+
+ const result = index.search(query, k)
+ return result.labels.map((id, index) => {
+ const uuid = vectorStore._mapping[id]
+ return [vectorStore.docstore.search(uuid), result.distances[index]] as [Document, number]
+ })
+ }
+
if (output === 'retriever') {
const retriever = vectorStore.asRetriever(k)
return retriever
diff --git a/packages/components/nodes/vectorstores/Faiss_Upsert/Faiss_Upsert.ts b/packages/components/nodes/vectorstores/Faiss_Upsert/Faiss_Upsert.ts
index f56eccdf..c234a4f5 100644
--- a/packages/components/nodes/vectorstores/Faiss_Upsert/Faiss_Upsert.ts
+++ b/packages/components/nodes/vectorstores/Faiss_Upsert/Faiss_Upsert.ts
@@ -86,6 +86,23 @@ class FaissUpsert_VectorStores implements INode {
const vectorStore = await FaissStore.fromDocuments(finalDocs, embeddings)
await vectorStore.save(basePath)
+ // Avoid illegal invocation error
+ vectorStore.similaritySearchVectorWithScore = async (query: number[], k: number) => {
+ const index = vectorStore.index
+
+ if (k > index.ntotal()) {
+ const total = index.ntotal()
+ console.warn(`k (${k}) is greater than the number of elements in the index (${total}), setting k to ${total}`)
+ k = total
+ }
+
+ const result = index.search(query, k)
+ return result.labels.map((id, index) => {
+ const uuid = vectorStore._mapping[id]
+ return [vectorStore.docstore.search(uuid), result.distances[index]] as [Document, number]
+ })
+ }
+
if (output === 'retriever') {
const retriever = vectorStore.asRetriever(k)
return retriever
diff --git a/packages/components/nodes/vectorstores/Milvus/Milvus_Existing.ts b/packages/components/nodes/vectorstores/Milvus/Milvus_Existing.ts
new file mode 100644
index 00000000..514fdc73
--- /dev/null
+++ b/packages/components/nodes/vectorstores/Milvus/Milvus_Existing.ts
@@ -0,0 +1,185 @@
+import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
+import { DataType, ErrorCode } from '@zilliz/milvus2-sdk-node'
+import { MilvusLibArgs, Milvus } from 'langchain/vectorstores/milvus'
+import { Embeddings } from 'langchain/embeddings/base'
+import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
+import { Document } from 'langchain/document'
+
+class Milvus_Existing_VectorStores implements INode {
+ label: string
+ name: string
+ version: number
+ description: string
+ type: string
+ icon: string
+ category: string
+ baseClasses: string[]
+ inputs: INodeParams[]
+ credential: INodeParams
+ outputs: INodeOutputsValue[]
+
+ constructor() {
+ this.label = 'Milvus Load Existing collection'
+ this.name = 'milvusExistingCollection'
+ this.version = 1.0
+ this.type = 'Milvus'
+ this.icon = 'milvus.svg'
+ this.category = 'Vector Stores'
+ this.description = 'Load existing collection from Milvus (i.e: Document has been upserted)'
+ this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
+ this.credential = {
+ label: 'Connect Credential',
+ name: 'credential',
+ type: 'credential',
+ optional: true,
+ credentialNames: ['milvusAuth']
+ }
+ this.inputs = [
+ {
+ label: 'Embeddings',
+ name: 'embeddings',
+ type: 'Embeddings'
+ },
+ {
+ label: 'Milvus Server URL',
+ name: 'milvusServerUrl',
+ type: 'string',
+ placeholder: 'http://localhost:19530'
+ },
+ {
+ label: 'Milvus Collection Name',
+ name: 'milvusCollection',
+ type: 'string'
+ }
+ ]
+ this.outputs = [
+ {
+ label: 'Milvus Retriever',
+ name: 'retriever',
+ baseClasses: this.baseClasses
+ },
+ {
+ label: 'Milvus Vector Store',
+ name: 'vectorStore',
+ baseClasses: [this.type, ...getBaseClasses(Milvus)]
+ }
+ ]
+ }
+
+ async init(nodeData: INodeData, _: string, options: ICommonObject): Promise {
+ // server setup
+ const address = nodeData.inputs?.milvusServerUrl as string
+ const collectionName = nodeData.inputs?.milvusCollection as string
+
+ // embeddings
+ const embeddings = nodeData.inputs?.embeddings as Embeddings
+ const topK = nodeData.inputs?.topK as string
+
+ // output
+ const output = nodeData.outputs?.output as string
+
+ // format data
+ const k = topK ? parseInt(topK, 10) : 4
+
+ // credential
+ const credentialData = await getCredentialData(nodeData.credential ?? '', options)
+ const milvusUser = getCredentialParam('milvusUser', credentialData, nodeData)
+ const milvusPassword = getCredentialParam('milvusPassword', credentialData, nodeData)
+
+ // init MilvusLibArgs
+ const milVusArgs: MilvusLibArgs = {
+ url: address,
+ collectionName: collectionName
+ }
+
+ if (milvusUser) milVusArgs.username = milvusUser
+ if (milvusPassword) milVusArgs.password = milvusPassword
+
+ const vectorStore = await Milvus.fromExistingCollection(embeddings, milVusArgs)
+
+ // Avoid Illegal Invocation
+ vectorStore.similaritySearchVectorWithScore = async (query: number[], k: number, filter?: string) => {
+ const hasColResp = await vectorStore.client.hasCollection({
+ collection_name: vectorStore.collectionName
+ })
+ if (hasColResp.status.error_code !== ErrorCode.SUCCESS) {
+ throw new Error(`Error checking collection: ${hasColResp}`)
+ }
+ if (hasColResp.value === false) {
+ throw new Error(`Collection not found: ${vectorStore.collectionName}, please create collection before search.`)
+ }
+
+ const filterStr = filter ?? ''
+
+ await vectorStore.grabCollectionFields()
+
+ const loadResp = await vectorStore.client.loadCollectionSync({
+ collection_name: vectorStore.collectionName
+ })
+
+ if (loadResp.error_code !== ErrorCode.SUCCESS) {
+ throw new Error(`Error loading collection: ${loadResp}`)
+ }
+
+ const outputFields = vectorStore.fields.filter((field) => field !== vectorStore.vectorField)
+
+ const searchResp = await vectorStore.client.search({
+ collection_name: vectorStore.collectionName,
+ search_params: {
+ anns_field: vectorStore.vectorField,
+ topk: k.toString(),
+ metric_type: vectorStore.indexCreateParams.metric_type,
+ params: vectorStore.indexSearchParams
+ },
+ output_fields: outputFields,
+ vector_type: DataType.FloatVector,
+ vectors: [query],
+ filter: filterStr
+ })
+ if (searchResp.status.error_code !== ErrorCode.SUCCESS) {
+ throw new Error(`Error searching data: ${JSON.stringify(searchResp)}`)
+ }
+ const results: [Document, number][] = []
+ searchResp.results.forEach((result) => {
+ const fields = {
+ pageContent: '',
+ metadata: {} as Record
+ }
+ Object.keys(result).forEach((key) => {
+ if (key === vectorStore.textField) {
+ fields.pageContent = result[key]
+ } else if (vectorStore.fields.includes(key) || key === vectorStore.primaryField) {
+ if (typeof result[key] === 'string') {
+ const { isJson, obj } = checkJsonString(result[key])
+ fields.metadata[key] = isJson ? obj : result[key]
+ } else {
+ fields.metadata[key] = result[key]
+ }
+ }
+ })
+ results.push([new Document(fields), result.score])
+ })
+ return results
+ }
+
+ if (output === 'retriever') {
+ const retriever = vectorStore.asRetriever(k)
+ return retriever
+ } else if (output === 'vectorStore') {
+ ;(vectorStore as any).k = k
+ return vectorStore
+ }
+ return vectorStore
+ }
+}
+
+function checkJsonString(value: string): { isJson: boolean; obj: any } {
+ try {
+ const result = JSON.parse(value)
+ return { isJson: true, obj: result }
+ } catch (e) {
+ return { isJson: false, obj: null }
+ }
+}
+
+module.exports = { nodeClass: Milvus_Existing_VectorStores }
diff --git a/packages/components/nodes/vectorstores/Milvus/Milvus_Upsert.ts b/packages/components/nodes/vectorstores/Milvus/Milvus_Upsert.ts
new file mode 100644
index 00000000..ca69cb39
--- /dev/null
+++ b/packages/components/nodes/vectorstores/Milvus/Milvus_Upsert.ts
@@ -0,0 +1,281 @@
+import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
+import { DataType, ErrorCode, MetricType, IndexType } from '@zilliz/milvus2-sdk-node'
+import { MilvusLibArgs, Milvus } from 'langchain/vectorstores/milvus'
+import { Embeddings } from 'langchain/embeddings/base'
+import { Document } from 'langchain/document'
+import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
+import { flatten } from 'lodash'
+
+interface InsertRow {
+ [x: string]: string | number[]
+}
+
+class Milvus_Upsert_VectorStores implements INode {
+ label: string
+ name: string
+ version: number
+ description: string
+ type: string
+ icon: string
+ category: string
+ baseClasses: string[]
+ inputs: INodeParams[]
+ credential: INodeParams
+ outputs: INodeOutputsValue[]
+
+ constructor() {
+ this.label = 'Milvus Upsert Document'
+ this.name = 'milvusUpsert'
+ this.version = 1.0
+ this.type = 'Milvus'
+ this.icon = 'milvus.svg'
+ this.category = 'Vector Stores'
+ this.description = 'Upsert documents to Milvus'
+ this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
+ this.credential = {
+ label: 'Connect Credential',
+ name: 'credential',
+ type: 'credential',
+ optional: true,
+ credentialNames: ['milvusAuth']
+ }
+ this.inputs = [
+ {
+ label: 'Document',
+ name: 'document',
+ type: 'Document',
+ list: true
+ },
+ {
+ label: 'Embeddings',
+ name: 'embeddings',
+ type: 'Embeddings'
+ },
+ {
+ label: 'Milvus Server URL',
+ name: 'milvusServerUrl',
+ type: 'string',
+ placeholder: 'http://localhost:19530'
+ },
+ {
+ label: 'Milvus Collection Name',
+ name: 'milvusCollection',
+ type: 'string'
+ }
+ ]
+ this.outputs = [
+ {
+ label: 'Milvus Retriever',
+ name: 'retriever',
+ baseClasses: this.baseClasses
+ },
+ {
+ label: 'Milvus Vector Store',
+ name: 'vectorStore',
+ baseClasses: [this.type, ...getBaseClasses(Milvus)]
+ }
+ ]
+ }
+
+ async init(nodeData: INodeData, _: string, options: ICommonObject): Promise {
+ // server setup
+ const address = nodeData.inputs?.milvusServerUrl as string
+ const collectionName = nodeData.inputs?.milvusCollection as string
+
+ // embeddings
+ const docs = nodeData.inputs?.document as Document[]
+ const embeddings = nodeData.inputs?.embeddings as Embeddings
+ const topK = nodeData.inputs?.topK as string
+
+ // output
+ const output = nodeData.outputs?.output as string
+
+ // format data
+ const k = topK ? parseInt(topK, 10) : 4
+
+ // credential
+ const credentialData = await getCredentialData(nodeData.credential ?? '', options)
+ const milvusUser = getCredentialParam('milvusUser', credentialData, nodeData)
+ const milvusPassword = getCredentialParam('milvusPassword', credentialData, nodeData)
+
+ // init MilvusLibArgs
+ const milVusArgs: MilvusLibArgs = {
+ url: address,
+ collectionName: collectionName
+ }
+
+ if (milvusUser) milVusArgs.username = milvusUser
+ if (milvusPassword) milVusArgs.password = milvusPassword
+
+ const flattenDocs = docs && docs.length ? flatten(docs) : []
+ const finalDocs = []
+ for (let i = 0; i < flattenDocs.length; i += 1) {
+ finalDocs.push(new Document(flattenDocs[i]))
+ }
+
+ const vectorStore = await MilvusUpsert.fromDocuments(finalDocs, embeddings, milVusArgs)
+
+ // Avoid Illegal Invocation
+ vectorStore.similaritySearchVectorWithScore = async (query: number[], k: number, filter?: string) => {
+ const hasColResp = await vectorStore.client.hasCollection({
+ collection_name: vectorStore.collectionName
+ })
+ if (hasColResp.status.error_code !== ErrorCode.SUCCESS) {
+ throw new Error(`Error checking collection: ${hasColResp}`)
+ }
+ if (hasColResp.value === false) {
+ throw new Error(`Collection not found: ${vectorStore.collectionName}, please create collection before search.`)
+ }
+
+ const filterStr = filter ?? ''
+
+ await vectorStore.grabCollectionFields()
+
+ const loadResp = await vectorStore.client.loadCollectionSync({
+ collection_name: vectorStore.collectionName
+ })
+ if (loadResp.error_code !== ErrorCode.SUCCESS) {
+ throw new Error(`Error loading collection: ${loadResp}`)
+ }
+
+ const outputFields = vectorStore.fields.filter((field) => field !== vectorStore.vectorField)
+
+ const searchResp = await vectorStore.client.search({
+ collection_name: vectorStore.collectionName,
+ search_params: {
+ anns_field: vectorStore.vectorField,
+ topk: k.toString(),
+ metric_type: vectorStore.indexCreateParams.metric_type,
+ params: vectorStore.indexSearchParams
+ },
+ output_fields: outputFields,
+ vector_type: DataType.FloatVector,
+ vectors: [query],
+ filter: filterStr
+ })
+ if (searchResp.status.error_code !== ErrorCode.SUCCESS) {
+ throw new Error(`Error searching data: ${JSON.stringify(searchResp)}`)
+ }
+ const results: [Document, number][] = []
+ searchResp.results.forEach((result) => {
+ const fields = {
+ pageContent: '',
+ metadata: {} as Record
+ }
+ Object.keys(result).forEach((key) => {
+ if (key === vectorStore.textField) {
+ fields.pageContent = result[key]
+ } else if (vectorStore.fields.includes(key) || key === vectorStore.primaryField) {
+ if (typeof result[key] === 'string') {
+ const { isJson, obj } = checkJsonString(result[key])
+ fields.metadata[key] = isJson ? obj : result[key]
+ } else {
+ fields.metadata[key] = result[key]
+ }
+ }
+ })
+ results.push([new Document(fields), result.score])
+ })
+ return results
+ }
+
+ if (output === 'retriever') {
+ const retriever = vectorStore.asRetriever(k)
+ return retriever
+ } else if (output === 'vectorStore') {
+ ;(vectorStore as any).k = k
+ return vectorStore
+ }
+ return vectorStore
+ }
+}
+
+function checkJsonString(value: string): { isJson: boolean; obj: any } {
+ try {
+ const result = JSON.parse(value)
+ return { isJson: true, obj: result }
+ } catch (e) {
+ return { isJson: false, obj: null }
+ }
+}
+
+class MilvusUpsert extends Milvus {
+ async addVectors(vectors: number[][], documents: Document[]): Promise {
+ if (vectors.length === 0) {
+ return
+ }
+ await this.ensureCollection(vectors, documents)
+
+ const insertDatas: InsertRow[] = []
+
+ for (let index = 0; index < vectors.length; index++) {
+ const vec = vectors[index]
+ const doc = documents[index]
+ const data: InsertRow = {
+ [this.textField]: doc.pageContent,
+ [this.vectorField]: vec
+ }
+ this.fields.forEach((field) => {
+ switch (field) {
+ case this.primaryField:
+ if (!this.autoId) {
+ if (doc.metadata[this.primaryField] === undefined) {
+ throw new Error(
+ `The Collection's primaryField is configured with autoId=false, thus its value must be provided through metadata.`
+ )
+ }
+ data[field] = doc.metadata[this.primaryField]
+ }
+ break
+ case this.textField:
+ data[field] = doc.pageContent
+ break
+ case this.vectorField:
+ data[field] = vec
+ break
+ default: // metadata fields
+ if (doc.metadata[field] === undefined) {
+ throw new Error(`The field "${field}" is not provided in documents[${index}].metadata.`)
+ } else if (typeof doc.metadata[field] === 'object') {
+ data[field] = JSON.stringify(doc.metadata[field])
+ } else {
+ data[field] = doc.metadata[field]
+ }
+ break
+ }
+ })
+
+ insertDatas.push(data)
+ }
+
+ const descIndexResp = await this.client.describeIndex({
+ collection_name: this.collectionName
+ })
+
+ if (descIndexResp.status.error_code === ErrorCode.INDEX_NOT_EXIST) {
+ const resp = await this.client.createIndex({
+ collection_name: this.collectionName,
+ field_name: this.vectorField,
+ index_name: `myindex_${Date.now().toString()}`,
+ index_type: IndexType.AUTOINDEX,
+ metric_type: MetricType.L2
+ })
+ if (resp.error_code !== ErrorCode.SUCCESS) {
+ throw new Error(`Error creating index`)
+ }
+ }
+
+ const insertResp = await this.client.insert({
+ collection_name: this.collectionName,
+ fields_data: insertDatas
+ })
+
+ if (insertResp.status.error_code !== ErrorCode.SUCCESS) {
+ throw new Error(`Error inserting data: ${JSON.stringify(insertResp)}`)
+ }
+
+ await this.client.flushSync({ collection_names: [this.collectionName] })
+ }
+}
+
+module.exports = { nodeClass: Milvus_Upsert_VectorStores }
diff --git a/packages/components/nodes/vectorstores/Milvus/milvus.svg b/packages/components/nodes/vectorstores/Milvus/milvus.svg
new file mode 100644
index 00000000..68dfef66
--- /dev/null
+++ b/packages/components/nodes/vectorstores/Milvus/milvus.svg
@@ -0,0 +1,5 @@
+
diff --git a/packages/components/nodes/vectorstores/Vectara_Existing/Vectara_Existing.ts b/packages/components/nodes/vectorstores/Vectara_Existing/Vectara_Existing.ts
index f344338a..3ef04f07 100644
--- a/packages/components/nodes/vectorstores/Vectara_Existing/Vectara_Existing.ts
+++ b/packages/components/nodes/vectorstores/Vectara_Existing/Vectara_Existing.ts
@@ -1,6 +1,6 @@
import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
-import { VectaraStore, VectaraLibArgs, VectaraFilter } from 'langchain/vectorstores/vectara'
+import { VectaraStore, VectaraLibArgs, VectaraFilter, VectaraContextConfig } from 'langchain/vectorstores/vectara'
class VectaraExisting_VectorStores implements INode {
label: string
@@ -40,9 +40,27 @@ class VectaraExisting_VectorStores implements INode {
additionalParams: true,
optional: true
},
+ {
+ label: 'Sentences Before',
+ name: 'sentencesBefore',
+ description: 'Number of sentences to fetch before the matched sentence. Defaults to 2.',
+ type: 'number',
+ additionalParams: true,
+ optional: true
+ },
+ {
+ label: 'Sentences After',
+ name: 'sentencesAfter',
+ description: 'Number of sentences to fetch after the matched sentence. Defaults to 2.',
+ type: 'number',
+ additionalParams: true,
+ optional: true
+ },
{
label: 'Lambda',
name: 'lambda',
+ description:
+ 'Improves retrieval accuracy by adjusting the balance (from 0 to 1) between neural search and keyword-based search factors.',
type: 'number',
additionalParams: true,
optional: true
@@ -77,6 +95,8 @@ class VectaraExisting_VectorStores implements INode {
const corpusId = getCredentialParam('corpusID', credentialData, nodeData)
const vectaraMetadataFilter = nodeData.inputs?.filter as string
+ const sentencesBefore = nodeData.inputs?.sentencesBefore as number
+ const sentencesAfter = nodeData.inputs?.sentencesAfter as number
const lambda = nodeData.inputs?.lambda as number
const output = nodeData.outputs?.output as string
const topK = nodeData.inputs?.topK as string
@@ -92,6 +112,11 @@ class VectaraExisting_VectorStores implements INode {
if (vectaraMetadataFilter) vectaraFilter.filter = vectaraMetadataFilter
if (lambda) vectaraFilter.lambda = lambda
+ const vectaraContextConfig: VectaraContextConfig = {}
+ if (sentencesBefore) vectaraContextConfig.sentencesBefore = sentencesBefore
+ if (sentencesAfter) vectaraContextConfig.sentencesAfter = sentencesAfter
+ vectaraFilter.contextConfig = vectaraContextConfig
+
const vectorStore = new VectaraStore(vectaraArgs)
if (output === 'retriever') {
diff --git a/packages/components/nodes/vectorstores/Vectara_Upsert/Vectara_Upsert.ts b/packages/components/nodes/vectorstores/Vectara_Upsert/Vectara_Upsert.ts
index b2ee79e7..51fb67ed 100644
--- a/packages/components/nodes/vectorstores/Vectara_Upsert/Vectara_Upsert.ts
+++ b/packages/components/nodes/vectorstores/Vectara_Upsert/Vectara_Upsert.ts
@@ -1,7 +1,7 @@
import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
import { Embeddings } from 'langchain/embeddings/base'
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
-import { VectaraStore, VectaraLibArgs, VectaraFilter } from 'langchain/vectorstores/vectara'
+import { VectaraStore, VectaraLibArgs, VectaraFilter, VectaraContextConfig } from 'langchain/vectorstores/vectara'
import { Document } from 'langchain/document'
import { flatten } from 'lodash'
@@ -49,9 +49,27 @@ class VectaraUpsert_VectorStores implements INode {
additionalParams: true,
optional: true
},
+ {
+ label: 'Sentences Before',
+ name: 'sentencesBefore',
+ description: 'Number of sentences to fetch before the matched sentence. Defaults to 2.',
+ type: 'number',
+ additionalParams: true,
+ optional: true
+ },
+ {
+ label: 'Sentences After',
+ name: 'sentencesAfter',
+ description: 'Number of sentences to fetch after the matched sentence. Defaults to 2.',
+ type: 'number',
+ additionalParams: true,
+ optional: true
+ },
{
label: 'Lambda',
name: 'lambda',
+ description:
+ 'Improves retrieval accuracy by adjusting the balance (from 0 to 1) between neural search and keyword-based search factors.',
type: 'number',
additionalParams: true,
optional: true
@@ -88,6 +106,8 @@ class VectaraUpsert_VectorStores implements INode {
const docs = nodeData.inputs?.document as Document[]
const embeddings = {} as Embeddings
const vectaraMetadataFilter = nodeData.inputs?.filter as string
+ const sentencesBefore = nodeData.inputs?.sentencesBefore as number
+ const sentencesAfter = nodeData.inputs?.sentencesAfter as number
const lambda = nodeData.inputs?.lambda as number
const output = nodeData.outputs?.output as string
const topK = nodeData.inputs?.topK as string
@@ -103,6 +123,11 @@ class VectaraUpsert_VectorStores implements INode {
if (vectaraMetadataFilter) vectaraFilter.filter = vectaraMetadataFilter
if (lambda) vectaraFilter.lambda = lambda
+ const vectaraContextConfig: VectaraContextConfig = {}
+ if (sentencesBefore) vectaraContextConfig.sentencesBefore = sentencesBefore
+ if (sentencesAfter) vectaraContextConfig.sentencesAfter = sentencesAfter
+ vectaraFilter.contextConfig = vectaraContextConfig
+
const flattenDocs = docs && docs.length ? flatten(docs) : []
const finalDocs = []
for (let i = 0; i < flattenDocs.length; i += 1) {
diff --git a/packages/components/nodes/vectorstores/Zep/Zep_Existing.ts b/packages/components/nodes/vectorstores/Zep/Zep_Existing.ts
new file mode 100644
index 00000000..a2c2261f
--- /dev/null
+++ b/packages/components/nodes/vectorstores/Zep/Zep_Existing.ts
@@ -0,0 +1,235 @@
+import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
+import { ZepVectorStore, IZepConfig } from 'langchain/vectorstores/zep'
+import { Embeddings } from 'langchain/embeddings/base'
+import { Document } from 'langchain/document'
+import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
+import { IDocument, ZepClient } from '@getzep/zep-js'
+
+class Zep_Existing_VectorStores implements INode {
+ label: string
+ name: string
+ version: number
+ description: string
+ type: string
+ icon: string
+ category: string
+ baseClasses: string[]
+ inputs: INodeParams[]
+ credential: INodeParams
+ outputs: INodeOutputsValue[]
+
+ constructor() {
+ this.label = 'Zep Load Existing Index'
+ this.name = 'zepExistingIndex'
+ this.version = 1.0
+ this.type = 'Zep'
+ this.icon = 'zep.png'
+ this.category = 'Vector Stores'
+ this.description = 'Load existing index from Zep (i.e: Document has been upserted)'
+ this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
+ this.credential = {
+ label: 'Connect Credential',
+ name: 'credential',
+ type: 'credential',
+ optional: true,
+ description: 'Configure JWT authentication on your Zep instance (Optional)',
+ credentialNames: ['zepMemoryApi']
+ }
+ this.inputs = [
+ {
+ label: 'Embeddings',
+ name: 'embeddings',
+ type: 'Embeddings'
+ },
+ {
+ label: 'Base URL',
+ name: 'baseURL',
+ type: 'string',
+ default: 'http://127.0.0.1:8000'
+ },
+ {
+ label: 'Zep Collection',
+ name: 'zepCollection',
+ type: 'string',
+ placeholder: 'my-first-collection'
+ },
+ {
+ label: 'Zep Metadata Filter',
+ name: 'zepMetadataFilter',
+ type: 'json',
+ optional: true,
+ additionalParams: true
+ },
+ {
+ label: 'Embedding Dimension',
+ name: 'dimension',
+ type: 'number',
+ default: 1536,
+ additionalParams: true
+ },
+ {
+ label: 'Top K',
+ name: 'topK',
+ description: 'Number of top results to fetch. Default to 4',
+ placeholder: '4',
+ type: 'number',
+ additionalParams: true,
+ optional: true
+ }
+ ]
+ this.outputs = [
+ {
+ label: 'Pinecone Retriever',
+ name: 'retriever',
+ baseClasses: this.baseClasses
+ },
+ {
+ label: 'Pinecone Vector Store',
+ name: 'vectorStore',
+ baseClasses: [this.type, ...getBaseClasses(ZepVectorStore)]
+ }
+ ]
+ }
+
+ async init(nodeData: INodeData, _: string, options: ICommonObject): Promise {
+ const baseURL = nodeData.inputs?.baseURL as string
+ const zepCollection = nodeData.inputs?.zepCollection as string
+ 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)
+
+ const zepConfig: IZepConfig & Partial = {
+ apiUrl: baseURL,
+ collectionName: zepCollection,
+ embeddingDimensions: dimension,
+ isAutoEmbedded: false
+ }
+ if (apiKey) zepConfig.apiKey = apiKey
+ if (zepMetadataFilter) {
+ const metadatafilter = typeof zepMetadataFilter === 'object' ? zepMetadataFilter : JSON.parse(zepMetadataFilter)
+ zepConfig.filter = metadatafilter
+ }
+
+ 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
+ }
+}
+
+interface ZepFilter {
+ filter: Record
+}
+
+function zepDocsToDocumentsAndScore(results: IDocument[]): [Document, number][] {
+ return results.map((d) => [
+ new Document({
+ pageContent: d.content,
+ metadata: d.metadata
+ }),
+ d.score ? d.score : 0
+ ])
+}
+
+function assignMetadata(value: string | Record | object | undefined): Record | undefined {
+ if (typeof value === 'object' && value !== null) {
+ return value as Record
+ }
+ if (value !== undefined) {
+ console.warn('Metadata filters must be an object, Record, or undefined.')
+ }
+ return undefined
+}
+
+class ZepExistingVS extends ZepVectorStore {
+ filter?: Record
+ args?: IZepConfig & Partial
+
+ constructor(embeddings: Embeddings, args: IZepConfig & Partial) {
+ super(embeddings, args)
+ this.filter = args.filter
+ this.args = args
+ }
+
+ async initalizeCollection(args: IZepConfig & Partial) {
+ this.client = await ZepClient.init(args.apiUrl, args.apiKey)
+ try {
+ this.collection = await this.client.document.getCollection(args.collectionName)
+ } catch (err) {
+ if (err instanceof Error) {
+ if (err.name === 'NotFoundError') {
+ await this.createNewCollection(args)
+ } else {
+ throw err
+ }
+ }
+ }
+ }
+
+ async createNewCollection(args: IZepConfig & Partial) {
+ if (!args.embeddingDimensions) {
+ throw new Error(
+ `Collection ${args.collectionName} not found. You can create a new Collection by providing embeddingDimensions.`
+ )
+ }
+
+ this.collection = await this.client.document.addCollection({
+ name: args.collectionName,
+ description: args.description,
+ metadata: args.metadata,
+ embeddingDimensions: args.embeddingDimensions,
+ isAutoEmbedded: false
+ })
+ }
+
+ async similaritySearchVectorWithScore(
+ query: number[],
+ k: number,
+ filter?: Record | undefined
+ ): Promise<[Document, number][]> {
+ if (filter && this.filter) {
+ throw new Error('cannot provide both `filter` and `this.filter`')
+ }
+ const _filters = filter ?? this.filter
+ const ANDFilters = []
+ for (const filterKey in _filters) {
+ let filterVal = _filters[filterKey]
+ if (typeof filterVal === 'string') filterVal = `"${filterVal}"`
+ ANDFilters.push({ jsonpath: `$[*] ? (@.${filterKey} == ${filterVal})` })
+ }
+ const newfilter = {
+ where: { and: ANDFilters }
+ }
+ await this.initalizeCollection(this.args!).catch((err) => {
+ console.error('Error initializing collection:', err)
+ throw err
+ })
+ const results = await this.collection.search(
+ {
+ embedding: new Float32Array(query),
+ metadata: assignMetadata(newfilter)
+ },
+ k
+ )
+ return zepDocsToDocumentsAndScore(results)
+ }
+
+ static async fromExistingIndex(embeddings: Embeddings, dbConfig: IZepConfig & Partial): Promise {
+ const instance = new this(embeddings, dbConfig)
+ return instance
+ }
+}
+
+module.exports = { nodeClass: Zep_Existing_VectorStores }
diff --git a/packages/components/nodes/vectorstores/Zep/Zep_Upsert.ts b/packages/components/nodes/vectorstores/Zep/Zep_Upsert.ts
new file mode 100644
index 00000000..0f976d2b
--- /dev/null
+++ b/packages/components/nodes/vectorstores/Zep/Zep_Upsert.ts
@@ -0,0 +1,133 @@
+import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
+import { ZepVectorStore, IZepConfig } from 'langchain/vectorstores/zep'
+import { Embeddings } from 'langchain/embeddings/base'
+import { Document } from 'langchain/document'
+import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
+import { flatten } from 'lodash'
+
+class Zep_Upsert_VectorStores implements INode {
+ label: string
+ name: string
+ version: number
+ description: string
+ type: string
+ icon: string
+ category: string
+ baseClasses: string[]
+ inputs: INodeParams[]
+ credential: INodeParams
+ outputs: INodeOutputsValue[]
+
+ constructor() {
+ this.label = 'Zep Upsert Document'
+ this.name = 'zepUpsert'
+ this.version = 1.0
+ this.type = 'Zep'
+ this.icon = 'zep.png'
+ this.category = 'Vector Stores'
+ this.description = 'Upsert documents to Zep'
+ this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
+ this.credential = {
+ label: 'Connect Credential',
+ name: 'credential',
+ type: 'credential',
+ optional: true,
+ description: 'Configure JWT authentication on your Zep instance (Optional)',
+ credentialNames: ['zepMemoryApi']
+ }
+ this.inputs = [
+ {
+ label: 'Document',
+ name: 'document',
+ type: 'Document',
+ list: true
+ },
+ {
+ label: 'Embeddings',
+ name: 'embeddings',
+ type: 'Embeddings'
+ },
+ {
+ label: 'Base URL',
+ name: 'baseURL',
+ type: 'string',
+ default: 'http://127.0.0.1:8000'
+ },
+ {
+ label: 'Zep Collection',
+ name: 'zepCollection',
+ type: 'string',
+ placeholder: 'my-first-collection'
+ },
+ {
+ label: 'Embedding Dimension',
+ name: 'dimension',
+ type: 'number',
+ default: 1536,
+ additionalParams: true
+ },
+ {
+ label: 'Top K',
+ name: 'topK',
+ description: 'Number of top results to fetch. Default to 4',
+ placeholder: '4',
+ type: 'number',
+ additionalParams: true,
+ optional: true
+ }
+ ]
+ this.outputs = [
+ {
+ label: 'Zep Retriever',
+ name: 'retriever',
+ baseClasses: this.baseClasses
+ },
+ {
+ label: 'Zep Vector Store',
+ name: 'vectorStore',
+ baseClasses: [this.type, ...getBaseClasses(ZepVectorStore)]
+ }
+ ]
+ }
+
+ async init(nodeData: INodeData, _: string, options: ICommonObject): Promise {
+ const baseURL = nodeData.inputs?.baseURL as string
+ const zepCollection = nodeData.inputs?.zepCollection as string
+ const dimension = (nodeData.inputs?.dimension as number) ?? 1536
+ const docs = nodeData.inputs?.document as Document[]
+ const embeddings = nodeData.inputs?.embeddings as Embeddings
+ const topK = nodeData.inputs?.topK as string
+ const k = topK ? parseFloat(topK) : 4
+ const output = nodeData.outputs?.output as string
+
+ const credentialData = await getCredentialData(nodeData.credential ?? '', options)
+ const apiKey = getCredentialParam('apiKey', credentialData, nodeData)
+
+ const flattenDocs = docs && docs.length ? flatten(docs) : []
+ const finalDocs = []
+ for (let i = 0; i < flattenDocs.length; i += 1) {
+ finalDocs.push(new Document(flattenDocs[i]))
+ }
+
+ const zepConfig: IZepConfig = {
+ apiUrl: baseURL,
+ collectionName: zepCollection,
+ embeddingDimensions: dimension,
+ isAutoEmbedded: false
+ }
+ if (apiKey) zepConfig.apiKey = apiKey
+
+ const vectorStore = await ZepVectorStore.fromDocuments(finalDocs, 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
+ }
+}
+
+module.exports = { nodeClass: Zep_Upsert_VectorStores }
diff --git a/packages/components/nodes/vectorstores/Zep/zep.png b/packages/components/nodes/vectorstores/Zep/zep.png
new file mode 100644
index 00000000..2fdb2382
Binary files /dev/null and b/packages/components/nodes/vectorstores/Zep/zep.png differ
diff --git a/packages/components/package.json b/packages/components/package.json
index c48cba73..4653f744 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -18,7 +18,7 @@
"dependencies": {
"@aws-sdk/client-dynamodb": "^3.360.0",
"@dqbd/tiktoken": "^1.0.7",
- "@getzep/zep-js": "^0.4.1",
+ "@getzep/zep-js": "^0.6.3",
"@huggingface/inference": "^2.6.1",
"@notionhq/client": "^2.2.8",
"@opensearch-project/opensearch": "^1.2.0",
@@ -28,6 +28,7 @@
"@types/js-yaml": "^4.0.5",
"apify-client": "^2.7.1",
"@types/jsdom": "^21.1.1",
+ "@zilliz/milvus2-sdk-node": "^2.2.24",
"axios": "^0.27.2",
"cheerio": "^1.0.0-rc.12",
"chromadb": "^1.5.3",
@@ -40,7 +41,7 @@
"google-auth-library": "^9.0.0",
"graphql": "^16.6.0",
"html-to-text": "^9.0.5",
- "langchain": "^0.0.122",
+ "langchain": "^0.0.128",
"linkifyjs": "^4.1.1",
"mammoth": "^1.5.1",
"moment": "^2.29.3",
diff --git a/packages/components/src/utils.ts b/packages/components/src/utils.ts
index bcca834a..8d06a650 100644
--- a/packages/components/src/utils.ts
+++ b/packages/components/src/utils.ts
@@ -520,3 +520,22 @@ export const mapChatHistory = (options: ICommonObject): ChatMessageHistory => {
}
return new ChatMessageHistory(chatHistory)
}
+
+/**
+ * Convert incoming chat history to string
+ * @param {IMessage[]} chatHistory
+ * @returns {string}
+ */
+export const convertChatHistoryToText = (chatHistory: IMessage[]): string => {
+ return chatHistory
+ .map((chatMessage) => {
+ if (chatMessage.type === 'apiMessage') {
+ return `Assistant: ${chatMessage.message}`
+ } else if (chatMessage.type === 'userMessage') {
+ return `Human: ${chatMessage.message}`
+ } else {
+ return `${chatMessage.message}`
+ }
+ })
+ .join('\n')
+}
diff --git a/packages/server/marketplaces/chatflows/Prompt Chaining with VectorStore.json b/packages/server/marketplaces/chatflows/Prompt Chaining with VectorStore.json
new file mode 100644
index 00000000..9d6838eb
--- /dev/null
+++ b/packages/server/marketplaces/chatflows/Prompt Chaining with VectorStore.json
@@ -0,0 +1,966 @@
+{
+ "description": "Use chat history to rephrase user question, and answer the rephrased question using retrieved docs from vector store",
+ "nodes": [
+ {
+ "width": 300,
+ "height": 503,
+ "id": "pineconeExistingIndex_0",
+ "position": {
+ "x": 1062.7418678410986,
+ "y": -109.27680365777141
+ },
+ "type": "customNode",
+ "data": {
+ "id": "pineconeExistingIndex_0",
+ "label": "Pinecone Load Existing Index",
+ "version": 1,
+ "name": "pineconeExistingIndex",
+ "type": "Pinecone",
+ "baseClasses": ["Pinecone", "VectorStoreRetriever", "BaseRetriever"],
+ "category": "Vector Stores",
+ "description": "Load existing index from Pinecone (i.e: Document has been upserted)",
+ "inputParams": [
+ {
+ "label": "Connect Credential",
+ "name": "credential",
+ "type": "credential",
+ "credentialNames": ["pineconeApi"],
+ "id": "pineconeExistingIndex_0-input-credential-credential"
+ },
+ {
+ "label": "Pinecone Index",
+ "name": "pineconeIndex",
+ "type": "string",
+ "id": "pineconeExistingIndex_0-input-pineconeIndex-string"
+ },
+ {
+ "label": "Pinecone Namespace",
+ "name": "pineconeNamespace",
+ "type": "string",
+ "placeholder": "my-first-namespace",
+ "additionalParams": true,
+ "optional": true,
+ "id": "pineconeExistingIndex_0-input-pineconeNamespace-string"
+ },
+ {
+ "label": "Pinecone Metadata Filter",
+ "name": "pineconeMetadataFilter",
+ "type": "json",
+ "optional": true,
+ "additionalParams": true,
+ "id": "pineconeExistingIndex_0-input-pineconeMetadataFilter-json"
+ },
+ {
+ "label": "Top K",
+ "name": "topK",
+ "description": "Number of top results to fetch. Default to 4",
+ "placeholder": "4",
+ "type": "number",
+ "additionalParams": true,
+ "optional": true,
+ "id": "pineconeExistingIndex_0-input-topK-number"
+ }
+ ],
+ "inputAnchors": [
+ {
+ "label": "Embeddings",
+ "name": "embeddings",
+ "type": "Embeddings",
+ "id": "pineconeExistingIndex_0-input-embeddings-Embeddings"
+ }
+ ],
+ "inputs": {
+ "embeddings": "{{openAIEmbeddings_0.data.instance}}",
+ "pineconeIndex": "newindex",
+ "pineconeNamespace": "",
+ "pineconeMetadataFilter": "{}",
+ "topK": ""
+ },
+ "outputAnchors": [
+ {
+ "name": "output",
+ "label": "Output",
+ "type": "options",
+ "options": [
+ {
+ "id": "pineconeExistingIndex_0-output-retriever-Pinecone|VectorStoreRetriever|BaseRetriever",
+ "name": "retriever",
+ "label": "Pinecone Retriever",
+ "type": "Pinecone | VectorStoreRetriever | BaseRetriever"
+ },
+ {
+ "id": "pineconeExistingIndex_0-output-vectorStore-Pinecone|VectorStore",
+ "name": "vectorStore",
+ "label": "Pinecone Vector Store",
+ "type": "Pinecone | VectorStore"
+ }
+ ],
+ "default": "retriever"
+ }
+ ],
+ "outputs": {
+ "output": "vectorStore"
+ },
+ "selected": false
+ },
+ "selected": false,
+ "positionAbsolute": {
+ "x": 1062.7418678410986,
+ "y": -109.27680365777141
+ },
+ "dragging": false
+ },
+ {
+ "width": 300,
+ "height": 327,
+ "id": "openAIEmbeddings_0",
+ "position": {
+ "x": 711.3971966563331,
+ "y": 7.7184225021727
+ },
+ "type": "customNode",
+ "data": {
+ "id": "openAIEmbeddings_0",
+ "label": "OpenAI Embeddings",
+ "version": 1,
+ "name": "openAIEmbeddings",
+ "type": "OpenAIEmbeddings",
+ "baseClasses": ["OpenAIEmbeddings", "Embeddings"],
+ "category": "Embeddings",
+ "description": "OpenAI API to generate embeddings for a given text",
+ "inputParams": [
+ {
+ "label": "Connect Credential",
+ "name": "credential",
+ "type": "credential",
+ "credentialNames": ["openAIApi"],
+ "id": "openAIEmbeddings_0-input-credential-credential"
+ },
+ {
+ "label": "Strip New Lines",
+ "name": "stripNewLines",
+ "type": "boolean",
+ "optional": true,
+ "additionalParams": true,
+ "id": "openAIEmbeddings_0-input-stripNewLines-boolean"
+ },
+ {
+ "label": "Batch Size",
+ "name": "batchSize",
+ "type": "number",
+ "optional": true,
+ "additionalParams": true,
+ "id": "openAIEmbeddings_0-input-batchSize-number"
+ },
+ {
+ "label": "Timeout",
+ "name": "timeout",
+ "type": "number",
+ "optional": true,
+ "additionalParams": true,
+ "id": "openAIEmbeddings_0-input-timeout-number"
+ },
+ {
+ "label": "BasePath",
+ "name": "basepath",
+ "type": "string",
+ "optional": true,
+ "additionalParams": true,
+ "id": "openAIEmbeddings_0-input-basepath-string"
+ }
+ ],
+ "inputAnchors": [],
+ "inputs": {
+ "stripNewLines": "",
+ "batchSize": "",
+ "timeout": "",
+ "basepath": ""
+ },
+ "outputAnchors": [
+ {
+ "id": "openAIEmbeddings_0-output-openAIEmbeddings-OpenAIEmbeddings|Embeddings",
+ "name": "openAIEmbeddings",
+ "label": "OpenAIEmbeddings",
+ "type": "OpenAIEmbeddings | Embeddings"
+ }
+ ],
+ "outputs": {},
+ "selected": false
+ },
+ "selected": false,
+ "positionAbsolute": {
+ "x": 711.3971966563331,
+ "y": 7.7184225021727
+ },
+ "dragging": false
+ },
+ {
+ "width": 300,
+ "height": 473,
+ "id": "promptTemplate_0",
+ "position": {
+ "x": 348.2881107399286,
+ "y": -97.74510214137423
+ },
+ "type": "customNode",
+ "data": {
+ "id": "promptTemplate_0",
+ "label": "Prompt Template",
+ "version": 1,
+ "name": "promptTemplate",
+ "type": "PromptTemplate",
+ "baseClasses": ["PromptTemplate", "BaseStringPromptTemplate", "BasePromptTemplate", "Runnable"],
+ "category": "Prompts",
+ "description": "Schema to represent a basic prompt for an LLM",
+ "inputParams": [
+ {
+ "label": "Template",
+ "name": "template",
+ "type": "string",
+ "rows": 4,
+ "placeholder": "What is a good name for a company that makes {product}?",
+ "id": "promptTemplate_0-input-template-string"
+ },
+ {
+ "label": "Format Prompt Values",
+ "name": "promptValues",
+ "type": "json",
+ "optional": true,
+ "acceptVariable": true,
+ "list": true,
+ "id": "promptTemplate_0-input-promptValues-json"
+ }
+ ],
+ "inputAnchors": [],
+ "inputs": {
+ "template": "Given the following conversation and a follow up question, rephrase the follow up question to be a standalone question.\n\nChat History:\n{chat_history}\nFollow Up Input: {question}\nStandalone question:",
+ "promptValues": "{\"question\":\"{{question}}\",\"chat_history\":\"{{chat_history}}\"}"
+ },
+ "outputAnchors": [
+ {
+ "id": "promptTemplate_0-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate|Runnable",
+ "name": "promptTemplate",
+ "label": "PromptTemplate",
+ "type": "PromptTemplate | BaseStringPromptTemplate | BasePromptTemplate | Runnable"
+ }
+ ],
+ "outputs": {},
+ "selected": false
+ },
+ "selected": false,
+ "positionAbsolute": {
+ "x": 348.2881107399286,
+ "y": -97.74510214137423
+ },
+ "dragging": false
+ },
+ {
+ "width": 300,
+ "height": 522,
+ "id": "chatOpenAI_0",
+ "position": {
+ "x": 335.7621848973805,
+ "y": -651.7411273245009
+ },
+ "type": "customNode",
+ "data": {
+ "id": "chatOpenAI_0",
+ "label": "ChatOpenAI",
+ "version": 1,
+ "name": "chatOpenAI",
+ "type": "ChatOpenAI",
+ "baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
+ "category": "Chat Models",
+ "description": "Wrapper around OpenAI large language models that use the Chat endpoint",
+ "inputParams": [
+ {
+ "label": "Connect Credential",
+ "name": "credential",
+ "type": "credential",
+ "credentialNames": ["openAIApi"],
+ "id": "chatOpenAI_0-input-credential-credential"
+ },
+ {
+ "label": "Model Name",
+ "name": "modelName",
+ "type": "options",
+ "options": [
+ {
+ "label": "gpt-4",
+ "name": "gpt-4"
+ },
+ {
+ "label": "gpt-4-0613",
+ "name": "gpt-4-0613"
+ },
+ {
+ "label": "gpt-4-32k",
+ "name": "gpt-4-32k"
+ },
+ {
+ "label": "gpt-4-32k-0613",
+ "name": "gpt-4-32k-0613"
+ },
+ {
+ "label": "gpt-3.5-turbo",
+ "name": "gpt-3.5-turbo"
+ },
+ {
+ "label": "gpt-3.5-turbo-0613",
+ "name": "gpt-3.5-turbo-0613"
+ },
+ {
+ "label": "gpt-3.5-turbo-16k",
+ "name": "gpt-3.5-turbo-16k"
+ },
+ {
+ "label": "gpt-3.5-turbo-16k-0613",
+ "name": "gpt-3.5-turbo-16k-0613"
+ }
+ ],
+ "default": "gpt-3.5-turbo",
+ "optional": true,
+ "id": "chatOpenAI_0-input-modelName-options"
+ },
+ {
+ "label": "Temperature",
+ "name": "temperature",
+ "type": "number",
+ "step": 0.1,
+ "default": 0.9,
+ "optional": true,
+ "id": "chatOpenAI_0-input-temperature-number"
+ },
+ {
+ "label": "Max Tokens",
+ "name": "maxTokens",
+ "type": "number",
+ "step": 1,
+ "optional": true,
+ "additionalParams": true,
+ "id": "chatOpenAI_0-input-maxTokens-number"
+ },
+ {
+ "label": "Top Probability",
+ "name": "topP",
+ "type": "number",
+ "step": 0.1,
+ "optional": true,
+ "additionalParams": true,
+ "id": "chatOpenAI_0-input-topP-number"
+ },
+ {
+ "label": "Frequency Penalty",
+ "name": "frequencyPenalty",
+ "type": "number",
+ "step": 0.1,
+ "optional": true,
+ "additionalParams": true,
+ "id": "chatOpenAI_0-input-frequencyPenalty-number"
+ },
+ {
+ "label": "Presence Penalty",
+ "name": "presencePenalty",
+ "type": "number",
+ "step": 0.1,
+ "optional": true,
+ "additionalParams": true,
+ "id": "chatOpenAI_0-input-presencePenalty-number"
+ },
+ {
+ "label": "Timeout",
+ "name": "timeout",
+ "type": "number",
+ "step": 1,
+ "optional": true,
+ "additionalParams": true,
+ "id": "chatOpenAI_0-input-timeout-number"
+ },
+ {
+ "label": "BasePath",
+ "name": "basepath",
+ "type": "string",
+ "optional": true,
+ "additionalParams": true,
+ "id": "chatOpenAI_0-input-basepath-string"
+ }
+ ],
+ "inputAnchors": [],
+ "inputs": {
+ "modelName": "gpt-3.5-turbo-16k",
+ "temperature": 0.9,
+ "maxTokens": "",
+ "topP": "",
+ "frequencyPenalty": "",
+ "presencePenalty": "",
+ "timeout": "",
+ "basepath": ""
+ },
+ "outputAnchors": [
+ {
+ "id": "chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel|Runnable",
+ "name": "chatOpenAI",
+ "label": "ChatOpenAI",
+ "type": "ChatOpenAI | BaseChatModel | BaseLanguageModel | Runnable"
+ }
+ ],
+ "outputs": {},
+ "selected": false
+ },
+ "selected": false,
+ "dragging": false,
+ "positionAbsolute": {
+ "x": 335.7621848973805,
+ "y": -651.7411273245009
+ }
+ },
+ {
+ "width": 300,
+ "height": 522,
+ "id": "chatOpenAI_1",
+ "position": {
+ "x": 1765.2801848172305,
+ "y": -667.9261054149061
+ },
+ "type": "customNode",
+ "data": {
+ "id": "chatOpenAI_1",
+ "label": "ChatOpenAI",
+ "version": 1,
+ "name": "chatOpenAI",
+ "type": "ChatOpenAI",
+ "baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
+ "category": "Chat Models",
+ "description": "Wrapper around OpenAI large language models that use the Chat endpoint",
+ "inputParams": [
+ {
+ "label": "Connect Credential",
+ "name": "credential",
+ "type": "credential",
+ "credentialNames": ["openAIApi"],
+ "id": "chatOpenAI_1-input-credential-credential"
+ },
+ {
+ "label": "Model Name",
+ "name": "modelName",
+ "type": "options",
+ "options": [
+ {
+ "label": "gpt-4",
+ "name": "gpt-4"
+ },
+ {
+ "label": "gpt-4-0613",
+ "name": "gpt-4-0613"
+ },
+ {
+ "label": "gpt-4-32k",
+ "name": "gpt-4-32k"
+ },
+ {
+ "label": "gpt-4-32k-0613",
+ "name": "gpt-4-32k-0613"
+ },
+ {
+ "label": "gpt-3.5-turbo",
+ "name": "gpt-3.5-turbo"
+ },
+ {
+ "label": "gpt-3.5-turbo-0613",
+ "name": "gpt-3.5-turbo-0613"
+ },
+ {
+ "label": "gpt-3.5-turbo-16k",
+ "name": "gpt-3.5-turbo-16k"
+ },
+ {
+ "label": "gpt-3.5-turbo-16k-0613",
+ "name": "gpt-3.5-turbo-16k-0613"
+ }
+ ],
+ "default": "gpt-3.5-turbo",
+ "optional": true,
+ "id": "chatOpenAI_1-input-modelName-options"
+ },
+ {
+ "label": "Temperature",
+ "name": "temperature",
+ "type": "number",
+ "step": 0.1,
+ "default": 0.9,
+ "optional": true,
+ "id": "chatOpenAI_1-input-temperature-number"
+ },
+ {
+ "label": "Max Tokens",
+ "name": "maxTokens",
+ "type": "number",
+ "step": 1,
+ "optional": true,
+ "additionalParams": true,
+ "id": "chatOpenAI_1-input-maxTokens-number"
+ },
+ {
+ "label": "Top Probability",
+ "name": "topP",
+ "type": "number",
+ "step": 0.1,
+ "optional": true,
+ "additionalParams": true,
+ "id": "chatOpenAI_1-input-topP-number"
+ },
+ {
+ "label": "Frequency Penalty",
+ "name": "frequencyPenalty",
+ "type": "number",
+ "step": 0.1,
+ "optional": true,
+ "additionalParams": true,
+ "id": "chatOpenAI_1-input-frequencyPenalty-number"
+ },
+ {
+ "label": "Presence Penalty",
+ "name": "presencePenalty",
+ "type": "number",
+ "step": 0.1,
+ "optional": true,
+ "additionalParams": true,
+ "id": "chatOpenAI_1-input-presencePenalty-number"
+ },
+ {
+ "label": "Timeout",
+ "name": "timeout",
+ "type": "number",
+ "step": 1,
+ "optional": true,
+ "additionalParams": true,
+ "id": "chatOpenAI_1-input-timeout-number"
+ },
+ {
+ "label": "BasePath",
+ "name": "basepath",
+ "type": "string",
+ "optional": true,
+ "additionalParams": true,
+ "id": "chatOpenAI_1-input-basepath-string"
+ }
+ ],
+ "inputAnchors": [],
+ "inputs": {
+ "modelName": "gpt-3.5-turbo-16k",
+ "temperature": 0.9,
+ "maxTokens": "",
+ "topP": "",
+ "frequencyPenalty": "",
+ "presencePenalty": "",
+ "timeout": "",
+ "basepath": ""
+ },
+ "outputAnchors": [
+ {
+ "id": "chatOpenAI_1-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel|Runnable",
+ "name": "chatOpenAI",
+ "label": "ChatOpenAI",
+ "type": "ChatOpenAI | BaseChatModel | BaseLanguageModel | Runnable"
+ }
+ ],
+ "outputs": {},
+ "selected": false
+ },
+ "selected": false,
+ "dragging": false,
+ "positionAbsolute": {
+ "x": 1765.2801848172305,
+ "y": -667.9261054149061
+ }
+ },
+ {
+ "width": 300,
+ "height": 473,
+ "id": "promptTemplate_1",
+ "position": {
+ "x": 1773.720934090435,
+ "y": -116.71323227575395
+ },
+ "type": "customNode",
+ "data": {
+ "id": "promptTemplate_1",
+ "label": "Prompt Template",
+ "version": 1,
+ "name": "promptTemplate",
+ "type": "PromptTemplate",
+ "baseClasses": ["PromptTemplate", "BaseStringPromptTemplate", "BasePromptTemplate", "Runnable"],
+ "category": "Prompts",
+ "description": "Schema to represent a basic prompt for an LLM",
+ "inputParams": [
+ {
+ "label": "Template",
+ "name": "template",
+ "type": "string",
+ "rows": 4,
+ "placeholder": "What is a good name for a company that makes {product}?",
+ "id": "promptTemplate_1-input-template-string"
+ },
+ {
+ "label": "Format Prompt Values",
+ "name": "promptValues",
+ "type": "json",
+ "optional": true,
+ "acceptVariable": true,
+ "list": true,
+ "id": "promptTemplate_1-input-promptValues-json"
+ }
+ ],
+ "inputAnchors": [],
+ "inputs": {
+ "template": "Use the following pieces of context to answer the question at the end.\n\n{context}\n\nQuestion: {question}\nHelpful Answer:",
+ "promptValues": "{\"context\":\"{{vectorStoreToDocument_0.data.instance}}\",\"question\":\"{{llmChain_0.data.instance}}\"}"
+ },
+ "outputAnchors": [
+ {
+ "id": "promptTemplate_1-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate|Runnable",
+ "name": "promptTemplate",
+ "label": "PromptTemplate",
+ "type": "PromptTemplate | BaseStringPromptTemplate | BasePromptTemplate | Runnable"
+ }
+ ],
+ "outputs": {},
+ "selected": false
+ },
+ "selected": false,
+ "positionAbsolute": {
+ "x": 1773.720934090435,
+ "y": -116.71323227575395
+ },
+ "dragging": false
+ },
+ {
+ "width": 300,
+ "height": 404,
+ "id": "llmChain_0",
+ "position": {
+ "x": 756.1670091985342,
+ "y": -592.5151355056942
+ },
+ "type": "customNode",
+ "data": {
+ "id": "llmChain_0",
+ "label": "LLM Chain",
+ "version": 1,
+ "name": "llmChain",
+ "type": "LLMChain",
+ "baseClasses": ["LLMChain", "BaseChain", "Runnable"],
+ "category": "Chains",
+ "description": "Chain to run queries against LLMs",
+ "inputParams": [
+ {
+ "label": "Chain Name",
+ "name": "chainName",
+ "type": "string",
+ "placeholder": "Name Your Chain",
+ "optional": true,
+ "id": "llmChain_0-input-chainName-string"
+ }
+ ],
+ "inputAnchors": [
+ {
+ "label": "Language Model",
+ "name": "model",
+ "type": "BaseLanguageModel",
+ "id": "llmChain_0-input-model-BaseLanguageModel"
+ },
+ {
+ "label": "Prompt",
+ "name": "prompt",
+ "type": "BasePromptTemplate",
+ "id": "llmChain_0-input-prompt-BasePromptTemplate"
+ }
+ ],
+ "inputs": {
+ "model": "{{chatOpenAI_0.data.instance}}",
+ "prompt": "{{promptTemplate_0.data.instance}}",
+ "chainName": "QuestionChain"
+ },
+ "outputAnchors": [
+ {
+ "name": "output",
+ "label": "Output",
+ "type": "options",
+ "options": [
+ {
+ "id": "llmChain_0-output-llmChain-LLMChain|BaseChain|Runnable",
+ "name": "llmChain",
+ "label": "LLM Chain",
+ "type": "LLMChain | BaseChain | Runnable"
+ },
+ {
+ "id": "llmChain_0-output-outputPrediction-string|json",
+ "name": "outputPrediction",
+ "label": "Output Prediction",
+ "type": "string | json"
+ }
+ ],
+ "default": "llmChain"
+ }
+ ],
+ "outputs": {
+ "output": "outputPrediction"
+ },
+ "selected": false
+ },
+ "selected": false,
+ "positionAbsolute": {
+ "x": 756.1670091985342,
+ "y": -592.5151355056942
+ },
+ "dragging": false
+ },
+ {
+ "width": 300,
+ "height": 404,
+ "id": "llmChain_1",
+ "position": {
+ "x": 2200.1274896215496,
+ "y": -144.29167974642334
+ },
+ "type": "customNode",
+ "data": {
+ "id": "llmChain_1",
+ "label": "LLM Chain",
+ "version": 1,
+ "name": "llmChain",
+ "type": "LLMChain",
+ "baseClasses": ["LLMChain", "BaseChain", "Runnable"],
+ "category": "Chains",
+ "description": "Chain to run queries against LLMs",
+ "inputParams": [
+ {
+ "label": "Chain Name",
+ "name": "chainName",
+ "type": "string",
+ "placeholder": "Name Your Chain",
+ "optional": true,
+ "id": "llmChain_1-input-chainName-string"
+ }
+ ],
+ "inputAnchors": [
+ {
+ "label": "Language Model",
+ "name": "model",
+ "type": "BaseLanguageModel",
+ "id": "llmChain_1-input-model-BaseLanguageModel"
+ },
+ {
+ "label": "Prompt",
+ "name": "prompt",
+ "type": "BasePromptTemplate",
+ "id": "llmChain_1-input-prompt-BasePromptTemplate"
+ }
+ ],
+ "inputs": {
+ "model": "{{chatOpenAI_1.data.instance}}",
+ "prompt": "{{promptTemplate_1.data.instance}}",
+ "chainName": ""
+ },
+ "outputAnchors": [
+ {
+ "name": "output",
+ "label": "Output",
+ "type": "options",
+ "options": [
+ {
+ "id": "llmChain_1-output-llmChain-LLMChain|BaseChain|Runnable",
+ "name": "llmChain",
+ "label": "LLM Chain",
+ "type": "LLMChain | BaseChain | Runnable"
+ },
+ {
+ "id": "llmChain_1-output-outputPrediction-string|json",
+ "name": "outputPrediction",
+ "label": "Output Prediction",
+ "type": "string | json"
+ }
+ ],
+ "default": "llmChain"
+ }
+ ],
+ "outputs": {
+ "output": "llmChain"
+ },
+ "selected": false
+ },
+ "selected": false,
+ "positionAbsolute": {
+ "x": 2200.1274896215496,
+ "y": -144.29167974642334
+ },
+ "dragging": false
+ },
+ {
+ "width": 300,
+ "height": 353,
+ "id": "vectorStoreToDocument_0",
+ "position": {
+ "x": 1407.7038120189868,
+ "y": -26.16468811205081
+ },
+ "type": "customNode",
+ "data": {
+ "id": "vectorStoreToDocument_0",
+ "label": "VectorStore To Document",
+ "version": 1,
+ "name": "vectorStoreToDocument",
+ "type": "Document",
+ "baseClasses": ["Document"],
+ "category": "Document Loaders",
+ "description": "Search documents with scores from vector store",
+ "inputParams": [
+ {
+ "label": "Minimum Score (%)",
+ "name": "minScore",
+ "type": "number",
+ "optional": true,
+ "placeholder": "75",
+ "step": 1,
+ "description": "Minumum score for embeddings documents to be included",
+ "id": "vectorStoreToDocument_0-input-minScore-number"
+ }
+ ],
+ "inputAnchors": [
+ {
+ "label": "Vector Store",
+ "name": "vectorStore",
+ "type": "VectorStore",
+ "id": "vectorStoreToDocument_0-input-vectorStore-VectorStore"
+ }
+ ],
+ "inputs": {
+ "vectorStore": "{{pineconeExistingIndex_0.data.instance}}",
+ "minScore": ""
+ },
+ "outputAnchors": [
+ {
+ "name": "output",
+ "label": "Output",
+ "type": "options",
+ "options": [
+ {
+ "id": "vectorStoreToDocument_0-output-document-Document",
+ "name": "document",
+ "label": "Document",
+ "type": "Document"
+ },
+ {
+ "id": "vectorStoreToDocument_0-output-text-string|json",
+ "name": "text",
+ "label": "Text",
+ "type": "string | json"
+ }
+ ],
+ "default": "document"
+ }
+ ],
+ "outputs": {
+ "output": "text"
+ },
+ "selected": false
+ },
+ "selected": false,
+ "positionAbsolute": {
+ "x": 1407.7038120189868,
+ "y": -26.16468811205081
+ },
+ "dragging": false
+ }
+ ],
+ "edges": [
+ {
+ "source": "openAIEmbeddings_0",
+ "sourceHandle": "openAIEmbeddings_0-output-openAIEmbeddings-OpenAIEmbeddings|Embeddings",
+ "target": "pineconeExistingIndex_0",
+ "targetHandle": "pineconeExistingIndex_0-input-embeddings-Embeddings",
+ "type": "buttonedge",
+ "id": "openAIEmbeddings_0-openAIEmbeddings_0-output-openAIEmbeddings-OpenAIEmbeddings|Embeddings-pineconeExistingIndex_0-pineconeExistingIndex_0-input-embeddings-Embeddings",
+ "data": {
+ "label": ""
+ }
+ },
+ {
+ "source": "pineconeExistingIndex_0",
+ "sourceHandle": "pineconeExistingIndex_0-output-vectorStore-Pinecone|VectorStore",
+ "target": "vectorStoreToDocument_0",
+ "targetHandle": "vectorStoreToDocument_0-input-vectorStore-VectorStore",
+ "type": "buttonedge",
+ "id": "pineconeExistingIndex_0-pineconeExistingIndex_0-output-vectorStore-Pinecone|VectorStore-vectorStoreToDocument_0-vectorStoreToDocument_0-input-vectorStore-VectorStore",
+ "data": {
+ "label": ""
+ }
+ },
+ {
+ "source": "vectorStoreToDocument_0",
+ "sourceHandle": "vectorStoreToDocument_0-output-text-string|json",
+ "target": "promptTemplate_1",
+ "targetHandle": "promptTemplate_1-input-promptValues-json",
+ "type": "buttonedge",
+ "id": "vectorStoreToDocument_0-vectorStoreToDocument_0-output-text-string|json-promptTemplate_1-promptTemplate_1-input-promptValues-json",
+ "data": {
+ "label": ""
+ }
+ },
+ {
+ "source": "chatOpenAI_0",
+ "sourceHandle": "chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel|Runnable",
+ "target": "llmChain_0",
+ "targetHandle": "llmChain_0-input-model-BaseLanguageModel",
+ "type": "buttonedge",
+ "id": "chatOpenAI_0-chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel|Runnable-llmChain_0-llmChain_0-input-model-BaseLanguageModel",
+ "data": {
+ "label": ""
+ }
+ },
+ {
+ "source": "promptTemplate_0",
+ "sourceHandle": "promptTemplate_0-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate|Runnable",
+ "target": "llmChain_0",
+ "targetHandle": "llmChain_0-input-prompt-BasePromptTemplate",
+ "type": "buttonedge",
+ "id": "promptTemplate_0-promptTemplate_0-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate|Runnable-llmChain_0-llmChain_0-input-prompt-BasePromptTemplate",
+ "data": {
+ "label": ""
+ }
+ },
+ {
+ "source": "llmChain_0",
+ "sourceHandle": "llmChain_0-output-outputPrediction-string|json",
+ "target": "promptTemplate_1",
+ "targetHandle": "promptTemplate_1-input-promptValues-json",
+ "type": "buttonedge",
+ "id": "llmChain_0-llmChain_0-output-outputPrediction-string|json-promptTemplate_1-promptTemplate_1-input-promptValues-json",
+ "data": {
+ "label": ""
+ }
+ },
+ {
+ "source": "chatOpenAI_1",
+ "sourceHandle": "chatOpenAI_1-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel|Runnable",
+ "target": "llmChain_1",
+ "targetHandle": "llmChain_1-input-model-BaseLanguageModel",
+ "type": "buttonedge",
+ "id": "chatOpenAI_1-chatOpenAI_1-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel|Runnable-llmChain_1-llmChain_1-input-model-BaseLanguageModel",
+ "data": {
+ "label": ""
+ }
+ },
+ {
+ "source": "promptTemplate_1",
+ "sourceHandle": "promptTemplate_1-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate|Runnable",
+ "target": "llmChain_1",
+ "targetHandle": "llmChain_1-input-prompt-BasePromptTemplate",
+ "type": "buttonedge",
+ "id": "promptTemplate_1-promptTemplate_1-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate|Runnable-llmChain_1-llmChain_1-input-prompt-BasePromptTemplate",
+ "data": {
+ "label": ""
+ }
+ }
+ ]
+}
diff --git a/packages/server/marketplaces/chatflows/Vectara LLM Chain Upload.json b/packages/server/marketplaces/chatflows/Vectara LLM Chain Upload.json
index 0758ec9a..784ad240 100644
--- a/packages/server/marketplaces/chatflows/Vectara LLM Chain Upload.json
+++ b/packages/server/marketplaces/chatflows/Vectara LLM Chain Upload.json
@@ -1,186 +1,11 @@
{
"description": "A simple LLM chain that uses Vectara to enable conversations with uploaded documents",
"nodes": [
- {
- "width": 300,
- "height": 408,
- "id": "vectaraUpsert_0",
- "position": { "x": 438, "y": 214 },
- "type": "customNode",
- "data": {
- "id": "vectaraUpsert_0",
- "label": "Vectara Upsert Document",
- "version": 1,
- "name": "vectaraUpsert",
- "type": "Vectara",
- "baseClasses": ["Vectara", "VectorStoreRetriever", "BaseRetriever"],
- "category": "Vector Stores",
- "description": "Upsert documents to Vectara",
- "inputParams": [
- {
- "label": "Connect Credential",
- "name": "credential",
- "type": "credential",
- "credentialNames": ["vectaraApi"],
- "id": "vectaraUpsert_0-input-credential-credential"
- },
- {
- "label": "Filter",
- "name": "filter",
- "type": "json",
- "additionalParams": true,
- "optional": true,
- "id": "vectaraUpsert_0-input-filter-json"
- },
- {
- "label": "Lambda",
- "name": "lambda",
- "type": "number",
- "additionalParams": true,
- "optional": true,
- "id": "vectaraUpsert_0-input-lambda-number"
- },
- {
- "label": "Top K",
- "name": "topK",
- "description": "Number of top results to fetch. Defaults to 4",
- "placeholder": "4",
- "type": "number",
- "additionalParams": true,
- "optional": true,
- "id": "vectaraUpsert_0-input-topK-number"
- }
- ],
- "inputAnchors": [
- {
- "label": "Document",
- "name": "document",
- "type": "Document",
- "list": true,
- "id": "vectaraUpsert_0-input-document-Document"
- }
- ],
- "inputs": {
- "document": ["{{pdfFile_0.data.instance}}"],
- "filter": "",
- "lambda": "",
- "topK": ""
- },
- "outputAnchors": [
- {
- "name": "output",
- "label": "Output",
- "type": "options",
- "options": [
- {
- "id": "vectaraUpsert_0-output-retriever-Vectara|VectorStoreRetriever|BaseRetriever",
- "name": "retriever",
- "label": "Vectara Retriever",
- "type": "Vectara | VectorStoreRetriever | BaseRetriever"
- },
- {
- "id": "vectaraUpsert_0-output-vectorStore-Vectara|VectorStore",
- "name": "vectorStore",
- "label": "Vectara Vector Store",
- "type": "Vectara | VectorStore"
- }
- ],
- "default": "retriever"
- }
- ],
- "outputs": { "output": "retriever" },
- "selected": false
- },
- "selected": false,
- "dragging": false,
- "positionAbsolute": { "x": 438, "y": 214 }
- },
- {
- "width": 300,
- "height": 509,
- "id": "pdfFile_0",
- "position": { "x": 68.3013317598369, "y": 199.60454731299677 },
- "type": "customNode",
- "data": {
- "id": "pdfFile_0",
- "label": "Pdf File",
- "version": 1,
- "name": "pdfFile",
- "type": "Document",
- "baseClasses": ["Document"],
- "category": "Document Loaders",
- "description": "Load data from PDF files",
- "inputParams": [
- {
- "label": "Pdf File",
- "name": "pdfFile",
- "type": "file",
- "fileType": ".pdf",
- "id": "pdfFile_0-input-pdfFile-file"
- },
- {
- "label": "Usage",
- "name": "usage",
- "type": "options",
- "options": [
- { "label": "One document per page", "name": "perPage" },
- { "label": "One document per file", "name": "perFile" }
- ],
- "default": "perPage",
- "id": "pdfFile_0-input-usage-options"
- },
- {
- "label": "Use Legacy Build",
- "name": "legacyBuild",
- "type": "boolean",
- "optional": true,
- "additionalParams": true,
- "id": "pdfFile_0-input-legacyBuild-boolean"
- },
- {
- "label": "Metadata",
- "name": "metadata",
- "type": "json",
- "optional": true,
- "additionalParams": true,
- "id": "pdfFile_0-input-metadata-json"
- }
- ],
- "inputAnchors": [
- {
- "label": "Text Splitter",
- "name": "textSplitter",
- "type": "TextSplitter",
- "optional": true,
- "id": "pdfFile_0-input-textSplitter-TextSplitter"
- }
- ],
- "inputs": {
- "textSplitter": "",
- "usage": "perPage",
- "legacyBuild": "",
- "metadata": ""
- },
- "outputAnchors": [
- {
- "id": "pdfFile_0-output-pdfFile-Document",
- "name": "pdfFile",
- "label": "Document",
- "type": "Document"
- }
- ],
- "outputs": {},
- "selected": false
- },
- "selected": false,
- "positionAbsolute": { "x": 68.3013317598369, "y": 199.60454731299677 },
- "dragging": false
- },
{
"width": 300,
"height": 525,
"id": "chatOpenAI_0",
- "position": { "x": 804.3889791707068, "y": 195.11620799951592 },
+ "position": { "x": 514.1088940275924, "y": 199.574479681537 },
"type": "customNode",
"data": {
"id": "chatOpenAI_0",
@@ -211,10 +36,7 @@
{ "label": "gpt-3.5-turbo", "name": "gpt-3.5-turbo" },
{ "label": "gpt-3.5-turbo-0613", "name": "gpt-3.5-turbo-0613" },
{ "label": "gpt-3.5-turbo-16k", "name": "gpt-3.5-turbo-16k" },
- {
- "label": "gpt-3.5-turbo-16k-0613",
- "name": "gpt-3.5-turbo-16k-0613"
- }
+ { "label": "gpt-3.5-turbo-16k-0613", "name": "gpt-3.5-turbo-16k-0613" }
],
"default": "gpt-3.5-turbo",
"optional": true,
@@ -286,7 +108,7 @@
"inputAnchors": [],
"inputs": {
"modelName": "gpt-3.5-turbo",
- "temperature": "0.2",
+ "temperature": "0.5",
"maxTokens": "",
"topP": "",
"frequencyPenalty": "",
@@ -306,14 +128,14 @@
"selected": false
},
"selected": false,
- "positionAbsolute": { "x": 804.3889791707068, "y": 195.11620799951592 },
+ "positionAbsolute": { "x": 514.1088940275924, "y": 199.574479681537 },
"dragging": false
},
{
"width": 300,
"height": 481,
"id": "conversationalRetrievalQAChain_0",
- "position": { "x": 1160.4877473512795, "y": 259.2799138505109 },
+ "position": { "x": 900.4793407261002, "y": 205.9476004518217 },
"type": "customNode",
"data": {
"id": "conversationalRetrievalQAChain_0",
@@ -410,11 +232,200 @@
"selected": false
},
"selected": false,
- "positionAbsolute": { "x": 1160.4877473512795, "y": 259.2799138505109 },
+ "positionAbsolute": { "x": 900.4793407261002, "y": 205.9476004518217 },
"dragging": false
+ },
+ {
+ "width": 300,
+ "height": 509,
+ "id": "pdfFile_0",
+ "position": { "x": -210.44158723479913, "y": 236.6627524951051 },
+ "type": "customNode",
+ "data": {
+ "id": "pdfFile_0",
+ "label": "Pdf File",
+ "version": 1,
+ "name": "pdfFile",
+ "type": "Document",
+ "baseClasses": ["Document"],
+ "category": "Document Loaders",
+ "description": "Load data from PDF files",
+ "inputParams": [
+ { "label": "Pdf File", "name": "pdfFile", "type": "file", "fileType": ".pdf", "id": "pdfFile_0-input-pdfFile-file" },
+ {
+ "label": "Usage",
+ "name": "usage",
+ "type": "options",
+ "options": [
+ { "label": "One document per page", "name": "perPage" },
+ { "label": "One document per file", "name": "perFile" }
+ ],
+ "default": "perPage",
+ "id": "pdfFile_0-input-usage-options"
+ },
+ {
+ "label": "Use Legacy Build",
+ "name": "legacyBuild",
+ "type": "boolean",
+ "optional": true,
+ "additionalParams": true,
+ "id": "pdfFile_0-input-legacyBuild-boolean"
+ },
+ {
+ "label": "Metadata",
+ "name": "metadata",
+ "type": "json",
+ "optional": true,
+ "additionalParams": true,
+ "id": "pdfFile_0-input-metadata-json"
+ }
+ ],
+ "inputAnchors": [
+ {
+ "label": "Text Splitter",
+ "name": "textSplitter",
+ "type": "TextSplitter",
+ "optional": true,
+ "id": "pdfFile_0-input-textSplitter-TextSplitter"
+ }
+ ],
+ "inputs": { "textSplitter": "", "usage": "perPage", "legacyBuild": "", "metadata": "" },
+ "outputAnchors": [
+ { "id": "pdfFile_0-output-pdfFile-Document", "name": "pdfFile", "label": "Document", "type": "Document" }
+ ],
+ "outputs": {},
+ "selected": false
+ },
+ "selected": false,
+ "positionAbsolute": { "x": -210.44158723479913, "y": 236.6627524951051 },
+ "dragging": false
+ },
+ {
+ "width": 300,
+ "height": 408,
+ "id": "vectaraUpsert_0",
+ "position": { "x": 172.06946164914868, "y": 373.11406233089934 },
+ "type": "customNode",
+ "data": {
+ "id": "vectaraUpsert_0",
+ "label": "Vectara Upsert Document",
+ "version": 1,
+ "name": "vectaraUpsert",
+ "type": "Vectara",
+ "baseClasses": ["Vectara", "VectorStoreRetriever", "BaseRetriever"],
+ "category": "Vector Stores",
+ "description": "Upsert documents to Vectara",
+ "inputParams": [
+ {
+ "label": "Connect Credential",
+ "name": "credential",
+ "type": "credential",
+ "credentialNames": ["vectaraApi"],
+ "id": "vectaraUpsert_0-input-credential-credential"
+ },
+ {
+ "label": "Vectara Metadata Filter",
+ "name": "filter",
+ "description": "Filter to apply to Vectara metadata. Refer to the documentation on how to use Vectara filters with Flowise.",
+ "type": "string",
+ "additionalParams": true,
+ "optional": true,
+ "id": "vectaraUpsert_0-input-filter-string"
+ },
+ {
+ "label": "Sentences Before",
+ "name": "sentencesBefore",
+ "description": "Number of sentences to fetch before the matched sentence. Defaults to 2.",
+ "type": "number",
+ "additionalParams": true,
+ "optional": true,
+ "id": "vectaraUpsert_0-input-sentencesBefore-number"
+ },
+ {
+ "label": "Sentences After",
+ "name": "sentencesAfter",
+ "description": "Number of sentences to fetch after the matched sentence. Defaults to 2.",
+ "type": "number",
+ "additionalParams": true,
+ "optional": true,
+ "id": "vectaraUpsert_0-input-sentencesAfter-number"
+ },
+ {
+ "label": "Lambda",
+ "name": "lambda",
+ "description": "Improves retrieval accuracy by adjusting the balance (from 0 to 1) between neural search and keyword-based search factors.",
+ "type": "number",
+ "additionalParams": true,
+ "optional": true,
+ "id": "vectaraUpsert_0-input-lambda-number"
+ },
+ {
+ "label": "Top K",
+ "name": "topK",
+ "description": "Number of top results to fetch. Defaults to 4",
+ "placeholder": "4",
+ "type": "number",
+ "additionalParams": true,
+ "optional": true,
+ "id": "vectaraUpsert_0-input-topK-number"
+ }
+ ],
+ "inputAnchors": [
+ {
+ "label": "Document",
+ "name": "document",
+ "type": "Document",
+ "list": true,
+ "id": "vectaraUpsert_0-input-document-Document"
+ }
+ ],
+ "inputs": {
+ "document": ["{{pdfFile_0.data.instance}}"],
+ "filter": "",
+ "sentencesBefore": "",
+ "sentencesAfter": "",
+ "lambda": "",
+ "topK": ""
+ },
+ "outputAnchors": [
+ {
+ "name": "output",
+ "label": "Output",
+ "type": "options",
+ "options": [
+ {
+ "id": "vectaraUpsert_0-output-retriever-Vectara|VectorStoreRetriever|BaseRetriever",
+ "name": "retriever",
+ "label": "Vectara Retriever",
+ "type": "Vectara | VectorStoreRetriever | BaseRetriever"
+ },
+ {
+ "id": "vectaraUpsert_0-output-vectorStore-Vectara|VectorStore",
+ "name": "vectorStore",
+ "label": "Vectara Vector Store",
+ "type": "Vectara | VectorStore"
+ }
+ ],
+ "default": "retriever"
+ }
+ ],
+ "outputs": { "output": "retriever" },
+ "selected": false
+ },
+ "positionAbsolute": { "x": 172.06946164914868, "y": 373.11406233089934 },
+ "selected": false
}
],
"edges": [
+ {
+ "source": "chatOpenAI_0",
+ "sourceHandle": "chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel",
+ "target": "conversationalRetrievalQAChain_0",
+ "targetHandle": "conversationalRetrievalQAChain_0-input-model-BaseLanguageModel",
+ "type": "buttonedge",
+ "id": "chatOpenAI_0-chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel-conversationalRetrievalQAChain_0-conversationalRetrievalQAChain_0-input-model-BaseLanguageModel",
+ "data": { "label": "" }
+ },
{
"source": "pdfFile_0",
"sourceHandle": "pdfFile_0-output-pdfFile-Document",
@@ -432,15 +443,6 @@
"type": "buttonedge",
"id": "vectaraUpsert_0-vectaraUpsert_0-output-retriever-Vectara|VectorStoreRetriever|BaseRetriever-conversationalRetrievalQAChain_0-conversationalRetrievalQAChain_0-input-vectorStoreRetriever-BaseRetriever",
"data": { "label": "" }
- },
- {
- "source": "chatOpenAI_0",
- "sourceHandle": "chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel",
- "target": "conversationalRetrievalQAChain_0",
- "targetHandle": "conversationalRetrievalQAChain_0-input-model-BaseLanguageModel",
- "type": "buttonedge",
- "id": "chatOpenAI_0-chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel-conversationalRetrievalQAChain_0-conversationalRetrievalQAChain_0-input-model-BaseLanguageModel",
- "data": { "label": "" }
}
]
}
diff --git a/packages/server/marketplaces/chatflows/WebPage QnA.json b/packages/server/marketplaces/chatflows/WebPage QnA.json
index 8197c20a..812f0bd5 100644
--- a/packages/server/marketplaces/chatflows/WebPage QnA.json
+++ b/packages/server/marketplaces/chatflows/WebPage QnA.json
@@ -466,7 +466,7 @@
"optional": true,
"additionalParams": true,
"description": "Only used when \"Get Relative Links Method\" is selected. Set 0 to retrieve all relative links, default limit is 10.",
- "warning": "Retreiving all links might take long time, and all links will be upserted again if the flow's state changed (eg: different URL, chunk size, etc)",
+ "warning": "Retrieving all links might take long time, and all links will be upserted again if the flow's state changed (eg: different URL, chunk size, etc)",
"id": "cheerioWebScraper_0-input-limit-number"
},
{
diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts
index b7289149..23b5bdf4 100644
--- a/packages/server/src/index.ts
+++ b/packages/server/src/index.ts
@@ -36,7 +36,6 @@ import {
isSameOverrideConfig,
replaceAllAPIKeys,
isFlowValidForStream,
- isVectorStoreFaiss,
databaseEntities,
getApiKey,
transformToCredentialEntity,
@@ -811,11 +810,17 @@ export class App {
}
}
+ /*** Get chatflows and prepare data ***/
+ const flowData = chatflow.flowData
+ const parsedFlowData: IReactFlowObject = JSON.parse(flowData)
+ const nodes = parsedFlowData.nodes
+ const edges = parsedFlowData.edges
+
/* Reuse the flow without having to rebuild (to avoid duplicated upsert, recomputation) when all these conditions met:
* - Node Data already exists in pool
* - Still in sync (i.e the flow has not been modified since)
* - Existing overrideConfig and new overrideConfig are the same
- * - Flow doesn't start with nodes that depend on incomingInput.question
+ * - Flow doesn't start with/contain nodes that depend on incomingInput.question
***/
const isFlowReusable = () => {
return (
@@ -826,16 +831,10 @@ export class App {
this.chatflowPool.activeChatflows[chatflowid].overrideConfig,
incomingInput.overrideConfig
) &&
- !isStartNodeDependOnInput(this.chatflowPool.activeChatflows[chatflowid].startingNodes)
+ !isStartNodeDependOnInput(this.chatflowPool.activeChatflows[chatflowid].startingNodes, nodes)
)
}
- /*** Get chatflows and prepare data ***/
- const flowData = chatflow.flowData
- const parsedFlowData: IReactFlowObject = JSON.parse(flowData)
- const nodes = parsedFlowData.nodes
- const edges = parsedFlowData.edges
-
if (isFlowReusable()) {
nodeToExecuteData = this.chatflowPool.activeChatflows[chatflowid].endingNodeData
isStreamValid = isFlowValidForStream(nodes, nodeToExecuteData)
@@ -884,6 +883,7 @@ export class App {
depthQueue,
this.nodesPool.componentNodes,
incomingInput.question,
+ incomingInput.history,
chatId,
this.AppDataSource,
incomingInput?.overrideConfig
@@ -894,7 +894,12 @@ export class App {
if (incomingInput.overrideConfig)
nodeToExecute.data = replaceInputsWithConfig(nodeToExecute.data, incomingInput.overrideConfig)
- const reactFlowNodeData: INodeData = resolveVariables(nodeToExecute.data, reactFlowNodes, incomingInput.question)
+ const reactFlowNodeData: INodeData = resolveVariables(
+ nodeToExecute.data,
+ reactFlowNodes,
+ incomingInput.question,
+ incomingInput.history
+ )
nodeToExecuteData = reactFlowNodeData
const startingNodes = nodes.filter((nd) => startingNodeIds.includes(nd.id))
@@ -905,7 +910,6 @@ export class App {
const nodeModule = await import(nodeInstanceFilePath)
const nodeInstance = new nodeModule.nodeClass()
- isStreamValid = isStreamValid && !isVectorStoreFaiss(nodeToExecuteData)
logger.debug(`[server]: Running ${nodeToExecuteData.label} (${nodeToExecuteData.id})`)
if (nodeToExecuteData.instance) checkMemorySessionId(nodeToExecuteData.instance, chatId)
diff --git a/packages/server/src/utils/index.ts b/packages/server/src/utils/index.ts
index cf4a9c3f..9be26987 100644
--- a/packages/server/src/utils/index.ts
+++ b/packages/server/src/utils/index.ts
@@ -18,8 +18,15 @@ import {
IComponentCredentials,
ICredentialReqBody
} from '../Interface'
-import { cloneDeep, get, omit, merge, isEqual } from 'lodash'
-import { ICommonObject, getInputVariables, IDatabaseEntity, handleEscapeCharacters } from 'flowise-components'
+import { cloneDeep, get, isEqual } from 'lodash'
+import {
+ ICommonObject,
+ getInputVariables,
+ IDatabaseEntity,
+ handleEscapeCharacters,
+ IMessage,
+ convertChatHistoryToText
+} from 'flowise-components'
import { scryptSync, randomBytes, timingSafeEqual } from 'crypto'
import { lib, PBKDF2, AES, enc } from 'crypto-js'
@@ -30,6 +37,7 @@ import { Tool } from '../entity/Tool'
import { DataSource } from 'typeorm'
const QUESTION_VAR_PREFIX = 'question'
+const CHAT_HISTORY_VAR_PREFIX = 'chat_history'
const REDACTED_CREDENTIAL_VALUE = '_FLOWISE_BLANK_07167752-1a71-43b1-bf8f-4f32252165db'
export const databaseEntities: IDatabaseEntity = { ChatFlow: ChatFlow, ChatMessage: ChatMessage, Tool: Tool, Credential: Credential }
@@ -199,6 +207,7 @@ export const buildLangchain = async (
depthQueue: IDepthQueue,
componentNodes: IComponentNodes,
question: string,
+ chatHistory: IMessage[],
chatId: string,
appDataSource: DataSource,
overrideConfig?: ICommonObject
@@ -231,7 +240,7 @@ export const buildLangchain = async (
let flowNodeData = cloneDeep(reactFlowNode.data)
if (overrideConfig) flowNodeData = replaceInputsWithConfig(flowNodeData, overrideConfig)
- const reactFlowNodeData: INodeData = resolveVariables(flowNodeData, flowNodes, question)
+ const reactFlowNodeData: INodeData = resolveVariables(flowNodeData, flowNodes, question, chatHistory)
logger.debug(`[server]: Initializing ${reactFlowNode.data.label} (${reactFlowNode.data.id})`)
flowNodes[nodeIndex].data.instance = await newNodeInstance.init(reactFlowNodeData, question, {
@@ -315,7 +324,13 @@ export const clearSessionMemory = async (
* @param {boolean} isAcceptVariable
* @returns {string}
*/
-export const getVariableValue = (paramValue: string, reactFlowNodes: IReactFlowNode[], question: string, isAcceptVariable = false) => {
+export const getVariableValue = (
+ paramValue: string,
+ reactFlowNodes: IReactFlowNode[],
+ question: string,
+ chatHistory: IMessage[],
+ isAcceptVariable = false
+) => {
let returnVal = paramValue
const variableStack = []
const variableDict = {} as IVariableDict
@@ -345,6 +360,10 @@ export const getVariableValue = (paramValue: string, reactFlowNodes: IReactFlowN
variableDict[`{{${variableFullPath}}}`] = handleEscapeCharacters(question, false)
}
+ if (isAcceptVariable && variableFullPath === CHAT_HISTORY_VAR_PREFIX) {
+ variableDict[`{{${variableFullPath}}}`] = handleEscapeCharacters(convertChatHistoryToText(chatHistory), false)
+ }
+
// Split by first occurrence of '.' to get just nodeId
const [variableNodeId, _] = variableFullPath.split('.')
const executedNode = reactFlowNodes.find((nd) => nd.id === variableNodeId)
@@ -374,25 +393,6 @@ export const getVariableValue = (paramValue: string, reactFlowNodes: IReactFlowN
return returnVal
}
-/**
- * Temporarily disable streaming if vectorStore is Faiss
- * @param {INodeData} flowNodeData
- * @returns {boolean}
- */
-export const isVectorStoreFaiss = (flowNodeData: INodeData) => {
- if (flowNodeData.inputs && flowNodeData.inputs.vectorStoreRetriever) {
- const vectorStoreRetriever = flowNodeData.inputs.vectorStoreRetriever
- if (typeof vectorStoreRetriever === 'string' && vectorStoreRetriever.includes('faiss')) return true
- if (
- typeof vectorStoreRetriever === 'object' &&
- vectorStoreRetriever.vectorStore &&
- vectorStoreRetriever.vectorStore.constructor.name === 'FaissStore'
- )
- return true
- }
- return false
-}
-
/**
* Loop through each inputs and resolve variable if neccessary
* @param {INodeData} reactFlowNodeData
@@ -400,13 +400,13 @@ export const isVectorStoreFaiss = (flowNodeData: INodeData) => {
* @param {string} question
* @returns {INodeData}
*/
-export const resolveVariables = (reactFlowNodeData: INodeData, reactFlowNodes: IReactFlowNode[], question: string): INodeData => {
+export const resolveVariables = (
+ reactFlowNodeData: INodeData,
+ reactFlowNodes: IReactFlowNode[],
+ question: string,
+ chatHistory: IMessage[]
+): INodeData => {
let flowNodeData = cloneDeep(reactFlowNodeData)
- if (reactFlowNodeData.instance && isVectorStoreFaiss(reactFlowNodeData)) {
- // omit and merge because cloneDeep of instance gives "Illegal invocation" Exception
- const flowNodeDataWithoutInstance = cloneDeep(omit(reactFlowNodeData, ['instance']))
- flowNodeData = merge(flowNodeDataWithoutInstance, { instance: reactFlowNodeData.instance })
- }
const types = 'inputs'
const getParamValues = (paramsObj: ICommonObject) => {
@@ -415,13 +415,13 @@ export const resolveVariables = (reactFlowNodeData: INodeData, reactFlowNodes: I
if (Array.isArray(paramValue)) {
const resolvedInstances = []
for (const param of paramValue) {
- const resolvedInstance = getVariableValue(param, reactFlowNodes, question)
+ const resolvedInstance = getVariableValue(param, reactFlowNodes, question, chatHistory)
resolvedInstances.push(resolvedInstance)
}
paramsObj[key] = resolvedInstances
} else {
const isAcceptVariable = reactFlowNodeData.inputParams.find((param) => param.name === key)?.acceptVariable ?? false
- const resolvedInstance = getVariableValue(paramValue, reactFlowNodes, question, isAcceptVariable)
+ const resolvedInstance = getVariableValue(paramValue, reactFlowNodes, question, chatHistory, isAcceptVariable)
paramsObj[key] = resolvedInstance
}
}
@@ -474,13 +474,17 @@ export const replaceInputsWithConfig = (flowNodeData: INodeData, overrideConfig:
* @param {IReactFlowNode[]} startingNodes
* @returns {boolean}
*/
-export const isStartNodeDependOnInput = (startingNodes: IReactFlowNode[]): boolean => {
+export const isStartNodeDependOnInput = (startingNodes: IReactFlowNode[], nodes: IReactFlowNode[]): boolean => {
for (const node of startingNodes) {
for (const inputName in node.data.inputs) {
const inputVariables = getInputVariables(node.data.inputs[inputName])
if (inputVariables.length > 0) return true
}
}
+ const whitelistNodeNames = ['vectorStoreToDocument']
+ for (const node of nodes) {
+ if (whitelistNodeNames.includes(node.data.name)) return true
+ }
return false
}
@@ -791,7 +795,7 @@ export const isFlowValidForStream = (reactFlowNodes: IReactFlowNode[], endingNod
isValidChainOrAgent = whitelistAgents.includes(endingNodeData.name)
}
- return isChatOrLLMsExist && isValidChainOrAgent && !isVectorStoreFaiss(endingNodeData)
+ return isChatOrLLMsExist && isValidChainOrAgent
}
/**
diff --git a/packages/ui/README-ZH.md b/packages/ui/README-ZH.md
index 5d33c07f..c6307935 100644
--- a/packages/ui/README-ZH.md
+++ b/packages/ui/README-ZH.md
@@ -16,4 +16,4 @@ npm i flowise-ui
## 许可证
-本仓库中的源代码在[MIT许可证](https://github.com/FlowiseAI/Flowise/blob/master/LICENSE.md)下提供。
\ No newline at end of file
+本仓库中的源代码在[MIT许可证](https://github.com/FlowiseAI/Flowise/blob/master/LICENSE.md)下提供。
diff --git a/packages/ui/src/assets/images/chathistory.png b/packages/ui/src/assets/images/chathistory.png
new file mode 100644
index 00000000..52f496a8
Binary files /dev/null and b/packages/ui/src/assets/images/chathistory.png differ
diff --git a/packages/ui/src/ui-component/dialog/NodeInfoDialog.js b/packages/ui/src/ui-component/dialog/NodeInfoDialog.js
index 054353fc..74c45a1a 100644
--- a/packages/ui/src/ui-component/dialog/NodeInfoDialog.js
+++ b/packages/ui/src/ui-component/dialog/NodeInfoDialog.js
@@ -123,7 +123,7 @@ const NodeInfoDialog = ({ show, dialogProps, onCancel }) => {
)}
{getNodeConfigApi.data && getNodeConfigApi.data.length > 0 && (
-
+
)}
diff --git a/packages/ui/src/ui-component/json/SelectVariable.js b/packages/ui/src/ui-component/json/SelectVariable.js
index 1b891ed1..7a482bae 100644
--- a/packages/ui/src/ui-component/json/SelectVariable.js
+++ b/packages/ui/src/ui-component/json/SelectVariable.js
@@ -2,14 +2,15 @@ import { useSelector } from 'react-redux'
import PropTypes from 'prop-types'
import { Box, List, ListItemButton, ListItem, ListItemAvatar, ListItemText, Typography, Stack } from '@mui/material'
import PerfectScrollbar from 'react-perfect-scrollbar'
-
+import robotPNG from 'assets/images/robot.png'
+import chatPNG from 'assets/images/chathistory.png'
import { baseURL } from 'store/constant'
const SelectVariable = ({ availableNodesForVariable, disabled = false, onSelectAndReturnVal }) => {
const customization = useSelector((state) => state.customization)
- const onSelectOutputResponseClick = (node, isUserQuestion = false) => {
- let variablePath = isUserQuestion ? `question` : `${node.id}.data.instance`
+ const onSelectOutputResponseClick = (node, prefix) => {
+ let variablePath = node ? `${node.id}.data.instance` : prefix
const newInput = `{{${variablePath}}}`
onSelectAndReturnVal(newInput)
}
@@ -32,7 +33,7 @@ const SelectVariable = ({ availableNodesForVariable, disabled = false, onSelectA
mb: 1
}}
disabled={disabled}
- onClick={() => onSelectOutputResponseClick(null, true)}
+ onClick={() => onSelectOutputResponseClick(null, 'question')}
>
@@ -52,13 +53,52 @@ const SelectVariable = ({ availableNodesForVariable, disabled = false, onSelectA
objectFit: 'contain'
}}
alt='AI'
- src='https://raw.githubusercontent.com/zahidkhawaja/langchain-chat-nextjs/main/public/parroticon.png'
+ src={robotPNG}
/>
+ onSelectOutputResponseClick(null, 'chat_history')}
+ >
+
+
+
+

+
+
+
+
+
{availableNodesForVariable &&
availableNodesForVariable.length > 0 &&
availableNodesForVariable.map((node, index) => {
diff --git a/packages/ui/src/views/chatmessage/ChatMessage.js b/packages/ui/src/views/chatmessage/ChatMessage.js
index 98eef72a..506f02da 100644
--- a/packages/ui/src/views/chatmessage/ChatMessage.js
+++ b/packages/ui/src/views/chatmessage/ChatMessage.js
@@ -346,7 +346,9 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => {
key={index}
label={
URL
- ? `${URL.pathname.substring(0, 15)}...`
+ ? URL.pathname.substring(0, 15) === '/'
+ ? URL.host
+ : `${URL.pathname.substring(0, 15)}...`
: `${source.pageContent.substring(0, 15)}...`
}
component='a'