@librechat/agents 3.3.2 → 3.3.4
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 +2 -1
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +342 -109
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +3 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/langfuse.cjs +6 -0
- package/dist/cjs/langfuse.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +79 -6
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +37 -10
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/google/utils/common.cjs +19 -7
- package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
- package/dist/cjs/llm/invoke.cjs +64 -3
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +41 -5
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs +25 -4
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/index.cjs +4 -1
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +19 -2
- package/dist/cjs/messages/cache.cjs +54 -33
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/messages/contextPruning.cjs +17 -43
- package/dist/cjs/messages/contextPruning.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +315 -23
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +218 -60
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/index.cjs +1 -1
- package/dist/cjs/messages/prune.cjs +503 -127
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/stream.cjs +7 -3
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +28 -14
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +54 -41
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/toolOutputReferences.cjs +6 -0
- package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -1
- package/dist/cjs/utils/index.cjs +1 -1
- package/dist/cjs/utils/llm.cjs +1 -1
- package/dist/cjs/utils/llm.cjs.map +1 -1
- package/dist/cjs/utils/tokens.cjs +307 -20
- package/dist/cjs/utils/tokens.cjs.map +1 -1
- package/dist/cjs/utils/toolContent.cjs +1514 -0
- package/dist/cjs/utils/toolContent.cjs.map +1 -0
- package/dist/esm/agents/AgentContext.mjs +2 -1
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +349 -116
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +3 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/langfuse.mjs +6 -0
- package/dist/esm/langfuse.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +79 -6
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs +37 -10
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/google/utils/common.mjs +19 -7
- package/dist/esm/llm/google/utils/common.mjs.map +1 -1
- package/dist/esm/llm/invoke.mjs +65 -5
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +41 -5
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs +25 -4
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/index.mjs +4 -1
- package/dist/esm/llm/openrouter/index.mjs.map +1 -1
- package/dist/esm/main.mjs +7 -7
- package/dist/esm/messages/cache.mjs +55 -34
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/messages/contextPruning.mjs +17 -43
- package/dist/esm/messages/contextPruning.mjs.map +1 -1
- package/dist/esm/messages/core.mjs +305 -24
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +218 -61
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/index.mjs +1 -1
- package/dist/esm/messages/prune.mjs +500 -128
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/stream.mjs +7 -3
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +28 -14
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +54 -41
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/toolOutputReferences.mjs +6 -0
- package/dist/esm/tools/toolOutputReferences.mjs.map +1 -1
- package/dist/esm/utils/index.mjs +1 -1
- package/dist/esm/utils/llm.mjs +1 -1
- package/dist/esm/utils/llm.mjs.map +1 -1
- package/dist/esm/utils/tokens.mjs +307 -21
- package/dist/esm/utils/tokens.mjs.map +1 -1
- package/dist/esm/utils/toolContent.mjs +1503 -0
- package/dist/esm/utils/toolContent.mjs.map +1 -0
- package/dist/types/graphs/Graph.d.ts +1 -1
- package/dist/types/llm/invoke.d.ts +27 -1
- package/dist/types/llm/openai/index.d.ts +4 -0
- package/dist/types/llm/openai/utils/index.d.ts +2 -0
- package/dist/types/llm/openrouter/index.d.ts +5 -0
- package/dist/types/messages/contextPruning.d.ts +2 -1
- package/dist/types/messages/core.d.ts +36 -0
- package/dist/types/messages/format.d.ts +6 -0
- package/dist/types/messages/prune.d.ts +16 -12
- package/dist/types/types/stream.d.ts +1 -1
- package/dist/types/utils/tokens.d.ts +7 -0
- package/dist/types/utils/toolContent.d.ts +107 -0
- package/package.json +13 -9
- package/src/__tests__/stream.eagerEventExecution.test.ts +74 -0
- package/src/agents/AgentContext.ts +1 -0
- package/src/graphs/Graph.ts +698 -200
- package/src/graphs/MultiAgentGraph.ts +6 -1
- package/src/graphs/__tests__/Graph.contextOverflow.test.ts +1128 -4
- package/src/langfuse.ts +15 -0
- package/src/llm/anthropic/utils/message_inputs.ts +158 -12
- package/src/llm/anthropic/utils/tool-id-normalization.test.ts +109 -0
- package/src/llm/bedrock/utils/message_inputs.test.ts +49 -1
- package/src/llm/bedrock/utils/message_inputs.ts +86 -13
- package/src/llm/google/utils/common.test.ts +75 -9
- package/src/llm/google/utils/common.ts +34 -3
- package/src/llm/invoke.test.ts +488 -0
- package/src/llm/invoke.ts +196 -2
- package/src/llm/openai/index.ts +106 -6
- package/src/llm/openai/utils/index.ts +109 -53
- package/src/llm/openai/utils/messages.test.ts +330 -1
- package/src/llm/openrouter/index.ts +12 -2
- package/src/llm/openrouter/reasoning.test.ts +314 -0
- package/src/messages/__tests__/observationMasking.test.ts +93 -2
- package/src/messages/cache.tail.test.ts +193 -0
- package/src/messages/cache.test.ts +113 -0
- package/src/messages/cache.ts +92 -60
- package/src/messages/contextPruning.test.ts +184 -0
- package/src/messages/contextPruning.ts +49 -42
- package/src/messages/core.ts +653 -40
- package/src/messages/ensureThinkingBlock.test.ts +49 -1
- package/src/messages/foldToollessToolBlocks.test.ts +163 -5
- package/src/messages/format.ts +363 -91
- package/src/messages/formatAgentMessages.test.ts +604 -14
- package/src/messages/formatAgentMessages.tools.test.ts +103 -0
- package/src/messages/prune.ts +996 -183
- package/src/specs/langfuse-callbacks.test.ts +203 -1
- package/src/specs/prune.test.ts +1083 -6
- package/src/specs/summarization-unit.test.ts +105 -0
- package/src/specs/tokens.test.ts +609 -32
- package/src/stream.ts +23 -15
- package/src/summarization/__tests__/node.test.ts +77 -0
- package/src/summarization/node.ts +46 -18
- package/src/tools/ToolNode.ts +147 -68
- package/src/tools/__tests__/ToolNode.onResultCompletion.test.ts +148 -1
- package/src/tools/__tests__/ToolNode.outputReferences.test.ts +112 -3
- package/src/tools/__tests__/annotateMessagesForLLM.test.ts +32 -0
- package/src/tools/__tests__/directToolHooks.test.ts +255 -7
- package/src/tools/toolOutputReferences.ts +6 -0
- package/src/types/stream.ts +1 -1
- package/src/utils/llm.test.ts +18 -0
- package/src/utils/llm.ts +4 -1
- package/src/utils/tokens.ts +586 -31
- package/src/utils/toolContent.test.ts +1432 -0
- package/src/utils/toolContent.ts +2707 -0
|
@@ -755,6 +755,54 @@ describe('ensureThinkingBlockInMessages', () => {
|
|
|
755
755
|
expect(result[0]).toBeInstanceOf(HumanMessage);
|
|
756
756
|
expect(result[1]).toBeInstanceOf(ToolMessage);
|
|
757
757
|
});
|
|
758
|
+
|
|
759
|
+
test('should not emit an empty user message after the shared fold budget is exhausted', () => {
|
|
760
|
+
const messages = [
|
|
761
|
+
new HumanMessage({ content: 'Run both tools' }),
|
|
762
|
+
new AIMessage({
|
|
763
|
+
content: '',
|
|
764
|
+
tool_calls: [
|
|
765
|
+
{ id: 'first', name: 'query', args: {}, type: 'tool_call' },
|
|
766
|
+
],
|
|
767
|
+
}),
|
|
768
|
+
new ToolMessage({
|
|
769
|
+
content: Array.from({ length: 60 }, () => ({
|
|
770
|
+
type: 'text',
|
|
771
|
+
text: 'x'.repeat(8_000),
|
|
772
|
+
})),
|
|
773
|
+
tool_call_id: 'first',
|
|
774
|
+
}),
|
|
775
|
+
new AIMessage({
|
|
776
|
+
content: '',
|
|
777
|
+
tool_calls: [
|
|
778
|
+
{ id: 'second', name: 'query', args: {}, type: 'tool_call' },
|
|
779
|
+
],
|
|
780
|
+
}),
|
|
781
|
+
new ToolMessage({
|
|
782
|
+
content: 'second result',
|
|
783
|
+
tool_call_id: 'second',
|
|
784
|
+
}),
|
|
785
|
+
];
|
|
786
|
+
|
|
787
|
+
const result = ensureThinkingBlockInMessages(
|
|
788
|
+
messages,
|
|
789
|
+
Providers.ANTHROPIC
|
|
790
|
+
);
|
|
791
|
+
|
|
792
|
+
expect(result).toHaveLength(2);
|
|
793
|
+
expect(getTextContent(result[1])).toContain(
|
|
794
|
+
'additional folded context omitted'
|
|
795
|
+
);
|
|
796
|
+
expect(
|
|
797
|
+
result
|
|
798
|
+
.filter((message) => message instanceof HumanMessage)
|
|
799
|
+
.every((message) =>
|
|
800
|
+
typeof message.content === 'string'
|
|
801
|
+
? message.content.length > 0
|
|
802
|
+
: message.content.length > 0
|
|
803
|
+
)
|
|
804
|
+
).toBe(true);
|
|
805
|
+
});
|
|
758
806
|
});
|
|
759
807
|
|
|
760
808
|
describe('image content preservation (token amplification fix)', () => {
|
|
@@ -1132,7 +1180,7 @@ describe('ensureThinkingBlockInMessages', () => {
|
|
|
1132
1180
|
|
|
1133
1181
|
expect(result).toHaveLength(2);
|
|
1134
1182
|
const allText = getTextContent(result[1]);
|
|
1135
|
-
// The resource block should be serialized as text, not silently dropped
|
|
1183
|
+
// The resource block should be serialized as text, not silently dropped.
|
|
1136
1184
|
expect(allText).toContain('[resource]');
|
|
1137
1185
|
expect(allText).toContain('data.csv');
|
|
1138
1186
|
});
|
|
@@ -6,7 +6,11 @@ import {
|
|
|
6
6
|
ToolMessage,
|
|
7
7
|
} from '@langchain/core/messages';
|
|
8
8
|
import type { ExtendedMessageContent } from '@/types';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
foldToolBlocksForToollessAgent,
|
|
11
|
+
isSyntheticProviderContextMessage,
|
|
12
|
+
} from './format';
|
|
13
|
+
import { HARD_MAX_TOOL_RESULT_CHARS } from '@/utils/truncation';
|
|
10
14
|
|
|
11
15
|
/** Concatenated text across a message's content (string or structured array). */
|
|
12
16
|
function getTextContent(msg: {
|
|
@@ -96,6 +100,12 @@ describe('foldToolBlocksForToollessAgent', () => {
|
|
|
96
100
|
expect(folded).toContain('file_search');
|
|
97
101
|
expect(folded).toContain('roadmap');
|
|
98
102
|
expect(folded).toContain('Found roadmap.md');
|
|
103
|
+
expect(isSyntheticProviderContextMessage(result[1])).toBe(true);
|
|
104
|
+
expect(
|
|
105
|
+
isSyntheticProviderContextMessage(
|
|
106
|
+
new HumanMessage('[Previous tool interaction] user-authored text')
|
|
107
|
+
)
|
|
108
|
+
).toBe(false);
|
|
99
109
|
});
|
|
100
110
|
|
|
101
111
|
test('folds historical tool content that precedes the last human turn (the reported bug)', () => {
|
|
@@ -338,7 +348,11 @@ describe('foldToolBlocksForToollessAgent', () => {
|
|
|
338
348
|
}),
|
|
339
349
|
new HumanMessage({
|
|
340
350
|
content: [
|
|
341
|
-
{
|
|
351
|
+
{
|
|
352
|
+
type: 'tool_result',
|
|
353
|
+
tool_use_id: 'c1',
|
|
354
|
+
content: 'Found roadmap.md',
|
|
355
|
+
},
|
|
342
356
|
],
|
|
343
357
|
}),
|
|
344
358
|
new HumanMessage('thanks'),
|
|
@@ -393,9 +407,7 @@ describe('foldToolBlocksForToollessAgent', () => {
|
|
|
393
407
|
new HumanMessage('Render a chart'),
|
|
394
408
|
new AIMessage({
|
|
395
409
|
content: '',
|
|
396
|
-
tool_calls: [
|
|
397
|
-
{ id: 'c', name: 'chart', args: {}, type: 'tool_call' },
|
|
398
|
-
],
|
|
410
|
+
tool_calls: [{ id: 'c', name: 'chart', args: {}, type: 'tool_call' }],
|
|
399
411
|
}),
|
|
400
412
|
new ToolMessage({
|
|
401
413
|
content: [
|
|
@@ -422,6 +434,152 @@ describe('foldToolBlocksForToollessAgent', () => {
|
|
|
422
434
|
).toBe(true);
|
|
423
435
|
});
|
|
424
436
|
|
|
437
|
+
test('bounds non-portable media blocks instead of preserving or JSON-expanding them', () => {
|
|
438
|
+
const blob = 'A'.repeat(20_000);
|
|
439
|
+
const resource = {
|
|
440
|
+
type: 'resource',
|
|
441
|
+
resource: { uri: 'file:///report.bin', blob },
|
|
442
|
+
};
|
|
443
|
+
const messages = [
|
|
444
|
+
new HumanMessage('Read the report'),
|
|
445
|
+
new AIMessage({
|
|
446
|
+
content: '',
|
|
447
|
+
tool_calls: [
|
|
448
|
+
{ id: 'doc', name: 'read_document', args: {}, type: 'tool_call' },
|
|
449
|
+
],
|
|
450
|
+
}),
|
|
451
|
+
new ToolMessage({
|
|
452
|
+
content: [{ type: 'text', text: 'report:' }, resource],
|
|
453
|
+
tool_call_id: 'doc',
|
|
454
|
+
name: 'read_document',
|
|
455
|
+
}),
|
|
456
|
+
];
|
|
457
|
+
|
|
458
|
+
const result = foldToolBlocksForToollessAgent(messages);
|
|
459
|
+
const foldedContent = result[result.length - 1].content;
|
|
460
|
+
const foldedText = getTextContent(result[result.length - 1]);
|
|
461
|
+
|
|
462
|
+
expect(
|
|
463
|
+
Array.isArray(foldedContent) &&
|
|
464
|
+
foldedContent.some(
|
|
465
|
+
(block) => typeof block === 'object' && block.type === 'resource'
|
|
466
|
+
)
|
|
467
|
+
).toBe(false);
|
|
468
|
+
expect(foldedText).toContain('[resource]');
|
|
469
|
+
expect(foldedText).toContain('report.bin');
|
|
470
|
+
expect(foldedText.length).toBeLessThan(9_000);
|
|
471
|
+
expect(foldedText).not.toContain(blob);
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
test('does not invoke toJSON while folding a small non-portable media block', () => {
|
|
475
|
+
let toJSONCalls = 0;
|
|
476
|
+
const resource = {
|
|
477
|
+
type: 'resource',
|
|
478
|
+
resource: { uri: 'file:///report.bin', blob: 'AAAA' },
|
|
479
|
+
toJSON() {
|
|
480
|
+
toJSONCalls++;
|
|
481
|
+
return { expanded: 'x'.repeat(100_000) };
|
|
482
|
+
},
|
|
483
|
+
};
|
|
484
|
+
const messages = [
|
|
485
|
+
new HumanMessage('Read the report'),
|
|
486
|
+
new AIMessage({
|
|
487
|
+
content: '',
|
|
488
|
+
tool_calls: [
|
|
489
|
+
{ id: 'doc', name: 'read_document', args: {}, type: 'tool_call' },
|
|
490
|
+
],
|
|
491
|
+
}),
|
|
492
|
+
new ToolMessage({
|
|
493
|
+
content: [resource],
|
|
494
|
+
tool_call_id: 'doc',
|
|
495
|
+
name: 'read_document',
|
|
496
|
+
}),
|
|
497
|
+
];
|
|
498
|
+
|
|
499
|
+
const result = foldToolBlocksForToollessAgent(messages);
|
|
500
|
+
const foldedText = getTextContent(result[result.length - 1]);
|
|
501
|
+
|
|
502
|
+
expect(toJSONCalls).toBe(0);
|
|
503
|
+
expect(foldedText).toContain('report.bin');
|
|
504
|
+
expect(foldedText.length).toBeLessThan(9_000);
|
|
505
|
+
expect(foldedText).not.toContain('x'.repeat(1_000));
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
test('bounds the aggregate folded context across many large text blocks', () => {
|
|
509
|
+
const messages = [
|
|
510
|
+
new HumanMessage('Summarize the query'),
|
|
511
|
+
new AIMessage({
|
|
512
|
+
content: '',
|
|
513
|
+
tool_calls: [
|
|
514
|
+
{ id: 'query', name: 'run_query', args: {}, type: 'tool_call' },
|
|
515
|
+
],
|
|
516
|
+
}),
|
|
517
|
+
new ToolMessage({
|
|
518
|
+
content: Array.from({ length: 100 }, (_, index) => ({
|
|
519
|
+
type: 'text',
|
|
520
|
+
text: `${index}:${'x'.repeat(8_000)}`,
|
|
521
|
+
})),
|
|
522
|
+
tool_call_id: 'query',
|
|
523
|
+
name: 'run_query',
|
|
524
|
+
}),
|
|
525
|
+
];
|
|
526
|
+
|
|
527
|
+
const result = foldToolBlocksForToollessAgent(messages);
|
|
528
|
+
const foldedText = getTextContent(result[result.length - 1]);
|
|
529
|
+
|
|
530
|
+
expect(foldedText.length).toBeLessThanOrEqual(HARD_MAX_TOOL_RESULT_CHARS);
|
|
531
|
+
expect(foldedText).toContain('additional folded context omitted');
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
test('does not emit an empty user message when an earlier fold exhausts the shared budget', () => {
|
|
535
|
+
const messages = [
|
|
536
|
+
new HumanMessage('Run both queries'),
|
|
537
|
+
new AIMessage({
|
|
538
|
+
content: '',
|
|
539
|
+
tool_calls: [
|
|
540
|
+
{ id: 'first', name: 'run_query', args: {}, type: 'tool_call' },
|
|
541
|
+
],
|
|
542
|
+
}),
|
|
543
|
+
new ToolMessage({
|
|
544
|
+
content: Array.from({ length: 60 }, () => ({
|
|
545
|
+
type: 'text',
|
|
546
|
+
text: 'x'.repeat(8_000),
|
|
547
|
+
})),
|
|
548
|
+
tool_call_id: 'first',
|
|
549
|
+
name: 'run_query',
|
|
550
|
+
}),
|
|
551
|
+
new AIMessage({
|
|
552
|
+
content: '',
|
|
553
|
+
tool_calls: [
|
|
554
|
+
{ id: 'second', name: 'run_query', args: {}, type: 'tool_call' },
|
|
555
|
+
],
|
|
556
|
+
}),
|
|
557
|
+
new ToolMessage({
|
|
558
|
+
content: 'second result',
|
|
559
|
+
tool_call_id: 'second',
|
|
560
|
+
name: 'run_query',
|
|
561
|
+
}),
|
|
562
|
+
];
|
|
563
|
+
|
|
564
|
+
const result = foldToolBlocksForToollessAgent(messages);
|
|
565
|
+
const syntheticMessages = result.filter(isSyntheticProviderContextMessage);
|
|
566
|
+
|
|
567
|
+
expect(hasResidualToolContent(result)).toBe(false);
|
|
568
|
+
expect(syntheticMessages).toHaveLength(1);
|
|
569
|
+
expect(getTextContent(syntheticMessages[0])).toContain(
|
|
570
|
+
'additional folded context omitted'
|
|
571
|
+
);
|
|
572
|
+
expect(
|
|
573
|
+
result
|
|
574
|
+
.filter((message) => message instanceof HumanMessage)
|
|
575
|
+
.every((message) =>
|
|
576
|
+
typeof message.content === 'string'
|
|
577
|
+
? message.content.length > 0
|
|
578
|
+
: message.content.length > 0
|
|
579
|
+
)
|
|
580
|
+
).toBe(true);
|
|
581
|
+
});
|
|
582
|
+
|
|
425
583
|
test('leaves non-tool conversations untouched', () => {
|
|
426
584
|
const messages = [
|
|
427
585
|
new SystemMessage('sys'),
|