fix(editor): Memory getting rendered in chat on workflow load (#14346)

This commit is contained in:
Suguru Inoue
2025-04-04 11:07:35 +02:00
committed by GitHub
parent 73748e300e
commit 5382531970
3 changed files with 22 additions and 82 deletions

View File

@@ -315,12 +315,15 @@ describe('CanvasChat', () => {
];
beforeEach(() => {
vi.spyOn(useChatMessaging, 'useChatMessaging').mockReturnValue({
getChatMessages: vi.fn().mockReturnValue(mockMessages),
sendMessage: vi.fn(),
extractResponseMessage: vi.fn(),
previousMessageIndex: ref(0),
isLoading: computed(() => false),
vi.spyOn(useChatMessaging, 'useChatMessaging').mockImplementation(({ messages }) => {
messages.value.push(...mockMessages);
return {
sendMessage: vi.fn(),
extractResponseMessage: vi.fn(),
previousMessageIndex: ref(0),
isLoading: computed(() => false),
};
});
});
@@ -381,7 +384,6 @@ describe('CanvasChat', () => {
describe('file handling', () => {
beforeEach(() => {
vi.spyOn(useChatMessaging, 'useChatMessaging').mockReturnValue({
getChatMessages: vi.fn().mockReturnValue([]),
sendMessage: vi.fn(),
extractResponseMessage: vi.fn(),
previousMessageIndex: ref(0),
@@ -478,12 +480,15 @@ describe('CanvasChat', () => {
createdAt: new Date().toISOString(),
},
];
vi.spyOn(useChatMessaging, 'useChatMessaging').mockReturnValue({
getChatMessages: vi.fn().mockReturnValue(mockMessages),
sendMessage: sendMessageSpy,
extractResponseMessage: vi.fn(),
previousMessageIndex: ref(0),
isLoading: computed(() => false),
vi.spyOn(useChatMessaging, 'useChatMessaging').mockImplementation(({ messages }) => {
messages.value.push(...mockMessages);
return {
sendMessage: sendMessageSpy,
extractResponseMessage: vi.fn(),
previousMessageIndex: ref(0),
isLoading: computed(() => false),
};
});
workflowsStore.messages = mockMessages;
});
@@ -584,22 +589,4 @@ describe('CanvasChat', () => {
expect(input).toHaveValue('Line 1\nLine 2');
});
});
describe('chat synchronization', () => {
it('should load initial chat history when first opening panel', async () => {
const getChatMessagesSpy = vi.fn().mockReturnValue(['Previous message']);
vi.spyOn(useChatMessaging, 'useChatMessaging').mockReturnValue({
...vi.fn()(),
getChatMessages: getChatMessagesSpy,
});
workflowsStore.chatPanelState = LOGS_PANEL_STATE.CLOSED;
const { rerender } = renderComponent();
workflowsStore.chatPanelState = LOGS_PANEL_STATE.ATTACHED;
await rerender({});
expect(getChatMessagesSpy).toHaveBeenCalled();
});
});
});

View File

@@ -1,8 +1,8 @@
import type { ComputedRef, Ref } from 'vue';
import { computed, ref } from 'vue';
import { v4 as uuid } from 'uuid';
import type { ChatMessage, ChatMessageText } from '@n8n/chat/types';
import { NodeConnectionTypes, CHAT_TRIGGER_NODE_TYPE } from 'n8n-workflow';
import type { ChatMessage } from '@n8n/chat/types';
import { CHAT_TRIGGER_NODE_TYPE } from 'n8n-workflow';
import type {
ITaskData,
INodeExecutionData,
@@ -10,16 +10,14 @@ import type {
IDataObject,
IBinaryData,
BinaryFileType,
Workflow,
IRunExecutionData,
} from 'n8n-workflow';
import { useToast } from '@/composables/useToast';
import { useMessage } from '@/composables/useMessage';
import { usePinnedData } from '@/composables/usePinnedData';
import { get, isEmpty, last } from 'lodash-es';
import { get, isEmpty } from 'lodash-es';
import { MANUAL_CHAT_TRIGGER_NODE_TYPE, MODAL_CONFIRM } from '@/constants';
import { useI18n } from '@/composables/useI18n';
import type { MemoryOutput } from '../types/chat';
import type { IExecutionPushResponse, INodeUi } from '@/Interface';
export type RunWorkflowChatPayload = {
@@ -30,12 +28,9 @@ export type RunWorkflowChatPayload = {
};
export interface ChatMessagingDependencies {
chatTrigger: Ref<INodeUi | null>;
connectedNode: Ref<INodeUi | null>;
messages: Ref<ChatMessage[]>;
sessionId: Ref<string>;
workflow: ComputedRef<Workflow>;
executionResultData: ComputedRef<IRunExecutionData['resultData'] | undefined>;
getWorkflowResultDataByNodeName: (nodeName: string) => ITaskData[] | null;
onRunChatWorkflow: (
payload: RunWorkflowChatPayload,
) => Promise<IExecutionPushResponse | undefined>;
@@ -43,12 +38,9 @@ export interface ChatMessagingDependencies {
export function useChatMessaging({
chatTrigger,
connectedNode,
messages,
sessionId,
workflow,
executionResultData,
getWorkflowResultDataByNodeName,
onRunChatWorkflow,
}: ChatMessagingDependencies) {
const locale = useI18n();
@@ -247,42 +239,10 @@ export function useChatMessaging({
await startWorkflowWithMessage(newMessage.text, files);
}
function getChatMessages(): ChatMessageText[] {
if (!connectedNode.value) return [];
const connectedMemoryInputs =
workflow.value.connectionsByDestinationNode?.[connectedNode.value.name]?.[
NodeConnectionTypes.AiMemory
];
if (!connectedMemoryInputs) return [];
const memoryConnection = (connectedMemoryInputs ?? []).find((i) => (i ?? []).length > 0)?.[0];
if (!memoryConnection) return [];
const nodeResultData = getWorkflowResultDataByNodeName(memoryConnection.node);
const memoryOutputData = (nodeResultData ?? [])
.map(
(data) => get(data, ['data', NodeConnectionTypes.AiMemory, 0, 0, 'json']) as MemoryOutput,
)
.find((data) => data && data.action === 'saveContext');
return (memoryOutputData?.chatHistory ?? []).map((message, index) => {
return {
createdAt: new Date().toISOString(),
text: message.kwargs.content,
id: `preload__${index}`,
sender: last(message.id) === 'HumanMessage' ? 'user' : 'bot',
};
});
}
return {
previousMessageIndex,
isLoading: computed(() => isLoading.value),
sendMessage,
extractResponseMessage,
getChatMessages,
};
}

View File

@@ -60,14 +60,11 @@ export function useChatState(isDisabled: Ref<boolean>, onWindowResize: () => voi
getNodeType: nodeTypesStore.getNodeType,
});
const { sendMessage, getChatMessages, isLoading } = useChatMessaging({
const { sendMessage, isLoading } = useChatMessaging({
chatTrigger: chatTriggerNode,
connectedNode,
messages,
sessionId: currentSessionId,
workflow,
executionResultData: computed(() => workflowsStore.getWorkflowExecution?.data?.resultData),
getWorkflowResultDataByNodeName: workflowsStore.getWorkflowResultDataByNodeName,
onRunChatWorkflow,
});
@@ -134,10 +131,6 @@ export function useChatState(isDisabled: Ref<boolean>, onWindowResize: () => voi
setChatTriggerNode();
setConnectedNode();
if (messages.value.length === 0) {
messages.value = getChatMessages();
}
setTimeout(() => {
onWindowResize();
chatEventBus.emit('focusInput');