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:
कारतोफ्फेलस्क्रिप्ट™
2023-10-02 17:33:43 +02:00
committed by GitHub
parent 04dfcd73be
commit 00a4b8b0c6
93 changed files with 6209 additions and 728 deletions

View File

@@ -156,6 +156,7 @@
{{ $locale.baseText(linkedRuns ? 'runData.unlinking.hint' : 'runData.linking.hint') }}
</template>
<n8n-icon-button
class="linkRun"
:icon="linkedRuns ? 'unlink' : 'link'"
text
type="tertiary"
@@ -166,6 +167,7 @@
<slot name="run-info"></slot>
</div>
<slot name="before-data" />
<div
v-if="maxOutputIndex > 0 && branches.length > 1"
@@ -247,6 +249,7 @@
})
}}
</n8n-text>
<slot name="content" v-else-if="$slots['content']"></slot>
<NodeErrorView
v-else
:error="workflowRunData[node.name][runIndex].error"
@@ -292,6 +295,9 @@
/>
</div>
<!-- V-else slot named content which only renders if $slots.content is passed and hasNodeRun -->
<slot name="content" v-else-if="hasNodeRun && $slots['content']"></slot>
<div
v-else-if="
hasNodeRun &&
@@ -491,6 +497,7 @@ import type { PropType } from 'vue';
import { mapStores } from 'pinia';
import { saveAs } from 'file-saver';
import type {
ConnectionTypes,
IBinaryData,
IBinaryKeyData,
IDataObject,
@@ -499,6 +506,7 @@ import type {
IRunData,
IRunExecutionData,
} from 'n8n-workflow';
import { NodeHelpers, NodeConnectionType } from 'n8n-workflow';
import type {
IExecutionResponse,
@@ -613,6 +621,7 @@ export default defineComponent({
},
data() {
return {
connectionType: NodeConnectionType.Main,
binaryDataPreviewActive: false,
dataSize: 0,
showData: false,
@@ -678,10 +687,32 @@ export default defineComponent({
return this.displayMode === 'schema';
},
isTriggerNode(): boolean {
if (this.node === null) {
return false;
}
return this.nodeTypesStore.isTriggerNode(this.node.type);
},
canPinData(): boolean {
// Only "main" inputs can pin data
if (this.node === null) {
return false;
}
const workflow = this.workflowsStore.getCurrentWorkflow();
const workflowNode = workflow.getNode(this.node.name);
const inputs = NodeHelpers.getNodeInputs(workflow, workflowNode!, this.nodeType!);
const nonMainInputs = !!inputs.find((input) => {
if (typeof input === 'string') {
return input !== NodeConnectionType.Main;
}
return input.type !== NodeConnectionType.Main;
});
return (
!nonMainInputs &&
!this.isPaneTypeInput &&
this.isPinDataNodeType &&
!(this.binaryData && this.binaryData.length > 0)
@@ -698,7 +729,7 @@ export default defineComponent({
}
const schemaView = { label: this.$locale.baseText('runData.schema'), value: 'schema' };
if (this.isPaneTypeInput) {
if (this.isPaneTypeInput && !isEmpty(this.jsonData)) {
defaults.unshift(schemaView);
} else {
defaults.push(schemaView);
@@ -732,13 +763,7 @@ export default defineComponent({
return Boolean(this.subworkflowExecutionError);
},
hasRunError(): boolean {
return Boolean(
this.node &&
this.workflowRunData &&
this.workflowRunData[this.node.name] &&
this.workflowRunData[this.node.name][this.runIndex] &&
this.workflowRunData[this.node.name][this.runIndex].error,
);
return Boolean(this.node && this.workflowRunData?.[this.node.name]?.[this.runIndex]?.error);
},
workflowExecution(): IExecutionResponse | null {
return this.workflowsStore.getWorkflowExecution;
@@ -748,7 +773,7 @@ export default defineComponent({
return null;
}
const executionData: IRunExecutionData | undefined = this.workflowExecution.data;
if (executionData && executionData.resultData) {
if (executionData?.resultData) {
return executionData.resultData.runData;
}
return null;
@@ -760,7 +785,7 @@ export default defineComponent({
return (this.dataSize / 1024 / 1000).toLocaleString();
},
maxOutputIndex(): number {
if (this.node === null) {
if (this.node === null || this.runIndex === undefined) {
return 0;
}
@@ -776,7 +801,7 @@ export default defineComponent({
if (runData[this.node.name][this.runIndex]) {
const taskData = runData[this.node.name][this.runIndex].data;
if (taskData && taskData.main) {
if (taskData?.main) {
return taskData.main.length - 1;
}
}
@@ -807,7 +832,13 @@ export default defineComponent({
let inputData: INodeExecutionData[] = [];
if (this.node) {
inputData = this.getNodeInputData(this.node, this.runIndex, this.currentOutputIndex);
inputData = this.getNodeInputData(
this.node,
this.runIndex,
this.currentOutputIndex,
this.paneType,
this.connectionType,
);
}
if (inputData.length === 0 || !Array.isArray(inputData)) {
@@ -858,12 +889,8 @@ export default defineComponent({
return binaryData.filter((data) => Boolean(data && Object.keys(data).length));
},
currentOutputIndex(): number {
if (
this.overrideOutputs &&
this.overrideOutputs.length &&
!this.overrideOutputs.includes(this.outputIndex)
) {
return this.overrideOutputs[0] as number;
if (this.overrideOutputs?.length && !this.overrideOutputs.includes(this.outputIndex)) {
return this.overrideOutputs[0];
}
return this.outputIndex;
@@ -1216,7 +1243,11 @@ export default defineComponent({
const itemsLabel = itemsCount > 0 ? ` (${itemsCount} ${items})` : '';
return option + this.$locale.baseText('ndv.output.of') + (this.maxRunIndex + 1) + itemsLabel;
},
getDataCount(runIndex: number, outputIndex: number) {
getDataCount(
runIndex: number,
outputIndex: number,
connectionType: ConnectionTypes = NodeConnectionType.Main,
) {
if (this.pinData) {
return this.pinData.length;
}
@@ -1246,7 +1277,11 @@ export default defineComponent({
return 0;
}
const inputData = this.getMainInputData(runData[this.node.name][runIndex].data!, outputIndex);
const inputData = this.getInputData(
runData[this.node.name][runIndex].data!,
outputIndex,
connectionType,
);
return inputData.length;
},
@@ -1255,6 +1290,14 @@ export default defineComponent({
this.outputIndex = 0;
this.refreshDataSize();
this.closeBinaryDataDisplay();
let outputTypes: ConnectionTypes[] = [];
if (this.nodeType !== null && this.node !== null) {
const workflow = this.workflowsStore.getCurrentWorkflow();
const workflowNode = workflow.getNode(this.node.name);
const outputs = NodeHelpers.getNodeOutputs(workflow, workflowNode, this.nodeType);
outputTypes = NodeHelpers.getConnectionTypes(outputs);
}
this.connectionType = outputTypes.length === 0 ? NodeConnectionType.Main : outputTypes[0];
if (this.binaryData.length > 0) {
this.ndvStore.setPanelDisplayMode({
pane: this.paneType as 'input' | 'output',
@@ -1297,7 +1340,13 @@ export default defineComponent({
}
},
async downloadJsonData() {
const inputData = this.getNodeInputData(this.node, this.runIndex, this.currentOutputIndex);
const inputData = this.getNodeInputData(
this.node,
this.runIndex,
this.currentOutputIndex,
this.paneType,
this.connectionType,
);
const fileName = this.node!.name.replace(/[^\w\d]/g, '_');
const blob = new Blob([JSON.stringify(inputData, null, 2)], { type: 'application/json' });
@@ -1321,7 +1370,7 @@ export default defineComponent({
}
const nodeType = this.nodeType;
if (!nodeType || !nodeType.outputNames || nodeType.outputNames.length <= outputIndex) {
if (!nodeType?.outputNames || nodeType.outputNames.length <= outputIndex) {
return outputIndex + 1;
}
@@ -1332,7 +1381,13 @@ export default defineComponent({
this.showData = false;
// Check how much data there is to display
const inputData = this.getNodeInputData(this.node, this.runIndex, this.currentOutputIndex);
const inputData = this.getNodeInputData(
this.node,
this.runIndex,
this.currentOutputIndex,
this.paneType,
this.connectionType,
);
const offset = this.pageSize * (this.currentPage - 1);
const jsonItems = inputData.slice(offset, offset + this.pageSize).map((item) => item.json);
@@ -1468,6 +1523,7 @@ export default defineComponent({
.dataContainer {
position: relative;
overflow-y: auto;
height: 100%;
&:hover {
@@ -1517,7 +1573,7 @@ export default defineComponent({
align-items: center;
bottom: 0;
padding: 5px;
overflow: auto;
overflow-y: hidden;
}
.pageSizeSelector {