fix: refactor to update types

This commit is contained in:
Mutasem Aldmour 2024-11-11 16:56:00 +01:00
parent 9d6f0c5dec
commit ad990236eb
No known key found for this signature in database
GPG key ID: 3DFA8122BB7FD6B8
11 changed files with 113 additions and 81 deletions

View file

@ -352,6 +352,9 @@ export class RetrieverWorkflow implements INodeType {
}, },
); );
} }
// same as current workflow
baseMetadata.workflowId = workflowProxy.$workflow.id;
} }
const rawData: IDataObject = { query }; const rawData: IDataObject = { query };
@ -395,8 +398,10 @@ export class RetrieverWorkflow implements INodeType {
config?.getChild(), config?.getChild(),
{ {
startMetadata: { startMetadata: {
executionId: workflowProxy.$execution.id, parentExecution: {
workflowId: workflowProxy.$workflow.id, executionId: workflowProxy.$execution.id,
workflowId: workflowProxy.$workflow.id,
},
}, },
}, },
); );

View file

@ -360,9 +360,13 @@ export class ToolWorkflow implements INodeType {
}; };
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> { async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
const workflowProxy = this.getWorkflowDataProxy(0);
const name = this.getNodeParameter('name', itemIndex) as string; const name = this.getNodeParameter('name', itemIndex) as string;
const description = this.getNodeParameter('description', itemIndex) as string; const description = this.getNodeParameter('description', itemIndex) as string;
let executionId: string | undefined = undefined;
let subExecutionId: string | undefined;
let subWorkflowId: string | undefined;
const useSchema = this.getNodeParameter('specifyInputSchema', itemIndex) as boolean; const useSchema = this.getNodeParameter('specifyInputSchema', itemIndex) as boolean;
let tool: DynamicTool | DynamicStructuredTool | undefined = undefined; let tool: DynamicTool | DynamicStructuredTool | undefined = undefined;
@ -399,11 +403,16 @@ export class ToolWorkflow implements INodeType {
) as INodeParameterResourceLocator; ) as INodeParameterResourceLocator;
workflowInfo.id = value as string; workflowInfo.id = value as string;
} }
subWorkflowId = workflowInfo.id;
} else if (source === 'parameter') { } else if (source === 'parameter') {
// Read workflow from parameter // Read workflow from parameter
const workflowJson = this.getNodeParameter('workflowJson', itemIndex) as string; const workflowJson = this.getNodeParameter('workflowJson', itemIndex) as string;
try { try {
workflowInfo.code = JSON.parse(workflowJson) as IWorkflowBase; workflowInfo.code = JSON.parse(workflowJson) as IWorkflowBase;
// subworkflow is same as parent workflow
subWorkflowId = workflowProxy.$workflow.id;
} catch (error) { } catch (error) {
throw new NodeOperationError( throw new NodeOperationError(
this.getNode(), this.getNode(),
@ -443,17 +452,17 @@ export class ToolWorkflow implements INodeType {
const items = [newItem] as INodeExecutionData[]; const items = [newItem] as INodeExecutionData[];
const workflowProxy = this.getWorkflowDataProxy(0);
let receivedData: ExecuteWorkflowData; let receivedData: ExecuteWorkflowData;
try { try {
receivedData = await this.executeWorkflow(workflowInfo, items, runManager?.getChild(), { receivedData = await this.executeWorkflow(workflowInfo, items, runManager?.getChild(), {
startMetadata: { startMetadata: {
executionId: workflowProxy.$execution.id, parentExecution: {
workflowId: workflowProxy.$workflow.id, executionId: workflowProxy.$execution.id,
workflowId: workflowProxy.$workflow.id,
},
}, },
}); });
executionId = receivedData.executionId; subExecutionId = receivedData.executionId;
} catch (error) { } catch (error) {
// Make sure a valid error gets returned that can by json-serialized else it will // Make sure a valid error gets returned that can by json-serialized else it will
// not show up in the frontend // not show up in the frontend
@ -512,9 +521,12 @@ export class ToolWorkflow implements INodeType {
} }
let metadata: ITaskMetadata | undefined; let metadata: ITaskMetadata | undefined;
if (executionId) { if (subExecutionId && subWorkflowId) {
metadata = { metadata = {
executionId, subExecution: {
executionId: subExecutionId,
workflowId: subWorkflowId,
},
}; };
} }

View file

@ -10,7 +10,12 @@ import type { Tool } from '@langchain/core/tools';
import { VectorStore } from '@langchain/core/vectorstores'; import { VectorStore } from '@langchain/core/vectorstores';
import { TextSplitter } from '@langchain/textsplitters'; import { TextSplitter } from '@langchain/textsplitters';
import type { BaseDocumentLoader } from 'langchain/dist/document_loaders/base'; import type { BaseDocumentLoader } from 'langchain/dist/document_loaders/base';
import type { IExecuteFunctions, INodeExecutionData, ISupplyDataFunctions } from 'n8n-workflow'; import type {
IExecuteFunctions,
INodeExecutionData,
ISupplyDataFunctions,
ITaskMetadata,
} from 'n8n-workflow';
import { NodeOperationError, NodeConnectionType } from 'n8n-workflow'; import { NodeOperationError, NodeConnectionType } from 'n8n-workflow';
import { logAiEvent, isToolsInstance, isBaseChatMemory, isBaseChatMessageHistory } from './helpers'; import { logAiEvent, isToolsInstance, isBaseChatMemory, isBaseChatMessageHistory } from './helpers';
@ -223,11 +228,21 @@ export function logWrapper(
const executionId: string | undefined = response[0]?.metadata?.executionId as string; const executionId: string | undefined = response[0]?.metadata?.executionId as string;
const workflowId: string | undefined = response[0]?.metadata?.workflowId as string; const workflowId: string | undefined = response[0]?.metadata?.workflowId as string;
const metadata: ITaskMetadata = {};
if (executionId && workflowId) {
metadata.subExecution = {
executionId,
workflowId,
};
}
logAiEvent(executeFunctions, 'ai-documents-retrieved', { query }); logAiEvent(executeFunctions, 'ai-documents-retrieved', { query });
executeFunctions.addOutputData(connectionType, index, [[{ json: { response } }]], { executeFunctions.addOutputData(
executionId, connectionType,
workflowId, index,
}); [[{ json: { response } }]],
metadata,
);
return response; return response;
}; };
} }

View file

@ -33,7 +33,6 @@ import get from 'lodash/get';
import isEmpty from 'lodash/isEmpty'; import isEmpty from 'lodash/isEmpty';
import merge from 'lodash/merge'; import merge from 'lodash/merge';
import pick from 'lodash/pick'; import pick from 'lodash/pick';
import set from 'lodash/set';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { extension, lookup } from 'mime-types'; import { extension, lookup } from 'mime-types';
import type { import type {
@ -3583,13 +3582,6 @@ export function getExecuteTriggerFunctions(
return new TriggerContext(workflow, node, additionalData, mode, activation); return new TriggerContext(workflow, node, additionalData, mode, activation);
} }
function setMetadata(executeData: IExecuteData, key: string, value: string) {
if (!executeData.metadata) {
executeData.metadata = {};
}
set(executeData.metadata, key, value);
}
/** /**
* Returns the execute functions regular nodes have access to. * Returns the execute functions regular nodes have access to.
*/ */
@ -3625,8 +3617,11 @@ export function getExecuteFunctions(
itemIndex, itemIndex,
), ),
getExecuteData: () => executeData, getExecuteData: () => executeData,
setMetadata: (key: string, value: string): void => { setMetadata: (metadata: ITaskMetadata): void => {
return setMetadata(executeData, key, value); executeData.metadata = {
...(executeData.metadata ?? {}),
...metadata,
};
}, },
continueOnFail: () => { continueOnFail: () => {
return continueOnFail(node); return continueOnFail(node);

View file

@ -1,4 +1,3 @@
import set from 'lodash/set';
import type { import type {
ICredentialDataDecryptedObject, ICredentialDataDecryptedObject,
IGetNodeParameterOptions, IGetNodeParameterOptions,
@ -14,6 +13,7 @@ import type {
ContextType, ContextType,
AiEvent, AiEvent,
ISourceData, ISourceData,
ITaskMetadata,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { import {
ApplicationError, ApplicationError,
@ -37,14 +37,6 @@ import {
import { NodeExecutionContext } from './node-execution-context'; import { NodeExecutionContext } from './node-execution-context';
// todo simplify
function setMetadata(executeData: IExecuteData, key: string, value: string) {
if (!executeData.metadata) {
executeData.metadata = {};
}
set(executeData.metadata, key, value);
}
export class ExecuteSingleContext extends NodeExecutionContext implements IExecuteSingleFunctions { export class ExecuteSingleContext extends NodeExecutionContext implements IExecuteSingleFunctions {
readonly helpers: IExecuteSingleFunctions['helpers']; readonly helpers: IExecuteSingleFunctions['helpers'];
@ -94,8 +86,11 @@ export class ExecuteSingleContext extends NodeExecutionContext implements IExecu
this.abortSignal?.addEventListener('abort', fn); this.abortSignal?.addEventListener('abort', fn);
} }
setMetadata(key: string, value: string): void { setMetadata(metadata: ITaskMetadata): void {
return setMetadata(this.executeData, key, value); this.executeData.metadata = {
...(this.executeData.metadata ?? {}),
...metadata,
};
} }
continueOnFail() { continueOnFail() {

View file

@ -182,7 +182,10 @@ export interface IAiDataContent {
metadata: { metadata: {
executionTime: number; executionTime: number;
startTime: number; startTime: number;
executionId?: string; subExecution?: {
workflowId: string;
executionId: string;
};
}; };
} }

View file

@ -9,6 +9,7 @@ import type {
INodeOutputConfiguration, INodeOutputConfiguration,
IRunData, IRunData,
IRunExecutionData, IRunExecutionData,
ITaskMetadata,
NodeError, NodeError,
NodeHint, NodeHint,
Workflow, Workflow,
@ -508,18 +509,17 @@ const pinButtonDisabled = computed(
readOnlyEnv.value, readOnlyEnv.value,
); );
const subWorkflowData = computed((): { executionId: string; workflowId?: string } | null => { const subWorkflowData = computed((): ITaskMetadata | null => {
if (!node.value) { if (!node.value) {
return null; return null;
} }
const metadata = get(workflowRunData.value, [node.value.name, props.runIndex, 'metadata'], null); const metadata = get(workflowRunData.value, [node.value.name, props.runIndex, 'metadata'], null);
if (metadata?.executionId) { console.log('yo', metadata);
return { if (!metadata?.parentExecution && !metadata?.subExecution) {
executionId: metadata?.executionId, return null;
workflowId: metadata?.workflowId,
};
} }
return null;
return metadata;
}); });
const hasInputOverwrite = computed((): boolean => { const hasInputOverwrite = computed((): boolean => {
@ -1210,17 +1210,16 @@ function onSearchClear() {
document.dispatchEvent(new KeyboardEvent('keyup', { key: '/' })); document.dispatchEvent(new KeyboardEvent('keyup', { key: '/' }));
} }
function onOpenRelatedExecution(executionId: string, workflowId?: string) { function onOpenRelatedExecution({ parentExecution, subExecution }: ITaskMetadata) {
if (!nodeType.value) { const info = parentExecution || subExecution;
if (!info) {
return; return;
} }
openExecutionInNewTab(executionId, workflowId); openExecutionInNewTab(info.executionId, info.workflowId);
// todo better distinguish these two
const isTrigger = nodeType.value.group.includes('trigger');
telemetry.track( telemetry.track(
isTrigger ? 'User clicked parent execution button' : 'User clicked inspect sub-workflow', parentExecution ? 'User clicked parent execution button' : 'User clicked inspect sub-workflow',
{ {
view: displayMode.value, view: displayMode.value,
}, },
@ -1455,14 +1454,10 @@ defineExpose({ enterEditMode });
v-if="subWorkflowData && !(paneType === 'input' && hasInputOverwrite)" v-if="subWorkflowData && !(paneType === 'input' && hasInputOverwrite)"
:class="$style.parentExecutionInfo" :class="$style.parentExecutionInfo"
> >
<a <a @click.stop="onOpenRelatedExecution(subWorkflowData)">
@click.stop="
onOpenRelatedExecution(subWorkflowData.executionId, subWorkflowData.workflowId)
"
>
<N8nIcon icon="external-link-alt" size="xsmall" /> <N8nIcon icon="external-link-alt" size="xsmall" />
{{ {{
nodeType?.group.includes('trigger') subWorkflowData.parentExecution
? $locale.baseText('runData.openParentExecution') ? $locale.baseText('runData.openParentExecution')
: $locale.baseText('runData.openSubExecution') : $locale.baseText('runData.openSubExecution')
}} }}

View file

@ -61,7 +61,7 @@ function getReferencedData(
metadata: { metadata: {
executionTime: taskData.executionTime, executionTime: taskData.executionTime,
startTime: taskData.startTime, startTime: taskData.startTime,
executionId: taskData.metadata?.executionId, subExecution: taskData.metadata?.subExecution,
}, },
}); });
}); });

View file

@ -20,7 +20,10 @@ interface RunMeta {
node: INodeTypeDescription | null; node: INodeTypeDescription | null;
type: 'input' | 'output'; type: 'input' | 'output';
connectionType: NodeConnectionType; connectionType: NodeConnectionType;
executionId?: string; subExecution?: {
workflowId: string;
executionId: string;
};
} }
const props = defineProps<{ const props = defineProps<{
inputData: IAiData; inputData: IAiData;
@ -82,7 +85,7 @@ function extractRunMeta(run: IAiDataContent) {
node: nodeType, node: nodeType,
type: run.inOut, type: run.inOut,
connectionType: run.type, connectionType: run.type,
executionId: run.metadata.executionId, subExecution: run.metadata?.subExecution,
}; };
return runMeta; return runMeta;
@ -110,13 +113,8 @@ const outputError = computed(() => {
}); });
// todo unify function across components // todo unify function across components
function openExecution({ executionId }: RunMeta) { function openExecution({ executionId, workflowId }: { workflowId: string; executionId: string }) {
if (!executionId) { openExecutionInNewTab(executionId, workflowId);
return;
}
// todo add workflow id
openExecutionInNewTab(executionId);
telemetry.track('User clicked inspect sub-workflow', { telemetry.track('User clicked inspect sub-workflow', {
view: 'ai', view: 'ai',
@ -153,8 +151,8 @@ function openExecution({ executionId }: RunMeta) {
}} }}
</n8n-tooltip> </n8n-tooltip>
</li> </li>
<li v-if="runMeta?.executionId"> <li v-if="runMeta?.subExecution">
<a @click.stop="openExecution(runMeta)"> <a @click.stop="openExecution(runMeta.subExecution)">
<N8nIcon icon="external-link-alt" size="xsmall" /> <N8nIcon icon="external-link-alt" size="xsmall" />
{{ $locale.baseText('runData.openSubExecution') }} {{ $locale.baseText('runData.openSubExecution') }}
</a> </a>

View file

@ -231,8 +231,10 @@ export class ExecuteWorkflow implements INodeType {
undefined, undefined,
{ {
startMetadata: { startMetadata: {
executionId: workflowProxy.$execution.id, parentExecution: {
workflowId: workflowProxy.$workflow.id, executionId: workflowProxy.$execution.id,
workflowId: workflowProxy.$workflow.id,
},
}, },
}, },
); );
@ -261,8 +263,10 @@ export class ExecuteWorkflow implements INodeType {
{ {
doNotWaitToFinish: true, doNotWaitToFinish: true,
startMetadata: { startMetadata: {
executionId: workflowProxy.$execution.id, parentExecution: {
workflowId: workflowProxy.$workflow.id, executionId: workflowProxy.$execution.id,
workflowId: workflowProxy.$workflow.id,
},
}, },
}, },
); );
@ -311,16 +315,20 @@ export class ExecuteWorkflow implements INodeType {
{ {
doNotWaitToFinish: !waitForSubWorkflow, doNotWaitToFinish: !waitForSubWorkflow,
startMetadata: { startMetadata: {
executionId: workflowProxy.$execution.id, parentExecution: {
workflowId: workflowProxy.$workflow.id, executionId: workflowProxy.$execution.id,
workflowId: workflowProxy.$workflow.id,
},
}, },
}, },
); );
this.setMetadata('executionId', executionResult.executionId); this.setMetadata({
if (workflowInfo.id !== undefined) { subExecution: {
this.setMetadata('workflowId', workflowInfo.id); executionId: executionResult.executionId,
} workflowId: workflowInfo.id ?? (workflowProxy.$workflow.id as string),
},
});
if (!waitForSubWorkflow) { if (!waitForSubWorkflow) {
return [items]; return [items];

View file

@ -936,7 +936,7 @@ export type ContextType = 'flow' | 'node';
type BaseExecutionFunctions = FunctionsBaseWithRequiredKeys<'getMode'> & { type BaseExecutionFunctions = FunctionsBaseWithRequiredKeys<'getMode'> & {
continueOnFail(): boolean; continueOnFail(): boolean;
setMetadata(key: string, value: string): void; setMetadata(metadata: ITaskMetadata): void;
evaluateExpression(expression: string, itemIndex: number): NodeParameterValueType; evaluateExpression(expression: string, itemIndex: number): NodeParameterValueType;
getContext(type: ContextType): IContextObject; getContext(type: ContextType): IContextObject;
getExecuteData(): IExecuteData; getExecuteData(): IExecuteData;
@ -2143,8 +2143,14 @@ export interface ITaskSubRunMetadata {
} }
export interface ITaskMetadata { export interface ITaskMetadata {
executionId?: string; parentExecution?: {
workflowId?: string; executionId: string;
workflowId: string;
};
subExecution?: {
executionId: string;
workflowId: string;
};
subRun?: ITaskSubRunMetadata[]; subRun?: ITaskSubRunMetadata[];
} }