@librechat/agents 3.1.66-dev.0 → 3.1.67
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/cjs/agents/AgentContext.cjs +24 -15
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/common/enum.cjs +0 -13
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +0 -3
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/main.cjs +0 -40
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +12 -74
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/run.cjs +0 -111
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +140 -304
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +24 -15
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/common/enum.mjs +1 -12
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +0 -3
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/main.mjs +1 -10
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +4 -66
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/run.mjs +0 -111
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +142 -306
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +6 -0
- package/dist/types/common/enum.d.ts +1 -7
- package/dist/types/graphs/Graph.d.ts +0 -2
- package/dist/types/index.d.ts +0 -6
- package/dist/types/messages/format.d.ts +1 -2
- package/dist/types/run.d.ts +0 -1
- package/dist/types/tools/ToolNode.d.ts +2 -24
- package/dist/types/types/index.d.ts +0 -1
- package/dist/types/types/llm.d.ts +14 -2
- package/dist/types/types/run.d.ts +0 -20
- package/dist/types/types/tools.d.ts +1 -38
- package/package.json +1 -1
- package/src/agents/AgentContext.ts +28 -15
- package/src/agents/__tests__/AgentContext.test.ts +110 -0
- package/src/common/enum.ts +0 -12
- package/src/graphs/Graph.ts +0 -4
- package/src/index.ts +0 -8
- package/src/messages/format.ts +4 -74
- package/src/run.ts +0 -126
- package/src/tools/ToolNode.ts +169 -391
- package/src/tools/__tests__/ToolNode.session.test.ts +12 -12
- package/src/types/index.ts +0 -1
- package/src/types/llm.ts +16 -2
- package/src/types/run.ts +0 -20
- package/src/types/tools.ts +1 -41
- package/dist/cjs/hooks/HookRegistry.cjs +0 -162
- package/dist/cjs/hooks/HookRegistry.cjs.map +0 -1
- package/dist/cjs/hooks/executeHooks.cjs +0 -276
- package/dist/cjs/hooks/executeHooks.cjs.map +0 -1
- package/dist/cjs/hooks/matchers.cjs +0 -256
- package/dist/cjs/hooks/matchers.cjs.map +0 -1
- package/dist/cjs/hooks/types.cjs +0 -27
- package/dist/cjs/hooks/types.cjs.map +0 -1
- package/dist/cjs/tools/BashExecutor.cjs +0 -175
- package/dist/cjs/tools/BashExecutor.cjs.map +0 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +0 -296
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +0 -1
- package/dist/cjs/tools/ReadFile.cjs +0 -43
- package/dist/cjs/tools/ReadFile.cjs.map +0 -1
- package/dist/cjs/tools/SkillTool.cjs +0 -50
- package/dist/cjs/tools/SkillTool.cjs.map +0 -1
- package/dist/cjs/tools/skillCatalog.cjs +0 -84
- package/dist/cjs/tools/skillCatalog.cjs.map +0 -1
- package/dist/esm/hooks/HookRegistry.mjs +0 -160
- package/dist/esm/hooks/HookRegistry.mjs.map +0 -1
- package/dist/esm/hooks/executeHooks.mjs +0 -273
- package/dist/esm/hooks/executeHooks.mjs.map +0 -1
- package/dist/esm/hooks/matchers.mjs +0 -251
- package/dist/esm/hooks/matchers.mjs.map +0 -1
- package/dist/esm/hooks/types.mjs +0 -25
- package/dist/esm/hooks/types.mjs.map +0 -1
- package/dist/esm/tools/BashExecutor.mjs +0 -169
- package/dist/esm/tools/BashExecutor.mjs.map +0 -1
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs +0 -287
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +0 -1
- package/dist/esm/tools/ReadFile.mjs +0 -38
- package/dist/esm/tools/ReadFile.mjs.map +0 -1
- package/dist/esm/tools/SkillTool.mjs +0 -45
- package/dist/esm/tools/SkillTool.mjs.map +0 -1
- package/dist/esm/tools/skillCatalog.mjs +0 -82
- package/dist/esm/tools/skillCatalog.mjs.map +0 -1
- package/dist/types/hooks/HookRegistry.d.ts +0 -56
- package/dist/types/hooks/executeHooks.d.ts +0 -79
- package/dist/types/hooks/index.d.ts +0 -6
- package/dist/types/hooks/matchers.d.ts +0 -95
- package/dist/types/hooks/types.d.ts +0 -309
- package/dist/types/tools/BashExecutor.d.ts +0 -45
- package/dist/types/tools/BashProgrammaticToolCalling.d.ts +0 -72
- package/dist/types/tools/ReadFile.d.ts +0 -28
- package/dist/types/tools/SkillTool.d.ts +0 -40
- package/dist/types/tools/skillCatalog.d.ts +0 -19
- package/dist/types/types/skill.d.ts +0 -9
- package/src/hooks/HookRegistry.ts +0 -208
- package/src/hooks/__tests__/HookRegistry.test.ts +0 -190
- package/src/hooks/__tests__/executeHooks.test.ts +0 -1013
- package/src/hooks/__tests__/integration.test.ts +0 -337
- package/src/hooks/__tests__/matchers.test.ts +0 -238
- package/src/hooks/__tests__/toolHooks.test.ts +0 -669
- package/src/hooks/executeHooks.ts +0 -375
- package/src/hooks/index.ts +0 -55
- package/src/hooks/matchers.ts +0 -280
- package/src/hooks/types.ts +0 -388
- package/src/messages/formatAgentMessages.skills.test.ts +0 -334
- package/src/tools/BashExecutor.ts +0 -205
- package/src/tools/BashProgrammaticToolCalling.ts +0 -397
- package/src/tools/ReadFile.ts +0 -39
- package/src/tools/SkillTool.ts +0 -46
- package/src/tools/__tests__/ReadFile.test.ts +0 -44
- package/src/tools/__tests__/SkillTool.test.ts +0 -442
- package/src/tools/__tests__/skillCatalog.test.ts +0 -161
- package/src/tools/skillCatalog.ts +0 -126
- package/src/types/skill.ts +0 -11
|
@@ -664,6 +664,17 @@ export class AgentContext {
|
|
|
664
664
|
this.indexTokenCountMap = { ...baseTokenMap };
|
|
665
665
|
}
|
|
666
666
|
|
|
667
|
+
/** Active tool definitions for token accounting (excludes deferred-and-undiscovered entries). */
|
|
668
|
+
private getActiveToolDefinitions(): t.LCTool[] {
|
|
669
|
+
if (!this.toolDefinitions) {
|
|
670
|
+
return [];
|
|
671
|
+
}
|
|
672
|
+
return this.toolDefinitions.filter(
|
|
673
|
+
(def) =>
|
|
674
|
+
def.defer_loading !== true || this.discoveredToolNames.has(def.name)
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
|
|
667
678
|
/**
|
|
668
679
|
* Calculate tool tokens and add to instruction tokens
|
|
669
680
|
* Note: System message tokens are calculated during systemRunnable creation
|
|
@@ -697,21 +708,19 @@ export class AgentContext {
|
|
|
697
708
|
}
|
|
698
709
|
}
|
|
699
710
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
continue;
|
|
704
|
-
}
|
|
705
|
-
const schema = {
|
|
706
|
-
type: 'function',
|
|
707
|
-
function: {
|
|
708
|
-
name: def.name,
|
|
709
|
-
description: def.description ?? '',
|
|
710
|
-
parameters: def.parameters ?? {},
|
|
711
|
-
},
|
|
712
|
-
};
|
|
713
|
-
toolTokens += tokenCounter(new SystemMessage(JSON.stringify(schema)));
|
|
711
|
+
for (const def of this.getActiveToolDefinitions()) {
|
|
712
|
+
if (countedToolNames.has(def.name)) {
|
|
713
|
+
continue;
|
|
714
714
|
}
|
|
715
|
+
const schema = {
|
|
716
|
+
type: 'function',
|
|
717
|
+
function: {
|
|
718
|
+
name: def.name,
|
|
719
|
+
description: def.description ?? '',
|
|
720
|
+
parameters: def.parameters ?? {},
|
|
721
|
+
},
|
|
722
|
+
};
|
|
723
|
+
toolTokens += tokenCounter(new SystemMessage(JSON.stringify(schema)));
|
|
715
724
|
}
|
|
716
725
|
|
|
717
726
|
const isAnthropic =
|
|
@@ -860,11 +869,15 @@ export class AgentContext {
|
|
|
860
869
|
/**
|
|
861
870
|
* Returns a structured breakdown of how the context token budget is consumed.
|
|
862
871
|
* Useful for diagnostics when context overflow or pruning issues occur.
|
|
872
|
+
*
|
|
873
|
+
* Note: `toolCount` reflects discoveries immediately, but `toolSchemaTokens`
|
|
874
|
+
* is a snapshot taken during `calculateInstructionTokens` and is not
|
|
875
|
+
* recomputed when `markToolsAsDiscovered` is called mid-run.
|
|
863
876
|
*/
|
|
864
877
|
getTokenBudgetBreakdown(messages?: BaseMessage[]): t.TokenBudgetBreakdown {
|
|
865
878
|
const maxContextTokens = this.maxContextTokens ?? 0;
|
|
866
879
|
const toolCount =
|
|
867
|
-
(this.tools?.length ?? 0) +
|
|
880
|
+
(this.tools?.length ?? 0) + this.getActiveToolDefinitions().length;
|
|
868
881
|
const messageCount = messages?.length ?? 0;
|
|
869
882
|
|
|
870
883
|
let messageTokens = 0;
|
|
@@ -375,6 +375,116 @@ describe('AgentContext', () => {
|
|
|
375
375
|
|
|
376
376
|
expect(ctx.instructionTokens).toBeGreaterThan(initialTokens);
|
|
377
377
|
});
|
|
378
|
+
|
|
379
|
+
it('excludes deferred-undiscovered toolDefinitions from toolSchemaTokens', async () => {
|
|
380
|
+
const activeDef: t.LCTool = {
|
|
381
|
+
name: 'active_tool',
|
|
382
|
+
description: 'Always loaded',
|
|
383
|
+
parameters: { type: 'object', properties: {} },
|
|
384
|
+
};
|
|
385
|
+
const deferredDef: t.LCTool = {
|
|
386
|
+
name: 'deferred_tool',
|
|
387
|
+
description: 'Loaded via tool search',
|
|
388
|
+
parameters: { type: 'object', properties: {} },
|
|
389
|
+
defer_loading: true,
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
const ctxBase = createBasicContext({
|
|
393
|
+
agentConfig: { toolDefinitions: [activeDef] },
|
|
394
|
+
tokenCounter: mockTokenCounter,
|
|
395
|
+
});
|
|
396
|
+
const ctxWithDeferred = createBasicContext({
|
|
397
|
+
agentConfig: { toolDefinitions: [activeDef, deferredDef] },
|
|
398
|
+
tokenCounter: mockTokenCounter,
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
await ctxBase.tokenCalculationPromise;
|
|
402
|
+
await ctxWithDeferred.tokenCalculationPromise;
|
|
403
|
+
|
|
404
|
+
expect(ctxWithDeferred.toolSchemaTokens).toBe(ctxBase.toolSchemaTokens);
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it('includes deferred toolDefinitions once discovered via discoveredTools input', async () => {
|
|
408
|
+
const toolDefinitions: t.LCTool[] = [
|
|
409
|
+
{
|
|
410
|
+
name: 'deferred_tool',
|
|
411
|
+
description: 'Loaded via tool search',
|
|
412
|
+
parameters: { type: 'object', properties: {} },
|
|
413
|
+
defer_loading: true,
|
|
414
|
+
},
|
|
415
|
+
];
|
|
416
|
+
|
|
417
|
+
const ctxUndiscovered = createBasicContext({
|
|
418
|
+
agentConfig: { toolDefinitions },
|
|
419
|
+
tokenCounter: mockTokenCounter,
|
|
420
|
+
});
|
|
421
|
+
const ctxDiscovered = createBasicContext({
|
|
422
|
+
agentConfig: { toolDefinitions, discoveredTools: ['deferred_tool'] },
|
|
423
|
+
tokenCounter: mockTokenCounter,
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
await ctxUndiscovered.tokenCalculationPromise;
|
|
427
|
+
await ctxDiscovered.tokenCalculationPromise;
|
|
428
|
+
|
|
429
|
+
expect(ctxUndiscovered.toolSchemaTokens).toBe(0);
|
|
430
|
+
expect(ctxDiscovered.toolSchemaTokens).toBeGreaterThan(0);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
it('getTokenBudgetBreakdown toolCount excludes deferred-undiscovered toolDefinitions', () => {
|
|
434
|
+
const toolDefinitions: t.LCTool[] = [
|
|
435
|
+
{
|
|
436
|
+
name: 'active',
|
|
437
|
+
parameters: { type: 'object', properties: {} },
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
name: 'deferred',
|
|
441
|
+
defer_loading: true,
|
|
442
|
+
parameters: { type: 'object', properties: {} },
|
|
443
|
+
},
|
|
444
|
+
];
|
|
445
|
+
|
|
446
|
+
const ctx = createBasicContext({ agentConfig: { toolDefinitions } });
|
|
447
|
+
|
|
448
|
+
expect(ctx.getTokenBudgetBreakdown().toolCount).toBe(1);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('getTokenBudgetBreakdown toolCount reflects newly discovered deferred tools', () => {
|
|
452
|
+
const toolDefinitions: t.LCTool[] = [
|
|
453
|
+
{
|
|
454
|
+
name: 'deferred',
|
|
455
|
+
defer_loading: true,
|
|
456
|
+
parameters: { type: 'object', properties: {} },
|
|
457
|
+
},
|
|
458
|
+
];
|
|
459
|
+
|
|
460
|
+
const ctx = createBasicContext({ agentConfig: { toolDefinitions } });
|
|
461
|
+
|
|
462
|
+
expect(ctx.getTokenBudgetBreakdown().toolCount).toBe(0);
|
|
463
|
+
ctx.markToolsAsDiscovered(['deferred']);
|
|
464
|
+
expect(ctx.getTokenBudgetBreakdown().toolCount).toBe(1);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it('toolSchemaTokens snapshot does not auto-update after markToolsAsDiscovered', async () => {
|
|
468
|
+
const toolDefinitions: t.LCTool[] = [
|
|
469
|
+
{
|
|
470
|
+
name: 'deferred',
|
|
471
|
+
description: 'Loaded via tool search',
|
|
472
|
+
parameters: { type: 'object', properties: {} },
|
|
473
|
+
defer_loading: true,
|
|
474
|
+
},
|
|
475
|
+
];
|
|
476
|
+
|
|
477
|
+
const ctx = createBasicContext({
|
|
478
|
+
agentConfig: { toolDefinitions },
|
|
479
|
+
tokenCounter: mockTokenCounter,
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
await ctx.tokenCalculationPromise;
|
|
483
|
+
expect(ctx.toolSchemaTokens).toBe(0);
|
|
484
|
+
|
|
485
|
+
ctx.markToolsAsDiscovered(['deferred']);
|
|
486
|
+
expect(ctx.toolSchemaTokens).toBe(0);
|
|
487
|
+
});
|
|
378
488
|
});
|
|
379
489
|
|
|
380
490
|
describe('reset()', () => {
|
package/src/common/enum.ts
CHANGED
|
@@ -182,20 +182,8 @@ export enum Constants {
|
|
|
182
182
|
MCP_DELIMITER = '_mcp_',
|
|
183
183
|
/** Anthropic server tool ID prefix (web_search, code_execution, etc.) */
|
|
184
184
|
ANTHROPIC_SERVER_TOOL_PREFIX = 'srvtoolu_',
|
|
185
|
-
SKILL_TOOL = 'skill',
|
|
186
|
-
READ_FILE = 'read_file',
|
|
187
|
-
BASH_TOOL = 'bash_tool',
|
|
188
|
-
BASH_PROGRAMMATIC_TOOL_CALLING = 'run_tools_with_bash',
|
|
189
185
|
}
|
|
190
186
|
|
|
191
|
-
/** Tool names that use the code execution environment (shared session, file tracking). */
|
|
192
|
-
export const CODE_EXECUTION_TOOLS: ReadonlySet<string> = new Set([
|
|
193
|
-
Constants.EXECUTE_CODE,
|
|
194
|
-
Constants.BASH_TOOL,
|
|
195
|
-
Constants.PROGRAMMATIC_TOOL_CALLING,
|
|
196
|
-
Constants.BASH_PROGRAMMATIC_TOOL_CALLING,
|
|
197
|
-
]);
|
|
198
|
-
|
|
199
187
|
export enum TitleMethod {
|
|
200
188
|
STRUCTURED = 'structured',
|
|
201
189
|
FUNCTIONS = 'functions',
|
package/src/graphs/Graph.ts
CHANGED
|
@@ -53,7 +53,6 @@ import { isThinkingEnabled } from '@/llm/request';
|
|
|
53
53
|
import { initializeModel } from '@/llm/init';
|
|
54
54
|
import { HandlerRegistry } from '@/events';
|
|
55
55
|
import { ChatOpenAI } from '@/llm/openai';
|
|
56
|
-
import type { HookRegistry } from '@/hooks';
|
|
57
56
|
|
|
58
57
|
const { AGENT, TOOLS, SUMMARIZE } = GraphNodeKeys;
|
|
59
58
|
|
|
@@ -124,7 +123,6 @@ export abstract class Graph<
|
|
|
124
123
|
/** Set of invoked tool call IDs from non-message run steps completed mid-run, if any */
|
|
125
124
|
invokedToolIds?: Set<string>;
|
|
126
125
|
handlerRegistry: HandlerRegistry | undefined;
|
|
127
|
-
hookRegistry: HookRegistry | undefined;
|
|
128
126
|
/**
|
|
129
127
|
* Tool session contexts for automatic state persistence across tool invocations.
|
|
130
128
|
* Keyed by tool name (e.g., Constants.EXECUTE_CODE).
|
|
@@ -149,7 +147,6 @@ export abstract class Graph<
|
|
|
149
147
|
this.prelimMessageIdsByStepKey = new Map();
|
|
150
148
|
this.invokedToolIds = undefined;
|
|
151
149
|
this.handlerRegistry = undefined;
|
|
152
|
-
this.hookRegistry = undefined;
|
|
153
150
|
this.sessions.clear();
|
|
154
151
|
}
|
|
155
152
|
}
|
|
@@ -509,7 +506,6 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
509
506
|
agentId: agentContext?.agentId,
|
|
510
507
|
toolCallStepIds: this.toolCallStepIds,
|
|
511
508
|
toolRegistry: agentContext?.toolRegistry,
|
|
512
|
-
hookRegistry: this.hookRegistry,
|
|
513
509
|
directToolNames: directToolNames.size > 0 ? directToolNames : undefined,
|
|
514
510
|
maxContextTokens: agentContext?.maxContextTokens,
|
|
515
511
|
maxToolResultChars: agentContext?.maxToolResultChars,
|
package/src/index.ts
CHANGED
|
@@ -14,12 +14,7 @@ export * from './summarization';
|
|
|
14
14
|
/* Tools */
|
|
15
15
|
export * from './tools/Calculator';
|
|
16
16
|
export * from './tools/CodeExecutor';
|
|
17
|
-
export * from './tools/BashExecutor';
|
|
18
17
|
export * from './tools/ProgrammaticToolCalling';
|
|
19
|
-
export * from './tools/BashProgrammaticToolCalling';
|
|
20
|
-
export * from './tools/SkillTool';
|
|
21
|
-
export * from './tools/ReadFile';
|
|
22
|
-
export * from './tools/skillCatalog';
|
|
23
18
|
export * from './tools/ToolSearch';
|
|
24
19
|
export * from './tools/ToolNode';
|
|
25
20
|
export * from './tools/schema';
|
|
@@ -30,9 +25,6 @@ export * from './tools/search';
|
|
|
30
25
|
export * from './common';
|
|
31
26
|
export * from './utils';
|
|
32
27
|
|
|
33
|
-
/* Hooks */
|
|
34
|
-
export * from './hooks';
|
|
35
|
-
|
|
36
28
|
/* Types */
|
|
37
29
|
export type * from './types';
|
|
38
30
|
|
package/src/messages/format.ts
CHANGED
|
@@ -797,39 +797,18 @@ function contentPartCharLength(part: MessageContentComplex): number {
|
|
|
797
797
|
return len;
|
|
798
798
|
}
|
|
799
799
|
|
|
800
|
-
/** Extracts the skillName from a skill tool_call's args (string or object). */
|
|
801
|
-
function extractSkillName(args: unknown): string | undefined {
|
|
802
|
-
let parsed: Record<string, unknown> | undefined;
|
|
803
|
-
if (typeof args === 'string') {
|
|
804
|
-
try {
|
|
805
|
-
parsed = JSON.parse(args) as Record<string, unknown>;
|
|
806
|
-
} catch {
|
|
807
|
-
/* malformed args — skip */
|
|
808
|
-
}
|
|
809
|
-
} else {
|
|
810
|
-
parsed = args as Record<string, unknown> | undefined;
|
|
811
|
-
}
|
|
812
|
-
const name = parsed?.skillName;
|
|
813
|
-
return typeof name === 'string' && name !== '' ? name : undefined;
|
|
814
|
-
}
|
|
815
|
-
|
|
816
800
|
/**
|
|
817
801
|
* Formats an array of messages for LangChain, handling tool calls and creating ToolMessage instances.
|
|
818
802
|
*
|
|
819
803
|
* @param payload - The array of messages to format.
|
|
820
804
|
* @param indexTokenCountMap - Optional map of message indices to token counts.
|
|
821
805
|
* @param tools - Optional set of tool names that are allowed in the request.
|
|
822
|
-
* @param skills - Optional map of skill name to body for reconstructing skill HumanMessages.
|
|
823
806
|
* @returns - Object containing formatted messages and updated indexTokenCountMap if provided.
|
|
824
807
|
*/
|
|
825
808
|
export const formatAgentMessages = (
|
|
826
809
|
payload: TPayload,
|
|
827
810
|
indexTokenCountMap?: Record<number, number | undefined>,
|
|
828
|
-
tools?: Set<string
|
|
829
|
-
/** Pre-resolved skill bodies keyed by skill name. When present, HumanMessages
|
|
830
|
-
* are reconstructed after skill ToolMessages to restore skill instructions
|
|
831
|
-
* that were only in LangGraph state during the original run. */
|
|
832
|
-
skills?: Map<string, string>
|
|
811
|
+
tools?: Set<string>
|
|
833
812
|
): {
|
|
834
813
|
messages: Array<HumanMessage | AIMessage | SystemMessage | ToolMessage>;
|
|
835
814
|
indexTokenCountMap?: Record<number, number>;
|
|
@@ -923,7 +902,6 @@ export const formatAgentMessages = (
|
|
|
923
902
|
* - Dynamically expand the set when tool_search results are encountered
|
|
924
903
|
*/
|
|
925
904
|
let processedMessage = message;
|
|
926
|
-
let pendingSkillNames: Set<string> | undefined;
|
|
927
905
|
if (discoveredTools) {
|
|
928
906
|
const content = message.content;
|
|
929
907
|
if (content != null && Array.isArray(content)) {
|
|
@@ -972,17 +950,8 @@ export const formatAgentMessages = (
|
|
|
972
950
|
}
|
|
973
951
|
|
|
974
952
|
if (discoveredTools.has(toolName)) {
|
|
953
|
+
/** Valid tool - keep it */
|
|
975
954
|
filteredContent.push(part);
|
|
976
|
-
if (
|
|
977
|
-
toolName === Constants.SKILL_TOOL &&
|
|
978
|
-
skills?.size != null &&
|
|
979
|
-
skills.size > 0
|
|
980
|
-
) {
|
|
981
|
-
const skillName = extractSkillName(part.tool_call.args) ?? '';
|
|
982
|
-
if (skillName) {
|
|
983
|
-
(pendingSkillNames ??= new Set()).add(skillName);
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
955
|
} else {
|
|
987
956
|
/** Invalid tool - convert to string for context preservation */
|
|
988
957
|
if (
|
|
@@ -1058,25 +1027,6 @@ export const formatAgentMessages = (
|
|
|
1058
1027
|
}
|
|
1059
1028
|
}
|
|
1060
1029
|
|
|
1061
|
-
/** When tools filtering is off, still detect skill tool_calls for body reconstruction */
|
|
1062
|
-
if (!discoveredTools && skills?.size != null && skills.size > 0) {
|
|
1063
|
-
const content = processedMessage.content;
|
|
1064
|
-
if (Array.isArray(content)) {
|
|
1065
|
-
for (const part of content) {
|
|
1066
|
-
if (
|
|
1067
|
-
part.type !== ContentTypes.TOOL_CALL ||
|
|
1068
|
-
part.tool_call?.name !== Constants.SKILL_TOOL
|
|
1069
|
-
) {
|
|
1070
|
-
continue;
|
|
1071
|
-
}
|
|
1072
|
-
const skillName = extractSkillName(part.tool_call.args) ?? '';
|
|
1073
|
-
if (skillName) {
|
|
1074
|
-
(pendingSkillNames ??= new Set()).add(skillName);
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
1030
|
const formattedMessages = formatAssistantMessage(processedMessage);
|
|
1081
1031
|
if (sourceMessageId != null && sourceMessageId !== '') {
|
|
1082
1032
|
for (const formattedMessage of formattedMessages) {
|
|
@@ -1085,29 +1035,9 @@ export const formatAgentMessages = (
|
|
|
1085
1035
|
}
|
|
1086
1036
|
messages.push(...formattedMessages);
|
|
1087
1037
|
|
|
1088
|
-
//
|
|
1089
|
-
//
|
|
1038
|
+
// Update the index mapping for this assistant message
|
|
1039
|
+
// Store all indices that were created from this original message
|
|
1090
1040
|
const endMessageIndex = messages.length;
|
|
1091
|
-
|
|
1092
|
-
if (pendingSkillNames?.size != null && pendingSkillNames.size > 0) {
|
|
1093
|
-
for (const skillName of pendingSkillNames) {
|
|
1094
|
-
const body = skills?.get(skillName) ?? '';
|
|
1095
|
-
if (body) {
|
|
1096
|
-
messages.push(
|
|
1097
|
-
new HumanMessage({
|
|
1098
|
-
content: body,
|
|
1099
|
-
additional_kwargs: {
|
|
1100
|
-
role: 'user',
|
|
1101
|
-
isMeta: true,
|
|
1102
|
-
source: 'skill',
|
|
1103
|
-
skillName,
|
|
1104
|
-
},
|
|
1105
|
-
})
|
|
1106
|
-
);
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
1041
|
const resultIndices = [];
|
|
1112
1042
|
for (let j = startMessageIndex; j < endMessageIndex; j++) {
|
|
1113
1043
|
resultIndices.push(j);
|
package/src/run.ts
CHANGED
|
@@ -22,10 +22,8 @@ import { MultiAgentGraph } from '@/graphs/MultiAgentGraph';
|
|
|
22
22
|
import { StandardGraph } from '@/graphs/Graph';
|
|
23
23
|
import { initializeModel } from '@/llm/init';
|
|
24
24
|
import { HandlerRegistry } from '@/events';
|
|
25
|
-
import { executeHooks } from '@/hooks';
|
|
26
25
|
import { isOpenAILike } from '@/utils/llm';
|
|
27
26
|
import { isPresent } from '@/utils/misc';
|
|
28
|
-
import type { HookRegistry } from '@/hooks';
|
|
29
27
|
|
|
30
28
|
export const defaultOmitOptions = new Set([
|
|
31
29
|
'stream',
|
|
@@ -44,7 +42,6 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
44
42
|
id: string;
|
|
45
43
|
private tokenCounter?: t.TokenCounter;
|
|
46
44
|
private handlerRegistry?: HandlerRegistry;
|
|
47
|
-
private hookRegistry?: HookRegistry;
|
|
48
45
|
private indexTokenCountMap?: Record<string, number>;
|
|
49
46
|
calibrationRatio: number = 1;
|
|
50
47
|
graphRunnable?: t.CompiledStateWorkflow;
|
|
@@ -77,7 +74,6 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
77
74
|
}
|
|
78
75
|
|
|
79
76
|
this.handlerRegistry = handlerRegistry;
|
|
80
|
-
this.hookRegistry = config.hooks;
|
|
81
77
|
|
|
82
78
|
if (!config.graphConfig) {
|
|
83
79
|
throw new Error('Graph config not provided');
|
|
@@ -99,12 +95,6 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
99
95
|
}
|
|
100
96
|
}
|
|
101
97
|
|
|
102
|
-
if (config.initialSessions && this.Graph) {
|
|
103
|
-
for (const [key, value] of config.initialSessions) {
|
|
104
|
-
this.Graph.sessions.set(key, value);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
98
|
this.returnContent = config.returnContent ?? false;
|
|
109
99
|
this.skipCleanup = config.skipCleanup ?? false;
|
|
110
100
|
}
|
|
@@ -153,7 +143,6 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
153
143
|
});
|
|
154
144
|
/** Propagate compile options from graph config */
|
|
155
145
|
standardGraph.compileOptions = config.compileOptions;
|
|
156
|
-
standardGraph.hookRegistry = this.hookRegistry;
|
|
157
146
|
this.Graph = standardGraph;
|
|
158
147
|
return standardGraph.createWorkflow();
|
|
159
148
|
}
|
|
@@ -176,7 +165,6 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
176
165
|
multiAgentGraph.compileOptions = compileOptions;
|
|
177
166
|
}
|
|
178
167
|
|
|
179
|
-
multiAgentGraph.hookRegistry = this.hookRegistry;
|
|
180
168
|
this.Graph = multiAgentGraph;
|
|
181
169
|
return multiAgentGraph.createWorkflow();
|
|
182
170
|
}
|
|
@@ -344,47 +332,6 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
344
332
|
run_id: this.id,
|
|
345
333
|
});
|
|
346
334
|
|
|
347
|
-
const threadId = config.configurable.thread_id as string | undefined;
|
|
348
|
-
|
|
349
|
-
if (this.hookRegistry != null) {
|
|
350
|
-
await executeHooks({
|
|
351
|
-
registry: this.hookRegistry,
|
|
352
|
-
input: {
|
|
353
|
-
hook_event_name: 'RunStart',
|
|
354
|
-
runId: this.id,
|
|
355
|
-
threadId,
|
|
356
|
-
agentId: this.Graph.defaultAgentId,
|
|
357
|
-
messages: inputs.messages,
|
|
358
|
-
},
|
|
359
|
-
sessionId: this.id,
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
const lastHuman = findLastMessageOfType(inputs.messages, 'human');
|
|
363
|
-
if (lastHuman != null) {
|
|
364
|
-
const promptResult = await executeHooks({
|
|
365
|
-
registry: this.hookRegistry,
|
|
366
|
-
input: {
|
|
367
|
-
hook_event_name: 'UserPromptSubmit',
|
|
368
|
-
runId: this.id,
|
|
369
|
-
threadId,
|
|
370
|
-
agentId: this.Graph.defaultAgentId,
|
|
371
|
-
prompt: extractPromptText(lastHuman),
|
|
372
|
-
// attachments: not yet wired — Phase 2 will extract
|
|
373
|
-
// non-text content blocks (images, files) from messages
|
|
374
|
-
},
|
|
375
|
-
sessionId: this.id,
|
|
376
|
-
});
|
|
377
|
-
if (
|
|
378
|
-
promptResult.decision === 'deny' ||
|
|
379
|
-
promptResult.decision === 'ask'
|
|
380
|
-
) {
|
|
381
|
-
this.hookRegistry.clearSession(this.id);
|
|
382
|
-
config.callbacks = undefined;
|
|
383
|
-
return undefined;
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
335
|
const stream = this.graphRunnable.streamEvents(inputs, config, {
|
|
389
336
|
raiseError: true,
|
|
390
337
|
/**
|
|
@@ -414,45 +361,7 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
414
361
|
await handler.handle(eventName, data, metadata, this.Graph);
|
|
415
362
|
}
|
|
416
363
|
}
|
|
417
|
-
|
|
418
|
-
if (this.hookRegistry?.hasHookFor('Stop', this.id) === true) {
|
|
419
|
-
await executeHooks({
|
|
420
|
-
registry: this.hookRegistry,
|
|
421
|
-
input: {
|
|
422
|
-
hook_event_name: 'Stop',
|
|
423
|
-
runId: this.id,
|
|
424
|
-
threadId,
|
|
425
|
-
agentId: this.Graph.defaultAgentId,
|
|
426
|
-
messages: this.Graph.getRunMessages() ?? inputs.messages,
|
|
427
|
-
stopHookActive: false, // will be true when stop is triggered by a hook (Phase 2)
|
|
428
|
-
},
|
|
429
|
-
sessionId: this.id,
|
|
430
|
-
}).catch(() => {
|
|
431
|
-
/* Stop hook errors must not masquerade as stream failures */
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
} catch (err) {
|
|
435
|
-
if (this.hookRegistry?.hasHookFor('StopFailure', this.id) === true) {
|
|
436
|
-
const runMessages = this.Graph.getRunMessages() ?? [];
|
|
437
|
-
await executeHooks({
|
|
438
|
-
registry: this.hookRegistry,
|
|
439
|
-
input: {
|
|
440
|
-
hook_event_name: 'StopFailure',
|
|
441
|
-
runId: this.id,
|
|
442
|
-
threadId,
|
|
443
|
-
agentId: this.Graph.defaultAgentId,
|
|
444
|
-
error: err instanceof Error ? err.message : String(err),
|
|
445
|
-
lastAssistantMessage: findLastMessageOfType(runMessages, 'ai'),
|
|
446
|
-
},
|
|
447
|
-
sessionId: this.id,
|
|
448
|
-
}).catch(() => {
|
|
449
|
-
/* swallow hook errors — the original error must propagate */
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
throw err;
|
|
453
364
|
} finally {
|
|
454
|
-
this.hookRegistry?.clearSession(this.id);
|
|
455
|
-
|
|
456
365
|
/**
|
|
457
366
|
* Break the reference chain that keeps heavy data alive via
|
|
458
367
|
* LangGraph's internal `__pregel_scratchpad.currentTaskInput` →
|
|
@@ -648,38 +557,3 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
648
557
|
}
|
|
649
558
|
}
|
|
650
559
|
}
|
|
651
|
-
|
|
652
|
-
function findLastMessageOfType(
|
|
653
|
-
messages: BaseMessage[],
|
|
654
|
-
type: string
|
|
655
|
-
): BaseMessage | undefined {
|
|
656
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
657
|
-
if (messages[i].getType() === type) {
|
|
658
|
-
return messages[i];
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
return undefined;
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
function extractPromptText(message: BaseMessage): string {
|
|
665
|
-
const content = message.content;
|
|
666
|
-
if (typeof content === 'string') {
|
|
667
|
-
return content;
|
|
668
|
-
}
|
|
669
|
-
if (!Array.isArray(content)) {
|
|
670
|
-
return String(content);
|
|
671
|
-
}
|
|
672
|
-
const parts: string[] = [];
|
|
673
|
-
for (const block of content) {
|
|
674
|
-
if (
|
|
675
|
-
typeof block === 'object' &&
|
|
676
|
-
'type' in block &&
|
|
677
|
-
block.type === 'text' &&
|
|
678
|
-
'text' in block &&
|
|
679
|
-
typeof block.text === 'string'
|
|
680
|
-
) {
|
|
681
|
-
parts.push(block.text);
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
return parts.join('\n');
|
|
685
|
-
}
|