@librechat/agents 3.1.85 → 3.1.87
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 +69 -0
- package/dist/cjs/agents/AgentContext.cjs +7 -2
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/events.cjs +23 -0
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +133 -18
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/index.cjs +251 -53
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/init.cjs +1 -5
- package/dist/cjs/llm/init.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +113 -24
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/index.cjs +3 -1
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +18 -5
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/openai/index.cjs +253 -0
- package/dist/cjs/openai/index.cjs.map +1 -0
- package/dist/cjs/responses/index.cjs +448 -0
- package/dist/cjs/responses/index.cjs.map +1 -0
- package/dist/cjs/run.cjs +108 -7
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/AgentSession.cjs +1057 -0
- package/dist/cjs/session/AgentSession.cjs.map +1 -0
- package/dist/cjs/session/JsonlSessionStore.cjs +425 -0
- package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -0
- package/dist/cjs/session/handlers.cjs +221 -0
- package/dist/cjs/session/handlers.cjs.map +1 -0
- package/dist/cjs/session/ids.cjs +22 -0
- package/dist/cjs/session/ids.cjs.map +1 -0
- package/dist/cjs/session/messageSerialization.cjs +179 -0
- package/dist/cjs/session/messageSerialization.cjs.map +1 -0
- package/dist/cjs/stream.cjs +472 -11
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +1 -1
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +177 -59
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/eagerEventExecution.cjs +113 -0
- package/dist/cjs/tools/eagerEventExecution.cjs.map +1 -0
- package/dist/cjs/tools/handlers.cjs +1 -1
- package/dist/cjs/tools/handlers.cjs.map +1 -1
- package/dist/cjs/tools/streamedToolCallSeals.cjs +42 -0
- package/dist/cjs/tools/streamedToolCallSeals.cjs.map +1 -0
- package/dist/esm/agents/AgentContext.mjs +7 -2
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/events.mjs +23 -1
- package/dist/esm/events.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +133 -18
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/llm/anthropic/index.mjs +251 -53
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/init.mjs +1 -5
- package/dist/esm/llm/init.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +113 -25
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/index.mjs +4 -2
- package/dist/esm/llm/openrouter/index.mjs.map +1 -1
- package/dist/esm/main.mjs +5 -1
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/openai/index.mjs +246 -0
- package/dist/esm/openai/index.mjs.map +1 -0
- package/dist/esm/responses/index.mjs +440 -0
- package/dist/esm/responses/index.mjs.map +1 -0
- package/dist/esm/run.mjs +108 -7
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/AgentSession.mjs +1054 -0
- package/dist/esm/session/AgentSession.mjs.map +1 -0
- package/dist/esm/session/JsonlSessionStore.mjs +422 -0
- package/dist/esm/session/JsonlSessionStore.mjs.map +1 -0
- package/dist/esm/session/handlers.mjs +219 -0
- package/dist/esm/session/handlers.mjs.map +1 -0
- package/dist/esm/session/ids.mjs +17 -0
- package/dist/esm/session/ids.mjs.map +1 -0
- package/dist/esm/session/messageSerialization.mjs +173 -0
- package/dist/esm/session/messageSerialization.mjs.map +1 -0
- package/dist/esm/stream.mjs +473 -12
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +1 -1
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +177 -59
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/eagerEventExecution.mjs +107 -0
- package/dist/esm/tools/eagerEventExecution.mjs.map +1 -0
- package/dist/esm/tools/handlers.mjs +1 -1
- package/dist/esm/tools/handlers.mjs.map +1 -1
- package/dist/esm/tools/streamedToolCallSeals.mjs +36 -0
- package/dist/esm/tools/streamedToolCallSeals.mjs.map +1 -0
- package/dist/types/events.d.ts +1 -0
- package/dist/types/graphs/Graph.d.ts +24 -9
- package/dist/types/index.d.ts +1 -0
- package/dist/types/llm/openai/index.d.ts +1 -0
- package/dist/types/openai/index.d.ts +75 -0
- package/dist/types/responses/index.d.ts +97 -0
- package/dist/types/run.d.ts +2 -0
- package/dist/types/session/AgentSession.d.ts +32 -0
- package/dist/types/session/JsonlSessionStore.d.ts +67 -0
- package/dist/types/session/handlers.d.ts +8 -0
- package/dist/types/session/ids.d.ts +4 -0
- package/dist/types/session/index.d.ts +5 -0
- package/dist/types/session/messageSerialization.d.ts +7 -0
- package/dist/types/session/types.d.ts +191 -0
- package/dist/types/tools/ToolNode.d.ts +12 -1
- package/dist/types/tools/eagerEventExecution.d.ts +23 -0
- package/dist/types/tools/streamedToolCallSeals.d.ts +13 -0
- package/dist/types/types/hitl.d.ts +4 -0
- package/dist/types/types/run.d.ts +11 -1
- package/dist/types/types/tools.d.ts +36 -0
- package/package.json +19 -2
- package/src/__tests__/stream.eagerEventExecution.test.ts +2458 -0
- package/src/agents/AgentContext.ts +7 -2
- package/src/agents/__tests__/AgentContext.test.ts +254 -5
- package/src/events.ts +29 -0
- package/src/graphs/Graph.ts +224 -50
- package/src/graphs/MultiAgentGraph.ts +1 -1
- package/src/graphs/__tests__/composition.smoke.test.ts +30 -0
- package/src/index.ts +3 -0
- package/src/llm/anthropic/index.ts +356 -84
- package/src/llm/anthropic/llm.spec.ts +64 -0
- package/src/llm/custom-chat-models.smoke.test.ts +175 -4
- package/src/llm/openai/contentBlocks.test.ts +35 -0
- package/src/llm/openai/deepseek.test.ts +201 -2
- package/src/llm/openai/index.ts +171 -26
- package/src/llm/openai/utils/index.ts +22 -0
- package/src/llm/openrouter/index.ts +4 -2
- package/src/openai/__tests__/openai.test.ts +337 -0
- package/src/openai/index.ts +404 -0
- package/src/responses/__tests__/responses.test.ts +652 -0
- package/src/responses/index.ts +677 -0
- package/src/run.ts +158 -8
- package/src/scripts/compare_pi_vs_ours.ts +592 -173
- package/src/scripts/session_live.ts +548 -0
- package/src/session/AgentSession.ts +1432 -0
- package/src/session/JsonlSessionStore.ts +572 -0
- package/src/session/__tests__/JsonlSessionStore.test.ts +1410 -0
- package/src/session/__tests__/handlers.test.ts +161 -0
- package/src/session/handlers.ts +272 -0
- package/src/session/ids.ts +17 -0
- package/src/session/index.ts +44 -0
- package/src/session/messageSerialization.ts +207 -0
- package/src/session/types.ts +275 -0
- package/src/specs/custom-event-await.test.ts +89 -0
- package/src/specs/summarization.test.ts +1 -1
- package/src/stream.ts +755 -48
- package/src/summarization/node.ts +1 -1
- package/src/tools/ToolNode.ts +299 -126
- package/src/tools/__tests__/ToolNode.eagerEventExecution.test.ts +373 -0
- package/src/tools/__tests__/handlers.test.ts +2 -1
- package/src/tools/__tests__/hitl.test.ts +206 -110
- package/src/tools/eagerEventExecution.ts +153 -0
- package/src/tools/handlers.ts +8 -4
- package/src/tools/streamedToolCallSeals.ts +57 -0
- package/src/types/hitl.ts +4 -0
- package/src/types/run.ts +11 -0
- package/src/types/tools.ts +36 -0
- package/dist/cjs/llm/text.cjs +0 -69
- package/dist/cjs/llm/text.cjs.map +0 -1
- package/dist/esm/llm/text.mjs +0 -67
- package/dist/esm/llm/text.mjs.map +0 -1
|
@@ -758,8 +758,13 @@ export class AgentContext {
|
|
|
758
758
|
return messages.length;
|
|
759
759
|
}
|
|
760
760
|
|
|
761
|
-
|
|
762
|
-
|
|
761
|
+
for (let index = lastIndex; index >= 0; index--) {
|
|
762
|
+
if (messages[index].getType() === 'human') {
|
|
763
|
+
if (promptCacheProvider === Providers.OPENROUTER && index === 0) {
|
|
764
|
+
return 1;
|
|
765
|
+
}
|
|
766
|
+
return index;
|
|
767
|
+
}
|
|
763
768
|
}
|
|
764
769
|
|
|
765
770
|
return messages.length;
|
|
@@ -368,7 +368,7 @@ describe('AgentContext', () => {
|
|
|
368
368
|
expect(result[4].content).toBe('Latest');
|
|
369
369
|
});
|
|
370
370
|
|
|
371
|
-
it('
|
|
371
|
+
it('keeps Anthropic dynamic instructions attached to the latest user turn during tool follow-up', async () => {
|
|
372
372
|
const ctx = createBasicContext({
|
|
373
373
|
agentConfig: {
|
|
374
374
|
provider: Providers.ANTHROPIC,
|
|
@@ -401,10 +401,115 @@ describe('AgentContext', () => {
|
|
|
401
401
|
}),
|
|
402
402
|
]);
|
|
403
403
|
|
|
404
|
-
expect(result[1].content).toBe('
|
|
405
|
-
expect(
|
|
406
|
-
expect(result[3].
|
|
407
|
-
expect(result[4].
|
|
404
|
+
expect(result[1].content).toBe('Dynamic instructions');
|
|
405
|
+
expect(result[2].content).toBe('Use the tool');
|
|
406
|
+
expect((result[3] as AIMessage).tool_calls?.[0]?.id).toBe('call_1');
|
|
407
|
+
expect(result[4].getType()).toBe('tool');
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('keeps Anthropic stable history cacheable before dynamic tool-follow-up context', async () => {
|
|
411
|
+
const ctx = createBasicContext({
|
|
412
|
+
agentConfig: {
|
|
413
|
+
provider: Providers.ANTHROPIC,
|
|
414
|
+
clientOptions: {
|
|
415
|
+
model: 'claude-3-5-sonnet',
|
|
416
|
+
promptCache: true,
|
|
417
|
+
},
|
|
418
|
+
instructions: 'Stable instructions',
|
|
419
|
+
additional_instructions: 'Dynamic instructions',
|
|
420
|
+
},
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
const result = await ctx.systemRunnable!.invoke([
|
|
424
|
+
new HumanMessage('Earlier'),
|
|
425
|
+
new AIMessage('Earlier assistant response'),
|
|
426
|
+
new HumanMessage('Use the tool'),
|
|
427
|
+
new AIMessage({
|
|
428
|
+
content: '',
|
|
429
|
+
tool_calls: [
|
|
430
|
+
{
|
|
431
|
+
id: 'call_1',
|
|
432
|
+
name: 'calculator',
|
|
433
|
+
args: { expression: '2+2' },
|
|
434
|
+
type: 'tool_call',
|
|
435
|
+
},
|
|
436
|
+
],
|
|
437
|
+
}),
|
|
438
|
+
new ToolMessage({
|
|
439
|
+
content: '4',
|
|
440
|
+
name: 'calculator',
|
|
441
|
+
tool_call_id: 'call_1',
|
|
442
|
+
}),
|
|
443
|
+
]);
|
|
444
|
+
const stableAssistant = result[2].content as TestSystemContentBlock[];
|
|
445
|
+
|
|
446
|
+
expect(result[1].content).toBe('Earlier');
|
|
447
|
+
expect(stableAssistant[0]).toMatchObject({
|
|
448
|
+
type: 'text',
|
|
449
|
+
text: 'Earlier assistant response',
|
|
450
|
+
cache_control: { type: 'ephemeral' },
|
|
451
|
+
});
|
|
452
|
+
expect(result[3].content).toBe('Dynamic instructions');
|
|
453
|
+
expect(result[4].content).toBe('Use the tool');
|
|
454
|
+
expect((result[5] as AIMessage).tool_calls?.[0]?.id).toBe('call_1');
|
|
455
|
+
expect(result[6].getType()).toBe('tool');
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
it('keeps Anthropic dynamic context on latest no-tool turn after mixed prior turns', async () => {
|
|
459
|
+
const ctx = createBasicContext({
|
|
460
|
+
agentConfig: {
|
|
461
|
+
provider: Providers.ANTHROPIC,
|
|
462
|
+
clientOptions: {
|
|
463
|
+
model: 'claude-3-5-sonnet',
|
|
464
|
+
promptCache: true,
|
|
465
|
+
},
|
|
466
|
+
instructions: 'Stable instructions',
|
|
467
|
+
additional_instructions: 'Dynamic instructions',
|
|
468
|
+
},
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
const result = await ctx.systemRunnable!.invoke([
|
|
472
|
+
new HumanMessage('First turn, no tools'),
|
|
473
|
+
new AIMessage('First assistant response'),
|
|
474
|
+
new HumanMessage('Use the tool'),
|
|
475
|
+
new AIMessage({
|
|
476
|
+
content: '',
|
|
477
|
+
tool_calls: [
|
|
478
|
+
{
|
|
479
|
+
id: 'call_1',
|
|
480
|
+
name: 'calculator',
|
|
481
|
+
args: { expression: '2+2' },
|
|
482
|
+
type: 'tool_call',
|
|
483
|
+
},
|
|
484
|
+
],
|
|
485
|
+
}),
|
|
486
|
+
new ToolMessage({
|
|
487
|
+
content: '4',
|
|
488
|
+
name: 'calculator',
|
|
489
|
+
tool_call_id: 'call_1',
|
|
490
|
+
}),
|
|
491
|
+
new AIMessage('4'),
|
|
492
|
+
new HumanMessage('Now answer without tools'),
|
|
493
|
+
]);
|
|
494
|
+
const firstAssistant = result[2].content as TestSystemContentBlock[];
|
|
495
|
+
const toolAnswer = result[6].content as TestSystemContentBlock[];
|
|
496
|
+
|
|
497
|
+
expect(result[1].content).toBe('First turn, no tools');
|
|
498
|
+
expect(firstAssistant[0]).toMatchObject({
|
|
499
|
+
type: 'text',
|
|
500
|
+
text: 'First assistant response',
|
|
501
|
+
cache_control: { type: 'ephemeral' },
|
|
502
|
+
});
|
|
503
|
+
expect(result[3].content).toBe('Use the tool');
|
|
504
|
+
expect((result[4] as AIMessage).tool_calls?.[0]?.id).toBe('call_1');
|
|
505
|
+
expect(result[5].getType()).toBe('tool');
|
|
506
|
+
expect(toolAnswer[0]).toMatchObject({
|
|
507
|
+
type: 'text',
|
|
508
|
+
text: '4',
|
|
509
|
+
cache_control: { type: 'ephemeral' },
|
|
510
|
+
});
|
|
511
|
+
expect(result[7].content).toBe('Dynamic instructions');
|
|
512
|
+
expect(result[8].content).toBe('Now answer without tools');
|
|
408
513
|
});
|
|
409
514
|
|
|
410
515
|
it('caches stable OpenRouter history before dynamic instructions', async () => {
|
|
@@ -437,6 +542,150 @@ describe('AgentContext', () => {
|
|
|
437
542
|
expect(result[4].content).toBe('Latest');
|
|
438
543
|
});
|
|
439
544
|
|
|
545
|
+
it('keeps OpenRouter opening user message before dynamic tool-follow-up context', async () => {
|
|
546
|
+
const ctx = createBasicContext({
|
|
547
|
+
agentConfig: {
|
|
548
|
+
provider: Providers.OPENROUTER,
|
|
549
|
+
clientOptions: {
|
|
550
|
+
model: 'anthropic/claude-haiku-4.5',
|
|
551
|
+
promptCache: true,
|
|
552
|
+
},
|
|
553
|
+
instructions: 'Stable instructions',
|
|
554
|
+
additional_instructions: 'Dynamic instructions',
|
|
555
|
+
},
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
const result = await ctx.systemRunnable!.invoke([
|
|
559
|
+
new HumanMessage('Use the tool'),
|
|
560
|
+
new AIMessage({
|
|
561
|
+
content: '',
|
|
562
|
+
tool_calls: [
|
|
563
|
+
{
|
|
564
|
+
id: 'call_1',
|
|
565
|
+
name: 'calculator',
|
|
566
|
+
args: { expression: '2+2' },
|
|
567
|
+
type: 'tool_call',
|
|
568
|
+
},
|
|
569
|
+
],
|
|
570
|
+
}),
|
|
571
|
+
new ToolMessage({
|
|
572
|
+
content: '4',
|
|
573
|
+
name: 'calculator',
|
|
574
|
+
tool_call_id: 'call_1',
|
|
575
|
+
}),
|
|
576
|
+
]);
|
|
577
|
+
|
|
578
|
+
expect(result[1].content).toBe('Use the tool');
|
|
579
|
+
expect(result[2].content).toBe('Dynamic instructions');
|
|
580
|
+
expect((result[3] as AIMessage).tool_calls?.[0]?.id).toBe('call_1');
|
|
581
|
+
expect(result[4].getType()).toBe('tool');
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
it('keeps OpenRouter stable history cacheable before dynamic tool-follow-up context', async () => {
|
|
585
|
+
const ctx = createBasicContext({
|
|
586
|
+
agentConfig: {
|
|
587
|
+
provider: Providers.OPENROUTER,
|
|
588
|
+
clientOptions: {
|
|
589
|
+
model: 'anthropic/claude-haiku-4.5',
|
|
590
|
+
promptCache: true,
|
|
591
|
+
},
|
|
592
|
+
instructions: 'Stable instructions',
|
|
593
|
+
additional_instructions: 'Dynamic instructions',
|
|
594
|
+
},
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
const result = await ctx.systemRunnable!.invoke([
|
|
598
|
+
new HumanMessage('Earlier'),
|
|
599
|
+
new AIMessage('Earlier assistant response'),
|
|
600
|
+
new HumanMessage('Use the tool'),
|
|
601
|
+
new AIMessage({
|
|
602
|
+
content: '',
|
|
603
|
+
tool_calls: [
|
|
604
|
+
{
|
|
605
|
+
id: 'call_1',
|
|
606
|
+
name: 'calculator',
|
|
607
|
+
args: { expression: '2+2' },
|
|
608
|
+
type: 'tool_call',
|
|
609
|
+
},
|
|
610
|
+
],
|
|
611
|
+
}),
|
|
612
|
+
new ToolMessage({
|
|
613
|
+
content: '4',
|
|
614
|
+
name: 'calculator',
|
|
615
|
+
tool_call_id: 'call_1',
|
|
616
|
+
}),
|
|
617
|
+
]);
|
|
618
|
+
const stableAssistant = result[2].content as TestSystemContentBlock[];
|
|
619
|
+
|
|
620
|
+
expect(result[1].content).toBe('Earlier');
|
|
621
|
+
expect(stableAssistant[0]).toMatchObject({
|
|
622
|
+
type: 'text',
|
|
623
|
+
text: 'Earlier assistant response',
|
|
624
|
+
cache_control: { type: 'ephemeral' },
|
|
625
|
+
});
|
|
626
|
+
expect(result[3].content).toBe('Dynamic instructions');
|
|
627
|
+
expect(result[4].content).toBe('Use the tool');
|
|
628
|
+
expect((result[5] as AIMessage).tool_calls?.[0]?.id).toBe('call_1');
|
|
629
|
+
expect(result[6].getType()).toBe('tool');
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
it('keeps OpenRouter dynamic context on latest no-tool turn after mixed prior turns', async () => {
|
|
633
|
+
const ctx = createBasicContext({
|
|
634
|
+
agentConfig: {
|
|
635
|
+
provider: Providers.OPENROUTER,
|
|
636
|
+
clientOptions: {
|
|
637
|
+
model: 'anthropic/claude-haiku-4.5',
|
|
638
|
+
promptCache: true,
|
|
639
|
+
},
|
|
640
|
+
instructions: 'Stable instructions',
|
|
641
|
+
additional_instructions: 'Dynamic instructions',
|
|
642
|
+
},
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
const result = await ctx.systemRunnable!.invoke([
|
|
646
|
+
new HumanMessage('First turn, no tools'),
|
|
647
|
+
new AIMessage('First assistant response'),
|
|
648
|
+
new HumanMessage('Use the tool'),
|
|
649
|
+
new AIMessage({
|
|
650
|
+
content: '',
|
|
651
|
+
tool_calls: [
|
|
652
|
+
{
|
|
653
|
+
id: 'call_1',
|
|
654
|
+
name: 'calculator',
|
|
655
|
+
args: { expression: '2+2' },
|
|
656
|
+
type: 'tool_call',
|
|
657
|
+
},
|
|
658
|
+
],
|
|
659
|
+
}),
|
|
660
|
+
new ToolMessage({
|
|
661
|
+
content: '4',
|
|
662
|
+
name: 'calculator',
|
|
663
|
+
tool_call_id: 'call_1',
|
|
664
|
+
}),
|
|
665
|
+
new AIMessage('4'),
|
|
666
|
+
new HumanMessage('Now answer without tools'),
|
|
667
|
+
]);
|
|
668
|
+
const firstAssistant = result[2].content as TestSystemContentBlock[];
|
|
669
|
+
const toolAnswer = result[6].content as TestSystemContentBlock[];
|
|
670
|
+
|
|
671
|
+
expect(result[1].content).toBe('First turn, no tools');
|
|
672
|
+
expect(firstAssistant[0]).toMatchObject({
|
|
673
|
+
type: 'text',
|
|
674
|
+
text: 'First assistant response',
|
|
675
|
+
cache_control: { type: 'ephemeral' },
|
|
676
|
+
});
|
|
677
|
+
expect(result[3].content).toBe('Use the tool');
|
|
678
|
+
expect((result[4] as AIMessage).tool_calls?.[0]?.id).toBe('call_1');
|
|
679
|
+
expect(result[5].getType()).toBe('tool');
|
|
680
|
+
expect(toolAnswer[0]).toMatchObject({
|
|
681
|
+
type: 'text',
|
|
682
|
+
text: '4',
|
|
683
|
+
cache_control: { type: 'ephemeral' },
|
|
684
|
+
});
|
|
685
|
+
expect(result[7].content).toBe('Dynamic instructions');
|
|
686
|
+
expect(result[8].content).toBe('Now answer without tools');
|
|
687
|
+
});
|
|
688
|
+
|
|
440
689
|
it('adds OpenRouter body cache points when there is no dynamic tail', async () => {
|
|
441
690
|
const ctx = createBasicContext({
|
|
442
691
|
agentConfig: {
|
package/src/events.ts
CHANGED
|
@@ -21,6 +21,35 @@ export class HandlerRegistry {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
export function composeEventHandlers(
|
|
25
|
+
...handlerSets: Array<Record<string, t.EventHandler> | undefined>
|
|
26
|
+
): Record<string, t.EventHandler> {
|
|
27
|
+
const composed: Partial<Record<string, t.EventHandler>> = {};
|
|
28
|
+
|
|
29
|
+
for (const handlerSet of handlerSets) {
|
|
30
|
+
if (!handlerSet) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
for (const [eventType, handler] of Object.entries(handlerSet)) {
|
|
34
|
+
const previous = composed[eventType];
|
|
35
|
+
if (previous === undefined) {
|
|
36
|
+
composed[eventType] = handler;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
composed[eventType] = {
|
|
40
|
+
handle: async (
|
|
41
|
+
...args: Parameters<t.EventHandler['handle']>
|
|
42
|
+
): Promise<void> => {
|
|
43
|
+
await previous.handle(...args);
|
|
44
|
+
await handler.handle(...args);
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return composed as Record<string, t.EventHandler>;
|
|
51
|
+
}
|
|
52
|
+
|
|
24
53
|
export class ModelEndHandler implements t.EventHandler {
|
|
25
54
|
collectedUsage?: UsageMetadata[];
|
|
26
55
|
constructor(collectedUsage?: UsageMetadata[]) {
|