From 21b2ef7f1dfff71c1d50765aa26ca3bbe5ed8bfe Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 12 Oct 2023 15:31:19 +0100 Subject: [PATCH] add unstructured --- .../credentials/UnstructuredApi.credential.ts | 26 +++++ .../Unstructured/UnstructuredFile.ts | 93 ++++++++++++++++++ .../Unstructured/UnstructuredFolder.ts | 93 ++++++++++++++++++ .../Unstructured/unstructured.png | Bin 0 -> 16710 bytes 4 files changed, 212 insertions(+) create mode 100644 packages/components/credentials/UnstructuredApi.credential.ts create mode 100644 packages/components/nodes/documentloaders/Unstructured/UnstructuredFile.ts create mode 100644 packages/components/nodes/documentloaders/Unstructured/UnstructuredFolder.ts create mode 100644 packages/components/nodes/documentloaders/Unstructured/unstructured.png diff --git a/packages/components/credentials/UnstructuredApi.credential.ts b/packages/components/credentials/UnstructuredApi.credential.ts new file mode 100644 index 000000000..5c77895a1 --- /dev/null +++ b/packages/components/credentials/UnstructuredApi.credential.ts @@ -0,0 +1,26 @@ +import { INodeParams, INodeCredential } from '../src/Interface' + +class UnstructuredApi implements INodeCredential { + label: string + name: string + version: number + description: string + inputs: INodeParams[] + + constructor() { + this.label = 'Unstructured API' + this.name = 'unstructuredApi' + this.version = 1.0 + this.description = + 'Refer to official guide on how to get api key on Unstructured' + this.inputs = [ + { + label: 'API Key', + name: 'unstructuredAPIKey', + type: 'password' + } + ] + } +} + +module.exports = { credClass: UnstructuredApi } diff --git a/packages/components/nodes/documentloaders/Unstructured/UnstructuredFile.ts b/packages/components/nodes/documentloaders/Unstructured/UnstructuredFile.ts new file mode 100644 index 000000000..820aaab55 --- /dev/null +++ b/packages/components/nodes/documentloaders/Unstructured/UnstructuredFile.ts @@ -0,0 +1,93 @@ +import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface' +import { UnstructuredLoader, UnstructuredLoaderOptions } from 'langchain/document_loaders/fs/unstructured' +import { getCredentialData, getCredentialParam } from '../../../src/utils' + +class UnstructuredFile_DocumentLoaders implements INode { + label: string + name: string + version: number + description: string + type: string + icon: string + category: string + baseClasses: string[] + credential: INodeParams + inputs: INodeParams[] + + constructor() { + this.label = 'Unstructured File Loader' + this.name = 'unstructuredFileLoader' + this.version = 1.0 + this.type = 'Document' + this.icon = 'unstructured.png' + this.category = 'Document Loaders' + this.description = 'Use Unstructured.io to load data from a file path' + this.baseClasses = [this.type] + this.credential = { + label: 'Connect Credential', + name: 'credential', + type: 'credential', + credentialNames: ['unstructuredApi'], + optional: true + } + this.inputs = [ + { + label: 'File Path', + name: 'filePath', + type: 'string', + placeholder: '' + }, + { + label: 'Unstructured API URL', + name: 'unstructuredAPIUrl', + description: + 'Unstructured API URL. Read more on how to get started', + type: 'string', + default: 'http://localhost:8000/general/v0/general' + }, + { + label: 'Metadata', + name: 'metadata', + type: 'json', + optional: true, + additionalParams: true + } + /*TODO Add Filter Options*/ + ] + } + + async init(nodeData: INodeData, _: string, options: ICommonObject): Promise { + const filePath = nodeData.inputs?.filePath as string + const unstructuredAPIUrl = nodeData.inputs?.unstructuredAPIUrl as string + const metadata = nodeData.inputs?.metadata + + const obj: UnstructuredLoaderOptions = { apiUrl: unstructuredAPIUrl } + + const credentialData = await getCredentialData(nodeData.credential ?? '', options) + const unstructuredAPIKey = getCredentialParam('unstructuredAPIKey', credentialData, nodeData) + if (unstructuredAPIKey) obj.apiKey = unstructuredAPIKey + + const loader = new UnstructuredLoader(filePath, obj) + const docs = await loader.load() + + if (metadata) { + const parsedMetadata = typeof metadata === 'object' ? metadata : JSON.parse(metadata) + let finaldocs = [] + for (const doc of docs) { + const newdoc = { + ...doc, + metadata: { + ...doc.metadata, + ...parsedMetadata + } + } + finaldocs.push(newdoc) + } + return finaldocs + } + + return docs + } +} + +module.exports = { nodeClass: UnstructuredFile_DocumentLoaders } diff --git a/packages/components/nodes/documentloaders/Unstructured/UnstructuredFolder.ts b/packages/components/nodes/documentloaders/Unstructured/UnstructuredFolder.ts new file mode 100644 index 000000000..4a52fb5a6 --- /dev/null +++ b/packages/components/nodes/documentloaders/Unstructured/UnstructuredFolder.ts @@ -0,0 +1,93 @@ +import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface' +import { UnstructuredDirectoryLoader, UnstructuredLoaderOptions } from 'langchain/document_loaders/fs/unstructured' +import { getCredentialData, getCredentialParam } from '../../../src/utils' + +class UnstructuredFolder_DocumentLoaders implements INode { + label: string + name: string + version: number + description: string + type: string + icon: string + category: string + baseClasses: string[] + credential: INodeParams + inputs: INodeParams[] + + constructor() { + this.label = 'Unstructured Folder Loader' + this.name = 'unstructuredFolderLoader' + this.version = 1.0 + this.type = 'Document' + this.icon = 'unstructured.png' + this.category = 'Document Loaders' + this.description = 'Use Unstructured.io to load data from a folder' + this.baseClasses = [this.type] + this.credential = { + label: 'Connect Credential', + name: 'credential', + type: 'credential', + credentialNames: ['unstructuredApi'], + optional: true + } + this.inputs = [ + { + label: 'Folder Path', + name: 'folderPath', + type: 'string', + placeholder: '' + }, + { + label: 'Unstructured API URL', + name: 'unstructuredAPIUrl', + description: + 'Unstructured API URL. Read more on how to get started', + type: 'string', + default: 'http://localhost:8000/general/v0/general' + }, + { + label: 'Metadata', + name: 'metadata', + type: 'json', + optional: true, + additionalParams: true + } + /*TODO Add Filter Options*/ + ] + } + + async init(nodeData: INodeData, _: string, options: ICommonObject): Promise { + const folderPath = nodeData.inputs?.folderPath as string + const unstructuredAPIUrl = nodeData.inputs?.unstructuredAPIUrl as string + const metadata = nodeData.inputs?.metadata + + const obj: UnstructuredLoaderOptions = { apiUrl: unstructuredAPIUrl } + + const credentialData = await getCredentialData(nodeData.credential ?? '', options) + const unstructuredAPIKey = getCredentialParam('unstructuredAPIKey', credentialData, nodeData) + if (unstructuredAPIKey) obj.apiKey = unstructuredAPIKey + + const loader = new UnstructuredDirectoryLoader(folderPath, obj) + const docs = await loader.load() + + if (metadata) { + const parsedMetadata = typeof metadata === 'object' ? metadata : JSON.parse(metadata) + let finaldocs = [] + for (const doc of docs) { + const newdoc = { + ...doc, + metadata: { + ...doc.metadata, + ...parsedMetadata + } + } + finaldocs.push(newdoc) + } + return finaldocs + } + + return docs + } +} + +module.exports = { nodeClass: UnstructuredFolder_DocumentLoaders } diff --git a/packages/components/nodes/documentloaders/Unstructured/unstructured.png b/packages/components/nodes/documentloaders/Unstructured/unstructured.png new file mode 100644 index 0000000000000000000000000000000000000000..435219bf7011d7bd4fdbdeb9d08e943a1b349043 GIT binary patch literal 16710 zcmeHuX*gBy`|mPEsgSQELi$GLB2&^PiHsE)%2cL8C{t|N+m#FbDb9fJeAp0(9 zT)2uLw7&4il4%$G^3PtUVEBi|^{V=LB(I%k5-#ZNRCH7jq%fR$>m~zS?{?8JaYc{= zZrC51L0i5D@Z-%p2F7msPIp{%Y}{^I-*mePzaa=-z{>HC%Vp;~PHwIOXKvsH9tfN{ zDJI|re?Ju4xssJu_&;9Do>cgs5&s#FU7J08PrTlUAj(dcE~pqhFrOV@j5E7dy|#!x zZ`E$L_Rfg?SNZT!_pkW#XC4WPsA(cA)q^>`v>sQgP8j~f#=Ylh)`_Q2H6qOEjs`5B zlaANU5R5nPXvO!~@*TJ;m^tgZIyk-Q#m8$sFz%Jl8M%TM_B!`Go&0+MHd1DuyKaa5 z%Tvz+3t*?m!G#X%7W{o$Wq}SsI_dxajsMG$q$R7T;Pq=3{^Ak|49Y^OX#cUL@$sS4 z=$=lNdX^u?wSS4GZa-N@{VghcO=;57MQgqD3pultXgc@Rv*dZc%DzS&p9_4oJoa;R zu~#-e2XV_-r7aQiSyE@=!qcZQllV+$=hRaoBlxy*fBl`19@~TPtPR(mwVGkWZT$T< zwukIQk1P9%Gw(m4-{Y%8T}ufZHP%Wj--Bdc#ed55-NIXST*fn!ZDCY%&vx|v&$06n zG8SbwWM?F#jcCX--6=0t!&rPqg&NFh%h+H|ojqO0OR39xQq&5`rgX_XIU^c=7lPMs zaVyF4*&6kbY6cf}5+BdJUAU5+n_JChpinCZ!ZhH?ON}io%~IOs0&C`(o|G$X&J7

lRr|=5>~u+In>m(vBSFGu0}N9`KLo3Ez#Zl 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 aKv}egM1C~Z(msdHBbU^4FXWxKB>gY8%~GfU literal 0 HcmV?d00001