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 };
@ -395,8 +398,10 @@ export class RetrieverWorkflow implements INodeType {
config?.getChild(),
{
startMetadata: {
executionId: workflowProxy.$execution.id,
workflowId: workflowProxy.$workflow.id,
parentExecution: {
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> {
const workflowProxy = this.getWorkflowDataProxy(0);
const name = this.getNodeParameter('name', 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;
let tool: DynamicTool | DynamicStructuredTool | undefined = undefined;
@ -399,11 +403,16 @@ export class ToolWorkflow implements INodeType {
) as INodeParameterResourceLocator;
workflowInfo.id = value as string;
}
subWorkflowId = workflowInfo.id;
} else if (source === 'parameter') {
// Read workflow from parameter
const workflowJson = this.getNodeParameter('workflowJson', itemIndex) as string;
try {
workflowInfo.code = JSON.parse(workflowJson) as IWorkflowBase;
// subworkflow is same as parent workflow
subWorkflowId = workflowProxy.$workflow.id;
} catch (error) {
throw new NodeOperationError(
this.getNode(),
@ -443,17 +452,17 @@ export class ToolWorkflow implements INodeType {
const items = [newItem] as INodeExecutionData[];
const workflowProxy = this.getWorkflowDataProxy(0);
let receivedData: ExecuteWorkflowData;
try {
receivedData = await this.executeWorkflow(workflowInfo, items, runManager?.getChild(), {
startMetadata: {
executionId: workflowProxy.$execution.id,
workflowId: workflowProxy.$workflow.id,
parentExecution: {
executionId: workflowProxy.$execution.id,
workflowId: workflowProxy.$workflow.id,
},
},
});
executionId = receivedData.executionId;
subExecutionId = receivedData.executionId;
} catch (error) {
// Make sure a valid error gets returned that can by json-serialized else it will
// not show up in the frontend
@ -512,9 +521,12 @@ export class ToolWorkflow implements INodeType {
}
let metadata: ITaskMetadata | undefined;
if (executionId) {
if (subExecutionId && subWorkflowId) {
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 { TextSplitter } from '@langchain/textsplitters';
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 { logAiEvent, isToolsInstance, isBaseChatMemory, isBaseChatMessageHistory } from './helpers';
@ -223,11 +228,21 @@ export function logWrapper(
const executionId: string | undefined = response[0]?.metadata?.executionId 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 });
executeFunctions.addOutputData(connectionType, index, [[{ json: { response } }]], {
executionId,
workflowId,
});
executeFunctions.addOutputData(
connectionType,
index,
[[{ json: { response } }]],
metadata,
);
return response;
};
}

View file

@ -33,7 +33,6 @@ import get from 'lodash/get';
import isEmpty from 'lodash/isEmpty';
import merge from 'lodash/merge';
import pick from 'lodash/pick';
import set from 'lodash/set';
import { DateTime } from 'luxon';
import { extension, lookup } from 'mime-types';
import type {
@ -3583,13 +3582,6 @@ export function getExecuteTriggerFunctions(
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.
*/
@ -3625,8 +3617,11 @@ export function getExecuteFunctions(
itemIndex,
),
getExecuteData: () => executeData,
setMetadata: (key: string, value: string): void => {
return setMetadata(executeData, key, value);
setMetadata: (metadata: ITaskMetadata): void => {
executeData.metadata = {
...(executeData.metadata ?? {}),
...metadata,
};
},
continueOnFail: () => {
return continueOnFail(node);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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