@memberjunction/ai-agents 5.38.0 → 5.39.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/dist/ArtifactToolManager.d.ts +13 -0
- package/dist/ArtifactToolManager.d.ts.map +1 -1
- package/dist/ArtifactToolManager.js +60 -1
- package/dist/ArtifactToolManager.js.map +1 -1
- package/dist/agent-run-watchdog.d.ts +109 -0
- package/dist/agent-run-watchdog.d.ts.map +1 -0
- package/dist/agent-run-watchdog.js +278 -0
- package/dist/agent-run-watchdog.js.map +1 -0
- package/dist/agent-types/loop-agent-prompt-params.d.ts +13 -0
- package/dist/agent-types/loop-agent-prompt-params.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-prompt-params.js +3 -1
- package/dist/agent-types/loop-agent-prompt-params.js.map +1 -1
- package/dist/agent-types/loop-agent-response-type.d.ts +12 -3
- package/dist/agent-types/loop-agent-response-type.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-response-type.js.map +1 -1
- package/dist/agent-types/loop-agent-type.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-type.js +29 -1
- package/dist/agent-types/loop-agent-type.js.map +1 -1
- package/dist/artifact-tools/DataSnapshotToolLibrary.js +2 -2
- package/dist/artifact-tools/JSONToolLibrary.d.ts.map +1 -1
- package/dist/artifact-tools/JSONToolLibrary.js +7 -1
- package/dist/artifact-tools/JSONToolLibrary.js.map +1 -1
- package/dist/base-agent.d.ts +53 -1
- package/dist/base-agent.d.ts.map +1 -1
- package/dist/base-agent.js +209 -9
- package/dist/base-agent.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/pipeline/coerce.d.ts +43 -0
- package/dist/pipeline/coerce.d.ts.map +1 -0
- package/dist/pipeline/coerce.js +92 -0
- package/dist/pipeline/coerce.js.map +1 -0
- package/dist/pipeline/index.d.ts +20 -0
- package/dist/pipeline/index.d.ts.map +1 -0
- package/dist/pipeline/index.js +20 -0
- package/dist/pipeline/index.js.map +1 -0
- package/dist/pipeline/jsonpath-eval.d.ts +29 -0
- package/dist/pipeline/jsonpath-eval.d.ts.map +1 -0
- package/dist/pipeline/jsonpath-eval.js +144 -0
- package/dist/pipeline/jsonpath-eval.js.map +1 -0
- package/dist/pipeline/operators.d.ts +15 -0
- package/dist/pipeline/operators.d.ts.map +1 -0
- package/dist/pipeline/operators.js +332 -0
- package/dist/pipeline/operators.js.map +1 -0
- package/dist/pipeline/path.d.ts +16 -0
- package/dist/pipeline/path.d.ts.map +1 -0
- package/dist/pipeline/path.js +34 -0
- package/dist/pipeline/path.js.map +1 -0
- package/dist/pipeline/pipeline-docs.d.ts +6 -0
- package/dist/pipeline/pipeline-docs.d.ts.map +1 -0
- package/dist/pipeline/pipeline-docs.js +137 -0
- package/dist/pipeline/pipeline-docs.js.map +1 -0
- package/dist/pipeline/pipeline-executor.d.ts +72 -0
- package/dist/pipeline/pipeline-executor.d.ts.map +1 -0
- package/dist/pipeline/pipeline-executor.js +326 -0
- package/dist/pipeline/pipeline-executor.js.map +1 -0
- package/dist/pipeline/pipeline-registry.d.ts +24 -0
- package/dist/pipeline/pipeline-registry.d.ts.map +1 -0
- package/dist/pipeline/pipeline-registry.js +28 -0
- package/dist/pipeline/pipeline-registry.js.map +1 -0
- package/dist/pipeline/pipeline.types.d.ts +89 -0
- package/dist/pipeline/pipeline.types.d.ts.map +1 -0
- package/dist/pipeline/pipeline.types.js +13 -0
- package/dist/pipeline/pipeline.types.js.map +1 -0
- package/dist/pipeline/predicate.d.ts +23 -0
- package/dist/pipeline/predicate.d.ts.map +1 -0
- package/dist/pipeline/predicate.js +306 -0
- package/dist/pipeline/predicate.js.map +1 -0
- package/dist/pipeline/providers/action-provider.d.ts +21 -0
- package/dist/pipeline/providers/action-provider.d.ts.map +1 -0
- package/dist/pipeline/providers/action-provider.js +24 -0
- package/dist/pipeline/providers/action-provider.js.map +1 -0
- package/dist/pipeline/providers/artifact-tool-provider.d.ts +20 -0
- package/dist/pipeline/providers/artifact-tool-provider.d.ts.map +1 -0
- package/dist/pipeline/providers/artifact-tool-provider.js +27 -0
- package/dist/pipeline/providers/artifact-tool-provider.js.map +1 -0
- package/dist/pipeline/providers/serialize.d.ts +23 -0
- package/dist/pipeline/providers/serialize.d.ts.map +1 -0
- package/dist/pipeline/providers/serialize.js +99 -0
- package/dist/pipeline/providers/serialize.js.map +1 -0
- package/dist/pipeline/template.d.ts +18 -0
- package/dist/pipeline/template.d.ts.map +1 -0
- package/dist/pipeline/template.js +47 -0
- package/dist/pipeline/template.js.map +1 -0
- package/package.json +19 -17
package/dist/base-agent.js
CHANGED
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* @since 2.49.0
|
|
12
12
|
*/
|
|
13
13
|
import { FileStorageEngineBase } from '@memberjunction/core-entities';
|
|
14
|
-
import { Metadata, RunView, LogStatus, LogStatusEx, LogError, LogErrorEx, IsVerboseLoggingEnabled } from '@memberjunction/core';
|
|
14
|
+
import { Metadata, RunView, LogStatus, LogStatusEx, LogError, LogErrorEx, IsVerboseLoggingEnabled, DatabaseProviderBase } from '@memberjunction/core';
|
|
15
|
+
import { AgentRunWatchdog } from './agent-run-watchdog.js';
|
|
15
16
|
import { AIPromptRunner } from '@memberjunction/ai-prompts';
|
|
16
17
|
import { BaseAgentType } from './agent-types/base-agent-type.js';
|
|
17
18
|
import { CopyScalarsAndArrays, JSONValidator, SafeExpressionEvaluator, UUIDsEqual } from '@memberjunction/global';
|
|
@@ -26,6 +27,7 @@ import { AgentRunner } from './AgentRunner.js';
|
|
|
26
27
|
import { PayloadManager } from './PayloadManager.js';
|
|
27
28
|
import { ScratchpadManager } from './ScratchpadManager.js';
|
|
28
29
|
import { ArtifactToolManager } from './ArtifactToolManager.js';
|
|
30
|
+
import { PipelineExecutor, PipelineToolRegistry, ActionInvocable, ArtifactToolInvocable, BuildPipelineToolDocs, formatFinalOutput, summarizePipelineStages, } from './pipeline/index.js';
|
|
29
31
|
import { AgentDataPreloader } from './AgentDataPreloader.js';
|
|
30
32
|
import { ClientToolRequestManager } from './ClientToolRequestManager.js';
|
|
31
33
|
import { ConversationMessageResolver } from './utils/ConversationMessageResolver.js';
|
|
@@ -94,6 +96,11 @@ export class BaseAgent {
|
|
|
94
96
|
/**
|
|
95
97
|
* Queue map to chain database saves sequentially per step entity.
|
|
96
98
|
* Prevents UPDATE queries running before INSERT queries on quick steps.
|
|
99
|
+
*
|
|
100
|
+
* Keyed by the step ENTITY INSTANCE, not its `ID`: a new step's `ID` is empty at create time and
|
|
101
|
+
* only gets populated during its INSERT `Save()`, so keying by `ID` would file the create and the
|
|
102
|
+
* finalize under different buckets and defeat the chain — letting the UPDATE race ahead of the
|
|
103
|
+
* INSERT on millisecond-fast steps (e.g. pipelines), which left them stuck at `Running`.
|
|
97
104
|
*/
|
|
98
105
|
this._stepSavePromises = new Map();
|
|
99
106
|
/**
|
|
@@ -1813,6 +1820,21 @@ export class BaseAgent {
|
|
|
1813
1820
|
else if (this._artifactToolManager.HasArtifacts()) {
|
|
1814
1821
|
this.logStatus(`[ArtifactTools] Artifacts present but tools disabled by agent config (includeArtifactToolsDocs=false)`, true, params);
|
|
1815
1822
|
}
|
|
1823
|
+
// Inject pipeline tool docs when pipelines are enabled and at least one source exists.
|
|
1824
|
+
// A pipeline's first step must be a source (Action or artifact tool); with none
|
|
1825
|
+
// available pipelines are impossible, so BuildPipelineToolDocs returns '' and the
|
|
1826
|
+
// template's `{{ _PIPELINE_TOOLS }}` block stays empty.
|
|
1827
|
+
const pipelineDocsEnabled = agentTypePromptParams?.includePipelineDocs !== false;
|
|
1828
|
+
if (pipelineDocsEnabled) {
|
|
1829
|
+
const sourceNames = [
|
|
1830
|
+
...this.getEffectiveActionsForValidation(params.agent.ID).map((a) => a.Name),
|
|
1831
|
+
...this._artifactToolManager.GetAvailableToolNames(),
|
|
1832
|
+
];
|
|
1833
|
+
const pipelineDocs = BuildPipelineToolDocs(sourceNames);
|
|
1834
|
+
if (pipelineDocs) {
|
|
1835
|
+
promptParams.data['_PIPELINE_TOOLS'] = pipelineDocs;
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1816
1838
|
// Pass file artifacts as candidate native file inputs.
|
|
1817
1839
|
// The AIPromptRunner will check these against the resolved driver's
|
|
1818
1840
|
// FileCapabilities and attach qualifying files as native content blocks.
|
|
@@ -3305,9 +3327,10 @@ The context is now within limits. Please retry your request with the recovered c
|
|
|
3305
3327
|
const body = toolResults.map((r, i) => {
|
|
3306
3328
|
const heading = `### ${i + 1}. ${r.artifactId}.${r.tool}(${JSON.stringify(r.input)})`;
|
|
3307
3329
|
if (r.result.success) {
|
|
3308
|
-
const
|
|
3330
|
+
const raw = typeof r.result.data === 'string'
|
|
3309
3331
|
? r.result.data
|
|
3310
3332
|
: JSON.stringify(r.result.data, null, 2);
|
|
3333
|
+
const data = this.capStandaloneToolResultText(raw);
|
|
3311
3334
|
return `${heading}\n\`\`\`json\n${data}\n\`\`\``;
|
|
3312
3335
|
}
|
|
3313
3336
|
return `${heading}\n**Error:** ${r.result.errorMessage}`;
|
|
@@ -3331,6 +3354,154 @@ The context is now within limits. Please retry your request with the recovered c
|
|
|
3331
3354
|
};
|
|
3332
3355
|
params.conversationMessages.push(message);
|
|
3333
3356
|
}
|
|
3357
|
+
/**
|
|
3358
|
+
* Character budget (~4 chars/token) for a SINGLE standalone artifact-tool result injected into
|
|
3359
|
+
* the conversation. A `get_full` on a large artifact can otherwise dump the whole thing into
|
|
3360
|
+
* context and overflow the model's window — the exact failure pipelines exist to avoid. Override
|
|
3361
|
+
* in a subclass to tune. Pipelines are unaffected: their intermediate results never flow through
|
|
3362
|
+
* here, and the executor already caps a pipeline's final output.
|
|
3363
|
+
*
|
|
3364
|
+
* @protected
|
|
3365
|
+
*/
|
|
3366
|
+
get maxStandaloneToolResultChars() {
|
|
3367
|
+
return 100_000; // ~25k tokens
|
|
3368
|
+
}
|
|
3369
|
+
/**
|
|
3370
|
+
* Bound a standalone tool result to {@link maxStandaloneToolResultChars}: return a head slice
|
|
3371
|
+
* plus a redirect that teaches the agent to page (`get_rows`) or reduce (`pipeline`) instead of
|
|
3372
|
+
* reading a whole large artifact. Mirrors how read/search tools cap output at the tool boundary.
|
|
3373
|
+
*
|
|
3374
|
+
* @protected
|
|
3375
|
+
*/
|
|
3376
|
+
capStandaloneToolResultText(text) {
|
|
3377
|
+
const budget = this.maxStandaloneToolResultChars;
|
|
3378
|
+
if (text.length <= budget) {
|
|
3379
|
+
return text;
|
|
3380
|
+
}
|
|
3381
|
+
const omitted = text.length - budget;
|
|
3382
|
+
return (text.slice(0, budget) +
|
|
3383
|
+
`\n\n…[truncated ${omitted.toLocaleString()} chars. This artifact is too large to read whole ` +
|
|
3384
|
+
`(~${Math.round(text.length / 4000)}k tokens) — reading it in full overflows the context window. ` +
|
|
3385
|
+
`Instead: page it with get_rows(start, count), or run a pipeline that filters/aggregates it ` +
|
|
3386
|
+
`server-side (where / select / groupBy → only the small final result returns to you).]`);
|
|
3387
|
+
}
|
|
3388
|
+
/**
|
|
3389
|
+
* Builds a per-run {@link PipelineToolRegistry} that unifies the three pipeline-able
|
|
3390
|
+
* substrates behind one namespace: built-in transforms, the agent's effective Actions, and
|
|
3391
|
+
* the run's artifact tools. Transforms register first so their reserved names win; a source
|
|
3392
|
+
* whose name collides with a transform is skipped for pipeline use (still callable normally)
|
|
3393
|
+
* and logged, rather than aborting the whole pipeline.
|
|
3394
|
+
*
|
|
3395
|
+
* @protected
|
|
3396
|
+
*/
|
|
3397
|
+
buildPipelineRegistry(params) {
|
|
3398
|
+
const registry = new PipelineToolRegistry();
|
|
3399
|
+
const register = (invocable) => {
|
|
3400
|
+
try {
|
|
3401
|
+
registry.Register(invocable);
|
|
3402
|
+
}
|
|
3403
|
+
catch (e) {
|
|
3404
|
+
this.logStatus(`[Pipeline] Skipped tool "${invocable.toolName}": ${e.message}`, true, params);
|
|
3405
|
+
}
|
|
3406
|
+
};
|
|
3407
|
+
// Operators (where/select/map/…) are pure code-defined verbs, not registry tools — only
|
|
3408
|
+
// capabilities (Actions + artifact tools) live here as pipeline sources/stages.
|
|
3409
|
+
// Actions — each wrapped to run via the existing single-action execution path.
|
|
3410
|
+
this.getEffectiveActionsForValidation(params.agent.ID).forEach((actionEntity) => register(new ActionInvocable(actionEntity.Name, (p) => this.ExecuteSingleAction(params, { name: actionEntity.Name, params: p }, actionEntity, params.contextUser))));
|
|
3411
|
+
// Artifact tools — one invocable per distinct tool name; `artifactId` is supplied as a
|
|
3412
|
+
// call-time param so the same `{ tool, params }` step shape works across all substrates.
|
|
3413
|
+
this._artifactToolManager.GetAvailableToolNames().forEach((toolName) => register(new ArtifactToolInvocable(toolName, async (tool, p) => {
|
|
3414
|
+
const stored = await this._artifactToolManager.ExecuteSingleToolCall({
|
|
3415
|
+
artifactId: String(p.artifactId ?? ''),
|
|
3416
|
+
tool,
|
|
3417
|
+
input: p,
|
|
3418
|
+
});
|
|
3419
|
+
return stored.result;
|
|
3420
|
+
})));
|
|
3421
|
+
return registry;
|
|
3422
|
+
}
|
|
3423
|
+
/**
|
|
3424
|
+
* Runs a tool pipeline as a single `Tool` step in the run tree (sibling of the prompt step that
|
|
3425
|
+
* requested it, matching artifact-tool steps). ALL pipeline observability lives in this step's
|
|
3426
|
+
* `OutputData` — the per-stage breakdown, totals, bytes saved, and the tool chain — so there are
|
|
3427
|
+
* no dedicated pipeline entities and no extra SQL I/O; the run tree alone carries everything a
|
|
3428
|
+
* debug UI needs.
|
|
3429
|
+
*
|
|
3430
|
+
* @protected
|
|
3431
|
+
*/
|
|
3432
|
+
async executePipelineAsStep(pipeline, params) {
|
|
3433
|
+
const stepEntity = await this.createStepEntity({
|
|
3434
|
+
stepType: 'Tool',
|
|
3435
|
+
stepName: `Pipeline: ${pipeline.steps.length} step(s)`,
|
|
3436
|
+
contextUser: params.contextUser,
|
|
3437
|
+
inputData: { steps: pipeline.steps },
|
|
3438
|
+
});
|
|
3439
|
+
const registry = this.buildPipelineRegistry(params);
|
|
3440
|
+
// The executor converts stage-level errors into a failed RESULT (it doesn't throw for those),
|
|
3441
|
+
// but an unexpected throw — e.g. a tool returning a non-serializable value (BigInt/circular)
|
|
3442
|
+
// that trips JSON.stringify in the executor's byte-accounting — must NEVER leave this step
|
|
3443
|
+
// stuck on 'Running'. Catch it and materialize a failed result so finalize always runs and the
|
|
3444
|
+
// failure surfaces as a 'Failed' step (answering "do pipeline errors show as errors?": yes).
|
|
3445
|
+
let result;
|
|
3446
|
+
try {
|
|
3447
|
+
result = await new PipelineExecutor(registry).Execute(pipeline.steps);
|
|
3448
|
+
}
|
|
3449
|
+
catch (e) {
|
|
3450
|
+
result = {
|
|
3451
|
+
success: false,
|
|
3452
|
+
finalOutput: null,
|
|
3453
|
+
steps: [],
|
|
3454
|
+
error: `Pipeline crashed: ${e?.message ?? String(e)}`,
|
|
3455
|
+
contextBytesSaved: 0,
|
|
3456
|
+
};
|
|
3457
|
+
}
|
|
3458
|
+
// A pipeline is ONE run-step — not a parent + a child step per stage. It runs server-side in a
|
|
3459
|
+
// single fast pass, so the full per-stage breakdown + totals live in this step's OutputData for
|
|
3460
|
+
// a debug UI to visualize — no separate entities, no extra DB writes.
|
|
3461
|
+
await this.finalizeStepEntity(stepEntity, result.success, result.success ? undefined : result.error, {
|
|
3462
|
+
success: result.success,
|
|
3463
|
+
toolChain: summarizePipelineStages(result.steps),
|
|
3464
|
+
steps: result.steps,
|
|
3465
|
+
contextBytesSaved: result.contextBytesSaved,
|
|
3466
|
+
totalBytesStreamed: result.steps.reduce((sum, s) => sum + s.outputSize, 0),
|
|
3467
|
+
totalDurationMs: result.steps.reduce((sum, s) => sum + s.durationMs, 0),
|
|
3468
|
+
failedStepIndex: result.failedStepIndex,
|
|
3469
|
+
});
|
|
3470
|
+
return result;
|
|
3471
|
+
}
|
|
3472
|
+
/**
|
|
3473
|
+
* Pushes the pipeline's final output (or its failure message) into the conversation for the
|
|
3474
|
+
* LLM's next turn, mirroring the artifact-tool "inject once, then expire" pattern. Only the
|
|
3475
|
+
* final output is surfaced — intermediate step outputs never enter the context window.
|
|
3476
|
+
*
|
|
3477
|
+
* @protected
|
|
3478
|
+
*/
|
|
3479
|
+
injectPipelineResultMessage(params, result) {
|
|
3480
|
+
const diagnostic = result.success && result.diagnostic
|
|
3481
|
+
? `\n⚠ Empty result — ${result.diagnostic}`
|
|
3482
|
+
: '';
|
|
3483
|
+
// Identify which pipeline this result belongs to (stage chain, e.g. `get_rows → where →
|
|
3484
|
+
// select`). Without it, multiple pipeline results across turns are indistinguishable once
|
|
3485
|
+
// compacted — mirrors how artifact-tool results name their tool/artifact.
|
|
3486
|
+
const label = summarizePipelineStages(result.steps);
|
|
3487
|
+
const content = result.success
|
|
3488
|
+
? `Pipeline result [${label}] (final stage value — intermediate stages stayed out of context, ~${result.contextBytesSaved} bytes saved):\n\`\`\`\n${formatFinalOutput(result.finalOutput)}\n\`\`\`${diagnostic}`
|
|
3489
|
+
: `Pipeline failed [${label}].\n${result.error}`;
|
|
3490
|
+
const message = {
|
|
3491
|
+
role: 'user',
|
|
3492
|
+
content,
|
|
3493
|
+
metadata: {
|
|
3494
|
+
turnAdded: this._promptTurnCount,
|
|
3495
|
+
messageType: 'tool-result',
|
|
3496
|
+
expirationTurns: 3,
|
|
3497
|
+
expirationMode: 'Compact',
|
|
3498
|
+
compactMode: 'First N Chars',
|
|
3499
|
+
compactLength: 500,
|
|
3500
|
+
compactPromptId: '',
|
|
3501
|
+
},
|
|
3502
|
+
};
|
|
3503
|
+
params.conversationMessages.push(message);
|
|
3504
|
+
}
|
|
3334
3505
|
/**
|
|
3335
3506
|
* Creates a chat message containing sub-agent execution results.
|
|
3336
3507
|
*
|
|
@@ -3523,7 +3694,8 @@ The context is now within limits. Please retry your request with the recovered c
|
|
|
3523
3694
|
{ docsFlag: 'includeForEachDocs', responseTypeKey: 'forEach' },
|
|
3524
3695
|
{ docsFlag: 'includeWhileDocs', responseTypeKey: 'while' },
|
|
3525
3696
|
{ docsFlag: 'includeScratchpadDocs', responseTypeKey: 'scratchpad' },
|
|
3526
|
-
{ docsFlag: 'includeArtifactToolsDocs', responseTypeKey: 'artifactToolCalls' }
|
|
3697
|
+
{ docsFlag: 'includeArtifactToolsDocs', responseTypeKey: 'artifactToolCalls' },
|
|
3698
|
+
{ docsFlag: 'includePipelineDocs', responseTypeKey: 'pipeline' }
|
|
3527
3699
|
];
|
|
3528
3700
|
for (const { docsFlag, responseTypeKey } of alignmentMappings) {
|
|
3529
3701
|
// Check if the user explicitly set this response type property
|
|
@@ -4468,6 +4640,13 @@ The context is now within limits. Please retry your request with the recovered c
|
|
|
4468
4640
|
const errorMessage = JSON.stringify(CopyScalarsAndArrays(this._agentRun.LatestResult));
|
|
4469
4641
|
throw new Error(`Failed to create agent run record: Details: ${errorMessage}`);
|
|
4470
4642
|
}
|
|
4643
|
+
// Hand the now-persisted run (it has a stable ID) to the watchdog so a process restart,
|
|
4644
|
+
// crash, or failed terminal-state write can't leave it stuck 'Running' forever. Only the
|
|
4645
|
+
// server-side DB provider can heartbeat via SQL; client/non-DB providers simply opt out.
|
|
4646
|
+
const runProvider = params.provider || this._activeProvider;
|
|
4647
|
+
if (runProvider instanceof DatabaseProviderBase && params.contextUser) {
|
|
4648
|
+
AgentRunWatchdog.Instance.Track(this._agentRun.ID, runProvider, params.contextUser);
|
|
4649
|
+
}
|
|
4471
4650
|
// Invoke callback if provided
|
|
4472
4651
|
if (modifiedParams.onAgentRunCreated) {
|
|
4473
4652
|
try {
|
|
@@ -4672,15 +4851,16 @@ The context is now within limits. Please retry your request with the recovered c
|
|
|
4672
4851
|
* @protected
|
|
4673
4852
|
*/
|
|
4674
4853
|
queueStepSave(stepEntity) {
|
|
4675
|
-
|
|
4676
|
-
|
|
4854
|
+
// Chain on the entity INSTANCE (stable), NOT stepEntity.ID — the ID is empty until the INSERT
|
|
4855
|
+
// Save() assigns it, so an ID-keyed chain breaks for fast create→finalize sequences.
|
|
4856
|
+
const previousSave = this._stepSavePromises.get(stepEntity) ?? Promise.resolve();
|
|
4677
4857
|
const currentSave = previousSave.then(() => stepEntity.Save()).then((ok) => {
|
|
4678
4858
|
if (!ok) {
|
|
4679
|
-
LogError(`Failed to save agent run step record ${
|
|
4859
|
+
LogError(`Failed to save agent run step record ${stepEntity.ID || '(unsaved)'}: ${stepEntity.LatestResult?.CompleteMessage ?? 'unknown error'}`);
|
|
4680
4860
|
}
|
|
4681
4861
|
return ok;
|
|
4682
4862
|
});
|
|
4683
|
-
this._stepSavePromises.set(
|
|
4863
|
+
this._stepSavePromises.set(stepEntity, currentSave);
|
|
4684
4864
|
this._pendingSaves.push(currentSave);
|
|
4685
4865
|
}
|
|
4686
4866
|
/**
|
|
@@ -5183,6 +5363,14 @@ The context is now within limits. Please retry your request with the recovered c
|
|
|
5183
5363
|
else if (this._artifactToolManager.HasArtifacts()) {
|
|
5184
5364
|
this.logStatus(`[ArtifactTools] LLM did not use artifact tools this turn (artifacts available but not accessed)`, true, params);
|
|
5185
5365
|
}
|
|
5366
|
+
// Execute a tool pipeline if provided (zero turn cost — processed inline). Each step's
|
|
5367
|
+
// output is threaded into the next server-side; only the final step's output returns to
|
|
5368
|
+
// the LLM, so intermediate payloads never enter the context window.
|
|
5369
|
+
if (initialNextStep.pipeline?.steps?.length) {
|
|
5370
|
+
this.logStatus(`[Pipeline] LLM requested a ${initialNextStep.pipeline.steps.length}-stage pipeline: ${initialNextStep.pipeline.steps.map(s => s.tool ?? Object.keys(s)[0]).join(' | ')}`, true, params);
|
|
5371
|
+
const pipelineResult = await this.executePipelineAsStep(initialNextStep.pipeline, params);
|
|
5372
|
+
this.injectPipelineResultMessage(params, pipelineResult);
|
|
5373
|
+
}
|
|
5186
5374
|
// now that we have processed the payload, we can process the next step which does validation and changes the next step if
|
|
5187
5375
|
// validation fails
|
|
5188
5376
|
const updatedNextStep = await this.processNextStep(initialNextStep, params, config.agentType, promptResult, finalPayload, stepEntity);
|
|
@@ -7901,6 +8089,8 @@ The context is now within limits. Please retry your request with the recovered c
|
|
|
7901
8089
|
this._agentRun.TotalTokensUsed = tokenStats.totalTokens;
|
|
7902
8090
|
this._agentRun.TotalPromptTokensUsed = tokenStats.promptTokens;
|
|
7903
8091
|
this._agentRun.TotalCompletionTokensUsed = tokenStats.completionTokens;
|
|
8092
|
+
this._agentRun.TotalCacheReadTokensUsed = tokenStats.cacheReadTokens;
|
|
8093
|
+
this._agentRun.TotalCacheWriteTokensUsed = tokenStats.cacheWriteTokens;
|
|
7904
8094
|
this._agentRun.TotalCost = tokenStats.totalCost;
|
|
7905
8095
|
await this._agentRun.Save();
|
|
7906
8096
|
}
|
|
@@ -7927,6 +8117,8 @@ The context is now within limits. Please retry your request with the recovered c
|
|
|
7927
8117
|
this._agentRun.TotalTokensUsed = tokenStats.totalTokens;
|
|
7928
8118
|
this._agentRun.TotalPromptTokensUsed = tokenStats.promptTokens;
|
|
7929
8119
|
this._agentRun.TotalCompletionTokensUsed = tokenStats.completionTokens;
|
|
8120
|
+
this._agentRun.TotalCacheReadTokensUsed = tokenStats.cacheReadTokens;
|
|
8121
|
+
this._agentRun.TotalCacheWriteTokensUsed = tokenStats.cacheWriteTokens;
|
|
7930
8122
|
this._agentRun.TotalCost = tokenStats.totalCost;
|
|
7931
8123
|
await this._agentRun.Save();
|
|
7932
8124
|
}
|
|
@@ -8008,6 +8200,8 @@ The context is now within limits. Please retry your request with the recovered c
|
|
|
8008
8200
|
this._agentRun.TotalTokensUsed = tokenStats.totalTokens;
|
|
8009
8201
|
this._agentRun.TotalPromptTokensUsed = tokenStats.promptTokens;
|
|
8010
8202
|
this._agentRun.TotalCompletionTokensUsed = tokenStats.completionTokens;
|
|
8203
|
+
this._agentRun.TotalCacheReadTokensUsed = tokenStats.cacheReadTokens;
|
|
8204
|
+
this._agentRun.TotalCacheWriteTokensUsed = tokenStats.cacheWriteTokens;
|
|
8011
8205
|
this._agentRun.TotalCost = tokenStats.totalCost;
|
|
8012
8206
|
const ok = await this._agentRun.Save();
|
|
8013
8207
|
if (!ok) {
|
|
@@ -8046,15 +8240,19 @@ The context is now within limits. Please retry your request with the recovered c
|
|
|
8046
8240
|
let totalTokens = 0;
|
|
8047
8241
|
let promptTokens = 0;
|
|
8048
8242
|
let completionTokens = 0;
|
|
8243
|
+
let cacheReadTokens = 0;
|
|
8244
|
+
let cacheWriteTokens = 0;
|
|
8049
8245
|
let totalCost = 0;
|
|
8050
8246
|
// Iterate through the agent run's steps to sum up tokens
|
|
8051
8247
|
if (this._agentRun?.Steps) {
|
|
8052
8248
|
for (const step of this._agentRun.Steps) {
|
|
8053
8249
|
if (step.StepType === 'Prompt' && step.PromptRun) {
|
|
8054
|
-
// Add tokens from prompt runs
|
|
8250
|
+
// Add tokens from prompt runs (rollup fields include any nested child prompt runs)
|
|
8055
8251
|
totalTokens += step.PromptRun.TokensUsedRollup || 0;
|
|
8056
8252
|
promptTokens += step.PromptRun.TokensPromptRollup || 0;
|
|
8057
8253
|
completionTokens += step.PromptRun.TokensCompletionRollup || 0;
|
|
8254
|
+
cacheReadTokens += step.PromptRun.TokensCacheReadRollup || 0;
|
|
8255
|
+
cacheWriteTokens += step.PromptRun.TokensCacheWriteRollup || 0;
|
|
8058
8256
|
totalCost += step.PromptRun.TotalCost || 0;
|
|
8059
8257
|
}
|
|
8060
8258
|
else if (step.StepType === 'Sub-Agent' && step.SubAgentRun) {
|
|
@@ -8062,11 +8260,13 @@ The context is now within limits. Please retry your request with the recovered c
|
|
|
8062
8260
|
totalTokens += step.SubAgentRun.TotalTokensUsed || 0;
|
|
8063
8261
|
promptTokens += step.SubAgentRun.TotalPromptTokensUsed || 0;
|
|
8064
8262
|
completionTokens += step.SubAgentRun.TotalCompletionTokensUsed || 0;
|
|
8263
|
+
cacheReadTokens += step.SubAgentRun.TotalCacheReadTokensUsed || 0;
|
|
8264
|
+
cacheWriteTokens += step.SubAgentRun.TotalCacheWriteTokensUsed || 0;
|
|
8065
8265
|
totalCost += step.SubAgentRun.TotalCost || 0;
|
|
8066
8266
|
}
|
|
8067
8267
|
}
|
|
8068
8268
|
}
|
|
8069
|
-
return { totalTokens, promptTokens, completionTokens, totalCost };
|
|
8269
|
+
return { totalTokens, promptTokens, completionTokens, cacheReadTokens, cacheWriteTokens, totalCost };
|
|
8070
8270
|
}
|
|
8071
8271
|
/**
|
|
8072
8272
|
* Gets the count of how many times a specific action has been executed in this agent run.
|