@exellix/graph-engine 7.0.1 → 7.2.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 +27 -0
- package/dist/src/errors/exellixGraphErrorCodes.d.ts +8 -4
- package/dist/src/errors/exellixGraphErrorCodes.js +7 -3
- package/dist/src/index.d.ts +8 -2
- package/dist/src/index.js +8 -2
- package/dist/src/inspection/contractInspection.js +12 -2
- package/dist/src/runtime/ExellixGraphRuntime.d.ts +3 -1
- package/dist/src/runtime/ExellixGraphRuntime.js +10 -5
- package/dist/src/runtime/aiTasksStrategyPhases.d.ts +1 -0
- package/dist/src/runtime/aiTasksStrategyPhases.js +4 -1
- package/dist/src/runtime/buildAiTasksRunTaskRequest.d.ts +4 -2
- package/dist/src/runtime/canonicalModelUsed.js +6 -1
- package/dist/src/runtime/graphAiModelConfig.d.ts +37 -0
- package/dist/src/runtime/graphAiModelConfig.js +119 -0
- package/dist/src/runtime/modelAliasContract.d.ts +8 -44
- package/dist/src/runtime/modelAliasContract.js +8 -127
- package/dist/src/runtime/modelConfigSelection.js +8 -5
- package/dist/src/runtime/resolveModelConfigForNode.d.ts +6 -5
- package/dist/src/runtime/resolveModelConfigForNode.js +9 -9
- package/dist/src/runtime/stepRetry.d.ts +5 -0
- package/dist/src/runtime/stepRetry.js +16 -10
- package/dist/src/runtime/taskNodeRunTaskPreflight.d.ts +4 -4
- package/dist/src/runtime/taskNodeRunTaskPreflight.js +8 -14
- package/dist/src/runtime/validateCanonicalGraphDocument.js +7 -8
- package/dist/src/types/options.d.ts +2 -2
- package/dist/src/types/refs.d.ts +9 -4
- package/dist/testkit/RealTasksClient.js +5 -1
- package/dist/testkit/testModelAliasRuntime.d.ts +7 -7
- package/dist/testkit/testModelAliasRuntime.js +10 -20
- package/package.json +9 -6
- package/scripts/patch-ai-tasks-xynthesis-export.mjs +22 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 7.1.2
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Step retry token bump:** Reads `llmCall.maxTokensCap` (ai-tasks v8) before legacy `maxTokens` / `max_tokens`, and writes bumps back as `maxTokensCap` on retry.
|
|
8
|
+
- **Dependency workaround:** `postinstall` patches `@exellix/ai-tasks@8.0.7` broken re-export of `MODEL_CAPABILITIES` from `@exellix/xynthesis` (removed in xynthesis 4.x) until upstream publishes a fix.
|
|
9
|
+
|
|
10
|
+
## 7.1.0
|
|
11
|
+
|
|
12
|
+
### Breaking
|
|
13
|
+
|
|
14
|
+
- **Three-phase `GraphAiModelConfig`:** `{ preActionModel, skillModel, postActionModel }` replaces `{ xynthesisModel, skillModel }`. `postActionModel` is required; POST utilities no longer reuse `skillModel`.
|
|
15
|
+
- **AI profile resolution:** Graph JSON profile names resolve via `@x12i/ai-profiles` (bundled registry). `runtime.aliasConfig` is no longer required.
|
|
16
|
+
- **Phase routing:** PRE / MAIN / POST `runTask` calls receive independent models on the ai-tasks wire (`toRunTaskModelConfigForPhase`).
|
|
17
|
+
- **Preflight async:** `buildTaskNodeRunTaskRequest` and `validateTaskNodeRunTaskConfig` are async.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Dependency `@x12i/ai-profiles`.
|
|
22
|
+
- Exports: `resolveGraphAiModelConfig`, `toRunTaskModelConfigForPhase`, `DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG`, `isGraphAiProfileName`, `MODEL_CONFIG_INCOMPLETE`, `MODEL_PROFILE_UNRESOLVED`.
|
|
23
|
+
|
|
24
|
+
## 7.0.3
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- **Default model ids (JSON-serializable):** `DEFAULT_GRAPH_AI_MODEL_IDS` (`google/gemma-4-31b-it` synthesis, `deepseek/deepseek-r1` skills), `DEFAULT_RUNTIME_ALIAS_CONFIG`, and `DEFAULT_SYNTHESIS_MODEL_ALIAS` / `DEFAULT_SKILL_MODEL_ALIAS` for implicit fallback (`default-xynthesis`, `default-skill`). Testkit `buildTestAliasConfig` merges `DEFAULT_RUNTIME_ALIAS_CONFIG` automatically.
|
|
29
|
+
|
|
3
30
|
## 7.0.0
|
|
4
31
|
|
|
5
32
|
### Breaking
|
|
@@ -28,10 +28,14 @@ export declare enum ExellixGraphErrorCode {
|
|
|
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
30
|
MAIN_READINESS_FAILED = "MAIN_READINESS_FAILED",
|
|
31
|
-
/**
|
|
31
|
+
/** @deprecated Legacy alias map — unused since 7.1. */
|
|
32
32
|
MODEL_ALIAS_CONFIG_REQUIRED = "MODEL_ALIAS_CONFIG_REQUIRED",
|
|
33
|
-
/**
|
|
33
|
+
/** @deprecated Legacy alias map — unused since 7.1. */
|
|
34
34
|
MODEL_ALIAS_UNRESOLVED = "MODEL_ALIAS_UNRESOLVED",
|
|
35
|
-
/** Graph JSON `modelConfig` contains a provider model id instead of an
|
|
36
|
-
NON_CANONICAL_MODEL_CONFIG = "NON_CANONICAL_MODEL_CONFIG"
|
|
35
|
+
/** Graph JSON `modelConfig` contains a provider model id instead of an AI profile name. */
|
|
36
|
+
NON_CANONICAL_MODEL_CONFIG = "NON_CANONICAL_MODEL_CONFIG",
|
|
37
|
+
/** `modelConfig` is missing or not exactly `{ preActionModel, skillModel, postActionModel }`. */
|
|
38
|
+
MODEL_CONFIG_INCOMPLETE = "MODEL_CONFIG_INCOMPLETE",
|
|
39
|
+
/** An AI profile name could not be resolved via `@x12i/ai-profiles`. */
|
|
40
|
+
MODEL_PROFILE_UNRESOLVED = "MODEL_PROFILE_UNRESOLVED"
|
|
37
41
|
}
|
|
@@ -29,10 +29,14 @@ 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
|
-
/**
|
|
32
|
+
/** @deprecated Legacy alias map — unused since 7.1. */
|
|
33
33
|
ExellixGraphErrorCode["MODEL_ALIAS_CONFIG_REQUIRED"] = "MODEL_ALIAS_CONFIG_REQUIRED";
|
|
34
|
-
/**
|
|
34
|
+
/** @deprecated Legacy alias map — unused since 7.1. */
|
|
35
35
|
ExellixGraphErrorCode["MODEL_ALIAS_UNRESOLVED"] = "MODEL_ALIAS_UNRESOLVED";
|
|
36
|
-
/** Graph JSON `modelConfig` contains a provider model id instead of an
|
|
36
|
+
/** Graph JSON `modelConfig` contains a provider model id instead of an AI profile name. */
|
|
37
37
|
ExellixGraphErrorCode["NON_CANONICAL_MODEL_CONFIG"] = "NON_CANONICAL_MODEL_CONFIG";
|
|
38
|
+
/** `modelConfig` is missing or not exactly `{ preActionModel, skillModel, postActionModel }`. */
|
|
39
|
+
ExellixGraphErrorCode["MODEL_CONFIG_INCOMPLETE"] = "MODEL_CONFIG_INCOMPLETE";
|
|
40
|
+
/** An AI profile name could not be resolved via `@x12i/ai-profiles`. */
|
|
41
|
+
ExellixGraphErrorCode["MODEL_PROFILE_UNRESOLVED"] = "MODEL_PROFILE_UNRESOLVED";
|
|
38
42
|
})(ExellixGraphErrorCode || (ExellixGraphErrorCode = {}));
|
package/dist/src/index.d.ts
CHANGED
|
@@ -38,8 +38,14 @@ 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,
|
|
42
|
-
export {
|
|
41
|
+
export { resolveModelConfigForNode, resolveGraphAiModelConfig, toRunTaskModelConfigForPhase, DEFAULT_GRAPH_AI_MODEL_IDS, DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, } from './runtime/resolveModelConfigForNode.js';
|
|
42
|
+
export { looksLikeConcreteModelId, isGraphAiProfileName, assertGraphAiProfileNameString, type RunTaskModelConfigWire, type EngineModelPhase, } from './runtime/graphAiModelConfig.js';
|
|
43
|
+
/** @deprecated Use {@link resolveGraphAiModelConfig}. */
|
|
44
|
+
export { resolveGraphAiModelConfig as resolveGraphAiModelConfigAliases } from './runtime/graphAiModelConfig.js';
|
|
45
|
+
/** @deprecated Use {@link isGraphAiProfileName}. */
|
|
46
|
+
export { isGraphAiProfileName as isModelAliasString } from './runtime/graphAiModelConfig.js';
|
|
47
|
+
/** @deprecated Use {@link assertGraphAiProfileNameString}. */
|
|
48
|
+
export { assertGraphAiProfileNameString as assertGraphModelAliasString } from './runtime/graphAiModelConfig.js';
|
|
43
49
|
export { isGraphAiModelConfig, isModelConfigSelection, isEmptyConditionWhen, conditionWhenSignature, countDefaultModelConfigCases, } from './runtime/modelConfigSelection.js';
|
|
44
50
|
export { GRAPH_ENGINE_MEMORY_PATH_ROOTS, splitGraphEngineMemoryPath, isAllowedGraphEngineMemoryPath, graphEngineMemoryPathValidationMessage, } from './runtime/graphEngineMemoryPaths.js';
|
|
45
51
|
export { buildRunTaskMainInput, extractCallerInputsBag, buildGraphEngineMemoryResolutionRoot, buildGraphEngineMemoryResolutionRootFromWorkingMemory, resolveGraphEngineMemoryPathValue, } from './runtime/resolveGraphEngineMemoryPaths.js';
|
package/dist/src/index.js
CHANGED
|
@@ -28,8 +28,14 @@ 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,
|
|
32
|
-
export {
|
|
31
|
+
export { resolveModelConfigForNode, resolveGraphAiModelConfig, toRunTaskModelConfigForPhase, DEFAULT_GRAPH_AI_MODEL_IDS, DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, } from './runtime/resolveModelConfigForNode.js';
|
|
32
|
+
export { looksLikeConcreteModelId, isGraphAiProfileName, assertGraphAiProfileNameString, } from './runtime/graphAiModelConfig.js';
|
|
33
|
+
/** @deprecated Use {@link resolveGraphAiModelConfig}. */
|
|
34
|
+
export { resolveGraphAiModelConfig as resolveGraphAiModelConfigAliases } from './runtime/graphAiModelConfig.js';
|
|
35
|
+
/** @deprecated Use {@link isGraphAiProfileName}. */
|
|
36
|
+
export { isGraphAiProfileName as isModelAliasString } from './runtime/graphAiModelConfig.js';
|
|
37
|
+
/** @deprecated Use {@link assertGraphAiProfileNameString}. */
|
|
38
|
+
export { assertGraphAiProfileNameString as assertGraphModelAliasString } from './runtime/graphAiModelConfig.js';
|
|
33
39
|
export { isGraphAiModelConfig, isModelConfigSelection, isEmptyConditionWhen, conditionWhenSignature, countDefaultModelConfigCases, } from './runtime/modelConfigSelection.js';
|
|
34
40
|
export { GRAPH_ENGINE_MEMORY_PATH_ROOTS, splitGraphEngineMemoryPath, isAllowedGraphEngineMemoryPath, graphEngineMemoryPathValidationMessage, } from './runtime/graphEngineMemoryPaths.js';
|
|
35
41
|
export { buildRunTaskMainInput, extractCallerInputsBag, buildGraphEngineMemoryResolutionRoot, buildGraphEngineMemoryResolutionRootFromWorkingMemory, resolveGraphEngineMemoryPathValue, } from './runtime/resolveGraphEngineMemoryPaths.js';
|
|
@@ -293,9 +293,19 @@ function buildOutputContract(node) {
|
|
|
293
293
|
const tc = task.taskConfiguration;
|
|
294
294
|
const webCfg = tc?.aiTaskProfile;
|
|
295
295
|
const webScoping = webCfg?.webScoping;
|
|
296
|
+
const narrixCfg = tc?.narrix;
|
|
296
297
|
const webWrites = [];
|
|
297
|
-
if (
|
|
298
|
-
webWrites.push({
|
|
298
|
+
if (narrixCfg?.enableWebScope === true) {
|
|
299
|
+
webWrites.push({
|
|
300
|
+
path: 'webContext',
|
|
301
|
+
source: 'taskConfiguration.narrix.enableWebScope → @exellix/ai-tasks (runtime-known)',
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
else if (webScoping && webScoping.enabled === true) {
|
|
305
|
+
webWrites.push({
|
|
306
|
+
path: 'webContext',
|
|
307
|
+
source: 'taskConfiguration.aiTaskProfile.webScoping → executeNode (runtime-known)',
|
|
308
|
+
});
|
|
299
309
|
}
|
|
300
310
|
const synthWrites = [];
|
|
301
311
|
const inputSynth = webCfg?.inputSynthesis;
|
|
@@ -91,7 +91,9 @@ export interface GraphRuntimeObject extends HostExecuteGraphRunOptions {
|
|
|
91
91
|
taskVariables?: Record<string, unknown>;
|
|
92
92
|
/** Runtime default model selection (`cases`); overrides GraphModelObject.modelConfig. */
|
|
93
93
|
modelConfig?: ModelConfigSelection;
|
|
94
|
-
/**
|
|
94
|
+
/**
|
|
95
|
+
* @deprecated Unused since 7.1 — model profiles resolve via `@x12i/ai-profiles`.
|
|
96
|
+
*/
|
|
95
97
|
aliasConfig?: GraphModelAliasConfig;
|
|
96
98
|
/** Task-node runtime overrides keyed by node id. */
|
|
97
99
|
nodes?: Record<string, TaskNodeRuntimeObject>;
|
|
@@ -34,7 +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 {
|
|
37
|
+
import { toRunTaskModelConfigForPhase } from "./graphAiModelConfig.js";
|
|
38
38
|
import { createRunx } from "@x12i/runx";
|
|
39
39
|
import { selectByPath } from "./pathExpr.js";
|
|
40
40
|
import { createGraphStartEvent, createGraphCompleteEvent, createGraphFailEvent, createNodeStartEvent, createNodeCompleteEvent, createNodeFailEvent, } from "./events.js";
|
|
@@ -393,7 +393,11 @@ export function createExellixGraphRuntime(opts) {
|
|
|
393
393
|
: {}),
|
|
394
394
|
kind: "finalizer",
|
|
395
395
|
},
|
|
396
|
-
...(input.modelConfig != null
|
|
396
|
+
...(input.modelConfig != null
|
|
397
|
+
? {
|
|
398
|
+
modelConfig: toRunTaskModelConfigForPhase(input.modelConfig, 'pre'),
|
|
399
|
+
}
|
|
400
|
+
: {}),
|
|
397
401
|
...(effectiveLlmCall != null ? { llmCall: effectiveLlmCall } : {}),
|
|
398
402
|
...(forwardRunTaskTrace ? { executionMode: "trace" } : {}),
|
|
399
403
|
...((input.runTaskDiagnostics ?? opts.runTaskDiagnostics) != null
|
|
@@ -603,6 +607,9 @@ export function createExellixGraphRuntime(opts) {
|
|
|
603
607
|
input.eventEmitter.emit(createNodeStartEvent(lifecycleJobId, input.graphId, graphRunTaskId, input.node, skillKey, undefined, input.jobCorrelation));
|
|
604
608
|
}
|
|
605
609
|
const effectiveModelConfig = input.modelConfig;
|
|
610
|
+
const mainRunTaskModelConfig = effectiveModelConfig != null
|
|
611
|
+
? toRunTaskModelConfigForPhase(effectiveModelConfig, 'main')
|
|
612
|
+
: undefined;
|
|
606
613
|
// DEBUG: Verify execution object before sending
|
|
607
614
|
if (process.env.DEBUG_EXECUTION_MEMORY === 'true') {
|
|
608
615
|
console.log(`[DEBUG] Node ${input.node?.id}: execution object =`, JSON.stringify(input.execution, null, 2));
|
|
@@ -830,7 +837,7 @@ export function createExellixGraphRuntime(opts) {
|
|
|
830
837
|
? input.node.taskConfiguration.aiTasksOutputValidation
|
|
831
838
|
: undefined,
|
|
832
839
|
diagnostics: (input.runTaskDiagnostics ?? opts.runTaskDiagnostics),
|
|
833
|
-
modelConfig:
|
|
840
|
+
modelConfig: mainRunTaskModelConfig,
|
|
834
841
|
llmCall: effectiveLlmCall,
|
|
835
842
|
identity: buildRunTaskIdentityEnvelope({
|
|
836
843
|
base: runTaskIdentityBase,
|
|
@@ -1152,7 +1159,6 @@ export function createExellixGraphRuntime(opts) {
|
|
|
1152
1159
|
}
|
|
1153
1160
|
const resolvedGraphId = String(resolvedGraphIdRaw);
|
|
1154
1161
|
assertCanonicalGraphDocument(graph, { jobId, graphId: resolvedGraphId });
|
|
1155
|
-
assertRuntimeAliasConfigCoversGraph(graph, merged.aliasConfig, { modelConfig: merged.modelConfig, nodes: merged.nodes }, { jobId, graphId: resolvedGraphId });
|
|
1156
1162
|
let runxClient = opts.runx;
|
|
1157
1163
|
if (graphNeedsRunxClient(graph, merged.modelConfig)) {
|
|
1158
1164
|
if (!runxClient) {
|
|
@@ -1569,7 +1575,6 @@ export function createExellixGraphRuntime(opts) {
|
|
|
1569
1575
|
nodeModelConfig: node.taskConfiguration?.modelConfig,
|
|
1570
1576
|
runtimeModelConfig: merged.modelConfig,
|
|
1571
1577
|
graphModelConfig: graph.modelConfig,
|
|
1572
|
-
runtimeAliasConfig: merged.aliasConfig,
|
|
1573
1578
|
conditionCtx: buildPredicateEvalContextForNode({
|
|
1574
1579
|
executionMemory: currentExecution,
|
|
1575
1580
|
jobMemory: currentJobMemory,
|
|
@@ -25,6 +25,7 @@ export type RunEngineAiTasksStrategyPhaseArgs = {
|
|
|
25
25
|
taskMemory?: unknown;
|
|
26
26
|
jobContext?: Record<string, unknown>;
|
|
27
27
|
prevNodeId?: string;
|
|
28
|
+
/** Resolved three-phase config; routed to the correct ai-tasks slot for this strategy phase. */
|
|
28
29
|
modelConfig?: GraphAiModelConfig;
|
|
29
30
|
llmCall?: RunTaskRequest['llmCall'];
|
|
30
31
|
runTaskIdentity?: Record<string, unknown>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { toRunTaskModelConfigForPhase } from './graphAiModelConfig.js';
|
|
1
2
|
import { buildAiTasksRunTaskRequest } from './buildAiTasksRunTaskRequest.js';
|
|
2
3
|
import { buildRunTaskIdentityEnvelope } from './runTaskAugments.js';
|
|
3
4
|
import { runTaskResponseSucceeded } from './runTaskResponse.js';
|
|
@@ -53,7 +54,9 @@ export async function runEngineAiTasksStrategyPhase(args) {
|
|
|
53
54
|
prevNodeId: args.prevNodeId,
|
|
54
55
|
executionPipeline: [{ phase: 'main', type: 'direct' }],
|
|
55
56
|
executionStrategies: [],
|
|
56
|
-
modelConfig: args.modelConfig
|
|
57
|
+
modelConfig: args.modelConfig != null
|
|
58
|
+
? toRunTaskModelConfigForPhase(args.modelConfig, args.phase)
|
|
59
|
+
: undefined,
|
|
57
60
|
llmCall: args.llmCall ?? undefined,
|
|
58
61
|
identity,
|
|
59
62
|
...(args.forwardRunTaskTrace ? { executionMode: 'trace' } : {}),
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { LlmCallConfig, RunTaskRequest } from '@exellix/ai-tasks';
|
|
11
11
|
import type { SmartInputConfig, XynthesizedMemory } from '../types/aiTasksDerivedTypes.js';
|
|
12
|
-
import type {
|
|
12
|
+
import type { Job } from '../types/refs.js';
|
|
13
|
+
import type { RunTaskModelConfigWire } from './graphAiModelConfig.js';
|
|
13
14
|
import type { ExecutionStepOption } from '../types/options.js';
|
|
14
15
|
import type { ResolvedNarrixWirePayload } from '../types/narrix.js';
|
|
15
16
|
/** Reads task-node `taskConfiguration` keys forwarded to `@exellix/ai-tasks` `RunTaskRequest` (runtime strategy / Narrix wiring). */
|
|
@@ -45,7 +46,8 @@ export type BuildAiTasksRunTaskRequestArgs = {
|
|
|
45
46
|
narrix?: ResolvedNarrixWirePayload;
|
|
46
47
|
outputValidation?: RunTaskRequest['outputValidation'];
|
|
47
48
|
diagnostics?: RunTaskRequest['diagnostics'];
|
|
48
|
-
|
|
49
|
+
/** Resolved ai-tasks slot pair (`xynthesisModel` / `skillModel`) for this runTask phase. */
|
|
50
|
+
modelConfig?: RunTaskModelConfigWire;
|
|
49
51
|
llmCall?: LlmCallConfig;
|
|
50
52
|
identity?: Record<string, unknown>;
|
|
51
53
|
executionMode?: 'default' | 'trace';
|
|
@@ -14,7 +14,12 @@ export function resolveCanonicalModelUsed(response) {
|
|
|
14
14
|
return direct.trim();
|
|
15
15
|
const rawConfig = meta.rawConfig;
|
|
16
16
|
if (isPlainObject(rawConfig)) {
|
|
17
|
-
const fromRaw = rawConfig.modelUsed ??
|
|
17
|
+
const fromRaw = rawConfig.modelUsed ??
|
|
18
|
+
rawConfig.model ??
|
|
19
|
+
rawConfig.skillModel ??
|
|
20
|
+
rawConfig.preActionModel ??
|
|
21
|
+
rawConfig.postActionModel ??
|
|
22
|
+
rawConfig.xynthesisModel;
|
|
18
23
|
if (typeof fromRaw === 'string' && fromRaw.trim() !== '')
|
|
19
24
|
return fromRaw.trim();
|
|
20
25
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { GraphAiModelConfig } from '../types/refs.js';
|
|
2
|
+
/** Wire shape expected by `@exellix/ai-tasks` `RunTaskRequest.modelConfig`. */
|
|
3
|
+
export type RunTaskModelConfigWire = {
|
|
4
|
+
xynthesisModel: string;
|
|
5
|
+
skillModel: string;
|
|
6
|
+
};
|
|
7
|
+
/** Profile names used when no modelConfig tier resolves. */
|
|
8
|
+
export declare const DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG: GraphAiModelConfig;
|
|
9
|
+
/**
|
|
10
|
+
* Canonical default concrete provider model ids (JSON-serializable).
|
|
11
|
+
* Derived from bundled `@x12i/ai-profiles` defaults for {@link DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG}.
|
|
12
|
+
*/
|
|
13
|
+
export declare const DEFAULT_GRAPH_AI_MODEL_IDS: GraphAiModelConfig;
|
|
14
|
+
export type GraphAiModelConfigResolveContext = {
|
|
15
|
+
jobId?: string;
|
|
16
|
+
graphId?: string;
|
|
17
|
+
nodeId?: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* True when a value looks like a concrete provider model id (host-resolved / post profile resolution).
|
|
21
|
+
*/
|
|
22
|
+
export declare function looksLikeConcreteModelId(value: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* True when a string is a valid graph-authored AI profile name (not a provider model id).
|
|
25
|
+
*/
|
|
26
|
+
export declare function isGraphAiProfileName(value: unknown): value is string;
|
|
27
|
+
export declare function assertGraphAiProfileNameString(value: unknown, path: string, context?: GraphAiModelConfigResolveContext): asserts value is string;
|
|
28
|
+
/**
|
|
29
|
+
* Resolves graph/runtime {@link GraphAiModelConfig} to concrete provider model ids.
|
|
30
|
+
* Profile names resolve via bundled `@x12i/ai-profiles`; concrete ids pass through unchanged.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveGraphAiModelConfig(modelConfig: GraphAiModelConfig | undefined, context?: GraphAiModelConfigResolveContext): Promise<GraphAiModelConfig>;
|
|
33
|
+
export type EngineModelPhase = 'pre' | 'main' | 'post';
|
|
34
|
+
/**
|
|
35
|
+
* Maps resolved three-phase graph config to ai-tasks slot pair for a single runTask phase.
|
|
36
|
+
*/
|
|
37
|
+
export declare function toRunTaskModelConfigForPhase(config: GraphAiModelConfig, phase: EngineModelPhase): RunTaskModelConfigWire;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { resolveAIProfile } from '@x12i/ai-profiles';
|
|
2
|
+
import { ExellixGraphError } from '../errors/ExellixGraphError.js';
|
|
3
|
+
import { ExellixGraphErrorCode } from '../errors/exellixGraphErrorCodes.js';
|
|
4
|
+
import { isGraphAiModelConfig } from './modelConfigSelection.js';
|
|
5
|
+
/** Profile names used when no modelConfig tier resolves. */
|
|
6
|
+
export const DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG = {
|
|
7
|
+
preActionModel: 'cheap',
|
|
8
|
+
skillModel: 'balanced',
|
|
9
|
+
postActionModel: 'cheap',
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Canonical default concrete provider model ids (JSON-serializable).
|
|
13
|
+
* Derived from bundled `@x12i/ai-profiles` defaults for {@link DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG}.
|
|
14
|
+
*/
|
|
15
|
+
export const DEFAULT_GRAPH_AI_MODEL_IDS = {
|
|
16
|
+
preActionModel: 'google/gemini-2.5-flash-lite',
|
|
17
|
+
skillModel: 'openai/gpt-5.4',
|
|
18
|
+
postActionModel: 'google/gemini-2.5-flash-lite',
|
|
19
|
+
};
|
|
20
|
+
const CONCRETE_MODEL_PREFIXES = [
|
|
21
|
+
'openrouter/',
|
|
22
|
+
'anthropic/',
|
|
23
|
+
'openai/',
|
|
24
|
+
'google/',
|
|
25
|
+
'x-ai/',
|
|
26
|
+
'meta-llama/',
|
|
27
|
+
'mistral/',
|
|
28
|
+
'cohere/',
|
|
29
|
+
'deepseek/',
|
|
30
|
+
'groq/',
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* True when a value looks like a concrete provider model id (host-resolved / post profile resolution).
|
|
34
|
+
*/
|
|
35
|
+
export function looksLikeConcreteModelId(value) {
|
|
36
|
+
const trimmed = value.trim();
|
|
37
|
+
if (trimmed === '')
|
|
38
|
+
return false;
|
|
39
|
+
if (trimmed.includes('/'))
|
|
40
|
+
return true;
|
|
41
|
+
const lower = trimmed.toLowerCase();
|
|
42
|
+
return CONCRETE_MODEL_PREFIXES.some((p) => lower.startsWith(p));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* True when a string is a valid graph-authored AI profile name (not a provider model id).
|
|
46
|
+
*/
|
|
47
|
+
export function isGraphAiProfileName(value) {
|
|
48
|
+
if (typeof value !== 'string')
|
|
49
|
+
return false;
|
|
50
|
+
const trimmed = value.trim();
|
|
51
|
+
if (trimmed === '')
|
|
52
|
+
return false;
|
|
53
|
+
return !looksLikeConcreteModelId(trimmed);
|
|
54
|
+
}
|
|
55
|
+
export function assertGraphAiProfileNameString(value, path, context) {
|
|
56
|
+
if (!isGraphAiProfileName(value)) {
|
|
57
|
+
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_MODEL_CONFIG, `${path} must be an AI profile name (non-empty string without "/" or provider prefixes); got ${JSON.stringify(value)}`, context);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function formatResolvedModelId(provider, modelId) {
|
|
61
|
+
const mid = modelId.trim();
|
|
62
|
+
if (mid.includes('/'))
|
|
63
|
+
return mid;
|
|
64
|
+
const prov = provider.trim();
|
|
65
|
+
return prov ? `${prov}/${mid}` : mid;
|
|
66
|
+
}
|
|
67
|
+
async function resolveModelSlot(slot, context) {
|
|
68
|
+
const trimmed = slot.trim();
|
|
69
|
+
if (trimmed === '') {
|
|
70
|
+
throw new ExellixGraphError(ExellixGraphErrorCode.MODEL_CONFIG_INCOMPLETE, 'modelConfig slot must be a non-empty string', context);
|
|
71
|
+
}
|
|
72
|
+
if (looksLikeConcreteModelId(trimmed))
|
|
73
|
+
return trimmed;
|
|
74
|
+
try {
|
|
75
|
+
const resolved = await resolveAIProfile(trimmed, { source: 'bundled' });
|
|
76
|
+
return formatResolvedModelId(resolved.provider, resolved.modelId);
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
const code = err?.code ?? 'UNKNOWN_PROFILE';
|
|
80
|
+
throw new ExellixGraphError(ExellixGraphErrorCode.MODEL_PROFILE_UNRESOLVED, `Unresolved AI profile "${trimmed}" (${String(code)})`, { ...context, profile: trimmed });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Resolves graph/runtime {@link GraphAiModelConfig} to concrete provider model ids.
|
|
85
|
+
* Profile names resolve via bundled `@x12i/ai-profiles`; concrete ids pass through unchanged.
|
|
86
|
+
*/
|
|
87
|
+
export async function resolveGraphAiModelConfig(modelConfig, context) {
|
|
88
|
+
if (!isGraphAiModelConfig(modelConfig)) {
|
|
89
|
+
throw new ExellixGraphError(ExellixGraphErrorCode.MODEL_CONFIG_INCOMPLETE, 'modelConfig must be { preActionModel: string, skillModel: string, postActionModel: string } with non-empty values', context);
|
|
90
|
+
}
|
|
91
|
+
const [preActionModel, skillModel, postActionModel] = await Promise.all([
|
|
92
|
+
resolveModelSlot(modelConfig.preActionModel, context),
|
|
93
|
+
resolveModelSlot(modelConfig.skillModel, context),
|
|
94
|
+
resolveModelSlot(modelConfig.postActionModel, context),
|
|
95
|
+
]);
|
|
96
|
+
return { preActionModel, skillModel, postActionModel };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Maps resolved three-phase graph config to ai-tasks slot pair for a single runTask phase.
|
|
100
|
+
*/
|
|
101
|
+
export function toRunTaskModelConfigForPhase(config, phase) {
|
|
102
|
+
switch (phase) {
|
|
103
|
+
case 'pre':
|
|
104
|
+
return {
|
|
105
|
+
xynthesisModel: config.preActionModel,
|
|
106
|
+
skillModel: config.preActionModel,
|
|
107
|
+
};
|
|
108
|
+
case 'main':
|
|
109
|
+
return {
|
|
110
|
+
xynthesisModel: config.preActionModel,
|
|
111
|
+
skillModel: config.skillModel,
|
|
112
|
+
};
|
|
113
|
+
case 'post':
|
|
114
|
+
return {
|
|
115
|
+
xynthesisModel: config.postActionModel,
|
|
116
|
+
skillModel: config.postActionModel,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -1,46 +1,10 @@
|
|
|
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
1
|
/**
|
|
6
|
-
*
|
|
2
|
+
* @deprecated Legacy alias-map exports removed in 7.1. Use {@link graphAiModelConfig.js} and `@x12i/ai-profiles`.
|
|
7
3
|
*/
|
|
8
|
-
export
|
|
9
|
-
/**
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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;
|
|
4
|
+
export { DEFAULT_GRAPH_AI_MODEL_IDS, DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, looksLikeConcreteModelId, isGraphAiProfileName, assertGraphAiProfileNameString, resolveGraphAiModelConfig, toRunTaskModelConfigForPhase, type RunTaskModelConfigWire, type EngineModelPhase, type GraphAiModelConfigResolveContext, } from './graphAiModelConfig.js';
|
|
5
|
+
/** @deprecated Use {@link isGraphAiProfileName}. */
|
|
6
|
+
export { isGraphAiProfileName as isModelAliasString } from './graphAiModelConfig.js';
|
|
7
|
+
/** @deprecated Use {@link assertGraphAiProfileNameString}. */
|
|
8
|
+
export { assertGraphAiProfileNameString as assertGraphModelAliasString } from './graphAiModelConfig.js';
|
|
9
|
+
/** @deprecated Use {@link resolveGraphAiModelConfig}. */
|
|
10
|
+
export { resolveGraphAiModelConfig as resolveGraphAiModelConfigAliases } from './graphAiModelConfig.js';
|
|
@@ -1,129 +1,10 @@
|
|
|
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
1
|
/**
|
|
12
|
-
*
|
|
2
|
+
* @deprecated Legacy alias-map exports removed in 7.1. Use {@link graphAiModelConfig.js} and `@x12i/ai-profiles`.
|
|
13
3
|
*/
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
}
|
|
4
|
+
export { DEFAULT_GRAPH_AI_MODEL_IDS, DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, looksLikeConcreteModelId, isGraphAiProfileName, assertGraphAiProfileNameString, resolveGraphAiModelConfig, toRunTaskModelConfigForPhase, } from './graphAiModelConfig.js';
|
|
5
|
+
/** @deprecated Use {@link isGraphAiProfileName}. */
|
|
6
|
+
export { isGraphAiProfileName as isModelAliasString } from './graphAiModelConfig.js';
|
|
7
|
+
/** @deprecated Use {@link assertGraphAiProfileNameString}. */
|
|
8
|
+
export { assertGraphAiProfileNameString as assertGraphModelAliasString } from './graphAiModelConfig.js';
|
|
9
|
+
/** @deprecated Use {@link resolveGraphAiModelConfig}. */
|
|
10
|
+
export { resolveGraphAiModelConfig as resolveGraphAiModelConfigAliases } from './graphAiModelConfig.js';
|
|
@@ -3,13 +3,16 @@ export function isGraphAiModelConfig(value) {
|
|
|
3
3
|
return false;
|
|
4
4
|
const o = value;
|
|
5
5
|
const keys = Object.keys(o);
|
|
6
|
-
return (keys.length ===
|
|
7
|
-
keys.includes('
|
|
6
|
+
return (keys.length === 3 &&
|
|
7
|
+
keys.includes('preActionModel') &&
|
|
8
8
|
keys.includes('skillModel') &&
|
|
9
|
-
|
|
10
|
-
o.
|
|
9
|
+
keys.includes('postActionModel') &&
|
|
10
|
+
typeof o.preActionModel === 'string' &&
|
|
11
|
+
o.preActionModel.trim() !== '' &&
|
|
11
12
|
typeof o.skillModel === 'string' &&
|
|
12
|
-
o.skillModel.trim() !== ''
|
|
13
|
+
o.skillModel.trim() !== '' &&
|
|
14
|
+
typeof o.postActionModel === 'string' &&
|
|
15
|
+
o.postActionModel.trim() !== '');
|
|
13
16
|
}
|
|
14
17
|
export function isEmptyConditionWhen(when) {
|
|
15
18
|
if (when == null)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RunxClient } from '@x12i/runx';
|
|
2
|
-
import type { GraphAiModelConfig,
|
|
2
|
+
import type { GraphAiModelConfig, ModelConfigSelection, TaskNodeRuntimeObject } from '../types/refs.js';
|
|
3
3
|
import { conditionWhenSignature, isEmptyConditionWhen } from './modelConfigSelection.js';
|
|
4
4
|
import { type TaskNodeConditionEvalContext } from './taskNodeConditionsEvaluation.js';
|
|
5
5
|
export type ResolveModelConfigForNodeArgs = {
|
|
@@ -7,7 +7,6 @@ export type ResolveModelConfigForNodeArgs = {
|
|
|
7
7
|
nodeModelConfig?: unknown;
|
|
8
8
|
runtimeModelConfig?: ModelConfigSelection;
|
|
9
9
|
graphModelConfig?: ModelConfigSelection;
|
|
10
|
-
runtimeAliasConfig?: GraphModelAliasConfig;
|
|
11
10
|
conditionCtx: TaskNodeConditionEvalContext;
|
|
12
11
|
executionInput: Record<string, unknown>;
|
|
13
12
|
runx?: RunxClient;
|
|
@@ -16,9 +15,11 @@ export type ResolveModelConfigForNodeArgs = {
|
|
|
16
15
|
jobId?: string;
|
|
17
16
|
};
|
|
18
17
|
/**
|
|
19
|
-
* Resolves effective model config for a task node (precedence + conditional cases
|
|
20
|
-
* Returns
|
|
18
|
+
* Resolves effective model config for a task node (precedence + conditional cases).
|
|
19
|
+
* Returns profile names (or host-resolved concrete ids from runtime overrides) for
|
|
20
|
+
* {@link toRunTaskModelConfigForPhase} / ai-tasks wire — not pre-resolved provider ids.
|
|
21
|
+
* Use {@link resolveGraphAiModelConfig} when concrete ids are required (observability, docs).
|
|
21
22
|
*/
|
|
22
23
|
export declare function resolveModelConfigForNode(args: ResolveModelConfigForNodeArgs): Promise<GraphAiModelConfig>;
|
|
23
24
|
export { conditionWhenSignature, isEmptyConditionWhen };
|
|
24
|
-
export {
|
|
25
|
+
export { resolveGraphAiModelConfig, toRunTaskModelConfigForPhase, DEFAULT_GRAPH_AI_MODEL_IDS, DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, } from './graphAiModelConfig.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { conditionWhenSignature, isEmptyConditionWhen, isGraphAiModelConfig, isModelConfigSelection, } from './modelConfigSelection.js';
|
|
2
2
|
import { evaluateConditionWhen } from './taskNodeConditionsEvaluation.js';
|
|
3
|
-
import {
|
|
3
|
+
import { DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, } 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)
|
|
@@ -34,24 +34,24 @@ async function resolveTier(value, ctx, executionInput, runx) {
|
|
|
34
34
|
return undefined;
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
|
-
* Resolves effective model config for a task node (precedence + conditional cases
|
|
38
|
-
* Returns
|
|
37
|
+
* Resolves effective model config for a task node (precedence + conditional cases).
|
|
38
|
+
* Returns profile names (or host-resolved concrete ids from runtime overrides) for
|
|
39
|
+
* {@link toRunTaskModelConfigForPhase} / ai-tasks wire — not pre-resolved provider ids.
|
|
40
|
+
* Use {@link resolveGraphAiModelConfig} when concrete ids are required (observability, docs).
|
|
39
41
|
*/
|
|
40
42
|
export async function resolveModelConfigForNode(args) {
|
|
41
|
-
const aliasConfig = mergeAliasConfigs(args.runtimeAliasConfig, args.runtimeNodeConfig?.aliasConfig);
|
|
42
|
-
const resolveCtx = { graphId: args.graphId, nodeId: args.nodeId, jobId: args.jobId };
|
|
43
43
|
const staticHost = args.runtimeNodeConfig?.modelConfig;
|
|
44
44
|
if (isGraphAiModelConfig(staticHost)) {
|
|
45
|
-
return
|
|
45
|
+
return staticHost;
|
|
46
46
|
}
|
|
47
47
|
const tiers = [args.nodeModelConfig, args.runtimeModelConfig, args.graphModelConfig];
|
|
48
48
|
for (const tier of tiers) {
|
|
49
49
|
const selected = await resolveTier(tier, args.conditionCtx, args.executionInput, args.runx);
|
|
50
50
|
if (selected != null) {
|
|
51
|
-
return
|
|
51
|
+
return selected;
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
-
return
|
|
54
|
+
return DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG;
|
|
55
55
|
}
|
|
56
56
|
export { conditionWhenSignature, isEmptyConditionWhen };
|
|
57
|
-
export {
|
|
57
|
+
export { resolveGraphAiModelConfig, toRunTaskModelConfigForPhase, DEFAULT_GRAPH_AI_MODEL_IDS, DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, } from './graphAiModelConfig.js';
|
|
@@ -4,6 +4,11 @@ import type { TaskNode } from '../types/refs.js';
|
|
|
4
4
|
import type { StepAttemptRecord } from '../types/results.js';
|
|
5
5
|
export type ResolvedStepRetryPolicy = Required<Pick<StepRetryPolicy, 'maxAttempts' | 'retryOnTimeout' | 'retryOnTokenLimit' | 'tokenBumpMultiplier' | 'tokenBumpCap' | 'baseMaxTokensFallback'>>;
|
|
6
6
|
export declare function resolveStepRetryPolicy(graphPolicy: StepRetryPolicy | undefined, node: Pick<TaskNode, 'taskConfiguration'> | undefined): ResolvedStepRetryPolicy;
|
|
7
|
+
/**
|
|
8
|
+
* Reads the effective completion cap from outbound `llmCall`.
|
|
9
|
+
* ai-tasks v8 canonical field is `maxTokensCap`; legacy `maxTokens` / `max_tokens` still honored.
|
|
10
|
+
*/
|
|
11
|
+
export declare function readMaxTokensFromRunTaskRequest(req: RunTaskRequest): number | undefined;
|
|
7
12
|
export type RunTaskWithRetryResult = {
|
|
8
13
|
response: RunTaskResponse;
|
|
9
14
|
attempts: StepAttemptRecord[];
|
|
@@ -25,9 +25,14 @@ export function resolveStepRetryPolicy(graphPolicy, node) {
|
|
|
25
25
|
function readNum(v) {
|
|
26
26
|
return typeof v === 'number' && Number.isFinite(v) ? v : undefined;
|
|
27
27
|
}
|
|
28
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Reads the effective completion cap from outbound `llmCall`.
|
|
30
|
+
* ai-tasks v8 canonical field is `maxTokensCap`; legacy `maxTokens` / `max_tokens` still honored.
|
|
31
|
+
*/
|
|
32
|
+
export function readMaxTokensFromRunTaskRequest(req) {
|
|
29
33
|
const llm = req.llmCall;
|
|
30
|
-
return (readNum(llm?.
|
|
34
|
+
return (readNum(llm?.maxTokensCap) ??
|
|
35
|
+
readNum(llm?.maxTokens) ??
|
|
31
36
|
readNum(llm?.max_tokens));
|
|
32
37
|
}
|
|
33
38
|
function shallowCloneRunTaskRequest(req) {
|
|
@@ -45,12 +50,13 @@ function shallowCloneRunTaskRequest(req) {
|
|
|
45
50
|
};
|
|
46
51
|
}
|
|
47
52
|
function applyTokenBump(workingReq, policy) {
|
|
48
|
-
const current =
|
|
53
|
+
const current = readMaxTokensFromRunTaskRequest(workingReq) ?? policy.baseMaxTokensFallback;
|
|
49
54
|
const after = Math.min(policy.tokenBumpCap, Math.floor(current * policy.tokenBumpMultiplier));
|
|
50
|
-
workingReq.llmCall
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
const bumped = workingReq.llmCall != null && typeof workingReq.llmCall === 'object' && !Array.isArray(workingReq.llmCall)
|
|
56
|
+
? { ...workingReq.llmCall }
|
|
57
|
+
: {};
|
|
58
|
+
bumped.maxTokensCap = after;
|
|
59
|
+
workingReq.llmCall = bumped;
|
|
54
60
|
return { before: current, after };
|
|
55
61
|
}
|
|
56
62
|
function classifyThrownError(err) {
|
|
@@ -134,7 +140,7 @@ export async function runRunTaskWithRetry(args) {
|
|
|
134
140
|
}
|
|
135
141
|
const endedAt = Date.now();
|
|
136
142
|
const durationMs = endedAt - startedAt;
|
|
137
|
-
const maxTok =
|
|
143
|
+
const maxTok = readMaxTokensFromRunTaskRequest(workingReq);
|
|
138
144
|
if (thrown != null) {
|
|
139
145
|
const c = classifyThrownError(thrown);
|
|
140
146
|
attempts.push({
|
|
@@ -171,7 +177,7 @@ export async function runRunTaskWithRetry(args) {
|
|
|
171
177
|
nodeId,
|
|
172
178
|
skillKey,
|
|
173
179
|
level: 'info',
|
|
174
|
-
message: `step retry ${attemptIndex + 2}/${policy.maxAttempts}: token-limit bump
|
|
180
|
+
message: `step retry ${attemptIndex + 2}/${policy.maxAttempts}: token-limit bump maxTokensCap ${before} -> ${after}`,
|
|
175
181
|
data: { classification: c, maxTokensBefore: before, maxTokensAfter: after, attempt: attemptIndex + 1 },
|
|
176
182
|
});
|
|
177
183
|
}
|
|
@@ -226,7 +232,7 @@ export async function runRunTaskWithRetry(args) {
|
|
|
226
232
|
nodeId,
|
|
227
233
|
skillKey,
|
|
228
234
|
level: 'info',
|
|
229
|
-
message: `step retry ${attemptIndex + 2}/${policy.maxAttempts}: token-limit bump
|
|
235
|
+
message: `step retry ${attemptIndex + 2}/${policy.maxAttempts}: token-limit bump maxTokensCap ${before} -> ${after}`,
|
|
230
236
|
data: { classification: c, maxTokensBefore: before, maxTokensAfter: after, attempt: attemptIndex + 1 },
|
|
231
237
|
});
|
|
232
238
|
}
|
|
@@ -30,9 +30,9 @@ 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
|
-
/**
|
|
33
|
+
/** Resolved or profile-name selection; resolved via `@x12i/ai-profiles` when building the request. */
|
|
34
34
|
modelConfig?: GraphAiModelConfig;
|
|
35
|
-
/**
|
|
35
|
+
/** @deprecated Unused since 7.1. */
|
|
36
36
|
aliasConfig?: GraphModelAliasConfig;
|
|
37
37
|
llmCall?: Record<string, unknown>;
|
|
38
38
|
/** Runtime default merged with per-node `taskConfiguration.llmCall`. */
|
|
@@ -50,7 +50,7 @@ export type BuildTaskNodeRunTaskRequestArgs = {
|
|
|
50
50
|
* Materialize the outbound `RunTaskRequest` graph-engine would send for a task node
|
|
51
51
|
* (mirrors `executeNode` request assembly; does not run PRE strategy phases or `runTask`).
|
|
52
52
|
*/
|
|
53
|
-
export declare function buildTaskNodeRunTaskRequest(args: BuildTaskNodeRunTaskRequestArgs): BuildTaskNodeRunTaskRequestResult
|
|
53
|
+
export declare function buildTaskNodeRunTaskRequest(args: BuildTaskNodeRunTaskRequestArgs): Promise<BuildTaskNodeRunTaskRequestResult>;
|
|
54
54
|
export type ValidateTaskNodeRunTaskConfigArgs = BuildTaskNodeRunTaskRequestArgs;
|
|
55
55
|
export type ValidateTaskNodeRunTaskConfigResult = {
|
|
56
56
|
skipped: true;
|
|
@@ -62,7 +62,7 @@ export type ValidateTaskNodeRunTaskConfigResult = {
|
|
|
62
62
|
request: RunTaskRequest;
|
|
63
63
|
} & RunTaskValidationResult);
|
|
64
64
|
/** Static ai-tasks validation on the graph-built {@link RunTaskRequest}. */
|
|
65
|
-
export declare function validateTaskNodeRunTaskConfig(args: ValidateTaskNodeRunTaskConfigArgs): ValidateTaskNodeRunTaskConfigResult
|
|
65
|
+
export declare function validateTaskNodeRunTaskConfig(args: ValidateTaskNodeRunTaskConfigArgs): Promise<ValidateTaskNodeRunTaskConfigResult>;
|
|
66
66
|
export type ValidateTaskNodeRunTaskInvokeArgs = BuildTaskNodeRunTaskRequestArgs & Omit<ValidateRunTaskInvokeParams, 'request'>;
|
|
67
67
|
export type ValidateTaskNodeRunTaskInvokeResult = {
|
|
68
68
|
skipped: true;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { analyzeRunTaskRequest, validateRunTaskConfig, validateRunTaskInvoke, } from '@exellix/ai-tasks';
|
|
2
|
-
import {
|
|
2
|
+
import { toRunTaskModelConfigForPhase } from './graphAiModelConfig.js';
|
|
3
3
|
import { resolveTaskKey } from './resolveTaskKey.js';
|
|
4
4
|
import { isLocalSkillKey } from './localSkills/index.js';
|
|
5
5
|
import { buildAiTasksRunTaskRequest, extractRunTaskStrategyOverrides, } from './buildAiTasksRunTaskRequest.js';
|
|
@@ -35,7 +35,7 @@ function mergeKnowledgePatchIntoRunTaskMemory(memory, patch) {
|
|
|
35
35
|
* Materialize the outbound `RunTaskRequest` graph-engine would send for a task node
|
|
36
36
|
* (mirrors `executeNode` request assembly; does not run PRE strategy phases or `runTask`).
|
|
37
37
|
*/
|
|
38
|
-
export function buildTaskNodeRunTaskRequest(args) {
|
|
38
|
+
export async function buildTaskNodeRunTaskRequest(args) {
|
|
39
39
|
const node = args.node;
|
|
40
40
|
if (node.type === 'finalizer') {
|
|
41
41
|
return { runnable: false, skillKey: 'finalizer', reason: 'finalizer' };
|
|
@@ -122,13 +122,7 @@ export function buildTaskNodeRunTaskRequest(args) {
|
|
|
122
122
|
});
|
|
123
123
|
const metaStrats = node.taskConfiguration?.executionStrategies;
|
|
124
124
|
const ov = node.taskConfiguration?.aiTasksOutputValidation;
|
|
125
|
-
const
|
|
126
|
-
? resolveGraphAiModelConfigAliases(args.modelConfig, args.aliasConfig, {
|
|
127
|
-
graphId: args.graphId,
|
|
128
|
-
nodeId: String(node.id),
|
|
129
|
-
jobId: lifecycleJobId,
|
|
130
|
-
})
|
|
131
|
-
: args.modelConfig;
|
|
125
|
+
const mainRunTaskModelConfig = args.modelConfig != null ? toRunTaskModelConfigForPhase(args.modelConfig, 'main') : undefined;
|
|
132
126
|
const request = buildAiTasksRunTaskRequest({
|
|
133
127
|
skillKey,
|
|
134
128
|
job: args.job,
|
|
@@ -153,7 +147,7 @@ export function buildTaskNodeRunTaskRequest(args) {
|
|
|
153
147
|
? ov
|
|
154
148
|
: undefined,
|
|
155
149
|
diagnostics: args.runTaskDiagnostics,
|
|
156
|
-
modelConfig:
|
|
150
|
+
modelConfig: mainRunTaskModelConfig,
|
|
157
151
|
llmCall: effectiveLlmCall,
|
|
158
152
|
identity: buildRunTaskIdentityEnvelope({
|
|
159
153
|
base: args.runTaskIdentity,
|
|
@@ -173,8 +167,8 @@ export function buildTaskNodeRunTaskRequest(args) {
|
|
|
173
167
|
return { runnable: true, skillKey, request };
|
|
174
168
|
}
|
|
175
169
|
/** Static ai-tasks validation on the graph-built {@link RunTaskRequest}. */
|
|
176
|
-
export function validateTaskNodeRunTaskConfig(args) {
|
|
177
|
-
const built = buildTaskNodeRunTaskRequest(args);
|
|
170
|
+
export async function validateTaskNodeRunTaskConfig(args) {
|
|
171
|
+
const built = await buildTaskNodeRunTaskRequest(args);
|
|
178
172
|
if (!built.runnable) {
|
|
179
173
|
return { skipped: true, skillKey: built.skillKey, reason: built.reason };
|
|
180
174
|
}
|
|
@@ -183,7 +177,7 @@ export function validateTaskNodeRunTaskConfig(args) {
|
|
|
183
177
|
}
|
|
184
178
|
/** Config + payload + template path validation via ai-tasks {@link validateRunTaskInvoke}. */
|
|
185
179
|
export async function validateTaskNodeRunTaskInvoke(args) {
|
|
186
|
-
const built = buildTaskNodeRunTaskRequest(args);
|
|
180
|
+
const built = await buildTaskNodeRunTaskRequest(args);
|
|
187
181
|
if (!built.runnable) {
|
|
188
182
|
return { skipped: true, skillKey: built.skillKey, reason: built.reason };
|
|
189
183
|
}
|
|
@@ -203,7 +197,7 @@ export async function validateTaskNodeRunTaskInvoke(args) {
|
|
|
203
197
|
}
|
|
204
198
|
/** Catalox-backed skill request analysis on the graph-built {@link RunTaskRequest}. */
|
|
205
199
|
export async function analyzeTaskNodeRunTaskRequest(args) {
|
|
206
|
-
const built = buildTaskNodeRunTaskRequest(args);
|
|
200
|
+
const built = await buildTaskNodeRunTaskRequest(args);
|
|
207
201
|
if (!built.runnable) {
|
|
208
202
|
return { skipped: true, skillKey: built.skillKey, reason: built.reason };
|
|
209
203
|
}
|
|
@@ -4,7 +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 {
|
|
7
|
+
import { assertGraphAiProfileNameString } from './graphAiModelConfig.js';
|
|
8
8
|
/** Top-level keys permitted on a canonical exellix-graph executable graph JSON document. */
|
|
9
9
|
export const CANONICAL_GRAPH_TOP_LEVEL_KEYS = [
|
|
10
10
|
'id',
|
|
@@ -279,7 +279,7 @@ function assertModelConfigSelection(value, path, context) {
|
|
|
279
279
|
? ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE
|
|
280
280
|
: ExellixGraphErrorCode.NON_CANONICAL_GRAPH_DOCUMENT;
|
|
281
281
|
if (isGraphAiModelConfig(value)) {
|
|
282
|
-
throw new ExellixGraphError(code, `${path}: flat
|
|
282
|
+
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);
|
|
283
283
|
}
|
|
284
284
|
if (!isModelConfigSelection(value)) {
|
|
285
285
|
throw new ExellixGraphError(code, `${path} must be { cases: ModelConfigCase[] } where each case has modelConfig and optional when.`, context);
|
|
@@ -297,10 +297,11 @@ function assertModelConfigSelection(value, path, context) {
|
|
|
297
297
|
throw new ExellixGraphError(code, `${casePath} must be a plain object.`, context);
|
|
298
298
|
}
|
|
299
299
|
if (!isGraphAiModelConfig(c.modelConfig)) {
|
|
300
|
-
throw new ExellixGraphError(code, `${casePath}.modelConfig must be {
|
|
300
|
+
throw new ExellixGraphError(code, `${casePath}.modelConfig must be { preActionModel: string, skillModel: string, postActionModel: string } with non-empty AI profile names.`, context);
|
|
301
301
|
}
|
|
302
|
-
|
|
303
|
-
|
|
302
|
+
assertGraphAiProfileNameString(c.modelConfig.preActionModel, `${casePath}.modelConfig.preActionModel`, context);
|
|
303
|
+
assertGraphAiProfileNameString(c.modelConfig.skillModel, `${casePath}.modelConfig.skillModel`, context);
|
|
304
|
+
assertGraphAiProfileNameString(c.modelConfig.postActionModel, `${casePath}.modelConfig.postActionModel`, context);
|
|
304
305
|
if (isEmptyConditionWhen(c.when)) {
|
|
305
306
|
continue;
|
|
306
307
|
}
|
|
@@ -321,10 +322,8 @@ function assertOptionalRuntimeFlatModelConfig(value, path, context) {
|
|
|
321
322
|
if (value === undefined)
|
|
322
323
|
return;
|
|
323
324
|
if (!isGraphAiModelConfig(value)) {
|
|
324
|
-
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `${path} must be exactly {
|
|
325
|
+
throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `${path} must be exactly { preActionModel: string, skillModel: string, postActionModel: string } (runtime host override may use concrete provider ids or profile names).`, context);
|
|
325
326
|
}
|
|
326
|
-
assertGraphModelAliasString(value.xynthesisModel, `${path}.xynthesisModel`, context);
|
|
327
|
-
assertGraphModelAliasString(value.skillModel, `${path}.skillModel`, context);
|
|
328
327
|
}
|
|
329
328
|
function assertContextualKnowledgeScope(scope, nodeId, context) {
|
|
330
329
|
if (scope === undefined)
|
|
@@ -37,8 +37,8 @@ export interface StepRetryPolicy {
|
|
|
37
37
|
/** Upper bound for `maxTokens` after bump (default **16000**). */
|
|
38
38
|
tokenBumpCap?: number;
|
|
39
39
|
/**
|
|
40
|
-
* Used when
|
|
41
|
-
* (default **2000**).
|
|
40
|
+
* Used when `llmCall.maxTokensCap` (or legacy `maxTokens` / `max_tokens`) is unset on the outbound request
|
|
41
|
+
* before the first token-limit retry bump (default **2000**).
|
|
42
42
|
*/
|
|
43
43
|
baseMaxTokensFallback?: number;
|
|
44
44
|
}
|
package/dist/src/types/refs.d.ts
CHANGED
|
@@ -13,18 +13,23 @@ export type TaskNodeExecutionPipelineStep = {
|
|
|
13
13
|
* Type references for graph execution
|
|
14
14
|
*/
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* Three-phase AI model selection on graph model / node `modelConfig`.
|
|
17
|
+
* Values are `@x12i/ai-profiles` profile names (`cheap`, `balanced`, `deep`, …) in graph JSON,
|
|
18
|
+
* or host-resolved concrete provider ids on runtime overrides.
|
|
18
19
|
*/
|
|
19
20
|
export type GraphAiModelConfig = {
|
|
20
|
-
|
|
21
|
+
preActionModel: string;
|
|
21
22
|
skillModel: string;
|
|
23
|
+
postActionModel: string;
|
|
22
24
|
};
|
|
23
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* @deprecated Legacy alias map — no longer required for execute. Hosts may omit `runtime.aliasConfig`.
|
|
27
|
+
*/
|
|
24
28
|
export type GraphModelAliasConfig = Record<string, string>;
|
|
25
29
|
/** Runtime overrides for a single task node, stored at GraphRuntimeObject.nodes[nodeId]. */
|
|
26
30
|
export type TaskNodeRuntimeObject = {
|
|
27
31
|
modelConfig?: GraphAiModelConfig;
|
|
32
|
+
/** @deprecated Unused since 7.1 — profile resolution uses `@x12i/ai-profiles`. */
|
|
28
33
|
aliasConfig?: GraphModelAliasConfig;
|
|
29
34
|
};
|
|
30
35
|
/** Backwards-readable alias for per-node runtime overrides keyed by task node id. */
|
|
@@ -82,7 +82,11 @@ export class RealTasksClient {
|
|
|
82
82
|
if (process.env.DEBUG_AI_PROVIDER === 'true') {
|
|
83
83
|
const mc = aiTasksRequest.modelConfig;
|
|
84
84
|
const effective = mc
|
|
85
|
-
? {
|
|
85
|
+
? {
|
|
86
|
+
preActionModel: mc.preActionModel ?? mc.xynthesisModel,
|
|
87
|
+
skillModel: mc.skillModel,
|
|
88
|
+
postActionModel: mc.postActionModel ?? mc.skillModel,
|
|
89
|
+
}
|
|
86
90
|
: aiTasksRequest.config
|
|
87
91
|
? {
|
|
88
92
|
provider: aiTasksRequest.config.provider,
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import type { Graph } from '../src/types/refs.js';
|
|
2
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
3
|
type CreateOpts = Parameters<typeof createExellixGraphRuntime>[0];
|
|
10
4
|
/**
|
|
11
|
-
* Testkit runtime
|
|
5
|
+
* Testkit runtime for graphs using `@x12i/ai-profiles` (no `runtime.aliasConfig` required).
|
|
12
6
|
*/
|
|
13
7
|
export declare function createTestExellixGraphRuntime(opts: CreateOpts): ReturnType<typeof createExellixGraphRuntime>;
|
|
8
|
+
/** @deprecated Alias map test helper removed in 7.1 — profiles resolve via `@x12i/ai-profiles`. */
|
|
9
|
+
export declare function buildTestAliasConfig(_graph: Pick<Graph, 'modelConfig' | 'nodes'>, runtimePartial?: Pick<GraphRuntimeObject, 'modelConfig' | 'nodes'> & {
|
|
10
|
+
aliasConfig?: Record<string, string>;
|
|
11
|
+
}): Record<string, string>;
|
|
12
|
+
/** @deprecated No-op — `runtime.aliasConfig` is not required in 7.1. */
|
|
13
|
+
export declare function withTestAliasRuntime(_graph: Pick<Graph, 'id' | 'modelConfig' | 'nodes'>, runtime: Partial<GraphRuntimeObject> & Pick<GraphRuntimeObject, 'jobId' | 'job'>): GraphRuntimeObject;
|
|
14
14
|
export {};
|
|
@@ -1,24 +1,6 @@
|
|
|
1
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
2
|
/**
|
|
21
|
-
* Testkit runtime
|
|
3
|
+
* Testkit runtime for graphs using `@x12i/ai-profiles` (no `runtime.aliasConfig` required).
|
|
22
4
|
*/
|
|
23
5
|
export function createTestExellixGraphRuntime(opts) {
|
|
24
6
|
const runtime = createExellixGraphRuntime(opts);
|
|
@@ -33,8 +15,16 @@ export function createTestExellixGraphRuntime(opts) {
|
|
|
33
15
|
: { agentId: 'test-agent', jobType: 'unit-test' };
|
|
34
16
|
return executeGraph({
|
|
35
17
|
...input,
|
|
36
|
-
runtime:
|
|
18
|
+
runtime: { ...base, jobId, job },
|
|
37
19
|
});
|
|
38
20
|
};
|
|
39
21
|
return runtime;
|
|
40
22
|
}
|
|
23
|
+
/** @deprecated Alias map test helper removed in 7.1 — profiles resolve via `@x12i/ai-profiles`. */
|
|
24
|
+
export function buildTestAliasConfig(_graph, runtimePartial) {
|
|
25
|
+
return { ...(runtimePartial?.aliasConfig ?? {}) };
|
|
26
|
+
}
|
|
27
|
+
/** @deprecated No-op — `runtime.aliasConfig` is not required in 7.1. */
|
|
28
|
+
export function withTestAliasRuntime(_graph, runtime) {
|
|
29
|
+
return runtime;
|
|
30
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exellix/graph-engine",
|
|
3
|
-
"version": "7.0
|
|
3
|
+
"version": "7.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Graph executor SDK",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
20
|
"dist/**",
|
|
21
|
+
"scripts/patch-ai-tasks-xynthesis-export.mjs",
|
|
21
22
|
".env.example",
|
|
22
23
|
"CHANGELOG.md",
|
|
23
24
|
"README.md"
|
|
@@ -25,11 +26,12 @@
|
|
|
25
26
|
"scripts": {
|
|
26
27
|
"prebuild": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true, maxRetries: 10, retryDelay: 100 })\"",
|
|
27
28
|
"build": "tsc",
|
|
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
|
+
"test": "npm run build && npm run check:no-legacy && node scripts/patch-ai-tasks-xynthesis-export.mjs && node --test --test-force-exit tests/model-alias-contract.test.ts tests/reports-fixtures-pre-synthesis.test.ts tests/passthrough-parity.test.ts tests/step-retry-llm-call.test.ts",
|
|
30
|
+
"test:full": "npm run build && npm run check:no-legacy && node scripts/patch-ai-tasks-xynthesis-export.mjs && 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 tests/step-retry-llm-call.test.ts",
|
|
30
31
|
"run:pre-synthesis": "npm run build && node --env-file=.env scripts/run-pre-synthesis-graph.mjs",
|
|
31
32
|
"check:no-legacy": "node scripts/check-no-legacy.mjs",
|
|
32
33
|
"lint": "eslint src testkit --ext .ts",
|
|
34
|
+
"postinstall": "node scripts/patch-ai-tasks-xynthesis-export.mjs",
|
|
33
35
|
"prepublishOnly": "npm run build"
|
|
34
36
|
},
|
|
35
37
|
"keywords": [
|
|
@@ -49,11 +51,12 @@
|
|
|
49
51
|
"access": "public"
|
|
50
52
|
},
|
|
51
53
|
"dependencies": {
|
|
52
|
-
"@exellix/ai-tasks": "^8.0.
|
|
53
|
-
"@x12i/activix": "^
|
|
54
|
+
"@exellix/ai-tasks": "^8.0.7",
|
|
55
|
+
"@x12i/activix": "^8.0.0",
|
|
56
|
+
"@x12i/ai-profiles": "^1.2.0",
|
|
54
57
|
"@x12i/catalox": "^5.1.1",
|
|
55
58
|
"@x12i/env": "^4.0.1",
|
|
56
|
-
"@x12i/funcx": "^4.0
|
|
59
|
+
"@x12i/funcx": "^4.2.0",
|
|
57
60
|
"@x12i/graphenix": "^2.5.0",
|
|
58
61
|
"@x12i/graphenix-format": "^2.0.0",
|
|
59
62
|
"@x12i/logxer": "^4.3.6",
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workaround: @exellix/ai-tasks@8.0.7 re-exports MODEL_CAPABILITIES from @exellix/xynthesis,
|
|
3
|
+
* but xynthesis 4.0.x removed that export (capabilities come from getModelCapabilities / ai-profiles).
|
|
4
|
+
* Remove the broken re-export so graph-engine and tests can import @exellix/ai-tasks.
|
|
5
|
+
*/
|
|
6
|
+
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
|
|
9
|
+
const aiTasksRoot = join(process.cwd(), 'node_modules', '@exellix', 'ai-tasks', 'dist');
|
|
10
|
+
const broken =
|
|
11
|
+
'export { buildInvokeAttemptSummary, ACTION_OUTPUT_DEFAULTS, MODEL_CAPABILITIES } from "@exellix/xynthesis";';
|
|
12
|
+
const fixed =
|
|
13
|
+
'export { buildInvokeAttemptSummary, ACTION_OUTPUT_DEFAULTS } from "@exellix/xynthesis";';
|
|
14
|
+
|
|
15
|
+
for (const file of ['index.js', 'index.d.ts']) {
|
|
16
|
+
const path = join(aiTasksRoot, file);
|
|
17
|
+
if (!existsSync(path)) continue;
|
|
18
|
+
const text = readFileSync(path, 'utf8');
|
|
19
|
+
if (!text.includes(broken)) continue;
|
|
20
|
+
writeFileSync(path, text.replace(broken, fixed), 'utf8');
|
|
21
|
+
console.log(`[patch-ai-tasks-xynthesis-export] patched ${file}`);
|
|
22
|
+
}
|