mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 21:00:58 +03:00
feat: Add options to filter conversation history messages used in sequential LLM and Agent nodes (#3653)
* feat: Add option to disable conversation history - Add new `disableConversationHistory` boolean parameter in LLMNodes.ts and Agent.ts to optionally skip including conversation history in prompts - Fix potential error in Agent.ts when messages array is empty by adding null safety checks - Improve memory efficiency by allowing stateless interactions when history isn't needed * feat: add conversation history filtering options Replace the disable conversation history feature with a more flexible filtering system that allows selecting: - User question only - Last message only - All messages (default) - No messages This provides more granular control over conversation context management. * chore: break lines * chore: removed ending semi columns * chore: fix eslint errors * fix(sequentialagents): improve conversation history filtering logic - Remove unnecessary state.messages check for user_question case - Add proper null handling for last_message and all_messages cases - Remove @ts-ignore comments with proper typing * Update LLMNode.ts * Update Agent.ts --------- Co-authored-by: Henry Heng <henryheng@flowiseai.com>
This commit is contained in:
@@ -19,7 +19,8 @@ import {
|
|||||||
IDatabaseEntity,
|
IDatabaseEntity,
|
||||||
IUsedTool,
|
IUsedTool,
|
||||||
IDocument,
|
IDocument,
|
||||||
IStateWithMessages
|
IStateWithMessages,
|
||||||
|
ConversationHistorySelection
|
||||||
} from '../../../src/Interface'
|
} from '../../../src/Interface'
|
||||||
import { ToolCallingAgentOutputParser, AgentExecutor, SOURCE_DOCUMENTS_PREFIX, ARTIFACTS_PREFIX } from '../../../src/agents'
|
import { ToolCallingAgentOutputParser, AgentExecutor, SOURCE_DOCUMENTS_PREFIX, ARTIFACTS_PREFIX } from '../../../src/agents'
|
||||||
import { getInputVariables, getVars, handleEscapeCharacters, prepareSandboxVars, removeInvalidImageMarkdown } from '../../../src/utils'
|
import { getInputVariables, getVars, handleEscapeCharacters, prepareSandboxVars, removeInvalidImageMarkdown } from '../../../src/utils'
|
||||||
@@ -28,6 +29,7 @@ import {
|
|||||||
getVM,
|
getVM,
|
||||||
processImageMessage,
|
processImageMessage,
|
||||||
transformObjectPropertyToFunction,
|
transformObjectPropertyToFunction,
|
||||||
|
filterConversationHistory,
|
||||||
restructureMessages,
|
restructureMessages,
|
||||||
MessagesState,
|
MessagesState,
|
||||||
RunnableCallable,
|
RunnableCallable,
|
||||||
@@ -195,7 +197,7 @@ class Agent_SeqAgents implements INode {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.label = 'Agent'
|
this.label = 'Agent'
|
||||||
this.name = 'seqAgent'
|
this.name = 'seqAgent'
|
||||||
this.version = 3.1
|
this.version = 4.0
|
||||||
this.type = 'Agent'
|
this.type = 'Agent'
|
||||||
this.icon = 'seqAgent.png'
|
this.icon = 'seqAgent.png'
|
||||||
this.category = 'Sequential Agents'
|
this.category = 'Sequential Agents'
|
||||||
@@ -217,6 +219,53 @@ class Agent_SeqAgents implements INode {
|
|||||||
optional: true,
|
optional: true,
|
||||||
default: examplePrompt
|
default: examplePrompt
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Prepend Messages History',
|
||||||
|
name: 'messageHistory',
|
||||||
|
description:
|
||||||
|
'Prepend 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: 'Conversation History',
|
||||||
|
name: 'conversationHistorySelection',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'User Question',
|
||||||
|
name: 'user_question',
|
||||||
|
description: 'Use the user question from the historical conversation messages as input.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Last Conversation Message',
|
||||||
|
name: 'last_message',
|
||||||
|
description: 'Use the last conversation message from the historical conversation messages as input.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'All Conversation Messages',
|
||||||
|
name: 'all_messages',
|
||||||
|
description: 'Use all conversation messages from the historical conversation messages as input.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Empty',
|
||||||
|
name: 'empty',
|
||||||
|
description:
|
||||||
|
'Do not use any messages from the conversation history. ' +
|
||||||
|
'Ensure to use either System Prompt, Human Prompt, or Messages History.'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
default: 'all_messages',
|
||||||
|
optional: true,
|
||||||
|
description:
|
||||||
|
'Select which messages from the conversation history to include in the prompt. ' +
|
||||||
|
'The selected messages will be inserted between the System Prompt (if defined) and ' +
|
||||||
|
'[Messages History, Human Prompt].',
|
||||||
|
additionalParams: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Human Prompt',
|
label: 'Human Prompt',
|
||||||
name: 'humanMessagePrompt',
|
name: 'humanMessagePrompt',
|
||||||
@@ -226,17 +275,6 @@ class Agent_SeqAgents implements INode {
|
|||||||
optional: true,
|
optional: true,
|
||||||
additionalParams: 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',
|
label: 'Tools',
|
||||||
name: 'tools',
|
name: 'tools',
|
||||||
@@ -727,6 +765,9 @@ async function agentNode(
|
|||||||
throw new Error('Aborted!')
|
throw new Error('Aborted!')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const historySelection = (nodeData.inputs?.conversationHistorySelection || 'all_messages') as ConversationHistorySelection
|
||||||
|
// @ts-ignore
|
||||||
|
state.messages = filterConversationHistory(historySelection, input, state)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
state.messages = restructureMessages(llm, state)
|
state.messages = restructureMessages(llm, state)
|
||||||
|
|
||||||
@@ -734,10 +775,10 @@ async function agentNode(
|
|||||||
|
|
||||||
if (interrupt) {
|
if (interrupt) {
|
||||||
const messages = state.messages as unknown as BaseMessage[]
|
const messages = state.messages as unknown as BaseMessage[]
|
||||||
const lastMessage = messages[messages.length - 1]
|
const lastMessage = messages.length ? messages[messages.length - 1] : null
|
||||||
|
|
||||||
// If the last message is a tool message and is an interrupted message, format output into standard agent output
|
// If the last message is a tool message and is an interrupted message, format output into standard agent output
|
||||||
if (lastMessage._getType() === 'tool' && lastMessage.additional_kwargs?.nodeId === nodeData.id) {
|
if (lastMessage && lastMessage._getType() === 'tool' && lastMessage.additional_kwargs?.nodeId === nodeData.id) {
|
||||||
let formattedAgentResult: {
|
let formattedAgentResult: {
|
||||||
output?: string
|
output?: string
|
||||||
usedTools?: IUsedTool[]
|
usedTools?: IUsedTool[]
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ import {
|
|||||||
MessageContentImageUrl,
|
MessageContentImageUrl,
|
||||||
INodeOutputsValue,
|
INodeOutputsValue,
|
||||||
ISeqAgentNode,
|
ISeqAgentNode,
|
||||||
IDatabaseEntity
|
IDatabaseEntity,
|
||||||
|
ConversationHistorySelection
|
||||||
} from '../../../src/Interface'
|
} from '../../../src/Interface'
|
||||||
import { AgentExecutor } from '../../../src/agents'
|
import { AgentExecutor } from '../../../src/agents'
|
||||||
import { getInputVariables, getVars, handleEscapeCharacters, prepareSandboxVars } from '../../../src/utils'
|
import { getInputVariables, getVars, handleEscapeCharacters, prepareSandboxVars } from '../../../src/utils'
|
||||||
@@ -25,6 +26,7 @@ import {
|
|||||||
getVM,
|
getVM,
|
||||||
processImageMessage,
|
processImageMessage,
|
||||||
transformObjectPropertyToFunction,
|
transformObjectPropertyToFunction,
|
||||||
|
filterConversationHistory,
|
||||||
restructureMessages,
|
restructureMessages,
|
||||||
checkMessageHistory
|
checkMessageHistory
|
||||||
} from '../commonUtils'
|
} from '../commonUtils'
|
||||||
@@ -173,7 +175,7 @@ class LLMNode_SeqAgents implements INode {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.label = 'LLM Node'
|
this.label = 'LLM Node'
|
||||||
this.name = 'seqLLMNode'
|
this.name = 'seqLLMNode'
|
||||||
this.version = 3.0
|
this.version = 4.0
|
||||||
this.type = 'LLMNode'
|
this.type = 'LLMNode'
|
||||||
this.icon = 'llmNode.svg'
|
this.icon = 'llmNode.svg'
|
||||||
this.category = 'Sequential Agents'
|
this.category = 'Sequential Agents'
|
||||||
@@ -195,6 +197,53 @@ class LLMNode_SeqAgents implements INode {
|
|||||||
optional: true,
|
optional: true,
|
||||||
additionalParams: true
|
additionalParams: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Prepend Messages History',
|
||||||
|
name: 'messageHistory',
|
||||||
|
description:
|
||||||
|
'Prepend 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: 'Conversation History',
|
||||||
|
name: 'conversationHistorySelection',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'User Question',
|
||||||
|
name: 'user_question',
|
||||||
|
description: 'Use the user question from the historical conversation messages as input.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Last Conversation Message',
|
||||||
|
name: 'last_message',
|
||||||
|
description: 'Use the last conversation message from the historical conversation messages as input.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'All Conversation Messages',
|
||||||
|
name: 'all_messages',
|
||||||
|
description: 'Use all conversation messages from the historical conversation messages as input.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Empty',
|
||||||
|
name: 'empty',
|
||||||
|
description:
|
||||||
|
'Do not use any messages from the conversation history. ' +
|
||||||
|
'Ensure to use either System Prompt, Human Prompt, or Messages History.'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
default: 'all_messages',
|
||||||
|
optional: true,
|
||||||
|
description:
|
||||||
|
'Select which messages from the conversation history to include in the prompt. ' +
|
||||||
|
'The selected messages will be inserted between the System Prompt (if defined) and ' +
|
||||||
|
'[Messages History, Human Prompt].',
|
||||||
|
additionalParams: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Human Prompt',
|
label: 'Human Prompt',
|
||||||
name: 'humanMessagePrompt',
|
name: 'humanMessagePrompt',
|
||||||
@@ -204,17 +253,6 @@ class LLMNode_SeqAgents implements INode {
|
|||||||
optional: true,
|
optional: true,
|
||||||
additionalParams: 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',
|
label: 'Start | Agent | Condition | LLM | Tool Node',
|
||||||
name: 'sequentialNode',
|
name: 'sequentialNode',
|
||||||
@@ -534,6 +572,9 @@ async function agentNode(
|
|||||||
throw new Error('Aborted!')
|
throw new Error('Aborted!')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const historySelection = (nodeData.inputs?.conversationHistorySelection || 'all_messages') as ConversationHistorySelection
|
||||||
|
// @ts-ignore
|
||||||
|
state.messages = filterConversationHistory(historySelection, input, state)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
state.messages = restructureMessages(llm, state)
|
state.messages = restructureMessages(llm, state)
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,14 @@ import { Runnable, RunnableConfig, mergeConfigs } from '@langchain/core/runnable
|
|||||||
import { AIMessage, BaseMessage, HumanMessage, MessageContentImageUrl, ToolMessage } from '@langchain/core/messages'
|
import { AIMessage, BaseMessage, HumanMessage, MessageContentImageUrl, ToolMessage } from '@langchain/core/messages'
|
||||||
import { BaseChatModel } from '@langchain/core/language_models/chat_models'
|
import { BaseChatModel } from '@langchain/core/language_models/chat_models'
|
||||||
import { addImagesToMessages, llmSupportsVision } from '../../src/multiModalUtils'
|
import { addImagesToMessages, llmSupportsVision } from '../../src/multiModalUtils'
|
||||||
import { ICommonObject, IDatabaseEntity, INodeData, ISeqAgentsState, IVisionChatModal } from '../../src/Interface'
|
import {
|
||||||
|
ICommonObject,
|
||||||
|
IDatabaseEntity,
|
||||||
|
INodeData,
|
||||||
|
ISeqAgentsState,
|
||||||
|
IVisionChatModal,
|
||||||
|
ConversationHistorySelection
|
||||||
|
} from '../../src/Interface'
|
||||||
import { availableDependencies, defaultAllowBuiltInDep, getVars, prepareSandboxVars } from '../../src/utils'
|
import { availableDependencies, defaultAllowBuiltInDep, getVars, prepareSandboxVars } from '../../src/utils'
|
||||||
import { ChatPromptTemplate, BaseMessagePromptTemplateLike } from '@langchain/core/prompts'
|
import { ChatPromptTemplate, BaseMessagePromptTemplateLike } from '@langchain/core/prompts'
|
||||||
|
|
||||||
@@ -208,6 +215,34 @@ export const convertStructuredSchemaToZod = (schema: string | object): ICommonOb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the conversation history based on the selected option.
|
||||||
|
*
|
||||||
|
* @param historySelection - The selected history option.
|
||||||
|
* @param input - The user input.
|
||||||
|
* @param state - The current state of the sequential llm or agent node.
|
||||||
|
*/
|
||||||
|
export function filterConversationHistory(
|
||||||
|
historySelection: ConversationHistorySelection,
|
||||||
|
input: string,
|
||||||
|
state: ISeqAgentsState
|
||||||
|
): BaseMessage[] {
|
||||||
|
switch (historySelection) {
|
||||||
|
case 'user_question':
|
||||||
|
return [new HumanMessage(input)]
|
||||||
|
case 'last_message':
|
||||||
|
// @ts-ignore
|
||||||
|
return state.messages?.length ? [state.messages[state.messages.length - 1] as BaseMessage] : []
|
||||||
|
case 'empty':
|
||||||
|
return []
|
||||||
|
case 'all_messages':
|
||||||
|
// @ts-ignore
|
||||||
|
return (state.messages as BaseMessage[]) ?? []
|
||||||
|
default:
|
||||||
|
throw new Error(`Unhandled conversationHistorySelection: ${historySelection}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const restructureMessages = (llm: BaseChatModel, state: ISeqAgentsState) => {
|
export const restructureMessages = (llm: BaseChatModel, state: ISeqAgentsState) => {
|
||||||
const messages: BaseMessage[] = []
|
const messages: BaseMessage[] = []
|
||||||
for (const message of state.messages as unknown as BaseMessage[]) {
|
for (const message of state.messages as unknown as BaseMessage[]) {
|
||||||
|
|||||||
@@ -183,6 +183,7 @@ export interface IMultiAgentNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SeqAgentType = 'agent' | 'condition' | 'end' | 'start' | 'tool' | 'state' | 'llm'
|
type SeqAgentType = 'agent' | 'condition' | 'end' | 'start' | 'tool' | 'state' | 'llm'
|
||||||
|
export type ConversationHistorySelection = 'user_question' | 'last_message' | 'all_messages' | 'empty'
|
||||||
|
|
||||||
export interface ISeqAgentNode {
|
export interface ISeqAgentNode {
|
||||||
id: string
|
id: string
|
||||||
|
|||||||
Reference in New Issue
Block a user