mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-29 05:01:10 +03:00
Feature/Add teams, gmail, outlook tools (#4577)
* add teams, gmail, outlook tools * update docs link * update credentials for oauth2 * add jira tool * add google drive, google calendar, google sheets tools, powerpoint, excel, word doc loader * update jira logo * Refactor Gmail and Outlook tools to remove maxOutputLength parameter and enhance request handling. Update response formatting to include parameters in the output. Adjust Google Drive tools to simplify success messages by removing unnecessary parameter details.
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
import { Document } from '@langchain/core/documents'
|
||||
import { BufferLoader } from 'langchain/document_loaders/fs/buffer'
|
||||
import { read, utils } from 'xlsx'
|
||||
|
||||
/**
|
||||
* Document loader that uses SheetJS to load documents.
|
||||
*
|
||||
* Each worksheet is parsed into an array of row objects using the SheetJS
|
||||
* `sheet_to_json` method and projected to a `Document`. Metadata includes
|
||||
* original sheet name, row data, and row index
|
||||
*/
|
||||
export class LoadOfSheet extends BufferLoader {
|
||||
attributes: { name: string; description: string; type: string }[] = []
|
||||
|
||||
constructor(filePathOrBlob: string | Blob) {
|
||||
super(filePathOrBlob)
|
||||
this.attributes = []
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse document
|
||||
*
|
||||
* NOTE: column labels in multiple sheets are not disambiguated!
|
||||
*
|
||||
* @param raw Raw data Buffer
|
||||
* @param metadata Document metadata
|
||||
* @returns Array of Documents
|
||||
*/
|
||||
async parse(raw: Buffer, metadata: Document['metadata']): Promise<Document[]> {
|
||||
const result: Document[] = []
|
||||
|
||||
this.attributes = [
|
||||
{ name: 'worksheet', description: 'Sheet or Worksheet Name', type: 'string' },
|
||||
{ name: 'rowNum', description: 'Row index', type: 'number' }
|
||||
]
|
||||
|
||||
const wb = read(raw, { type: 'buffer' })
|
||||
for (let name of wb.SheetNames) {
|
||||
const fields: Record<string, Record<string, boolean>> = {}
|
||||
const ws = wb.Sheets[name]
|
||||
if (!ws) continue
|
||||
|
||||
const aoo = utils.sheet_to_json(ws) as Record<string, unknown>[]
|
||||
aoo.forEach((row) => {
|
||||
result.push({
|
||||
pageContent:
|
||||
Object.entries(row)
|
||||
.map((kv) => `- ${kv[0]}: ${kv[1]}`)
|
||||
.join('\n') + '\n',
|
||||
metadata: {
|
||||
worksheet: name,
|
||||
rowNum: row['__rowNum__'],
|
||||
...metadata,
|
||||
...row
|
||||
}
|
||||
})
|
||||
Object.entries(row).forEach(([k, v]) => {
|
||||
if (v != null) (fields[k] || (fields[k] = {}))[v instanceof Date ? 'date' : typeof v] = true
|
||||
})
|
||||
})
|
||||
Object.entries(fields).forEach(([k, v]) =>
|
||||
this.attributes.push({
|
||||
name: k,
|
||||
description: k,
|
||||
type: Object.keys(v).join(' or ')
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
import { TextSplitter } from 'langchain/text_splitter'
|
||||
import { LoadOfSheet } from './ExcelLoader'
|
||||
import { getFileFromStorage, handleDocumentLoaderDocuments, handleDocumentLoaderMetadata, handleDocumentLoaderOutput } from '../../../src'
|
||||
import { ICommonObject, IDocument, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
|
||||
|
||||
class MicrosoftExcel_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 = 'Microsoft Excel'
|
||||
this.name = 'microsoftExcel'
|
||||
this.version = 1.0
|
||||
this.type = 'Document'
|
||||
this.icon = 'excel.svg'
|
||||
this.category = 'Document Loaders'
|
||||
this.description = `Load data from Microsoft Excel files`
|
||||
this.baseClasses = [this.type]
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'Excel File',
|
||||
name: 'excelFile',
|
||||
type: 'file',
|
||||
fileType: '.xlsx, .xls, .xlsm, .xlsb'
|
||||
},
|
||||
{
|
||||
label: 'Text Splitter',
|
||||
name: 'textSplitter',
|
||||
type: 'TextSplitter',
|
||||
optional: 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
|
||||
}
|
||||
]
|
||||
this.outputs = [
|
||||
{
|
||||
label: 'Document',
|
||||
name: 'document',
|
||||
description: 'Array of document objects containing metadata and pageContent',
|
||||
baseClasses: [...this.baseClasses, 'json']
|
||||
},
|
||||
{
|
||||
label: 'Text',
|
||||
name: 'text',
|
||||
description: 'Concatenated string from pageContent of documents',
|
||||
baseClasses: ['string', 'json']
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
getFiles(nodeData: INodeData) {
|
||||
const excelFileBase64 = nodeData.inputs?.excelFile as string
|
||||
|
||||
let files: string[] = []
|
||||
let fromStorage: boolean = true
|
||||
|
||||
if (excelFileBase64.startsWith('FILE-STORAGE::')) {
|
||||
const fileName = excelFileBase64.replace('FILE-STORAGE::', '')
|
||||
if (fileName.startsWith('[') && fileName.endsWith(']')) {
|
||||
files = JSON.parse(fileName)
|
||||
} else {
|
||||
files = [fileName]
|
||||
}
|
||||
} else {
|
||||
if (excelFileBase64.startsWith('[') && excelFileBase64.endsWith(']')) {
|
||||
files = JSON.parse(excelFileBase64)
|
||||
} else {
|
||||
files = [excelFileBase64]
|
||||
}
|
||||
|
||||
fromStorage = false
|
||||
}
|
||||
|
||||
return { files, fromStorage }
|
||||
}
|
||||
|
||||
async getFileData(file: string, { orgId, chatflowid }: { orgId: string; chatflowid: string }, fromStorage?: boolean) {
|
||||
if (fromStorage) {
|
||||
return getFileFromStorage(file, orgId, chatflowid)
|
||||
} else {
|
||||
const splitDataURI = file.split(',')
|
||||
splitDataURI.pop()
|
||||
return Buffer.from(splitDataURI.pop() || '', 'base64')
|
||||
}
|
||||
}
|
||||
|
||||
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
|
||||
const textSplitter = nodeData.inputs?.textSplitter as TextSplitter
|
||||
const metadata = nodeData.inputs?.metadata
|
||||
const output = nodeData.outputs?.output as string
|
||||
const _omitMetadataKeys = nodeData.inputs?.omitMetadataKeys as string
|
||||
|
||||
let docs: IDocument[] = []
|
||||
|
||||
const orgId = options.orgId
|
||||
const chatflowid = options.chatflowid
|
||||
|
||||
const { files, fromStorage } = this.getFiles(nodeData)
|
||||
|
||||
for (const file of files) {
|
||||
if (!file) continue
|
||||
|
||||
const fileData = await this.getFileData(file, { orgId, chatflowid }, fromStorage)
|
||||
const blob = new Blob([fileData])
|
||||
const loader = new LoadOfSheet(blob)
|
||||
|
||||
// use spread instead of push, because it raises RangeError: Maximum call stack size exceeded when too many docs
|
||||
docs = [...docs, ...(await handleDocumentLoaderDocuments(loader, textSplitter))]
|
||||
}
|
||||
|
||||
docs = handleDocumentLoaderMetadata(docs, _omitMetadataKeys, metadata)
|
||||
|
||||
return handleDocumentLoaderOutput(docs, output)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { nodeClass: MicrosoftExcel_DocumentLoaders }
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="96px" height="96px"><path fill="#169154" d="M29,6H15.744C14.781,6,14,6.781,14,7.744v7.259h15V6z"/><path fill="#18482a" d="M14,33.054v7.202C14,41.219,14.781,42,15.743,42H29v-8.946H14z"/><path fill="#0c8045" d="M14 15.003H29V24.005000000000003H14z"/><path fill="#17472a" d="M14 24.005H29V33.055H14z"/><g><path fill="#29c27f" d="M42.256,6H29v9.003h15V7.744C44,6.781,43.219,6,42.256,6z"/><path fill="#27663f" d="M29,33.054V42h13.257C43.219,42,44,41.219,44,40.257v-7.202H29z"/><path fill="#19ac65" d="M29 15.003H44V24.005000000000003H29z"/><path fill="#129652" d="M29 24.005H44V33.055H29z"/></g><path fill="#0c7238" d="M22.319,34H5.681C4.753,34,4,33.247,4,32.319V15.681C4,14.753,4.753,14,5.681,14h16.638 C23.247,14,24,14.753,24,15.681v16.638C24,33.247,23.247,34,22.319,34z"/><path fill="#fff" d="M9.807 19L12.193 19 14.129 22.754 16.175 19 18.404 19 15.333 24 18.474 29 16.123 29 14.013 25.07 11.912 29 9.526 29 12.719 23.982z"/></svg>
|
||||
|
After Width: | Height: | Size: 998 B |
Reference in New Issue
Block a user