Skip to content

Commit

Permalink
fix(editor): Partial execution of a workflow with manual chat trigger (
Browse files Browse the repository at this point in the history
…#12662)

Co-authored-by: Oleg Ivaniv <me@olegivaniv.com>
  • Loading branch information
burivuhster and OlegIvaniv authored Jan 20, 2025
1 parent 174cd44 commit 2f81b29
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 3 deletions.
48 changes: 48 additions & 0 deletions cypress/e2e/30-langchain.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
});
77 changes: 77 additions & 0 deletions cypress/fixtures/Test_workflow_chat_partial_execution.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
11 changes: 9 additions & 2 deletions packages/editor-ui/src/components/CanvasChat/CanvasChat.vue
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,18 @@ async function createExecutionPromise() {
async function onRunChatWorkflow(payload: RunWorkflowChatPayload) {
try {
const response = await runWorkflow({
const runWorkflowOptions: Parameters<typeof runWorkflow>[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();
Expand Down
1 change: 1 addition & 0 deletions packages/editor-ui/src/components/NodeExecuteButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
7 changes: 6 additions & 1 deletion packages/editor-ui/src/composables/useRunWorkflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType<typeof u
);
newRunData = { [options.triggerNode]: [options.nodeData] };
executedNode = options.triggerNode;
}

if (options.triggerNode && options.nodeData) {
triggerToStartFrom = {
name: options.triggerNode,
data: options.nodeData,
Expand All @@ -174,7 +177,8 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType<typeof u
if (
options.destinationNode &&
(workflowsStore.checkIfNodeHasChatParent(options.destinationNode) ||
destinationNodeType === CHAT_TRIGGER_NODE_TYPE)
destinationNodeType === CHAT_TRIGGER_NODE_TYPE) &&
options.source !== 'RunData.ManualChatMessage'
) {
const startNode = workflow.getStartNode(options.destinationNode);
if (startNode && startNode.type === CHAT_TRIGGER_NODE_TYPE) {
Expand All @@ -186,6 +190,7 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType<typeof u
// If the chat node has no input data or pin data, open the chat modal
// and halt the execution
if (!chatHasInputData && !chatHasPinData) {
workflowsStore.chatPartialExecutionDestinationNode = options.destinationNode;
workflowsStore.setPanelOpen('chat', true);
return;
}
Expand Down
2 changes: 2 additions & 0 deletions packages/editor-ui/src/stores/workflows.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
const nodeMetadata = ref<NodeMetadataMap>({});
const isInDebugMode = ref(false);
const chatMessages = ref<string[]>([]);
const chatPartialExecutionDestinationNode = ref<string | null>(null);
const isChatPanelOpen = ref(false);
const isLogsPanelOpen = ref(false);

Expand Down Expand Up @@ -1634,6 +1635,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
nodeMetadata,
isInDebugMode,
chatMessages,
chatPartialExecutionDestinationNode,
workflowName,
workflowId,
workflowVersionId,
Expand Down

0 comments on commit 2f81b29

Please sign in to comment.