From de4e4a63defa8094a2c843286eccd6f5955ecf09 Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 22 May 2023 13:38:31 +0100 Subject: [PATCH] add conversation chain --- .../ConversationChain/ConversationChain.ts | 97 ++++++ .../nodes/chains/ConversationChain/chain.svg | 6 + .../Simple Conversation Chain.json | 282 ++++++++++++++++++ .../server/marketplaces/Simple LLM Chain.json | 2 +- 4 files changed, 386 insertions(+), 1 deletion(-) create mode 100644 packages/components/nodes/chains/ConversationChain/ConversationChain.ts create mode 100644 packages/components/nodes/chains/ConversationChain/chain.svg create mode 100644 packages/server/marketplaces/Simple Conversation Chain.json diff --git a/packages/components/nodes/chains/ConversationChain/ConversationChain.ts b/packages/components/nodes/chains/ConversationChain/ConversationChain.ts new file mode 100644 index 00000000..19e28752 --- /dev/null +++ b/packages/components/nodes/chains/ConversationChain/ConversationChain.ts @@ -0,0 +1,97 @@ +import { ICommonObject, IMessage, INode, INodeData, INodeParams } from '../../../src/Interface' +import { ConversationChain } from 'langchain/chains' +import { getBaseClasses } from '../../../src/utils' +import { ChatPromptTemplate, HumanMessagePromptTemplate, MessagesPlaceholder, SystemMessagePromptTemplate } from 'langchain/prompts' +import { BufferMemory, ChatMessageHistory } from 'langchain/memory' +import { BaseChatModel } from 'langchain/chat_models/base' +import { AIChatMessage, HumanChatMessage } from 'langchain/schema' + +const 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.` + +class ConversationChain_Chains implements INode { + label: string + name: string + type: string + icon: string + category: string + baseClasses: string[] + description: string + inputs: INodeParams[] + + constructor() { + this.label = 'Conversation Chain' + this.name = 'conversationChain' + this.type = 'ConversationChain' + this.icon = 'chain.svg' + this.category = 'Chains' + this.description = 'Chat models specific conversational chain with memory' + this.baseClasses = [this.type, ...getBaseClasses(ConversationChain)] + this.inputs = [ + { + label: 'Language Model', + name: 'model', + type: 'BaseChatModel' + }, + { + label: 'Memory', + name: 'memory', + type: 'BaseMemory' + }, + { + label: 'System Message', + name: 'systemMessagePrompt', + type: 'string', + rows: 4, + additionalParams: true, + optional: true, + placeholder: 'You are a helpful assistant that write codes' + } + ] + } + + async init(nodeData: INodeData): Promise { + const model = nodeData.inputs?.model as BaseChatModel + const memory = nodeData.inputs?.memory as BufferMemory + const prompt = nodeData.inputs?.systemMessagePrompt as string + + const obj: any = { + llm: model, + memory + } + + const chatPrompt = ChatPromptTemplate.fromPromptMessages([ + SystemMessagePromptTemplate.fromTemplate(prompt ? `${prompt}\n${systemMessage}` : systemMessage), + new MessagesPlaceholder(memory.memoryKey ?? 'chat_history'), + HumanMessagePromptTemplate.fromTemplate('{input}') + ]) + obj.prompt = chatPrompt + + const chain = new ConversationChain(obj) + return chain + } + + async run(nodeData: INodeData, input: string, options: ICommonObject): Promise { + const chain = nodeData.instance as ConversationChain + const memory = nodeData.inputs?.memory as BufferMemory + + if (options && options.chatHistory) { + const chatHistory = [] + const histories: IMessage[] = options.chatHistory + + for (const message of histories) { + if (message.type === 'apiMessage') { + chatHistory.push(new AIChatMessage(message.message)) + } else if (message.type === 'userMessage') { + chatHistory.push(new HumanChatMessage(message.message)) + } + } + memory.chatHistory = new ChatMessageHistory(chatHistory) + chain.memory = memory + } + + const res = await chain.call({ input }) + return res?.response + } +} + +module.exports = { nodeClass: ConversationChain_Chains } diff --git a/packages/components/nodes/chains/ConversationChain/chain.svg b/packages/components/nodes/chains/ConversationChain/chain.svg new file mode 100644 index 00000000..a5b32f90 --- /dev/null +++ b/packages/components/nodes/chains/ConversationChain/chain.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/packages/server/marketplaces/Simple Conversation Chain.json b/packages/server/marketplaces/Simple Conversation Chain.json new file mode 100644 index 00000000..bb1a5fff --- /dev/null +++ b/packages/server/marketplaces/Simple Conversation Chain.json @@ -0,0 +1,282 @@ +{ + "description": "Basic example of Conversation Chain with built-in memory - works exactly like ChatGPT", + "nodes": [ + { + "width": 300, + "height": 524, + "id": "chatOpenAI_0", + "position": { + "x": 750.6529856117049, + "y": -75.72544375812092 + }, + "type": "customNode", + "data": { + "id": "chatOpenAI_0", + "label": "ChatOpenAI", + "name": "chatOpenAI", + "type": "ChatOpenAI", + "baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "BaseLangChain"], + "category": "Chat Models", + "description": "Wrapper around OpenAI large language models that use the Chat endpoint", + "inputParams": [ + { + "label": "OpenAI Api Key", + "name": "openAIApiKey", + "type": "password", + "id": "chatOpenAI_0-input-openAIApiKey-password" + }, + { + "label": "Model Name", + "name": "modelName", + "type": "options", + "options": [ + { + "label": "gpt-4", + "name": "gpt-4" + }, + { + "label": "gpt-4-0314", + "name": "gpt-4-0314" + }, + { + "label": "gpt-4-32k-0314", + "name": "gpt-4-32k-0314" + }, + { + "label": "gpt-3.5-turbo", + "name": "gpt-3.5-turbo" + }, + { + "label": "gpt-3.5-turbo-0301", + "name": "gpt-3.5-turbo-0301" + } + ], + "default": "gpt-3.5-turbo", + "optional": true, + "id": "chatOpenAI_0-input-modelName-options" + }, + { + "label": "Temperature", + "name": "temperature", + "type": "number", + "default": 0.9, + "optional": true, + "id": "chatOpenAI_0-input-temperature-number" + }, + { + "label": "Max Tokens", + "name": "maxTokens", + "type": "number", + "optional": true, + "additionalParams": true, + "id": "chatOpenAI_0-input-maxTokens-number" + }, + { + "label": "Top Probability", + "name": "topP", + "type": "number", + "optional": true, + "additionalParams": true, + "id": "chatOpenAI_0-input-topP-number" + }, + { + "label": "Frequency Penalty", + "name": "frequencyPenalty", + "type": "number", + "optional": true, + "additionalParams": true, + "id": "chatOpenAI_0-input-frequencyPenalty-number" + }, + { + "label": "Presence Penalty", + "name": "presencePenalty", + "type": "number", + "optional": true, + "additionalParams": true, + "id": "chatOpenAI_0-input-presencePenalty-number" + }, + { + "label": "Timeout", + "name": "timeout", + "type": "number", + "optional": true, + "additionalParams": true, + "id": "chatOpenAI_0-input-timeout-number" + } + ], + "inputAnchors": [], + "inputs": { + "modelName": "gpt-3.5-turbo", + "temperature": 0.9, + "maxTokens": "", + "topP": "", + "frequencyPenalty": "", + "presencePenalty": "", + "timeout": "" + }, + "outputAnchors": [ + { + "id": "chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel|BaseLangChain", + "name": "chatOpenAI", + "label": "ChatOpenAI", + "type": "ChatOpenAI | BaseChatModel | BaseLanguageModel | BaseLangChain" + } + ], + "outputs": {}, + "selected": false + }, + "positionAbsolute": { + "x": 750.6529856117049, + "y": -75.72544375812092 + }, + "selected": false, + "dragging": false + }, + { + "width": 300, + "height": 376, + "id": "bufferMemory_0", + "position": { + "x": 753.4300788823234, + "y": 479.5336426526603 + }, + "type": "customNode", + "data": { + "id": "bufferMemory_0", + "label": "Buffer Memory", + "name": "bufferMemory", + "type": "BufferMemory", + "baseClasses": ["BufferMemory", "BaseChatMemory", "BaseMemory"], + "category": "Memory", + "description": "Remembers previous conversational back and forths directly", + "inputParams": [ + { + "label": "Memory Key", + "name": "memoryKey", + "type": "string", + "default": "chat_history", + "id": "bufferMemory_0-input-memoryKey-string" + }, + { + "label": "Input Key", + "name": "inputKey", + "type": "string", + "default": "input", + "id": "bufferMemory_0-input-inputKey-string" + } + ], + "inputAnchors": [], + "inputs": { + "memoryKey": "chat_history", + "inputKey": "input" + }, + "outputAnchors": [ + { + "id": "bufferMemory_0-output-bufferMemory-BufferMemory|BaseChatMemory|BaseMemory", + "name": "bufferMemory", + "label": "BufferMemory", + "type": "BufferMemory | BaseChatMemory | BaseMemory" + } + ], + "outputs": {}, + "selected": false + }, + "selected": false, + "positionAbsolute": { + "x": 753.4300788823234, + "y": 479.5336426526603 + }, + "dragging": false + }, + { + "width": 300, + "height": 332, + "id": "conversationChain_0", + "position": { + "x": 1201.6630991237407, + "y": 291.86981791303066 + }, + "type": "customNode", + "data": { + "id": "conversationChain_0", + "label": "Conversation Chain", + "name": "conversationChain", + "type": "ConversationChain", + "baseClasses": ["ConversationChain", "LLMChain", "BaseChain", "BaseLangChain"], + "category": "Chains", + "description": "Chat models specific conversational chain with memory", + "inputParams": [ + { + "label": "System Message", + "name": "systemMessagePrompt", + "type": "string", + "rows": 4, + "additionalParams": true, + "optional": true, + "placeholder": "You are a helpful assistant that write codes", + "id": "conversationChain_0-input-systemMessagePrompt-string" + } + ], + "inputAnchors": [ + { + "label": "Language Model", + "name": "model", + "type": "BaseChatModel", + "id": "conversationChain_0-input-model-BaseChatModel" + }, + { + "label": "Memory", + "name": "memory", + "type": "BaseMemory", + "id": "conversationChain_0-input-memory-BaseMemory" + } + ], + "inputs": { + "model": "{{chatOpenAI_0.data.instance}}", + "memory": "{{bufferMemory_0.data.instance}}", + "systemMessagePrompt": "" + }, + "outputAnchors": [ + { + "id": "conversationChain_0-output-conversationChain-ConversationChain|LLMChain|BaseChain|BaseLangChain", + "name": "conversationChain", + "label": "ConversationChain", + "type": "ConversationChain | LLMChain | BaseChain | BaseLangChain" + } + ], + "outputs": {}, + "selected": false + }, + "selected": false, + "positionAbsolute": { + "x": 1201.6630991237407, + "y": 291.86981791303066 + }, + "dragging": false + } + ], + "edges": [ + { + "source": "chatOpenAI_0", + "sourceHandle": "chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel|BaseLangChain", + "target": "conversationChain_0", + "targetHandle": "conversationChain_0-input-model-BaseChatModel", + "type": "buttonedge", + "id": "chatOpenAI_0-chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel|BaseLangChain-conversationChain_0-conversationChain_0-input-model-BaseChatModel", + "data": { + "label": "" + } + }, + { + "source": "bufferMemory_0", + "sourceHandle": "bufferMemory_0-output-bufferMemory-BufferMemory|BaseChatMemory|BaseMemory", + "target": "conversationChain_0", + "targetHandle": "conversationChain_0-input-memory-BaseMemory", + "type": "buttonedge", + "id": "bufferMemory_0-bufferMemory_0-output-bufferMemory-BufferMemory|BaseChatMemory|BaseMemory-conversationChain_0-conversationChain_0-input-memory-BaseMemory", + "data": { + "label": "" + } + } + ] +} diff --git a/packages/server/marketplaces/Simple LLM Chain.json b/packages/server/marketplaces/Simple LLM Chain.json index 3bd5ac2e..26a3a2ee 100644 --- a/packages/server/marketplaces/Simple LLM Chain.json +++ b/packages/server/marketplaces/Simple LLM Chain.json @@ -1,5 +1,5 @@ { - "description": "Basic example of LLM Chain with a Prompt Template and LLM Model", + "description": "Basic example of stateless (no memory) LLM Chain with a Prompt Template and LLM Model", "nodes": [ { "width": 300,