@exellix/graph-engine 7.4.3 → 7.5.1
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/README.md +17 -4
- package/dist/src/index.d.ts +6 -5
- package/dist/src/index.js +4 -3
- package/dist/src/inspection/graphModelSelection.d.ts +33 -0
- package/dist/src/inspection/graphModelSelection.js +108 -0
- package/dist/src/inspection/index.d.ts +2 -0
- package/dist/src/inspection/index.js +1 -0
- package/dist/src/inspection/nodeInspection.js +2 -4
- package/dist/src/runtime/ExellixGraphRuntime.js +1 -3
- package/dist/src/runtime/finalizers/executeFinalizer.js +8 -12
- package/dist/src/runtime/finalizers/validateFinalizer.js +79 -35
- package/dist/src/runtime/graphModelStudioSeparation.d.ts +43 -0
- package/dist/src/runtime/graphModelStudioSeparation.js +102 -0
- package/dist/src/runtime/graphResponseMapping.js +2 -3
- package/dist/src/runtime/materializeStructuredRunTaskInput.d.ts +0 -5
- package/dist/src/runtime/materializeStructuredRunTaskInput.js +0 -18
- package/dist/src/runtime/readTaskNodeInputsConfig.d.ts +2 -13
- package/dist/src/runtime/readTaskNodeInputsConfig.js +4 -5
- package/dist/src/runtime/resolveTaskNodeInputs.d.ts +1 -3
- package/dist/src/runtime/resolveTaskNodeInputs.js +2 -27
- package/dist/src/runtime/taskNodeRunTaskPreflight.js +1 -2
- package/dist/src/runtime/validateCanonicalGraphDocument.js +50 -0
- package/dist/src/types/refs.d.ts +30 -36
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -383,12 +383,25 @@ Implemented deterministic finalizer types:
|
|
|
383
383
|
|
|
384
384
|
Build an object by mapping named inputs (from `executionMemoryPath` or literals) into output keys.
|
|
385
385
|
|
|
386
|
-
#### `aggregate` — `strategy: "report-schema"` (
|
|
386
|
+
#### `aggregate` — `strategy: "report-schema"` (output object assembled from run memory)
|
|
387
387
|
|
|
388
|
-
Builds
|
|
388
|
+
Builds the finalizer output object: **`config.sections`** maps each **output field key** to a memory read, using the same `{ type, path }` idiom as finalizer `inputs` and `response.shape` selectors:
|
|
389
389
|
|
|
390
|
-
|
|
391
|
-
|
|
390
|
+
```jsonc
|
|
391
|
+
"config": {
|
|
392
|
+
"strategy": "report-schema",
|
|
393
|
+
"sections": {
|
|
394
|
+
"q1": { "type": "executionMemoryPath", "path": "answers.q1", "optional": true },
|
|
395
|
+
"summary": { "type": "outputsMemoryPath", "path": "report.summary" }
|
|
396
|
+
},
|
|
397
|
+
"collectEpistemicTags": true
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
- Each section: **`type`** (`executionMemoryPath` | `outputsMemoryPath`), **`path`**, and **`optional`** (when `true`, missing values become `null` instead of throwing).
|
|
402
|
+
- **`collectEpistemicTags`**: when `true`, walks all section values and collects unique epistemic strings among `CONFIRMED`, `INFERRED`, `ASSUMED`, `UNKNOWN` into the **`collectedTags`** output field (array).
|
|
403
|
+
|
|
404
|
+
> **Breaking (model v8):** `sections[].path: string` → `sections[].{ type, path }`; `collect_tags` → `collectEpistemicTags`; output `collected_tags` → `collectedTags`. Section `title` and the literal-merge `meta` block were **removed** — authoring titles and studio hints belong in the studio document, and literal output fields belong in `graph.response.shape`. The validator rejects the old keys with a migration message.
|
|
392
405
|
|
|
393
406
|
Bundled graphs under [`graphs/`](graphs/) (see [graphs/README.md](graphs/README.md)) use this strategy for multi-section reports, sometimes after **`scoped-answer-assembler`** and **`scoped-answer-writer`** persistence (typical store: **`x-scoped-data`** in deployments that use that collection). The graph file shape is defined in [.docs/exellix-graph-engine-format.md](.docs/exellix-graph-engine-format.md).
|
|
394
407
|
|
package/dist/src/index.d.ts
CHANGED
|
@@ -33,7 +33,6 @@ export { readExecutionVariableBuckets, seedGraphVariableBucketsOnExecution, mirr
|
|
|
33
33
|
export type { ExecutionVariableBuckets } from './runtime/executionVariableBuckets.js';
|
|
34
34
|
export { resolveTaskNodeInputsForRunTask, resolveTaskNodeTaskVariableForRunTask, resolveTaskNodeInputBindingsForRunTask, } from './runtime/resolveTaskNodeInputs.js';
|
|
35
35
|
export { readTaskNodeInputsConfig, readTaskNodeModelInputSurface, isNodeInputsConfigSelectorType, } from './runtime/readTaskNodeInputsConfig.js';
|
|
36
|
-
export type { TaskNodeInputsConfigCarrier } from './runtime/readTaskNodeInputsConfig.js';
|
|
37
36
|
export { mergeMemory } from './runtime/memory.js';
|
|
38
37
|
export { selectByPath, writeByPath, parsePath } from './runtime/pathExpr.js';
|
|
39
38
|
export type { PathSegment } from './runtime/pathExpr.js';
|
|
@@ -51,7 +50,7 @@ export { assertGraphAiProfileNameString as assertGraphModelAliasString } from '.
|
|
|
51
50
|
export { isGraphAiModelConfig, isModelConfigSelection, isEmptyConditionWhen, conditionWhenSignature, countDefaultModelConfigCases, } from './runtime/modelConfigSelection.js';
|
|
52
51
|
export { GRAPH_ENGINE_MEMORY_PATH_ROOTS, splitGraphEngineMemoryPath, isAllowedGraphEngineMemoryPath, graphEngineMemoryPathValidationMessage, } from './runtime/graphEngineMemoryPaths.js';
|
|
53
52
|
export { buildRunTaskMainInput, extractCallerInputsBag, buildGraphEngineMemoryResolutionRoot, buildGraphEngineMemoryResolutionRootFromWorkingMemory, resolveGraphEngineMemoryPathValue, } from './runtime/resolveGraphEngineMemoryPaths.js';
|
|
54
|
-
export {
|
|
53
|
+
export { mirrorStructuredInputOntoExecutionMemory, } from './runtime/materializeStructuredRunTaskInput.js';
|
|
55
54
|
export { buildRunTaskTaskConfigurationForward } from './runtime/buildRunTaskTaskConfigurationForward.js';
|
|
56
55
|
export { normalizeSmartInputPath, collectSmartInputPaths, normalizeSmartInputConfigForRunTask, } from './runtime/smartInputPaths.js';
|
|
57
56
|
export { evaluateTaskNodeMainReadiness, buildMainReadinessResolutionRoot, resolveMainReadinessPolicy, collectInputSynthesisSourcePaths, } from './runtime/taskNodeMainReadiness.js';
|
|
@@ -79,7 +78,9 @@ export type { RunTaskValidationIssue, RunTaskValidationResult, RunTaskInvokeVali
|
|
|
79
78
|
export { buildExellixGraphRuntimeObjects, setRuntimeObjectsLastJobId, summarizeRuntimeObjectsForPlayground, EXELLIX_GRAPH_RUNTIME_PACKAGE_NAME, EXELLIX_AI_TASKS_PACKAGE_NAME, } from './runtime/runtimeObjects.js';
|
|
80
79
|
export type { ActivixQueryableClient, LogxerQueryableClient, LogxerLogLine, PackageRuntimeObjects, RuntimeObjects, BuildExellixGraphRuntimeObjectsInput, RuntimeObjectsObservabilitySummary, } from './runtime/runtimeObjects.js';
|
|
81
80
|
export type { GetJobLogsInput, GetJobLogsResult, QueryableLogLine } from '@x12i/logxer';
|
|
82
|
-
export { assertCanonicalGraphDocument, getCanonicalGraphDocumentViolations, CANONICAL_GRAPH_TOP_LEVEL_KEYS, } from './runtime/validateCanonicalGraphDocument.js';
|
|
81
|
+
export { assertCanonicalGraphDocument, assertCanonicalTaskNode, getCanonicalGraphDocumentViolations, CANONICAL_GRAPH_TOP_LEVEL_KEYS, } from './runtime/validateCanonicalGraphDocument.js';
|
|
82
|
+
export { GRAPH_ENTRY_STUDIO_ONLY_KEYS, GRAPH_METADATA_STUDIO_ONLY_KEYS, stripGraphModelStudioFields, primaryRuntimeInputFromStudioDocument, getGraphEntryStudioOnlyKeyViolations, getGraphMetadataStudioOnlyKeyViolations, getGraphEntryEmptyInputPathViolations, } from './runtime/graphModelStudioSeparation.js';
|
|
83
|
+
export type { GraphStudioDocument } from './runtime/graphModelStudioSeparation.js';
|
|
83
84
|
export { computeGraphDocumentContentSha256, stableStringifyGraphDocument, } from './runtime/graphDocumentFingerprint.js';
|
|
84
85
|
export { migrateLegacyGraphResponse, migrateLegacyGraphResponseDefinition, } from './runtime/graphResponseMigration.js';
|
|
85
86
|
export type { MigrateGraphResponseResult } from './runtime/graphResponseMigration.js';
|
|
@@ -106,7 +107,7 @@ export type { GraphCatalogValidationIssue } from './integrations/cataloxGraphCat
|
|
|
106
107
|
export { composeEventEmitters } from './runtime/events.js';
|
|
107
108
|
export { createPlaygroundReporter } from './playground/index.js';
|
|
108
109
|
export type { CreatePlaygroundReporterOptions, PlaygroundDebugArtifact, PlaygroundDebugArtifactKind, PlaygroundDebugSnapshot, PlaygroundReporter, PlaygroundStep, } from './playground/PlaygroundReporter.js';
|
|
109
|
-
export { getNodeScopingQuestion, getNodeMemoryShape, getNodeNarrixDiscovery, fetchNodeScopingData, inspectNode, resolveNodeSkillKey, getGraphNodes, getGraphCatalogs, inspectGraph, collectPredicatePaths, executionMemoryPathTail, executionMemoryTailsMatch, getNodeExecutionMemoryWriteTails, getNodeControlDependencies, getNodeSideEffects, inspectFinalizer, inspectNodeContract, inspectGraphContracts, EXELLIX_VIRTUAL_GRAPH_ENTRY_NODE_ID, EXELLIX_VIRTUAL_GRAPH_RESPONSE_NODE_ID, EXELLIX_VIRTUAL_BOUNDARY_NODE_METADATA_KEY, EXELLIX_VIRTUAL_BOUNDARY_EDGE_FLAG_KEY, getVirtualGraphEntryLayer, getVirtualGraphResponseLayer, materializeVirtualBoundaryDiagram, stripMaterializedVirtualBoundaryDiagram, validateCatalogPlanning, isCatalogBinding, isCatalogRequestEntry, } from './inspection/index.js';
|
|
110
|
-
export type { NodeScopingQuestion, NodeScopeSource, NodeScopeTarget, NodeScopingData, NodeMemoryShape, MemoryPath, NodeNarrixDiscovery, NodeInspection, GraphInspection, NodeIOEdge, GraphCatalogs, CatalogBindingSummary, CatalogPlanningValidationIssue, GraphVirtualIO, GraphVirtualIONode, GraphVirtualIOEdge, GraphVirtualIONodeRole, VirtualGraphEntryLayer, VirtualGraphResponseLayer, MaterializeVirtualBoundaryDiagramOptions, MaterializedVirtualBoundaryDiagram, MaterializedVirtualBoundaryDiagramMeta, InspectNodeContractOptions, LocalSkillKey, NodeExecutionType, FactProvenance, ProvenancedSection, NodeInputBindingEntry, ScopedDataDependency, NodeInputContract, ExecutionMemoryWriteSpec, NodeOutputContract, NodeSideEffects, ControlBranchEntry, NodeControlContract, NodeValidationContract, FinalizerContractInspection, NodeContractInspection, GraphContractsInspection, } from './inspection/index.js';
|
|
110
|
+
export { getNodeScopingQuestion, getNodeMemoryShape, getNodeNarrixDiscovery, fetchNodeScopingData, inspectNode, resolveNodeSkillKey, getGraphNodes, getGraphCatalogs, inspectGraph, collectPredicatePaths, executionMemoryPathTail, executionMemoryTailsMatch, getNodeExecutionMemoryWriteTails, getNodeControlDependencies, getNodeSideEffects, inspectFinalizer, inspectNodeContract, inspectGraphContracts, EXELLIX_VIRTUAL_GRAPH_ENTRY_NODE_ID, EXELLIX_VIRTUAL_GRAPH_RESPONSE_NODE_ID, EXELLIX_VIRTUAL_BOUNDARY_NODE_METADATA_KEY, EXELLIX_VIRTUAL_BOUNDARY_EDGE_FLAG_KEY, getVirtualGraphEntryLayer, getVirtualGraphResponseLayer, materializeVirtualBoundaryDiagram, stripMaterializedVirtualBoundaryDiagram, validateCatalogPlanning, isCatalogBinding, isCatalogRequestEntry, inspectGraphModelSelection, } from './inspection/index.js';
|
|
111
|
+
export type { GraphModelSelectionInspection, NodeModelSelection, ModelSelectionSource, NodeScopingQuestion, NodeScopeSource, NodeScopeTarget, NodeScopingData, NodeMemoryShape, MemoryPath, NodeNarrixDiscovery, NodeInspection, GraphInspection, NodeIOEdge, GraphCatalogs, CatalogBindingSummary, CatalogPlanningValidationIssue, GraphVirtualIO, GraphVirtualIONode, GraphVirtualIOEdge, GraphVirtualIONodeRole, VirtualGraphEntryLayer, VirtualGraphResponseLayer, MaterializeVirtualBoundaryDiagramOptions, MaterializedVirtualBoundaryDiagram, MaterializedVirtualBoundaryDiagramMeta, InspectNodeContractOptions, LocalSkillKey, NodeExecutionType, FactProvenance, ProvenancedSection, NodeInputBindingEntry, ScopedDataDependency, NodeInputContract, ExecutionMemoryWriteSpec, NodeOutputContract, NodeSideEffects, ControlBranchEntry, NodeControlContract, NodeValidationContract, FinalizerContractInspection, NodeContractInspection, GraphContractsInspection, } from './inspection/index.js';
|
|
111
112
|
export type { NarrixPreProcessorConfig, WebScopeQuestion, LocalTaskHandler, LocalTaskContext } from './types/narrix.js';
|
|
112
113
|
export type { ScopedDataReaderConfig, ScopedDataReaderOutput, ScopedDataReaderPackOutput, ScopedAnswerAssemblerConfig, ScopedAnswerAssemblerOutput, ScopedAnswerWriterConfig, ScopedAnswerWriterOutput, } from './runtime/localSkills/index.js';
|
package/dist/src/index.js
CHANGED
|
@@ -40,7 +40,7 @@ export { assertGraphAiProfileNameString as assertGraphModelAliasString } from '.
|
|
|
40
40
|
export { isGraphAiModelConfig, isModelConfigSelection, isEmptyConditionWhen, conditionWhenSignature, countDefaultModelConfigCases, } from './runtime/modelConfigSelection.js';
|
|
41
41
|
export { GRAPH_ENGINE_MEMORY_PATH_ROOTS, splitGraphEngineMemoryPath, isAllowedGraphEngineMemoryPath, graphEngineMemoryPathValidationMessage, } from './runtime/graphEngineMemoryPaths.js';
|
|
42
42
|
export { buildRunTaskMainInput, extractCallerInputsBag, buildGraphEngineMemoryResolutionRoot, buildGraphEngineMemoryResolutionRootFromWorkingMemory, resolveGraphEngineMemoryPathValue, } from './runtime/resolveGraphEngineMemoryPaths.js';
|
|
43
|
-
export {
|
|
43
|
+
export { mirrorStructuredInputOntoExecutionMemory, } from './runtime/materializeStructuredRunTaskInput.js';
|
|
44
44
|
export { buildRunTaskTaskConfigurationForward } from './runtime/buildRunTaskTaskConfigurationForward.js';
|
|
45
45
|
export { normalizeSmartInputPath, collectSmartInputPaths, normalizeSmartInputConfigForRunTask, } from './runtime/smartInputPaths.js';
|
|
46
46
|
export { evaluateTaskNodeMainReadiness, buildMainReadinessResolutionRoot, resolveMainReadinessPolicy, collectInputSynthesisSourcePaths, } from './runtime/taskNodeMainReadiness.js';
|
|
@@ -58,7 +58,8 @@ export { buildTaskNodeRunTaskRequest, validateTaskNodeRunTaskConfig, validateTas
|
|
|
58
58
|
/** Re-exported from `@exellix/ai-tasks` — preflight validation and analysis without `runTask`. */
|
|
59
59
|
export { validateRunTaskConfig, validateRunTaskInvoke, analyzeExpectedRunTaskInput, checkExpectedInputAgainstRequest, collectSmartInputValidationIssues, buildRunTaskValidationContext, analyzeRunTaskRequest, validateParsedOutput, analyzeSkillRequest, buildSkillRequestAnalysisPacket, formatSkillRequestAnalysisMarkdown, listTokens, analyzeTemplateResolution, renderSmartInput, extractTokenNamesFromStrings, getSkillTokens, getSkillContent, } from '@exellix/ai-tasks';
|
|
60
60
|
export { buildExellixGraphRuntimeObjects, setRuntimeObjectsLastJobId, summarizeRuntimeObjectsForPlayground, EXELLIX_GRAPH_RUNTIME_PACKAGE_NAME, EXELLIX_AI_TASKS_PACKAGE_NAME, } from './runtime/runtimeObjects.js';
|
|
61
|
-
export { assertCanonicalGraphDocument, getCanonicalGraphDocumentViolations, CANONICAL_GRAPH_TOP_LEVEL_KEYS, } from './runtime/validateCanonicalGraphDocument.js';
|
|
61
|
+
export { assertCanonicalGraphDocument, assertCanonicalTaskNode, getCanonicalGraphDocumentViolations, CANONICAL_GRAPH_TOP_LEVEL_KEYS, } from './runtime/validateCanonicalGraphDocument.js';
|
|
62
|
+
export { GRAPH_ENTRY_STUDIO_ONLY_KEYS, GRAPH_METADATA_STUDIO_ONLY_KEYS, stripGraphModelStudioFields, primaryRuntimeInputFromStudioDocument, getGraphEntryStudioOnlyKeyViolations, getGraphMetadataStudioOnlyKeyViolations, getGraphEntryEmptyInputPathViolations, } from './runtime/graphModelStudioSeparation.js';
|
|
62
63
|
export { computeGraphDocumentContentSha256, stableStringifyGraphDocument, } from './runtime/graphDocumentFingerprint.js';
|
|
63
64
|
export { migrateLegacyGraphResponse, migrateLegacyGraphResponseDefinition, } from './runtime/graphResponseMigration.js';
|
|
64
65
|
export { applyAiTaskProfileWebScopingToNarrix, mapAiTaskProfileQuestionsToWebScopeQuestions, } from './runtime/applyAiTaskProfileWebScopingToNarrix.js';
|
|
@@ -81,5 +82,5 @@ export { composeEventEmitters } from './runtime/events.js';
|
|
|
81
82
|
// Playground (full-visibility MD report)
|
|
82
83
|
export { createPlaygroundReporter } from './playground/index.js';
|
|
83
84
|
// Graph Inspection API
|
|
84
|
-
export { getNodeScopingQuestion, getNodeMemoryShape, getNodeNarrixDiscovery, fetchNodeScopingData, inspectNode, resolveNodeSkillKey, getGraphNodes, getGraphCatalogs, inspectGraph, collectPredicatePaths, executionMemoryPathTail, executionMemoryTailsMatch, getNodeExecutionMemoryWriteTails, getNodeControlDependencies, getNodeSideEffects, inspectFinalizer, inspectNodeContract, inspectGraphContracts, EXELLIX_VIRTUAL_GRAPH_ENTRY_NODE_ID, EXELLIX_VIRTUAL_GRAPH_RESPONSE_NODE_ID, EXELLIX_VIRTUAL_BOUNDARY_NODE_METADATA_KEY, EXELLIX_VIRTUAL_BOUNDARY_EDGE_FLAG_KEY, getVirtualGraphEntryLayer, getVirtualGraphResponseLayer, materializeVirtualBoundaryDiagram, stripMaterializedVirtualBoundaryDiagram, validateCatalogPlanning, isCatalogBinding, isCatalogRequestEntry, } from './inspection/index.js';
|
|
85
|
+
export { getNodeScopingQuestion, getNodeMemoryShape, getNodeNarrixDiscovery, fetchNodeScopingData, inspectNode, resolveNodeSkillKey, getGraphNodes, getGraphCatalogs, inspectGraph, collectPredicatePaths, executionMemoryPathTail, executionMemoryTailsMatch, getNodeExecutionMemoryWriteTails, getNodeControlDependencies, getNodeSideEffects, inspectFinalizer, inspectNodeContract, inspectGraphContracts, EXELLIX_VIRTUAL_GRAPH_ENTRY_NODE_ID, EXELLIX_VIRTUAL_GRAPH_RESPONSE_NODE_ID, EXELLIX_VIRTUAL_BOUNDARY_NODE_METADATA_KEY, EXELLIX_VIRTUAL_BOUNDARY_EDGE_FLAG_KEY, getVirtualGraphEntryLayer, getVirtualGraphResponseLayer, materializeVirtualBoundaryDiagram, stripMaterializedVirtualBoundaryDiagram, validateCatalogPlanning, isCatalogBinding, isCatalogRequestEntry, inspectGraphModelSelection, } from './inspection/index.js';
|
|
85
86
|
// Web context rendering — consume execution.webContext → markdown for prompts (Layer 04)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Graph, GraphAiModelConfig } from '../types/refs.js';
|
|
2
|
+
/** Where the effective model-profile triplet for a node came from. */
|
|
3
|
+
export type ModelSelectionSource = 'node' | 'graph' | 'default' | 'none';
|
|
4
|
+
export type NodeModelSelection = {
|
|
5
|
+
nodeId: string;
|
|
6
|
+
kind: 'task' | 'finalizer';
|
|
7
|
+
/** False for deterministic finalizers (aggregate / select / bundle); those call no model. */
|
|
8
|
+
usesModel: boolean;
|
|
9
|
+
/** Effective default-case profile aliases for this node (undefined when `usesModel` is false). */
|
|
10
|
+
profiles?: GraphAiModelConfig;
|
|
11
|
+
source: ModelSelectionSource;
|
|
12
|
+
/** Count of conditional (`when`-gated) cases that may override the default at runtime. */
|
|
13
|
+
conditionalCaseCount: number;
|
|
14
|
+
};
|
|
15
|
+
export type GraphModelSelectionInspection = {
|
|
16
|
+
/** Graph-level default profile aliases (undefined when the graph declares none). */
|
|
17
|
+
graphDefaultProfiles?: GraphAiModelConfig;
|
|
18
|
+
nodes: NodeModelSelection[];
|
|
19
|
+
/** Distinct profile alias names referenced anywhere in the model (for `runtime.aliasConfig` wiring). */
|
|
20
|
+
aliasesUsed: string[];
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Static, runtime-free view of which model-profile aliases each node would use.
|
|
24
|
+
*
|
|
25
|
+
* Answers "where are the models (aliases) that will be used?" without executing:
|
|
26
|
+
* - task nodes resolve `node.taskConfiguration.modelConfig` (override) → graph `modelConfig` → engine default;
|
|
27
|
+
* - `synthesize` finalizers use the graph default; other finalizers call no model.
|
|
28
|
+
*
|
|
29
|
+
* Only the unconditional (`when`-less) default case is reported per tier; `conditionalCaseCount`
|
|
30
|
+
* flags nodes whose effective model may change at runtime via gated cases. Alias → concrete
|
|
31
|
+
* provider id still happens at runtime through `runtime.aliasConfig`.
|
|
32
|
+
*/
|
|
33
|
+
export declare function inspectGraphModelSelection(graph: Graph): GraphModelSelectionInspection;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { isEmptyConditionWhen, isModelConfigSelection } from '../runtime/modelConfigSelection.js';
|
|
2
|
+
import { DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG } from '../runtime/graphAiModelConfig.js';
|
|
3
|
+
function asArrayNodes(graph) {
|
|
4
|
+
return Array.isArray(graph.nodes) ? graph.nodes : Object.values(graph.nodes ?? {});
|
|
5
|
+
}
|
|
6
|
+
/** Returns the no-`when` default case profiles from a selection, if present and valid. */
|
|
7
|
+
function defaultCaseProfiles(selection) {
|
|
8
|
+
if (!isModelConfigSelection(selection))
|
|
9
|
+
return undefined;
|
|
10
|
+
const sel = selection;
|
|
11
|
+
for (const c of sel.cases ?? []) {
|
|
12
|
+
if (isEmptyConditionWhen(c.when))
|
|
13
|
+
return c.modelConfig;
|
|
14
|
+
}
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
function conditionalCaseCount(selection) {
|
|
18
|
+
if (!isModelConfigSelection(selection))
|
|
19
|
+
return 0;
|
|
20
|
+
const sel = selection;
|
|
21
|
+
return (sel.cases ?? []).filter((c) => !isEmptyConditionWhen(c.when)).length;
|
|
22
|
+
}
|
|
23
|
+
/** True for finalizer types that invoke a model (`synthesize`); deterministic kinds do not. */
|
|
24
|
+
function finalizerUsesModel(node) {
|
|
25
|
+
return node.finalizerType === 'synthesize';
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Static, runtime-free view of which model-profile aliases each node would use.
|
|
29
|
+
*
|
|
30
|
+
* Answers "where are the models (aliases) that will be used?" without executing:
|
|
31
|
+
* - task nodes resolve `node.taskConfiguration.modelConfig` (override) → graph `modelConfig` → engine default;
|
|
32
|
+
* - `synthesize` finalizers use the graph default; other finalizers call no model.
|
|
33
|
+
*
|
|
34
|
+
* Only the unconditional (`when`-less) default case is reported per tier; `conditionalCaseCount`
|
|
35
|
+
* flags nodes whose effective model may change at runtime via gated cases. Alias → concrete
|
|
36
|
+
* provider id still happens at runtime through `runtime.aliasConfig`.
|
|
37
|
+
*/
|
|
38
|
+
export function inspectGraphModelSelection(graph) {
|
|
39
|
+
const graphDefaultProfiles = defaultCaseProfiles(graph.modelConfig);
|
|
40
|
+
const nodes = [];
|
|
41
|
+
const aliases = new Set();
|
|
42
|
+
const addAliases = (cfg) => {
|
|
43
|
+
if (cfg == null)
|
|
44
|
+
return;
|
|
45
|
+
aliases.add(cfg.preActionModel);
|
|
46
|
+
aliases.add(cfg.skillModel);
|
|
47
|
+
aliases.add(cfg.postActionModel);
|
|
48
|
+
};
|
|
49
|
+
for (const raw of asArrayNodes(graph)) {
|
|
50
|
+
const node = raw;
|
|
51
|
+
const isFinalizer = node.type === 'finalizer';
|
|
52
|
+
if (isFinalizer) {
|
|
53
|
+
const fin = raw;
|
|
54
|
+
if (!finalizerUsesModel(fin)) {
|
|
55
|
+
nodes.push({
|
|
56
|
+
nodeId: String(fin.id),
|
|
57
|
+
kind: 'finalizer',
|
|
58
|
+
usesModel: false,
|
|
59
|
+
source: 'none',
|
|
60
|
+
conditionalCaseCount: 0,
|
|
61
|
+
});
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const profiles = graphDefaultProfiles ?? DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG;
|
|
65
|
+
addAliases(profiles);
|
|
66
|
+
nodes.push({
|
|
67
|
+
nodeId: String(fin.id),
|
|
68
|
+
kind: 'finalizer',
|
|
69
|
+
usesModel: true,
|
|
70
|
+
profiles,
|
|
71
|
+
source: graphDefaultProfiles ? 'graph' : 'default',
|
|
72
|
+
conditionalCaseCount: 0,
|
|
73
|
+
});
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const taskNode = raw;
|
|
77
|
+
const nodeSelection = taskNode.taskConfiguration?.modelConfig;
|
|
78
|
+
const nodeDefault = defaultCaseProfiles(nodeSelection);
|
|
79
|
+
let profiles;
|
|
80
|
+
let source;
|
|
81
|
+
if (nodeDefault) {
|
|
82
|
+
profiles = nodeDefault;
|
|
83
|
+
source = 'node';
|
|
84
|
+
}
|
|
85
|
+
else if (graphDefaultProfiles) {
|
|
86
|
+
profiles = graphDefaultProfiles;
|
|
87
|
+
source = 'graph';
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
profiles = DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG;
|
|
91
|
+
source = 'default';
|
|
92
|
+
}
|
|
93
|
+
addAliases(profiles);
|
|
94
|
+
nodes.push({
|
|
95
|
+
nodeId: String(taskNode.id),
|
|
96
|
+
kind: 'task',
|
|
97
|
+
usesModel: true,
|
|
98
|
+
profiles,
|
|
99
|
+
source,
|
|
100
|
+
conditionalCaseCount: conditionalCaseCount(nodeSelection) + conditionalCaseCount(graph.modelConfig),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
graphDefaultProfiles,
|
|
105
|
+
nodes,
|
|
106
|
+
aliasesUsed: [...aliases].sort(),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -15,6 +15,8 @@ export type { CatalogPlanningValidationIssue } from './validateCatalogPlanning.j
|
|
|
15
15
|
export { collectPredicatePaths, executionMemoryPathTail, executionMemoryTailsMatch, getNodeExecutionMemoryWriteTails, getNodeControlDependencies, } from './controlInspection.js';
|
|
16
16
|
export { getNodeSideEffects, inspectFinalizer, inspectNodeContract, inspectGraphContracts, } from './contractInspection.js';
|
|
17
17
|
export type { InspectNodeContractOptions } from './contractInspection.js';
|
|
18
|
+
export { inspectGraphModelSelection } from './graphModelSelection.js';
|
|
19
|
+
export type { GraphModelSelectionInspection, NodeModelSelection, ModelSelectionSource, } from './graphModelSelection.js';
|
|
18
20
|
export type { NodeScopingQuestion, NodeScopeSource, NodeScopeTarget, NodeScopingData, NodeMemoryShape, MemoryPath, NodeNarrixDiscovery, NodeInspection, GraphInspection, NodeIOEdge, GraphCatalogs, CatalogBindingSummary, GraphVirtualIO, GraphVirtualIONode, GraphVirtualIOEdge, GraphVirtualIONodeRole, VirtualGraphEntryLayer, VirtualGraphResponseLayer, MaterializeVirtualBoundaryDiagramOptions, MaterializedVirtualBoundaryDiagram, MaterializedVirtualBoundaryDiagramMeta, } from './types.js';
|
|
19
21
|
export { EXELLIX_VIRTUAL_GRAPH_ENTRY_NODE_ID, EXELLIX_VIRTUAL_GRAPH_RESPONSE_NODE_ID, EXELLIX_VIRTUAL_BOUNDARY_NODE_METADATA_KEY, EXELLIX_VIRTUAL_BOUNDARY_EDGE_FLAG_KEY, } from './types.js';
|
|
20
22
|
export type { GraphDocumentMetadata } from '../types/refs.js';
|
|
@@ -14,4 +14,5 @@ export { getGraphNodes, getGraphCatalogs, inspectGraph, getVirtualGraphEntryLaye
|
|
|
14
14
|
export { validateCatalogPlanning, isCatalogBinding, isCatalogRequestEntry, } from './validateCatalogPlanning.js';
|
|
15
15
|
export { collectPredicatePaths, executionMemoryPathTail, executionMemoryTailsMatch, getNodeExecutionMemoryWriteTails, getNodeControlDependencies, } from './controlInspection.js';
|
|
16
16
|
export { getNodeSideEffects, inspectFinalizer, inspectNodeContract, inspectGraphContracts, } from './contractInspection.js';
|
|
17
|
+
export { inspectGraphModelSelection } from './graphModelSelection.js';
|
|
17
18
|
export { EXELLIX_VIRTUAL_GRAPH_ENTRY_NODE_ID, EXELLIX_VIRTUAL_GRAPH_RESPONSE_NODE_ID, EXELLIX_VIRTUAL_BOUNDARY_NODE_METADATA_KEY, EXELLIX_VIRTUAL_BOUNDARY_EDGE_FLAG_KEY, } from './types.js';
|
|
@@ -52,7 +52,7 @@ function finalizerRuntimeReadSources(node, graph) {
|
|
|
52
52
|
for (const [key, spec] of Object.entries(cfg.sections)) {
|
|
53
53
|
if (!isPlainObject(spec))
|
|
54
54
|
continue;
|
|
55
|
-
pushDedupedRead(reads, spec.path, `config.sections.${key}`, 'executionMemory');
|
|
55
|
+
pushDedupedRead(reads, spec.path, `config.sections.${key}`, spec.type === 'outputsMemoryPath' ? 'outputsMemory' : 'executionMemory');
|
|
56
56
|
}
|
|
57
57
|
return reads;
|
|
58
58
|
}
|
|
@@ -240,10 +240,8 @@ export function getNodeScopingQuestion(node) {
|
|
|
240
240
|
const task = node;
|
|
241
241
|
const readability = task.metadata?.graphReadability;
|
|
242
242
|
const name = readability?.title;
|
|
243
|
-
const legacyInputsConfig = readTaskNodeInputsConfig(task);
|
|
244
243
|
const question = readability?.asks ??
|
|
245
|
-
(typeof task.taskVariable?.question === 'string' ? task.taskVariable.question : undefined)
|
|
246
|
-
(typeof legacyInputsConfig.question === 'string' ? legacyInputsConfig.question : undefined);
|
|
244
|
+
(typeof task.taskVariable?.question === 'string' ? task.taskVariable.question : undefined);
|
|
247
245
|
const kind = readability?.kind;
|
|
248
246
|
// Sources
|
|
249
247
|
const sources = [];
|
|
@@ -8,7 +8,7 @@ import { resolveExecutionPipelineForTaskNode } from "./resolveExecutionPipelineF
|
|
|
8
8
|
import { resolveTaskNodeInputsForRunTask } from "./resolveTaskNodeInputs.js";
|
|
9
9
|
import { assertAiTasksNodeExtensionsValid } from "../inspection/validateAiTasksNodeExtensions.js";
|
|
10
10
|
import { buildRunTaskMainInput, extractCallerInputsBag } from "./resolveGraphEngineMemoryPaths.js";
|
|
11
|
-
import {
|
|
11
|
+
import { mirrorStructuredInputOntoExecutionMemory, } from "./materializeStructuredRunTaskInput.js";
|
|
12
12
|
import { buildMainReadinessResolutionRoot, evaluateTaskNodeMainReadiness, resolveMainReadinessPolicy, } from "./taskNodeMainReadiness.js";
|
|
13
13
|
import { mergeXynthesizedPatchIntoExecution, seedGraphRunExecutionState, xynthesizedOutboundForNode, } from "./graphRunExecutionSeed.js";
|
|
14
14
|
import { buildTaskNodeJobContext } from "./buildTaskNodeJobContext.js";
|
|
@@ -253,7 +253,6 @@ function normalizeRuntimeExecutionMemory(runtime) {
|
|
|
253
253
|
if (isPlainRecord(runtime.inputs)) {
|
|
254
254
|
execution.inputs = { ...runtime.inputs };
|
|
255
255
|
}
|
|
256
|
-
mirrorRuntimeInputRawOnExecutionMemory(execution);
|
|
257
256
|
return execution;
|
|
258
257
|
}
|
|
259
258
|
function normalizeRuntimeOutputsMemory(runtime) {
|
|
@@ -681,7 +680,6 @@ export function createExellixGraphRuntime(opts) {
|
|
|
681
680
|
const clearSynthForTask = input.clearSynthesizedContextPerNode === true &&
|
|
682
681
|
hasSynthesizedContextPreStep &&
|
|
683
682
|
hasExistingSynthesizedContext;
|
|
684
|
-
mirrorRuntimeInputRawOnExecutionMemory(executionRec);
|
|
685
683
|
const nodeBindings = resolveTaskNodeInputsForRunTask({
|
|
686
684
|
node: input.node,
|
|
687
685
|
execution: executionRec,
|
|
@@ -171,7 +171,7 @@ function executeAggregateQuestionDriven(args) {
|
|
|
171
171
|
path: `config.items.${outKey}.nodeId`,
|
|
172
172
|
});
|
|
173
173
|
}
|
|
174
|
-
const questionPath = spec.questionPath ?? '
|
|
174
|
+
const questionPath = spec.questionPath ?? 'taskVariable.question';
|
|
175
175
|
const question = getByDotPath(node, questionPath);
|
|
176
176
|
if (typeof question !== 'string' || question.length === 0) {
|
|
177
177
|
throw createFinalizerError({
|
|
@@ -284,11 +284,12 @@ function collectEpistemicTags(value, found) {
|
|
|
284
284
|
}
|
|
285
285
|
}
|
|
286
286
|
function executeReportSchema(args) {
|
|
287
|
-
const { cfg, executionMemory } = args;
|
|
287
|
+
const { cfg, executionMemory, outputsMemory } = args;
|
|
288
288
|
const finalizerNodeId = String(args.finalizer.id);
|
|
289
289
|
const out = {};
|
|
290
290
|
for (const [key, spec] of Object.entries(cfg.sections ?? {})) {
|
|
291
|
-
|
|
291
|
+
const root = spec.type === 'outputsMemoryPath' ? outputsMemory : executionMemory;
|
|
292
|
+
let v = getByDotPath(root, spec.path);
|
|
292
293
|
if (isNodeFailureMarker(v)) {
|
|
293
294
|
if (spec.optional === true) {
|
|
294
295
|
out[key] = null;
|
|
@@ -296,7 +297,7 @@ function executeReportSchema(args) {
|
|
|
296
297
|
}
|
|
297
298
|
throw createFinalizerError({
|
|
298
299
|
code: 'GRAPH_FINALIZER_NODE_FAILED',
|
|
299
|
-
message: `report-schema: section "${key}" points at a failed graph node (
|
|
300
|
+
message: `report-schema: section "${key}" points at a failed graph node (${spec.type}="${spec.path}")`,
|
|
300
301
|
finalizerNodeId,
|
|
301
302
|
path: `config.sections.${key}`,
|
|
302
303
|
details: { spec, marker: v },
|
|
@@ -305,7 +306,7 @@ function executeReportSchema(args) {
|
|
|
305
306
|
if ((v === undefined || v === null) && spec.optional !== true) {
|
|
306
307
|
throw createFinalizerError({
|
|
307
308
|
code: 'GRAPH_FINALIZER_INPUT_MISSING',
|
|
308
|
-
message: `report-schema: required section "${key}" not found at
|
|
309
|
+
message: `report-schema: required section "${key}" not found at ${spec.type} "${spec.path}"`,
|
|
309
310
|
finalizerNodeId,
|
|
310
311
|
path: `config.sections.${key}`,
|
|
311
312
|
details: { spec },
|
|
@@ -313,16 +314,11 @@ function executeReportSchema(args) {
|
|
|
313
314
|
}
|
|
314
315
|
out[key] = v ?? null;
|
|
315
316
|
}
|
|
316
|
-
if (cfg.
|
|
317
|
+
if (cfg.collectEpistemicTags) {
|
|
317
318
|
const tags = new Set();
|
|
318
319
|
for (const v of Object.values(out))
|
|
319
320
|
collectEpistemicTags(v, tags);
|
|
320
|
-
out.
|
|
321
|
-
}
|
|
322
|
-
if (cfg.meta) {
|
|
323
|
-
for (const [k, v] of Object.entries(cfg.meta)) {
|
|
324
|
-
out[k] = v;
|
|
325
|
-
}
|
|
321
|
+
out.collectedTags = Array.from(tags);
|
|
326
322
|
}
|
|
327
323
|
return out;
|
|
328
324
|
}
|
|
@@ -55,7 +55,8 @@ export function collectRequiredFinalizerExecutionMemoryReads(finalizer, graph) {
|
|
|
55
55
|
for (const [key, spec] of Object.entries(cfg.sections)) {
|
|
56
56
|
if (!isPlainObject(spec))
|
|
57
57
|
continue;
|
|
58
|
-
|
|
58
|
+
const memory = spec.type === 'outputsMemoryPath' ? 'outputs' : 'execution';
|
|
59
|
+
pushRequiredRead(reads, memory, spec.path, `config.sections.${key}`, spec.optional);
|
|
59
60
|
}
|
|
60
61
|
return reads;
|
|
61
62
|
}
|
|
@@ -180,6 +181,46 @@ export function assertFinalizerRequiredReadsResolvable(args) {
|
|
|
180
181
|
}
|
|
181
182
|
}
|
|
182
183
|
}
|
|
184
|
+
/** Validates a `{ type: executionMemoryPath | outputsMemoryPath, path, optional? }` memory read. */
|
|
185
|
+
function validateMemoryRef(ref, path, finalizerNodeId, label) {
|
|
186
|
+
if (!isPlainObject(ref)) {
|
|
187
|
+
throw createFinalizerError({
|
|
188
|
+
code: 'GRAPH_FINALIZER_INVALID',
|
|
189
|
+
message: `${label} at ${path} must be an object { type, path }`,
|
|
190
|
+
finalizerNodeId,
|
|
191
|
+
path,
|
|
192
|
+
details: { ref },
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
const type = ref.type;
|
|
196
|
+
if (type !== 'executionMemoryPath' && type !== 'outputsMemoryPath') {
|
|
197
|
+
throw createFinalizerError({
|
|
198
|
+
code: 'GRAPH_FINALIZER_INVALID',
|
|
199
|
+
message: `${label} at ${path} must declare type "executionMemoryPath" or "outputsMemoryPath" (got ${String(type)})`,
|
|
200
|
+
finalizerNodeId,
|
|
201
|
+
path: `${path}.type`,
|
|
202
|
+
details: { ref },
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
const p = ref.path;
|
|
206
|
+
if (typeof p !== 'string' || p.length === 0) {
|
|
207
|
+
throw createFinalizerError({
|
|
208
|
+
code: 'GRAPH_FINALIZER_INVALID',
|
|
209
|
+
message: `${label} at ${path} requires a non-empty path`,
|
|
210
|
+
finalizerNodeId,
|
|
211
|
+
path: `${path}.path`,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
const optional = ref.optional;
|
|
215
|
+
if (optional !== undefined && typeof optional !== 'boolean') {
|
|
216
|
+
throw createFinalizerError({
|
|
217
|
+
code: 'GRAPH_FINALIZER_INVALID',
|
|
218
|
+
message: `${label} at ${path} optional must be boolean when provided`,
|
|
219
|
+
finalizerNodeId,
|
|
220
|
+
path: `${path}.optional`,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
183
224
|
function validateBinding(name, b, finalizerNodeId) {
|
|
184
225
|
if (!isPlainObject(b)) {
|
|
185
226
|
throw createFinalizerError({
|
|
@@ -245,54 +286,57 @@ function validateAggregateConfig(cfg, finalizerNodeId) {
|
|
|
245
286
|
return;
|
|
246
287
|
}
|
|
247
288
|
if (strategy === 'report-schema') {
|
|
248
|
-
const
|
|
249
|
-
|
|
289
|
+
const REPORT_SCHEMA_ALLOWED_KEYS = new Set(['strategy', 'sections', 'collectEpistemicTags']);
|
|
290
|
+
const REPORT_SCHEMA_REMOVED_KEYS = {
|
|
291
|
+
collect_tags: 'collectEpistemicTags (boolean)',
|
|
292
|
+
meta: 'removed — put literal output fields in graph.response.shape, and authoring titles in the studio document',
|
|
293
|
+
title: 'removed from sections — authoring titles belong in the studio document',
|
|
294
|
+
schemaVersion: 'removed — not part of the executable model',
|
|
295
|
+
skeletonFromConcept: 'removed — studio authoring hint, not part of the executable model',
|
|
296
|
+
};
|
|
297
|
+
for (const key of Object.keys(cfg)) {
|
|
298
|
+
if (REPORT_SCHEMA_ALLOWED_KEYS.has(key))
|
|
299
|
+
continue;
|
|
300
|
+
const migration = REPORT_SCHEMA_REMOVED_KEYS[key];
|
|
250
301
|
throw createFinalizerError({
|
|
251
302
|
code: 'GRAPH_FINALIZER_INVALID',
|
|
252
|
-
message:
|
|
303
|
+
message: migration
|
|
304
|
+
? `report-schema finalizer config.${key} is no longer supported. Use: ${migration}.`
|
|
305
|
+
: `report-schema finalizer has unknown config key "${key}". Allowed: ${[...REPORT_SCHEMA_ALLOWED_KEYS].join(', ')}.`,
|
|
253
306
|
finalizerNodeId,
|
|
254
|
-
path:
|
|
307
|
+
path: `config.${key}`,
|
|
255
308
|
details: { config: cfg },
|
|
256
309
|
});
|
|
257
310
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
if (!isPlainObject(spec) || typeof spec.path !== 'string' || spec.path.length === 0) {
|
|
261
|
-
throw createFinalizerError({
|
|
262
|
-
code: 'GRAPH_FINALIZER_INVALID',
|
|
263
|
-
message: `report-schema section "${k}" must have a non-empty path`,
|
|
264
|
-
finalizerNodeId,
|
|
265
|
-
path: `config.sections.${k}.path`,
|
|
266
|
-
details: { spec },
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
const optional = spec.optional;
|
|
270
|
-
if (optional !== undefined && typeof optional !== 'boolean') {
|
|
271
|
-
throw createFinalizerError({
|
|
272
|
-
code: 'GRAPH_FINALIZER_INVALID',
|
|
273
|
-
message: `report-schema section "${k}" optional must be boolean when provided`,
|
|
274
|
-
finalizerNodeId,
|
|
275
|
-
path: `config.sections.${k}.optional`,
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
const collectTags = cfg.collect_tags;
|
|
281
|
-
if (collectTags !== undefined && typeof collectTags !== 'boolean') {
|
|
311
|
+
const sections = cfg.sections;
|
|
312
|
+
if (!isPlainObject(sections) || Object.keys(sections).length === 0) {
|
|
282
313
|
throw createFinalizerError({
|
|
283
314
|
code: 'GRAPH_FINALIZER_INVALID',
|
|
284
|
-
message: `report-schema aggregate finalizer config.
|
|
315
|
+
message: `report-schema aggregate finalizer requires a non-empty config.sections object (output key → { type, path }).`,
|
|
285
316
|
finalizerNodeId,
|
|
286
|
-
path: 'config.
|
|
317
|
+
path: 'config.sections',
|
|
318
|
+
details: { config: cfg },
|
|
287
319
|
});
|
|
288
320
|
}
|
|
289
|
-
const
|
|
290
|
-
|
|
321
|
+
for (const [k, spec] of Object.entries(sections)) {
|
|
322
|
+
validateMemoryRef(spec, `config.sections.${k}`, finalizerNodeId, 'report-schema section');
|
|
323
|
+
const title = spec.title;
|
|
324
|
+
if (title !== undefined) {
|
|
325
|
+
throw createFinalizerError({
|
|
326
|
+
code: 'GRAPH_FINALIZER_INVALID',
|
|
327
|
+
message: `report-schema section "${k}" no longer accepts "title"; move authoring titles to the studio document.`,
|
|
328
|
+
finalizerNodeId,
|
|
329
|
+
path: `config.sections.${k}.title`,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
const collectTags = cfg.collectEpistemicTags;
|
|
334
|
+
if (collectTags !== undefined && typeof collectTags !== 'boolean') {
|
|
291
335
|
throw createFinalizerError({
|
|
292
336
|
code: 'GRAPH_FINALIZER_INVALID',
|
|
293
|
-
message: `report-schema aggregate finalizer config.
|
|
337
|
+
message: `report-schema aggregate finalizer config.collectEpistemicTags must be boolean when provided`,
|
|
294
338
|
finalizerNodeId,
|
|
295
|
-
path: 'config.
|
|
339
|
+
path: 'config.collectEpistemicTags',
|
|
296
340
|
});
|
|
297
341
|
}
|
|
298
342
|
return;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Graph } from '../types/refs.js';
|
|
2
|
+
/** Example payloads and playground request samples — studio DB only, not graph model. */
|
|
3
|
+
export declare const GRAPH_ENTRY_STUDIO_ONLY_KEYS: readonly ["exampleInput", "exampleInputs", "requestExample"];
|
|
4
|
+
/** Planning / UI metadata — studio DB only, not versioned executable graph model. */
|
|
5
|
+
export declare const GRAPH_METADATA_STUDIO_ONLY_KEYS: readonly ["requestId", "graphConcept", "graphsStudio"];
|
|
6
|
+
export type GraphEntryStudioOnlyKey = (typeof GRAPH_ENTRY_STUDIO_ONLY_KEYS)[number];
|
|
7
|
+
export type GraphMetadataStudioOnlyKey = (typeof GRAPH_METADATA_STUDIO_ONLY_KEYS)[number];
|
|
8
|
+
/**
|
|
9
|
+
* Studio-side companion document keyed by graph id (graphs-studio database).
|
|
10
|
+
* Not part of {@link GraphModelObject} / canonical graph JSON.
|
|
11
|
+
*/
|
|
12
|
+
export type GraphStudioDocument = {
|
|
13
|
+
graphId: string;
|
|
14
|
+
graphConcept?: Record<string, unknown>;
|
|
15
|
+
graphsStudio?: Record<string, unknown>;
|
|
16
|
+
/** Flat {@link GraphRuntimeObject.input} examples for playground and tests. */
|
|
17
|
+
exampleInputs?: Array<{
|
|
18
|
+
title?: string;
|
|
19
|
+
runtimeInput: Record<string, unknown>;
|
|
20
|
+
}>;
|
|
21
|
+
[key: string]: unknown;
|
|
22
|
+
};
|
|
23
|
+
export declare function getGraphEntryStudioOnlyKeyViolations(graphEntry: unknown): GraphEntryStudioOnlyKey[];
|
|
24
|
+
export declare function getGraphMetadataStudioOnlyKeyViolations(metadata: unknown): GraphMetadataStudioOnlyKey[];
|
|
25
|
+
/** Returns dot-paths under graphEntry.inputs with empty `path` for value kinds. */
|
|
26
|
+
export declare function getGraphEntryEmptyInputPathViolations(graphEntry: unknown): string[];
|
|
27
|
+
/**
|
|
28
|
+
* Deep-clones a graph model and removes studio-only metadata and graphEntry example fields.
|
|
29
|
+
* Use at publish time when upstream serializers still attach playground artifacts.
|
|
30
|
+
*/
|
|
31
|
+
export declare function stripGraphModelStudioFields(graph: Graph): Graph;
|
|
32
|
+
export declare function assertCanonicalGraphEntryContract(graphEntry: unknown, context: {
|
|
33
|
+
jobId?: string;
|
|
34
|
+
graphId?: string;
|
|
35
|
+
graphLabel?: string;
|
|
36
|
+
}): void;
|
|
37
|
+
export declare function assertCanonicalGraphDocumentMetadata(metadata: unknown, context: {
|
|
38
|
+
jobId?: string;
|
|
39
|
+
graphId?: string;
|
|
40
|
+
graphLabel?: string;
|
|
41
|
+
}): void;
|
|
42
|
+
/** Primary flat runtime input from a studio companion document. */
|
|
43
|
+
export declare function primaryRuntimeInputFromStudioDocument(studio: GraphStudioDocument | undefined): Record<string, unknown> | undefined;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { ExellixGraphError } from '../errors/ExellixGraphError.js';
|
|
2
|
+
import { ExellixGraphErrorCode } from '../errors/exellixGraphErrorCodes.js';
|
|
3
|
+
/** Example payloads and playground request samples — studio DB only, not graph model. */
|
|
4
|
+
export const GRAPH_ENTRY_STUDIO_ONLY_KEYS = [
|
|
5
|
+
'exampleInput',
|
|
6
|
+
'exampleInputs',
|
|
7
|
+
'requestExample',
|
|
8
|
+
];
|
|
9
|
+
/** Planning / UI metadata — studio DB only, not versioned executable graph model. */
|
|
10
|
+
export const GRAPH_METADATA_STUDIO_ONLY_KEYS = [
|
|
11
|
+
'requestId',
|
|
12
|
+
'graphConcept',
|
|
13
|
+
'graphsStudio',
|
|
14
|
+
];
|
|
15
|
+
function isPlainObject(v) {
|
|
16
|
+
return v != null && typeof v === 'object' && !Array.isArray(v);
|
|
17
|
+
}
|
|
18
|
+
function isValueInputSpec(spec) {
|
|
19
|
+
return spec.kind !== 'execution';
|
|
20
|
+
}
|
|
21
|
+
export function getGraphEntryStudioOnlyKeyViolations(graphEntry) {
|
|
22
|
+
if (!isPlainObject(graphEntry))
|
|
23
|
+
return [];
|
|
24
|
+
return GRAPH_ENTRY_STUDIO_ONLY_KEYS.filter((key) => Object.prototype.hasOwnProperty.call(graphEntry, key));
|
|
25
|
+
}
|
|
26
|
+
export function getGraphMetadataStudioOnlyKeyViolations(metadata) {
|
|
27
|
+
if (!isPlainObject(metadata))
|
|
28
|
+
return [];
|
|
29
|
+
return GRAPH_METADATA_STUDIO_ONLY_KEYS.filter((key) => Object.prototype.hasOwnProperty.call(metadata, key));
|
|
30
|
+
}
|
|
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
|
+
/**
|
|
52
|
+
* Deep-clones a graph model and removes studio-only metadata and graphEntry example fields.
|
|
53
|
+
* Use at publish time when upstream serializers still attach playground artifacts.
|
|
54
|
+
*/
|
|
55
|
+
export function stripGraphModelStudioFields(graph) {
|
|
56
|
+
const clone = structuredClone(graph);
|
|
57
|
+
if (isPlainObject(clone.metadata)) {
|
|
58
|
+
const meta = { ...clone.metadata };
|
|
59
|
+
for (const key of GRAPH_METADATA_STUDIO_ONLY_KEYS) {
|
|
60
|
+
delete meta[key];
|
|
61
|
+
}
|
|
62
|
+
if (isPlainObject(meta.graphEntry)) {
|
|
63
|
+
const ge = { ...meta.graphEntry };
|
|
64
|
+
for (const key of GRAPH_ENTRY_STUDIO_ONLY_KEYS) {
|
|
65
|
+
delete ge[key];
|
|
66
|
+
}
|
|
67
|
+
meta.graphEntry = ge;
|
|
68
|
+
}
|
|
69
|
+
clone.metadata = meta;
|
|
70
|
+
}
|
|
71
|
+
return clone;
|
|
72
|
+
}
|
|
73
|
+
export function assertCanonicalGraphEntryContract(graphEntry, context) {
|
|
74
|
+
const label = context.graphLabel ?? `Graph "${String(context.graphId ?? '?')}"`;
|
|
75
|
+
const studioKeys = getGraphEntryStudioOnlyKeyViolations(graphEntry);
|
|
76
|
+
if (studioKeys.length > 0) {
|
|
77
|
+
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
|
+
}
|
|
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
|
+
}
|
|
84
|
+
export function assertCanonicalGraphDocumentMetadata(metadata, context) {
|
|
85
|
+
const label = context.graphLabel ?? `Graph "${String(context.graphId ?? '?')}"`;
|
|
86
|
+
const studioKeys = getGraphMetadataStudioOnlyKeyViolations(metadata);
|
|
87
|
+
if (studioKeys.length > 0) {
|
|
88
|
+
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_GRAPH_DOCUMENT, `${label}: metadata must not include studio-only field(s): ${studioKeys.join(', ')}. Move planning and UI metadata to the graphs-studio database (GraphStudioDocument), not the executable graph model.`, { ...context, metadataStudioOnlyKeys: studioKeys });
|
|
89
|
+
}
|
|
90
|
+
if (isPlainObject(metadata) && metadata.graphEntry != null) {
|
|
91
|
+
assertCanonicalGraphEntryContract(metadata.graphEntry, context);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** Primary flat runtime input from a studio companion document. */
|
|
95
|
+
export function primaryRuntimeInputFromStudioDocument(studio) {
|
|
96
|
+
const first = studio?.exampleInputs?.[0];
|
|
97
|
+
const runtimeInput = first?.runtimeInput;
|
|
98
|
+
if (runtimeInput != null && isPlainObject(runtimeInput) && !Array.isArray(runtimeInput)) {
|
|
99
|
+
return runtimeInput;
|
|
100
|
+
}
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { selectByPath } from './pathExpr.js';
|
|
2
|
-
import {
|
|
2
|
+
import { readTaskNodeModelInputSurface, } from './readTaskNodeInputsConfig.js';
|
|
3
3
|
const GRAPH_RESPONSE_MAPPING_ERROR_CODE = 'GRAPH_RESPONSE_MAPPING_INVALID';
|
|
4
4
|
const OMIT = Symbol('graphResponseMapping.omit');
|
|
5
5
|
const FORBIDDEN_GRAPH_RESPONSE_KEYS = [
|
|
@@ -30,7 +30,6 @@ function isSupportedSelector(v) {
|
|
|
30
30
|
v.type === 'executionPath' ||
|
|
31
31
|
v.type === 'nodeMetadata' ||
|
|
32
32
|
v.type === 'nodeInputsConfig' ||
|
|
33
|
-
v.type === 'nodeInputs' ||
|
|
34
33
|
v.type === 'literal' ||
|
|
35
34
|
v.type === 'firstPresent');
|
|
36
35
|
}
|
|
@@ -94,7 +93,7 @@ function resolveSelector(selector, context, path, missing) {
|
|
|
94
93
|
}
|
|
95
94
|
return presentOrMissing(selectByPath(node.metadata, sourcePath), missing);
|
|
96
95
|
}
|
|
97
|
-
if (
|
|
96
|
+
if (selector.type === 'nodeInputsConfig') {
|
|
98
97
|
const inputSelector = selector;
|
|
99
98
|
const nodeId = assertNonEmptyString(inputSelector.nodeId, `${path}.nodeId`, `${selector.type}.nodeId`);
|
|
100
99
|
const sourcePath = assertNonEmptyString(inputSelector.path, `${path}.path`, `${selector.type}.path`);
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ensures `executionMemory.input.raw` holds the active MAIN payload for PRE synthesis paths
|
|
3
|
-
* (e.g. `inputsConfig` bindings with `path: "input.raw"`).
|
|
4
|
-
*/
|
|
5
|
-
export declare function mirrorRuntimeInputRawOnExecutionMemory(executionMemory: Record<string, unknown>): void;
|
|
6
1
|
/**
|
|
7
2
|
* Mirrors MAIN task input onto `executionMemory.input` for the current graph run.
|
|
8
3
|
*/
|
|
@@ -1,24 +1,6 @@
|
|
|
1
1
|
function isPlainObject(v) {
|
|
2
2
|
return v != null && typeof v === 'object' && !Array.isArray(v);
|
|
3
3
|
}
|
|
4
|
-
/**
|
|
5
|
-
* Ensures `executionMemory.input.raw` holds the active MAIN payload for PRE synthesis paths
|
|
6
|
-
* (e.g. `inputsConfig` bindings with `path: "input.raw"`).
|
|
7
|
-
*/
|
|
8
|
-
export function mirrorRuntimeInputRawOnExecutionMemory(executionMemory) {
|
|
9
|
-
const main = executionMemory.input;
|
|
10
|
-
if (main === undefined)
|
|
11
|
-
return;
|
|
12
|
-
const raw = isPlainObject(main)
|
|
13
|
-
? (() => {
|
|
14
|
-
const copy = { ...main };
|
|
15
|
-
delete copy.raw;
|
|
16
|
-
return copy;
|
|
17
|
-
})()
|
|
18
|
-
: main;
|
|
19
|
-
const prev = isPlainObject(executionMemory.input) ? executionMemory.input : {};
|
|
20
|
-
executionMemory.input = { ...prev, raw };
|
|
21
|
-
}
|
|
22
4
|
/**
|
|
23
5
|
* Mirrors MAIN task input onto `executionMemory.input` for the current graph run.
|
|
24
6
|
*/
|
|
@@ -1,22 +1,11 @@
|
|
|
1
1
|
import type { TaskNode } from '../types/refs.js';
|
|
2
|
-
export type TaskNodeInputsConfigCarrier = TaskNode & {
|
|
3
|
-
/** Runtime payload bindings (`executionMemoryPath`); canonical task-node field. */
|
|
4
|
-
inputsConfig?: Record<string, unknown>;
|
|
5
|
-
/** @deprecated Prefer `inputsConfig`. */
|
|
6
|
-
inputBindings?: Record<string, unknown>;
|
|
7
|
-
/**
|
|
8
|
-
* @deprecated Prefer `inputsConfig` — collides with `runtime.inputs` (caller bag)
|
|
9
|
-
* and `RunTaskRequest.inputs` on the wire.
|
|
10
|
-
*/
|
|
11
|
-
inputs?: Record<string, unknown>;
|
|
12
|
-
};
|
|
13
2
|
/**
|
|
14
|
-
* Reads a task node's runtime binding recipe
|
|
3
|
+
* Reads a task node's runtime binding recipe (`inputsConfig` only).
|
|
15
4
|
* Finalizers use `FinalizerNode.inputs` directly; this helper is for task nodes only.
|
|
16
5
|
*/
|
|
17
6
|
export declare function readTaskNodeInputsConfig(node: TaskNode): Record<string, unknown>;
|
|
18
7
|
/**
|
|
19
|
-
* Model surface for graph.response
|
|
8
|
+
* Model surface for graph.response `nodeInputsConfig` selectors:
|
|
20
9
|
* runtime bindings plus `taskVariable` (taskVariable wins on key collision).
|
|
21
10
|
*/
|
|
22
11
|
export declare function readTaskNodeModelInputSurface(node: TaskNode): Record<string, unknown>;
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Reads a task node's runtime binding recipe
|
|
2
|
+
* Reads a task node's runtime binding recipe (`inputsConfig` only).
|
|
3
3
|
* Finalizers use `FinalizerNode.inputs` directly; this helper is for task nodes only.
|
|
4
4
|
*/
|
|
5
5
|
export function readTaskNodeInputsConfig(node) {
|
|
6
|
-
const
|
|
7
|
-
const recipe = n.inputsConfig ?? n.inputBindings ?? n.inputs;
|
|
6
|
+
const recipe = node.inputsConfig;
|
|
8
7
|
if (recipe != null && typeof recipe === 'object' && !Array.isArray(recipe)) {
|
|
9
8
|
return recipe;
|
|
10
9
|
}
|
|
11
10
|
return {};
|
|
12
11
|
}
|
|
13
12
|
/**
|
|
14
|
-
* Model surface for graph.response
|
|
13
|
+
* Model surface for graph.response `nodeInputsConfig` selectors:
|
|
15
14
|
* runtime bindings plus `taskVariable` (taskVariable wins on key collision).
|
|
16
15
|
*/
|
|
17
16
|
export function readTaskNodeModelInputSurface(node) {
|
|
@@ -23,5 +22,5 @@ export function readTaskNodeModelInputSurface(node) {
|
|
|
23
22
|
return bindings;
|
|
24
23
|
}
|
|
25
24
|
export function isNodeInputsConfigSelectorType(type) {
|
|
26
|
-
return type === 'nodeInputsConfig'
|
|
25
|
+
return type === 'nodeInputsConfig';
|
|
27
26
|
}
|
|
@@ -8,9 +8,7 @@ export declare function resolveTaskNodeTaskVariableForRunTask(args: {
|
|
|
8
8
|
node: TaskNode;
|
|
9
9
|
buckets: ExecutionVariableBuckets;
|
|
10
10
|
}): Record<string, unknown>;
|
|
11
|
-
/**
|
|
12
|
-
* Resolves runtime payload bindings from `node.inputsConfig` only (plus deprecated aliases).
|
|
13
|
-
*/
|
|
11
|
+
/** Resolves runtime payload bindings from `node.inputsConfig` only. */
|
|
14
12
|
export declare function resolveTaskNodeInputBindingsForRunTask(args: {
|
|
15
13
|
node: TaskNode;
|
|
16
14
|
execution: Record<string, unknown>;
|
|
@@ -81,9 +81,7 @@ function resolveTaskVariableValue(val, buckets) {
|
|
|
81
81
|
}
|
|
82
82
|
return Object.keys(nested).length > 0 ? nested : val;
|
|
83
83
|
}
|
|
84
|
-
/**
|
|
85
|
-
* Resolves runtime payload bindings from `node.inputsConfig` only (plus deprecated aliases).
|
|
86
|
-
*/
|
|
84
|
+
/** Resolves runtime payload bindings from `node.inputsConfig` only. */
|
|
87
85
|
export function resolveTaskNodeInputBindingsForRunTask(args) {
|
|
88
86
|
const recipe = readTaskNodeInputsConfig(args.node);
|
|
89
87
|
const out = {};
|
|
@@ -102,39 +100,16 @@ export function resolveTaskNodeInputBindingsForRunTask(args) {
|
|
|
102
100
|
}
|
|
103
101
|
return out;
|
|
104
102
|
}
|
|
105
|
-
/** Legacy literals / variable refs still under deprecated `inputs`. */
|
|
106
|
-
function resolveLegacyTaskVariableFromInputBindings(args) {
|
|
107
|
-
const recipe = readTaskNodeInputsConfig(args.node);
|
|
108
|
-
const out = {};
|
|
109
|
-
for (const [key, val] of Object.entries(recipe)) {
|
|
110
|
-
if (isExecutionMemoryBinding(val))
|
|
111
|
-
continue;
|
|
112
|
-
if (isPlainObject(val) && typeof val.$path === 'string') {
|
|
113
|
-
const p = val.$path.trim();
|
|
114
|
-
if (!p.startsWith('variables.') && !p.startsWith('jobVariables.') && !p.startsWith('taskVariables.')) {
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
const resolved = resolveTaskVariableValue(val, args.buckets);
|
|
119
|
-
if (resolved !== undefined)
|
|
120
|
-
out[key] = resolved;
|
|
121
|
-
}
|
|
122
|
-
return out;
|
|
123
|
-
}
|
|
124
103
|
/**
|
|
125
104
|
* Resolves task-node model input for MAIN `RunTaskRequest.input`:
|
|
126
105
|
* `taskVariable` plus execution-memory bindings (separate buckets, no merge).
|
|
127
106
|
*/
|
|
128
107
|
export function resolveTaskNodeInputsForRunTask(args) {
|
|
129
108
|
const buckets = readExecutionVariableBuckets(args.execution);
|
|
130
|
-
const legacyTaskVar = resolveLegacyTaskVariableFromInputBindings({
|
|
131
|
-
node: args.node,
|
|
132
|
-
buckets,
|
|
133
|
-
});
|
|
134
109
|
const taskVariable = resolveTaskNodeTaskVariableForRunTask({ node: args.node, buckets });
|
|
135
110
|
const bindings = resolveTaskNodeInputBindingsForRunTask({
|
|
136
111
|
node: args.node,
|
|
137
112
|
execution: args.execution,
|
|
138
113
|
});
|
|
139
|
-
return { ...
|
|
114
|
+
return { ...taskVariable, ...bindings };
|
|
140
115
|
}
|
|
@@ -7,7 +7,7 @@ import { resolveExecutionPipelineForTaskNode } from './resolveExecutionPipelineF
|
|
|
7
7
|
import { resolveTaskNodeInputsForRunTask } from './resolveTaskNodeInputs.js';
|
|
8
8
|
import { assertAiTasksNodeExtensionsValid } from '../inspection/validateAiTasksNodeExtensions.js';
|
|
9
9
|
import { buildRunTaskMainInput, extractCallerInputsBag } from './resolveGraphEngineMemoryPaths.js';
|
|
10
|
-
import {
|
|
10
|
+
import { mirrorStructuredInputOntoExecutionMemory, } from './materializeStructuredRunTaskInput.js';
|
|
11
11
|
import { xynthesizedOutboundForNode } from './graphRunExecutionSeed.js';
|
|
12
12
|
import { buildTaskNodeJobContext } from './buildTaskNodeJobContext.js';
|
|
13
13
|
import { resolveNarrixForTaskNode } from './resolveNarrixForTaskNode.js';
|
|
@@ -76,7 +76,6 @@ export async function buildTaskNodeRunTaskRequest(args) {
|
|
|
76
76
|
const clearSynthForTask = args.clearSynthesizedContextPerNode === true &&
|
|
77
77
|
hasSynthesizedContextPreStep &&
|
|
78
78
|
hasExistingSynthesizedContext;
|
|
79
|
-
mirrorRuntimeInputRawOnExecutionMemory(execution);
|
|
80
79
|
const nodeBindings = resolveTaskNodeInputsForRunTask({ node, execution });
|
|
81
80
|
const jobContext = buildTaskNodeJobContext({
|
|
82
81
|
node,
|
|
@@ -5,6 +5,7 @@ import { EXELLIX_VIRTUAL_BOUNDARY_NODE_METADATA_KEY } from '../inspection/types.
|
|
|
5
5
|
import { getStructuredDataFilterPathViolations } from './dataFiltersEvaluation.js';
|
|
6
6
|
import { conditionWhenSignature, countDefaultModelConfigCases, isEmptyConditionWhen, isGraphAiModelConfig, isModelConfigSelection, } from './modelConfigSelection.js';
|
|
7
7
|
import { assertGraphAiProfileNameString } from './graphAiModelConfig.js';
|
|
8
|
+
import { assertCanonicalGraphDocumentMetadata } from './graphModelStudioSeparation.js';
|
|
8
9
|
/** Top-level keys permitted on a canonical exellix-graph executable graph JSON document. */
|
|
9
10
|
export const CANONICAL_GRAPH_TOP_LEVEL_KEYS = [
|
|
10
11
|
'id',
|
|
@@ -381,6 +382,34 @@ function assertGraphResponseDefinition(value, path, context) {
|
|
|
381
382
|
if (!Object.prototype.hasOwnProperty.call(value, 'shape')) {
|
|
382
383
|
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_GRAPH_DOCUMENT, `${path}.shape is required.`, context);
|
|
383
384
|
}
|
|
385
|
+
assertGraphResponseShapeNoLegacy(value.shape, `${path}.shape`, context);
|
|
386
|
+
}
|
|
387
|
+
function isExecutionMemoryPathBinding(val) {
|
|
388
|
+
return isPlainObject(val) && val.type === 'executionMemoryPath' && typeof val.path === 'string';
|
|
389
|
+
}
|
|
390
|
+
function assertForbidInputRawExecutionPath(dotPath, label, context) {
|
|
391
|
+
if (dotPath === 'input.raw' || dotPath.endsWith('.raw')) {
|
|
392
|
+
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `${label}: executionMemoryPath "${dotPath}" is forbidden — bind to "input" (flat MAIN bucket). input.raw mirroring was removed.`, context);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
function assertGraphResponseShapeNoLegacy(shape, path, context) {
|
|
396
|
+
if (Array.isArray(shape)) {
|
|
397
|
+
shape.forEach((item, index) => assertGraphResponseShapeNoLegacy(item, `${path}[${index}]`, context));
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
if (!isPlainObject(shape))
|
|
401
|
+
return;
|
|
402
|
+
if (shape.type === 'nodeInputs') {
|
|
403
|
+
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_GRAPH_DOCUMENT, `${path}.type "nodeInputs" was removed; use nodeInputsConfig.`, context);
|
|
404
|
+
}
|
|
405
|
+
if (shape.type === 'firstPresent' && Array.isArray(shape.sources)) {
|
|
406
|
+
shape.sources.forEach((source, index) => assertGraphResponseShapeNoLegacy(source, `${path}.sources[${index}]`, context));
|
|
407
|
+
}
|
|
408
|
+
for (const [key, val] of Object.entries(shape)) {
|
|
409
|
+
if (key === 'type' || key === 'nodeId' || key === 'path' || key === 'value' || key === 'sources')
|
|
410
|
+
continue;
|
|
411
|
+
assertGraphResponseShapeNoLegacy(val, `${path}.${key}`, context);
|
|
412
|
+
}
|
|
384
413
|
}
|
|
385
414
|
function findFinalizerType(node) {
|
|
386
415
|
if (!isPlainObject(node))
|
|
@@ -493,6 +522,23 @@ export function assertCanonicalTaskNode(node, context) {
|
|
|
493
522
|
}
|
|
494
523
|
}
|
|
495
524
|
}
|
|
525
|
+
if ('inputs' in taskNode && taskNode.inputs !== undefined) {
|
|
526
|
+
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `Task node "${String(node.id)}": node.inputs was removed — use inputsConfig for executionMemoryPath bindings and taskVariable for prompt literals.`, { jobId: context?.jobId, graphId: context?.graphId, nodeId: String(node.id) });
|
|
527
|
+
}
|
|
528
|
+
if ('inputBindings' in taskNode && taskNode.inputBindings !== undefined) {
|
|
529
|
+
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `Task node "${String(node.id)}": node.inputBindings was removed — use inputsConfig.`, { jobId: context?.jobId, graphId: context?.graphId, nodeId: String(node.id) });
|
|
530
|
+
}
|
|
531
|
+
const inputsConfig = taskNode.inputsConfig;
|
|
532
|
+
if (inputsConfig !== undefined) {
|
|
533
|
+
if (!isPlainObject(inputsConfig) || Array.isArray(inputsConfig)) {
|
|
534
|
+
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `Task node "${String(node.id)}": inputsConfig must be a plain object when present.`, { jobId: context?.jobId, graphId: context?.graphId, nodeId: String(node.id) });
|
|
535
|
+
}
|
|
536
|
+
for (const [bindingKey, bindingVal] of Object.entries(inputsConfig)) {
|
|
537
|
+
if (isExecutionMemoryPathBinding(bindingVal)) {
|
|
538
|
+
assertForbidInputRawExecutionPath(bindingVal.path, `Task node "${String(node.id)}": inputsConfig.${bindingKey}`, { jobId: context?.jobId, graphId: context?.graphId, nodeId: String(node.id) });
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
496
542
|
}
|
|
497
543
|
/**
|
|
498
544
|
* Throws {@link ExellixGraphError} when the value is not a canonical graph document
|
|
@@ -548,6 +594,10 @@ export function assertCanonicalGraphDocument(graph, context) {
|
|
|
548
594
|
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_GRAPH_DOCUMENT, `Graph "${String(resolvedGraphId ?? '?')}": metadata.${key} is not part of the static metadata block. Move this field to \`${target}\`.`, { jobId: context?.jobId, graphId: resolvedGraphId, metadataKey: key });
|
|
549
595
|
}
|
|
550
596
|
}
|
|
597
|
+
assertCanonicalGraphDocumentMetadata(metadata, {
|
|
598
|
+
jobId: context?.jobId,
|
|
599
|
+
graphId: resolvedGraphId,
|
|
600
|
+
});
|
|
551
601
|
const graphEntry = metadata.graphEntry;
|
|
552
602
|
if (graphEntry != null && typeof graphEntry === 'object' && !Array.isArray(graphEntry)) {
|
|
553
603
|
const ge = graphEntry;
|
package/dist/src/types/refs.d.ts
CHANGED
|
@@ -149,12 +149,6 @@ export type GraphResponseSelector = {
|
|
|
149
149
|
type: 'nodeInputsConfig';
|
|
150
150
|
nodeId: string;
|
|
151
151
|
path: string;
|
|
152
|
-
}
|
|
153
|
-
/** @deprecated Use `nodeInputsConfig`. */
|
|
154
|
-
| {
|
|
155
|
-
type: 'nodeInputs';
|
|
156
|
-
nodeId: string;
|
|
157
|
-
path: string;
|
|
158
152
|
} | {
|
|
159
153
|
type: 'literal';
|
|
160
154
|
value: unknown;
|
|
@@ -194,7 +188,7 @@ export type QuestionDrivenItemSpec = {
|
|
|
194
188
|
nodeId: string;
|
|
195
189
|
/**
|
|
196
190
|
* Dot-path under the node definition to read the question from.
|
|
197
|
-
* Default: "
|
|
191
|
+
* Default: "taskVariable.question"
|
|
198
192
|
*/
|
|
199
193
|
questionPath?: string;
|
|
200
194
|
/**
|
|
@@ -207,14 +201,23 @@ export type QuestionDrivenItemSpec = {
|
|
|
207
201
|
*/
|
|
208
202
|
optional?: boolean;
|
|
209
203
|
};
|
|
210
|
-
|
|
211
|
-
|
|
204
|
+
/**
|
|
205
|
+
* A single "read one value from run memory" reference. Same idiom as
|
|
206
|
+
* {@link FinalizerInputBinding} (minus `literal`) and {@link GraphResponseSelector}
|
|
207
|
+
* memory selectors — finalizer configs reuse it so every memory read in the model
|
|
208
|
+
* looks identical: `{ type, path }`.
|
|
209
|
+
*/
|
|
210
|
+
export type FinalizerMemoryRef = {
|
|
211
|
+
type: 'executionMemoryPath';
|
|
212
|
+
path: string;
|
|
213
|
+
optional?: boolean;
|
|
214
|
+
} | {
|
|
215
|
+
type: 'outputsMemoryPath';
|
|
212
216
|
path: string;
|
|
213
|
-
/** Human-readable title for this section. */
|
|
214
|
-
title?: string;
|
|
215
217
|
optional?: boolean;
|
|
216
218
|
};
|
|
217
219
|
export type AggregateFinalizerConfig = {
|
|
220
|
+
/** Build the output object field-by-field from named finalizer `inputs`. */
|
|
218
221
|
strategy: 'object-map';
|
|
219
222
|
map: Record<string, AggregateExpr>;
|
|
220
223
|
omitUndefined?: boolean;
|
|
@@ -228,25 +231,23 @@ export type AggregateFinalizerConfig = {
|
|
|
228
231
|
contractVersion?: string;
|
|
229
232
|
/** Map output keys → node specs. */
|
|
230
233
|
items: Record<string, QuestionDrivenItemSpec>;
|
|
231
|
-
/** Optional extra
|
|
232
|
-
meta?: Record<string,
|
|
233
|
-
type: 'executionMemoryPath' | 'outputsMemoryPath';
|
|
234
|
-
path: string;
|
|
235
|
-
optional?: boolean;
|
|
236
|
-
}>;
|
|
234
|
+
/** Optional extra top-level fields, each read from run memory. */
|
|
235
|
+
meta?: Record<string, FinalizerMemoryRef>;
|
|
237
236
|
} | {
|
|
238
237
|
/**
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
238
|
+
* Build the graph output object by reading one run-memory value per output key.
|
|
239
|
+
* This **is** the finalizer's output: `sections` maps each output field to the
|
|
240
|
+
* memory path it is read from. (Authoring titles belong in the studio document,
|
|
241
|
+
* not here.)
|
|
242
242
|
*/
|
|
243
243
|
strategy: 'report-schema';
|
|
244
|
-
/**
|
|
245
|
-
sections: Record<string,
|
|
246
|
-
/**
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
244
|
+
/** Output field key → where its value is read from in run memory. */
|
|
245
|
+
sections: Record<string, FinalizerMemoryRef>;
|
|
246
|
+
/**
|
|
247
|
+
* When true, scan all section values for CONFIRMED/INFERRED/ASSUMED/UNKNOWN
|
|
248
|
+
* and emit them under the `collectedTags` output field.
|
|
249
|
+
*/
|
|
250
|
+
collectEpistemicTags?: boolean;
|
|
250
251
|
};
|
|
251
252
|
export type BundleFinalizerConfig = {
|
|
252
253
|
strategy: 'bundle';
|
|
@@ -275,8 +276,8 @@ export interface FinalizerNode {
|
|
|
275
276
|
type: 'finalizer';
|
|
276
277
|
finalizerType: 'aggregate' | 'compose' | 'select' | 'bundle' | 'synthesize';
|
|
277
278
|
inputs: Record<string, FinalizerInputBinding>;
|
|
278
|
-
/**
|
|
279
|
-
config: AggregateFinalizerConfig | BundleFinalizerConfig | SelectFinalizerConfig | SynthesizeFinalizerConfig
|
|
279
|
+
/** Strict per-`finalizerType` config. The runtime validator rejects unknown keys and legacy field names. */
|
|
280
|
+
config: AggregateFinalizerConfig | BundleFinalizerConfig | SelectFinalizerConfig | SynthesizeFinalizerConfig;
|
|
280
281
|
/** Writes selected finalizer result fields into graph-owned outputsMemory for final response assembly. */
|
|
281
282
|
outputMapping?: TaskNodeResultMapping;
|
|
282
283
|
outputSchema?: OutputSchema;
|
|
@@ -416,16 +417,10 @@ export interface TaskNode {
|
|
|
416
417
|
}>;
|
|
417
418
|
};
|
|
418
419
|
/**
|
|
419
|
-
* Runtime payload bindings only (`executionMemoryPath` /
|
|
420
|
+
* Runtime payload bindings only (`executionMemoryPath` / memory `$path` refs).
|
|
420
421
|
* See `taskVariable` for prompts and graph-variable hooks.
|
|
421
422
|
*/
|
|
422
423
|
inputsConfig?: Record<string, unknown>;
|
|
423
|
-
/** @deprecated Prefer `inputsConfig`. */
|
|
424
|
-
inputBindings?: Record<string, unknown>;
|
|
425
|
-
/**
|
|
426
|
-
* @deprecated Prefer `inputsConfig` — name collides with `runtime.inputs` (caller bag).
|
|
427
|
-
*/
|
|
428
|
-
inputs?: Record<string, any>;
|
|
429
424
|
/**
|
|
430
425
|
* Dynamic task configuration: prompts, literals, and `{ "$path": "variables.*" }` refs.
|
|
431
426
|
* Not execution-memory payload wires (those belong in `inputsConfig`).
|
|
@@ -612,7 +607,6 @@ export type GraphEntryContract = {
|
|
|
612
607
|
* Optional JSON Schema (e.g. draft 2020-12) for the merged `execution` object. Validators may use it; core runtime does not.
|
|
613
608
|
*/
|
|
614
609
|
executionSchema?: Record<string, unknown>;
|
|
615
|
-
[key: string]: unknown;
|
|
616
610
|
};
|
|
617
611
|
/**
|
|
618
612
|
* @deprecated Use root-level {@link GraphModelObject.response}. The final response shape is executable
|