mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-10 06:34:05 -08:00
feat(core): Update LLM applications building support (no-changelog) (#7418)
extracted out of #7336 --------- Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com> Co-authored-by: OlegIvaniv <me@olegivaniv.com> Co-authored-by: Jan Oberhauser <janober@users.noreply.github.com> Co-authored-by: Val <68596159+valya@users.noreply.github.com> Co-authored-by: Alex Grozav <alex@grozav.com> Co-authored-by: Deborah <deborah@starfallprojects.co.uk> Co-authored-by: Jesper Bylund <mail@jesperbylund.com> Co-authored-by: Jon <jonathan.bennetts@gmail.com>
This commit is contained in:
parent
34f3f8001e
commit
91dfc4d513
|
@ -2469,6 +2469,10 @@ const addExecutionDataFunctions = async (
|
|||
});
|
||||
}
|
||||
|
||||
if (get(runExecutionData, 'executionData.metadata', undefined) === undefined) {
|
||||
runExecutionData.executionData!.metadata = {};
|
||||
}
|
||||
|
||||
let sourceTaskData = get(runExecutionData, `executionData.metadata[${sourceNodeName}]`);
|
||||
|
||||
if (!sourceTaskData) {
|
||||
|
@ -3033,7 +3037,7 @@ export function getExecuteFunctions(
|
|||
};
|
||||
|
||||
try {
|
||||
return await nodeType.supplyData.call(context);
|
||||
return await nodeType.supplyData.call(context, itemIndex);
|
||||
} catch (error) {
|
||||
if (!(error instanceof ExecutionBaseError)) {
|
||||
error = new NodeOperationError(connectedNode, error, {
|
||||
|
|
|
@ -69,9 +69,9 @@ import { createChat } from '@n8n/chat';`,
|
|||
}));
|
||||
|
||||
const cdnCode = computed(
|
||||
() => `<link href="https://cdn.jsdelivr.net/npm/@n8n/chat/style.css" type="text/css" />
|
||||
() => `<link href="https://cdn.jsdelivr.net/npm/@n8n/chat/style.css" rel="stylesheet" />
|
||||
<script type="module">
|
||||
import { createChat } from 'https://cdn.jsdelivr.net/npm/@n8n/chat/chat.js';
|
||||
import { createChat } from 'https://cdn.jsdelivr.net/npm/@n8n/chat/chat.bundle.es.js';
|
||||
|
||||
${commonCode.value.createChat}
|
||||
</${'script'}>`,
|
||||
|
|
|
@ -274,6 +274,21 @@ export default defineComponent({
|
|||
return [];
|
||||
},
|
||||
parentNode(): string | undefined {
|
||||
const pinData = this.workflowsStore.getPinData;
|
||||
|
||||
// Return the first parent node that contains data
|
||||
for (const parentNodeName of this.parentNodes) {
|
||||
// Check first for pinned data
|
||||
if (pinData[parentNodeName]) {
|
||||
return parentNodeName;
|
||||
}
|
||||
|
||||
// Check then the data of the current execution
|
||||
if (this.workflowRunData?.[parentNodeName]) {
|
||||
return parentNodeName;
|
||||
}
|
||||
}
|
||||
|
||||
return this.parentNodes[0];
|
||||
},
|
||||
isExecutableTriggerNode(): boolean {
|
||||
|
@ -617,8 +632,8 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
if (
|
||||
typeof this.activeNodeType.outputs === 'string' ||
|
||||
typeof this.activeNodeType.inputs === 'string'
|
||||
typeof this.activeNodeType?.outputs === 'string' ||
|
||||
typeof this.activeNodeType?.inputs === 'string'
|
||||
) {
|
||||
// TODO: We should keep track of if it actually changed and only do if required
|
||||
// Whenever a node with custom inputs and outputs gets closed redraw it in case
|
||||
|
|
|
@ -98,6 +98,9 @@ export default defineComponent({
|
|||
return this.uiStore.isActionActive('workflowRunning');
|
||||
},
|
||||
isTriggerNode(): boolean {
|
||||
if (!this.node) {
|
||||
return false;
|
||||
}
|
||||
return this.nodeTypesStore.isTriggerNode(this.node.type);
|
||||
},
|
||||
isManualTriggerNode(): boolean {
|
||||
|
|
|
@ -233,13 +233,17 @@ export default defineComponent({
|
|||
return this.readOnly || this.hasForeignCredential;
|
||||
},
|
||||
isExecutable(): boolean {
|
||||
if (
|
||||
this.nodeType &&
|
||||
!this.isTriggerNode &&
|
||||
!this.nodeType.inputs.includes(NodeConnectionType.Main)
|
||||
) {
|
||||
if (this.nodeType && this.node) {
|
||||
const workflow = this.workflowsStore.getCurrentWorkflow();
|
||||
const workflowNode = workflow.getNode(this.node.name);
|
||||
const inputs = NodeHelpers.getNodeInputs(workflow, workflowNode!, this.nodeType!);
|
||||
const inputNames = NodeHelpers.getConnectionTypes(inputs);
|
||||
|
||||
if (!inputNames.includes(NodeConnectionType.Main) && !this.isTriggerNode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return this.executable || this.hasForeignCredential;
|
||||
},
|
||||
nodeTypeName(): string {
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
</template>
|
||||
|
||||
<template #content v-if="outputMode === 'logs'">
|
||||
<run-data-ai :node="node" />
|
||||
<run-data-ai :node="node" :run-index="runIndex" />
|
||||
</template>
|
||||
<template #recovered-artificial-output-data>
|
||||
<div :class="$style.recoveredOutputData">
|
||||
|
@ -184,11 +184,11 @@ export default defineComponent({
|
|||
if (this.node) {
|
||||
const resultData = this.workflowsStore.getWorkflowResultDataByNodeName(this.node.name);
|
||||
|
||||
if (!resultData || !Array.isArray(resultData)) {
|
||||
if (!resultData || !Array.isArray(resultData) || resultData.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!resultData[resultData.length - 1!].metadata;
|
||||
return !!resultData[resultData.length - 1].metadata;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
|
|
@ -702,14 +702,9 @@ export default defineComponent({
|
|||
const workflow = this.workflowsStore.getCurrentWorkflow();
|
||||
const workflowNode = workflow.getNode(this.node.name);
|
||||
const inputs = NodeHelpers.getNodeInputs(workflow, workflowNode!, this.nodeType!);
|
||||
const inputNames = NodeHelpers.getConnectionTypes(inputs);
|
||||
|
||||
const nonMainInputs = !!inputs.find((input) => {
|
||||
if (typeof input === 'string') {
|
||||
return input !== NodeConnectionType.Main;
|
||||
}
|
||||
|
||||
return input.type !== NodeConnectionType.Main;
|
||||
});
|
||||
const nonMainInputs = !!inputNames.find((inputName) => inputName !== NodeConnectionType.Main);
|
||||
|
||||
return (
|
||||
!nonMainInputs &&
|
||||
|
|
|
@ -161,7 +161,7 @@ export default defineComponent({
|
|||
});
|
||||
},
|
||||
onDragStart(el: HTMLElement) {
|
||||
if (el && el.dataset.path) {
|
||||
if (el?.dataset.path) {
|
||||
this.draggingPath = el.dataset.path;
|
||||
}
|
||||
|
||||
|
@ -169,8 +169,6 @@ export default defineComponent({
|
|||
},
|
||||
onDragEnd(el: HTMLElement) {
|
||||
this.draggingPath = null;
|
||||
|
||||
setTimeout(() => {
|
||||
const mappingTelemetry = this.ndvStore.mappingTelemetry;
|
||||
const telemetryPayload = {
|
||||
src_node_type: this.node.type,
|
||||
|
@ -185,8 +183,8 @@ export default defineComponent({
|
|||
...mappingTelemetry,
|
||||
};
|
||||
|
||||
setTimeout(() => {
|
||||
void this.$externalHooks().run('runDataJson.onDragEnd', telemetryPayload);
|
||||
|
||||
this.$telemetry.track('User dragged data for mapping', telemetryPayload);
|
||||
}, 1000); // ensure dest data gets set if drop
|
||||
},
|
||||
|
|
|
@ -594,7 +594,6 @@ export default defineComponent({
|
|||
|
||||
const executionData = this.workflowsStore.getWorkflowExecution;
|
||||
let parentNode = this.workflow.getParentNodes(this.activeNode.name, inputName, 1);
|
||||
console.log('parentNode', parentNode);
|
||||
let runData = this.workflowsStore.getWorkflowRunData;
|
||||
|
||||
if (runData === null) {
|
||||
|
|
|
@ -133,13 +133,8 @@ import { get, last } from 'lodash-es';
|
|||
|
||||
import { useUIStore, useWorkflowsStore } from '@/stores';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
import {
|
||||
type INode,
|
||||
type INodeType,
|
||||
type ITaskData,
|
||||
NodeHelpers,
|
||||
NodeConnectionType,
|
||||
} from 'n8n-workflow';
|
||||
import type { IDataObject, INodeType, INode, ITaskData } from 'n8n-workflow';
|
||||
import { NodeHelpers, NodeConnectionType } from 'n8n-workflow';
|
||||
import type { INodeUi } from '@/Interface';
|
||||
|
||||
const RunDataAi = defineAsyncComponent(async () => import('@/components/RunDataAi/RunDataAi.vue'));
|
||||
|
@ -433,6 +428,17 @@ export default defineComponent({
|
|||
|
||||
this.waitForExecution(response.executionId);
|
||||
},
|
||||
extractResponseMessage(responseData?: IDataObject) {
|
||||
if (!responseData) return '<NO RESPONSE FOUND>';
|
||||
|
||||
// Paths where the response message might be located
|
||||
const paths = ['output', 'text', 'response.text'];
|
||||
const matchedPath = paths.find((path) => get(responseData, path));
|
||||
|
||||
if (!matchedPath) return JSON.stringify(responseData, null, 2);
|
||||
|
||||
return get(responseData, matchedPath) as string;
|
||||
},
|
||||
waitForExecution(executionId?: string) {
|
||||
const that = this;
|
||||
const waitInterval = setInterval(() => {
|
||||
|
@ -455,18 +461,7 @@ export default defineComponent({
|
|||
responseMessage = '[ERROR: ' + get(nodeResponseData, ['error', 'message']) + ']';
|
||||
} else {
|
||||
const responseData = get(nodeResponseData, 'data.main[0][0].json');
|
||||
if (responseData) {
|
||||
const responseObj = responseData as object & { output?: string };
|
||||
if (responseObj.output !== undefined) {
|
||||
responseMessage = responseObj.output;
|
||||
} else if (Object.keys(responseObj).length === 0) {
|
||||
responseMessage = '<NO RESPONSE FOUND>';
|
||||
} else {
|
||||
responseMessage = JSON.stringify(responseObj, null, 2);
|
||||
}
|
||||
} else {
|
||||
responseMessage = '<NO RESPONSE FOUND>';
|
||||
}
|
||||
responseMessage = this.extractResponseMessage(responseData);
|
||||
}
|
||||
|
||||
this.messages.push({
|
||||
|
|
|
@ -168,7 +168,7 @@ export const NON_ACTIVATABLE_TRIGGER_NODE_TYPES = [
|
|||
MANUAL_CHAT_TRIGGER_NODE_TYPE,
|
||||
];
|
||||
|
||||
export const NODES_USING_CODE_NODE_EDITOR = [CODE_NODE_TYPE];
|
||||
export const NODES_USING_CODE_NODE_EDITOR = [CODE_NODE_TYPE, AI_CODE_NODE_TYPE];
|
||||
|
||||
export const PIN_DATA_NODE_TYPES_DENYLIST = [SPLIT_IN_BATCHES_NODE_TYPE];
|
||||
|
||||
|
|
|
@ -114,22 +114,22 @@ export function resolveParameter(
|
|||
let itemIndex = opts?.targetItem?.itemIndex || 0;
|
||||
|
||||
const inputName = NodeConnectionType.Main;
|
||||
let activeNode = useNDVStore().activeNode;
|
||||
const activeNode = useNDVStore().activeNode;
|
||||
let contextNode = activeNode;
|
||||
|
||||
const workflow = getCurrentWorkflow();
|
||||
|
||||
// Should actually just do that for incoming data and not things like parameters
|
||||
if (activeNode) {
|
||||
activeNode = getParentMainInputNode(workflow, activeNode);
|
||||
contextNode = getParentMainInputNode(workflow, activeNode);
|
||||
}
|
||||
|
||||
const workflowRunData = useWorkflowsStore().getWorkflowRunData;
|
||||
let parentNode = workflow.getParentNodes(activeNode!.name, inputName, 1);
|
||||
let parentNode = workflow.getParentNodes(contextNode!.name, inputName, 1);
|
||||
const executionData = useWorkflowsStore().getWorkflowExecution;
|
||||
|
||||
let runIndexParent = opts?.inputRunIndex ?? 0;
|
||||
const nodeConnection = workflow.getNodeConnectionIndexes(activeNode!.name, parentNode[0]);
|
||||
if (opts.targetItem && opts?.targetItem?.nodeName === activeNode!.name && executionData) {
|
||||
const nodeConnection = workflow.getNodeConnectionIndexes(contextNode!.name, parentNode[0]);
|
||||
if (opts.targetItem && opts?.targetItem?.nodeName === contextNode!.name && executionData) {
|
||||
const sourceItems = getSourceItems(executionData, opts.targetItem);
|
||||
if (!sourceItems.length) {
|
||||
return null;
|
||||
|
@ -158,7 +158,7 @@ export function resolveParameter(
|
|||
|
||||
let _connectionInputData = connectionInputData(
|
||||
parentNode,
|
||||
activeNode!.name,
|
||||
contextNode!.name,
|
||||
inputName,
|
||||
runIndexParent,
|
||||
nodeConnection,
|
||||
|
@ -198,11 +198,11 @@ export function resolveParameter(
|
|||
if (
|
||||
opts?.targetItem === undefined &&
|
||||
workflowRunData !== null &&
|
||||
workflowRunData[activeNode!.name]
|
||||
workflowRunData[contextNode!.name]
|
||||
) {
|
||||
runIndexCurrent = workflowRunData[activeNode!.name].length - 1;
|
||||
runIndexCurrent = workflowRunData[contextNode!.name].length - 1;
|
||||
}
|
||||
const _executeData = executeData(parentNode, activeNode!.name, inputName, runIndexCurrent);
|
||||
const _executeData = executeData(parentNode, contextNode!.name, inputName, runIndexCurrent);
|
||||
|
||||
ExpressionEvaluatorProxy.setEvaluator(
|
||||
useSettingsStore().settings.expressions?.evaluator ?? 'tmpl',
|
||||
|
@ -220,6 +220,8 @@ export function resolveParameter(
|
|||
additionalKeys,
|
||||
_executeData,
|
||||
false,
|
||||
{},
|
||||
contextNode!.name,
|
||||
) as IDataObject;
|
||||
}
|
||||
|
||||
|
|
|
@ -2211,6 +2211,7 @@ export default defineComponent({
|
|||
if (lastSelectedNodeEndpointUuid && !isAutoAdd) {
|
||||
const lastSelectedEndpoint = this.instance.getEndpoint(lastSelectedNodeEndpointUuid);
|
||||
if (
|
||||
lastSelectedEndpoint &&
|
||||
this.checkNodeConnectionAllowed(
|
||||
lastSelectedNode!,
|
||||
newNodeData,
|
||||
|
@ -2382,7 +2383,14 @@ export default defineComponent({
|
|||
);
|
||||
|
||||
if (targetNodeType?.inputs?.length) {
|
||||
for (const input of targetNodeType?.inputs || []) {
|
||||
const workflow = this.getCurrentWorkflow();
|
||||
const workflowNode = workflow.getNode(targetNode.name);
|
||||
let inputs: Array<ConnectionTypes | INodeInputConfiguration> = [];
|
||||
if (targetNodeType) {
|
||||
inputs = NodeHelpers.getNodeInputs(workflow, workflowNode!, targetNodeType);
|
||||
}
|
||||
|
||||
for (const input of inputs || []) {
|
||||
if (typeof input === 'string' || input.type !== targetInfoType || !input.filter) {
|
||||
// No filters defined or wrong connection type
|
||||
continue;
|
||||
|
@ -3356,7 +3364,6 @@ export default defineComponent({
|
|||
nodeConnections || [],
|
||||
(connectionType as ConnectionTypes) ?? NodeConnectionType.Main,
|
||||
);
|
||||
|
||||
Object.keys(outputMap).forEach((sourceOutputIndex: string) => {
|
||||
Object.keys(outputMap[sourceOutputIndex]).forEach((targetNodeName: string) => {
|
||||
Object.keys(outputMap[sourceOutputIndex][targetNodeName]).forEach(
|
||||
|
|
|
@ -773,7 +773,6 @@ export type IExecuteFunctions = ExecuteFunctions.GetNodeParameterFn &
|
|||
inputName: ConnectionTypes,
|
||||
itemIndex: number,
|
||||
inputIndex?: number,
|
||||
nodeNameOverride?: string,
|
||||
): Promise<unknown>;
|
||||
getInputData(inputIndex?: number, inputName?: string): INodeExecutionData[];
|
||||
getNodeOutputs(): INodeOutputConfiguration[];
|
||||
|
@ -1290,7 +1289,7 @@ export interface SupplyData {
|
|||
|
||||
export interface INodeType {
|
||||
description: INodeTypeDescription;
|
||||
supplyData?(this: IExecuteFunctions): Promise<SupplyData>;
|
||||
supplyData?(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData>;
|
||||
execute?(
|
||||
this: IExecuteFunctions,
|
||||
): Promise<INodeExecutionData[][] | NodeExecutionWithMetadata[][] | null>;
|
||||
|
@ -1534,8 +1533,6 @@ export const enum NodeConnectionType {
|
|||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
AiTool = 'ai_tool',
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
AiVectorRetriever = 'ai_vectorRetriever',
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
AiVectorStore = 'ai_vectorStore',
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
Main = 'main',
|
||||
|
|
Loading…
Reference in a new issue