@exellix/graph-engine 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +3 -0
- package/CHANGELOG.md +208 -0
- package/README.md +827 -0
- package/dist/src/errors/ExellixGraphError.d.ts +38 -0
- package/dist/src/errors/ExellixGraphError.js +21 -0
- package/dist/src/errors/exellixGraphErrorCodes.d.ts +31 -0
- package/dist/src/errors/exellixGraphErrorCodes.js +32 -0
- package/dist/src/index.d.ts +100 -0
- package/dist/src/index.js +75 -0
- package/dist/src/inspection/contractInspection.d.ts +21 -0
- package/dist/src/inspection/contractInspection.js +526 -0
- package/dist/src/inspection/contractTypes.d.ts +137 -0
- package/dist/src/inspection/contractTypes.js +1 -0
- package/dist/src/inspection/controlInspection.d.ts +22 -0
- package/dist/src/inspection/controlInspection.js +130 -0
- package/dist/src/inspection/graphInspection.d.ts +51 -0
- package/dist/src/inspection/graphInspection.js +467 -0
- package/dist/src/inspection/index.d.ts +21 -0
- package/dist/src/inspection/index.js +17 -0
- package/dist/src/inspection/nodeInspection.d.ts +42 -0
- package/dist/src/inspection/nodeInspection.js +474 -0
- package/dist/src/inspection/types.d.ts +321 -0
- package/dist/src/inspection/types.js +14 -0
- package/dist/src/inspection/validateAiTasksNodeExtensions.d.ts +12 -0
- package/dist/src/inspection/validateAiTasksNodeExtensions.js +119 -0
- package/dist/src/inspection/validateCatalogPlanning.d.ts +21 -0
- package/dist/src/inspection/validateCatalogPlanning.js +187 -0
- package/dist/src/integrations/ActivityTrackerIntegration.d.ts +86 -0
- package/dist/src/integrations/ActivityTrackerIntegration.js +134 -0
- package/dist/src/integrations/ActivixGraphRunIntegration.d.ts +34 -0
- package/dist/src/integrations/ActivixGraphRunIntegration.js +338 -0
- package/dist/src/integrations/ActivixNodeActivityIntegration.d.ts +33 -0
- package/dist/src/integrations/ActivixNodeActivityIntegration.js +220 -0
- package/dist/src/integrations/cataloxGraphCatalog.d.ts +21 -0
- package/dist/src/integrations/cataloxGraphCatalog.js +30 -0
- package/dist/src/integrations/createActivixExellixIntegration.d.ts +14 -0
- package/dist/src/integrations/createActivixExellixIntegration.js +16 -0
- package/dist/src/integrations/createActivixFromEnv.d.ts +31 -0
- package/dist/src/integrations/createActivixFromEnv.js +53 -0
- package/dist/src/loaders/FileGraphLoader.d.ts +23 -0
- package/dist/src/loaders/FileGraphLoader.js +31 -0
- package/dist/src/playground/PlaygroundReporter.d.ts +40 -0
- package/dist/src/playground/PlaygroundReporter.js +480 -0
- package/dist/src/playground/index.d.ts +1 -0
- package/dist/src/playground/index.js +1 -0
- package/dist/src/runtime/ExellixGraphRuntime.d.ts +263 -0
- package/dist/src/runtime/ExellixGraphRuntime.js +1716 -0
- package/dist/src/runtime/GraphEngine.d.ts +33 -0
- package/dist/src/runtime/GraphEngine.js +4 -0
- package/dist/src/runtime/aiTasksObservability.d.ts +6 -0
- package/dist/src/runtime/aiTasksObservability.js +37 -0
- package/dist/src/runtime/aiTasksStrategyPhases.d.ts +46 -0
- package/dist/src/runtime/aiTasksStrategyPhases.js +93 -0
- package/dist/src/runtime/applyAiTaskProfileWebScopingToNarrix.d.ts +17 -0
- package/dist/src/runtime/applyAiTaskProfileWebScopingToNarrix.js +46 -0
- package/dist/src/runtime/buildAiTasksRunTaskRequest.d.ts +67 -0
- package/dist/src/runtime/buildAiTasksRunTaskRequest.js +164 -0
- package/dist/src/runtime/buildRunLog.d.ts +27 -0
- package/dist/src/runtime/buildRunLog.js +234 -0
- package/dist/src/runtime/buildRunTaskTaskConfigurationForward.d.ts +9 -0
- package/dist/src/runtime/buildRunTaskTaskConfigurationForward.js +80 -0
- package/dist/src/runtime/buildTaskNodeJobContext.d.ts +11 -0
- package/dist/src/runtime/buildTaskNodeJobContext.js +30 -0
- package/dist/src/runtime/canonicalModelUsed.d.ts +6 -0
- package/dist/src/runtime/canonicalModelUsed.js +36 -0
- package/dist/src/runtime/contextualScope.d.ts +7 -0
- package/dist/src/runtime/contextualScope.js +121 -0
- package/dist/src/runtime/dataFiltersEvaluation.d.ts +60 -0
- package/dist/src/runtime/dataFiltersEvaluation.js +169 -0
- package/dist/src/runtime/deepMerge.d.ts +5 -0
- package/dist/src/runtime/deepMerge.js +22 -0
- package/dist/src/runtime/events.d.ts +92 -0
- package/dist/src/runtime/events.js +122 -0
- package/dist/src/runtime/executionMatrixHost.d.ts +98 -0
- package/dist/src/runtime/executionMatrixHost.js +134 -0
- package/dist/src/runtime/executionVariableBuckets.d.ts +67 -0
- package/dist/src/runtime/executionVariableBuckets.js +96 -0
- package/dist/src/runtime/finalizers/errors.d.ts +9 -0
- package/dist/src/runtime/finalizers/errors.js +10 -0
- package/dist/src/runtime/finalizers/executeFinalizer.d.ts +40 -0
- package/dist/src/runtime/finalizers/executeFinalizer.js +471 -0
- package/dist/src/runtime/finalizers/schema.d.ts +18 -0
- package/dist/src/runtime/finalizers/schema.js +63 -0
- package/dist/src/runtime/finalizers/validateFinalizer.d.ts +16 -0
- package/dist/src/runtime/finalizers/validateFinalizer.js +534 -0
- package/dist/src/runtime/graphDocumentFingerprint.d.ts +8 -0
- package/dist/src/runtime/graphDocumentFingerprint.js +21 -0
- package/dist/src/runtime/graphEngineMemoryPaths.d.ts +12 -0
- package/dist/src/runtime/graphEngineMemoryPaths.js +55 -0
- package/dist/src/runtime/graphResponseMapping.d.ts +23 -0
- package/dist/src/runtime/graphResponseMapping.js +156 -0
- package/dist/src/runtime/graphResponseMigration.d.ts +7 -0
- package/dist/src/runtime/graphResponseMigration.js +44 -0
- package/dist/src/runtime/graphRunExecutionSeed.d.ts +29 -0
- package/dist/src/runtime/graphRunExecutionSeed.js +61 -0
- package/dist/src/runtime/graphRunIdentity.d.ts +7 -0
- package/dist/src/runtime/graphRunIdentity.js +18 -0
- package/dist/src/runtime/localSkills/deterministicRule.d.ts +137 -0
- package/dist/src/runtime/localSkills/deterministicRule.js +196 -0
- package/dist/src/runtime/localSkills/index.d.ts +12 -0
- package/dist/src/runtime/localSkills/index.js +14 -0
- package/dist/src/runtime/localSkills/memorixItemToScopedOutput.d.ts +7 -0
- package/dist/src/runtime/localSkills/memorixItemToScopedOutput.js +104 -0
- package/dist/src/runtime/localSkills/memorixRuntime.d.ts +9 -0
- package/dist/src/runtime/localSkills/memorixRuntime.js +70 -0
- package/dist/src/runtime/localSkills/memorixScopedConfig.d.ts +16 -0
- package/dist/src/runtime/localSkills/memorixScopedConfig.js +18 -0
- package/dist/src/runtime/localSkills/scopedAnswerAssembler.d.ts +23 -0
- package/dist/src/runtime/localSkills/scopedAnswerAssembler.js +35 -0
- package/dist/src/runtime/localSkills/scopedAnswerFields.d.ts +12 -0
- package/dist/src/runtime/localSkills/scopedAnswerFields.js +66 -0
- package/dist/src/runtime/localSkills/scopedAnswerWriter.d.ts +32 -0
- package/dist/src/runtime/localSkills/scopedAnswerWriter.js +156 -0
- package/dist/src/runtime/localSkills/scopedDataReader.d.ts +47 -0
- package/dist/src/runtime/localSkills/scopedDataReader.js +89 -0
- package/dist/src/runtime/localSkills/utils.d.ts +12 -0
- package/dist/src/runtime/localSkills/utils.js +39 -0
- package/dist/src/runtime/materializeStructuredRunTaskInput.d.ts +9 -0
- package/dist/src/runtime/materializeStructuredRunTaskInput.js +34 -0
- package/dist/src/runtime/memory.d.ts +51 -0
- package/dist/src/runtime/memory.js +250 -0
- package/dist/src/runtime/mergeExellixGraphRuntimeInvocation.d.ts +18 -0
- package/dist/src/runtime/mergeExellixGraphRuntimeInvocation.js +32 -0
- package/dist/src/runtime/modelConfigSelection.d.ts +7 -0
- package/dist/src/runtime/modelConfigSelection.js +37 -0
- package/dist/src/runtime/narrixIngestEnv.d.ts +9 -0
- package/dist/src/runtime/narrixIngestEnv.js +18 -0
- package/dist/src/runtime/pathExpr.d.ts +36 -0
- package/dist/src/runtime/pathExpr.js +131 -0
- package/dist/src/runtime/predicates.d.ts +14 -0
- package/dist/src/runtime/predicates.js +86 -0
- package/dist/src/runtime/readTaskNodeInputsConfig.d.ts +23 -0
- package/dist/src/runtime/readTaskNodeInputsConfig.js +27 -0
- package/dist/src/runtime/resolveExecutionPipelineForTaskNode.d.ts +11 -0
- package/dist/src/runtime/resolveExecutionPipelineForTaskNode.js +93 -0
- package/dist/src/runtime/resolveGraphEngineMemoryPaths.d.ts +63 -0
- package/dist/src/runtime/resolveGraphEngineMemoryPaths.js +213 -0
- package/dist/src/runtime/resolveModelConfigForNode.d.ts +20 -0
- package/dist/src/runtime/resolveModelConfigForNode.js +69 -0
- package/dist/src/runtime/resolveNarrixForTaskNode.d.ts +14 -0
- package/dist/src/runtime/resolveNarrixForTaskNode.js +19 -0
- package/dist/src/runtime/resolveTaskKey.d.ts +11 -0
- package/dist/src/runtime/resolveTaskKey.js +28 -0
- package/dist/src/runtime/resolveTaskNodeInputs.d.ts +25 -0
- package/dist/src/runtime/resolveTaskNodeInputs.js +140 -0
- package/dist/src/runtime/runTaskAugments.d.ts +17 -0
- package/dist/src/runtime/runTaskAugments.js +37 -0
- package/dist/src/runtime/runTaskResponse.d.ts +4 -0
- package/dist/src/runtime/runTaskResponse.js +13 -0
- package/dist/src/runtime/runtimeObjects.d.ts +85 -0
- package/dist/src/runtime/runtimeObjects.js +50 -0
- package/dist/src/runtime/smartInputPaths.d.ts +13 -0
- package/dist/src/runtime/smartInputPaths.js +38 -0
- package/dist/src/runtime/stepRetry.d.ts +21 -0
- package/dist/src/runtime/stepRetry.js +238 -0
- package/dist/src/runtime/synthesizedContextPipeline.d.ts +12 -0
- package/dist/src/runtime/synthesizedContextPipeline.js +28 -0
- package/dist/src/runtime/taskNodeConditionsEvaluation.d.ts +27 -0
- package/dist/src/runtime/taskNodeConditionsEvaluation.js +140 -0
- package/dist/src/runtime/taskNodeMainReadiness.d.ts +45 -0
- package/dist/src/runtime/taskNodeMainReadiness.js +164 -0
- package/dist/src/runtime/taskNodeRunTaskPreflight.d.ts +89 -0
- package/dist/src/runtime/taskNodeRunTaskPreflight.js +204 -0
- package/dist/src/runtime/validateCanonicalGraphDocument.d.ts +25 -0
- package/dist/src/runtime/validateCanonicalGraphDocument.js +567 -0
- package/dist/src/runtime/variables.d.ts +2 -0
- package/dist/src/runtime/variables.js +1 -0
- package/dist/src/runtime/withTimeout.d.ts +5 -0
- package/dist/src/runtime/withTimeout.js +20 -0
- package/dist/src/types/aiTaskProfile.d.ts +41 -0
- package/dist/src/types/aiTaskProfile.js +6 -0
- package/dist/src/types/aiTasksDerivedTypes.d.ts +5 -0
- package/dist/src/types/aiTasksDerivedTypes.js +1 -0
- package/dist/src/types/events.d.ts +23 -0
- package/dist/src/types/events.js +1 -0
- package/dist/src/types/job.d.ts +9 -0
- package/dist/src/types/job.js +1 -0
- package/dist/src/types/narrix.d.ts +60 -0
- package/dist/src/types/narrix.js +1 -0
- package/dist/src/types/options.d.ts +122 -0
- package/dist/src/types/options.js +1 -0
- package/dist/src/types/refs.d.ts +747 -0
- package/dist/src/types/refs.js +12 -0
- package/dist/src/types/results.d.ts +103 -0
- package/dist/src/types/results.js +1 -0
- package/dist/src/types/runLog.d.ts +72 -0
- package/dist/src/types/runLog.js +18 -0
- package/dist/src/types/taskNodeConfiguration.d.ts +95 -0
- package/dist/src/types/taskNodeConfiguration.js +3 -0
- package/dist/src/util/packageVersion.d.ts +2 -0
- package/dist/src/util/packageVersion.js +12 -0
- package/dist/testkit/RealTasksClient.d.ts +16 -0
- package/dist/testkit/RealTasksClient.js +143 -0
- package/dist/testkit/depGraphEngineFactory.d.ts +6 -0
- package/dist/testkit/depGraphEngineFactory.js +54 -0
- package/dist/testkit/exellixRuntimeObjects.d.ts +7 -0
- package/dist/testkit/exellixRuntimeObjects.js +25 -0
- package/dist/testkit/inMemoryGraphLoader.d.ts +6 -0
- package/dist/testkit/inMemoryGraphLoader.js +12 -0
- package/dist/testkit/index.d.ts +4 -0
- package/dist/testkit/index.js +4 -0
- package/package.json +70 -0
|
@@ -0,0 +1,1716 @@
|
|
|
1
|
+
import { mergeExecutionObject, copyExecutionContextFields, } from "./memory.js";
|
|
2
|
+
import { buildAiTasksRunTaskRequest, extractRunTaskStrategyOverrides, resolveJobTypeId, } from "./buildAiTasksRunTaskRequest.js";
|
|
3
|
+
import { runTaskResponseSucceeded } from "./runTaskResponse.js";
|
|
4
|
+
import { getAiTaskProfileStrategyKeys, runEngineAiTasksStrategyPhase, } from "./aiTasksStrategyPhases.js";
|
|
5
|
+
import { mergeGraphDocumentModel, EXELLIX_GRAPH_MODEL_VARIABLE_KEY, } from "../types/refs.js";
|
|
6
|
+
import { normalizeSynthesizedContextConfig } from "./synthesizedContextPipeline.js";
|
|
7
|
+
import { resolveExecutionPipelineForTaskNode } from "./resolveExecutionPipelineForTaskNode.js";
|
|
8
|
+
import { resolveTaskNodeInputsForRunTask } from "./resolveTaskNodeInputs.js";
|
|
9
|
+
import { assertAiTasksNodeExtensionsValid } from "../inspection/validateAiTasksNodeExtensions.js";
|
|
10
|
+
import { buildRunTaskMainInput, extractCallerInputsBag } from "./resolveGraphEngineMemoryPaths.js";
|
|
11
|
+
import { mirrorRuntimeInputRawOnExecutionMemory, mirrorStructuredInputOntoExecutionMemory, } from "./materializeStructuredRunTaskInput.js";
|
|
12
|
+
import { buildMainReadinessResolutionRoot, evaluateTaskNodeMainReadiness, resolveMainReadinessPolicy, } from "./taskNodeMainReadiness.js";
|
|
13
|
+
import { mergeXynthesizedPatchIntoExecution, seedGraphRunExecutionState, xynthesizedOutboundForNode, } from "./graphRunExecutionSeed.js";
|
|
14
|
+
import { buildTaskNodeJobContext } from "./buildTaskNodeJobContext.js";
|
|
15
|
+
import { resolveNarrixForTaskNode } from "./resolveNarrixForTaskNode.js";
|
|
16
|
+
import { assertFinalizerRequiredReadsResolvable, validateGraphFinalizer, } from "./finalizers/validateFinalizer.js";
|
|
17
|
+
import { executeDeterministicFinalizer, executeSynthesizeFinalizer } from "./finalizers/executeFinalizer.js";
|
|
18
|
+
import { createFinalizerError } from "./finalizers/errors.js";
|
|
19
|
+
import { assertCanonicalGraphDocument } from "./validateCanonicalGraphDocument.js";
|
|
20
|
+
import { applyGraphResponseDefinition } from "./graphResponseMapping.js";
|
|
21
|
+
import { migrateLegacyGraphResponse } from "./graphResponseMigration.js";
|
|
22
|
+
import { computeGraphDocumentContentSha256 } from "./graphDocumentFingerprint.js";
|
|
23
|
+
import { buildAiTasksObservabilityRecord } from "./aiTasksObservability.js";
|
|
24
|
+
import { buildRunTaskIdentityEnvelope, mergeDefinedLlmCallParts, shouldForwardRunTaskTraceMode, } from "./runTaskAugments.js";
|
|
25
|
+
import { buildRunLog, extractLogxerCorrelationFromMetadata, extractTaskRunLogFromMetadata, resolveRunLogLimits, } from "./buildRunLog.js";
|
|
26
|
+
import { setRuntimeObjectsLastJobId, summarizeRuntimeObjectsForPlayground } from "./runtimeObjects.js";
|
|
27
|
+
import { assertHostJobId, newGraphRunTaskId } from "./graphRunIdentity.js";
|
|
28
|
+
import { resolveTaskKey } from "./resolveTaskKey.js";
|
|
29
|
+
import { buildPredicateEvalContextForNode, mirrorTaskVariablesOnExecution, readExecutionVariableBuckets, seedGraphVariableBucketsFromRuntime, } from "./variables.js";
|
|
30
|
+
import { mergeExellixGraphRuntimeInvocation } from "./mergeExellixGraphRuntimeInvocation.js";
|
|
31
|
+
import { resolveStepRetryPolicy, runRunTaskWithRetry } from "./stepRetry.js";
|
|
32
|
+
import { isLocalSkillKey, runScopedDataReader, runDeterministicRule, runScopedAnswerWriter, runScopedAnswerAssembler, } from "./localSkills/index.js";
|
|
33
|
+
import { evaluateGraphPredicate } from "./predicates.js";
|
|
34
|
+
import { evaluateStructuredDataFilters } from "./dataFiltersEvaluation.js";
|
|
35
|
+
import { evaluateTaskNodeConditions } from "./taskNodeConditionsEvaluation.js";
|
|
36
|
+
import { resolveModelConfigForNode } from "./resolveModelConfigForNode.js";
|
|
37
|
+
import { createRunx } from "@x12i/runx";
|
|
38
|
+
import { selectByPath } from "./pathExpr.js";
|
|
39
|
+
import { createGraphStartEvent, createGraphCompleteEvent, createGraphFailEvent, createNodeStartEvent, createNodeCompleteEvent, createNodeFailEvent, } from "./events.js";
|
|
40
|
+
import { ExellixGraphErrorCode } from "../errors/exellixGraphErrorCodes.js";
|
|
41
|
+
const NODE_LIFECYCLE_EMITTED = "__exellixNodeLifecycleEmitted";
|
|
42
|
+
function skippedByConditionsOutput(skipReason) {
|
|
43
|
+
return Object.freeze({
|
|
44
|
+
__exellixSkipped: true,
|
|
45
|
+
skipReason,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const DEFAULT_CORE_OBJECTIVE_PROPERTY_NAME = "coreObjective";
|
|
49
|
+
const DEFAULT_CORE_OBJECTIVE_SOURCE_PATH = "input.coreObjective";
|
|
50
|
+
const DEFAULT_NODE_RESPONSE_SINGULAR = "nodeResponse";
|
|
51
|
+
const DEFAULT_NODE_RESPONSE_PLURAL = "nodeResponses";
|
|
52
|
+
const SUPPORTED_CORE_OBJECTIVE_SOURCE_ROOTS = new Set([
|
|
53
|
+
"input",
|
|
54
|
+
"execution",
|
|
55
|
+
"variables",
|
|
56
|
+
"jobVariables",
|
|
57
|
+
"taskVariables",
|
|
58
|
+
"jobMemory",
|
|
59
|
+
"taskMemory",
|
|
60
|
+
"job",
|
|
61
|
+
]);
|
|
62
|
+
function markNodeLifecycleEmitted(err) {
|
|
63
|
+
if (err != null && typeof err === "object") {
|
|
64
|
+
err[NODE_LIFECYCLE_EMITTED] = true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function wasNodeLifecycleEmitted(err) {
|
|
68
|
+
return (err != null &&
|
|
69
|
+
typeof err === "object" &&
|
|
70
|
+
err[NODE_LIFECYCLE_EMITTED] === true);
|
|
71
|
+
}
|
|
72
|
+
function taskRunSucceeded(res) {
|
|
73
|
+
return runTaskResponseSucceeded(res);
|
|
74
|
+
}
|
|
75
|
+
function unwrapNodeResponse(output) {
|
|
76
|
+
if (output != null &&
|
|
77
|
+
typeof output === "object" &&
|
|
78
|
+
!Array.isArray(output) &&
|
|
79
|
+
output.parsed !== undefined) {
|
|
80
|
+
return output.parsed;
|
|
81
|
+
}
|
|
82
|
+
return output;
|
|
83
|
+
}
|
|
84
|
+
function resolveNodeResponseKeys(graphExecution) {
|
|
85
|
+
const singular = graphExecution?.nodesResponses?.singular;
|
|
86
|
+
const plural = graphExecution?.nodesResponses?.plural;
|
|
87
|
+
return {
|
|
88
|
+
singular: typeof singular === "string" && singular.length > 0 ? singular : DEFAULT_NODE_RESPONSE_SINGULAR,
|
|
89
|
+
plural: typeof plural === "string" && plural.length > 0 ? plural : DEFAULT_NODE_RESPONSE_PLURAL,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function resolveCoreObjectiveConfig(graphExecution) {
|
|
93
|
+
const propertyName = graphExecution?.coreObjective?.propertyName;
|
|
94
|
+
const sourcePath = graphExecution?.coreObjective?.sourcePath;
|
|
95
|
+
return {
|
|
96
|
+
propertyName: typeof propertyName === "string" && propertyName.length > 0
|
|
97
|
+
? propertyName
|
|
98
|
+
: DEFAULT_CORE_OBJECTIVE_PROPERTY_NAME,
|
|
99
|
+
sourcePath: typeof sourcePath === "string" && sourcePath.length > 0
|
|
100
|
+
? sourcePath
|
|
101
|
+
: DEFAULT_CORE_OBJECTIVE_SOURCE_PATH,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function isSupportedCoreObjectiveSourcePath(path) {
|
|
105
|
+
const root = path.split(".")[0];
|
|
106
|
+
return SUPPORTED_CORE_OBJECTIVE_SOURCE_ROOTS.has(root);
|
|
107
|
+
}
|
|
108
|
+
function resolveCoreObjectiveValue(args) {
|
|
109
|
+
if (!isSupportedCoreObjectiveSourcePath(args.sourcePath))
|
|
110
|
+
return undefined;
|
|
111
|
+
const executionRec = args.execution != null && typeof args.execution === "object" && !Array.isArray(args.execution)
|
|
112
|
+
? args.execution
|
|
113
|
+
: undefined;
|
|
114
|
+
return selectByPath({
|
|
115
|
+
input: executionRec?.input,
|
|
116
|
+
execution: args.execution,
|
|
117
|
+
variables: executionRec?.jobVariables ?? args.variables,
|
|
118
|
+
jobVariables: executionRec?.jobVariables,
|
|
119
|
+
taskVariables: executionRec?.taskVariables,
|
|
120
|
+
jobMemory: args.jobMemory,
|
|
121
|
+
taskMemory: args.taskMemory,
|
|
122
|
+
job: args.job,
|
|
123
|
+
}, args.sourcePath);
|
|
124
|
+
}
|
|
125
|
+
function isPlainRecord(v) {
|
|
126
|
+
return v != null && typeof v === "object" && !Array.isArray(v);
|
|
127
|
+
}
|
|
128
|
+
function cloneJsonLike(value) {
|
|
129
|
+
if (value === undefined || value === null)
|
|
130
|
+
return value;
|
|
131
|
+
return JSON.parse(JSON.stringify(value));
|
|
132
|
+
}
|
|
133
|
+
function jsonValuesEqual(a, b) {
|
|
134
|
+
if (a === b)
|
|
135
|
+
return true;
|
|
136
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
137
|
+
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length)
|
|
138
|
+
return false;
|
|
139
|
+
return a.every((item, index) => jsonValuesEqual(item, b[index]));
|
|
140
|
+
}
|
|
141
|
+
if (isPlainRecord(a) || isPlainRecord(b)) {
|
|
142
|
+
if (!isPlainRecord(a) || !isPlainRecord(b))
|
|
143
|
+
return false;
|
|
144
|
+
const aKeys = Object.keys(a);
|
|
145
|
+
const bKeys = Object.keys(b);
|
|
146
|
+
if (aKeys.length !== bKeys.length)
|
|
147
|
+
return false;
|
|
148
|
+
return aKeys.every((key) => Object.prototype.hasOwnProperty.call(b, key) && jsonValuesEqual(a[key], b[key]));
|
|
149
|
+
}
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
function arrayStartsWith(value, prefix) {
|
|
153
|
+
if (prefix.length > value.length)
|
|
154
|
+
return false;
|
|
155
|
+
return prefix.every((item, index) => jsonValuesEqual(value[index], item));
|
|
156
|
+
}
|
|
157
|
+
function applyExecutionDelta(target, next, base) {
|
|
158
|
+
for (const [key, nextValue] of Object.entries(next)) {
|
|
159
|
+
const baseValue = base[key];
|
|
160
|
+
if (jsonValuesEqual(nextValue, baseValue))
|
|
161
|
+
continue;
|
|
162
|
+
const currentValue = target[key];
|
|
163
|
+
if (isPlainRecord(nextValue) && isPlainRecord(currentValue)) {
|
|
164
|
+
applyExecutionDelta(currentValue, nextValue, isPlainRecord(baseValue) ? baseValue : {});
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (Array.isArray(nextValue) && Array.isArray(baseValue) && Array.isArray(currentValue) && arrayStartsWith(nextValue, baseValue)) {
|
|
168
|
+
target[key] = [...currentValue, ...nextValue.slice(baseValue.length)];
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
target[key] = cloneJsonLike(nextValue);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function mergeExecutionUpdate(current, next, base) {
|
|
175
|
+
if (!isPlainRecord(next))
|
|
176
|
+
return next;
|
|
177
|
+
if (!isPlainRecord(current))
|
|
178
|
+
return cloneJsonLike(next);
|
|
179
|
+
const merged = cloneJsonLike(current);
|
|
180
|
+
applyExecutionDelta(merged, next, isPlainRecord(base) ? base : {});
|
|
181
|
+
return merged;
|
|
182
|
+
}
|
|
183
|
+
function readRuntimeNodeConfig(nodes, nodeId) {
|
|
184
|
+
if (nodes == null || typeof nodeId !== "string" || nodeId.length === 0)
|
|
185
|
+
return undefined;
|
|
186
|
+
const candidate = nodes[nodeId];
|
|
187
|
+
return isPlainRecord(candidate) ? candidate : undefined;
|
|
188
|
+
}
|
|
189
|
+
function mergeAliasConfigs(rootAliases, nodeAliases) {
|
|
190
|
+
const aliases = {};
|
|
191
|
+
for (const source of [rootAliases, nodeAliases]) {
|
|
192
|
+
if (!isPlainRecord(source))
|
|
193
|
+
continue;
|
|
194
|
+
for (const [alias, model] of Object.entries(source)) {
|
|
195
|
+
if (alias.trim() !== "" && typeof model === "string" && model.trim() !== "") {
|
|
196
|
+
aliases[alias] = model;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return Object.keys(aliases).length > 0 ? aliases : undefined;
|
|
201
|
+
}
|
|
202
|
+
function taskNodeNeedsRunxClient(conditions) {
|
|
203
|
+
return conditions?.jsConditionFunction != null || conditions?.aiCondition != null;
|
|
204
|
+
}
|
|
205
|
+
function modelConfigSelectionNeedsRunx(sel) {
|
|
206
|
+
if (sel == null || !Array.isArray(sel.cases))
|
|
207
|
+
return false;
|
|
208
|
+
return sel.cases.some((c) => c.when?.jsConditionFunction != null || c.when?.aiCondition != null);
|
|
209
|
+
}
|
|
210
|
+
function graphNeedsRunxClient(graph, runtimeModelConfig) {
|
|
211
|
+
if (modelConfigSelectionNeedsRunx(runtimeModelConfig))
|
|
212
|
+
return true;
|
|
213
|
+
if (modelConfigSelectionNeedsRunx(graph.modelConfig))
|
|
214
|
+
return true;
|
|
215
|
+
for (const n of graph.nodes ?? []) {
|
|
216
|
+
const tn = n;
|
|
217
|
+
if (taskNodeNeedsRunxClient(tn.conditions))
|
|
218
|
+
return true;
|
|
219
|
+
if (modelConfigSelectionNeedsRunx(tn.taskConfiguration?.modelConfig))
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
function mergeKnowledgePatchIntoRunTaskMemory(memory, patch) {
|
|
225
|
+
if (patch == null)
|
|
226
|
+
return memory;
|
|
227
|
+
const base = isPlainRecord(memory) ? { ...memory } : {};
|
|
228
|
+
const existingKnowledge = isPlainRecord(base.knowledge) ? base.knowledge : {};
|
|
229
|
+
return {
|
|
230
|
+
...base,
|
|
231
|
+
knowledge: {
|
|
232
|
+
...existingKnowledge,
|
|
233
|
+
...patch,
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function applyTaskResultMapping(args) {
|
|
238
|
+
if (args.mapping == null || args.outputForMapping === undefined)
|
|
239
|
+
return args.targetMemory;
|
|
240
|
+
const { path, mode, map, fields } = args.mapping;
|
|
241
|
+
return mergeExecutionObject(isPlainRecord(args.targetMemory) ? args.targetMemory : {}, path, args.outputForMapping, mode, map || fields ? { map, fields } : undefined, {
|
|
242
|
+
node: args.node,
|
|
243
|
+
variables: args.variables,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
async function resolveKnowledgePatch(args) {
|
|
247
|
+
const refs = [...new Set((args.refs ?? []).filter((ref) => typeof ref === "string" && ref.trim() !== "").map((ref) => ref.trim()))];
|
|
248
|
+
if (refs.length === 0 || args.resolver == null)
|
|
249
|
+
return undefined;
|
|
250
|
+
const patch = await args.resolver(refs, args.context);
|
|
251
|
+
return patch ?? undefined;
|
|
252
|
+
}
|
|
253
|
+
function normalizeRuntimeExecutionMemory(runtime) {
|
|
254
|
+
const execution = isPlainRecord(runtime.executionMemory)
|
|
255
|
+
? runtime.executionMemory
|
|
256
|
+
: {};
|
|
257
|
+
if (runtime.input !== undefined) {
|
|
258
|
+
execution.input = isPlainRecord(runtime.input) ? { ...runtime.input } : runtime.input;
|
|
259
|
+
}
|
|
260
|
+
else if (!isPlainRecord(execution.input)) {
|
|
261
|
+
execution.input = {};
|
|
262
|
+
}
|
|
263
|
+
if (isPlainRecord(runtime.inputs)) {
|
|
264
|
+
execution.inputs = { ...runtime.inputs };
|
|
265
|
+
}
|
|
266
|
+
mirrorRuntimeInputRawOnExecutionMemory(execution);
|
|
267
|
+
return execution;
|
|
268
|
+
}
|
|
269
|
+
function normalizeRuntimeOutputsMemory(runtime) {
|
|
270
|
+
return isPlainRecord(runtime.outputsMemory) ? runtime.outputsMemory : {};
|
|
271
|
+
}
|
|
272
|
+
export function createExellixGraphRuntime(opts) {
|
|
273
|
+
function parsePositiveInt(v) {
|
|
274
|
+
const n = Number(v);
|
|
275
|
+
if (!Number.isFinite(n))
|
|
276
|
+
return undefined;
|
|
277
|
+
const i = Math.floor(n);
|
|
278
|
+
return i > 0 ? i : undefined;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Concurrency tiers (highest → lowest):
|
|
282
|
+
* 1) node-level: node.concurrency | node.taskConfiguration.concurrency | node.data.concurrency
|
|
283
|
+
* 2) graph-level: graph.concurrency | graph.metadata.concurrency | graph.config?.concurrency
|
|
284
|
+
* 3) env-level: process.env.WOREX_GRAPH_CONCURRENCY
|
|
285
|
+
* 4) default fallback: 4
|
|
286
|
+
*
|
|
287
|
+
* Node-level is "confusing" because it affects siblings, so we interpret it as:
|
|
288
|
+
* - If multiple runnable nodes specify concurrency, we take the MIN across them
|
|
289
|
+
* (strictest wins) for the current batch.
|
|
290
|
+
*/
|
|
291
|
+
function resolveConcurrencyLimit(args) {
|
|
292
|
+
// Tier 4: default fallback
|
|
293
|
+
let limit = 4;
|
|
294
|
+
// Tier 3: env
|
|
295
|
+
const envLimit = parsePositiveInt(process.env.WOREX_GRAPH_CONCURRENCY);
|
|
296
|
+
if (envLimit)
|
|
297
|
+
limit = envLimit;
|
|
298
|
+
// Tier 2: graph-level
|
|
299
|
+
const graphLimit = parsePositiveInt(args.graph?.concurrency) ??
|
|
300
|
+
parsePositiveInt(args.graph?.metadata?.concurrency) ??
|
|
301
|
+
parsePositiveInt(args.graph?.config?.concurrency);
|
|
302
|
+
if (graphLimit)
|
|
303
|
+
limit = graphLimit;
|
|
304
|
+
// Tier 1: node-level (strictest wins for the batch)
|
|
305
|
+
const nodeLimits = args.runnableNodes
|
|
306
|
+
.map((n) => parsePositiveInt(n?.concurrency) ??
|
|
307
|
+
parsePositiveInt(n?.taskConfiguration?.concurrency) ??
|
|
308
|
+
parsePositiveInt(n?.data?.concurrency))
|
|
309
|
+
.filter((x) => typeof x === "number");
|
|
310
|
+
if (nodeLimits.length) {
|
|
311
|
+
limit = Math.min(limit, ...nodeLimits);
|
|
312
|
+
}
|
|
313
|
+
// Also respect runtime option if you still want it as a hard cap:
|
|
314
|
+
const hardCap = parsePositiveInt(opts.concurrency);
|
|
315
|
+
if (hardCap)
|
|
316
|
+
limit = Math.min(limit, hardCap);
|
|
317
|
+
return Math.max(1, limit);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Runs items with a concurrency limit. If failFast=true, stops scheduling new items
|
|
321
|
+
* after first error, but does not "cancel" in-flight executions.
|
|
322
|
+
*/
|
|
323
|
+
async function runPool(items, concurrency, worker, options) {
|
|
324
|
+
const failFastLocal = options?.failFast ?? false;
|
|
325
|
+
let index = 0;
|
|
326
|
+
let aborted = false;
|
|
327
|
+
async function runner() {
|
|
328
|
+
while (true) {
|
|
329
|
+
if (aborted)
|
|
330
|
+
return;
|
|
331
|
+
const i = index++;
|
|
332
|
+
if (i >= items.length)
|
|
333
|
+
return;
|
|
334
|
+
try {
|
|
335
|
+
await worker(items[i]);
|
|
336
|
+
}
|
|
337
|
+
catch (e) {
|
|
338
|
+
if (failFastLocal)
|
|
339
|
+
aborted = true;
|
|
340
|
+
throw e;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => runner());
|
|
345
|
+
// We want all worker errors, but caller usually handles errors per-node.
|
|
346
|
+
// Here, we let errors bubble.
|
|
347
|
+
await Promise.all(workers);
|
|
348
|
+
}
|
|
349
|
+
async function executeNode(input) {
|
|
350
|
+
const graphRunTaskId = input.graphRunTaskId ?? newGraphRunTaskId();
|
|
351
|
+
const runTaskJobIdStr = String(input.job?.jobId ?? input.job?.id ?? "");
|
|
352
|
+
const lifecycleJobId = runTaskJobIdStr || String(input.job?.jobId ?? input.job?.id ?? "standalone-job");
|
|
353
|
+
const effectiveLlmCall = mergeDefinedLlmCallParts((input.llmCall ?? opts.llmCall), input.node?.taskConfiguration?.llmCall);
|
|
354
|
+
const forwardRunTaskTrace = shouldForwardRunTaskTraceMode({
|
|
355
|
+
runTaskExecutionMode: input.runTaskExecutionMode ?? opts.runTaskExecutionMode,
|
|
356
|
+
});
|
|
357
|
+
const runTaskIdentityBase = input.runTaskIdentity ?? opts.runTaskIdentity;
|
|
358
|
+
if (input.node?.type === 'finalizer') {
|
|
359
|
+
const finalizer = input.node;
|
|
360
|
+
let execution = input.execution;
|
|
361
|
+
const outputsMemory = isPlainRecord(input.outputsMemory) ? input.outputsMemory : {};
|
|
362
|
+
if (execution == null || typeof execution !== 'object')
|
|
363
|
+
execution = {};
|
|
364
|
+
if (!execution._trace)
|
|
365
|
+
execution._trace = { nodes: {} };
|
|
366
|
+
if (!execution._trace.nodes)
|
|
367
|
+
execution._trace.nodes = {};
|
|
368
|
+
const startedAt = Date.now();
|
|
369
|
+
const finalizerIdentity = buildRunTaskIdentityEnvelope({
|
|
370
|
+
base: runTaskIdentityBase,
|
|
371
|
+
nodeMeta: finalizer.metadata,
|
|
372
|
+
graphId: input.graphId,
|
|
373
|
+
nodeId: String(finalizer.id),
|
|
374
|
+
taskId: graphRunTaskId,
|
|
375
|
+
jobId: lifecycleJobId,
|
|
376
|
+
});
|
|
377
|
+
if (input.eventEmitter) {
|
|
378
|
+
input.eventEmitter.emit(createNodeStartEvent(lifecycleJobId, input.graphId, graphRunTaskId, finalizer, "finalizer", undefined, input.jobCorrelation));
|
|
379
|
+
}
|
|
380
|
+
try {
|
|
381
|
+
const mode = finalizer.finalizerType === 'synthesize' ? 'ai' : 'deterministic';
|
|
382
|
+
const { parsed, rawContent } = finalizer.finalizerType === 'synthesize'
|
|
383
|
+
? await executeSynthesizeFinalizer({
|
|
384
|
+
finalizer,
|
|
385
|
+
config: finalizer.config,
|
|
386
|
+
executionMemory: execution,
|
|
387
|
+
outputsMemory,
|
|
388
|
+
runTask: (req) => opts.tasksClient.runTask({
|
|
389
|
+
...req,
|
|
390
|
+
agentId: input.job?.agentId ?? "standalone-agent",
|
|
391
|
+
jobTypeId: resolveJobTypeId(input.job),
|
|
392
|
+
taskTypeId: `exellix-graph-finalizer:${String(finalizer.config?.utilityKey ?? "synthesize")}`,
|
|
393
|
+
coreSkillId: String(finalizer.id),
|
|
394
|
+
nodeId: String(finalizer.id),
|
|
395
|
+
graphId: input.graphId,
|
|
396
|
+
masterSkillId: input.graphId,
|
|
397
|
+
masterSkillActivityId: `${lifecycleJobId}:${finalizer.id}`,
|
|
398
|
+
input: req.input ?? {},
|
|
399
|
+
identity: {
|
|
400
|
+
...finalizerIdentity,
|
|
401
|
+
...(req.identity &&
|
|
402
|
+
typeof req.identity === "object" &&
|
|
403
|
+
!Array.isArray(req.identity)
|
|
404
|
+
? req.identity
|
|
405
|
+
: {}),
|
|
406
|
+
kind: "finalizer",
|
|
407
|
+
},
|
|
408
|
+
...(input.modelConfig != null ? { modelConfig: input.modelConfig } : {}),
|
|
409
|
+
...(effectiveLlmCall != null ? { llmCall: effectiveLlmCall } : {}),
|
|
410
|
+
...(forwardRunTaskTrace ? { executionMode: "trace" } : {}),
|
|
411
|
+
...((input.runTaskDiagnostics ?? opts.runTaskDiagnostics) != null
|
|
412
|
+
? { diagnostics: input.runTaskDiagnostics ?? opts.runTaskDiagnostics }
|
|
413
|
+
: {}),
|
|
414
|
+
}),
|
|
415
|
+
context: {
|
|
416
|
+
graphId: input.graphId,
|
|
417
|
+
jobId: input.job?.jobId,
|
|
418
|
+
taskId: graphRunTaskId,
|
|
419
|
+
agentId: input.job?.agentId,
|
|
420
|
+
},
|
|
421
|
+
})
|
|
422
|
+
: {
|
|
423
|
+
...executeDeterministicFinalizer({
|
|
424
|
+
finalizer,
|
|
425
|
+
executionMemory: execution,
|
|
426
|
+
outputsMemory,
|
|
427
|
+
graph: input.graph,
|
|
428
|
+
}),
|
|
429
|
+
rawContent: undefined,
|
|
430
|
+
};
|
|
431
|
+
const endedAt = Date.now();
|
|
432
|
+
execution._trace.nodes[String(finalizer.id)] = {
|
|
433
|
+
startedAt,
|
|
434
|
+
endedAt,
|
|
435
|
+
skillKey: 'finalizer',
|
|
436
|
+
ok: true,
|
|
437
|
+
durationMs: endedAt - startedAt,
|
|
438
|
+
summary: {
|
|
439
|
+
finalizerType: finalizer.finalizerType,
|
|
440
|
+
mode,
|
|
441
|
+
...(mode === 'ai' ? { executionPolicy: finalizer.config?.executionPolicy } : {}),
|
|
442
|
+
},
|
|
443
|
+
};
|
|
444
|
+
const finalizerOut = { parsed, ...(rawContent ? { rawContent } : {}) };
|
|
445
|
+
const outputForMapping = parsed !== undefined
|
|
446
|
+
? (isPlainRecord(parsed)
|
|
447
|
+
? { ...parsed, parsed, ...(rawContent ? { rawContent } : {}) }
|
|
448
|
+
: { parsed, ...(rawContent ? { rawContent } : {}) })
|
|
449
|
+
: rawContent
|
|
450
|
+
? { rawContent }
|
|
451
|
+
: undefined;
|
|
452
|
+
const updatedOutputsMemory = applyTaskResultMapping({
|
|
453
|
+
targetMemory: outputsMemory,
|
|
454
|
+
mapping: finalizer.outputMapping,
|
|
455
|
+
outputForMapping,
|
|
456
|
+
node: finalizer,
|
|
457
|
+
variables: {},
|
|
458
|
+
});
|
|
459
|
+
if (input.eventEmitter) {
|
|
460
|
+
input.eventEmitter.emit(createNodeCompleteEvent(lifecycleJobId, input.graphId, graphRunTaskId, finalizer, "finalizer", {
|
|
461
|
+
output: finalizerOut,
|
|
462
|
+
memoryAfter: {
|
|
463
|
+
jobMemory: input.jobMemory,
|
|
464
|
+
taskMemory: input.taskMemory,
|
|
465
|
+
execution,
|
|
466
|
+
outputsMemory: updatedOutputsMemory,
|
|
467
|
+
},
|
|
468
|
+
}, input.jobCorrelation));
|
|
469
|
+
}
|
|
470
|
+
return {
|
|
471
|
+
nodeId: String(finalizer.id),
|
|
472
|
+
skillKey: 'finalizer',
|
|
473
|
+
output: finalizerOut,
|
|
474
|
+
execution,
|
|
475
|
+
outputsMemory: updatedOutputsMemory,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
catch (e) {
|
|
479
|
+
const endedAt = Date.now();
|
|
480
|
+
const err = e?.code != null
|
|
481
|
+
? e
|
|
482
|
+
: createFinalizerError({
|
|
483
|
+
code: 'GRAPH_FINALIZER_OUTPUT_INVALID',
|
|
484
|
+
message: e?.message ?? 'Finalizer failed',
|
|
485
|
+
finalizerNodeId: String(finalizer.id),
|
|
486
|
+
details: { error: e },
|
|
487
|
+
});
|
|
488
|
+
execution._trace.nodes[String(finalizer.id)] = {
|
|
489
|
+
startedAt,
|
|
490
|
+
endedAt,
|
|
491
|
+
skillKey: 'finalizer',
|
|
492
|
+
ok: false,
|
|
493
|
+
durationMs: endedAt - startedAt,
|
|
494
|
+
error: { message: err.message, code: err.code, stack: err.stack },
|
|
495
|
+
};
|
|
496
|
+
if (input.eventEmitter) {
|
|
497
|
+
input.eventEmitter.emit(createNodeFailEvent(lifecycleJobId, input.graphId, graphRunTaskId, finalizer, "finalizer", {
|
|
498
|
+
error: { message: err.message, code: err.code, stack: err.stack },
|
|
499
|
+
memoryAfter: {
|
|
500
|
+
jobMemory: input.jobMemory,
|
|
501
|
+
taskMemory: input.taskMemory,
|
|
502
|
+
execution,
|
|
503
|
+
outputsMemory,
|
|
504
|
+
},
|
|
505
|
+
response: undefined,
|
|
506
|
+
}, input.jobCorrelation));
|
|
507
|
+
}
|
|
508
|
+
markNodeLifecycleEmitted(err);
|
|
509
|
+
throw err;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
const skillKey = resolveTaskKey(input.node, input.skillKeyResolution ?? {}, {
|
|
513
|
+
jobId: runTaskJobIdStr || undefined,
|
|
514
|
+
graphId: input.graphId,
|
|
515
|
+
});
|
|
516
|
+
// Local-skill interception: scoped-data-reader, deterministic-rule, scoped-answer-writer,
|
|
517
|
+
// scoped-answer-assembler all run in-process — never call `tasksClient.runTask`.
|
|
518
|
+
if (isLocalSkillKey(skillKey)) {
|
|
519
|
+
let execution = input.execution;
|
|
520
|
+
const updatedOutputsMemory = isPlainRecord(input.outputsMemory) ? input.outputsMemory : {};
|
|
521
|
+
if (execution == null || typeof execution !== "object")
|
|
522
|
+
execution = {};
|
|
523
|
+
if (!execution._trace)
|
|
524
|
+
execution._trace = { nodes: {} };
|
|
525
|
+
if (!execution._trace.nodes)
|
|
526
|
+
execution._trace.nodes = {};
|
|
527
|
+
const startedAt = Date.now();
|
|
528
|
+
const localCtx = {
|
|
529
|
+
jobId: lifecycleJobId,
|
|
530
|
+
graphId: input.graphId,
|
|
531
|
+
taskId: graphRunTaskId,
|
|
532
|
+
};
|
|
533
|
+
if (input.eventEmitter) {
|
|
534
|
+
input.eventEmitter.emit(createNodeStartEvent(localCtx.jobId, localCtx.graphId, localCtx.taskId, input.node, skillKey, undefined, input.jobCorrelation));
|
|
535
|
+
}
|
|
536
|
+
try {
|
|
537
|
+
const cfg = input.node?.taskConfiguration ?? {};
|
|
538
|
+
const pathOpts = { graphTaskNodeId: String(input.node.id) };
|
|
539
|
+
let output;
|
|
540
|
+
if (skillKey === 'scoped-data-reader') {
|
|
541
|
+
output = (await runScopedDataReader(cfg, execution, pathOpts));
|
|
542
|
+
}
|
|
543
|
+
else if (skillKey === 'deterministic-rule') {
|
|
544
|
+
output = runDeterministicRule(cfg, execution, pathOpts);
|
|
545
|
+
}
|
|
546
|
+
else if (skillKey === 'scoped-answer-writer') {
|
|
547
|
+
output = (await runScopedAnswerWriter(cfg, execution, pathOpts));
|
|
548
|
+
}
|
|
549
|
+
else {
|
|
550
|
+
output = runScopedAnswerAssembler(cfg, execution, pathOpts);
|
|
551
|
+
}
|
|
552
|
+
const outputForMapping = { ...output, parsed: output };
|
|
553
|
+
execution = applyTaskResultMapping({
|
|
554
|
+
targetMemory: execution,
|
|
555
|
+
mapping: input.node?.executionMapping,
|
|
556
|
+
outputForMapping,
|
|
557
|
+
node: input.node,
|
|
558
|
+
variables: {},
|
|
559
|
+
});
|
|
560
|
+
const endedAt = Date.now();
|
|
561
|
+
if (execution == null || typeof execution !== 'object')
|
|
562
|
+
execution = {};
|
|
563
|
+
if (!execution._trace)
|
|
564
|
+
execution._trace = { nodes: {} };
|
|
565
|
+
if (!execution._trace.nodes)
|
|
566
|
+
execution._trace.nodes = {};
|
|
567
|
+
execution._trace.nodes[String(input.node.id)] = {
|
|
568
|
+
startedAt,
|
|
569
|
+
endedAt,
|
|
570
|
+
skillKey,
|
|
571
|
+
ok: true,
|
|
572
|
+
durationMs: endedAt - startedAt,
|
|
573
|
+
};
|
|
574
|
+
if (input.eventEmitter) {
|
|
575
|
+
input.eventEmitter.emit(createNodeCompleteEvent(localCtx.jobId, localCtx.graphId, localCtx.taskId, input.node, skillKey, {
|
|
576
|
+
output,
|
|
577
|
+
memoryAfter: { jobMemory: input.jobMemory, taskMemory: input.taskMemory, execution, outputsMemory: updatedOutputsMemory },
|
|
578
|
+
response: { success: true, output },
|
|
579
|
+
}, input.jobCorrelation));
|
|
580
|
+
}
|
|
581
|
+
return {
|
|
582
|
+
nodeId: input.node.id,
|
|
583
|
+
skillKey,
|
|
584
|
+
output: { ...output, parsed: output },
|
|
585
|
+
execution,
|
|
586
|
+
outputsMemory: updatedOutputsMemory,
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
catch (err) {
|
|
590
|
+
const endedAt = Date.now();
|
|
591
|
+
execution._trace.nodes[String(input.node.id)] = {
|
|
592
|
+
startedAt,
|
|
593
|
+
endedAt,
|
|
594
|
+
skillKey,
|
|
595
|
+
ok: false,
|
|
596
|
+
durationMs: endedAt - startedAt,
|
|
597
|
+
error: { message: err?.message ?? 'Local skill failed', code: err?.code },
|
|
598
|
+
};
|
|
599
|
+
if (input.eventEmitter) {
|
|
600
|
+
input.eventEmitter.emit(createNodeFailEvent(localCtx.jobId, localCtx.graphId, localCtx.taskId, input.node, skillKey, {
|
|
601
|
+
error: { code: err?.code ?? 'LOCAL_SKILL_FAILED', message: err?.message ?? 'Local skill failed' },
|
|
602
|
+
memoryAfter: { jobMemory: input.jobMemory, taskMemory: input.taskMemory, execution },
|
|
603
|
+
response: undefined,
|
|
604
|
+
}, input.jobCorrelation));
|
|
605
|
+
}
|
|
606
|
+
const wrapped = err?.code != null ? err : new Error(err?.message ?? 'Local skill failed');
|
|
607
|
+
if (wrapped.code == null)
|
|
608
|
+
wrapped.code = 'LOCAL_SKILL_FAILED';
|
|
609
|
+
wrapped.nodeId = input.node?.id;
|
|
610
|
+
markNodeLifecycleEmitted(wrapped);
|
|
611
|
+
throw wrapped;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
if (input.eventEmitter) {
|
|
615
|
+
input.eventEmitter.emit(createNodeStartEvent(lifecycleJobId, input.graphId, graphRunTaskId, input.node, skillKey, undefined, input.jobCorrelation));
|
|
616
|
+
}
|
|
617
|
+
const effectiveModelConfig = input.modelConfig;
|
|
618
|
+
// DEBUG: Verify execution object before sending
|
|
619
|
+
if (process.env.DEBUG_EXECUTION_MEMORY === 'true') {
|
|
620
|
+
console.log(`[DEBUG] Node ${input.node?.id}: execution object =`, JSON.stringify(input.execution, null, 2));
|
|
621
|
+
}
|
|
622
|
+
let execution = input.execution;
|
|
623
|
+
if (execution == null || typeof execution !== "object")
|
|
624
|
+
execution = {};
|
|
625
|
+
if (!execution._trace)
|
|
626
|
+
execution._trace = { nodes: {} };
|
|
627
|
+
if (!execution._trace.nodes)
|
|
628
|
+
execution._trace.nodes = {};
|
|
629
|
+
try {
|
|
630
|
+
assertAiTasksNodeExtensionsValid(input.node, `nodes.${String(input.node.id)}`);
|
|
631
|
+
}
|
|
632
|
+
catch (pe) {
|
|
633
|
+
if (input.eventEmitter) {
|
|
634
|
+
input.eventEmitter.emit(createNodeFailEvent(lifecycleJobId, input.graphId, graphRunTaskId, input.node, skillKey, {
|
|
635
|
+
error: { message: pe?.message, code: pe?.code, stack: pe?.stack },
|
|
636
|
+
memoryAfter: { jobMemory: input.jobMemory, taskMemory: input.taskMemory, execution },
|
|
637
|
+
response: undefined,
|
|
638
|
+
}, input.jobCorrelation));
|
|
639
|
+
}
|
|
640
|
+
markNodeLifecycleEmitted(pe);
|
|
641
|
+
throw pe;
|
|
642
|
+
}
|
|
643
|
+
let executionPipeline;
|
|
644
|
+
try {
|
|
645
|
+
executionPipeline = resolveExecutionPipelineForTaskNode({
|
|
646
|
+
node: input.node,
|
|
647
|
+
optionsExecutionPipeline: input.graphExecutionPipeline,
|
|
648
|
+
executionExecutionPipeline: input.execution?.executionPipeline,
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
catch (pe) {
|
|
652
|
+
if (input.eventEmitter) {
|
|
653
|
+
input.eventEmitter.emit(createNodeFailEvent(lifecycleJobId, input.graphId, graphRunTaskId, input.node, skillKey, {
|
|
654
|
+
error: { message: pe?.message, code: pe?.code, stack: pe?.stack },
|
|
655
|
+
memoryAfter: { jobMemory: input.jobMemory, taskMemory: input.taskMemory, execution },
|
|
656
|
+
response: undefined,
|
|
657
|
+
}, input.jobCorrelation));
|
|
658
|
+
}
|
|
659
|
+
const err = new Error(pe?.message ?? "Execution pipeline resolution failed");
|
|
660
|
+
err.code = pe?.code ?? "INPUT_SYNTHESIS_PIPELINE_CONFLICT";
|
|
661
|
+
err.nodeId = input.node?.id;
|
|
662
|
+
markNodeLifecycleEmitted(err);
|
|
663
|
+
throw err;
|
|
664
|
+
}
|
|
665
|
+
const hasSynthesizedContextPreStep = Array.isArray(executionPipeline) &&
|
|
666
|
+
executionPipeline.some((s) => s.phase === "pre" && s.type === "synthesized-context");
|
|
667
|
+
const preStepConfig = hasSynthesizedContextPreStep && Array.isArray(executionPipeline)
|
|
668
|
+
? normalizeSynthesizedContextConfig(executionPipeline.find((s) => s.phase === "pre" && s.type === "synthesized-context")?.config)
|
|
669
|
+
: undefined;
|
|
670
|
+
const includeContextInPrompt = hasSynthesizedContextPreStep && preStepConfig?.autoEnableContext !== true;
|
|
671
|
+
const executionRec = execution;
|
|
672
|
+
mirrorTaskVariablesOnExecution({
|
|
673
|
+
execution: executionRec,
|
|
674
|
+
node: input.node,
|
|
675
|
+
runtimeTaskVariables: input.taskVariables ?? undefined,
|
|
676
|
+
});
|
|
677
|
+
const variableBuckets = readExecutionVariableBuckets(executionRec);
|
|
678
|
+
const jobVariables = variableBuckets.jobVariables;
|
|
679
|
+
const hasExistingSynthesizedContext = execution != null &&
|
|
680
|
+
typeof execution === "object" &&
|
|
681
|
+
execution.synthesizedContext != null;
|
|
682
|
+
const clearSynthForTask = input.clearSynthesizedContextPerNode === true &&
|
|
683
|
+
hasSynthesizedContextPreStep &&
|
|
684
|
+
hasExistingSynthesizedContext;
|
|
685
|
+
mirrorRuntimeInputRawOnExecutionMemory(executionRec);
|
|
686
|
+
const nodeBindings = resolveTaskNodeInputsForRunTask({
|
|
687
|
+
node: input.node,
|
|
688
|
+
execution: executionRec,
|
|
689
|
+
});
|
|
690
|
+
const jobContext = buildTaskNodeJobContext({
|
|
691
|
+
node: input.node,
|
|
692
|
+
jobMemory: input.jobMemory,
|
|
693
|
+
executionMemory: executionRec,
|
|
694
|
+
variables: jobVariables,
|
|
695
|
+
});
|
|
696
|
+
const narrix = resolveNarrixForTaskNode({
|
|
697
|
+
nodeMetadataNarrix: input.node?.taskConfiguration?.narrix != null
|
|
698
|
+
? { ...input.node.taskConfiguration.narrix }
|
|
699
|
+
: undefined,
|
|
700
|
+
graphVariablesNarrix: jobVariables?.narrix,
|
|
701
|
+
aiTaskProfile: input.node?.taskConfiguration?.aiTaskProfile,
|
|
702
|
+
});
|
|
703
|
+
const metaTk = input.node?.taskConfiguration?.taskKind;
|
|
704
|
+
const taskKindForward = metaTk === "decision" || metaTk === "utility" || metaTk === "content" ? metaTk : undefined;
|
|
705
|
+
const autoValDec = input.node?.taskConfiguration?.autoValidateDecisionOutput;
|
|
706
|
+
const strategyKeys = getAiTaskProfileStrategyKeys(input.node?.taskConfiguration);
|
|
707
|
+
const startedAt = Date.now();
|
|
708
|
+
if (strategyKeys.preStrategyKey != null) {
|
|
709
|
+
const preRes = await runEngineAiTasksStrategyPhase({
|
|
710
|
+
phase: "pre",
|
|
711
|
+
strategySkillKey: strategyKeys.preStrategyKey,
|
|
712
|
+
parentSkillKey: skillKey,
|
|
713
|
+
aiTasks: opts.tasksClient,
|
|
714
|
+
job: input.job,
|
|
715
|
+
nodeId: String(input.node.id),
|
|
716
|
+
graphId: input.graphId,
|
|
717
|
+
taskId: graphRunTaskId,
|
|
718
|
+
runTaskJobId: lifecycleJobId,
|
|
719
|
+
execution: executionRec,
|
|
720
|
+
variables: jobVariables,
|
|
721
|
+
jobMemory: input.jobMemory,
|
|
722
|
+
taskMemory: input.taskMemory,
|
|
723
|
+
jobContext,
|
|
724
|
+
prevNodeId: input.prevNodeId,
|
|
725
|
+
modelConfig: effectiveModelConfig,
|
|
726
|
+
llmCall: effectiveLlmCall,
|
|
727
|
+
runTaskIdentity: runTaskIdentityBase,
|
|
728
|
+
nodeTaskConfiguration: input.node?.taskConfiguration,
|
|
729
|
+
forwardRunTaskTrace,
|
|
730
|
+
runTaskDiagnostics: (input.runTaskDiagnostics ?? opts.runTaskDiagnostics),
|
|
731
|
+
nodeTimeoutMs: input.nodeTimeoutMs,
|
|
732
|
+
});
|
|
733
|
+
if (!preRes.ok) {
|
|
734
|
+
const err = new Error(preRes.response.error?.message ?? "ENGINE_PRE_STRATEGY_FAILED");
|
|
735
|
+
err.code = preRes.response.error?.code ?? "ENGINE_PRE_STRATEGY_FAILED";
|
|
736
|
+
err.nodeId = input.node?.id;
|
|
737
|
+
if (input.eventEmitter) {
|
|
738
|
+
input.eventEmitter.emit(createNodeFailEvent(lifecycleJobId, input.graphId, graphRunTaskId, input.node, skillKey, {
|
|
739
|
+
error: {
|
|
740
|
+
message: err.message,
|
|
741
|
+
code: err.code,
|
|
742
|
+
...(preRes.response.error != null ? { details: preRes.response.error } : {}),
|
|
743
|
+
},
|
|
744
|
+
memoryAfter: { jobMemory: input.jobMemory, taskMemory: input.taskMemory, execution },
|
|
745
|
+
response: preRes.response,
|
|
746
|
+
}, input.jobCorrelation));
|
|
747
|
+
}
|
|
748
|
+
markNodeLifecycleEmitted(err);
|
|
749
|
+
throw err;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
const executionForTask = clearSynthForTask
|
|
753
|
+
? (() => {
|
|
754
|
+
const c = { ...executionRec };
|
|
755
|
+
delete c.synthesizedContext;
|
|
756
|
+
return c;
|
|
757
|
+
})()
|
|
758
|
+
: executionRec;
|
|
759
|
+
const callerInputs = extractCallerInputsBag({
|
|
760
|
+
execution: executionRec,
|
|
761
|
+
jobMemory: isPlainRecord(input.jobMemory) ? input.jobMemory : undefined,
|
|
762
|
+
});
|
|
763
|
+
const taskInput = buildRunTaskMainInput({
|
|
764
|
+
execution: executionRec,
|
|
765
|
+
jobMemory: isPlainRecord(input.jobMemory) ? input.jobMemory : undefined,
|
|
766
|
+
nodeBindings: {
|
|
767
|
+
...nodeBindings,
|
|
768
|
+
...(input.node?.data?.upstreamNodeIds !== undefined
|
|
769
|
+
? { upstreamNodeIds: input.node.data.upstreamNodeIds }
|
|
770
|
+
: {}),
|
|
771
|
+
},
|
|
772
|
+
});
|
|
773
|
+
mirrorStructuredInputOntoExecutionMemory(executionForTask, taskInput);
|
|
774
|
+
const graphDocModel = jobVariables[EXELLIX_GRAPH_MODEL_VARIABLE_KEY];
|
|
775
|
+
const graphEntry = graphDocModel != null && typeof graphDocModel === "object" && !Array.isArray(graphDocModel)
|
|
776
|
+
? graphDocModel.graphEntry
|
|
777
|
+
: undefined;
|
|
778
|
+
const readinessPolicy = resolveMainReadinessPolicy({
|
|
779
|
+
runtimePolicy: input.mainReadinessPolicy,
|
|
780
|
+
nodeTaskConfiguration: input.node?.taskConfiguration,
|
|
781
|
+
});
|
|
782
|
+
const readinessRoot = buildMainReadinessResolutionRoot({
|
|
783
|
+
taskInput,
|
|
784
|
+
callerInputs,
|
|
785
|
+
executionMemory: executionForTask,
|
|
786
|
+
xynthesized: xynthesizedOutboundForNode(executionRec, String(input.node.id)),
|
|
787
|
+
jobMemory: input.jobMemory,
|
|
788
|
+
taskMemory: input.taskMemory,
|
|
789
|
+
});
|
|
790
|
+
const readiness = evaluateTaskNodeMainReadiness({
|
|
791
|
+
policy: readinessPolicy,
|
|
792
|
+
node: input.node,
|
|
793
|
+
graphEntry,
|
|
794
|
+
resolutionRoot: readinessRoot,
|
|
795
|
+
});
|
|
796
|
+
if (!readiness.ok) {
|
|
797
|
+
const err = new Error(`MAIN_READINESS_FAILED: ${readiness.failedChecks.join(", ")} (nodeId=${String(input.node.id)})`);
|
|
798
|
+
err.code = ExellixGraphErrorCode.MAIN_READINESS_FAILED;
|
|
799
|
+
err.nodeId = input.node?.id;
|
|
800
|
+
err.readiness = readiness;
|
|
801
|
+
if (input.eventEmitter) {
|
|
802
|
+
input.eventEmitter.emit(createNodeFailEvent(lifecycleJobId, input.graphId, graphRunTaskId, input.node, skillKey, {
|
|
803
|
+
error: {
|
|
804
|
+
message: err.message,
|
|
805
|
+
code: err.code,
|
|
806
|
+
details: { readiness },
|
|
807
|
+
},
|
|
808
|
+
memoryAfter: { jobMemory: input.jobMemory, taskMemory: input.taskMemory, execution: executionRec },
|
|
809
|
+
response: undefined,
|
|
810
|
+
}, input.jobCorrelation));
|
|
811
|
+
}
|
|
812
|
+
markNodeLifecycleEmitted(err);
|
|
813
|
+
throw err;
|
|
814
|
+
}
|
|
815
|
+
const runTaskJobMemory = mergeKnowledgePatchIntoRunTaskMemory(input.jobMemory, input.jobKnowledgePatch);
|
|
816
|
+
const runTaskTaskMemory = mergeKnowledgePatchIntoRunTaskMemory(input.taskMemory, input.taskKnowledgePatch);
|
|
817
|
+
const metaStrats = input.node?.taskConfiguration?.executionStrategies;
|
|
818
|
+
const req = buildAiTasksRunTaskRequest({
|
|
819
|
+
skillKey,
|
|
820
|
+
job: input.job,
|
|
821
|
+
nodeId: String(input.node.id),
|
|
822
|
+
taskConfiguration: input.node?.taskConfiguration,
|
|
823
|
+
graphId: input.graphId,
|
|
824
|
+
taskId: graphRunTaskId,
|
|
825
|
+
runTaskJobId: lifecycleJobId,
|
|
826
|
+
taskInput,
|
|
827
|
+
callerInputs,
|
|
828
|
+
variables: jobVariables,
|
|
829
|
+
jobMemory: runTaskJobMemory,
|
|
830
|
+
taskMemory: runTaskTaskMemory,
|
|
831
|
+
executionMemory: executionForTask,
|
|
832
|
+
jobContext,
|
|
833
|
+
prevNodeId: input.prevNodeId,
|
|
834
|
+
executionPipeline: executionPipeline,
|
|
835
|
+
includeContextInPrompt: includeContextInPrompt === true ? true : undefined,
|
|
836
|
+
narrix: narrix ?? undefined,
|
|
837
|
+
...extractRunTaskStrategyOverrides(input.node?.taskConfiguration),
|
|
838
|
+
outputValidation: input.node?.taskConfiguration?.aiTasksOutputValidation != null &&
|
|
839
|
+
typeof input.node.taskConfiguration.aiTasksOutputValidation === "object" &&
|
|
840
|
+
input.node.taskConfiguration.aiTasksOutputValidation !== null &&
|
|
841
|
+
"schema" in input.node.taskConfiguration.aiTasksOutputValidation
|
|
842
|
+
? input.node.taskConfiguration.aiTasksOutputValidation
|
|
843
|
+
: undefined,
|
|
844
|
+
diagnostics: (input.runTaskDiagnostics ?? opts.runTaskDiagnostics),
|
|
845
|
+
modelConfig: effectiveModelConfig ?? undefined,
|
|
846
|
+
llmCall: effectiveLlmCall,
|
|
847
|
+
identity: buildRunTaskIdentityEnvelope({
|
|
848
|
+
base: runTaskIdentityBase,
|
|
849
|
+
nodeMeta: input.node?.taskConfiguration,
|
|
850
|
+
graphId: input.graphId,
|
|
851
|
+
nodeId: String(input.node.id),
|
|
852
|
+
taskId: graphRunTaskId,
|
|
853
|
+
jobId: lifecycleJobId,
|
|
854
|
+
}),
|
|
855
|
+
executionMode: forwardRunTaskTrace ? "trace" : undefined,
|
|
856
|
+
taskKind: taskKindForward,
|
|
857
|
+
autoValidateDecisionOutput: typeof autoValDec === "boolean" ? autoValDec : undefined,
|
|
858
|
+
executionStrategies: Array.isArray(metaStrats) ? metaStrats : [],
|
|
859
|
+
xynthesized: xynthesizedOutboundForNode(executionRec, String(input.node.id)),
|
|
860
|
+
smartInput: input.node.smartInput,
|
|
861
|
+
});
|
|
862
|
+
// TRACE: Validate request object before sending
|
|
863
|
+
if (process.env.DEBUG_EXECUTION_MEMORY === 'true') {
|
|
864
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: Node ${input.node?.id} - Request built`);
|
|
865
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: req.executionMemory =`, JSON.stringify(req.executionMemory, null, 2));
|
|
866
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: req.executionMemory type =`, typeof req.executionMemory);
|
|
867
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: req.executionMemory is undefined?`, req.executionMemory === undefined);
|
|
868
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: req.executionMemory is null?`, req.executionMemory === null);
|
|
869
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: req has executionMemory property?`, 'executionMemory' in req);
|
|
870
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: req keys =`, Object.keys(req));
|
|
871
|
+
if (req.executionMemory && typeof req.executionMemory === 'object') {
|
|
872
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: req.executionMemory keys =`, Object.keys(req.executionMemory));
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
let res;
|
|
876
|
+
const nodeTimeoutMs = input.nodeTimeoutMs;
|
|
877
|
+
const stepRetryPolicyResolved = resolveStepRetryPolicy(input.stepRetryPolicy ?? opts.stepRetryPolicy, input.node);
|
|
878
|
+
let retryOutcome;
|
|
879
|
+
try {
|
|
880
|
+
retryOutcome = await runRunTaskWithRetry({
|
|
881
|
+
initialRequest: req,
|
|
882
|
+
doRunTask: (r) => opts.tasksClient.runTask(r),
|
|
883
|
+
nodeTimeoutMs,
|
|
884
|
+
policy: stepRetryPolicyResolved,
|
|
885
|
+
nodeId: String(input.node.id),
|
|
886
|
+
skillKey,
|
|
887
|
+
nodeTimeoutErrorFactory: () => {
|
|
888
|
+
const err = new Error(`Node execution timed out after ${nodeTimeoutMs}ms (nodeId=${String(input.node.id)}, skillKey=${String(skillKey)})`);
|
|
889
|
+
err.code = "NODE_TIMEOUT";
|
|
890
|
+
err.nodeId = input.node.id;
|
|
891
|
+
return err;
|
|
892
|
+
},
|
|
893
|
+
});
|
|
894
|
+
res = retryOutcome.response;
|
|
895
|
+
}
|
|
896
|
+
catch (runErr) {
|
|
897
|
+
const endedAt = Date.now();
|
|
898
|
+
const att = Array.isArray(runErr?.stepRetryAttempts) ? runErr.stepRetryAttempts : [];
|
|
899
|
+
execution._trace.nodes[input.node.id] = {
|
|
900
|
+
startedAt,
|
|
901
|
+
endedAt,
|
|
902
|
+
skillKey,
|
|
903
|
+
ok: false,
|
|
904
|
+
durationMs: endedAt - startedAt,
|
|
905
|
+
error: {
|
|
906
|
+
message: runErr?.message ?? "Task run failed",
|
|
907
|
+
code: runErr?.code,
|
|
908
|
+
stack: runErr?.stack,
|
|
909
|
+
},
|
|
910
|
+
...(att.length ? { attempts: att } : {}),
|
|
911
|
+
};
|
|
912
|
+
const syn = Array.isArray(runErr?.syntheticStepRetryRunLog) ? runErr.syntheticStepRetryRunLog : [];
|
|
913
|
+
const prevLog = Array.isArray(runErr?.taskRunLog) ? runErr.taskRunLog : [];
|
|
914
|
+
runErr.taskRunLog = [...syn, ...prevLog];
|
|
915
|
+
if (input.eventEmitter) {
|
|
916
|
+
input.eventEmitter.emit(createNodeFailEvent(lifecycleJobId, input.graphId, graphRunTaskId, input.node, skillKey, {
|
|
917
|
+
error: {
|
|
918
|
+
message: runErr?.message ?? "Task run failed",
|
|
919
|
+
code: runErr?.code,
|
|
920
|
+
stack: runErr?.stack,
|
|
921
|
+
},
|
|
922
|
+
memoryAfter: { jobMemory: input.jobMemory, taskMemory: input.taskMemory, execution },
|
|
923
|
+
response: undefined,
|
|
924
|
+
}, input.jobCorrelation));
|
|
925
|
+
}
|
|
926
|
+
markNodeLifecycleEmitted(runErr);
|
|
927
|
+
throw runErr;
|
|
928
|
+
}
|
|
929
|
+
const endedAt = Date.now();
|
|
930
|
+
const durationMs = res.parsed?.meta?.durationMs ?? (endedAt - startedAt);
|
|
931
|
+
const activityId = res.parsed?.meta?.activityId ?? res.parsed?.meta?.localTaskId;
|
|
932
|
+
const summary = res.parsed?.meta != null ? { ...res.parsed.meta } : undefined;
|
|
933
|
+
if (!taskRunSucceeded(res)) {
|
|
934
|
+
execution._trace.nodes[input.node.id] = {
|
|
935
|
+
startedAt,
|
|
936
|
+
endedAt,
|
|
937
|
+
skillKey,
|
|
938
|
+
ok: false,
|
|
939
|
+
durationMs,
|
|
940
|
+
activityId,
|
|
941
|
+
summary,
|
|
942
|
+
error: {
|
|
943
|
+
message: res.error?.message ?? "Task run failed",
|
|
944
|
+
code: res.error?.code,
|
|
945
|
+
},
|
|
946
|
+
attempts: retryOutcome.attempts,
|
|
947
|
+
};
|
|
948
|
+
const resMeta = res.metadata ?? res.meta;
|
|
949
|
+
const taskRunLog = [...retryOutcome.syntheticRunLog, ...extractTaskRunLogFromMetadata(resMeta)];
|
|
950
|
+
const logxerCorrelationId = extractLogxerCorrelationFromMetadata(resMeta);
|
|
951
|
+
const err = new Error(`TASK_RUN_FAILED: ${skillKey}`);
|
|
952
|
+
err.code = res.error?.code ?? "TASK_RUN_FAILED";
|
|
953
|
+
err.skillKey = skillKey;
|
|
954
|
+
err.diagnostics = res.diagnostics;
|
|
955
|
+
err.nodeId = input.node?.id;
|
|
956
|
+
if (taskRunLog.length)
|
|
957
|
+
err.taskRunLog = taskRunLog;
|
|
958
|
+
if (logxerCorrelationId)
|
|
959
|
+
err.logxerCorrelationId = logxerCorrelationId;
|
|
960
|
+
err.stepRetryAttempts = retryOutcome.attempts;
|
|
961
|
+
if (input.eventEmitter) {
|
|
962
|
+
input.eventEmitter.emit(createNodeFailEvent(lifecycleJobId, input.graphId, graphRunTaskId, input.node, skillKey, {
|
|
963
|
+
error: {
|
|
964
|
+
message: err.message,
|
|
965
|
+
code: err.code,
|
|
966
|
+
},
|
|
967
|
+
memoryAfter: { jobMemory: input.jobMemory, taskMemory: input.taskMemory, execution },
|
|
968
|
+
response: res,
|
|
969
|
+
}, input.jobCorrelation));
|
|
970
|
+
}
|
|
971
|
+
markNodeLifecycleEmitted(err);
|
|
972
|
+
throw err;
|
|
973
|
+
}
|
|
974
|
+
let updatedExecution = res.execution !== undefined ? res.execution : execution;
|
|
975
|
+
copyExecutionContextFields(updatedExecution, execution, ["webContext", "synthesizedContext"]);
|
|
976
|
+
copyExecutionContextFields(updatedExecution, res.executionMemory, ["webContext", "synthesizedContext"]);
|
|
977
|
+
const xPatchRt = res.xynthesizedPatch;
|
|
978
|
+
mergeXynthesizedPatchIntoExecution({
|
|
979
|
+
execution: updatedExecution,
|
|
980
|
+
nodeId: String(input.node.id),
|
|
981
|
+
patch: xPatchRt,
|
|
982
|
+
});
|
|
983
|
+
const traceSynthExtrasRt = {
|
|
984
|
+
smartInput: { paths: [...(input.node.smartInput?.paths ?? [])] },
|
|
985
|
+
xynthesized: {
|
|
986
|
+
jobPatchKeys: Object.keys(xPatchRt?.job ?? {}),
|
|
987
|
+
taskPatchKeys: Object.keys(xPatchRt?.task ?? {}),
|
|
988
|
+
executionPatchKeys: Object.keys(xPatchRt?.execution ?? {}),
|
|
989
|
+
},
|
|
990
|
+
};
|
|
991
|
+
const mergedTraceSummaryRt = summary != null && typeof summary === "object" && !Array.isArray(summary)
|
|
992
|
+
? { ...summary, ...traceSynthExtrasRt }
|
|
993
|
+
: traceSynthExtrasRt;
|
|
994
|
+
execution._trace.nodes[input.node.id] = {
|
|
995
|
+
startedAt,
|
|
996
|
+
endedAt,
|
|
997
|
+
skillKey,
|
|
998
|
+
ok: true,
|
|
999
|
+
durationMs,
|
|
1000
|
+
activityId,
|
|
1001
|
+
summary: mergedTraceSummaryRt,
|
|
1002
|
+
attempts: retryOutcome.attempts,
|
|
1003
|
+
};
|
|
1004
|
+
// Handle execution object updates from response (patch merged above)
|
|
1005
|
+
// Note: We don't modify jobMemory or taskMemory - they're set before the job starts and passed through as-is
|
|
1006
|
+
if (strategyKeys.postStrategyKey != null) {
|
|
1007
|
+
const postExec = updatedExecution != null && typeof updatedExecution === "object" && !Array.isArray(updatedExecution)
|
|
1008
|
+
? updatedExecution
|
|
1009
|
+
: executionRec;
|
|
1010
|
+
const postRes = await runEngineAiTasksStrategyPhase({
|
|
1011
|
+
phase: "post",
|
|
1012
|
+
strategySkillKey: strategyKeys.postStrategyKey,
|
|
1013
|
+
parentSkillKey: skillKey,
|
|
1014
|
+
aiTasks: opts.tasksClient,
|
|
1015
|
+
job: input.job,
|
|
1016
|
+
nodeId: String(input.node.id),
|
|
1017
|
+
graphId: input.graphId,
|
|
1018
|
+
taskId: graphRunTaskId,
|
|
1019
|
+
runTaskJobId: lifecycleJobId,
|
|
1020
|
+
execution: postExec,
|
|
1021
|
+
variables: jobVariables,
|
|
1022
|
+
jobMemory: input.jobMemory,
|
|
1023
|
+
taskMemory: input.taskMemory,
|
|
1024
|
+
jobContext,
|
|
1025
|
+
prevNodeId: input.prevNodeId,
|
|
1026
|
+
modelConfig: effectiveModelConfig,
|
|
1027
|
+
llmCall: effectiveLlmCall,
|
|
1028
|
+
runTaskIdentity: runTaskIdentityBase,
|
|
1029
|
+
nodeTaskConfiguration: input.node?.taskConfiguration,
|
|
1030
|
+
forwardRunTaskTrace,
|
|
1031
|
+
runTaskDiagnostics: (input.runTaskDiagnostics ?? opts.runTaskDiagnostics),
|
|
1032
|
+
nodeTimeoutMs: input.nodeTimeoutMs,
|
|
1033
|
+
});
|
|
1034
|
+
if (!postRes.ok) {
|
|
1035
|
+
const err = new Error(postRes.response.error?.message ?? "ENGINE_POST_STRATEGY_FAILED");
|
|
1036
|
+
err.code = postRes.response.error?.code ?? "ENGINE_POST_STRATEGY_FAILED";
|
|
1037
|
+
err.nodeId = input.node?.id;
|
|
1038
|
+
if (input.eventEmitter) {
|
|
1039
|
+
input.eventEmitter.emit(createNodeFailEvent(lifecycleJobId, input.graphId, graphRunTaskId, input.node, skillKey, {
|
|
1040
|
+
error: {
|
|
1041
|
+
message: err.message,
|
|
1042
|
+
code: err.code,
|
|
1043
|
+
...(postRes.response.error != null ? { details: postRes.response.error } : {}),
|
|
1044
|
+
},
|
|
1045
|
+
memoryAfter: { jobMemory: input.jobMemory, taskMemory: input.taskMemory, execution: postExec },
|
|
1046
|
+
response: postRes.response,
|
|
1047
|
+
}, input.jobCorrelation));
|
|
1048
|
+
}
|
|
1049
|
+
markNodeLifecycleEmitted(err);
|
|
1050
|
+
throw err;
|
|
1051
|
+
}
|
|
1052
|
+
updatedExecution = postExec;
|
|
1053
|
+
}
|
|
1054
|
+
// TRACE: Log execution object from response
|
|
1055
|
+
if (process.env.DEBUG_EXECUTION_MEMORY === 'true' || process.env.DEBUG_OUTPUT_MAPPING === 'true') {
|
|
1056
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: Node ${input.node?.id} - Processing response`);
|
|
1057
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: res.execution =`, JSON.stringify(res.execution, null, 2));
|
|
1058
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: input.execution =`, JSON.stringify(input.execution, null, 2));
|
|
1059
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: updatedExecution (before mapping) =`, JSON.stringify(updatedExecution, null, 2));
|
|
1060
|
+
}
|
|
1061
|
+
// Normalize task output for executionMapping.
|
|
1062
|
+
// Normalize: create output object that includes both response output and parsed
|
|
1063
|
+
// This allows path resolution to access both "output.parsed.shortAnswer" and "output.shortAnswer"
|
|
1064
|
+
// If response.parsed exists, wrap it in { parsed: ... } structure for path resolution
|
|
1065
|
+
const responseParsed = res.parsed;
|
|
1066
|
+
// TRACE: Log response structure
|
|
1067
|
+
if (process.env.DEBUG_OUTPUT_MAPPING === 'true' || process.env.DEBUG_EXECUTION_MEMORY === 'true') {
|
|
1068
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: Node ${input.node?.id} - Response structure`);
|
|
1069
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: res.parsed =`, JSON.stringify(res.parsed, null, 2));
|
|
1070
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: res.parsed type =`, typeof res.parsed);
|
|
1071
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: res.parsed keys =`, res.parsed && typeof res.parsed === 'object' ? Object.keys(res.parsed) : 'N/A');
|
|
1072
|
+
if (res.parsed && typeof res.parsed === 'object') {
|
|
1073
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: res.parsed.shortAnswer =`, res.parsed.shortAnswer);
|
|
1074
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: res.parsed.fullAnswer =`, res.parsed.fullAnswer);
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
// Create output object similar to executeNode.ts - use parsed as the base, or create structure
|
|
1078
|
+
const outputForMapping = res.parsed !== undefined
|
|
1079
|
+
? (typeof res.parsed === 'object' && res.parsed !== null && !Array.isArray(res.parsed))
|
|
1080
|
+
? { ...res.parsed, parsed: responseParsed || res.parsed }
|
|
1081
|
+
: { parsed: responseParsed } // Wrap parsed in object structure for path resolution
|
|
1082
|
+
: undefined;
|
|
1083
|
+
// TRACE: Log outputForMapping structure
|
|
1084
|
+
if (process.env.DEBUG_OUTPUT_MAPPING === 'true' || process.env.DEBUG_EXECUTION_MEMORY === 'true') {
|
|
1085
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: outputForMapping structure`);
|
|
1086
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: outputForMapping =`, JSON.stringify(outputForMapping, null, 2));
|
|
1087
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: outputForMapping.parsed =`, JSON.stringify(outputForMapping?.parsed, null, 2));
|
|
1088
|
+
if (outputForMapping?.parsed) {
|
|
1089
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: outputForMapping.parsed.shortAnswer =`, outputForMapping.parsed.shortAnswer);
|
|
1090
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: outputForMapping.parsed.fullAnswer =`, outputForMapping.parsed.fullAnswer);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
updatedExecution = applyTaskResultMapping({
|
|
1094
|
+
targetMemory: updatedExecution,
|
|
1095
|
+
mapping: input.node?.executionMapping,
|
|
1096
|
+
outputForMapping,
|
|
1097
|
+
node: input.node,
|
|
1098
|
+
variables: jobVariables,
|
|
1099
|
+
});
|
|
1100
|
+
const updatedOutputsMemory = isPlainRecord(input.outputsMemory) ? input.outputsMemory : {};
|
|
1101
|
+
// TRACE: Log final execution state before returning
|
|
1102
|
+
if (process.env.DEBUG_EXECUTION_MEMORY === 'true' || process.env.DEBUG_OUTPUT_MAPPING === 'true') {
|
|
1103
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: Node ${input.node?.id} - Final execution state before returning`);
|
|
1104
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: updatedExecution =`, JSON.stringify(updatedExecution, null, 2));
|
|
1105
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: updatedExecution type =`, typeof updatedExecution);
|
|
1106
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeNode: updatedExecution is undefined?`, updatedExecution === undefined);
|
|
1107
|
+
}
|
|
1108
|
+
const resMetaOk = res.metadata ?? res.meta;
|
|
1109
|
+
const taskRunLogOk = [...retryOutcome.syntheticRunLog, ...extractTaskRunLogFromMetadata(resMetaOk)];
|
|
1110
|
+
const logxerCorrelationIdOk = extractLogxerCorrelationFromMetadata(resMetaOk);
|
|
1111
|
+
const aiTasksObservability = buildAiTasksObservabilityRecord(res, retryOutcome.lastRequest);
|
|
1112
|
+
if (retryOutcome.attempts.length) {
|
|
1113
|
+
aiTasksObservability.attempts = retryOutcome.attempts;
|
|
1114
|
+
}
|
|
1115
|
+
if (readiness.warnings.length > 0) {
|
|
1116
|
+
aiTasksObservability.mainReadinessWarnings = readiness.warnings;
|
|
1117
|
+
aiTasksObservability.mainReadiness = readiness;
|
|
1118
|
+
}
|
|
1119
|
+
const taskOutput = {
|
|
1120
|
+
rawContent: res.rawContent ?? res.rawText,
|
|
1121
|
+
parsed: res.parsed,
|
|
1122
|
+
};
|
|
1123
|
+
if (input.eventEmitter) {
|
|
1124
|
+
input.eventEmitter.emit(createNodeCompleteEvent(lifecycleJobId, input.graphId, graphRunTaskId, input.node, skillKey, {
|
|
1125
|
+
output: taskOutput,
|
|
1126
|
+
memoryAfter: {
|
|
1127
|
+
jobMemory: input.jobMemory,
|
|
1128
|
+
taskMemory: input.taskMemory,
|
|
1129
|
+
execution: updatedExecution,
|
|
1130
|
+
outputsMemory: updatedOutputsMemory,
|
|
1131
|
+
},
|
|
1132
|
+
}, input.jobCorrelation));
|
|
1133
|
+
}
|
|
1134
|
+
return {
|
|
1135
|
+
nodeId: input.node.id,
|
|
1136
|
+
skillKey,
|
|
1137
|
+
output: taskOutput,
|
|
1138
|
+
execution: updatedExecution, // Return execution object so it can be accumulated
|
|
1139
|
+
outputsMemory: updatedOutputsMemory,
|
|
1140
|
+
request: req,
|
|
1141
|
+
...(taskRunLogOk.length ? { taskRunLog: taskRunLogOk } : {}),
|
|
1142
|
+
...(logxerCorrelationIdOk ? { logxerCorrelationId: logxerCorrelationIdOk } : {}),
|
|
1143
|
+
aiTasksObservability,
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1146
|
+
async function executeGraph(input) {
|
|
1147
|
+
const { model: suppliedGraph, runtime } = input;
|
|
1148
|
+
const { graph } = migrateLegacyGraphResponse(suppliedGraph);
|
|
1149
|
+
const merged = mergeExellixGraphRuntimeInvocation(runtime, opts);
|
|
1150
|
+
const failFast = merged.failFast;
|
|
1151
|
+
const debugMode = runtime.debugMode === true;
|
|
1152
|
+
const eventEmitter = runtime.eventEmitter ?? opts.eventEmitter;
|
|
1153
|
+
const jobId = assertHostJobId(runtime.jobId);
|
|
1154
|
+
const graphTaskId = newGraphRunTaskId();
|
|
1155
|
+
const job = { ...(runtime.job ?? {}), id: jobId, jobId };
|
|
1156
|
+
if (runtime.mode === "backward" && !runtime.goalNodeId) {
|
|
1157
|
+
const err = new Error(`BACKWARD_GOAL_REQUIRED: mode=backward requires goalNodeId`);
|
|
1158
|
+
err.code = "BACKWARD_GOAL_REQUIRED";
|
|
1159
|
+
throw err;
|
|
1160
|
+
}
|
|
1161
|
+
const resolvedGraphIdRaw = graph?.id;
|
|
1162
|
+
if (resolvedGraphIdRaw == null || resolvedGraphIdRaw === '') {
|
|
1163
|
+
throw new Error('GRAPH_ID_REQUIRED: execution request model must include a non-empty id');
|
|
1164
|
+
}
|
|
1165
|
+
const resolvedGraphId = String(resolvedGraphIdRaw);
|
|
1166
|
+
assertCanonicalGraphDocument(graph, { jobId, graphId: resolvedGraphId });
|
|
1167
|
+
let runxClient = opts.runx;
|
|
1168
|
+
if (graphNeedsRunxClient(graph, merged.modelConfig)) {
|
|
1169
|
+
if (!runxClient) {
|
|
1170
|
+
if (!opts.runxCreateOptions) {
|
|
1171
|
+
const err = new Error("RUNX_REQUIRED: graph uses jsConditionFunction, aiCondition, or conditional modelConfig cases that require runx. Pass runx or runxCreateOptions on createExellixGraphRuntime.");
|
|
1172
|
+
err.code = "RUNX_REQUIRED";
|
|
1173
|
+
throw err;
|
|
1174
|
+
}
|
|
1175
|
+
runxClient = await createRunx(opts.runxCreateOptions);
|
|
1176
|
+
await runxClient.bootstrap();
|
|
1177
|
+
await runxClient.reload();
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
const graphAudit = {
|
|
1181
|
+
source: 'model',
|
|
1182
|
+
contentSha256: computeGraphDocumentContentSha256(suppliedGraph),
|
|
1183
|
+
};
|
|
1184
|
+
const graphDocumentModel = mergeGraphDocumentModel(graph);
|
|
1185
|
+
const graphExecution = graphDocumentModel.graphExecution;
|
|
1186
|
+
const nodeResponseKeys = resolveNodeResponseKeys(graphExecution);
|
|
1187
|
+
const coreObjectiveConfig = resolveCoreObjectiveConfig(graphExecution);
|
|
1188
|
+
setRuntimeObjectsLastJobId(merged.runtimeObjects, jobId);
|
|
1189
|
+
const { finalizer } = validateGraphFinalizer(graph);
|
|
1190
|
+
const engine = opts.engineFactory.create({
|
|
1191
|
+
graph,
|
|
1192
|
+
mode: runtime.mode,
|
|
1193
|
+
goalNodeId: runtime.goalNodeId,
|
|
1194
|
+
dimension: runtime.dimension,
|
|
1195
|
+
initialState: runtime.initialState,
|
|
1196
|
+
initialVariables: runtime.initialVariables,
|
|
1197
|
+
});
|
|
1198
|
+
const outputsByNodeId = {};
|
|
1199
|
+
const stepsResponses = [];
|
|
1200
|
+
const errors = [];
|
|
1201
|
+
const finalizerNodeId = String(finalizer.id);
|
|
1202
|
+
const jobCorrelation = job?.jobType != null ? { jobType: job.jobType } : undefined;
|
|
1203
|
+
// Pre-compute incoming-edge map for conditional-edge filtering of plan.nextNodes.
|
|
1204
|
+
const rawEdges = Array.isArray(graph.edges) ? graph.edges : [];
|
|
1205
|
+
const hasConditionalEdges = rawEdges.some((e) => e && typeof e === "object" && e.when != null);
|
|
1206
|
+
const incomingByTo = new Map();
|
|
1207
|
+
if (hasConditionalEdges) {
|
|
1208
|
+
for (const e of rawEdges) {
|
|
1209
|
+
if (!e || typeof e !== "object")
|
|
1210
|
+
continue;
|
|
1211
|
+
const to = e.to;
|
|
1212
|
+
if (typeof to !== "string" || to.length === 0)
|
|
1213
|
+
continue;
|
|
1214
|
+
const list = incomingByTo.get(to) ?? [];
|
|
1215
|
+
list.push(e);
|
|
1216
|
+
incomingByTo.set(to, list);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
const graphRunStartedAt = Date.now();
|
|
1220
|
+
const taskRunLogBuffer = [];
|
|
1221
|
+
let logxerCorrelationIdLast;
|
|
1222
|
+
function appendTaskRunLogFromError(e) {
|
|
1223
|
+
if (Array.isArray(e?.taskRunLog))
|
|
1224
|
+
taskRunLogBuffer.push(...e.taskRunLog);
|
|
1225
|
+
if (typeof e?.logxerCorrelationId === "string" && e.logxerCorrelationId.length > 0) {
|
|
1226
|
+
logxerCorrelationIdLast = e.logxerCorrelationId;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
function finalizeGraphPayload(graphStatus) {
|
|
1230
|
+
const lim = resolveRunLogLimits({
|
|
1231
|
+
runLogMode: merged.runLogMode,
|
|
1232
|
+
maxRunLogEntries: merged.maxRunLogEntries,
|
|
1233
|
+
maxRunLogDataJsonChars: merged.maxRunLogDataJsonChars,
|
|
1234
|
+
defaults: {},
|
|
1235
|
+
});
|
|
1236
|
+
const built = buildRunLog({
|
|
1237
|
+
...lim,
|
|
1238
|
+
jobId,
|
|
1239
|
+
taskId: graphTaskId,
|
|
1240
|
+
graphId: resolvedGraphId,
|
|
1241
|
+
graphRunStartedAt,
|
|
1242
|
+
graphStatus,
|
|
1243
|
+
execution: currentExecution,
|
|
1244
|
+
taskAppendedEntries: taskRunLogBuffer,
|
|
1245
|
+
logxerCorrelationId: logxerCorrelationIdLast,
|
|
1246
|
+
});
|
|
1247
|
+
return {
|
|
1248
|
+
execution: currentExecution,
|
|
1249
|
+
outputsMemory: currentOutputsMemory,
|
|
1250
|
+
...built,
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
1253
|
+
// Track current memory and execution state (will be updated as nodes execute)
|
|
1254
|
+
let currentJobMemory = runtime.jobMemory || {};
|
|
1255
|
+
let currentTaskMemory = runtime.taskMemory;
|
|
1256
|
+
let currentExecution = normalizeRuntimeExecutionMemory(runtime);
|
|
1257
|
+
let currentOutputsMemory = normalizeRuntimeOutputsMemory(runtime);
|
|
1258
|
+
if (!currentExecution._trace)
|
|
1259
|
+
currentExecution._trace = { nodes: {} };
|
|
1260
|
+
if (!currentExecution._trace.nodes)
|
|
1261
|
+
currentExecution._trace.nodes = {};
|
|
1262
|
+
const jobKnowledgePatch = await resolveKnowledgePatch({
|
|
1263
|
+
refs: graph.jobKnowledge,
|
|
1264
|
+
resolver: opts.resolveJobKnowledge,
|
|
1265
|
+
context: { model: graph, runtime, graphId: resolvedGraphId, jobId, taskId: graphTaskId },
|
|
1266
|
+
});
|
|
1267
|
+
seedGraphRunExecutionState({
|
|
1268
|
+
execution: currentExecution,
|
|
1269
|
+
jobMemory: currentJobMemory,
|
|
1270
|
+
job: runtime.job,
|
|
1271
|
+
});
|
|
1272
|
+
seedGraphVariableBucketsFromRuntime({
|
|
1273
|
+
execution: currentExecution,
|
|
1274
|
+
graph,
|
|
1275
|
+
runtime,
|
|
1276
|
+
job,
|
|
1277
|
+
});
|
|
1278
|
+
assertFinalizerRequiredReadsResolvable({
|
|
1279
|
+
graph,
|
|
1280
|
+
finalizer,
|
|
1281
|
+
executionMemory: currentExecution,
|
|
1282
|
+
outputsMemory: currentOutputsMemory,
|
|
1283
|
+
});
|
|
1284
|
+
const coreObjectiveValue = resolveCoreObjectiveValue({
|
|
1285
|
+
sourcePath: coreObjectiveConfig.sourcePath,
|
|
1286
|
+
execution: currentExecution,
|
|
1287
|
+
jobMemory: currentJobMemory,
|
|
1288
|
+
taskMemory: currentTaskMemory,
|
|
1289
|
+
job,
|
|
1290
|
+
});
|
|
1291
|
+
if (eventEmitter) {
|
|
1292
|
+
eventEmitter.emit(createGraphStartEvent(jobId, resolvedGraphId, graphTaskId, {
|
|
1293
|
+
agentId: job?.agentId,
|
|
1294
|
+
input: {
|
|
1295
|
+
variables: runtime.variables,
|
|
1296
|
+
jobMemory: currentJobMemory,
|
|
1297
|
+
taskMemory: currentTaskMemory,
|
|
1298
|
+
},
|
|
1299
|
+
...(jobCorrelation ?? {}),
|
|
1300
|
+
}));
|
|
1301
|
+
}
|
|
1302
|
+
function buildDebugTrace() {
|
|
1303
|
+
if (!debugMode)
|
|
1304
|
+
return undefined;
|
|
1305
|
+
const traceNodes = (currentExecution?._trace?.nodes ?? {});
|
|
1306
|
+
const out = [];
|
|
1307
|
+
for (const [nodeId, entry] of Object.entries(traceNodes)) {
|
|
1308
|
+
if (entry == null || typeof entry !== "object")
|
|
1309
|
+
continue;
|
|
1310
|
+
out.push({ nodeId, ...entry });
|
|
1311
|
+
}
|
|
1312
|
+
// Stable order by startedAt, then by nodeId.
|
|
1313
|
+
out.sort((a, b) => (a.startedAt ?? 0) - (b.startedAt ?? 0) || a.nodeId.localeCompare(b.nodeId));
|
|
1314
|
+
return { nodes: out };
|
|
1315
|
+
}
|
|
1316
|
+
function appendStepResponse(node, output) {
|
|
1317
|
+
if (node?.type === "finalizer")
|
|
1318
|
+
return;
|
|
1319
|
+
const nodeResponse = unwrapNodeResponse(output);
|
|
1320
|
+
stepsResponses.push({
|
|
1321
|
+
[coreObjectiveConfig.propertyName]: coreObjectiveValue,
|
|
1322
|
+
[nodeResponseKeys.singular]: nodeResponse,
|
|
1323
|
+
});
|
|
1324
|
+
}
|
|
1325
|
+
function buildFinalOutputFromGraphResponse() {
|
|
1326
|
+
const executionMemoryForResponse = isPlainRecord(currentExecution)
|
|
1327
|
+
? structuredClone(currentExecution)
|
|
1328
|
+
: currentExecution;
|
|
1329
|
+
return applyGraphResponseDefinition({
|
|
1330
|
+
response: graph.response,
|
|
1331
|
+
context: {
|
|
1332
|
+
graph,
|
|
1333
|
+
executionMemory: executionMemoryForResponse,
|
|
1334
|
+
outputsMemory: isPlainRecord(currentOutputsMemory)
|
|
1335
|
+
? structuredClone(currentOutputsMemory)
|
|
1336
|
+
: currentOutputsMemory,
|
|
1337
|
+
outputsByNodeId,
|
|
1338
|
+
stepsResponses,
|
|
1339
|
+
},
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
const playgroundReporter = runtime.playgroundReporter ?? opts.playgroundReporter;
|
|
1343
|
+
if (playgroundReporter) {
|
|
1344
|
+
playgroundReporter.step("graph:start", {
|
|
1345
|
+
jobId,
|
|
1346
|
+
taskId: graphTaskId,
|
|
1347
|
+
graphId: resolvedGraphId,
|
|
1348
|
+
executionKeys: Object.keys(currentExecution),
|
|
1349
|
+
variablesKeys: runtime.variables != null ? Object.keys(runtime.variables) : [],
|
|
1350
|
+
executionExcerpt: typeof currentExecution === "object" && currentExecution !== null
|
|
1351
|
+
? JSON.stringify(currentExecution, null, 2).slice(0, 500)
|
|
1352
|
+
: undefined,
|
|
1353
|
+
...(merged.playgroundMeta != null && typeof merged.playgroundMeta === "object"
|
|
1354
|
+
? merged.playgroundMeta
|
|
1355
|
+
: {}),
|
|
1356
|
+
});
|
|
1357
|
+
if (merged.runtimeObjects) {
|
|
1358
|
+
playgroundReporter.step("graph:observability", summarizeRuntimeObjectsForPlayground(merged.runtimeObjects));
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
// TRACE: Log initial execution state
|
|
1362
|
+
if (process.env.DEBUG_EXECUTION_MEMORY === 'true') {
|
|
1363
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeGraph: Initial execution state`);
|
|
1364
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeGraph: runtime.executionMemory =`, JSON.stringify(runtime.executionMemory, null, 2));
|
|
1365
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeGraph: currentExecution =`, JSON.stringify(currentExecution, null, 2));
|
|
1366
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeGraph: currentExecution type =`, typeof currentExecution);
|
|
1367
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeGraph: currentExecution is undefined?`, currentExecution === undefined);
|
|
1368
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeGraph: currentExecution is null?`, currentExecution === null);
|
|
1369
|
+
}
|
|
1370
|
+
function recordForStructuredDataFilters() {
|
|
1371
|
+
return isPlainRecord(currentExecution?.input)
|
|
1372
|
+
? currentExecution.input
|
|
1373
|
+
: {};
|
|
1374
|
+
}
|
|
1375
|
+
const graphEntryForGate = graphDocumentModel.graphEntry;
|
|
1376
|
+
if (graphEntryForGate?.dataFilters !== undefined) {
|
|
1377
|
+
const entryEv = evaluateStructuredDataFilters(graphEntryForGate.dataFilters, recordForStructuredDataFilters());
|
|
1378
|
+
if (entryEv.status === "unsupported_shape" ||
|
|
1379
|
+
(entryEv.status === "evaluated" && !entryEv.ok)) {
|
|
1380
|
+
const err = new Error("GRAPH_ENTRY_DATA_FILTERS_REJECTED: metadata.graphEntry.dataFilters (structured) did not accept runtime.executionMemory.input");
|
|
1381
|
+
err.code = ExellixGraphErrorCode.GRAPH_ENTRY_DATA_FILTERS_REJECTED;
|
|
1382
|
+
const finalOutput = buildFinalOutputFromGraphResponse();
|
|
1383
|
+
const result = {
|
|
1384
|
+
jobId,
|
|
1385
|
+
taskId: graphTaskId,
|
|
1386
|
+
graphId: resolvedGraphId,
|
|
1387
|
+
status: "failed",
|
|
1388
|
+
outputsByNodeId,
|
|
1389
|
+
stepsResponses,
|
|
1390
|
+
engineSnapshot: engine.snapshot(),
|
|
1391
|
+
...(finalOutput !== undefined ? { finalOutput } : {}),
|
|
1392
|
+
errors: [...errors, { error: err }],
|
|
1393
|
+
graphAudit,
|
|
1394
|
+
...finalizeGraphPayload("failed"),
|
|
1395
|
+
};
|
|
1396
|
+
const debug = buildDebugTrace();
|
|
1397
|
+
if (debug)
|
|
1398
|
+
result.debug = debug;
|
|
1399
|
+
if (eventEmitter) {
|
|
1400
|
+
eventEmitter.emit(createGraphFailEvent(jobId, resolvedGraphId, graphTaskId, err, {
|
|
1401
|
+
finalMemory: { jobMemory: currentJobMemory, taskMemory: currentTaskMemory, execution: currentExecution, outputsMemory: currentOutputsMemory },
|
|
1402
|
+
...(jobCorrelation ?? {}),
|
|
1403
|
+
}));
|
|
1404
|
+
}
|
|
1405
|
+
return result;
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
while (true) {
|
|
1409
|
+
const plan = engine.plan();
|
|
1410
|
+
if (plan.status === "completed") {
|
|
1411
|
+
const graphStatus = errors.length ? "failed" : "completed";
|
|
1412
|
+
const finalOutput = buildFinalOutputFromGraphResponse();
|
|
1413
|
+
const result = {
|
|
1414
|
+
jobId,
|
|
1415
|
+
taskId: graphTaskId,
|
|
1416
|
+
graphId: resolvedGraphId,
|
|
1417
|
+
status: graphStatus,
|
|
1418
|
+
outputsByNodeId,
|
|
1419
|
+
stepsResponses,
|
|
1420
|
+
engineSnapshot: engine.snapshot(),
|
|
1421
|
+
...(finalOutput !== undefined ? { finalOutput } : {}),
|
|
1422
|
+
...(errors.length === 0 ? { finalizerNodeId, finalizerType: finalizer.finalizerType } : {}),
|
|
1423
|
+
errors: errors.length ? errors : undefined,
|
|
1424
|
+
graphAudit,
|
|
1425
|
+
...finalizeGraphPayload(graphStatus),
|
|
1426
|
+
};
|
|
1427
|
+
const debug = buildDebugTrace();
|
|
1428
|
+
if (debug)
|
|
1429
|
+
result.debug = debug;
|
|
1430
|
+
if (eventEmitter) {
|
|
1431
|
+
if (graphStatus === "completed") {
|
|
1432
|
+
eventEmitter.emit(createGraphCompleteEvent(jobId, resolvedGraphId, graphTaskId, {
|
|
1433
|
+
output: finalOutput,
|
|
1434
|
+
nodesExecuted: Object.keys(outputsByNodeId).length,
|
|
1435
|
+
finalMemory: { jobMemory: currentJobMemory, taskMemory: currentTaskMemory, execution: currentExecution, outputsMemory: currentOutputsMemory },
|
|
1436
|
+
...(jobCorrelation ?? {}),
|
|
1437
|
+
}));
|
|
1438
|
+
}
|
|
1439
|
+
else {
|
|
1440
|
+
eventEmitter.emit(createGraphFailEvent(jobId, resolvedGraphId, graphTaskId, errors[0]?.error, {
|
|
1441
|
+
finalMemory: { jobMemory: currentJobMemory, taskMemory: currentTaskMemory, execution: currentExecution, outputsMemory: currentOutputsMemory },
|
|
1442
|
+
...(jobCorrelation ?? {}),
|
|
1443
|
+
}));
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
return result;
|
|
1447
|
+
}
|
|
1448
|
+
if (plan.status !== "continue") {
|
|
1449
|
+
const err = new Error(`GRAPH_BLOCKED: status=${plan.status}`);
|
|
1450
|
+
err.code = "GRAPH_BLOCKED";
|
|
1451
|
+
const finalOutput = buildFinalOutputFromGraphResponse();
|
|
1452
|
+
const result = {
|
|
1453
|
+
jobId,
|
|
1454
|
+
taskId: graphTaskId,
|
|
1455
|
+
graphId: resolvedGraphId,
|
|
1456
|
+
status: "failed",
|
|
1457
|
+
outputsByNodeId,
|
|
1458
|
+
stepsResponses,
|
|
1459
|
+
engineSnapshot: engine.snapshot(),
|
|
1460
|
+
...(finalOutput !== undefined ? { finalOutput } : {}),
|
|
1461
|
+
errors: [...errors, { error: err }],
|
|
1462
|
+
graphAudit,
|
|
1463
|
+
...finalizeGraphPayload("failed"),
|
|
1464
|
+
};
|
|
1465
|
+
const debug = buildDebugTrace();
|
|
1466
|
+
if (debug)
|
|
1467
|
+
result.debug = debug;
|
|
1468
|
+
if (eventEmitter) {
|
|
1469
|
+
eventEmitter.emit(createGraphFailEvent(jobId, resolvedGraphId, graphTaskId, err, {
|
|
1470
|
+
finalMemory: { jobMemory: currentJobMemory, taskMemory: currentTaskMemory, execution: currentExecution, outputsMemory: currentOutputsMemory },
|
|
1471
|
+
...(jobCorrelation ?? {}),
|
|
1472
|
+
}));
|
|
1473
|
+
}
|
|
1474
|
+
return result;
|
|
1475
|
+
}
|
|
1476
|
+
let runnableNodes = plan.nextNodes ?? [];
|
|
1477
|
+
// Conditional edge filtering: a node with incoming edges that are *all* conditional must
|
|
1478
|
+
// have at least one incoming edge whose `when` evaluates true to be runnable this round.
|
|
1479
|
+
// Roots and nodes with at least one unconditional incoming edge always pass through.
|
|
1480
|
+
if (hasConditionalEdges && runnableNodes.length > 0) {
|
|
1481
|
+
const planningContextBase = {
|
|
1482
|
+
executionMemory: currentExecution,
|
|
1483
|
+
jobMemory: currentJobMemory,
|
|
1484
|
+
taskMemory: currentTaskMemory,
|
|
1485
|
+
};
|
|
1486
|
+
runnableNodes = runnableNodes.filter((n) => {
|
|
1487
|
+
const id = n?.id;
|
|
1488
|
+
if (typeof id !== "string" || id.length === 0)
|
|
1489
|
+
return true;
|
|
1490
|
+
const incoming = incomingByTo.get(id) ?? [];
|
|
1491
|
+
if (incoming.length === 0)
|
|
1492
|
+
return true;
|
|
1493
|
+
const hasUnconditional = incoming.some((e) => e?.when == null);
|
|
1494
|
+
if (hasUnconditional)
|
|
1495
|
+
return true;
|
|
1496
|
+
const planningContext = buildPredicateEvalContextForNode({
|
|
1497
|
+
executionMemory: currentExecution,
|
|
1498
|
+
jobMemory: currentJobMemory,
|
|
1499
|
+
taskMemory: currentTaskMemory,
|
|
1500
|
+
node: n,
|
|
1501
|
+
runtimeTaskVariables: runtime.taskVariables,
|
|
1502
|
+
});
|
|
1503
|
+
return incoming.some((e) => evaluateGraphPredicate(e.when, planningContext));
|
|
1504
|
+
});
|
|
1505
|
+
}
|
|
1506
|
+
const dataFiltersRecord = recordForStructuredDataFilters();
|
|
1507
|
+
const conditionCtxBase = {
|
|
1508
|
+
executionMemory: currentExecution,
|
|
1509
|
+
jobMemory: currentJobMemory,
|
|
1510
|
+
taskMemory: currentTaskMemory,
|
|
1511
|
+
job,
|
|
1512
|
+
};
|
|
1513
|
+
const skippedByConditions = [];
|
|
1514
|
+
const gatedRunnable = [];
|
|
1515
|
+
for (const n of runnableNodes) {
|
|
1516
|
+
if (n?.type === "finalizer") {
|
|
1517
|
+
gatedRunnable.push(n);
|
|
1518
|
+
continue;
|
|
1519
|
+
}
|
|
1520
|
+
const nodePredicateCtx = buildPredicateEvalContextForNode({
|
|
1521
|
+
executionMemory: currentExecution,
|
|
1522
|
+
jobMemory: currentJobMemory,
|
|
1523
|
+
taskMemory: currentTaskMemory,
|
|
1524
|
+
node: n,
|
|
1525
|
+
runtimeTaskVariables: runtime.taskVariables,
|
|
1526
|
+
});
|
|
1527
|
+
const condEv = await evaluateTaskNodeConditions(n.conditions, { ...conditionCtxBase, ...nodePredicateCtx }, dataFiltersRecord, { runx: runxClient });
|
|
1528
|
+
if (!condEv.ok) {
|
|
1529
|
+
skippedByConditions.push({
|
|
1530
|
+
node: n,
|
|
1531
|
+
skipReason: condEv.skipReason ?? "condition_eval_error",
|
|
1532
|
+
});
|
|
1533
|
+
continue;
|
|
1534
|
+
}
|
|
1535
|
+
gatedRunnable.push(n);
|
|
1536
|
+
}
|
|
1537
|
+
for (const { node, skipReason } of skippedByConditions) {
|
|
1538
|
+
engine.commit({
|
|
1539
|
+
nodeId: String(node.id),
|
|
1540
|
+
output: { ...skippedByConditionsOutput(skipReason) },
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
runnableNodes = gatedRunnable;
|
|
1544
|
+
const concurrency = resolveConcurrencyLimit({ graph, runnableNodes });
|
|
1545
|
+
// Execute the current runnable batch in parallel (bounded)
|
|
1546
|
+
// Important: we only call plan() again AFTER all commits from this batch land.
|
|
1547
|
+
let batchHadFailFastError = false;
|
|
1548
|
+
await runPool(runnableNodes, concurrency, async (node) => {
|
|
1549
|
+
// If failFast was triggered, don't schedule more work (runPool also stops scheduling)
|
|
1550
|
+
if (batchHadFailFastError)
|
|
1551
|
+
return;
|
|
1552
|
+
const executionBaseForNode = cloneJsonLike(currentExecution);
|
|
1553
|
+
const executionForNode = cloneJsonLike(executionBaseForNode);
|
|
1554
|
+
const outputsBaseForNode = cloneJsonLike(currentOutputsMemory);
|
|
1555
|
+
const outputsForNode = cloneJsonLike(outputsBaseForNode);
|
|
1556
|
+
// TRACE: Log execution before passing to node
|
|
1557
|
+
if (process.env.DEBUG_EXECUTION_MEMORY === 'true') {
|
|
1558
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeGraph: About to execute node ${node.id}`);
|
|
1559
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeGraph: executionForNode =`, JSON.stringify(executionForNode, null, 2));
|
|
1560
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeGraph: executionForNode type =`, typeof executionForNode);
|
|
1561
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeGraph: executionForNode is undefined?`, executionForNode === undefined);
|
|
1562
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeGraph: executionForNode is null?`, executionForNode === null);
|
|
1563
|
+
if (executionForNode && typeof executionForNode === 'object') {
|
|
1564
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeGraph: executionForNode keys =`, Object.keys(executionForNode));
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
try {
|
|
1568
|
+
const isTaskNodeForKnowledge = node?.type !== "finalizer";
|
|
1569
|
+
const taskKnowledgePatch = await resolveKnowledgePatch({
|
|
1570
|
+
refs: isTaskNodeForKnowledge
|
|
1571
|
+
? (Array.isArray(node.taskKnowledge) ? node.taskKnowledge : [])
|
|
1572
|
+
: undefined,
|
|
1573
|
+
resolver: opts.resolveTaskKnowledge,
|
|
1574
|
+
context: { model: graph, runtime, graphId: resolvedGraphId, jobId, taskId: graphTaskId, node: node },
|
|
1575
|
+
});
|
|
1576
|
+
const nodeTaskMemory = isPlainRecord(currentTaskMemory) ? { ...currentTaskMemory } : currentTaskMemory;
|
|
1577
|
+
const runtimeNodeConfig = readRuntimeNodeConfig(merged.nodes, typeof node.id === "string" ? node.id : undefined);
|
|
1578
|
+
const effectiveModelConfig = await resolveModelConfigForNode({
|
|
1579
|
+
runtimeNodeConfig,
|
|
1580
|
+
nodeModelConfig: node.taskConfiguration?.modelConfig,
|
|
1581
|
+
runtimeModelConfig: merged.modelConfig,
|
|
1582
|
+
graphModelConfig: graph.modelConfig,
|
|
1583
|
+
runtimeAliasConfig: merged.aliasConfig,
|
|
1584
|
+
conditionCtx: buildPredicateEvalContextForNode({
|
|
1585
|
+
executionMemory: currentExecution,
|
|
1586
|
+
jobMemory: currentJobMemory,
|
|
1587
|
+
taskMemory: currentTaskMemory,
|
|
1588
|
+
node: node,
|
|
1589
|
+
runtimeTaskVariables: runtime.taskVariables,
|
|
1590
|
+
}),
|
|
1591
|
+
executionInput: dataFiltersRecord,
|
|
1592
|
+
runx: runxClient,
|
|
1593
|
+
});
|
|
1594
|
+
const r = await executeNode({
|
|
1595
|
+
graphId: resolvedGraphId,
|
|
1596
|
+
graph,
|
|
1597
|
+
graphRunTaskId: graphTaskId,
|
|
1598
|
+
node,
|
|
1599
|
+
job,
|
|
1600
|
+
jobMemory: currentJobMemory,
|
|
1601
|
+
taskMemory: nodeTaskMemory,
|
|
1602
|
+
jobKnowledgePatch,
|
|
1603
|
+
taskKnowledgePatch,
|
|
1604
|
+
execution: executionForNode,
|
|
1605
|
+
outputsMemory: outputsForNode,
|
|
1606
|
+
variables: runtime.variables,
|
|
1607
|
+
taskVariables: runtime.taskVariables,
|
|
1608
|
+
modelConfig: effectiveModelConfig,
|
|
1609
|
+
llmCall: merged.llmCall,
|
|
1610
|
+
runTaskIdentity: merged.runTaskIdentity,
|
|
1611
|
+
runTaskExecutionMode: merged.runTaskExecutionMode,
|
|
1612
|
+
runTaskDiagnostics: merged.runTaskDiagnostics,
|
|
1613
|
+
graphExecutionPipeline: merged.executionPipeline,
|
|
1614
|
+
skillKeyResolution: merged.skillKeyResolution,
|
|
1615
|
+
nodeTimeoutMs: merged.nodeTimeoutMs,
|
|
1616
|
+
clearSynthesizedContextPerNode: merged.clearSynthesizedContextPerNode,
|
|
1617
|
+
stepRetryPolicy: merged.stepRetryPolicy,
|
|
1618
|
+
mainReadinessPolicy: merged.mainReadinessPolicy,
|
|
1619
|
+
eventEmitter,
|
|
1620
|
+
jobCorrelation,
|
|
1621
|
+
});
|
|
1622
|
+
outputsByNodeId[r.nodeId] = r.output;
|
|
1623
|
+
appendStepResponse(node, r.output);
|
|
1624
|
+
engine.commit({ nodeId: r.nodeId, output: r.output });
|
|
1625
|
+
const trl = r.taskRunLog;
|
|
1626
|
+
if (trl?.length)
|
|
1627
|
+
taskRunLogBuffer.push(...trl);
|
|
1628
|
+
const lcid = r.logxerCorrelationId;
|
|
1629
|
+
if (typeof lcid === "string" && lcid.length > 0)
|
|
1630
|
+
logxerCorrelationIdLast = lcid;
|
|
1631
|
+
// Update execution object from node result if available.
|
|
1632
|
+
// Task nodes write execution state through executionMapping.
|
|
1633
|
+
if (r.execution !== undefined) {
|
|
1634
|
+
// TRACE: Log execution object update
|
|
1635
|
+
if (process.env.DEBUG_EXECUTION_MEMORY === 'true') {
|
|
1636
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeGraph: Node ${node.id} returned execution object`);
|
|
1637
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeGraph: r.execution =`, JSON.stringify(r.execution, null, 2));
|
|
1638
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeGraph: r.execution type =`, typeof r.execution);
|
|
1639
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeGraph: r.execution keys =`, r.execution && typeof r.execution === 'object' ? Object.keys(r.execution) : 'N/A');
|
|
1640
|
+
}
|
|
1641
|
+
currentExecution = mergeExecutionUpdate(currentExecution, r.execution, executionBaseForNode);
|
|
1642
|
+
// TRACE: Log updated currentExecution
|
|
1643
|
+
if (process.env.DEBUG_EXECUTION_MEMORY === 'true') {
|
|
1644
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeGraph: Updated currentExecution =`, JSON.stringify(currentExecution, null, 2));
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
else {
|
|
1648
|
+
// TRACE: Log when execution is not returned
|
|
1649
|
+
if (process.env.DEBUG_EXECUTION_MEMORY === 'true') {
|
|
1650
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeGraph: Node ${node.id} did NOT return execution object`);
|
|
1651
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeGraph: r.execution =`, r.execution);
|
|
1652
|
+
console.log(`[TRACE] ExellixGraphRuntime.executeGraph: currentExecution remains =`, JSON.stringify(currentExecution, null, 2));
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
if (r.outputsMemory !== undefined) {
|
|
1656
|
+
currentOutputsMemory = mergeExecutionUpdate(currentOutputsMemory, r.outputsMemory, outputsBaseForNode);
|
|
1657
|
+
}
|
|
1658
|
+
// Note: We don't modify jobMemory or taskMemory here
|
|
1659
|
+
// They are set before the job starts and we just pass them through
|
|
1660
|
+
}
|
|
1661
|
+
catch (e) {
|
|
1662
|
+
appendTaskRunLogFromError(e);
|
|
1663
|
+
errors.push({ nodeId: node?.id, error: e });
|
|
1664
|
+
engine.commit({ nodeId: node?.id, error: e });
|
|
1665
|
+
if (eventEmitter && !wasNodeLifecycleEmitted(e)) {
|
|
1666
|
+
eventEmitter.emit(createNodeFailEvent(jobId, resolvedGraphId, graphTaskId, node, String(e?.skillKey ?? ""), {
|
|
1667
|
+
error: { code: e?.code, message: e?.message, stack: e?.stack },
|
|
1668
|
+
memoryAfter: { jobMemory: currentJobMemory, taskMemory: currentTaskMemory, execution: currentExecution, outputsMemory: currentOutputsMemory },
|
|
1669
|
+
response: undefined,
|
|
1670
|
+
}, jobCorrelation));
|
|
1671
|
+
}
|
|
1672
|
+
if (failFast) {
|
|
1673
|
+
batchHadFailFastError = true;
|
|
1674
|
+
// Throw to stop scheduling new nodes in runPool
|
|
1675
|
+
throw e;
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
}, { failFast }).catch((e) => {
|
|
1679
|
+
// failFast path: we intentionally end early after stopping scheduling
|
|
1680
|
+
// (in-flight nodes may still finish; JS won't cancel them).
|
|
1681
|
+
if (!failFast)
|
|
1682
|
+
throw e;
|
|
1683
|
+
});
|
|
1684
|
+
if (failFast && errors.length) {
|
|
1685
|
+
const finalOutput = buildFinalOutputFromGraphResponse();
|
|
1686
|
+
const result = {
|
|
1687
|
+
jobId,
|
|
1688
|
+
taskId: graphTaskId,
|
|
1689
|
+
graphId: resolvedGraphId,
|
|
1690
|
+
status: "failed",
|
|
1691
|
+
outputsByNodeId,
|
|
1692
|
+
stepsResponses,
|
|
1693
|
+
engineSnapshot: engine.snapshot(),
|
|
1694
|
+
...(finalOutput !== undefined ? { finalOutput } : {}),
|
|
1695
|
+
errors,
|
|
1696
|
+
graphAudit,
|
|
1697
|
+
...finalizeGraphPayload("failed"),
|
|
1698
|
+
};
|
|
1699
|
+
const debug = buildDebugTrace();
|
|
1700
|
+
if (debug)
|
|
1701
|
+
result.debug = debug;
|
|
1702
|
+
if (eventEmitter) {
|
|
1703
|
+
eventEmitter.emit(createGraphFailEvent(jobId, resolvedGraphId, graphTaskId, errors[0]?.error, {
|
|
1704
|
+
finalMemory: { jobMemory: currentJobMemory, taskMemory: currentTaskMemory, execution: currentExecution, outputsMemory: currentOutputsMemory },
|
|
1705
|
+
...(jobCorrelation ?? {}),
|
|
1706
|
+
}));
|
|
1707
|
+
}
|
|
1708
|
+
return result;
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
return {
|
|
1713
|
+
executeGraph,
|
|
1714
|
+
executeNode, // exported for direct single-node test if you want
|
|
1715
|
+
};
|
|
1716
|
+
}
|