diff --git a/cypress/e2e/30-langchain.cy.ts b/cypress/e2e/30-langchain.cy.ts index 78934c3ce5..3d7219b5a3 100644 --- a/cypress/e2e/30-langchain.cy.ts +++ b/cypress/e2e/30-langchain.cy.ts @@ -358,14 +358,6 @@ describe('Langchain Integration', () => { getConnectionBySourceAndTarget(CHAT_TRIGGER_NODE_DISPLAY_NAME, AGENT_NODE_NAME).should('exist'); getNodes().should('have.length', 3); }); - it('should not auto-add nodes if ChatTrigger is already present', () => { - addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true); - addNodeToCanvas(AGENT_NODE_NAME, true); - - addNodeToCanvas(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, true); - getConnectionBySourceAndTarget(CHAT_TRIGGER_NODE_DISPLAY_NAME, AGENT_NODE_NAME).should('exist'); - getNodes().should('have.length', 3); - }); it('should render runItems for sub-nodes and allow switching between them', () => { const workflowPage = new WorkflowPage(); const ndv = new NDV(); diff --git a/packages/editor-ui/src/components/Node/NodeCreator/composables/useActions.ts b/packages/editor-ui/src/components/Node/NodeCreator/composables/useActions.ts index 92a5563d80..87be1105b7 100644 --- a/packages/editor-ui/src/components/Node/NodeCreator/composables/useActions.ts +++ b/packages/editor-ui/src/components/Node/NodeCreator/composables/useActions.ts @@ -19,6 +19,7 @@ import { AI_CATEGORY_LANGUAGE_MODELS, BASIC_CHAIN_NODE_TYPE, CHAT_TRIGGER_NODE_TYPE, + MANUAL_CHAT_TRIGGER_NODE_TYPE, MANUAL_TRIGGER_NODE_TYPE, NODE_CREATOR_OPEN_SOURCES, NO_OP_NODE_TYPE, @@ -203,6 +204,8 @@ export const useActions = () => { ); } function shouldPrependChatTrigger(addedNodes: AddedNode[]): boolean { + const { allNodes } = useWorkflowsStore(); + const COMPATIBLE_CHAT_NODES = [ QA_CHAIN_NODE_TYPE, AGENT_NODE_TYPE, @@ -211,25 +214,13 @@ export const useActions = () => { OPEN_AI_NODE_MESSAGE_ASSISTANT_TYPE, ]; + const isChatTriggerMissing = + allNodes.find((node) => + [MANUAL_CHAT_TRIGGER_NODE_TYPE, CHAT_TRIGGER_NODE_TYPE].includes(node.type), + ) === undefined; const isCompatibleNode = addedNodes.some((node) => COMPATIBLE_CHAT_NODES.includes(node.type)); - if (!isCompatibleNode) return false; - - const { allNodes, getNodeTypes } = useWorkflowsStore(); - const { getByNameAndVersion } = getNodeTypes(); - - // We want to add a trigger if there are no triggers other than Manual Triggers - // Performance here should be fine as `getByNameAndVersion` fetches nodeTypes once in bulk - // and `every` aborts on first `false` - const shouldAddChatTrigger = allNodes.every((node) => { - const nodeType = getByNameAndVersion(node.type, node.typeVersion); - - return ( - !nodeType.description.group.includes('trigger') || node.type === MANUAL_TRIGGER_NODE_TYPE - ); - }); - - return shouldAddChatTrigger; + return isCompatibleNode && isChatTriggerMissing; } // AI-226: Prepend LLM Chain node when adding a language model diff --git a/packages/editor-ui/src/components/Node/NodeCreator/useActions.test.ts b/packages/editor-ui/src/components/Node/NodeCreator/useActions.test.ts index cf8fa94014..4b5682cbbd 100644 --- a/packages/editor-ui/src/components/Node/NodeCreator/useActions.test.ts +++ b/packages/editor-ui/src/components/Node/NodeCreator/useActions.test.ts @@ -5,8 +5,6 @@ import { useNodeTypesStore } from '@/stores/nodeTypes.store'; import { useWorkflowsStore } from '@/stores/workflows.store'; import { useActions } from './composables/useActions'; import { - AGENT_NODE_TYPE, - GITHUB_TRIGGER_NODE_TYPE, HTTP_REQUEST_NODE_TYPE, MANUAL_TRIGGER_NODE_TYPE, NODE_CREATOR_OPEN_SOURCES, @@ -17,7 +15,6 @@ import { TRIGGER_NODE_CREATOR_VIEW, WEBHOOK_NODE_TYPE, } from '@/constants'; -import { CHAT_TRIGGER_NODE_TYPE } from 'n8n-workflow'; describe('useActions', () => { beforeAll(() => { @@ -57,9 +54,6 @@ describe('useActions', () => { vi.spyOn(workflowsStore, 'workflowTriggerNodes', 'get').mockReturnValue([ { type: SCHEDULE_TRIGGER_NODE_TYPE } as never, ]); - vi.spyOn(workflowsStore, 'getNodeTypes').mockReturnValue({ - getByNameAndVersion: () => ({ description: { group: ['trigger'] } }), - } as never); vi.spyOn(nodeCreatorStore, 'openSource', 'get').mockReturnValue( NODE_CREATOR_OPEN_SOURCES.ADD_NODE_BUTTON, ); @@ -73,100 +67,6 @@ describe('useActions', () => { }); }); - test('should insert a ChatTrigger node when an AI Agent is added without trigger', () => { - const workflowsStore = useWorkflowsStore(); - - vi.spyOn(workflowsStore, 'workflowTriggerNodes', 'get').mockReturnValue([]); - - const { getAddedNodesAndConnections } = useActions(); - - expect(getAddedNodesAndConnections([{ type: AGENT_NODE_TYPE }])).toEqual({ - connections: [ - { - from: { - nodeIndex: 0, - }, - to: { - nodeIndex: 1, - }, - }, - ], - nodes: [ - { type: CHAT_TRIGGER_NODE_TYPE, isAutoAdd: true }, - { type: AGENT_NODE_TYPE, openDetail: true }, - ], - }); - }); - - test('should insert a ChatTrigger node when an AI Agent is added with only a Manual Trigger', () => { - const workflowsStore = useWorkflowsStore(); - - vi.spyOn(workflowsStore, 'workflowTriggerNodes', 'get').mockReturnValue([ - { type: MANUAL_TRIGGER_NODE_TYPE } as never, - ]); - vi.spyOn(workflowsStore, 'getNodeTypes').mockReturnValue({ - getByNameAndVersion: () => ({ description: { group: ['trigger'] } }), - } as never); - - const { getAddedNodesAndConnections } = useActions(); - - expect(getAddedNodesAndConnections([{ type: AGENT_NODE_TYPE }])).toEqual({ - connections: [ - { - from: { - nodeIndex: 0, - }, - to: { - nodeIndex: 1, - }, - }, - ], - nodes: [ - { type: CHAT_TRIGGER_NODE_TYPE, isAutoAdd: true }, - { type: AGENT_NODE_TYPE, openDetail: true }, - ], - }); - }); - - test('should not insert a ChatTrigger node when an AI Agent is added with a trigger already present', () => { - const workflowsStore = useWorkflowsStore(); - - vi.spyOn(workflowsStore, 'allNodes', 'get').mockReturnValue([ - { type: GITHUB_TRIGGER_NODE_TYPE } as never, - ]); - vi.spyOn(workflowsStore, 'getNodeTypes').mockReturnValue({ - getByNameAndVersion: () => ({ description: { group: ['trigger'] } }), - } as never); - - const { getAddedNodesAndConnections } = useActions(); - - expect(getAddedNodesAndConnections([{ type: AGENT_NODE_TYPE }])).toEqual({ - connections: [], - nodes: [{ type: AGENT_NODE_TYPE, openDetail: true }], - }); - }); - - test('should not insert a ChatTrigger node when an AI Agent is added with a Chat Trigger already present', () => { - const workflowsStore = useWorkflowsStore(); - - vi.spyOn(workflowsStore, 'workflowTriggerNodes', 'get').mockReturnValue([ - { type: CHAT_TRIGGER_NODE_TYPE } as never, - ]); - vi.spyOn(workflowsStore, 'allNodes', 'get').mockReturnValue([ - { type: CHAT_TRIGGER_NODE_TYPE } as never, - ]); - vi.spyOn(workflowsStore, 'getNodeTypes').mockReturnValue({ - getByNameAndVersion: () => ({ description: { group: ['trigger'] } }), - } as never); - - const { getAddedNodesAndConnections } = useActions(); - - expect(getAddedNodesAndConnections([{ type: AGENT_NODE_TYPE }])).toEqual({ - connections: [], - nodes: [{ type: AGENT_NODE_TYPE, openDetail: true }], - }); - }); - test('should insert a No Op node when a Loop Over Items Node is added', () => { const workflowsStore = useWorkflowsStore(); const nodeCreatorStore = useNodeCreatorStore();