@exellix/graph-engine 6.0.2 → 7.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +19 -6
- package/dist/src/errors/exellixGraphErrorCodes.d.ts +7 -1
- package/dist/src/errors/exellixGraphErrorCodes.js +6 -0
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.js +2 -1
- package/dist/src/runtime/ExellixGraphRuntime.js +5 -13
- package/dist/src/runtime/modelAliasContract.d.ts +46 -0
- package/dist/src/runtime/modelAliasContract.js +129 -0
- package/dist/src/runtime/resolveModelConfigForNode.d.ts +7 -3
- package/dist/src/runtime/resolveModelConfigForNode.js +8 -20
- package/dist/src/runtime/taskNodeRunTaskPreflight.d.ts +4 -1
- package/dist/src/runtime/taskNodeRunTaskPreflight.js +9 -1
- package/dist/src/runtime/validateCanonicalGraphDocument.js +7 -2
- package/dist/src/types/refs.d.ts +3 -3
- package/dist/testkit/index.d.ts +1 -0
- package/dist/testkit/index.js +1 -0
- package/dist/testkit/testModelAliasRuntime.d.ts +14 -0
- package/dist/testkit/testModelAliasRuntime.js +40 -0
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 7.0.0
|
|
4
|
+
|
|
5
|
+
### Breaking
|
|
6
|
+
|
|
7
|
+
- **Model profile aliases only on graph JSON:** `modelConfig` / `taskConfiguration.modelConfig` values must be profile alias names (`strong`, `weak`, `default`, …), not provider model ids. `assertCanonicalGraphDocument` throws `NON_CANONICAL_MODEL_CONFIG` for provider-like strings.
|
|
8
|
+
- **Required `runtime.aliasConfig`:** Every `executeGraph` must supply a non-empty alias → concrete model map covering all aliases used by the graph (including `default`). Missing map or keys throws `MODEL_ALIAS_CONFIG_REQUIRED` / `MODEL_ALIAS_UNRESOLVED`.
|
|
9
|
+
- **No alias pass-through:** Resolved concrete models are sent on `RunTaskRequest.modelConfig` only after strict alias resolution.
|
|
10
|
+
- **Exports:** `collectModelAliasesUsedInGraph`, `assertRuntimeAliasConfigCoversGraph`, `DEFAULT_MODEL_ALIAS`, testkit `createTestExellixGraphRuntime` / `buildTestAliasConfig`.
|
|
11
|
+
|
|
3
12
|
## 5.16.0
|
|
4
13
|
|
|
5
14
|
### Breaking
|
package/README.md
CHANGED
|
@@ -33,6 +33,7 @@ A minimal, focused SDK for executing graphs in the exellix ecosystem.
|
|
|
33
33
|
| Layer 01 / 08 graph entry & response contracts | [`.docs/graph-io-visibility.md`](.docs/graph-io-visibility.md) |
|
|
34
34
|
| Graph entry `dataFilters` v1 / public evaluator | [`.docs/data-filters-evaluation.md`](.docs/data-filters-evaluation.md) |
|
|
35
35
|
| 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: graph = profile names, runtime = concrete models) | [`BREAKING-CHANGES.md`](BREAKING-CHANGES.md) §7.0.0, [`.docs/ai-tasks-model-profile-aliases-7x.md`](.docs/ai-tasks-model-profile-aliases-7x.md) (ai-tasks), [`.docs/fr-model-alias-descriptors.md`](.docs/fr-model-alias-descriptors.md) (upstream FRs) |
|
|
36
37
|
| Platform vs implementation (no domain operators in schema) | [`.docs/platform-generic-vs-implementation.md`](.docs/platform-generic-vs-implementation.md) |
|
|
37
38
|
| Bundled graph examples & bundle README | [`graphs/README.md`](graphs/README.md) |
|
|
38
39
|
|
|
@@ -112,10 +113,14 @@ const result = await runtime.executeGraph({
|
|
|
112
113
|
jobId: 'job-123',
|
|
113
114
|
job: { agentId: 'agent-1', input: {} },
|
|
114
115
|
input: { question: 'Analyze this record' },
|
|
115
|
-
|
|
116
|
+
// 7.x: graph modelConfig values are profile aliases; concrete models bind here (required).
|
|
117
|
+
modelConfig: {
|
|
118
|
+
cases: [{ modelConfig: { xynthesisModel: 'weak', skillModel: 'strong' } }],
|
|
119
|
+
},
|
|
116
120
|
aliasConfig: {
|
|
117
|
-
|
|
118
|
-
|
|
121
|
+
strong: 'anthropic/claude-sonnet-4',
|
|
122
|
+
weak: 'google/gemini-2.5-flash',
|
|
123
|
+
default: 'google/gemini-2.5-flash',
|
|
119
124
|
},
|
|
120
125
|
},
|
|
121
126
|
});
|
|
@@ -164,10 +169,14 @@ interface GraphRuntimeObject {
|
|
|
164
169
|
taskMemory?: any;
|
|
165
170
|
executionMemory?: any;
|
|
166
171
|
variables?: Record<string, any>;
|
|
167
|
-
|
|
168
|
-
|
|
172
|
+
/** Run-level model profile override (`cases`); values are alias names, not provider ids. */
|
|
173
|
+
modelConfig?: { cases: Array<{ when?: unknown; modelConfig: { xynthesisModel: string; skillModel: string } }> };
|
|
174
|
+
/** Required (7.x): profile alias → concrete provider model id for this execution. */
|
|
175
|
+
aliasConfig: Record<string, string>;
|
|
169
176
|
nodes?: Record<string, {
|
|
177
|
+
/** Per-node profile override (alias names). */
|
|
170
178
|
modelConfig?: { xynthesisModel: string; skillModel: string };
|
|
179
|
+
/** Per-node alias bindings (overlay `aliasConfig`). */
|
|
171
180
|
aliasConfig?: Record<string, string>;
|
|
172
181
|
}>;
|
|
173
182
|
mode?: 'forward' | 'backward' | 'hybrid';
|
|
@@ -266,7 +275,11 @@ Graph-engine builds a canonical `RunTaskRequest` for every outbound task call it
|
|
|
266
275
|
- **Canonical task payload:** **`input`** object only (merged execution slice + **materialized** `node.inputs`). Root-level **`question`**, **`raw`**, **`jobInput`**, duplicate **`inputs`**, and legacy **`executionType`** are **not** sent on the request object.
|
|
267
276
|
- **Graph telemetry:** `graphId`, **`nodeId`**, **`coreSkillId`** (node id), `masterSkillId`, `masterSkillActivityId`, `jobId`, `taskId`, optional `identity`.
|
|
268
277
|
|
|
269
|
-
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.
|
|
278
|
+
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
|
+
|
|
280
|
+
**Model profiles (7.x):** Graph and node `modelConfig` carry **profile alias names** only (`strong`, `weak`, `default`, …) — never provider model ids in graph JSON. **`runtime.aliasConfig` is required** and maps every alias used by the run (including `default` when fallback applies) to concrete provider models. Selection order: `runtime.nodes[nodeId].modelConfig` → `node.taskConfiguration.modelConfig` → `runtime.modelConfig` → `model.modelConfig` → implicit `{ default, default }`; then strict resolution through `runtime.aliasConfig` plus `runtime.nodes[nodeId].aliasConfig`. The **resolved** `{ xynthesisModel, skillModel }` is forwarded to MAIN, engine PRE/POST utility calls, and `synthesize` finalizer calls. Snapshot `aliasConfig` on execution records for reproducibility. See [`BREAKING-CHANGES.md`](BREAKING-CHANGES.md) §7.0.0.
|
|
281
|
+
|
|
282
|
+
Graph-engine still derives correlation fields such as `graphId`, `nodeId`, `coreSkillId`, `masterSkillId`, `taskId`, and `masterSkillActivityId` from those authored values.
|
|
270
283
|
|
|
271
284
|
#### Mandatory `executionStrategies` (breaking vs pre–v7 authoring)
|
|
272
285
|
|
|
@@ -27,5 +27,11 @@ export declare enum ExellixGraphErrorCode {
|
|
|
27
27
|
*/
|
|
28
28
|
GRAPH_ENTRY_DATA_FILTERS_REJECTED = "GRAPH_ENTRY_DATA_FILTERS_REJECTED",
|
|
29
29
|
/** MAIN readiness guard rejected empty execution input or synthesis context before runTask. */
|
|
30
|
-
MAIN_READINESS_FAILED = "MAIN_READINESS_FAILED"
|
|
30
|
+
MAIN_READINESS_FAILED = "MAIN_READINESS_FAILED",
|
|
31
|
+
/** `executeGraph` called without a non-empty `runtime.aliasConfig`. */
|
|
32
|
+
MODEL_ALIAS_CONFIG_REQUIRED = "MODEL_ALIAS_CONFIG_REQUIRED",
|
|
33
|
+
/** Selected model alias has no concrete model in the merged runtime alias map. */
|
|
34
|
+
MODEL_ALIAS_UNRESOLVED = "MODEL_ALIAS_UNRESOLVED",
|
|
35
|
+
/** Graph JSON `modelConfig` contains a provider model id instead of an alias name. */
|
|
36
|
+
NON_CANONICAL_MODEL_CONFIG = "NON_CANONICAL_MODEL_CONFIG"
|
|
31
37
|
}
|
|
@@ -29,4 +29,10 @@ export var ExellixGraphErrorCode;
|
|
|
29
29
|
ExellixGraphErrorCode["GRAPH_ENTRY_DATA_FILTERS_REJECTED"] = "GRAPH_ENTRY_DATA_FILTERS_REJECTED";
|
|
30
30
|
/** MAIN readiness guard rejected empty execution input or synthesis context before runTask. */
|
|
31
31
|
ExellixGraphErrorCode["MAIN_READINESS_FAILED"] = "MAIN_READINESS_FAILED";
|
|
32
|
+
/** `executeGraph` called without a non-empty `runtime.aliasConfig`. */
|
|
33
|
+
ExellixGraphErrorCode["MODEL_ALIAS_CONFIG_REQUIRED"] = "MODEL_ALIAS_CONFIG_REQUIRED";
|
|
34
|
+
/** Selected model alias has no concrete model in the merged runtime alias map. */
|
|
35
|
+
ExellixGraphErrorCode["MODEL_ALIAS_UNRESOLVED"] = "MODEL_ALIAS_UNRESOLVED";
|
|
36
|
+
/** Graph JSON `modelConfig` contains a provider model id instead of an alias name. */
|
|
37
|
+
ExellixGraphErrorCode["NON_CANONICAL_MODEL_CONFIG"] = "NON_CANONICAL_MODEL_CONFIG";
|
|
32
38
|
})(ExellixGraphErrorCode || (ExellixGraphErrorCode = {}));
|
package/dist/src/index.d.ts
CHANGED
|
@@ -38,7 +38,8 @@ export type { PathSegment } from './runtime/pathExpr.js';
|
|
|
38
38
|
export { evaluateStructuredDataFilters, evaluateDataFilterPredicate, getStructuredDataFilterPathViolations, isStructuredDataFiltersV1, } from './runtime/dataFiltersEvaluation.js';
|
|
39
39
|
export { evaluateTaskNodeConditions, evaluateConditionWhen, applyConditionNegate, } from './runtime/taskNodeConditionsEvaluation.js';
|
|
40
40
|
export type { TaskNodeConditionsEvalDeps, TaskNodeConditionsEvalResult, TaskNodeConditionEvalContext, } from './runtime/taskNodeConditionsEvaluation.js';
|
|
41
|
-
export { resolveModelConfigForNode, resolveGraphAiModelConfigAliases, } from './runtime/resolveModelConfigForNode.js';
|
|
41
|
+
export { resolveModelConfigForNode, resolveGraphAiModelConfigAliases, mergeAliasConfigs, } from './runtime/resolveModelConfigForNode.js';
|
|
42
|
+
export { DEFAULT_MODEL_ALIAS, DEFAULT_GRAPH_AI_MODEL_ALIAS_CONFIG, isModelAliasString, looksLikeConcreteModelId, assertGraphModelAliasString, collectModelAliasesUsedInGraph, assertRuntimeAliasConfigPresent, assertRuntimeAliasConfigCoversGraph, } from './runtime/modelAliasContract.js';
|
|
42
43
|
export { isGraphAiModelConfig, isModelConfigSelection, isEmptyConditionWhen, conditionWhenSignature, countDefaultModelConfigCases, } from './runtime/modelConfigSelection.js';
|
|
43
44
|
export { GRAPH_ENGINE_MEMORY_PATH_ROOTS, splitGraphEngineMemoryPath, isAllowedGraphEngineMemoryPath, graphEngineMemoryPathValidationMessage, } from './runtime/graphEngineMemoryPaths.js';
|
|
44
45
|
export { buildRunTaskMainInput, extractCallerInputsBag, buildGraphEngineMemoryResolutionRoot, buildGraphEngineMemoryResolutionRootFromWorkingMemory, resolveGraphEngineMemoryPathValue, } from './runtime/resolveGraphEngineMemoryPaths.js';
|
package/dist/src/index.js
CHANGED
|
@@ -28,7 +28,8 @@ export { mergeMemory } from './runtime/memory.js';
|
|
|
28
28
|
export { selectByPath, writeByPath, parsePath } from './runtime/pathExpr.js';
|
|
29
29
|
export { evaluateStructuredDataFilters, evaluateDataFilterPredicate, getStructuredDataFilterPathViolations, isStructuredDataFiltersV1, } from './runtime/dataFiltersEvaluation.js';
|
|
30
30
|
export { evaluateTaskNodeConditions, evaluateConditionWhen, applyConditionNegate, } from './runtime/taskNodeConditionsEvaluation.js';
|
|
31
|
-
export { resolveModelConfigForNode, resolveGraphAiModelConfigAliases, } from './runtime/resolveModelConfigForNode.js';
|
|
31
|
+
export { resolveModelConfigForNode, resolveGraphAiModelConfigAliases, mergeAliasConfigs, } from './runtime/resolveModelConfigForNode.js';
|
|
32
|
+
export { DEFAULT_MODEL_ALIAS, DEFAULT_GRAPH_AI_MODEL_ALIAS_CONFIG, isModelAliasString, looksLikeConcreteModelId, assertGraphModelAliasString, collectModelAliasesUsedInGraph, assertRuntimeAliasConfigPresent, assertRuntimeAliasConfigCoversGraph, } from './runtime/modelAliasContract.js';
|
|
32
33
|
export { isGraphAiModelConfig, isModelConfigSelection, isEmptyConditionWhen, conditionWhenSignature, countDefaultModelConfigCases, } from './runtime/modelConfigSelection.js';
|
|
33
34
|
export { GRAPH_ENGINE_MEMORY_PATH_ROOTS, splitGraphEngineMemoryPath, isAllowedGraphEngineMemoryPath, graphEngineMemoryPathValidationMessage, } from './runtime/graphEngineMemoryPaths.js';
|
|
34
35
|
export { buildRunTaskMainInput, extractCallerInputsBag, buildGraphEngineMemoryResolutionRoot, buildGraphEngineMemoryResolutionRootFromWorkingMemory, resolveGraphEngineMemoryPathValue, } from './runtime/resolveGraphEngineMemoryPaths.js';
|
|
@@ -34,6 +34,7 @@ import { evaluateGraphPredicate } from "./predicates.js";
|
|
|
34
34
|
import { evaluateStructuredDataFilters } from "./dataFiltersEvaluation.js";
|
|
35
35
|
import { evaluateTaskNodeConditions } from "./taskNodeConditionsEvaluation.js";
|
|
36
36
|
import { resolveModelConfigForNode } from "./resolveModelConfigForNode.js";
|
|
37
|
+
import { assertRuntimeAliasConfigCoversGraph } from "./modelAliasContract.js";
|
|
37
38
|
import { createRunx } from "@x12i/runx";
|
|
38
39
|
import { selectByPath } from "./pathExpr.js";
|
|
39
40
|
import { createGraphStartEvent, createGraphCompleteEvent, createGraphFailEvent, createNodeStartEvent, createNodeCompleteEvent, createNodeFailEvent, } from "./events.js";
|
|
@@ -186,19 +187,6 @@ function readRuntimeNodeConfig(nodes, nodeId) {
|
|
|
186
187
|
const candidate = nodes[nodeId];
|
|
187
188
|
return isPlainRecord(candidate) ? candidate : undefined;
|
|
188
189
|
}
|
|
189
|
-
function mergeAliasConfigs(rootAliases, nodeAliases) {
|
|
190
|
-
const aliases = {};
|
|
191
|
-
for (const source of [rootAliases, nodeAliases]) {
|
|
192
|
-
if (!isPlainRecord(source))
|
|
193
|
-
continue;
|
|
194
|
-
for (const [alias, model] of Object.entries(source)) {
|
|
195
|
-
if (alias.trim() !== "" && typeof model === "string" && model.trim() !== "") {
|
|
196
|
-
aliases[alias] = model;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
return Object.keys(aliases).length > 0 ? aliases : undefined;
|
|
201
|
-
}
|
|
202
190
|
function taskNodeNeedsRunxClient(conditions) {
|
|
203
191
|
return conditions?.jsConditionFunction != null || conditions?.aiCondition != null;
|
|
204
192
|
}
|
|
@@ -1164,6 +1152,7 @@ export function createExellixGraphRuntime(opts) {
|
|
|
1164
1152
|
}
|
|
1165
1153
|
const resolvedGraphId = String(resolvedGraphIdRaw);
|
|
1166
1154
|
assertCanonicalGraphDocument(graph, { jobId, graphId: resolvedGraphId });
|
|
1155
|
+
assertRuntimeAliasConfigCoversGraph(graph, merged.aliasConfig, { modelConfig: merged.modelConfig, nodes: merged.nodes }, { jobId, graphId: resolvedGraphId });
|
|
1167
1156
|
let runxClient = opts.runx;
|
|
1168
1157
|
if (graphNeedsRunxClient(graph, merged.modelConfig)) {
|
|
1169
1158
|
if (!runxClient) {
|
|
@@ -1590,6 +1579,9 @@ export function createExellixGraphRuntime(opts) {
|
|
|
1590
1579
|
}),
|
|
1591
1580
|
executionInput: dataFiltersRecord,
|
|
1592
1581
|
runx: runxClient,
|
|
1582
|
+
graphId: resolvedGraphId,
|
|
1583
|
+
nodeId: typeof node.id === "string" ? node.id : String(node.id),
|
|
1584
|
+
jobId,
|
|
1593
1585
|
});
|
|
1594
1586
|
const r = await executeNode({
|
|
1595
1587
|
graphId: resolvedGraphId,
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Graph, GraphAiModelConfig, GraphModelAliasConfig, ModelConfigSelection } from '../types/refs.js';
|
|
2
|
+
/** Fallback alias pair when no modelConfig tier resolves. */
|
|
3
|
+
export declare const DEFAULT_MODEL_ALIAS = "default";
|
|
4
|
+
export declare const DEFAULT_GRAPH_AI_MODEL_ALIAS_CONFIG: GraphAiModelConfig;
|
|
5
|
+
/**
|
|
6
|
+
* True when a string is a valid graph-authored model alias (not a provider model id).
|
|
7
|
+
*/
|
|
8
|
+
export declare function isModelAliasString(value: unknown): value is string;
|
|
9
|
+
/** True when a value looks like a concrete provider model id (forbidden in graph JSON). */
|
|
10
|
+
export declare function looksLikeConcreteModelId(value: string): boolean;
|
|
11
|
+
export declare function assertGraphModelAliasString(value: unknown, path: string, context?: {
|
|
12
|
+
jobId?: string;
|
|
13
|
+
graphId?: string;
|
|
14
|
+
nodeId?: string;
|
|
15
|
+
}): asserts value is string;
|
|
16
|
+
export declare function mergeAliasConfigs(root?: GraphModelAliasConfig, node?: GraphModelAliasConfig): GraphModelAliasConfig;
|
|
17
|
+
/**
|
|
18
|
+
* Collects every alias name referenced by graph + optional runtime overrides (before resolution).
|
|
19
|
+
* Always includes `default` because resolution may fall back to it.
|
|
20
|
+
*/
|
|
21
|
+
export declare function collectModelAliasesUsedInGraph(graph: Pick<Graph, 'modelConfig' | 'nodes'>, runtime?: {
|
|
22
|
+
modelConfig?: ModelConfigSelection;
|
|
23
|
+
nodes?: Record<string, {
|
|
24
|
+
modelConfig?: GraphAiModelConfig;
|
|
25
|
+
aliasConfig?: GraphModelAliasConfig;
|
|
26
|
+
}>;
|
|
27
|
+
}): Set<string>;
|
|
28
|
+
export declare function assertRuntimeAliasConfigPresent(aliasConfig: GraphModelAliasConfig | undefined, context?: {
|
|
29
|
+
jobId?: string;
|
|
30
|
+
graphId?: string;
|
|
31
|
+
}): asserts aliasConfig is GraphModelAliasConfig;
|
|
32
|
+
/**
|
|
33
|
+
* Ensures merged runtime alias map covers every alias used by the graph (+ runtime overrides).
|
|
34
|
+
*/
|
|
35
|
+
export declare function assertRuntimeAliasConfigCoversGraph(graph: Pick<Graph, 'id' | 'modelConfig' | 'nodes'>, aliasConfig: GraphModelAliasConfig | undefined, runtime?: Parameters<typeof collectModelAliasesUsedInGraph>[1], context?: {
|
|
36
|
+
jobId?: string;
|
|
37
|
+
graphId?: string;
|
|
38
|
+
}): void;
|
|
39
|
+
/**
|
|
40
|
+
* Resolves alias-only {@link GraphAiModelConfig} to concrete provider model ids via runtime map.
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolveGraphAiModelConfigAliases(modelConfig: GraphAiModelConfig | undefined, aliasConfig: GraphModelAliasConfig | undefined, context?: {
|
|
43
|
+
jobId?: string;
|
|
44
|
+
graphId?: string;
|
|
45
|
+
nodeId?: string;
|
|
46
|
+
}): GraphAiModelConfig | undefined;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { ExellixGraphError } from '../errors/ExellixGraphError.js';
|
|
2
|
+
import { ExellixGraphErrorCode } from '../errors/exellixGraphErrorCodes.js';
|
|
3
|
+
import { isGraphAiModelConfig, isModelConfigSelection } from './modelConfigSelection.js';
|
|
4
|
+
/** Fallback alias pair when no modelConfig tier resolves. */
|
|
5
|
+
export const DEFAULT_MODEL_ALIAS = 'default';
|
|
6
|
+
export const DEFAULT_GRAPH_AI_MODEL_ALIAS_CONFIG = {
|
|
7
|
+
xynthesisModel: DEFAULT_MODEL_ALIAS,
|
|
8
|
+
skillModel: DEFAULT_MODEL_ALIAS,
|
|
9
|
+
};
|
|
10
|
+
const CONCRETE_MODEL_PREFIXES = ['openrouter/', 'anthropic/', 'openai/', 'google/', 'x-ai/', 'meta-llama/'];
|
|
11
|
+
/**
|
|
12
|
+
* True when a string is a valid graph-authored model alias (not a provider model id).
|
|
13
|
+
*/
|
|
14
|
+
export function isModelAliasString(value) {
|
|
15
|
+
if (typeof value !== 'string')
|
|
16
|
+
return false;
|
|
17
|
+
const trimmed = value.trim();
|
|
18
|
+
if (trimmed === '')
|
|
19
|
+
return false;
|
|
20
|
+
return !looksLikeConcreteModelId(trimmed);
|
|
21
|
+
}
|
|
22
|
+
/** True when a value looks like a concrete provider model id (forbidden in graph JSON). */
|
|
23
|
+
export function looksLikeConcreteModelId(value) {
|
|
24
|
+
const trimmed = value.trim();
|
|
25
|
+
if (trimmed.includes('/'))
|
|
26
|
+
return true;
|
|
27
|
+
const lower = trimmed.toLowerCase();
|
|
28
|
+
return CONCRETE_MODEL_PREFIXES.some((p) => lower.startsWith(p));
|
|
29
|
+
}
|
|
30
|
+
export function assertGraphModelAliasString(value, path, context) {
|
|
31
|
+
if (!isModelAliasString(value)) {
|
|
32
|
+
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_MODEL_CONFIG, `${path} must be a model alias name (non-empty string without "/" or provider prefixes); got ${JSON.stringify(value)}`, context);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function mergeAliasConfigs(root, node) {
|
|
36
|
+
const aliases = {};
|
|
37
|
+
for (const source of [root, node]) {
|
|
38
|
+
if (source == null || typeof source !== 'object' || Array.isArray(source))
|
|
39
|
+
continue;
|
|
40
|
+
for (const [alias, model] of Object.entries(source)) {
|
|
41
|
+
if (alias.trim() !== '' && typeof model === 'string' && model.trim() !== '') {
|
|
42
|
+
aliases[alias] = model;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return aliases;
|
|
47
|
+
}
|
|
48
|
+
function collectAliasesFromGraphAiModelConfig(config, out) {
|
|
49
|
+
if (!isGraphAiModelConfig(config))
|
|
50
|
+
return;
|
|
51
|
+
out.add(config.xynthesisModel.trim());
|
|
52
|
+
out.add(config.skillModel.trim());
|
|
53
|
+
}
|
|
54
|
+
function collectAliasesFromModelConfigSelection(selection, out) {
|
|
55
|
+
if (!isModelConfigSelection(selection))
|
|
56
|
+
return;
|
|
57
|
+
for (const c of selection.cases) {
|
|
58
|
+
collectAliasesFromGraphAiModelConfig(c.modelConfig, out);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Collects every alias name referenced by graph + optional runtime overrides (before resolution).
|
|
63
|
+
* Always includes `default` because resolution may fall back to it.
|
|
64
|
+
*/
|
|
65
|
+
export function collectModelAliasesUsedInGraph(graph, runtime) {
|
|
66
|
+
const out = new Set([DEFAULT_MODEL_ALIAS]);
|
|
67
|
+
collectAliasesFromModelConfigSelection(graph.modelConfig, out);
|
|
68
|
+
for (const node of graph.nodes ?? []) {
|
|
69
|
+
const tn = node;
|
|
70
|
+
if (tn.type === 'finalizer')
|
|
71
|
+
continue;
|
|
72
|
+
collectAliasesFromModelConfigSelection(tn.taskConfiguration?.modelConfig, out);
|
|
73
|
+
}
|
|
74
|
+
if (runtime != null) {
|
|
75
|
+
collectAliasesFromModelConfigSelection(runtime.modelConfig, out);
|
|
76
|
+
if (runtime.nodes != null && typeof runtime.nodes === 'object') {
|
|
77
|
+
for (const nodeRuntime of Object.values(runtime.nodes)) {
|
|
78
|
+
if (nodeRuntime == null || typeof nodeRuntime !== 'object')
|
|
79
|
+
continue;
|
|
80
|
+
collectAliasesFromGraphAiModelConfig(nodeRuntime.modelConfig, out);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
export function assertRuntimeAliasConfigPresent(aliasConfig, context) {
|
|
87
|
+
if (aliasConfig == null ||
|
|
88
|
+
typeof aliasConfig !== 'object' ||
|
|
89
|
+
Array.isArray(aliasConfig) ||
|
|
90
|
+
Object.keys(aliasConfig).length === 0) {
|
|
91
|
+
throw new ExellixGraphError(ExellixGraphErrorCode.MODEL_ALIAS_CONFIG_REQUIRED, 'runtime.aliasConfig is required and must be a non-empty map of alias name → concrete model id', context);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Ensures merged runtime alias map covers every alias used by the graph (+ runtime overrides).
|
|
96
|
+
*/
|
|
97
|
+
export function assertRuntimeAliasConfigCoversGraph(graph, aliasConfig, runtime, context) {
|
|
98
|
+
assertRuntimeAliasConfigPresent(aliasConfig, context);
|
|
99
|
+
const needed = collectModelAliasesUsedInGraph(graph, runtime);
|
|
100
|
+
const missing = [];
|
|
101
|
+
for (const alias of needed) {
|
|
102
|
+
const concrete = aliasConfig[alias];
|
|
103
|
+
if (typeof concrete !== 'string' || concrete.trim() === '') {
|
|
104
|
+
missing.push(alias);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (missing.length > 0) {
|
|
108
|
+
throw new ExellixGraphError(ExellixGraphErrorCode.MODEL_ALIAS_UNRESOLVED, `runtime.aliasConfig is missing concrete models for alias(es): ${missing.join(', ')}`, { ...context, graphId: context?.graphId ?? graph.id, missingAliases: missing });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function resolveModelAliasStrict(alias, aliasConfig, context) {
|
|
112
|
+
const concrete = aliasConfig[alias];
|
|
113
|
+
if (typeof concrete !== 'string' || concrete.trim() === '') {
|
|
114
|
+
throw new ExellixGraphError(ExellixGraphErrorCode.MODEL_ALIAS_UNRESOLVED, `Unresolved model alias "${alias}" — add it to runtime.aliasConfig (and runtime.nodes[nodeId].aliasConfig when scoped per node)`, { ...context, alias });
|
|
115
|
+
}
|
|
116
|
+
return concrete.trim();
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Resolves alias-only {@link GraphAiModelConfig} to concrete provider model ids via runtime map.
|
|
120
|
+
*/
|
|
121
|
+
export function resolveGraphAiModelConfigAliases(modelConfig, aliasConfig, context) {
|
|
122
|
+
if (!isGraphAiModelConfig(modelConfig))
|
|
123
|
+
return undefined;
|
|
124
|
+
assertRuntimeAliasConfigPresent(aliasConfig, context);
|
|
125
|
+
return {
|
|
126
|
+
xynthesisModel: resolveModelAliasStrict(modelConfig.xynthesisModel, aliasConfig, context),
|
|
127
|
+
skillModel: resolveModelAliasStrict(modelConfig.skillModel, aliasConfig, context),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
@@ -11,10 +11,14 @@ export type ResolveModelConfigForNodeArgs = {
|
|
|
11
11
|
conditionCtx: TaskNodeConditionEvalContext;
|
|
12
12
|
executionInput: Record<string, unknown>;
|
|
13
13
|
runx?: RunxClient;
|
|
14
|
+
graphId?: string;
|
|
15
|
+
nodeId?: string;
|
|
16
|
+
jobId?: string;
|
|
14
17
|
};
|
|
15
|
-
export declare function resolveGraphAiModelConfigAliases(modelConfig: GraphAiModelConfig | undefined, aliasConfig: GraphModelAliasConfig | undefined): GraphAiModelConfig | undefined;
|
|
16
18
|
/**
|
|
17
|
-
* Resolves effective model config for a task node (precedence + conditional cases +
|
|
19
|
+
* Resolves effective model config for a task node (precedence + conditional cases + strict alias resolution).
|
|
20
|
+
* Returns concrete provider model ids for `RunTaskRequest.modelConfig`.
|
|
18
21
|
*/
|
|
19
|
-
export declare function resolveModelConfigForNode(args: ResolveModelConfigForNodeArgs): Promise<GraphAiModelConfig
|
|
22
|
+
export declare function resolveModelConfigForNode(args: ResolveModelConfigForNodeArgs): Promise<GraphAiModelConfig>;
|
|
20
23
|
export { conditionWhenSignature, isEmptyConditionWhen };
|
|
24
|
+
export { resolveGraphAiModelConfigAliases, mergeAliasConfigs } from './modelAliasContract.js';
|
|
@@ -1,21 +1,6 @@
|
|
|
1
1
|
import { conditionWhenSignature, isEmptyConditionWhen, isGraphAiModelConfig, isModelConfigSelection, } from './modelConfigSelection.js';
|
|
2
2
|
import { evaluateConditionWhen } from './taskNodeConditionsEvaluation.js';
|
|
3
|
-
|
|
4
|
-
return aliases?.[modelOrAlias] ?? modelOrAlias;
|
|
5
|
-
}
|
|
6
|
-
function mergeAliasConfigs(root, node) {
|
|
7
|
-
if (root == null && node == null)
|
|
8
|
-
return undefined;
|
|
9
|
-
return { ...(root ?? {}), ...(node ?? {}) };
|
|
10
|
-
}
|
|
11
|
-
export function resolveGraphAiModelConfigAliases(modelConfig, aliasConfig) {
|
|
12
|
-
if (!isGraphAiModelConfig(modelConfig))
|
|
13
|
-
return undefined;
|
|
14
|
-
return {
|
|
15
|
-
xynthesisModel: resolveModelAlias(modelConfig.xynthesisModel, aliasConfig),
|
|
16
|
-
skillModel: resolveModelAlias(modelConfig.skillModel, aliasConfig),
|
|
17
|
-
};
|
|
18
|
-
}
|
|
3
|
+
import { DEFAULT_GRAPH_AI_MODEL_ALIAS_CONFIG, mergeAliasConfigs, resolveGraphAiModelConfigAliases, } from './modelAliasContract.js';
|
|
19
4
|
async function selectFromCases(selection, ctx, executionInput, runx) {
|
|
20
5
|
const cases = selection.cases;
|
|
21
6
|
if (!Array.isArray(cases) || cases.length === 0)
|
|
@@ -49,21 +34,24 @@ async function resolveTier(value, ctx, executionInput, runx) {
|
|
|
49
34
|
return undefined;
|
|
50
35
|
}
|
|
51
36
|
/**
|
|
52
|
-
* Resolves effective model config for a task node (precedence + conditional cases +
|
|
37
|
+
* Resolves effective model config for a task node (precedence + conditional cases + strict alias resolution).
|
|
38
|
+
* Returns concrete provider model ids for `RunTaskRequest.modelConfig`.
|
|
53
39
|
*/
|
|
54
40
|
export async function resolveModelConfigForNode(args) {
|
|
55
41
|
const aliasConfig = mergeAliasConfigs(args.runtimeAliasConfig, args.runtimeNodeConfig?.aliasConfig);
|
|
42
|
+
const resolveCtx = { graphId: args.graphId, nodeId: args.nodeId, jobId: args.jobId };
|
|
56
43
|
const staticHost = args.runtimeNodeConfig?.modelConfig;
|
|
57
44
|
if (isGraphAiModelConfig(staticHost)) {
|
|
58
|
-
return resolveGraphAiModelConfigAliases(staticHost, aliasConfig);
|
|
45
|
+
return resolveGraphAiModelConfigAliases(staticHost, aliasConfig, resolveCtx);
|
|
59
46
|
}
|
|
60
47
|
const tiers = [args.nodeModelConfig, args.runtimeModelConfig, args.graphModelConfig];
|
|
61
48
|
for (const tier of tiers) {
|
|
62
49
|
const selected = await resolveTier(tier, args.conditionCtx, args.executionInput, args.runx);
|
|
63
50
|
if (selected != null) {
|
|
64
|
-
return resolveGraphAiModelConfigAliases(selected, aliasConfig);
|
|
51
|
+
return resolveGraphAiModelConfigAliases(selected, aliasConfig, resolveCtx);
|
|
65
52
|
}
|
|
66
53
|
}
|
|
67
|
-
return
|
|
54
|
+
return resolveGraphAiModelConfigAliases(DEFAULT_GRAPH_AI_MODEL_ALIAS_CONFIG, aliasConfig, resolveCtx);
|
|
68
55
|
}
|
|
69
56
|
export { conditionWhenSignature, isEmptyConditionWhen };
|
|
57
|
+
export { resolveGraphAiModelConfigAliases, mergeAliasConfigs } from './modelAliasContract.js';
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import type { Catalox } from '@x12i/catalox';
|
|
6
6
|
import type { RunTaskRequest } from '@exellix/ai-tasks';
|
|
7
7
|
import { type AnalyzeRunTaskRequestOptions, type AnalyzeRunTaskRequestResult, type RunTaskInvokeValidationResult, type RunTaskValidationResult, type ValidateRunTaskInvokeParams } from '@exellix/ai-tasks';
|
|
8
|
-
import type { GraphAiModelConfig, Job, TaskNode } from '../types/refs.js';
|
|
8
|
+
import type { GraphAiModelConfig, GraphModelAliasConfig, Job, TaskNode } from '../types/refs.js';
|
|
9
9
|
import type { ExecutionStepOption, RunTaskDiagnostics, SkillKeyResolutionOptions } from '../types/options.js';
|
|
10
10
|
export type TaskNodeRunTaskPreflightSkipReason = 'local_skill' | 'finalizer';
|
|
11
11
|
export type BuildTaskNodeRunTaskRequestResult = {
|
|
@@ -30,7 +30,10 @@ export type BuildTaskNodeRunTaskRequestArgs = {
|
|
|
30
30
|
graphRunTaskId?: string;
|
|
31
31
|
/** Host job id on `job.jobId` / `job.id`; defaults from `job` when omitted. */
|
|
32
32
|
runTaskJobId?: string;
|
|
33
|
+
/** Alias-only selection; resolved via `aliasConfig` before building the request. */
|
|
33
34
|
modelConfig?: GraphAiModelConfig;
|
|
35
|
+
/** Required when resolving `modelConfig` aliases for preflight (mirrors `runtime.aliasConfig`). */
|
|
36
|
+
aliasConfig?: GraphModelAliasConfig;
|
|
34
37
|
llmCall?: Record<string, unknown>;
|
|
35
38
|
/** Runtime default merged with per-node `taskConfiguration.llmCall`. */
|
|
36
39
|
runtimeLlmCall?: Record<string, unknown>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { analyzeRunTaskRequest, validateRunTaskConfig, validateRunTaskInvoke, } from '@exellix/ai-tasks';
|
|
2
|
+
import { resolveGraphAiModelConfigAliases } from './modelAliasContract.js';
|
|
2
3
|
import { resolveTaskKey } from './resolveTaskKey.js';
|
|
3
4
|
import { isLocalSkillKey } from './localSkills/index.js';
|
|
4
5
|
import { buildAiTasksRunTaskRequest, extractRunTaskStrategyOverrides, } from './buildAiTasksRunTaskRequest.js';
|
|
@@ -121,6 +122,13 @@ export function buildTaskNodeRunTaskRequest(args) {
|
|
|
121
122
|
});
|
|
122
123
|
const metaStrats = node.taskConfiguration?.executionStrategies;
|
|
123
124
|
const ov = node.taskConfiguration?.aiTasksOutputValidation;
|
|
125
|
+
const resolvedModelConfig = args.modelConfig != null && args.aliasConfig != null
|
|
126
|
+
? resolveGraphAiModelConfigAliases(args.modelConfig, args.aliasConfig, {
|
|
127
|
+
graphId: args.graphId,
|
|
128
|
+
nodeId: String(node.id),
|
|
129
|
+
jobId: lifecycleJobId,
|
|
130
|
+
})
|
|
131
|
+
: args.modelConfig;
|
|
124
132
|
const request = buildAiTasksRunTaskRequest({
|
|
125
133
|
skillKey,
|
|
126
134
|
job: args.job,
|
|
@@ -145,7 +153,7 @@ export function buildTaskNodeRunTaskRequest(args) {
|
|
|
145
153
|
? ov
|
|
146
154
|
: undefined,
|
|
147
155
|
diagnostics: args.runTaskDiagnostics,
|
|
148
|
-
modelConfig:
|
|
156
|
+
modelConfig: resolvedModelConfig ?? undefined,
|
|
149
157
|
llmCall: effectiveLlmCall,
|
|
150
158
|
identity: buildRunTaskIdentityEnvelope({
|
|
151
159
|
base: args.runTaskIdentity,
|
|
@@ -4,6 +4,7 @@ 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
6
|
import { conditionWhenSignature, countDefaultModelConfigCases, isEmptyConditionWhen, isGraphAiModelConfig, isModelConfigSelection, } from './modelConfigSelection.js';
|
|
7
|
+
import { assertGraphModelAliasString } from './modelAliasContract.js';
|
|
7
8
|
/** Top-level keys permitted on a canonical exellix-graph executable graph JSON document. */
|
|
8
9
|
export const CANONICAL_GRAPH_TOP_LEVEL_KEYS = [
|
|
9
10
|
'id',
|
|
@@ -296,8 +297,10 @@ function assertModelConfigSelection(value, path, context) {
|
|
|
296
297
|
throw new ExellixGraphError(code, `${casePath} must be a plain object.`, context);
|
|
297
298
|
}
|
|
298
299
|
if (!isGraphAiModelConfig(c.modelConfig)) {
|
|
299
|
-
throw new ExellixGraphError(code, `${casePath}.modelConfig must be { xynthesisModel: string, skillModel: string } with non-empty values.`, context);
|
|
300
|
+
throw new ExellixGraphError(code, `${casePath}.modelConfig must be { xynthesisModel: string, skillModel: string } with non-empty alias values.`, context);
|
|
300
301
|
}
|
|
302
|
+
assertGraphModelAliasString(c.modelConfig.xynthesisModel, `${casePath}.modelConfig.xynthesisModel`, context);
|
|
303
|
+
assertGraphModelAliasString(c.modelConfig.skillModel, `${casePath}.modelConfig.skillModel`, context);
|
|
301
304
|
if (isEmptyConditionWhen(c.when)) {
|
|
302
305
|
continue;
|
|
303
306
|
}
|
|
@@ -318,8 +321,10 @@ function assertOptionalRuntimeFlatModelConfig(value, path, context) {
|
|
|
318
321
|
if (value === undefined)
|
|
319
322
|
return;
|
|
320
323
|
if (!isGraphAiModelConfig(value)) {
|
|
321
|
-
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `${path} must be exactly { xynthesisModel: string, skillModel: string } (runtime host override does not use cases).`, context);
|
|
324
|
+
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `${path} must be exactly { xynthesisModel: string, skillModel: string } alias names (runtime host override does not use cases).`, context);
|
|
322
325
|
}
|
|
326
|
+
assertGraphModelAliasString(value.xynthesisModel, `${path}.xynthesisModel`, context);
|
|
327
|
+
assertGraphModelAliasString(value.skillModel, `${path}.skillModel`, context);
|
|
323
328
|
}
|
|
324
329
|
function assertContextualKnowledgeScope(scope, nodeId, context) {
|
|
325
330
|
if (scope === undefined)
|
package/dist/src/types/refs.d.ts
CHANGED
|
@@ -13,14 +13,14 @@ export type TaskNodeExecutionPipelineStep = {
|
|
|
13
13
|
* Type references for graph execution
|
|
14
14
|
*/
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* Alias-only AI model slot selection on graph model / node `modelConfig`.
|
|
17
|
+
* Concrete provider model ids are supplied only via {@link GraphModelAliasConfig} at execution.
|
|
18
18
|
*/
|
|
19
19
|
export type GraphAiModelConfig = {
|
|
20
20
|
xynthesisModel: string;
|
|
21
21
|
skillModel: string;
|
|
22
22
|
};
|
|
23
|
-
/** Runtime alias
|
|
23
|
+
/** Runtime map: alias name → concrete provider model id (required on every `executeGraph`). */
|
|
24
24
|
export type GraphModelAliasConfig = Record<string, string>;
|
|
25
25
|
/** Runtime overrides for a single task node, stored at GraphRuntimeObject.nodes[nodeId]. */
|
|
26
26
|
export type TaskNodeRuntimeObject = {
|
package/dist/testkit/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { InMemoryGraphLoader } from './inMemoryGraphLoader.js';
|
|
2
2
|
export { DepGraphEngineFactory } from './depGraphEngineFactory.js';
|
|
3
3
|
export { RealTasksClient } from './RealTasksClient.js';
|
|
4
|
+
export { buildTestAliasConfig, withTestAliasRuntime, createTestExellixGraphRuntime, } from './testModelAliasRuntime.js';
|
|
4
5
|
export { tryLoadExellixAiTasksRuntimeSubtree, loadExellixGraphRuntimeObjects, } from './exellixRuntimeObjects.js';
|
package/dist/testkit/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { InMemoryGraphLoader } from './inMemoryGraphLoader.js';
|
|
2
2
|
export { DepGraphEngineFactory } from './depGraphEngineFactory.js';
|
|
3
3
|
export { RealTasksClient } from './RealTasksClient.js';
|
|
4
|
+
export { buildTestAliasConfig, withTestAliasRuntime, createTestExellixGraphRuntime, } from './testModelAliasRuntime.js';
|
|
4
5
|
export { tryLoadExellixAiTasksRuntimeSubtree, loadExellixGraphRuntimeObjects, } from './exellixRuntimeObjects.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Graph } from '../src/types/refs.js';
|
|
2
|
+
import { createExellixGraphRuntime, type GraphRuntimeObject } from '../src/runtime/ExellixGraphRuntime.js';
|
|
3
|
+
/** Maps every alias used by a graph (plus `default`) to `openrouter/<alias>` for unit tests. */
|
|
4
|
+
export declare function buildTestAliasConfig(graph: Pick<Graph, 'modelConfig' | 'nodes'>, runtimePartial?: Pick<GraphRuntimeObject, 'modelConfig' | 'nodes'> & {
|
|
5
|
+
aliasConfig?: Record<string, string>;
|
|
6
|
+
}): Record<string, string>;
|
|
7
|
+
/** Merges test alias bindings into a runtime object (required for executeGraph in 7.x). */
|
|
8
|
+
export declare function withTestAliasRuntime(graph: Pick<Graph, 'id' | 'modelConfig' | 'nodes'>, runtime: Partial<GraphRuntimeObject> & Pick<GraphRuntimeObject, 'jobId' | 'job'>): GraphRuntimeObject;
|
|
9
|
+
type CreateOpts = Parameters<typeof createExellixGraphRuntime>[0];
|
|
10
|
+
/**
|
|
11
|
+
* Testkit runtime that auto-injects `runtime.aliasConfig` on every `executeGraph` call.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createTestExellixGraphRuntime(opts: CreateOpts): ReturnType<typeof createExellixGraphRuntime>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createExellixGraphRuntime, } from '../src/runtime/ExellixGraphRuntime.js';
|
|
2
|
+
import { collectModelAliasesUsedInGraph } from '../src/runtime/modelAliasContract.js';
|
|
3
|
+
/** Maps every alias used by a graph (plus `default`) to `openrouter/<alias>` for unit tests. */
|
|
4
|
+
export function buildTestAliasConfig(graph, runtimePartial) {
|
|
5
|
+
const needed = collectModelAliasesUsedInGraph(graph, runtimePartial);
|
|
6
|
+
const map = {};
|
|
7
|
+
for (const alias of needed) {
|
|
8
|
+
map[alias] = `openrouter/${alias}`;
|
|
9
|
+
}
|
|
10
|
+
return { ...map, ...(runtimePartial?.aliasConfig ?? {}) };
|
|
11
|
+
}
|
|
12
|
+
/** Merges test alias bindings into a runtime object (required for executeGraph in 7.x). */
|
|
13
|
+
export function withTestAliasRuntime(graph, runtime) {
|
|
14
|
+
const aliasConfig = buildTestAliasConfig(graph, runtime);
|
|
15
|
+
return {
|
|
16
|
+
...runtime,
|
|
17
|
+
aliasConfig,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Testkit runtime that auto-injects `runtime.aliasConfig` on every `executeGraph` call.
|
|
22
|
+
*/
|
|
23
|
+
export function createTestExellixGraphRuntime(opts) {
|
|
24
|
+
const runtime = createExellixGraphRuntime(opts);
|
|
25
|
+
const executeGraph = runtime.executeGraph.bind(runtime);
|
|
26
|
+
runtime.executeGraph = async (input) => {
|
|
27
|
+
const base = input.runtime ?? {};
|
|
28
|
+
const jobId = typeof base.jobId === 'string' && base.jobId.trim() !== ''
|
|
29
|
+
? base.jobId
|
|
30
|
+
: `job-${input.model.id ?? 'graph'}`;
|
|
31
|
+
const job = base.job != null && typeof base.job === 'object'
|
|
32
|
+
? base.job
|
|
33
|
+
: { agentId: 'test-agent', jobType: 'unit-test' };
|
|
34
|
+
return executeGraph({
|
|
35
|
+
...input,
|
|
36
|
+
runtime: withTestAliasRuntime(input.model, { ...base, jobId, job }),
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
return runtime;
|
|
40
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exellix/graph-engine",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Graph executor SDK",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"scripts": {
|
|
26
26
|
"prebuild": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true, maxRetries: 10, retryDelay: 100 })\"",
|
|
27
27
|
"build": "tsc",
|
|
28
|
-
"test": "npm run build && npm run check:no-legacy &&
|
|
28
|
+
"test": "npm run build && npm run check:no-legacy && node --test --test-force-exit tests/model-alias-contract.test.ts tests/reports-fixtures-pre-synthesis.test.ts tests/passthrough-parity.test.ts",
|
|
29
|
+
"test:full": "npm run build && npm run check:no-legacy && tsx --test --test-force-exit tests/graph-engine.test.ts tests/passthrough-parity.test.ts tests/task-node-run-task-preflight.test.ts tests/reports-fixtures-pre-synthesis.test.ts tests/model-alias-contract.test.ts",
|
|
29
30
|
"run:pre-synthesis": "npm run build && node --env-file=.env scripts/run-pre-synthesis-graph.mjs",
|
|
30
31
|
"check:no-legacy": "node scripts/check-no-legacy.mjs",
|
|
31
32
|
"lint": "eslint src testkit --ext .ts",
|
|
@@ -48,7 +49,7 @@
|
|
|
48
49
|
"access": "public"
|
|
49
50
|
},
|
|
50
51
|
"dependencies": {
|
|
51
|
-
"@exellix/ai-tasks": "^
|
|
52
|
+
"@exellix/ai-tasks": "^8.0.0",
|
|
52
53
|
"@x12i/activix": "^7.1.0",
|
|
53
54
|
"@x12i/catalox": "^5.1.1",
|
|
54
55
|
"@x12i/env": "^4.0.1",
|