Merge branch 'FlowiseAI:main' into Feature/Postgres-Vector-Store

This commit is contained in:
Fabio Valencio
2023-08-18 18:41:25 +01:00
committed by GitHub
34 changed files with 2485 additions and 281 deletions
+1 -1
View File
@@ -16,4 +16,4 @@ npm i flowise-components
## 许可证
此存储库中的源代码在[MIT许可证](https://github.com/FlowiseAI/Flowise/blob/master/LICENSE.md)下提供。
此存储库中的源代码在[MIT许可证](https://github.com/FlowiseAI/Flowise/blob/master/LICENSE.md)下提供。
@@ -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 <a target="_blank" href="https://milvus.io/docs/authenticate.md#Authenticate-User-Access">here</a> page.'
this.inputs = [
{
label: 'Milvus User',
name: 'milvusUser',
type: 'string'
},
{
label: 'Milvus Password',
name: 'milvusPassword',
type: 'password'
}
]
}
}
module.exports = { credClass: MilvusCredential }
@@ -1,3 +1,3 @@
<?xml version="1.0" encoding="utf-8"?><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 122.88 92.96" style="enable-background:new 0 0 122.88 92.96" xml:space="preserve"><style type="text/css"><![CDATA[
.st0{fill-rule:evenodd;clip-rule:evenodd;}
<?xml version="1.0" encoding="utf-8"?><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 122.88 92.96" style="enable-background:new 0 0 122.88 92.96" xml:space="preserve"><style type="text/css"><![CDATA[
.st0{fill-rule:evenodd;clip-rule:evenodd;}
]]></style><g><path class="st0" d="M100.09,6.87l2.5,3.3c0.66,0.87,0.49,2.12-0.38,2.77l-2.66,2.02c0.48,1.29,0.79,2.65,0.92,4.06l3.03,0.41 c1.08,0.15,1.84,1.15,1.69,2.23l-0.56,4.1c-0.15,1.08-1.15,1.84-2.23,1.69l-3.3-0.45c-0.59,1.28-1.34,2.46-2.22,3.52l1.85,2.43 c0.66,0.87,0.49,2.12-0.38,2.78l-3.3,2.5c-0.87,0.66-2.12,0.49-2.78-0.38l-2.02-2.66c-1.29,0.48-2.66,0.79-4.06,0.92l-0.41,3.03 c-0.15,1.08-1.15,1.84-2.23,1.69l-4.1-0.56c-1.08-0.15-1.84-1.15-1.69-2.23l0.45-3.3c-1.28-0.59-2.46-1.34-3.52-2.23l-2.43,1.85 c-0.87,0.66-2.12,0.49-2.78-0.38l-2.5-3.3c-0.66-0.87-0.49-2.12,0.38-2.77l2.66-2.02c-0.48-1.29-0.79-2.65-0.92-4.06l-3.03-0.41 c-1.08-0.15-1.84-1.15-1.69-2.23l0.56-4.1c0.15-1.08,1.15-1.84,2.23-1.69l3.3,0.45c0.59-1.28,1.34-2.46,2.23-3.52L70.84,7.9 c-0.66-0.87-0.49-2.12,0.38-2.78l3.3-2.5c0.87-0.66,2.12-0.49,2.78,0.38l2.02,2.66c1.29-0.48,2.66-0.79,4.06-0.92l0.41-3.02 c0.15-1.08,1.15-1.84,2.23-1.69l4.1,0.56c1.08,0.15,1.84,1.15,1.69,2.23l-0.45,3.3c1.28,0.59,2.46,1.34,3.52,2.23l2.43-1.85 C98.19,5.83,99.44,6,100.09,6.87L100.09,6.87L100.09,6.87z M55.71,13.75c-0.23,0.02-0.46,0.04-0.69,0.06 c-5.63,0.54-11.1,2.59-15.62,6.1c-5.23,4.05-9.2,10.11-10.73,18.14l-0.48,2.51L25.69,41c-2.45,0.43-4.64,1.02-6.56,1.77 c-1.86,0.72-3.52,1.61-4.97,2.66c-1.16,0.84-2.16,1.78-3.01,2.8c-2.63,3.15-3.85,7.1-3.82,11.1c0.03,4.06,1.35,8.16,3.79,11.53 c0.91,1.25,1.96,2.4,3.16,3.4l0.03,0.02l-2.68,7.13c-0.71-0.47-1.4-0.98-2.04-1.52c-1.68-1.4-3.15-2.99-4.4-4.72 C1.84,70.57,0.04,64.95,0,59.35c-0.04-5.66,1.72-11.29,5.52-15.85c1.23-1.48,2.68-2.84,4.34-4.04c1.93-1.4,4.14-2.58,6.64-3.55 c1.72-0.67,3.56-1.23,5.5-1.68c2.2-8.74,6.89-15.47,12.92-20.14c5.64-4.37,12.43-6.92,19.42-7.59c2.13-0.21,4.29-0.24,6.43-0.09 c-0.47,0.25-0.91,0.53-1.33,0.85l-0.03,0.02c-1.93,1.47-3.32,3.7-3.68,6.3L55.71,13.75L55.71,13.75z M43.85,87.38H31.99l-1.7,5.58 H19.6l12.75-33.87h11.46l12.7,33.87H45.55L43.85,87.38L43.85,87.38z M41.63,80.04l-3.7-12.17l-3.71,12.17H41.63L41.63,80.04z M59.78,59.09h17.41c3.79,0,6.64,0.9,8.52,2.7c1.88,1.8,2.83,4.38,2.83,7.71c0,3.42-1.04,6.1-3.09,8.03 c-2.06,1.93-5.21,2.89-9.43,2.89h-5.74v12.54h-10.5V59.09L59.78,59.09z M70.28,73.56h2.58c2.03,0,3.46-0.35,4.28-1.06 c0.82-0.7,1.23-1.6,1.23-2.7c0-1.06-0.36-1.96-1.07-2.7c-0.71-0.74-2.05-1.11-4.02-1.11h-3V73.56L70.28,73.56z M92.77,59.09h10.5 v33.87h-10.5V59.09L92.77,59.09z M112.01,31.74c1.07,0.83,2.09,1.77,3.07,2.82c1.07,1.15,2.08,2.45,3.03,3.9 c3.2,4.92,4.84,11.49,4.77,17.92c-0.07,6.31-1.77,12.59-5.25,17.21c-0.84,1.11-1.77,2.15-2.78,3.12V62.15 c0.43-1.87,0.65-3.84,0.67-5.83c0.06-5.07-1.18-10.16-3.59-13.86c-0.69-1.07-1.45-2.03-2.25-2.89c-0.65-0.7-1.34-1.34-2.05-1.9 c0.07-0.3,0.13-0.6,0.17-0.9c0.08-0.62,0.11-1.25,0.07-1.88c0.82-0.32,1.58-0.75,2.26-1.27l0.03-0.02 C110.86,33.06,111.48,32.44,112.01,31.74L112.01,31.74z M85.89,12.37c4.45,0.61,7.57,4.71,6.96,9.17 c-0.61,4.45-4.71,7.57-9.17,6.96c-4.45-0.61-7.57-4.71-6.96-9.17S81.44,11.76,85.89,12.37L85.89,12.37L85.89,12.37z"/></g></svg>

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

@@ -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
},
@@ -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
}
@@ -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',
@@ -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<any> {
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<string> => {
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 {
@@ -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<any> {
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<string> => {
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 {
@@ -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<any> {
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 }
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file-database" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 12.75m-4 0a4 1.75 0 1 0 8 0a4 1.75 0 1 0 -8 0"></path>
<path d="M8 12.5v3.75c0 .966 1.79 1.75 4 1.75s4 -.784 4 -1.75v-3.75"></path>
<path d="M14 3v4a1 1 0 0 0 1 1h4"></path>
<path d="M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 588 B

@@ -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<void> {
// 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 }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 24 KiB

@@ -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
@@ -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
@@ -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<any> {
// 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<string, any>
}
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 }
@@ -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<any> {
// 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<string, any>
}
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<void> {
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 }
@@ -0,0 +1,5 @@
<svg width="362" height="246" viewBox="0 0 362 246" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M255.407 36.2761C207.644 -11.661 130.191 -11.661 82.427 36.2761L4.35891 114.626C-0.255949 119.262 -0.255949 126.728 4.35891 131.364L82.427 209.714C130.191 257.651 207.644 257.651 255.407 209.793C303.248 161.934 303.248 84.2132 255.407 36.2761ZM236.948 186.767C201.875 221.973 144.958 221.973 109.885 186.767L52.4304 129.164C49.0461 125.785 49.0461 120.284 52.4304 116.826L109.808 59.3016C144.881 24.0953 201.798 24.0953 236.871 59.3016C272.021 94.5078 272.021 151.561 236.948 186.767Z" fill="#00B3FF"/>
<path d="M357.699 114.704L323.318 79.5765C321.241 77.4547 317.78 79.4193 318.472 82.327C324.395 109.125 324.395 137.101 318.472 163.899C317.857 166.806 321.318 168.692 323.318 166.649L357.699 131.521C362.237 126.806 362.237 119.341 357.699 114.704Z" fill="#00B3FF"/>
<path d="M173.799 184.646C207.059 184.646 234.023 157.097 234.023 123.113C234.023 89.13 207.059 61.5811 173.799 61.5811C140.538 61.5811 113.575 89.13 113.575 123.113C113.575 157.097 140.538 184.646 173.799 184.646Z" fill="#00B3FF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -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') {
@@ -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) {
@@ -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<any> {
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<ZepFilter> = {
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<string, any>
}
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<string, unknown> | object | undefined): Record<string, unknown> | undefined {
if (typeof value === 'object' && value !== null) {
return value as Record<string, unknown>
}
if (value !== undefined) {
console.warn('Metadata filters must be an object, Record, or undefined.')
}
return undefined
}
class ZepExistingVS extends ZepVectorStore {
filter?: Record<string, any>
args?: IZepConfig & Partial<ZepFilter>
constructor(embeddings: Embeddings, args: IZepConfig & Partial<ZepFilter>) {
super(embeddings, args)
this.filter = args.filter
this.args = args
}
async initalizeCollection(args: IZepConfig & Partial<ZepFilter>) {
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<ZepFilter>) {
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<string, unknown> | 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<ZepFilter>): Promise<ZepVectorStore> {
const instance = new this(embeddings, dbConfig)
return instance
}
}
module.exports = { nodeClass: Zep_Existing_VectorStores }
@@ -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<any> {
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 }
Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

+3 -2
View File
@@ -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",
+19
View File
@@ -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')
}
@@ -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": ""
}
}
]
}
@@ -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 <a target=\"_blank\" href=\"https://docs.flowiseai.com/vector-stores/vectara\">documentation</a> 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": "" }
}
]
}
@@ -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"
},
{
+15 -11
View File
@@ -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)
+37 -33
View File
@@ -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
}
/**
+1 -1
View File
@@ -16,4 +16,4 @@ npm i flowise-ui
## 许可证
本仓库中的源代码在[MIT许可证](https://github.com/FlowiseAI/Flowise/blob/master/LICENSE.md)下提供。
本仓库中的源代码在[MIT许可证](https://github.com/FlowiseAI/Flowise/blob/master/LICENSE.md)下提供。
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

@@ -123,7 +123,7 @@ const NodeInfoDialog = ({ show, dialogProps, onCancel }) => {
</div>
)}
{getNodeConfigApi.data && getNodeConfigApi.data.length > 0 && (
<TableViewOnly rows={getNodeConfigApi.data} columns={Object.keys(getNodeConfigApi.data[0])} />
<TableViewOnly rows={getNodeConfigApi.data} columns={Object.keys(getNodeConfigApi.data[0]).slice(-3)} />
)}
</DialogContent>
</Dialog>
@@ -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')}
>
<ListItem alignItems='center'>
<ListItemAvatar>
@@ -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}
/>
</div>
</ListItemAvatar>
<ListItemText sx={{ ml: 1 }} primary='question' secondary={`User's question from chatbox`} />
</ListItem>
</ListItemButton>
<ListItemButton
sx={{
p: 0,
borderRadius: `${customization.borderRadius}px`,
boxShadow: '0 2px 14px 0 rgb(32 40 45 / 8%)',
mb: 1
}}
disabled={disabled}
onClick={() => onSelectOutputResponseClick(null, 'chat_history')}
>
<ListItem alignItems='center'>
<ListItemAvatar>
<div
style={{
width: 50,
height: 50,
borderRadius: '50%',
backgroundColor: 'white'
}}
>
<img
style={{
width: '100%',
height: '100%',
padding: 10,
objectFit: 'contain'
}}
alt='chatHistory'
src={chatPNG}
/>
</div>
</ListItemAvatar>
<ListItemText
sx={{ ml: 1 }}
primary='chat_history'
secondary={`Past conversation history between user and AI`}
/>
</ListItem>
</ListItemButton>
{availableNodesForVariable &&
availableNodesForVariable.length > 0 &&
availableNodesForVariable.map((node, index) => {
@@ -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'