@exellix/graph-engine 7.7.5 → 7.7.8
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 +17 -0
- package/dist/src/index.d.ts +4 -3
- package/dist/src/index.js +2 -2
- package/dist/src/inspection/graphModelSelection.js +13 -12
- package/dist/src/runtime/ExellixGraphRuntime.js +1 -1
- package/dist/src/runtime/graphAiModelConfig.d.ts +6 -1
- package/dist/src/runtime/graphAiModelConfig.js +24 -3
- package/dist/src/runtime/modelConfigSelection.d.ts +3 -1
- package/dist/src/runtime/modelConfigSelection.js +16 -11
- package/dist/src/runtime/resolveModelConfigForNode.d.ts +3 -2
- package/dist/src/runtime/resolveModelConfigForNode.js +16 -15
- package/dist/src/runtime/validateCanonicalGraphDocument.d.ts +7 -2
- package/dist/src/runtime/validateCanonicalGraphDocument.js +32 -13
- package/dist/src/types/refs.d.ts +7 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 7.7.8
|
|
4
|
+
|
|
5
|
+
### Added (CR-11 execute-time model routing)
|
|
6
|
+
|
|
7
|
+
- **`mergeGraphAiModelConfig`**, **`PartialGraphAiModelConfig`**, **`isPartialGraphAiModelConfig`** — job-default + per-task partial override merge at execute time (`task[slot] ?? graph[slot] ?? engine default`).
|
|
8
|
+
- **`assertCanonicalGraphDocument(..., { mode: 'execute' })`** — accepts partial task `taskConfiguration.modelConfig` and `profile/choice` slot encoding (`cheap/default`, `cyber/deep_forensics`); **`persist`** mode (default) unchanged for save/inspection paths.
|
|
9
|
+
- **`executeGraph`** uses execute-mode validation so hosts can POST authored graph JSON without pre-merging model slots or stripping `/`.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- **`looksLikeConcreteModelId`** — only provider-prefixed ids are concrete; `profile/choice` strings are valid graph aliases.
|
|
14
|
+
- **`inspectGraphModelSelection`** — reports merged effective routing when task overrides are partial.
|
|
15
|
+
|
|
16
|
+
### Migration (hosts / graphs-studio)
|
|
17
|
+
|
|
18
|
+
- Remove execute-time `skipCanonicalAssert`, host-side job→node model merge, and slot `/` stripping once on **7.7.8+** (see CR-11 / CR-9).
|
|
19
|
+
|
|
3
20
|
## 7.7.3
|
|
4
21
|
|
|
5
22
|
### Changed
|
package/dist/src/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
export type { HostExecuteGraphRunOptions, MainReadinessPolicy, ExecutionStepOption, StepRetryPolicy, ActivixNodeActivityExellixConfig, SkillKeyResolutionOptions, RunTaskRequest as ExellixGraphRunTaskRequest, RunTaskResponse as ExellixGraphRunTaskResponse, } from './types/options.js';
|
|
16
16
|
export type { AiTaskProfileMetadata, AiTaskProfileWebScoping, AiTaskProfileInputSynthesis, } from './types/aiTaskProfile.js';
|
|
17
17
|
export type { ExecutionStrategyInvocation, ExecutionStrategyPhase, ExecutionStrategyWrapperKey, SmartInputConfig, TaskStrategyItemData, XynthesizedDestinationScope, XynthesizedMemory, XynthesizedOutputConfig, } from './types/aiTasksDerivedTypes.js';
|
|
18
|
-
export type { Graph, GraphModelObject, GraphAiModelConfig, GraphModelAliasConfig, GraphRuntimeNodeConfig, TaskNodeRuntimeObject, GraphDocumentMetadata, GraphEntryContract, GraphEntryExecutionInputSpec, GraphEntryInputKind, GraphEntryInputSpecBase, GraphEntryInputSpec, GraphEntryValueInputKind, GraphEntryValueInputSpec, GraphResponseDefinition, GraphResponseMissingBehavior, GraphResponseSelector, GraphResponseShape, GraphResponseContract, GraphResponseMapping, GraphResponseMappingMissingBehavior, GraphResponseMappingSelector, GraphResponseMappingTarget, GraphExecutionDefaults, GraphExecutionMode, GraphOutputMode, GraphFlowOutline, GraphCoreObjective, GraphNodesResponses, GraphNode, TaskNode, TaskNodeConditions, TaskNodeConditionWhen, TaskNodeJsonCondition, TaskNodeJsConditionFunction, TaskNodeAiCondition, TaskNodeConditionParameters, ModelConfigSelection, ModelConfigCase, TaskNodePureMetadata, TaskNodeExecutionPipelineStep, TaskOutputValidation, FinalizerNode, FinalizerInputBinding, OutputSchema, AggregateFinalizerConfig, BundleFinalizerConfig, SelectFinalizerConfig, SynthesizeFinalizerConfig, UtilityExecutionPolicy, Job, CatalogPlanningKind, CatalogRequestStatus, CatalogBinding, ScopedQuestionCatalogRequestEntry, DiscoveryDefinitionCatalogRequestEntry, DiscoveryDefinitionCatalogRequest, DiscoveryDefinitionPlanningFields, CatalogRequestEntry, StructuredDataFiltersV1, ConditionsDataFilters, } from './types/refs.js';
|
|
18
|
+
export type { Graph, GraphModelObject, GraphAiModelConfig, PartialGraphAiModelConfig, GraphModelAliasConfig, GraphRuntimeNodeConfig, TaskNodeRuntimeObject, GraphDocumentMetadata, GraphEntryContract, GraphEntryExecutionInputSpec, GraphEntryInputKind, GraphEntryInputSpecBase, GraphEntryInputSpec, GraphEntryValueInputKind, GraphEntryValueInputSpec, GraphResponseDefinition, GraphResponseMissingBehavior, GraphResponseSelector, GraphResponseShape, GraphResponseContract, GraphResponseMapping, GraphResponseMappingMissingBehavior, GraphResponseMappingSelector, GraphResponseMappingTarget, GraphExecutionDefaults, GraphExecutionMode, GraphOutputMode, GraphFlowOutline, GraphCoreObjective, GraphNodesResponses, GraphNode, TaskNode, TaskNodeConditions, TaskNodeConditionWhen, TaskNodeJsonCondition, TaskNodeJsConditionFunction, TaskNodeAiCondition, TaskNodeConditionParameters, ModelConfigSelection, ModelConfigCase, TaskNodePureMetadata, TaskNodeExecutionPipelineStep, TaskOutputValidation, FinalizerNode, FinalizerInputBinding, OutputSchema, AggregateFinalizerConfig, BundleFinalizerConfig, SelectFinalizerConfig, SynthesizeFinalizerConfig, UtilityExecutionPolicy, Job, CatalogPlanningKind, CatalogRequestStatus, CatalogBinding, ScopedQuestionCatalogRequestEntry, DiscoveryDefinitionCatalogRequestEntry, DiscoveryDefinitionCatalogRequest, DiscoveryDefinitionPlanningFields, CatalogRequestEntry, StructuredDataFiltersV1, ConditionsDataFilters, } from './types/refs.js';
|
|
19
19
|
export { mergeGraphDocumentModel, EXELLIX_GRAPH_MODEL_VARIABLE_KEY, EXELLIX_STRUCTURED_DATA_FILTERS_V1, } from './types/refs.js';
|
|
20
20
|
export type { TaskNodeTaskConfiguration, TaskNodeScopedDataReaderPackSlot, } from './types/taskNodeConfiguration.js';
|
|
21
21
|
export { getTaskConfiguration } from './types/taskNodeConfiguration.js';
|
|
@@ -39,7 +39,7 @@ export type { PathSegment } from './runtime/pathExpr.js';
|
|
|
39
39
|
export { evaluateStructuredDataFilters, evaluateDataFilterPredicate, getStructuredDataFilterPathViolations, isStructuredDataFiltersV1, } from './runtime/dataFiltersEvaluation.js';
|
|
40
40
|
export { evaluateTaskNodeConditions, evaluateConditionWhen, applyConditionNegate, } from './runtime/taskNodeConditionsEvaluation.js';
|
|
41
41
|
export type { TaskNodeConditionsEvalDeps, TaskNodeConditionsEvalResult, TaskNodeConditionEvalContext, } from './runtime/taskNodeConditionsEvaluation.js';
|
|
42
|
-
export { resolveModelConfigForNode, resolveGraphAiModelConfig, toRunTaskModelConfig, DEFAULT_GRAPH_AI_MODEL_IDS, DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, } from './runtime/resolveModelConfigForNode.js';
|
|
42
|
+
export { resolveModelConfigForNode, resolveGraphAiModelConfig, toRunTaskModelConfig, mergeGraphAiModelConfig, DEFAULT_GRAPH_AI_MODEL_IDS, DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, } from './runtime/resolveModelConfigForNode.js';
|
|
43
43
|
export { looksLikeConcreteModelId, isGraphAiProfileName, assertGraphAiProfileNameString, type RunTaskModelConfigWire, type EngineModelPhase, } from './runtime/graphAiModelConfig.js';
|
|
44
44
|
/** @deprecated Use {@link resolveGraphAiModelConfig}. */
|
|
45
45
|
export { resolveGraphAiModelConfig as resolveGraphAiModelConfigAliases } from './runtime/graphAiModelConfig.js';
|
|
@@ -47,7 +47,7 @@ export { resolveGraphAiModelConfig as resolveGraphAiModelConfigAliases } from '.
|
|
|
47
47
|
export { isGraphAiProfileName as isModelAliasString } from './runtime/graphAiModelConfig.js';
|
|
48
48
|
/** @deprecated Use {@link assertGraphAiProfileNameString}. */
|
|
49
49
|
export { assertGraphAiProfileNameString as assertGraphModelAliasString } from './runtime/graphAiModelConfig.js';
|
|
50
|
-
export { isGraphAiModelConfig, isModelConfigSelection, isEmptyConditionWhen, conditionWhenSignature, countDefaultModelConfigCases, } from './runtime/modelConfigSelection.js';
|
|
50
|
+
export { isGraphAiModelConfig, isPartialGraphAiModelConfig, isModelConfigSelection, isEmptyConditionWhen, conditionWhenSignature, countDefaultModelConfigCases, } from './runtime/modelConfigSelection.js';
|
|
51
51
|
export { GRAPH_ENGINE_MEMORY_PATH_ROOTS, splitGraphEngineMemoryPath, isAllowedGraphEngineMemoryPath, graphEngineMemoryPathValidationMessage, } from './runtime/graphEngineMemoryPaths.js';
|
|
52
52
|
export { buildRunTaskMainInput, extractCallerInputsBag, buildGraphEngineMemoryResolutionRoot, buildGraphEngineMemoryResolutionRootFromWorkingMemory, resolveGraphEngineMemoryPathValue, } from './runtime/resolveGraphEngineMemoryPaths.js';
|
|
53
53
|
export { mirrorStructuredInputOntoExecutionMemory, } from './runtime/materializeStructuredRunTaskInput.js';
|
|
@@ -79,6 +79,7 @@ export { buildExellixGraphRuntimeObjects, setRuntimeObjectsLastJobId, summarizeR
|
|
|
79
79
|
export type { ActivixQueryableClient, LogxerQueryableClient, LogxerLogLine, PackageRuntimeObjects, RuntimeObjects, BuildExellixGraphRuntimeObjectsInput, RuntimeObjectsObservabilitySummary, } from './runtime/runtimeObjects.js';
|
|
80
80
|
export type { GetJobLogsInput, GetJobLogsResult, QueryableLogLine } from '@x12i/logxer';
|
|
81
81
|
export { assertCanonicalGraphDocument, assertCanonicalTaskNode, getCanonicalGraphDocumentViolations, CANONICAL_GRAPH_TOP_LEVEL_KEYS, } from './runtime/validateCanonicalGraphDocument.js';
|
|
82
|
+
export type { AssertCanonicalGraphDocumentOptions, CanonicalGraphDocumentValidationMode, } from './runtime/validateCanonicalGraphDocument.js';
|
|
82
83
|
export { assertCanonicalGraphRuntimeObject } from './runtime/validateCanonicalGraphRuntime.js';
|
|
83
84
|
export { GRAPH_ENTRY_STUDIO_ONLY_KEYS, GRAPH_METADATA_STUDIO_ONLY_KEYS, stripGraphModelStudioFields, primaryRuntimeInputFromStudioDocument, getGraphEntryStudioOnlyKeyViolations, getGraphMetadataStudioOnlyKeyViolations, getGraphEntryEmptyInputPathViolations, } from './runtime/graphModelStudioSeparation.js';
|
|
84
85
|
export type { GraphStudioDocument } from './runtime/graphModelStudioSeparation.js';
|
package/dist/src/index.js
CHANGED
|
@@ -29,7 +29,7 @@ export { mergeMemory } from './runtime/memory.js';
|
|
|
29
29
|
export { selectByPath, writeByPath, parsePath } from './runtime/pathExpr.js';
|
|
30
30
|
export { evaluateStructuredDataFilters, evaluateDataFilterPredicate, getStructuredDataFilterPathViolations, isStructuredDataFiltersV1, } from './runtime/dataFiltersEvaluation.js';
|
|
31
31
|
export { evaluateTaskNodeConditions, evaluateConditionWhen, applyConditionNegate, } from './runtime/taskNodeConditionsEvaluation.js';
|
|
32
|
-
export { resolveModelConfigForNode, resolveGraphAiModelConfig, toRunTaskModelConfig, DEFAULT_GRAPH_AI_MODEL_IDS, DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, } from './runtime/resolveModelConfigForNode.js';
|
|
32
|
+
export { resolveModelConfigForNode, resolveGraphAiModelConfig, toRunTaskModelConfig, mergeGraphAiModelConfig, DEFAULT_GRAPH_AI_MODEL_IDS, DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, } from './runtime/resolveModelConfigForNode.js';
|
|
33
33
|
export { looksLikeConcreteModelId, isGraphAiProfileName, assertGraphAiProfileNameString, } from './runtime/graphAiModelConfig.js';
|
|
34
34
|
/** @deprecated Use {@link resolveGraphAiModelConfig}. */
|
|
35
35
|
export { resolveGraphAiModelConfig as resolveGraphAiModelConfigAliases } from './runtime/graphAiModelConfig.js';
|
|
@@ -37,7 +37,7 @@ export { resolveGraphAiModelConfig as resolveGraphAiModelConfigAliases } from '.
|
|
|
37
37
|
export { isGraphAiProfileName as isModelAliasString } from './runtime/graphAiModelConfig.js';
|
|
38
38
|
/** @deprecated Use {@link assertGraphAiProfileNameString}. */
|
|
39
39
|
export { assertGraphAiProfileNameString as assertGraphModelAliasString } from './runtime/graphAiModelConfig.js';
|
|
40
|
-
export { isGraphAiModelConfig, isModelConfigSelection, isEmptyConditionWhen, conditionWhenSignature, countDefaultModelConfigCases, } from './runtime/modelConfigSelection.js';
|
|
40
|
+
export { isGraphAiModelConfig, isPartialGraphAiModelConfig, 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
43
|
export { mirrorStructuredInputOntoExecutionMemory, } from './runtime/materializeStructuredRunTaskInput.js';
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { isEmptyConditionWhen, isModelConfigSelection } from '../runtime/modelConfigSelection.js';
|
|
2
|
-
import { DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG } from '../runtime/graphAiModelConfig.js';
|
|
1
|
+
import { isEmptyConditionWhen, isModelConfigSelection, isPartialGraphAiModelConfig } from '../runtime/modelConfigSelection.js';
|
|
2
|
+
import { DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, mergeGraphAiModelConfig } from '../runtime/graphAiModelConfig.js';
|
|
3
3
|
function asArrayNodes(graph) {
|
|
4
4
|
return Array.isArray(graph.nodes) ? graph.nodes : Object.values(graph.nodes ?? {});
|
|
5
5
|
}
|
|
6
|
-
/** Returns the no-`when` default case profiles from a selection, if present
|
|
6
|
+
/** Returns the no-`when` default case partial profiles from a selection, if present. */
|
|
7
7
|
function defaultCaseProfiles(selection) {
|
|
8
8
|
if (!isModelConfigSelection(selection))
|
|
9
9
|
return undefined;
|
|
10
10
|
const sel = selection;
|
|
11
11
|
for (const c of sel.cases ?? []) {
|
|
12
|
-
if (isEmptyConditionWhen(c.when))
|
|
12
|
+
if (isEmptyConditionWhen(c.when) && isPartialGraphAiModelConfig(c.modelConfig)) {
|
|
13
13
|
return c.modelConfig;
|
|
14
|
+
}
|
|
14
15
|
}
|
|
15
16
|
return undefined;
|
|
16
17
|
}
|
|
@@ -36,7 +37,10 @@ function finalizerUsesModel(node) {
|
|
|
36
37
|
* provider id resolution happens inside `@exellix/ai-tasks`.
|
|
37
38
|
*/
|
|
38
39
|
export function inspectGraphModelSelection(graph) {
|
|
39
|
-
const
|
|
40
|
+
const graphDefaultPartial = defaultCaseProfiles(graph.modelConfig);
|
|
41
|
+
const graphDefaultProfiles = graphDefaultPartial
|
|
42
|
+
? mergeGraphAiModelConfig(DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, graphDefaultPartial)
|
|
43
|
+
: undefined;
|
|
40
44
|
const nodes = [];
|
|
41
45
|
const aliases = new Set();
|
|
42
46
|
const addAliases = (cfg) => {
|
|
@@ -76,15 +80,12 @@ export function inspectGraphModelSelection(graph) {
|
|
|
76
80
|
const taskNode = raw;
|
|
77
81
|
const nodeSelection = taskNode.taskConfiguration?.modelConfig;
|
|
78
82
|
const nodeDefault = defaultCaseProfiles(nodeSelection);
|
|
83
|
+
const graphPartial = graphDefaultPartial;
|
|
79
84
|
let profiles;
|
|
80
85
|
let source;
|
|
81
|
-
if (nodeDefault) {
|
|
82
|
-
profiles = nodeDefault;
|
|
83
|
-
source = 'node';
|
|
84
|
-
}
|
|
85
|
-
else if (graphDefaultProfiles) {
|
|
86
|
-
profiles = graphDefaultProfiles;
|
|
87
|
-
source = 'graph';
|
|
86
|
+
if (nodeDefault != null || graphPartial != null) {
|
|
87
|
+
profiles = mergeGraphAiModelConfig(DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, graphPartial, nodeDefault);
|
|
88
|
+
source = nodeDefault != null ? 'node' : 'graph';
|
|
88
89
|
}
|
|
89
90
|
else {
|
|
90
91
|
profiles = DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG;
|
|
@@ -1175,7 +1175,7 @@ export function createExellixGraphRuntime(opts) {
|
|
|
1175
1175
|
return runWithAiTasksStackLogging(merged.logging, () => runGraphWithLogContext({ jobId, taskId: graphTaskId, graphId: resolvedGraphId, runId: graphTaskId }, async () => {
|
|
1176
1176
|
bindGraphEngineRunLogxer(runLogxer);
|
|
1177
1177
|
try {
|
|
1178
|
-
assertCanonicalGraphDocument(graph, { jobId, graphId: resolvedGraphId });
|
|
1178
|
+
assertCanonicalGraphDocument(graph, { jobId, graphId: resolvedGraphId }, { mode: 'execute' });
|
|
1179
1179
|
assertCanonicalGraphRuntimeObject(runtime, { jobId, graphId: resolvedGraphId });
|
|
1180
1180
|
let runxClient = opts.runx;
|
|
1181
1181
|
if (graphNeedsRunxClient(graph)) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GraphAiModelConfig } from '../types/refs.js';
|
|
1
|
+
import type { GraphAiModelConfig, PartialGraphAiModelConfig } from '../types/refs.js';
|
|
2
2
|
/** Wire shape for `@exellix/ai-tasks` `RunTaskRequest.modelConfig` (8.4+). */
|
|
3
3
|
export type RunTaskModelConfigWire = {
|
|
4
4
|
preActionModel: string;
|
|
@@ -38,3 +38,8 @@ export type EngineModelPhase = 'pre' | 'main' | 'post';
|
|
|
38
38
|
* Values are profile alias names from the graph document; ai-tasks resolves and routes slots per phase.
|
|
39
39
|
*/
|
|
40
40
|
export declare function toRunTaskModelConfig(config: GraphAiModelConfig): RunTaskModelConfigWire;
|
|
41
|
+
/**
|
|
42
|
+
* Merges layered partial configs left-to-right; later layers override earlier slots.
|
|
43
|
+
* Missing slots after all layers fall back to {@link DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG}.
|
|
44
|
+
*/
|
|
45
|
+
export declare function mergeGraphAiModelConfig(...layers: Array<PartialGraphAiModelConfig | undefined>): GraphAiModelConfig;
|
|
@@ -37,8 +37,6 @@ export function looksLikeConcreteModelId(value) {
|
|
|
37
37
|
const trimmed = value.trim();
|
|
38
38
|
if (trimmed === '')
|
|
39
39
|
return false;
|
|
40
|
-
if (trimmed.includes('/'))
|
|
41
|
-
return true;
|
|
42
40
|
const lower = trimmed.toLowerCase();
|
|
43
41
|
return CONCRETE_MODEL_PREFIXES.some((p) => lower.startsWith(p));
|
|
44
42
|
}
|
|
@@ -55,7 +53,7 @@ export function isGraphAiProfileName(value) {
|
|
|
55
53
|
}
|
|
56
54
|
export function assertGraphAiProfileNameString(value, path, context) {
|
|
57
55
|
if (!isGraphAiProfileName(value)) {
|
|
58
|
-
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_MODEL_CONFIG, `${path} must be an AI profile alias
|
|
56
|
+
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_MODEL_CONFIG, `${path} must be an AI profile alias, profile/choice key, or shortcut (not a concrete provider model id); got ${JSON.stringify(value)}`, context);
|
|
59
57
|
}
|
|
60
58
|
}
|
|
61
59
|
function normalizeModelSlot(slot, context) {
|
|
@@ -89,3 +87,26 @@ export function toRunTaskModelConfig(config) {
|
|
|
89
87
|
postActionModel: config.postActionModel,
|
|
90
88
|
};
|
|
91
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Merges layered partial configs left-to-right; later layers override earlier slots.
|
|
92
|
+
* Missing slots after all layers fall back to {@link DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG}.
|
|
93
|
+
*/
|
|
94
|
+
export function mergeGraphAiModelConfig(...layers) {
|
|
95
|
+
const merged = {};
|
|
96
|
+
for (const layer of layers) {
|
|
97
|
+
if (layer == null)
|
|
98
|
+
continue;
|
|
99
|
+
if (layer.preActionModel != null)
|
|
100
|
+
merged.preActionModel = layer.preActionModel.trim();
|
|
101
|
+
if (layer.skillModel != null)
|
|
102
|
+
merged.skillModel = layer.skillModel.trim();
|
|
103
|
+
if (layer.postActionModel != null)
|
|
104
|
+
merged.postActionModel = layer.postActionModel.trim();
|
|
105
|
+
}
|
|
106
|
+
const base = DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG;
|
|
107
|
+
return {
|
|
108
|
+
preActionModel: merged.preActionModel ?? base.preActionModel,
|
|
109
|
+
skillModel: merged.skillModel ?? base.skillModel,
|
|
110
|
+
postActionModel: merged.postActionModel ?? base.postActionModel,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type { GraphAiModelConfig, ModelConfigCase, ModelConfigSelection, TaskNodeConditionWhen } from '../types/refs.js';
|
|
1
|
+
import type { GraphAiModelConfig, ModelConfigCase, ModelConfigSelection, PartialGraphAiModelConfig, TaskNodeConditionWhen } from '../types/refs.js';
|
|
2
|
+
/** True when `value` is a non-empty subset of the three-phase model slots (partial or complete). */
|
|
3
|
+
export declare function isPartialGraphAiModelConfig(value: unknown): value is PartialGraphAiModelConfig;
|
|
2
4
|
export declare function isGraphAiModelConfig(value: unknown): value is GraphAiModelConfig;
|
|
3
5
|
export declare function isEmptyConditionWhen(when: TaskNodeConditionWhen | undefined): boolean;
|
|
4
6
|
export declare function isModelConfigSelection(value: unknown): value is ModelConfigSelection;
|
|
@@ -1,18 +1,23 @@
|
|
|
1
|
-
|
|
1
|
+
const MODEL_CONFIG_SLOT_KEYS = ['preActionModel', 'skillModel', 'postActionModel'];
|
|
2
|
+
/** True when `value` is a non-empty subset of the three-phase model slots (partial or complete). */
|
|
3
|
+
export function isPartialGraphAiModelConfig(value) {
|
|
2
4
|
if (value == null || typeof value !== 'object' || Array.isArray(value))
|
|
3
5
|
return false;
|
|
4
6
|
const o = value;
|
|
5
7
|
const keys = Object.keys(o);
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
if (keys.length === 0 || keys.length > MODEL_CONFIG_SLOT_KEYS.length)
|
|
9
|
+
return false;
|
|
10
|
+
if (!keys.every((k) => MODEL_CONFIG_SLOT_KEYS.includes(k)))
|
|
11
|
+
return false;
|
|
12
|
+
return keys.every((k) => typeof o[k] === 'string' && o[k].trim() !== '');
|
|
13
|
+
}
|
|
14
|
+
export function isGraphAiModelConfig(value) {
|
|
15
|
+
if (!isPartialGraphAiModelConfig(value))
|
|
16
|
+
return false;
|
|
17
|
+
const o = value;
|
|
18
|
+
return (o.preActionModel != null &&
|
|
19
|
+
o.skillModel != null &&
|
|
20
|
+
o.postActionModel != null);
|
|
16
21
|
}
|
|
17
22
|
export function isEmptyConditionWhen(when) {
|
|
18
23
|
if (when == null)
|
|
@@ -14,8 +14,9 @@ export type ResolveModelConfigForNodeArgs = {
|
|
|
14
14
|
};
|
|
15
15
|
/**
|
|
16
16
|
* Resolves effective model config for a task node from the graph document only.
|
|
17
|
-
*
|
|
17
|
+
* Merges `node.taskConfiguration.modelConfig` (partial override) over `graph.modelConfig` (job default),
|
|
18
|
+
* then engine defaults for any remaining slots.
|
|
18
19
|
*/
|
|
19
20
|
export declare function resolveModelConfigForNode(args: ResolveModelConfigForNodeArgs): Promise<GraphAiModelConfig>;
|
|
20
21
|
export { conditionWhenSignature, isEmptyConditionWhen };
|
|
21
|
-
export { resolveGraphAiModelConfig, toRunTaskModelConfig, DEFAULT_GRAPH_AI_MODEL_IDS, DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, } from './graphAiModelConfig.js';
|
|
22
|
+
export { resolveGraphAiModelConfig, toRunTaskModelConfig, mergeGraphAiModelConfig, DEFAULT_GRAPH_AI_MODEL_IDS, DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, } from './graphAiModelConfig.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { conditionWhenSignature, isEmptyConditionWhen,
|
|
1
|
+
import { conditionWhenSignature, isEmptyConditionWhen, isModelConfigSelection, isPartialGraphAiModelConfig, } from './modelConfigSelection.js';
|
|
2
2
|
import { evaluateConditionWhen } from './taskNodeConditionsEvaluation.js';
|
|
3
|
-
import { DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, } from './graphAiModelConfig.js';
|
|
3
|
+
import { DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, mergeGraphAiModelConfig, resolveGraphAiModelConfig, } from './graphAiModelConfig.js';
|
|
4
4
|
async function selectFromCases(selection, ctx, executionInput, runx) {
|
|
5
5
|
const cases = selection.cases;
|
|
6
6
|
if (!Array.isArray(cases) || cases.length === 0)
|
|
@@ -16,17 +16,17 @@ async function selectFromCases(selection, ctx, executionInput, runx) {
|
|
|
16
16
|
const ev = await evaluateConditionWhen(c.when, ctx, executionInput, { runx });
|
|
17
17
|
if (ev.error)
|
|
18
18
|
continue;
|
|
19
|
-
if (ev.ok &&
|
|
19
|
+
if (ev.ok && isPartialGraphAiModelConfig(c.modelConfig)) {
|
|
20
20
|
return c.modelConfig;
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
-
if (defaultCase != null &&
|
|
23
|
+
if (defaultCase != null && isPartialGraphAiModelConfig(defaultCase.modelConfig)) {
|
|
24
24
|
return defaultCase.modelConfig;
|
|
25
25
|
}
|
|
26
26
|
return undefined;
|
|
27
27
|
}
|
|
28
28
|
async function resolveTier(value, ctx, executionInput, runx) {
|
|
29
|
-
if (
|
|
29
|
+
if (isPartialGraphAiModelConfig(value))
|
|
30
30
|
return value;
|
|
31
31
|
if (isModelConfigSelection(value)) {
|
|
32
32
|
return selectFromCases(value, ctx, executionInput, runx);
|
|
@@ -35,17 +35,18 @@ async function resolveTier(value, ctx, executionInput, runx) {
|
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
37
|
* Resolves effective model config for a task node from the graph document only.
|
|
38
|
-
*
|
|
38
|
+
* Merges `node.taskConfiguration.modelConfig` (partial override) over `graph.modelConfig` (job default),
|
|
39
|
+
* then engine defaults for any remaining slots.
|
|
39
40
|
*/
|
|
40
41
|
export async function resolveModelConfigForNode(args) {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
const graphPartial = await resolveTier(args.graphModelConfig, args.conditionCtx, args.executionInput, args.runx);
|
|
43
|
+
const nodePartial = await resolveTier(args.nodeModelConfig, args.conditionCtx, args.executionInput, args.runx);
|
|
44
|
+
const merged = mergeGraphAiModelConfig(DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, graphPartial, nodePartial);
|
|
45
|
+
return resolveGraphAiModelConfig(merged, {
|
|
46
|
+
graphId: args.graphId,
|
|
47
|
+
nodeId: args.nodeId,
|
|
48
|
+
jobId: args.jobId,
|
|
49
|
+
});
|
|
49
50
|
}
|
|
50
51
|
export { conditionWhenSignature, isEmptyConditionWhen };
|
|
51
|
-
export { resolveGraphAiModelConfig, toRunTaskModelConfig, DEFAULT_GRAPH_AI_MODEL_IDS, DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, } from './graphAiModelConfig.js';
|
|
52
|
+
export { resolveGraphAiModelConfig, toRunTaskModelConfig, mergeGraphAiModelConfig, DEFAULT_GRAPH_AI_MODEL_IDS, DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, } from './graphAiModelConfig.js';
|
|
@@ -5,12 +5,17 @@ export declare const CANONICAL_GRAPH_TOP_LEVEL_KEYS: readonly ["id", "version",
|
|
|
5
5
|
* Returns top-level keys on `graph` that are not part of the canonical executable document contract.
|
|
6
6
|
*/
|
|
7
7
|
export declare function getCanonicalGraphDocumentViolations(graph: unknown): string[];
|
|
8
|
+
export type CanonicalGraphDocumentValidationMode = 'persist' | 'execute';
|
|
9
|
+
export type AssertCanonicalGraphDocumentOptions = {
|
|
10
|
+
/** `execute` accepts partial task overrides and profile/choice slot encoding; `persist` stays strict. */
|
|
11
|
+
mode?: CanonicalGraphDocumentValidationMode;
|
|
12
|
+
};
|
|
8
13
|
/** Single-node validation. Throws `NON_CANONICAL_TASK_NODE` on the first canonical violation. */
|
|
9
14
|
export declare function assertCanonicalTaskNode(node: unknown, context?: {
|
|
10
15
|
jobId?: string;
|
|
11
16
|
graphId?: string;
|
|
12
17
|
nodeIndex?: number;
|
|
13
|
-
}): void;
|
|
18
|
+
}, options?: AssertCanonicalGraphDocumentOptions): void;
|
|
14
19
|
/**
|
|
15
20
|
* Throws {@link ExellixGraphError} when the value is not a canonical graph document
|
|
16
21
|
* (forbidden top-level keys, record-keyed `nodes`, root `outputConstraints`, or any
|
|
@@ -22,4 +27,4 @@ export declare function assertCanonicalTaskNode(node: unknown, context?: {
|
|
|
22
27
|
export declare function assertCanonicalGraphDocument(graph: Graph, context?: {
|
|
23
28
|
jobId?: string;
|
|
24
29
|
graphId?: string;
|
|
25
|
-
}): void;
|
|
30
|
+
}, options?: AssertCanonicalGraphDocumentOptions): void;
|
|
@@ -3,7 +3,7 @@ import { ExellixGraphError } from '../errors/ExellixGraphError.js';
|
|
|
3
3
|
import { ExellixGraphErrorCode } from '../errors/exellixGraphErrorCodes.js';
|
|
4
4
|
import { EXELLIX_VIRTUAL_BOUNDARY_NODE_METADATA_KEY } from '../inspection/types.js';
|
|
5
5
|
import { getStructuredDataFilterPathViolations } from './dataFiltersEvaluation.js';
|
|
6
|
-
import { conditionWhenSignature, countDefaultModelConfigCases, isEmptyConditionWhen, isGraphAiModelConfig, isModelConfigSelection, } from './modelConfigSelection.js';
|
|
6
|
+
import { conditionWhenSignature, countDefaultModelConfigCases, isEmptyConditionWhen, isGraphAiModelConfig, isModelConfigSelection, isPartialGraphAiModelConfig, } from './modelConfigSelection.js';
|
|
7
7
|
import { assertGraphAiProfileNameString } from './graphAiModelConfig.js';
|
|
8
8
|
import { assertCanonicalGraphDocumentMetadata } from './graphModelStudioSeparation.js';
|
|
9
9
|
/** Top-level keys permitted on a canonical exellix-graph executable graph JSON document. */
|
|
@@ -273,13 +273,20 @@ function assertOptionalTaskNodeConditions(value, nodeId, context) {
|
|
|
273
273
|
assertTaskNodeJsCondition(value.jsConditionFunction, `Task node "${nodeId}": conditions.jsConditionFunction`, ctx);
|
|
274
274
|
assertTaskNodeAiCondition(value.aiCondition, `Task node "${nodeId}": conditions.aiCondition`, ctx);
|
|
275
275
|
}
|
|
276
|
-
function
|
|
276
|
+
function assertModelConfigSlotIfPresent(slot, slotPath, context) {
|
|
277
|
+
if (slot === undefined)
|
|
278
|
+
return;
|
|
279
|
+
assertGraphAiProfileNameString(slot, slotPath, context);
|
|
280
|
+
}
|
|
281
|
+
function assertModelConfigSelection(value, path, context, options) {
|
|
277
282
|
if (value === undefined)
|
|
278
283
|
return;
|
|
284
|
+
const mode = options?.mode ?? 'persist';
|
|
285
|
+
const allowPartial = mode === 'execute';
|
|
279
286
|
const code = context?.nodeId
|
|
280
287
|
? ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE
|
|
281
288
|
: ExellixGraphErrorCode.NON_CANONICAL_GRAPH_DOCUMENT;
|
|
282
|
-
if (isGraphAiModelConfig(value)) {
|
|
289
|
+
if (isGraphAiModelConfig(value) || (allowPartial && isPartialGraphAiModelConfig(value))) {
|
|
283
290
|
throw new ExellixGraphError(code, `${path}: flat modelConfig was removed. Use { cases: [{ modelConfig: { preActionModel, skillModel, postActionModel } }] } (one case with no \`when\` for a single default).`, context);
|
|
284
291
|
}
|
|
285
292
|
if (!isModelConfigSelection(value)) {
|
|
@@ -297,12 +304,24 @@ function assertModelConfigSelection(value, path, context) {
|
|
|
297
304
|
if (!isPlainObject(c)) {
|
|
298
305
|
throw new ExellixGraphError(code, `${casePath} must be a plain object.`, context);
|
|
299
306
|
}
|
|
300
|
-
|
|
301
|
-
|
|
307
|
+
const mc = c.modelConfig;
|
|
308
|
+
const mcValid = allowPartial ? isPartialGraphAiModelConfig(mc) : isGraphAiModelConfig(mc);
|
|
309
|
+
if (!mcValid) {
|
|
310
|
+
throw new ExellixGraphError(code, allowPartial
|
|
311
|
+
? `${casePath}.modelConfig must include at least one of { preActionModel, skillModel, postActionModel } with non-empty alias or profile/choice values.`
|
|
312
|
+
: `${casePath}.modelConfig must be { preActionModel: string, skillModel: string, postActionModel: string } with non-empty AI profile names.`, context);
|
|
313
|
+
}
|
|
314
|
+
const partial = mc;
|
|
315
|
+
if (!allowPartial) {
|
|
316
|
+
assertGraphAiProfileNameString(partial.preActionModel, `${casePath}.modelConfig.preActionModel`, context);
|
|
317
|
+
assertGraphAiProfileNameString(partial.skillModel, `${casePath}.modelConfig.skillModel`, context);
|
|
318
|
+
assertGraphAiProfileNameString(partial.postActionModel, `${casePath}.modelConfig.postActionModel`, context);
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
assertModelConfigSlotIfPresent(partial.preActionModel, `${casePath}.modelConfig.preActionModel`, context);
|
|
322
|
+
assertModelConfigSlotIfPresent(partial.skillModel, `${casePath}.modelConfig.skillModel`, context);
|
|
323
|
+
assertModelConfigSlotIfPresent(partial.postActionModel, `${casePath}.modelConfig.postActionModel`, context);
|
|
302
324
|
}
|
|
303
|
-
assertGraphAiProfileNameString(c.modelConfig.preActionModel, `${casePath}.modelConfig.preActionModel`, context);
|
|
304
|
-
assertGraphAiProfileNameString(c.modelConfig.skillModel, `${casePath}.modelConfig.skillModel`, context);
|
|
305
|
-
assertGraphAiProfileNameString(c.modelConfig.postActionModel, `${casePath}.modelConfig.postActionModel`, context);
|
|
306
325
|
if (isEmptyConditionWhen(c.when)) {
|
|
307
326
|
continue;
|
|
308
327
|
}
|
|
@@ -424,7 +443,7 @@ function isTaskShape(node) {
|
|
|
424
443
|
return true;
|
|
425
444
|
}
|
|
426
445
|
/** Single-node validation. Throws `NON_CANONICAL_TASK_NODE` on the first canonical violation. */
|
|
427
|
-
export function assertCanonicalTaskNode(node, context) {
|
|
446
|
+
export function assertCanonicalTaskNode(node, context, options) {
|
|
428
447
|
if (!isPlainObject(node)) {
|
|
429
448
|
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `Graph node at index ${context?.nodeIndex ?? '?'} is not a plain object.`, { jobId: context?.jobId, graphId: context?.graphId });
|
|
430
449
|
}
|
|
@@ -479,7 +498,7 @@ export function assertCanonicalTaskNode(node, context) {
|
|
|
479
498
|
jobId: context?.jobId,
|
|
480
499
|
graphId: context?.graphId,
|
|
481
500
|
nodeId: String(node.id),
|
|
482
|
-
});
|
|
501
|
+
}, options);
|
|
483
502
|
assertContextualKnowledgeScope(taskNode.scope, String(node.id), {
|
|
484
503
|
jobId: context?.jobId,
|
|
485
504
|
graphId: context?.graphId,
|
|
@@ -548,7 +567,7 @@ export function assertCanonicalTaskNode(node, context) {
|
|
|
548
567
|
* `variables`, required `response`, and `metadata`); runtime fields belong under the execution
|
|
549
568
|
* request `runtime` object.
|
|
550
569
|
*/
|
|
551
|
-
export function assertCanonicalGraphDocument(graph, context) {
|
|
570
|
+
export function assertCanonicalGraphDocument(graph, context, options) {
|
|
552
571
|
const violations = getCanonicalGraphDocumentViolations(graph);
|
|
553
572
|
if (violations.length > 0) {
|
|
554
573
|
const graphId = context?.graphId ??
|
|
@@ -583,7 +602,7 @@ export function assertCanonicalGraphDocument(graph, context) {
|
|
|
583
602
|
assertModelConfigSelection(graph.modelConfig, 'graph.modelConfig', {
|
|
584
603
|
jobId: context?.jobId,
|
|
585
604
|
graphId: resolvedGraphId,
|
|
586
|
-
});
|
|
605
|
+
}, options);
|
|
587
606
|
const metadata = isPlainObject(graph.metadata)
|
|
588
607
|
? graph.metadata
|
|
589
608
|
: undefined;
|
|
@@ -613,7 +632,7 @@ export function assertCanonicalGraphDocument(graph, context) {
|
|
|
613
632
|
// Per-node canonical checks.
|
|
614
633
|
const nodes = graph.nodes;
|
|
615
634
|
for (let i = 0; i < nodes.length; i++) {
|
|
616
|
-
assertCanonicalTaskNode(nodes[i], { jobId: context?.jobId, graphId: resolvedGraphId, nodeIndex: i });
|
|
635
|
+
assertCanonicalTaskNode(nodes[i], { jobId: context?.jobId, graphId: resolvedGraphId, nodeIndex: i }, options);
|
|
617
636
|
}
|
|
618
637
|
if (!Object.prototype.hasOwnProperty.call(graph, 'response')) {
|
|
619
638
|
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_GRAPH_DOCUMENT, `Graph "${String(resolvedGraphId ?? '?')}" must declare root graph.response. Final response shaping must not live under metadata.graphResponse.responseMapping or finalizer output selection.`, { jobId: context?.jobId, graphId: resolvedGraphId });
|
package/dist/src/types/refs.d.ts
CHANGED
|
@@ -21,6 +21,12 @@ export type GraphAiModelConfig = {
|
|
|
21
21
|
skillModel: string;
|
|
22
22
|
postActionModel: string;
|
|
23
23
|
};
|
|
24
|
+
/** Authoring / execute ingress: one or more phase slots; omitted slots inherit at runtime. */
|
|
25
|
+
export type PartialGraphAiModelConfig = {
|
|
26
|
+
preActionModel?: string;
|
|
27
|
+
skillModel?: string;
|
|
28
|
+
postActionModel?: string;
|
|
29
|
+
};
|
|
24
30
|
/**
|
|
25
31
|
* @deprecated Legacy alias map — no longer required for execute. Hosts may omit `runtime.aliasConfig`.
|
|
26
32
|
*/
|
|
@@ -347,7 +353,7 @@ export type TaskNodeConditions = {
|
|
|
347
353
|
};
|
|
348
354
|
export type ModelConfigCase = {
|
|
349
355
|
when?: TaskNodeConditionWhen;
|
|
350
|
-
modelConfig:
|
|
356
|
+
modelConfig: PartialGraphAiModelConfig;
|
|
351
357
|
};
|
|
352
358
|
/** Canonical authoring shape for graph and taskConfiguration model selection. */
|
|
353
359
|
export type ModelConfigSelection = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exellix/graph-engine",
|
|
3
|
-
"version": "7.7.
|
|
3
|
+
"version": "7.7.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Graph executor SDK",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"@x12i/memorix-retrieval": "1.11.2",
|
|
65
65
|
"@x12i/memorix-writer": "1.0.0",
|
|
66
66
|
"@x12i/rendrix": "4.3.0",
|
|
67
|
-
"@x12i/runx": "1.3.
|
|
67
|
+
"@x12i/runx": "1.3.2"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
70
|
"@types/node": "25.9.1",
|