@librechat/agents 3.2.41 → 3.2.43
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 +4 -3
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +5 -2
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/hooks/createWorkspacePolicyHook.cjs +1 -1
- package/dist/cjs/llm/bedrock/index.cjs +9 -1
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +6 -0
- package/dist/cjs/messages/cache.cjs +43 -0
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/session/JsonlSessionStore.cjs +1 -1
- package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs +2 -2
- package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/cloudflare/CloudflareSandboxExecutionEngine.cjs +118 -22
- package/dist/cjs/tools/cloudflare/CloudflareSandboxExecutionEngine.cjs.map +1 -1
- package/dist/cjs/tools/local/CompileCheckTool.cjs +11 -3
- package/dist/cjs/tools/local/CompileCheckTool.cjs.map +1 -1
- package/dist/cjs/tools/local/FileCheckpointer.cjs +8 -3
- package/dist/cjs/tools/local/FileCheckpointer.cjs.map +1 -1
- package/dist/cjs/tools/local/LocalCodingTools.cjs +26 -7
- package/dist/cjs/tools/local/LocalCodingTools.cjs.map +1 -1
- package/dist/cjs/tools/local/LocalExecutionEngine.cjs +2 -2
- package/dist/cjs/tools/local/syntaxCheck.cjs +6 -2
- package/dist/cjs/tools/local/syntaxCheck.cjs.map +1 -1
- package/dist/cjs/tools/local/workspaceFS.cjs +20 -0
- package/dist/cjs/tools/local/workspaceFS.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +5 -4
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +6 -3
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/hooks/createWorkspacePolicyHook.mjs +1 -1
- package/dist/esm/llm/bedrock/index.mjs +10 -2
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/main.mjs +3 -3
- package/dist/esm/messages/cache.mjs +42 -1
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/session/JsonlSessionStore.mjs +1 -1
- package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs +3 -3
- package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/cloudflare/CloudflareSandboxExecutionEngine.mjs +115 -23
- package/dist/esm/tools/cloudflare/CloudflareSandboxExecutionEngine.mjs.map +1 -1
- package/dist/esm/tools/local/CompileCheckTool.mjs +11 -3
- package/dist/esm/tools/local/CompileCheckTool.mjs.map +1 -1
- package/dist/esm/tools/local/FileCheckpointer.mjs +9 -4
- package/dist/esm/tools/local/FileCheckpointer.mjs.map +1 -1
- package/dist/esm/tools/local/LocalCodingTools.mjs +26 -7
- package/dist/esm/tools/local/LocalCodingTools.mjs.map +1 -1
- package/dist/esm/tools/local/LocalExecutionEngine.mjs +2 -2
- package/dist/esm/tools/local/syntaxCheck.mjs +6 -2
- package/dist/esm/tools/local/syntaxCheck.mjs.map +1 -1
- package/dist/esm/tools/local/workspaceFS.mjs +19 -1
- package/dist/esm/tools/local/workspaceFS.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +3 -2
- package/dist/types/llm/bedrock/index.d.ts +7 -0
- package/dist/types/messages/cache.d.ts +27 -0
- package/dist/types/tools/cloudflare/CloudflareSandboxExecutionEngine.d.ts +53 -0
- package/dist/types/tools/local/workspaceFS.d.ts +13 -0
- package/package.json +1 -1
- package/src/agents/AgentContext.ts +10 -3
- package/src/graphs/Graph.ts +24 -6
- package/src/llm/bedrock/index.ts +19 -3
- package/src/llm/bedrock/llm.spec.ts +97 -0
- package/src/messages/cache.test.ts +63 -0
- package/src/messages/cache.ts +51 -0
- package/src/specs/vllm-reasoning-toolcalls.test.ts +340 -0
- package/src/tools/__tests__/CloudflareSandboxExecution.test.ts +323 -0
- package/src/tools/__tests__/LocalExecutionTools.test.ts +54 -0
- package/src/tools/cloudflare/CloudflareProgrammaticToolCalling.ts +7 -2
- package/src/tools/cloudflare/CloudflareSandboxExecutionEngine.ts +269 -37
- package/src/tools/local/CompileCheckTool.ts +19 -3
- package/src/tools/local/FileCheckpointer.ts +20 -4
- package/src/tools/local/LocalCodingTools.ts +61 -8
- package/src/tools/local/__tests__/FileCheckpointer.test.ts +42 -0
- package/src/tools/local/syntaxCheck.ts +14 -2
- package/src/tools/local/workspaceFS.ts +27 -0
|
@@ -26,6 +26,19 @@ export type ReaddirEntry = {
|
|
|
26
26
|
isDirectory(): boolean;
|
|
27
27
|
isSymbolicLink(): boolean;
|
|
28
28
|
};
|
|
29
|
+
/**
|
|
30
|
+
* Thrown when a {@link WorkspaceFS} operation is abandoned by a client-side
|
|
31
|
+
* backstop timeout (a stalled remote/sandbox RPC that never returned). This is
|
|
32
|
+
* DISTINCT from "file not found": callers that catch generic FS failures as
|
|
33
|
+
* absence — e.g. a pre-read before an overwrite, or a `stat` before snapshotting
|
|
34
|
+
* — MUST rethrow this so a stalled read isn't mistaken for a missing file (which
|
|
35
|
+
* would otherwise overwrite an existing file or snapshot it as absent).
|
|
36
|
+
*/
|
|
37
|
+
export declare class WorkspaceClientTimeoutError extends Error {
|
|
38
|
+
readonly code = "WORKSPACE_CLIENT_TIMEOUT";
|
|
39
|
+
constructor(message: string);
|
|
40
|
+
}
|
|
41
|
+
export declare function isWorkspaceClientTimeoutError(error: unknown): error is WorkspaceClientTimeoutError;
|
|
29
42
|
export interface WorkspaceFS {
|
|
30
43
|
readFile(path: string, encoding: 'utf8'): Promise<string>;
|
|
31
44
|
readFile(path: string): Promise<Buffer>;
|
package/package.json
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
buildAnthropicCacheControl,
|
|
15
15
|
buildBedrockCachePoint,
|
|
16
16
|
resolvePromptCacheTtl,
|
|
17
|
+
resolveBedrockPromptCacheTtl,
|
|
17
18
|
cloneMessage,
|
|
18
19
|
type PromptCacheTtl,
|
|
19
20
|
} from '@/messages/cache';
|
|
@@ -846,6 +847,8 @@ export class AgentContext {
|
|
|
846
847
|
const bedrockOptions = this.clientOptions as
|
|
847
848
|
| t.BedrockAnthropicClientOptions
|
|
848
849
|
| undefined;
|
|
850
|
+
// Nova accepts system/message cachePoints (only the tool checkpoint is
|
|
851
|
+
// Claude-only), so this is gated on promptCache alone.
|
|
849
852
|
return bedrockOptions?.promptCache === true;
|
|
850
853
|
}
|
|
851
854
|
|
|
@@ -867,14 +870,18 @@ export class AgentContext {
|
|
|
867
870
|
}
|
|
868
871
|
|
|
869
872
|
/**
|
|
870
|
-
* Resolved TTL for Bedrock prompt-cache checkpoints (default `'1h'`).
|
|
871
|
-
*
|
|
873
|
+
* Resolved TTL for Bedrock prompt-cache checkpoints (default `'1h'` on Claude).
|
|
874
|
+
* Claude models downgrade an unsupported 1h to 5m server-side; non-Claude
|
|
875
|
+
* models (Nova) reject the extended TTL, so they are clamped to 5m.
|
|
872
876
|
*/
|
|
873
877
|
private getBedrockPromptCacheTtl(): PromptCacheTtl {
|
|
874
878
|
const bedrockOptions = this.clientOptions as
|
|
875
879
|
| t.BedrockAnthropicClientOptions
|
|
876
880
|
| undefined;
|
|
877
|
-
return
|
|
881
|
+
return resolveBedrockPromptCacheTtl(
|
|
882
|
+
bedrockOptions?.promptCacheTtl,
|
|
883
|
+
(bedrockOptions as { model?: string } | undefined)?.model
|
|
884
|
+
);
|
|
878
885
|
}
|
|
879
886
|
|
|
880
887
|
private buildSystemMessage({
|
package/src/graphs/Graph.ts
CHANGED
|
@@ -28,6 +28,8 @@ import {
|
|
|
28
28
|
syncBudgetDerivedFields,
|
|
29
29
|
addTailCacheControl,
|
|
30
30
|
resolvePromptCacheTtl,
|
|
31
|
+
resolveBedrockPromptCacheTtl,
|
|
32
|
+
supportsBedrockToolCache,
|
|
31
33
|
getMessageId,
|
|
32
34
|
makeIsDeferred,
|
|
33
35
|
partitionAndMarkAnthropicToolCache,
|
|
@@ -1442,11 +1444,19 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1442
1444
|
| undefined
|
|
1443
1445
|
)?.promptCache === true
|
|
1444
1446
|
) {
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1447
|
+
const bedrockModel = (
|
|
1448
|
+
agentContext.clientOptions as { model?: string } | undefined
|
|
1449
|
+
)?.model;
|
|
1450
|
+
// An omitted model falls back to LangChain's default Claude model (which
|
|
1451
|
+
// supports tool caching); only an explicit non-Claude model (e.g. Nova)
|
|
1452
|
+
// skips tool marking so its stray marker never leaks into toolConfig.
|
|
1453
|
+
if (bedrockModel == null || supportsBedrockToolCache(bedrockModel)) {
|
|
1454
|
+
toolsForBinding =
|
|
1455
|
+
partitionAndMarkBedrockToolCache(
|
|
1456
|
+
rawToolsForBinding,
|
|
1457
|
+
makeIsDeferred(agentContext.toolDefinitions)
|
|
1458
|
+
) ?? rawToolsForBinding;
|
|
1459
|
+
}
|
|
1450
1460
|
}
|
|
1451
1461
|
|
|
1452
1462
|
let model =
|
|
@@ -1790,6 +1800,9 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1790
1800
|
| t.ProviderOptionsMap[Providers.OPENROUTER]
|
|
1791
1801
|
| undefined
|
|
1792
1802
|
)?.promptCache === true;
|
|
1803
|
+
// Message/system cache points work on all cache-capable Bedrock models,
|
|
1804
|
+
// including Nova (verified live: HTTP 200 with cacheWriteInputTokens). Only
|
|
1805
|
+
// the tool checkpoint is Claude-only, so this is gated on promptCache alone.
|
|
1793
1806
|
const bedrockPromptCacheEnabled =
|
|
1794
1807
|
agentContext.provider === Providers.BEDROCK &&
|
|
1795
1808
|
(
|
|
@@ -1868,9 +1881,14 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1868
1881
|
const bedrockOptions = agentContext.clientOptions as
|
|
1869
1882
|
| t.BedrockAnthropicClientOptions
|
|
1870
1883
|
| undefined;
|
|
1884
|
+
// Non-Claude models (Nova) reject the extended 1h TTL, so resolve it
|
|
1885
|
+
// against the model — message/system caching stays on, clamped to 5m.
|
|
1871
1886
|
finalMessages = addBedrockTailCacheControl<BaseMessage>(
|
|
1872
1887
|
finalMessages,
|
|
1873
|
-
|
|
1888
|
+
resolveBedrockPromptCacheTtl(
|
|
1889
|
+
bedrockOptions?.promptCacheTtl,
|
|
1890
|
+
(bedrockOptions as { model?: string } | undefined)?.model
|
|
1891
|
+
)
|
|
1874
1892
|
);
|
|
1875
1893
|
}
|
|
1876
1894
|
|
package/src/llm/bedrock/index.ts
CHANGED
|
@@ -39,7 +39,11 @@ import {
|
|
|
39
39
|
handleConverseStreamContentBlockDelta,
|
|
40
40
|
handleConverseStreamMetadata,
|
|
41
41
|
} from './utils';
|
|
42
|
-
import {
|
|
42
|
+
import {
|
|
43
|
+
resolveBedrockPromptCacheTtl,
|
|
44
|
+
supportsBedrockToolCache,
|
|
45
|
+
type PromptCacheTtl,
|
|
46
|
+
} from '@/messages/cache';
|
|
43
47
|
import { insertBedrockToolCachePoint } from './toolCache';
|
|
44
48
|
|
|
45
49
|
/**
|
|
@@ -134,12 +138,24 @@ export class CustomChatBedrockConverse extends ChatBedrockConverse {
|
|
|
134
138
|
*/
|
|
135
139
|
serviceTier?: ServiceTierType;
|
|
136
140
|
|
|
141
|
+
/**
|
|
142
|
+
* The configured model id, captured at construction so it survives the
|
|
143
|
+
* temporary `this.model` swap to an application-inference-profile ARN during
|
|
144
|
+
* generation. Used to gate the Bedrock tool cache point to Claude models
|
|
145
|
+
* (see {@link supportsBedrockToolCache}).
|
|
146
|
+
*/
|
|
147
|
+
private readonly cacheModelId: string;
|
|
148
|
+
|
|
137
149
|
constructor(fields?: CustomChatBedrockConverseInput) {
|
|
138
150
|
super(fields);
|
|
139
151
|
this.promptCache = fields?.promptCache;
|
|
140
152
|
this.promptCacheTtl = fields?.promptCacheTtl;
|
|
141
153
|
this.applicationInferenceProfile = fields?.applicationInferenceProfile;
|
|
142
154
|
this.serviceTier = fields?.serviceTier;
|
|
155
|
+
// `super(fields)` initializes `this.model` to LangChain's default Claude
|
|
156
|
+
// model when `fields.model` is omitted, so fall back to it rather than ''
|
|
157
|
+
// (which would treat the default Claude model as tool-cache-unsupported).
|
|
158
|
+
this.cacheModelId = fields?.model ?? this.model;
|
|
143
159
|
}
|
|
144
160
|
|
|
145
161
|
static lc_name(): string {
|
|
@@ -164,11 +180,11 @@ export class CustomChatBedrockConverse extends ChatBedrockConverse {
|
|
|
164
180
|
} {
|
|
165
181
|
const baseParams = super.invocationParams(options);
|
|
166
182
|
const toolConfig =
|
|
167
|
-
this.promptCache === true
|
|
183
|
+
this.promptCache === true && supportsBedrockToolCache(this.cacheModelId)
|
|
168
184
|
? insertBedrockToolCachePoint(
|
|
169
185
|
baseParams.toolConfig,
|
|
170
186
|
true,
|
|
171
|
-
|
|
187
|
+
resolveBedrockPromptCacheTtl(this.promptCacheTtl, this.cacheModelId)
|
|
172
188
|
)
|
|
173
189
|
: baseParams.toolConfig;
|
|
174
190
|
|
|
@@ -375,6 +375,7 @@ describe('CustomChatBedrockConverse', () => {
|
|
|
375
375
|
test('adds a Bedrock cache point for directly-bound tools', () => {
|
|
376
376
|
const model = new CustomChatBedrockConverse({
|
|
377
377
|
...baseConstructorArgs,
|
|
378
|
+
model: 'anthropic.claude-3-haiku-20240307-v1:0',
|
|
378
379
|
promptCache: true,
|
|
379
380
|
});
|
|
380
381
|
|
|
@@ -400,6 +401,7 @@ describe('CustomChatBedrockConverse', () => {
|
|
|
400
401
|
test('defaults the tool cache point to the 1h extended TTL', () => {
|
|
401
402
|
const model = new CustomChatBedrockConverse({
|
|
402
403
|
...baseConstructorArgs,
|
|
404
|
+
model: 'anthropic.claude-3-haiku-20240307-v1:0',
|
|
403
405
|
promptCache: true,
|
|
404
406
|
});
|
|
405
407
|
|
|
@@ -430,6 +432,7 @@ describe('CustomChatBedrockConverse', () => {
|
|
|
430
432
|
test('honors an explicit 5m promptCacheTtl on the tool cache point', () => {
|
|
431
433
|
const model = new CustomChatBedrockConverse({
|
|
432
434
|
...baseConstructorArgs,
|
|
435
|
+
model: 'anthropic.claude-3-haiku-20240307-v1:0',
|
|
433
436
|
promptCache: true,
|
|
434
437
|
promptCacheTtl: '5m',
|
|
435
438
|
});
|
|
@@ -461,6 +464,7 @@ describe('CustomChatBedrockConverse', () => {
|
|
|
461
464
|
test('adds the Bedrock cache point before deferred tools', () => {
|
|
462
465
|
const model = new CustomChatBedrockConverse({
|
|
463
466
|
...baseConstructorArgs,
|
|
467
|
+
model: 'anthropic.claude-3-haiku-20240307-v1:0',
|
|
464
468
|
promptCache: true,
|
|
465
469
|
});
|
|
466
470
|
const tools = partitionAndMarkBedrockToolCache(
|
|
@@ -500,6 +504,7 @@ describe('CustomChatBedrockConverse', () => {
|
|
|
500
504
|
test('does not fall back to caching when Graph marks all tools deferred', () => {
|
|
501
505
|
const model = new CustomChatBedrockConverse({
|
|
502
506
|
...baseConstructorArgs,
|
|
507
|
+
model: 'anthropic.claude-3-haiku-20240307-v1:0',
|
|
503
508
|
promptCache: true,
|
|
504
509
|
});
|
|
505
510
|
const tools = partitionAndMarkBedrockToolCache(
|
|
@@ -525,6 +530,98 @@ describe('CustomChatBedrockConverse', () => {
|
|
|
525
530
|
'__lc_bedrock_skip_tool_cache'
|
|
526
531
|
);
|
|
527
532
|
});
|
|
533
|
+
|
|
534
|
+
test('does not add a tool cache point for Amazon Nova models', () => {
|
|
535
|
+
// Regression for danny-avila/LibreChat#13838: Nova rejects a cachePoint in
|
|
536
|
+
// toolConfig.tools ("Malformed input request: #/toolConfig/tools/0:
|
|
537
|
+
// extraneous key [cachePoint] is not permitted"). Only the tool checkpoint
|
|
538
|
+
// is Claude-only — Nova still caches system/messages — so promptCache: true
|
|
539
|
+
// on Nova must omit just the tool cache point.
|
|
540
|
+
const model = new CustomChatBedrockConverse({
|
|
541
|
+
...baseConstructorArgs,
|
|
542
|
+
model: 'us.amazon.nova-pro-v1:0',
|
|
543
|
+
promptCache: true,
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
const params = model.invocationParams({
|
|
547
|
+
tools: [
|
|
548
|
+
{
|
|
549
|
+
type: 'function',
|
|
550
|
+
function: {
|
|
551
|
+
name: 'direct_tool',
|
|
552
|
+
description: 'Direct tool',
|
|
553
|
+
parameters: { type: 'object', properties: {} },
|
|
554
|
+
},
|
|
555
|
+
},
|
|
556
|
+
],
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
expect(getBedrockToolNames(params.toolConfig?.tools)).toEqual([
|
|
560
|
+
'direct_tool',
|
|
561
|
+
]);
|
|
562
|
+
expect(JSON.stringify(params.toolConfig?.tools ?? [])).not.toContain(
|
|
563
|
+
'cachePoint'
|
|
564
|
+
);
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
test('still adds a tool cache point for a Claude model behind an inference profile', () => {
|
|
568
|
+
// The configured Claude model id is captured at construction, so the gate
|
|
569
|
+
// survives the application-inference-profile ARN swap during generation.
|
|
570
|
+
const model = new CustomChatBedrockConverse({
|
|
571
|
+
...baseConstructorArgs,
|
|
572
|
+
model: 'anthropic.claude-sonnet-4-5-20250929-v1:0',
|
|
573
|
+
applicationInferenceProfile:
|
|
574
|
+
'arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/test',
|
|
575
|
+
promptCache: true,
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
const params = model.invocationParams({
|
|
579
|
+
tools: [
|
|
580
|
+
{
|
|
581
|
+
type: 'function',
|
|
582
|
+
function: {
|
|
583
|
+
name: 'direct_tool',
|
|
584
|
+
description: 'Direct tool',
|
|
585
|
+
parameters: { type: 'object', properties: {} },
|
|
586
|
+
},
|
|
587
|
+
},
|
|
588
|
+
],
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
expect(getBedrockToolNames(params.toolConfig?.tools)).toEqual([
|
|
592
|
+
'direct_tool',
|
|
593
|
+
'cachePoint',
|
|
594
|
+
]);
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
test('still adds a tool cache point when model is omitted (default is Claude)', () => {
|
|
598
|
+
// When no model is provided, LangChain initializes this.model to a default
|
|
599
|
+
// Claude model, so the tool cache point must still apply.
|
|
600
|
+
const model = new CustomChatBedrockConverse({
|
|
601
|
+
...baseConstructorArgs,
|
|
602
|
+
promptCache: true,
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
expect(model.model).toMatch(/claude/i);
|
|
606
|
+
|
|
607
|
+
const params = model.invocationParams({
|
|
608
|
+
tools: [
|
|
609
|
+
{
|
|
610
|
+
type: 'function',
|
|
611
|
+
function: {
|
|
612
|
+
name: 'direct_tool',
|
|
613
|
+
description: 'Direct tool',
|
|
614
|
+
parameters: { type: 'object', properties: {} },
|
|
615
|
+
},
|
|
616
|
+
},
|
|
617
|
+
],
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
expect(getBedrockToolNames(params.toolConfig?.tools)).toEqual([
|
|
621
|
+
'direct_tool',
|
|
622
|
+
'cachePoint',
|
|
623
|
+
]);
|
|
624
|
+
});
|
|
528
625
|
});
|
|
529
626
|
|
|
530
627
|
describe('contentBlockIndex cleanup', () => {
|
|
@@ -19,6 +19,8 @@ import {
|
|
|
19
19
|
buildAnthropicCacheControl,
|
|
20
20
|
buildBedrockCachePoint,
|
|
21
21
|
resolvePromptCacheTtl,
|
|
22
|
+
resolveBedrockPromptCacheTtl,
|
|
23
|
+
supportsBedrockToolCache,
|
|
22
24
|
DEFAULT_PROMPT_CACHE_TTL,
|
|
23
25
|
} from './cache';
|
|
24
26
|
import { _convertMessagesToOpenAIParams } from '@/llm/openai/utils';
|
|
@@ -895,6 +897,67 @@ describe('synthetic skill/meta messages are not cache-anchored', () => {
|
|
|
895
897
|
});
|
|
896
898
|
});
|
|
897
899
|
|
|
900
|
+
describe('supportsBedrockToolCache', () => {
|
|
901
|
+
test('returns true for Claude model ids (incl. cross-region + profile)', () => {
|
|
902
|
+
expect(
|
|
903
|
+
supportsBedrockToolCache('anthropic.claude-3-5-sonnet-20241022-v2:0')
|
|
904
|
+
).toBe(true);
|
|
905
|
+
expect(
|
|
906
|
+
supportsBedrockToolCache('us.anthropic.claude-sonnet-4-5-20250929-v1:0')
|
|
907
|
+
).toBe(true);
|
|
908
|
+
expect(supportsBedrockToolCache('anthropic.claude-opus-4-6-v1')).toBe(true);
|
|
909
|
+
});
|
|
910
|
+
|
|
911
|
+
test('returns false for Nova and other non-Claude families (tool cache only)', () => {
|
|
912
|
+
// Nova accepts system/message cachePoints but rejects the tool checkpoint.
|
|
913
|
+
expect(supportsBedrockToolCache('us.amazon.nova-pro-v1:0')).toBe(false);
|
|
914
|
+
expect(supportsBedrockToolCache('amazon.nova-lite-v1:0')).toBe(false);
|
|
915
|
+
expect(supportsBedrockToolCache('meta.llama3-1-70b-instruct-v1:0')).toBe(
|
|
916
|
+
false
|
|
917
|
+
);
|
|
918
|
+
expect(supportsBedrockToolCache('mistral.mistral-large-2407-v1:0')).toBe(
|
|
919
|
+
false
|
|
920
|
+
);
|
|
921
|
+
});
|
|
922
|
+
|
|
923
|
+
test('returns false for empty / missing model', () => {
|
|
924
|
+
expect(supportsBedrockToolCache('')).toBe(false);
|
|
925
|
+
expect(supportsBedrockToolCache(undefined)).toBe(false);
|
|
926
|
+
expect(supportsBedrockToolCache(null)).toBe(false);
|
|
927
|
+
});
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
describe('resolveBedrockPromptCacheTtl', () => {
|
|
931
|
+
test('Claude: defaults to 1h and honors an explicit ttl', () => {
|
|
932
|
+
const claude = 'us.anthropic.claude-sonnet-4-5-20250929-v1:0';
|
|
933
|
+
expect(resolveBedrockPromptCacheTtl(undefined, claude)).toBe('1h');
|
|
934
|
+
expect(resolveBedrockPromptCacheTtl('1h', claude)).toBe('1h');
|
|
935
|
+
expect(resolveBedrockPromptCacheTtl('5m', claude)).toBe('5m');
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
test('Nova: clamps to 5m even when 1h is configured (extended TTL Anthropic-only)', () => {
|
|
939
|
+
const nova = 'us.amazon.nova-lite-v1:0';
|
|
940
|
+
expect(resolveBedrockPromptCacheTtl(undefined, nova)).toBe('5m');
|
|
941
|
+
expect(resolveBedrockPromptCacheTtl('1h', nova)).toBe('5m');
|
|
942
|
+
expect(resolveBedrockPromptCacheTtl('5m', nova)).toBe('5m');
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
test('other non-Claude families clamp to 5m', () => {
|
|
946
|
+
expect(
|
|
947
|
+
resolveBedrockPromptCacheTtl('1h', 'meta.llama3-1-70b-instruct-v1:0')
|
|
948
|
+
).toBe('5m');
|
|
949
|
+
expect(
|
|
950
|
+
resolveBedrockPromptCacheTtl('1h', 'mistral.mistral-large-2407-v1:0')
|
|
951
|
+
).toBe('5m');
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
test('omitted model defaults to Claude behavior (1h)', () => {
|
|
955
|
+
expect(resolveBedrockPromptCacheTtl(undefined, undefined)).toBe('1h');
|
|
956
|
+
expect(resolveBedrockPromptCacheTtl(undefined, null)).toBe('1h');
|
|
957
|
+
expect(resolveBedrockPromptCacheTtl('1h', undefined)).toBe('1h');
|
|
958
|
+
});
|
|
959
|
+
});
|
|
960
|
+
|
|
898
961
|
describe('stripAnthropicCacheControl', () => {
|
|
899
962
|
it('removes cache_control fields from content blocks', () => {
|
|
900
963
|
const messages: TestMsg[] = [
|
package/src/messages/cache.ts
CHANGED
|
@@ -79,6 +79,57 @@ export function buildBedrockCachePoint(
|
|
|
79
79
|
return ttl === '1h' ? { type: 'default', ttl: '1h' } : { type: 'default' };
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Whether a Bedrock model id is an Anthropic Claude model. Claude is the only
|
|
84
|
+
* Bedrock family that accepts the explicit cache features below; Amazon Nova and
|
|
85
|
+
* others reject them. Matches both the `anthropic.` and cross-region
|
|
86
|
+
* (`us.anthropic.`) / bare `claude` forms.
|
|
87
|
+
*/
|
|
88
|
+
function isBedrockAnthropicModel(model: string | undefined | null): boolean {
|
|
89
|
+
return typeof model === 'string' && /claude|anthropic/i.test(model);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* A `cachePoint` under `toolConfig.tools` is only accepted on Anthropic Claude
|
|
94
|
+
* models. Amazon Nova rejects it outright — `Malformed input request:
|
|
95
|
+
* #/toolConfig/tools/0: extraneous key [cachePoint] is not permitted` — even
|
|
96
|
+
* though it accepts `cachePoint` in `system` and `messages` (verified live:
|
|
97
|
+
* Nova returns HTTP 200 with real `cacheWriteInputTokens` for both). Per the AWS
|
|
98
|
+
* prompt-caching docs the support table lists `tools` for Claude only.
|
|
99
|
+
*
|
|
100
|
+
* Gate ONLY the tool checkpoint on this, so a `promptCache: true` config on Nova
|
|
101
|
+
* keeps its valid message/system caching instead of either 400-ing (tool point)
|
|
102
|
+
* or losing caching entirely.
|
|
103
|
+
*
|
|
104
|
+
* @see https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html
|
|
105
|
+
* @see https://github.com/danny-avila/LibreChat/issues/13838
|
|
106
|
+
*/
|
|
107
|
+
export function supportsBedrockToolCache(
|
|
108
|
+
model: string | undefined | null
|
|
109
|
+
): boolean {
|
|
110
|
+
return isBedrockAnthropicModel(model);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Resolve the Bedrock `cachePoint` TTL for a model. The extended `1h` TTL is
|
|
115
|
+
* Anthropic-only: non-Claude models (e.g. Nova) hard-reject it with
|
|
116
|
+
* `Extended TTL prompt caching is only supported for Anthropic models`, so they
|
|
117
|
+
* are clamped to `5m` even when `1h` is configured (verified live). An omitted
|
|
118
|
+
* model defaults to a Claude model, and Claude downgrades an unsupported `1h` to
|
|
119
|
+
* `5m` server-side, so the `1h` default is safe for the Claude/undefined paths.
|
|
120
|
+
*
|
|
121
|
+
* @see https://github.com/danny-avila/LibreChat/issues/13838
|
|
122
|
+
*/
|
|
123
|
+
export function resolveBedrockPromptCacheTtl(
|
|
124
|
+
ttl: PromptCacheTtl | undefined,
|
|
125
|
+
model: string | undefined | null
|
|
126
|
+
): PromptCacheTtl {
|
|
127
|
+
if (model != null && !isBedrockAnthropicModel(model)) {
|
|
128
|
+
return '5m';
|
|
129
|
+
}
|
|
130
|
+
return resolvePromptCacheTtl(ttl);
|
|
131
|
+
}
|
|
132
|
+
|
|
82
133
|
/**
|
|
83
134
|
* Deep clones a message's content to prevent mutation of the original.
|
|
84
135
|
*/
|