mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
Merge branch 'ADO-2728/feature-change-auto-add-of-chattrigger' into ADO-2729/feature-set-field-default-value-of-added-node-based-on-previous
This commit is contained in:
commit
2f33d6f352
|
@ -359,6 +359,14 @@ describe('Langchain Integration', () => {
|
||||||
getConnectionBySourceAndTarget(CHAT_TRIGGER_NODE_DISPLAY_NAME, AGENT_NODE_NAME).should('exist');
|
getConnectionBySourceAndTarget(CHAT_TRIGGER_NODE_DISPLAY_NAME, AGENT_NODE_NAME).should('exist');
|
||||||
getNodes().should('have.length', 3);
|
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', () => {
|
it('should render runItems for sub-nodes and allow switching between them', () => {
|
||||||
const workflowPage = new WorkflowPage();
|
const workflowPage = new WorkflowPage();
|
||||||
const ndv = new NDV();
|
const ndv = new NDV();
|
||||||
|
|
|
@ -19,7 +19,6 @@ import {
|
||||||
AI_CATEGORY_LANGUAGE_MODELS,
|
AI_CATEGORY_LANGUAGE_MODELS,
|
||||||
BASIC_CHAIN_NODE_TYPE,
|
BASIC_CHAIN_NODE_TYPE,
|
||||||
CHAT_TRIGGER_NODE_TYPE,
|
CHAT_TRIGGER_NODE_TYPE,
|
||||||
MANUAL_CHAT_TRIGGER_NODE_TYPE,
|
|
||||||
MANUAL_TRIGGER_NODE_TYPE,
|
MANUAL_TRIGGER_NODE_TYPE,
|
||||||
NODE_CREATOR_OPEN_SOURCES,
|
NODE_CREATOR_OPEN_SOURCES,
|
||||||
NO_OP_NODE_TYPE,
|
NO_OP_NODE_TYPE,
|
||||||
|
@ -205,8 +204,6 @@ export const useActions = () => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
function shouldPrependChatTrigger(addedNodes: AddedNode[]): boolean {
|
function shouldPrependChatTrigger(addedNodes: AddedNode[]): boolean {
|
||||||
const { allNodes } = useWorkflowsStore();
|
|
||||||
|
|
||||||
const COMPATIBLE_CHAT_NODES = [
|
const COMPATIBLE_CHAT_NODES = [
|
||||||
QA_CHAIN_NODE_TYPE,
|
QA_CHAIN_NODE_TYPE,
|
||||||
AGENT_NODE_TYPE,
|
AGENT_NODE_TYPE,
|
||||||
|
@ -215,13 +212,25 @@ export const useActions = () => {
|
||||||
OPEN_AI_NODE_MESSAGE_ASSISTANT_TYPE,
|
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));
|
const isCompatibleNode = addedNodes.some((node) => COMPATIBLE_CHAT_NODES.includes(node.type));
|
||||||
|
|
||||||
return isCompatibleNode && isChatTriggerMissing;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// AI-226: Prepend LLM Chain node when adding a language model
|
// AI-226: Prepend LLM Chain node when adding a language model
|
||||||
|
|
|
@ -5,6 +5,8 @@ import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
import { useActions } from './composables/useActions';
|
import { useActions } from './composables/useActions';
|
||||||
import {
|
import {
|
||||||
|
AGENT_NODE_TYPE,
|
||||||
|
GITHUB_TRIGGER_NODE_TYPE,
|
||||||
HTTP_REQUEST_NODE_TYPE,
|
HTTP_REQUEST_NODE_TYPE,
|
||||||
MANUAL_TRIGGER_NODE_TYPE,
|
MANUAL_TRIGGER_NODE_TYPE,
|
||||||
NODE_CREATOR_OPEN_SOURCES,
|
NODE_CREATOR_OPEN_SOURCES,
|
||||||
|
@ -15,6 +17,7 @@ import {
|
||||||
TRIGGER_NODE_CREATOR_VIEW,
|
TRIGGER_NODE_CREATOR_VIEW,
|
||||||
WEBHOOK_NODE_TYPE,
|
WEBHOOK_NODE_TYPE,
|
||||||
} from '@/constants';
|
} from '@/constants';
|
||||||
|
import { CHAT_TRIGGER_NODE_TYPE } from 'n8n-workflow';
|
||||||
|
|
||||||
describe('useActions', () => {
|
describe('useActions', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
|
@ -54,6 +57,9 @@ describe('useActions', () => {
|
||||||
vi.spyOn(workflowsStore, 'workflowTriggerNodes', 'get').mockReturnValue([
|
vi.spyOn(workflowsStore, 'workflowTriggerNodes', 'get').mockReturnValue([
|
||||||
{ type: SCHEDULE_TRIGGER_NODE_TYPE } as never,
|
{ type: SCHEDULE_TRIGGER_NODE_TYPE } as never,
|
||||||
]);
|
]);
|
||||||
|
vi.spyOn(workflowsStore, 'getNodeTypes').mockReturnValue({
|
||||||
|
getByNameAndVersion: () => ({ description: { group: ['trigger'] } }),
|
||||||
|
} as never);
|
||||||
vi.spyOn(nodeCreatorStore, 'openSource', 'get').mockReturnValue(
|
vi.spyOn(nodeCreatorStore, 'openSource', 'get').mockReturnValue(
|
||||||
NODE_CREATOR_OPEN_SOURCES.ADD_NODE_BUTTON,
|
NODE_CREATOR_OPEN_SOURCES.ADD_NODE_BUTTON,
|
||||||
);
|
);
|
||||||
|
@ -67,6 +73,100 @@ 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', () => {
|
test('should insert a No Op node when a Loop Over Items Node is added', () => {
|
||||||
const workflowsStore = useWorkflowsStore();
|
const workflowsStore = useWorkflowsStore();
|
||||||
const nodeCreatorStore = useNodeCreatorStore();
|
const nodeCreatorStore = useNodeCreatorStore();
|
||||||
|
|
Loading…
Reference in a new issue