@librechat/agents 3.1.57 → 3.1.61
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 +326 -62
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/common/enum.cjs +13 -0
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/events.cjs +7 -27
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +303 -222
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +4 -4
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +6 -2
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/init.cjs +60 -0
- package/dist/cjs/llm/init.cjs.map +1 -0
- package/dist/cjs/llm/invoke.cjs +90 -0
- package/dist/cjs/llm/invoke.cjs.map +1 -0
- package/dist/cjs/llm/openai/index.cjs +2 -0
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/request.cjs +41 -0
- package/dist/cjs/llm/request.cjs.map +1 -0
- package/dist/cjs/main.cjs +40 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/cache.cjs +76 -89
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/messages/contextPruning.cjs +156 -0
- package/dist/cjs/messages/contextPruning.cjs.map +1 -0
- package/dist/cjs/messages/contextPruningSettings.cjs +53 -0
- package/dist/cjs/messages/contextPruningSettings.cjs.map +1 -0
- package/dist/cjs/messages/core.cjs +23 -37
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +156 -11
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/prune.cjs +1161 -49
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/messages/reducer.cjs +87 -0
- package/dist/cjs/messages/reducer.cjs.map +1 -0
- package/dist/cjs/run.cjs +81 -42
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/stream.cjs +54 -7
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/index.cjs +75 -0
- package/dist/cjs/summarization/index.cjs.map +1 -0
- package/dist/cjs/summarization/node.cjs +663 -0
- package/dist/cjs/summarization/node.cjs.map +1 -0
- package/dist/cjs/tools/ToolNode.cjs +16 -8
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/handlers.cjs +2 -0
- package/dist/cjs/tools/handlers.cjs.map +1 -1
- package/dist/cjs/utils/errors.cjs +115 -0
- package/dist/cjs/utils/errors.cjs.map +1 -0
- package/dist/cjs/utils/events.cjs +17 -0
- package/dist/cjs/utils/events.cjs.map +1 -1
- package/dist/cjs/utils/handlers.cjs +16 -0
- package/dist/cjs/utils/handlers.cjs.map +1 -1
- package/dist/cjs/utils/llm.cjs +10 -0
- package/dist/cjs/utils/llm.cjs.map +1 -1
- package/dist/cjs/utils/tokens.cjs +247 -14
- package/dist/cjs/utils/tokens.cjs.map +1 -1
- package/dist/cjs/utils/truncation.cjs +107 -0
- package/dist/cjs/utils/truncation.cjs.map +1 -0
- package/dist/esm/agents/AgentContext.mjs +325 -61
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/common/enum.mjs +13 -0
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/events.mjs +8 -28
- package/dist/esm/events.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +307 -226
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +4 -4
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs +6 -2
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/init.mjs +58 -0
- package/dist/esm/llm/init.mjs.map +1 -0
- package/dist/esm/llm/invoke.mjs +87 -0
- package/dist/esm/llm/invoke.mjs.map +1 -0
- package/dist/esm/llm/openai/index.mjs +2 -0
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/request.mjs +38 -0
- package/dist/esm/llm/request.mjs.map +1 -0
- package/dist/esm/main.mjs +13 -3
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/messages/cache.mjs +76 -89
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/messages/contextPruning.mjs +154 -0
- package/dist/esm/messages/contextPruning.mjs.map +1 -0
- package/dist/esm/messages/contextPruningSettings.mjs +50 -0
- package/dist/esm/messages/contextPruningSettings.mjs.map +1 -0
- package/dist/esm/messages/core.mjs +23 -37
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +156 -11
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/prune.mjs +1158 -52
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/messages/reducer.mjs +83 -0
- package/dist/esm/messages/reducer.mjs.map +1 -0
- package/dist/esm/run.mjs +82 -43
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/stream.mjs +54 -7
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/index.mjs +73 -0
- package/dist/esm/summarization/index.mjs.map +1 -0
- package/dist/esm/summarization/node.mjs +659 -0
- package/dist/esm/summarization/node.mjs.map +1 -0
- package/dist/esm/tools/ToolNode.mjs +16 -8
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/handlers.mjs +2 -0
- package/dist/esm/tools/handlers.mjs.map +1 -1
- package/dist/esm/utils/errors.mjs +111 -0
- package/dist/esm/utils/errors.mjs.map +1 -0
- package/dist/esm/utils/events.mjs +17 -1
- package/dist/esm/utils/events.mjs.map +1 -1
- package/dist/esm/utils/handlers.mjs +16 -0
- package/dist/esm/utils/handlers.mjs.map +1 -1
- package/dist/esm/utils/llm.mjs +10 -1
- package/dist/esm/utils/llm.mjs.map +1 -1
- package/dist/esm/utils/tokens.mjs +245 -15
- package/dist/esm/utils/tokens.mjs.map +1 -1
- package/dist/esm/utils/truncation.mjs +102 -0
- package/dist/esm/utils/truncation.mjs.map +1 -0
- package/dist/types/agents/AgentContext.d.ts +124 -6
- package/dist/types/common/enum.d.ts +14 -1
- package/dist/types/graphs/Graph.d.ts +22 -27
- package/dist/types/index.d.ts +5 -0
- package/dist/types/llm/init.d.ts +18 -0
- package/dist/types/llm/invoke.d.ts +48 -0
- package/dist/types/llm/request.d.ts +14 -0
- package/dist/types/messages/contextPruning.d.ts +42 -0
- package/dist/types/messages/contextPruningSettings.d.ts +44 -0
- package/dist/types/messages/core.d.ts +1 -1
- package/dist/types/messages/format.d.ts +17 -1
- package/dist/types/messages/index.d.ts +3 -0
- package/dist/types/messages/prune.d.ts +162 -1
- package/dist/types/messages/reducer.d.ts +18 -0
- package/dist/types/run.d.ts +12 -1
- package/dist/types/summarization/index.d.ts +20 -0
- package/dist/types/summarization/node.d.ts +29 -0
- package/dist/types/tools/ToolNode.d.ts +3 -1
- package/dist/types/types/graph.d.ts +44 -6
- package/dist/types/types/index.d.ts +1 -0
- package/dist/types/types/run.d.ts +30 -0
- package/dist/types/types/stream.d.ts +31 -4
- package/dist/types/types/summarize.d.ts +47 -0
- package/dist/types/types/tools.d.ts +7 -0
- package/dist/types/utils/errors.d.ts +28 -0
- package/dist/types/utils/events.d.ts +13 -0
- package/dist/types/utils/index.d.ts +2 -0
- package/dist/types/utils/llm.d.ts +4 -0
- package/dist/types/utils/tokens.d.ts +14 -1
- package/dist/types/utils/truncation.d.ts +49 -0
- package/package.json +3 -3
- package/src/agents/AgentContext.ts +388 -58
- package/src/agents/__tests__/AgentContext.test.ts +265 -5
- package/src/common/enum.ts +13 -0
- package/src/events.ts +9 -39
- package/src/graphs/Graph.ts +468 -331
- package/src/index.ts +7 -0
- package/src/llm/anthropic/llm.spec.ts +3 -3
- package/src/llm/anthropic/utils/message_inputs.ts +6 -4
- package/src/llm/bedrock/llm.spec.ts +1 -1
- package/src/llm/bedrock/utils/message_inputs.ts +6 -2
- package/src/llm/init.ts +63 -0
- package/src/llm/invoke.ts +144 -0
- package/src/llm/request.ts +55 -0
- package/src/messages/__tests__/observationMasking.test.ts +221 -0
- package/src/messages/cache.ts +77 -102
- package/src/messages/contextPruning.ts +191 -0
- package/src/messages/contextPruningSettings.ts +90 -0
- package/src/messages/core.ts +32 -53
- package/src/messages/ensureThinkingBlock.test.ts +39 -39
- package/src/messages/format.ts +227 -15
- package/src/messages/formatAgentMessages.test.ts +511 -1
- package/src/messages/index.ts +3 -0
- package/src/messages/prune.ts +1548 -62
- package/src/messages/reducer.ts +22 -0
- package/src/run.ts +104 -51
- package/src/scripts/bedrock-merge-test.ts +1 -1
- package/src/scripts/test-thinking-handoff-bedrock.ts +1 -1
- package/src/scripts/test-thinking-handoff.ts +1 -1
- package/src/scripts/thinking-bedrock.ts +1 -1
- package/src/scripts/thinking.ts +1 -1
- package/src/specs/anthropic.simple.test.ts +1 -1
- package/src/specs/multi-agent-summarization.test.ts +396 -0
- package/src/specs/prune.test.ts +1196 -23
- package/src/specs/summarization-unit.test.ts +868 -0
- package/src/specs/summarization.test.ts +3827 -0
- package/src/specs/summarize-prune.test.ts +376 -0
- package/src/specs/thinking-handoff.test.ts +10 -10
- package/src/specs/thinking-prune.test.ts +7 -4
- package/src/specs/token-accounting-e2e.test.ts +1034 -0
- package/src/specs/token-accounting-pipeline.test.ts +882 -0
- package/src/specs/token-distribution-edge-case.test.ts +25 -26
- package/src/splitStream.test.ts +42 -33
- package/src/stream.ts +64 -11
- package/src/summarization/__tests__/aggregator.test.ts +153 -0
- package/src/summarization/__tests__/node.test.ts +708 -0
- package/src/summarization/__tests__/trigger.test.ts +50 -0
- package/src/summarization/index.ts +102 -0
- package/src/summarization/node.ts +982 -0
- package/src/tools/ToolNode.ts +25 -3
- package/src/types/graph.ts +62 -7
- package/src/types/index.ts +1 -0
- package/src/types/run.ts +32 -0
- package/src/types/stream.ts +45 -5
- package/src/types/summarize.ts +58 -0
- package/src/types/tools.ts +7 -0
- package/src/utils/errors.ts +117 -0
- package/src/utils/events.ts +31 -0
- package/src/utils/handlers.ts +18 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/llm.ts +12 -0
- package/src/utils/tokens.ts +336 -18
- package/src/utils/truncation.ts +124 -0
- package/src/scripts/image.ts +0 -180
|
@@ -383,7 +383,7 @@ describe('AgentContext', () => {
|
|
|
383
383
|
|
|
384
384
|
ctx.markToolsAsDiscovered(['tool1']);
|
|
385
385
|
void ctx.systemRunnable;
|
|
386
|
-
ctx.
|
|
386
|
+
ctx.systemMessageTokens = 100;
|
|
387
387
|
ctx.indexTokenCountMap = { '0': 50 };
|
|
388
388
|
ctx.currentUsage = { input_tokens: 100 };
|
|
389
389
|
|
|
@@ -395,6 +395,70 @@ describe('AgentContext', () => {
|
|
|
395
395
|
expect(ctx.currentUsage).toBeUndefined();
|
|
396
396
|
});
|
|
397
397
|
|
|
398
|
+
it('preserves summarization settings across resets', () => {
|
|
399
|
+
const ctx = createBasicContext({
|
|
400
|
+
agentConfig: {
|
|
401
|
+
summarizationEnabled: true,
|
|
402
|
+
summarizationConfig: {
|
|
403
|
+
provider: Providers.ANTHROPIC,
|
|
404
|
+
model: 'claude-sonnet-4-5',
|
|
405
|
+
prompt: 'Keep decisions and next steps concise.',
|
|
406
|
+
trigger: {
|
|
407
|
+
type: 'token_ratio',
|
|
408
|
+
value: 0.8,
|
|
409
|
+
},
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
ctx.reset();
|
|
415
|
+
|
|
416
|
+
expect(ctx.summarizationEnabled).toBe(true);
|
|
417
|
+
expect(ctx.summarizationConfig).toEqual({
|
|
418
|
+
provider: 'anthropic',
|
|
419
|
+
model: 'claude-sonnet-4-5',
|
|
420
|
+
prompt: 'Keep decisions and next steps concise.',
|
|
421
|
+
trigger: {
|
|
422
|
+
type: 'token_ratio',
|
|
423
|
+
value: 0.8,
|
|
424
|
+
},
|
|
425
|
+
});
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('shouldSkipSummarization returns true when message count unchanged', () => {
|
|
429
|
+
const ctx = createBasicContext({
|
|
430
|
+
agentConfig: { summarizationEnabled: true },
|
|
431
|
+
});
|
|
432
|
+
ctx.markSummarizationTriggered(10);
|
|
433
|
+
expect(ctx.shouldSkipSummarization(10)).toBe(true);
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
it('shouldSkipSummarization returns false with any new messages', () => {
|
|
437
|
+
const ctx = createBasicContext({
|
|
438
|
+
agentConfig: { summarizationEnabled: true },
|
|
439
|
+
});
|
|
440
|
+
ctx.markSummarizationTriggered(10);
|
|
441
|
+
expect(ctx.shouldSkipSummarization(11)).toBe(false);
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it('shouldSkipSummarization returns false when no prior summarization', () => {
|
|
445
|
+
const ctx = createBasicContext({
|
|
446
|
+
agentConfig: { summarizationEnabled: true },
|
|
447
|
+
});
|
|
448
|
+
expect(ctx.shouldSkipSummarization(5)).toBe(false);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('shouldSkipSummarization allows unlimited summarizations per run', () => {
|
|
452
|
+
const ctx = createBasicContext({
|
|
453
|
+
agentConfig: { summarizationEnabled: true },
|
|
454
|
+
});
|
|
455
|
+
for (let i = 0; i < 10; i++) {
|
|
456
|
+
ctx.markSummarizationTriggered(i * 5);
|
|
457
|
+
}
|
|
458
|
+
// Even after 10 summarizations, new messages allow another
|
|
459
|
+
expect(ctx.shouldSkipSummarization(50)).toBe(false);
|
|
460
|
+
});
|
|
461
|
+
|
|
398
462
|
it('rebuilds indexTokenCountMap from base map after reset', async () => {
|
|
399
463
|
const tokenCounter = jest.fn(() => 5);
|
|
400
464
|
const ctx = createBasicContext({
|
|
@@ -611,7 +675,7 @@ describe('AgentContext', () => {
|
|
|
611
675
|
|
|
612
676
|
// Simulate token map update (as done in fromConfig flow)
|
|
613
677
|
ctx.updateTokenMapWithInstructions({ '0': 10, '1': 20 });
|
|
614
|
-
expect(ctx.indexTokenCountMap['0']).toBe(10
|
|
678
|
+
expect(ctx.indexTokenCountMap['0']).toBe(10);
|
|
615
679
|
expect(ctx.indexTokenCountMap['1']).toBe(20);
|
|
616
680
|
|
|
617
681
|
// ========== TURN 2: Tool search results come back ==========
|
|
@@ -674,8 +738,8 @@ describe('AgentContext', () => {
|
|
|
674
738
|
const messageTokenCounts = { '0': 50, '1': 100, '2': 75 };
|
|
675
739
|
ctx.updateTokenMapWithInstructions(messageTokenCounts);
|
|
676
740
|
|
|
677
|
-
// Verify token map: first message
|
|
678
|
-
expect(ctx.indexTokenCountMap['0']).toBe(50
|
|
741
|
+
// Verify token map: first message keeps its real token count (no inflation)
|
|
742
|
+
expect(ctx.indexTokenCountMap['0']).toBe(50);
|
|
679
743
|
expect(ctx.indexTokenCountMap['1']).toBe(100);
|
|
680
744
|
expect(ctx.indexTokenCountMap['2']).toBe(75);
|
|
681
745
|
|
|
@@ -708,7 +772,7 @@ describe('AgentContext', () => {
|
|
|
708
772
|
const newMessageTokenCounts = { '0': 60, '1': 110 };
|
|
709
773
|
ctx.updateTokenMapWithInstructions(newMessageTokenCounts);
|
|
710
774
|
|
|
711
|
-
expect(ctx.indexTokenCountMap['0']).toBe(60
|
|
775
|
+
expect(ctx.indexTokenCountMap['0']).toBe(60);
|
|
712
776
|
expect(ctx.indexTokenCountMap['1']).toBe(110);
|
|
713
777
|
});
|
|
714
778
|
|
|
@@ -823,4 +887,200 @@ describe('AgentContext', () => {
|
|
|
823
887
|
expect(ctx.instructionTokens).toBe(run1Tokens);
|
|
824
888
|
});
|
|
825
889
|
});
|
|
890
|
+
|
|
891
|
+
describe('Summary Token Accounting', () => {
|
|
892
|
+
const charTokenCounter: t.TokenCounter = (msg) => {
|
|
893
|
+
const raw = msg.content;
|
|
894
|
+
if (typeof raw === 'string') return raw.length;
|
|
895
|
+
if (Array.isArray(raw)) {
|
|
896
|
+
let total = 0;
|
|
897
|
+
for (let i = 0; i < raw.length; i++) {
|
|
898
|
+
const item = raw[i] as unknown;
|
|
899
|
+
if (typeof item === 'string') {
|
|
900
|
+
total += item.length;
|
|
901
|
+
} else if (
|
|
902
|
+
typeof item === 'object' &&
|
|
903
|
+
item != null &&
|
|
904
|
+
'text' in item
|
|
905
|
+
) {
|
|
906
|
+
const text = (item as Record<string, unknown>).text;
|
|
907
|
+
if (typeof text === 'string') total += text.length;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
return total;
|
|
911
|
+
}
|
|
912
|
+
return 0;
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
it('mid-run setSummary increases instructionTokens by the summary token count', () => {
|
|
916
|
+
const ctx = createBasicContext({
|
|
917
|
+
agentConfig: { instructions: 'Be helpful.' },
|
|
918
|
+
tokenCounter: charTokenCounter,
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
void ctx.systemRunnable;
|
|
922
|
+
const baseInstructionTokens = ctx.instructionTokens;
|
|
923
|
+
expect(baseInstructionTokens).toBeGreaterThan(0);
|
|
924
|
+
|
|
925
|
+
// Mid-run summary is injected as HumanMessage but still counts as
|
|
926
|
+
// instruction overhead so the pruner reserves budget for it.
|
|
927
|
+
ctx.setSummary('User asked about math. Key results: 2+2=4, 3*5=15.', 50);
|
|
928
|
+
expect(ctx.hasSummary()).toBe(true);
|
|
929
|
+
|
|
930
|
+
void ctx.systemRunnable;
|
|
931
|
+
expect(ctx.instructionTokens).toBe(baseInstructionTokens + 50);
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
it('summary text appears in rebuilt system message', () => {
|
|
935
|
+
const ctx = createBasicContext({
|
|
936
|
+
agentConfig: { instructions: 'Be helpful.' },
|
|
937
|
+
tokenCounter: charTokenCounter,
|
|
938
|
+
});
|
|
939
|
+
|
|
940
|
+
void ctx.systemRunnable;
|
|
941
|
+
ctx.setSummary('Prior context: user computed factorials.', 40);
|
|
942
|
+
|
|
943
|
+
const runnable = ctx.systemRunnable;
|
|
944
|
+
expect(runnable).toBeDefined();
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
it('clearSummary removes summary overhead from instructionTokens', () => {
|
|
948
|
+
const ctx = createBasicContext({
|
|
949
|
+
agentConfig: { instructions: 'Be helpful.' },
|
|
950
|
+
tokenCounter: charTokenCounter,
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
void ctx.systemRunnable;
|
|
954
|
+
const baseTokens = ctx.instructionTokens;
|
|
955
|
+
|
|
956
|
+
ctx.setSummary('Summary of the conversation so far.', 35);
|
|
957
|
+
void ctx.systemRunnable;
|
|
958
|
+
expect(ctx.instructionTokens).toBe(baseTokens + 35);
|
|
959
|
+
|
|
960
|
+
ctx.clearSummary();
|
|
961
|
+
void ctx.systemRunnable;
|
|
962
|
+
expect(ctx.instructionTokens).toBe(baseTokens);
|
|
963
|
+
});
|
|
964
|
+
|
|
965
|
+
it('reset preserves durable summary and maintains token counts', () => {
|
|
966
|
+
const ctx = createBasicContext({
|
|
967
|
+
agentConfig: { instructions: 'Be helpful.' },
|
|
968
|
+
tokenCounter: charTokenCounter,
|
|
969
|
+
indexTokenCountMap: { '0': 10, '1': 20 },
|
|
970
|
+
});
|
|
971
|
+
|
|
972
|
+
void ctx.systemRunnable;
|
|
973
|
+
ctx.setSummary('Summary text.', 15);
|
|
974
|
+
void ctx.systemRunnable;
|
|
975
|
+
expect(ctx.hasSummary()).toBe(true);
|
|
976
|
+
const tokensWithSummary = ctx.instructionTokens;
|
|
977
|
+
|
|
978
|
+
ctx.reset();
|
|
979
|
+
// Summary should survive reset (durable cross-run state)
|
|
980
|
+
expect(ctx.hasSummary()).toBe(true);
|
|
981
|
+
expect(ctx.getSummaryText()).toBe('Summary text.');
|
|
982
|
+
|
|
983
|
+
void ctx.systemRunnable;
|
|
984
|
+
const postResetTokens = ctx.instructionTokens;
|
|
985
|
+
expect(postResetTokens).toBeGreaterThan(0);
|
|
986
|
+
// Token count should be the same since summary is preserved
|
|
987
|
+
expect(postResetTokens).toBe(tokensWithSummary);
|
|
988
|
+
});
|
|
989
|
+
|
|
990
|
+
it('updateTokenMapWithInstructions copies base map without inflating index 0', () => {
|
|
991
|
+
const ctx = createBasicContext({
|
|
992
|
+
agentConfig: { instructions: 'Be helpful.' },
|
|
993
|
+
tokenCounter: charTokenCounter,
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
void ctx.systemRunnable;
|
|
997
|
+
ctx.setSummary('Summary of prior context with key facts.', 40);
|
|
998
|
+
void ctx.systemRunnable;
|
|
999
|
+
|
|
1000
|
+
const instructionTokens = ctx.instructionTokens;
|
|
1001
|
+
expect(instructionTokens).toBeGreaterThan(0);
|
|
1002
|
+
|
|
1003
|
+
const baseMap: Record<string, number> = { '0': 5, '1': 10 };
|
|
1004
|
+
ctx.updateTokenMapWithInstructions(baseMap);
|
|
1005
|
+
|
|
1006
|
+
// Index 0 should contain the real message token count, NOT inflated
|
|
1007
|
+
// with instruction tokens. Instruction overhead is now handled by
|
|
1008
|
+
// getInstructionTokens() in the pruning factory.
|
|
1009
|
+
expect(ctx.indexTokenCountMap['0']).toBe(5);
|
|
1010
|
+
expect(ctx.indexTokenCountMap['1']).toBe(10);
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
it('hasSummary returns false before setSummary and true after', () => {
|
|
1014
|
+
const ctx = createBasicContext({
|
|
1015
|
+
agentConfig: { instructions: 'Be helpful.' },
|
|
1016
|
+
});
|
|
1017
|
+
|
|
1018
|
+
expect(ctx.hasSummary()).toBe(false);
|
|
1019
|
+
ctx.setSummary('Some summary.', 10);
|
|
1020
|
+
expect(ctx.hasSummary()).toBe(true);
|
|
1021
|
+
ctx.clearSummary();
|
|
1022
|
+
expect(ctx.hasSummary()).toBe(false);
|
|
1023
|
+
});
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
describe('shouldSkipSummarization — re-trigger after summary', () => {
|
|
1027
|
+
it('allows re-summarization after rebuildTokenMapAfterSummarization resets baseline', () => {
|
|
1028
|
+
const ctx = createBasicContext();
|
|
1029
|
+
|
|
1030
|
+
expect(ctx.shouldSkipSummarization(25)).toBe(false);
|
|
1031
|
+
ctx.markSummarizationTriggered(25);
|
|
1032
|
+
|
|
1033
|
+
// Same count — skip
|
|
1034
|
+
expect(ctx.shouldSkipSummarization(25)).toBe(true);
|
|
1035
|
+
|
|
1036
|
+
ctx.setSummary('Summary of conversation', 100);
|
|
1037
|
+
// Full compaction: empty state, baseline resets to 0
|
|
1038
|
+
ctx.rebuildTokenMapAfterSummarization({});
|
|
1039
|
+
|
|
1040
|
+
// Baseline is 0 after full compaction. Guard `_lastSummarizationMsgCount > 0`
|
|
1041
|
+
// is false, so all counts are allowed.
|
|
1042
|
+
expect(ctx.shouldSkipSummarization(0)).toBe(false);
|
|
1043
|
+
expect(ctx.shouldSkipSummarization(1)).toBe(false);
|
|
1044
|
+
});
|
|
1045
|
+
|
|
1046
|
+
it('allows summarization after full compaction resets to empty state', () => {
|
|
1047
|
+
const ctx = createBasicContext();
|
|
1048
|
+
|
|
1049
|
+
ctx.markSummarizationTriggered(20);
|
|
1050
|
+
ctx.setSummary('Summary', 50);
|
|
1051
|
+
ctx.rebuildTokenMapAfterSummarization({});
|
|
1052
|
+
|
|
1053
|
+
// Baseline is 0 after full compaction. The guard `_lastSummarizationMsgCount > 0`
|
|
1054
|
+
// is false, so summarization is always allowed — the model starts fresh.
|
|
1055
|
+
expect(ctx.shouldSkipSummarization(0)).toBe(false);
|
|
1056
|
+
expect(ctx.shouldSkipSummarization(1)).toBe(false);
|
|
1057
|
+
});
|
|
1058
|
+
});
|
|
1059
|
+
|
|
1060
|
+
describe('updateLastCallUsage', () => {
|
|
1061
|
+
it('records usage without modifying toolSchemaTokens', () => {
|
|
1062
|
+
const ctx = createBasicContext();
|
|
1063
|
+
ctx.toolSchemaTokens = 200;
|
|
1064
|
+
|
|
1065
|
+
ctx.updateLastCallUsage({ input_tokens: 500, output_tokens: 30 });
|
|
1066
|
+
|
|
1067
|
+
expect(ctx.lastCallUsage).toBeDefined();
|
|
1068
|
+
expect(ctx.lastCallUsage!.inputTokens).toBe(500);
|
|
1069
|
+
expect(ctx.lastCallUsage!.outputTokens).toBe(30);
|
|
1070
|
+
expect(ctx.toolSchemaTokens).toBe(200);
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
it('handles additive cache tokens', () => {
|
|
1074
|
+
const ctx = createBasicContext();
|
|
1075
|
+
|
|
1076
|
+
ctx.updateLastCallUsage({
|
|
1077
|
+
input_tokens: 5,
|
|
1078
|
+
output_tokens: 100,
|
|
1079
|
+
input_token_details: { cache_creation: 8000, cache_read: 0 },
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
// cache_creation (8000) > input_tokens (5) → additive
|
|
1083
|
+
expect(ctx.lastCallUsage!.inputTokens).toBe(8005);
|
|
1084
|
+
});
|
|
1085
|
+
});
|
|
826
1086
|
});
|
package/src/common/enum.ts
CHANGED
|
@@ -21,6 +21,14 @@ export enum GraphEvents {
|
|
|
21
21
|
ON_REASONING_DELTA = 'on_reasoning_delta',
|
|
22
22
|
/** [Custom] Request to execute tools - dispatched by ToolNode, handled by host */
|
|
23
23
|
ON_TOOL_EXECUTE = 'on_tool_execute',
|
|
24
|
+
/** [Custom] Emitted when the summarize node begins generating a summary */
|
|
25
|
+
ON_SUMMARIZE_START = 'on_summarize_start',
|
|
26
|
+
/** [Custom] Delta event carrying the completed summary content */
|
|
27
|
+
ON_SUMMARIZE_DELTA = 'on_summarize_delta',
|
|
28
|
+
/** [Custom] Emitted when the summarize node completes with the final summary */
|
|
29
|
+
ON_SUMMARIZE_COMPLETE = 'on_summarize_complete',
|
|
30
|
+
/** [Custom] Diagnostic logging event for context management observability */
|
|
31
|
+
ON_AGENT_LOG = 'on_agent_log',
|
|
24
32
|
|
|
25
33
|
/* Official Events */
|
|
26
34
|
|
|
@@ -90,6 +98,7 @@ export enum Providers {
|
|
|
90
98
|
export enum GraphNodeKeys {
|
|
91
99
|
TOOLS = 'tools=',
|
|
92
100
|
AGENT = 'agent=',
|
|
101
|
+
SUMMARIZE = 'summarize=',
|
|
93
102
|
ROUTER = 'router',
|
|
94
103
|
PRE_TOOLS = 'pre_tools',
|
|
95
104
|
POST_TOOLS = 'post_tools',
|
|
@@ -123,6 +132,8 @@ export enum ContentTypes {
|
|
|
123
132
|
REASONING = 'reasoning',
|
|
124
133
|
/** Multi-Agent Switch */
|
|
125
134
|
AGENT_UPDATE = 'agent_update',
|
|
135
|
+
/** Framework-level conversation summary block */
|
|
136
|
+
SUMMARY = 'summary',
|
|
126
137
|
/** Bedrock */
|
|
127
138
|
REASONING_CONTENT = 'reasoning_content',
|
|
128
139
|
}
|
|
@@ -169,6 +180,8 @@ export enum Constants {
|
|
|
169
180
|
LC_TRANSFER_TO_ = 'lc_transfer_to_',
|
|
170
181
|
/** Delimiter for MCP tools: toolName_mcp_serverName */
|
|
171
182
|
MCP_DELIMITER = '_mcp_',
|
|
183
|
+
/** Anthropic server tool ID prefix (web_search, code_execution, etc.) */
|
|
184
|
+
ANTHROPIC_SERVER_TOOL_PREFIX = 'srvtoolu_',
|
|
172
185
|
}
|
|
173
186
|
|
|
174
187
|
export enum TitleMethod {
|
package/src/events.ts
CHANGED
|
@@ -7,8 +7,7 @@ import type {
|
|
|
7
7
|
import type { MultiAgentGraph, StandardGraph } from '@/graphs';
|
|
8
8
|
import type { Logger } from 'winston';
|
|
9
9
|
import type * as t from '@/types';
|
|
10
|
-
import {
|
|
11
|
-
import { Constants, Providers } from '@/common';
|
|
10
|
+
import { Constants } from '@/common';
|
|
12
11
|
|
|
13
12
|
export class HandlerRegistry {
|
|
14
13
|
private handlers: Map<string, t.EventHandler> = new Map();
|
|
@@ -46,29 +45,6 @@ export class ModelEndHandler implements t.EventHandler {
|
|
|
46
45
|
if (usage != null && this.collectedUsage != null) {
|
|
47
46
|
this.collectedUsage.push(usage);
|
|
48
47
|
}
|
|
49
|
-
|
|
50
|
-
if (metadata.ls_provider === 'FakeListChatModel') {
|
|
51
|
-
return handleToolCalls(data?.output?.tool_calls, metadata, graph);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
console.log(`====== ${event.toUpperCase()} ======`);
|
|
55
|
-
console.dir(
|
|
56
|
-
{
|
|
57
|
-
usage,
|
|
58
|
-
},
|
|
59
|
-
{ depth: null }
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
const agentContext = graph.getAgentContext(metadata);
|
|
63
|
-
|
|
64
|
-
if (
|
|
65
|
-
agentContext.provider !== Providers.GOOGLE &&
|
|
66
|
-
agentContext.provider !== Providers.BEDROCK
|
|
67
|
-
) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
await handleToolCalls(data?.output?.tool_calls, metadata, graph);
|
|
72
48
|
}
|
|
73
49
|
}
|
|
74
50
|
|
|
@@ -138,10 +114,8 @@ export class TestLLMStreamHandler implements t.EventHandler {
|
|
|
138
114
|
const msg = isMessageChunk ? chunk.message : undefined;
|
|
139
115
|
if (msg && msg.tool_call_chunks && msg.tool_call_chunks.length > 0) {
|
|
140
116
|
console.log(msg.tool_call_chunks);
|
|
141
|
-
} else if (msg && msg.content) {
|
|
142
|
-
|
|
143
|
-
process.stdout.write(msg.content);
|
|
144
|
-
}
|
|
117
|
+
} else if (msg && typeof msg.content === 'string') {
|
|
118
|
+
process.stdout.write(msg.content);
|
|
145
119
|
}
|
|
146
120
|
}
|
|
147
121
|
}
|
|
@@ -150,12 +124,12 @@ export class TestChatStreamHandler implements t.EventHandler {
|
|
|
150
124
|
handle(event: string, data: t.StreamEventData | undefined): void {
|
|
151
125
|
const chunk = data?.chunk;
|
|
152
126
|
const isContentChunk = !!(chunk && 'content' in chunk);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (!content || !isContentChunk) {
|
|
127
|
+
if (!isContentChunk) {
|
|
156
128
|
return;
|
|
157
129
|
}
|
|
158
130
|
|
|
131
|
+
const content = chunk.content;
|
|
132
|
+
|
|
159
133
|
if (chunk.tool_call_chunks && chunk.tool_call_chunks.length > 0) {
|
|
160
134
|
console.dir(chunk.tool_call_chunks, { depth: null });
|
|
161
135
|
}
|
|
@@ -176,18 +150,14 @@ export class LLMStreamHandler implements t.EventHandler {
|
|
|
176
150
|
): void {
|
|
177
151
|
const chunk = data?.chunk;
|
|
178
152
|
const isMessageChunk = !!(chunk && 'message' in chunk);
|
|
179
|
-
const msg = isMessageChunk
|
|
153
|
+
const msg = isMessageChunk ? chunk.message : undefined;
|
|
180
154
|
if (metadata) {
|
|
181
155
|
console.log(metadata);
|
|
182
156
|
}
|
|
183
157
|
if (msg && msg.tool_call_chunks && msg.tool_call_chunks.length > 0) {
|
|
184
158
|
console.log(msg.tool_call_chunks);
|
|
185
|
-
} else if (msg && msg.content) {
|
|
186
|
-
|
|
187
|
-
// const text_delta = msg.content;
|
|
188
|
-
// dispatchCustomEvent(GraphEvents.CHAT_MODEL_STREAM, { chunk }, config);
|
|
189
|
-
process.stdout.write(msg.content);
|
|
190
|
-
}
|
|
159
|
+
} else if (msg && typeof msg.content === 'string') {
|
|
160
|
+
process.stdout.write(msg.content);
|
|
191
161
|
}
|
|
192
162
|
}
|
|
193
163
|
}
|