diff --git a/Dockerfile b/Dockerfile index fe01ed8d..e485cd3e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,12 @@ FROM node:18-alpine RUN apk add --update libc6-compat python3 make g++ # needed for pdfjs-dist RUN apk add --no-cache build-base cairo-dev pango-dev + +# Install Chromium +RUN apk add --no-cache chromium + ENV PUPPETEER_SKIP_DOWNLOAD=true +ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser WORKDIR /usr/src/packages diff --git a/docker/Dockerfile b/docker/Dockerfile index 2203af11..1ad1bf5e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -6,7 +6,12 @@ RUN apk add --no-cache git RUN apk add --no-cache python3 py3-pip make g++ # needed for pdfjs-dist RUN apk add --no-cache build-base cairo-dev pango-dev + +# Install Chromium +RUN apk add --no-cache chromium + ENV PUPPETEER_SKIP_DOWNLOAD=true +ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser # You can install a specific version like: flowise@1.0.0 RUN npm install -g flowise diff --git a/packages/components/nodes/chains/ApiChain/OpenAPIChain.ts b/packages/components/nodes/chains/ApiChain/OpenAPIChain.ts new file mode 100644 index 00000000..ae1ae3c0 --- /dev/null +++ b/packages/components/nodes/chains/ApiChain/OpenAPIChain.ts @@ -0,0 +1,95 @@ +import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface' +import { APIChain, createOpenAPIChain } from 'langchain/chains' +import { CustomChainHandler, getBaseClasses } from '../../../src/utils' +import { ChatOpenAI } from 'langchain/chat_models/openai' + +class OpenApiChain_Chains implements INode { + label: string + name: string + type: string + icon: string + category: string + baseClasses: string[] + description: string + inputs: INodeParams[] + + constructor() { + this.label = 'OpenAPI Chain' + this.name = 'openApiChain' + this.type = 'openApiChain' + this.icon = 'openapi.png' + this.category = 'Chains' + this.description = 'Chain to run queries against OpenAPI' + this.baseClasses = [this.type, ...getBaseClasses(APIChain)] + this.inputs = [ + { + label: 'ChatOpenAI Model', + name: 'model', + type: 'ChatOpenAI' + }, + { + label: 'YAML Link', + name: 'yamlLink', + type: 'string', + placeholder: 'https://api.speak.com/openapi.yaml', + description: 'If YAML link is provided, uploaded YAML File will be ignored and YAML link will be used instead' + }, + { + label: 'YAML File', + name: 'yamlFile', + type: 'file', + fileType: '.yaml', + description: 'If YAML link is provided, uploaded YAML File will be ignored and YAML link will be used instead' + }, + { + label: 'Headers', + name: 'headers', + type: 'json', + additionalParams: true, + optional: true + } + ] + } + + async init(nodeData: INodeData): Promise { + return await initChain(nodeData) + } + + async run(nodeData: INodeData, input: string, options: ICommonObject): Promise { + const chain = await initChain(nodeData) + if (options.socketIO && options.socketIOClientId) { + const handler = new CustomChainHandler(options.socketIO, options.socketIOClientId) + const res = await chain.run(input, [handler]) + return res + } else { + const res = await chain.run(input) + return res + } + } +} + +const initChain = async (nodeData: INodeData) => { + const model = nodeData.inputs?.model as ChatOpenAI + const headers = nodeData.inputs?.headers as string + const yamlLink = nodeData.inputs?.yamlLink as string + const yamlFileBase64 = nodeData.inputs?.yamlFile as string + + let yamlString = '' + + if (yamlLink) { + yamlString = yamlLink + } else { + const splitDataURI = yamlFileBase64.split(',') + splitDataURI.pop() + const bf = Buffer.from(splitDataURI.pop() || '', 'base64') + yamlString = bf.toString('utf-8') + } + + return await createOpenAPIChain(yamlString, { + llm: model, + headers: typeof headers === 'object' ? headers : headers ? JSON.parse(headers) : {}, + verbose: process.env.DEBUG === 'true' ? true : false + }) +} + +module.exports = { nodeClass: OpenApiChain_Chains } diff --git a/packages/components/nodes/chains/ApiChain/openapi.png b/packages/components/nodes/chains/ApiChain/openapi.png new file mode 100644 index 00000000..457c2e40 Binary files /dev/null and b/packages/components/nodes/chains/ApiChain/openapi.png differ diff --git a/packages/components/nodes/chains/SqlDatabaseChain/SqlDatabaseChain.ts b/packages/components/nodes/chains/SqlDatabaseChain/SqlDatabaseChain.ts index c6c40600..08d3eee5 100644 --- a/packages/components/nodes/chains/SqlDatabaseChain/SqlDatabaseChain.ts +++ b/packages/components/nodes/chains/SqlDatabaseChain/SqlDatabaseChain.ts @@ -1,5 +1,5 @@ import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface' -import { SqlDatabaseChain, SqlDatabaseChainInput } from 'langchain/chains' +import { SqlDatabaseChain, SqlDatabaseChainInput } from 'langchain/chains/sql_db' import { CustomChainHandler, getBaseClasses } from '../../../src/utils' import { DataSource } from 'typeorm' import { SqlDatabase } from 'langchain/sql_db' diff --git a/packages/components/nodes/chatmodels/ChatHuggingFace/core.ts b/packages/components/nodes/chatmodels/ChatHuggingFace/core.ts index 958e9072..416567f0 100644 --- a/packages/components/nodes/chatmodels/ChatHuggingFace/core.ts +++ b/packages/components/nodes/chatmodels/ChatHuggingFace/core.ts @@ -78,9 +78,7 @@ export class HuggingFaceInference extends LLM implements HFInput { async _call(prompt: string, options: this['ParsedCallOptions']): Promise { const { HfInference } = await HuggingFaceInference.imports() const hf = new HfInference(this.apiKey) - if (this.endpoint) hf.endpoint(this.endpoint) - const res = await this.caller.callWithOptions({ signal: options.signal }, hf.textGeneration.bind(hf), { - model: this.model, + const obj: any = { parameters: { // make it behave similar to openai, returning only the generated text return_full_text: false, @@ -91,7 +89,13 @@ export class HuggingFaceInference extends LLM implements HFInput { repetition_penalty: this.frequencyPenalty }, inputs: prompt - }) + } + if (this.endpoint) { + hf.endpoint(this.endpoint) + } else { + obj.model = this.model + } + const res = await this.caller.callWithOptions({ signal: options.signal }, hf.textGeneration.bind(hf), obj) return res.generated_text } diff --git a/packages/components/nodes/documentloaders/Puppeteer/Puppeteer.ts b/packages/components/nodes/documentloaders/Puppeteer/Puppeteer.ts index 86a9477d..618d110b 100644 --- a/packages/components/nodes/documentloaders/Puppeteer/Puppeteer.ts +++ b/packages/components/nodes/documentloaders/Puppeteer/Puppeteer.ts @@ -86,7 +86,12 @@ class Puppeteer_DocumentLoaders implements INode { async function puppeteerLoader(url: string): Promise { try { let docs = [] - const loader = new PuppeteerWebBaseLoader(url) + const loader = new PuppeteerWebBaseLoader(url, { + launchOptions: { + args: ['--no-sandbox'], + headless: 'new' + } + }) if (textSplitter) { docs = await loader.loadAndSplit(textSplitter) } else { diff --git a/packages/components/nodes/embeddings/HuggingFaceInferenceEmbedding/core.ts b/packages/components/nodes/embeddings/HuggingFaceInferenceEmbedding/core.ts index b8d89ebe..41e63aa4 100644 --- a/packages/components/nodes/embeddings/HuggingFaceInferenceEmbedding/core.ts +++ b/packages/components/nodes/embeddings/HuggingFaceInferenceEmbedding/core.ts @@ -30,12 +30,11 @@ export class HuggingFaceInferenceEmbeddings extends Embeddings implements Huggin async _embed(texts: string[]): Promise { // replace newlines, which can negatively affect performance. const clean = texts.map((text) => text.replace(/\n/g, ' ')) - return this.caller.call(() => - this.client.featureExtraction({ - model: this.model, - inputs: clean - }) - ) as Promise + const obj: any = { + inputs: clean + } + if (!this.endpoint) obj.model = this.model + return this.caller.call(() => this.client.featureExtraction(obj)) as Promise } embedQuery(document: string): Promise { diff --git a/packages/components/nodes/llms/HuggingFaceInference/core.ts b/packages/components/nodes/llms/HuggingFaceInference/core.ts index 958e9072..416567f0 100644 --- a/packages/components/nodes/llms/HuggingFaceInference/core.ts +++ b/packages/components/nodes/llms/HuggingFaceInference/core.ts @@ -78,9 +78,7 @@ export class HuggingFaceInference extends LLM implements HFInput { async _call(prompt: string, options: this['ParsedCallOptions']): Promise { const { HfInference } = await HuggingFaceInference.imports() const hf = new HfInference(this.apiKey) - if (this.endpoint) hf.endpoint(this.endpoint) - const res = await this.caller.callWithOptions({ signal: options.signal }, hf.textGeneration.bind(hf), { - model: this.model, + const obj: any = { parameters: { // make it behave similar to openai, returning only the generated text return_full_text: false, @@ -91,7 +89,13 @@ export class HuggingFaceInference extends LLM implements HFInput { repetition_penalty: this.frequencyPenalty }, inputs: prompt - }) + } + if (this.endpoint) { + hf.endpoint(this.endpoint) + } else { + obj.model = this.model + } + const res = await this.caller.callWithOptions({ signal: options.signal }, hf.textGeneration.bind(hf), obj) return res.generated_text } diff --git a/packages/components/package.json b/packages/components/package.json index a4823117..68416661 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -36,7 +36,7 @@ "form-data": "^4.0.0", "graphql": "^16.6.0", "html-to-text": "^9.0.5", - "langchain": "^0.0.96", + "langchain": "^0.0.104", "linkifyjs": "^4.1.1", "mammoth": "^1.5.1", "moment": "^2.29.3", diff --git a/packages/server/marketplaces/chatflows/HuggingFace LLM Chain.json b/packages/server/marketplaces/chatflows/HuggingFace LLM Chain.json index d46f9d64..63a04b03 100644 --- a/packages/server/marketplaces/chatflows/HuggingFace LLM Chain.json +++ b/packages/server/marketplaces/chatflows/HuggingFace LLM Chain.json @@ -156,6 +156,16 @@ "optional": true, "additionalParams": true, "id": "huggingFaceInference_LLMs_0-input-frequencyPenalty-number" + }, + { + "label": "Endpoint", + "name": "endpoint", + "type": "string", + "placeholder": "https://xyz.eu-west-1.aws.endpoints.huggingface.cloud/gpt2", + "description": "Using your own inference endpoint", + "optional": true, + "additionalParams": true, + "id": "huggingFaceInference_LLMs_0-input-endpoint-string" } ], "inputAnchors": [], diff --git a/packages/server/src/utils/index.ts b/packages/server/src/utils/index.ts index 005f4a4b..d32faa8b 100644 --- a/packages/server/src/utils/index.ts +++ b/packages/server/src/utils/index.ts @@ -680,10 +680,16 @@ export const isFlowValidForStream = (reactFlowNodes: IReactFlowNode[], endingNod } } - return ( - isChatOrLLMsExist && - (endingNodeData.category === 'Chains' || endingNodeData.name === 'openAIFunctionAgent') && - !isVectorStoreFaiss(endingNodeData) && - process.env.EXECUTION_MODE !== 'child' - ) + let isValidChainOrAgent = false + if (endingNodeData.category === 'Chains') { + // Chains that are not available to stream + const blacklistChains = ['openApiChain'] + isValidChainOrAgent = !blacklistChains.includes(endingNodeData.name) + } else if (endingNodeData.category === 'Agents') { + // Agent that are available to stream + const whitelistAgents = ['openAIFunctionAgent'] + isValidChainOrAgent = whitelistAgents.includes(endingNodeData.name) + } + + return isChatOrLLMsExist && isValidChainOrAgent && !isVectorStoreFaiss(endingNodeData) && process.env.EXECUTION_MODE !== 'child' }