diff --git a/packages/components/credentials/GroqApi.credential.ts b/packages/components/credentials/GroqApi.credential.ts new file mode 100644 index 00000000..a1c79060 --- /dev/null +++ b/packages/components/credentials/GroqApi.credential.ts @@ -0,0 +1,23 @@ +import { INodeParams, INodeCredential } from '../src/Interface' + +class GroqApi implements INodeCredential { + label: string + name: string + version: number + inputs: INodeParams[] + + constructor() { + this.label = 'Groq API' + this.name = 'groqApi' + this.version = 1.0 + this.inputs = [ + { + label: 'Groq Api Key', + name: 'groqApiKey', + type: 'password' + } + ] + } +} + +module.exports = { credClass: GroqApi } diff --git a/packages/components/nodes/agents/MRKLAgentChat/MRKLAgentChat.ts b/packages/components/nodes/agents/MRKLAgentChat/MRKLAgentChat.ts index d59de540..5923d77e 100644 --- a/packages/components/nodes/agents/MRKLAgentChat/MRKLAgentChat.ts +++ b/packages/components/nodes/agents/MRKLAgentChat/MRKLAgentChat.ts @@ -1,17 +1,17 @@ import { flatten } from 'lodash' import { AgentExecutor } from 'langchain/agents' -import { pull } from 'langchain/hub' +import { HumanMessage } from '@langchain/core/messages' +import { ChatPromptTemplate, HumanMessagePromptTemplate } from '@langchain/core/prompts' import { Tool } from '@langchain/core/tools' import type { PromptTemplate } from '@langchain/core/prompts' import { BaseChatModel } from '@langchain/core/language_models/chat_models' +import { pull } from 'langchain/hub' import { additionalCallbacks } from '../../../src/handler' import { FlowiseMemory, ICommonObject, IMessage, INode, INodeData, INodeParams } from '../../../src/Interface' import { getBaseClasses } from '../../../src/utils' import { createReactAgent } from '../../../src/agents' import { ChatOpenAI } from '../../chatmodels/ChatOpenAI/FlowiseChatOpenAI' -import { HumanMessage } from '@langchain/core/messages' import { addImagesToMessages } from '../../../src/multiModalUtils' -import { ChatPromptTemplate, HumanMessagePromptTemplate } from 'langchain/prompts' class MRKLAgentChat_Agents implements INode { label: string diff --git a/packages/components/nodes/agents/MRKLAgentLLM/MRKLAgentLLM.ts b/packages/components/nodes/agents/MRKLAgentLLM/MRKLAgentLLM.ts index 596d20c5..452cf437 100644 --- a/packages/components/nodes/agents/MRKLAgentLLM/MRKLAgentLLM.ts +++ b/packages/components/nodes/agents/MRKLAgentLLM/MRKLAgentLLM.ts @@ -3,7 +3,7 @@ import { AgentExecutor } from 'langchain/agents' import { pull } from 'langchain/hub' import { Tool } from '@langchain/core/tools' import type { PromptTemplate } from '@langchain/core/prompts' -import { BaseLanguageModel } from 'langchain/base_language' +import { BaseLanguageModel } from '@langchain/core/language_models/base' import { additionalCallbacks } from '../../../src/handler' import { getBaseClasses } from '../../../src/utils' import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface' diff --git a/packages/components/nodes/chains/LLMChain/LLMChain.ts b/packages/components/nodes/chains/LLMChain/LLMChain.ts index c60b4b29..6adee1e1 100644 --- a/packages/components/nodes/chains/LLMChain/LLMChain.ts +++ b/packages/components/nodes/chains/LLMChain/LLMChain.ts @@ -1,5 +1,6 @@ import { BaseLanguageModel, BaseLanguageModelCallOptions } from '@langchain/core/language_models/base' import { BaseLLMOutputParser, BaseOutputParser } from '@langchain/core/output_parsers' +import { HumanMessage } from '@langchain/core/messages' import { ChatPromptTemplate, FewShotPromptTemplate, PromptTemplate, HumanMessagePromptTemplate } from '@langchain/core/prompts' import { OutputFixingParser } from 'langchain/output_parsers' import { LLMChain } from 'langchain/chains' @@ -10,7 +11,6 @@ import { checkInputs, Moderation, streamResponse } from '../../moderation/Modera import { formatResponse, injectOutputParser } from '../../outputparsers/OutputParserHelpers' import { ChatOpenAI } from '../../chatmodels/ChatOpenAI/FlowiseChatOpenAI' import { addImagesToMessages } from '../../../src/multiModalUtils' -import { HumanMessage } from 'langchain/schema' class LLMChain_Chains implements INode { label: string diff --git a/packages/components/nodes/chatmodels/ChatMistral/ChatMistral.ts b/packages/components/nodes/chatmodels/ChatMistral/ChatMistral.ts index 87ad240a..0b65fb87 100644 --- a/packages/components/nodes/chatmodels/ChatMistral/ChatMistral.ts +++ b/packages/components/nodes/chatmodels/ChatMistral/ChatMistral.ts @@ -18,7 +18,7 @@ class ChatMistral_ChatModels implements INode { constructor() { this.label = 'ChatMistralAI' this.name = 'chatMistralAI' - this.version = 1.0 + this.version = 2.0 this.type = 'ChatMistralAI' this.icon = 'MistralAI.svg' this.category = 'Chat Models' @@ -40,21 +40,9 @@ class ChatMistral_ChatModels implements INode { { label: 'Model Name', name: 'modelName', - type: 'options', - options: [ - { - label: 'mistral-tiny', - name: 'mistral-tiny' - }, - { - label: 'mistral-small', - name: 'mistral-small' - }, - { - label: 'mistral-medium', - name: 'mistral-medium' - } - ], + type: 'string', + description: + 'Refer to Model Selection for more available models', default: 'mistral-tiny' }, { diff --git a/packages/components/nodes/chatmodels/ChatOpenAI/FlowiseChatOpenAI.ts b/packages/components/nodes/chatmodels/ChatOpenAI/FlowiseChatOpenAI.ts index 2fa65375..9049bb79 100644 --- a/packages/components/nodes/chatmodels/ChatOpenAI/FlowiseChatOpenAI.ts +++ b/packages/components/nodes/chatmodels/ChatOpenAI/FlowiseChatOpenAI.ts @@ -7,9 +7,10 @@ import { ChatOpenAICallOptions } from '@langchain/openai' import { BaseChatModelParams } from '@langchain/core/language_models/chat_models' -import { IMultiModalOption } from '../../../src' -import { BaseMessageLike, LLMResult } from 'langchain/schema' +import { BaseMessageLike } from '@langchain/core/messages' import { Callbacks } from '@langchain/core/callbacks/manager' +import { LLMResult } from '@langchain/core/outputs' +import { IMultiModalOption } from '../../../src' export class ChatOpenAI extends LangchainChatOpenAI { configuredModel: string diff --git a/packages/components/nodes/chatmodels/Groq/Groq.ts b/packages/components/nodes/chatmodels/Groq/Groq.ts new file mode 100644 index 00000000..91e96aef --- /dev/null +++ b/packages/components/nodes/chatmodels/Groq/Groq.ts @@ -0,0 +1,80 @@ +import { BaseCache } from '@langchain/core/caches' +import { ChatGroq, ChatGroqInput } from '@langchain/groq' +import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface' +import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' + +class Groq_ChatModels implements INode { + label: string + name: string + version: number + type: string + icon: string + category: string + description: string + baseClasses: string[] + credential: INodeParams + inputs: INodeParams[] + + constructor() { + this.label = 'GroqChat' + this.name = 'groqChat' + this.version = 2.0 + this.type = 'GroqChat' + this.icon = 'groq.png' + this.category = 'Chat Models' + this.description = 'Wrapper around Groq API with LPU Inference Engine' + this.baseClasses = [this.type, ...getBaseClasses(ChatGroq)] + this.credential = { + label: 'Connect Credential', + name: 'credential', + type: 'credential', + credentialNames: ['groqApi'], + optional: true + } + this.inputs = [ + { + label: 'Cache', + name: 'cache', + type: 'BaseCache', + optional: true + }, + { + label: 'Model Name', + name: 'modelName', + type: 'string', + placeholder: 'ft:gpt-3.5-turbo:my-org:custom_suffix:id' + }, + { + label: 'Temperature', + name: 'temperature', + type: 'number', + step: 0.1, + default: 0.9, + optional: true + } + ] + } + + async init(nodeData: INodeData, _: string, options: ICommonObject): Promise { + const modelName = nodeData.inputs?.modelName as string + const cache = nodeData.inputs?.cache as BaseCache + const temperature = nodeData.inputs?.temperature as string + const streaming = nodeData.inputs?.streaming as boolean + + const credentialData = await getCredentialData(nodeData.credential ?? '', options) + const groqApiKey = getCredentialParam('groqApiKey', credentialData, nodeData) + + const obj: ChatGroqInput = { + modelName, + temperature: parseFloat(temperature), + apiKey: groqApiKey, + streaming: streaming ?? true + } + if (cache) obj.cache = cache + + const model = new ChatGroq(obj) + return model + } +} + +module.exports = { nodeClass: Groq_ChatModels } diff --git a/packages/components/nodes/chatmodels/Groq/groq.png b/packages/components/nodes/chatmodels/Groq/groq.png new file mode 100644 index 00000000..31564145 Binary files /dev/null and b/packages/components/nodes/chatmodels/Groq/groq.png differ diff --git a/packages/components/nodes/memory/ConversationSummaryMemory/ConversationSummaryMemory.ts b/packages/components/nodes/memory/ConversationSummaryMemory/ConversationSummaryMemory.ts index c3b3df91..88c8e34c 100644 --- a/packages/components/nodes/memory/ConversationSummaryMemory/ConversationSummaryMemory.ts +++ b/packages/components/nodes/memory/ConversationSummaryMemory/ConversationSummaryMemory.ts @@ -1,8 +1,8 @@ import { FlowiseSummaryMemory, IMessage, INode, INodeData, INodeParams, MemoryMethods } from '../../../src/Interface' import { convertBaseMessagetoIMessage, getBaseClasses } from '../../../src/utils' -import { ConversationSummaryMemory, ConversationSummaryMemoryInput } from 'langchain/memory' -import { BaseLanguageModel } from 'langchain/base_language' +import { BaseLanguageModel } from '@langchain/core/language_models/base' import { BaseMessage } from '@langchain/core/messages' +import { ConversationSummaryMemory, ConversationSummaryMemoryInput } from 'langchain/memory' class ConversationSummaryMemory_Memory implements INode { label: string diff --git a/packages/components/nodes/utilities/CustomFunction/CustomFunction.ts b/packages/components/nodes/utilities/CustomFunction/CustomFunction.ts index 4ab2a6f2..9c1469e0 100644 --- a/packages/components/nodes/utilities/CustomFunction/CustomFunction.ts +++ b/packages/components/nodes/utilities/CustomFunction/CustomFunction.ts @@ -90,12 +90,17 @@ class CustomFunction_Utilities implements INode { // Some values might be a stringified JSON, parse it for (const key in inputVars) { - if (typeof inputVars[key] === 'string' && inputVars[key].startsWith('{') && inputVars[key].endsWith('}')) { - try { - inputVars[key] = JSON.parse(inputVars[key]) - } catch (e) { - continue + let value = inputVars[key] + if (typeof value === 'string') { + value = handleEscapeCharacters(value, true) + if (value.startsWith('{') && value.endsWith('}')) { + try { + value = JSON.parse(value) + } catch (e) { + // ignore + } } + inputVars[key] = value } } @@ -105,11 +110,7 @@ class CustomFunction_Utilities implements INode { if (Object.keys(inputVars).length) { for (const item in inputVars) { - let value = inputVars[item] - if (typeof value === 'string') { - value = handleEscapeCharacters(value, true) - } - sandbox[`$${item}`] = value + sandbox[`$${item}`] = inputVars[item] } } diff --git a/packages/components/nodes/utilities/IfElseFunction/IfElseFunction.ts b/packages/components/nodes/utilities/IfElseFunction/IfElseFunction.ts index 1c5c0b7d..1e61616c 100644 --- a/packages/components/nodes/utilities/IfElseFunction/IfElseFunction.ts +++ b/packages/components/nodes/utilities/IfElseFunction/IfElseFunction.ts @@ -101,12 +101,17 @@ class IfElseFunction_Utilities implements INode { // Some values might be a stringified JSON, parse it for (const key in inputVars) { - if (typeof inputVars[key] === 'string' && inputVars[key].startsWith('{') && inputVars[key].endsWith('}')) { - try { - inputVars[key] = JSON.parse(inputVars[key]) - } catch (e) { - continue + let value = inputVars[key] + if (typeof value === 'string') { + value = handleEscapeCharacters(value, true) + if (value.startsWith('{') && value.endsWith('}')) { + try { + value = JSON.parse(value) + } catch (e) { + // ignore + } } + inputVars[key] = value } } @@ -116,11 +121,7 @@ class IfElseFunction_Utilities implements INode { if (Object.keys(inputVars).length) { for (const item in inputVars) { - let value = inputVars[item] - if (typeof value === 'string') { - value = handleEscapeCharacters(value, true) - } - sandbox[`$${item}`] = value + sandbox[`$${item}`] = inputVars[item] } } diff --git a/packages/components/package.json b/packages/components/package.json index 20aef68a..69ca3b5f 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -33,6 +33,7 @@ "@langchain/cohere": "^0.0.5", "@langchain/community": "^0.0.30", "@langchain/google-genai": "^0.0.10", + "@langchain/groq": "^0.0.2", "@langchain/mistralai": "^0.0.7", "@langchain/openai": "^0.0.14", "@langchain/pinecone": "^0.0.3", @@ -72,7 +73,7 @@ "lunary": "^0.6.16", "mammoth": "^1.5.1", "moment": "^2.29.3", - "mongodb": "^6.2.0", + "mongodb": "6.2.0", "mysql2": "^3.5.1", "node-fetch": "^2.6.11", "node-html-markdown": "^1.3.0", diff --git a/packages/components/src/multiModalUtils.ts b/packages/components/src/multiModalUtils.ts index 94414e58..00cc5bf3 100644 --- a/packages/components/src/multiModalUtils.ts +++ b/packages/components/src/multiModalUtils.ts @@ -1,5 +1,5 @@ import { ICommonObject, IFileUpload, IMultiModalOption, INodeData, MessageContentImageUrl } from './Interface' -import { ChatOpenAI as LangchainChatOpenAI } from 'langchain/chat_models/openai' +import { ChatOpenAI } from '../nodes/chatmodels/ChatOpenAI/FlowiseChatOpenAI' import path from 'path' import { getStoragePath } from './utils' import fs from 'fs' @@ -12,7 +12,7 @@ export const addImagesToMessages = ( const imageContent: MessageContentImageUrl[] = [] let model = nodeData.inputs?.model - if (model instanceof LangchainChatOpenAI && multiModalOption) { + if (model instanceof ChatOpenAI && multiModalOption) { // Image Uploaded if (multiModalOption.image && multiModalOption.image.allowImageUploads && options?.uploads && options?.uploads.length > 0) { const imageUploads = getImageUploads(options.uploads) diff --git a/packages/server/marketplaces/chatflows/OpenAI Assistant.json b/packages/server/marketplaces/chatflows/OpenAI Assistant.json index 73c01413..9941e703 100644 --- a/packages/server/marketplaces/chatflows/OpenAI Assistant.json +++ b/packages/server/marketplaces/chatflows/OpenAI Assistant.json @@ -5,12 +5,10 @@ "badge": "NEW", "nodes": [ { - "width": 300, - "height": 327, "id": "openAIAssistant_0", "position": { - "x": 895.3722263184736, - "y": 118.50795801755544 + "x": 1237.914576178543, + "y": 140 }, "type": "customNode", "data": { @@ -60,33 +58,36 @@ ], "inputs": { "selectedAssistant": "", - "tools": ["{{calculator_0.data.instance}}", "{{serper_0.data.instance}}", "{{customTool_0.data.instance}}"] + "tools": ["{{calculator_0.data.instance}}", "{{serper_0.data.instance}}", "{{customTool_0.data.instance}}"], + "inputModeration": "", + "disableFileDownload": "" }, "outputAnchors": [ { "id": "openAIAssistant_0-output-openAIAssistant-OpenAIAssistant", "name": "openAIAssistant", "label": "OpenAIAssistant", + "description": "An agent that uses OpenAI Assistant API to pick the tool and args to call", "type": "OpenAIAssistant" } ], "outputs": {}, "selected": false }, + "width": 300, + "height": 419, "selected": false, "dragging": false, "positionAbsolute": { - "x": 895.3722263184736, - "y": 118.50795801755544 + "x": 1237.914576178543, + "y": 140 } }, { - "width": 300, - "height": 143, "id": "calculator_0", "position": { - "x": 454.74423492660145, - "y": -56.08375600705064 + "x": 854.0341531341463, + "y": 48.134746169036475 }, "type": "customNode", "data": { @@ -106,26 +107,75 @@ "id": "calculator_0-output-calculator-Calculator|Tool|StructuredTool|Runnable", "name": "calculator", "label": "Calculator", + "description": "Perform calculations on response", "type": "Calculator | Tool | StructuredTool | Runnable" } ], "outputs": {}, "selected": false }, + "width": 300, + "height": 142, "selected": false, "positionAbsolute": { - "x": 454.74423492660145, - "y": -56.08375600705064 + "x": 854.0341531341463, + "y": 48.134746169036475 }, "dragging": false }, { + "id": "serper_0", + "position": { + "x": 852.623106275503, + "y": 205.46647090775525 + }, + "type": "customNode", + "data": { + "id": "serper_0", + "label": "Serper", + "version": 1, + "name": "serper", + "type": "Serper", + "baseClasses": ["Serper", "Tool", "StructuredTool", "Runnable"], + "category": "Tools", + "description": "Wrapper around Serper.dev - Google Search API", + "inputParams": [ + { + "label": "Connect Credential", + "name": "credential", + "type": "credential", + "credentialNames": ["serperApi"], + "id": "serper_0-input-credential-credential" + } + ], + "inputAnchors": [], + "inputs": {}, + "outputAnchors": [ + { + "id": "serper_0-output-serper-Serper|Tool|StructuredTool|Runnable", + "name": "serper", + "label": "Serper", + "description": "Wrapper around Serper.dev - Google Search API", + "type": "Serper | Tool | StructuredTool | Runnable" + } + ], + "outputs": {}, + "selected": false + }, "width": 300, - "height": 277, + "height": 276, + "selected": false, + "positionAbsolute": { + "x": 852.623106275503, + "y": 205.46647090775525 + }, + "dragging": false + }, + { "id": "customTool_0", "position": { - "x": 454.43871855431365, - "y": 401.2171774551178 + "x": 850.6759101766447, + "y": 496.68759375469654 }, "type": "customNode", "data": { @@ -155,63 +205,19 @@ "id": "customTool_0-output-customTool-CustomTool|Tool|StructuredTool|Runnable", "name": "customTool", "label": "CustomTool", + "description": "Use custom tool you've created in Flowise within chatflow", "type": "CustomTool | Tool | StructuredTool | Runnable" } ], "outputs": {}, "selected": false }, - "selected": false, - "positionAbsolute": { - "x": 454.43871855431365, - "y": 401.2171774551178 - }, - "dragging": false - }, - { "width": 300, - "height": 277, - "id": "serper_0", - "position": { - "x": 452.2514874331948, - "y": 99.6087116015905 - }, - "type": "customNode", - "data": { - "id": "serper_0", - "label": "Serper", - "version": 1, - "name": "serper", - "type": "Serper", - "baseClasses": ["Serper", "Tool", "StructuredTool", "Runnable"], - "category": "Tools", - "description": "Wrapper around Serper.dev - Google Search API", - "inputParams": [ - { - "label": "Connect Credential", - "name": "credential", - "type": "credential", - "credentialNames": ["serperApi"], - "id": "serper_0-input-credential-credential" - } - ], - "inputAnchors": [], - "inputs": {}, - "outputAnchors": [ - { - "id": "serper_0-output-serper-Serper|Tool|StructuredTool|Runnable", - "name": "serper", - "label": "Serper", - "type": "Serper | Tool | StructuredTool | Runnable" - } - ], - "outputs": {}, - "selected": false - }, + "height": 276, "selected": false, "positionAbsolute": { - "x": 452.2514874331948, - "y": 99.6087116015905 + "x": 850.6759101766447, + "y": 496.68759375469654 }, "dragging": false } @@ -223,10 +229,7 @@ "target": "openAIAssistant_0", "targetHandle": "openAIAssistant_0-input-tools-Tool", "type": "buttonedge", - "id": "calculator_0-calculator_0-output-calculator-Calculator|Tool|StructuredTool|Runnable-openAIAssistant_0-openAIAssistant_0-input-tools-Tool", - "data": { - "label": "" - } + "id": "calculator_0-calculator_0-output-calculator-Calculator|Tool|StructuredTool|Runnable-openAIAssistant_0-openAIAssistant_0-input-tools-Tool" }, { "source": "serper_0", @@ -234,10 +237,7 @@ "target": "openAIAssistant_0", "targetHandle": "openAIAssistant_0-input-tools-Tool", "type": "buttonedge", - "id": "serper_0-serper_0-output-serper-Serper|Tool|StructuredTool|Runnable-openAIAssistant_0-openAIAssistant_0-input-tools-Tool", - "data": { - "label": "" - } + "id": "serper_0-serper_0-output-serper-Serper|Tool|StructuredTool|Runnable-openAIAssistant_0-openAIAssistant_0-input-tools-Tool" }, { "source": "customTool_0", @@ -245,10 +245,7 @@ "target": "openAIAssistant_0", "targetHandle": "openAIAssistant_0-input-tools-Tool", "type": "buttonedge", - "id": "customTool_0-customTool_0-output-customTool-CustomTool|Tool|StructuredTool|Runnable-openAIAssistant_0-openAIAssistant_0-input-tools-Tool", - "data": { - "label": "" - } + "id": "customTool_0-customTool_0-output-customTool-CustomTool|Tool|StructuredTool|Runnable-openAIAssistant_0-openAIAssistant_0-input-tools-Tool" } ] } diff --git a/packages/server/src/utils/index.ts b/packages/server/src/utils/index.ts index 8092fbd8..f5f2d653 100644 --- a/packages/server/src/utils/index.ts +++ b/packages/server/src/utils/index.ts @@ -532,11 +532,45 @@ export const getVariableValue = ( variableDict[`{{${variableFullPath}}}`] = handleEscapeCharacters(convertChatHistoryToText(chatHistory), false) } - // Split by first occurrence of '.' to get just nodeId - const [variableNodeId, _] = variableFullPath.split('.') + // Resolve values with following case. + // 1: .data.instance + // 2: .data.instance.pathtokey + const variableFullPathParts = variableFullPath.split('.') + const variableNodeId = variableFullPathParts[0] const executedNode = reactFlowNodes.find((nd) => nd.id === variableNodeId) if (executedNode) { - const variableValue = get(executedNode.data, 'instance') + let variableValue = get(executedNode.data, 'instance') + + // Handle path such as `.data.instance.key` + if (variableFullPathParts.length > 3) { + let variableObj = null + switch (typeof variableValue) { + case 'string': { + const unEscapedVariableValue = handleEscapeCharacters(variableValue, true) + if (unEscapedVariableValue.startsWith('{') && unEscapedVariableValue.endsWith('}')) { + try { + variableObj = JSON.parse(unEscapedVariableValue) + } catch (e) { + // ignore + } + } + break + } + case 'object': { + variableObj = variableValue + break + } + default: + break + } + if (variableObj) { + variableObj = get(variableObj, variableFullPathParts.slice(3)) + variableValue = handleEscapeCharacters( + typeof variableObj === 'object' ? JSON.stringify(variableObj) : variableObj, + false + ) + } + } if (isAcceptVariable) { variableDict[`{{${variableFullPath}}}`] = variableValue } else { @@ -855,7 +889,8 @@ export const isFlowValidForStream = (reactFlowNodes: IReactFlowNode[], endingNod 'chatAnthropic_LlamaIndex', 'chatOllama', 'awsChatBedrock', - 'chatMistralAI' + 'chatMistralAI', + 'groqChat' ], LLMs: ['azureOpenAI', 'openAI', 'ollama'] }