diff --git a/cypress/e2e/30-langchain.cy.ts b/cypress/e2e/30-langchain.cy.ts index b6f1b56eed..f5def3f7f8 100644 --- a/cypress/e2e/30-langchain.cy.ts +++ b/cypress/e2e/30-langchain.cy.ts @@ -518,4 +518,52 @@ describe('Langchain Integration', () => { getRunDataInfoCallout().should('not.exist'); }); + + it('should execute up to Node 1 when using partial execution', () => { + const workflowPage = new WorkflowPage(); + + cy.visit(workflowPage.url); + cy.createFixtureWorkflow('Test_workflow_chat_partial_execution.json'); + workflowPage.actions.zoomToFit(); + + getManualChatModal().should('not.exist'); + workflowPage.actions.executeNode('Node 1'); + + getManualChatModal().should('exist'); + sendManualChatMessage('Test'); + + getManualChatMessages().should('contain', 'this_my_field_1'); + cy.getByTestId('refresh-session-button').click(); + cy.get('button').contains('Reset').click(); + getManualChatMessages().should('not.exist'); + + sendManualChatMessage('Another test'); + getManualChatMessages().should('contain', 'this_my_field_3'); + getManualChatMessages().should('contain', 'this_my_field_4'); + }); + + it('should execute up to Node 1 when using partial execution', () => { + const workflowPage = new WorkflowPage(); + const ndv = new NDV(); + + cy.visit(workflowPage.url); + cy.createFixtureWorkflow('Test_workflow_chat_partial_execution.json'); + workflowPage.actions.zoomToFit(); + + getManualChatModal().should('not.exist'); + openNode('Node 1'); + ndv.actions.execute(); + + getManualChatModal().should('exist'); + sendManualChatMessage('Test'); + + getManualChatMessages().should('contain', 'this_my_field_1'); + cy.getByTestId('refresh-session-button').click(); + cy.get('button').contains('Reset').click(); + getManualChatMessages().should('not.exist'); + + sendManualChatMessage('Another test'); + getManualChatMessages().should('contain', 'this_my_field_3'); + getManualChatMessages().should('contain', 'this_my_field_4'); + }); }); diff --git a/cypress/fixtures/Test_workflow_chat_partial_execution.json b/cypress/fixtures/Test_workflow_chat_partial_execution.json new file mode 100644 index 0000000000..451ddcf964 --- /dev/null +++ b/cypress/fixtures/Test_workflow_chat_partial_execution.json @@ -0,0 +1,77 @@ +{ + "nodes": [ + { + "parameters": { + "options": {} + }, + "id": "535fd3dd-e78f-4ffa-a085-79723fc81b38", + "name": "When chat message received", + "type": "@n8n/n8n-nodes-langchain.chatTrigger", + "typeVersion": 1.1, + "position": [ + 320, + -380 + ], + "webhookId": "4fb58136-3481-494a-a30f-d9e064dac186" + }, + { + "parameters": { + "mode": "raw", + "jsonOutput": "{\n \"this_my_field_1\": \"value\",\n \"this_my_field_2\": 1\n}\n", + "options": {} + }, + "id": "78201ec2-6def-40b7-85e5-97b580d7f642", + "name": "Node 1", + "type": "n8n-nodes-base.set", + "typeVersion": 3.4, + "position": [ + 580, + -380 + ] + }, + { + "parameters": { + "mode": "raw", + "jsonOutput": "{\n \"this_my_field_3\": \"value\",\n \"this_my_field_4\": 1\n}\n", + "options": {} + }, + "id": "1cfca06d-3ec3-427f-89f7-1ef321e025ff", + "name": "Node 2", + "type": "n8n-nodes-base.set", + "typeVersion": 3.4, + "position": [ + 780, + -380 + ] + } + ], + "connections": { + "When chat message received": { + "main": [ + [ + { + "node": "Node 1", + "type": "main", + "index": 0 + } + ] + ] + }, + "Node 1": { + "main": [ + [ + { + "node": "Node 2", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "pinData": {}, + "meta": { + "templateCredsSetupCompleted": true, + "instanceId": "178ef8a5109fc76c716d40bcadb720c455319f7b7a3fd5a39e4f336a091f524a" + } +} diff --git a/packages/editor-ui/src/components/CanvasChat/CanvasChat.vue b/packages/editor-ui/src/components/CanvasChat/CanvasChat.vue index adfbc8cdd4..085edeaf73 100644 --- a/packages/editor-ui/src/components/CanvasChat/CanvasChat.vue +++ b/packages/editor-ui/src/components/CanvasChat/CanvasChat.vue @@ -201,11 +201,18 @@ async function createExecutionPromise() { async function onRunChatWorkflow(payload: RunWorkflowChatPayload) { try { - const response = await runWorkflow({ + const runWorkflowOptions: Parameters[0] = { triggerNode: payload.triggerNode, nodeData: payload.nodeData, source: payload.source, - }); + }; + + if (workflowsStore.chatPartialExecutionDestinationNode) { + runWorkflowOptions.destinationNode = workflowsStore.chatPartialExecutionDestinationNode; + workflowsStore.chatPartialExecutionDestinationNode = null; + } + + const response = await runWorkflow(runWorkflowOptions); if (response) { await createExecutionPromise(); diff --git a/packages/editor-ui/src/components/NodeExecuteButton.vue b/packages/editor-ui/src/components/NodeExecuteButton.vue index b46c6b0a43..8ef10dd1be 100644 --- a/packages/editor-ui/src/components/NodeExecuteButton.vue +++ b/packages/editor-ui/src/components/NodeExecuteButton.vue @@ -319,6 +319,7 @@ async function onClick() { if (isChatNode.value || (isChatChild.value && ndvStore.isInputPanelEmpty)) { ndvStore.setActiveNodeName(null); + workflowsStore.chatPartialExecutionDestinationNode = props.nodeName; nodeViewEventBus.emit('openChat'); } else if (isListeningForEvents.value) { await stopWaitingForWebhook(); diff --git a/packages/editor-ui/src/composables/useRunWorkflow.ts b/packages/editor-ui/src/composables/useRunWorkflow.ts index a5489af869..e765922156 100644 --- a/packages/editor-ui/src/composables/useRunWorkflow.ts +++ b/packages/editor-ui/src/composables/useRunWorkflow.ts @@ -164,6 +164,9 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType { const nodeMetadata = ref({}); const isInDebugMode = ref(false); const chatMessages = ref([]); + const chatPartialExecutionDestinationNode = ref(null); const isChatPanelOpen = ref(false); const isLogsPanelOpen = ref(false); @@ -1634,6 +1635,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => { nodeMetadata, isInDebugMode, chatMessages, + chatPartialExecutionDestinationNode, workflowName, workflowId, workflowVersionId,