@exellix/graph-engine 7.8.0 → 7.8.2
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 +28 -0
- package/README.md +1 -1
- package/dist/src/integrations/ActivixNodeActivityIntegration.js +7 -0
- package/dist/src/runtime/ExellixGraphRuntime.js +5 -2
- package/dist/src/runtime/buildAiTasksRunTaskRequest.d.ts +2 -0
- package/dist/src/runtime/buildAiTasksRunTaskRequest.js +21 -0
- package/dist/src/runtime/graphAiModelConfig.js +3 -3
- package/dist/src/runtime/runTaskAugments.d.ts +3 -0
- package/dist/src/runtime/runTaskAugments.js +20 -0
- package/dist/src/runtime/stepRetry.d.ts +1 -6
- package/dist/src/runtime/stepRetry.js +4 -41
- package/dist/src/types/options.d.ts +1 -10
- package/dist/testkit/RealTasksClient.js +3 -8
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,38 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 7.8.2
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **ai-tasks error propagation:** Node failures preserve upstream `error.code` and full `error.message` from `@exellix/ai-tasks` (no `TASK_RUN_FAILED: {skillKey}` message wrapper).
|
|
8
|
+
- **Activix node fail records:** `failRecord` updates include `errorCode` alongside the upstream error message.
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **Dependencies:** `@exellix/ai-tasks` ^8.8.0 (transitive: `@exellix/ai-skills` ≥6.5.0, `@exellix/xynthesis` ≥4.6.0, `@x12i/ai-profiles` ≥3.2.0).
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Tests:** SubNets (`graph-qcrbz6t`) synthesis PRE alias pass-through; ai-tasks `SKILL_MODEL_RESOLUTION_FAILED` / `XYNTHESIS_*` error surfacing on `node:fail` and Activix `failRecord`.
|
|
17
|
+
|
|
18
|
+
## 7.8.1
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- **Dependencies:** `@exellix/ai-tasks` ^8.7.0 (transitive floors: `@exellix/ai-skills` ≥6.3.7, `@exellix/xynthesis` ≥4.4.9, `@x12i/ai-gateway` ≥10.0.6, `@x12i/ai-profiles` ≥2.1.1).
|
|
23
|
+
- **Synthesize finalizer:** outbound `runTask` includes `executionStrategies: []`.
|
|
24
|
+
- **Step retry:** token-limit retries no longer mutate `llmCall` completion budget fields (Optimixer-owned).
|
|
25
|
+
- **`buildMainLlmCallForRunTask`:** strips `maxTokens`, `maxTokensCap`, and legacy token keys from forwarded `llmCall`.
|
|
26
|
+
- **`buildAiTasksRunTaskRequest`:** rejects provider API key passthrough on `taskConfiguration` (env-only).
|
|
27
|
+
- **Default graph profile triplet:** `cheap/default`, `pro/default`, `cheap/default`.
|
|
28
|
+
- **testkit:** removed `xynthesisModel` debug fallback in `RealTasksClient`.
|
|
29
|
+
|
|
3
30
|
## 7.7.9
|
|
4
31
|
|
|
5
32
|
### Changed
|
|
6
33
|
|
|
7
34
|
- **Documentation:** Align README, [`.docs/exellix-graph-engine-format.md`](.docs/exellix-graph-engine-format.md), format docs, and [`.docs/ai-tasks-model-profile-aliases-7x.md`](.docs/ai-tasks-model-profile-aliases-7x.md) with ai-tasks **8.6.8** / funcx **4.4.4**: three-slot `modelConfig`, graph-only selection (7.7+), partial task overrides (7.7.8+), studio preflight via `@exellix/graph-composer`, funcx-for-runx vs xynthesis-for-execution-strategies.
|
|
35
|
+
- **Cross-package boundary:** [`.docs/graph-engine-ai-tasks-boundary.md`](.docs/graph-engine-ai-tasks-boundary.md) — normative graph-engine ↔ ai-tasks contract; [upstream-doc-patches/@exellix/ai-tasks/](upstream-doc-patches/@exellix/ai-tasks/README.md) — copy-ready ai-tasks doc fixes (RUNTASK_REQUEST, flow-io xynthesis-pre/post, workplan status).
|
|
8
36
|
|
|
9
37
|
### Dependencies
|
|
10
38
|
|
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ A minimal, focused SDK for executing graphs in the exellix ecosystem.
|
|
|
32
32
|
| Layer 01 / 08 graph entry & response contracts | [`.docs/graph-io-visibility.md`](.docs/graph-io-visibility.md) |
|
|
33
33
|
| Graph entry `dataFilters` v1 / public evaluator | [`.docs/data-filters-evaluation.md`](.docs/data-filters-evaluation.md) |
|
|
34
34
|
| Task-node `conditions` + conditional `modelConfig.cases` (runx) | [`.docs/task-node-conditions-evaluation.md`](.docs/task-node-conditions-evaluation.md) |
|
|
35
|
-
| **Model profile aliases** (7.x+: graph = aliases / `profile/choice`; ai-tasks resolves per phase) | [`BREAKING-CHANGES.md`](BREAKING-CHANGES.md) §7.6 / §7.7, [`.docs/ai-tasks-model-profile-aliases-7x.md`](.docs/ai-tasks-model-profile-aliases-7x.md) |
|
|
35
|
+
| **Model profile aliases** (7.x+: graph = aliases / `profile/choice`; ai-tasks resolves per phase) | [`BREAKING-CHANGES.md`](BREAKING-CHANGES.md) §7.6 / §7.7, [`.docs/ai-tasks-model-profile-aliases-7x.md`](.docs/ai-tasks-model-profile-aliases-7x.md), [`.docs/graph-engine-ai-tasks-boundary.md`](.docs/graph-engine-ai-tasks-boundary.md) |
|
|
36
36
|
| Platform vs implementation (no domain operators in schema) | [`.docs/platform-generic-vs-implementation.md`](.docs/platform-generic-vs-implementation.md) |
|
|
37
37
|
| Activix records, `runContext`, collection tracking (`@x12i/activix` 8.4+) | [`.docs/activix-records.md`](.docs/activix-records.md) |
|
|
38
38
|
| Bundled graph examples & bundle README | [`graphs/README.md`](graphs/README.md) |
|
|
@@ -149,11 +149,18 @@ export function createActivixNodeActivityIntegration(ax, options) {
|
|
|
149
149
|
: typeof err === 'object' && err && 'message' in err
|
|
150
150
|
? String(err.message)
|
|
151
151
|
: String(err ?? 'unknown error');
|
|
152
|
+
const errorCode = (() => {
|
|
153
|
+
if (err == null || typeof err !== 'object')
|
|
154
|
+
return undefined;
|
|
155
|
+
const code = err.code;
|
|
156
|
+
return code != null ? String(code) : undefined;
|
|
157
|
+
})();
|
|
152
158
|
const outerResponse = nodeEvent.data?.response;
|
|
153
159
|
const outerMemoryEnd = nodeEvent.data?.memoryAfter;
|
|
154
160
|
const recordId = await recordPromise;
|
|
155
161
|
await ax.failRecord(recordId, message, {
|
|
156
162
|
failedAt: nodeEvent.timestamp,
|
|
163
|
+
...(errorCode != null ? { errorCode } : {}),
|
|
157
164
|
outer: {
|
|
158
165
|
output: {
|
|
159
166
|
response: outerResponse,
|
|
@@ -422,6 +422,7 @@ export function createExellixGraphRuntime(opts) {
|
|
|
422
422
|
},
|
|
423
423
|
modelConfig: wireModelConfig,
|
|
424
424
|
llmCall: effectiveLlmCall,
|
|
425
|
+
executionStrategies: [],
|
|
425
426
|
...(forwardRunTaskTrace ? { executionMode: "trace" } : {}),
|
|
426
427
|
...((input.runTaskDiagnostics ?? opts.runTaskDiagnostics) != null
|
|
427
428
|
? { diagnostics: input.runTaskDiagnostics ?? opts.runTaskDiagnostics }
|
|
@@ -962,8 +963,10 @@ export function createExellixGraphRuntime(opts) {
|
|
|
962
963
|
const resMeta = res.metadata ?? res.meta;
|
|
963
964
|
const taskRunLog = [...retryOutcome.syntheticRunLog, ...extractTaskRunLogFromMetadata(resMeta)];
|
|
964
965
|
const logxerCorrelationId = extractLogxerCorrelationFromMetadata(resMeta);
|
|
965
|
-
const
|
|
966
|
-
|
|
966
|
+
const taskErrorMessage = res.error?.message ?? "Task run failed";
|
|
967
|
+
const taskErrorCode = res.error?.code ?? "TASK_RUN_FAILED";
|
|
968
|
+
const err = new Error(taskErrorMessage);
|
|
969
|
+
err.code = taskErrorCode;
|
|
967
970
|
err.skillKey = skillKey;
|
|
968
971
|
err.diagnostics = res.diagnostics;
|
|
969
972
|
err.nodeId = input.node?.id;
|
|
@@ -13,6 +13,8 @@ import type { Job } from '../types/refs.js';
|
|
|
13
13
|
import type { RunTaskModelConfigWire } from './graphAiModelConfig.js';
|
|
14
14
|
import type { ExecutionStepOption } from '../types/options.js';
|
|
15
15
|
import type { ResolvedNarrixWirePayload } from '../types/narrix.js';
|
|
16
|
+
/** Request/metadata keys that must never carry provider API secrets (env-only). */
|
|
17
|
+
export declare const FORBIDDEN_RUN_TASK_SECRET_KEYS: readonly ["openrouterApiKey", "OPENROUTER_API_KEY", "OPEN_ROUTER_KEY", "apiKey", "api_key", "openRouterApiKey"];
|
|
16
18
|
/** Reads task-node `taskConfiguration` keys forwarded to `@exellix/ai-tasks` `RunTaskRequest` (runtime strategy / Narrix wiring). */
|
|
17
19
|
export declare function extractRunTaskStrategyOverrides(taskConfiguration: Record<string, unknown> | undefined | null): Partial<Pick<BuildAiTasksRunTaskRequestArgs, 'narrixMode' | 'inputStrategyKey' | 'narrixInput'>>;
|
|
18
20
|
/** Optional `RunTaskRequest` fields commonly authored on task-node `taskConfiguration` (whitelist — closed merge order). */
|
|
@@ -1,7 +1,27 @@
|
|
|
1
1
|
import { buildRunTaskTaskConfigurationForward } from './buildRunTaskTaskConfigurationForward.js';
|
|
2
2
|
import { normalizeSmartInputConfigForRunTask } from './smartInputPaths.js';
|
|
3
|
+
/** Request/metadata keys that must never carry provider API secrets (env-only). */
|
|
4
|
+
export const FORBIDDEN_RUN_TASK_SECRET_KEYS = [
|
|
5
|
+
'openrouterApiKey',
|
|
6
|
+
'OPENROUTER_API_KEY',
|
|
7
|
+
'OPEN_ROUTER_KEY',
|
|
8
|
+
'apiKey',
|
|
9
|
+
'api_key',
|
|
10
|
+
'openRouterApiKey',
|
|
11
|
+
];
|
|
12
|
+
function assertNoSecretPassthrough(taskConfiguration, context) {
|
|
13
|
+
if (taskConfiguration == null || typeof taskConfiguration !== 'object' || Array.isArray(taskConfiguration)) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
for (const key of FORBIDDEN_RUN_TASK_SECRET_KEYS) {
|
|
17
|
+
if (key in taskConfiguration && taskConfiguration[key] !== undefined) {
|
|
18
|
+
throw new Error(`${context}: taskConfiguration.${key} is forbidden — provider API keys belong in process environment only (e.g. OPENROUTER_API_KEY).`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
3
22
|
/** Reads task-node `taskConfiguration` keys forwarded to `@exellix/ai-tasks` `RunTaskRequest` (runtime strategy / Narrix wiring). */
|
|
4
23
|
export function extractRunTaskStrategyOverrides(taskConfiguration) {
|
|
24
|
+
assertNoSecretPassthrough(taskConfiguration, 'extractRunTaskStrategyOverrides');
|
|
5
25
|
if (taskConfiguration == null || typeof taskConfiguration !== 'object' || Array.isArray(taskConfiguration))
|
|
6
26
|
return {};
|
|
7
27
|
const m = taskConfiguration;
|
|
@@ -25,6 +45,7 @@ export function extractRunTaskStrategyOverrides(taskConfiguration) {
|
|
|
25
45
|
* (`RunSkillRequest` / `RunTaskRequest` extras not covered by {@link extractRunTaskStrategyOverrides}).
|
|
26
46
|
*/
|
|
27
47
|
export function extractRunTaskMetadataPassthrough(taskConfiguration) {
|
|
48
|
+
assertNoSecretPassthrough(taskConfiguration, 'extractRunTaskMetadataPassthrough');
|
|
28
49
|
if (taskConfiguration == null || typeof taskConfiguration !== 'object' || Array.isArray(taskConfiguration))
|
|
29
50
|
return {};
|
|
30
51
|
const m = taskConfiguration;
|
|
@@ -3,9 +3,9 @@ import { ExellixGraphErrorCode } from '../errors/exellixGraphErrorCodes.js';
|
|
|
3
3
|
import { isGraphAiModelConfig } from './modelConfigSelection.js';
|
|
4
4
|
/** Profile alias names used when no modelConfig tier resolves. */
|
|
5
5
|
export const DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG = {
|
|
6
|
-
preActionModel: 'cheap',
|
|
7
|
-
skillModel: '
|
|
8
|
-
postActionModel: 'cheap',
|
|
6
|
+
preActionModel: 'cheap/default',
|
|
7
|
+
skillModel: 'pro/default',
|
|
8
|
+
postActionModel: 'cheap/default',
|
|
9
9
|
};
|
|
10
10
|
/**
|
|
11
11
|
* Illustrative concrete ids for {@link DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG}.
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { LlmCallConfig } from '@exellix/ai-tasks';
|
|
5
5
|
import type { RunTaskModelConfigWire } from './graphAiModelConfig.js';
|
|
6
|
+
/** Completion budget fields removed from outbound runTask wires (ai-tasks 8.6+ / Optimixer-owned). */
|
|
7
|
+
export declare const STRIPPED_LLM_CALL_TOKEN_KEYS: readonly ["maxTokens", "maxTokensCap", "max_tokens", "max_completion_tokens", "max_output_tokens"];
|
|
8
|
+
export declare function stripLlmCallTokenCapFields(llmCall: Record<string, unknown> | undefined | null): Record<string, unknown> | undefined;
|
|
6
9
|
export declare function mergeDefinedLlmCallParts(...parts: Array<Record<string, unknown> | undefined | null>): Record<string, unknown> | undefined;
|
|
7
10
|
/**
|
|
8
11
|
* MAIN-only `llmCall` for ai-tasks 8.4+: `model` is always {@link RunTaskModelConfigWire.skillModel};
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
/** Completion budget fields removed from outbound runTask wires (ai-tasks 8.6+ / Optimixer-owned). */
|
|
2
|
+
export const STRIPPED_LLM_CALL_TOKEN_KEYS = [
|
|
3
|
+
'maxTokens',
|
|
4
|
+
'maxTokensCap',
|
|
5
|
+
'max_tokens',
|
|
6
|
+
'max_completion_tokens',
|
|
7
|
+
'max_output_tokens',
|
|
8
|
+
];
|
|
9
|
+
export function stripLlmCallTokenCapFields(llmCall) {
|
|
10
|
+
if (!llmCall || typeof llmCall !== 'object' || Array.isArray(llmCall))
|
|
11
|
+
return undefined;
|
|
12
|
+
const out = { ...llmCall };
|
|
13
|
+
for (const key of STRIPPED_LLM_CALL_TOKEN_KEYS) {
|
|
14
|
+
delete out[key];
|
|
15
|
+
}
|
|
16
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
17
|
+
}
|
|
1
18
|
export function mergeDefinedLlmCallParts(...parts) {
|
|
2
19
|
const out = {};
|
|
3
20
|
for (const p of parts) {
|
|
@@ -8,6 +25,9 @@ export function mergeDefinedLlmCallParts(...parts) {
|
|
|
8
25
|
out[k] = v;
|
|
9
26
|
}
|
|
10
27
|
}
|
|
28
|
+
for (const key of STRIPPED_LLM_CALL_TOKEN_KEYS) {
|
|
29
|
+
delete out[key];
|
|
30
|
+
}
|
|
11
31
|
return Object.keys(out).length > 0 ? out : undefined;
|
|
12
32
|
}
|
|
13
33
|
/**
|
|
@@ -2,13 +2,8 @@ import type { RunLogEntry } from '../types/runLog.js';
|
|
|
2
2
|
import type { RunTaskRequest, RunTaskResponse, StepRetryPolicy } from '../types/options.js';
|
|
3
3
|
import type { TaskNode } from '../types/refs.js';
|
|
4
4
|
import type { StepAttemptRecord } from '../types/results.js';
|
|
5
|
-
export type ResolvedStepRetryPolicy = Required<Pick<StepRetryPolicy, 'maxAttempts' | 'retryOnTimeout' | 'retryOnTokenLimit'
|
|
5
|
+
export type ResolvedStepRetryPolicy = Required<Pick<StepRetryPolicy, 'maxAttempts' | 'retryOnTimeout' | 'retryOnTokenLimit'>>;
|
|
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;
|
|
12
7
|
export type RunTaskWithRetryResult = {
|
|
13
8
|
response: RunTaskResponse;
|
|
14
9
|
attempts: StepAttemptRecord[];
|
|
@@ -4,9 +4,6 @@ const DEFAULT_POLICY = {
|
|
|
4
4
|
maxAttempts: 3,
|
|
5
5
|
retryOnTimeout: true,
|
|
6
6
|
retryOnTokenLimit: true,
|
|
7
|
-
tokenBumpMultiplier: 2,
|
|
8
|
-
tokenBumpCap: 16_000,
|
|
9
|
-
baseMaxTokensFallback: 2000,
|
|
10
7
|
};
|
|
11
8
|
export function resolveStepRetryPolicy(graphPolicy, node) {
|
|
12
9
|
const nodeRaw = node?.taskConfiguration?.stepRetryPolicy;
|
|
@@ -17,24 +14,8 @@ export function resolveStepRetryPolicy(graphPolicy, node) {
|
|
|
17
14
|
maxAttempts,
|
|
18
15
|
retryOnTimeout: merged.retryOnTimeout !== false,
|
|
19
16
|
retryOnTokenLimit: merged.retryOnTokenLimit !== false,
|
|
20
|
-
tokenBumpMultiplier: Math.max(1, merged.tokenBumpMultiplier ?? DEFAULT_POLICY.tokenBumpMultiplier),
|
|
21
|
-
tokenBumpCap: Math.max(1, merged.tokenBumpCap ?? DEFAULT_POLICY.tokenBumpCap),
|
|
22
|
-
baseMaxTokensFallback: Math.max(1, merged.baseMaxTokensFallback ?? DEFAULT_POLICY.baseMaxTokensFallback),
|
|
23
17
|
};
|
|
24
18
|
}
|
|
25
|
-
function readNum(v) {
|
|
26
|
-
return typeof v === 'number' && Number.isFinite(v) ? v : undefined;
|
|
27
|
-
}
|
|
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) {
|
|
33
|
-
const llm = req.llmCall;
|
|
34
|
-
return (readNum(llm?.maxTokensCap) ??
|
|
35
|
-
readNum(llm?.maxTokens) ??
|
|
36
|
-
readNum(llm?.max_tokens));
|
|
37
|
-
}
|
|
38
19
|
function shallowCloneRunTaskRequest(req) {
|
|
39
20
|
return {
|
|
40
21
|
...req,
|
|
@@ -49,16 +30,6 @@ function shallowCloneRunTaskRequest(req) {
|
|
|
49
30
|
: {}),
|
|
50
31
|
};
|
|
51
32
|
}
|
|
52
|
-
function applyTokenBump(workingReq, policy) {
|
|
53
|
-
const current = readMaxTokensFromRunTaskRequest(workingReq) ?? policy.baseMaxTokensFallback;
|
|
54
|
-
const after = Math.min(policy.tokenBumpCap, Math.floor(current * policy.tokenBumpMultiplier));
|
|
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;
|
|
60
|
-
return { before: current, after };
|
|
61
|
-
}
|
|
62
33
|
function classifyThrownError(err) {
|
|
63
34
|
if (err == null || typeof err !== 'object')
|
|
64
35
|
return 'other';
|
|
@@ -140,7 +111,6 @@ export async function runRunTaskWithRetry(args) {
|
|
|
140
111
|
}
|
|
141
112
|
const endedAt = Date.now();
|
|
142
113
|
const durationMs = endedAt - startedAt;
|
|
143
|
-
const maxTok = readMaxTokensFromRunTaskRequest(workingReq);
|
|
144
114
|
if (thrown != null) {
|
|
145
115
|
const c = classifyThrownError(thrown);
|
|
146
116
|
attempts.push({
|
|
@@ -152,7 +122,6 @@ export async function runRunTaskWithRetry(args) {
|
|
|
152
122
|
classification: c,
|
|
153
123
|
errorCode: thrown?.code,
|
|
154
124
|
errorMessage: thrown?.message,
|
|
155
|
-
maxTokensRequested: maxTok,
|
|
156
125
|
});
|
|
157
126
|
pushSyntheticRunLog(syntheticRunLog, {
|
|
158
127
|
nodeId,
|
|
@@ -162,7 +131,6 @@ export async function runRunTaskWithRetry(args) {
|
|
|
162
131
|
data: {
|
|
163
132
|
classification: c,
|
|
164
133
|
errorCode: thrown?.code,
|
|
165
|
-
maxTokensRequested: maxTok,
|
|
166
134
|
},
|
|
167
135
|
});
|
|
168
136
|
if (!shouldRetryClassification(c, policy, attemptIndex)) {
|
|
@@ -172,13 +140,12 @@ export async function runRunTaskWithRetry(args) {
|
|
|
172
140
|
throw err;
|
|
173
141
|
}
|
|
174
142
|
if (c === 'token-limit' && policy.retryOnTokenLimit) {
|
|
175
|
-
const { before, after } = applyTokenBump(workingReq, policy);
|
|
176
143
|
pushSyntheticRunLog(syntheticRunLog, {
|
|
177
144
|
nodeId,
|
|
178
145
|
skillKey,
|
|
179
146
|
level: 'info',
|
|
180
|
-
message: `step retry ${attemptIndex + 2}/${policy.maxAttempts}: token-limit
|
|
181
|
-
data: { classification: c,
|
|
147
|
+
message: `step retry ${attemptIndex + 2}/${policy.maxAttempts}: token-limit (completion budget is Optimixer-owned; request unchanged)`,
|
|
148
|
+
data: { classification: c, attempt: attemptIndex + 1 },
|
|
182
149
|
});
|
|
183
150
|
}
|
|
184
151
|
continue;
|
|
@@ -196,7 +163,6 @@ export async function runRunTaskWithRetry(args) {
|
|
|
196
163
|
startedAt,
|
|
197
164
|
endedAt,
|
|
198
165
|
durationMs,
|
|
199
|
-
maxTokensRequested: maxTok,
|
|
200
166
|
});
|
|
201
167
|
return { response, attempts, syntheticRunLog, lastRequest: workingReq };
|
|
202
168
|
}
|
|
@@ -210,7 +176,6 @@ export async function runRunTaskWithRetry(args) {
|
|
|
210
176
|
classification: c,
|
|
211
177
|
errorCode: response.error?.code,
|
|
212
178
|
errorMessage: response.error?.message,
|
|
213
|
-
maxTokensRequested: maxTok,
|
|
214
179
|
});
|
|
215
180
|
pushSyntheticRunLog(syntheticRunLog, {
|
|
216
181
|
nodeId,
|
|
@@ -220,20 +185,18 @@ export async function runRunTaskWithRetry(args) {
|
|
|
220
185
|
data: {
|
|
221
186
|
classification: c,
|
|
222
187
|
errorCode: response.error?.code,
|
|
223
|
-
maxTokensRequested: maxTok,
|
|
224
188
|
},
|
|
225
189
|
});
|
|
226
190
|
if (!shouldRetryClassification(c, policy, attemptIndex)) {
|
|
227
191
|
return { response, attempts, syntheticRunLog, lastRequest: workingReq };
|
|
228
192
|
}
|
|
229
193
|
if (c === 'token-limit' && policy.retryOnTokenLimit) {
|
|
230
|
-
const { before, after } = applyTokenBump(workingReq, policy);
|
|
231
194
|
pushSyntheticRunLog(syntheticRunLog, {
|
|
232
195
|
nodeId,
|
|
233
196
|
skillKey,
|
|
234
197
|
level: 'info',
|
|
235
|
-
message: `step retry ${attemptIndex + 2}/${policy.maxAttempts}: token-limit
|
|
236
|
-
data: { classification: c,
|
|
198
|
+
message: `step retry ${attemptIndex + 2}/${policy.maxAttempts}: token-limit (completion budget is Optimixer-owned; request unchanged)`,
|
|
199
|
+
data: { classification: c, attempt: attemptIndex + 1 },
|
|
237
200
|
});
|
|
238
201
|
}
|
|
239
202
|
}
|
|
@@ -31,17 +31,8 @@ export interface StepRetryPolicy {
|
|
|
31
31
|
maxAttempts?: number;
|
|
32
32
|
/** When false, timeouts do not trigger an automatic retry. Default **true**. */
|
|
33
33
|
retryOnTimeout?: boolean;
|
|
34
|
-
/** When false, token-limit signals do not trigger retry
|
|
34
|
+
/** When false, token-limit signals do not trigger retry. Default **true**. Completion budget is Optimixer-owned — retries re-run unchanged. */
|
|
35
35
|
retryOnTokenLimit?: boolean;
|
|
36
|
-
/** Applied to effective max tokens before each token-limit retry (default **2**). */
|
|
37
|
-
tokenBumpMultiplier?: number;
|
|
38
|
-
/** Upper bound for `maxTokens` after bump (default **16000**). */
|
|
39
|
-
tokenBumpCap?: number;
|
|
40
|
-
/**
|
|
41
|
-
* Used when `llmCall.maxTokensCap` (or legacy `maxTokens` / `max_tokens`) is unset on the outbound request
|
|
42
|
-
* before the first token-limit retry bump (default **2000**).
|
|
43
|
-
*/
|
|
44
|
-
baseMaxTokensFallback?: number;
|
|
45
36
|
}
|
|
46
37
|
/** MAIN gate before `runTask`: empty execution input / synthesis context (graphs-studio G1, G4). */
|
|
47
38
|
export type MainReadinessPolicy = 'fail' | 'warn' | 'off';
|
|
@@ -83,16 +83,11 @@ export class RealTasksClient {
|
|
|
83
83
|
const mc = aiTasksRequest.modelConfig;
|
|
84
84
|
const effective = mc
|
|
85
85
|
? {
|
|
86
|
-
preActionModel: mc.preActionModel
|
|
86
|
+
preActionModel: mc.preActionModel,
|
|
87
87
|
skillModel: mc.skillModel,
|
|
88
|
-
postActionModel: mc.postActionModel
|
|
88
|
+
postActionModel: mc.postActionModel,
|
|
89
89
|
}
|
|
90
|
-
:
|
|
91
|
-
? {
|
|
92
|
-
provider: aiTasksRequest.config.provider,
|
|
93
|
-
model: aiTasksRequest.config.model,
|
|
94
|
-
}
|
|
95
|
-
: { provider: '(none)', model: '(none)' };
|
|
90
|
+
: { preActionModel: '(none)', skillModel: '(none)', postActionModel: '(none)' };
|
|
96
91
|
console.log('[AI_PROVIDER] RealTasksClient → ai-tasks:', effective);
|
|
97
92
|
}
|
|
98
93
|
const sdk = await getPackagedClientPromise();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exellix/graph-engine",
|
|
3
|
-
"version": "7.8.
|
|
3
|
+
"version": "7.8.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Graph executor SDK",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"scripts": {
|
|
26
26
|
"prebuild": "node scripts/clean-dist.mjs",
|
|
27
27
|
"build": "tsc",
|
|
28
|
-
"test": "npm run build && npm run check:no-legacy && tsx --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 tests/run-log-diagnostics.test.ts",
|
|
28
|
+
"test": "npm run build && npm run check:no-legacy && tsx --test --test-force-exit tests/model-alias-contract.test.ts tests/ai-tasks-error-propagation.test.ts tests/reports-fixtures-pre-synthesis.test.ts tests/passthrough-parity.test.ts tests/step-retry-llm-call.test.ts tests/run-log-diagnostics.test.ts",
|
|
29
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 tests/step-retry-llm-call.test.ts",
|
|
30
30
|
"test:live": "npm run run:pre-synthesis",
|
|
31
31
|
"test:subnets-graph-fixture": "npm run build && node --test tests/subnets-graph.fixture.test.mjs",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"access": "public"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@exellix/ai-tasks": "^8.
|
|
55
|
+
"@exellix/ai-tasks": "^8.8.0",
|
|
56
56
|
"@x12i/activix": "8.5.0",
|
|
57
57
|
"@x12i/catalox": "5.1.3",
|
|
58
58
|
"@x12i/env": "4.0.1",
|