illuma-agents 1.0.10 → 1.0.11
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/LICENSE +1 -1
- package/dist/cjs/agents/AgentContext.cjs +228 -27
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/common/enum.cjs +2 -0
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/events.cjs +3 -11
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +27 -18
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/instrumentation.cjs +1 -3
- package/dist/cjs/instrumentation.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/index.cjs +1 -1
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/index.cjs +122 -7
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/llm/google/index.cjs +1 -1
- package/dist/cjs/llm/google/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +6 -6
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/index.cjs +1 -1
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +18 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/cache.cjs +149 -54
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/messages/tools.cjs +85 -0
- package/dist/cjs/messages/tools.cjs.map +1 -0
- package/dist/cjs/run.cjs +0 -8
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +4 -0
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +438 -0
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -0
- package/dist/cjs/tools/ToolNode.cjs +53 -15
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearchRegex.cjs +455 -0
- package/dist/cjs/tools/ToolSearchRegex.cjs.map +1 -0
- package/dist/cjs/tools/search/schema.cjs +7 -9
- package/dist/cjs/tools/search/schema.cjs.map +1 -1
- package/dist/cjs/utils/run.cjs +5 -1
- package/dist/cjs/utils/run.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +228 -27
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/common/enum.mjs +2 -0
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/events.mjs +4 -12
- package/dist/esm/events.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +27 -18
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/instrumentation.mjs +1 -3
- package/dist/esm/instrumentation.mjs.map +1 -1
- package/dist/esm/llm/anthropic/index.mjs +1 -1
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs +122 -7
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/llm/google/index.mjs +1 -1
- package/dist/esm/llm/google/index.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +6 -6
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/index.mjs +1 -1
- package/dist/esm/llm/openrouter/index.mjs.map +1 -1
- package/dist/esm/main.mjs +3 -0
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/messages/cache.mjs +149 -54
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/messages/tools.mjs +82 -0
- package/dist/esm/messages/tools.mjs.map +1 -0
- package/dist/esm/run.mjs +0 -8
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +4 -0
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +430 -0
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -0
- package/dist/esm/tools/ToolNode.mjs +53 -15
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/ToolSearchRegex.mjs +448 -0
- package/dist/esm/tools/ToolSearchRegex.mjs.map +1 -0
- package/dist/esm/tools/search/schema.mjs +7 -9
- package/dist/esm/tools/search/schema.mjs.map +1 -1
- package/dist/esm/utils/run.mjs +5 -1
- package/dist/esm/utils/run.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +65 -5
- package/dist/types/common/enum.d.ts +2 -0
- package/dist/types/graphs/Graph.d.ts +3 -2
- package/dist/types/index.d.ts +2 -0
- package/dist/types/llm/anthropic/index.d.ts +1 -1
- package/dist/types/llm/bedrock/index.d.ts +31 -4
- package/dist/types/llm/google/index.d.ts +1 -1
- package/dist/types/llm/openai/index.d.ts +3 -3
- package/dist/types/llm/openrouter/index.d.ts +1 -1
- package/dist/types/messages/cache.d.ts +23 -8
- package/dist/types/messages/index.d.ts +1 -0
- package/dist/types/messages/tools.d.ts +17 -0
- package/dist/types/test/mockTools.d.ts +28 -0
- package/dist/types/tools/ProgrammaticToolCalling.d.ts +91 -0
- package/dist/types/tools/ToolNode.d.ts +10 -2
- package/dist/types/tools/ToolSearchRegex.d.ts +80 -0
- package/dist/types/types/graph.d.ts +7 -1
- package/dist/types/types/tools.d.ts +138 -0
- package/package.json +7 -2
- package/src/agents/AgentContext.ts +267 -27
- package/src/agents/__tests__/AgentContext.test.ts +805 -0
- package/src/common/enum.ts +2 -0
- package/src/events.ts +5 -12
- package/src/graphs/Graph.ts +33 -19
- package/src/index.ts +2 -0
- package/src/instrumentation.ts +1 -4
- package/src/llm/anthropic/index.ts +2 -2
- package/src/llm/bedrock/__tests__/bedrock-caching.test.ts +473 -0
- package/src/llm/bedrock/index.ts +150 -13
- package/src/llm/google/index.ts +2 -2
- package/src/llm/openai/index.ts +9 -9
- package/src/llm/openrouter/index.ts +2 -2
- package/src/messages/__tests__/tools.test.ts +473 -0
- package/src/messages/cache.ts +163 -61
- package/src/messages/index.ts +1 -0
- package/src/messages/tools.ts +99 -0
- package/src/run.ts +0 -9
- package/src/scripts/code_exec_ptc.ts +334 -0
- package/src/scripts/image.ts +178 -0
- package/src/scripts/programmatic_exec.ts +396 -0
- package/src/scripts/programmatic_exec_agent.ts +231 -0
- package/src/scripts/test-tools-before-handoff.ts +5 -1
- package/src/scripts/tool_search_regex.ts +162 -0
- package/src/scripts/tools.ts +4 -1
- package/src/specs/thinking-prune.test.ts +52 -118
- package/src/test/mockTools.ts +366 -0
- package/src/tools/CodeExecutor.ts +4 -0
- package/src/tools/ProgrammaticToolCalling.ts +558 -0
- package/src/tools/ToolNode.ts +59 -18
- package/src/tools/ToolSearchRegex.ts +535 -0
- package/src/tools/__tests__/ProgrammaticToolCalling.integration.test.ts +318 -0
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +853 -0
- package/src/tools/__tests__/ToolSearchRegex.integration.test.ts +161 -0
- package/src/tools/__tests__/ToolSearchRegex.test.ts +232 -0
- package/src/tools/search/jina-reranker.test.ts +16 -16
- package/src/tools/search/schema.ts +7 -9
- package/src/types/graph.ts +7 -1
- package/src/types/tools.ts +166 -0
- package/src/utils/run.ts +5 -1
- package/src/tools/search/direct-url.test.ts +0 -530
package/src/common/enum.ts
CHANGED
|
@@ -159,6 +159,8 @@ export enum Callback {
|
|
|
159
159
|
export enum Constants {
|
|
160
160
|
OFFICIAL_CODE_BASEURL = 'https://api.illuma.ai/v1',
|
|
161
161
|
EXECUTE_CODE = 'execute_code',
|
|
162
|
+
TOOL_SEARCH_REGEX = 'tool_search_regex',
|
|
163
|
+
PROGRAMMATIC_TOOL_CALLING = 'run_tools_with_code',
|
|
162
164
|
WEB_SEARCH = 'web_search',
|
|
163
165
|
CONTENT_AND_ARTIFACT = 'content_and_artifact',
|
|
164
166
|
LC_TRANSFER_TO_ = 'lc_transfer_to_',
|
package/src/events.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type { MultiAgentGraph, StandardGraph } from '@/graphs';
|
|
|
9
9
|
import type { Logger } from 'winston';
|
|
10
10
|
import type * as t from '@/types';
|
|
11
11
|
import { handleToolCalls } from '@/tools/handlers';
|
|
12
|
-
import { Providers } from '@/common';
|
|
12
|
+
import { Constants, Providers } from '@/common';
|
|
13
13
|
|
|
14
14
|
export class HandlerRegistry {
|
|
15
15
|
private handlers: Map<string, t.EventHandler> = new Map();
|
|
@@ -92,17 +92,6 @@ export class ToolEndHandler implements t.EventHandler {
|
|
|
92
92
|
metadata?: Record<string, unknown>,
|
|
93
93
|
graph?: StandardGraph | MultiAgentGraph
|
|
94
94
|
): Promise<void> {
|
|
95
|
-
const toolData = data as t.ToolEndData;
|
|
96
|
-
const output = toolData?.output as ToolMessage | undefined;
|
|
97
|
-
console.log('[ToolEndHandler] handle called', {
|
|
98
|
-
event,
|
|
99
|
-
hasData: !!data,
|
|
100
|
-
hasGraph: !!graph,
|
|
101
|
-
hasMetadata: !!metadata,
|
|
102
|
-
hasCallback: !!this.callback,
|
|
103
|
-
outputName: output?.name,
|
|
104
|
-
hasArtifact: !!output?.artifact,
|
|
105
|
-
});
|
|
106
95
|
try {
|
|
107
96
|
if (!graph || !metadata) {
|
|
108
97
|
if (this.logger) {
|
|
@@ -123,6 +112,10 @@ export class ToolEndHandler implements t.EventHandler {
|
|
|
123
112
|
return;
|
|
124
113
|
}
|
|
125
114
|
|
|
115
|
+
if (metadata[Constants.PROGRAMMATIC_TOOL_CALLING] === true) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
126
119
|
if (this.callback) {
|
|
127
120
|
await this.callback(toolEndData, metadata);
|
|
128
121
|
}
|
package/src/graphs/Graph.ts
CHANGED
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
formatContentStrings,
|
|
47
47
|
createPruneMessages,
|
|
48
48
|
addCacheControl,
|
|
49
|
+
extractToolDiscoveries,
|
|
49
50
|
} from '@/messages';
|
|
50
51
|
import {
|
|
51
52
|
resetIfNotEmpty,
|
|
@@ -443,9 +444,11 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
443
444
|
initializeTools({
|
|
444
445
|
currentTools,
|
|
445
446
|
currentToolMap,
|
|
447
|
+
agentContext,
|
|
446
448
|
}: {
|
|
447
449
|
currentTools?: t.GraphTools;
|
|
448
450
|
currentToolMap?: t.ToolMap;
|
|
451
|
+
agentContext?: AgentContext;
|
|
449
452
|
}): CustomToolNode<t.BaseGraphState> | ToolNode<t.BaseGraphState> {
|
|
450
453
|
return new CustomToolNode<t.BaseGraphState>({
|
|
451
454
|
tools: (currentTools as t.GenericTool[] | undefined) ?? [],
|
|
@@ -453,6 +456,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
453
456
|
toolCallStepIds: this.toolCallStepIds,
|
|
454
457
|
errorHandler: (data, metadata) =>
|
|
455
458
|
StandardGraph.handleToolCallErrorStatic(this, data, metadata),
|
|
459
|
+
toolRegistry: agentContext?.toolRegistry,
|
|
456
460
|
});
|
|
457
461
|
}
|
|
458
462
|
|
|
@@ -603,7 +607,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
603
607
|
client.abortHandler = undefined;
|
|
604
608
|
}
|
|
605
609
|
|
|
606
|
-
createCallModel(agentId = 'default'
|
|
610
|
+
createCallModel(agentId = 'default') {
|
|
607
611
|
return async (
|
|
608
612
|
state: t.BaseGraphState,
|
|
609
613
|
config?: RunnableConfig
|
|
@@ -616,15 +620,31 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
616
620
|
throw new Error(`Agent context not found for agentId: ${agentId}`);
|
|
617
621
|
}
|
|
618
622
|
|
|
619
|
-
const model = this.overrideModel ?? currentModel;
|
|
620
|
-
if (!model) {
|
|
621
|
-
throw new Error('No Graph model found');
|
|
622
|
-
}
|
|
623
623
|
if (!config) {
|
|
624
624
|
throw new Error('No config provided');
|
|
625
625
|
}
|
|
626
626
|
|
|
627
|
-
|
|
627
|
+
const { messages } = state;
|
|
628
|
+
|
|
629
|
+
// Extract tool discoveries from current turn only (similar to formatArtifactPayload pattern)
|
|
630
|
+
const discoveredNames = extractToolDiscoveries(messages);
|
|
631
|
+
if (discoveredNames.length > 0) {
|
|
632
|
+
agentContext.markToolsAsDiscovered(discoveredNames);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
const toolsForBinding = agentContext.getToolsForBinding();
|
|
636
|
+
let model =
|
|
637
|
+
this.overrideModel ??
|
|
638
|
+
this.initializeModel({
|
|
639
|
+
tools: toolsForBinding,
|
|
640
|
+
provider: agentContext.provider,
|
|
641
|
+
clientOptions: agentContext.clientOptions,
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
if (agentContext.systemRunnable) {
|
|
645
|
+
model = agentContext.systemRunnable.pipe(model as Runnable);
|
|
646
|
+
}
|
|
647
|
+
|
|
628
648
|
if (agentContext.tokenCalculationPromise) {
|
|
629
649
|
await agentContext.tokenCalculationPromise;
|
|
630
650
|
}
|
|
@@ -632,7 +652,6 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
632
652
|
config.signal = this.signal;
|
|
633
653
|
}
|
|
634
654
|
this.config = config;
|
|
635
|
-
const { messages } = state;
|
|
636
655
|
|
|
637
656
|
let messagesToUse = messages;
|
|
638
657
|
if (
|
|
@@ -730,7 +749,11 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
730
749
|
const bedrockOptions = agentContext.clientOptions as
|
|
731
750
|
| t.BedrockAnthropicClientOptions
|
|
732
751
|
| undefined;
|
|
733
|
-
|
|
752
|
+
// Both Claude and Nova models support cachePoint in system and messages
|
|
753
|
+
// (Llama, Titan, and other models do NOT support cachePoint)
|
|
754
|
+
const modelId = bedrockOptions?.model?.toLowerCase() ?? '';
|
|
755
|
+
const supportsCaching = modelId.includes('claude') || modelId.includes('anthropic') || modelId.includes('nova');
|
|
756
|
+
if (bedrockOptions?.promptCache === true && supportsCaching) {
|
|
734
757
|
finalMessages = addBedrockCacheControl<BaseMessage>(finalMessages);
|
|
735
758
|
}
|
|
736
759
|
}
|
|
@@ -844,16 +867,6 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
844
867
|
throw new Error(`Agent context not found for agentId: ${agentId}`);
|
|
845
868
|
}
|
|
846
869
|
|
|
847
|
-
let currentModel = this.initializeModel({
|
|
848
|
-
tools: agentContext.tools,
|
|
849
|
-
provider: agentContext.provider,
|
|
850
|
-
clientOptions: agentContext.clientOptions,
|
|
851
|
-
});
|
|
852
|
-
|
|
853
|
-
if (agentContext.systemRunnable) {
|
|
854
|
-
currentModel = agentContext.systemRunnable.pipe(currentModel);
|
|
855
|
-
}
|
|
856
|
-
|
|
857
870
|
const agentNode = `${AGENT}${agentId}` as const;
|
|
858
871
|
const toolNode = `${TOOLS}${agentId}` as const;
|
|
859
872
|
|
|
@@ -873,12 +886,13 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
873
886
|
});
|
|
874
887
|
|
|
875
888
|
const workflow = new StateGraph(StateAnnotation)
|
|
876
|
-
.addNode(agentNode, this.createCallModel(agentId
|
|
889
|
+
.addNode(agentNode, this.createCallModel(agentId))
|
|
877
890
|
.addNode(
|
|
878
891
|
toolNode,
|
|
879
892
|
this.initializeTools({
|
|
880
893
|
currentTools: agentContext.tools,
|
|
881
894
|
currentToolMap: agentContext.toolMap,
|
|
895
|
+
agentContext,
|
|
882
896
|
})
|
|
883
897
|
)
|
|
884
898
|
.addEdge(START, agentNode)
|
package/src/index.ts
CHANGED
|
@@ -11,6 +11,8 @@ export * from './graphs';
|
|
|
11
11
|
/* Tools */
|
|
12
12
|
export * from './tools/Calculator';
|
|
13
13
|
export * from './tools/CodeExecutor';
|
|
14
|
+
export * from './tools/ProgrammaticToolCalling';
|
|
15
|
+
export * from './tools/ToolSearchRegex';
|
|
14
16
|
export * from './tools/handlers';
|
|
15
17
|
export * from './tools/search';
|
|
16
18
|
|
package/src/instrumentation.ts
CHANGED
|
@@ -11,10 +11,7 @@ if (
|
|
|
11
11
|
publicKey: process.env.LANGFUSE_PUBLIC_KEY,
|
|
12
12
|
secretKey: process.env.LANGFUSE_SECRET_KEY,
|
|
13
13
|
baseUrl: process.env.LANGFUSE_BASE_URL,
|
|
14
|
-
environment:
|
|
15
|
-
process.env.LANGFUSE_TRACING_ENVIRONMENT ??
|
|
16
|
-
process.env.NODE_ENV ??
|
|
17
|
-
'development',
|
|
14
|
+
environment: process.env.LANGFUSE_TRACING_ENVIRONMENT ?? process.env.NODE_ENV ?? 'development',
|
|
18
15
|
});
|
|
19
16
|
|
|
20
17
|
const sdk = new NodeSDK({
|
|
@@ -143,8 +143,8 @@ export class CustomAnthropic extends ChatAnthropicMessages {
|
|
|
143
143
|
this._lc_stream_delay = fields?._lc_stream_delay ?? 25;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
static lc_name(): '
|
|
147
|
-
return '
|
|
146
|
+
static lc_name(): 'LibreChatAnthropic' {
|
|
147
|
+
return 'LibreChatAnthropic';
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
/**
|
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for Bedrock Prompt Caching functionality
|
|
3
|
+
*
|
|
4
|
+
* Tests cover:
|
|
5
|
+
* 1. CustomChatBedrockConverse - Tool caching with cachePoint
|
|
6
|
+
* 2. AgentContext - System message caching with cachePoint for Bedrock
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { CustomChatBedrockConverse } from '../index';
|
|
10
|
+
import { AgentContext } from '@/agents/AgentContext';
|
|
11
|
+
import { Providers } from '@/common';
|
|
12
|
+
import type * as t from '@/types';
|
|
13
|
+
|
|
14
|
+
describe('Bedrock Prompt Caching', () => {
|
|
15
|
+
describe('CustomChatBedrockConverse - Tool Caching', () => {
|
|
16
|
+
describe('invocationParams with promptCache enabled', () => {
|
|
17
|
+
it('should add cachePoint to tools array when promptCache is true', () => {
|
|
18
|
+
const model = new CustomChatBedrockConverse({
|
|
19
|
+
model: 'anthropic.claude-3-haiku-20240307-v1:0',
|
|
20
|
+
region: 'us-east-1',
|
|
21
|
+
promptCache: true,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const mockTools = [
|
|
25
|
+
{
|
|
26
|
+
toolSpec: {
|
|
27
|
+
name: 'get_weather',
|
|
28
|
+
description: 'Get weather for a location',
|
|
29
|
+
inputSchema: { json: { type: 'object', properties: {} } },
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
toolSpec: {
|
|
34
|
+
name: 'search_web',
|
|
35
|
+
description: 'Search the web',
|
|
36
|
+
inputSchema: { json: { type: 'object', properties: {} } },
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const params = model.invocationParams({
|
|
42
|
+
tools: mockTools,
|
|
43
|
+
} as any);
|
|
44
|
+
|
|
45
|
+
// Should have tools + cachePoint
|
|
46
|
+
expect(params.toolConfig?.tools).toHaveLength(3);
|
|
47
|
+
expect(params.toolConfig?.tools?.[2]).toEqual({
|
|
48
|
+
cachePoint: { type: 'default' },
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should NOT add cachePoint when promptCache is false', () => {
|
|
53
|
+
const model = new CustomChatBedrockConverse({
|
|
54
|
+
model: 'anthropic.claude-3-haiku-20240307-v1:0',
|
|
55
|
+
region: 'us-east-1',
|
|
56
|
+
promptCache: false,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const mockTools = [
|
|
60
|
+
{
|
|
61
|
+
toolSpec: {
|
|
62
|
+
name: 'get_weather',
|
|
63
|
+
description: 'Get weather',
|
|
64
|
+
inputSchema: { json: { type: 'object', properties: {} } },
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const params = model.invocationParams({
|
|
70
|
+
tools: mockTools,
|
|
71
|
+
} as any);
|
|
72
|
+
|
|
73
|
+
// Should only have original tools, no cachePoint
|
|
74
|
+
expect(params.toolConfig?.tools).toHaveLength(1);
|
|
75
|
+
expect(params.toolConfig?.tools?.[0]).toEqual(mockTools[0]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should NOT add cachePoint when promptCache is undefined (default)', () => {
|
|
79
|
+
const model = new CustomChatBedrockConverse({
|
|
80
|
+
model: 'anthropic.claude-3-haiku-20240307-v1:0',
|
|
81
|
+
region: 'us-east-1',
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const mockTools = [
|
|
85
|
+
{
|
|
86
|
+
toolSpec: {
|
|
87
|
+
name: 'get_weather',
|
|
88
|
+
description: 'Get weather',
|
|
89
|
+
inputSchema: { json: { type: 'object', properties: {} } },
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
const params = model.invocationParams({
|
|
95
|
+
tools: mockTools,
|
|
96
|
+
} as any);
|
|
97
|
+
|
|
98
|
+
// Should only have original tools
|
|
99
|
+
expect(params.toolConfig?.tools).toHaveLength(1);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should NOT add cachePoint when no tools are provided', () => {
|
|
103
|
+
const model = new CustomChatBedrockConverse({
|
|
104
|
+
model: 'anthropic.claude-3-haiku-20240307-v1:0',
|
|
105
|
+
region: 'us-east-1',
|
|
106
|
+
promptCache: true,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const params = model.invocationParams({} as any);
|
|
110
|
+
|
|
111
|
+
// toolConfig should be undefined or have no tools
|
|
112
|
+
expect(params.toolConfig?.tools).toBeUndefined();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should NOT add cachePoint when tools array is empty', () => {
|
|
116
|
+
const model = new CustomChatBedrockConverse({
|
|
117
|
+
model: 'anthropic.claude-3-haiku-20240307-v1:0',
|
|
118
|
+
region: 'us-east-1',
|
|
119
|
+
promptCache: true,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const params = model.invocationParams({
|
|
123
|
+
tools: [],
|
|
124
|
+
} as any);
|
|
125
|
+
|
|
126
|
+
// Empty tools array results in undefined toolConfig
|
|
127
|
+
expect(params.toolConfig).toBeUndefined();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should preserve other invocationParams properties', () => {
|
|
131
|
+
const model = new CustomChatBedrockConverse({
|
|
132
|
+
model: 'anthropic.claude-3-haiku-20240307-v1:0',
|
|
133
|
+
region: 'us-east-1',
|
|
134
|
+
promptCache: true,
|
|
135
|
+
temperature: 0.7,
|
|
136
|
+
maxTokens: 1000,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const mockTools = [
|
|
140
|
+
{
|
|
141
|
+
toolSpec: {
|
|
142
|
+
name: 'test_tool',
|
|
143
|
+
description: 'Test',
|
|
144
|
+
inputSchema: { json: { type: 'object', properties: {} } },
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
];
|
|
148
|
+
|
|
149
|
+
const params = model.invocationParams({
|
|
150
|
+
tools: mockTools,
|
|
151
|
+
} as any);
|
|
152
|
+
|
|
153
|
+
// Check that other params are preserved
|
|
154
|
+
expect(params.inferenceConfig?.temperature).toBe(0.7);
|
|
155
|
+
expect(params.inferenceConfig?.maxTokens).toBe(1000);
|
|
156
|
+
// And cachePoint is still added
|
|
157
|
+
expect(params.toolConfig?.tools).toHaveLength(2);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe('promptCache property', () => {
|
|
162
|
+
it('should store promptCache value from constructor', () => {
|
|
163
|
+
const modelWithCache = new CustomChatBedrockConverse({
|
|
164
|
+
model: 'test-model',
|
|
165
|
+
region: 'us-east-1',
|
|
166
|
+
promptCache: true,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const modelWithoutCache = new CustomChatBedrockConverse({
|
|
170
|
+
model: 'test-model',
|
|
171
|
+
region: 'us-east-1',
|
|
172
|
+
promptCache: false,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
expect(modelWithCache.promptCache).toBe(true);
|
|
176
|
+
expect(modelWithoutCache.promptCache).toBe(false);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should default promptCache to false when not provided', () => {
|
|
180
|
+
const model = new CustomChatBedrockConverse({
|
|
181
|
+
model: 'test-model',
|
|
182
|
+
region: 'us-east-1',
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
expect(model.promptCache).toBe(false);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe('AgentContext - System Message Caching', () => {
|
|
191
|
+
const createBedrockContext = (options: {
|
|
192
|
+
instructions?: string;
|
|
193
|
+
promptCache?: boolean;
|
|
194
|
+
additionalInstructions?: string;
|
|
195
|
+
}): AgentContext => {
|
|
196
|
+
const clientOptions: t.BedrockAnthropicInput = {
|
|
197
|
+
model: 'anthropic.claude-3-haiku-20240307-v1:0',
|
|
198
|
+
region: 'us-east-1',
|
|
199
|
+
promptCache: options.promptCache,
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
return AgentContext.fromConfig({
|
|
203
|
+
agentId: 'test-bedrock-agent',
|
|
204
|
+
provider: Providers.BEDROCK,
|
|
205
|
+
instructions: options.instructions,
|
|
206
|
+
additional_instructions: options.additionalInstructions,
|
|
207
|
+
clientOptions,
|
|
208
|
+
});
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const createAnthropicContext = (options: {
|
|
212
|
+
instructions?: string;
|
|
213
|
+
promptCachingEnabled?: boolean;
|
|
214
|
+
}): AgentContext => {
|
|
215
|
+
const clientOptions: t.AnthropicClientOptions = {
|
|
216
|
+
clientOptions: {
|
|
217
|
+
defaultHeaders: options.promptCachingEnabled
|
|
218
|
+
? { 'anthropic-beta': 'prompt-caching-2024-07-31' }
|
|
219
|
+
: undefined,
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
return AgentContext.fromConfig({
|
|
224
|
+
agentId: 'test-anthropic-agent',
|
|
225
|
+
provider: Providers.ANTHROPIC,
|
|
226
|
+
instructions: options.instructions,
|
|
227
|
+
clientOptions,
|
|
228
|
+
});
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
describe('Bedrock system message with promptCache: true', () => {
|
|
232
|
+
it('should add cachePoint to system message content', async () => {
|
|
233
|
+
const ctx = createBedrockContext({
|
|
234
|
+
instructions: 'You are a helpful assistant.',
|
|
235
|
+
promptCache: true,
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const systemRunnable = ctx.systemRunnable;
|
|
239
|
+
expect(systemRunnable).toBeDefined();
|
|
240
|
+
|
|
241
|
+
// Invoke the runnable to get the messages
|
|
242
|
+
const result = await systemRunnable!.invoke([]);
|
|
243
|
+
const systemMessage = result[0];
|
|
244
|
+
|
|
245
|
+
// Check content structure has cachePoint
|
|
246
|
+
expect(systemMessage.content).toBeInstanceOf(Array);
|
|
247
|
+
const content = systemMessage.content as Array<Record<string, unknown>>;
|
|
248
|
+
|
|
249
|
+
expect(content).toHaveLength(2);
|
|
250
|
+
expect(content[0]).toEqual({
|
|
251
|
+
type: 'text',
|
|
252
|
+
text: 'You are a helpful assistant.',
|
|
253
|
+
});
|
|
254
|
+
expect(content[1]).toEqual({
|
|
255
|
+
cachePoint: { type: 'default' },
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('should include combined instructions and additional_instructions with cachePoint', async () => {
|
|
260
|
+
const ctx = createBedrockContext({
|
|
261
|
+
instructions: 'Base instructions.',
|
|
262
|
+
additionalInstructions: 'Additional context.',
|
|
263
|
+
promptCache: true,
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
const result = await ctx.systemRunnable!.invoke([]);
|
|
267
|
+
const systemMessage = result[0];
|
|
268
|
+
const content = systemMessage.content as Array<Record<string, unknown>>;
|
|
269
|
+
|
|
270
|
+
expect(content).toHaveLength(2);
|
|
271
|
+
expect((content[0] as { text: string }).text).toContain('Base instructions.');
|
|
272
|
+
expect((content[0] as { text: string }).text).toContain('Additional context.');
|
|
273
|
+
expect(content[1]).toEqual({
|
|
274
|
+
cachePoint: { type: 'default' },
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
describe('Bedrock system message with promptCache: false', () => {
|
|
280
|
+
it('should NOT add cachePoint when promptCache is false', async () => {
|
|
281
|
+
const ctx = createBedrockContext({
|
|
282
|
+
instructions: 'You are a helpful assistant.',
|
|
283
|
+
promptCache: false,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
const result = await ctx.systemRunnable!.invoke([]);
|
|
287
|
+
const systemMessage = result[0];
|
|
288
|
+
|
|
289
|
+
// Content should be plain string, not array with cachePoint
|
|
290
|
+
expect(typeof systemMessage.content).toBe('string');
|
|
291
|
+
expect(systemMessage.content).toBe('You are a helpful assistant.');
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('should NOT add cachePoint when promptCache is undefined', async () => {
|
|
295
|
+
const ctx = createBedrockContext({
|
|
296
|
+
instructions: 'You are a helpful assistant.',
|
|
297
|
+
promptCache: undefined,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
const result = await ctx.systemRunnable!.invoke([]);
|
|
301
|
+
const systemMessage = result[0];
|
|
302
|
+
|
|
303
|
+
expect(typeof systemMessage.content).toBe('string');
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
describe('Anthropic system message caching (for comparison)', () => {
|
|
308
|
+
it('should add cache_control for Anthropic with prompt-caching beta', async () => {
|
|
309
|
+
const ctx = createAnthropicContext({
|
|
310
|
+
instructions: 'You are a helpful assistant.',
|
|
311
|
+
promptCachingEnabled: true,
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
const result = await ctx.systemRunnable!.invoke([]);
|
|
315
|
+
const systemMessage = result[0];
|
|
316
|
+
const content = systemMessage.content as Array<Record<string, unknown>>;
|
|
317
|
+
|
|
318
|
+
expect(content).toHaveLength(1);
|
|
319
|
+
expect(content[0]).toEqual({
|
|
320
|
+
type: 'text',
|
|
321
|
+
text: 'You are a helpful assistant.',
|
|
322
|
+
cache_control: { type: 'ephemeral' },
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('should NOT add cache_control for Anthropic without beta header', async () => {
|
|
327
|
+
const ctx = createAnthropicContext({
|
|
328
|
+
instructions: 'You are a helpful assistant.',
|
|
329
|
+
promptCachingEnabled: false,
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const result = await ctx.systemRunnable!.invoke([]);
|
|
333
|
+
const systemMessage = result[0];
|
|
334
|
+
|
|
335
|
+
expect(typeof systemMessage.content).toBe('string');
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
describe('Provider-specific caching behavior', () => {
|
|
340
|
+
it('should use cachePoint format for Bedrock, not cache_control', async () => {
|
|
341
|
+
const bedrockCtx = createBedrockContext({
|
|
342
|
+
instructions: 'Test',
|
|
343
|
+
promptCache: true,
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
const result = await bedrockCtx.systemRunnable!.invoke([]);
|
|
347
|
+
const content = result[0].content as Array<Record<string, unknown>>;
|
|
348
|
+
|
|
349
|
+
// Bedrock uses cachePoint, NOT cache_control
|
|
350
|
+
expect(content.some((c) => 'cachePoint' in c)).toBe(true);
|
|
351
|
+
expect(content.some((c) => 'cache_control' in c)).toBe(false);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('should use cache_control format for Anthropic, not cachePoint', async () => {
|
|
355
|
+
const anthropicCtx = createAnthropicContext({
|
|
356
|
+
instructions: 'Test',
|
|
357
|
+
promptCachingEnabled: true,
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
const result = await anthropicCtx.systemRunnable!.invoke([]);
|
|
361
|
+
const content = result[0].content as Array<Record<string, unknown>>;
|
|
362
|
+
|
|
363
|
+
// Anthropic uses cache_control, NOT cachePoint
|
|
364
|
+
expect(content.some((c) => 'cache_control' in c)).toBe(true);
|
|
365
|
+
expect(content.some((c) => 'cachePoint' in c)).toBe(false);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it('should not add any caching for OpenAI provider', async () => {
|
|
369
|
+
const openaiCtx = AgentContext.fromConfig({
|
|
370
|
+
agentId: 'test-openai-agent',
|
|
371
|
+
provider: Providers.OPENAI,
|
|
372
|
+
instructions: 'Test instructions',
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
const result = await openaiCtx.systemRunnable!.invoke([]);
|
|
376
|
+
const systemMessage = result[0];
|
|
377
|
+
|
|
378
|
+
// OpenAI should have plain string content
|
|
379
|
+
expect(typeof systemMessage.content).toBe('string');
|
|
380
|
+
expect(systemMessage.content).toBe('Test instructions');
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
describe('Integration scenarios', () => {
|
|
386
|
+
it('should handle multi-turn caching scenario', async () => {
|
|
387
|
+
// Simulate: Query 1 with tools → Query 2 with same tools
|
|
388
|
+
// Both should have cachePoint, enabling cache reuse
|
|
389
|
+
|
|
390
|
+
const model = new CustomChatBedrockConverse({
|
|
391
|
+
model: 'anthropic.claude-3-haiku-20240307-v1:0',
|
|
392
|
+
region: 'us-east-1',
|
|
393
|
+
promptCache: true,
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
const tools = [
|
|
397
|
+
{
|
|
398
|
+
toolSpec: {
|
|
399
|
+
name: 'execute_code',
|
|
400
|
+
description: 'Execute Python code',
|
|
401
|
+
inputSchema: { json: { type: 'object', properties: {} } },
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
toolSpec: {
|
|
406
|
+
name: 'file_search',
|
|
407
|
+
description: 'Search files',
|
|
408
|
+
inputSchema: { json: { type: 'object', properties: {} } },
|
|
409
|
+
},
|
|
410
|
+
},
|
|
411
|
+
];
|
|
412
|
+
|
|
413
|
+
// Query 1
|
|
414
|
+
const params1 = model.invocationParams({ tools } as any);
|
|
415
|
+
|
|
416
|
+
// Query 2 (same tools)
|
|
417
|
+
const params2 = model.invocationParams({ tools } as any);
|
|
418
|
+
|
|
419
|
+
// Both should have identical tool configs with cachePoint
|
|
420
|
+
expect(params1.toolConfig?.tools).toEqual(params2.toolConfig?.tools);
|
|
421
|
+
expect(params1.toolConfig?.tools).toHaveLength(3); // 2 tools + cachePoint
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it('should handle dynamic tool selection scenario', async () => {
|
|
425
|
+
// Simulate: Query 1 with [A, B] → Query 2 with [A, B, C]
|
|
426
|
+
// Tool set changed, so cache won't match (expected behavior)
|
|
427
|
+
|
|
428
|
+
const model = new CustomChatBedrockConverse({
|
|
429
|
+
model: 'anthropic.claude-3-haiku-20240307-v1:0',
|
|
430
|
+
region: 'us-east-1',
|
|
431
|
+
promptCache: true,
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
const toolA = {
|
|
435
|
+
toolSpec: {
|
|
436
|
+
name: 'tool_a',
|
|
437
|
+
description: 'Tool A',
|
|
438
|
+
inputSchema: { json: { type: 'object', properties: {} } },
|
|
439
|
+
},
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
const toolB = {
|
|
443
|
+
toolSpec: {
|
|
444
|
+
name: 'tool_b',
|
|
445
|
+
description: 'Tool B',
|
|
446
|
+
inputSchema: { json: { type: 'object', properties: {} } },
|
|
447
|
+
},
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
const toolC = {
|
|
451
|
+
toolSpec: {
|
|
452
|
+
name: 'tool_c',
|
|
453
|
+
description: 'Tool C',
|
|
454
|
+
inputSchema: { json: { type: 'object', properties: {} } },
|
|
455
|
+
},
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
// Query 1: [A, B]
|
|
459
|
+
const params1 = model.invocationParams({ tools: [toolA, toolB] } as any);
|
|
460
|
+
|
|
461
|
+
// Query 2: [A, B, C] - different tool set
|
|
462
|
+
const params2 = model.invocationParams({ tools: [toolA, toolB, toolC] } as any);
|
|
463
|
+
|
|
464
|
+
// Different lengths (cache prefix won't match beyond common prefix)
|
|
465
|
+
expect(params1.toolConfig?.tools).toHaveLength(3); // 2 tools + cachePoint
|
|
466
|
+
expect(params2.toolConfig?.tools).toHaveLength(4); // 3 tools + cachePoint
|
|
467
|
+
|
|
468
|
+
// But both have cachePoint at the end
|
|
469
|
+
expect(params1.toolConfig?.tools?.[2]).toEqual({ cachePoint: { type: 'default' } });
|
|
470
|
+
expect(params2.toolConfig?.tools?.[3]).toEqual({ cachePoint: { type: 'default' } });
|
|
471
|
+
});
|
|
472
|
+
});
|
|
473
|
+
});
|