@librechat/agents 3.7.3 → 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.
- package/dist/cjs/graphs/Graph.cjs +16 -16
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/promptCacheTools.cjs +30 -0
- package/dist/cjs/llm/promptCacheTools.cjs.map +1 -0
- package/dist/cjs/stream.cjs +3 -1
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +29 -6
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +10 -3
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/eagerEventExecution.cjs +32 -2
- package/dist/cjs/tools/eagerEventExecution.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +18 -18
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/promptCacheTools.mjs +30 -0
- package/dist/esm/llm/promptCacheTools.mjs.map +1 -0
- package/dist/esm/stream.mjs +3 -1
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +30 -7
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +11 -4
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/eagerEventExecution.mjs +32 -3
- package/dist/esm/tools/eagerEventExecution.mjs.map +1 -1
- package/dist/types/graphs/Graph.d.ts +1 -0
- package/dist/types/llm/promptCacheTools.d.ts +7 -0
- package/dist/types/summarization/node.d.ts +13 -0
- package/dist/types/tools/ToolNode.d.ts +2 -0
- package/dist/types/tools/eagerEventExecution.d.ts +6 -0
- package/package.json +1 -1
- package/src/graphs/Graph.ts +32 -74
- package/src/llm/promptCacheTools.ts +58 -0
- package/src/stream.ts +7 -0
- package/src/summarization/node.ts +97 -12
- package/src/tools/ToolNode.ts +29 -3
- package/src/tools/eagerEventExecution.ts +90 -2
package/src/graphs/Graph.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
+
}
|
package/src/stream.ts
CHANGED
|
@@ -736,6 +736,12 @@ function createEagerToolExecutionPlan(args: {
|
|
|
736
736
|
if (candidateToolCalls.length === 0) {
|
|
737
737
|
return [];
|
|
738
738
|
}
|
|
739
|
+
const toolSchemas = new Map(
|
|
740
|
+
agentContext?.toolDefinitions?.map(({ name, parameters }) => [
|
|
741
|
+
name,
|
|
742
|
+
parameters,
|
|
743
|
+
])
|
|
744
|
+
);
|
|
739
745
|
|
|
740
746
|
// Eager execution must preserve ToolNode batch semantics exactly for every
|
|
741
747
|
// unstarted call. If any candidate cannot be planned, fall back for that
|
|
@@ -770,6 +776,7 @@ function createEagerToolExecutionPlan(args: {
|
|
|
770
776
|
),
|
|
771
777
|
})),
|
|
772
778
|
usageCount: graph.getEagerEventToolUsageCount(agentContext?.agentId),
|
|
779
|
+
getToolSchema: (toolName) => toolSchemas.get(toolName),
|
|
773
780
|
});
|
|
774
781
|
if (plan == null) {
|
|
775
782
|
return undefined;
|
|
@@ -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 & {
|
|
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:
|
|
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
|
-
|
|
682
|
-
|
|
704
|
+
clientConfig.provider === Providers.OPENROUTER ||
|
|
705
|
+
clientConfig.provider === Providers.BEDROCK
|
|
706
|
+
? (
|
|
683
707
|
clientConfig.clientOptions as {
|
|
684
708
|
promptCacheTtl?: PromptCacheTtl;
|
|
685
709
|
}
|
|
686
|
-
|
|
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
|
-
(
|
|
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
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
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
|
{
|
package/src/tools/ToolNode.ts
CHANGED
|
@@ -71,6 +71,8 @@ import {
|
|
|
71
71
|
} from '@/tools/intentArg';
|
|
72
72
|
import {
|
|
73
73
|
buildToolExecutionRequestPlan,
|
|
74
|
+
coerceArgsForSchema,
|
|
75
|
+
coerceRecordArgs,
|
|
74
76
|
resolveRuntimeSessionHint,
|
|
75
77
|
recordArgsEqual,
|
|
76
78
|
} from '@/tools/eagerEventExecution';
|
|
@@ -1129,6 +1131,26 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
1129
1131
|
);
|
|
1130
1132
|
}
|
|
1131
1133
|
|
|
1134
|
+
private getToolParameterSchema(
|
|
1135
|
+
toolName: string
|
|
1136
|
+
): t.JsonSchemaType | undefined {
|
|
1137
|
+
return (
|
|
1138
|
+
this.toolDefinitions?.get(toolName)?.parameters ??
|
|
1139
|
+
this.toolRegistry?.get(toolName)?.parameters
|
|
1140
|
+
);
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
private coerceEventToolArgs(
|
|
1144
|
+
toolName: string,
|
|
1145
|
+
args: unknown
|
|
1146
|
+
): Record<string, unknown> {
|
|
1147
|
+
const recordArgs = coerceRecordArgs(args);
|
|
1148
|
+
return coerceArgsForSchema(
|
|
1149
|
+
recordArgs ?? (args as Record<string, unknown>),
|
|
1150
|
+
this.getToolParameterSchema(toolName)
|
|
1151
|
+
);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1132
1154
|
/** Serializes the live caller projection for event-driven hosts. */
|
|
1133
1155
|
private getCallerCapabilityProjectionSnapshot(): t.CallerCapabilityProjectionSnapshot {
|
|
1134
1156
|
return createCallerCapabilityProjectionSnapshot(
|
|
@@ -2758,7 +2780,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2758
2780
|
return {
|
|
2759
2781
|
call,
|
|
2760
2782
|
stepId: this.toolCallStepIds?.get(call.id!) ?? '',
|
|
2761
|
-
args: resolvedArgs,
|
|
2783
|
+
args: this.coerceEventToolArgs(call.name, resolvedArgs),
|
|
2762
2784
|
batchIndex: batchIndices?.[i],
|
|
2763
2785
|
};
|
|
2764
2786
|
});
|
|
@@ -3002,7 +3024,10 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3002
3024
|
entry.call.name
|
|
3003
3025
|
),
|
|
3004
3026
|
});
|
|
3005
|
-
entry.args =
|
|
3027
|
+
entry.args = this.coerceEventToolArgs(
|
|
3028
|
+
entry.call.name,
|
|
3029
|
+
resolved as Record<string, unknown>
|
|
3030
|
+
);
|
|
3006
3031
|
if (entry.call.id != null) {
|
|
3007
3032
|
if (unresolved.length > 0) {
|
|
3008
3033
|
unresolvedByCallId.set(entry.call.id, unresolved);
|
|
@@ -3012,7 +3037,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3012
3037
|
}
|
|
3013
3038
|
return;
|
|
3014
3039
|
}
|
|
3015
|
-
entry.args = nextArgs;
|
|
3040
|
+
entry.args = this.coerceEventToolArgs(entry.call.name, nextArgs);
|
|
3016
3041
|
};
|
|
3017
3042
|
|
|
3018
3043
|
const askEntries: Array<{
|
|
@@ -3369,6 +3394,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3369
3394
|
}),
|
|
3370
3395
|
usageCount: this.toolUsageCount,
|
|
3371
3396
|
invalidArgsBehavior: 'error-result',
|
|
3397
|
+
getToolSchema: (toolName) => this.getToolParameterSchema(toolName),
|
|
3372
3398
|
recordTurn: (toolName, reservedTurn, callId) => {
|
|
3373
3399
|
this.recordEventToolPlanningTurn(
|
|
3374
3400
|
toolName,
|
|
@@ -19,6 +19,89 @@ export function coerceRecordArgs(
|
|
|
19
19
|
return args as Record<string, unknown>;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
const INTEGER_STRING = /^-?(?:0|[1-9]\d*)$/;
|
|
23
|
+
const NUMBER_STRING = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
|
|
24
|
+
|
|
25
|
+
function coerceValueForSchema(
|
|
26
|
+
value: unknown,
|
|
27
|
+
schema: t.JsonSchemaType | undefined
|
|
28
|
+
): unknown {
|
|
29
|
+
if (schema == null) {
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (schema.type === 'integer' && typeof value === 'string') {
|
|
34
|
+
if (!INTEGER_STRING.test(value)) {
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
const parsed = Number(value);
|
|
38
|
+
return Number.isSafeInteger(parsed) ? parsed : value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (
|
|
42
|
+
(schema.type === 'number' || schema.type === 'float') &&
|
|
43
|
+
typeof value === 'string'
|
|
44
|
+
) {
|
|
45
|
+
if (!NUMBER_STRING.test(value)) {
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
const parsed = Number(value);
|
|
49
|
+
return Number.isFinite(parsed) && String(parsed) === value
|
|
50
|
+
? parsed
|
|
51
|
+
: value;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (schema.type === 'boolean' && typeof value === 'string') {
|
|
55
|
+
if (value === 'true') {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
if (value === 'false') {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (schema.type === 'array' && Array.isArray(value)) {
|
|
65
|
+
return value.map((item) => coerceValueForSchema(item, schema.items));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (
|
|
69
|
+
schema.type !== 'object' ||
|
|
70
|
+
value == null ||
|
|
71
|
+
typeof value !== 'object' ||
|
|
72
|
+
Array.isArray(value)
|
|
73
|
+
) {
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const record = value as Record<string, unknown>;
|
|
78
|
+
const additionalProperties =
|
|
79
|
+
typeof schema.additionalProperties === 'object'
|
|
80
|
+
? schema.additionalProperties
|
|
81
|
+
: undefined;
|
|
82
|
+
|
|
83
|
+
return Object.fromEntries(
|
|
84
|
+
Object.entries(record).map(([key, entry]) => [
|
|
85
|
+
key,
|
|
86
|
+
coerceValueForSchema(
|
|
87
|
+
entry,
|
|
88
|
+
schema.properties?.[key] ?? additionalProperties
|
|
89
|
+
),
|
|
90
|
+
])
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Applies only lossless, schema-directed repairs to model-generated arguments.
|
|
96
|
+
* The host remains responsible for full schema validation and all business rules.
|
|
97
|
+
*/
|
|
98
|
+
export function coerceArgsForSchema(
|
|
99
|
+
args: Record<string, unknown>,
|
|
100
|
+
schema: t.JsonSchemaType | undefined
|
|
101
|
+
): Record<string, unknown> {
|
|
102
|
+
return coerceValueForSchema(args, schema) as Record<string, unknown>;
|
|
103
|
+
}
|
|
104
|
+
|
|
22
105
|
export function stableStringify(value: unknown): string {
|
|
23
106
|
if (Array.isArray(value)) {
|
|
24
107
|
return `[${value.map((item) => stableStringify(item)).join(',')}]`;
|
|
@@ -87,6 +170,7 @@ export function buildToolExecutionRequestPlan(args: {
|
|
|
87
170
|
usageCount: Map<string, number>;
|
|
88
171
|
invalidArgsBehavior?: 'abort' | 'error-result';
|
|
89
172
|
recordTurn?: (toolName: string, turn: number, callId: string) => void;
|
|
173
|
+
getToolSchema?: (toolName: string) => t.JsonSchemaType | undefined;
|
|
90
174
|
}): ToolExecutionRequestPlan | undefined {
|
|
91
175
|
const invalidArgsBehavior = args.invalidArgsBehavior ?? 'abort';
|
|
92
176
|
const prepared: Array<{
|
|
@@ -103,8 +187,8 @@ export function buildToolExecutionRequestPlan(args: {
|
|
|
103
187
|
if (toolCall.id == null || toolCall.id === '' || toolCall.name === '') {
|
|
104
188
|
return undefined;
|
|
105
189
|
}
|
|
106
|
-
const
|
|
107
|
-
if (
|
|
190
|
+
const recordArgs = coerceRecordArgs(toolCall.args);
|
|
191
|
+
if (recordArgs == null) {
|
|
108
192
|
if (invalidArgsBehavior === 'abort') {
|
|
109
193
|
return undefined;
|
|
110
194
|
}
|
|
@@ -120,6 +204,10 @@ export function buildToolExecutionRequestPlan(args: {
|
|
|
120
204
|
});
|
|
121
205
|
continue;
|
|
122
206
|
}
|
|
207
|
+
const coercedArgs = coerceArgsForSchema(
|
|
208
|
+
recordArgs,
|
|
209
|
+
args.getToolSchema?.(toolCall.name)
|
|
210
|
+
);
|
|
123
211
|
prepared.push({
|
|
124
212
|
id: toolCall.id,
|
|
125
213
|
name: toolCall.name,
|