feat(core): Expose $evaluation.runId expression global in eval executions (#32843)

This commit is contained in:
Benjamin Schroth
2026-06-24 12:57:29 +00:00
committed by GitHub
parent 30fd834c5b
commit 52e7df6b59
14 changed files with 41 additions and 2 deletions
@@ -284,6 +284,7 @@ describe('BuiltInsParser', () => {
'Interval',
'Duration',
'$execution',
'$evaluation',
'$vars',
'$secrets',
'$executionId',
@@ -99,6 +99,8 @@ export interface PartialAdditionalData {
executionTimeoutTimestamp?: number;
userId?: string;
variables: IDataObject;
/** Parent evaluation TestRun.id, exposed to Code nodes as `$evaluation.runId`. */
evaluationRunId?: string;
}
/** RPC methods that are exposed directly to the Code Node */
@@ -783,6 +783,7 @@ describe('TestRunnerService', () => {
},
userId: metadata.userId,
forceFullExecutionData: true,
evaluationRunId: metadata.testRunId,
triggerToStartFrom: {
name: triggerNodeName,
},
@@ -931,6 +932,7 @@ describe('TestRunnerService', () => {
},
},
userId: metadata.userId,
evaluationRunId: metadata.testRunId,
triggerToStartFrom: {
name: triggerNodeName,
},
@@ -945,6 +947,7 @@ describe('TestRunnerService', () => {
},
manualData: {
userId: metadata.userId,
evaluationRunId: metadata.testRunId,
triggerToStartFrom: {
name: triggerNodeName,
},
@@ -290,6 +290,7 @@ export class TestRunnerService {
},
},
userId: metadata.userId,
evaluationRunId: metadata.testRunId,
triggerToStartFrom: {
name: triggerNode.name,
},
@@ -305,6 +306,7 @@ export class TestRunnerService {
},
manualData: {
userId: metadata.userId,
evaluationRunId: metadata.testRunId,
triggerToStartFrom: {
name: triggerNode.name,
},
@@ -165,6 +165,7 @@ export class JobProcessor {
});
additionalData.streamingEnabled = job.data.streamingEnabled;
additionalData.restartExecutionId = job.data.restartExecutionId;
additionalData.evaluationRunId = execution.data.manualData?.evaluationRunId;
const { pushRef } = job.data;
@@ -24,6 +24,7 @@ const additionalData = mock<PartialAdditionalData>({
currentNodeParameters: undefined,
executionTimeoutTimestamp: undefined,
restartExecutionId: undefined,
evaluationRunId: 'test-run-id-123',
});
const node = mock<INode>();
@@ -108,6 +109,7 @@ describe('DataRequestResponseBuilder', () => {
currentNodeParameters: undefined,
executionTimeoutTimestamp: undefined,
restartExecutionId: undefined,
evaluationRunId: 'test-run-id-123',
});
});
@@ -50,6 +50,7 @@ export class DataRequestResponseBuilder {
executionTimeoutTimestamp: additionalData.executionTimeoutTimestamp,
restartExecutionId: additionalData.restartExecutionId,
userId: additionalData.userId,
evaluationRunId: additionalData.evaluationRunId,
};
}
@@ -521,6 +521,8 @@ async function startExecution(
// mode (e.g. 'manual') even though their own WorkflowExecute runs as 'integrated'
additionalDataIntegrated.rootExecutionMode =
additionalData.rootExecutionMode ?? options.executionMode;
// Propagate the eval run id so sub-workflows of an eval run expose `$evaluation.runId`
additionalDataIntegrated.evaluationRunId = additionalData.evaluationRunId;
if (additionalData.httpResponse) {
additionalDataIntegrated.httpResponse = additionalData.httpResponse;
}
+1
View File
@@ -378,6 +378,7 @@ export class WorkflowRunner {
additionalData.encryptedRunnerIdentity = data.encryptedRunnerIdentity;
additionalData.executionId = executionId;
additionalData.evaluationRunId = data.evaluationRunId;
this.logger.debug(
`Execution for workflow ${data.workflowData.name} was assigned id ${executionId}`,
@@ -156,6 +156,20 @@ describe('getAdditionalKeys', () => {
expect(result.$execution?.customData).toBeUndefined();
});
it('should expose $evaluation.runId when evaluationRunId is set', () => {
const dataWithRunId = { ...additionalData, evaluationRunId: 'run-123' };
const result = getAdditionalKeys(dataWithRunId, 'manual', null);
expect(result.$evaluation).toEqual({ runId: 'run-123' });
});
it('should leave $evaluation undefined when evaluationRunId is unset', () => {
const dataWithoutRunId = { ...additionalData, evaluationRunId: undefined };
const result = getAdditionalKeys(dataWithoutRunId, 'manual', null);
expect(result.$evaluation).toBeUndefined();
});
it('should respect metadata KV limit', () => {
const result = getAdditionalKeys(additionalData, 'manual', runExecutionData);
const customData = result.$execution?.customData;
@@ -41,6 +41,11 @@ export function getAdditionalKeys(
? createExecutionCustomData({ runExecutionData, mode })
: undefined,
},
// Gated on the value's presence, not on `mode`: sub-workflows of an eval run
// execute as 'integrated' but inherit evaluationRunId, so they must expose it too.
$evaluation: additionalData.evaluationRunId
? { runId: additionalData.evaluationRunId }
: undefined,
$vars: additionalData.variables,
$secrets: options.isCredential ? getSecretsProxy(additionalData) : undefined,
+5
View File
@@ -2820,6 +2820,7 @@ export type IWorkflowDataProxyAdditionalKeys = IDataObject & {
resumeFormUrl: string;
customData?: IWorkflowExecutionCustomData;
};
$evaluation?: { runId: string };
$vars?: IDataObject;
$secrets?: IDataObject;
$pageCount?: number;
@@ -3266,6 +3267,8 @@ export interface IWorkflowExecutionDataProcess {
tracingContext?: { traceparent: string; tracestate?: string };
/** Encrypted credential context for a manual editor-triggered execution. */
encryptedRunnerIdentity?: string;
/** Parent evaluation TestRun.id, exposed to expressions as `$evaluation.runId`. */
evaluationRunId?: string;
}
export interface ExecuteWorkflowOptions {
@@ -3375,6 +3378,8 @@ export interface IWorkflowExecuteAdditionalData {
* data consistently across the entire execution tree.
*/
rootExecutionMode?: WorkflowExecuteMode;
/** Parent evaluation TestRun.id, exposed to expressions as `$evaluation.runId`. */
evaluationRunId?: string;
startRunnerTask<T, E = unknown>(
additionalData: IWorkflowExecuteAdditionalData,
jobType: string,
@@ -51,6 +51,6 @@ export interface IRunExecutionDataV0 {
/** Data needed for a worker to run a manual execution. */
manualData?: Pick<
IWorkflowExecutionDataProcess,
'dirtyNodeNames' | 'triggerToStartFrom' | 'userId'
'dirtyNodeNames' | 'triggerToStartFrom' | 'userId' | 'evaluationRunId'
>;
}
@@ -72,7 +72,7 @@ export interface IRunExecutionDataV1 {
/** Data needed for a worker to run a manual execution. */
manualData?: Pick<
IWorkflowExecutionDataProcess,
'dirtyNodeNames' | 'triggerToStartFrom' | 'userId'
'dirtyNodeNames' | 'triggerToStartFrom' | 'userId' | 'evaluationRunId'
>;
/** Metadata about whether and how this execution's data was redacted. */