Fix merge conflicts

This commit is contained in:
Ilango
2024-02-27 16:00:54 +05:30
107 changed files with 5282 additions and 528 deletions
+1
View File
@@ -125,6 +125,7 @@ Flowise 支持不同的环境变量来配置您的实例。您可以在 `package
| FLOWISE_PASSWORD | 登录密码 | 字符串 | |
| FLOWISE_FILE_SIZE_LIMIT | 上传文件大小限制 | 字符串 | 50mb |
| DEBUG | 打印组件的日志 | 布尔值 | |
| BLOB_STORAGE_PATH | 存储位置 | 字符串 | `your-home-dir/.flowise/storage` |
| LOG_PATH | 存储日志文件的位置 | 字符串 | `your-path/Flowise/logs` |
| LOG_LEVEL | 日志的不同级别 | 枚举字符串: `error`, `info`, `verbose`, `debug` | `info` |
| APIKEY_PATH | 存储 API 密钥的位置 | 字符串 | `your-path/Flowise/packages/server` |
+1
View File
@@ -129,6 +129,7 @@ Flowise support different environment variables to configure your instance. You
| FLOWISE_PASSWORD | Password to login | String | |
| FLOWISE_FILE_SIZE_LIMIT | Upload File Size Limit | String | 50mb |
| DEBUG | Print logs from components | Boolean | |
| BLOB_STORAGE_PATH | Location where uploaded files are stored | String | `your-home-dir/.flowise/storage` |
| LOG_PATH | Location where log files are stored | String | `your-path/Flowise/logs` |
| LOG_LEVEL | Different levels of logs | Enum String: `error`, `info`, `verbose`, `debug` | `info` |
| APIKEY_PATH | Location where api keys are saved | String | `your-path/Flowise/packages/server` |
+1
View File
@@ -3,6 +3,7 @@ DATABASE_PATH=/root/.flowise
APIKEY_PATH=/root/.flowise
SECRETKEY_PATH=/root/.flowise
LOG_PATH=/root/.flowise/logs
BLOB_STORAGE_PATH=/root/.flowise/storage
# CORS_ORIGINS="*"
# IFRAME_ORIGINS="*"
+1
View File
@@ -31,5 +31,6 @@ If you like to persist your data (flows, logs, apikeys, credentials), set these
- APIKEY_PATH=/root/.flowise
- LOG_PATH=/root/.flowise/logs
- SECRETKEY_PATH=/root/.flowise
- BLOB_STORAGE_PATH=/root/.flowise/storage
Flowise also support different environment variables to configure your instance. Read [more](https://docs.flowiseai.com/environment-variables)
+1
View File
@@ -26,6 +26,7 @@ services:
- FLOWISE_SECRETKEY_OVERWRITE=${FLOWISE_SECRETKEY_OVERWRITE}
- LOG_LEVEL=${LOG_LEVEL}
- LOG_PATH=${LOG_PATH}
- BLOB_STORAGE_PATH=${BLOB_STORAGE_PATH}
- DISABLE_FLOWISE_TELEMETRY=${DISABLE_FLOWISE_TELEMETRY}
ports:
- '${PORT}:${PORT}'
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "flowise",
"version": "1.5.0",
"version": "1.5.1",
"private": true,
"homepage": "https://flowiseai.com",
"workspaces": [
@@ -0,0 +1,23 @@
import { INodeParams, INodeCredential } from '../src/Interface'
class AssemblyAIApi implements INodeCredential {
label: string
name: string
version: number
inputs: INodeParams[]
constructor() {
this.label = 'AssemblyAI API'
this.name = 'assemblyAIApi'
this.version = 1.0
this.inputs = [
{
label: 'AssemblyAI Api Key',
name: 'assemblyAIApiKey',
type: 'password'
}
]
}
}
module.exports = { credClass: AssemblyAIApi }
@@ -4,13 +4,15 @@ import { BaseChatModel } from '@langchain/core/language_models/chat_models'
import { AIMessage, BaseMessage, HumanMessage } from '@langchain/core/messages'
import { ChainValues } from '@langchain/core/utils/types'
import { AgentStep } from '@langchain/core/agents'
import { renderTemplate } from '@langchain/core/prompts'
import { renderTemplate, MessagesPlaceholder } from '@langchain/core/prompts'
import { RunnableSequence } from '@langchain/core/runnables'
import { ChatConversationalAgent } from 'langchain/agents'
import { getBaseClasses } from '../../../src/utils'
import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler'
import { FlowiseMemory, ICommonObject, IMessage, INode, INodeData, INodeParams } from '../../../src/Interface'
import { AgentExecutor } from '../../../src/agents'
import { ChatOpenAI } from '../../chatmodels/ChatOpenAI/FlowiseChatOpenAI'
import { addImagesToMessages } from '../../../src/multiModalUtils'
const DEFAULT_PREFIX = `Assistant is a large language model trained by OpenAI.
@@ -81,12 +83,18 @@ class ConversationalAgent_Agents implements INode {
}
async init(nodeData: INodeData, input: string, options: ICommonObject): Promise<any> {
return prepareAgent(nodeData, { sessionId: this.sessionId, chatId: options.chatId, input }, options.chatHistory)
return prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input }, options.chatHistory)
}
async run(nodeData: INodeData, input: string, options: ICommonObject): Promise<string> {
const memory = nodeData.inputs?.memory as FlowiseMemory
const executor = await prepareAgent(nodeData, { sessionId: this.sessionId, chatId: options.chatId, input }, options.chatHistory)
const executor = await prepareAgent(
nodeData,
options,
{ sessionId: this.sessionId, chatId: options.chatId, input },
options.chatHistory
)
const loggerHandler = new ConsoleCallbackHandler(options.logger)
const callbacks = await additionalCallbacks(nodeData, options)
@@ -120,6 +128,7 @@ class ConversationalAgent_Agents implements INode {
const prepareAgent = async (
nodeData: INodeData,
options: ICommonObject,
flowObj: { sessionId?: string; chatId?: string; input?: string },
chatHistory: IMessage[] = []
) => {
@@ -131,11 +140,6 @@ const prepareAgent = async (
const memoryKey = memory.memoryKey ? memory.memoryKey : 'chat_history'
const inputKey = memory.inputKey ? memory.inputKey : 'input'
/** Bind a stop token to the model */
const modelWithStop = model.bind({
stop: ['\nObservation']
})
const outputParser = ChatConversationalAgent.getDefaultOutputParser({
llm: model,
toolNames: tools.map((tool) => tool.name)
@@ -146,6 +150,41 @@ const prepareAgent = async (
outputParser
})
if (model instanceof ChatOpenAI) {
let humanImageMessages: HumanMessage[] = []
const messageContent = addImagesToMessages(nodeData, options, model.multiModalOption)
if (messageContent?.length) {
// Change model to gpt-4-vision
model.modelName = 'gpt-4-vision-preview'
// Change default max token to higher when using gpt-4-vision
model.maxTokens = 1024
for (const msg of messageContent) {
humanImageMessages.push(new HumanMessage({ content: [msg] }))
}
// Pop the `agent_scratchpad` MessagePlaceHolder
let messagePlaceholder = prompt.promptMessages.pop() as MessagesPlaceholder
// Add the HumanMessage for images
prompt.promptMessages.push(...humanImageMessages)
// Add the `agent_scratchpad` MessagePlaceHolder back
prompt.promptMessages.push(messagePlaceholder)
} else {
// revert to previous values if image upload is empty
model.modelName = model.configuredModel
model.maxTokens = model.configuredMaxToken
}
}
/** Bind a stop token to the model */
const modelWithStop = model.bind({
stop: ['\nObservation']
})
const runnableAgent = RunnableSequence.from([
{
[inputKey]: (i: { input: string; steps: AgentStep[] }) => i.input,
@@ -166,7 +205,7 @@ const prepareAgent = async (
sessionId: flowObj?.sessionId,
chatId: flowObj?.chatId,
input: flowObj?.input,
verbose: process.env.DEBUG === 'true' ? true : false
verbose: process.env.DEBUG === 'true'
})
return executor
@@ -1,12 +1,17 @@
import { flatten } from 'lodash'
import { AgentExecutor, createReactAgent } from 'langchain/agents'
import { AgentExecutor } from 'langchain/agents'
import { pull } from 'langchain/hub'
import { Tool } from '@langchain/core/tools'
import type { PromptTemplate } from '@langchain/core/prompts'
import { BaseChatModel } from '@langchain/core/language_models/chat_models'
import { additionalCallbacks } from '../../../src/handler'
import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface'
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
@@ -18,11 +23,12 @@ class MRKLAgentChat_Agents implements INode {
category: string
baseClasses: string[]
inputs: INodeParams[]
sessionId?: string
constructor() {
constructor(fields?: { sessionId?: string }) {
this.label = 'ReAct Agent for Chat Models'
this.name = 'mrklAgentChat'
this.version = 2.0
this.version = 3.0
this.type = 'AgentExecutor'
this.category = 'Agents'
this.icon = 'agent.svg'
@@ -39,8 +45,14 @@ class MRKLAgentChat_Agents implements INode {
label: 'Chat Model',
name: 'model',
type: 'BaseChatModel'
},
{
label: 'Memory',
name: 'memory',
type: 'BaseChatMemory'
}
]
this.sessionId = fields?.sessionId
}
async init(): Promise<any> {
@@ -48,30 +60,67 @@ class MRKLAgentChat_Agents implements INode {
}
async run(nodeData: INodeData, input: string, options: ICommonObject): Promise<string> {
const memory = nodeData.inputs?.memory as FlowiseMemory
const model = nodeData.inputs?.model as BaseChatModel
let tools = nodeData.inputs?.tools as Tool[]
tools = flatten(tools)
const promptWithChat = await pull<PromptTemplate>('hwchase17/react-chat')
const prompt = await pull<PromptTemplate>('hwchase17/react-chat')
let chatPromptTemplate = undefined
if (model instanceof ChatOpenAI) {
const messageContent = addImagesToMessages(nodeData, options, model.multiModalOption)
if (messageContent?.length) {
// Change model to gpt-4-vision
model.modelName = 'gpt-4-vision-preview'
// Change default max token to higher when using gpt-4-vision
model.maxTokens = 1024
const oldTemplate = prompt.template as string
chatPromptTemplate = ChatPromptTemplate.fromMessages([HumanMessagePromptTemplate.fromTemplate(oldTemplate)])
chatPromptTemplate.promptMessages.push(new HumanMessage({ content: messageContent }))
} else {
// revert to previous values if image upload is empty
model.modelName = model.configuredModel
model.maxTokens = model.configuredMaxToken
}
}
const agent = await createReactAgent({
llm: model,
tools,
prompt: promptWithChat
prompt: chatPromptTemplate ?? prompt
})
const executor = new AgentExecutor({
agent,
tools,
verbose: process.env.DEBUG === 'true' ? true : false
verbose: process.env.DEBUG === 'true'
})
const callbacks = await additionalCallbacks(nodeData, options)
const result = await executor.invoke({
input,
callbacks
})
const prevChatHistory = options.chatHistory
const chatHistory = ((await memory.getChatMessages(this.sessionId, false, prevChatHistory)) as IMessage[]) ?? []
const chatHistoryString = chatHistory.map((hist) => hist.message).join('\\n')
const result = await executor.invoke({ input, chat_history: chatHistoryString }, { callbacks })
await memory.addChatMessages(
[
{
text: input,
type: 'userMessage'
},
{
text: result?.output,
type: 'apiMessage'
}
],
this.sessionId
)
return result?.output
}
@@ -1,5 +1,5 @@
import { flatten } from 'lodash'
import { AgentExecutor, createReactAgent } from 'langchain/agents'
import { AgentExecutor } from 'langchain/agents'
import { pull } from 'langchain/hub'
import { Tool } from '@langchain/core/tools'
import type { PromptTemplate } from '@langchain/core/prompts'
@@ -7,6 +7,7 @@ import { BaseLanguageModel } from 'langchain/base_language'
import { additionalCallbacks } from '../../../src/handler'
import { getBaseClasses } from '../../../src/utils'
import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface'
import { createReactAgent } from '../../../src/agents'
class MRKLAgentLLM_Agents implements INode {
label: string
@@ -68,10 +69,7 @@ class MRKLAgentLLM_Agents implements INode {
const callbacks = await additionalCallbacks(nodeData, options)
const result = await executor.invoke({
input,
callbacks
})
const result = await executor.invoke({ input }, { callbacks })
return result?.output
}
@@ -1,14 +1,16 @@
import { ConversationChain } from 'langchain/chains'
import { ChatPromptTemplate, HumanMessagePromptTemplate, MessagesPlaceholder, SystemMessagePromptTemplate } from '@langchain/core/prompts'
import { BaseChatModel } from '@langchain/core/language_models/chat_models'
import { RunnableSequence } from '@langchain/core/runnables'
import { StringOutputParser } from '@langchain/core/output_parsers'
import { HumanMessage } from '@langchain/core/messages'
import { ConsoleCallbackHandler as LCConsoleCallbackHandler } from '@langchain/core/tracers/console'
import { ConversationChain } from 'langchain/chains'
import { FlowiseMemory, ICommonObject, IMessage, INode, INodeData, INodeParams } from '../../../src/Interface'
import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler'
import { getBaseClasses, handleEscapeCharacters } from '../../../src/utils'
import { checkInputs, Moderation, streamResponse } from '../../moderation/Moderation'
import { formatResponse } from '../../outputparsers/OutputParserHelpers'
import { addImagesToMessages } from '../../../src/multiModalUtils'
import { ChatOpenAI } from '../../chatmodels/ChatOpenAI/FlowiseChatOpenAI'
import { FlowiseMemory, ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface'
import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler'
import { getBaseClasses, handleEscapeCharacters } from '../../../src/utils'
let systemMessage = `The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.`
const inputKey = 'input'
@@ -86,12 +88,14 @@ class ConversationChain_Chains implements INode {
}
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
const chain = prepareChain(nodeData, this.sessionId, options.chatHistory)
const chain = prepareChain(nodeData, options, this.sessionId)
return chain
}
async run(nodeData: INodeData, input: string, options: ICommonObject): Promise<string | object> {
const memory = nodeData.inputs?.memory
const chain = prepareChain(nodeData, options, this.sessionId)
const moderations = nodeData.inputs?.inputModeration as Moderation[]
if (moderations && moderations.length > 0) {
@@ -105,8 +109,6 @@ class ConversationChain_Chains implements INode {
}
}
const chain = prepareChain(nodeData, this.sessionId, options.chatHistory)
const loggerHandler = new ConsoleCallbackHandler(options.logger)
const additionalCallback = await additionalCallbacks(nodeData, options)
@@ -143,7 +145,7 @@ class ConversationChain_Chains implements INode {
}
}
const prepareChatPrompt = (nodeData: INodeData) => {
const prepareChatPrompt = (nodeData: INodeData, humanImageMessages: HumanMessage[]) => {
const memory = nodeData.inputs?.memory as FlowiseMemory
const prompt = nodeData.inputs?.systemMessagePrompt as string
const chatPromptTemplate = nodeData.inputs?.chatPromptTemplate as ChatPromptTemplate
@@ -151,12 +153,10 @@ const prepareChatPrompt = (nodeData: INodeData) => {
if (chatPromptTemplate && chatPromptTemplate.promptMessages.length) {
const sysPrompt = chatPromptTemplate.promptMessages[0]
const humanPrompt = chatPromptTemplate.promptMessages[chatPromptTemplate.promptMessages.length - 1]
const chatPrompt = ChatPromptTemplate.fromMessages([
sysPrompt,
new MessagesPlaceholder(memory.memoryKey ?? 'chat_history'),
humanPrompt
])
const messages = [sysPrompt, new MessagesPlaceholder(memory.memoryKey ?? 'chat_history'), humanPrompt]
if (humanImageMessages.length) messages.push(...humanImageMessages)
const chatPrompt = ChatPromptTemplate.fromMessages(messages)
if ((chatPromptTemplate as any).promptValues) {
// @ts-ignore
chatPrompt.promptValues = (chatPromptTemplate as any).promptValues
@@ -165,21 +165,46 @@ const prepareChatPrompt = (nodeData: INodeData) => {
return chatPrompt
}
const chatPrompt = ChatPromptTemplate.fromMessages([
const messages = [
SystemMessagePromptTemplate.fromTemplate(prompt ? prompt : systemMessage),
new MessagesPlaceholder(memory.memoryKey ?? 'chat_history'),
HumanMessagePromptTemplate.fromTemplate(`{${inputKey}}`)
])
]
if (humanImageMessages.length) messages.push(...(humanImageMessages as any[]))
const chatPrompt = ChatPromptTemplate.fromMessages(messages)
return chatPrompt
}
const prepareChain = (nodeData: INodeData, sessionId?: string, chatHistory: IMessage[] = []) => {
const model = nodeData.inputs?.model as BaseChatModel
const prepareChain = (nodeData: INodeData, options: ICommonObject, sessionId?: string) => {
const chatHistory = options.chatHistory
let model = nodeData.inputs?.model as ChatOpenAI
const memory = nodeData.inputs?.memory as FlowiseMemory
const memoryKey = memory.memoryKey ?? 'chat_history'
const chatPrompt = prepareChatPrompt(nodeData)
let humanImageMessages: HumanMessage[] = []
if (model instanceof ChatOpenAI) {
const messageContent = addImagesToMessages(nodeData, options, model.multiModalOption)
if (messageContent?.length) {
// Change model to gpt-4-vision
model.modelName = 'gpt-4-vision-preview'
// Change default max token to higher when using gpt-4-vision
model.maxTokens = 1024
for (const msg of messageContent) {
humanImageMessages.push(new HumanMessage({ content: [msg] }))
}
} else {
// revert to previous values if image upload is empty
model.modelName = model.configuredModel
model.maxTokens = model.configuredMaxToken
}
}
const chatPrompt = prepareChatPrompt(nodeData, humanImageMessages)
let promptVariables = {}
const promptValuesRaw = (chatPrompt as any).promptValues
if (promptValuesRaw) {
@@ -203,7 +228,7 @@ const prepareChain = (nodeData: INodeData, sessionId?: string, chatHistory: IMes
},
...promptVariables
},
chatPrompt,
prepareChatPrompt(nodeData, humanImageMessages),
model,
new StringOutputParser()
])
@@ -1,5 +1,6 @@
import { BaseLanguageModel, BaseLanguageModelCallOptions } from '@langchain/core/language_models/base'
import { BaseLLMOutputParser, BaseOutputParser } from '@langchain/core/output_parsers'
import { ChatPromptTemplate, FewShotPromptTemplate, PromptTemplate, HumanMessagePromptTemplate } from '@langchain/core/prompts'
import { OutputFixingParser } from 'langchain/output_parsers'
import { LLMChain } from 'langchain/chains'
import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
@@ -7,6 +8,9 @@ import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from
import { getBaseClasses, handleEscapeCharacters } from '../../../src/utils'
import { checkInputs, Moderation, streamResponse } from '../../moderation/Moderation'
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
@@ -160,12 +164,7 @@ const runPrediction = async (
const socketIO = isStreaming ? options.socketIO : undefined
const socketIOClientId = isStreaming ? options.socketIOClientId : ''
const moderations = nodeData.inputs?.inputModeration as Moderation[]
/**
* Apply string transformation to reverse converted special chars:
* FROM: { "value": "hello i am benFLOWISE_NEWLINEFLOWISE_NEWLINEFLOWISE_TABhow are you?" }
* TO: { "value": "hello i am ben\n\n\thow are you?" }
*/
const promptValues = handleEscapeCharacters(promptValuesRaw, true)
let model = nodeData.inputs?.model as ChatOpenAI
if (moderations && moderations.length > 0) {
try {
@@ -178,6 +177,46 @@ const runPrediction = async (
}
}
/**
* Apply string transformation to reverse converted special chars:
* FROM: { "value": "hello i am benFLOWISE_NEWLINEFLOWISE_NEWLINEFLOWISE_TABhow are you?" }
* TO: { "value": "hello i am ben\n\n\thow are you?" }
*/
const promptValues = handleEscapeCharacters(promptValuesRaw, true)
const messageContent = addImagesToMessages(nodeData, options, model.multiModalOption)
if (chain.llm instanceof ChatOpenAI) {
const chatOpenAI = chain.llm as ChatOpenAI
if (messageContent?.length) {
// Change model to gpt-4-vision && max token to higher when using gpt-4-vision
chatOpenAI.modelName = 'gpt-4-vision-preview'
chatOpenAI.maxTokens = 1024
// Add image to the message
if (chain.prompt instanceof PromptTemplate) {
const existingPromptTemplate = chain.prompt.template as string
let newChatPromptTemplate = ChatPromptTemplate.fromMessages([
HumanMessagePromptTemplate.fromTemplate(existingPromptTemplate)
])
newChatPromptTemplate.promptMessages.push(new HumanMessage({ content: messageContent }))
chain.prompt = newChatPromptTemplate
} else if (chain.prompt instanceof ChatPromptTemplate) {
chain.prompt.promptMessages.push(new HumanMessage({ content: messageContent }))
} else if (chain.prompt instanceof FewShotPromptTemplate) {
let existingFewShotPromptTemplate = chain.prompt.examplePrompt.template as string
let newFewShotPromptTemplate = ChatPromptTemplate.fromMessages([
HumanMessagePromptTemplate.fromTemplate(existingFewShotPromptTemplate)
])
newFewShotPromptTemplate.promptMessages.push(new HumanMessage({ content: messageContent }))
// @ts-ignore
chain.prompt.examplePrompt = newFewShotPromptTemplate
}
} else {
// revert to previous values if image upload is empty
chatOpenAI.modelName = model.configuredModel
chatOpenAI.maxTokens = model.configuredMaxToken
}
}
if (promptValues && inputVariables.length > 0) {
let seen: string[] = []
@@ -1,8 +1,10 @@
import { ChatOpenAI, OpenAIChatInput } from '@langchain/openai'
import type { ClientOptions } from 'openai'
import { ChatOpenAI as LangchainChatOpenAI, OpenAIChatInput, AzureOpenAIInput, LegacyOpenAIInput } from '@langchain/openai'
import { BaseCache } from '@langchain/core/caches'
import { BaseLLMParams } from '@langchain/core/language_models/llms'
import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface'
import { BaseChatModelParams } from '@langchain/core/language_models/chat_models'
import { ICommonObject, IMultiModalOption, INode, INodeData, INodeParams } from '../../../src/Interface'
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
import { ChatOpenAI } from './FlowiseChatOpenAI'
class ChatOpenAI_ChatModels implements INode {
label: string
@@ -19,12 +21,12 @@ class ChatOpenAI_ChatModels implements INode {
constructor() {
this.label = 'ChatOpenAI'
this.name = 'chatOpenAI'
this.version = 3.0
this.version = 5.0
this.type = 'ChatOpenAI'
this.icon = 'openai.svg'
this.category = 'Chat Models'
this.description = 'Wrapper around OpenAI large language models that use the Chat endpoint'
this.baseClasses = [this.type, ...getBaseClasses(ChatOpenAI)]
this.baseClasses = [this.type, ...getBaseClasses(LangchainChatOpenAI)]
this.credential = {
label: 'Connect Credential',
name: 'credential',
@@ -168,6 +170,38 @@ class ChatOpenAI_ChatModels implements INode {
type: 'json',
optional: true,
additionalParams: true
},
{
label: 'Allow Image Uploads',
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',
default: false,
optional: true
},
{
label: 'Image Resolution',
description: 'This parameter controls the resolution in which the model views the image.',
name: 'imageResolution',
type: 'options',
options: [
{
label: 'Low',
name: 'low'
},
{
label: 'High',
name: 'high'
},
{
label: 'Auto',
name: 'auto'
}
],
default: 'low',
optional: false,
additionalParams: true
}
]
}
@@ -184,12 +218,17 @@ class ChatOpenAI_ChatModels implements INode {
const basePath = nodeData.inputs?.basepath as string
const baseOptions = nodeData.inputs?.baseOptions
const allowImageUploads = nodeData.inputs?.allowImageUploads as boolean
const imageResolution = nodeData.inputs?.imageResolution as string
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const openAIApiKey = getCredentialParam('openAIApiKey', credentialData, nodeData)
const cache = nodeData.inputs?.cache as BaseCache
const obj: Partial<OpenAIChatInput> & BaseLLMParams & { openAIApiKey?: string } = {
const obj: Partial<OpenAIChatInput> &
Partial<AzureOpenAIInput> &
BaseChatModelParams & { configuration?: ClientOptions & LegacyOpenAIInput; multiModalOption?: IMultiModalOption } = {
temperature: parseFloat(temperature),
modelName,
openAIApiKey,
@@ -212,10 +251,24 @@ class ChatOpenAI_ChatModels implements INode {
throw new Error("Invalid JSON in the ChatOpenAI's BaseOptions: " + exception)
}
}
const model = new ChatOpenAI(obj, {
basePath,
baseOptions: parsedBaseOptions
})
if (basePath || parsedBaseOptions) {
obj.configuration = {
baseURL: basePath,
baseOptions: parsedBaseOptions
}
}
const multiModalOption: IMultiModalOption = {
image: {
allowImageUploads: allowImageUploads ?? false,
imageResolution
}
}
obj.multiModalOption = multiModalOption
const model = new ChatOpenAI(nodeData.id, obj)
return model
}
}
@@ -0,0 +1,38 @@
import type { ClientOptions } from 'openai'
import {
ChatOpenAI as LangchainChatOpenAI,
OpenAIChatInput,
LegacyOpenAIInput,
AzureOpenAIInput,
ChatOpenAICallOptions
} from '@langchain/openai'
import { BaseChatModelParams } from '@langchain/core/language_models/chat_models'
import { IMultiModalOption } from '../../../src'
import { BaseMessageLike, LLMResult } from 'langchain/schema'
import { Callbacks } from '@langchain/core/callbacks/manager'
export class ChatOpenAI extends LangchainChatOpenAI {
configuredModel: string
configuredMaxToken?: number
multiModalOption?: IMultiModalOption
id: string
constructor(
id: string,
fields?: Partial<OpenAIChatInput> &
Partial<AzureOpenAIInput> &
BaseChatModelParams & { configuration?: ClientOptions & LegacyOpenAIInput; multiModalOption?: IMultiModalOption },
/** @deprecated */
configuration?: ClientOptions & LegacyOpenAIInput
) {
super(fields, configuration)
this.id = id
this.multiModalOption = fields?.multiModalOption
this.configuredModel = fields?.modelName ?? 'gpt-3.5-turbo'
this.configuredMaxToken = fields?.maxTokens
}
async generate(messages: BaseMessageLike[][], options?: string[] | ChatOpenAICallOptions, callbacks?: Callbacks): Promise<LLMResult> {
return super.generate(messages, options, callbacks)
}
}
@@ -1,4 +1,5 @@
import { Redis } from '@upstash/redis'
import { Redis, RedisConfigNodejs } from '@upstash/redis'
import { isEqual } from 'lodash'
import { BufferMemory, BufferMemoryInput } from 'langchain/memory'
import { UpstashRedisChatMessageHistory } from '@langchain/community/stores/message/upstash_redis'
import { mapStoredMessageToChatMessage, AIMessage, HumanMessage, StoredMessage, BaseMessage } from '@langchain/core/messages'
@@ -6,6 +7,24 @@ import { FlowiseMemory, IMessage, INode, INodeData, INodeParams, MemoryMethods,
import { convertBaseMessagetoIMessage, getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
import { ICommonObject } from '../../../src/Interface'
let redisClientSingleton: Redis
let redisClientOption: RedisConfigNodejs
const getRedisClientbyOption = (option: RedisConfigNodejs) => {
if (!redisClientSingleton) {
// if client doesn't exists
redisClientSingleton = new Redis(option)
redisClientOption = option
return redisClientSingleton
} else if (redisClientSingleton && !isEqual(option, redisClientOption)) {
// if client exists but option changed
redisClientSingleton = new Redis(option)
redisClientOption = option
return redisClientSingleton
}
return redisClientSingleton
}
class UpstashRedisBackedChatMemory_Memory implements INode {
label: string
name: string
@@ -75,7 +94,7 @@ const initalizeUpstashRedis = async (nodeData: INodeData, options: ICommonObject
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const upstashRestToken = getCredentialParam('upstashRestToken', credentialData, nodeData)
const client = new Redis({
const client = getRedisClientbyOption({
url: baseURL,
token: upstashRestToken
})
@@ -17,7 +17,7 @@ class ZepMemory_Memory implements INode {
inputs: INodeParams[]
constructor() {
this.label = 'Zep Memory'
this.label = 'Zep Memory - Open Source'
this.name = 'ZepMemory'
this.version = 2.0
this.type = 'ZepMemory'
@@ -97,11 +97,11 @@ class ZepMemory_Memory implements INode {
}
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
return await initalizeZep(nodeData, options)
return await initializeZep(nodeData, options)
}
}
const initalizeZep = async (nodeData: INodeData, options: ICommonObject): Promise<ZepMemory> => {
const initializeZep = async (nodeData: INodeData, options: ICommonObject): Promise<ZepMemory> => {
const baseURL = nodeData.inputs?.baseURL as string
const aiPrefix = nodeData.inputs?.aiPrefix as string
const humanPrefix = nodeData.inputs?.humanPrefix as string
@@ -0,0 +1,181 @@
import { IMessage, INode, INodeData, INodeParams, MemoryMethods, MessageType } from '../../../src/Interface'
import { convertBaseMessagetoIMessage, getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
import { ZepMemory, ZepMemoryInput } from '@getzep/zep-cloud/langchain'
import { ICommonObject } from '../../../src'
import { InputValues, MemoryVariables, OutputValues } from 'langchain/memory'
import { BaseMessage } from 'langchain/schema'
class ZepMemoryCloud_Memory 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 = 'Zep Memory - Cloud'
this.name = 'ZepMemoryCloud'
this.version = 2.0
this.type = 'ZepMemory'
this.icon = 'zep.svg'
this.category = 'Memory'
this.description = 'Summarizes the conversation and stores the memory in zep server'
this.baseClasses = [this.type, ...getBaseClasses(ZepMemory)]
this.credential = {
label: 'Connect Credential',
name: 'credential',
type: 'credential',
optional: true,
description: 'Configure JWT authentication on your Zep instance (Optional)',
credentialNames: ['zepMemoryApi']
}
this.inputs = [
{
label: 'Session Id',
name: 'sessionId',
type: 'string',
description:
'If not specified, a random id will be used. Learn <a target="_blank" href="https://docs.flowiseai.com/memory/long-term-memory#ui-and-embedded-chat">more</a>',
default: '',
additionalParams: true,
optional: true
},
{
label: 'Memory Type',
name: 'memoryType',
type: 'string',
default: 'perpetual',
description: 'Zep Memory Type, can be perpetual or message_window',
additionalParams: true
},
{
label: 'AI Prefix',
name: 'aiPrefix',
type: 'string',
default: 'ai',
additionalParams: true
},
{
label: 'Human Prefix',
name: 'humanPrefix',
type: 'string',
default: 'human',
additionalParams: true
},
{
label: 'Memory Key',
name: 'memoryKey',
type: 'string',
default: 'chat_history',
additionalParams: true
},
{
label: 'Input Key',
name: 'inputKey',
type: 'string',
default: 'input',
additionalParams: true
},
{
label: 'Output Key',
name: 'outputKey',
type: 'string',
default: 'text',
additionalParams: true
}
]
}
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
return await initializeZep(nodeData, options)
}
}
const initializeZep = async (nodeData: INodeData, options: ICommonObject): Promise<ZepMemory> => {
const aiPrefix = nodeData.inputs?.aiPrefix as string
const humanPrefix = nodeData.inputs?.humanPrefix as string
const memoryKey = nodeData.inputs?.memoryKey as string
const inputKey = nodeData.inputs?.inputKey as string
const memoryType = nodeData.inputs?.memoryType as 'perpetual' | 'message_window'
const sessionId = nodeData.inputs?.sessionId as string
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const apiKey = getCredentialParam('apiKey', credentialData, nodeData)
const obj: ZepMemoryInput & ZepMemoryExtendedInput = {
apiKey,
aiPrefix,
humanPrefix,
memoryKey,
sessionId,
inputKey,
memoryType: memoryType,
returnMessages: true
}
return new ZepMemoryExtended(obj)
}
interface ZepMemoryExtendedInput {
memoryType?: 'perpetual' | 'message_window'
}
class ZepMemoryExtended extends ZepMemory implements MemoryMethods {
memoryType: 'perpetual' | 'message_window'
constructor(fields: ZepMemoryInput & ZepMemoryExtendedInput) {
super(fields)
this.memoryType = fields.memoryType ?? 'perpetual'
}
async loadMemoryVariables(values: InputValues, overrideSessionId = ''): Promise<MemoryVariables> {
if (overrideSessionId) {
this.sessionId = overrideSessionId
}
return super.loadMemoryVariables({ ...values, memoryType: this.memoryType })
}
async saveContext(inputValues: InputValues, outputValues: OutputValues, overrideSessionId = ''): Promise<void> {
if (overrideSessionId) {
this.sessionId = overrideSessionId
}
return super.saveContext(inputValues, outputValues)
}
async clear(overrideSessionId = ''): Promise<void> {
if (overrideSessionId) {
this.sessionId = overrideSessionId
}
return super.clear()
}
async getChatMessages(overrideSessionId = '', returnBaseMessages = false): Promise<IMessage[] | BaseMessage[]> {
const id = overrideSessionId ? overrideSessionId : this.sessionId
const memoryVariables = await this.loadMemoryVariables({}, id)
const baseMessages = memoryVariables[this.memoryKey]
return returnBaseMessages ? baseMessages : convertBaseMessagetoIMessage(baseMessages)
}
async addChatMessages(msgArray: { text: string; type: MessageType }[], overrideSessionId = ''): Promise<void> {
const id = overrideSessionId ? overrideSessionId : this.sessionId
const input = msgArray.find((msg) => msg.type === 'userMessage')
const output = msgArray.find((msg) => msg.type === 'apiMessage')
const inputValues = { [this.inputKey ?? 'input']: input?.text }
const outputValues = { output: output?.text }
await this.saveContext(inputValues, outputValues, id)
}
async clearChatMessages(overrideSessionId = ''): Promise<void> {
const id = overrideSessionId ? overrideSessionId : this.sessionId
await this.clear(id)
}
}
module.exports = { nodeClass: ZepMemoryCloud_Memory }
@@ -0,0 +1,19 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="16" cy="16" r="14" fill="url(#paint0_linear_119_15736)"/>
<path d="M12.6665 9.33333V12.6667H19.3332V9.33333C19.3332 8.59695 18.7362 8 17.9998 8H13.9998C13.2635 8 12.6665 8.59695 12.6665 9.33333Z" fill="white" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19.3333 14.6667H12.6667C12.2985 14.6667 12 14.9652 12 15.3334V18.0001C12 18.3683 12.2985 18.6667 12.6667 18.6667H19.3333C19.7015 18.6667 20 18.3683 20 18.0001V15.3334C20 14.9652 19.7015 14.6667 19.3333 14.6667Z" fill="white" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 14.6667V20.0001" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M22 14.6667V20.0001" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14 20.6667V24.0001" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M18 20.6667V24.0001" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.3335 24.6667H14.6668" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.3335 24.6667H18.6668" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="14.3889" cy="10.0556" r="0.888889" fill="#7734A6"/>
<circle cx="17.7224" cy="10.0556" r="0.888889" fill="#7734A6"/>
<defs>
<linearGradient id="paint0_linear_119_15736" x1="5.5" y1="6.5" x2="24.5" y2="26" gradientUnits="userSpaceOnUse">
<stop stop-color="#6A31A6"/>
<stop offset="1" stop-color="#9A3BA3"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

@@ -0,0 +1,33 @@
import { INode, INodeParams } from '../../../src/Interface'
class AssemblyAI_SpeechToText implements INode {
label: string
name: string
version: number
description: string
type: string
icon: string
category: string
baseClasses: string[]
inputs?: INodeParams[]
credential: INodeParams
constructor() {
this.label = 'AssemblyAI'
this.name = 'assemblyAI'
this.version = 1.0
this.type = 'AssemblyAI'
this.icon = 'assemblyai.png'
this.category = 'SpeechToText'
this.baseClasses = [this.type]
this.inputs = []
this.credential = {
label: 'Connect Credential',
name: 'credential',
type: 'credential',
credentialNames: ['assemblyAIApi']
}
}
}
module.exports = { nodeClass: AssemblyAI_SpeechToText }
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

@@ -22,7 +22,7 @@ class Zep_VectorStores implements INode {
outputs: INodeOutputsValue[]
constructor() {
this.label = 'Zep'
this.label = 'Zep Collection - Open Source'
this.name = 'zep'
this.version = 2.0
this.type = 'Zep'
@@ -20,7 +20,7 @@ class Zep_Existing_VectorStores implements INode {
outputs: INodeOutputsValue[]
constructor() {
this.label = 'Zep Load Existing Index'
this.label = 'Zep Load Existing Index - Open Source'
this.name = 'zepExistingIndex'
this.version = 1.0
this.type = 'Zep'
@@ -20,7 +20,7 @@ class Zep_Upsert_VectorStores implements INode {
outputs: INodeOutputsValue[]
constructor() {
this.label = 'Zep Upsert Document'
this.label = 'Zep Upsert Document - Open Source'
this.name = 'zepUpsert'
this.version = 1.0
this.type = 'Zep'
@@ -0,0 +1,231 @@
import { flatten } from 'lodash'
import { IDocument, ZepClient } from '@getzep/zep-cloud'
import { IZepConfig, ZepVectorStore } from '@getzep/zep-cloud/langchain'
import { Embeddings } from 'langchain/embeddings/base'
import { Document } from 'langchain/document'
import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
import { addMMRInputParams, resolveVectorStoreOrRetriever } from '../VectorStoreUtils'
import { FakeEmbeddings } from 'langchain/embeddings/fake'
class Zep_CloudVectorStores implements INode {
label: string
name: string
version: number
description: string
type: string
icon: string
category: string
badge: string
baseClasses: string[]
inputs: INodeParams[]
credential: INodeParams
outputs: INodeOutputsValue[]
constructor() {
this.label = 'Zep Collection - Cloud'
this.name = 'zepCloud'
this.version = 2.0
this.type = 'Zep'
this.icon = 'zep.svg'
this.category = 'Vector Stores'
this.description =
'Upsert embedded data and perform similarity or mmr search upon query using Zep, a fast and scalable building block for LLM apps'
this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
this.badge = 'NEW'
this.credential = {
label: 'Connect Credential',
name: 'credential',
type: 'credential',
optional: false,
description: 'Configure JWT authentication on your Zep instance (Optional)',
credentialNames: ['zepMemoryApi']
}
this.inputs = [
{
label: 'Document',
name: 'document',
type: 'Document',
list: true,
optional: true
},
{
label: 'Zep Collection',
name: 'zepCollection',
type: 'string',
placeholder: 'my-first-collection'
},
{
label: 'Zep Metadata Filter',
name: 'zepMetadataFilter',
type: 'json',
optional: true,
additionalParams: true
},
{
label: 'Top K',
name: 'topK',
description: 'Number of top results to fetch. Default to 4',
placeholder: '4',
type: 'number',
additionalParams: true,
optional: true
}
]
addMMRInputParams(this.inputs)
this.outputs = [
{
label: 'Zep Retriever',
name: 'retriever',
baseClasses: this.baseClasses
},
{
label: 'Zep Vector Store',
name: 'vectorStore',
baseClasses: [this.type, ...getBaseClasses(ZepVectorStore)]
}
]
}
//@ts-ignore
vectorStoreMethods = {
async upsert(nodeData: INodeData, options: ICommonObject): Promise<void> {
const zepCollection = nodeData.inputs?.zepCollection as string
const docs = nodeData.inputs?.document as Document[]
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const apiKey = getCredentialParam('apiKey', credentialData, nodeData)
const flattenDocs = docs && docs.length ? flatten(docs) : []
const finalDocs = []
for (let i = 0; i < flattenDocs.length; i += 1) {
if (flattenDocs[i] && flattenDocs[i].pageContent) {
finalDocs.push(new Document(flattenDocs[i]))
}
}
const client = await ZepClient.init(apiKey)
const zepConfig = {
apiKey: apiKey,
collectionName: zepCollection,
client
}
try {
await ZepVectorStore.fromDocuments(finalDocs, new FakeEmbeddings(), zepConfig)
} catch (e) {
throw new Error(e)
}
}
}
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
const zepCollection = nodeData.inputs?.zepCollection as string
const zepMetadataFilter = nodeData.inputs?.zepMetadataFilter
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const apiKey = getCredentialParam('apiKey', credentialData, nodeData)
const zepConfig: IZepConfig & Partial<ZepFilter> = {
apiKey,
collectionName: zepCollection
}
if (zepMetadataFilter) {
zepConfig.filter = typeof zepMetadataFilter === 'object' ? zepMetadataFilter : JSON.parse(zepMetadataFilter)
}
zepConfig.client = await ZepClient.init(zepConfig.apiKey)
const vectorStore = await ZepExistingVS.init(zepConfig)
return resolveVectorStoreOrRetriever(nodeData, vectorStore)
}
}
interface ZepFilter {
filter: Record<string, any>
}
function zepDocsToDocumentsAndScore(results: IDocument[]): [Document, number][] {
return results.map((d) => [
new Document({
pageContent: d.content,
metadata: d.metadata
}),
d.score ? d.score : 0
])
}
function assignMetadata(value: string | Record<string, unknown> | object | undefined): Record<string, unknown> | undefined {
if (typeof value === 'object' && value !== null) {
return value as Record<string, unknown>
}
if (value !== undefined) {
console.warn('Metadata filters must be an object, Record, or undefined.')
}
return undefined
}
class ZepExistingVS extends ZepVectorStore {
filter?: Record<string, any>
args?: IZepConfig & Partial<ZepFilter>
constructor(embeddings: Embeddings, args: IZepConfig & Partial<ZepFilter>) {
super(embeddings, args)
this.filter = args.filter
this.args = args
}
async initializeCollection(args: IZepConfig & Partial<ZepFilter>) {
this.client = await ZepClient.init(args.apiKey, args.apiUrl)
try {
this.collection = await this.client.document.getCollection(args.collectionName)
} catch (err) {
if (err instanceof Error) {
if (err.name === 'NotFoundError') {
await this.createNewCollection(args)
} else {
throw err
}
}
}
}
async createNewCollection(args: IZepConfig & Partial<ZepFilter>) {
this.collection = await this.client.document.addCollection({
name: args.collectionName,
description: args.description,
metadata: args.metadata
})
}
async similaritySearchVectorWithScore(
query: number[],
k: number,
filter?: Record<string, unknown> | undefined
): Promise<[Document, number][]> {
if (filter && this.filter) {
throw new Error('cannot provide both `filter` and `this.filter`')
}
const _filters = filter ?? this.filter
const ANDFilters = []
for (const filterKey in _filters) {
let filterVal = _filters[filterKey]
if (typeof filterVal === 'string') filterVal = `"${filterVal}"`
ANDFilters.push({ jsonpath: `$[*] ? (@.${filterKey} == ${filterVal})` })
}
const newfilter = {
where: { and: ANDFilters }
}
await this.initializeCollection(this.args!).catch((err) => {
console.error('Error initializing collection:', err)
throw err
})
const results = await this.collection.search(
{
embedding: new Float32Array(query),
metadata: assignMetadata(newfilter)
},
k
)
return zepDocsToDocumentsAndScore(results)
}
static async fromExistingIndex(embeddings: Embeddings, dbConfig: IZepConfig & Partial<ZepFilter>): Promise<ZepVectorStore> {
return new this(embeddings, dbConfig)
}
}
module.exports = { nodeClass: Zep_CloudVectorStores }
@@ -0,0 +1,19 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="16" cy="16" r="14" fill="url(#paint0_linear_119_15736)"/>
<path d="M12.6665 9.33333V12.6667H19.3332V9.33333C19.3332 8.59695 18.7362 8 17.9998 8H13.9998C13.2635 8 12.6665 8.59695 12.6665 9.33333Z" fill="white" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19.3333 14.6667H12.6667C12.2985 14.6667 12 14.9652 12 15.3334V18.0001C12 18.3683 12.2985 18.6667 12.6667 18.6667H19.3333C19.7015 18.6667 20 18.3683 20 18.0001V15.3334C20 14.9652 19.7015 14.6667 19.3333 14.6667Z" fill="white" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 14.6667V20.0001" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M22 14.6667V20.0001" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14 20.6667V24.0001" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M18 20.6667V24.0001" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.3335 24.6667H14.6668" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.3335 24.6667H18.6668" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="14.3889" cy="10.0556" r="0.888889" fill="#7734A6"/>
<circle cx="17.7224" cy="10.0556" r="0.888889" fill="#7734A6"/>
<defs>
<linearGradient id="paint0_linear_119_15736" x1="5.5" y1="6.5" x2="24.5" y2="26" gradientUnits="userSpaceOnUse">
<stop stop-color="#6A31A6"/>
<stop offset="1" stop-color="#9A3BA3"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+6 -4
View File
@@ -1,6 +1,6 @@
{
"name": "flowise-components",
"version": "1.6.0",
"version": "1.6.1",
"description": "Flowiseai Components",
"main": "dist/src/index",
"types": "dist/src/index.d.ts",
@@ -22,6 +22,7 @@
"@datastax/astra-db-ts": "^0.1.2",
"@dqbd/tiktoken": "^1.0.7",
"@elastic/elasticsearch": "^8.9.0",
"@getzep/zep-cloud": "npm:@getzep/zep-js@next",
"@getzep/zep-js": "^0.9.0",
"@gomomento/sdk": "^1.51.1",
"@gomomento/sdk-core": "^1.51.1",
@@ -45,6 +46,7 @@
"@upstash/redis": "1.22.1",
"@zilliz/milvus2-sdk-node": "^2.2.24",
"apify-client": "^2.7.1",
"assemblyai": "^4.2.2",
"axios": "1.6.2",
"cheerio": "^1.0.0-rc.12",
"chromadb": "^1.5.11",
@@ -62,9 +64,9 @@
"ioredis": "^5.3.2",
"jsonpointer": "^5.0.1",
"langchain": "^0.1.20",
"langfuse": "2.0.2",
"langfuse-langchain": "^2.6.2-alpha.0",
"langsmith": "0.0.63",
"langfuse": "3.1.0",
"langfuse-langchain": "^3.1.0",
"langsmith": "0.1.6",
"linkifyjs": "^4.1.1",
"llamaindex": "^0.0.48",
"lunary": "^0.6.16",
+29
View File
@@ -21,6 +21,8 @@ export type CommonType = string | number | boolean | undefined | null
export type MessageType = 'apiMessage' | 'userMessage'
export type ImageDetail = 'auto' | 'low' | 'high'
/**
* Others
*/
@@ -146,6 +148,33 @@ export interface IUsedTool {
toolOutput: string | object
}
export interface IFileUpload {
data?: string
type: string
name: string
mime: string
}
export interface IMultiModalOption {
image?: Record<string, any>
audio?: Record<string, any>
}
export type MessageContentText = {
type: 'text'
text: string
}
export type MessageContentImageUrl = {
type: 'image_url'
image_url:
| string
| {
url: string
detail?: ImageDetail
}
}
/**
* Classes
*/
+121 -3
View File
@@ -3,12 +3,23 @@ import { ChainValues } from '@langchain/core/utils/types'
import { AgentStep, AgentAction } from '@langchain/core/agents'
import { BaseMessage, FunctionMessage, AIMessage } from '@langchain/core/messages'
import { OutputParserException } from '@langchain/core/output_parsers'
import { BaseLanguageModel } from '@langchain/core/language_models/base'
import { CallbackManager, CallbackManagerForChainRun, Callbacks } from '@langchain/core/callbacks/manager'
import { ToolInputParsingException, Tool } from '@langchain/core/tools'
import { Runnable } from '@langchain/core/runnables'
import { ToolInputParsingException, Tool, StructuredToolInterface } from '@langchain/core/tools'
import { Runnable, RunnableSequence, RunnablePassthrough } from '@langchain/core/runnables'
import { Serializable } from '@langchain/core/load/serializable'
import { renderTemplate } from '@langchain/core/prompts'
import { BaseChain, SerializedLLMChain } from 'langchain/chains'
import { AgentExecutorInput, BaseSingleActionAgent, BaseMultiActionAgent, RunnableAgent, StoppingMethod } from 'langchain/agents'
import {
CreateReactAgentParams,
AgentExecutorInput,
AgentActionOutputParser,
BaseSingleActionAgent,
BaseMultiActionAgent,
RunnableAgent,
StoppingMethod
} from 'langchain/agents'
import { formatLogToString } from 'langchain/agents/format_scratchpad/log'
export const SOURCE_DOCUMENTS_PREFIX = '\n\n----FLOWISE_SOURCE_DOCUMENTS----\n\n'
type AgentFinish = {
@@ -647,3 +658,110 @@ export const formatAgentSteps = (steps: AgentStep[]): BaseMessage[] =>
return [new AIMessage(action.log)]
}
})
const renderTextDescription = (tools: StructuredToolInterface[]): string => {
return tools.map((tool) => `${tool.name}: ${tool.description}`).join('\n')
}
export const createReactAgent = async ({ llm, tools, prompt }: CreateReactAgentParams) => {
const missingVariables = ['tools', 'tool_names', 'agent_scratchpad'].filter((v) => !prompt.inputVariables.includes(v))
if (missingVariables.length > 0) {
throw new Error(`Provided prompt is missing required input variables: ${JSON.stringify(missingVariables)}`)
}
const toolNames = tools.map((tool) => tool.name)
const partialedPrompt = await prompt.partial({
tools: renderTextDescription(tools),
tool_names: toolNames.join(', ')
})
// TODO: Add .bind to core runnable interface.
const llmWithStop = (llm as BaseLanguageModel).bind({
stop: ['\nObservation:']
})
const agent = RunnableSequence.from([
RunnablePassthrough.assign({
//@ts-ignore
agent_scratchpad: (input: { steps: AgentStep[] }) => formatLogToString(input.steps)
}),
partialedPrompt,
llmWithStop,
new ReActSingleInputOutputParser({
toolNames
})
])
return agent
}
class ReActSingleInputOutputParser extends AgentActionOutputParser {
lc_namespace = ['langchain', 'agents', 'react']
private toolNames: string[]
private FINAL_ANSWER_ACTION = 'Final Answer:'
private FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE = 'Parsing LLM output produced both a final answer and a parse-able action:'
private FORMAT_INSTRUCTIONS = `Use the following format:
Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question`
constructor(fields: { toolNames: string[] }) {
super(...arguments)
this.toolNames = fields.toolNames
}
/**
* Parses the given text into an AgentAction or AgentFinish object. If an
* output fixing parser is defined, uses it to parse the text.
* @param text Text to parse.
* @returns Promise that resolves to an AgentAction or AgentFinish object.
*/
async parse(text: string): Promise<AgentAction | AgentFinish> {
const includesAnswer = text.includes(this.FINAL_ANSWER_ACTION)
const regex = /Action\s*\d*\s*:[\s]*(.*?)[\s]*Action\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)/
const actionMatch = text.match(regex)
if (actionMatch) {
if (includesAnswer) {
throw new Error(`${this.FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE}: ${text}`)
}
const action = actionMatch[1]
const actionInput = actionMatch[2]
const toolInput = actionInput.trim().replace(/"/g, '')
return {
tool: action,
toolInput,
log: text
}
}
if (includesAnswer) {
const finalAnswerText = text.split(this.FINAL_ANSWER_ACTION)[1].trim()
return {
returnValues: {
output: finalAnswerText
},
log: text
}
}
// Instead of throwing Error, we return a AgentFinish object
return { returnValues: { output: text }, log: text }
}
/**
* Returns the format instructions as a string. If the 'raw' option is
* true, returns the raw FORMAT_INSTRUCTIONS.
* @param options Options for getting the format instructions.
* @returns Format instructions as a string.
*/
getFormatInstructions(): string {
return renderTemplate(this.FORMAT_INSTRUCTIONS, 'f-string', {
tool_names: this.toolNames.join(', ')
})
}
}
+1
View File
@@ -6,3 +6,4 @@ dotenv.config({ path: envPath, override: true })
export * from './Interface'
export * from './utils'
export * from './speechToText'
@@ -0,0 +1,48 @@
import { ICommonObject, IFileUpload, IMultiModalOption, INodeData, MessageContentImageUrl } from './Interface'
import { ChatOpenAI as LangchainChatOpenAI } from 'langchain/chat_models/openai'
import path from 'path'
import { getStoragePath } from './utils'
import fs from 'fs'
export const addImagesToMessages = (
nodeData: INodeData,
options: ICommonObject,
multiModalOption?: IMultiModalOption
): MessageContentImageUrl[] => {
const imageContent: MessageContentImageUrl[] = []
let model = nodeData.inputs?.model
if (model instanceof LangchainChatOpenAI && multiModalOption) {
// Image Uploaded
if (multiModalOption.image && multiModalOption.image.allowImageUploads && options?.uploads && options?.uploads.length > 0) {
const imageUploads = getImageUploads(options.uploads)
for (const upload of imageUploads) {
let bf = upload.data
if (upload.type == 'stored-file') {
const filePath = path.join(getStoragePath(), options.chatflowid, options.chatId, upload.name)
// as the image is stored in the server, read the file and convert it to base64
const contents = fs.readFileSync(filePath)
bf = 'data:' + upload.mime + ';base64,' + contents.toString('base64')
imageContent.push({
type: 'image_url',
image_url: {
url: bf,
detail: multiModalOption.image.imageResolution ?? 'low'
}
})
}
}
}
}
return imageContent
}
export const getAudioUploads = (uploads: IFileUpload[]) => {
return uploads.filter((upload: IFileUpload) => upload.mime.startsWith('audio/'))
}
export const getImageUploads = (uploads: IFileUpload[]) => {
return uploads.filter((upload: IFileUpload) => upload.mime.startsWith('image/'))
}
+51
View File
@@ -0,0 +1,51 @@
import { ICommonObject, IFileUpload } from './Interface'
import { getCredentialData, getStoragePath } from './utils'
import { type ClientOptions, OpenAIClient } from '@langchain/openai'
import fs from 'fs'
import path from 'path'
import { AssemblyAI } from 'assemblyai'
export const convertSpeechToText = async (upload: IFileUpload, speechToTextConfig: ICommonObject, options: ICommonObject) => {
if (speechToTextConfig) {
const credentialId = speechToTextConfig.credentialId as string
const credentialData = await getCredentialData(credentialId ?? '', options)
const filePath = path.join(getStoragePath(), options.chatflowid, options.chatId, upload.name)
const audio_file = fs.createReadStream(filePath)
if (speechToTextConfig.name === 'openAIWhisper') {
const openAIClientOptions: ClientOptions = {
apiKey: credentialData.openAIApiKey
}
const openAIClient = new OpenAIClient(openAIClientOptions)
const transcription = await openAIClient.audio.transcriptions.create({
file: audio_file,
model: 'whisper-1',
language: speechToTextConfig?.language,
temperature: speechToTextConfig?.temperature ? parseFloat(speechToTextConfig.temperature) : undefined,
prompt: speechToTextConfig?.prompt
})
if (transcription?.text) {
return transcription.text
}
} else if (speechToTextConfig.name === 'assemblyAiTranscribe') {
const client = new AssemblyAI({
apiKey: credentialData.assemblyAIApiKey
})
const params = {
audio: audio_file,
speaker_labels: false
}
const transcription = await client.transcripts.transcribe(params)
if (transcription?.text) {
return transcription.text
}
}
} else {
throw new Error('Speech to text is not selected, but found a recorded audio file. Please fix the chain.')
}
return undefined
}
+14 -17
View File
@@ -290,22 +290,12 @@ function getURLsFromHTML(htmlBody: string, baseURL: string): string[] {
const linkElements = dom.window.document.querySelectorAll('a')
const urls: string[] = []
for (const linkElement of linkElements) {
if (linkElement.href.slice(0, 1) === '/') {
try {
const urlObj = new URL(baseURL + linkElement.href)
urls.push(urlObj.href) //relative
} catch (err) {
if (process.env.DEBUG === 'true') console.error(`error with relative url: ${err.message}`)
continue
}
} else {
try {
const urlObj = new URL(linkElement.href)
urls.push(urlObj.href) //absolute
} catch (err) {
if (process.env.DEBUG === 'true') console.error(`error with absolute url: ${err.message}`)
continue
}
try {
const urlObj = new URL(linkElement.href, baseURL)
urls.push(urlObj.href)
} catch (err) {
if (process.env.DEBUG === 'true') console.error(`error with scraped URL: ${err.message}`)
continue
}
}
return urls
@@ -365,7 +355,7 @@ async function crawl(baseURL: string, currentURL: string, pages: string[], limit
}
const htmlBody = await resp.text()
const nextURLs = getURLsFromHTML(htmlBody, baseURL)
const nextURLs = getURLsFromHTML(htmlBody, currentURL)
for (const nextURL of nextURLs) {
pages = await crawl(baseURL, nextURL, pages, limit)
}
@@ -780,3 +770,10 @@ export const prepareSandboxVars = (variables: IVariable[]) => {
}
return vars
}
/**
* Prepare storage path
*/
export const getStoragePath = (): string => {
return process.env.BLOB_STORAGE_PATH ? path.join(process.env.BLOB_STORAGE_PATH) : path.join(getUserHome(), '.flowise', 'storage')
}
+1
View File
@@ -5,6 +5,7 @@ PORT=3000
# APIKEY_PATH=/your_api_key_path/.flowise
# SECRETKEY_PATH=/your_api_key_path/.flowise
# LOG_PATH=/your_log_path/.flowise/logs
# BLOB_STORAGE_PATH=/your_database_path/.flowise/storage
# NUMBER_OF_PROXIES= 1
@@ -90,7 +90,7 @@
"data": {
"id": "chatOpenAI_1",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel"],
@@ -237,6 +237,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_1-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_1-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_1-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -257,7 +290,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -437,7 +472,7 @@
"data": {
"id": "chatOpenAI_2",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel"],
@@ -584,6 +619,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_2-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_2-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_2-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -604,7 +672,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -398,7 +398,7 @@
"data": {
"id": "chatOpenAI_2",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel"],
@@ -545,6 +545,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_2-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_2-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_2-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -565,7 +598,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -597,7 +632,7 @@
"data": {
"id": "chatOpenAI_1",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel"],
@@ -744,6 +779,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_1-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_1-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_1-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -764,7 +832,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -796,7 +866,7 @@
"data": {
"id": "chatOpenAI_3",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel"],
@@ -943,6 +1013,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_3-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_3-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_3-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -963,7 +1066,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -181,7 +181,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -334,6 +334,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -355,7 +388,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -177,7 +177,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel"],
@@ -324,6 +324,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -344,7 +377,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -253,7 +253,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel"],
@@ -400,6 +400,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -420,7 +453,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -346,7 +346,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -499,6 +499,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -520,7 +553,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -72,7 +72,7 @@
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"name": "chatOpenAI",
"version": 3,
"version": 5,
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel"],
"category": "Chat Models",
@@ -218,6 +218,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -238,7 +271,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -196,7 +196,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -349,6 +349,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -370,7 +403,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -217,7 +217,7 @@
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"name": "chatOpenAI",
"version": 3,
"version": 5,
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel"],
"category": "Chat Models",
@@ -363,6 +363,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -383,7 +416,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -158,7 +158,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel"],
@@ -305,6 +305,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -325,7 +358,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -489,7 +489,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -642,6 +642,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -663,7 +696,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -371,7 +371,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -524,6 +524,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -545,7 +578,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -378,7 +378,7 @@
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"name": "chatOpenAI",
"version": 3,
"version": 5,
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel"],
"category": "Chat Models",
@@ -524,6 +524,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -544,7 +577,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -910,7 +910,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -1063,6 +1063,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -1084,7 +1117,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -454,7 +454,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -607,6 +607,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -628,7 +661,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -166,7 +166,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -319,6 +319,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -340,7 +373,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -225,7 +225,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -378,6 +378,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -399,7 +432,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -508,7 +508,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -661,6 +661,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -682,7 +715,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -455,7 +455,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -608,6 +608,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -629,7 +662,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -280,7 +280,7 @@
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"name": "chatOpenAI",
"version": 3,
"version": 5,
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel"],
"category": "Chat Models",
@@ -426,6 +426,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -446,7 +479,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -390,7 +390,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -543,6 +543,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -564,7 +597,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -487,7 +487,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -640,6 +640,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -661,7 +694,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -1013,7 +1048,7 @@
"data": {
"id": "chatOpenAI_1",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -1166,6 +1201,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_1-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_1-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_1-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -1187,7 +1255,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -1219,7 +1289,7 @@
"data": {
"id": "chatOpenAI_2",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -1372,6 +1442,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_2-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_2-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_2-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -1393,7 +1496,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -281,7 +281,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel"],
@@ -428,6 +428,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -448,7 +481,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -429,7 +429,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -582,6 +582,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -603,7 +636,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -635,7 +670,7 @@
"data": {
"id": "chatOpenAI_1",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -788,6 +823,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_1-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_1-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_1-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -809,7 +877,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -5,11 +5,11 @@
"nodes": [
{
"width": 300,
"height": 143,
"height": 142,
"id": "calculator_1",
"position": {
"x": 664.1366474718458,
"y": 123.16419000640141
"x": 466.86432329033937,
"y": 230.0825123205457
},
"type": "customNode",
"data": {
@@ -36,72 +36,177 @@
"selected": false
},
"positionAbsolute": {
"x": 664.1366474718458,
"y": 123.16419000640141
"x": 466.86432329033937,
"y": 230.0825123205457
},
"selected": false,
"dragging": false
},
{
"width": 300,
"height": 277,
"id": "serper_0",
"id": "mrklAgentChat_0",
"position": {
"x": 330.964079024626,
"y": 109.83185250619351
"x": 905.8535326018256,
"y": 388.58312223652564
},
"type": "customNode",
"data": {
"id": "serper_0",
"label": "Serper",
"version": 1,
"name": "serper",
"type": "Serper",
"baseClasses": ["Serper", "Tool", "StructuredTool"],
"category": "Tools",
"description": "Wrapper around Serper.dev - Google Search API",
"inputParams": [
"id": "mrklAgentChat_0",
"label": "ReAct Agent for Chat Models",
"version": 3,
"name": "mrklAgentChat",
"type": "AgentExecutor",
"baseClasses": ["AgentExecutor", "BaseChain", "Runnable"],
"category": "Agents",
"description": "Agent that uses the ReAct logic to decide what action to take, optimized to be used with Chat Models",
"inputParams": [],
"inputAnchors": [
{
"label": "Connect Credential",
"name": "credential",
"type": "credential",
"credentialNames": ["serperApi"],
"id": "serper_0-input-credential-credential"
"label": "Allowed Tools",
"name": "tools",
"type": "Tool",
"list": true,
"id": "mrklAgentChat_0-input-tools-Tool"
},
{
"label": "Chat Model",
"name": "model",
"type": "BaseChatModel",
"id": "mrklAgentChat_0-input-model-BaseChatModel"
},
{
"label": "Memory",
"name": "memory",
"type": "BaseChatMemory",
"id": "mrklAgentChat_0-input-memory-BaseChatMemory"
}
],
"inputAnchors": [],
"inputs": {},
"inputs": {
"tools": ["{{calculator_1.data.instance}}", "{{serper_0.data.instance}}"],
"model": "{{chatOpenAI_0.data.instance}}",
"memory": "{{RedisBackedChatMemory_0.data.instance}}"
},
"outputAnchors": [
{
"id": "serper_0-output-serper-Serper|Tool|StructuredTool",
"name": "serper",
"label": "Serper",
"type": "Serper | Tool | StructuredTool"
"id": "mrklAgentChat_0-output-mrklAgentChat-AgentExecutor|BaseChain|Runnable",
"name": "mrklAgentChat",
"label": "AgentExecutor",
"description": "Agent that uses the ReAct logic to decide what action to take, optimized to be used with Chat Models",
"type": "AgentExecutor | BaseChain | Runnable"
}
],
"outputs": {},
"selected": false
},
"width": 300,
"height": 330,
"selected": false,
"positionAbsolute": {
"x": 330.964079024626,
"y": 109.83185250619351
"x": 905.8535326018256,
"y": 388.58312223652564
},
"dragging": false
},
{
"id": "RedisBackedChatMemory_0",
"position": {
"x": 473.108799702029,
"y": 401.8098683245926
},
"type": "customNode",
"data": {
"id": "RedisBackedChatMemory_0",
"label": "Redis-Backed Chat Memory",
"version": 2,
"name": "RedisBackedChatMemory",
"type": "RedisBackedChatMemory",
"baseClasses": ["RedisBackedChatMemory", "BaseChatMemory", "BaseMemory"],
"category": "Memory",
"description": "Summarizes the conversation and stores the memory in Redis server",
"inputParams": [
{
"label": "Connect Credential",
"name": "credential",
"type": "credential",
"optional": true,
"credentialNames": ["redisCacheApi", "redisCacheUrlApi"],
"id": "RedisBackedChatMemory_0-input-credential-credential"
},
{
"label": "Session Id",
"name": "sessionId",
"type": "string",
"description": "If not specified, a random id will be used. Learn <a target=\"_blank\" href=\"https://docs.flowiseai.com/memory/long-term-memory#ui-and-embedded-chat\">more</a>",
"default": "",
"additionalParams": true,
"optional": true,
"id": "RedisBackedChatMemory_0-input-sessionId-string"
},
{
"label": "Session Timeouts",
"name": "sessionTTL",
"type": "number",
"description": "Omit this parameter to make sessions never expire",
"additionalParams": true,
"optional": true,
"id": "RedisBackedChatMemory_0-input-sessionTTL-number"
},
{
"label": "Memory Key",
"name": "memoryKey",
"type": "string",
"default": "chat_history",
"additionalParams": true,
"id": "RedisBackedChatMemory_0-input-memoryKey-string"
},
{
"label": "Window Size",
"name": "windowSize",
"type": "number",
"description": "Window of size k to surface the last k back-and-forth to use as memory.",
"additionalParams": true,
"optional": true,
"id": "RedisBackedChatMemory_0-input-windowSize-number"
}
],
"inputAnchors": [],
"inputs": {
"sessionId": "",
"sessionTTL": "",
"memoryKey": "chat_history",
"windowSize": ""
},
"outputAnchors": [
{
"id": "RedisBackedChatMemory_0-output-RedisBackedChatMemory-RedisBackedChatMemory|BaseChatMemory|BaseMemory",
"name": "RedisBackedChatMemory",
"label": "RedisBackedChatMemory",
"description": "Summarizes the conversation and stores the memory in Redis server",
"type": "RedisBackedChatMemory | BaseChatMemory | BaseMemory"
}
],
"outputs": {},
"selected": false
},
"width": 300,
"height": 574,
"height": 328,
"selected": false,
"positionAbsolute": {
"x": 473.108799702029,
"y": 401.8098683245926
},
"dragging": false
},
{
"id": "chatOpenAI_0",
"position": {
"x": -27.71074046118335,
"y": 243.62715178281059
"x": 81.2222202723384,
"y": 59.395597724017364
},
"type": "customNode",
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -254,6 +359,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -275,80 +413,78 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
"id": "chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel|Runnable",
"name": "chatOpenAI",
"label": "ChatOpenAI",
"description": "Wrapper around OpenAI large language models that use the Chat endpoint",
"type": "ChatOpenAI | BaseChatModel | BaseLanguageModel | Runnable"
}
],
"outputs": {},
"selected": false
},
"width": 300,
"height": 573,
"selected": false,
"positionAbsolute": {
"x": -27.71074046118335,
"y": 243.62715178281059
"x": 81.2222202723384,
"y": 59.395597724017364
},
"dragging": false
},
{
"width": 300,
"height": 280,
"id": "mrklAgentChat_0",
"id": "serper_0",
"position": {
"x": 1090.2058867451212,
"y": 423.2174695788541
"x": 466.4499611299051,
"y": -67.74721119468873
},
"type": "customNode",
"data": {
"id": "mrklAgentChat_0",
"label": "ReAct Agent for Chat Models",
"id": "serper_0",
"label": "Serper",
"version": 1,
"name": "mrklAgentChat",
"type": "AgentExecutor",
"baseClasses": ["AgentExecutor", "BaseChain", "Runnable"],
"category": "Agents",
"description": "Agent that uses the ReAct logic to decide what action to take, optimized to be used with Chat Models",
"inputParams": [],
"inputAnchors": [
"name": "serper",
"type": "Serper",
"baseClasses": ["Serper", "Tool", "StructuredTool", "Runnable"],
"category": "Tools",
"description": "Wrapper around Serper.dev - Google Search API",
"inputParams": [
{
"label": "Allowed Tools",
"name": "tools",
"type": "Tool",
"list": true,
"id": "mrklAgentChat_0-input-tools-Tool"
},
{
"label": "Language Model",
"name": "model",
"type": "BaseLanguageModel",
"id": "mrklAgentChat_0-input-model-BaseLanguageModel"
"label": "Connect Credential",
"name": "credential",
"type": "credential",
"credentialNames": ["serperApi"],
"id": "serper_0-input-credential-credential"
}
],
"inputs": {
"tools": ["{{calculator_1.data.instance}}", "{{serper_0.data.instance}}"],
"model": "{{chatOpenAI_0.data.instance}}"
},
"inputAnchors": [],
"inputs": {},
"outputAnchors": [
{
"id": "mrklAgentChat_0-output-mrklAgentChat-AgentExecutor|BaseChain|Runnable",
"name": "mrklAgentChat",
"label": "AgentExecutor",
"type": "AgentExecutor | BaseChain | Runnable"
"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": 276,
"selected": false,
"positionAbsolute": {
"x": 1090.2058867451212,
"y": 423.2174695788541
"x": 466.4499611299051,
"y": -67.74721119468873
},
"selected": false
"dragging": false
}
],
"edges": [
@@ -358,32 +494,31 @@
"target": "mrklAgentChat_0",
"targetHandle": "mrklAgentChat_0-input-tools-Tool",
"type": "buttonedge",
"id": "calculator_1-calculator_1-output-calculator-Calculator|Tool|StructuredTool|BaseLangChain-mrklAgentChat_0-mrklAgentChat_0-input-tools-Tool",
"data": {
"label": ""
}
"id": "calculator_1-calculator_1-output-calculator-Calculator|Tool|StructuredTool|BaseLangChain-mrklAgentChat_0-mrklAgentChat_0-input-tools-Tool"
},
{
"source": "serper_0",
"sourceHandle": "serper_0-output-serper-Serper|Tool|StructuredTool",
"source": "RedisBackedChatMemory_0",
"sourceHandle": "RedisBackedChatMemory_0-output-RedisBackedChatMemory-RedisBackedChatMemory|BaseChatMemory|BaseMemory",
"target": "mrklAgentChat_0",
"targetHandle": "mrklAgentChat_0-input-tools-Tool",
"targetHandle": "mrklAgentChat_0-input-memory-BaseChatMemory",
"type": "buttonedge",
"id": "serper_0-serper_0-output-serper-Serper|Tool|StructuredTool-mrklAgentChat_0-mrklAgentChat_0-input-tools-Tool",
"data": {
"label": ""
}
"id": "RedisBackedChatMemory_0-RedisBackedChatMemory_0-output-RedisBackedChatMemory-RedisBackedChatMemory|BaseChatMemory|BaseMemory-mrklAgentChat_0-mrklAgentChat_0-input-memory-BaseChatMemory"
},
{
"source": "chatOpenAI_0",
"sourceHandle": "chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel|Runnable",
"target": "mrklAgentChat_0",
"targetHandle": "mrklAgentChat_0-input-model-BaseLanguageModel",
"targetHandle": "mrklAgentChat_0-input-model-BaseChatModel",
"type": "buttonedge",
"id": "chatOpenAI_0-chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel|Runnable-mrklAgentChat_0-mrklAgentChat_0-input-model-BaseLanguageModel",
"data": {
"label": ""
}
"id": "chatOpenAI_0-chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel|Runnable-mrklAgentChat_0-mrklAgentChat_0-input-model-BaseChatModel"
},
{
"source": "serper_0",
"sourceHandle": "serper_0-output-serper-Serper|Tool|StructuredTool|Runnable",
"target": "mrklAgentChat_0",
"targetHandle": "mrklAgentChat_0-input-tools-Tool",
"type": "buttonedge",
"id": "serper_0-serper_0-output-serper-Serper|Tool|StructuredTool|Runnable-mrklAgentChat_0-mrklAgentChat_0-input-tools-Tool"
}
]
}
@@ -15,7 +15,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel"],
@@ -162,6 +162,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -182,7 +215,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -175,7 +175,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -328,6 +328,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -349,7 +382,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -381,7 +416,7 @@
"data": {
"id": "chatOpenAI_1",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -534,6 +569,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_1-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_1-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_1-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -555,7 +623,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -1313,7 +1383,7 @@
"data": {
"id": "chatOpenAI_2",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -1466,6 +1536,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_2-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_2-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_2-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -1487,7 +1590,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -16,7 +16,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -169,6 +169,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -190,7 +223,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -16,7 +16,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -169,6 +169,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -190,7 +223,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -84,7 +84,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel"],
@@ -231,6 +231,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -251,7 +284,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -127,7 +127,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel"],
@@ -274,6 +274,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -294,7 +327,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -433,7 +468,7 @@
"data": {
"id": "chatOpenAI_1",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel"],
@@ -580,6 +615,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_1-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_1-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_1-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -600,7 +668,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
@@ -394,7 +394,7 @@
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 3,
"version": 5,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
@@ -547,6 +547,39 @@
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json"
},
{
"label": "Allow Image Uploads",
"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",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"additionalParams": true,
"id": "chatOpenAI_0-input-imageResolution-options"
}
],
"inputAnchors": [
@@ -568,7 +601,9 @@
"presencePenalty": "",
"timeout": "",
"basepath": "",
"baseOptions": ""
"baseOptions": "",
"allowImageUploads": true,
"imageResolution": "low"
},
"outputAnchors": [
{
+3 -1
View File
@@ -1,6 +1,6 @@
{
"name": "flowise",
"version": "1.5.0",
"version": "1.5.1",
"description": "Flowiseai Server",
"main": "dist/index",
"types": "dist/index.d.ts",
@@ -48,6 +48,7 @@
"@oclif/core": "^1.13.10",
"async-mutex": "^0.4.0",
"axios": "1.6.2",
"content-disposition": "0.5.4",
"cors": "^2.8.5",
"crypto-js": "^4.1.1",
"dotenv": "^16.0.0",
@@ -70,6 +71,7 @@
"winston": "^3.9.0"
},
"devDependencies": {
"@types/content-disposition": "0.5.8",
"@types/cors": "^2.8.12",
"@types/crypto-js": "^4.1.1",
"@types/multer": "^1.4.7",
+8 -1
View File
@@ -1,4 +1,4 @@
import { ICommonObject, INode, INodeData as INodeDataFromComponent, INodeParams } from 'flowise-components'
import { ICommonObject, IFileUpload, INode, INodeData as INodeDataFromComponent, INodeParams } from 'flowise-components'
export type MessageType = 'apiMessage' | 'userMessage'
@@ -31,6 +31,7 @@ export interface IChatMessage {
sourceDocuments?: string
usedTools?: string
fileAnnotations?: string
fileUploads?: string
chatType: string
chatId: string
memoryType?: string
@@ -176,6 +177,7 @@ export interface IncomingInput {
socketIOClientId?: string
chatId?: string
stopNodeId?: string
uploads?: IFileUpload[]
}
export interface IActiveChatflows {
@@ -212,3 +214,8 @@ export interface ICredentialReqBody {
export interface ICredentialReturnResponse extends ICredential {
plainDataObj: ICredentialDataDecrypted
}
export interface IUploadFileSizeAndTypes {
fileTypes: string[]
maxUploadSize: number
}
+28 -23
View File
@@ -4,6 +4,7 @@ import { Dirent } from 'fs'
import { getNodeModulesPackagePath } from './utils'
import { promises } from 'fs'
import { ICommonObject } from 'flowise-components'
import logger from './utils/logger'
export class NodesPool {
componentNodes: IComponentNodes = {}
@@ -28,36 +29,40 @@ export class NodesPool {
return Promise.all(
nodeFiles.map(async (file) => {
if (file.endsWith('.js')) {
const nodeModule = await require(file)
try {
const nodeModule = await require(file)
if (nodeModule.nodeClass) {
const newNodeInstance = new nodeModule.nodeClass()
newNodeInstance.filePath = file
if (nodeModule.nodeClass) {
const newNodeInstance = new nodeModule.nodeClass()
newNodeInstance.filePath = file
// Replace file icon with absolute path
if (
newNodeInstance.icon &&
(newNodeInstance.icon.endsWith('.svg') ||
newNodeInstance.icon.endsWith('.png') ||
newNodeInstance.icon.endsWith('.jpg'))
) {
const filePath = file.replace(/\\/g, '/').split('/')
filePath.pop()
const nodeIconAbsolutePath = `${filePath.join('/')}/${newNodeInstance.icon}`
newNodeInstance.icon = nodeIconAbsolutePath
// Replace file icon with absolute path
if (
newNodeInstance.icon &&
(newNodeInstance.icon.endsWith('.svg') ||
newNodeInstance.icon.endsWith('.png') ||
newNodeInstance.icon.endsWith('.jpg'))
) {
const filePath = file.replace(/\\/g, '/').split('/')
filePath.pop()
const nodeIconAbsolutePath = `${filePath.join('/')}/${newNodeInstance.icon}`
newNodeInstance.icon = nodeIconAbsolutePath
// Store icon path for componentCredentials
if (newNodeInstance.credential) {
for (const credName of newNodeInstance.credential.credentialNames) {
this.credentialIconPath[credName] = nodeIconAbsolutePath
// Store icon path for componentCredentials
if (newNodeInstance.credential) {
for (const credName of newNodeInstance.credential.credentialNames) {
this.credentialIconPath[credName] = nodeIconAbsolutePath
}
}
}
}
const skipCategories = ['Analytic']
if (!skipCategories.includes(newNodeInstance.category)) {
this.componentNodes[newNodeInstance.name] = newNodeInstance
const skipCategories = ['Analytic', 'SpeechToText']
if (!skipCategories.includes(newNodeInstance.category)) {
this.componentNodes[newNodeInstance.name] = newNodeInstance
}
}
} catch (err) {
logger.error(`❌ [server]: Error during initDatabase with file ${file}:`, err)
}
}
})
+4
View File
@@ -23,6 +23,7 @@ export default class Start extends Command {
CORS_ORIGINS: Flags.string(),
IFRAME_ORIGINS: Flags.string(),
DEBUG: Flags.string(),
BLOB_STORAGE_PATH: Flags.string(),
APIKEY_PATH: Flags.string(),
SECRETKEY_PATH: Flags.string(),
FLOWISE_SECRETKEY_OVERWRITE: Flags.string(),
@@ -92,6 +93,9 @@ export default class Start extends Command {
if (flags.FLOWISE_PASSWORD) process.env.FLOWISE_PASSWORD = flags.FLOWISE_PASSWORD
if (flags.APIKEY_PATH) process.env.APIKEY_PATH = flags.APIKEY_PATH
// Storage
if (flags.BLOB_STORAGE_PATH) process.env.BLOB_STORAGE_PATH = flags.BLOB_STORAGE_PATH
//API Configuration
if (flags.FLOWISE_FILE_SIZE_LIMIT) process.env.FLOWISE_FILE_SIZE_LIMIT = flags.FLOWISE_FILE_SIZE_LIMIT
@@ -31,6 +31,9 @@ export class ChatFlow implements IChatFlow {
@Column({ nullable: true, type: 'text' })
analytic?: string
@Column({ nullable: true, type: 'text' })
speechToText?: string
@CreateDateColumn()
createdDate: Date
@@ -26,6 +26,9 @@ export class ChatMessage implements IChatMessage {
@Column({ nullable: true, type: 'text' })
fileAnnotations?: string
@Column({ nullable: true, type: 'text' })
fileUploads?: string
@Column()
chatType: string
@@ -0,0 +1,12 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddFileUploadsToChatMessage1701788586491 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
const columnExists = await queryRunner.hasColumn('chat_message', 'fileUploads')
if (!columnExists) queryRunner.query(`ALTER TABLE \`chat_message\` ADD COLUMN \`fileUploads\` TEXT;`)
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE \`chat_message\` DROP COLUMN \`fileUploads\`;`)
}
}
@@ -0,0 +1,12 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddSpeechToText1706364937060 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
const columnExists = await queryRunner.hasColumn('chat_flow', 'speechToText')
if (!columnExists) queryRunner.query(`ALTER TABLE \`chat_flow\` ADD COLUMN \`speechToText\` TEXT;`)
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE \`chat_flow\` DROP COLUMN \`speechToText\`;`)
}
}
@@ -10,7 +10,9 @@ import { AddAssistantEntity1699325775451 } from './1699325775451-AddAssistantEnt
import { AddUsedToolsToChatMessage1699481607341 } from './1699481607341-AddUsedToolsToChatMessage'
import { AddCategoryToChatFlow1699900910291 } from './1699900910291-AddCategoryToChatFlow'
import { AddFileAnnotationsToChatMessage1700271021237 } from './1700271021237-AddFileAnnotationsToChatMessage'
import { AddFileUploadsToChatMessage1701788586491 } from './1701788586491-AddFileUploadsToChatMessage'
import { AddVariableEntity1699325775451 } from './1702200925471-AddVariableEntity'
import { AddSpeechToText1706364937060 } from './1706364937060-AddSpeechToText'
export const mysqlMigrations = [
Init1693840429259,
@@ -25,5 +27,7 @@ export const mysqlMigrations = [
AddUsedToolsToChatMessage1699481607341,
AddCategoryToChatFlow1699900910291,
AddFileAnnotationsToChatMessage1700271021237,
AddVariableEntity1699325775451
AddFileUploadsToChatMessage1701788586491,
AddVariableEntity1699325775451,
AddSpeechToText1706364937060
]
@@ -0,0 +1,11 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddFileUploadsToChatMessage1701788586491 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "chat_message" ADD COLUMN IF NOT EXISTS "fileUploads" TEXT;`)
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "chat_message" DROP COLUMN "fileUploads";`)
}
}
@@ -0,0 +1,11 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddSpeechToText1706364937060 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "chat_flow" ADD COLUMN IF NOT EXISTS "speechToText" TEXT;`)
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "chat_flow" DROP COLUMN "speechToText";`)
}
}
@@ -10,7 +10,9 @@ import { AddAssistantEntity1699325775451 } from './1699325775451-AddAssistantEnt
import { AddUsedToolsToChatMessage1699481607341 } from './1699481607341-AddUsedToolsToChatMessage'
import { AddCategoryToChatFlow1699900910291 } from './1699900910291-AddCategoryToChatFlow'
import { AddFileAnnotationsToChatMessage1700271021237 } from './1700271021237-AddFileAnnotationsToChatMessage'
import { AddFileUploadsToChatMessage1701788586491 } from './1701788586491-AddFileUploadsToChatMessage'
import { AddVariableEntity1699325775451 } from './1702200925471-AddVariableEntity'
import { AddSpeechToText1706364937060 } from './1706364937060-AddSpeechToText'
export const postgresMigrations = [
Init1693891895163,
@@ -25,5 +27,7 @@ export const postgresMigrations = [
AddUsedToolsToChatMessage1699481607341,
AddCategoryToChatFlow1699900910291,
AddFileAnnotationsToChatMessage1700271021237,
AddVariableEntity1699325775451
AddFileUploadsToChatMessage1701788586491,
AddVariableEntity1699325775451,
AddSpeechToText1706364937060
]
@@ -0,0 +1,20 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddFileUploadsToChatMessage1701788586491 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "temp_chat_message" ("id" varchar PRIMARY KEY NOT NULL, "role" varchar NOT NULL, "chatflowid" varchar NOT NULL, "content" text NOT NULL, "sourceDocuments" text, "usedTools" text, "fileAnnotations" text, "fileUploads" text, "createdDate" datetime NOT NULL DEFAULT (datetime('now')), "chatType" VARCHAR NOT NULL DEFAULT 'INTERNAL', "chatId" VARCHAR NOT NULL, "memoryType" VARCHAR, "sessionId" VARCHAR);`
)
await queryRunner.query(
`INSERT INTO "temp_chat_message" ("id", "role", "chatflowid", "content", "sourceDocuments", "fileAnnotations", "usedTools", "createdDate", "chatType", "chatId", "memoryType", "sessionId") SELECT "id", "role", "chatflowid", "content", "sourceDocuments", "usedTools", "fileAnnotations", "createdDate", "chatType", "chatId", "memoryType", "sessionId" FROM "chat_message";`
)
await queryRunner.query(`DROP TABLE "chat_message";`)
await queryRunner.query(`ALTER TABLE "temp_chat_message" RENAME TO "chat_message";`)
await queryRunner.query(`CREATE INDEX "IDX_e574527322272fd838f4f0f3d3" ON "chat_message" ("chatflowid") ;`)
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE IF EXISTS "temp_chat_message";`)
await queryRunner.query(`ALTER TABLE "chat_message" DROP COLUMN "fileUploads";`)
}
}
@@ -0,0 +1,11 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddSpeechToText1706364937060 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "chat_flow" ADD COLUMN "speechToText" TEXT;`)
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "chat_flow" DROP COLUMN "speechToText";`)
}
}
@@ -10,7 +10,9 @@ import { AddAssistantEntity1699325775451 } from './1699325775451-AddAssistantEnt
import { AddUsedToolsToChatMessage1699481607341 } from './1699481607341-AddUsedToolsToChatMessage'
import { AddCategoryToChatFlow1699900910291 } from './1699900910291-AddCategoryToChatFlow'
import { AddFileAnnotationsToChatMessage1700271021237 } from './1700271021237-AddFileAnnotationsToChatMessage'
import { AddFileUploadsToChatMessage1701788586491 } from './1701788586491-AddFileUploadsToChatMessage'
import { AddVariableEntity1699325775451 } from './1702200925471-AddVariableEntity'
import { AddSpeechToText1706364937060 } from './1706364937060-AddSpeechToText'
export const sqliteMigrations = [
Init1693835579790,
@@ -25,5 +27,7 @@ export const sqliteMigrations = [
AddUsedToolsToChatMessage1699481607341,
AddCategoryToChatFlow1699900910291,
AddFileAnnotationsToChatMessage1700271021237,
AddVariableEntity1699325775451
AddFileUploadsToChatMessage1701788586491,
AddVariableEntity1699325775451,
AddSpeechToText1706364937060
]
+253 -25
View File
@@ -5,6 +5,7 @@ import cors from 'cors'
import http from 'http'
import * as fs from 'fs'
import basicAuth from 'express-basic-auth'
import contentDisposition from 'content-disposition'
import { Server } from 'socket.io'
import logger from './utils/logger'
import { expressRequestLogger } from './utils/logger'
@@ -21,7 +22,8 @@ import {
chatType,
IChatMessage,
IDepthQueue,
INodeDirectedGraph
INodeDirectedGraph,
IUploadFileSizeAndTypes
} from './Interface'
import {
getNodeModulesPackagePath,
@@ -46,6 +48,7 @@ import {
getAllConnectedNodes,
clearSessionMemory,
findMemoryNode,
deleteFolderRecursive,
getTelemetryFlowObj,
getAppVersion
} from './utils'
@@ -59,7 +62,18 @@ import { Tool } from './database/entities/Tool'
import { Assistant } from './database/entities/Assistant'
import { ChatflowPool } from './ChatflowPool'
import { CachePool } from './CachePool'
import { ICommonObject, IMessage, INodeOptionsValue, handleEscapeCharacters, webCrawl, xmlScrape } from 'flowise-components'
import {
ICommonObject,
IMessage,
INodeOptionsValue,
INodeParams,
handleEscapeCharacters,
convertSpeechToText,
xmlScrape,
webCrawl,
getStoragePath,
IFileUpload
} from 'flowise-components'
import { createRateLimiter, getRateLimiter, initializeRateLimiter } from './utils/rateLimit'
import { addAPIKey, compareKeys, deleteAPIKey, getApiKey, getAPIKeys, updateAPIKey } from './utils/apiKey'
import { sanitizeMiddleware, getCorsOptions, getAllowedIframeOrigins } from './utils/XSS'
@@ -85,7 +99,7 @@ export class App {
// Initialize database
this.AppDataSource.initialize()
.then(async () => {
logger.info('📦 [server]: Data Source has been initialized!')
logger.info('📦 [server]: Data Source is being initialized!')
// Run Migrations Scripts
await this.AppDataSource.runMigrations({ transaction: 'each' })
@@ -112,6 +126,7 @@ export class App {
// Initialize telemetry
this.telemetry = new Telemetry()
logger.info('📦 [server]: Data Source has been initialized!')
})
.catch((err) => {
logger.error('❌ [server]: Error during Data Source initialization:', err)
@@ -167,7 +182,9 @@ export class App {
'/api/v1/node-icon/',
'/api/v1/components-credentials-icon/',
'/api/v1/chatflows-streaming',
'/api/v1/chatflows-uploads',
'/api/v1/openai-assistants-file',
'/api/v1/get-upload-file',
'/api/v1/ip'
]
this.app.use((req, res, next) => {
@@ -307,7 +324,10 @@ export class App {
// execute custom function node
this.app.post('/api/v1/node-custom-function', async (req: Request, res: Response) => {
const body = req.body
const nodeData = { inputs: body }
const functionInputVariables = Object.fromEntries(
[...(body?.javascriptFunction ?? '').matchAll(/\$([a-zA-Z0-9_]+)/g)].map((g) => [g[1], undefined])
)
const nodeData = { inputs: { functionInputVariables, ...body } }
if (Object.prototype.hasOwnProperty.call(this.nodesPool.componentNodes, 'customFunction')) {
try {
const nodeInstanceFilePath = this.nodesPool.componentNodes['customFunction'].filePath as string
@@ -388,10 +408,13 @@ export class App {
id: req.params.id
})
if (!chatflow) return res.status(404).send(`Chatflow ${req.params.id} not found`)
if (chatflow.chatbotConfig) {
const uploadsConfig = await this.getUploadsConfig(req.params.id)
// even if chatbotConfig is not set but uploads are enabled
// send uploadsConfig to the chatbot
if (chatflow.chatbotConfig || uploadsConfig) {
try {
const parsedConfig = JSON.parse(chatflow.chatbotConfig)
return res.json(parsedConfig)
const parsedConfig = chatflow.chatbotConfig ? JSON.parse(chatflow.chatbotConfig) : {}
return res.json({ ...parsedConfig, uploads: uploadsConfig })
} catch (e) {
return res.status(500).send(`Error parsing Chatbot Config for Chatflow ${req.params.id}`)
}
@@ -451,6 +474,15 @@ export class App {
// Delete chatflow via id
this.app.delete('/api/v1/chatflows/:id', async (req: Request, res: Response) => {
const results = await this.AppDataSource.getRepository(ChatFlow).delete({ id: req.params.id })
try {
// Delete all uploads corresponding to this chatflow
const directory = path.join(getStoragePath(), req.params.id)
deleteFolderRecursive(directory)
} catch (e) {
logger.error(`[server]: Error deleting file storage for chatflow ${req.params.id}: ${e}`)
}
return res.json(results)
})
@@ -501,6 +533,16 @@ export class App {
return res.json(obj)
})
// Check if chatflow valid for uploads
this.app.get('/api/v1/chatflows-uploads/:id', async (req: Request, res: Response) => {
try {
const uploadsConfig = await this.getUploadsConfig(req.params.id)
return res.json(uploadsConfig)
} catch (e) {
return res.status(500).send(e)
}
})
// ----------------------------------------
// ChatMessage
// ----------------------------------------
@@ -598,6 +640,16 @@ export class App {
if (sessionId) deleteOptions.sessionId = sessionId
if (chatType) deleteOptions.chatType = chatType
// Delete all uploads corresponding to this chatflow/chatId
if (chatId) {
try {
const directory = path.join(getStoragePath(), chatflowid, chatId)
deleteFolderRecursive(directory)
} catch (e) {
logger.error(`[server]: Error deleting file storage for chatflow ${chatflowid}, chatId ${chatId}: ${e}`)
}
}
const results = await this.AppDataSource.getRepository(ChatMessage).delete(deleteOptions)
return res.json(results)
})
@@ -1080,6 +1132,11 @@ export class App {
}
})
function streamFileToUser(res: Response, filePath: string) {
const fileStream = fs.createReadStream(filePath)
fileStream.pipe(res)
}
// Download file from assistant
this.app.post('/api/v1/openai-assistants-file', async (req: Request, res: Response) => {
const filePath = path.join(getUserHome(), '.flowise', 'openai-assistant', req.body.fileName)
@@ -1089,9 +1146,48 @@ export class App {
if (filePath.includes('..')) return res.status(500).send(`Invalid file path`)
//only return from the .flowise openai-assistant folder
if (!(filePath.includes('.flowise') && filePath.includes('openai-assistant'))) return res.status(500).send(`Invalid file path`)
res.setHeader('Content-Disposition', 'attachment; filename=' + path.basename(filePath))
const fileStream = fs.createReadStream(filePath)
fileStream.pipe(res)
if (fs.existsSync(filePath)) {
res.setHeader('Content-Disposition', contentDisposition(path.basename(filePath)))
streamFileToUser(res, filePath)
} else {
return res.status(404).send(`File ${req.body.fileName} not found`)
}
})
this.app.get('/api/v1/get-upload-path', async (req: Request, res: Response) => {
return res.json({
storagePath: getStoragePath()
})
})
// stream uploaded image
this.app.get('/api/v1/get-upload-file', async (req: Request, res: Response) => {
try {
if (!req.query.chatflowId || !req.query.chatId || !req.query.fileName) {
return res.status(500).send(`Invalid file path`)
}
const chatflowId = req.query.chatflowId as string
const chatId = req.query.chatId as string
const fileName = req.query.fileName as string
const filePath = path.join(getStoragePath(), chatflowId, chatId, fileName)
//raise error if file path is not absolute
if (!path.isAbsolute(filePath)) return res.status(500).send(`Invalid file path`)
//raise error if file path contains '..'
if (filePath.includes('..')) return res.status(500).send(`Invalid file path`)
//only return from the storage folder
if (!filePath.startsWith(getStoragePath())) return res.status(500).send(`Invalid file path`)
if (fs.existsSync(filePath)) {
res.setHeader('Content-Disposition', contentDisposition(path.basename(filePath)))
streamFileToUser(res, filePath)
} else {
return res.status(404).send(`File ${fileName} not found`)
}
} catch (error) {
return res.status(500).send(`Invalid file path`)
}
})
// ----------------------------------------
@@ -1149,18 +1245,22 @@ export class App {
// ----------------------------------------
this.app.get('/api/v1/fetch-links', async (req: Request, res: Response) => {
const url = decodeURIComponent(req.query.url as string)
const relativeLinksMethod = req.query.relativeLinksMethod as string
if (!relativeLinksMethod) {
return res.status(500).send('Please choose a Relative Links Method in Additional Parameters.')
try {
const url = decodeURIComponent(req.query.url as string)
const relativeLinksMethod = req.query.relativeLinksMethod as string
if (!relativeLinksMethod) {
return res.status(500).send('Please choose a Relative Links Method in Additional Parameters.')
}
const limit = parseInt(req.query.limit as string)
if (process.env.DEBUG === 'true') console.info(`Start ${relativeLinksMethod}`)
const links: string[] = relativeLinksMethod === 'webCrawl' ? await webCrawl(url, limit) : await xmlScrape(url, limit)
if (process.env.DEBUG === 'true') console.info(`Finish ${relativeLinksMethod}`)
res.json({ status: 'OK', links })
} catch (e: any) {
return res.status(500).send('Could not fetch links from the URL.')
}
const limit = parseInt(req.query.limit as string)
if (process.env.DEBUG === 'true') console.info(`Start ${relativeLinksMethod}`)
const links: string[] = relativeLinksMethod === 'webCrawl' ? await webCrawl(url, limit) : await xmlScrape(url, limit)
if (process.env.DEBUG === 'true') console.info(`Finish ${relativeLinksMethod}`)
res.json({ status: 'OK', links })
})
// ----------------------------------------
@@ -1444,6 +1544,74 @@ export class App {
return false
}
/**
* Method that checks if uploads are enabled in the chatflow
* @param {string} chatflowid
*/
async getUploadsConfig(chatflowid: string): Promise<any> {
const chatflow = await this.AppDataSource.getRepository(ChatFlow).findOneBy({
id: chatflowid
})
if (!chatflow) return `Chatflow ${chatflowid} not found`
const uploadAllowedNodes = ['llmChain', 'conversationChain', 'mrklAgentChat', 'conversationalAgent']
const uploadProcessingNodes = ['chatOpenAI']
const flowObj = JSON.parse(chatflow.flowData)
const imgUploadSizeAndTypes: IUploadFileSizeAndTypes[] = []
let isSpeechToTextEnabled = false
if (chatflow.speechToText) {
const speechToTextProviders = JSON.parse(chatflow.speechToText)
for (const provider in speechToTextProviders) {
if (provider !== 'none') {
const providerObj = speechToTextProviders[provider]
if (providerObj.status) {
isSpeechToTextEnabled = true
break
}
}
}
}
let isImageUploadAllowed = false
const nodes: IReactFlowNode[] = flowObj.nodes
/*
* Condition for isImageUploadAllowed
* 1.) one of the uploadAllowedNodes exists
* 2.) one of the uploadProcessingNodes exists + allowImageUploads is ON
*/
if (!nodes.some((node) => uploadAllowedNodes.includes(node.data.name))) {
return {
isSpeechToTextEnabled,
isImageUploadAllowed: false,
imgUploadSizeAndTypes
}
}
nodes.forEach((node: IReactFlowNode) => {
if (uploadProcessingNodes.indexOf(node.data.name) > -1) {
// TODO: for now the maxUploadSize is hardcoded to 5MB, we need to add it to the node properties
node.data.inputParams.map((param: INodeParams) => {
if (param.name === 'allowImageUploads' && node.data.inputs?.['allowImageUploads']) {
imgUploadSizeAndTypes.push({
fileTypes: 'image/gif;image/jpeg;image/png;image/webp;'.split(';'),
maxUploadSize: 5
})
isImageUploadAllowed = true
}
})
}
})
return {
isSpeechToTextEnabled,
isImageUploadAllowed,
imgUploadSizeAndTypes
}
}
/**
* Method that get chat messages.
* @param {string} chatflowid
@@ -1657,6 +1825,57 @@ export class App {
if (!isKeyValidated) return res.status(401).send('Unauthorized')
}
let fileUploads: IFileUpload[] = []
if (incomingInput.uploads) {
fileUploads = incomingInput.uploads
for (let i = 0; i < fileUploads.length; i += 1) {
const upload = fileUploads[i]
if ((upload.type === 'file' || upload.type === 'audio') && upload.data) {
const filename = upload.name
const dir = path.join(getStoragePath(), chatflowid, chatId)
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true })
}
const filePath = path.join(dir, filename)
const splitDataURI = upload.data.split(',')
const bf = Buffer.from(splitDataURI.pop() || '', 'base64')
fs.writeFileSync(filePath, bf)
// Omit upload.data since we don't store the content in database
upload.type = 'stored-file'
fileUploads[i] = omit(upload, ['data'])
}
// Run Speech to Text conversion
if (upload.mime === 'audio/webm') {
let speechToTextConfig: ICommonObject = {}
if (chatflow.speechToText) {
const speechToTextProviders = JSON.parse(chatflow.speechToText)
for (const provider in speechToTextProviders) {
const providerObj = speechToTextProviders[provider]
if (providerObj.status) {
speechToTextConfig = providerObj
speechToTextConfig['name'] = provider
break
}
}
}
if (speechToTextConfig) {
const options: ICommonObject = {
chatId,
chatflowid,
appDataSource: this.AppDataSource,
databaseEntities: databaseEntities
}
const speechToTextResult = await convertSpeechToText(upload, speechToTextConfig, options)
if (speechToTextResult) {
incomingInput.question = speechToTextResult
}
}
}
}
}
let isStreamValid = false
const files = (req.files as any[]) || []
@@ -1822,7 +2041,10 @@ export class App {
chatflowid,
this.AppDataSource,
incomingInput?.overrideConfig,
this.cachePool
this.cachePool,
false,
undefined,
incomingInput.uploads
)
const nodeToExecute =
@@ -1861,6 +2083,7 @@ export class App {
appDataSource: this.AppDataSource,
databaseEntities,
analytic: chatflow.analytic,
uploads: incomingInput.uploads,
socketIO,
socketIOClientId: incomingInput.socketIOClientId
})
@@ -1871,7 +2094,8 @@ export class App {
logger,
appDataSource: this.AppDataSource,
databaseEntities,
analytic: chatflow.analytic
analytic: chatflow.analytic,
uploads: incomingInput.uploads
})
result = typeof result === 'string' ? { text: result } : result
@@ -1889,7 +2113,8 @@ export class App {
chatId,
memoryType,
sessionId,
createdDate: userMessageDateTime
createdDate: userMessageDateTime,
fileUploads: incomingInput.uploads ? JSON.stringify(fileUploads) : undefined
}
await this.addChatMessage(userMessage)
@@ -1911,7 +2136,6 @@ export class App {
if (result?.usedTools) apiMessage.usedTools = JSON.stringify(result.usedTools)
if (result?.fileAnnotations) apiMessage.fileAnnotations = JSON.stringify(result.fileAnnotations)
const chatMessage = await this.addChatMessage(apiMessage)
result.chatMessageId = chatMessage.id
logger.debug(`[server]: Finished running ${nodeToExecuteData.label} (${nodeToExecuteData.id})`)
await this.telemetry.sendTelemetry('prediction_sent', {
@@ -1923,7 +2147,11 @@ export class App {
})
// Prepare response
// return the question in the response
// this is used when input text is empty but question is in audio format
result.question = incomingInput.question
result.chatId = chatId
result.chatMessageId = chatMessage.id
if (sessionId) result.sessionId = sessionId
if (memoryType) result.memoryType = memoryType
+63 -6
View File
@@ -27,7 +27,8 @@ import {
ICommonObject,
IDatabaseEntity,
IMessage,
FlowiseMemory
FlowiseMemory,
IFileUpload
} from 'flowise-components'
import { randomBytes } from 'crypto'
import { AES, enc } from 'crypto-js'
@@ -279,7 +280,8 @@ export const buildFlow = async (
overrideConfig?: ICommonObject,
cachePool?: CachePool,
isUpsert?: boolean,
stopNodeId?: string
stopNodeId?: string,
uploads?: IFileUpload[]
) => {
const flowNodes = cloneDeep(reactFlowNodes)
@@ -325,7 +327,8 @@ export const buildFlow = async (
appDataSource,
databaseEntities,
cachePool,
dynamicVariables
dynamicVariables,
uploads
})
logger.debug(`[server]: Finished upserting ${reactFlowNode.data.label} (${reactFlowNode.data.id})`)
break
@@ -340,7 +343,8 @@ export const buildFlow = async (
appDataSource,
databaseEntities,
cachePool,
dynamicVariables
dynamicVariables,
uploads
})
// Save dynamic variables
@@ -596,7 +600,6 @@ export const resolveVariables = (
}
const paramsObj = flowNodeData[types] ?? {}
getParamValues(paramsObj)
return flowNodeData
@@ -629,7 +632,33 @@ export const replaceInputsWithConfig = (flowNodeData: INodeData, overrideConfig:
}
}
let paramValue = overrideConfig[config] ?? inputsObj[config]
let paramValue = inputsObj[config]
const overrideConfigValue = overrideConfig[config]
if (overrideConfigValue) {
if (typeof overrideConfigValue === 'object') {
switch (typeof paramValue) {
case 'string':
if (paramValue.startsWith('{') && paramValue.endsWith('}')) {
try {
paramValue = Object.assign({}, JSON.parse(paramValue), overrideConfigValue)
break
} catch (e) {
// ignore
}
}
paramValue = overrideConfigValue
break
case 'object':
paramValue = Object.assign({}, paramValue, overrideConfigValue)
break
default:
paramValue = overrideConfigValue
break
}
} else {
paramValue = overrideConfigValue
}
}
// Check if boolean
if (paramValue === 'true') paramValue = true
else if (paramValue === 'false') paramValue = false
@@ -1102,6 +1131,34 @@ export const getAllValuesFromJson = (obj: any): any[] => {
return values
}
/**
* Delete file & folder recursively
* @param {string} directory
*/
export const deleteFolderRecursive = (directory: string) => {
if (fs.existsSync(directory)) {
fs.readdir(directory, (error, files) => {
if (error) throw new Error('Could not read directory')
files.forEach((file) => {
const file_path = path.join(directory, file)
fs.stat(file_path, (error, stat) => {
if (error) throw new Error('File do not exist')
if (!stat.isDirectory()) {
fs.unlink(file_path, (error) => {
if (error) throw new Error('Could not delete file')
})
} else {
deleteFolderRecursive(file_path)
}
})
})
})
}
}
/**
* Get only essential flow data items for telemetry
* @param {IReactFlowNode[]} nodes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "flowise-ui",
"version": "1.5.0",
"version": "1.5.1",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://flowiseai.com",
"author": {
+4 -1
View File
@@ -14,6 +14,8 @@ const deleteChatflow = (id) => client.delete(`/chatflows/${id}`)
const getIsChatflowStreaming = (id) => client.get(`/chatflows-streaming/${id}`)
const getAllowChatflowUploads = (id) => client.get(`/chatflows-uploads/${id}`)
export default {
getAllChatflows,
getSpecificChatflow,
@@ -21,5 +23,6 @@ export default {
createNewChatflow,
updateChatflow,
deleteChatflow,
getIsChatflowStreaming
getIsChatflowStreaming,
getAllowChatflowUploads
}
+3 -1
View File
@@ -4,10 +4,12 @@ const getInternalChatmessageFromChatflow = (id) => client.get(`/internal-chatmes
const getAllChatmessageFromChatflow = (id, params = {}) => client.get(`/chatmessage/${id}`, { params: { order: 'DESC', ...params } })
const getChatmessageFromPK = (id, params = {}) => client.get(`/chatmessage/${id}`, { params: { order: 'ASC', ...params } })
const deleteChatmessage = (id, params = {}) => client.delete(`/chatmessage/${id}`, { params: { ...params } })
const getStoragePath = () => client.get(`/get-upload-path`)
export default {
getInternalChatmessageFromChatflow,
getAllChatmessageFromChatflow,
getChatmessageFromPK,
deleteChatmessage
deleteChatmessage,
getStoragePath
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

+8
View File
@@ -0,0 +1,8 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.9814 25.2665C15.353 26.2672 16.0645 27.1054 16.9914 27.6347C17.9183 28.164 19.0018 28.3507 20.0524 28.1622C21.103 27.9737 22.054 27.422 22.7391 26.6034C23.4242 25.7849 23.7998 24.7517 23.8004 23.6842V17.5533C23.8004 17.1909 23.6043 16.8569 23.2879 16.6802L15.9995 12.6108" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.17701 19.5848C6.49568 20.4069 6.12505 21.4424 6.12993 22.5101C6.13481 23.5779 6.51489 24.6099 7.2037 25.4258C7.89252 26.2416 8.84622 26.7893 9.89802 26.9732C10.9498 27.157 12.0328 26.9653 12.9575 26.4314L18.1044 23.4263C18.4114 23.247 18.6002 22.9182 18.6002 22.5627V14.106" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8.19877 9.98459C6.39026 9.67775 4.57524 10.4982 3.60403 12.1806C3.00524 13.2178 2.84295 14.4504 3.15284 15.6073C3.46273 16.7642 4.21943 17.7507 5.25652 18.3498L10.3049 21.3269C10.6109 21.5074 10.9898 21.5119 11.3001 21.3388L18.6 17.2655" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.0172 6.06585C16.6456 5.06522 15.9342 4.227 15.0072 3.6977C14.0803 3.1684 12.9969 2.98168 11.9462 3.17018C10.8956 3.35869 9.94464 3.91042 9.25954 4.72895C8.57444 5.54747 8.19879 6.58074 8.19824 7.64814V13.6575C8.19824 14.0154 8.38951 14.346 8.69977 14.5244L15.9992 18.7215" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M24.8216 11.7476C25.5029 10.9255 25.8735 9.89004 25.8687 8.8223C25.8638 7.75457 25.4837 6.72253 24.7949 5.90667C24.1061 5.09082 23.1524 4.54308 22.1006 4.35924C21.0488 4.17541 19.9658 4.36718 19.0411 4.90101L13.8942 7.90613C13.5872 8.08539 13.3984 8.41418 13.3984 8.76971V17.2265" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M23.7997 21.2595C25.6082 21.5663 27.4232 20.7459 28.3944 19.0635C28.9932 18.0263 29.1555 16.7937 28.8456 15.6368C28.5357 14.4799 27.779 13.4934 26.7419 12.8943L21.6409 9.91752C21.3316 9.73703 20.9494 9.7357 20.6388 9.91405L17.2696 11.849L13.3984 14.0723" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 KiB

+20 -2
View File
@@ -7,11 +7,22 @@ import {
IconSearch,
IconMessage,
IconPictureInPictureOff,
IconLink
IconLink,
IconMicrophone
} from '@tabler/icons'
// constant
const icons = { IconTrash, IconFileUpload, IconFileExport, IconCopy, IconSearch, IconMessage, IconPictureInPictureOff, IconLink }
const icons = {
IconTrash,
IconFileUpload,
IconFileExport,
IconCopy,
IconSearch,
IconMessage,
IconPictureInPictureOff,
IconLink,
IconMicrophone
}
// ==============================|| SETTINGS MENU ITEMS ||============================== //
@@ -41,6 +52,13 @@ const settings = {
url: '',
icon: icons.IconLink
},
{
id: 'enableSpeechToText',
title: 'Speech to Text',
type: 'item',
url: '',
icon: icons.IconMicrophone
},
{
id: 'duplicateChatflow',
title: 'Duplicate Chatflow',
@@ -0,0 +1,57 @@
import { styled } from '@mui/material/styles'
import ButtonBase from '@mui/material/ButtonBase'
export const ImageButton = styled(ButtonBase)(({ theme }) => ({
position: 'relative',
height: 200,
borderRadius: '10px',
[theme.breakpoints.down('sm')]: {
width: '100% !important', // Overrides inline-style
height: 100
},
'&:hover, &.Mui-focusVisible': {
zIndex: 1,
'& .MuiImageBackdrop-root': {
opacity: 0.4
},
'& .MuiImageMarked-root': {
opacity: 1
},
'& .MuiTypography-root': {
border: '4px solid currentColor'
}
}
}))
export const ImageSrc = styled('span')({
position: 'absolute',
borderRadius: '10px',
left: 0,
right: 0,
top: 0,
bottom: 0,
backgroundSize: 'cover',
backgroundPosition: 'center 40%'
})
export const ImageBackdrop = styled('span')(({ theme }) => ({
position: 'absolute',
borderRadius: '10px',
left: 0,
right: 0,
top: 0,
bottom: 0,
backgroundColor: theme.palette.common.black,
opacity: 0.1,
transition: theme.transitions.create('opacity')
}))
export const ImageMarked = styled('span')(() => ({
height: 25,
width: 25,
backgroundColor: 'transparent',
position: 'absolute',
top: 'auto',
left: 'auto',
opacity: 0
}))
@@ -1,7 +1,4 @@
.button-container {
position: absolute;
bottom: 0;
z-index: 1000;
display: flex;
overflow-x: auto;
-webkit-overflow-scrolling: touch; /* For momentum scroll on mobile devices */
@@ -10,5 +7,4 @@
.button {
flex: 0 0 auto; /* Don't grow, don't shrink, base width on content */
margin: 5px; /* Adjust as needed for spacing between buttons */
}
@@ -3,9 +3,12 @@ import PropTypes from 'prop-types'
import { Chip } from '@mui/material'
import './StarterPromptsCard.css'
const StarterPromptsCard = ({ isGrid, starterPrompts, onPromptClick }) => {
const StarterPromptsCard = ({ isGrid, starterPrompts, sx, onPromptClick }) => {
return (
<Box className={'button-container'} sx={{ maxWidth: isGrid ? 'inherit' : '400px', m: 1 }}>
<Box
className={'button-container'}
sx={{ width: '100%', maxWidth: isGrid ? 'inherit' : '400px', p: 1.5, display: 'flex', gap: 1, ...sx }}
>
{starterPrompts.map((sp, index) => (
<Chip label={sp.prompt} className={'button'} key={index} onClick={(e) => onPromptClick(sp.prompt, e)} />
))}
@@ -15,7 +18,8 @@ const StarterPromptsCard = ({ isGrid, starterPrompts, onPromptClick }) => {
StarterPromptsCard.propTypes = {
isGrid: PropTypes.bool,
starterPrompts: PropTypes.arrayOf(PropTypes.string),
starterPrompts: PropTypes.array,
sx: PropTypes.object,
onPromptClick: PropTypes.func
}
@@ -16,7 +16,7 @@ import {
Stack,
Typography
} from '@mui/material'
import { IconTrash, IconX } from '@tabler/icons'
import { IconEraser, IconTrash, IconX } from '@tabler/icons'
import PerfectScrollbar from 'react-perfect-scrollbar'
import { BackdropLoader } from 'ui-component/loading/BackdropLoader'
@@ -113,6 +113,10 @@ const ManageScrapedLinksDialog = ({ show, dialogProps, onCancel, onSave }) => {
setSelectedLinks(links)
}
const handleRemoveAllLinks = () => {
setSelectedLinks([])
}
const handleSaveLinks = () => {
onSave(url, selectedLinks)
}
@@ -145,6 +149,7 @@ const ManageScrapedLinksDialog = ({ show, dialogProps, onCancel, onSave }) => {
/>
</FormControl>
<Button
disabled={!url}
sx={{ borderRadius: '12px', mt: 1, display: 'flex', flexShrink: 0 }}
size='small'
variant='contained'
@@ -154,7 +159,21 @@ const ManageScrapedLinksDialog = ({ show, dialogProps, onCancel, onSave }) => {
</Button>
</Stack>
</Box>
<Typography sx={{ mb: 2, fontWeight: 500 }}>Scraped Links</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 1.5 }}>
<Typography sx={{ fontWeight: 500 }}>Scraped Links</Typography>
{selectedLinks.length > 0 ? (
<StyledButton
sx={{ height: 'max-content', width: 'max-content' }}
variant='outlined'
color='error'
title='Clear All Links'
onClick={handleRemoveAllLinks}
startIcon={<IconEraser />}
>
Clear All
</StyledButton>
) : null}
</Box>
<>
{loading && <BackdropLoader open={loading} />}
{selectedLinks.length > 0 ? (
@@ -0,0 +1,348 @@
import { createPortal } from 'react-dom'
import { useDispatch } from 'react-redux'
import { useState, useEffect } from 'react'
import PropTypes from 'prop-types'
import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction, SET_CHATFLOW } from 'store/actions'
// material-ui
import {
Typography,
Box,
Button,
Dialog,
DialogContent,
DialogTitle,
DialogActions,
FormControl,
ListItem,
ListItemAvatar,
ListItemText,
MenuItem,
Select
} from '@mui/material'
import { IconX } from '@tabler/icons'
// Project import
import CredentialInputHandler from 'views/canvas/CredentialInputHandler'
import { TooltipWithParser } from 'ui-component/tooltip/TooltipWithParser'
import { SwitchInput } from 'ui-component/switch/Switch'
import { Input } from 'ui-component/input/Input'
import { StyledButton } from 'ui-component/button/StyledButton'
import { Dropdown } from 'ui-component/dropdown/Dropdown'
import openAISVG from 'assets/images/openai.svg'
import assemblyAIPng from 'assets/images/assemblyai.png'
// store
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions'
import useNotifier from 'utils/useNotifier'
// API
import chatflowsApi from 'api/chatflows'
const speechToTextProviders = {
openAIWhisper: {
label: 'OpenAI Whisper',
name: 'openAIWhisper',
icon: openAISVG,
url: 'https://platform.openai.com/docs/guides/speech-to-text',
inputs: [
{
label: 'Connect Credential',
name: 'credential',
type: 'credential',
credentialNames: ['openAIApi']
},
{
label: 'Language',
name: 'language',
type: 'string',
description:
'The language of the input audio. Supplying the input language in ISO-639-1 format will improve accuracy and latency.',
placeholder: 'en',
optional: true
},
{
label: 'Prompt',
name: 'prompt',
type: 'string',
rows: 4,
description: `An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language.`,
optional: true
},
{
label: 'Temperature',
name: 'temperature',
type: 'number',
step: 0.1,
description: `The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.`,
optional: true
}
]
},
assemblyAiTranscribe: {
label: 'Assembly AI',
name: 'assemblyAiTranscribe',
icon: assemblyAIPng,
url: 'https://www.assemblyai.com/',
inputs: [
{
label: 'Connect Credential',
name: 'credential',
type: 'credential',
credentialNames: ['assemblyAIApi']
}
]
}
}
const SpeechToTextDialog = ({ show, dialogProps, onCancel }) => {
const portalElement = document.getElementById('portal')
const dispatch = useDispatch()
useNotifier()
const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args))
const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))
const [speechToText, setSpeechToText] = useState({})
const [selectedProvider, setSelectedProvider] = useState('none')
const onSave = async () => {
const speechToText = setValue(true, selectedProvider, 'status')
try {
const saveResp = await chatflowsApi.updateChatflow(dialogProps.chatflow.id, {
speechToText: JSON.stringify(speechToText)
})
if (saveResp.data) {
enqueueSnackbar({
message: 'Speech To Text Configuration Saved',
options: {
key: new Date().getTime() + Math.random(),
variant: 'success',
action: (key) => (
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
<IconX />
</Button>
)
}
})
dispatch({ type: SET_CHATFLOW, chatflow: saveResp.data })
}
onCancel()
} catch (error) {
const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`
enqueueSnackbar({
message: `Failed to save Speech To Text Configuration: ${errorData}`,
options: {
key: new Date().getTime() + Math.random(),
variant: 'error',
persist: true,
action: (key) => (
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
<IconX />
</Button>
)
}
})
}
}
const setValue = (value, providerName, inputParamName) => {
let newVal = {}
if (!Object.prototype.hasOwnProperty.call(speechToText, providerName)) {
newVal = { ...speechToText, [providerName]: {} }
} else {
newVal = { ...speechToText }
}
newVal[providerName][inputParamName] = value
if (inputParamName === 'status' && value === true) {
// ensure that the others are turned off
Object.keys(speechToTextProviders).forEach((key) => {
const provider = speechToTextProviders[key]
if (provider.name !== providerName) {
newVal[provider.name] = { ...speechToText[provider.name], status: false }
}
})
}
setSpeechToText(newVal)
return newVal
}
const handleProviderChange = (event) => {
setSelectedProvider(event.target.value)
}
useEffect(() => {
if (dialogProps.chatflow && dialogProps.chatflow.speechToText) {
try {
const speechToText = JSON.parse(dialogProps.chatflow.speechToText)
let selectedProvider = 'none'
Object.keys(speechToTextProviders).forEach((key) => {
const providerConfig = speechToText[key]
if (providerConfig && providerConfig.status) {
selectedProvider = key
}
})
setSelectedProvider(selectedProvider)
setSpeechToText(speechToText)
} catch (e) {
setSpeechToText({})
setSelectedProvider('none')
console.error(e)
}
}
return () => {
setSpeechToText({})
setSelectedProvider('none')
}
}, [dialogProps])
useEffect(() => {
if (show) dispatch({ type: SHOW_CANVAS_DIALOG })
else dispatch({ type: HIDE_CANVAS_DIALOG })
return () => dispatch({ type: HIDE_CANVAS_DIALOG })
}, [show, dispatch])
const component = (
<Dialog
onClose={onCancel}
open={show}
fullWidth
maxWidth='sm'
aria-labelledby='alert-dialog-title'
aria-describedby='alert-dialog-description'
>
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
Speech To Text Configuration
</DialogTitle>
<DialogContent>
<Box fullWidth sx={{ my: 2, display: 'flex', flexDirection: 'column', gap: 1 }}>
<Typography>Speech To Text Providers</Typography>
<FormControl fullWidth>
<Select value={selectedProvider} onChange={handleProviderChange}>
<MenuItem value='none'>None</MenuItem>
<MenuItem value='openAIWhisper'>OpenAI Whisper</MenuItem>
<MenuItem value='assemblyAiTranscribe'>Assembly AI</MenuItem>
</Select>
</FormControl>
</Box>
{selectedProvider !== 'none' && (
<>
<ListItem style={{ padding: 0, margin: 0 }} alignItems='center'>
<ListItemAvatar>
<div
style={{
width: 50,
height: 50,
borderRadius: '50%',
backgroundColor: 'white'
}}
>
<img
style={{
width: '100%',
height: '100%',
padding: 10,
objectFit: 'contain'
}}
alt='AI'
src={speechToTextProviders[selectedProvider].icon}
/>
</div>
</ListItemAvatar>
<ListItemText
sx={{ ml: 1 }}
primary={speechToTextProviders[selectedProvider].label}
secondary={
<a target='_blank' rel='noreferrer' href={speechToTextProviders[selectedProvider].url}>
{speechToTextProviders[selectedProvider].url}
</a>
}
/>
</ListItem>
{speechToTextProviders[selectedProvider].inputs.map((inputParam, index) => (
<Box key={index} sx={{ p: 2 }}>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Typography>
{inputParam.label}
{!inputParam.optional && <span style={{ color: 'red' }}>&nbsp;*</span>}
{inputParam.description && (
<TooltipWithParser style={{ marginLeft: 10 }} title={inputParam.description} />
)}
</Typography>
</div>
{inputParam.type === 'credential' && (
<CredentialInputHandler
key={speechToText[selectedProvider]?.credentialId}
data={
speechToText[selectedProvider]?.credentialId
? { credential: speechToText[selectedProvider].credentialId }
: {}
}
inputParam={inputParam}
onSelect={(newValue) => setValue(newValue, selectedProvider, 'credentialId')}
/>
)}
{inputParam.type === 'boolean' && (
<SwitchInput
onChange={(newValue) => setValue(newValue, selectedProvider, inputParam.name)}
value={
speechToText[selectedProvider]
? speechToText[selectedProvider][inputParam.name]
: inputParam.default ?? false
}
/>
)}
{(inputParam.type === 'string' || inputParam.type === 'password' || inputParam.type === 'number') && (
<Input
inputParam={inputParam}
onChange={(newValue) => setValue(newValue, selectedProvider, inputParam.name)}
value={
speechToText[selectedProvider]
? speechToText[selectedProvider][inputParam.name]
: inputParam.default ?? ''
}
/>
)}
{inputParam.type === 'options' && (
<Dropdown
name={inputParam.name}
options={inputParam.options}
onSelect={(newValue) => setValue(newValue, selectedProvider, inputParam.name)}
value={
speechToText[selectedProvider]
? speechToText[selectedProvider][inputParam.name]
: inputParam.default ?? 'choose an option'
}
/>
)}
</Box>
))}
</>
)}
</DialogContent>
<DialogActions>
<StyledButton
disabled={selectedProvider !== 'none' && !speechToText[selectedProvider]?.credentialId}
variant='contained'
onClick={onSave}
>
Save
</StyledButton>
</DialogActions>
</Dialog>
)
return createPortal(component, portalElement)
}
SpeechToTextDialog.propTypes = {
show: PropTypes.bool,
dialogProps: PropTypes.object,
onCancel: PropTypes.func
}
export default SpeechToTextDialog
@@ -21,7 +21,9 @@ import {
DialogTitle,
ListItem,
ListItemText,
Chip
Chip,
Card,
CardMedia
} from '@mui/material'
import { useTheme } from '@mui/material/styles'
import DatePicker from 'react-datepicker'
@@ -47,7 +49,7 @@ import useApi from 'hooks/useApi'
import useConfirm from 'hooks/useConfirm'
// Utils
import { isValidURL, removeDuplicateURL } from 'utils/genericHelper'
import { getOS, isValidURL, removeDuplicateURL } from 'utils/genericHelper'
import useNotifier from 'utils/useNotifier'
import { baseURL } from 'store/constant'
@@ -69,6 +71,12 @@ DatePickerCustomInput.propTypes = {
onClick: PropTypes.func
}
const messageImageStyle = {
width: '128px',
height: '128px',
objectFit: 'cover'
}
const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
const portalElement = document.getElementById('portal')
const dispatch = useDispatch()
@@ -92,6 +100,8 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
const getChatmessageApi = useApi(chatmessageApi.getAllChatmessageFromChatflow)
const getChatmessageFromPKApi = useApi(chatmessageApi.getChatmessageFromPK)
const getStoragePathFromServer = useApi(chatmessageApi.getStoragePath)
let storagePath = ''
const onStartDateSelected = (date) => {
setStartDate(date)
@@ -120,16 +130,35 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
})
}
const exportMessages = () => {
const exportMessages = async () => {
if (!storagePath && getStoragePathFromServer.data) {
storagePath = getStoragePathFromServer.data.storagePath
}
const obj = {}
let fileSeparator = '/'
if ('windows' === getOS()) {
fileSeparator = '\\'
}
for (let i = 0; i < allChatlogs.length; i += 1) {
const chatmsg = allChatlogs[i]
const chatPK = getChatPK(chatmsg)
let filePaths = []
if (chatmsg.fileUploads) {
chatmsg.fileUploads = JSON.parse(chatmsg.fileUploads)
chatmsg.fileUploads.forEach((file) => {
if (file.type === 'stored-file') {
filePaths.push(
`${storagePath}${fileSeparator}${chatmsg.chatflowid}${fileSeparator}${chatmsg.chatId}${fileSeparator}${file.name}`
)
}
})
}
const msg = {
content: chatmsg.content,
role: chatmsg.role === 'apiMessage' ? 'bot' : 'user',
time: chatmsg.createdDate
}
if (filePaths.length) msg.filePaths = filePaths
if (chatmsg.sourceDocuments) msg.sourceDocuments = JSON.parse(chatmsg.sourceDocuments)
if (chatmsg.usedTools) msg.usedTools = JSON.parse(chatmsg.usedTools)
if (chatmsg.fileAnnotations) msg.fileAnnotations = JSON.parse(chatmsg.fileAnnotations)
@@ -249,6 +278,14 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
})
}
}
if (chatmsg.fileUploads) {
chatmsg.fileUploads = JSON.parse(chatmsg.fileUploads)
chatmsg.fileUploads.forEach((file) => {
if (file.type === 'stored-file') {
file.data = `${baseURL}/api/v1/get-upload-file?chatflowId=${chatmsg.chatflowid}&chatId=${chatmsg.chatId}&fileName=${file.name}`
}
})
}
const obj = {
...chatmsg,
message: chatmsg.content,
@@ -357,6 +394,8 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
useEffect(() => {
if (getChatmessageApi.data) {
getStoragePathFromServer.request()
setAllChatLogs(getChatmessageApi.data)
const chatPK = processChatLogs(getChatmessageApi.data)
setSelectedMessageIndex(0)
@@ -593,8 +632,8 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
sx={{
background:
message.type === 'apiMessage' ? theme.palette.asyncSelect.main : '',
pl: 1,
pr: 1
py: '1rem',
px: '1.5rem'
}}
key={index}
style={{ display: 'flex', justifyContent: 'center', alignContent: 'center' }}
@@ -644,6 +683,51 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
})}
</div>
)}
{message.fileUploads && message.fileUploads.length > 0 && (
<div
style={{
display: 'flex',
flexWrap: 'wrap',
flexDirection: 'column',
width: '100%',
gap: '8px'
}}
>
{message.fileUploads.map((item, index) => {
return (
<>
{item.mime.startsWith('image/') ? (
<Card
key={index}
sx={{
p: 0,
m: 0,
maxWidth: 128,
marginRight: '10px',
flex: '0 0 auto'
}}
>
<CardMedia
component='img'
image={item.data}
sx={{ height: 64 }}
alt={'preview'}
style={messageImageStyle}
/>
</Card>
) : (
// eslint-disable-next-line jsx-a11y/media-has-caption
<audio controls='controls'>
Your browser does not support the &lt;audio&gt;
tag.
<source src={item.data} type={item.mime} />
</audio>
)}
</>
)
})}
</div>
)}
<div className='markdownanswer'>
{/* Messages are being rendered in Markdown format */}
<MemoizedReactMarkdown
+22
View File
@@ -607,3 +607,25 @@ export const getConfigExamplesForCurl = (configData, bodyType, isMultiple, stopN
}
return finalStr
}
export const getOS = () => {
let userAgent = window.navigator.userAgent.toLowerCase(),
macosPlatforms = /(macintosh|macintel|macppc|mac68k|macos)/i,
windowsPlatforms = /(win32|win64|windows|wince)/i,
iosPlatforms = /(iphone|ipad|ipod)/i,
os = null
if (macosPlatforms.test(userAgent)) {
os = 'macos'
} else if (iosPlatforms.test(userAgent)) {
os = 'ios'
} else if (windowsPlatforms.test(userAgent)) {
os = 'windows'
} else if (/android/.test(userAgent)) {
os = 'android'
} else if (!os && /linux/.test(userAgent)) {
os = 'linux'
}
return os
}
@@ -29,6 +29,7 @@ import useApi from 'hooks/useApi'
import { generateExportFlowData } from 'utils/genericHelper'
import { uiBaseURL } from 'store/constant'
import { SET_CHATFLOW } from 'store/actions'
import SpeechToTextDialog from '../../ui-component/dialog/SpeechToTextDialog'
// ==============================|| CANVAS HEADER ||============================== //
@@ -47,6 +48,8 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl
const [apiDialogProps, setAPIDialogProps] = useState({})
const [analyseDialogOpen, setAnalyseDialogOpen] = useState(false)
const [analyseDialogProps, setAnalyseDialogProps] = useState({})
const [speechToAudioDialogOpen, setSpeechToAudioDialogOpen] = useState(false)
const [speechToAudioDialogProps, setSpeechToAudioialogProps] = useState({})
const [conversationStartersDialogOpen, setConversationStartersDialogOpen] = useState(false)
const [conversationStartersDialogProps, setConversationStartersDialogProps] = useState({})
const [viewMessagesDialogOpen, setViewMessagesDialogOpen] = useState(false)
@@ -80,6 +83,12 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl
chatflow: chatflow
})
setAnalyseDialogOpen(true)
} else if (setting === 'enableSpeechToText') {
setSpeechToAudioialogProps({
title: 'Speech to Text',
chatflow: chatflow
})
setSpeechToAudioDialogOpen(true)
} else if (setting === 'viewMessages') {
setViewMessagesDialogProps({
title: 'View Messages',
@@ -394,6 +403,11 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl
/>
<APICodeDialog show={apiDialogOpen} dialogProps={apiDialogProps} onCancel={() => setAPIDialogOpen(false)} />
<AnalyseFlowDialog show={analyseDialogOpen} dialogProps={analyseDialogProps} onCancel={() => setAnalyseDialogOpen(false)} />
<SpeechToTextDialog
show={speechToAudioDialogOpen}
dialogProps={speechToAudioDialogProps}
onCancel={() => setSpeechToAudioDialogOpen(false)}
/>
<StarterPromptsDialog
show={conversationStartersDialogOpen}
dialogProps={conversationStartersDialogProps}
@@ -1,5 +1,5 @@
import PropTypes from 'prop-types'
import { useRef, useState } from 'react'
import { useEffect, useRef, useState } from 'react'
// material-ui
import { IconButton } from '@mui/material'
@@ -88,6 +88,10 @@ const CredentialInputHandler = ({ inputParam, data, onSelect, disabled = false }
setShowSpecificCredentialDialog(true)
}
useEffect(() => {
setCredentialId(data?.credential ?? '')
}, [data])
return (
<div ref={ref}>
{inputParam && (
+1
View File
@@ -47,6 +47,7 @@ const Chatflows = () => {
const [view, setView] = React.useState(localStorage.getItem('flowDisplayStyle') || 'card')
const handleChange = (event, nextView) => {
if (nextView === null) return
localStorage.setItem('flowDisplayStyle', nextView)
setView(nextView)
}
@@ -7,7 +7,7 @@ import { ChatMessage } from './ChatMessage'
import { StyledButton } from 'ui-component/button/StyledButton'
import { IconEraser } from '@tabler/icons'
const ChatExpandDialog = ({ show, dialogProps, onClear, onCancel }) => {
const ChatExpandDialog = ({ show, dialogProps, onClear, onCancel, previews, setPreviews }) => {
const portalElement = document.getElementById('portal')
const customization = useSelector((state) => state.customization)
@@ -21,7 +21,7 @@ const ChatExpandDialog = ({ show, dialogProps, onClear, onCancel }) => {
aria-describedby='alert-dialog-description'
sx={{ overflow: 'visible' }}
>
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
<DialogTitle sx={{ fontSize: '1rem', p: 1.5 }} id='alert-dialog-title'>
<div style={{ display: 'flex', flexDirection: 'row' }}>
{dialogProps.title}
<div style={{ flex: 1 }}></div>
@@ -43,8 +43,17 @@ const ChatExpandDialog = ({ show, dialogProps, onClear, onCancel }) => {
)}
</div>
</DialogTitle>
<DialogContent sx={{ display: 'flex', justifyContent: 'flex-end', flexDirection: 'column' }}>
<ChatMessage isDialog={true} open={dialogProps.open} chatflowid={dialogProps.chatflowid} />
<DialogContent
className='cloud-dialog-wrapper'
sx={{ display: 'flex', justifyContent: 'flex-end', flexDirection: 'column', p: 0 }}
>
<ChatMessage
isDialog={true}
open={dialogProps.open}
chatflowid={dialogProps.chatflowid}
previews={previews}
setPreviews={setPreviews}
/>
</DialogContent>
</Dialog>
) : null
@@ -56,7 +65,9 @@ ChatExpandDialog.propTypes = {
show: PropTypes.bool,
dialogProps: PropTypes.object,
onClear: PropTypes.func,
onCancel: PropTypes.func
onCancel: PropTypes.func,
previews: PropTypes.array,
setPreviews: PropTypes.func
}
export default ChatExpandDialog

Some files were not shown because too many files have changed in this diff Show More