@librechat/agents 3.7.4 → 3.7.5

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.
@@ -59,14 +59,12 @@ import {
59
59
  addTailCacheControl,
60
60
  resolvePromptCacheTtl,
61
61
  resolveBedrockPromptCacheTtl,
62
- supportsBedrockToolCache,
63
62
  isSyntheticProviderContextMessage,
64
63
  compactSyntheticProviderContextMessage,
65
64
  getMessageId,
66
65
  getMessageCreationContentMetadata,
67
66
  splitAssistantTextContentByPhase,
68
67
  makeIsDeferred,
69
- partitionAndMarkAnthropicToolCache,
70
68
  DEFAULT_RETAIN_RECENT_TURNS,
71
69
  resolveIntraTurnRetainTokens,
72
70
  splitAtRecencyBoundary,
@@ -149,14 +147,13 @@ import {
149
147
  findCallback,
150
148
  type CallbackEntry,
151
149
  } from '@/utils/callbacks';
152
- import { partitionAndMarkOpenRouterToolCache } from '@/llm/openrouter/toolCache';
153
150
  import { ToolNode as CustomToolNode, toolsCondition } from '@/tools/ToolNode';
154
151
  import { shouldTraceToolNodeForLangfuse } from '@/langfuseToolOutputTracing';
155
152
  import { createLocalCodingToolBundle } from '@/tools/local/LocalCodingTools';
156
153
  import { SUBAGENT_REPLAY_CONTROLLER } from '@/tools/subagent/SubagentReplay';
157
154
  import { applyGraphRuntimeConfig } from '@/graphs/applyGraphRuntimeConfig';
158
- import { partitionAndMarkBedrockToolCache } from '@/llm/bedrock/toolCache';
159
155
  import { createContextPressureMeter } from '@/llm/contextPressureMeter';
156
+ import { prepareToolsForPromptCache } from '@/llm/promptCacheTools';
160
157
  import { safeDispatchCustomEvent, emitAgentLog } from '@/utils/events';
161
158
  import { prepareProviderRequest } from '@/llm/prepareProviderRequest';
162
159
  import { createCloudflareCodingToolBundle } from '@/tools/cloudflare';
@@ -2789,6 +2786,27 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
2789
2786
  };
2790
2787
  }
2791
2788
 
2789
+ private getPreparedToolsForBinding(
2790
+ agentContext: AgentContext,
2791
+ provider: t.ProviderName = agentContext.provider,
2792
+ clientOptions: t.ClientOptions | undefined = agentContext.clientOptions
2793
+ ): t.GraphTools | undefined {
2794
+ const tools = resolveLocalToolsForBinding({
2795
+ tools: agentContext.getToolsForBinding(),
2796
+ toolExecution: this.toolExecution,
2797
+ toolRegistry: agentContext.toolRegistry,
2798
+ discoveredToolNames: new Set(agentContext.getDiscoveredTools()),
2799
+ });
2800
+ return prepareToolsForPromptCache({
2801
+ provider,
2802
+ clientOptions,
2803
+ tools,
2804
+ isDeferred: makeIsDeferred(
2805
+ agentContext.getEffectiveToolDefinitions()
2806
+ ),
2807
+ });
2808
+ }
2809
+
2792
2810
  createCallModel(agentId = 'default') {
2793
2811
  return async (
2794
2812
  state: t.AgentSubgraphState,
@@ -2846,13 +2864,6 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
2846
2864
  agentContext.markToolsAsDiscovered(discoveredNames);
2847
2865
  }
2848
2866
 
2849
- const rawToolsForBinding = resolveLocalToolsForBinding({
2850
- tools: agentContext.getToolsForBinding(),
2851
- toolExecution: this.toolExecution,
2852
- toolRegistry: agentContext.toolRegistry,
2853
- discoveredToolNames: new Set(agentContext.getDiscoveredTools()),
2854
- });
2855
-
2856
2867
  /**
2857
2868
  * Anthropic prompt-cache breakpoint on the tool definitions.
2858
2869
  *
@@ -2866,69 +2877,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
2866
2877
  * Discovered deferred tools that arrive across turns sit *after*
2867
2878
  * the breakpoint and don't invalidate the prefix.
2868
2879
  */
2869
- let toolsForBinding = rawToolsForBinding;
2870
- const isDeferredTool = makeIsDeferred(
2871
- agentContext.getEffectiveToolDefinitions()
2872
- );
2873
- if (
2874
- agentContext.provider === Providers.ANTHROPIC &&
2875
- (agentContext.clientOptions as t.AnthropicClientOptions | undefined)
2876
- ?.promptCache === true
2877
- ) {
2878
- toolsForBinding =
2879
- partitionAndMarkAnthropicToolCache(
2880
- rawToolsForBinding,
2881
- isDeferredTool,
2882
- resolvePromptCacheTtl(
2883
- (
2884
- agentContext.clientOptions as
2885
- | t.AnthropicClientOptions
2886
- | undefined
2887
- )?.promptCacheTtl
2888
- )
2889
- ) ?? rawToolsForBinding;
2890
- } else if (
2891
- agentContext.provider === Providers.OPENROUTER &&
2892
- (
2893
- agentContext.clientOptions as
2894
- | t.ProviderOptionsMap[Providers.OPENROUTER]
2895
- | undefined
2896
- )?.promptCache === true
2897
- ) {
2898
- toolsForBinding =
2899
- partitionAndMarkOpenRouterToolCache(
2900
- rawToolsForBinding,
2901
- isDeferredTool,
2902
- resolvePromptCacheTtl(
2903
- (
2904
- agentContext.clientOptions as
2905
- | t.ProviderOptionsMap[Providers.OPENROUTER]
2906
- | undefined
2907
- )?.promptCacheTtl
2908
- )
2909
- ) ?? rawToolsForBinding;
2910
- } else if (
2911
- agentContext.provider === Providers.BEDROCK &&
2912
- (
2913
- agentContext.clientOptions as
2914
- | t.BedrockAnthropicClientOptions
2915
- | undefined
2916
- )?.promptCache === true
2917
- ) {
2918
- const bedrockModel = (
2919
- agentContext.clientOptions as { model?: string } | undefined
2920
- )?.model;
2921
- // An omitted model falls back to LangChain's default Claude model (which
2922
- // supports tool caching); only an explicit non-Claude model (e.g. Nova)
2923
- // skips tool marking so its stray marker never leaks into toolConfig.
2924
- if (bedrockModel == null || supportsBedrockToolCache(bedrockModel)) {
2925
- toolsForBinding =
2926
- partitionAndMarkBedrockToolCache(
2927
- rawToolsForBinding,
2928
- isDeferredTool
2929
- ) ?? rawToolsForBinding;
2930
- }
2931
- }
2880
+ const toolsForBinding = this.getPreparedToolsForBinding(agentContext);
2932
2881
 
2933
2882
  let model =
2934
2883
  this.overrideModel ??
@@ -4924,6 +4873,15 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
4924
4873
  runId: this.runId,
4925
4874
  isMultiAgent: this.isMultiAgentGraph(),
4926
4875
  hookRegistry: this.hookRegistry,
4876
+ getToolsForBinding: (
4877
+ provider: t.ProviderName,
4878
+ clientOptions: t.ClientOptions | undefined
4879
+ ): t.GraphTools | undefined =>
4880
+ this.getPreparedToolsForBinding(
4881
+ agentContext,
4882
+ provider,
4883
+ clientOptions
4884
+ ),
4927
4885
  /**
4928
4886
  * Live references (both maps are cleared in place, never
4929
4887
  * replaced), so summarization streams share the run's event
@@ -0,0 +1,58 @@
1
+ import type * as t from '@/types';
2
+ import {
3
+ resolvePromptCacheTtl,
4
+ supportsBedrockToolCache,
5
+ } from '@/messages/cache';
6
+ import { partitionAndMarkAnthropicToolCache } from '@/messages/anthropicToolCache';
7
+ import { partitionAndMarkOpenRouterToolCache } from '@/llm/openrouter/toolCache';
8
+ import { partitionAndMarkBedrockToolCache } from '@/llm/bedrock/toolCache';
9
+ import { Providers } from '@/common';
10
+
11
+ export function prepareToolsForPromptCache(params: {
12
+ provider: t.ProviderName;
13
+ clientOptions?: t.ClientOptions;
14
+ tools?: t.GraphTools;
15
+ isDeferred: (toolName: string) => boolean;
16
+ }): t.GraphTools | undefined {
17
+ const { provider, clientOptions, tools, isDeferred } = params;
18
+ if (provider === Providers.ANTHROPIC) {
19
+ const options = clientOptions as t.AnthropicClientOptions | undefined;
20
+ if (options?.promptCache !== true) {
21
+ return tools;
22
+ }
23
+ return (
24
+ partitionAndMarkAnthropicToolCache(
25
+ tools,
26
+ isDeferred,
27
+ resolvePromptCacheTtl(options.promptCacheTtl)
28
+ ) ?? tools
29
+ );
30
+ }
31
+ if (provider === Providers.OPENROUTER) {
32
+ const options = clientOptions as
33
+ | t.ProviderOptionsMap[Providers.OPENROUTER]
34
+ | undefined;
35
+ if (options?.promptCache !== true) {
36
+ return tools;
37
+ }
38
+ return (
39
+ partitionAndMarkOpenRouterToolCache(
40
+ tools,
41
+ isDeferred,
42
+ resolvePromptCacheTtl(options.promptCacheTtl)
43
+ ) ?? tools
44
+ );
45
+ }
46
+ if (provider !== Providers.BEDROCK) {
47
+ return tools;
48
+ }
49
+ const options = clientOptions as t.BedrockAnthropicClientOptions | undefined;
50
+ if (options?.promptCache !== true) {
51
+ return tools;
52
+ }
53
+ const model = (options as { model?: string }).model;
54
+ if (model != null && !supportsBedrockToolCache(model)) {
55
+ return tools;
56
+ }
57
+ return partitionAndMarkBedrockToolCache(tools, isDeferred) ?? tools;
58
+ }
@@ -24,7 +24,9 @@ import {
24
24
  } from '@/llm/streamLimits';
25
25
  import {
26
26
  addTailCacheControl,
27
+ addBedrockTailCacheControl,
27
28
  resolvePromptCacheTtl,
29
+ resolveBedrockPromptCacheTtl,
28
30
  type PromptCacheTtl,
29
31
  } from '@/messages/cache';
30
32
  import {
@@ -41,7 +43,9 @@ import {
41
43
  } from '@/common';
42
44
  import { safeDispatchCustomEvent, emitAgentLog } from '@/utils/events';
43
45
  import { attemptInvoke, tryFallbackProviders } from '@/llm/invoke';
46
+ import { prepareToolsForPromptCache } from '@/llm/promptCacheTools';
44
47
  import { calculateMaxToolResultChars } from '@/utils/truncation';
48
+ import { makeIsDeferred } from '@/messages/anthropicToolCache';
45
49
  import { createRemoveAllMessage } from '@/messages/reducer';
46
50
  import { getMaxOutputTokensKey } from '@/llm/request';
47
51
  import { initializeModel } from '@/llm/init';
@@ -623,7 +627,13 @@ async function executeSummarizationWithFallback(params: {
623
627
  usePromptCache: boolean;
624
628
  log: LogFn;
625
629
  /** Carries the run's stream limits so the event cap covers summary streams. */
626
- graph?: StreamLimitState & { getBreakerSignal?: () => AbortSignal };
630
+ graph?: StreamLimitState & {
631
+ getBreakerSignal?: () => AbortSignal;
632
+ getToolsForBinding?: (
633
+ provider: t.ProviderName,
634
+ clientOptions: t.ClientOptions | undefined
635
+ ) => t.GraphTools | undefined;
636
+ };
627
637
  }): Promise<{
628
638
  text: string;
629
639
  usage?: Partial<UsageMetadata>;
@@ -657,10 +667,24 @@ async function executeSummarizationWithFallback(params: {
657
667
  * (e.g. an unrecognized summarization.provider) surfaces through the
658
668
  * `log('error', ...)` path below rather than bubbling up silently.
659
669
  */
670
+ const summarizationTools = usePromptCache
671
+ ? (graph?.getToolsForBinding?.(
672
+ clientConfig.provider as t.ProviderName,
673
+ clientConfig.clientOptions as t.ClientOptions
674
+ ) ??
675
+ prepareToolsForPromptCache({
676
+ provider: clientConfig.provider as t.ProviderName,
677
+ clientOptions: clientConfig.clientOptions as t.ClientOptions,
678
+ tools: agentContext.getToolsForBinding(),
679
+ isDeferred: makeIsDeferred(
680
+ agentContext.getEffectiveToolDefinitions()
681
+ ),
682
+ }))
683
+ : agentContext.getToolsForBinding();
660
684
  const summarizationModel = initializeModel({
661
685
  provider: clientConfig.provider,
662
686
  clientOptions: clientConfig.clientOptions as t.ClientOptions,
663
- tools: agentContext.getToolsForBinding(),
687
+ tools: summarizationTools,
664
688
  }) as t.ChatModel;
665
689
 
666
690
  const result = await summarizeWithCacheHit({
@@ -677,13 +701,23 @@ async function executeSummarizationWithFallback(params: {
677
701
  usePromptCache,
678
702
  promptCacheTtl:
679
703
  clientConfig.provider === Providers.ANTHROPIC ||
680
- clientConfig.provider === Providers.OPENROUTER
681
- ? resolvePromptCacheTtl(
682
- (
704
+ clientConfig.provider === Providers.OPENROUTER ||
705
+ clientConfig.provider === Providers.BEDROCK
706
+ ? (
683
707
  clientConfig.clientOptions as {
684
708
  promptCacheTtl?: PromptCacheTtl;
685
709
  }
686
- ).promptCacheTtl
710
+ ).promptCacheTtl
711
+ : undefined,
712
+ bedrockModelId:
713
+ clientConfig.provider === Providers.BEDROCK
714
+ ? resolveBedrockCompactionCacheModel(
715
+ clientConfig.clientOptions as
716
+ | {
717
+ applicationInferenceProfile?: string;
718
+ model?: string;
719
+ }
720
+ | undefined
687
721
  )
688
722
  : undefined,
689
723
  log,
@@ -900,6 +934,10 @@ interface CreateSummarizeNodeParams {
900
934
  runId?: string;
901
935
  isMultiAgent: boolean;
902
936
  hookRegistry?: HookRegistry;
937
+ getToolsForBinding?: (
938
+ provider: t.ProviderName,
939
+ clientOptions: t.ClientOptions | undefined
940
+ ) => t.GraphTools | undefined;
903
941
  dispatchRunStep: (
904
942
  runStep: t.RunStep,
905
943
  config?: RunnableConfig
@@ -1221,7 +1259,7 @@ export function createSummarizeNode({
1221
1259
  clientConfig.provider === (agentContext.provider as string);
1222
1260
  const hasPromptCache =
1223
1261
  isSelfSummarizeModel &&
1224
- (agentContext.clientOptions as Record<string, unknown> | undefined)
1262
+ (clientConfig.clientOptions as Record<string, unknown> | undefined)
1225
1263
  ?.promptCache === true;
1226
1264
 
1227
1265
  const log: LogFn = (level, message, data) => {
@@ -1615,6 +1653,45 @@ function traceConfig(
1615
1653
  };
1616
1654
  }
1617
1655
 
1656
+ export function applySummarizationHistoryCache(params: {
1657
+ messages: BaseMessage[];
1658
+ provider: t.ProviderName;
1659
+ enabled: boolean;
1660
+ promptCacheTtl?: PromptCacheTtl;
1661
+ bedrockModelId?: string;
1662
+ }): BaseMessage[] {
1663
+ if (!params.enabled) {
1664
+ return params.messages;
1665
+ }
1666
+ if (params.provider === Providers.BEDROCK) {
1667
+ return addBedrockTailCacheControl(
1668
+ [...params.messages],
1669
+ resolveBedrockPromptCacheTtl(
1670
+ params.promptCacheTtl,
1671
+ params.bedrockModelId
1672
+ )
1673
+ );
1674
+ }
1675
+ if (
1676
+ params.provider !== Providers.ANTHROPIC &&
1677
+ params.provider !== Providers.OPENROUTER
1678
+ ) {
1679
+ return params.messages;
1680
+ }
1681
+ return addTailCacheControl(
1682
+ [...params.messages],
1683
+ resolvePromptCacheTtl(params.promptCacheTtl)
1684
+ );
1685
+ }
1686
+
1687
+ export function resolveBedrockCompactionCacheModel(
1688
+ options:
1689
+ | { applicationInferenceProfile?: string; model?: string }
1690
+ | undefined
1691
+ ): string | undefined {
1692
+ return options?.model;
1693
+ }
1694
+
1618
1695
  /**
1619
1696
  * Cache-friendly compaction: sends raw conversation messages with the
1620
1697
  * summarization instruction appended as the final HumanMessage. Bound tool
@@ -1635,6 +1712,7 @@ async function summarizeWithCacheHit({
1635
1712
  graph,
1636
1713
  usePromptCache,
1637
1714
  promptCacheTtl,
1715
+ bedrockModelId,
1638
1716
  log,
1639
1717
  }: {
1640
1718
  model: t.ChatModel;
@@ -1649,6 +1727,7 @@ async function summarizeWithCacheHit({
1649
1727
  graph?: StreamLimitState & { getBreakerSignal?: () => AbortSignal };
1650
1728
  usePromptCache?: boolean;
1651
1729
  promptCacheTtl?: PromptCacheTtl;
1730
+ bedrockModelId?: string;
1652
1731
  log?: LogFn;
1653
1732
  }): Promise<{ text: string; usage?: Partial<UsageMetadata> }> {
1654
1733
  const instruction = buildSummarizationInstruction(
@@ -1657,11 +1736,17 @@ async function summarizeWithCacheHit({
1657
1736
  priorSummaryText
1658
1737
  );
1659
1738
 
1660
- const fullMessages = [...messages, new HumanMessage(instruction)];
1661
- const invokeMessages =
1662
- usePromptCache === true
1663
- ? addTailCacheControl(fullMessages, promptCacheTtl)
1664
- : fullMessages;
1739
+ const cachedHistory = applySummarizationHistoryCache({
1740
+ messages,
1741
+ provider,
1742
+ enabled: usePromptCache === true,
1743
+ promptCacheTtl,
1744
+ bedrockModelId,
1745
+ });
1746
+ const invokeMessages = [
1747
+ ...cachedHistory,
1748
+ new HumanMessage(instruction),
1749
+ ];
1665
1750
 
1666
1751
  const result = await attemptInvoke(
1667
1752
  {