mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
feat(n8n Evaluation Trigger Node): Add Evaluation Trigger and Evaluation Node (#15194)
This commit is contained in:
@@ -76,7 +76,7 @@ describe('NodesListPanel', () => {
|
||||
await fireEvent.click(container.querySelector('.backButton')!);
|
||||
await nextTick();
|
||||
|
||||
expect(screen.queryAllByTestId('item-iterator-item')).toHaveLength(8);
|
||||
expect(screen.queryAllByTestId('item-iterator-item')).toHaveLength(9);
|
||||
});
|
||||
|
||||
it('should render regular nodes', async () => {
|
||||
|
||||
@@ -132,3 +132,63 @@ exports[`viewsData > AIView > should return ai view without ai transform node if
|
||||
"value": "AI",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`viewsData > AIView > should return ai view without ai transform node if ask ai is not enabled and node is not in the list 1`] = `
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"key": "ai_templates_root",
|
||||
"properties": {
|
||||
"description": "See what's possible and get started 5x faster",
|
||||
"icon": "box-open",
|
||||
"name": "ai_templates_root",
|
||||
"tag": {
|
||||
"text": "Recommended",
|
||||
"type": "info",
|
||||
},
|
||||
"title": "AI Templates",
|
||||
"url": "template-repository-url.n8n.io?test=value&utm_user_role=AdvancedAI",
|
||||
},
|
||||
"type": "link",
|
||||
},
|
||||
{
|
||||
"key": "agent",
|
||||
"properties": {
|
||||
"description": "example mock agent node",
|
||||
"displayName": "agent",
|
||||
"group": [],
|
||||
"icon": "fa:pen",
|
||||
"iconUrl": "nodes/test-node/icon.svg",
|
||||
"name": "agent",
|
||||
"title": "agent",
|
||||
},
|
||||
"type": "node",
|
||||
},
|
||||
{
|
||||
"key": "chain",
|
||||
"properties": {
|
||||
"description": "example mock chain node",
|
||||
"displayName": "chain",
|
||||
"group": [],
|
||||
"icon": "fa:pen",
|
||||
"iconUrl": "nodes/test-node/icon.svg",
|
||||
"name": "chain",
|
||||
"title": "chain",
|
||||
},
|
||||
"type": "node",
|
||||
},
|
||||
{
|
||||
"key": "AI Other",
|
||||
"properties": {
|
||||
"description": "Embeddings, Vector Stores, LLMs and other AI nodes",
|
||||
"icon": "robot",
|
||||
"title": "Other AI Nodes",
|
||||
},
|
||||
"type": "view",
|
||||
},
|
||||
],
|
||||
"subtitle": "Select an AI Node to add to your workflow",
|
||||
"title": "AI Nodes",
|
||||
"value": "AI",
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
AI_CATEGORY_TOOLS,
|
||||
AI_SUBCATEGORY,
|
||||
CUSTOM_API_CALL_KEY,
|
||||
EVALUATION_TRIGGER,
|
||||
HTTP_REQUEST_NODE_TYPE,
|
||||
} from '@/constants';
|
||||
import { memoize, startCase } from 'lodash-es';
|
||||
@@ -19,6 +20,7 @@ import { i18n } from '@/plugins/i18n';
|
||||
|
||||
import { getCredentialOnlyNodeType } from '@/utils/credentialOnlyNodes';
|
||||
import { formatTriggerActionName } from '../utils';
|
||||
import { usePostHog } from '@/stores/posthog.store';
|
||||
|
||||
const PLACEHOLDER_RECOMMENDED_ACTION_KEY = 'placeholder_recommended';
|
||||
|
||||
@@ -330,7 +332,23 @@ export function useActionsGenerator() {
|
||||
nodeTypes: INodeTypeDescription[],
|
||||
httpOnlyCredentials: ICredentialType[],
|
||||
) {
|
||||
const visibleNodeTypes = [...nodeTypes];
|
||||
const posthogStore = usePostHog();
|
||||
|
||||
const isEvaluationVariantEnabled = posthogStore.isVariantEnabled(
|
||||
EVALUATION_TRIGGER.name,
|
||||
EVALUATION_TRIGGER.variant,
|
||||
);
|
||||
|
||||
const visibleNodeTypes = nodeTypes.filter((node) => {
|
||||
if (isEvaluationVariantEnabled) {
|
||||
return true;
|
||||
}
|
||||
return (
|
||||
node.name !== 'n8n-nodes-base.evaluation' &&
|
||||
node.name !== 'n8n-nodes-base.evaluationTrigger'
|
||||
);
|
||||
});
|
||||
|
||||
const actions: ActionsRecord<typeof mergedNodes> = {};
|
||||
const mergedNodes: SimplifiedNodeType[] = [];
|
||||
visibleNodeTypes
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { NodeConnectionTypes, type INodeProperties, type INodeTypeDescription } from 'n8n-workflow';
|
||||
import { useActionsGenerator } from './composables/useActionsGeneration';
|
||||
import { usePostHog } from '@/stores/posthog.store';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import { setActivePinia } from 'pinia';
|
||||
|
||||
let posthogStore: ReturnType<typeof usePostHog>;
|
||||
|
||||
describe('useActionsGenerator', () => {
|
||||
const { generateMergedNodesAndActions } = useActionsGenerator();
|
||||
@@ -19,6 +24,17 @@ describe('useActionsGenerator', () => {
|
||||
properties: [],
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
const pinia = createTestingPinia({ stubActions: false });
|
||||
setActivePinia(pinia);
|
||||
|
||||
posthogStore = usePostHog();
|
||||
|
||||
vi.spyOn(posthogStore, 'isVariantEnabled').mockReturnValue(true);
|
||||
});
|
||||
|
||||
describe('App actions for resource category', () => {
|
||||
const resourcePropertyWithUser: INodeProperties = {
|
||||
displayName: 'Resource',
|
||||
@@ -386,5 +402,48 @@ describe('useActionsGenerator', () => {
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should not return evaluation or evaluation trigger node if variant is not enabled', () => {
|
||||
vi.spyOn(posthogStore, 'isVariantEnabled').mockReturnValue(false);
|
||||
|
||||
const node: INodeTypeDescription = {
|
||||
...baseV2NodeWoProps,
|
||||
properties: [
|
||||
resourcePropertyWithUser,
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {},
|
||||
options: [
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get description',
|
||||
},
|
||||
],
|
||||
default: 'get',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const evalNode: INodeTypeDescription = {
|
||||
...baseV2NodeWoProps,
|
||||
name: 'n8n-nodes-base.evaluation',
|
||||
};
|
||||
|
||||
const evalNodeTrigger: INodeTypeDescription = {
|
||||
...baseV2NodeWoProps,
|
||||
name: 'n8n-nodes-base.evaluationTrigger',
|
||||
};
|
||||
|
||||
const { mergedNodes } = generateMergedNodesAndActions([node, evalNode, evalNodeTrigger], []);
|
||||
|
||||
mergedNodes.forEach((mergedNode) => {
|
||||
expect(mergedNode.name).not.toEqual('n8n-nodes-base.evaluation');
|
||||
expect(mergedNode.name).not.toEqual('n8n-nodes-base.evaluationTrigger');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,6 +7,9 @@ import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { AIView } from './viewsData';
|
||||
import { mockNodeTypeDescription } from '@/__tests__/mocks';
|
||||
import { useTemplatesStore } from '@/stores/templates.store';
|
||||
import { usePostHog } from '@/stores/posthog.store';
|
||||
|
||||
let posthogStore: ReturnType<typeof usePostHog>;
|
||||
|
||||
const getNodeType = vi.fn();
|
||||
|
||||
@@ -51,6 +54,9 @@ describe('viewsData', () => {
|
||||
beforeAll(() => {
|
||||
setActivePinia(createTestingPinia());
|
||||
|
||||
posthogStore = usePostHog();
|
||||
vi.spyOn(posthogStore, 'isVariantEnabled').mockReturnValue(true);
|
||||
|
||||
const templatesStore = useTemplatesStore();
|
||||
|
||||
vi.spyOn(templatesStore, 'websiteTemplateRepositoryParameters', 'get').mockImplementation(
|
||||
@@ -86,5 +92,14 @@ describe('viewsData', () => {
|
||||
|
||||
expect(AIView([])).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should return ai view without ai transform node if ask ai is not enabled and node is not in the list', () => {
|
||||
vi.spyOn(posthogStore, 'isVariantEnabled').mockReturnValue(false);
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
vi.spyOn(settingsStore, 'isAskAiEnabled', 'get').mockReturnValue(false);
|
||||
|
||||
expect(AIView([])).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -57,17 +57,19 @@ import {
|
||||
AI_CODE_TOOL_LANGCHAIN_NODE_TYPE,
|
||||
AI_WORKFLOW_TOOL_LANGCHAIN_NODE_TYPE,
|
||||
HUMAN_IN_THE_LOOP_CATEGORY,
|
||||
EVALUATION_TRIGGER,
|
||||
} from '@/constants';
|
||||
import { useI18n } from '@/composables/useI18n';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import type { SimplifiedNodeType } from '@/Interface';
|
||||
import type { INodeTypeDescription, Themed } from 'n8n-workflow';
|
||||
import { NodeConnectionTypes } from 'n8n-workflow';
|
||||
import { EVALUATION_TRIGGER_NODE_TYPE, NodeConnectionTypes } from 'n8n-workflow';
|
||||
import type { NodeConnectionType } from 'n8n-workflow';
|
||||
import { useTemplatesStore } from '@/stores/templates.store';
|
||||
import type { BaseTextKey } from '@/plugins/i18n';
|
||||
import { camelCase } from 'lodash-es';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { usePostHog } from '@/stores/posthog.store';
|
||||
|
||||
export interface NodeViewItemSection {
|
||||
key: string;
|
||||
@@ -141,6 +143,16 @@ export function AIView(_nodes: SimplifiedNodeType[]): NodeView {
|
||||
const i18n = useI18n();
|
||||
const nodeTypesStore = useNodeTypesStore();
|
||||
const templatesStore = useTemplatesStore();
|
||||
const posthogStore = usePostHog();
|
||||
|
||||
const isEvaluationVariantEnabled = posthogStore.isVariantEnabled(
|
||||
EVALUATION_TRIGGER.name,
|
||||
EVALUATION_TRIGGER.variant,
|
||||
);
|
||||
|
||||
const evaluationNodeStore = nodeTypesStore.getNodeType('n8n-nodes-base.evaluation');
|
||||
const evaluationNode =
|
||||
isEvaluationVariantEnabled && evaluationNodeStore ? [getNodeView(evaluationNodeStore)] : [];
|
||||
|
||||
const chainNodes = getAiNodesBySubcategory(nodeTypesStore.allLatestNodeTypes, AI_CATEGORY_CHAINS);
|
||||
const agentNodes = getAiNodesBySubcategory(nodeTypesStore.allLatestNodeTypes, AI_CATEGORY_AGENTS);
|
||||
@@ -177,6 +189,7 @@ export function AIView(_nodes: SimplifiedNodeType[]): NodeView {
|
||||
...agentNodes,
|
||||
...chainNodes,
|
||||
...transformNode,
|
||||
...evaluationNode,
|
||||
{
|
||||
key: AI_OTHERS_NODE_CREATOR_VIEW,
|
||||
type: 'view',
|
||||
@@ -424,6 +437,18 @@ export function TriggerView() {
|
||||
icon: 'fa:comments',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: EVALUATION_TRIGGER_NODE_TYPE,
|
||||
type: 'node',
|
||||
category: [CORE_NODES_CATEGORY],
|
||||
properties: {
|
||||
group: [],
|
||||
name: EVALUATION_TRIGGER_NODE_TYPE,
|
||||
displayName: 'Evaluation Trigger',
|
||||
description: 'Run a dataset through your workflow to test performance',
|
||||
icon: 'fa:check-double',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'subcategory',
|
||||
key: OTHER_TRIGGER_NODES_SUBCATEGORY,
|
||||
|
||||
Reference in New Issue
Block a user