@exellix/graph-engine 8.4.0 → 8.6.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/CHANGELOG.md +40 -0
- package/README.md +3 -21
- package/dist/src/index.d.ts +6 -5
- package/dist/src/index.js +3 -2
- package/dist/src/inspection/contractInspection.js +7 -16
- package/dist/src/inspection/controlInspection.js +2 -2
- package/dist/src/inspection/nodeInspection.js +9 -10
- package/dist/src/inspection/types.d.ts +4 -13
- package/dist/src/runtime/ExellixGraphRuntime.js +6 -3
- package/dist/src/runtime/buildAiTasksRunTaskRequest.d.ts +3 -8
- package/dist/src/runtime/buildAiTasksRunTaskRequest.js +1 -16
- package/dist/src/runtime/buildRunTaskTaskConfigurationForward.d.ts +3 -3
- package/dist/src/runtime/buildRunTaskTaskConfigurationForward.js +3 -7
- package/dist/src/runtime/executionMatrixHost.d.ts +6 -17
- package/dist/src/runtime/executionMatrixHost.js +8 -48
- package/dist/src/runtime/graphModelStudioSeparation.d.ts +4 -2
- package/dist/src/runtime/graphModelStudioSeparation.js +11 -27
- package/dist/src/runtime/resolveNarrixForTaskNode.d.ts +3 -7
- package/dist/src/runtime/resolveNarrixForTaskNode.js +4 -7
- package/dist/src/runtime/runTaskNodePlan.d.ts +10 -0
- package/dist/src/runtime/runTaskNodePlan.js +132 -0
- package/dist/src/runtime/taskNodeMainReadiness.js +1 -12
- package/dist/src/runtime/taskNodeRunTaskPreflight.js +6 -3
- package/dist/src/runtime/validateCanonicalGraphDocument.js +6 -7
- package/dist/src/types/aiTaskProfile.d.ts +10 -15
- package/dist/src/types/aiTaskProfile.js +11 -3
- package/dist/src/types/narrix.d.ts +33 -24
- package/dist/src/types/refs.d.ts +1 -57
- package/dist/src/types/taskNodeConfiguration.d.ts +3 -2
- package/docs/handoff/graphs-studio-graphenix-2.7.3.md +13 -0
- package/package.json +18 -17
- package/dist/src/runtime/applyAiTaskProfileWebScopingToNarrix.d.ts +0 -15
- package/dist/src/runtime/applyAiTaskProfileWebScopingToNarrix.js +0 -43
|
@@ -15,9 +15,6 @@ export const GRAPH_METADATA_STUDIO_ONLY_KEYS = [
|
|
|
15
15
|
function isPlainObject(v) {
|
|
16
16
|
return v != null && typeof v === 'object' && !Array.isArray(v);
|
|
17
17
|
}
|
|
18
|
-
function isValueInputSpec(spec) {
|
|
19
|
-
return spec.kind !== 'execution';
|
|
20
|
-
}
|
|
21
18
|
export function getGraphEntryStudioOnlyKeyViolations(graphEntry) {
|
|
22
19
|
if (!isPlainObject(graphEntry))
|
|
23
20
|
return [];
|
|
@@ -28,26 +25,6 @@ export function getGraphMetadataStudioOnlyKeyViolations(metadata) {
|
|
|
28
25
|
return [];
|
|
29
26
|
return GRAPH_METADATA_STUDIO_ONLY_KEYS.filter((key) => Object.prototype.hasOwnProperty.call(metadata, key));
|
|
30
27
|
}
|
|
31
|
-
/** Returns dot-paths under graphEntry.inputs with empty `path` for value kinds. */
|
|
32
|
-
export function getGraphEntryEmptyInputPathViolations(graphEntry) {
|
|
33
|
-
if (!isPlainObject(graphEntry))
|
|
34
|
-
return [];
|
|
35
|
-
const inputs = graphEntry.inputs;
|
|
36
|
-
if (!Array.isArray(inputs))
|
|
37
|
-
return [];
|
|
38
|
-
const bad = [];
|
|
39
|
-
for (let i = 0; i < inputs.length; i++) {
|
|
40
|
-
const spec = inputs[i];
|
|
41
|
-
if (!isPlainObject(spec))
|
|
42
|
-
continue;
|
|
43
|
-
if (spec.kind === 'execution')
|
|
44
|
-
continue;
|
|
45
|
-
const path = typeof spec.path === 'string' ? spec.path.trim() : '';
|
|
46
|
-
if (!path)
|
|
47
|
-
bad.push(`metadata.graphEntry.inputs[${i}].path`);
|
|
48
|
-
}
|
|
49
|
-
return bad;
|
|
50
|
-
}
|
|
51
28
|
/**
|
|
52
29
|
* Deep-clones a graph model and removes studio-only metadata and graphEntry example fields.
|
|
53
30
|
* Use at publish time when upstream serializers still attach playground artifacts.
|
|
@@ -70,16 +47,23 @@ export function stripGraphModelStudioFields(graph) {
|
|
|
70
47
|
}
|
|
71
48
|
return clone;
|
|
72
49
|
}
|
|
50
|
+
/** Legacy graph-entry input declaration — removed in Graphenix 2.7.0 (CR-006). */
|
|
51
|
+
export const GRAPH_ENTRY_REMOVED_KEYS = ['inputs', 'inputTypes'];
|
|
52
|
+
export function getGraphEntryRemovedKeyViolations(graphEntry) {
|
|
53
|
+
if (!isPlainObject(graphEntry))
|
|
54
|
+
return [];
|
|
55
|
+
return GRAPH_ENTRY_REMOVED_KEYS.filter((key) => Object.prototype.hasOwnProperty.call(graphEntry, key));
|
|
56
|
+
}
|
|
73
57
|
export function assertCanonicalGraphEntryContract(graphEntry, context) {
|
|
74
58
|
const label = context.graphLabel ?? `Graph "${String(context.graphId ?? '?')}"`;
|
|
59
|
+
const removedKeys = getGraphEntryRemovedKeyViolations(graphEntry);
|
|
60
|
+
if (removedKeys.length > 0) {
|
|
61
|
+
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_GRAPH_DOCUMENT, `${label}: metadata.graphEntry must not include removed field(s): ${removedKeys.join(', ')}. Data source selection is an operator concern (JobDef.graphs[*].sourcePoll); use requiredExecutionPaths only.`, { ...context, graphEntryRemovedKeys: removedKeys });
|
|
62
|
+
}
|
|
75
63
|
const studioKeys = getGraphEntryStudioOnlyKeyViolations(graphEntry);
|
|
76
64
|
if (studioKeys.length > 0) {
|
|
77
65
|
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_GRAPH_DOCUMENT, `${label}: metadata.graphEntry must not include studio-only example field(s): ${studioKeys.join(', ')}. Store example payloads in the graphs-studio database (GraphStudioDocument.exampleInputs), not in the executable graph model.`, { ...context, graphEntryStudioOnlyKeys: studioKeys });
|
|
78
66
|
}
|
|
79
|
-
const emptyPaths = getGraphEntryEmptyInputPathViolations(graphEntry);
|
|
80
|
-
if (emptyPaths.length > 0) {
|
|
81
|
-
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_GRAPH_DOCUMENT, `${label}: metadata.graphEntry.inputs must declare a non-empty dot-path under merged execution (e.g. "input" or "input.subnetId"); empty path at ${emptyPaths.join(', ')}.`, { ...context, graphEntryEmptyInputPaths: emptyPaths });
|
|
82
|
-
}
|
|
83
67
|
}
|
|
84
68
|
export function assertCanonicalGraphDocumentMetadata(metadata, context) {
|
|
85
69
|
const label = context.graphLabel ?? `Graph "${String(context.graphId ?? '?')}"`;
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import type { NarrixPreProcessorConfig, ResolvedNarrixWirePayload } from '../types/narrix.js';
|
|
2
|
-
import type { AiTaskProfileMetadata } from '../types/aiTaskProfile.js';
|
|
3
2
|
/**
|
|
4
|
-
* Resolves
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Shared by {@link ExellixGraphRuntime}.
|
|
3
|
+
* Resolves discovery narrix for runtime plan patching: shallow-merge of node `taskConfiguration.narrix`
|
|
4
|
+
* with graph-level `variables.narrix`. Web scope is a PRE `webScope` unit compiled from
|
|
5
|
+
* `taskConfiguration.aiTaskProfile.webQueryTemplate` (Graphenix 2.7.x+).
|
|
9
6
|
*/
|
|
10
7
|
export declare function resolveNarrixForTaskNode(args: {
|
|
11
8
|
nodeMetadataNarrix: NarrixPreProcessorConfig | undefined;
|
|
12
9
|
graphVariablesNarrix: Partial<NarrixPreProcessorConfig> | undefined;
|
|
13
|
-
aiTaskProfile: AiTaskProfileMetadata | undefined;
|
|
14
10
|
}): ResolvedNarrixWirePayload | undefined;
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import { applyAiTaskProfileWebScopingToNarrix } from './applyAiTaskProfileWebScopingToNarrix.js';
|
|
2
1
|
/**
|
|
3
|
-
* Resolves
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Shared by {@link ExellixGraphRuntime}.
|
|
2
|
+
* Resolves discovery narrix for runtime plan patching: shallow-merge of node `taskConfiguration.narrix`
|
|
3
|
+
* with graph-level `variables.narrix`. Web scope is a PRE `webScope` unit compiled from
|
|
4
|
+
* `taskConfiguration.aiTaskProfile.webQueryTemplate` (Graphenix 2.7.x+).
|
|
8
5
|
*/
|
|
9
6
|
export function resolveNarrixForTaskNode(args) {
|
|
10
7
|
let resolved = args.nodeMetadataNarrix != null ? { ...args.nodeMetadataNarrix } : undefined;
|
|
@@ -15,5 +12,5 @@ export function resolveNarrixForTaskNode(args) {
|
|
|
15
12
|
...(resolved ?? {}),
|
|
16
13
|
};
|
|
17
14
|
}
|
|
18
|
-
return
|
|
15
|
+
return resolved;
|
|
19
16
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FinalizerExecutionPlan, NodeExecutionPlan } from '@x12i/graphenix-executable-contracts';
|
|
2
|
+
import type { NarrixPreProcessorConfig } from '../types/narrix.js';
|
|
2
3
|
import type { RunTaskModelConfigWire } from './graphAiModelConfig.js';
|
|
3
4
|
/** Resolve the frozen node plan required by `@exellix/ai-tasks` ≥ 9. */
|
|
4
5
|
export declare function resolveRunTaskNodePlan(args: {
|
|
@@ -10,6 +11,15 @@ export declare function resolveRunTaskNodePlan(args: {
|
|
|
10
11
|
}): NodeExecutionPlan;
|
|
11
12
|
/** Standalone {@link executeNode} without compile: patch MAIN unit selection from resolved graph profiles. */
|
|
12
13
|
export declare function patchNodePlanMainModelFromWire(plan: NodeExecutionPlan, wire: RunTaskModelConfigWire): NodeExecutionPlan;
|
|
14
|
+
/**
|
|
15
|
+
* Patches graphenix node-plan invoke pipeline with runtime narrix resolution (graph `variables.narrix`
|
|
16
|
+
* merge) and optional `taskConfiguration` narrixScope / mode / input when absent from compile.
|
|
17
|
+
*/
|
|
18
|
+
export declare function patchNodePlanRuntimePipeline(args: {
|
|
19
|
+
plan: NodeExecutionPlan;
|
|
20
|
+
resolvedNarrix?: NarrixPreProcessorConfig;
|
|
21
|
+
taskConfiguration?: Record<string, unknown>;
|
|
22
|
+
}): NodeExecutionPlan;
|
|
13
23
|
export declare function nodePlanFromFinalizerPlan(args: {
|
|
14
24
|
finalizerId: string;
|
|
15
25
|
utilityKey: string;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { minimalNodePlanForSkillKey } from '@exellix/ai-tasks';
|
|
2
|
+
function isPlainRecord(v) {
|
|
3
|
+
return v != null && typeof v === 'object' && !Array.isArray(v);
|
|
4
|
+
}
|
|
2
5
|
function mainSkillUnit(plan) {
|
|
3
6
|
return plan.executionUnits.find((u) => u.unitKind === 'mainSkill');
|
|
4
7
|
}
|
|
@@ -7,6 +10,102 @@ function selectionFromWireSlot(value) {
|
|
|
7
10
|
return undefined;
|
|
8
11
|
return { kind: 'profileChoice', key: value.trim() };
|
|
9
12
|
}
|
|
13
|
+
function resolveNarrativeMode(pipeline, tc) {
|
|
14
|
+
const explicit = pipeline.narrixMode ?? tc?.narrixMode;
|
|
15
|
+
if (explicit === 'preprocessor' || explicit === 'handler')
|
|
16
|
+
return explicit;
|
|
17
|
+
if (explicit === 'off')
|
|
18
|
+
return 'off';
|
|
19
|
+
if (pipeline.narrixInput != null || tc?.narrixInput != null)
|
|
20
|
+
return 'handler';
|
|
21
|
+
if (pipeline.narrix != null || tc?.narrix != null)
|
|
22
|
+
return 'preprocessor';
|
|
23
|
+
return 'off';
|
|
24
|
+
}
|
|
25
|
+
function planHasNarrativeUnit(plan) {
|
|
26
|
+
return plan.executionUnits.some((u) => u.unitKind === 'narrativePreprocessor' || u.unitKind === 'narrativeHandler');
|
|
27
|
+
}
|
|
28
|
+
function defaultPreActionSelection(plan) {
|
|
29
|
+
const preUtility = plan.executionUnits.find((u) => u.unitKind === 'externalPreUtility');
|
|
30
|
+
if (preUtility?.modelSelection) {
|
|
31
|
+
return { selection: preUtility.modelSelection, source: preUtility.modelSource ?? 'graphDefault' };
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
selection: { kind: 'profileChoice', key: 'cheap/default' },
|
|
35
|
+
source: 'graphDefault',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Standalone executeNode path: mirror Graphenix `buildNarrativeUnits` when plan was not compiled. */
|
|
39
|
+
function injectRuntimeNarrativeUnitsIfAbsent(plan, pipeline, taskConfiguration) {
|
|
40
|
+
if (planHasNarrativeUnit(plan))
|
|
41
|
+
return plan;
|
|
42
|
+
const mode = resolveNarrativeMode(pipeline, taskConfiguration);
|
|
43
|
+
if (mode === 'off')
|
|
44
|
+
return plan;
|
|
45
|
+
const narrix = isPlainRecord(pipeline.narrix) ? pipeline.narrix : {};
|
|
46
|
+
const narrixInput = isPlainRecord(pipeline.narrixInput) ? pipeline.narrixInput : {};
|
|
47
|
+
const attachToField = (typeof narrix.attachToField === 'string' && narrix.attachToField) ||
|
|
48
|
+
(typeof narrixInput.attachToField === 'string' && narrixInput.attachToField) ||
|
|
49
|
+
undefined;
|
|
50
|
+
const narrativeWriteKey = attachToField ?? 'executionMemory._narrative';
|
|
51
|
+
const datasetId = (typeof narrix.datasetId === 'string' && narrix.datasetId) ||
|
|
52
|
+
(typeof narrixInput.datasetId === 'string' && narrixInput.datasetId) ||
|
|
53
|
+
undefined;
|
|
54
|
+
const preModel = defaultPreActionSelection(plan);
|
|
55
|
+
const nodeId = plan.nodeId;
|
|
56
|
+
const narrativeContract = {
|
|
57
|
+
reads: ['input'],
|
|
58
|
+
writes: [
|
|
59
|
+
{
|
|
60
|
+
destination: 'execution',
|
|
61
|
+
key: narrativeWriteKey,
|
|
62
|
+
mode: 'merge',
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
};
|
|
66
|
+
const bumpedUnits = plan.executionUnits.map((u) => ({
|
|
67
|
+
...u,
|
|
68
|
+
order: u.order + 1,
|
|
69
|
+
}));
|
|
70
|
+
let narrativeUnit;
|
|
71
|
+
if (mode === 'preprocessor') {
|
|
72
|
+
narrativeUnit = {
|
|
73
|
+
unitId: `unit:${nodeId}:narrative:pre`,
|
|
74
|
+
nodeId,
|
|
75
|
+
unitKind: 'narrativePreprocessor',
|
|
76
|
+
invokeMode: 'narrativePreprocessor',
|
|
77
|
+
order: 0,
|
|
78
|
+
strategyKey: 'narrative-preprocessor',
|
|
79
|
+
modelSlot: 'preActionModel',
|
|
80
|
+
modelSelection: preModel.selection,
|
|
81
|
+
modelSource: preModel.source,
|
|
82
|
+
unitParams: { datasetId, attachToField },
|
|
83
|
+
invokeContract: narrativeContract,
|
|
84
|
+
sourcePath: '/runtime/narrativePreprocessor',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
narrativeUnit = {
|
|
89
|
+
unitId: `unit:${nodeId}:narrative:handler`,
|
|
90
|
+
nodeId,
|
|
91
|
+
unitKind: 'narrativeHandler',
|
|
92
|
+
invokeMode: 'narrativeHandler',
|
|
93
|
+
order: 0,
|
|
94
|
+
strategyKey: 'narrative-handler',
|
|
95
|
+
modelSlot: 'preActionModel',
|
|
96
|
+
modelSelection: preModel.selection,
|
|
97
|
+
modelSource: preModel.source,
|
|
98
|
+
terminal: true,
|
|
99
|
+
unitParams: { datasetId, attachToField, narrixInput },
|
|
100
|
+
invokeContract: narrativeContract,
|
|
101
|
+
sourcePath: '/runtime/narrativeHandler',
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
...plan,
|
|
106
|
+
executionUnits: [narrativeUnit, ...bumpedUnits],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
10
109
|
/** Resolve the frozen node plan required by `@exellix/ai-tasks` ≥ 9. */
|
|
11
110
|
export function resolveRunTaskNodePlan(args) {
|
|
12
111
|
if (args.nodePlan)
|
|
@@ -27,6 +126,39 @@ export function patchNodePlanMainModelFromWire(plan, wire) {
|
|
|
27
126
|
}
|
|
28
127
|
return next;
|
|
29
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Patches graphenix node-plan invoke pipeline with runtime narrix resolution (graph `variables.narrix`
|
|
131
|
+
* merge) and optional `taskConfiguration` narrixScope / mode / input when absent from compile.
|
|
132
|
+
*/
|
|
133
|
+
export function patchNodePlanRuntimePipeline(args) {
|
|
134
|
+
const tc = args.taskConfiguration;
|
|
135
|
+
const narrixScope = tc?.narrixScope;
|
|
136
|
+
const narrixMode = tc?.narrixMode;
|
|
137
|
+
const narrixInput = tc?.narrixInput;
|
|
138
|
+
const hasPatch = args.resolvedNarrix != null ||
|
|
139
|
+
(narrixScope != null && typeof narrixScope === 'object' && !Array.isArray(narrixScope)) ||
|
|
140
|
+
(typeof narrixMode === 'string' && narrixMode.trim() !== '') ||
|
|
141
|
+
narrixInput != null;
|
|
142
|
+
if (!hasPatch)
|
|
143
|
+
return args.plan;
|
|
144
|
+
const next = structuredClone(args.plan);
|
|
145
|
+
const ic = next.invokeContract ?? {};
|
|
146
|
+
const pipeline = { ...(isPlainRecord(ic.pipeline) ? ic.pipeline : {}) };
|
|
147
|
+
if (args.resolvedNarrix != null) {
|
|
148
|
+
pipeline.narrix = { ...args.resolvedNarrix };
|
|
149
|
+
}
|
|
150
|
+
if (narrixScope != null && typeof narrixScope === 'object' && !Array.isArray(narrixScope)) {
|
|
151
|
+
pipeline.narrixScope = narrixScope;
|
|
152
|
+
}
|
|
153
|
+
if (pipeline.narrixMode === undefined && typeof narrixMode === 'string' && narrixMode.trim() !== '') {
|
|
154
|
+
pipeline.narrixMode = narrixMode.trim();
|
|
155
|
+
}
|
|
156
|
+
if (pipeline.narrixInput === undefined && narrixInput != null) {
|
|
157
|
+
pipeline.narrixInput = narrixInput;
|
|
158
|
+
}
|
|
159
|
+
next.invokeContract = { ...ic, pipeline };
|
|
160
|
+
return injectRuntimeNarrativeUnitsIfAbsent(next, pipeline, tc);
|
|
161
|
+
}
|
|
30
162
|
export function nodePlanFromFinalizerPlan(args) {
|
|
31
163
|
const slots = args.finalizerPlan?.modelSlots;
|
|
32
164
|
const plan = minimalNodePlanForSkillKey(args.utilityKey, args.finalizerId);
|
|
@@ -32,17 +32,6 @@ function collectGraphEntryInputPaths(graphEntry) {
|
|
|
32
32
|
if (typeof p === 'string' && p.trim() !== '')
|
|
33
33
|
paths.add(p.trim());
|
|
34
34
|
}
|
|
35
|
-
for (const spec of graphEntry?.inputs ?? []) {
|
|
36
|
-
if (spec == null || typeof spec !== 'object')
|
|
37
|
-
continue;
|
|
38
|
-
const rec = spec;
|
|
39
|
-
if (rec.kind === 'execution')
|
|
40
|
-
continue;
|
|
41
|
-
if (rec.required === false)
|
|
42
|
-
continue;
|
|
43
|
-
if (typeof rec.path === 'string' && rec.path.trim() !== '')
|
|
44
|
-
paths.add(rec.path.trim());
|
|
45
|
-
}
|
|
46
35
|
return [...paths];
|
|
47
36
|
}
|
|
48
37
|
function executionInputWatchPaths(args) {
|
|
@@ -76,7 +65,7 @@ function pathRequiresDeclaredEntry(path, graphEntry) {
|
|
|
76
65
|
const declared = collectGraphEntryInputPaths(graphEntry);
|
|
77
66
|
if (declared.length > 0)
|
|
78
67
|
return true;
|
|
79
|
-
return (graphEntry.
|
|
68
|
+
return (graphEntry.requiredExecutionPaths?.length ?? 0) > 0;
|
|
80
69
|
}
|
|
81
70
|
/**
|
|
82
71
|
* Evaluates MAIN-readiness before invoking the skill. Paths resolve via {@link resolveGraphEngineMemoryPathValue}
|
|
@@ -12,7 +12,7 @@ import { resolveNarrixForTaskNode } from './resolveNarrixForTaskNode.js';
|
|
|
12
12
|
import { buildRunTaskIdentityEnvelope, shouldForwardRunTaskTraceMode, } from './runTaskAugments.js';
|
|
13
13
|
import { readExecutionVariableBuckets } from './executionVariableBuckets.js';
|
|
14
14
|
import { newGraphRunTaskId } from './graphRunIdentity.js';
|
|
15
|
-
import { patchNodePlanMainModelFromWire, resolveRunTaskNodePlan } from './runTaskNodePlan.js';
|
|
15
|
+
import { patchNodePlanMainModelFromWire, patchNodePlanRuntimePipeline, resolveRunTaskNodePlan } from './runTaskNodePlan.js';
|
|
16
16
|
function isPlainRecord(v) {
|
|
17
17
|
return v != null && typeof v === 'object' && !Array.isArray(v);
|
|
18
18
|
}
|
|
@@ -81,7 +81,11 @@ export async function buildTaskNodeRunTaskRequest(args) {
|
|
|
81
81
|
? { ...node.taskConfiguration.narrix }
|
|
82
82
|
: undefined,
|
|
83
83
|
graphVariablesNarrix: jobVariables?.narrix,
|
|
84
|
-
|
|
84
|
+
});
|
|
85
|
+
runTaskNodePlan = patchNodePlanRuntimePipeline({
|
|
86
|
+
plan: runTaskNodePlan,
|
|
87
|
+
resolvedNarrix: narrix ?? undefined,
|
|
88
|
+
taskConfiguration: node.taskConfiguration,
|
|
85
89
|
});
|
|
86
90
|
const metaTk = node.taskConfiguration?.taskKind;
|
|
87
91
|
const taskKindForward = metaTk === 'decision' || metaTk === 'utility' || metaTk === 'content' ? metaTk : undefined;
|
|
@@ -141,7 +145,6 @@ export async function buildTaskNodeRunTaskRequest(args) {
|
|
|
141
145
|
executionMemory: executionMemoryForWire,
|
|
142
146
|
jobContext,
|
|
143
147
|
prevNodeId: args.prevNodeId,
|
|
144
|
-
narrix: narrix ?? undefined,
|
|
145
148
|
...extractRunTaskStrategyOverrides(node.taskConfiguration),
|
|
146
149
|
outputValidation: outputValidationFromPlan ??
|
|
147
150
|
(ov != null && typeof ov === 'object' && !Array.isArray(ov) && 'schema' in ov
|
|
@@ -98,8 +98,9 @@ const LEGACY_METADATA_TO_TASK_CONFIGURATION = {
|
|
|
98
98
|
executionMemory: 'runtime.executionMemory',
|
|
99
99
|
outputsMemory: 'runtime.outputsMemory',
|
|
100
100
|
};
|
|
101
|
-
/** Web-scope keys are not allowed under `taskConfiguration.narrix`
|
|
101
|
+
/** Web-scope keys are not allowed under `taskConfiguration.narrix` (use `taskConfiguration.aiTaskProfile.webQueryTemplate`). */
|
|
102
102
|
const FORBIDDEN_NARRIX_WEB_KEYS = [
|
|
103
|
+
'enableWebScope',
|
|
103
104
|
'forceWebScope',
|
|
104
105
|
'webScopeQuestions',
|
|
105
106
|
'webScoping',
|
|
@@ -550,18 +551,16 @@ export function assertCanonicalTaskNode(node, context, options) {
|
|
|
550
551
|
}
|
|
551
552
|
const narrixTc = tc && isPlainObject(tc.narrix) ? tc.narrix : undefined;
|
|
552
553
|
if (narrixTc) {
|
|
553
|
-
if ('enableWebScope' in narrixTc &&
|
|
554
|
-
narrixTc.enableWebScope != null &&
|
|
555
|
-
typeof narrixTc.enableWebScope !== 'boolean') {
|
|
556
|
-
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `Task node "${String(node.id)}": taskConfiguration.narrix.enableWebScope must be a boolean when present.`, { jobId: context?.jobId, graphId: context?.graphId, nodeId: String(node.id), narrixWebKey: 'enableWebScope' });
|
|
557
|
-
}
|
|
558
554
|
for (const k of FORBIDDEN_NARRIX_WEB_KEYS) {
|
|
559
555
|
if (k in narrixTc) {
|
|
560
|
-
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `Task node "${String(node.id)}": taskConfiguration.narrix.${k} is forbidden — author web scope under taskConfiguration.aiTaskProfile.
|
|
556
|
+
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `Task node "${String(node.id)}": taskConfiguration.narrix.${k} is forbidden — author web scope under taskConfiguration.aiTaskProfile.webQueryTemplate.`, { jobId: context?.jobId, graphId: context?.graphId, nodeId: String(node.id), narrixWebKey: k });
|
|
561
557
|
}
|
|
562
558
|
}
|
|
563
559
|
}
|
|
564
560
|
const profile = tc && isPlainObject(tc.aiTaskProfile) ? tc.aiTaskProfile : undefined;
|
|
561
|
+
if (profile && 'webScoping' in profile && profile.webScoping != null) {
|
|
562
|
+
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `Task node "${String(node.id)}": taskConfiguration.aiTaskProfile.webScoping is not allowed — use webQueryTemplate (Rendrix string).`, { jobId: context?.jobId, graphId: context?.graphId, nodeId: String(node.id) });
|
|
563
|
+
}
|
|
565
564
|
const inputSynthesis = profile && isPlainObject(profile.inputSynthesis) ? profile.inputSynthesis : undefined;
|
|
566
565
|
if (inputSynthesis && 'alsoWriteLegacySynthesizedContext' in inputSynthesis) {
|
|
567
566
|
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `Task node "${String(node.id)}": taskConfiguration.aiTaskProfile.inputSynthesis.alsoWriteLegacySynthesizedContext is removed in 5.x.`, { jobId: context?.jobId, graphId: context?.graphId, nodeId: String(node.id) });
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Optional task metadata aligned with `@x12i/graph-composer` authoring / `reportTaskNodeProtocolGaps`.
|
|
3
|
-
*
|
|
4
|
-
* when `enabled: true` and `questions` is non-empty (see `applyAiTaskProfileWebScopingToNarrix`).
|
|
3
|
+
* Web scope is authored via {@link AiTaskProfileMetadata.webQueryTemplate} (Graphenix 2.7.3+ PRE `webScope` unit).
|
|
5
4
|
*/
|
|
6
|
-
export type AiTaskProfileWebScoping = {
|
|
7
|
-
enabled: boolean;
|
|
8
|
-
/** When `enabled` is `true`, must be a non-empty array of non-empty strings (graph-composer contract). */
|
|
9
|
-
questions?: string[];
|
|
10
|
-
};
|
|
11
5
|
export type AiTaskProfileInputSynthesis = {
|
|
12
6
|
enabled: boolean;
|
|
13
7
|
/** Memory paths fed to PRE `synthesized-context` when `enabled` (validated against graph-engine allowlist). */
|
|
@@ -23,19 +17,20 @@ export type AiTaskProfileInputSynthesis = {
|
|
|
23
17
|
strategyKey?: string;
|
|
24
18
|
};
|
|
25
19
|
/**
|
|
26
|
-
* PRE/POST strategies, optional web
|
|
20
|
+
* PRE/POST strategies, optional web query template, optional input synthesis for AI task nodes.
|
|
27
21
|
* `@x12i/graph-composer` requires `preStrategyKey` / `postStrategyKey` for validated AI nodes.
|
|
28
|
-
* Exellix-graph-engine merges `webScoping` into the resolved narrix payload for execution when active.
|
|
29
|
-
*
|
|
30
|
-
* **Runtime (graph-engine ≥5):** When `preStrategyKey` / `postStrategyKey` are non-empty strings, the executor issues
|
|
31
|
-
* additional `@exellix/ai-tasks` **`runTask`** calls (utility `skillKey`) **before** and **after** the MAIN node task — graph-engine does **not** call the Xynthesis package directly. Results are merged under **`execution.xynthesis.pre`** / **`execution.xynthesis.post`** (historical execution-memory slot names). This is separate from `executionPipeline` PRE `synthesized-context`, which still runs inside a single ai-tasks `runTask`.
|
|
32
22
|
*
|
|
33
|
-
*
|
|
34
|
-
* `
|
|
23
|
+
* **Web scope (Graphenix 2.7.3+):** Non-empty `webQueryTemplate` (or `webQueryTemplates[]`) compiles to a PRE
|
|
24
|
+
* `webScope` unit; graph-engine does not merge web intent into `taskConfiguration.narrix`.
|
|
35
25
|
*/
|
|
36
26
|
export type AiTaskProfileMetadata = {
|
|
37
27
|
preStrategyKey?: string;
|
|
38
28
|
postStrategyKey?: string;
|
|
39
|
-
|
|
29
|
+
/** Rendrix template for PRE webScope (e.g. `Patch status for {{input.cveId}}?`). */
|
|
30
|
+
webQueryTemplate?: string;
|
|
31
|
+
/** Pack-mode web query templates (`webQueryTemplate` optional when non-empty). */
|
|
32
|
+
webQueryTemplates?: string[];
|
|
33
|
+
webScopeOptions?: Record<string, unknown>;
|
|
40
34
|
inputSynthesis?: AiTaskProfileInputSynthesis;
|
|
41
35
|
};
|
|
36
|
+
export declare function hasWebScopeAuthoring(profile: AiTaskProfileMetadata | undefined): boolean;
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Optional task metadata aligned with `@x12i/graph-composer` authoring / `reportTaskNodeProtocolGaps`.
|
|
3
|
-
*
|
|
4
|
-
* when `enabled: true` and `questions` is non-empty (see `applyAiTaskProfileWebScopingToNarrix`).
|
|
3
|
+
* Web scope is authored via {@link AiTaskProfileMetadata.webQueryTemplate} (Graphenix 2.7.3+ PRE `webScope` unit).
|
|
5
4
|
*/
|
|
6
|
-
export {
|
|
5
|
+
export function hasWebScopeAuthoring(profile) {
|
|
6
|
+
if (profile == null)
|
|
7
|
+
return false;
|
|
8
|
+
if (typeof profile.webQueryTemplate === 'string' && profile.webQueryTemplate.trim().length > 0) {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
const pack = profile.webQueryTemplates;
|
|
12
|
+
return (Array.isArray(pack) &&
|
|
13
|
+
pack.some((entry) => typeof entry === 'string' && entry.trim().length > 0));
|
|
14
|
+
}
|
|
@@ -1,22 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/** Canonical Narrix invocation modes (Graphenix plan / ai-tasks narrative units). */
|
|
2
|
+
export type NarrixModeKey = 'off' | 'preprocessor' | 'handler';
|
|
3
|
+
/** Filters Narrix handler output before writing to task memory. */
|
|
4
|
+
export type NarrixScope = {
|
|
5
|
+
includeSignals?: string[];
|
|
6
|
+
excludeSignals?: string[];
|
|
7
|
+
includeStories?: string[];
|
|
8
|
+
excludeStories?: string[];
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
};
|
|
11
|
+
/** Structured Narrix handler input (ai-tasks `NarrixRunInput` shape). */
|
|
12
|
+
export type NarrixRunInput = ({
|
|
13
|
+
medium: 'record';
|
|
14
|
+
datasetId: string;
|
|
15
|
+
record: Record<string, unknown>;
|
|
16
|
+
} & Record<string, unknown>) | ({
|
|
17
|
+
medium: 'text';
|
|
18
|
+
datasetId: string;
|
|
19
|
+
text: string;
|
|
20
|
+
} & Record<string, unknown>) | ({
|
|
21
|
+
medium: 'docs';
|
|
22
|
+
datasetId: string;
|
|
23
|
+
document: Record<string, unknown>;
|
|
24
|
+
} & Record<string, unknown>) | ({
|
|
25
|
+
medium: 'chat';
|
|
26
|
+
datasetId: string;
|
|
27
|
+
thread: Record<string, unknown>;
|
|
28
|
+
} & Record<string, unknown>);
|
|
4
29
|
/**
|
|
5
|
-
*
|
|
6
|
-
* `
|
|
30
|
+
* Resolved discovery narrix after merging node `taskConfiguration.narrix` with graph-level
|
|
31
|
+
* `variables.narrix`. Patched onto `nodePlan.invokeContract.pipeline.narrix` before `runTask`.
|
|
7
32
|
*/
|
|
8
|
-
export type ResolvedNarrixWirePayload =
|
|
9
|
-
/** One web-scope question on the resolved narrix wire payload; produced from `aiTaskProfile.webScoping.questions`. */
|
|
10
|
-
export type WebScopeQuestion = {
|
|
11
|
-
id: string;
|
|
12
|
-
question: string;
|
|
13
|
-
purpose?: string;
|
|
14
|
-
};
|
|
33
|
+
export type ResolvedNarrixWirePayload = NarrixPreProcessorConfig;
|
|
15
34
|
/**
|
|
16
|
-
* Authoring shape for `taskConfiguration.narrix` on a graph task node.
|
|
17
|
-
*
|
|
18
|
-
* Discovery fields plus `enableWebScope` (forwarded to `RunTaskRequest.narrix.enableWebScope`).
|
|
19
|
-
* Question packs for web scoping may also be supplied via `taskConfiguration.aiTaskProfile.webScoping`.
|
|
35
|
+
* Authoring shape for `taskConfiguration.narrix` on a graph task node (discovery fields only).
|
|
36
|
+
* Web scope is authored on `taskConfiguration.aiTaskProfile.webQueryTemplate` (Graphenix 2.7.3+).
|
|
20
37
|
*/
|
|
21
38
|
export type NarrixPreProcessorConfig = {
|
|
22
39
|
/** Which pack and entity type the engine runs (e.g. `"network.vuln.instances"`). */
|
|
@@ -30,13 +47,6 @@ export type NarrixPreProcessorConfig = {
|
|
|
30
47
|
narrativeTypeIds?: string[];
|
|
31
48
|
/** Framework question this node answers (e.g. q0, q1, q2, q3, q5, q6). */
|
|
32
49
|
questionId?: string;
|
|
33
|
-
/** When true, ai-tasks runs `@exellix/narrix-web-scoper` after CNI and writes `executionMemory.webContext`. */
|
|
34
|
-
enableWebScope?: boolean;
|
|
35
|
-
/**
|
|
36
|
-
* When true with `enableWebScope`, skip internal web-scoper if `executionMemory.webContextMarkdown` is already set.
|
|
37
|
-
* See `@exellix/ai-tasks` `NarrixPreProcessorConfig.skipWebScopeWhenExternalWebMarkdownPresent`.
|
|
38
|
-
*/
|
|
39
|
-
skipWebScopeWhenExternalWebMarkdownPresent?: boolean;
|
|
40
50
|
/** Execution memory key for the Narrix attachment (default `_narrix` in ai-tasks). */
|
|
41
51
|
attachToField?: string;
|
|
42
52
|
};
|
|
@@ -63,4 +73,3 @@ export interface LocalTaskContext {
|
|
|
63
73
|
masterSkillId?: string;
|
|
64
74
|
masterSkillActivityId?: string;
|
|
65
75
|
}
|
|
66
|
-
export {};
|
package/dist/src/types/refs.d.ts
CHANGED
|
@@ -528,63 +528,12 @@ export type DiscoveryDefinitionCatalogRequest = DiscoveryDefinitionCatalogReques
|
|
|
528
528
|
* Declares a required catalog item that does not exist yet (gap / feature request).
|
|
529
529
|
*/
|
|
530
530
|
export type CatalogRequestEntry = ScopedQuestionCatalogRequestEntry | DiscoveryDefinitionCatalogRequestEntry;
|
|
531
|
-
/**
|
|
532
|
-
* Semantic class of data a graph expects at Layer 01. Combinations are represented
|
|
533
|
-
* by multiple `GraphEntryInputSpec` entries on `metadata.graphEntry.inputs`.
|
|
534
|
-
*/
|
|
535
|
-
export type GraphEntryInputKind = 'record' | 'query' | 'user-input' | 'content' | 'metadata' | 'execution';
|
|
536
|
-
export type GraphEntryValueInputKind = Exclude<GraphEntryInputKind, 'execution'>;
|
|
537
|
-
/**
|
|
538
|
-
* Design-time declaration shared by graph entry inputs.
|
|
539
|
-
* **Declarative only:** core runtime does not validate or coerce these specs.
|
|
540
|
-
*/
|
|
541
|
-
export type GraphEntryInputSpecBase = {
|
|
542
|
-
/** Semantic input class, e.g. `record`, `query`, `user-input`, `content`, or `execution`. */
|
|
543
|
-
kind: GraphEntryInputKind;
|
|
544
|
-
/** Stable name for tooling/templates when the path is not enough. */
|
|
545
|
-
name?: string;
|
|
546
|
-
/** One-line label for graph authors and UIs. */
|
|
547
|
-
title?: string;
|
|
548
|
-
/** Longer human-readable description for editors, catalogs, and docs. */
|
|
549
|
-
description?: string;
|
|
550
|
-
/** Defaults to true for authoring intent; runtime does not enforce it today. */
|
|
551
|
-
required?: boolean;
|
|
552
|
-
/** Optional JSON Schema for this specific input value. Validators may use it; core runtime does not. */
|
|
553
|
-
schema?: Record<string, unknown>;
|
|
554
|
-
[key: string]: unknown;
|
|
555
|
-
};
|
|
556
|
-
/**
|
|
557
|
-
* Design-time declaration of one caller-supplied value in the graph execution object.
|
|
558
|
-
* Paths are dot-paths under the merged `execution` object, usually `input.*`.
|
|
559
|
-
*/
|
|
560
|
-
export type GraphEntryValueInputSpec = GraphEntryInputSpecBase & {
|
|
561
|
-
kind: GraphEntryValueInputKind;
|
|
562
|
-
/** Dot-path under merged `execution` where this input is expected, e.g. `input.subnetId`. */
|
|
563
|
-
path: string;
|
|
564
|
-
};
|
|
565
|
-
/**
|
|
566
|
-
* Design-time declaration of a prior graph execution used as this graph's input.
|
|
567
|
-
* A host/orchestrator can use `graphId` and `metadataFilter` to find the source execution,
|
|
568
|
-
* then materialize it at `path` before calling `executeGraph`.
|
|
569
|
-
*/
|
|
570
|
-
export type GraphEntryExecutionInputSpec = Omit<GraphEntryInputSpecBase, 'kind'> & {
|
|
571
|
-
kind: 'execution';
|
|
572
|
-
/** Source graph id whose completed execution should be used as input. */
|
|
573
|
-
graphId: string;
|
|
574
|
-
/** Metadata fields used by host tooling to select the matching source execution. */
|
|
575
|
-
metadataFilter?: Record<string, unknown>;
|
|
576
|
-
/** Dot-path under merged `execution` where the selected graph execution is expected. */
|
|
577
|
-
path?: string;
|
|
578
|
-
};
|
|
579
|
-
/**
|
|
580
|
-
* Design-time declaration of one graph entry input.
|
|
581
|
-
*/
|
|
582
|
-
export type GraphEntryInputSpec = GraphEntryValueInputSpec | GraphEntryExecutionInputSpec;
|
|
583
531
|
/**
|
|
584
532
|
* Layer 01 (graph entry): declares what callers should supply in `ExecuteGraphOptions.execution`
|
|
585
533
|
* after merge — the seed for `execution.input.*` and related paths first-wave nodes read.
|
|
586
534
|
* **Declarative only:** `executeGraph` does not validate this object; it is exposed via
|
|
587
535
|
* `variables.__graphModel` for tools, composers, and documentation (see `.docs/graph-io-visibility.md`).
|
|
536
|
+
* Data source selection is an operator/work-factory concern (jobs-ui), not a graph document field.
|
|
588
537
|
*/
|
|
589
538
|
export type GraphEntryContract = {
|
|
590
539
|
/** One-line summary for authors and UIs. */
|
|
@@ -599,11 +548,6 @@ export type GraphEntryContract = {
|
|
|
599
548
|
* `input.entityId`, `input.vulnerabilityId`). Same convention as `metadata.entityIdPath` roots.
|
|
600
549
|
*/
|
|
601
550
|
requiredExecutionPaths?: string[];
|
|
602
|
-
/**
|
|
603
|
-
* Semantic entry inputs this graph is designed to work with. Use multiple entries for combinations
|
|
604
|
-
* (for example, `record` + `query`).
|
|
605
|
-
*/
|
|
606
|
-
inputs?: GraphEntryInputSpec[];
|
|
607
551
|
/**
|
|
608
552
|
* Optional JSON Schema (e.g. draft 2020-12) for the merged `execution` object. Validators may use it; core runtime does not.
|
|
609
553
|
*/
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { InputStrategyKey, LlmCallConfig,
|
|
1
|
+
import type { InputStrategyKey, LlmCallConfig, RunTaskRequest } from '@exellix/ai-tasks';
|
|
2
|
+
import type { NarrixModeKey, NarrixRunInput, NarrixScope } from './narrix.js';
|
|
2
3
|
import type { ExecutionStrategyInvocation, TaskStrategyItemData } from './aiTasksDerivedTypes.js';
|
|
3
4
|
import type { AiTaskProfileMetadata } from './aiTaskProfile.js';
|
|
4
5
|
import type { ModelConfigSelection } from './refs.js';
|
|
@@ -49,7 +50,7 @@ export type TaskNodeTaskConfiguration = {
|
|
|
49
50
|
identity?: Record<string, unknown>;
|
|
50
51
|
runTaskIdentity?: Record<string, unknown>;
|
|
51
52
|
memoryKey?: string;
|
|
52
|
-
narrixScope?:
|
|
53
|
+
narrixScope?: NarrixScope;
|
|
53
54
|
aiScoping?: RunTaskRequest['aiScoping'];
|
|
54
55
|
aiScopingOptions?: RunTaskRequest['aiScopingOptions'];
|
|
55
56
|
timeoutMs?: number;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# graphs-studio — Graphenix 2.7.3 alignment handoff
|
|
2
|
+
|
|
3
|
+
Shipped with **@exellix/graph-engine@8.6.0** / **@exellix/graph-composer@2.12.0**.
|
|
4
|
+
|
|
5
|
+
Full checklist: see Exellix monorepo `docs/handoff/graphs-studio-graphenix-2.7.3.md` (canonical copy). Summary:
|
|
6
|
+
|
|
7
|
+
1. **Web scope:** `taskConfiguration.aiTaskProfile.webQueryTemplate` — remove `webScoping` and narrix web keys.
|
|
8
|
+
2. **Graph entry:** no `graphEntry.inputs` (CR-006); use `requiredExecutionPaths` + jobs-ui data sources.
|
|
9
|
+
3. **Persistency:** `response.persistency` with `newRecord` + optional `link` (Graphenix 2.7.3).
|
|
10
|
+
4. **Preflight:** show `nodePlan.invokeContract.pipeline`, not root `RunTaskRequest.narrix`.
|
|
11
|
+
5. **CRS-FRS-005:** PRE synthesis export parity still pending in studio (separate track).
|
|
12
|
+
|
|
13
|
+
Pin: `graph-engine ^8.6.0`, `graph-composer ^2.12.0`, `ai-tasks ^10.0.12+`, `graphenix ^2.7.3`.
|