mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 17:01:00 +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,
|
||||
IUsedTool,
|
||||
IDocument,
|
||||
IStateWithMessages
|
||||
IStateWithMessages,
|
||||
ConversationHistorySelection
|
||||
} from '../../../src/Interface'
|
||||
import { ToolCallingAgentOutputParser, AgentExecutor, SOURCE_DOCUMENTS_PREFIX, ARTIFACTS_PREFIX } from '../../../src/agents'
|
||||
import { getInputVariables, getVars, handleEscapeCharacters, prepareSandboxVars, removeInvalidImageMarkdown } from '../../../src/utils'
|
||||
@@ -28,6 +29,7 @@ import {
|
||||
getVM,
|
||||
processImageMessage,
|
||||
transformObjectPropertyToFunction,
|
||||
filterConversationHistory,
|
||||
restructureMessages,
|
||||
MessagesState,
|
||||
RunnableCallable,
|
||||
@@ -195,7 +197,7 @@ class Agent_SeqAgents implements INode {
|
||||
constructor() {
|
||||
this.label = 'Agent'
|
||||
this.name = 'seqAgent'
|
||||
this.version = 3.1
|
||||
this.version = 4.0
|
||||
this.type = 'Agent'
|
||||
this.icon = 'seqAgent.png'
|
||||
this.category = 'Sequential Agents'
|
||||
@@ -217,6 +219,53 @@ class Agent_SeqAgents implements INode {
|
||||
optional: true,
|
||||
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',
|
||||
name: 'humanMessagePrompt',
|
||||
@@ -226,17 +275,6 @@ 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',
|
||||
@@ -727,6 +765,9 @@ async function agentNode(
|
||||
throw new Error('Aborted!')
|
||||
}
|
||||
|
||||
const historySelection = (nodeData.inputs?.conversationHistorySelection || 'all_messages') as ConversationHistorySelection
|
||||
// @ts-ignore
|
||||
state.messages = filterConversationHistory(historySelection, input, state)
|
||||
// @ts-ignore
|
||||
state.messages = restructureMessages(llm, state)
|
||||
|
||||
@@ -734,10 +775,10 @@ async function agentNode(
|
||||
|
||||
if (interrupt) {
|
||||
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 (lastMessage._getType() === 'tool' && lastMessage.additional_kwargs?.nodeId === nodeData.id) {
|
||||
if (lastMessage && lastMessage._getType() === 'tool' && lastMessage.additional_kwargs?.nodeId === nodeData.id) {
|
||||
let formattedAgentResult: {
|
||||
output?: string
|
||||
usedTools?: IUsedTool[]
|
||||
|
||||
@@ -14,7 +14,8 @@ import {
|
||||
MessageContentImageUrl,
|
||||
INodeOutputsValue,
|
||||
ISeqAgentNode,
|
||||
IDatabaseEntity
|
||||
IDatabaseEntity,
|
||||
ConversationHistorySelection
|
||||
} from '../../../src/Interface'
|
||||
import { AgentExecutor } from '../../../src/agents'
|
||||
import { getInputVariables, getVars, handleEscapeCharacters, prepareSandboxVars } from '../../../src/utils'
|
||||
@@ -25,6 +26,7 @@ import {
|
||||
getVM,
|
||||
processImageMessage,
|
||||
transformObjectPropertyToFunction,
|
||||
filterConversationHistory,
|
||||
restructureMessages,
|
||||
checkMessageHistory
|
||||
} from '../commonUtils'
|
||||
@@ -173,7 +175,7 @@ class LLMNode_SeqAgents implements INode {
|
||||
constructor() {
|
||||
this.label = 'LLM Node'
|
||||
this.name = 'seqLLMNode'
|
||||
this.version = 3.0
|
||||
this.version = 4.0
|
||||
this.type = 'LLMNode'
|
||||
this.icon = 'llmNode.svg'
|
||||
this.category = 'Sequential Agents'
|
||||
@@ -195,6 +197,53 @@ class LLMNode_SeqAgents implements INode {
|
||||
optional: 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',
|
||||
name: 'humanMessagePrompt',
|
||||
@@ -204,17 +253,6 @@ 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',
|
||||
@@ -534,6 +572,9 @@ async function agentNode(
|
||||
throw new Error('Aborted!')
|
||||
}
|
||||
|
||||
const historySelection = (nodeData.inputs?.conversationHistorySelection || 'all_messages') as ConversationHistorySelection
|
||||
// @ts-ignore
|
||||
state.messages = filterConversationHistory(historySelection, input, state)
|
||||
// @ts-ignore
|
||||
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 { BaseChatModel } from '@langchain/core/language_models/chat_models'
|
||||
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 { 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) => {
|
||||
const messages: 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'
|
||||
export type ConversationHistorySelection = 'user_question' | 'last_message' | 'all_messages' | 'empty'
|
||||
|
||||
export interface ISeqAgentNode {
|
||||
id: string
|
||||
|
||||
Reference in New Issue
Block a user