@exellix/graph-engine 7.7.7 → 7.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -1
- package/README.md +34 -33
- 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 +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 7.7.9
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Documentation:** Align README, [`.docs/exellix-graph-engine-format.md`](.docs/exellix-graph-engine-format.md), format docs, and [`.docs/ai-tasks-model-profile-aliases-7x.md`](.docs/ai-tasks-model-profile-aliases-7x.md) with ai-tasks **8.6.8** / funcx **4.4.4**: three-slot `modelConfig`, graph-only selection (7.7+), partial task overrides (7.7.8+), studio preflight via `@exellix/graph-composer`, funcx-for-runx vs xynthesis-for-execution-strategies.
|
|
8
|
+
|
|
9
|
+
### Dependencies
|
|
10
|
+
|
|
11
|
+
- **`@exellix/ai-tasks`:** ^8.6.8
|
|
12
|
+
- **`@x12i/funcx`:** 4.4.4
|
|
13
|
+
|
|
14
|
+
## 7.7.8
|
|
15
|
+
|
|
16
|
+
### Added (CR-11 execute-time model routing)
|
|
17
|
+
|
|
18
|
+
- **`mergeGraphAiModelConfig`**, **`PartialGraphAiModelConfig`**, **`isPartialGraphAiModelConfig`** — job-default + per-task partial override merge at execute time (`task[slot] ?? graph[slot] ?? engine default`).
|
|
19
|
+
- **`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.
|
|
20
|
+
- **`executeGraph`** uses execute-mode validation so hosts can POST authored graph JSON without pre-merging model slots or stripping `/`.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **`looksLikeConcreteModelId`** — only provider-prefixed ids are concrete; `profile/choice` strings are valid graph aliases.
|
|
25
|
+
- **`inspectGraphModelSelection`** — reports merged effective routing when task overrides are partial.
|
|
26
|
+
|
|
27
|
+
### Migration (hosts / graphs-studio)
|
|
28
|
+
|
|
29
|
+
- Remove execute-time `skipCanonicalAssert`, host-side job→node model merge, and slot `/` stripping once on **7.7.8+** (see CR-11 / CR-9).
|
|
30
|
+
|
|
3
31
|
## 7.7.3
|
|
4
32
|
|
|
5
33
|
### Changed
|
|
@@ -195,7 +223,7 @@
|
|
|
195
223
|
### Changed
|
|
196
224
|
|
|
197
225
|
- **Dual memory roots `input` and `inputs`:** Both are allowlisted for `smartInput.paths` and `inputSynthesis.sources`; paths are no longer rewritten to `input.*` at validation or runtime. Optional migration via `mapInputsMemoryPathToInputRoot` / `mapSmartInputPathsInputsToInput`.
|
|
198
|
-
- **`runTask` wire:** Forwards `inputs` (caller bag) alongside coalesced `input` when present.
|
|
226
|
+
- **`runTask` wire:** Forwards `inputs` (caller bag) alongside coalesced `input` when present.
|
|
199
227
|
|
|
200
228
|
## 5.4.0
|
|
201
229
|
|
package/README.md
CHANGED
|
@@ -27,22 +27,21 @@ A minimal, focused SDK for executing graphs in the exellix ecosystem.
|
|
|
27
27
|
| Topic | Document |
|
|
28
28
|
|--------|----------|
|
|
29
29
|
| **Object contracts** (model × runtime + per-call resolution view) | Index: [`formats-documentations/README.md`](formats-documentations/README.md) — [`graph-model`](formats-documentations/graph-model-object-format.md), [`graph-runtime`](formats-documentations/graph-runtime-object-format.md), [`task-node-model`](formats-documentations/task-node-model-object-format.md), [`task-node-runtime`](formats-documentations/task-node-runtime-object-format.md), [`ExellixRuntimeObject`](formats-documentations/exellix-runtime-object-format.md) |
|
|
30
|
-
| **Ecosystem acceptance** (ai-tasks, studio, matrix parity) | [`.docs/ecosystem-acceptance-criteria.md`](.docs/ecosystem-acceptance-criteria.md) |
|
|
31
30
|
| **Executable graph JSON** (top-level shape, task/finalizer nodes, edges, variables, `metadata.graphExecution`, canonical root enforcement, execution memory, local skills, Narrix / web scope (**forwarded `narrix` → `runTask`**), **graph JSON vs outbound `runTask`**) | **[`.docs/exellix-graph-engine-format.md`](.docs/exellix-graph-engine-format.md)** — start here for authors and schema tooling |
|
|
32
31
|
| Task node bridge (shell, `metadata`, `executionPipeline`, `aiTaskProfile` → Narrix web merge, composer alignment) | [`.docs/task-node-exellix-graph-engine-and-graph-composer.md`](.docs/task-node-exellix-graph-engine-and-graph-composer.md) |
|
|
33
32
|
| Layer 01 / 08 graph entry & response contracts | [`.docs/graph-io-visibility.md`](.docs/graph-io-visibility.md) |
|
|
34
33
|
| Graph entry `dataFilters` v1 / public evaluator | [`.docs/data-filters-evaluation.md`](.docs/data-filters-evaluation.md) |
|
|
35
34
|
| Task-node `conditions` + conditional `modelConfig.cases` (runx) | [`.docs/task-node-conditions-evaluation.md`](.docs/task-node-conditions-evaluation.md) |
|
|
36
|
-
| **Model profile aliases** (7.x
|
|
35
|
+
| **Model profile aliases** (7.x+: graph = aliases / `profile/choice`; ai-tasks resolves per phase) | [`BREAKING-CHANGES.md`](BREAKING-CHANGES.md) §7.6 / §7.7, [`.docs/ai-tasks-model-profile-aliases-7x.md`](.docs/ai-tasks-model-profile-aliases-7x.md) |
|
|
37
36
|
| Platform vs implementation (no domain operators in schema) | [`.docs/platform-generic-vs-implementation.md`](.docs/platform-generic-vs-implementation.md) |
|
|
38
37
|
| Activix records, `runContext`, collection tracking (`@x12i/activix` 8.4+) | [`.docs/activix-records.md`](.docs/activix-records.md) |
|
|
39
38
|
| Bundled graph examples & bundle README | [`graphs/README.md`](graphs/README.md) |
|
|
40
39
|
|
|
41
|
-
The **`runTask`** wire contract (identity, canonical **`input`**, optional extra ai-tasks PRE/POST utility calls vs `executionPipeline`) is summarized later under **Run identity** and **`runTask` request contract**, and expanded in the graph format doc’s [**Graph JSON vs outbound `runTask`**](.docs/exellix-graph-engine-format.md#graph-json-vs-outbound-runtask) section. Graph-engine integrates with **`@exellix/ai-tasks`** at the semver range declared in **`package.json`** (currently **`^
|
|
40
|
+
The **`runTask`** wire contract (identity, canonical **`input`**, optional extra ai-tasks PRE/POST utility calls vs `executionPipeline`) is summarized later under **Run identity** and **`runTask` request contract**, and expanded in the graph format doc’s [**Graph JSON vs outbound `runTask`**](.docs/exellix-graph-engine-format.md#graph-json-vs-outbound-runtask) section. Graph-engine integrates with **`@exellix/ai-tasks`** at the semver range declared in **`package.json`** (currently **`^8.6.8`**); use your **lockfile** as the tested line. Variable buckets align with ai-tasks **≥ 7.6.2** (two-scope passthrough). There is **no** minimum graph-engine ↔ ai-tasks matrix published inside ai-tasks — follow dependency semver and upstream **`CHANGELOG.md`**. Graph-engine does **not** import **`@exellix/xynthesis`** directly; **`@x12i/funcx`** is used only for optional **runx** (conditional edges / `modelConfig.cases`), not for ai-tasks execution strategies (those run via xynthesis inside ai-tasks **8.5+**).
|
|
42
41
|
|
|
43
42
|
### Canonical executable graph document (strict boundary)
|
|
44
43
|
|
|
45
|
-
An executable graph model object may have **only** these **top-level** keys: `id`, `version`, `modelConfig`, `jobKnowledge`, `nodes`, `edges`, `variables`, `response`, `metadata`. The required root **`response`** is the single executable final response contract used to build `ExecuteGraphResult.finalOutput`. Document-model and authoring fields (`name`, `description`, `exellixContractTarget`, `graphExecution`, `graphEntry`, `catalogRequests`, and similar) belong **under `metadata`** only. Node-scoped `taskKnowledge` belongs under each task node, not at the model root. Runtime state (`input`, `jobMemory`, `taskMemory`, `executionMemory`, `outputsMemory`,
|
|
44
|
+
An executable graph model object may have **only** these **top-level** keys: `id`, `version`, `modelConfig`, `jobKnowledge`, `nodes`, `edges`, `variables`, `response`, `metadata`. The required root **`response`** is the single executable final response contract used to build `ExecuteGraphResult.finalOutput`. Document-model and authoring fields (`name`, `description`, `exellixContractTarget`, `graphExecution`, `graphEntry`, `catalogRequests`, and similar) belong **under `metadata`** only. Node-scoped `taskKnowledge` belongs under each task node, not at the model root. Runtime state (`input`, `jobMemory`, `taskMemory`, `executionMemory`, `outputsMemory`, per-run options) belongs under the execution request’s **`runtime`** object and is rejected on the model. **`runtime.modelConfig`**, **`runtime.aliasConfig`**, and **`runtime.nodes[id].modelConfig`** were removed in **7.7** — model profiles belong on the graph document only.
|
|
46
45
|
|
|
47
46
|
`metadata.graphExecution` can document graph execution defaults and labels, including `mode: 'forward' | 'backward' | 'hybrid'`, optional `goalNodeId`, optional `dimension`, `outputMode: 'mappedAggregation' | 'lastExitNode'`, `coreObjective`, optional `nodesResponses`, and metadata-only `flowOutline: 'linearSequence' | 'convergingParallelFlow'`. Planner mode still comes from `executeGraph({ model, runtime }).runtime.mode`; `outputMode` does not decide the returned `finalOutput`.
|
|
48
47
|
|
|
@@ -54,7 +53,7 @@ Enforcement: `executeGraph`, `createExellixGraphRuntime().executeGraph`, `execut
|
|
|
54
53
|
npm install @exellix/graph-engine
|
|
55
54
|
```
|
|
56
55
|
|
|
57
|
-
**Upstream tasks SDK:** This package depends on **`@exellix/ai-tasks` ^
|
|
56
|
+
**Upstream tasks SDK:** This package depends on **`@exellix/ai-tasks` ^8.6.8** and **`@x12i/funcx` 4.4.4** (see **`package.json`**). Graph-engine emits **`RunTaskRequest`** shapes that match the ai-tasks **8.x** closed schema (mandatory **`executionStrategies`**, three-slot **`modelConfig`**, **`xynthesized`**, optional **`smartInput`**, no legacy root mirrors). Pin compatible versions in your app lockfile. Optional CI improvement: fail or warn when the resolved ai-tasks major/minor drifts outside an allowlist (not enforced in-repo today).
|
|
58
57
|
|
|
59
58
|
### Execution matrix hosts (`@exellix/exellix-runtime`) — documentation only for the engine
|
|
60
59
|
|
|
@@ -83,7 +82,7 @@ For Firebase Admin, `createCataloxFromEnv`, `listAiSkillsCatalogItems`, and rela
|
|
|
83
82
|
|
|
84
83
|
### Task-node preflight (validation & analysis, no `runTask`)
|
|
85
84
|
|
|
86
|
-
Graph-engine exposes the **`@exellix/ai-tasks
|
|
85
|
+
Graph-engine exposes the **`@exellix/ai-tasks`** preflight surface so studios and matrix hosts can validate a node **before** execution without a second dependency on ai-tasks:
|
|
87
86
|
|
|
88
87
|
| Export | Purpose |
|
|
89
88
|
|--------|---------|
|
|
@@ -91,7 +90,7 @@ Graph-engine exposes the **`@exellix/ai-tasks` ≥ 7.6** preflight surface so st
|
|
|
91
90
|
| **`validateTaskNodeRunTaskConfig`** | Static config checks (`agentId`, pipeline, `smartInput`, `llmCall`, …). |
|
|
92
91
|
| **`validateTaskNodeRunTaskInvoke`** | Config + payload path resolution + optional template/smart-input render checks. |
|
|
93
92
|
|
|
94
|
-
Lower-level helpers (`validateRunTaskConfig`, `validateRunTaskInvoke`, `analyzeExpectedRunTaskInput`, Rendrix `listTokens` / `analyzeTemplateResolution`, …) are **re-exported** from the package root when you already have a `RunTaskRequest`. Catalox-backed skill invoke packet analysis was removed from `@exellix/ai-tasks` — use studio / skills-manager preflight (`@
|
|
93
|
+
Lower-level helpers (`validateRunTaskConfig`, `validateRunTaskInvoke`, `analyzeExpectedRunTaskInput`, Rendrix `listTokens` / `analyzeTemplateResolution`, …) are **re-exported** from the package root when you already have a `RunTaskRequest`. Catalox-backed skill invoke packet analysis was removed from `@exellix/ai-tasks` **8.6** — use **`@exellix/graph-composer`** for studio / skills-manager invoke preflight (see `@exellix/ai-tasks` `documenations/studio-skill-invoke-preflight.md`).
|
|
95
94
|
|
|
96
95
|
**Testing safety:** Default `npm test` uses **mocked** Catalox in catalog validation tests and does not open Firestore. Do **not** point `FIRESTORE_LIVE_TESTS` / integration flags at a **production** Firebase project; Catalox’s own docs recommend a dedicated test project for live integration runs.
|
|
97
96
|
|
|
@@ -106,6 +105,22 @@ const runtime = createExellixGraphRuntime({
|
|
|
106
105
|
tasksClient: myTasksClient, // TasksClientLike — responses may use `ok` or `success` (both accepted)
|
|
107
106
|
});
|
|
108
107
|
|
|
108
|
+
// graphModel carries static modelConfig (profile aliases on the graph document — not on runtime).
|
|
109
|
+
const graphModel = {
|
|
110
|
+
id: 'my-graph',
|
|
111
|
+
modelConfig: {
|
|
112
|
+
cases: [{
|
|
113
|
+
modelConfig: {
|
|
114
|
+
preActionModel: 'cheap',
|
|
115
|
+
skillModel: 'balanced',
|
|
116
|
+
postActionModel: 'cheap',
|
|
117
|
+
},
|
|
118
|
+
}],
|
|
119
|
+
},
|
|
120
|
+
nodes: [/* … */],
|
|
121
|
+
// …
|
|
122
|
+
};
|
|
123
|
+
|
|
109
124
|
// Host correlation id (required). Engine sets `job.id` / `job.jobId` from it and generates `result.taskId`.
|
|
110
125
|
const result = await runtime.executeGraph({
|
|
111
126
|
model: graphModel,
|
|
@@ -113,15 +128,7 @@ const result = await runtime.executeGraph({
|
|
|
113
128
|
jobId: 'job-123',
|
|
114
129
|
job: { agentId: 'agent-1', input: {} },
|
|
115
130
|
input: { question: 'Analyze this record' },
|
|
116
|
-
//
|
|
117
|
-
modelConfig: {
|
|
118
|
-
cases: [{ modelConfig: { xynthesisModel: 'weak', skillModel: 'strong' } }],
|
|
119
|
-
},
|
|
120
|
-
aliasConfig: {
|
|
121
|
-
strong: 'anthropic/claude-sonnet-4',
|
|
122
|
-
weak: 'google/gemini-2.5-flash',
|
|
123
|
-
default: 'google/gemini-2.5-flash',
|
|
124
|
-
},
|
|
131
|
+
// no modelConfig or aliasConfig on runtime (removed in 7.7)
|
|
125
132
|
},
|
|
126
133
|
});
|
|
127
134
|
|
|
@@ -144,9 +151,9 @@ const runtime = createExellixGraphRuntime({
|
|
|
144
151
|
graphLoader,
|
|
145
152
|
engineFactory,
|
|
146
153
|
tasksClient,
|
|
147
|
-
modelConfig, // optional explicit fallback: { xynthesisModel, skillModel }
|
|
148
154
|
eventEmitter, // optional graph/node lifecycle events
|
|
149
155
|
playgroundReporter, // optional
|
|
156
|
+
runxCreateOptions, // optional — lazy runx + funcx when graph needs conditional modelConfig / conditions
|
|
150
157
|
// …stepRetryPolicy, runLogMode, concurrency, runTaskDiagnostics, etc.
|
|
151
158
|
});
|
|
152
159
|
|
|
@@ -161,6 +168,9 @@ interface GraphExecutionRequest {
|
|
|
161
168
|
// Task-node model: TaskNode
|
|
162
169
|
// Task-node runtime: TaskNodeRuntimeObject at runtime.nodes[nodeId]
|
|
163
170
|
|
|
171
|
+
// Task-node runtime: TaskNodeRuntimeObject at runtime.nodes[nodeId] — extensible bag;
|
|
172
|
+
// modelConfig / aliasConfig on runtime.nodes were removed in 7.7.
|
|
173
|
+
|
|
164
174
|
interface GraphRuntimeObject {
|
|
165
175
|
jobId: string; // required: host correlation id
|
|
166
176
|
job: any; // host envelope: agentId, input, jobType, …
|
|
@@ -169,21 +179,13 @@ interface GraphRuntimeObject {
|
|
|
169
179
|
taskMemory?: any;
|
|
170
180
|
executionMemory?: any;
|
|
171
181
|
variables?: Record<string, any>;
|
|
172
|
-
/**
|
|
173
|
-
|
|
174
|
-
/** Required (7.x): profile alias → concrete provider model id for this execution. */
|
|
175
|
-
aliasConfig: Record<string, string>;
|
|
176
|
-
nodes?: Record<string, {
|
|
177
|
-
/** Per-node profile override (alias names). */
|
|
178
|
-
modelConfig?: { xynthesisModel: string; skillModel: string };
|
|
179
|
-
/** Per-node alias bindings (overlay `aliasConfig`). */
|
|
180
|
-
aliasConfig?: Record<string, string>;
|
|
181
|
-
}>;
|
|
182
|
+
/** Per-node runtime bag (must not carry modelConfig or aliasConfig since 7.7). */
|
|
183
|
+
nodes?: Record<string, Record<string, unknown>>;
|
|
182
184
|
mode?: 'forward' | 'backward' | 'hybrid';
|
|
183
185
|
goalNodeId?: string; // required when mode === 'backward'
|
|
184
186
|
debugMode?: boolean; // include per-node trace on result.debug
|
|
185
187
|
failFast?: boolean; // default: false
|
|
186
|
-
// …
|
|
188
|
+
// …stepRetryPolicy, runLogMode, runtimeObjects, runTaskDiagnostics, etc.
|
|
187
189
|
}
|
|
188
190
|
```
|
|
189
191
|
|
|
@@ -265,7 +267,7 @@ const markdown = playgroundReporter.getMarkdown();
|
|
|
265
267
|
|
|
266
268
|
**Standalone node debugging:** The runtime also exposes **`runtime.executeNode(...)`** for single-node test runs. Provide `node`, `job`, optional `graph` / `execution`, and (when continuing an existing run) `graphRunTaskId` from the parent `ExecuteGraphResult.taskId` so `runTask` and Activix stay aligned.
|
|
267
269
|
|
|
268
|
-
### `runTask` request contract (`@exellix/ai-tasks`
|
|
270
|
+
### `runTask` request contract (`@exellix/ai-tasks` 8.x)
|
|
269
271
|
|
|
270
272
|
Graph-engine builds a canonical `RunTaskRequest` for every outbound task call it owns: MAIN task-node invokes, engine PRE/POST utility invokes, and `synthesize` finalizer invokes. MAIN request assembly lives in [`src/runtime/buildAiTasksRunTaskRequest.ts`](src/runtime/buildAiTasksRunTaskRequest.ts); all paths follow **`RUNTASK_REQUEST.md`** in `@exellix/ai-tasks`. Types align with **`RunTaskRequest`** / **`ExellixGraphRunTaskRequest`** exported from this package.
|
|
271
273
|
|
|
@@ -277,7 +279,7 @@ Graph-engine builds a canonical `RunTaskRequest` for every outbound task call it
|
|
|
277
279
|
|
|
278
280
|
To build `RunTaskRequest` without fallback defaults, the execution request must provide both sides of the contract: `model.id`, `node.id`, `node.skillKey`, explicit `node.taskConfiguration.taskTypeId` (even when it matches `skillKey`), `node.taskConfiguration.executionStrategies` (use `[]` for plain MAIN), `runtime.jobId`, `runtime.job.agentId`, `runtime.job.jobTypeId` or `runtime.job.jobType`, active input/memory, and any model/LLM/diagnostic options needed by the task.
|
|
279
281
|
|
|
280
|
-
**Model profiles (7.x):** Graph and node `modelConfig` carry **profile alias names** only (`
|
|
282
|
+
**Model profiles (7.x+):** Graph and node `modelConfig` carry **profile alias names** only (`cheap`, `balanced`, `deep`, or `profile/choice` keys like `cheap/default`, `cyber/deep_forensics`) — never provider model ids in graph JSON. **Since 7.7**, model selection is **graph-document only**: merge `node.taskConfiguration.modelConfig` (partial override per slot) over `model.modelConfig`, then engine defaults (`cheap` / `balanced` / `cheap`). Graph-engine forwards the three-slot triplet `{ preActionModel, skillModel, postActionModel }` on every outbound `runTask`; **`@exellix/ai-tasks`** resolves aliases via `@x12i/ai-profiles`. **7.7.8+** execute-mode validation accepts partial per-task overrides and `profile/choice` encoding without host-side pre-merge. See [`BREAKING-CHANGES.md`](BREAKING-CHANGES.md) §7.6 / §7.7 / §7.7.8.
|
|
281
283
|
|
|
282
284
|
Graph-engine still derives correlation fields such as `graphId`, `nodeId`, `coreSkillId`, `masterSkillId`, `taskId`, and `masterSkillActivityId` from those authored values.
|
|
283
285
|
|
|
@@ -286,7 +288,7 @@ Graph-engine still derives correlation fields such as `graphId`, `nodeId`, `core
|
|
|
286
288
|
- Every MAIN (and engine PRE/POST utility) **`runTask`** includes **`executionStrategies`**: an array of **`ExecutionStrategyInvocation`** objects (semantics defined by **`@exellix/ai-tasks`** / `RUNTASK_REQUEST.md`).
|
|
287
289
|
- **Plain MAIN** (no wrappers): graph-engine sends **`executionStrategies: []`**.
|
|
288
290
|
- Optional task-node authoring: **`taskConfiguration.executionStrategies`** — when present and non-empty, it overrides the default **`[]`** for that node’s MAIN call.
|
|
289
|
-
- Optional catalog metadata: **`taskConfiguration.executionStrategyCatalogItems`** is forwarded to `ai-tasks`; `ai-tasks` still validates the runtime invocation shape and consumes only safe catalog fields such as wrapper default
|
|
291
|
+
- Optional catalog metadata: **`taskConfiguration.executionStrategyCatalogItems`** is forwarded to `ai-tasks`; planner/optimizer rows use **`runtimeKind: "xynthesis-action"`** in ai-tasks **8.5+** (not funcx). `ai-tasks` still validates the runtime invocation shape and consumes only safe catalog fields such as wrapper default sidekick actions.
|
|
290
292
|
- **Removed in 5.0:** `metadata.executionStrategyKey` typing and code branches. Configure planners/optimizers through **`executionStrategies`** per ai-tasks (see upstream docs / Catalox task-strategy catalogs).
|
|
291
293
|
|
|
292
294
|
#### `xynthesized` and internal `execution.xynthesized`
|
|
@@ -480,7 +482,6 @@ const result = await runtime.executeGraph({
|
|
|
480
482
|
jobId: 'job-123',
|
|
481
483
|
job: { agentId: 'agent-1', jobType: 'analysis' },
|
|
482
484
|
input: entryInput,
|
|
483
|
-
aliasConfig: { /* … */ },
|
|
484
485
|
runtimeObjects: buildExellixGraphRuntimeObjects({ graphActivixClient: activixClient }),
|
|
485
486
|
},
|
|
486
487
|
});
|
|
@@ -919,7 +920,7 @@ Bundled **graph definitions** (JSON DAGs) live in [`graphs/`](graphs/). **[graph
|
|
|
919
920
|
|
|
920
921
|
To execute a graph, supply a `graphLoader` that resolves `graphId` to JSON, build a `job` with the input shape your graph expects (often `execution.input` with `raw` and optional `metadata`), and call `executeGraph`. Further integration notes may live under [.docs/](.docs/).
|
|
921
922
|
|
|
922
|
-
**`taskConfiguration.aiTasksOutputValidation`** on task nodes is forwarded as **`outputValidation`** on the `runTask` request (**`@exellix/ai-tasks`
|
|
923
|
+
**`taskConfiguration.aiTasksOutputValidation`** on task nodes is forwarded as **`outputValidation`** on the `runTask` request (**`@exellix/ai-tasks` 8.x**). Root **`outputConstraints`** is not part of the closed schema.
|
|
923
924
|
|
|
924
925
|
## Runtime observability (`runtimeObjects`)
|
|
925
926
|
|
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.
|
|
3
|
+
"version": "7.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Graph executor SDK",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -52,11 +52,11 @@
|
|
|
52
52
|
"access": "public"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@exellix/ai-tasks": "^8.6.
|
|
55
|
+
"@exellix/ai-tasks": "^8.6.8",
|
|
56
56
|
"@x12i/activix": "8.5.0",
|
|
57
57
|
"@x12i/catalox": "5.1.3",
|
|
58
58
|
"@x12i/env": "4.0.1",
|
|
59
|
-
"@x12i/funcx": "4.4.
|
|
59
|
+
"@x12i/funcx": "4.4.4",
|
|
60
60
|
"@x12i/graphenix": "2.5.0",
|
|
61
61
|
"@x12i/graphenix-format": "2.0.0",
|
|
62
62
|
"@x12i/logxer": "^4.6.0",
|