From d19f72db6f990c6ccd5d8c5621813f4d0f625a8a Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 11 Sep 2023 23:44:20 +0100 Subject: [PATCH 1/6] add analytic --- .../credentials/LangfuseApi.credential.ts | 39 ++ .../credentials/LangsmithApi.credential.ts | 33 ++ .../agents/AirtableAgent/AirtableAgent.ts | 9 +- .../nodes/agents/CSVAgent/CSVAgent.ts | 9 +- .../ConversationalRetrievalAgent.ts | 7 +- .../OpenAIFunctionAgent.ts | 7 +- .../nodes/analytic/LangFuse/LangFuse.ts | 33 ++ .../nodes/analytic/LangFuse/langfuse.png | Bin 0 -> 15485 bytes .../nodes/analytic/LangSmith/LangSmith.ts | 33 ++ .../nodes/analytic/LangSmith/langchain.png | Bin 0 -> 58913 bytes .../nodes/chains/ApiChain/GETApiChain.ts | 7 +- .../nodes/chains/ApiChain/OpenAPIChain.ts | 7 +- .../nodes/chains/ApiChain/POSTApiChain.ts | 7 +- .../ConversationChain/ConversationChain.ts | 7 +- .../ConversationalRetrievalQAChain.ts | 7 +- .../nodes/chains/LLMChain/LLMChain.ts | 23 +- .../MultiPromptChain/MultiPromptChain.ts | 7 +- .../MultiRetrievalQAChain.ts | 7 +- .../RetrievalQAChain/RetrievalQAChain.ts | 7 +- .../SqlDatabaseChain/SqlDatabaseChain.ts | 7 +- .../chains/VectorDBQAChain/VectorDBQAChain.ts | 7 +- packages/components/package.json | 4 +- packages/components/src/handler.ts | 67 ++++ packages/server/src/Interface.ts | 1 + packages/server/src/NodesPool.ts | 13 +- .../server/src/database/entities/ChatFlow.ts | 3 + .../mysql/1694432361423-AddAnalytic.ts | 11 + .../src/database/migrations/mysql/index.ts | 4 +- .../postgres/1694432361423-AddAnalytic.ts | 11 + .../src/database/migrations/postgres/index.ts | 4 +- .../sqlite/1694432361423-AddAnalytic.ts | 11 + .../src/database/migrations/sqlite/index.ts | 4 +- packages/server/src/index.ts | 6 +- packages/server/src/utils/index.ts | 19 +- packages/ui/src/assets/images/langchain.png | Bin 0 -> 58913 bytes packages/ui/src/assets/images/langfuse.png | Bin 0 -> 15485 bytes packages/ui/src/menu-items/settings.js | 11 +- packages/ui/src/store/constant.js | 1 + .../ui-component/dialog/AnalyseFlowDialog.js | 358 ++++++++++++++++++ packages/ui/src/ui-component/switch/Switch.js | 2 +- packages/ui/src/views/canvas/CanvasHeader.js | 10 + .../credentials/AddEditCredentialDialog.js | 21 +- 42 files changed, 742 insertions(+), 82 deletions(-) create mode 100644 packages/components/credentials/LangfuseApi.credential.ts create mode 100644 packages/components/credentials/LangsmithApi.credential.ts create mode 100644 packages/components/nodes/analytic/LangFuse/LangFuse.ts create mode 100644 packages/components/nodes/analytic/LangFuse/langfuse.png create mode 100644 packages/components/nodes/analytic/LangSmith/LangSmith.ts create mode 100644 packages/components/nodes/analytic/LangSmith/langchain.png create mode 100644 packages/server/src/database/migrations/mysql/1694432361423-AddAnalytic.ts create mode 100644 packages/server/src/database/migrations/postgres/1694432361423-AddAnalytic.ts create mode 100644 packages/server/src/database/migrations/sqlite/1694432361423-AddAnalytic.ts create mode 100644 packages/ui/src/assets/images/langchain.png create mode 100644 packages/ui/src/assets/images/langfuse.png create mode 100644 packages/ui/src/ui-component/dialog/AnalyseFlowDialog.js diff --git a/packages/components/credentials/LangfuseApi.credential.ts b/packages/components/credentials/LangfuseApi.credential.ts new file mode 100644 index 00000000..452ca989 --- /dev/null +++ b/packages/components/credentials/LangfuseApi.credential.ts @@ -0,0 +1,39 @@ +import { INodeParams, INodeCredential } from '../src/Interface' + +class LangfuseApi implements INodeCredential { + label: string + name: string + version: number + description: string + inputs: INodeParams[] + + constructor() { + this.label = 'Langfuse API' + this.name = 'langfuseApi' + this.version = 1.0 + this.description = + 'Refer to official guide on how to get API key on Langfuse' + this.inputs = [ + { + label: 'Secret Key', + name: 'langFuseSecretKey', + type: 'password', + placeholder: 'sk-lf-abcdefg' + }, + { + label: 'Public Key', + name: 'langFusePublicKey', + type: 'string', + placeholder: 'pk-lf-abcdefg' + }, + { + label: 'Endpoint', + name: 'langFuseEndpoint', + type: 'string', + default: 'https://cloud.langfuse.com' + } + ] + } +} + +module.exports = { credClass: LangfuseApi } diff --git a/packages/components/credentials/LangsmithApi.credential.ts b/packages/components/credentials/LangsmithApi.credential.ts new file mode 100644 index 00000000..09ae6773 --- /dev/null +++ b/packages/components/credentials/LangsmithApi.credential.ts @@ -0,0 +1,33 @@ +import { INodeParams, INodeCredential } from '../src/Interface' + +class LangsmithApi implements INodeCredential { + label: string + name: string + version: number + description: string + inputs: INodeParams[] + + constructor() { + this.label = 'Langsmith API' + this.name = 'langsmithApi' + this.version = 1.0 + this.description = + 'Refer to official guide on how to get API key on Langsmith' + this.inputs = [ + { + label: 'API Key', + name: 'langSmithApiKey', + type: 'password', + placeholder: '' + }, + { + label: 'Endpoint', + name: 'langSmithEndpoint', + type: 'string', + default: 'https://api.smith.langchain.com' + } + ] + } +} + +module.exports = { credClass: LangsmithApi } diff --git a/packages/components/nodes/agents/AirtableAgent/AirtableAgent.ts b/packages/components/nodes/agents/AirtableAgent/AirtableAgent.ts index 074f39c1..7bdbb65a 100644 --- a/packages/components/nodes/agents/AirtableAgent/AirtableAgent.ts +++ b/packages/components/nodes/agents/AirtableAgent/AirtableAgent.ts @@ -4,7 +4,7 @@ import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../ import { LoadPyodide, finalSystemPrompt, systemPrompt } from './core' import { LLMChain } from 'langchain/chains' import { BaseLanguageModel } from 'langchain/base_language' -import { ConsoleCallbackHandler, CustomChainHandler } from '../../../src/handler' +import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler' import axios from 'axios' class Airtable_Agents implements INode { @@ -102,6 +102,7 @@ class Airtable_Agents implements INode { const loggerHandler = new ConsoleCallbackHandler(options.logger) const handler = new CustomChainHandler(options.socketIO, options.socketIOClientId) + const callbacks = await additionalCallbacks(nodeData, options) const pyodide = await LoadPyodide() @@ -141,7 +142,7 @@ json.dumps(my_dict)` dict: dataframeColDict, question: input } - const res = await chain.call(inputs, [loggerHandler]) + const res = await chain.call(inputs, [loggerHandler, ...callbacks]) pythonCode = res?.text } @@ -169,10 +170,10 @@ json.dumps(my_dict)` } if (options.socketIO && options.socketIOClientId) { - const result = await chain.call(inputs, [loggerHandler, handler]) + const result = await chain.call(inputs, [loggerHandler, handler, ...callbacks]) return result?.text } else { - const result = await chain.call(inputs, [loggerHandler]) + const result = await chain.call(inputs, [loggerHandler, ...callbacks]) return result?.text } } diff --git a/packages/components/nodes/agents/CSVAgent/CSVAgent.ts b/packages/components/nodes/agents/CSVAgent/CSVAgent.ts index 4a42592f..905bef02 100644 --- a/packages/components/nodes/agents/CSVAgent/CSVAgent.ts +++ b/packages/components/nodes/agents/CSVAgent/CSVAgent.ts @@ -4,7 +4,7 @@ import { getBaseClasses } from '../../../src/utils' import { LoadPyodide, finalSystemPrompt, systemPrompt } from './core' import { LLMChain } from 'langchain/chains' import { BaseLanguageModel } from 'langchain/base_language' -import { ConsoleCallbackHandler, CustomChainHandler } from '../../../src/handler' +import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler' class CSV_Agents implements INode { label: string @@ -63,6 +63,7 @@ class CSV_Agents implements INode { const loggerHandler = new ConsoleCallbackHandler(options.logger) const handler = new CustomChainHandler(options.socketIO, options.socketIOClientId) + const callbacks = await additionalCallbacks(nodeData, options) let files: string[] = [] @@ -119,7 +120,7 @@ json.dumps(my_dict)` dict: dataframeColDict, question: input } - const res = await chain.call(inputs, [loggerHandler]) + const res = await chain.call(inputs, [loggerHandler, ...callbacks]) pythonCode = res?.text } @@ -149,10 +150,10 @@ json.dumps(my_dict)` } if (options.socketIO && options.socketIOClientId) { - const result = await chain.call(inputs, [loggerHandler, handler]) + const result = await chain.call(inputs, [loggerHandler, handler, ...callbacks]) return result?.text } else { - const result = await chain.call(inputs, [loggerHandler]) + const result = await chain.call(inputs, [loggerHandler, ...callbacks]) return result?.text } } diff --git a/packages/components/nodes/agents/ConversationalRetrievalAgent/ConversationalRetrievalAgent.ts b/packages/components/nodes/agents/ConversationalRetrievalAgent/ConversationalRetrievalAgent.ts index c0cef052..3d70a2d3 100644 --- a/packages/components/nodes/agents/ConversationalRetrievalAgent/ConversationalRetrievalAgent.ts +++ b/packages/components/nodes/agents/ConversationalRetrievalAgent/ConversationalRetrievalAgent.ts @@ -3,7 +3,7 @@ import { initializeAgentExecutorWithOptions, AgentExecutor } from 'langchain/age import { getBaseClasses, mapChatHistory } from '../../../src/utils' import { flatten } from 'lodash' import { BaseChatMemory } from 'langchain/memory' -import { ConsoleCallbackHandler, CustomChainHandler } from '../../../src/handler' +import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler' const defaultMessage = `Do your best to answer the questions. Feel free to use any tools available to look up relevant information, only if necessary.` @@ -86,13 +86,14 @@ class ConversationalRetrievalAgent_Agents implements INode { } const loggerHandler = new ConsoleCallbackHandler(options.logger) + const callbacks = await additionalCallbacks(nodeData, options) if (options.socketIO && options.socketIOClientId) { const handler = new CustomChainHandler(options.socketIO, options.socketIOClientId) - const result = await executor.call({ input }, [loggerHandler, handler]) + const result = await executor.call({ input }, [loggerHandler, handler, ...callbacks]) return result?.output } else { - const result = await executor.call({ input }, [loggerHandler]) + const result = await executor.call({ input }, [loggerHandler, ...callbacks]) return result?.output } } diff --git a/packages/components/nodes/agents/OpenAIFunctionAgent/OpenAIFunctionAgent.ts b/packages/components/nodes/agents/OpenAIFunctionAgent/OpenAIFunctionAgent.ts index 8c182d1a..c1bd32ec 100644 --- a/packages/components/nodes/agents/OpenAIFunctionAgent/OpenAIFunctionAgent.ts +++ b/packages/components/nodes/agents/OpenAIFunctionAgent/OpenAIFunctionAgent.ts @@ -4,7 +4,7 @@ import { getBaseClasses, mapChatHistory } from '../../../src/utils' import { BaseLanguageModel } from 'langchain/base_language' import { flatten } from 'lodash' import { BaseChatMemory } from 'langchain/memory' -import { ConsoleCallbackHandler, CustomChainHandler } from '../../../src/handler' +import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler' class OpenAIFunctionAgent_Agents implements INode { label: string @@ -86,13 +86,14 @@ class OpenAIFunctionAgent_Agents implements INode { } const loggerHandler = new ConsoleCallbackHandler(options.logger) + const callbacks = await additionalCallbacks(nodeData, options) if (options.socketIO && options.socketIOClientId) { const handler = new CustomChainHandler(options.socketIO, options.socketIOClientId) - const result = await executor.run(input, [loggerHandler, handler]) + const result = await executor.run(input, [loggerHandler, handler, ...callbacks]) return result } else { - const result = await executor.run(input, [loggerHandler]) + const result = await executor.run(input, [loggerHandler, ...callbacks]) return result } } diff --git a/packages/components/nodes/analytic/LangFuse/LangFuse.ts b/packages/components/nodes/analytic/LangFuse/LangFuse.ts new file mode 100644 index 00000000..dcfc3d2a --- /dev/null +++ b/packages/components/nodes/analytic/LangFuse/LangFuse.ts @@ -0,0 +1,33 @@ +import { INode, INodeParams } from '../../../src/Interface' + +class LangFuse_Analytic 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 = 'LangFuse' + this.name = 'langFuse' + this.version = 1.0 + this.type = 'LangFuse' + this.icon = 'langfuse.png' + this.category = 'Analytic' + this.baseClasses = [this.type] + this.inputs = [] + this.credential = { + label: 'Connect Credential', + name: 'credential', + type: 'credential', + credentialNames: ['langfuseApi'] + } + } +} + +module.exports = { nodeClass: LangFuse_Analytic } diff --git a/packages/components/nodes/analytic/LangFuse/langfuse.png b/packages/components/nodes/analytic/LangFuse/langfuse.png new file mode 100644 index 0000000000000000000000000000000000000000..df9181b8dfad5286ad1cb6d2cf8f5a0631a5ef3a GIT binary patch literal 15485 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 @Bok*SLeD@x@yGivkx9M1+2R)2) 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°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 S_sACEb*U(5$k)o6h5Qczr~XI) literal 0 HcmV?d00001 diff --git a/packages/components/nodes/chains/ApiChain/GETApiChain.ts b/packages/components/nodes/chains/ApiChain/GETApiChain.ts index bd4f3bc0..89aa2f61 100644 --- a/packages/components/nodes/chains/ApiChain/GETApiChain.ts +++ b/packages/components/nodes/chains/ApiChain/GETApiChain.ts @@ -3,7 +3,7 @@ import { APIChain } from 'langchain/chains' import { getBaseClasses } from '../../../src/utils' import { BaseLanguageModel } from 'langchain/base_language' import { PromptTemplate } from 'langchain/prompts' -import { ConsoleCallbackHandler, CustomChainHandler } from '../../../src/handler' +import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler' export const API_URL_RAW_PROMPT_TEMPLATE = `You are given the below API Documentation: {api_docs} @@ -99,13 +99,14 @@ class GETApiChain_Chains implements INode { const chain = await getAPIChain(apiDocs, model, headers, urlPrompt, ansPrompt) const loggerHandler = new ConsoleCallbackHandler(options.logger) + const callbacks = await additionalCallbacks(nodeData, options) if (options.socketIO && options.socketIOClientId) { const handler = new CustomChainHandler(options.socketIO, options.socketIOClientId, 2) - const res = await chain.run(input, [loggerHandler, handler]) + const res = await chain.run(input, [loggerHandler, handler, ...callbacks]) return res } else { - const res = await chain.run(input, [loggerHandler]) + const res = await chain.run(input, [loggerHandler, ...callbacks]) return res } } diff --git a/packages/components/nodes/chains/ApiChain/OpenAPIChain.ts b/packages/components/nodes/chains/ApiChain/OpenAPIChain.ts index 9f6c79e4..08dc3cc8 100644 --- a/packages/components/nodes/chains/ApiChain/OpenAPIChain.ts +++ b/packages/components/nodes/chains/ApiChain/OpenAPIChain.ts @@ -2,7 +2,7 @@ import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Inter import { APIChain, createOpenAPIChain } from 'langchain/chains' import { getBaseClasses } from '../../../src/utils' import { ChatOpenAI } from 'langchain/chat_models/openai' -import { ConsoleCallbackHandler, CustomChainHandler } from '../../../src/handler' +import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler' class OpenApiChain_Chains implements INode { label: string @@ -61,13 +61,14 @@ class OpenApiChain_Chains implements INode { async run(nodeData: INodeData, input: string, options: ICommonObject): Promise { const chain = await initChain(nodeData) const loggerHandler = new ConsoleCallbackHandler(options.logger) + const callbacks = await additionalCallbacks(nodeData, options) if (options.socketIO && options.socketIOClientId) { const handler = new CustomChainHandler(options.socketIO, options.socketIOClientId) - const res = await chain.run(input, [loggerHandler, handler]) + const res = await chain.run(input, [loggerHandler, handler, ...callbacks]) return res } else { - const res = await chain.run(input, [loggerHandler]) + const res = await chain.run(input, [loggerHandler, ...callbacks]) return res } } diff --git a/packages/components/nodes/chains/ApiChain/POSTApiChain.ts b/packages/components/nodes/chains/ApiChain/POSTApiChain.ts index cba4a297..70277ee9 100644 --- a/packages/components/nodes/chains/ApiChain/POSTApiChain.ts +++ b/packages/components/nodes/chains/ApiChain/POSTApiChain.ts @@ -3,7 +3,7 @@ import { getBaseClasses } from '../../../src/utils' import { BaseLanguageModel } from 'langchain/base_language' import { PromptTemplate } from 'langchain/prompts' import { API_RESPONSE_RAW_PROMPT_TEMPLATE, API_URL_RAW_PROMPT_TEMPLATE, APIChain } from './postCore' -import { ConsoleCallbackHandler, CustomChainHandler } from '../../../src/handler' +import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler' class POSTApiChain_Chains implements INode { label: string @@ -88,13 +88,14 @@ class POSTApiChain_Chains implements INode { const chain = await getAPIChain(apiDocs, model, headers, urlPrompt, ansPrompt) const loggerHandler = new ConsoleCallbackHandler(options.logger) + const callbacks = await additionalCallbacks(nodeData, options) if (options.socketIO && options.socketIOClientId) { const handler = new CustomChainHandler(options.socketIO, options.socketIOClientId, 2) - const res = await chain.run(input, [loggerHandler, handler]) + const res = await chain.run(input, [loggerHandler, handler, ...callbacks]) return res } else { - const res = await chain.run(input, [loggerHandler]) + const res = await chain.run(input, [loggerHandler, ...callbacks]) return res } } diff --git a/packages/components/nodes/chains/ConversationChain/ConversationChain.ts b/packages/components/nodes/chains/ConversationChain/ConversationChain.ts index 08663395..b26603e2 100644 --- a/packages/components/nodes/chains/ConversationChain/ConversationChain.ts +++ b/packages/components/nodes/chains/ConversationChain/ConversationChain.ts @@ -4,7 +4,7 @@ import { getBaseClasses, mapChatHistory } from '../../../src/utils' import { ChatPromptTemplate, HumanMessagePromptTemplate, MessagesPlaceholder, SystemMessagePromptTemplate } from 'langchain/prompts' import { BufferMemory } from 'langchain/memory' import { BaseChatModel } from 'langchain/chat_models/base' -import { ConsoleCallbackHandler, CustomChainHandler } from '../../../src/handler' +import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler' import { flatten } from 'lodash' import { Document } from 'langchain/document' @@ -111,13 +111,14 @@ class ConversationChain_Chains implements INode { } const loggerHandler = new ConsoleCallbackHandler(options.logger) + const callbacks = await additionalCallbacks(nodeData, options) if (options.socketIO && options.socketIOClientId) { const handler = new CustomChainHandler(options.socketIO, options.socketIOClientId) - const res = await chain.call({ input }, [loggerHandler, handler]) + const res = await chain.call({ input }, [loggerHandler, handler, ...callbacks]) return res?.response } else { - const res = await chain.call({ input }, [loggerHandler]) + const res = await chain.call({ input }, [loggerHandler, ...callbacks]) return res?.response } } diff --git a/packages/components/nodes/chains/ConversationalRetrievalQAChain/ConversationalRetrievalQAChain.ts b/packages/components/nodes/chains/ConversationalRetrievalQAChain/ConversationalRetrievalQAChain.ts index c14b292d..1b4675bd 100644 --- a/packages/components/nodes/chains/ConversationalRetrievalQAChain/ConversationalRetrievalQAChain.ts +++ b/packages/components/nodes/chains/ConversationalRetrievalQAChain/ConversationalRetrievalQAChain.ts @@ -5,7 +5,7 @@ import { ConversationalRetrievalQAChain, QAChainParams } from 'langchain/chains' import { BaseRetriever } from 'langchain/schema/retriever' import { BufferMemory, BufferMemoryInput } from 'langchain/memory' import { PromptTemplate } from 'langchain/prompts' -import { ConsoleCallbackHandler, CustomChainHandler } from '../../../src/handler' +import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler' import { default_map_reduce_template, default_qa_template, @@ -183,6 +183,7 @@ class ConversationalRetrievalQAChain_Chains implements INode { } const loggerHandler = new ConsoleCallbackHandler(options.logger) + const callbacks = await additionalCallbacks(nodeData, options) if (options.socketIO && options.socketIOClientId) { const handler = new CustomChainHandler( @@ -191,7 +192,7 @@ class ConversationalRetrievalQAChain_Chains implements INode { chainOption === 'refine' ? 4 : undefined, returnSourceDocuments ) - const res = await chain.call(obj, [loggerHandler, handler]) + const res = await chain.call(obj, [loggerHandler, handler, ...callbacks]) if (chainOption === 'refine') { if (res.output_text && res.sourceDocuments) { return { @@ -204,7 +205,7 @@ class ConversationalRetrievalQAChain_Chains implements INode { if (res.text && res.sourceDocuments) return res return res?.text } else { - const res = await chain.call(obj, [loggerHandler]) + const res = await chain.call(obj, [loggerHandler, ...callbacks]) if (res.text && res.sourceDocuments) return res return res?.text } diff --git a/packages/components/nodes/chains/LLMChain/LLMChain.ts b/packages/components/nodes/chains/LLMChain/LLMChain.ts index 5088b34d..63994b13 100644 --- a/packages/components/nodes/chains/LLMChain/LLMChain.ts +++ b/packages/components/nodes/chains/LLMChain/LLMChain.ts @@ -2,7 +2,7 @@ import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from import { getBaseClasses, handleEscapeCharacters } from '../../../src/utils' import { LLMChain } from 'langchain/chains' import { BaseLanguageModel } from 'langchain/base_language' -import { ConsoleCallbackHandler, CustomChainHandler } from '../../../src/handler' +import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler' class LLMChain_Chains implements INode { label: string @@ -70,7 +70,7 @@ class LLMChain_Chains implements INode { } else if (output === 'outputPrediction') { const chain = new LLMChain({ llm: model, prompt, verbose: process.env.DEBUG === 'true' ? true : false }) const inputVariables = chain.prompt.inputVariables as string[] // ["product"] - const res = await runPrediction(inputVariables, chain, input, promptValues, options) + const res = await runPrediction(inputVariables, chain, input, promptValues, options, nodeData) // eslint-disable-next-line no-console console.log('\x1b[92m\x1b[1m\n*****OUTPUT PREDICTION*****\n\x1b[0m\x1b[0m') // eslint-disable-next-line no-console @@ -88,7 +88,7 @@ class LLMChain_Chains implements INode { const inputVariables = nodeData.instance.prompt.inputVariables as string[] // ["product"] const chain = nodeData.instance as LLMChain const promptValues = nodeData.inputs?.prompt.promptValues as ICommonObject - const res = await runPrediction(inputVariables, chain, input, promptValues, options) + const res = await runPrediction(inputVariables, chain, input, promptValues, options, nodeData) // eslint-disable-next-line no-console console.log('\x1b[93m\x1b[1m\n*****FINAL RESULT*****\n\x1b[0m\x1b[0m') // eslint-disable-next-line no-console @@ -102,9 +102,12 @@ const runPrediction = async ( chain: LLMChain, input: string, promptValuesRaw: ICommonObject, - options: ICommonObject + options: ICommonObject, + nodeData: INodeData ) => { const loggerHandler = new ConsoleCallbackHandler(options.logger) + const callbacks = await additionalCallbacks(nodeData, options) + const isStreaming = options.socketIO && options.socketIOClientId const socketIO = isStreaming ? options.socketIO : undefined const socketIOClientId = isStreaming ? options.socketIOClientId : '' @@ -131,10 +134,10 @@ const runPrediction = async ( const options = { ...promptValues } if (isStreaming) { const handler = new CustomChainHandler(socketIO, socketIOClientId) - const res = await chain.call(options, [loggerHandler, handler]) + const res = await chain.call(options, [loggerHandler, handler, ...callbacks]) return res?.text } else { - const res = await chain.call(options, [loggerHandler]) + const res = await chain.call(options, [loggerHandler, ...callbacks]) return res?.text } } else if (seen.length === 1) { @@ -147,10 +150,10 @@ const runPrediction = async ( } if (isStreaming) { const handler = new CustomChainHandler(socketIO, socketIOClientId) - const res = await chain.call(options, [loggerHandler, handler]) + const res = await chain.call(options, [loggerHandler, handler, ...callbacks]) return res?.text } else { - const res = await chain.call(options, [loggerHandler]) + const res = await chain.call(options, [loggerHandler, ...callbacks]) return res?.text } } else { @@ -159,10 +162,10 @@ const runPrediction = async ( } else { if (isStreaming) { const handler = new CustomChainHandler(socketIO, socketIOClientId) - const res = await chain.run(input, [loggerHandler, handler]) + const res = await chain.run(input, [loggerHandler, handler, ...callbacks]) return res } else { - const res = await chain.run(input, [loggerHandler]) + const res = await chain.run(input, [loggerHandler, ...callbacks]) return res } } diff --git a/packages/components/nodes/chains/MultiPromptChain/MultiPromptChain.ts b/packages/components/nodes/chains/MultiPromptChain/MultiPromptChain.ts index 0d137714..d171e514 100644 --- a/packages/components/nodes/chains/MultiPromptChain/MultiPromptChain.ts +++ b/packages/components/nodes/chains/MultiPromptChain/MultiPromptChain.ts @@ -2,7 +2,7 @@ import { BaseLanguageModel } from 'langchain/base_language' import { ICommonObject, INode, INodeData, INodeParams, PromptRetriever } from '../../../src/Interface' import { getBaseClasses } from '../../../src/utils' import { MultiPromptChain } from 'langchain/chains' -import { ConsoleCallbackHandler, CustomChainHandler } from '../../../src/handler' +import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler' class MultiPromptChain_Chains implements INode { label: string @@ -67,13 +67,14 @@ class MultiPromptChain_Chains implements INode { const obj = { input } const loggerHandler = new ConsoleCallbackHandler(options.logger) + const callbacks = await additionalCallbacks(nodeData, options) if (options.socketIO && options.socketIOClientId) { const handler = new CustomChainHandler(options.socketIO, options.socketIOClientId, 2) - const res = await chain.call(obj, [loggerHandler, handler]) + const res = await chain.call(obj, [loggerHandler, handler, ...callbacks]) return res?.text } else { - const res = await chain.call(obj, [loggerHandler]) + const res = await chain.call(obj, [loggerHandler, ...callbacks]) return res?.text } } diff --git a/packages/components/nodes/chains/MultiRetrievalQAChain/MultiRetrievalQAChain.ts b/packages/components/nodes/chains/MultiRetrievalQAChain/MultiRetrievalQAChain.ts index 6d150647..93bf2255 100644 --- a/packages/components/nodes/chains/MultiRetrievalQAChain/MultiRetrievalQAChain.ts +++ b/packages/components/nodes/chains/MultiRetrievalQAChain/MultiRetrievalQAChain.ts @@ -2,7 +2,7 @@ import { BaseLanguageModel } from 'langchain/base_language' import { ICommonObject, INode, INodeData, INodeParams, VectorStoreRetriever } from '../../../src/Interface' import { getBaseClasses } from '../../../src/utils' import { MultiRetrievalQAChain } from 'langchain/chains' -import { ConsoleCallbackHandler, CustomChainHandler } from '../../../src/handler' +import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler' class MultiRetrievalQAChain_Chains implements INode { label: string @@ -75,14 +75,15 @@ class MultiRetrievalQAChain_Chains implements INode { const obj = { input } const loggerHandler = new ConsoleCallbackHandler(options.logger) + const callbacks = await additionalCallbacks(nodeData, options) if (options.socketIO && options.socketIOClientId) { const handler = new CustomChainHandler(options.socketIO, options.socketIOClientId, 2, returnSourceDocuments) - const res = await chain.call(obj, [loggerHandler, handler]) + const res = await chain.call(obj, [loggerHandler, handler, ...callbacks]) if (res.text && res.sourceDocuments) return res return res?.text } else { - const res = await chain.call(obj, [loggerHandler]) + const res = await chain.call(obj, [loggerHandler, ...callbacks]) if (res.text && res.sourceDocuments) return res return res?.text } diff --git a/packages/components/nodes/chains/RetrievalQAChain/RetrievalQAChain.ts b/packages/components/nodes/chains/RetrievalQAChain/RetrievalQAChain.ts index 935866ca..bff2a0a7 100644 --- a/packages/components/nodes/chains/RetrievalQAChain/RetrievalQAChain.ts +++ b/packages/components/nodes/chains/RetrievalQAChain/RetrievalQAChain.ts @@ -3,7 +3,7 @@ import { RetrievalQAChain } from 'langchain/chains' import { BaseRetriever } from 'langchain/schema/retriever' import { getBaseClasses } from '../../../src/utils' import { BaseLanguageModel } from 'langchain/base_language' -import { ConsoleCallbackHandler, CustomChainHandler } from '../../../src/handler' +import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler' class RetrievalQAChain_Chains implements INode { label: string @@ -53,13 +53,14 @@ class RetrievalQAChain_Chains implements INode { query: input } const loggerHandler = new ConsoleCallbackHandler(options.logger) + const callbacks = await additionalCallbacks(nodeData, options) if (options.socketIO && options.socketIOClientId) { const handler = new CustomChainHandler(options.socketIO, options.socketIOClientId) - const res = await chain.call(obj, [loggerHandler, handler]) + const res = await chain.call(obj, [loggerHandler, handler, ...callbacks]) return res?.text } else { - const res = await chain.call(obj, [loggerHandler]) + const res = await chain.call(obj, [loggerHandler, ...callbacks]) return res?.text } } diff --git a/packages/components/nodes/chains/SqlDatabaseChain/SqlDatabaseChain.ts b/packages/components/nodes/chains/SqlDatabaseChain/SqlDatabaseChain.ts index 04d704a5..4061d269 100644 --- a/packages/components/nodes/chains/SqlDatabaseChain/SqlDatabaseChain.ts +++ b/packages/components/nodes/chains/SqlDatabaseChain/SqlDatabaseChain.ts @@ -5,7 +5,7 @@ import { DataSource } from 'typeorm' import { SqlDatabase } from 'langchain/sql_db' import { BaseLanguageModel } from 'langchain/base_language' import { PromptTemplate, PromptTemplateInput } from 'langchain/prompts' -import { ConsoleCallbackHandler, CustomChainHandler } from '../../../src/handler' +import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler' import { DataSourceOptions } from 'typeorm/data-source' type DatabaseType = 'sqlite' | 'postgres' | 'mssql' | 'mysql' @@ -119,13 +119,14 @@ class SqlDatabaseChain_Chains implements INode { const chain = await getSQLDBChain(databaseType, url, model, customPrompt) const loggerHandler = new ConsoleCallbackHandler(options.logger) + const callbacks = await additionalCallbacks(nodeData, options) if (options.socketIO && options.socketIOClientId) { const handler = new CustomChainHandler(options.socketIO, options.socketIOClientId, 2) - const res = await chain.run(input, [loggerHandler, handler]) + const res = await chain.run(input, [loggerHandler, handler, ...callbacks]) return res } else { - const res = await chain.run(input, [loggerHandler]) + const res = await chain.run(input, [loggerHandler, ...callbacks]) return res } } diff --git a/packages/components/nodes/chains/VectorDBQAChain/VectorDBQAChain.ts b/packages/components/nodes/chains/VectorDBQAChain/VectorDBQAChain.ts index 03811682..6c9447d3 100644 --- a/packages/components/nodes/chains/VectorDBQAChain/VectorDBQAChain.ts +++ b/packages/components/nodes/chains/VectorDBQAChain/VectorDBQAChain.ts @@ -3,7 +3,7 @@ import { getBaseClasses } from '../../../src/utils' import { VectorDBQAChain } from 'langchain/chains' import { BaseLanguageModel } from 'langchain/base_language' import { VectorStore } from 'langchain/vectorstores' -import { ConsoleCallbackHandler, CustomChainHandler } from '../../../src/handler' +import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler' class VectorDBQAChain_Chains implements INode { label: string @@ -57,13 +57,14 @@ class VectorDBQAChain_Chains implements INode { } const loggerHandler = new ConsoleCallbackHandler(options.logger) + const callbacks = await additionalCallbacks(nodeData, options) if (options.socketIO && options.socketIOClientId) { const handler = new CustomChainHandler(options.socketIO, options.socketIOClientId) - const res = await chain.call(obj, [loggerHandler, handler]) + const res = await chain.call(obj, [loggerHandler, handler, ...callbacks]) return res?.text } else { - const res = await chain.call(obj, [loggerHandler]) + const res = await chain.call(obj, [loggerHandler, ...callbacks]) return res?.text } } diff --git a/packages/components/package.json b/packages/components/package.json index d3dd4e53..6e522a5d 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -42,7 +42,9 @@ "google-auth-library": "^9.0.0", "graphql": "^16.6.0", "html-to-text": "^9.0.5", - "langchain": "^0.0.133", + "langchain": "^0.0.145", + "langfuse-langchain": "^1.0.14-alpha.0", + "langsmith": "^0.0.32", "linkifyjs": "^4.1.1", "mammoth": "^1.5.1", "moment": "^2.29.3", diff --git a/packages/components/src/handler.ts b/packages/components/src/handler.ts index 8e363361..f13719ce 100644 --- a/packages/components/src/handler.ts +++ b/packages/components/src/handler.ts @@ -2,6 +2,11 @@ import { BaseTracer, Run, BaseCallbackHandler } from 'langchain/callbacks' import { AgentAction, ChainValues } from 'langchain/schema' import { Logger } from 'winston' import { Server } from 'socket.io' +import { Client } from 'langsmith' +import { LangChainTracer } from 'langchain/callbacks' +import { getCredentialData, getCredentialParam } from './utils' +import { ICommonObject, INodeData } from './Interface' +import CallbackHandler from 'langfuse-langchain' interface AgentRun extends Run { actions: AgentAction[] @@ -178,3 +183,65 @@ export class CustomChainHandler extends BaseCallbackHandler { } } } + +export const additionalCallbacks = async (nodeData: INodeData, options: ICommonObject) => { + try { + if (!options.analytic) return [] + + const analytic = JSON.parse(options.analytic) + const callbacks: any = [] + + for (const provider in analytic) { + const providerStatus = analytic[provider].status as boolean + if (providerStatus) { + if (provider === 'langSmith') { + const credentialId = analytic[provider].credentialId as string + const langSmithProject = analytic[provider].projectName as string + + const credentialData = await getCredentialData(credentialId ?? '', options) + const langSmithApiKey = getCredentialParam('langSmithApiKey', credentialData, nodeData) + const langSmithEndpoint = getCredentialParam('langSmithEndpoint', credentialData, nodeData) + + const client = new Client({ + apiUrl: langSmithEndpoint ?? 'https://api.smith.langchain.com', + apiKey: langSmithApiKey + }) + + const tracer = new LangChainTracer({ + projectName: langSmithProject ?? 'default', + //@ts-ignore + client + }) + callbacks.push(tracer) + } else if (provider === 'langFuse') { + const credentialId = analytic[provider].credentialId as string + const flushAt = analytic[provider].flushAt as string + const flushInterval = analytic[provider].flushInterval as string + const requestTimeout = analytic[provider].requestTimeout as string + const release = analytic[provider].release as string + + const credentialData = await getCredentialData(credentialId ?? '', options) + const langFuseSecretKey = getCredentialParam('langFuseSecretKey', credentialData, nodeData) + const langFusePublicKey = getCredentialParam('langFusePublicKey', credentialData, nodeData) + const langFuseEndpoint = getCredentialParam('langFuseEndpoint', credentialData, nodeData) + + const langFuseOptions: ICommonObject = { + secretKey: langFuseSecretKey, + publicKey: langFusePublicKey, + baseUrl: langFuseEndpoint ?? 'https://cloud.langfuse.com' + } + if (flushAt) langFuseOptions.flushAt = parseInt(flushAt, 10) + if (flushInterval) langFuseOptions.flushInterval = parseInt(flushInterval, 10) + if (requestTimeout) langFuseOptions.requestTimeout = parseInt(requestTimeout, 10) + if (release) langFuseOptions.release = release + + const handler = new CallbackHandler(langFuseOptions) + callbacks.push(handler) + } + } + } + return callbacks + } catch (e) { + throw new Error(e) + } +} diff --git a/packages/server/src/Interface.ts b/packages/server/src/Interface.ts index 42c1231a..58740b86 100644 --- a/packages/server/src/Interface.ts +++ b/packages/server/src/Interface.ts @@ -14,6 +14,7 @@ export interface IChatFlow { deployed?: boolean isPublic?: boolean apikeyid?: string + analytic?: string chatbotConfig?: string apiConfig?: any } diff --git a/packages/server/src/NodesPool.ts b/packages/server/src/NodesPool.ts index 62db41ba..f4681d4a 100644 --- a/packages/server/src/NodesPool.ts +++ b/packages/server/src/NodesPool.ts @@ -15,7 +15,7 @@ export class NodesPool { */ async initialize() { await this.initializeNodes() - await this.initializeCrdentials() + await this.initializeCredentials() } /** @@ -34,8 +34,6 @@ export class NodesPool { const newNodeInstance = new nodeModule.nodeClass() newNodeInstance.filePath = file - this.componentNodes[newNodeInstance.name] = newNodeInstance - // Replace file icon with absolute path if ( newNodeInstance.icon && @@ -46,7 +44,7 @@ export class NodesPool { const filePath = file.replace(/\\/g, '/').split('/') filePath.pop() const nodeIconAbsolutePath = `${filePath.join('/')}/${newNodeInstance.icon}` - this.componentNodes[newNodeInstance.name].icon = nodeIconAbsolutePath + newNodeInstance.icon = nodeIconAbsolutePath // Store icon path for componentCredentials if (newNodeInstance.credential) { @@ -55,6 +53,11 @@ export class NodesPool { } } } + + const skipCategories = ['Analytic'] + if (!skipCategories.includes(newNodeInstance.category)) { + this.componentNodes[newNodeInstance.name] = newNodeInstance + } } } }) @@ -64,7 +67,7 @@ export class NodesPool { /** * Initialize credentials */ - private async initializeCrdentials() { + private async initializeCredentials() { const packagePath = getNodeModulesPackagePath('flowise-components') const nodesPath = path.join(packagePath, 'dist', 'credentials') const nodeFiles = await this.getFiles(nodesPath) diff --git a/packages/server/src/database/entities/ChatFlow.ts b/packages/server/src/database/entities/ChatFlow.ts index 29f58e5c..376a100b 100644 --- a/packages/server/src/database/entities/ChatFlow.ts +++ b/packages/server/src/database/entities/ChatFlow.ts @@ -28,6 +28,9 @@ export class ChatFlow implements IChatFlow { @Column({ nullable: true, type: 'text' }) apiConfig?: string + @Column({ nullable: true, type: 'text' }) + analytic?: string + @CreateDateColumn() createdDate: Date diff --git a/packages/server/src/database/migrations/mysql/1694432361423-AddAnalytic.ts b/packages/server/src/database/migrations/mysql/1694432361423-AddAnalytic.ts new file mode 100644 index 00000000..a5e088fa --- /dev/null +++ b/packages/server/src/database/migrations/mysql/1694432361423-AddAnalytic.ts @@ -0,0 +1,11 @@ +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class AddAnalytic1694432361423 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE \`chat_flow\` ADD COLUMN \`analytic\` TEXT;`) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE \`chat_flow\` DROP COLUMN \`analytic\`;`) + } +} diff --git a/packages/server/src/database/migrations/mysql/index.ts b/packages/server/src/database/migrations/mysql/index.ts index e5c16773..ea26789e 100644 --- a/packages/server/src/database/migrations/mysql/index.ts +++ b/packages/server/src/database/migrations/mysql/index.ts @@ -4,6 +4,7 @@ import { ModifyChatMessage1693999022236 } from './1693999022236-ModifyChatMessag import { ModifyCredential1693999261583 } from './1693999261583-ModifyCredential' import { ModifyTool1694001465232 } from './1694001465232-ModifyTool' import { AddApiConfig1694099200729 } from './1694099200729-AddApiConfig' +import { AddAnalytic1694432361423 } from './1694432361423-AddAnalytic' export const mysqlMigrations = [ Init1693840429259, @@ -11,5 +12,6 @@ export const mysqlMigrations = [ ModifyChatMessage1693999022236, ModifyCredential1693999261583, ModifyTool1694001465232, - AddApiConfig1694099200729 + AddApiConfig1694099200729, + AddAnalytic1694432361423 ] diff --git a/packages/server/src/database/migrations/postgres/1694432361423-AddAnalytic.ts b/packages/server/src/database/migrations/postgres/1694432361423-AddAnalytic.ts new file mode 100644 index 00000000..ed83c833 --- /dev/null +++ b/packages/server/src/database/migrations/postgres/1694432361423-AddAnalytic.ts @@ -0,0 +1,11 @@ +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class AddAnalytic1694432361423 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "chat_flow" ADD COLUMN "analytic" TEXT;`) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "chat_flow" DROP COLUMN "analytic";`) + } +} diff --git a/packages/server/src/database/migrations/postgres/index.ts b/packages/server/src/database/migrations/postgres/index.ts index 04579cd6..4a78556b 100644 --- a/packages/server/src/database/migrations/postgres/index.ts +++ b/packages/server/src/database/migrations/postgres/index.ts @@ -4,6 +4,7 @@ import { ModifyChatMessage1693996694528 } from './1693996694528-ModifyChatMessag import { ModifyCredential1693997070000 } from './1693997070000-ModifyCredential' import { ModifyTool1693997339912 } from './1693997339912-ModifyTool' import { AddApiConfig1694099183389 } from './1694099183389-AddApiConfig' +import { AddAnalytic1694432361423 } from './1694432361423-AddAnalytic' export const postgresMigrations = [ Init1693891895163, @@ -11,5 +12,6 @@ export const postgresMigrations = [ ModifyChatMessage1693996694528, ModifyCredential1693997070000, ModifyTool1693997339912, - AddApiConfig1694099183389 + AddApiConfig1694099183389, + AddAnalytic1694432361423 ] diff --git a/packages/server/src/database/migrations/sqlite/1694432361423-AddAnalytic.ts b/packages/server/src/database/migrations/sqlite/1694432361423-AddAnalytic.ts new file mode 100644 index 00000000..ed83c833 --- /dev/null +++ b/packages/server/src/database/migrations/sqlite/1694432361423-AddAnalytic.ts @@ -0,0 +1,11 @@ +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class AddAnalytic1694432361423 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "chat_flow" ADD COLUMN "analytic" TEXT;`) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "chat_flow" DROP COLUMN "analytic";`) + } +} diff --git a/packages/server/src/database/migrations/sqlite/index.ts b/packages/server/src/database/migrations/sqlite/index.ts index 234dd220..bff926b0 100644 --- a/packages/server/src/database/migrations/sqlite/index.ts +++ b/packages/server/src/database/migrations/sqlite/index.ts @@ -4,6 +4,7 @@ import { ModifyChatMessage1693921865247 } from './1693921865247-ModifyChatMessag import { ModifyCredential1693923551694 } from './1693923551694-ModifyCredential' import { ModifyTool1693924207475 } from './1693924207475-ModifyTool' import { AddApiConfig1694090982460 } from './1694090982460-AddApiConfig' +import { AddAnalytic1694432361423 } from './1694432361423-AddAnalytic' export const sqliteMigrations = [ Init1693835579790, @@ -11,5 +12,6 @@ export const sqliteMigrations = [ ModifyChatMessage1693921865247, ModifyCredential1693923551694, ModifyTool1693924207475, - AddApiConfig1694090982460 + AddApiConfig1694090982460, + AddAnalytic1694432361423 ] diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 8d83cb8a..d74600ac 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -980,13 +980,15 @@ export class App { socketIOClientId: incomingInput.socketIOClientId, logger, appDataSource: this.AppDataSource, - databaseEntities + databaseEntities, + analytic: chatflow.analytic }) : await nodeInstance.run(nodeToExecuteData, incomingInput.question, { chatHistory: incomingInput.history, logger, appDataSource: this.AppDataSource, - databaseEntities + databaseEntities, + analytic: chatflow.analytic }) logger.debug(`[server]: Finished running ${nodeToExecuteData.label} (${nodeToExecuteData.id})`) diff --git a/packages/server/src/utils/index.ts b/packages/server/src/utils/index.ts index 73c748a3..b1f7e5a2 100644 --- a/packages/server/src/utils/index.ts +++ b/packages/server/src/utils/index.ts @@ -448,9 +448,10 @@ export const replaceInputsWithConfig = (flowNodeData: INodeData, overrideConfig: // If overrideConfig[key] is object if (overrideConfig[config] && typeof overrideConfig[config] === 'object') { const nodeIds = Object.keys(overrideConfig[config]) - if (!nodeIds.includes(flowNodeData.id)) continue - else paramsObj[config] = overrideConfig[config][flowNodeData.id] - continue + if (nodeIds.includes(flowNodeData.id)) { + paramsObj[config] = overrideConfig[config][flowNodeData.id] + continue + } } let paramValue = overrideConfig[config] ?? paramsObj[config] @@ -877,12 +878,14 @@ export const decryptCredentialData = async ( * @returns {Credential} */ export const transformToCredentialEntity = async (body: ICredentialReqBody): Promise => { - const encryptedData = await encryptCredentialData(body.plainDataObj) - - const credentialBody = { + const credentialBody: ICommonObject = { name: body.name, - credentialName: body.credentialName, - encryptedData + credentialName: body.credentialName + } + + if (body.plainDataObj) { + const encryptedData = await encryptCredentialData(body.plainDataObj) + credentialBody.encryptedData = encryptedData } const newCredential = new Credential() diff --git a/packages/ui/src/assets/images/langchain.png b/packages/ui/src/assets/images/langchain.png new file mode 100644 index 0000000000000000000000000000000000000000..587dd314071048b3b8d892e0ba7f409c12da2712 GIT binary patch literal 58913 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 @Bok*SLeD@x@yGivkx9M1+2R)2) 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°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 S_sACEb*U(5$k)o6h5Qczr~XI) literal 0 HcmV?d00001 diff --git a/packages/ui/src/assets/images/langfuse.png b/packages/ui/src/assets/images/langfuse.png new file mode 100644 index 0000000000000000000000000000000000000000..df9181b8dfad5286ad1cb6d2cf8f5a0631a5ef3a GIT binary patch literal 15485 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 { + const portalElement = document.getElementById('portal') + const dispatch = useDispatch() + + useNotifier() + + const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args)) + const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args)) + + const [analytic, setAnalytic] = useState({}) + const [providerExpanded, setProviderExpanded] = useState({}) + + const onSave = async () => { + try { + const saveResp = await chatflowsApi.updateChatflow(dialogProps.chatflow.id, { + analytic: JSON.stringify(analytic) + }) + if (saveResp.data) { + enqueueSnackbar({ + message: 'Analytic Configuration Saved', + options: { + key: new Date().getTime() + Math.random(), + variant: 'success', + action: (key) => ( + + ) + } + }) + 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 Analytic Configuration: ${errorData}`, + options: { + key: new Date().getTime() + Math.random(), + variant: 'error', + persist: true, + action: (key) => ( + + ) + } + }) + } + } + + const setValue = (value, providerName, inputParamName) => { + let newVal = {} + if (!Object.prototype.hasOwnProperty.call(analytic, providerName)) { + newVal = { ...analytic, [providerName]: {} } + } else { + newVal = { ...analytic } + } + + newVal[providerName][inputParamName] = value + setAnalytic(newVal) + } + + const handleAccordionChange = (providerName) => (event, isExpanded) => { + const accordianProviders = { ...providerExpanded } + accordianProviders[providerName] = isExpanded + setProviderExpanded(accordianProviders) + } + + useEffect(() => { + if (dialogProps.chatflow && dialogProps.chatflow.analytic) { + try { + setAnalytic(JSON.parse(dialogProps.chatflow.analytic)) + } catch (e) { + setAnalytic({}) + console.error(e) + } + } + + return () => { + setAnalytic({}) + setProviderExpanded({}) + } + }, [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 = show ? ( + + + Analyse Chatflow + + + {analyticProviders.map((provider, index) => ( + + } aria-controls={provider.name} id={provider.name}> + + +
+ AI +
+
+ + {provider.url} + + } + /> + {analytic[provider.name] && analytic[provider.name].status && ( +
+
+ ON +
+ )} + + + + {provider.inputs.map((inputParam, index) => ( + +
+ + {inputParam.label} + {!inputParam.optional &&  *} + {inputParam.description && ( + + )} + +
+ {providerExpanded[provider.name] && inputParam.type === 'credential' && ( + setValue(newValue, provider.name, 'credentialId')} + /> + )} + {providerExpanded[provider.name] && inputParam.type === 'boolean' && ( + setValue(newValue, provider.name, inputParam.name)} + value={ + analytic[provider.name] + ? analytic[provider.name][inputParam.name] + : inputParam.default ?? false + } + /> + )} + {providerExpanded[provider.name] && + (inputParam.type === 'string' || + inputParam.type === 'password' || + inputParam.type === 'number') && ( + setValue(newValue, provider.name, inputParam.name)} + value={ + analytic[provider.name] + ? analytic[provider.name][inputParam.name] + : inputParam.default ?? '' + } + /> + )} +
+ ))} +
+ + ))} + + + + Save + + +
+ ) : null + + return createPortal(component, portalElement) +} + +AnalyseFlowDialog.propTypes = { + show: PropTypes.bool, + dialogProps: PropTypes.object, + onCancel: PropTypes.func +} + +export default AnalyseFlowDialog diff --git a/packages/ui/src/ui-component/switch/Switch.js b/packages/ui/src/ui-component/switch/Switch.js index 04ea1704..16a923f1 100644 --- a/packages/ui/src/ui-component/switch/Switch.js +++ b/packages/ui/src/ui-component/switch/Switch.js @@ -22,7 +22,7 @@ export const SwitchInput = ({ value, onChange, disabled = false }) => { } SwitchInput.propTypes = { - value: PropTypes.string, + value: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), onChange: PropTypes.func, disabled: PropTypes.bool } diff --git a/packages/ui/src/views/canvas/CanvasHeader.js b/packages/ui/src/views/canvas/CanvasHeader.js index d2563532..a9d2f39e 100644 --- a/packages/ui/src/views/canvas/CanvasHeader.js +++ b/packages/ui/src/views/canvas/CanvasHeader.js @@ -14,6 +14,7 @@ import { IconSettings, IconChevronLeft, IconDeviceFloppy, IconPencil, IconCheck, import Settings from 'views/settings' import SaveChatflowDialog from 'ui-component/dialog/SaveChatflowDialog' import APICodeDialog from 'views/chatflows/APICodeDialog' +import AnalyseFlowDialog from 'ui-component/dialog/AnalyseFlowDialog' // API import chatflowsApi from 'api/chatflows' @@ -41,6 +42,8 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl const [flowDialogOpen, setFlowDialogOpen] = useState(false) const [apiDialogOpen, setAPIDialogOpen] = useState(false) const [apiDialogProps, setAPIDialogProps] = useState({}) + const [analyseDialogOpen, setAnalyseDialogOpen] = useState(false) + const [analyseDialogProps, setAnalyseDialogProps] = useState({}) const updateChatflowApi = useApi(chatflowsApi.updateChatflow) const canvas = useSelector((state) => state.canvas) @@ -50,6 +53,12 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl if (setting === 'deleteChatflow') { handleDeleteFlow() + } else if (setting === 'analyseChatflow') { + setAnalyseDialogProps({ + title: 'Analyse Chatflow', + chatflow: chatflow + }) + setAnalyseDialogOpen(true) } else if (setting === 'duplicateChatflow') { try { localStorage.setItem('duplicatedFlowData', chatflow.flowData) @@ -357,6 +366,7 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl onConfirm={onConfirmSaveName} /> setAPIDialogOpen(false)} /> + setAnalyseDialogOpen(false)} /> ) } diff --git a/packages/ui/src/views/credentials/AddEditCredentialDialog.js b/packages/ui/src/views/credentials/AddEditCredentialDialog.js index 65b72a5f..b865a7ff 100644 --- a/packages/ui/src/views/credentials/AddEditCredentialDialog.js +++ b/packages/ui/src/views/credentials/AddEditCredentialDialog.js @@ -26,7 +26,7 @@ import useApi from 'hooks/useApi' import useNotifier from 'utils/useNotifier' // const -import { baseURL } from 'store/constant' +import { baseURL, REDACTED_CREDENTIAL_VALUE } from 'store/constant' import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions' const AddEditCredentialDialog = ({ show, dialogProps, onCancel, onConfirm }) => { @@ -118,7 +118,7 @@ const AddEditCredentialDialog = ({ show, dialogProps, onCancel, onConfirm }) => onConfirm(createResp.data.id) } } catch (error) { - const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}` + const errorData = typeof err === 'string' ? err : err.response.data || `${err.response.status}: ${err.response.statusText}` enqueueSnackbar({ message: `Failed to add new Credential: ${errorData}`, options: { @@ -138,11 +138,20 @@ const AddEditCredentialDialog = ({ show, dialogProps, onCancel, onConfirm }) => const saveCredential = async () => { try { - const saveResp = await credentialsApi.updateCredential(credential.id, { + const saveObj = { name, - credentialName: componentCredential.name, - plainDataObj: credentialData - }) + credentialName: componentCredential.name + } + + let plainDataObj = {} + for (const key in credentialData) { + if (credentialData[key] !== REDACTED_CREDENTIAL_VALUE) { + plainDataObj[key] = credentialData[key] + } + } + if (Object.keys(plainDataObj).length) saveObj.plainDataObj = plainDataObj + + const saveResp = await credentialsApi.updateCredential(credential.id, saveObj) if (saveResp.data) { enqueueSnackbar({ message: 'Credential saved', From 0bf298e890089e0ba277fbb1edca548aa7b3de15 Mon Sep 17 00:00:00 2001 From: Anush008 <46051506+Anush008@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:20:29 +0530 Subject: [PATCH 2/6] feat: filter qdrant existing --- .../Qdrant_Existing/Qdrant_Existing.ts | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/packages/components/nodes/vectorstores/Qdrant_Existing/Qdrant_Existing.ts b/packages/components/nodes/vectorstores/Qdrant_Existing/Qdrant_Existing.ts index 16f83b08..58ef6df5 100644 --- a/packages/components/nodes/vectorstores/Qdrant_Existing/Qdrant_Existing.ts +++ b/packages/components/nodes/vectorstores/Qdrant_Existing/Qdrant_Existing.ts @@ -3,6 +3,9 @@ import { QdrantClient } from '@qdrant/js-client-rest' import { QdrantVectorStore, QdrantLibArgs } from 'langchain/vectorstores/qdrant' import { Embeddings } from 'langchain/embeddings/base' import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' +import { VectorStoreRetrieverInput } from 'langchain/vectorstores/base' + +type RetrieverConfig = Partial> class Qdrant_Existing_VectorStores implements INode { label: string @@ -53,7 +56,7 @@ class Qdrant_Existing_VectorStores implements INode { }, { label: 'Qdrant Collection Cofiguration', - name: 'qdrantCollectionCofiguration', + name: 'qdrantCollectionConfiguration', type: 'json', optional: true, additionalParams: true @@ -66,6 +69,14 @@ class Qdrant_Existing_VectorStores implements INode { type: 'number', additionalParams: true, optional: true + }, + { + label: 'Qdrant Search Filter', + name: 'qdrantFilter', + description: 'Only return points which satisfy the conditions', + type: 'json', + additionalParams: true, + optional: true } ] this.outputs = [ @@ -85,10 +96,12 @@ class Qdrant_Existing_VectorStores implements INode { async init(nodeData: INodeData, _: string, options: ICommonObject): Promise { const qdrantServerUrl = nodeData.inputs?.qdrantServerUrl as string const collectionName = nodeData.inputs?.qdrantCollection as string - let qdrantCollectionCofiguration = nodeData.inputs?.qdrantCollectionCofiguration + let qdrantCollectionConfiguration = nodeData.inputs?.qdrantCollectionConfiguration const embeddings = nodeData.inputs?.embeddings as Embeddings const output = nodeData.outputs?.output as string const topK = nodeData.inputs?.topK as string + let queryFilter = nodeData.inputs?.queryFilter + const k = topK ? parseFloat(topK) : 4 const credentialData = await getCredentialData(nodeData.credential ?? '', options) @@ -104,16 +117,26 @@ class Qdrant_Existing_VectorStores implements INode { collectionName } - if (qdrantCollectionCofiguration) { - qdrantCollectionCofiguration = - typeof qdrantCollectionCofiguration === 'object' ? qdrantCollectionCofiguration : JSON.parse(qdrantCollectionCofiguration) - dbConfig.collectionConfig = qdrantCollectionCofiguration + const retrieverConfig: RetrieverConfig = { + k + } + + if (qdrantCollectionConfiguration) { + qdrantCollectionConfiguration = + typeof qdrantCollectionConfiguration === 'object' + ? qdrantCollectionConfiguration + : JSON.parse(qdrantCollectionConfiguration) + dbConfig.collectionConfig = qdrantCollectionConfiguration + } + + if (queryFilter) { + retrieverConfig.filter = typeof queryFilter === 'object' ? queryFilter : JSON.parse(queryFilter) } const vectorStore = await QdrantVectorStore.fromExistingCollection(embeddings, dbConfig) if (output === 'retriever') { - const retriever = vectorStore.asRetriever(k) + const retriever = vectorStore.asRetriever(retrieverConfig) return retriever } else if (output === 'vectorStore') { ;(vectorStore as any).k = k From 73d7d4ecbc39db84a348de5fa05733abd0dbaa21 Mon Sep 17 00:00:00 2001 From: Anush008 <46051506+Anush008@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:22:03 +0530 Subject: [PATCH 3/6] feat: filter qdrant upsert --- .../Qdrant_Upsert/Qdrant_Upsert.ts | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/components/nodes/vectorstores/Qdrant_Upsert/Qdrant_Upsert.ts b/packages/components/nodes/vectorstores/Qdrant_Upsert/Qdrant_Upsert.ts index dcc3099d..6f3773ff 100644 --- a/packages/components/nodes/vectorstores/Qdrant_Upsert/Qdrant_Upsert.ts +++ b/packages/components/nodes/vectorstores/Qdrant_Upsert/Qdrant_Upsert.ts @@ -5,6 +5,9 @@ import { Embeddings } from 'langchain/embeddings/base' import { Document } from 'langchain/document' import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' import { flatten } from 'lodash' +import { VectorStoreRetrieverInput } from 'langchain/vectorstores/base' + +type RetrieverConfig = Partial> class QdrantUpsert_VectorStores implements INode { label: string @@ -67,6 +70,14 @@ class QdrantUpsert_VectorStores implements INode { type: 'number', additionalParams: true, optional: true + }, + { + label: 'Qdrant Search Filter', + name: 'qdrantFilter', + description: 'Only return points which satisfy the conditions', + type: 'json', + additionalParams: true, + optional: true } ] this.outputs = [ @@ -91,6 +102,7 @@ class QdrantUpsert_VectorStores implements INode { const output = nodeData.outputs?.output as string const topK = nodeData.inputs?.topK as string const k = topK ? parseFloat(topK) : 4 + let queryFilter = nodeData.inputs?.qdrantFilter const credentialData = await getCredentialData(nodeData.credential ?? '', options) const qdrantApiKey = getCredentialParam('qdrantApiKey', credentialData, nodeData) @@ -111,10 +123,19 @@ class QdrantUpsert_VectorStores implements INode { url: qdrantServerUrl, collectionName } + + const retrieverConfig: RetrieverConfig = { + k + } + + if (queryFilter) { + retrieverConfig.filter = typeof queryFilter === 'object' ? queryFilter : JSON.parse(queryFilter) + } + const vectorStore = await QdrantVectorStore.fromDocuments(finalDocs, embeddings, dbConfig) if (output === 'retriever') { - const retriever = vectorStore.asRetriever(k) + const retriever = vectorStore.asRetriever(retrieverConfig) return retriever } else if (output === 'vectorStore') { ;(vectorStore as any).k = k From b3e4e353095c477b7860b0ac857fc0dca5e88c88 Mon Sep 17 00:00:00 2001 From: chungyau97 Date: Wed, 13 Sep 2023 10:59:05 +0800 Subject: [PATCH 4/6] Add instruction to setup Rate Limit --- packages/server/src/index.ts | 2 +- packages/ui/src/views/chatflows/Configuration.js | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 8d83cb8a..af6701c0 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -142,7 +142,7 @@ export class App { this.app.get('/api/v1/ip', (request, response) => { response.send({ ip: request.ip, - msg: 'See the IP returned in the response. If it matches your IP address (which you can get by going to http://ip.nfriedly.com/ or https://api.ipify.org/), then the number of proxies is correct and the rate limiter should now work correctly. If not, then keep increasing the number until it does.' + msg: 'See the returned IP address in the response. If it matches your current IP address ( which you can get by going to http://ip.nfriedly.com/ or https://api.ipify.org/ ), then the number of proxies is correct and the rate limiter should now work correctly. If not, increase the number of proxies by 1 until the IP address matches your own. Visit https://docs.flowiseai.com/deployment#rate-limit-setup-guide for more information.' }) }) diff --git a/packages/ui/src/views/chatflows/Configuration.js b/packages/ui/src/views/chatflows/Configuration.js index 51826c44..51b8d61c 100644 --- a/packages/ui/src/views/chatflows/Configuration.js +++ b/packages/ui/src/views/chatflows/Configuration.js @@ -16,6 +16,7 @@ import chatflowsApi from 'api/chatflows' // utils import useNotifier from 'utils/useNotifier' +import { TooltipWithParser } from 'ui-component/tooltip/TooltipWithParser' const Configuration = () => { const dispatch = useDispatch() @@ -131,7 +132,13 @@ const Configuration = () => { <> {/*Rate Limit*/} - Rate Limit + Rate Limit{' '} + Rate Limit Setup Guide to set up Rate Limit correctly in your hosting environment.' + } + /> {textField(limitMax, 'limitMax', 'Message Limit per Duration', 'number')} {textField(limitDuration, 'limitDuration', 'Duration in Second', 'number')} From e9ed663d1f2b12cf22aa423299e5067450e32e64 Mon Sep 17 00:00:00 2001 From: chungyau97 Date: Wed, 13 Sep 2023 16:15:57 +0800 Subject: [PATCH 5/6] add analytic into Conversational Agent --- .../nodes/agents/ConversationalAgent/ConversationalAgent.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/components/nodes/agents/ConversationalAgent/ConversationalAgent.ts b/packages/components/nodes/agents/ConversationalAgent/ConversationalAgent.ts index d8d8506c..661ef151 100644 --- a/packages/components/nodes/agents/ConversationalAgent/ConversationalAgent.ts +++ b/packages/components/nodes/agents/ConversationalAgent/ConversationalAgent.ts @@ -5,6 +5,7 @@ import { BaseChatMemory } from 'langchain/memory' import { getBaseClasses, mapChatHistory } from '../../../src/utils' import { BaseLanguageModel } from 'langchain/base_language' import { flatten } from 'lodash' +import { additionalCallbacks } from '../../../src/handler' const DEFAULT_PREFIX = `Assistant is a large language model trained by OpenAI. @@ -91,13 +92,14 @@ class ConversationalAgent_Agents implements INode { const executor = nodeData.instance as AgentExecutor const memory = nodeData.inputs?.memory as BaseChatMemory + const callbacks = await additionalCallbacks(nodeData, options) + if (options && options.chatHistory) { memory.chatHistory = mapChatHistory(options) executor.memory = memory } - const result = await executor.call({ input }) - + const result = await executor.call({ input }, [...callbacks]) return result?.output } } From 76ba977a5ca195ec9925317665207e11e5e2e264 Mon Sep 17 00:00:00 2001 From: Henry Date: Wed, 13 Sep 2023 09:57:32 +0100 Subject: [PATCH 6/6] add callbacks to agents --- .../nodes/agents/MRKLAgentChat/MRKLAgentChat.ts | 10 +++++++--- .../nodes/agents/MRKLAgentLLM/MRKLAgentLLM.ts | 10 +++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/components/nodes/agents/MRKLAgentChat/MRKLAgentChat.ts b/packages/components/nodes/agents/MRKLAgentChat/MRKLAgentChat.ts index 0a9e744c..ed169e62 100644 --- a/packages/components/nodes/agents/MRKLAgentChat/MRKLAgentChat.ts +++ b/packages/components/nodes/agents/MRKLAgentChat/MRKLAgentChat.ts @@ -1,9 +1,10 @@ -import { INode, INodeData, INodeParams } from '../../../src/Interface' +import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface' import { initializeAgentExecutorWithOptions, AgentExecutor } from 'langchain/agents' import { getBaseClasses } from '../../../src/utils' import { Tool } from 'langchain/tools' import { BaseLanguageModel } from 'langchain/base_language' import { flatten } from 'lodash' +import { additionalCallbacks } from '../../../src/handler' class MRKLAgentChat_Agents implements INode { label: string @@ -51,9 +52,12 @@ class MRKLAgentChat_Agents implements INode { return executor } - async run(nodeData: INodeData, input: string): Promise { + async run(nodeData: INodeData, input: string, options: ICommonObject): Promise { const executor = nodeData.instance as AgentExecutor - const result = await executor.call({ input }) + + const callbacks = await additionalCallbacks(nodeData, options) + + const result = await executor.call({ input }, [...callbacks]) return result?.output } diff --git a/packages/components/nodes/agents/MRKLAgentLLM/MRKLAgentLLM.ts b/packages/components/nodes/agents/MRKLAgentLLM/MRKLAgentLLM.ts index d7af586b..74929af8 100644 --- a/packages/components/nodes/agents/MRKLAgentLLM/MRKLAgentLLM.ts +++ b/packages/components/nodes/agents/MRKLAgentLLM/MRKLAgentLLM.ts @@ -1,9 +1,10 @@ -import { INode, INodeData, INodeParams } from '../../../src/Interface' +import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface' import { initializeAgentExecutorWithOptions, AgentExecutor } from 'langchain/agents' import { Tool } from 'langchain/tools' import { getBaseClasses } from '../../../src/utils' import { BaseLanguageModel } from 'langchain/base_language' import { flatten } from 'lodash' +import { additionalCallbacks } from '../../../src/handler' class MRKLAgentLLM_Agents implements INode { label: string @@ -52,9 +53,12 @@ class MRKLAgentLLM_Agents implements INode { return executor } - async run(nodeData: INodeData, input: string): Promise { + async run(nodeData: INodeData, input: string, options: ICommonObject): Promise { const executor = nodeData.instance as AgentExecutor - const result = await executor.call({ input }) + + const callbacks = await additionalCallbacks(nodeData, options) + + const result = await executor.call({ input }, [...callbacks]) return result?.output }