Feature/Add message history to agents (#3031)

add message history to agents
This commit is contained in:
Henry Heng
2024-08-17 19:28:01 +01:00
committed by GitHub
parent 36c6c6425c
commit 0a36aa7ef4
6 changed files with 256 additions and 16 deletions
@@ -29,7 +29,8 @@ import {
transformObjectPropertyToFunction,
restructureMessages,
MessagesState,
RunnableCallable
RunnableCallable,
checkMessageHistory
} from '../commonUtils'
import { END, StateGraph } from '@langchain/langgraph'
import { StructuredTool } from '@langchain/core/tools'
@@ -149,6 +150,31 @@ const defaultFunc = `const result = $flow.output;
return {
aggregate: [result.content]
};`
const messageHistoryExample = `const { AIMessage, HumanMessage, ToolMessage } = require('@langchain/core/messages');
return [
new HumanMessage("What is 333382 🦜 1932?"),
new AIMessage({
content: "",
tool_calls: [
{
id: "12345",
name: "calulator",
args: {
number1: 333382,
number2: 1932,
operation: "divide",
},
},
],
}),
new ToolMessage({
tool_call_id: "12345",
content: "The answer is 172.558.",
}),
new AIMessage("The answer is 172.558."),
]`
const TAB_IDENTIFIER = 'selectedUpdateStateMemoryTab'
class Agent_SeqAgents implements INode {
@@ -168,7 +194,7 @@ class Agent_SeqAgents implements INode {
constructor() {
this.label = 'Agent'
this.name = 'seqAgent'
this.version = 2.0
this.version = 3.0
this.type = 'Agent'
this.icon = 'seqAgent.png'
this.category = 'Sequential Agents'
@@ -199,6 +225,17 @@ class Agent_SeqAgents implements INode {
optional: true,
additionalParams: true
},
{
label: 'Messages History',
name: 'messageHistory',
description:
'Return a list of messages between System Prompt and Human Prompt. This is useful when you want to provide few shot examples',
type: 'code',
hideCodeExecute: true,
codeExample: messageHistoryExample,
optional: true,
additionalParams: true
},
{
label: 'Tools',
name: 'tools',
@@ -426,6 +463,8 @@ class Agent_SeqAgents implements INode {
llm,
interrupt,
agent: await createAgent(
nodeData,
options,
agentName,
state,
llm,
@@ -515,6 +554,8 @@ class Agent_SeqAgents implements INode {
}
async function createAgent(
nodeData: INodeData,
options: ICommonObject,
agentName: string,
state: ISeqAgentsState,
llm: BaseChatModel,
@@ -535,7 +576,8 @@ async function createAgent(
if (systemPrompt) promptArrays.unshift(['system', systemPrompt])
if (humanPrompt) promptArrays.push(['human', humanPrompt])
const prompt = ChatPromptTemplate.fromMessages(promptArrays)
let prompt = ChatPromptTemplate.fromMessages(promptArrays)
prompt = await checkMessageHistory(nodeData, options, prompt, promptArrays, systemPrompt)
if (multiModalMessageContent.length) {
const msg = HumanMessagePromptTemplate.fromTemplate([...multiModalMessageContent])
@@ -597,7 +639,9 @@ async function createAgent(
if (systemPrompt) promptArrays.unshift(['system', systemPrompt])
if (humanPrompt) promptArrays.push(['human', humanPrompt])
const prompt = ChatPromptTemplate.fromMessages(promptArrays)
let prompt = ChatPromptTemplate.fromMessages(promptArrays)
prompt = await checkMessageHistory(nodeData, options, prompt, promptArrays, systemPrompt)
if (multiModalMessageContent.length) {
const msg = HumanMessagePromptTemplate.fromTemplate([...multiModalMessageContent])
prompt.promptMessages.splice(1, 0, msg)
@@ -624,7 +668,8 @@ async function createAgent(
if (systemPrompt) promptArrays.unshift(['system', systemPrompt])
if (humanPrompt) promptArrays.push(['human', humanPrompt])
const prompt = ChatPromptTemplate.fromMessages(promptArrays)
let prompt = ChatPromptTemplate.fromMessages(promptArrays)
prompt = await checkMessageHistory(nodeData, options, prompt, promptArrays, systemPrompt)
if (multiModalMessageContent.length) {
const msg = HumanMessagePromptTemplate.fromTemplate([...multiModalMessageContent])
@@ -25,7 +25,8 @@ import {
getVM,
processImageMessage,
transformObjectPropertyToFunction,
restructureMessages
restructureMessages,
checkMessageHistory
} from '../commonUtils'
import { ChatGoogleGenerativeAI } from '../../chatmodels/ChatGoogleGenerativeAI/FlowiseChatGoogleGenerativeAI'
@@ -130,6 +131,31 @@ return {
aggregate: [result.content]
};`
const messageHistoryExample = `const { AIMessage, HumanMessage, ToolMessage } = require('@langchain/core/messages');
return [
new HumanMessage("What is 333382 🦜 1932?"),
new AIMessage({
content: "",
tool_calls: [
{
id: "12345",
name: "calulator",
args: {
number1: 333382,
number2: 1932,
operation: "divide",
},
},
],
}),
new ToolMessage({
tool_call_id: "12345",
content: "The answer is 172.558.",
}),
new AIMessage("The answer is 172.558."),
]`
class LLMNode_SeqAgents implements INode {
label: string
name: string
@@ -147,7 +173,7 @@ class LLMNode_SeqAgents implements INode {
constructor() {
this.label = 'LLM Node'
this.name = 'seqLLMNode'
this.version = 2.0
this.version = 3.0
this.type = 'LLMNode'
this.icon = 'llmNode.svg'
this.category = 'Sequential Agents'
@@ -178,6 +204,17 @@ class LLMNode_SeqAgents implements INode {
optional: true,
additionalParams: true
},
{
label: 'Messages History',
name: 'messageHistory',
description:
'Return a list of messages between System Prompt and Human Prompt. This is useful when you want to provide few shot examples',
type: 'code',
hideCodeExecute: true,
codeExample: messageHistoryExample,
optional: true,
additionalParams: true
},
{
label: 'Start | Agent | Condition | LLM | Tool Node',
name: 'sequentialNode',
@@ -355,6 +392,8 @@ class LLMNode_SeqAgents implements INode {
state,
llm,
agent: await createAgent(
nodeData,
options,
llmNodeName,
state,
bindModel || llm,
@@ -394,6 +433,8 @@ class LLMNode_SeqAgents implements INode {
}
async function createAgent(
nodeData: INodeData,
options: ICommonObject,
llmNodeName: string,
state: ISeqAgentsState,
llm: BaseChatModel,
@@ -438,7 +479,9 @@ async function createAgent(
if (systemPrompt) promptArrays.unshift(['system', systemPrompt])
if (humanPrompt) promptArrays.push(['human', humanPrompt])
const prompt = ChatPromptTemplate.fromMessages(promptArrays)
let prompt = ChatPromptTemplate.fromMessages(promptArrays)
prompt = await checkMessageHistory(nodeData, options, prompt, promptArrays, systemPrompt)
if (multiModalMessageContent.length) {
const msg = HumanMessagePromptTemplate.fromTemplate([...multiModalMessageContent])
prompt.promptMessages.splice(1, 0, msg)
@@ -11,6 +11,7 @@ import { BaseChatModel } from '@langchain/core/language_models/chat_models'
import { addImagesToMessages, llmSupportsVision } from '../../src/multiModalUtils'
import { ICommonObject, IDatabaseEntity, INodeData, ISeqAgentsState, IVisionChatModal } from '../../src/Interface'
import { availableDependencies, defaultAllowBuiltInDep, getVars, prepareSandboxVars } from '../../src/utils'
import { ChatPromptTemplate, BaseMessagePromptTemplateLike } from '@langchain/core/prompts'
export const checkCondition = (input: string | number | undefined, condition: string, value: string | number = ''): boolean => {
if (!input && condition === 'Is Empty') return true
@@ -344,3 +345,34 @@ export class RunnableCallable<I = unknown, O = unknown> extends Runnable<I, O> {
return returnValue
}
}
export const checkMessageHistory = async (
nodeData: INodeData,
options: ICommonObject,
prompt: ChatPromptTemplate,
promptArrays: BaseMessagePromptTemplateLike[],
sysPrompt: string
) => {
const messageHistory = nodeData.inputs?.messageHistory
if (messageHistory) {
const appDataSource = options.appDataSource as DataSource
const databaseEntities = options.databaseEntities as IDatabaseEntity
const vm = await getVM(appDataSource, databaseEntities, nodeData, {})
try {
const response = await vm.run(`module.exports = async function() {${messageHistory}}()`, __dirname)
if (!Array.isArray(response)) throw new Error('Returned message history must be an array')
if (sysPrompt) {
// insert at index 1
promptArrays.splice(1, 0, ...response)
} else {
promptArrays.unshift(...response)
}
prompt = ChatPromptTemplate.fromMessages(promptArrays)
} catch (e) {
throw new Error(e)
}
}
return prompt
}