Merge branch 'FlowiseAI:main' into Feature/Postgres-Vector-Store
@@ -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 }
|
||||
|
||||
|
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 }
|
||||
|
After Width: | Height: | Size: 24 KiB |
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)下提供。
|
||||
|
||||
|
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'
|
||||
|
||||