@librechat/agents 3.2.38 → 3.2.41
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 +25 -8
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +7 -4
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/hooks/createWorkspacePolicyHook.cjs +4 -3
- package/dist/cjs/hooks/createWorkspacePolicyHook.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +20 -4
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/index.cjs +7 -1
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/toolCache.cjs +5 -4
- package/dist/cjs/llm/bedrock/toolCache.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +34 -17
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/index.cjs +1 -0
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/toolCache.cjs +18 -5
- package/dist/cjs/llm/openrouter/toolCache.cjs.map +1 -1
- package/dist/cjs/main.cjs +4 -0
- package/dist/cjs/messages/anthropicToolCache.cjs +75 -13
- package/dist/cjs/messages/anthropicToolCache.cjs.map +1 -1
- package/dist/cjs/messages/cache.cjs +91 -35
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +3 -2
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/ReadFile.cjs +2 -2
- package/dist/cjs/tools/ReadFile.cjs.map +1 -1
- package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs +11 -11
- package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/local/LocalCodingTools.cjs +11 -11
- package/dist/cjs/tools/local/LocalCodingTools.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +26 -9
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +8 -5
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/hooks/createWorkspacePolicyHook.mjs +4 -3
- package/dist/esm/hooks/createWorkspacePolicyHook.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +20 -4
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs +7 -1
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/llm/bedrock/toolCache.mjs +5 -4
- package/dist/esm/llm/bedrock/toolCache.mjs.map +1 -1
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs +34 -17
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/openrouter/index.mjs +1 -0
- package/dist/esm/llm/openrouter/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/toolCache.mjs +18 -5
- package/dist/esm/llm/openrouter/toolCache.mjs.map +1 -1
- package/dist/esm/main.mjs +2 -2
- package/dist/esm/messages/anthropicToolCache.mjs +75 -13
- package/dist/esm/messages/anthropicToolCache.mjs.map +1 -1
- package/dist/esm/messages/cache.mjs +88 -36
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +4 -3
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/ReadFile.mjs +2 -2
- package/dist/esm/tools/ReadFile.mjs.map +1 -1
- package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs +11 -11
- package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/local/LocalCodingTools.mjs +11 -11
- package/dist/esm/tools/local/LocalCodingTools.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +11 -0
- package/dist/types/agents/__tests__/promptCacheLiveHelpers.d.ts +2 -0
- package/dist/types/llm/bedrock/index.d.ts +13 -0
- package/dist/types/llm/bedrock/toolCache.d.ts +2 -1
- package/dist/types/llm/openrouter/index.d.ts +8 -0
- package/dist/types/llm/openrouter/toolCache.d.ts +2 -1
- package/dist/types/messages/anthropicToolCache.d.ts +2 -1
- package/dist/types/messages/cache.d.ts +49 -5
- package/dist/types/tools/ReadFile.d.ts +4 -4
- package/dist/types/types/llm.d.ts +14 -0
- package/package.json +1 -1
- package/src/agents/AgentContext.ts +64 -17
- package/src/agents/__tests__/AgentContext.anthropic.live.test.ts +6 -2
- package/src/agents/__tests__/AgentContext.bedrock.live.test.ts +7 -5
- package/src/agents/__tests__/AgentContext.openrouter.live.test.ts +1 -1
- package/src/agents/__tests__/AgentContext.test.ts +31 -19
- package/src/agents/__tests__/promptCacheLiveHelpers.ts +6 -2
- package/src/graphs/Graph.ts +40 -4
- package/src/hooks/__tests__/createWorkspacePolicyHook.test.ts +12 -12
- package/src/hooks/createWorkspacePolicyHook.ts +7 -6
- package/src/llm/anthropic/utils/message_inputs.ts +33 -6
- package/src/llm/bedrock/index.ts +21 -1
- package/src/llm/bedrock/llm.spec.ts +61 -0
- package/src/llm/bedrock/toolCache.test.ts +24 -0
- package/src/llm/bedrock/toolCache.ts +12 -7
- package/src/llm/bedrock/utils/message_inputs.ts +57 -40
- package/src/llm/openrouter/index.ts +9 -0
- package/src/llm/openrouter/toolCache.test.ts +52 -1
- package/src/llm/openrouter/toolCache.ts +40 -6
- package/src/messages/__tests__/anthropicToolCache.test.ts +168 -0
- package/src/messages/anthropicToolCache.ts +118 -15
- package/src/messages/cache.test.ts +175 -0
- package/src/messages/cache.ts +133 -48
- package/src/summarization/node.ts +21 -2
- package/src/tools/ReadFile.ts +2 -2
- package/src/tools/__tests__/LocalExecutionTools.test.ts +25 -25
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +5 -5
- package/src/tools/__tests__/ReadFile.test.ts +3 -3
- package/src/tools/__tests__/ToolNode.session.test.ts +2 -2
- package/src/tools/__tests__/workspaceSeam.test.ts +2 -2
- package/src/tools/cloudflare/CloudflareProgrammaticToolCalling.ts +11 -11
- package/src/tools/local/LocalCodingTools.ts +14 -14
- package/src/types/llm.ts +14 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildAnthropicCacheControl } from "./cache.mjs";
|
|
1
2
|
//#region src/messages/anthropicToolCache.ts
|
|
2
3
|
const ANTHROPIC_BUILT_IN_TOOL_PREFIXES = [
|
|
3
4
|
"text_editor_",
|
|
@@ -12,25 +13,69 @@ const ANTHROPIC_BUILT_IN_TOOL_PREFIXES = [
|
|
|
12
13
|
"tool_search_",
|
|
13
14
|
"mcp_toolset"
|
|
14
15
|
];
|
|
15
|
-
const CACHE_CONTROL = { type: "ephemeral" };
|
|
16
16
|
function isAnthropicBuiltInTool(tool) {
|
|
17
17
|
const { type } = tool;
|
|
18
18
|
return typeof type === "string" && ANTHROPIC_BUILT_IN_TOOL_PREFIXES.some((prefix) => type.startsWith(prefix));
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Whether a tool already carries a cache breakpoint. Built-ins use a direct
|
|
22
|
+
* `cache_control`; custom tools normally carry it under `extras`, but a
|
|
23
|
+
* caller-supplied Anthropic-native tool object can also put it directly on the
|
|
24
|
+
* block — check both so stale markers are never missed.
|
|
25
|
+
*/
|
|
20
26
|
function hasCacheControl(tool) {
|
|
21
27
|
if (isAnthropicBuiltInTool(tool)) return tool.cache_control != null;
|
|
22
|
-
return tool.extras?.cache_control != null;
|
|
28
|
+
return tool.cache_control != null || tool.extras?.cache_control != null;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Return the `cache_control` from the location that actually reaches the
|
|
32
|
+
* Anthropic payload for this tool's shape: directly on the block for
|
|
33
|
+
* provider-shaped tools (built-ins and raw Anthropic tools), or under `extras`
|
|
34
|
+
* for LangChain custom tools (the adapter promotes only that). Returns
|
|
35
|
+
* `undefined` when no marker sits in the effective location — a marker in the
|
|
36
|
+
* wrong place (e.g. a direct marker on a custom tool) does not count.
|
|
37
|
+
*/
|
|
38
|
+
function getEffectiveCacheControl(tool) {
|
|
39
|
+
return isProviderShapedTool(tool) ? tool.cache_control : tool.extras?.cache_control;
|
|
23
40
|
}
|
|
24
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Return a clone of `tool` with any `cache_control` removed — both the direct
|
|
43
|
+
* block marker and the `extras` marker — preserving the prototype chain. Used
|
|
44
|
+
* to clear stray markers off tools that must not anchor a competing breakpoint.
|
|
45
|
+
*/
|
|
46
|
+
function stripCacheControl(tool) {
|
|
25
47
|
const prototype = Object.getPrototypeOf(tool) ?? Object.prototype;
|
|
26
|
-
|
|
48
|
+
const wrapped = { ...tool };
|
|
49
|
+
delete wrapped.cache_control;
|
|
50
|
+
if (wrapped.extras != null) {
|
|
51
|
+
wrapped.extras = { ...wrapped.extras };
|
|
52
|
+
delete wrapped.extras.cache_control;
|
|
53
|
+
}
|
|
54
|
+
return Object.assign(Object.create(prototype), wrapped);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Whether `tool` is already in the Anthropic provider payload shape — an
|
|
58
|
+
* Anthropic built-in or a raw Anthropic tool object (has `input_schema`). These
|
|
59
|
+
* carry `cache_control` directly on the block; the LangChain adapter does NOT
|
|
60
|
+
* promote `extras.cache_control` for them. LangChain StructuredTools, by
|
|
61
|
+
* contrast, expose the marker via `extras`.
|
|
62
|
+
*/
|
|
63
|
+
function isProviderShapedTool(tool) {
|
|
64
|
+
return isAnthropicBuiltInTool(tool) || "input_schema" in tool;
|
|
65
|
+
}
|
|
66
|
+
function markCacheControl(tool, ttl) {
|
|
67
|
+
const cacheControl = buildAnthropicCacheControl(ttl);
|
|
68
|
+
const prototype = Object.getPrototypeOf(tool) ?? Object.prototype;
|
|
69
|
+
if (isProviderShapedTool(tool)) {
|
|
27
70
|
const wrapped = { ...tool };
|
|
28
71
|
delete wrapped.extras;
|
|
29
|
-
return Object.assign(Object.create(prototype), wrapped, { cache_control:
|
|
72
|
+
return Object.assign(Object.create(prototype), wrapped, { cache_control: cacheControl });
|
|
30
73
|
}
|
|
31
|
-
|
|
74
|
+
const wrapped = { ...tool };
|
|
75
|
+
delete wrapped.cache_control;
|
|
76
|
+
return Object.assign(Object.create(prototype), wrapped, { extras: {
|
|
32
77
|
...tool.extras ?? {},
|
|
33
|
-
cache_control:
|
|
78
|
+
cache_control: cacheControl
|
|
34
79
|
} });
|
|
35
80
|
}
|
|
36
81
|
/**
|
|
@@ -59,7 +104,7 @@ function makeIsDeferred(toolDefinitions) {
|
|
|
59
104
|
* `instanceof` checks still pass. For custom tools, `extras` is merged
|
|
60
105
|
* so any existing `providerToolDefinition` / other extras are kept.
|
|
61
106
|
*/
|
|
62
|
-
function partitionAndMarkAnthropicToolCache(tools, isDeferred) {
|
|
107
|
+
function partitionAndMarkAnthropicToolCache(tools, isDeferred, ttl) {
|
|
63
108
|
if (tools == null || tools.length === 0) return tools;
|
|
64
109
|
const staticTools = [];
|
|
65
110
|
const deferredTools = [];
|
|
@@ -68,13 +113,30 @@ function partitionAndMarkAnthropicToolCache(tools, isDeferred) {
|
|
|
68
113
|
if (typeof name === "string" && isDeferred(name)) deferredTools.push(tool);
|
|
69
114
|
else staticTools.push(tool);
|
|
70
115
|
}
|
|
71
|
-
|
|
116
|
+
let mutated = false;
|
|
117
|
+
for (let i = 0; i < deferredTools.length; i++) {
|
|
118
|
+
const candidate = deferredTools[i];
|
|
119
|
+
if (hasCacheControl(candidate)) {
|
|
120
|
+
deferredTools[i] = stripCacheControl(candidate);
|
|
121
|
+
mutated = true;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (staticTools.length === 0) return mutated ? [...deferredTools] : tools;
|
|
125
|
+
for (let i = 0; i < staticTools.length - 1; i++) {
|
|
126
|
+
const candidate = staticTools[i];
|
|
127
|
+
if (hasCacheControl(candidate)) {
|
|
128
|
+
staticTools[i] = stripCacheControl(candidate);
|
|
129
|
+
mutated = true;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
72
132
|
const last = staticTools[staticTools.length - 1];
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
133
|
+
const desiredTtl = ttl === "1h" ? "1h" : void 0;
|
|
134
|
+
const effective = getEffectiveCacheControl(last);
|
|
135
|
+
if (!(effective != null && (effective.ttl === "1h" ? "1h" : void 0) === desiredTtl)) {
|
|
136
|
+
staticTools[staticTools.length - 1] = markCacheControl(last, ttl);
|
|
137
|
+
mutated = true;
|
|
76
138
|
}
|
|
77
|
-
|
|
139
|
+
if (!mutated && deferredTools.length === 0) return tools;
|
|
78
140
|
return [...staticTools, ...deferredTools];
|
|
79
141
|
}
|
|
80
142
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropicToolCache.mjs","names":[],"sources":["../../../src/messages/anthropicToolCache.ts"],"sourcesContent":["/**\n * Anthropic prompt-caching helper for the `tools[]` request field.\n *\n * Anthropic accepts `cache_control: { type: 'ephemeral' }` on individual\n * tool definitions. Whichever tool carries the marker becomes the end of\n * a cached prefix: `tools[0..N]` (everything up to and including the\n * marked tool) is cached and rebated on subsequent matching requests.\n *\n * For agents that mix static and deferred (lazily-discovered) tools, the\n * winning configuration is:\n *\n * 1. Stable-partition tools so all *static* (non-deferred) tools come\n * first and discovered-deferred tools come last.\n * 2. Stamp `cache_control` on the LAST static tool.\n *\n * That way, the cached prefix covers exactly the static tool inventory.\n * Discovered tools that show up later (or vary turn-to-turn as new ones\n * get discovered) never invalidate the prefix because they sit *after*\n * the breakpoint.\n *\n * LangChain's Anthropic adapter passes the marker through via\n * `tool.extras.cache_control` for custom tools, while Anthropic built-ins\n * require direct `cache_control`. Either way, we stamp a fresh wrapper —\n * never mutating the original tool instance, since callers may share them\n * across runs.\n */\n\nimport type { GraphTools } from '@/types';\n\nconst ANTHROPIC_BUILT_IN_TOOL_PREFIXES = [\n 'text_editor_',\n 'computer_',\n 'bash_',\n 'web_search_',\n 'web_fetch_',\n 'str_replace_editor_',\n 'str_replace_based_edit_tool_',\n 'code_execution_',\n 'memory_',\n 'tool_search_',\n 'mcp_toolset',\n] as const;\n\nconst CACHE_CONTROL = { type: 'ephemeral' as const };\n\ntype AnthropicToolCacheCandidate = {\n name?: unknown;\n type?: unknown;\n extras?: Record<string, unknown>;\n cache_control?: unknown;\n};\n\nfunction isAnthropicBuiltInTool(\n tool: AnthropicToolCacheCandidate\n): tool is AnthropicToolCacheCandidate & { type: string } {\n const { type } = tool;\n return (\n typeof type === 'string' &&\n ANTHROPIC_BUILT_IN_TOOL_PREFIXES.some((prefix) => type.startsWith(prefix))\n );\n}\n\nfunction hasCacheControl(tool: AnthropicToolCacheCandidate): boolean {\n if (isAnthropicBuiltInTool(tool)) {\n return tool.cache_control != null;\n }\n return tool.extras?.cache_control != null;\n}\n\nfunction markCacheControl(\n tool: AnthropicToolCacheCandidate\n): AnthropicToolCacheCandidate {\n const prototype = Object.getPrototypeOf(tool) ?? Object.prototype;\n if (isAnthropicBuiltInTool(tool)) {\n const wrapped = { ...tool };\n delete wrapped.extras;\n return Object.assign(Object.create(prototype), wrapped, {\n cache_control: CACHE_CONTROL,\n });\n }\n\n return Object.assign(Object.create(prototype), tool, {\n extras: {\n ...(tool.extras ?? {}),\n cache_control: CACHE_CONTROL,\n },\n });\n}\n\n/**\n * Returns a callable that reports whether a given tool *name* is deferred\n * according to the supplied registry of tool definitions. Tools without\n * a registry entry are treated as non-deferred (i.e. static), matching\n * the host-supplied `graphTools` semantics elsewhere in the SDK.\n */\nexport function makeIsDeferred(\n toolDefinitions:\n | ReadonlyArray<{ name: string; defer_loading?: boolean }>\n | undefined\n): (toolName: string) => boolean {\n if (toolDefinitions == null || toolDefinitions.length === 0) {\n return () => false;\n }\n const deferred = new Set<string>();\n for (const def of toolDefinitions) {\n if (def.defer_loading === true) deferred.add(def.name);\n }\n if (deferred.size === 0) return () => false;\n return (name) => deferred.has(name);\n}\n\n/**\n * Stable-partition `tools` into [static..., deferred...] and stamp the\n * Anthropic `cache_control: ephemeral` marker on the last static tool.\n *\n * If `tools` is undefined or empty, or no entry has a usable `.name`,\n * returns the input unchanged. If there are no static tools at all,\n * also returns unchanged (nothing to cache).\n *\n * The original tool instances are never mutated. The marked entry is a\n * shallow wrapper that preserves the prototype chain so downstream\n * `instanceof` checks still pass. For custom tools, `extras` is merged\n * so any existing `providerToolDefinition` / other extras are kept.\n */\nexport function partitionAndMarkAnthropicToolCache(\n tools: GraphTools | undefined,\n isDeferred: (toolName: string) => boolean\n): GraphTools | undefined {\n if (tools == null || tools.length === 0) return tools;\n\n // Use unknown[] internally to avoid GraphTools' union-array variance\n // (each member is one of three array types). We cast back to\n // GraphTools when returning.\n const staticTools: unknown[] = [];\n const deferredTools: unknown[] = [];\n\n for (const tool of tools) {\n const name = (tool as { name?: unknown }).name;\n if (typeof name === 'string' && isDeferred(name)) {\n deferredTools.push(tool);\n } else {\n staticTools.push(tool);\n }\n }\n\n if (staticTools.length === 0) {\n return tools;\n }\n\n const last = staticTools[\n staticTools.length - 1\n ] as AnthropicToolCacheCandidate;\n // Already marked? Don't double-clone.\n if (hasCacheControl(last)) {\n if (deferredTools.length === 0) return tools;\n return [...staticTools, ...deferredTools] as GraphTools;\n }\n\n staticTools[staticTools.length - 1] = markCacheControl(last);\n return [...staticTools, ...deferredTools] as GraphTools;\n}\n"],"mappings":";AA6BA,MAAM,mCAAmC;CACvC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,MAAM,gBAAgB,EAAE,MAAM,YAAqB;AASnD,SAAS,uBACP,MACwD;CACxD,MAAM,EAAE,SAAS;CACjB,OACE,OAAO,SAAS,YAChB,iCAAiC,MAAM,WAAW,KAAK,WAAW,MAAM,CAAC;AAE7E;AAEA,SAAS,gBAAgB,MAA4C;CACnE,IAAI,uBAAuB,IAAI,GAC7B,OAAO,KAAK,iBAAiB;CAE/B,OAAO,KAAK,QAAQ,iBAAiB;AACvC;AAEA,SAAS,iBACP,MAC6B;CAC7B,MAAM,YAAY,OAAO,eAAe,IAAI,KAAK,OAAO;CACxD,IAAI,uBAAuB,IAAI,GAAG;EAChC,MAAM,UAAU,EAAE,GAAG,KAAK;EAC1B,OAAO,QAAQ;EACf,OAAO,OAAO,OAAO,OAAO,OAAO,SAAS,GAAG,SAAS,EACtD,eAAe,cACjB,CAAC;CACH;CAEA,OAAO,OAAO,OAAO,OAAO,OAAO,SAAS,GAAG,MAAM,EACnD,QAAQ;EACN,GAAI,KAAK,UAAU,CAAC;EACpB,eAAe;CACjB,EACF,CAAC;AACH;;;;;;;AAQA,SAAgB,eACd,iBAG+B;CAC/B,IAAI,mBAAmB,QAAQ,gBAAgB,WAAW,GACxD,aAAa;CAEf,MAAM,2BAAW,IAAI,IAAY;CACjC,KAAK,MAAM,OAAO,iBAChB,IAAI,IAAI,kBAAkB,MAAM,SAAS,IAAI,IAAI,IAAI;CAEvD,IAAI,SAAS,SAAS,GAAG,aAAa;CACtC,QAAQ,SAAS,SAAS,IAAI,IAAI;AACpC;;;;;;;;;;;;;;AAeA,SAAgB,mCACd,OACA,YACwB;CACxB,IAAI,SAAS,QAAQ,MAAM,WAAW,GAAG,OAAO;CAKhD,MAAM,cAAyB,CAAC;CAChC,MAAM,gBAA2B,CAAC;CAElC,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,OAAQ,KAA4B;EAC1C,IAAI,OAAO,SAAS,YAAY,WAAW,IAAI,GAC7C,cAAc,KAAK,IAAI;OAEvB,YAAY,KAAK,IAAI;CAEzB;CAEA,IAAI,YAAY,WAAW,GACzB,OAAO;CAGT,MAAM,OAAO,YACX,YAAY,SAAS;CAGvB,IAAI,gBAAgB,IAAI,GAAG;EACzB,IAAI,cAAc,WAAW,GAAG,OAAO;EACvC,OAAO,CAAC,GAAG,aAAa,GAAG,aAAa;CAC1C;CAEA,YAAY,YAAY,SAAS,KAAK,iBAAiB,IAAI;CAC3D,OAAO,CAAC,GAAG,aAAa,GAAG,aAAa;AAC1C"}
|
|
1
|
+
{"version":3,"file":"anthropicToolCache.mjs","names":[],"sources":["../../../src/messages/anthropicToolCache.ts"],"sourcesContent":["/**\n * Anthropic prompt-caching helper for the `tools[]` request field.\n *\n * Anthropic accepts `cache_control: { type: 'ephemeral' }` on individual\n * tool definitions. Whichever tool carries the marker becomes the end of\n * a cached prefix: `tools[0..N]` (everything up to and including the\n * marked tool) is cached and rebated on subsequent matching requests.\n *\n * For agents that mix static and deferred (lazily-discovered) tools, the\n * winning configuration is:\n *\n * 1. Stable-partition tools so all *static* (non-deferred) tools come\n * first and discovered-deferred tools come last.\n * 2. Stamp `cache_control` on the LAST static tool.\n *\n * That way, the cached prefix covers exactly the static tool inventory.\n * Discovered tools that show up later (or vary turn-to-turn as new ones\n * get discovered) never invalidate the prefix because they sit *after*\n * the breakpoint.\n *\n * LangChain's Anthropic adapter passes the marker through via\n * `tool.extras.cache_control` for custom tools, while Anthropic built-ins\n * require direct `cache_control`. Either way, we stamp a fresh wrapper —\n * never mutating the original tool instance, since callers may share them\n * across runs.\n */\n\nimport type { GraphTools } from '@/types';\nimport {\n buildAnthropicCacheControl,\n type PromptCacheTtl,\n} from '@/messages/cache';\n\nconst ANTHROPIC_BUILT_IN_TOOL_PREFIXES = [\n 'text_editor_',\n 'computer_',\n 'bash_',\n 'web_search_',\n 'web_fetch_',\n 'str_replace_editor_',\n 'str_replace_based_edit_tool_',\n 'code_execution_',\n 'memory_',\n 'tool_search_',\n 'mcp_toolset',\n] as const;\n\ntype AnthropicToolCacheCandidate = {\n name?: unknown;\n type?: unknown;\n extras?: Record<string, unknown>;\n cache_control?: unknown;\n};\n\nfunction isAnthropicBuiltInTool(\n tool: AnthropicToolCacheCandidate\n): tool is AnthropicToolCacheCandidate & { type: string } {\n const { type } = tool;\n return (\n typeof type === 'string' &&\n ANTHROPIC_BUILT_IN_TOOL_PREFIXES.some((prefix) => type.startsWith(prefix))\n );\n}\n\n/**\n * Whether a tool already carries a cache breakpoint. Built-ins use a direct\n * `cache_control`; custom tools normally carry it under `extras`, but a\n * caller-supplied Anthropic-native tool object can also put it directly on the\n * block — check both so stale markers are never missed.\n */\nfunction hasCacheControl(tool: AnthropicToolCacheCandidate): boolean {\n if (isAnthropicBuiltInTool(tool)) {\n return tool.cache_control != null;\n }\n return tool.cache_control != null || tool.extras?.cache_control != null;\n}\n\n/**\n * Return the `cache_control` from the location that actually reaches the\n * Anthropic payload for this tool's shape: directly on the block for\n * provider-shaped tools (built-ins and raw Anthropic tools), or under `extras`\n * for LangChain custom tools (the adapter promotes only that). Returns\n * `undefined` when no marker sits in the effective location — a marker in the\n * wrong place (e.g. a direct marker on a custom tool) does not count.\n */\nfunction getEffectiveCacheControl(\n tool: AnthropicToolCacheCandidate\n): { ttl?: unknown } | undefined {\n return (\n isProviderShapedTool(tool) ? tool.cache_control : tool.extras?.cache_control\n ) as { ttl?: unknown } | undefined;\n}\n\n/**\n * Return a clone of `tool` with any `cache_control` removed — both the direct\n * block marker and the `extras` marker — preserving the prototype chain. Used\n * to clear stray markers off tools that must not anchor a competing breakpoint.\n */\nfunction stripCacheControl(\n tool: AnthropicToolCacheCandidate\n): AnthropicToolCacheCandidate {\n const prototype = Object.getPrototypeOf(tool) ?? Object.prototype;\n const wrapped = { ...tool };\n delete wrapped.cache_control;\n if (wrapped.extras != null) {\n wrapped.extras = { ...wrapped.extras };\n delete wrapped.extras.cache_control;\n }\n return Object.assign(Object.create(prototype), wrapped);\n}\n\n/**\n * Whether `tool` is already in the Anthropic provider payload shape — an\n * Anthropic built-in or a raw Anthropic tool object (has `input_schema`). These\n * carry `cache_control` directly on the block; the LangChain adapter does NOT\n * promote `extras.cache_control` for them. LangChain StructuredTools, by\n * contrast, expose the marker via `extras`.\n */\nfunction isProviderShapedTool(tool: AnthropicToolCacheCandidate): boolean {\n return (\n isAnthropicBuiltInTool(tool) ||\n 'input_schema' in (tool as Record<string, unknown>)\n );\n}\n\nfunction markCacheControl(\n tool: AnthropicToolCacheCandidate,\n ttl?: PromptCacheTtl\n): AnthropicToolCacheCandidate {\n const cacheControl = buildAnthropicCacheControl(ttl);\n const prototype = Object.getPrototypeOf(tool) ?? Object.prototype;\n if (isProviderShapedTool(tool)) {\n // Built-ins and raw Anthropic tool objects carry cache_control directly on\n // the block; `extras` is not promoted onto the payload for these shapes.\n const wrapped = { ...tool };\n delete wrapped.extras;\n return Object.assign(Object.create(prototype), wrapped, {\n cache_control: cacheControl,\n });\n }\n\n // LangChain custom tools: drop any direct marker and expose the breakpoint via\n // `extras`, which the Anthropic adapter promotes onto the payload.\n const wrapped = { ...tool };\n delete wrapped.cache_control;\n return Object.assign(Object.create(prototype), wrapped, {\n extras: {\n ...(tool.extras ?? {}),\n cache_control: cacheControl,\n },\n });\n}\n\n/**\n * Returns a callable that reports whether a given tool *name* is deferred\n * according to the supplied registry of tool definitions. Tools without\n * a registry entry are treated as non-deferred (i.e. static), matching\n * the host-supplied `graphTools` semantics elsewhere in the SDK.\n */\nexport function makeIsDeferred(\n toolDefinitions:\n | ReadonlyArray<{ name: string; defer_loading?: boolean }>\n | undefined\n): (toolName: string) => boolean {\n if (toolDefinitions == null || toolDefinitions.length === 0) {\n return () => false;\n }\n const deferred = new Set<string>();\n for (const def of toolDefinitions) {\n if (def.defer_loading === true) deferred.add(def.name);\n }\n if (deferred.size === 0) return () => false;\n return (name) => deferred.has(name);\n}\n\n/**\n * Stable-partition `tools` into [static..., deferred...] and stamp the\n * Anthropic `cache_control: ephemeral` marker on the last static tool.\n *\n * If `tools` is undefined or empty, or no entry has a usable `.name`,\n * returns the input unchanged. If there are no static tools at all,\n * also returns unchanged (nothing to cache).\n *\n * The original tool instances are never mutated. The marked entry is a\n * shallow wrapper that preserves the prototype chain so downstream\n * `instanceof` checks still pass. For custom tools, `extras` is merged\n * so any existing `providerToolDefinition` / other extras are kept.\n */\nexport function partitionAndMarkAnthropicToolCache(\n tools: GraphTools | undefined,\n isDeferred: (toolName: string) => boolean,\n ttl?: PromptCacheTtl\n): GraphTools | undefined {\n if (tools == null || tools.length === 0) return tools;\n\n // Use unknown[] internally to avoid GraphTools' union-array variance\n // (each member is one of three array types). We cast back to\n // GraphTools when returning.\n const staticTools: unknown[] = [];\n const deferredTools: unknown[] = [];\n\n for (const tool of tools) {\n const name = (tool as { name?: unknown }).name;\n if (typeof name === 'string' && isDeferred(name)) {\n deferredTools.push(tool);\n } else {\n staticTools.push(tool);\n }\n }\n\n // Anthropic serializes ALL tools before system/messages, so a stray\n // cache_control on any tool — static or deferred — that survives the resolved\n // breakpoint would violate the longer-TTL-first ordering. Strip stale markers\n // off the deferred tools first (they sit after the breakpoint but still before\n // system/messages, and the all-deferred case has no breakpoint of its own).\n let mutated = false;\n for (let i = 0; i < deferredTools.length; i++) {\n const candidate = deferredTools[i] as AnthropicToolCacheCandidate;\n if (hasCacheControl(candidate)) {\n deferredTools[i] = stripCacheControl(candidate);\n mutated = true;\n }\n }\n\n if (staticTools.length === 0) {\n return mutated ? ([...deferredTools] as GraphTools) : tools;\n }\n\n // Strip any stray cache_control off the earlier static tools so a leftover\n // 5-minute marker never sits ahead of the resolved breakpoint, then stamp (or\n // re-stamp) only the last static tool with the resolved TTL.\n for (let i = 0; i < staticTools.length - 1; i++) {\n const candidate = staticTools[i] as AnthropicToolCacheCandidate;\n if (hasCacheControl(candidate)) {\n staticTools[i] = stripCacheControl(candidate);\n mutated = true;\n }\n }\n\n const last = staticTools[\n staticTools.length - 1\n ] as AnthropicToolCacheCandidate;\n const desiredTtl: '1h' | undefined = ttl === '1h' ? '1h' : undefined;\n // \"Already correct\" requires a marker IN the effective location (one that\n // reaches the payload for this tool's shape) carrying the resolved TTL. A\n // marker in the wrong place — e.g. a direct `cache_control` on a LangChain\n // custom tool — is ineffective and must be re-stamped.\n const effective = getEffectiveCacheControl(last);\n const lastAlreadyCorrect =\n effective != null &&\n (effective.ttl === '1h' ? '1h' : undefined) === desiredTtl;\n if (!lastAlreadyCorrect) {\n staticTools[staticTools.length - 1] = markCacheControl(last, ttl);\n mutated = true;\n }\n\n // Return the original reference only when nothing changed AND partitioning\n // moved nothing. When deferred tools exist they must end up after the cache\n // breakpoint, so the partitioned array is returned even if no marker changed.\n if (!mutated && deferredTools.length === 0) {\n return tools;\n }\n return [...staticTools, ...deferredTools] as GraphTools;\n}\n"],"mappings":";;AAiCA,MAAM,mCAAmC;CACvC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AASA,SAAS,uBACP,MACwD;CACxD,MAAM,EAAE,SAAS;CACjB,OACE,OAAO,SAAS,YAChB,iCAAiC,MAAM,WAAW,KAAK,WAAW,MAAM,CAAC;AAE7E;;;;;;;AAQA,SAAS,gBAAgB,MAA4C;CACnE,IAAI,uBAAuB,IAAI,GAC7B,OAAO,KAAK,iBAAiB;CAE/B,OAAO,KAAK,iBAAiB,QAAQ,KAAK,QAAQ,iBAAiB;AACrE;;;;;;;;;AAUA,SAAS,yBACP,MAC+B;CAC/B,OACE,qBAAqB,IAAI,IAAI,KAAK,gBAAgB,KAAK,QAAQ;AAEnE;;;;;;AAOA,SAAS,kBACP,MAC6B;CAC7B,MAAM,YAAY,OAAO,eAAe,IAAI,KAAK,OAAO;CACxD,MAAM,UAAU,EAAE,GAAG,KAAK;CAC1B,OAAO,QAAQ;CACf,IAAI,QAAQ,UAAU,MAAM;EAC1B,QAAQ,SAAS,EAAE,GAAG,QAAQ,OAAO;EACrC,OAAO,QAAQ,OAAO;CACxB;CACA,OAAO,OAAO,OAAO,OAAO,OAAO,SAAS,GAAG,OAAO;AACxD;;;;;;;;AASA,SAAS,qBAAqB,MAA4C;CACxE,OACE,uBAAuB,IAAI,KAC3B,kBAAmB;AAEvB;AAEA,SAAS,iBACP,MACA,KAC6B;CAC7B,MAAM,eAAe,2BAA2B,GAAG;CACnD,MAAM,YAAY,OAAO,eAAe,IAAI,KAAK,OAAO;CACxD,IAAI,qBAAqB,IAAI,GAAG;EAG9B,MAAM,UAAU,EAAE,GAAG,KAAK;EAC1B,OAAO,QAAQ;EACf,OAAO,OAAO,OAAO,OAAO,OAAO,SAAS,GAAG,SAAS,EACtD,eAAe,aACjB,CAAC;CACH;CAIA,MAAM,UAAU,EAAE,GAAG,KAAK;CAC1B,OAAO,QAAQ;CACf,OAAO,OAAO,OAAO,OAAO,OAAO,SAAS,GAAG,SAAS,EACtD,QAAQ;EACN,GAAI,KAAK,UAAU,CAAC;EACpB,eAAe;CACjB,EACF,CAAC;AACH;;;;;;;AAQA,SAAgB,eACd,iBAG+B;CAC/B,IAAI,mBAAmB,QAAQ,gBAAgB,WAAW,GACxD,aAAa;CAEf,MAAM,2BAAW,IAAI,IAAY;CACjC,KAAK,MAAM,OAAO,iBAChB,IAAI,IAAI,kBAAkB,MAAM,SAAS,IAAI,IAAI,IAAI;CAEvD,IAAI,SAAS,SAAS,GAAG,aAAa;CACtC,QAAQ,SAAS,SAAS,IAAI,IAAI;AACpC;;;;;;;;;;;;;;AAeA,SAAgB,mCACd,OACA,YACA,KACwB;CACxB,IAAI,SAAS,QAAQ,MAAM,WAAW,GAAG,OAAO;CAKhD,MAAM,cAAyB,CAAC;CAChC,MAAM,gBAA2B,CAAC;CAElC,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,OAAQ,KAA4B;EAC1C,IAAI,OAAO,SAAS,YAAY,WAAW,IAAI,GAC7C,cAAc,KAAK,IAAI;OAEvB,YAAY,KAAK,IAAI;CAEzB;CAOA,IAAI,UAAU;CACd,KAAK,IAAI,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;EAC7C,MAAM,YAAY,cAAc;EAChC,IAAI,gBAAgB,SAAS,GAAG;GAC9B,cAAc,KAAK,kBAAkB,SAAS;GAC9C,UAAU;EACZ;CACF;CAEA,IAAI,YAAY,WAAW,GACzB,OAAO,UAAW,CAAC,GAAG,aAAa,IAAmB;CAMxD,KAAK,IAAI,IAAI,GAAG,IAAI,YAAY,SAAS,GAAG,KAAK;EAC/C,MAAM,YAAY,YAAY;EAC9B,IAAI,gBAAgB,SAAS,GAAG;GAC9B,YAAY,KAAK,kBAAkB,SAAS;GAC5C,UAAU;EACZ;CACF;CAEA,MAAM,OAAO,YACX,YAAY,SAAS;CAEvB,MAAM,aAA+B,QAAQ,OAAO,OAAO,KAAA;CAK3D,MAAM,YAAY,yBAAyB,IAAI;CAI/C,IAAI,EAFF,aAAa,SACZ,UAAU,QAAQ,OAAO,OAAO,KAAA,OAAe,aACzB;EACvB,YAAY,YAAY,SAAS,KAAK,iBAAiB,MAAM,GAAG;EAChE,UAAU;CACZ;CAKA,IAAI,CAAC,WAAW,cAAc,WAAW,GACvC,OAAO;CAET,OAAO,CAAC,GAAG,aAAa,GAAG,aAAa;AAC1C"}
|
|
@@ -4,6 +4,42 @@ import { withMessageRole } from "./format.mjs";
|
|
|
4
4
|
import { AIMessage, BaseMessage, HumanMessage, SystemMessage, ToolMessage } from "@langchain/core/messages";
|
|
5
5
|
//#region src/messages/cache.ts
|
|
6
6
|
/**
|
|
7
|
+
* Default TTL applied wherever a prompt-cache breakpoint is added. The 1-hour
|
|
8
|
+
* extended cache keeps prefixes warm across longer gaps between turns, at the
|
|
9
|
+
* cost of a higher one-time cache-write multiplier (2x vs 1.25x for 5m).
|
|
10
|
+
*/
|
|
11
|
+
const DEFAULT_PROMPT_CACHE_TTL = "1h";
|
|
12
|
+
/**
|
|
13
|
+
* Resolve an optionally-configured TTL to a concrete value, defaulting to the
|
|
14
|
+
* 1-hour extended cache. Used at the Anthropic/Bedrock prompt-cache call sites.
|
|
15
|
+
*/
|
|
16
|
+
function resolvePromptCacheTtl(ttl) {
|
|
17
|
+
return ttl ?? "1h";
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Build an Anthropic `cache_control` breakpoint for the given TTL. `'5m'` (or
|
|
21
|
+
* `undefined`) omits the `ttl` field — that is the provider default, so the
|
|
22
|
+
* payload stays byte-identical to the legacy 5-minute marker. `'1h'` adds the
|
|
23
|
+
* explicit extended-cache `ttl`.
|
|
24
|
+
*/
|
|
25
|
+
function buildAnthropicCacheControl(ttl) {
|
|
26
|
+
return ttl === "1h" ? {
|
|
27
|
+
type: "ephemeral",
|
|
28
|
+
ttl: "1h"
|
|
29
|
+
} : { type: "ephemeral" };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Build a Bedrock `cachePoint` for the given TTL. Mirrors
|
|
33
|
+
* {@link buildAnthropicCacheControl}: `'5m'`/`undefined` omits `ttl` (the
|
|
34
|
+
* legacy default), `'1h'` adds the extended-cache `ttl`.
|
|
35
|
+
*/
|
|
36
|
+
function buildBedrockCachePoint(ttl) {
|
|
37
|
+
return ttl === "1h" ? {
|
|
38
|
+
type: "default",
|
|
39
|
+
ttl: "1h"
|
|
40
|
+
} : { type: "default" };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
7
43
|
* Deep clones a message's content to prevent mutation of the original.
|
|
8
44
|
*/
|
|
9
45
|
function deepCloneContent(content) {
|
|
@@ -56,25 +92,41 @@ function cloneMessage(message, content) {
|
|
|
56
92
|
}
|
|
57
93
|
return cloned;
|
|
58
94
|
}
|
|
59
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Sanitize a Bedrock system message: strip Anthropic `cache_control` (Bedrock
|
|
97
|
+
* conversion can't use it) and normalize any existing `cachePoint` to the
|
|
98
|
+
* resolved TTL. The normalization matters because Bedrock requires longer-TTL
|
|
99
|
+
* checkpoints to appear before shorter ones — a stale 5-minute system cachePoint
|
|
100
|
+
* (host-supplied or carried over from a 5m config) left ahead of a 1-hour
|
|
101
|
+
* message tail would make the request invalid. System messages are never
|
|
102
|
+
* anchored as the tail breakpoint; this only fixes markers already present.
|
|
103
|
+
*/
|
|
104
|
+
function sanitizeBedrockSystemMessage(message, ttl) {
|
|
105
|
+
const content = message.content;
|
|
106
|
+
if (!Array.isArray(content)) return message;
|
|
107
|
+
const sanitized = [];
|
|
60
108
|
let modified = false;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
109
|
+
for (const block of content) {
|
|
110
|
+
if (isCachePoint(block)) {
|
|
111
|
+
const existing = block.cachePoint;
|
|
112
|
+
const desired = buildBedrockCachePoint(ttl);
|
|
113
|
+
if (existing?.ttl !== desired.ttl) {
|
|
114
|
+
modified = true;
|
|
115
|
+
sanitized.push({ cachePoint: desired });
|
|
116
|
+
} else sanitized.push(block);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if ("cache_control" in block) {
|
|
64
120
|
const cloned = { ...block };
|
|
65
121
|
delete cloned.cache_control;
|
|
66
122
|
modified = true;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (!Array.isArray(content)) return message;
|
|
75
|
-
const stripped = stripAnthropicCacheControlFromBlocks(content);
|
|
76
|
-
if (!stripped.modified) return message;
|
|
77
|
-
return cloneMessage(message, stripped.content);
|
|
123
|
+
sanitized.push(cloned);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
sanitized.push(block);
|
|
127
|
+
}
|
|
128
|
+
if (!modified) return message;
|
|
129
|
+
return cloneMessage(message, sanitized);
|
|
78
130
|
}
|
|
79
131
|
/**
|
|
80
132
|
* Anthropic API: Adds cache control to the appropriate user messages in the payload.
|
|
@@ -85,7 +137,7 @@ function sanitizeBedrockSystemMessage(message) {
|
|
|
85
137
|
* @param messages - The array of message objects.
|
|
86
138
|
* @returns - A new array of message objects with cache control added.
|
|
87
139
|
*/
|
|
88
|
-
function addCacheControl(messages) {
|
|
140
|
+
function addCacheControl(messages, ttl) {
|
|
89
141
|
if (!Array.isArray(messages) || messages.length < 2) return messages;
|
|
90
142
|
const updatedMessages = [...messages];
|
|
91
143
|
let userMessagesModified = 0;
|
|
@@ -118,14 +170,14 @@ function addCacheControl(messages) {
|
|
|
118
170
|
}
|
|
119
171
|
if (!modified && !needsCacheAdd) continue;
|
|
120
172
|
if (needsCacheAdd && lastTextIndex >= 0) {
|
|
121
|
-
workingContent[lastTextIndex].cache_control =
|
|
173
|
+
workingContent[lastTextIndex].cache_control = buildAnthropicCacheControl(ttl);
|
|
122
174
|
userMessagesModified++;
|
|
123
175
|
}
|
|
124
176
|
} else if (typeof content === "string" && needsCacheAdd) {
|
|
125
177
|
workingContent = [{
|
|
126
178
|
type: "text",
|
|
127
179
|
text: content,
|
|
128
|
-
cache_control:
|
|
180
|
+
cache_control: buildAnthropicCacheControl(ttl)
|
|
129
181
|
}];
|
|
130
182
|
userMessagesModified++;
|
|
131
183
|
} else continue;
|
|
@@ -196,7 +248,7 @@ function isTailCacheableBlock(block) {
|
|
|
196
248
|
*
|
|
197
249
|
* Returns a new array; only messages that require modification are cloned.
|
|
198
250
|
*/
|
|
199
|
-
function addTailCacheControl(messages) {
|
|
251
|
+
function addTailCacheControl(messages, ttl) {
|
|
200
252
|
if (!Array.isArray(messages) || messages.length === 0) return messages;
|
|
201
253
|
const updatedMessages = [...messages];
|
|
202
254
|
let markerPlaced = false;
|
|
@@ -227,7 +279,7 @@ function addTailCacheControl(messages) {
|
|
|
227
279
|
workingContent.push(cloned);
|
|
228
280
|
}
|
|
229
281
|
if (canPlaceMarker && tailIndex >= 0) {
|
|
230
|
-
workingContent[tailIndex].cache_control =
|
|
282
|
+
workingContent[tailIndex].cache_control = buildAnthropicCacheControl(ttl);
|
|
231
283
|
markerPlaced = true;
|
|
232
284
|
modified = true;
|
|
233
285
|
}
|
|
@@ -236,7 +288,7 @@ function addTailCacheControl(messages) {
|
|
|
236
288
|
workingContent = [{
|
|
237
289
|
type: "text",
|
|
238
290
|
text: content,
|
|
239
|
-
cache_control:
|
|
291
|
+
cache_control: buildAnthropicCacheControl(ttl)
|
|
240
292
|
}];
|
|
241
293
|
markerPlaced = true;
|
|
242
294
|
} else continue;
|
|
@@ -273,7 +325,7 @@ function isAssistantConversationMessage(message) {
|
|
|
273
325
|
function hasCacheMarker(message) {
|
|
274
326
|
return Array.isArray(message.content) && message.content.some((block) => "cache_control" in block);
|
|
275
327
|
}
|
|
276
|
-
function addCacheControlToRecentMessages(messages, maxCachePoints, canUseMessage) {
|
|
328
|
+
function addCacheControlToRecentMessages(messages, maxCachePoints, canUseMessage, ttl) {
|
|
277
329
|
if (!Array.isArray(messages) || messages.length === 0 || maxCachePoints <= 0) return messages;
|
|
278
330
|
const updatedMessages = [...messages];
|
|
279
331
|
let cachePointsAdded = 0;
|
|
@@ -307,7 +359,7 @@ function addCacheControlToRecentMessages(messages, maxCachePoints, canUseMessage
|
|
|
307
359
|
workingContent.push(cloned);
|
|
308
360
|
}
|
|
309
361
|
if (canAddCache && lastNonEmptyTextIndex >= 0) {
|
|
310
|
-
workingContent[lastNonEmptyTextIndex].cache_control =
|
|
362
|
+
workingContent[lastNonEmptyTextIndex].cache_control = buildAnthropicCacheControl(ttl);
|
|
311
363
|
cachePointsAdded++;
|
|
312
364
|
modified = true;
|
|
313
365
|
}
|
|
@@ -316,7 +368,7 @@ function addCacheControlToRecentMessages(messages, maxCachePoints, canUseMessage
|
|
|
316
368
|
workingContent = [{
|
|
317
369
|
type: "text",
|
|
318
370
|
text: content,
|
|
319
|
-
cache_control:
|
|
371
|
+
cache_control: buildAnthropicCacheControl(ttl)
|
|
320
372
|
}];
|
|
321
373
|
cachePointsAdded++;
|
|
322
374
|
} else continue;
|
|
@@ -324,10 +376,10 @@ function addCacheControlToRecentMessages(messages, maxCachePoints, canUseMessage
|
|
|
324
376
|
}
|
|
325
377
|
return updatedMessages;
|
|
326
378
|
}
|
|
327
|
-
function addCacheControlToStablePrefixMessages(messages, maxCachePoints) {
|
|
328
|
-
const assistantMarked = addCacheControlToRecentMessages(messages, maxCachePoints, isAssistantConversationMessage);
|
|
379
|
+
function addCacheControlToStablePrefixMessages(messages, maxCachePoints, ttl) {
|
|
380
|
+
const assistantMarked = addCacheControlToRecentMessages(messages, maxCachePoints, isAssistantConversationMessage, ttl);
|
|
329
381
|
if (assistantMarked.some(hasCacheMarker)) return assistantMarked;
|
|
330
|
-
return addCacheControlToRecentMessages(messages, maxCachePoints, isCacheableConversationMessage);
|
|
382
|
+
return addCacheControlToRecentMessages(messages, maxCachePoints, isCacheableConversationMessage, ttl);
|
|
331
383
|
}
|
|
332
384
|
/**
|
|
333
385
|
* Checks if a message's content has Anthropic cache_control fields.
|
|
@@ -391,7 +443,7 @@ function stripBedrockCacheControl(messages) {
|
|
|
391
443
|
* @param messages - The array of message objects.
|
|
392
444
|
* @returns - A new array of message objects with cache points added.
|
|
393
445
|
*/
|
|
394
|
-
function addBedrockCacheControl(messages) {
|
|
446
|
+
function addBedrockCacheControl(messages, ttl) {
|
|
395
447
|
if (!Array.isArray(messages) || messages.length === 0) return messages;
|
|
396
448
|
const updatedMessages = [...messages];
|
|
397
449
|
let cachePointsAdded = 0;
|
|
@@ -400,7 +452,7 @@ function addBedrockCacheControl(messages) {
|
|
|
400
452
|
const messageType = "getType" in originalMessage && typeof originalMessage.getType === "function" ? originalMessage.getType() : void 0;
|
|
401
453
|
const messageRole = "role" in originalMessage && typeof originalMessage.role === "string" ? originalMessage.role : void 0;
|
|
402
454
|
if (messageType === "system" || messageRole === "system") {
|
|
403
|
-
updatedMessages[i] = sanitizeBedrockSystemMessage(originalMessage);
|
|
455
|
+
updatedMessages[i] = sanitizeBedrockSystemMessage(originalMessage, ttl);
|
|
404
456
|
continue;
|
|
405
457
|
}
|
|
406
458
|
const isToolMessage = messageType === "tool" || messageRole === "tool";
|
|
@@ -436,14 +488,14 @@ function addBedrockCacheControl(messages) {
|
|
|
436
488
|
}
|
|
437
489
|
if (!modified && !needsCacheAdd) continue;
|
|
438
490
|
if (needsCacheAdd && lastNonEmptyTextIndex >= 0) {
|
|
439
|
-
workingContent.splice(lastNonEmptyTextIndex + 1, 0, { cachePoint:
|
|
491
|
+
workingContent.splice(lastNonEmptyTextIndex + 1, 0, { cachePoint: buildBedrockCachePoint(ttl) });
|
|
440
492
|
cachePointsAdded++;
|
|
441
493
|
}
|
|
442
494
|
} else if (typeof content === "string" && needsCacheAdd) {
|
|
443
495
|
workingContent = [{
|
|
444
496
|
type: "text",
|
|
445
497
|
text: content
|
|
446
|
-
}, { cachePoint:
|
|
498
|
+
}, { cachePoint: buildBedrockCachePoint(ttl) }];
|
|
447
499
|
cachePointsAdded++;
|
|
448
500
|
} else if (typeof content === "string" && hasSerializationProps) workingContent = content;
|
|
449
501
|
else continue;
|
|
@@ -468,7 +520,7 @@ function addBedrockCacheControl(messages) {
|
|
|
468
520
|
*
|
|
469
521
|
* Returns a new array - only clones messages that require modification.
|
|
470
522
|
*/
|
|
471
|
-
function addBedrockTailCacheControl(messages) {
|
|
523
|
+
function addBedrockTailCacheControl(messages, ttl) {
|
|
472
524
|
if (!Array.isArray(messages) || messages.length === 0) return messages;
|
|
473
525
|
const updatedMessages = [...messages];
|
|
474
526
|
let cachePointPlaced = false;
|
|
@@ -477,7 +529,7 @@ function addBedrockTailCacheControl(messages) {
|
|
|
477
529
|
const messageType = "getType" in originalMessage && typeof originalMessage.getType === "function" ? originalMessage.getType() : void 0;
|
|
478
530
|
const messageRole = "role" in originalMessage && typeof originalMessage.role === "string" ? originalMessage.role : void 0;
|
|
479
531
|
if (messageType === "system" || messageRole === "system") {
|
|
480
|
-
updatedMessages[i] = sanitizeBedrockSystemMessage(originalMessage);
|
|
532
|
+
updatedMessages[i] = sanitizeBedrockSystemMessage(originalMessage, ttl);
|
|
481
533
|
continue;
|
|
482
534
|
}
|
|
483
535
|
const content = originalMessage.content;
|
|
@@ -511,7 +563,7 @@ function addBedrockTailCacheControl(messages) {
|
|
|
511
563
|
}
|
|
512
564
|
if (!modified && !canPlaceCachePoint) continue;
|
|
513
565
|
if (canPlaceCachePoint && lastNonEmptyTextIndex >= 0) {
|
|
514
|
-
workingContent.splice(lastNonEmptyTextIndex + 1, 0, { cachePoint:
|
|
566
|
+
workingContent.splice(lastNonEmptyTextIndex + 1, 0, { cachePoint: buildBedrockCachePoint(ttl) });
|
|
515
567
|
cachePointPlaced = true;
|
|
516
568
|
modified = true;
|
|
517
569
|
}
|
|
@@ -519,7 +571,7 @@ function addBedrockTailCacheControl(messages) {
|
|
|
519
571
|
workingContent = [{
|
|
520
572
|
type: "text",
|
|
521
573
|
text: content
|
|
522
|
-
}, { cachePoint:
|
|
574
|
+
}, { cachePoint: buildBedrockCachePoint(ttl) }];
|
|
523
575
|
cachePointPlaced = true;
|
|
524
576
|
} else if (typeof content === "string" && hasSerializationProps) workingContent = content;
|
|
525
577
|
else continue;
|
|
@@ -528,6 +580,6 @@ function addBedrockTailCacheControl(messages) {
|
|
|
528
580
|
return updatedMessages;
|
|
529
581
|
}
|
|
530
582
|
//#endregion
|
|
531
|
-
export { addBedrockCacheControl, addBedrockTailCacheControl, addCacheControl, addCacheControlToStablePrefixMessages, addTailCacheControl, cloneMessage, stripAnthropicCacheControl, stripBedrockCacheControl };
|
|
583
|
+
export { DEFAULT_PROMPT_CACHE_TTL, addBedrockCacheControl, addBedrockTailCacheControl, addCacheControl, addCacheControlToStablePrefixMessages, addTailCacheControl, buildAnthropicCacheControl, buildBedrockCachePoint, cloneMessage, resolvePromptCacheTtl, stripAnthropicCacheControl, stripBedrockCacheControl };
|
|
532
584
|
|
|
533
585
|
//# sourceMappingURL=cache.mjs.map
|