From 7b20c1e93d9817fee8d9fa8c6dd991607368cd5d Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Tue, 12 Nov 2024 06:58:46 -0500 Subject: [PATCH] fix(editor): Ignore all node run messages after a success message is sent (no-changelog) (#11668) --- cypress/e2e/45-ai-assistant.cy.ts | 48 +++++++++++++++++++ .../node_execution_succeeded_response.json | 22 +++++++++ .../editor-ui/src/stores/assistant.store.ts | 12 ++++- 3 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 cypress/fixtures/aiAssistant/responses/node_execution_succeeded_response.json diff --git a/cypress/e2e/45-ai-assistant.cy.ts b/cypress/e2e/45-ai-assistant.cy.ts index 9c69269b33..9b50fef44a 100644 --- a/cypress/e2e/45-ai-assistant.cy.ts +++ b/cypress/e2e/45-ai-assistant.cy.ts @@ -224,6 +224,54 @@ describe('AI Assistant::enabled', () => { .should('contain.text', 'item.json.myNewField = 1'); }); + it('Should ignore node execution success and error messages after the node run successfully once', () => { + const getParameter = () => ndv.getters.parameterInput('jsCode').should('be.visible'); + + const getEditor = () => getParameter().find('.cm-content').should('exist'); + + cy.intercept('POST', '/rest/ai/chat', { + statusCode: 200, + fixture: 'aiAssistant/responses/code_diff_suggestion_response.json', + }).as('chatRequest'); + + cy.createFixtureWorkflow('aiAssistant/workflows/test_workflow.json'); + wf.actions.openNode('Code'); + ndv.getters.nodeExecuteButton().click(); + aiAssistant.getters.nodeErrorViewAssistantButton().click({ force: true }); + cy.wait('@chatRequest'); + + cy.intercept('POST', '/rest/ai/chat', { + statusCode: 200, + fixture: 'aiAssistant/responses/node_execution_succeeded_response.json', + }).as('chatRequest2'); + + getEditor() + .type('{selectall}') + .paste( + 'for (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();', + ); + + ndv.getters.nodeExecuteButton().click(); + + getEditor() + .type('{selectall}') + .paste( + 'for (const item of $input.all()) {\n item.json.myNewField = 1aaaa!;\n}\n\nreturn $input.all();', + ); + + ndv.getters.nodeExecuteButton().click(); + + aiAssistant.getters.chatMessagesAssistant().should('have.length', 3); + + aiAssistant.getters + .chatMessagesAssistant() + .eq(2) + .should( + 'contain.text', + 'Code node ran successfully, did my solution help resolve your issue?\nQuick reply 👇Yes, thanksNo, I am still stuck', + ); + }); + it('should end chat session when `end_session` event is received', () => { cy.intercept('POST', '/rest/ai/chat', { statusCode: 200, diff --git a/cypress/fixtures/aiAssistant/responses/node_execution_succeeded_response.json b/cypress/fixtures/aiAssistant/responses/node_execution_succeeded_response.json new file mode 100644 index 0000000000..62caf2a6b5 --- /dev/null +++ b/cypress/fixtures/aiAssistant/responses/node_execution_succeeded_response.json @@ -0,0 +1,22 @@ +{ + "sessionId": "1", + "messages": [ + { + "role": "assistant", + "type": "message", + "text": "**Code** node ran successfully, did my solution help resolve your issue?", + "quickReplies": [ + { + "text": "Yes, thanks", + "type": "all-good", + "isFeedback": true + }, + { + "text": "No, I am still stuck", + "type": "still-stuck", + "isFeedback": true + } + ] + } + ] +} diff --git a/packages/editor-ui/src/stores/assistant.store.ts b/packages/editor-ui/src/stores/assistant.store.ts index 02178c0784..079db2b407 100644 --- a/packages/editor-ui/src/stores/assistant.store.ts +++ b/packages/editor-ui/src/stores/assistant.store.ts @@ -72,13 +72,15 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => { }; }>({}); + type NodeExecutionStatus = 'error' | 'not_executed' | 'success'; + const chatSessionCredType = ref(); const chatSessionError = ref(); const currentSessionId = ref(); const currentSessionActiveExecutionId = ref(); const currentSessionWorkflowId = ref(); const lastUnread = ref(); - const nodeExecutionStatus = ref<'not_executed' | 'success' | 'error'>('not_executed'); + const nodeExecutionStatus = ref('not_executed'); // This is used to show a message when the assistant is performing intermediate steps // We use streaming for assistants that support it, and this for agents const assistantThinkingMessage = ref(); @@ -536,10 +538,16 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => { (e) => handleServiceError(e, id, async () => await sendEvent(eventName, error)), ); } + async function onNodeExecution(pushEvent: PushPayload<'nodeExecuteAfter'>) { if (!chatSessionError.value || pushEvent.nodeName !== chatSessionError.value.node.name) { return; } + + if (nodeExecutionStatus.value === 'success') { + return; + } + if (pushEvent.data.error && nodeExecutionStatus.value !== 'error') { await sendEvent('node-execution-errored', pushEvent.data.error); nodeExecutionStatus.value = 'error'; @@ -550,7 +558,7 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => { }); } else if ( pushEvent.data.executionStatus === 'success' && - nodeExecutionStatus.value !== 'success' + ['error', 'not_executed'].includes(nodeExecutionStatus.value) ) { await sendEvent('node-execution-succeeded'); nodeExecutionStatus.value = 'success';