From 3fd97e4c7299928a498e359b16f6f21eed9f0878 Mon Sep 17 00:00:00 2001 From: Michael Kret <88898367+michael-radency@users.noreply.github.com> Date: Mon, 1 Apr 2024 17:08:00 +0300 Subject: [PATCH] fix: Prevent chat modal opening on 'Test workflow' click (#9009) Signed-off-by: Oleg Ivaniv Co-authored-by: Oleg Ivaniv --- cypress/composables/workflow.ts | 16 ++++- cypress/e2e/30-langchain.cy.ts | 70 +++++++++++++++++-- .../src/composables/useRunWorkflow.ts | 27 +++++++ packages/editor-ui/src/views/NodeView.vue | 6 ++ 4 files changed, 111 insertions(+), 8 deletions(-) diff --git a/cypress/composables/workflow.ts b/cypress/composables/workflow.ts index 1518805c6b..1aa469b194 100644 --- a/cypress/composables/workflow.ts +++ b/cypress/composables/workflow.ts @@ -48,6 +48,12 @@ export function getNodeByName(name: string) { return cy.getByTestId('canvas-node').filter(`[data-name="${name}"]`).eq(0); } +export function disableNode(name: string) { + const target = getNodeByName(name); + target.rightclick(name ? 'center' : 'topLeft', { force: true }); + cy.getByTestId(`context-menu-item-toggle_activation`).click(); +} + export function getConnectionBySourceAndTarget(source: string, target: string) { return cy .get('.jtk-connector') @@ -110,14 +116,20 @@ export function addSupplementalNodeToParent( ) { getAddInputEndpointByType(parentNodeName, endpointType).click({ force: true }); if (exactMatch) { - getNodeCreatorItems().contains(new RegExp("^" + nodeName + "$", "g")).click(); + getNodeCreatorItems() + .contains(new RegExp('^' + nodeName + '$', 'g')) + .click(); } else { getNodeCreatorItems().contains(nodeName).click(); } getConnectionBySourceAndTarget(parentNodeName, nodeName).should('exist'); } -export function addLanguageModelNodeToParent(nodeName: string, parentNodeName: string, exactMatch = false) { +export function addLanguageModelNodeToParent( + nodeName: string, + parentNodeName: string, + exactMatch = false, +) { addSupplementalNodeToParent(nodeName, 'ai_languageModel', parentNodeName, exactMatch); } diff --git a/cypress/e2e/30-langchain.cy.ts b/cypress/e2e/30-langchain.cy.ts index 6b69d3fb65..9536b3cf60 100644 --- a/cypress/e2e/30-langchain.cy.ts +++ b/cypress/e2e/30-langchain.cy.ts @@ -9,6 +9,7 @@ import { AI_TOOL_CODE_NODE_NAME, AI_TOOL_WIKIPEDIA_NODE_NAME, BASIC_LLM_CHAIN_NODE_NAME, + EDIT_FIELDS_SET_NODE_NAME, } from './../constants'; import { createMockNodeExecutionData, runMockWorkflowExcution } from '../utils'; import { @@ -17,7 +18,10 @@ import { addNodeToCanvas, addOutputParserNodeToParent, addToolNodeToParent, + clickExecuteWorkflowButton, clickManualChatButton, + disableNode, + getExecuteWorkflowButton, navigateToNewWorkflowPage, openNode, } from '../composables/workflow'; @@ -32,6 +36,7 @@ import { closeManualChatModal, getManualChatDialog, getManualChatMessages, + getManualChatModal, getManualChatModalLogs, getManualChatModalLogsEntries, getManualChatModalLogsTree, @@ -43,13 +48,58 @@ describe('Langchain Integration', () => { navigateToNewWorkflowPage(); }); + it('should not open chat modal', () => { + addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME, true); + addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME, true); + + clickGetBackToCanvas(); + + addNodeToCanvas(AGENT_NODE_NAME, true, true); + clickGetBackToCanvas(); + + addLanguageModelNodeToParent( + AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, + AGENT_NODE_NAME, + true, + ); + clickGetBackToCanvas(); + + clickExecuteWorkflowButton(); + getManualChatModal().should('not.exist'); + }); + + it('should disable test workflow button', () => { + addNodeToCanvas('Schedule Trigger', true); + addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME, true); + + clickGetBackToCanvas(); + + addNodeToCanvas(AGENT_NODE_NAME, true, true); + clickGetBackToCanvas(); + + addLanguageModelNodeToParent( + AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, + AGENT_NODE_NAME, + true, + ); + clickGetBackToCanvas(); + + disableNode('Schedule Trigger'); + + getExecuteWorkflowButton().should('be.disabled'); + }); + it('should add nodes to all Agent node input types', () => { addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME, true); addNodeToCanvas(AGENT_NODE_NAME, true, true); toggleParameterCheckboxInputByName('hasOutputParser'); clickGetBackToCanvas(); - addLanguageModelNodeToParent(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, AGENT_NODE_NAME, true); + addLanguageModelNodeToParent( + AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, + AGENT_NODE_NAME, + true, + ); clickGetBackToCanvas(); addMemoryNodeToParent(AI_MEMORY_WINDOW_BUFFER_MEMORY_NODE_NAME, AGENT_NODE_NAME); @@ -85,7 +135,7 @@ describe('Langchain Integration', () => { addLanguageModelNodeToParent( AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, BASIC_LLM_CHAIN_NODE_NAME, - true + true, ); clickCreateNewCredential(); @@ -98,7 +148,7 @@ describe('Langchain Integration', () => { const inputMessage = 'Hello!'; const outputMessage = 'Hi there! How can I assist you today?'; - clickExecuteNode() + clickExecuteNode(); runMockWorkflowExcution({ trigger: () => sendManualChatMessage(inputMessage), runData: [ @@ -121,7 +171,11 @@ describe('Langchain Integration', () => { addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true); addNodeToCanvas(AGENT_NODE_NAME, true); - addLanguageModelNodeToParent(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, AGENT_NODE_NAME, true); + addLanguageModelNodeToParent( + AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, + AGENT_NODE_NAME, + true, + ); clickCreateNewCredential(); setCredentialValues({ @@ -134,7 +188,7 @@ describe('Langchain Integration', () => { const inputMessage = 'Hello!'; const outputMessage = 'Hi there! How can I assist you today?'; - clickExecuteNode() + clickExecuteNode(); runMockWorkflowExcution({ trigger: () => sendManualChatMessage(inputMessage), runData: [ @@ -157,7 +211,11 @@ describe('Langchain Integration', () => { addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true); addNodeToCanvas(AGENT_NODE_NAME, true); - addLanguageModelNodeToParent(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, AGENT_NODE_NAME, true); + addLanguageModelNodeToParent( + AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, + AGENT_NODE_NAME, + true, + ); clickCreateNewCredential(); setCredentialValues({ diff --git a/packages/editor-ui/src/composables/useRunWorkflow.ts b/packages/editor-ui/src/composables/useRunWorkflow.ts index fa2dbd3d0d..6d642b3436 100644 --- a/packages/editor-ui/src/composables/useRunWorkflow.ts +++ b/packages/editor-ui/src/composables/useRunWorkflow.ts @@ -247,6 +247,33 @@ export function useRunWorkflow(options: { router: ReturnType } } } } + + //if no destination node is specified + //and execution is not triggered from chat + //and there are other triggers in the workflow + //disable chat trigger node to avoid modal opening and webhook creation + if ( + !options.destinationNode && + options.source !== 'RunData.ManualChatMessage' && + workflowData.nodes.some((node) => node.type === CHAT_TRIGGER_NODE_TYPE) + ) { + const otherTriggers = workflowData.nodes.filter( + (node) => + node.type !== CHAT_TRIGGER_NODE_TYPE && + node.type.toLowerCase().includes('trigger') && + !node.disabled, + ); + + if (otherTriggers.length) { + const chatTriggerNode = workflowData.nodes.find( + (node) => node.type === CHAT_TRIGGER_NODE_TYPE, + ); + if (chatTriggerNode) { + chatTriggerNode.disabled = true; + } + } + } + const startNodes: StartNodeData[] = startNodeNames.map((name) => { // Find for each start node the source data let sourceData = get(runData, [name, 0, 'source', 0], null); diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index 202389be66..47863f5c19 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -728,6 +728,12 @@ export default defineComponent({ return this.containsChatNodes && this.triggerNodes.length === 1; }, isExecutionDisabled(): boolean { + if ( + this.containsChatNodes && + this.triggerNodes.every((node) => node.disabled || node.type === CHAT_TRIGGER_NODE_TYPE) + ) { + return true; + } return !this.containsTrigger || this.allTriggersDisabled; }, getNodeViewOffsetPosition(): XYPosition {