feat(core): Add support for building LLM applications (#7235)
This extracts all core and editor changes from #7246 and #7137, so that we can get these changes merged first. ADO-1120 [DB Tests](https://github.com/n8n-io/n8n/actions/runs/6379749011) [E2E Tests](https://github.com/n8n-io/n8n/actions/runs/6379751480) [Workflow Tests](https://github.com/n8n-io/n8n/actions/runs/6379752828) --------- Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com> Co-authored-by: Oleg Ivaniv <me@olegivaniv.com> Co-authored-by: Alex Grozav <alex@grozav.com> Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
This commit is contained in:
committed by
GitHub
parent
04dfcd73be
commit
00a4b8b0c6
@@ -18,11 +18,7 @@ import type {
|
||||
DragStopEventParams,
|
||||
} from '@jsplumb/browser-ui';
|
||||
import { newInstance } from '@jsplumb/browser-ui';
|
||||
import { N8nPlusEndpointHandler } from '@/plugins/endpoints/N8nPlusEndpointType';
|
||||
import * as N8nPlusEndpointRenderer from '@/plugins/endpoints/N8nPlusEndpointRenderer';
|
||||
import { N8nConnector } from '@/plugins/connectors/N8nCustomConnector';
|
||||
import type { Connection } from '@jsplumb/core';
|
||||
import { EndpointFactory, Connectors } from '@jsplumb/core';
|
||||
import { MoveNodeCommand } from '@/models/history';
|
||||
import {
|
||||
DEFAULT_PLACEHOLDER_TRIGGER_BUTTON,
|
||||
@@ -67,10 +63,6 @@ export const useCanvasStore = defineStore('canvas', () => {
|
||||
}
|
||||
});
|
||||
|
||||
Connectors.register(N8nConnector.type, N8nConnector);
|
||||
N8nPlusEndpointRenderer.register();
|
||||
EndpointFactory.registerHandler(N8nPlusEndpointHandler);
|
||||
|
||||
const setRecenteredCanvasAddButtonPosition = (offset?: XYPosition) => {
|
||||
const position = getMidCanvasPosition(nodeViewScale.value, offset || [0, 0]);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import type {
|
||||
XYPosition,
|
||||
} from '@/Interface';
|
||||
import type { INodeIssues, IRunData } from 'n8n-workflow';
|
||||
import { NodeConnectionType } from 'n8n-workflow';
|
||||
import { defineStore } from 'pinia';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { useWorkflowsStore } from './workflows.store';
|
||||
@@ -124,7 +125,7 @@ export const useNDVStore = defineStore(STORES.NDV, {
|
||||
return false;
|
||||
}
|
||||
const workflow = useWorkflowsStore().getCurrentWorkflow();
|
||||
const parentNodes = workflow.getParentNodes(this.activeNode.name, 'main', 1);
|
||||
const parentNodes = workflow.getParentNodes(this.activeNode.name, NodeConnectionType.Main, 1);
|
||||
return parentNodes.includes(inputNodeName);
|
||||
},
|
||||
hoveringItemNumber(): number {
|
||||
@@ -139,6 +140,9 @@ export const useNDVStore = defineStore(STORES.NDV, {
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setActiveNodeName(nodeName: string | null): void {
|
||||
this.activeNodeName = nodeName;
|
||||
},
|
||||
setInputNodeName(nodeName: string | undefined): void {
|
||||
this.input = {
|
||||
...this.input,
|
||||
|
||||
@@ -7,7 +7,8 @@ import type {
|
||||
ActionsRecord,
|
||||
} from '@/Interface';
|
||||
|
||||
import { ref } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { transformNodeType } from '@/components/Node/NodeCreator/utils';
|
||||
|
||||
export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, () => {
|
||||
const selectedView = ref<NodeFilterType>(TRIGGER_NODE_CREATOR_VIEW);
|
||||
@@ -17,6 +18,10 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, () => {
|
||||
const showScrim = ref(false);
|
||||
const openSource = ref<NodeCreatorOpenSource>('');
|
||||
|
||||
const allNodeCreatorNodes = computed(() =>
|
||||
Object.values(mergedNodes.value).map((i) => transformNodeType(i)),
|
||||
);
|
||||
|
||||
function setMergeNodes(nodes: SimplifiedNodeType[]) {
|
||||
mergedNodes.value = nodes;
|
||||
}
|
||||
@@ -48,5 +53,6 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, () => {
|
||||
setOpenSource,
|
||||
setActions,
|
||||
setMergeNodes,
|
||||
allNodeCreatorNodes,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -16,17 +16,22 @@ import { addHeaders, addNodeTranslation } from '@/plugins/i18n';
|
||||
import { omit } from '@/utils';
|
||||
import type {
|
||||
ILoadOptions,
|
||||
INode,
|
||||
INodeCredentials,
|
||||
INodeListSearchResult,
|
||||
INodeOutputConfiguration,
|
||||
INodeParameters,
|
||||
INodePropertyOptions,
|
||||
INodeTypeDescription,
|
||||
INodeTypeNameVersion,
|
||||
ResourceMapperFields,
|
||||
Workflow,
|
||||
ConnectionTypes,
|
||||
} from 'n8n-workflow';
|
||||
import { defineStore } from 'pinia';
|
||||
import { useCredentialsStore } from './credentials.store';
|
||||
import { useRootStore } from './n8nRoot.store';
|
||||
import { NodeHelpers, NodeConnectionType } from 'n8n-workflow';
|
||||
|
||||
function getNodeVersions(nodeType: INodeTypeDescription) {
|
||||
return Array.isArray(nodeType.version) ? nodeType.version : [nodeType.version];
|
||||
@@ -73,6 +78,34 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
|
||||
return nodeType || null;
|
||||
};
|
||||
},
|
||||
isConfigNode() {
|
||||
return (workflow: Workflow, node: INode, nodeTypeName: string): boolean => {
|
||||
const nodeType = this.getNodeType(nodeTypeName);
|
||||
if (!nodeType) {
|
||||
return false;
|
||||
}
|
||||
const outputs = NodeHelpers.getNodeOutputs(workflow, node, nodeType);
|
||||
const outputTypes = NodeHelpers.getConnectionTypes(outputs);
|
||||
|
||||
return outputTypes
|
||||
? outputTypes.filter((output) => output !== NodeConnectionType.Main).length > 0
|
||||
: false;
|
||||
};
|
||||
},
|
||||
isConfigurableNode() {
|
||||
return (workflow: Workflow, node: INode, nodeTypeName: string): boolean => {
|
||||
const nodeType = this.getNodeType(nodeTypeName);
|
||||
if (nodeType === null) {
|
||||
return false;
|
||||
}
|
||||
const inputs = NodeHelpers.getNodeInputs(workflow, node, nodeType);
|
||||
const inputTypes = NodeHelpers.getConnectionTypes(inputs);
|
||||
|
||||
return inputTypes
|
||||
? inputTypes.filter((input) => input !== NodeConnectionType.Main).length > 0
|
||||
: false;
|
||||
};
|
||||
},
|
||||
isTriggerNode() {
|
||||
return (nodeTypeName: string) => {
|
||||
const nodeType = this.getNodeType(nodeTypeName);
|
||||
@@ -96,6 +129,48 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
|
||||
return acc;
|
||||
}, []);
|
||||
},
|
||||
visibleNodeTypesByOutputConnectionTypeNames(): { [key: string]: string[] } {
|
||||
const nodesByOutputType = this.visibleNodeTypes.reduce(
|
||||
(acc, node) => {
|
||||
const outputTypes = node.outputs;
|
||||
if (Array.isArray(outputTypes)) {
|
||||
outputTypes.forEach((value: ConnectionTypes | INodeOutputConfiguration) => {
|
||||
const outputType = typeof value === 'string' ? value : value.type;
|
||||
if (!acc[outputType]) {
|
||||
acc[outputType] = [];
|
||||
}
|
||||
acc[outputType].push(node.name);
|
||||
});
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as { [key: string]: string[] },
|
||||
);
|
||||
|
||||
return nodesByOutputType;
|
||||
},
|
||||
visibleNodeTypesByInputConnectionTypeNames(): { [key: string]: string[] } {
|
||||
const nodesByOutputType = this.visibleNodeTypes.reduce(
|
||||
(acc, node) => {
|
||||
const inputTypes = node.inputs;
|
||||
if (Array.isArray(inputTypes)) {
|
||||
inputTypes.forEach((value: ConnectionTypes | INodeOutputConfiguration) => {
|
||||
const outputType = typeof value === 'string' ? value : value.type;
|
||||
if (!acc[outputType]) {
|
||||
acc[outputType] = [];
|
||||
}
|
||||
acc[outputType].push(node.name);
|
||||
});
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as { [key: string]: string[] },
|
||||
);
|
||||
|
||||
return nodesByOutputType;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setNodeTypes(newNodeTypes: INodeTypeDescription[] = []): void {
|
||||
|
||||
@@ -57,7 +57,11 @@ export const useSegment = defineStore('segment', () => {
|
||||
const nodeRunData = runData.data.resultData.runData[nodeName];
|
||||
const node = workflowsStore.getNodeByName(nodeName);
|
||||
const nodeTypeName = node ? node.type : 'unknown';
|
||||
if (nodeRunData[0].data && nodeRunData[0].data.main.some((out) => out && out?.length > 1)) {
|
||||
if (
|
||||
nodeRunData[0].data &&
|
||||
nodeRunData[0].data.main &&
|
||||
nodeRunData[0].data.main.some((out) => out && out?.length > 1)
|
||||
) {
|
||||
multipleOutputNodes.add(nodeTypeName);
|
||||
}
|
||||
if (node && !node.disabled) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from '@/api/workflow-webhooks';
|
||||
import {
|
||||
ABOUT_MODAL_KEY,
|
||||
CHAT_EMBED_MODAL_KEY,
|
||||
CHANGE_PASSWORD_MODAL_KEY,
|
||||
COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY,
|
||||
COMMUNITY_PACKAGE_INSTALL_MODAL_KEY,
|
||||
@@ -27,6 +28,7 @@ import {
|
||||
VERSIONS_MODAL_KEY,
|
||||
VIEWS,
|
||||
WORKFLOW_ACTIVE_MODAL_KEY,
|
||||
WORKFLOW_LM_CHAT_MODAL_KEY,
|
||||
WORKFLOW_SETTINGS_MODAL_KEY,
|
||||
WORKFLOW_SHARE_MODAL_KEY,
|
||||
EXTERNAL_SECRETS_PROVIDER_MODAL_KEY,
|
||||
@@ -69,6 +71,9 @@ export const useUIStore = defineStore(STORES.UI, {
|
||||
[ABOUT_MODAL_KEY]: {
|
||||
open: false,
|
||||
},
|
||||
[CHAT_EMBED_MODAL_KEY]: {
|
||||
open: false,
|
||||
},
|
||||
[CHANGE_PASSWORD_MODAL_KEY]: {
|
||||
open: false,
|
||||
},
|
||||
@@ -103,6 +108,9 @@ export const useUIStore = defineStore(STORES.UI, {
|
||||
[VERSIONS_MODAL_KEY]: {
|
||||
open: false,
|
||||
},
|
||||
[WORKFLOW_LM_CHAT_MODAL_KEY]: {
|
||||
open: false,
|
||||
},
|
||||
[WORKFLOW_SETTINGS_MODAL_KEY]: {
|
||||
open: false,
|
||||
},
|
||||
|
||||
@@ -125,7 +125,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
workflowsById: {},
|
||||
subWorkflowExecutionError: null,
|
||||
activeExecutionId: null,
|
||||
executingNode: null,
|
||||
executingNode: [],
|
||||
executionWaitingForWebhook: false,
|
||||
nodeMetadata: {},
|
||||
isInDebugMode: false,
|
||||
@@ -262,6 +262,9 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
return (nodeName: string) =>
|
||||
this.nodeMetadata[nodeName] === undefined || this.nodeMetadata[nodeName].pristine;
|
||||
},
|
||||
isNodeExecuting(): (nodeName: string) => boolean {
|
||||
return (nodeName: string) => this.executingNode.includes(nodeName);
|
||||
},
|
||||
// Executions getters
|
||||
getExecutionDataById(): (id: string) => IExecutionsSummary | undefined {
|
||||
return (id: string): IExecutionsSummary | undefined =>
|
||||
@@ -434,10 +437,18 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
this.setWorkflowTagIds([]);
|
||||
|
||||
this.activeExecutionId = null;
|
||||
this.executingNode = null;
|
||||
this.executingNode.length = 0;
|
||||
this.executionWaitingForWebhook = false;
|
||||
},
|
||||
|
||||
addExecutingNode(nodeName: string): void {
|
||||
this.executingNode.push(nodeName);
|
||||
},
|
||||
|
||||
removeExecutingNode(nodeName: string): void {
|
||||
this.executingNode = this.executingNode.filter((name) => name !== nodeName);
|
||||
},
|
||||
|
||||
setWorkflowId(id: string): void {
|
||||
this.workflow.id = id === 'new' ? PLACEHOLDER_EMPTY_WORKFLOW_ID : id;
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user