@librechat/agents 3.1.73 → 3.1.75
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/README.md +66 -0
- package/dist/cjs/agents/AgentContext.cjs +146 -57
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +4 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/main.cjs +1 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/cache.cjs +37 -3
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/tools/BashExecutor.cjs +21 -11
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +37 -10
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +16 -11
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +147 -58
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +4 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/main.mjs +1 -1
- package/dist/esm/messages/cache.mjs +37 -3
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/tools/BashExecutor.mjs +22 -12
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +37 -11
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +17 -12
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +29 -4
- package/dist/types/agents/__tests__/promptCacheLiveHelpers.d.ts +46 -0
- package/dist/types/tools/CodeExecutor.d.ts +6 -0
- package/dist/types/types/graph.d.ts +3 -1
- package/dist/types/types/run.d.ts +2 -0
- package/dist/types/types/tools.d.ts +9 -0
- package/package.json +1 -1
- package/src/agents/AgentContext.ts +189 -71
- package/src/agents/__tests__/AgentContext.anthropic.live.test.ts +116 -0
- package/src/agents/__tests__/AgentContext.bedrock.live.test.ts +149 -0
- package/src/agents/__tests__/AgentContext.test.ts +333 -2
- package/src/agents/__tests__/promptCacheLiveHelpers.ts +165 -0
- package/src/llm/anthropic/utils/message_inputs.ts +6 -1
- package/src/llm/anthropic/utils/server-tool-inputs.test.ts +77 -0
- package/src/messages/cache.test.ts +104 -3
- package/src/messages/cache.ts +54 -3
- package/src/specs/anthropic.simple.test.ts +61 -0
- package/src/specs/summarization.test.ts +7 -3
- package/src/tools/BashExecutor.ts +37 -13
- package/src/tools/CodeExecutor.ts +55 -11
- package/src/tools/ProgrammaticToolCalling.ts +29 -14
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +60 -0
- package/src/types/graph.ts +3 -1
- package/src/types/run.ts +2 -0
- package/src/types/tools.ts +9 -0
package/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# @librechat/agents
|
|
2
|
+
|
|
3
|
+
TypeScript utilities for building LibreChat agent workflows. The package provides graph orchestration, streaming event handling, tool execution, provider adapters, and message formatting for single-agent and multi-agent runs.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- LangGraph-based single-agent and multi-agent workflows
|
|
8
|
+
- Streaming content aggregation and run-step event handlers
|
|
9
|
+
- Tool calling, tool search, subagent handoffs, and programmatic tool execution
|
|
10
|
+
- Provider adapters for Anthropic, Bedrock, Vertex AI, OpenAI-compatible providers, Google, Mistral, DeepSeek, and xAI
|
|
11
|
+
- Message formatting, context pruning, summarization, and cache-control helpers
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @librechat/agents
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Basic Usage
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { HumanMessage } from '@langchain/core/messages';
|
|
23
|
+
import { Providers, Run } from '@librechat/agents';
|
|
24
|
+
|
|
25
|
+
const run = await Run.create({
|
|
26
|
+
runId: crypto.randomUUID(),
|
|
27
|
+
graphConfig: {
|
|
28
|
+
type: 'standard',
|
|
29
|
+
instructions: 'You are a helpful assistant.',
|
|
30
|
+
llmConfig: {
|
|
31
|
+
provider: Providers.OPENAI,
|
|
32
|
+
model: 'gpt-4o-mini',
|
|
33
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
returnContent: true,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const content = await run.processStream(
|
|
40
|
+
{ messages: [new HumanMessage('Hello')] },
|
|
41
|
+
{
|
|
42
|
+
runId: crypto.randomUUID(),
|
|
43
|
+
streamMode: 'values',
|
|
44
|
+
version: 'v2',
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Development
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm ci
|
|
53
|
+
npm run build
|
|
54
|
+
npm test
|
|
55
|
+
npx tsc --noEmit
|
|
56
|
+
npx eslint src/
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Documentation
|
|
60
|
+
|
|
61
|
+
- [Multi-agent patterns](./docs/multi-agent-patterns.md)
|
|
62
|
+
- [Summarization behavior](./docs/summarization-behavior.md)
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
MIT
|
|
@@ -192,7 +192,7 @@ class AgentContext {
|
|
|
192
192
|
summaryTokenCount = 0;
|
|
193
193
|
/**
|
|
194
194
|
* Where the summary should be injected:
|
|
195
|
-
* - `'system_prompt'`: cross-run summary, included in
|
|
195
|
+
* - `'system_prompt'`: cross-run summary, included in the dynamic system tail
|
|
196
196
|
* - `'user_message'`: mid-run compaction, injected as HumanMessage on clean slate
|
|
197
197
|
* - `'none'`: no summary present
|
|
198
198
|
*/
|
|
@@ -298,15 +298,18 @@ class AgentContext {
|
|
|
298
298
|
}
|
|
299
299
|
/**
|
|
300
300
|
* Gets the system runnable, creating it lazily if needed.
|
|
301
|
-
* Includes instructions, additional instructions, and
|
|
301
|
+
* Includes stable instructions, dynamic additional instructions, and
|
|
302
|
+
* programmatic-only tools documentation.
|
|
302
303
|
* Only rebuilds when marked stale (via markToolsAsDiscovered).
|
|
303
304
|
*/
|
|
304
305
|
get systemRunnable() {
|
|
305
306
|
if (!this.systemRunnableStale && this.cachedSystemRunnable !== undefined) {
|
|
306
307
|
return this.cachedSystemRunnable;
|
|
307
308
|
}
|
|
308
|
-
|
|
309
|
-
|
|
309
|
+
this.cachedSystemRunnable = this.buildSystemRunnable({
|
|
310
|
+
stableInstructions: this.buildStableInstructionsString(),
|
|
311
|
+
dynamicInstructions: this.buildDynamicInstructionsString(),
|
|
312
|
+
});
|
|
310
313
|
this.systemRunnableStale = false;
|
|
311
314
|
return this.cachedSystemRunnable;
|
|
312
315
|
}
|
|
@@ -316,16 +319,18 @@ class AgentContext {
|
|
|
316
319
|
*/
|
|
317
320
|
initializeSystemRunnable() {
|
|
318
321
|
if (this.systemRunnableStale || this.cachedSystemRunnable === undefined) {
|
|
319
|
-
|
|
320
|
-
|
|
322
|
+
this.cachedSystemRunnable = this.buildSystemRunnable({
|
|
323
|
+
stableInstructions: this.buildStableInstructionsString(),
|
|
324
|
+
dynamicInstructions: this.buildDynamicInstructionsString(),
|
|
325
|
+
});
|
|
321
326
|
this.systemRunnableStale = false;
|
|
322
327
|
}
|
|
323
328
|
}
|
|
324
329
|
/**
|
|
325
|
-
* Builds the
|
|
330
|
+
* Builds the cacheable instructions string (without creating SystemMessage).
|
|
326
331
|
* Includes agent identity preamble and handoff context when available.
|
|
327
332
|
*/
|
|
328
|
-
|
|
333
|
+
buildStableInstructionsString() {
|
|
329
334
|
const parts = [];
|
|
330
335
|
const identityPreamble = this.buildIdentityPreamble();
|
|
331
336
|
if (identityPreamble) {
|
|
@@ -334,17 +339,27 @@ class AgentContext {
|
|
|
334
339
|
if (this.instructions != null && this.instructions !== '') {
|
|
335
340
|
parts.push(this.instructions);
|
|
336
341
|
}
|
|
337
|
-
if (this.additionalInstructions != null &&
|
|
338
|
-
this.additionalInstructions !== '') {
|
|
339
|
-
parts.push(this.additionalInstructions);
|
|
340
|
-
}
|
|
341
342
|
const programmaticToolsDoc = this.buildProgrammaticOnlyToolsInstructions();
|
|
342
343
|
if (programmaticToolsDoc) {
|
|
343
344
|
parts.push(programmaticToolsDoc);
|
|
344
345
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
346
|
+
return parts.join('\n\n');
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Builds the dynamic system-tail string (without creating SystemMessage).
|
|
350
|
+
* Keep this out of prompt-cache-marked content so volatile context does not
|
|
351
|
+
* invalidate the stable prefix.
|
|
352
|
+
*/
|
|
353
|
+
buildDynamicInstructionsString() {
|
|
354
|
+
const parts = [];
|
|
355
|
+
if (this.additionalInstructions != null &&
|
|
356
|
+
this.additionalInstructions !== '') {
|
|
357
|
+
parts.push(this.additionalInstructions);
|
|
358
|
+
}
|
|
359
|
+
// Cross-run summary: include in the system tail so the model has context
|
|
360
|
+
// from the prior run without invalidating the cacheable prefix. Mid-run
|
|
361
|
+
// summaries are injected as a HumanMessage on the post-compaction clean
|
|
362
|
+
// slate instead (see buildSystemRunnable).
|
|
348
363
|
if (this._summaryLocation === 'system_prompt' &&
|
|
349
364
|
this.summaryText != null &&
|
|
350
365
|
this.summaryText !== '') {
|
|
@@ -375,34 +390,20 @@ class AgentContext {
|
|
|
375
390
|
* Build system runnable from pre-built instructions string.
|
|
376
391
|
* Only called when content has actually changed.
|
|
377
392
|
*/
|
|
378
|
-
buildSystemRunnable(
|
|
393
|
+
buildSystemRunnable({ stableInstructions, dynamicInstructions, }) {
|
|
379
394
|
const hasMidRunSummary = this._summaryLocation === 'user_message' &&
|
|
380
395
|
this.summaryText != null &&
|
|
381
396
|
this.summaryText !== '';
|
|
382
|
-
if (!
|
|
397
|
+
if (!stableInstructions && !dynamicInstructions && !hasMidRunSummary) {
|
|
383
398
|
this.systemMessageTokens = 0;
|
|
384
399
|
return undefined;
|
|
385
400
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
finalInstructions = {
|
|
393
|
-
content: [
|
|
394
|
-
{
|
|
395
|
-
type: 'text',
|
|
396
|
-
text: instructionsString,
|
|
397
|
-
cache_control: { type: 'ephemeral' },
|
|
398
|
-
},
|
|
399
|
-
],
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
const systemMessage = instructionsString
|
|
404
|
-
? new messages.SystemMessage(finalInstructions)
|
|
405
|
-
: undefined;
|
|
401
|
+
const usePromptCache = this.hasAnthropicPromptCache();
|
|
402
|
+
const systemMessage = this.buildSystemMessage({
|
|
403
|
+
stableInstructions,
|
|
404
|
+
dynamicInstructions,
|
|
405
|
+
usePromptCache,
|
|
406
|
+
});
|
|
406
407
|
if (this.tokenCounter) {
|
|
407
408
|
this.systemMessageTokens = systemMessage
|
|
408
409
|
? this.tokenCounter(systemMessage)
|
|
@@ -444,6 +445,52 @@ class AgentContext {
|
|
|
444
445
|
return [...prefix, ...body];
|
|
445
446
|
}).withConfig({ runName: 'prompt' });
|
|
446
447
|
}
|
|
448
|
+
hasAnthropicPromptCache() {
|
|
449
|
+
if (this.provider !== _enum.Providers.ANTHROPIC) {
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
const anthropicOptions = this.clientOptions;
|
|
453
|
+
return anthropicOptions?.promptCache === true;
|
|
454
|
+
}
|
|
455
|
+
hasBedrockPromptCache() {
|
|
456
|
+
if (this.provider !== _enum.Providers.BEDROCK) {
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
const bedrockOptions = this.clientOptions;
|
|
460
|
+
return bedrockOptions?.promptCache === true;
|
|
461
|
+
}
|
|
462
|
+
buildSystemMessage({ stableInstructions, dynamicInstructions, usePromptCache, }) {
|
|
463
|
+
if (!stableInstructions && !dynamicInstructions) {
|
|
464
|
+
return undefined;
|
|
465
|
+
}
|
|
466
|
+
if (usePromptCache) {
|
|
467
|
+
const content = [];
|
|
468
|
+
if (stableInstructions) {
|
|
469
|
+
content.push({
|
|
470
|
+
type: 'text',
|
|
471
|
+
text: stableInstructions,
|
|
472
|
+
cache_control: { type: 'ephemeral' },
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
if (dynamicInstructions) {
|
|
476
|
+
content.push({ type: 'text', text: dynamicInstructions });
|
|
477
|
+
}
|
|
478
|
+
return new messages.SystemMessage({ content });
|
|
479
|
+
}
|
|
480
|
+
if (this.hasBedrockPromptCache() && stableInstructions) {
|
|
481
|
+
const content = [
|
|
482
|
+
{ type: 'text', text: stableInstructions },
|
|
483
|
+
{ cachePoint: { type: 'default' } },
|
|
484
|
+
];
|
|
485
|
+
if (dynamicInstructions) {
|
|
486
|
+
content.push({ type: 'text', text: dynamicInstructions });
|
|
487
|
+
}
|
|
488
|
+
return new messages.SystemMessage({ content });
|
|
489
|
+
}
|
|
490
|
+
return new messages.SystemMessage([stableInstructions, dynamicInstructions]
|
|
491
|
+
.filter((part) => part !== '')
|
|
492
|
+
.join('\n\n'));
|
|
493
|
+
}
|
|
447
494
|
/**
|
|
448
495
|
* Reset context for a new run
|
|
449
496
|
*/
|
|
@@ -505,7 +552,44 @@ class AgentContext {
|
|
|
505
552
|
if (!this.toolDefinitions) {
|
|
506
553
|
return [];
|
|
507
554
|
}
|
|
508
|
-
|
|
555
|
+
/**
|
|
556
|
+
* Mirror `getEventDrivenToolsForBinding`'s gate: a definition is only
|
|
557
|
+
* bound to the model when its `allowed_callers` include `'direct'` and
|
|
558
|
+
* (if deferred) it has been discovered. Filtering by `defer_loading`
|
|
559
|
+
* alone left programmatic-only definitions counted in
|
|
560
|
+
* `toolSchemaTokens` even though they were never bound.
|
|
561
|
+
*/
|
|
562
|
+
return this.toolDefinitions.filter((def) => {
|
|
563
|
+
const allowedCallers = def.allowed_callers ?? ['direct'];
|
|
564
|
+
if (!allowedCallers.includes('direct')) {
|
|
565
|
+
return false;
|
|
566
|
+
}
|
|
567
|
+
return (def.defer_loading !== true || this.discoveredToolNames.has(def.name));
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Single source of truth for "which entries of `this.tools` should be
|
|
572
|
+
* treated as actually bound". Callers:
|
|
573
|
+
* - `getToolsForBinding` (non-event-driven branch)
|
|
574
|
+
* - `getEventDrivenToolsForBinding` (appends instance tools alongside
|
|
575
|
+
* schema-only definitions)
|
|
576
|
+
* - `calculateInstructionTokens` (counts schema bytes for accounting)
|
|
577
|
+
*
|
|
578
|
+
* In event-driven mode (`toolDefinitions` present) instance tools are
|
|
579
|
+
* appended unfiltered; outside event-driven mode they pass through
|
|
580
|
+
* `filterToolsForBinding`. Centralizing the decision here prevents the
|
|
581
|
+
* accounting/binding paths from drifting apart, which was the root
|
|
582
|
+
* cause of the original miscount.
|
|
583
|
+
*/
|
|
584
|
+
getEffectiveInstanceTools() {
|
|
585
|
+
if (!this.tools) {
|
|
586
|
+
return undefined;
|
|
587
|
+
}
|
|
588
|
+
const isEventDriven = (this.toolDefinitions?.length ?? 0) > 0;
|
|
589
|
+
if (isEventDriven || !this.toolRegistry) {
|
|
590
|
+
return this.tools;
|
|
591
|
+
}
|
|
592
|
+
return this.filterToolsForBinding(this.tools);
|
|
509
593
|
}
|
|
510
594
|
/**
|
|
511
595
|
* Calculate tool tokens and add to instruction tokens
|
|
@@ -520,9 +604,17 @@ class AgentContext {
|
|
|
520
604
|
* populated after `fromConfig()` kicks off the initial calculation, so
|
|
521
605
|
* callers that mutate `graphTools` must re-trigger this method to
|
|
522
606
|
* refresh `toolSchemaTokens`.
|
|
607
|
+
*
|
|
608
|
+
* Use `getEffectiveInstanceTools()` so accounting reflects exactly the
|
|
609
|
+
* subset that `getToolsForBinding` would emit — preventing the
|
|
610
|
+
* worst-case-ceiling miscount that triggered spurious `empty_messages`
|
|
611
|
+
* preflight rejections at low `maxContextTokens`. Deferred and
|
|
612
|
+
* non-`'direct'` `toolDefinitions` are excluded by
|
|
613
|
+
* `getActiveToolDefinitions()` below.
|
|
523
614
|
*/
|
|
524
615
|
const instanceTools = [
|
|
525
|
-
...(this.
|
|
616
|
+
...(this.getEffectiveInstanceTools() ??
|
|
617
|
+
[]),
|
|
526
618
|
...(this.graphTools ?? []),
|
|
527
619
|
];
|
|
528
620
|
if (instanceTools.length > 0) {
|
|
@@ -682,7 +774,16 @@ class AgentContext {
|
|
|
682
774
|
*/
|
|
683
775
|
getTokenBudgetBreakdown(messages) {
|
|
684
776
|
const maxContextTokens = this.maxContextTokens ?? 0;
|
|
685
|
-
|
|
777
|
+
/**
|
|
778
|
+
* Derive `toolCount` from `getToolsForBinding()` so the diagnostic stays
|
|
779
|
+
* aligned with what is actually bound to the model — and with what
|
|
780
|
+
* `calculateInstructionTokens` counts into `toolSchemaTokens`. Using raw
|
|
781
|
+
* `this.tools.length` would inflate the count whenever the registry
|
|
782
|
+
* marks instance tools as deferred-undiscovered or non-`'direct'`,
|
|
783
|
+
* producing the same misleading "N tools" diagnostic this fix is meant
|
|
784
|
+
* to eliminate.
|
|
785
|
+
*/
|
|
786
|
+
const toolCount = this.getToolsForBinding()?.length ?? 0;
|
|
686
787
|
const messageCount = messages?.length ?? 0;
|
|
687
788
|
let messageTokens = 0;
|
|
688
789
|
if (messages != null) {
|
|
@@ -780,9 +881,7 @@ class AgentContext {
|
|
|
780
881
|
if (this.toolDefinitions && this.toolDefinitions.length > 0) {
|
|
781
882
|
return this.getEventDrivenToolsForBinding();
|
|
782
883
|
}
|
|
783
|
-
const filtered =
|
|
784
|
-
? this.tools
|
|
785
|
-
: this.filterToolsForBinding(this.tools);
|
|
884
|
+
const filtered = this.getEffectiveInstanceTools();
|
|
786
885
|
if (this.graphTools && this.graphTools.length > 0) {
|
|
787
886
|
return [...(filtered ?? []), ...this.graphTools];
|
|
788
887
|
}
|
|
@@ -793,24 +892,14 @@ class AgentContext {
|
|
|
793
892
|
if (!this.toolDefinitions) {
|
|
794
893
|
return this.graphTools ?? [];
|
|
795
894
|
}
|
|
796
|
-
const
|
|
797
|
-
const allowedCallers = def.allowed_callers ?? ['direct'];
|
|
798
|
-
if (!allowedCallers.includes('direct')) {
|
|
799
|
-
return false;
|
|
800
|
-
}
|
|
801
|
-
if (def.defer_loading === true &&
|
|
802
|
-
!this.discoveredToolNames.has(def.name)) {
|
|
803
|
-
return false;
|
|
804
|
-
}
|
|
805
|
-
return true;
|
|
806
|
-
});
|
|
807
|
-
const schemaTools = schema$1.createSchemaOnlyTools(defsToInclude);
|
|
895
|
+
const schemaTools = schema$1.createSchemaOnlyTools(this.getActiveToolDefinitions());
|
|
808
896
|
const allTools = [...schemaTools];
|
|
809
897
|
if (this.graphTools && this.graphTools.length > 0) {
|
|
810
898
|
allTools.push(...this.graphTools);
|
|
811
899
|
}
|
|
812
|
-
|
|
813
|
-
|
|
900
|
+
const instanceTools = this.getEffectiveInstanceTools();
|
|
901
|
+
if (instanceTools && instanceTools.length > 0) {
|
|
902
|
+
allTools.push(...instanceTools);
|
|
814
903
|
}
|
|
815
904
|
return allTools;
|
|
816
905
|
}
|