illuma-agents 1.0.10 → 1.0.12
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 +236 -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 +44 -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/bedrock/index.cjs +121 -6
- package/dist/cjs/llm/bedrock/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 +236 -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 +45 -19
- 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/bedrock/index.mjs +121 -6
- package/dist/esm/llm/bedrock/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 +72 -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/bedrock/index.d.ts +31 -4
- 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 +14 -1
- package/dist/types/types/tools.d.ts +138 -0
- package/package.json +7 -8
- package/src/agents/AgentContext.ts +278 -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 +57 -19
- package/src/index.ts +2 -0
- package/src/instrumentation.ts +1 -4
- package/src/llm/bedrock/__tests__/bedrock-caching.test.ts +473 -0
- package/src/llm/bedrock/index.ts +149 -12
- 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 +14 -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
|
@@ -25,6 +25,8 @@ export type ToolNodeOptions = {
|
|
|
25
25
|
loadRuntimeTools?: ToolRefGenerator;
|
|
26
26
|
toolCallStepIds?: Map<string, string>;
|
|
27
27
|
errorHandler?: (data: ToolErrorData, metadata?: Record<string, unknown>) => Promise<void>;
|
|
28
|
+
/** Tool registry for lazy computation of programmatic tools and tool search */
|
|
29
|
+
toolRegistry?: LCToolRegistry;
|
|
28
30
|
};
|
|
29
31
|
export type ToolNodeConstructorParams = ToolRefs & ToolNodeOptions;
|
|
30
32
|
export type ToolEndEvent = {
|
|
@@ -59,3 +61,139 @@ export type ExecuteResult = {
|
|
|
59
61
|
stderr: string;
|
|
60
62
|
files?: FileRefs;
|
|
61
63
|
};
|
|
64
|
+
/** JSON Schema type definition for tool parameters */
|
|
65
|
+
export type JsonSchemaType = {
|
|
66
|
+
type: 'string' | 'number' | 'integer' | 'float' | 'boolean' | 'array' | 'object';
|
|
67
|
+
enum?: string[];
|
|
68
|
+
items?: JsonSchemaType;
|
|
69
|
+
properties?: Record<string, JsonSchemaType>;
|
|
70
|
+
required?: string[];
|
|
71
|
+
description?: string;
|
|
72
|
+
additionalProperties?: boolean | JsonSchemaType;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Specifies which contexts can invoke a tool (inspired by Anthropic's allowed_callers)
|
|
76
|
+
* - 'direct': Only callable directly by the LLM (default if omitted)
|
|
77
|
+
* - 'code_execution': Only callable from within programmatic code execution
|
|
78
|
+
*/
|
|
79
|
+
export type AllowedCaller = 'direct' | 'code_execution';
|
|
80
|
+
/** Tool definition with optional deferred loading and caller restrictions */
|
|
81
|
+
export type LCTool = {
|
|
82
|
+
name: string;
|
|
83
|
+
description?: string;
|
|
84
|
+
parameters?: JsonSchemaType;
|
|
85
|
+
/** When true, tool is not loaded into context initially (for tool search) */
|
|
86
|
+
defer_loading?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Which contexts can invoke this tool.
|
|
89
|
+
* Default: ['direct'] (only callable directly by LLM)
|
|
90
|
+
* Options: 'direct', 'code_execution'
|
|
91
|
+
*/
|
|
92
|
+
allowed_callers?: AllowedCaller[];
|
|
93
|
+
};
|
|
94
|
+
/** Map of tool names to tool definitions */
|
|
95
|
+
export type LCToolRegistry = Map<string, LCTool>;
|
|
96
|
+
export type ProgrammaticCache = {
|
|
97
|
+
toolMap: ToolMap;
|
|
98
|
+
toolDefs: LCTool[];
|
|
99
|
+
};
|
|
100
|
+
/** Parameters for creating a Tool Search Regex tool */
|
|
101
|
+
export type ToolSearchRegexParams = {
|
|
102
|
+
apiKey?: string;
|
|
103
|
+
toolRegistry?: LCToolRegistry;
|
|
104
|
+
onlyDeferred?: boolean;
|
|
105
|
+
baseUrl?: string;
|
|
106
|
+
[key: string]: unknown;
|
|
107
|
+
};
|
|
108
|
+
/** Simplified tool metadata for search purposes */
|
|
109
|
+
export type ToolMetadata = {
|
|
110
|
+
name: string;
|
|
111
|
+
description: string;
|
|
112
|
+
parameters?: JsonSchemaType;
|
|
113
|
+
};
|
|
114
|
+
/** Individual search result for a matching tool */
|
|
115
|
+
export type ToolSearchResult = {
|
|
116
|
+
tool_name: string;
|
|
117
|
+
match_score: number;
|
|
118
|
+
matched_field: string;
|
|
119
|
+
snippet: string;
|
|
120
|
+
};
|
|
121
|
+
/** Response from the tool search operation */
|
|
122
|
+
export type ToolSearchResponse = {
|
|
123
|
+
tool_references: ToolSearchResult[];
|
|
124
|
+
total_tools_searched: number;
|
|
125
|
+
pattern_used: string;
|
|
126
|
+
};
|
|
127
|
+
/** Artifact returned alongside the formatted search results */
|
|
128
|
+
export type ToolSearchArtifact = {
|
|
129
|
+
tool_references: ToolSearchResult[];
|
|
130
|
+
metadata: {
|
|
131
|
+
total_searched: number;
|
|
132
|
+
pattern: string;
|
|
133
|
+
error?: string;
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Tool call requested by the Code API during programmatic execution
|
|
138
|
+
*/
|
|
139
|
+
export type PTCToolCall = {
|
|
140
|
+
/** Unique ID like "call_001" */
|
|
141
|
+
id: string;
|
|
142
|
+
/** Tool name */
|
|
143
|
+
name: string;
|
|
144
|
+
/** Input parameters */
|
|
145
|
+
input: Record<string, any>;
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Tool result sent back to the Code API
|
|
149
|
+
*/
|
|
150
|
+
export type PTCToolResult = {
|
|
151
|
+
/** Matches PTCToolCall.id */
|
|
152
|
+
call_id: string;
|
|
153
|
+
/** Tool execution result (any JSON-serializable value) */
|
|
154
|
+
result: any;
|
|
155
|
+
/** Whether tool execution failed */
|
|
156
|
+
is_error: boolean;
|
|
157
|
+
/** Error details if is_error=true */
|
|
158
|
+
error_message?: string;
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Response from the Code API for programmatic execution
|
|
162
|
+
*/
|
|
163
|
+
export type ProgrammaticExecutionResponse = {
|
|
164
|
+
status: 'tool_call_required' | 'completed' | 'error' | unknown;
|
|
165
|
+
session_id?: string;
|
|
166
|
+
/** Present when status='tool_call_required' */
|
|
167
|
+
continuation_token?: string;
|
|
168
|
+
tool_calls?: PTCToolCall[];
|
|
169
|
+
/** Present when status='completed' */
|
|
170
|
+
stdout?: string;
|
|
171
|
+
stderr?: string;
|
|
172
|
+
files?: FileRefs;
|
|
173
|
+
/** Present when status='error' */
|
|
174
|
+
error?: string;
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* Artifact returned by the PTC tool
|
|
178
|
+
*/
|
|
179
|
+
export type ProgrammaticExecutionArtifact = {
|
|
180
|
+
session_id?: string;
|
|
181
|
+
files?: FileRefs;
|
|
182
|
+
};
|
|
183
|
+
/**
|
|
184
|
+
* Initialization parameters for the PTC tool
|
|
185
|
+
*/
|
|
186
|
+
export type ProgrammaticToolCallingParams = {
|
|
187
|
+
/** Code API key (or use CODE_API_KEY env var) */
|
|
188
|
+
apiKey?: string;
|
|
189
|
+
/** Code API base URL (or use CODE_BASEURL env var) */
|
|
190
|
+
baseUrl?: string;
|
|
191
|
+
/** Safety limit for round-trips (default: 20) */
|
|
192
|
+
maxRoundTrips?: number;
|
|
193
|
+
/** HTTP proxy URL */
|
|
194
|
+
proxy?: string;
|
|
195
|
+
/** Enable debug logging (or set PTC_DEBUG=true env var) */
|
|
196
|
+
debug?: boolean;
|
|
197
|
+
/** Environment variable key for API key */
|
|
198
|
+
[key: string]: unknown;
|
|
199
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "illuma-agents",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
4
4
|
"main": "./dist/cjs/main.cjs",
|
|
5
5
|
"module": "./dist/esm/main.mjs",
|
|
6
6
|
"types": "./dist/types/index.d.ts",
|
|
@@ -15,12 +15,6 @@
|
|
|
15
15
|
"description": "Illuma AI Agents Library",
|
|
16
16
|
"author": "Varun Muppidi",
|
|
17
17
|
"license": "MIT",
|
|
18
|
-
"licenses": [
|
|
19
|
-
{
|
|
20
|
-
"type": "MIT",
|
|
21
|
-
"url": "https://github.com/danny-avila/agents/blob/main/LICENSE"
|
|
22
|
-
}
|
|
23
|
-
],
|
|
24
18
|
"packageManager": "npm@10.5.2",
|
|
25
19
|
"engines": {
|
|
26
20
|
"node": ">=14.0.0"
|
|
@@ -34,7 +28,7 @@
|
|
|
34
28
|
"scripts": {
|
|
35
29
|
"prepare": "node husky-setup.js",
|
|
36
30
|
"prepublishOnly": "npm run build",
|
|
37
|
-
"build": "
|
|
31
|
+
"build": "cross-env NODE_ENV=production rollup -c && tsc -p tsconfig.build.json",
|
|
38
32
|
"build:dev": "rollup -c",
|
|
39
33
|
"start": "node dist/esm/main.js",
|
|
40
34
|
"clean": "node ./config/clean.js",
|
|
@@ -53,6 +47,10 @@
|
|
|
53
47
|
"memory": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/memory.ts --provider 'openAI' --name 'Jo' --location 'New York, NY'",
|
|
54
48
|
"tool": "node --trace-warnings -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/tools.ts --provider 'openrouter' --name 'Jo' --location 'New York, NY'",
|
|
55
49
|
"search": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/search.ts --provider 'bedrock' --name 'Jo' --location 'New York, NY'",
|
|
50
|
+
"tool_search_regex": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/tool_search_regex.ts",
|
|
51
|
+
"programmatic_exec": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/programmatic_exec.ts",
|
|
52
|
+
"code_exec_ptc": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/code_exec_ptc.ts --provider 'openAI' --name 'Jo' --location 'New York, NY'",
|
|
53
|
+
"programmatic_exec_agent": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/programmatic_exec_agent.ts --provider 'openAI' --name 'Jo' --location 'New York, NY'",
|
|
56
54
|
"ant_web_search": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/ant_web_search.ts --name 'Jo' --location 'New York, NY'",
|
|
57
55
|
"ant_web_search_edge_case": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/ant_web_search_edge_case.ts --name 'Jo' --location 'New York, NY'",
|
|
58
56
|
"ant_web_search_error_edge_case": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/ant_web_search_error_edge_case.ts --name 'Jo' --location 'New York, NY'",
|
|
@@ -140,6 +138,7 @@
|
|
|
140
138
|
"@types/yargs-parser": "^21.0.3",
|
|
141
139
|
"@typescript-eslint/eslint-plugin": "^8.24.0",
|
|
142
140
|
"@typescript-eslint/parser": "^8.24.0",
|
|
141
|
+
"cross-env": "^10.1.0",
|
|
143
142
|
"eslint": "^9.39.1",
|
|
144
143
|
"eslint-import-resolver-typescript": "^3.7.0",
|
|
145
144
|
"eslint-plugin-import": "^2.31.0",
|
|
@@ -32,12 +32,14 @@ export class AgentContext {
|
|
|
32
32
|
tools,
|
|
33
33
|
toolMap,
|
|
34
34
|
toolEnd,
|
|
35
|
+
toolRegistry,
|
|
35
36
|
instructions,
|
|
36
37
|
additional_instructions,
|
|
37
38
|
streamBuffer,
|
|
38
39
|
maxContextTokens,
|
|
39
40
|
reasoningKey,
|
|
40
41
|
useLegacyContent,
|
|
42
|
+
dynamicContext,
|
|
41
43
|
} = agentConfig;
|
|
42
44
|
|
|
43
45
|
const agentContext = new AgentContext({
|
|
@@ -48,6 +50,7 @@ export class AgentContext {
|
|
|
48
50
|
streamBuffer,
|
|
49
51
|
tools,
|
|
50
52
|
toolMap,
|
|
53
|
+
toolRegistry,
|
|
51
54
|
instructions,
|
|
52
55
|
additionalInstructions: additional_instructions,
|
|
53
56
|
reasoningKey,
|
|
@@ -55,15 +58,21 @@ export class AgentContext {
|
|
|
55
58
|
instructionTokens: 0,
|
|
56
59
|
tokenCounter,
|
|
57
60
|
useLegacyContent,
|
|
61
|
+
dynamicContext,
|
|
58
62
|
});
|
|
59
63
|
|
|
60
64
|
if (tokenCounter) {
|
|
65
|
+
// Initialize system runnable BEFORE async tool token calculation
|
|
66
|
+
// This ensures system message tokens are in instructionTokens before
|
|
67
|
+
// updateTokenMapWithInstructions is called
|
|
68
|
+
agentContext.initializeSystemRunnable();
|
|
69
|
+
|
|
61
70
|
const tokenMap = indexTokenCountMap || {};
|
|
62
71
|
agentContext.indexTokenCountMap = tokenMap;
|
|
63
72
|
agentContext.tokenCalculationPromise = agentContext
|
|
64
73
|
.calculateInstructionTokens(tokenCounter)
|
|
65
74
|
.then(() => {
|
|
66
|
-
// Update token map with instruction tokens
|
|
75
|
+
// Update token map with instruction tokens (includes system + tool tokens)
|
|
67
76
|
agentContext.updateTokenMapWithInstructions(tokenMap);
|
|
68
77
|
})
|
|
69
78
|
.catch((err) => {
|
|
@@ -102,10 +111,23 @@ export class AgentContext {
|
|
|
102
111
|
tools?: t.GraphTools;
|
|
103
112
|
/** Tool map for this agent */
|
|
104
113
|
toolMap?: t.ToolMap;
|
|
114
|
+
/**
|
|
115
|
+
* Tool definitions registry (includes deferred and programmatic tool metadata).
|
|
116
|
+
* Used for tool search and programmatic tool calling.
|
|
117
|
+
*/
|
|
118
|
+
toolRegistry?: t.LCToolRegistry;
|
|
119
|
+
/** Set of tool names discovered via tool search (to be loaded) */
|
|
120
|
+
discoveredToolNames: Set<string> = new Set();
|
|
105
121
|
/** Instructions for this agent */
|
|
106
122
|
instructions?: string;
|
|
107
123
|
/** Additional instructions for this agent */
|
|
108
124
|
additionalInstructions?: string;
|
|
125
|
+
/**
|
|
126
|
+
* Dynamic context that changes per-request (e.g., current time, user info).
|
|
127
|
+
* This is NOT included in the system message to preserve cache.
|
|
128
|
+
* Instead, it's injected as a user message at the start of the conversation.
|
|
129
|
+
*/
|
|
130
|
+
dynamicContext?: string;
|
|
109
131
|
/** Reasoning key for this agent */
|
|
110
132
|
reasoningKey: 'reasoning_content' | 'reasoning' = 'reasoning_content';
|
|
111
133
|
/** Last token for reasoning detection */
|
|
@@ -117,12 +139,16 @@ export class AgentContext {
|
|
|
117
139
|
ContentTypes.TEXT;
|
|
118
140
|
/** Whether tools should end the workflow */
|
|
119
141
|
toolEnd: boolean = false;
|
|
120
|
-
/**
|
|
121
|
-
|
|
142
|
+
/** Cached system runnable (created lazily) */
|
|
143
|
+
private cachedSystemRunnable?: Runnable<
|
|
122
144
|
BaseMessage[],
|
|
123
145
|
(BaseMessage | SystemMessage)[],
|
|
124
146
|
RunnableConfig<Record<string, unknown>>
|
|
125
147
|
>;
|
|
148
|
+
/** Whether system runnable needs rebuild (set when discovered tools change) */
|
|
149
|
+
private systemRunnableStale: boolean = true;
|
|
150
|
+
/** Cached system message token count (separate from tool tokens) */
|
|
151
|
+
private systemMessageTokens: number = 0;
|
|
126
152
|
/** Promise for token calculation initialization */
|
|
127
153
|
tokenCalculationPromise?: Promise<void>;
|
|
128
154
|
/** Format content blocks as strings (for legacy compatibility) */
|
|
@@ -137,8 +163,10 @@ export class AgentContext {
|
|
|
137
163
|
tokenCounter,
|
|
138
164
|
tools,
|
|
139
165
|
toolMap,
|
|
166
|
+
toolRegistry,
|
|
140
167
|
instructions,
|
|
141
168
|
additionalInstructions,
|
|
169
|
+
dynamicContext,
|
|
142
170
|
reasoningKey,
|
|
143
171
|
toolEnd,
|
|
144
172
|
instructionTokens,
|
|
@@ -152,8 +180,10 @@ export class AgentContext {
|
|
|
152
180
|
tokenCounter?: t.TokenCounter;
|
|
153
181
|
tools?: t.GraphTools;
|
|
154
182
|
toolMap?: t.ToolMap;
|
|
183
|
+
toolRegistry?: t.LCToolRegistry;
|
|
155
184
|
instructions?: string;
|
|
156
185
|
additionalInstructions?: string;
|
|
186
|
+
dynamicContext?: string;
|
|
157
187
|
reasoningKey?: 'reasoning_content' | 'reasoning';
|
|
158
188
|
toolEnd?: boolean;
|
|
159
189
|
instructionTokens?: number;
|
|
@@ -167,8 +197,10 @@ export class AgentContext {
|
|
|
167
197
|
this.tokenCounter = tokenCounter;
|
|
168
198
|
this.tools = tools;
|
|
169
199
|
this.toolMap = toolMap;
|
|
200
|
+
this.toolRegistry = toolRegistry;
|
|
170
201
|
this.instructions = instructions;
|
|
171
202
|
this.additionalInstructions = additionalInstructions;
|
|
203
|
+
this.dynamicContext = dynamicContext;
|
|
172
204
|
if (reasoningKey) {
|
|
173
205
|
this.reasoningKey = reasoningKey;
|
|
174
206
|
}
|
|
@@ -180,39 +212,145 @@ export class AgentContext {
|
|
|
180
212
|
}
|
|
181
213
|
|
|
182
214
|
this.useLegacyContent = useLegacyContent ?? false;
|
|
215
|
+
}
|
|
183
216
|
|
|
184
|
-
|
|
217
|
+
/**
|
|
218
|
+
* Builds instructions text for tools that are ONLY callable via programmatic code execution.
|
|
219
|
+
* These tools cannot be called directly by the LLM but are available through the
|
|
220
|
+
* run_tools_with_code tool.
|
|
221
|
+
*
|
|
222
|
+
* Includes:
|
|
223
|
+
* - Code_execution-only tools that are NOT deferred
|
|
224
|
+
* - Code_execution-only tools that ARE deferred but have been discovered via tool search
|
|
225
|
+
*/
|
|
226
|
+
private buildProgrammaticOnlyToolsInstructions(): string {
|
|
227
|
+
if (!this.toolRegistry) return '';
|
|
228
|
+
|
|
229
|
+
const programmaticOnlyTools: t.LCTool[] = [];
|
|
230
|
+
for (const [name, toolDef] of this.toolRegistry) {
|
|
231
|
+
const allowedCallers = toolDef.allowed_callers ?? ['direct'];
|
|
232
|
+
const isCodeExecutionOnly =
|
|
233
|
+
allowedCallers.includes('code_execution') &&
|
|
234
|
+
!allowedCallers.includes('direct');
|
|
235
|
+
|
|
236
|
+
if (!isCodeExecutionOnly) continue;
|
|
237
|
+
|
|
238
|
+
// Include if: not deferred OR deferred but discovered
|
|
239
|
+
const isDeferred = toolDef.defer_loading === true;
|
|
240
|
+
const isDiscovered = this.discoveredToolNames.has(name);
|
|
241
|
+
if (!isDeferred || isDiscovered) {
|
|
242
|
+
programmaticOnlyTools.push(toolDef);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (programmaticOnlyTools.length === 0) return '';
|
|
247
|
+
|
|
248
|
+
const toolDescriptions = programmaticOnlyTools
|
|
249
|
+
.map((tool) => {
|
|
250
|
+
let desc = `- **${tool.name}**`;
|
|
251
|
+
if (tool.description != null && tool.description !== '') {
|
|
252
|
+
desc += `: ${tool.description}`;
|
|
253
|
+
}
|
|
254
|
+
if (tool.parameters) {
|
|
255
|
+
desc += `\n Parameters: ${JSON.stringify(tool.parameters, null, 2).replace(/\n/g, '\n ')}`;
|
|
256
|
+
}
|
|
257
|
+
return desc;
|
|
258
|
+
})
|
|
259
|
+
.join('\n\n');
|
|
260
|
+
|
|
261
|
+
return (
|
|
262
|
+
'\n\n## Programmatic-Only Tools\n\n' +
|
|
263
|
+
'The following tools are available exclusively through the `run_tools_with_code` tool. ' +
|
|
264
|
+
'You cannot call these tools directly; instead, use `run_tools_with_code` with Python code that invokes them.\n\n' +
|
|
265
|
+
toolDescriptions
|
|
266
|
+
);
|
|
185
267
|
}
|
|
186
268
|
|
|
187
269
|
/**
|
|
188
|
-
*
|
|
270
|
+
* Gets the system runnable, creating it lazily if needed.
|
|
271
|
+
* Includes instructions, additional instructions, and programmatic-only tools documentation.
|
|
272
|
+
* Only rebuilds when marked stale (via markToolsAsDiscovered).
|
|
189
273
|
*/
|
|
190
|
-
|
|
274
|
+
get systemRunnable():
|
|
191
275
|
| Runnable<
|
|
192
276
|
BaseMessage[],
|
|
193
277
|
(BaseMessage | SystemMessage)[],
|
|
194
278
|
RunnableConfig<Record<string, unknown>>
|
|
195
279
|
>
|
|
196
280
|
| undefined {
|
|
197
|
-
|
|
198
|
-
|
|
281
|
+
// Return cached if not stale
|
|
282
|
+
if (!this.systemRunnableStale && this.cachedSystemRunnable !== undefined) {
|
|
283
|
+
return this.cachedSystemRunnable;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Stale or first access - rebuild
|
|
287
|
+
const instructionsString = this.buildInstructionsString();
|
|
288
|
+
this.cachedSystemRunnable = this.buildSystemRunnable(instructionsString);
|
|
289
|
+
this.systemRunnableStale = false;
|
|
290
|
+
return this.cachedSystemRunnable;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Explicitly initializes the system runnable.
|
|
295
|
+
* Call this before async token calculation to ensure system message tokens are counted first.
|
|
296
|
+
*/
|
|
297
|
+
initializeSystemRunnable(): void {
|
|
298
|
+
if (this.systemRunnableStale || this.cachedSystemRunnable === undefined) {
|
|
299
|
+
const instructionsString = this.buildInstructionsString();
|
|
300
|
+
this.cachedSystemRunnable = this.buildSystemRunnable(instructionsString);
|
|
301
|
+
this.systemRunnableStale = false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Builds the raw instructions string (without creating SystemMessage).
|
|
307
|
+
*/
|
|
308
|
+
private buildInstructionsString(): string {
|
|
309
|
+
let result = this.instructions ?? '';
|
|
199
310
|
|
|
200
311
|
if (
|
|
201
312
|
this.additionalInstructions != null &&
|
|
202
313
|
this.additionalInstructions !== ''
|
|
203
314
|
) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
: this.additionalInstructions;
|
|
315
|
+
result = result
|
|
316
|
+
? `${result}\n\n${this.additionalInstructions}`
|
|
317
|
+
: this.additionalInstructions;
|
|
208
318
|
}
|
|
209
319
|
|
|
210
|
-
|
|
211
|
-
if (
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
320
|
+
const programmaticToolsDoc = this.buildProgrammaticOnlyToolsInstructions();
|
|
321
|
+
if (programmaticToolsDoc) {
|
|
322
|
+
result = result
|
|
323
|
+
? `${result}${programmaticToolsDoc}`
|
|
324
|
+
: programmaticToolsDoc;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return result;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Build system runnable from pre-built instructions string.
|
|
332
|
+
* Only called when content has actually changed.
|
|
333
|
+
*/
|
|
334
|
+
private buildSystemRunnable(
|
|
335
|
+
instructionsString: string
|
|
336
|
+
):
|
|
337
|
+
| Runnable<
|
|
338
|
+
BaseMessage[],
|
|
339
|
+
(BaseMessage | SystemMessage)[],
|
|
340
|
+
RunnableConfig<Record<string, unknown>>
|
|
341
|
+
>
|
|
342
|
+
| undefined {
|
|
343
|
+
if (!instructionsString) {
|
|
344
|
+
// Remove previous tokens if we had a system message before
|
|
345
|
+
this.instructionTokens -= this.systemMessageTokens;
|
|
346
|
+
this.systemMessageTokens = 0;
|
|
347
|
+
return undefined;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
let finalInstructions: string | BaseMessageFields = instructionsString;
|
|
351
|
+
|
|
352
|
+
// Handle Anthropic prompt caching (Direct API)
|
|
353
|
+
if (this.provider === Providers.ANTHROPIC) {
|
|
216
354
|
const anthropicOptions = this.clientOptions as
|
|
217
355
|
| t.AnthropicClientOptions
|
|
218
356
|
| undefined;
|
|
@@ -228,7 +366,7 @@ export class AgentContext {
|
|
|
228
366
|
content: [
|
|
229
367
|
{
|
|
230
368
|
type: 'text',
|
|
231
|
-
text:
|
|
369
|
+
text: instructionsString,
|
|
232
370
|
cache_control: { type: 'ephemeral' },
|
|
233
371
|
},
|
|
234
372
|
],
|
|
@@ -236,19 +374,47 @@ export class AgentContext {
|
|
|
236
374
|
}
|
|
237
375
|
}
|
|
238
376
|
|
|
239
|
-
|
|
240
|
-
|
|
377
|
+
// Handle Bedrock prompt caching (Converse API)
|
|
378
|
+
// Adds cachePoint block after text content for system message caching
|
|
379
|
+
// NOTE: Both Claude and Nova models support cachePoint in system and messages
|
|
380
|
+
// (Nova does NOT support cachePoint in tools - that check is in bedrock/index.ts)
|
|
381
|
+
if (this.provider === Providers.BEDROCK) {
|
|
382
|
+
const bedrockOptions = this.clientOptions as
|
|
383
|
+
| t.BedrockAnthropicInput
|
|
384
|
+
| undefined;
|
|
385
|
+
const modelId = bedrockOptions?.model?.toLowerCase() ?? '';
|
|
386
|
+
const supportsCaching = modelId.includes('claude') || modelId.includes('anthropic') || modelId.includes('nova');
|
|
241
387
|
|
|
242
|
-
if (
|
|
243
|
-
|
|
388
|
+
if (bedrockOptions?.promptCache === true && supportsCaching) {
|
|
389
|
+
// Always log system cache structure
|
|
390
|
+
console.log(`[Cache] 📝 System | chars=${instructionsString.length} | tokens=${this.systemMessageTokens} | model=${modelId}`);
|
|
391
|
+
|
|
392
|
+
finalInstructions = {
|
|
393
|
+
content: [
|
|
394
|
+
{
|
|
395
|
+
type: 'text',
|
|
396
|
+
text: instructionsString,
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
cachePoint: { type: 'default' },
|
|
400
|
+
},
|
|
401
|
+
],
|
|
402
|
+
};
|
|
244
403
|
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const systemMessage = new SystemMessage(finalInstructions);
|
|
245
407
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
408
|
+
// Update token counts (subtract old, add new)
|
|
409
|
+
if (this.tokenCounter) {
|
|
410
|
+
this.instructionTokens -= this.systemMessageTokens;
|
|
411
|
+
this.systemMessageTokens = this.tokenCounter(systemMessage);
|
|
412
|
+
this.instructionTokens += this.systemMessageTokens;
|
|
249
413
|
}
|
|
250
414
|
|
|
251
|
-
return
|
|
415
|
+
return RunnableLambda.from((messages: BaseMessage[]) => {
|
|
416
|
+
return [systemMessage, ...messages];
|
|
417
|
+
}).withConfig({ runName: 'prompt' });
|
|
252
418
|
}
|
|
253
419
|
|
|
254
420
|
/**
|
|
@@ -256,6 +422,9 @@ export class AgentContext {
|
|
|
256
422
|
*/
|
|
257
423
|
reset(): void {
|
|
258
424
|
this.instructionTokens = 0;
|
|
425
|
+
this.systemMessageTokens = 0;
|
|
426
|
+
this.cachedSystemRunnable = undefined;
|
|
427
|
+
this.systemRunnableStale = true;
|
|
259
428
|
this.lastToken = undefined;
|
|
260
429
|
this.indexTokenCountMap = {};
|
|
261
430
|
this.currentUsage = undefined;
|
|
@@ -263,6 +432,7 @@ export class AgentContext {
|
|
|
263
432
|
this.lastStreamCall = undefined;
|
|
264
433
|
this.tokenTypeSwitch = undefined;
|
|
265
434
|
this.currentTokenType = ContentTypes.TEXT;
|
|
435
|
+
this.discoveredToolNames.clear();
|
|
266
436
|
}
|
|
267
437
|
|
|
268
438
|
/**
|
|
@@ -320,4 +490,85 @@ export class AgentContext {
|
|
|
320
490
|
// Add tool tokens to existing instruction tokens (which may already include system message tokens)
|
|
321
491
|
this.instructionTokens += toolTokens;
|
|
322
492
|
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Gets the tool registry for deferred tools (for tool search).
|
|
496
|
+
* @param onlyDeferred If true, only returns tools with defer_loading=true
|
|
497
|
+
* @returns LCToolRegistry with tool definitions
|
|
498
|
+
*/
|
|
499
|
+
getDeferredToolRegistry(onlyDeferred: boolean = true): t.LCToolRegistry {
|
|
500
|
+
const registry: t.LCToolRegistry = new Map();
|
|
501
|
+
|
|
502
|
+
if (!this.toolRegistry) {
|
|
503
|
+
return registry;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
for (const [name, toolDef] of this.toolRegistry) {
|
|
507
|
+
if (!onlyDeferred || toolDef.defer_loading === true) {
|
|
508
|
+
registry.set(name, toolDef);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return registry;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Marks tools as discovered via tool search.
|
|
517
|
+
* Discovered tools will be included in the next model binding.
|
|
518
|
+
* Only marks system runnable stale if NEW tools were actually added.
|
|
519
|
+
* @param toolNames - Array of discovered tool names
|
|
520
|
+
* @returns true if any new tools were discovered
|
|
521
|
+
*/
|
|
522
|
+
markToolsAsDiscovered(toolNames: string[]): boolean {
|
|
523
|
+
let hasNewDiscoveries = false;
|
|
524
|
+
for (const name of toolNames) {
|
|
525
|
+
if (!this.discoveredToolNames.has(name)) {
|
|
526
|
+
this.discoveredToolNames.add(name);
|
|
527
|
+
hasNewDiscoveries = true;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
if (hasNewDiscoveries) {
|
|
531
|
+
this.systemRunnableStale = true;
|
|
532
|
+
}
|
|
533
|
+
return hasNewDiscoveries;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Gets tools that should be bound to the LLM.
|
|
538
|
+
* Includes:
|
|
539
|
+
* 1. Non-deferred tools with allowed_callers: ['direct']
|
|
540
|
+
* 2. Discovered tools (from tool search)
|
|
541
|
+
* @returns Array of tools to bind to model
|
|
542
|
+
*/
|
|
543
|
+
getToolsForBinding(): t.GraphTools | undefined {
|
|
544
|
+
if (!this.tools || !this.toolRegistry) {
|
|
545
|
+
return this.tools;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const toolsToInclude = this.tools.filter((tool) => {
|
|
549
|
+
if (!('name' in tool)) {
|
|
550
|
+
return true; // No name, include by default
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
const toolDef = this.toolRegistry?.get(tool.name);
|
|
554
|
+
if (!toolDef) {
|
|
555
|
+
return true; // Not in registry, include by default
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Check if discovered (overrides defer_loading)
|
|
559
|
+
if (this.discoveredToolNames.has(tool.name)) {
|
|
560
|
+
// Discovered tools must still have allowed_callers: ['direct']
|
|
561
|
+
const allowedCallers = toolDef.allowed_callers ?? ['direct'];
|
|
562
|
+
return allowedCallers.includes('direct');
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Not discovered: must be direct-callable AND not deferred
|
|
566
|
+
const allowedCallers = toolDef.allowed_callers ?? ['direct'];
|
|
567
|
+
return (
|
|
568
|
+
allowedCallers.includes('direct') && toolDef.defer_loading !== true
|
|
569
|
+
);
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
return toolsToInclude;
|
|
573
|
+
}
|
|
323
574
|
}
|