mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 15:00:57 +03:00
Feature/add ability to upload file from chat (#3059)
add ability to upload file from chat
This commit is contained in:
@@ -112,6 +112,7 @@ class CSV_Agents implements INode {
|
||||
const chatflowid = options.chatflowid
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
const fileData = await getFileFromStorage(file, chatflowid)
|
||||
base64String += fileData.toString('base64')
|
||||
}
|
||||
@@ -123,6 +124,7 @@ class CSV_Agents implements INode {
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
const splitDataURI = file.split(',')
|
||||
splitDataURI.pop()
|
||||
base64String += splitDataURI.pop() ?? ''
|
||||
|
||||
@@ -89,7 +89,7 @@ class AWSChatBedrock_ChatModels implements INode {
|
||||
name: 'allowImageUploads',
|
||||
type: 'boolean',
|
||||
description:
|
||||
'Only works with claude-3-* models when image is being uploaded from chat. Compatible with LLMChain, Conversation Chain, ReAct Agent, and Conversational Agent',
|
||||
'Only works with claude-3-* models when image is being uploaded from chat. Compatible with LLMChain, Conversation Chain, ReAct Agent, Conversational Agent, Tool Agent',
|
||||
default: false,
|
||||
optional: true
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ class AzureChatOpenAI_ChatModels implements INode {
|
||||
name: 'allowImageUploads',
|
||||
type: 'boolean',
|
||||
description:
|
||||
'Automatically uses gpt-4-vision-preview when image is being uploaded from chat. Only works with LLMChain, Conversation Chain, ReAct Agent, and Conversational Agent',
|
||||
'Automatically uses gpt-4-vision-preview when image is being uploaded from chat. Only works with LLMChain, Conversation Chain, ReAct Agent, Conversational Agent, Tool Agent',
|
||||
default: false,
|
||||
optional: true
|
||||
},
|
||||
|
||||
@@ -84,7 +84,7 @@ class ChatAnthropic_ChatModels implements INode {
|
||||
name: 'allowImageUploads',
|
||||
type: 'boolean',
|
||||
description:
|
||||
'Automatically uses claude-3-* models when image is being uploaded from chat. Only works with LLMChain, Conversation Chain, ReAct Agent, and Conversational Agent',
|
||||
'Automatically uses claude-3-* models when image is being uploaded from chat. Only works with LLMChain, Conversation Chain, ReAct Agent, Conversational Agent, Tool Agent',
|
||||
default: false,
|
||||
optional: true
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AnthropicInput, ChatAnthropic as LangchainChatAnthropic } from '@langchain/anthropic'
|
||||
import { BaseLLMParams } from '@langchain/core/language_models/llms'
|
||||
import { type BaseChatModelParams } from '@langchain/core/language_models/chat_models'
|
||||
import { IVisionChatModal, IMultiModalOption } from '../../../src'
|
||||
|
||||
export class ChatAnthropic extends LangchainChatAnthropic implements IVisionChatModal {
|
||||
@@ -8,8 +8,9 @@ export class ChatAnthropic extends LangchainChatAnthropic implements IVisionChat
|
||||
multiModalOption: IMultiModalOption
|
||||
id: string
|
||||
|
||||
constructor(id: string, fields: Partial<AnthropicInput> & BaseLLMParams & { anthropicApiKey?: string }) {
|
||||
super(fields)
|
||||
constructor(id: string, fields?: Partial<AnthropicInput> & BaseChatModelParams) {
|
||||
// @ts-ignore
|
||||
super(fields ?? {})
|
||||
this.id = id
|
||||
this.configuredModel = fields?.modelName || ''
|
||||
this.configuredMaxToken = fields?.maxTokens ?? 2048
|
||||
|
||||
+1
-1
@@ -145,7 +145,7 @@ class GoogleGenerativeAI_ChatModels implements INode {
|
||||
name: 'allowImageUploads',
|
||||
type: 'boolean',
|
||||
description:
|
||||
'Automatically uses vision model when image is being uploaded from chat. Only works with LLMChain, Conversation Chain, ReAct Agent, and Conversational Agent',
|
||||
'Automatically uses vision model when image is being uploaded from chat. Only works with LLMChain, Conversation Chain, ReAct Agent, Conversational Agent, Tool Agent',
|
||||
default: false,
|
||||
optional: true
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ class ChatOpenAI_ChatModels implements INode {
|
||||
name: 'allowImageUploads',
|
||||
type: 'boolean',
|
||||
description:
|
||||
'Automatically uses gpt-4-vision-preview when image is being uploaded from chat. Only works with LLMChain, Conversation Chain, ReAct Agent, and Conversational Agent',
|
||||
'Automatically uses gpt-4-vision-preview when image is being uploaded from chat. Only works with LLMChain, Conversation Chain, ReAct Agent, Conversational Agent, Tool Agent',
|
||||
default: false,
|
||||
optional: true
|
||||
},
|
||||
|
||||
@@ -108,6 +108,7 @@ class Csv_DocumentLoaders implements INode {
|
||||
const chatflowid = options.chatflowid
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
const fileData = await getFileFromStorage(file, chatflowid)
|
||||
const blob = new Blob([fileData])
|
||||
const loader = new CSVLoader(blob, columnName.trim().length === 0 ? undefined : columnName.trim())
|
||||
@@ -127,6 +128,7 @@ class Csv_DocumentLoaders implements INode {
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
const splitDataURI = file.split(',')
|
||||
splitDataURI.pop()
|
||||
const bf = Buffer.from(splitDataURI.pop() || '', 'base64')
|
||||
|
||||
@@ -83,6 +83,7 @@ class Docx_DocumentLoaders implements INode {
|
||||
const chatflowid = options.chatflowid
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
const fileData = await getFileFromStorage(file, chatflowid)
|
||||
const blob = new Blob([fileData])
|
||||
const loader = new DocxLoader(blob)
|
||||
@@ -103,6 +104,7 @@ class Docx_DocumentLoaders implements INode {
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
const splitDataURI = file.split(',')
|
||||
splitDataURI.pop()
|
||||
const bf = Buffer.from(splitDataURI.pop() || '', 'base64')
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
import { omit } from 'lodash'
|
||||
import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface'
|
||||
import { TextSplitter } from 'langchain/text_splitter'
|
||||
import { TextLoader } from 'langchain/document_loaders/fs/text'
|
||||
import { JSONLinesLoader, JSONLoader } from 'langchain/document_loaders/fs/json'
|
||||
import { CSVLoader } from '@langchain/community/document_loaders/fs/csv'
|
||||
import { PDFLoader } from '@langchain/community/document_loaders/fs/pdf'
|
||||
import { DocxLoader } from '@langchain/community/document_loaders/fs/docx'
|
||||
import { BaseDocumentLoader } from 'langchain/document_loaders/base'
|
||||
import { Document } from '@langchain/core/documents'
|
||||
import { getFileFromStorage } from '../../../src/storageUtils'
|
||||
import { mapMimeTypeToExt } from '../../../src/utils'
|
||||
|
||||
class File_DocumentLoaders implements INode {
|
||||
label: string
|
||||
name: string
|
||||
version: number
|
||||
description: string
|
||||
type: string
|
||||
icon: string
|
||||
category: string
|
||||
baseClasses: string[]
|
||||
inputs: INodeParams[]
|
||||
|
||||
constructor() {
|
||||
this.label = 'File Loader'
|
||||
this.name = 'fileLoader'
|
||||
this.version = 1.0
|
||||
this.type = 'Document'
|
||||
this.icon = 'file.svg'
|
||||
this.category = 'Document Loaders'
|
||||
this.description = `A generic file loader that can load txt, json, csv, docx, pdf, and other files`
|
||||
this.baseClasses = [this.type]
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'File',
|
||||
name: 'file',
|
||||
type: 'file',
|
||||
fileType: '*'
|
||||
},
|
||||
{
|
||||
label: 'Text Splitter',
|
||||
name: 'textSplitter',
|
||||
type: 'TextSplitter',
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'Pdf Usage',
|
||||
name: 'pdfUsage',
|
||||
type: 'options',
|
||||
description: 'Only when loading PDF files',
|
||||
options: [
|
||||
{
|
||||
label: 'One document per page',
|
||||
name: 'perPage'
|
||||
},
|
||||
{
|
||||
label: 'One document per file',
|
||||
name: 'perFile'
|
||||
}
|
||||
],
|
||||
default: 'perPage',
|
||||
optional: true,
|
||||
additionalParams: true
|
||||
},
|
||||
{
|
||||
label: 'JSONL Pointer Extraction',
|
||||
name: 'pointerName',
|
||||
type: 'string',
|
||||
description: 'Only when loading JSONL files',
|
||||
placeholder: '<pointerName>',
|
||||
optional: true,
|
||||
additionalParams: true
|
||||
},
|
||||
{
|
||||
label: 'Additional Metadata',
|
||||
name: 'metadata',
|
||||
type: 'json',
|
||||
description: 'Additional metadata to be added to the extracted documents',
|
||||
optional: true,
|
||||
additionalParams: true
|
||||
},
|
||||
{
|
||||
label: 'Omit Metadata Keys',
|
||||
name: 'omitMetadataKeys',
|
||||
type: 'string',
|
||||
rows: 4,
|
||||
description:
|
||||
'Each document loader comes with a default set of metadata keys that are extracted from the document. You can use this field to omit some of the default metadata keys. The value should be a list of keys, seperated by comma. Use * to omit all metadata keys execept the ones you specify in the Additional Metadata field',
|
||||
placeholder: 'key1, key2, key3.nestedKey1',
|
||||
optional: true,
|
||||
additionalParams: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
|
||||
const textSplitter = nodeData.inputs?.textSplitter as TextSplitter
|
||||
const fileBase64 = nodeData.inputs?.file as string
|
||||
const metadata = nodeData.inputs?.metadata
|
||||
const pdfUsage = nodeData.inputs?.pdfUsage
|
||||
const pointerName = nodeData.inputs?.pointerName as string
|
||||
const _omitMetadataKeys = nodeData.inputs?.omitMetadataKeys as string
|
||||
|
||||
let omitMetadataKeys: string[] = []
|
||||
if (_omitMetadataKeys) {
|
||||
omitMetadataKeys = _omitMetadataKeys.split(',').map((key) => key.trim())
|
||||
}
|
||||
|
||||
let files: string[] = []
|
||||
const fileBlobs: { blob: Blob; ext: string }[] = []
|
||||
|
||||
//FILE-STORAGE::["CONTRIBUTING.md","LICENSE.md","README.md"]
|
||||
const totalFiles = getOverrideFileInputs(nodeData) || fileBase64
|
||||
if (totalFiles.startsWith('FILE-STORAGE::')) {
|
||||
const fileName = totalFiles.replace('FILE-STORAGE::', '')
|
||||
if (fileName.startsWith('[') && fileName.endsWith(']')) {
|
||||
files = JSON.parse(fileName)
|
||||
} else {
|
||||
files = [fileName]
|
||||
}
|
||||
const chatflowid = options.chatflowid
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
const fileData = await getFileFromStorage(file, chatflowid)
|
||||
const blob = new Blob([fileData])
|
||||
fileBlobs.push({ blob, ext: file.split('.').pop() || '' })
|
||||
}
|
||||
} else {
|
||||
if (totalFiles.startsWith('[') && totalFiles.endsWith(']')) {
|
||||
files = JSON.parse(totalFiles)
|
||||
} else {
|
||||
files = [totalFiles]
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
const splitDataURI = file.split(',')
|
||||
splitDataURI.pop()
|
||||
const bf = Buffer.from(splitDataURI.pop() || '', 'base64')
|
||||
const blob = new Blob([bf])
|
||||
|
||||
let extension = ''
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
const match = file.match(/^data:([A-Za-z-+\/]+);base64,/)
|
||||
|
||||
if (!match) {
|
||||
fileBlobs.push({
|
||||
blob,
|
||||
ext: extension
|
||||
})
|
||||
} else {
|
||||
const mimeType = match[1]
|
||||
fileBlobs.push({
|
||||
blob,
|
||||
ext: mapMimeTypeToExt(mimeType)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const loader = new MultiFileLoader(fileBlobs, {
|
||||
json: (blob) => new JSONLoader(blob),
|
||||
jsonl: (blob) => new JSONLinesLoader(blob, '/' + pointerName.trim()),
|
||||
txt: (blob) => new TextLoader(blob),
|
||||
csv: (blob) => new CSVLoader(blob),
|
||||
xls: (blob) => new CSVLoader(blob),
|
||||
xlsx: (blob) => new CSVLoader(blob),
|
||||
docx: (blob) => new DocxLoader(blob),
|
||||
doc: (blob) => new DocxLoader(blob),
|
||||
pdf: (blob) =>
|
||||
pdfUsage === 'perFile'
|
||||
? // @ts-ignore
|
||||
new PDFLoader(blob, { splitPages: false, pdfjs: () => import('pdf-parse/lib/pdf.js/v1.10.100/build/pdf.js') })
|
||||
: // @ts-ignore
|
||||
new PDFLoader(blob, { pdfjs: () => import('pdf-parse/lib/pdf.js/v1.10.100/build/pdf.js') }),
|
||||
'': (blob) => new TextLoader(blob)
|
||||
})
|
||||
let docs = []
|
||||
|
||||
if (textSplitter) {
|
||||
docs = await loader.load()
|
||||
docs = await textSplitter.splitDocuments(docs)
|
||||
} else {
|
||||
docs = await loader.load()
|
||||
}
|
||||
|
||||
if (metadata) {
|
||||
const parsedMetadata = typeof metadata === 'object' ? metadata : JSON.parse(metadata)
|
||||
docs = docs.map((doc) => ({
|
||||
...doc,
|
||||
metadata:
|
||||
_omitMetadataKeys === '*'
|
||||
? {
|
||||
...parsedMetadata
|
||||
}
|
||||
: omit(
|
||||
{
|
||||
...doc.metadata,
|
||||
...parsedMetadata
|
||||
},
|
||||
omitMetadataKeys
|
||||
)
|
||||
}))
|
||||
} else {
|
||||
docs = docs.map((doc) => ({
|
||||
...doc,
|
||||
metadata:
|
||||
_omitMetadataKeys === '*'
|
||||
? {}
|
||||
: omit(
|
||||
{
|
||||
...doc.metadata
|
||||
},
|
||||
omitMetadataKeys
|
||||
)
|
||||
}))
|
||||
}
|
||||
|
||||
return docs
|
||||
}
|
||||
}
|
||||
|
||||
const getOverrideFileInputs = (nodeData: INodeData) => {
|
||||
const txtFileBase64 = nodeData.inputs?.txtFile as string
|
||||
const pdfFileBase64 = nodeData.inputs?.pdfFile as string
|
||||
const jsonFileBase64 = nodeData.inputs?.jsonFile as string
|
||||
const csvFileBase64 = nodeData.inputs?.csvFile as string
|
||||
const jsonlinesFileBase64 = nodeData.inputs?.jsonlinesFile as string
|
||||
const docxFileBase64 = nodeData.inputs?.docxFile as string
|
||||
const yamlFileBase64 = nodeData.inputs?.yamlFile as string
|
||||
|
||||
const removePrefix = (storageFile: string): string[] => {
|
||||
const fileName = storageFile.replace('FILE-STORAGE::', '')
|
||||
if (fileName.startsWith('[') && fileName.endsWith(']')) {
|
||||
return JSON.parse(fileName)
|
||||
}
|
||||
return [fileName]
|
||||
}
|
||||
|
||||
// If exists, combine all file inputs into an array
|
||||
const files: string[] = []
|
||||
if (txtFileBase64) {
|
||||
files.push(...removePrefix(txtFileBase64))
|
||||
}
|
||||
if (pdfFileBase64) {
|
||||
files.push(...removePrefix(pdfFileBase64))
|
||||
}
|
||||
if (jsonFileBase64) {
|
||||
files.push(...removePrefix(jsonFileBase64))
|
||||
}
|
||||
if (csvFileBase64) {
|
||||
files.push(...removePrefix(csvFileBase64))
|
||||
}
|
||||
if (jsonlinesFileBase64) {
|
||||
files.push(...removePrefix(jsonlinesFileBase64))
|
||||
}
|
||||
if (docxFileBase64) {
|
||||
files.push(...removePrefix(docxFileBase64))
|
||||
}
|
||||
if (yamlFileBase64) {
|
||||
files.push(...removePrefix(yamlFileBase64))
|
||||
}
|
||||
|
||||
return files.length ? `FILE-STORAGE::${JSON.stringify(files)}` : ''
|
||||
}
|
||||
|
||||
interface LoadersMapping {
|
||||
[extension: string]: (blob: Blob) => BaseDocumentLoader
|
||||
}
|
||||
|
||||
class MultiFileLoader extends BaseDocumentLoader {
|
||||
constructor(public fileBlobs: { blob: Blob; ext: string }[], public loaders: LoadersMapping) {
|
||||
super()
|
||||
|
||||
if (Object.keys(loaders).length === 0) {
|
||||
throw new Error('Must provide at least one loader')
|
||||
}
|
||||
}
|
||||
|
||||
public async load(): Promise<Document[]> {
|
||||
const documents: Document[] = []
|
||||
|
||||
for (const fileBlob of this.fileBlobs) {
|
||||
const loaderFactory = this.loaders[fileBlob.ext]
|
||||
if (loaderFactory) {
|
||||
const loader = loaderFactory(fileBlob.blob)
|
||||
documents.push(...(await loader.load()))
|
||||
} else {
|
||||
throw new Error(`Error loading file`)
|
||||
}
|
||||
}
|
||||
|
||||
return documents
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { nodeClass: File_DocumentLoaders }
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-files"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 3v4a1 1 0 0 0 1 1h4" /><path d="M18 17h-7a2 2 0 0 1 -2 -2v-10a2 2 0 0 1 2 -2h4l5 5v7a2 2 0 0 1 -2 2z" /><path d="M16 17v2a2 2 0 0 1 -2 2h-7a2 2 0 0 1 -2 -2v-10a2 2 0 0 1 2 -2h2" /></svg>
|
||||
|
After Width: | Height: | Size: 505 B |
@@ -3,7 +3,7 @@ import { INode, INodeData, INodeParams } from '../../../src/Interface'
|
||||
import { TextSplitter } from 'langchain/text_splitter'
|
||||
import { TextLoader } from 'langchain/document_loaders/fs/text'
|
||||
import { DirectoryLoader } from 'langchain/document_loaders/fs/directory'
|
||||
import { JSONLoader } from 'langchain/document_loaders/fs/json'
|
||||
import { JSONLinesLoader, JSONLoader } from 'langchain/document_loaders/fs/json'
|
||||
import { CSVLoader } from '@langchain/community/document_loaders/fs/csv'
|
||||
import { PDFLoader } from '@langchain/community/document_loaders/fs/pdf'
|
||||
import { DocxLoader } from '@langchain/community/document_loaders/fs/docx'
|
||||
@@ -22,7 +22,7 @@ class Folder_DocumentLoaders implements INode {
|
||||
constructor() {
|
||||
this.label = 'Folder with Files'
|
||||
this.name = 'folderFiles'
|
||||
this.version = 2.0
|
||||
this.version = 3.0
|
||||
this.type = 'Document'
|
||||
this.icon = 'folder.svg'
|
||||
this.category = 'Document Loaders'
|
||||
@@ -51,6 +51,7 @@ class Folder_DocumentLoaders implements INode {
|
||||
label: 'Pdf Usage',
|
||||
name: 'pdfUsage',
|
||||
type: 'options',
|
||||
description: 'Only when loading PDF files',
|
||||
options: [
|
||||
{
|
||||
label: 'One document per page',
|
||||
@@ -65,6 +66,15 @@ class Folder_DocumentLoaders implements INode {
|
||||
optional: true,
|
||||
additionalParams: true
|
||||
},
|
||||
{
|
||||
label: 'JSONL Pointer Extraction',
|
||||
name: 'pointerName',
|
||||
type: 'string',
|
||||
description: 'Only when loading JSONL files',
|
||||
placeholder: '<pointerName>',
|
||||
optional: true,
|
||||
additionalParams: true
|
||||
},
|
||||
{
|
||||
label: 'Additional Metadata',
|
||||
name: 'metadata',
|
||||
@@ -93,6 +103,7 @@ class Folder_DocumentLoaders implements INode {
|
||||
const metadata = nodeData.inputs?.metadata
|
||||
const recursive = nodeData.inputs?.recursive as boolean
|
||||
const pdfUsage = nodeData.inputs?.pdfUsage
|
||||
const pointerName = nodeData.inputs?.pointerName as string
|
||||
const _omitMetadataKeys = nodeData.inputs?.omitMetadataKeys as string
|
||||
|
||||
let omitMetadataKeys: string[] = []
|
||||
@@ -104,8 +115,12 @@ class Folder_DocumentLoaders implements INode {
|
||||
folderPath,
|
||||
{
|
||||
'.json': (path) => new JSONLoader(path),
|
||||
'.jsonl': (blob) => new JSONLinesLoader(blob, '/' + pointerName.trim()),
|
||||
'.txt': (path) => new TextLoader(path),
|
||||
'.csv': (path) => new CSVLoader(path),
|
||||
'.xls': (path) => new CSVLoader(path),
|
||||
'.xlsx': (path) => new CSVLoader(path),
|
||||
'.doc': (path) => new DocxLoader(path),
|
||||
'.docx': (path) => new DocxLoader(path),
|
||||
'.pdf': (path) =>
|
||||
pdfUsage === 'perFile'
|
||||
|
||||
@@ -99,6 +99,7 @@ class Json_DocumentLoaders implements INode {
|
||||
const chatflowid = options.chatflowid
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
const fileData = await getFileFromStorage(file, chatflowid)
|
||||
const blob = new Blob([fileData])
|
||||
const loader = new JSONLoader(blob, pointers.length != 0 ? pointers : undefined)
|
||||
@@ -119,6 +120,7 @@ class Json_DocumentLoaders implements INode {
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
const splitDataURI = file.split(',')
|
||||
splitDataURI.pop()
|
||||
const bf = Buffer.from(splitDataURI.pop() || '', 'base64')
|
||||
|
||||
@@ -93,6 +93,7 @@ class Jsonlines_DocumentLoaders implements INode {
|
||||
const chatflowid = options.chatflowid
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
const fileData = await getFileFromStorage(file, chatflowid)
|
||||
const blob = new Blob([fileData])
|
||||
const loader = new JSONLinesLoader(blob, pointer)
|
||||
@@ -113,6 +114,7 @@ class Jsonlines_DocumentLoaders implements INode {
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
const splitDataURI = file.split(',')
|
||||
splitDataURI.pop()
|
||||
const bf = Buffer.from(splitDataURI.pop() || '', 'base64')
|
||||
|
||||
@@ -109,6 +109,7 @@ class Pdf_DocumentLoaders implements INode {
|
||||
const chatflowid = options.chatflowid
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
const fileData = await getFileFromStorage(file, chatflowid)
|
||||
const bf = Buffer.from(fileData)
|
||||
await this.extractDocs(usage, bf, legacyBuild, textSplitter, docs)
|
||||
@@ -121,6 +122,7 @@ class Pdf_DocumentLoaders implements INode {
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
const splitDataURI = file.split(',')
|
||||
splitDataURI.pop()
|
||||
const bf = Buffer.from(splitDataURI.pop() || '', 'base64')
|
||||
|
||||
@@ -101,6 +101,7 @@ class Text_DocumentLoaders implements INode {
|
||||
const chatflowid = options.chatflowid
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
const fileData = await getFileFromStorage(file, chatflowid)
|
||||
const blob = new Blob([fileData])
|
||||
const loader = new TextLoader(blob)
|
||||
@@ -121,6 +122,7 @@ class Text_DocumentLoaders implements INode {
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
const splitDataURI = file.split(',')
|
||||
splitDataURI.pop()
|
||||
const bf = Buffer.from(splitDataURI.pop() || '', 'base64')
|
||||
|
||||
@@ -496,6 +496,7 @@ class UnstructuredFile_DocumentLoaders implements INode {
|
||||
const chatflowid = options.chatflowid
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
const fileData = await getFileFromStorage(file, chatflowid)
|
||||
const loaderDocs = await loader.loadAndSplitBuffer(fileData, file)
|
||||
docs.push(...loaderDocs)
|
||||
@@ -508,6 +509,7 @@ class UnstructuredFile_DocumentLoaders implements INode {
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
const splitDataURI = file.split(',')
|
||||
const filename = splitDataURI.pop()?.split(':')[1] ?? ''
|
||||
const bf = Buffer.from(splitDataURI.pop() || '', 'base64')
|
||||
|
||||
@@ -209,11 +209,11 @@ class Supervisor_MultiAgents implements INode {
|
||||
prompt = messages.prompt
|
||||
multiModalMessageContent = messages.multiModalMessageContent
|
||||
|
||||
if (llm.bindTools === undefined) {
|
||||
if ((llm as any).bindTools === undefined) {
|
||||
throw new Error(`This agent only compatible with function calling models.`)
|
||||
}
|
||||
|
||||
const modelWithTool = llm.bindTools([tool])
|
||||
const modelWithTool = (llm as any).bindTools([tool])
|
||||
|
||||
const outputParser = new ToolCallingAgentOutputParser()
|
||||
|
||||
@@ -464,11 +464,11 @@ class Supervisor_MultiAgents implements INode {
|
||||
prompt = messages.prompt
|
||||
multiModalMessageContent = messages.multiModalMessageContent
|
||||
|
||||
if (llm.bindTools === undefined) {
|
||||
if ((llm as any).bindTools === undefined) {
|
||||
throw new Error(`This agent only compatible with function calling models.`)
|
||||
}
|
||||
|
||||
const modelWithTool = llm.bindTools([tool])
|
||||
const modelWithTool = (llm as any).bindTools([tool])
|
||||
|
||||
const outputParser = new ToolCallingAgentOutputParser()
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ import { Document } from '@langchain/core/documents'
|
||||
import { MilvusLibArgs, Milvus } from '@langchain/community/vectorstores/milvus'
|
||||
import { Embeddings } from '@langchain/core/embeddings'
|
||||
import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams, IndexingResult } from '../../../src/Interface'
|
||||
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
|
||||
import { FLOWISE_CHATID, getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
|
||||
import { howToUseFileUpload } from '../VectorStoreUtils'
|
||||
|
||||
interface InsertRow {
|
||||
[x: string]: string | number[]
|
||||
@@ -27,7 +28,7 @@ class Milvus_VectorStores implements INode {
|
||||
constructor() {
|
||||
this.label = 'Milvus'
|
||||
this.name = 'milvus'
|
||||
this.version = 1.0
|
||||
this.version = 2.0
|
||||
this.type = 'Milvus'
|
||||
this.icon = 'milvus.svg'
|
||||
this.category = 'Vector Stores'
|
||||
@@ -64,6 +65,18 @@ class Milvus_VectorStores implements INode {
|
||||
name: 'milvusCollection',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
label: 'File Upload',
|
||||
name: 'fileUpload',
|
||||
description: 'Allow file upload on the chat',
|
||||
hint: {
|
||||
label: 'How to use',
|
||||
value: howToUseFileUpload
|
||||
},
|
||||
type: 'boolean',
|
||||
additionalParams: true,
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'Milvus Text Field',
|
||||
name: 'milvusTextField',
|
||||
@@ -116,6 +129,7 @@ class Milvus_VectorStores implements INode {
|
||||
// embeddings
|
||||
const docs = nodeData.inputs?.document as Document[]
|
||||
const embeddings = nodeData.inputs?.embeddings as Embeddings
|
||||
const isFileUploadEnabled = nodeData.inputs?.fileUpload as boolean
|
||||
|
||||
// credential
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
@@ -135,6 +149,9 @@ class Milvus_VectorStores implements INode {
|
||||
const finalDocs = []
|
||||
for (let i = 0; i < flattenDocs.length; i += 1) {
|
||||
if (flattenDocs[i] && flattenDocs[i].pageContent) {
|
||||
if (isFileUploadEnabled && options.chatId) {
|
||||
flattenDocs[i].metadata = { ...flattenDocs[i].metadata, [FLOWISE_CHATID]: options.chatId }
|
||||
}
|
||||
finalDocs.push(new Document(flattenDocs[i]))
|
||||
}
|
||||
}
|
||||
@@ -158,8 +175,9 @@ class Milvus_VectorStores implements INode {
|
||||
// server setup
|
||||
const address = nodeData.inputs?.milvusServerUrl as string
|
||||
const collectionName = nodeData.inputs?.milvusCollection as string
|
||||
const milvusFilter = nodeData.inputs?.milvusFilter as string
|
||||
const _milvusFilter = nodeData.inputs?.milvusFilter as string
|
||||
const textField = nodeData.inputs?.milvusTextField as string
|
||||
const isFileUploadEnabled = nodeData.inputs?.fileUpload as boolean
|
||||
|
||||
// embeddings
|
||||
const embeddings = nodeData.inputs?.embeddings as Embeddings
|
||||
@@ -186,6 +204,12 @@ class Milvus_VectorStores implements INode {
|
||||
if (milvusUser) milVusArgs.username = milvusUser
|
||||
if (milvusPassword) milVusArgs.password = milvusPassword
|
||||
|
||||
let milvusFilter = _milvusFilter
|
||||
if (isFileUploadEnabled && options.chatId) {
|
||||
if (milvusFilter) milvusFilter += ` OR ${FLOWISE_CHATID} == "${options.chatId}" OR NOT EXISTS(${FLOWISE_CHATID})`
|
||||
else milvusFilter = `${FLOWISE_CHATID} == "${options.chatId}" OR NOT EXISTS(${FLOWISE_CHATID})`
|
||||
}
|
||||
|
||||
const vectorStore = await Milvus.fromExistingCollection(embeddings, milVusArgs)
|
||||
|
||||
// Avoid Illegal Invocation
|
||||
|
||||
@@ -5,8 +5,8 @@ import { Embeddings } from '@langchain/core/embeddings'
|
||||
import { Document } from '@langchain/core/documents'
|
||||
import { VectorStore } from '@langchain/core/vectorstores'
|
||||
import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams, IndexingResult } from '../../../src/Interface'
|
||||
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
|
||||
import { addMMRInputParams, resolveVectorStoreOrRetriever } from '../VectorStoreUtils'
|
||||
import { FLOWISE_CHATID, getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
|
||||
import { addMMRInputParams, howToUseFileUpload, resolveVectorStoreOrRetriever } from '../VectorStoreUtils'
|
||||
import { index } from '../../../src/indexing'
|
||||
|
||||
let pineconeClientSingleton: Pinecone
|
||||
@@ -43,7 +43,7 @@ class Pinecone_VectorStores implements INode {
|
||||
constructor() {
|
||||
this.label = 'Pinecone'
|
||||
this.name = 'pinecone'
|
||||
this.version = 4.0
|
||||
this.version = 5.0
|
||||
this.type = 'Pinecone'
|
||||
this.icon = 'pinecone.svg'
|
||||
this.category = 'Vector Stores'
|
||||
@@ -88,6 +88,18 @@ class Pinecone_VectorStores implements INode {
|
||||
additionalParams: true,
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'File Upload',
|
||||
name: 'fileUpload',
|
||||
description: 'Allow file upload on the chat',
|
||||
hint: {
|
||||
label: 'How to use',
|
||||
value: howToUseFileUpload
|
||||
},
|
||||
type: 'boolean',
|
||||
additionalParams: true,
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'Pinecone Text Key',
|
||||
name: 'pineconeTextKey',
|
||||
@@ -138,6 +150,7 @@ class Pinecone_VectorStores implements INode {
|
||||
const embeddings = nodeData.inputs?.embeddings as Embeddings
|
||||
const recordManager = nodeData.inputs?.recordManager
|
||||
const pineconeTextKey = nodeData.inputs?.pineconeTextKey as string
|
||||
const isFileUploadEnabled = nodeData.inputs?.fileUpload as boolean
|
||||
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
const pineconeApiKey = getCredentialParam('pineconeApiKey', credentialData, nodeData)
|
||||
@@ -150,6 +163,9 @@ class Pinecone_VectorStores implements INode {
|
||||
const finalDocs = []
|
||||
for (let i = 0; i < flattenDocs.length; i += 1) {
|
||||
if (flattenDocs[i] && flattenDocs[i].pageContent) {
|
||||
if (isFileUploadEnabled && options.chatId) {
|
||||
flattenDocs[i].metadata = { ...flattenDocs[i].metadata, [FLOWISE_CHATID]: options.chatId }
|
||||
}
|
||||
finalDocs.push(new Document(flattenDocs[i]))
|
||||
}
|
||||
}
|
||||
@@ -232,6 +248,7 @@ class Pinecone_VectorStores implements INode {
|
||||
const pineconeMetadataFilter = nodeData.inputs?.pineconeMetadataFilter
|
||||
const embeddings = nodeData.inputs?.embeddings as Embeddings
|
||||
const pineconeTextKey = nodeData.inputs?.pineconeTextKey as string
|
||||
const isFileUploadEnabled = nodeData.inputs?.fileUpload as boolean
|
||||
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
const pineconeApiKey = getCredentialParam('pineconeApiKey', credentialData, nodeData)
|
||||
@@ -250,6 +267,14 @@ class Pinecone_VectorStores implements INode {
|
||||
const metadatafilter = typeof pineconeMetadataFilter === 'object' ? pineconeMetadataFilter : JSON.parse(pineconeMetadataFilter)
|
||||
obj.filter = metadatafilter
|
||||
}
|
||||
if (isFileUploadEnabled && options.chatId) {
|
||||
obj.filter = obj.filter || {}
|
||||
obj.filter.$or = [
|
||||
...(obj.filter.$or || []),
|
||||
{ [FLOWISE_CHATID]: { $eq: options.chatId } },
|
||||
{ [FLOWISE_CHATID]: { $exists: false } }
|
||||
]
|
||||
}
|
||||
|
||||
const vectorStore = (await PineconeStore.fromExistingIndex(embeddings, obj)) as unknown as VectorStore
|
||||
|
||||
|
||||
@@ -5,8 +5,9 @@ import { Embeddings } from '@langchain/core/embeddings'
|
||||
import { Document } from '@langchain/core/documents'
|
||||
import { TypeORMVectorStore, TypeORMVectorStoreDocument } from '@langchain/community/vectorstores/typeorm'
|
||||
import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams, IndexingResult } from '../../../src/Interface'
|
||||
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
|
||||
import { FLOWISE_CHATID, getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
|
||||
import { index } from '../../../src/indexing'
|
||||
import { howToUseFileUpload } from '../VectorStoreUtils'
|
||||
|
||||
class Postgres_VectorStores implements INode {
|
||||
label: string
|
||||
@@ -25,7 +26,7 @@ class Postgres_VectorStores implements INode {
|
||||
constructor() {
|
||||
this.label = 'Postgres'
|
||||
this.name = 'postgres'
|
||||
this.version = 5.0
|
||||
this.version = 6.0
|
||||
this.type = 'Postgres'
|
||||
this.icon = 'postgres.svg'
|
||||
this.category = 'Vector Stores'
|
||||
@@ -82,6 +83,18 @@ class Postgres_VectorStores implements INode {
|
||||
additionalParams: true,
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'File Upload',
|
||||
name: 'fileUpload',
|
||||
description: 'Allow file upload on the chat',
|
||||
hint: {
|
||||
label: 'How to use',
|
||||
value: howToUseFileUpload
|
||||
},
|
||||
type: 'boolean',
|
||||
additionalParams: true,
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'Additional Configuration',
|
||||
name: 'additionalConfig',
|
||||
@@ -132,6 +145,7 @@ class Postgres_VectorStores implements INode {
|
||||
const embeddings = nodeData.inputs?.embeddings as Embeddings
|
||||
const additionalConfig = nodeData.inputs?.additionalConfig as string
|
||||
const recordManager = nodeData.inputs?.recordManager
|
||||
const isFileUploadEnabled = nodeData.inputs?.fileUpload as boolean
|
||||
|
||||
let additionalConfiguration = {}
|
||||
if (additionalConfig) {
|
||||
@@ -161,6 +175,9 @@ class Postgres_VectorStores implements INode {
|
||||
const finalDocs = []
|
||||
for (let i = 0; i < flattenDocs.length; i += 1) {
|
||||
if (flattenDocs[i] && flattenDocs[i].pageContent) {
|
||||
if (isFileUploadEnabled && options.chatId) {
|
||||
flattenDocs[i].metadata = { ...flattenDocs[i].metadata, [FLOWISE_CHATID]: options.chatId }
|
||||
}
|
||||
finalDocs.push(new Document(flattenDocs[i]))
|
||||
}
|
||||
}
|
||||
@@ -268,11 +285,20 @@ class Postgres_VectorStores implements INode {
|
||||
const topK = nodeData.inputs?.topK as string
|
||||
const k = topK ? parseFloat(topK) : 4
|
||||
const _pgMetadataFilter = nodeData.inputs?.pgMetadataFilter
|
||||
const isFileUploadEnabled = nodeData.inputs?.fileUpload as boolean
|
||||
|
||||
let pgMetadataFilter: any
|
||||
if (_pgMetadataFilter) {
|
||||
pgMetadataFilter = typeof _pgMetadataFilter === 'object' ? _pgMetadataFilter : JSON.parse(_pgMetadataFilter)
|
||||
}
|
||||
if (isFileUploadEnabled && options.chatId) {
|
||||
pgMetadataFilter = pgMetadataFilter || {}
|
||||
pgMetadataFilter = {
|
||||
...pgMetadataFilter,
|
||||
[FLOWISE_CHATID]: options.chatId,
|
||||
$notexists: FLOWISE_CHATID // special filter to check if the field does not exist
|
||||
}
|
||||
}
|
||||
|
||||
let additionalConfiguration = {}
|
||||
if (additionalConfig) {
|
||||
@@ -334,12 +360,20 @@ const similaritySearchVectorWithScore = async (
|
||||
) => {
|
||||
const embeddingString = `[${query.join(',')}]`
|
||||
let _filter = '{}'
|
||||
if (filter && typeof filter === 'object') _filter = JSON.stringify(filter)
|
||||
let notExists = ''
|
||||
if (filter && typeof filter === 'object') {
|
||||
if (filter.$notexists) {
|
||||
notExists = `OR NOT (metadata ? '${filter.$notexists}')`
|
||||
delete filter.$notexists
|
||||
}
|
||||
_filter = JSON.stringify(filter)
|
||||
}
|
||||
|
||||
const queryString = `
|
||||
SELECT *, embedding <=> $1 as "_distance"
|
||||
FROM ${tableName}
|
||||
WHERE metadata @> $2
|
||||
${notExists}
|
||||
ORDER BY "_distance" ASC
|
||||
LIMIT $3;`
|
||||
|
||||
|
||||
@@ -6,8 +6,9 @@ import { Document } from '@langchain/core/documents'
|
||||
import { QdrantVectorStore, QdrantLibArgs } from '@langchain/qdrant'
|
||||
import { Embeddings } from '@langchain/core/embeddings'
|
||||
import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams, IndexingResult } from '../../../src/Interface'
|
||||
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
|
||||
import { FLOWISE_CHATID, getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
|
||||
import { index } from '../../../src/indexing'
|
||||
import { howToUseFileUpload } from '../VectorStoreUtils'
|
||||
|
||||
type RetrieverConfig = Partial<VectorStoreRetrieverInput<QdrantVectorStore>>
|
||||
type QdrantAddDocumentOptions = {
|
||||
@@ -32,7 +33,7 @@ class Qdrant_VectorStores implements INode {
|
||||
constructor() {
|
||||
this.label = 'Qdrant'
|
||||
this.name = 'qdrant'
|
||||
this.version = 4.0
|
||||
this.version = 5.0
|
||||
this.type = 'Qdrant'
|
||||
this.icon = 'qdrant.png'
|
||||
this.category = 'Vector Stores'
|
||||
@@ -78,6 +79,18 @@ class Qdrant_VectorStores implements INode {
|
||||
name: 'qdrantCollection',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
label: 'File Upload',
|
||||
name: 'fileUpload',
|
||||
description: 'Allow file upload on the chat',
|
||||
hint: {
|
||||
label: 'How to use',
|
||||
value: howToUseFileUpload
|
||||
},
|
||||
type: 'boolean',
|
||||
additionalParams: true,
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'Vector Dimension',
|
||||
name: 'qdrantVectorDimension',
|
||||
@@ -188,6 +201,7 @@ class Qdrant_VectorStores implements INode {
|
||||
const _batchSize = nodeData.inputs?.batchSize
|
||||
const contentPayloadKey = nodeData.inputs?.contentPayloadKey || 'content'
|
||||
const metadataPayloadKey = nodeData.inputs?.metadataPayloadKey || 'metadata'
|
||||
const isFileUploadEnabled = nodeData.inputs?.fileUpload as boolean
|
||||
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
const qdrantApiKey = getCredentialParam('qdrantApiKey', credentialData, nodeData)
|
||||
@@ -204,6 +218,9 @@ class Qdrant_VectorStores implements INode {
|
||||
const finalDocs = []
|
||||
for (let i = 0; i < flattenDocs.length; i += 1) {
|
||||
if (flattenDocs[i] && flattenDocs[i].pageContent) {
|
||||
if (isFileUploadEnabled && options.chatId) {
|
||||
flattenDocs[i].metadata = { ...flattenDocs[i].metadata, [FLOWISE_CHATID]: options.chatId }
|
||||
}
|
||||
finalDocs.push(new Document(flattenDocs[i]))
|
||||
}
|
||||
}
|
||||
@@ -391,6 +408,7 @@ class Qdrant_VectorStores implements INode {
|
||||
let queryFilter = nodeData.inputs?.qdrantFilter
|
||||
const contentPayloadKey = nodeData.inputs?.contentPayloadKey || 'content'
|
||||
const metadataPayloadKey = nodeData.inputs?.metadataPayloadKey || 'metadata'
|
||||
const isFileUploadEnabled = nodeData.inputs?.fileUpload as boolean
|
||||
|
||||
const k = topK ? parseFloat(topK) : 4
|
||||
|
||||
@@ -434,6 +452,25 @@ class Qdrant_VectorStores implements INode {
|
||||
if (queryFilter) {
|
||||
retrieverConfig.filter = typeof queryFilter === 'object' ? queryFilter : JSON.parse(queryFilter)
|
||||
}
|
||||
if (isFileUploadEnabled && options.chatId) {
|
||||
retrieverConfig.filter = retrieverConfig.filter || {}
|
||||
|
||||
retrieverConfig.filter.should = Array.isArray(retrieverConfig.filter.should) ? retrieverConfig.filter.should : []
|
||||
|
||||
retrieverConfig.filter.should.push(
|
||||
{
|
||||
key: `metadata.${FLOWISE_CHATID}`,
|
||||
match: {
|
||||
value: options.chatId
|
||||
}
|
||||
},
|
||||
{
|
||||
is_empty: {
|
||||
key: `metadata.${FLOWISE_CHATID}`
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const vectorStore = await QdrantVectorStore.fromExistingCollection(embeddings, dbConfig)
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { flatten } from 'lodash'
|
||||
import { IndexingResult, INode, INodeOutputsValue, INodeParams, INodeData, ICommonObject } from '../../../src/Interface'
|
||||
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
|
||||
import { FLOWISE_CHATID, getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
|
||||
import { Embeddings } from '@langchain/core/embeddings'
|
||||
import { Document } from '@langchain/core/documents'
|
||||
import { UpstashVectorStore } from '@langchain/community/vectorstores/upstash'
|
||||
import { Index as UpstashIndex } from '@upstash/vector'
|
||||
import { index } from '../../../src/indexing'
|
||||
import { resolveVectorStoreOrRetriever } from '../VectorStoreUtils'
|
||||
import { howToUseFileUpload, resolveVectorStoreOrRetriever } from '../VectorStoreUtils'
|
||||
|
||||
type UpstashVectorStoreParams = {
|
||||
index: UpstashIndex
|
||||
@@ -29,7 +29,7 @@ class Upstash_VectorStores implements INode {
|
||||
constructor() {
|
||||
this.label = 'Upstash Vector'
|
||||
this.name = 'upstash'
|
||||
this.version = 1.0
|
||||
this.version = 2.0
|
||||
this.type = 'Upstash'
|
||||
this.icon = 'upstash.svg'
|
||||
this.category = 'Vector Stores'
|
||||
@@ -63,6 +63,18 @@ class Upstash_VectorStores implements INode {
|
||||
description: 'Keep track of the record to prevent duplication',
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'File Upload',
|
||||
name: 'fileUpload',
|
||||
description: 'Allow file upload on the chat',
|
||||
hint: {
|
||||
label: 'How to use',
|
||||
value: howToUseFileUpload
|
||||
},
|
||||
type: 'boolean',
|
||||
additionalParams: true,
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'Upstash Metadata Filter',
|
||||
name: 'upstashMetadataFilter',
|
||||
@@ -100,6 +112,7 @@ class Upstash_VectorStores implements INode {
|
||||
const docs = nodeData.inputs?.document as Document[]
|
||||
const embeddings = nodeData.inputs?.embeddings as Embeddings
|
||||
const recordManager = nodeData.inputs?.recordManager
|
||||
const isFileUploadEnabled = nodeData.inputs?.fileUpload as boolean
|
||||
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
const UPSTASH_VECTOR_REST_URL = getCredentialParam('UPSTASH_VECTOR_REST_URL', credentialData, nodeData)
|
||||
@@ -114,6 +127,9 @@ class Upstash_VectorStores implements INode {
|
||||
const finalDocs = []
|
||||
for (let i = 0; i < flattenDocs.length; i += 1) {
|
||||
if (flattenDocs[i] && flattenDocs[i].pageContent) {
|
||||
if (isFileUploadEnabled && options.chatId) {
|
||||
flattenDocs[i].metadata = { ...flattenDocs[i].metadata, [FLOWISE_CHATID]: options.chatId }
|
||||
}
|
||||
finalDocs.push(new Document(flattenDocs[i]))
|
||||
}
|
||||
}
|
||||
@@ -186,6 +202,7 @@ class Upstash_VectorStores implements INode {
|
||||
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
|
||||
const upstashMetadataFilter = nodeData.inputs?.upstashMetadataFilter
|
||||
const embeddings = nodeData.inputs?.embeddings as Embeddings
|
||||
const isFileUploadEnabled = nodeData.inputs?.fileUpload as boolean
|
||||
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
const UPSTASH_VECTOR_REST_URL = getCredentialParam('UPSTASH_VECTOR_REST_URL', credentialData, nodeData)
|
||||
@@ -203,6 +220,10 @@ class Upstash_VectorStores implements INode {
|
||||
if (upstashMetadataFilter) {
|
||||
obj.filter = upstashMetadataFilter
|
||||
}
|
||||
if (isFileUploadEnabled && options.chatId) {
|
||||
if (upstashMetadataFilter) obj.filter += ` OR ${FLOWISE_CHATID} = "${options.chatId}" OR HAS NOT FIELD ${FLOWISE_CHATID}`
|
||||
else obj.filter = `${FLOWISE_CHATID} = "${options.chatId}" OR HAS NOT FIELD ${FLOWISE_CHATID}`
|
||||
}
|
||||
|
||||
const vectorStore = await UpstashVectorStore.fromExistingIndex(embeddings, obj)
|
||||
|
||||
|
||||
@@ -194,6 +194,7 @@ class Vectara_VectorStores implements INode {
|
||||
const chatflowid = options.chatflowid
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
const fileData = await getFileFromStorage(file, chatflowid)
|
||||
const blob = new Blob([fileData])
|
||||
vectaraFiles.push({ blob: blob, fileName: getFileName(file) })
|
||||
@@ -206,6 +207,7 @@ class Vectara_VectorStores implements INode {
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
const splitDataURI = file.split(',')
|
||||
splitDataURI.pop()
|
||||
const bf = Buffer.from(splitDataURI.pop() || '', 'base64')
|
||||
|
||||
@@ -83,3 +83,19 @@ export const addMMRInputParams = (inputs: any[]) => {
|
||||
|
||||
inputs.push(...mmrInputParams)
|
||||
}
|
||||
|
||||
export const howToUseFileUpload = `
|
||||
**File Upload**
|
||||
|
||||
This allows file upload on the chat. Uploaded files will be upserted on the fly to the vector store.
|
||||
|
||||
**Note:**
|
||||
- You can only turn on file upload for one vector store at a time.
|
||||
- At least one Document Loader node should be connected to the document input.
|
||||
- Document Loader should be file types like PDF, DOCX, TXT, etc.
|
||||
|
||||
**How it works**
|
||||
- Uploaded files will have the metadata updated with the chatId.
|
||||
- This will allow the file to be associated with the chatId.
|
||||
- When querying, metadata will be filtered by chatId to retrieve files associated with the chatId.
|
||||
`
|
||||
|
||||
@@ -11,6 +11,8 @@ import { AIMessage, HumanMessage, BaseMessage } from '@langchain/core/messages'
|
||||
|
||||
export const numberOrExpressionRegex = '^(\\d+\\.?\\d*|{{.*}})$' //return true if string consists only numbers OR expression {{}}
|
||||
export const notEmptyRegex = '(.|\\s)*\\S(.|\\s)*' //return true if string is not empty or blank
|
||||
export const FLOWISE_CHATID = 'flowise_chatId'
|
||||
|
||||
/*
|
||||
* List of dependencies allowed to be import in vm2
|
||||
*/
|
||||
@@ -815,3 +817,67 @@ export const getVersion: () => Promise<{ version: string }> = async () => {
|
||||
|
||||
throw new Error('None of the package.json paths could be parsed')
|
||||
}
|
||||
|
||||
/**
|
||||
* Map MimeType to InputField
|
||||
* @param {string} mimeType
|
||||
* @returns {string}
|
||||
*/
|
||||
export const mapMimeTypeToInputField = (mimeType: string) => {
|
||||
switch (mimeType) {
|
||||
case 'text/plain':
|
||||
return 'txtFile'
|
||||
case 'application/pdf':
|
||||
return 'pdfFile'
|
||||
case 'application/json':
|
||||
return 'jsonFile'
|
||||
case 'text/csv':
|
||||
return 'csvFile'
|
||||
case 'application/json-lines':
|
||||
case 'application/jsonl':
|
||||
case 'text/jsonl':
|
||||
return 'jsonlinesFile'
|
||||
case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
|
||||
return 'docxFile'
|
||||
case 'application/vnd.yaml':
|
||||
case 'application/x-yaml':
|
||||
case 'text/vnd.yaml':
|
||||
case 'text/x-yaml':
|
||||
case 'text/yaml':
|
||||
return 'yamlFile'
|
||||
default:
|
||||
return 'txtFile'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map MimeType to Extension
|
||||
* @param {string} mimeType
|
||||
* @returns {string}
|
||||
*/
|
||||
export const mapMimeTypeToExt = (mimeType: string) => {
|
||||
switch (mimeType) {
|
||||
case 'text/plain':
|
||||
return 'txt'
|
||||
case 'application/pdf':
|
||||
return 'pdf'
|
||||
case 'application/json':
|
||||
return 'json'
|
||||
case 'text/csv':
|
||||
return 'csv'
|
||||
case 'application/json-lines':
|
||||
case 'application/jsonl':
|
||||
case 'text/jsonl':
|
||||
return 'jsonl'
|
||||
case 'application/msword':
|
||||
return 'doc'
|
||||
case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
|
||||
return 'docx'
|
||||
case 'application/vnd.ms-excel':
|
||||
return 'xls'
|
||||
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
|
||||
return 'xlsx'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user