mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
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
@@ -3,14 +3,14 @@
|
||||
:nodeUi="currentNode"
|
||||
:runIndex="runIndex"
|
||||
:linkedRuns="linkedRuns"
|
||||
:canLinkRuns="canLinkRuns"
|
||||
:canLinkRuns="!mappedNode && canLinkRuns"
|
||||
:tooMuchDataTitle="$locale.baseText('ndv.input.tooMuchData.title')"
|
||||
:noDataInBranchMessage="$locale.baseText('ndv.input.noOutputDataInBranch')"
|
||||
:isExecuting="isExecutingPrevious"
|
||||
:executingMessage="$locale.baseText('ndv.input.executingPrevious')"
|
||||
:sessionId="sessionId"
|
||||
:overrideOutputs="connectedCurrentNodeOutputs"
|
||||
:mappingEnabled="!readOnly"
|
||||
:mappingEnabled="isMappingEnabled"
|
||||
:distanceFromActive="currentNodeDepth"
|
||||
:isProductionExecutionPreview="isProductionExecutionPreview"
|
||||
paneType="input"
|
||||
@@ -55,11 +55,43 @@
|
||||
</n8n-option>
|
||||
</n8n-select>
|
||||
<span v-else :class="$style.title">{{ $locale.baseText('ndv.input') }}</span>
|
||||
<n8n-radio-buttons
|
||||
v-if="isActiveNodeConfig && !readOnly"
|
||||
:options="inputModes"
|
||||
:modelValue="inputMode"
|
||||
@update:modelValue="onInputModeChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #before-data v-if="isMappingMode">
|
||||
<!--
|
||||
Hide the run linking buttons for both input and ouput panels when in 'Mapping Mode' because the run indices wouldn't match.
|
||||
Although this is not the most elegant solution, it's straightforward and simpler than introducing a new props and logic to handle this.
|
||||
-->
|
||||
<component :is="'style'">button.linkRun { display: none }</component>
|
||||
<div :class="$style.mappedNode">
|
||||
<n8n-select
|
||||
:modelValue="mappedNode"
|
||||
@update:modelValue="onMappedNodeSelected"
|
||||
size="small"
|
||||
@click.stop
|
||||
teleported
|
||||
>
|
||||
<template #prepend>{{ $locale.baseText('ndv.input.previousNode') }}</template>
|
||||
<n8n-option
|
||||
v-for="nodeName in rootNodesParents"
|
||||
:key="nodeName"
|
||||
:label="nodeName"
|
||||
:value="nodeName"
|
||||
/>
|
||||
</n8n-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #node-not-run>
|
||||
<div :class="$style.noOutputData" v-if="parentNodes.length">
|
||||
<div
|
||||
:class="$style.noOutputData"
|
||||
v-if="(isActiveNodeConfig && rootNode) || parentNodes.length"
|
||||
>
|
||||
<n8n-text tag="div" :bold="true" color="text-dark" size="large">{{
|
||||
$locale.baseText('ndv.input.noOutputData.title')
|
||||
}}</n8n-text>
|
||||
@@ -76,7 +108,7 @@
|
||||
<NodeExecuteButton
|
||||
type="secondary"
|
||||
:transparent="true"
|
||||
:nodeName="currentNodeName"
|
||||
:nodeName="isActiveNodeConfig ? rootNode : currentNodeName"
|
||||
:label="$locale.baseText('ndv.input.noOutputData.executePrevious')"
|
||||
@execute="onNodeExecute"
|
||||
telemetrySource="inputs"
|
||||
@@ -130,7 +162,8 @@
|
||||
import { defineComponent } from 'vue';
|
||||
import { mapStores } from 'pinia';
|
||||
import type { INodeUi } from '@/Interface';
|
||||
import type { IConnectedNode, INodeTypeDescription, Workflow } from 'n8n-workflow';
|
||||
import { NodeHelpers, NodeConnectionType } from 'n8n-workflow';
|
||||
import type { ConnectionTypes, IConnectedNode, INodeTypeDescription, Workflow } from 'n8n-workflow';
|
||||
import RunData from './RunData.vue';
|
||||
import { workflowHelpers } from '@/mixins/workflowHelpers';
|
||||
import NodeExecuteButton from './NodeExecuteButton.vue';
|
||||
@@ -145,6 +178,8 @@ import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
|
||||
type MappingMode = 'debugging' | 'mapping';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'InputPanel',
|
||||
mixins: [workflowHelpers],
|
||||
@@ -178,6 +213,12 @@ export default defineComponent({
|
||||
return {
|
||||
showDraggableHintWithDelay: false,
|
||||
draggableHintShown: false,
|
||||
inputMode: 'debugging' as MappingMode,
|
||||
mappedNode: null as string | null,
|
||||
inputModes: [
|
||||
{ value: 'mapping', label: this.$locale.baseText('ndv.input.mapping') },
|
||||
{ value: 'debugging', label: this.$locale.baseText('ndv.input.debugging') },
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -188,6 +229,9 @@ export default defineComponent({
|
||||
isUserOnboarded(): boolean {
|
||||
return this.ndvStore.isMappingOnboarded;
|
||||
},
|
||||
isMappingMode(): boolean {
|
||||
return this.isActiveNodeConfig && this.inputMode === 'mapping';
|
||||
},
|
||||
showDraggableHint(): boolean {
|
||||
const toIgnore = [
|
||||
START_NODE_TYPE,
|
||||
@@ -201,23 +245,59 @@ export default defineComponent({
|
||||
|
||||
return !!this.focusedMappableInput && !this.isUserOnboarded;
|
||||
},
|
||||
isActiveNodeConfig(): boolean {
|
||||
let inputs = this.activeNodeType?.inputs ?? [];
|
||||
let outputs = this.activeNodeType?.outputs ?? [];
|
||||
if (this.activeNode !== null && this.currentWorkflow !== null) {
|
||||
const node = this.currentWorkflow.getNode(this.activeNode.name);
|
||||
inputs = NodeHelpers.getNodeInputs(this.currentWorkflow, node!, this.activeNodeType!);
|
||||
outputs = NodeHelpers.getNodeOutputs(this.currentWorkflow, node!, this.activeNodeType!);
|
||||
} else {
|
||||
// If we can not figure out the node type we set no outputs
|
||||
if (!Array.isArray(inputs)) {
|
||||
inputs = [] as ConnectionTypes[];
|
||||
}
|
||||
if (!Array.isArray(outputs)) {
|
||||
outputs = [] as ConnectionTypes[];
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
(inputs.length === 0 ||
|
||||
inputs.find((inputName) => inputName !== NodeConnectionType.Main)) &&
|
||||
outputs.find((outputName) => outputName !== NodeConnectionType.Main)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
isMappingEnabled(): boolean {
|
||||
if (this.readOnly) return false;
|
||||
|
||||
// Mapping is only enabled in mapping mode for config nodes and if node to map is selected
|
||||
if (this.isActiveNodeConfig) return this.isMappingMode && this.mappedNode !== null;
|
||||
|
||||
return true;
|
||||
},
|
||||
isExecutingPrevious(): boolean {
|
||||
if (!this.workflowRunning) {
|
||||
return false;
|
||||
}
|
||||
const triggeredNode = this.workflowsStore.executedNode;
|
||||
const executingNode = this.workflowsStore.executingNode;
|
||||
|
||||
if (
|
||||
this.activeNode &&
|
||||
triggeredNode === this.activeNode.name &&
|
||||
this.activeNode.name !== executingNode
|
||||
!this.workflowsStore.isNodeExecuting(this.activeNode.name)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (executingNode || triggeredNode) {
|
||||
if (executingNode.length || triggeredNode) {
|
||||
return !!this.parentNodes.find(
|
||||
(node) => node.name === executingNode || node.name === triggeredNode,
|
||||
(node) => this.workflowsStore.isNodeExecuting(node.name) || node.name === triggeredNode,
|
||||
);
|
||||
}
|
||||
return false;
|
||||
@@ -231,7 +311,31 @@ export default defineComponent({
|
||||
activeNode(): INodeUi | null {
|
||||
return this.ndvStore.activeNode;
|
||||
},
|
||||
|
||||
rootNode(): string {
|
||||
const workflow = this.currentWorkflow;
|
||||
const rootNodes = workflow.getChildNodes(this.activeNode.name, 'ALL_NON_MAIN');
|
||||
|
||||
return rootNodes[0];
|
||||
},
|
||||
rootNodesParents(): string[] {
|
||||
const workflow = this.currentWorkflow;
|
||||
const parentNodes = [...workflow.getParentNodes(this.rootNode, 'main')].reverse();
|
||||
|
||||
return parentNodes;
|
||||
},
|
||||
currentNode(): INodeUi | null {
|
||||
if (this.isActiveNodeConfig) {
|
||||
// if we're mapping node we want to show the output of the mapped node
|
||||
if (this.mappedNode) {
|
||||
return this.workflowsStore.getNodeByName(this.mappedNode);
|
||||
}
|
||||
|
||||
// in debugging mode data does get set manually and is only for debugging
|
||||
// so we want to force the node to be the active node to make sure we show the correct data
|
||||
return this.activeNode;
|
||||
}
|
||||
|
||||
return this.workflowsStore.getNodeByName(this.currentNodeName);
|
||||
},
|
||||
connectedCurrentNodeOutputs(): number[] | undefined {
|
||||
@@ -272,13 +376,21 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onInputModeChange(val: MappingMode) {
|
||||
this.inputMode = val;
|
||||
},
|
||||
onMappedNodeSelected(val: string) {
|
||||
this.mappedNode = val;
|
||||
|
||||
this.onRunIndexChange(0);
|
||||
this.onUnlinkRun();
|
||||
},
|
||||
getMultipleNodesText(nodeName?: string): string {
|
||||
if (
|
||||
!nodeName ||
|
||||
!this.isMultiInputNode ||
|
||||
!this.activeNode ||
|
||||
this.activeNodeType === null ||
|
||||
this.activeNodeType.inputNames === undefined
|
||||
this.activeNodeType?.inputNames === undefined
|
||||
)
|
||||
return '';
|
||||
|
||||
@@ -292,10 +404,7 @@ export default defineComponent({
|
||||
|
||||
// Match connected input indexes to their names specified by active node
|
||||
const connectedInputs = connectedInputIndexes.map(
|
||||
(inputIndex) =>
|
||||
this.activeNodeType &&
|
||||
this.activeNodeType.inputNames &&
|
||||
this.activeNodeType.inputNames[inputIndex],
|
||||
(inputIndex) => this.activeNodeType?.inputNames?.[inputIndex],
|
||||
);
|
||||
|
||||
if (connectedInputs.length === 0) return '';
|
||||
@@ -347,6 +456,16 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
inputMode: {
|
||||
handler(val) {
|
||||
this.onRunIndexChange(-1);
|
||||
if (val === 'mapping') {
|
||||
this.onUnlinkRun();
|
||||
this.mappedNode = this.rootNodesParents[0];
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
showDraggableHint(curr: boolean, prev: boolean) {
|
||||
if (curr && !prev) {
|
||||
setTimeout(() => {
|
||||
@@ -371,15 +490,22 @@ export default defineComponent({
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.mappedNode {
|
||||
width: max-content;
|
||||
padding: 0 var(--spacing-s) var(--spacing-s);
|
||||
}
|
||||
.titleSection {
|
||||
display: flex;
|
||||
max-width: 300px;
|
||||
align-items: center;
|
||||
|
||||
> * {
|
||||
margin-right: var(--spacing-2xs);
|
||||
}
|
||||
}
|
||||
|
||||
.inputModeTab {
|
||||
margin-left: auto;
|
||||
}
|
||||
.noOutputData {
|
||||
max-width: 180px;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user