@librechat/agents 3.0.776 → 3.1.1

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.
Files changed (108) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +2 -5
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/graphs/Graph.cjs +20 -5
  4. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  5. package/dist/cjs/graphs/MultiAgentGraph.cjs +26 -17
  6. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  7. package/dist/cjs/llm/bedrock/index.cjs +98 -25
  8. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  9. package/dist/cjs/llm/openai/index.cjs +1 -0
  10. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  11. package/dist/cjs/main.cjs +3 -0
  12. package/dist/cjs/main.cjs.map +1 -1
  13. package/dist/cjs/messages/core.cjs +1 -1
  14. package/dist/cjs/messages/core.cjs.map +1 -1
  15. package/dist/cjs/stream.cjs +4 -2
  16. package/dist/cjs/stream.cjs.map +1 -1
  17. package/dist/cjs/tools/CodeExecutor.cjs +37 -27
  18. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  19. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +21 -17
  20. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  21. package/dist/cjs/tools/ToolNode.cjs +10 -5
  22. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  23. package/dist/cjs/tools/ToolSearch.cjs +37 -30
  24. package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
  25. package/dist/cjs/tools/search/schema.cjs +25 -23
  26. package/dist/cjs/tools/search/schema.cjs.map +1 -1
  27. package/dist/cjs/tools/search/tool.cjs +9 -33
  28. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  29. package/dist/cjs/utils/schema.cjs +27 -0
  30. package/dist/cjs/utils/schema.cjs.map +1 -0
  31. package/dist/cjs/utils/title.cjs +28 -14
  32. package/dist/cjs/utils/title.cjs.map +1 -1
  33. package/dist/esm/agents/AgentContext.mjs +2 -5
  34. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  35. package/dist/esm/graphs/Graph.mjs +20 -5
  36. package/dist/esm/graphs/Graph.mjs.map +1 -1
  37. package/dist/esm/graphs/MultiAgentGraph.mjs +26 -17
  38. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  39. package/dist/esm/llm/bedrock/index.mjs +97 -24
  40. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  41. package/dist/esm/llm/openai/index.mjs +1 -0
  42. package/dist/esm/llm/openai/index.mjs.map +1 -1
  43. package/dist/esm/main.mjs +1 -0
  44. package/dist/esm/main.mjs.map +1 -1
  45. package/dist/esm/messages/core.mjs +1 -1
  46. package/dist/esm/messages/core.mjs.map +1 -1
  47. package/dist/esm/stream.mjs +4 -2
  48. package/dist/esm/stream.mjs.map +1 -1
  49. package/dist/esm/tools/CodeExecutor.mjs +37 -27
  50. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  51. package/dist/esm/tools/ProgrammaticToolCalling.mjs +21 -17
  52. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  53. package/dist/esm/tools/ToolNode.mjs +10 -5
  54. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  55. package/dist/esm/tools/ToolSearch.mjs +37 -30
  56. package/dist/esm/tools/ToolSearch.mjs.map +1 -1
  57. package/dist/esm/tools/search/schema.mjs +25 -23
  58. package/dist/esm/tools/search/schema.mjs.map +1 -1
  59. package/dist/esm/tools/search/tool.mjs +10 -34
  60. package/dist/esm/tools/search/tool.mjs.map +1 -1
  61. package/dist/esm/utils/schema.mjs +24 -0
  62. package/dist/esm/utils/schema.mjs.map +1 -0
  63. package/dist/esm/utils/title.mjs +28 -14
  64. package/dist/esm/utils/title.mjs.map +1 -1
  65. package/dist/types/llm/bedrock/index.d.ts +86 -7
  66. package/dist/types/llm/bedrock/types.d.ts +27 -0
  67. package/dist/types/llm/bedrock/utils/index.d.ts +5 -0
  68. package/dist/types/llm/bedrock/utils/message_inputs.d.ts +31 -0
  69. package/dist/types/llm/bedrock/utils/message_outputs.d.ts +33 -0
  70. package/dist/types/tools/CodeExecutor.d.ts +1 -15
  71. package/dist/types/tools/ProgrammaticToolCalling.d.ts +1 -13
  72. package/dist/types/tools/ToolSearch.d.ts +1 -15
  73. package/dist/types/tools/search/schema.d.ts +25 -7
  74. package/dist/types/tools/search/tool.d.ts +1 -52
  75. package/dist/types/tools/search/types.d.ts +5 -23
  76. package/dist/types/types/tools.d.ts +2 -0
  77. package/dist/types/utils/index.d.ts +1 -0
  78. package/dist/types/utils/schema.d.ts +8 -0
  79. package/package.json +5 -2
  80. package/src/agents/AgentContext.ts +5 -11
  81. package/src/graphs/Graph.ts +23 -5
  82. package/src/graphs/MultiAgentGraph.ts +26 -17
  83. package/src/llm/bedrock/index.ts +180 -43
  84. package/src/llm/bedrock/llm.spec.ts +616 -0
  85. package/src/llm/bedrock/types.ts +51 -0
  86. package/src/llm/bedrock/utils/index.ts +18 -0
  87. package/src/llm/bedrock/utils/message_inputs.ts +563 -0
  88. package/src/llm/bedrock/utils/message_outputs.ts +310 -0
  89. package/src/messages/core.ts +1 -1
  90. package/src/scripts/code_exec_multi_session.ts +241 -0
  91. package/src/scripts/thinking-bedrock.ts +159 -0
  92. package/src/scripts/thinking.ts +39 -18
  93. package/src/scripts/tools.ts +7 -3
  94. package/src/specs/agent-handoffs.test.ts +1 -2
  95. package/src/specs/tool-error.test.ts +7 -2
  96. package/src/stream.ts +4 -2
  97. package/src/test/mockTools.ts +34 -14
  98. package/src/tools/CodeExecutor.ts +48 -31
  99. package/src/tools/ProgrammaticToolCalling.ts +24 -23
  100. package/src/tools/ToolNode.ts +9 -5
  101. package/src/tools/ToolSearch.ts +54 -43
  102. package/src/tools/search/schema.ts +30 -25
  103. package/src/tools/search/tool.ts +23 -16
  104. package/src/tools/search/types.ts +5 -29
  105. package/src/types/tools.ts +2 -0
  106. package/src/utils/index.ts +1 -0
  107. package/src/utils/schema.ts +35 -0
  108. package/src/utils/title.ts +31 -19
@@ -0,0 +1,159 @@
1
+ // src/scripts/thinking-bedrock.ts
2
+ import { config } from 'dotenv';
3
+ config();
4
+ import { HumanMessage, BaseMessage } from '@langchain/core/messages';
5
+ import type { UsageMetadata } from '@langchain/core/messages';
6
+ import * as t from '@/types';
7
+ import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
8
+ import { createCodeExecutionTool } from '@/tools/CodeExecutor';
9
+ import { ToolEndHandler, ModelEndHandler } from '@/events';
10
+ import { GraphEvents, Providers } from '@/common';
11
+ import { getLLMConfig } from '@/utils/llmConfig';
12
+ import { getArgs } from '@/scripts/args';
13
+ import { Run } from '@/run';
14
+
15
+ const conversationHistory: BaseMessage[] = [];
16
+ let _contentParts: t.MessageContentComplex[] = [];
17
+ const collectedUsage: UsageMetadata[] = [];
18
+
19
+ async function testBedrockThinking(): Promise<void> {
20
+ const { userName } = await getArgs();
21
+ const instructions = `You are a helpful AI assistant for ${userName}. When answering questions, be thorough in your reasoning.`;
22
+ const { contentParts, aggregateContent } = createContentAggregator();
23
+ _contentParts = contentParts as t.MessageContentComplex[];
24
+
25
+ // Set up event handlers
26
+ const customHandlers = {
27
+ [GraphEvents.TOOL_END]: new ToolEndHandler(),
28
+ [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(collectedUsage),
29
+ [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
30
+ [GraphEvents.ON_RUN_STEP_COMPLETED]: {
31
+ handle: (
32
+ event: GraphEvents.ON_RUN_STEP_COMPLETED,
33
+ data: t.StreamEventData
34
+ ): void => {
35
+ console.log('====== ON_RUN_STEP_COMPLETED ======');
36
+ aggregateContent({
37
+ event,
38
+ data: data as unknown as { result: t.ToolEndEvent },
39
+ });
40
+ },
41
+ },
42
+ [GraphEvents.ON_RUN_STEP]: {
43
+ handle: (event: GraphEvents.ON_RUN_STEP, data: t.RunStep) => {
44
+ aggregateContent({ event, data });
45
+ },
46
+ },
47
+ [GraphEvents.ON_RUN_STEP_DELTA]: {
48
+ handle: (
49
+ event: GraphEvents.ON_RUN_STEP_DELTA,
50
+ data: t.RunStepDeltaEvent
51
+ ) => {
52
+ aggregateContent({ event, data });
53
+ },
54
+ },
55
+ [GraphEvents.ON_MESSAGE_DELTA]: {
56
+ handle: (
57
+ event: GraphEvents.ON_MESSAGE_DELTA,
58
+ data: t.MessageDeltaEvent
59
+ ) => {
60
+ aggregateContent({ event, data });
61
+ },
62
+ },
63
+ [GraphEvents.ON_REASONING_DELTA]: {
64
+ handle: (
65
+ event: GraphEvents.ON_REASONING_DELTA,
66
+ data: t.ReasoningDeltaEvent
67
+ ) => {
68
+ aggregateContent({ event, data });
69
+ },
70
+ },
71
+ };
72
+
73
+ const baseLlmConfig = getLLMConfig(Providers.BEDROCK);
74
+
75
+ // Enable thinking with token budget for Bedrock
76
+ const llmConfig = {
77
+ ...baseLlmConfig,
78
+ model: 'us.anthropic.claude-3-7-sonnet-20250219-v1:0',
79
+ maxTokens: 5000,
80
+ additionalModelRequestFields: {
81
+ thinking: { type: 'enabled', budget_tokens: 2000 },
82
+ },
83
+ };
84
+
85
+ const run = await Run.create<t.IState>({
86
+ runId: 'test-bedrock-thinking-id',
87
+ graphConfig: {
88
+ instructions,
89
+ type: 'standard',
90
+ tools: [createCodeExecutionTool()],
91
+ llmConfig,
92
+ },
93
+ returnContent: true,
94
+ customHandlers: customHandlers as t.RunConfig['customHandlers'],
95
+ });
96
+
97
+ const config = {
98
+ configurable: {
99
+ thread_id: 'bedrock-thinking-test-thread',
100
+ },
101
+ streamMode: 'values',
102
+ version: 'v2' as const,
103
+ };
104
+
105
+ // Test 1: Regular thinking mode
106
+ console.log('\n\nTest 1: Bedrock Regular thinking mode');
107
+ const userMessage1 = `Please print 'hello world' in python`;
108
+ conversationHistory.push(new HumanMessage(userMessage1));
109
+
110
+ console.log('Running first query with Bedrock thinking enabled...');
111
+ const firstInputs = { messages: [...conversationHistory] };
112
+ await run.processStream(firstInputs, config);
113
+
114
+ // Extract and display thinking blocks
115
+ const finalMessages = run.getRunMessages();
116
+ console.log('\n\nFinal messages after Test 1:');
117
+ console.dir(finalMessages, { depth: null });
118
+
119
+ // Test 2: Try multi-turn conversation
120
+ console.log(
121
+ '\n\nTest 2: Multi-turn conversation with Bedrock thinking enabled'
122
+ );
123
+ const userMessage2 = `Given your previous analysis, what would be the most significant technical challenges in making this transition?`;
124
+ conversationHistory.push(new HumanMessage(userMessage2));
125
+
126
+ console.log('Running second query with Bedrock thinking enabled...');
127
+ const secondInputs = { messages: [...conversationHistory] };
128
+ await run.processStream(secondInputs, config);
129
+
130
+ // Display thinking blocks for second response
131
+ const finalMessages2 = run.getRunMessages();
132
+ console.log('\n\nBedrock thinking feature test completed!');
133
+ console.dir(finalMessages2, { depth: null });
134
+
135
+ console.log('\n\nContent parts:');
136
+ console.dir(_contentParts, { depth: null });
137
+ }
138
+
139
+ process.on('unhandledRejection', (reason, promise) => {
140
+ console.error('Unhandled Rejection at:', promise, 'reason:', reason);
141
+ console.log('Conversation history:');
142
+ console.dir(conversationHistory, { depth: null });
143
+ console.log('Content parts:');
144
+ console.dir(_contentParts, { depth: null });
145
+ process.exit(1);
146
+ });
147
+
148
+ process.on('uncaughtException', (err) => {
149
+ console.error('Uncaught Exception:', err);
150
+ });
151
+
152
+ testBedrockThinking().catch((err) => {
153
+ console.error(err);
154
+ console.log('Conversation history:');
155
+ console.dir(conversationHistory, { depth: null });
156
+ console.log('Content parts:');
157
+ console.dir(_contentParts, { depth: null });
158
+ process.exit(1);
159
+ });
@@ -1,7 +1,11 @@
1
1
  // src/scripts/test-thinking.ts
2
2
  import { config } from 'dotenv';
3
3
  config();
4
- import { HumanMessage, SystemMessage, BaseMessage } from '@langchain/core/messages';
4
+ import {
5
+ HumanMessage,
6
+ SystemMessage,
7
+ BaseMessage,
8
+ } from '@langchain/core/messages';
5
9
  import type { UsageMetadata } from '@langchain/core/messages';
6
10
  import * as t from '@/types';
7
11
  import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
@@ -21,17 +25,23 @@ async function testThinking(): Promise<void> {
21
25
  const instructions = `You are a helpful AI assistant for ${userName}. When answering questions, be thorough in your reasoning.`;
22
26
  const { contentParts, aggregateContent } = createContentAggregator();
23
27
  _contentParts = contentParts as t.MessageContentComplex[];
24
-
28
+
25
29
  // Set up event handlers
26
30
  const customHandlers = {
27
31
  [GraphEvents.TOOL_END]: new ToolEndHandler(),
28
32
  [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(collectedUsage),
29
33
  [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
30
34
  [GraphEvents.ON_RUN_STEP_COMPLETED]: {
31
- handle: (event: GraphEvents.ON_RUN_STEP_COMPLETED, data: t.StreamEventData): void => {
35
+ handle: (
36
+ event: GraphEvents.ON_RUN_STEP_COMPLETED,
37
+ data: t.StreamEventData
38
+ ): void => {
32
39
  console.log('====== ON_RUN_STEP_COMPLETED ======');
33
- aggregateContent({ event, data: data as unknown as { result: t.ToolEndEvent } });
34
- }
40
+ aggregateContent({
41
+ event,
42
+ data: data as unknown as { result: t.ToolEndEvent },
43
+ });
44
+ },
35
45
  },
36
46
  [GraphEvents.ON_RUN_STEP]: {
37
47
  handle: (event: GraphEvents.ON_RUN_STEP, data: t.RunStep) => {
@@ -39,29 +49,38 @@ async function testThinking(): Promise<void> {
39
49
  },
40
50
  },
41
51
  [GraphEvents.ON_RUN_STEP_DELTA]: {
42
- handle: (event: GraphEvents.ON_RUN_STEP_DELTA, data: t.RunStepDeltaEvent) => {
52
+ handle: (
53
+ event: GraphEvents.ON_RUN_STEP_DELTA,
54
+ data: t.RunStepDeltaEvent
55
+ ) => {
43
56
  aggregateContent({ event, data });
44
57
  },
45
58
  },
46
59
  [GraphEvents.ON_MESSAGE_DELTA]: {
47
- handle: (event: GraphEvents.ON_MESSAGE_DELTA, data: t.MessageDeltaEvent) => {
60
+ handle: (
61
+ event: GraphEvents.ON_MESSAGE_DELTA,
62
+ data: t.MessageDeltaEvent
63
+ ) => {
48
64
  aggregateContent({ event, data });
49
65
  },
50
66
  },
51
67
  [GraphEvents.ON_REASONING_DELTA]: {
52
- handle: (event: GraphEvents.ON_REASONING_DELTA, data: t.ReasoningDeltaEvent) => {
68
+ handle: (
69
+ event: GraphEvents.ON_REASONING_DELTA,
70
+ data: t.ReasoningDeltaEvent
71
+ ) => {
53
72
  aggregateContent({ event, data });
54
73
  },
55
74
  },
56
75
  };
57
76
 
58
77
  const baseLlmConfig: t.LLMConfig = getLLMConfig(Providers.ANTHROPIC);
59
-
78
+
60
79
  // Enable thinking with token budget
61
80
  const llmConfig = {
62
81
  ...baseLlmConfig,
63
82
  model: 'claude-3-7-sonnet-latest',
64
- thinking: { type: "enabled", budget_tokens: 2000 }
83
+ thinking: { type: 'enabled', budget_tokens: 2000 },
65
84
  };
66
85
 
67
86
  const run = await Run.create<t.IState>({
@@ -93,7 +112,7 @@ async function testThinking(): Promise<void> {
93
112
  console.log('Running first query with thinking enabled...');
94
113
  const firstInputs = { messages: [...conversationHistory] };
95
114
  await run.processStream(firstInputs, config);
96
-
115
+
97
116
  // Extract and display thinking blocks
98
117
  const finalMessages = run.getRunMessages();
99
118
 
@@ -101,30 +120,32 @@ async function testThinking(): Promise<void> {
101
120
  console.log('\n\nTest 2: Multi-turn conversation with thinking enabled');
102
121
  const userMessage2 = `Given your previous analysis, what would be the most significant technical challenges in making this transition?`;
103
122
  conversationHistory.push(new HumanMessage(userMessage2));
104
-
123
+
105
124
  console.log('Running second query with thinking enabled...');
106
125
  const secondInputs = { messages: [...conversationHistory] };
107
126
  await run.processStream(secondInputs, config);
108
-
127
+
109
128
  // Display thinking blocks for second response
110
129
  const finalMessages2 = run.getRunMessages();
111
130
 
112
131
  // Test 3: Redacted thinking mode
113
132
  console.log('\n\nTest 3: Redacted thinking mode');
114
- const magicString = "ANTHROPIC_MAGIC_STRING_TRIGGER_REDACTED_THINKING_46C9A13E193C177646C7398A98432ECCCE4C1253D5E2D82641AC0E52CC2876CB";
133
+ const magicString =
134
+ 'ANTHROPIC_MAGIC_STRING_TRIGGER_REDACTED_THINKING_46C9A13E193C177646C7398A98432ECCCE4C1253D5E2D82641AC0E52CC2876CB';
115
135
  const userMessage3 = `${magicString}\n\nExplain how quantum computing works in simple terms.`;
116
-
136
+
117
137
  // Reset conversation for clean test
118
138
  conversationHistory.length = 0;
119
139
  conversationHistory.push(new HumanMessage(userMessage3));
120
-
140
+
121
141
  console.log('Running query with redacted thinking...');
122
142
  const thirdInputs = { messages: [...conversationHistory] };
123
143
  await run.processStream(thirdInputs, config);
124
-
144
+
125
145
  // Display redacted thinking blocks
126
146
  const finalMessages3 = run.getRunMessages();
127
147
  console.log('\n\nThinking feature test completed!');
148
+ console.dir(finalMessages3, { depth: null });
128
149
  }
129
150
 
130
151
  process.on('unhandledRejection', (reason, promise) => {
@@ -147,4 +168,4 @@ testThinking().catch((err) => {
147
168
  console.log('Content parts:');
148
169
  console.dir(_contentParts, { depth: null });
149
170
  process.exit(1);
150
- });
171
+ });
@@ -18,9 +18,13 @@ async function testStandardStreaming(): Promise<void> {
18
18
  const { userName, location, provider, currentDate } = await getArgs();
19
19
  const { contentParts, aggregateContent } = createContentAggregator();
20
20
  const customHandlers = {
21
- [GraphEvents.TOOL_END]: new ToolEndHandler(undefined, (name?: string) => {
22
- return true;
23
- }),
21
+ [GraphEvents.TOOL_END]: new ToolEndHandler(
22
+ undefined,
23
+ undefined,
24
+ (name?: string) => {
25
+ return true;
26
+ }
27
+ ),
24
28
  [GraphEvents.CHAT_MODEL_END]: {
25
29
  handle: (
26
30
  _event: string,
@@ -1,5 +1,4 @@
1
1
  // src/specs/agent-handoffs.test.ts
2
- import { z } from 'zod';
3
2
  import { DynamicStructuredTool } from '@langchain/core/tools';
4
3
  import { HumanMessage, ToolMessage } from '@langchain/core/messages';
5
4
  import type { ToolCall } from '@langchain/core/messages/tool';
@@ -708,7 +707,7 @@ describe('Agent Handoffs Tests', () => {
708
707
  const customTool = new DynamicStructuredTool({
709
708
  name: 'custom_tool',
710
709
  description: 'A custom tool',
711
- schema: z.object({}),
710
+ schema: { type: 'object', properties: {}, required: [] },
712
711
  func: async (): Promise<string> => 'Tool result',
713
712
  });
714
713
 
@@ -1,4 +1,3 @@
1
- import { z } from 'zod';
2
1
  import { config } from 'dotenv';
3
2
  config();
4
3
  import { tool } from '@langchain/core/tools';
@@ -21,7 +20,13 @@ const errorTool = tool(
21
20
  {
22
21
  name: 'errorTool',
23
22
  description: 'A tool that always throws an error',
24
- schema: z.object({ input: z.string().optional() }),
23
+ schema: {
24
+ type: 'object',
25
+ properties: {
26
+ input: { type: 'string' },
27
+ },
28
+ required: [],
29
+ },
25
30
  }
26
31
  );
27
32
 
package/src/stream.ts CHANGED
@@ -339,7 +339,8 @@ hasToolCallChunks: ${hasToolCallChunks}
339
339
  (c) =>
340
340
  (c.type?.startsWith(ContentTypes.THINKING) ?? false) ||
341
341
  (c.type?.startsWith(ContentTypes.REASONING) ?? false) ||
342
- (c.type?.startsWith(ContentTypes.REASONING_CONTENT) ?? false)
342
+ (c.type?.startsWith(ContentTypes.REASONING_CONTENT) ?? false) ||
343
+ c.type === 'redacted_thinking'
343
344
  )
344
345
  ) {
345
346
  await graph.dispatchReasoningDelta(stepId, {
@@ -365,7 +366,8 @@ hasToolCallChunks: ${hasToolCallChunks}
365
366
  Array.isArray(chunk.content) &&
366
367
  (chunk.content[0]?.type === ContentTypes.THINKING ||
367
368
  chunk.content[0]?.type === ContentTypes.REASONING ||
368
- chunk.content[0]?.type === ContentTypes.REASONING_CONTENT)
369
+ chunk.content[0]?.type === ContentTypes.REASONING_CONTENT ||
370
+ chunk.content[0]?.type === 'redacted_thinking')
369
371
  ) {
370
372
  reasoning_content = 'valid';
371
373
  } else if (
@@ -3,7 +3,6 @@
3
3
  * Shared mock tools for testing across all test scripts.
4
4
  * Centralizes tool definitions to follow DRY principles.
5
5
  */
6
- import { z } from 'zod';
7
6
  import { tool } from '@langchain/core/tools';
8
7
  import type { StructuredToolInterface } from '@langchain/core/tools';
9
8
  import type { LCTool, LCToolRegistry } from '@/types';
@@ -29,7 +28,7 @@ export function createGetTeamMembersTool(): StructuredToolInterface {
29
28
  name: 'get_team_members',
30
29
  description:
31
30
  'Get list of team members. Returns array of objects with id, name, and department fields.',
32
- schema: z.object({}),
31
+ schema: { type: 'object', properties: {}, required: [] },
33
32
  }
34
33
  );
35
34
  }
@@ -59,7 +58,8 @@ export function createGetExpensesTool(): StructuredToolInterface {
59
58
  };
60
59
 
61
60
  return tool(
62
- async ({ user_id }: { user_id: string }) => {
61
+ async (input) => {
62
+ const { user_id } = input as { user_id: string };
63
63
  await new Promise((resolve) => setTimeout(resolve, 30));
64
64
  return expenseData[user_id] ?? [];
65
65
  },
@@ -67,9 +67,16 @@ export function createGetExpensesTool(): StructuredToolInterface {
67
67
  name: 'get_expenses',
68
68
  description:
69
69
  'Get expense records for a user. Returns array of objects with amount and category fields.',
70
- schema: z.object({
71
- user_id: z.string().describe('The user ID to fetch expenses for'),
72
- }),
70
+ schema: {
71
+ type: 'object',
72
+ properties: {
73
+ user_id: {
74
+ type: 'string',
75
+ description: 'The user ID to fetch expenses for',
76
+ },
77
+ },
78
+ required: ['user_id'],
79
+ },
73
80
  }
74
81
  );
75
82
  }
@@ -91,7 +98,8 @@ export function createGetWeatherTool(): StructuredToolInterface {
91
98
  };
92
99
 
93
100
  return tool(
94
- async ({ city }: { city: string }) => {
101
+ async (input) => {
102
+ const { city } = input as { city: string };
95
103
  await new Promise((resolve) => setTimeout(resolve, 40));
96
104
  const weather = weatherData[city];
97
105
  if (!weather) {
@@ -103,9 +111,13 @@ export function createGetWeatherTool(): StructuredToolInterface {
103
111
  name: 'get_weather',
104
112
  description:
105
113
  'Get current weather for a city. Returns object with temperature (number) and condition (string) fields.',
106
- schema: z.object({
107
- city: z.string().describe('City name'),
108
- }),
114
+ schema: {
115
+ type: 'object',
116
+ properties: {
117
+ city: { type: 'string', description: 'City name' },
118
+ },
119
+ required: ['city'],
120
+ },
109
121
  }
110
122
  );
111
123
  }
@@ -115,7 +127,8 @@ export function createGetWeatherTool(): StructuredToolInterface {
115
127
  */
116
128
  export function createCalculatorTool(): StructuredToolInterface {
117
129
  return tool(
118
- async ({ expression }: { expression: string }) => {
130
+ async (input) => {
131
+ const { expression } = input as { expression: string };
119
132
  await new Promise((resolve) => setTimeout(resolve, 10));
120
133
  // Simple eval for demo (in production, use a proper math parser)
121
134
 
@@ -125,9 +138,16 @@ export function createCalculatorTool(): StructuredToolInterface {
125
138
  {
126
139
  name: 'calculator',
127
140
  description: 'Evaluate a mathematical expression',
128
- schema: z.object({
129
- expression: z.string().describe('Mathematical expression to evaluate'),
130
- }),
141
+ schema: {
142
+ type: 'object',
143
+ properties: {
144
+ expression: {
145
+ type: 'string',
146
+ description: 'Mathematical expression to evaluate',
147
+ },
148
+ },
149
+ required: ['expression'],
150
+ },
131
151
  }
132
152
  );
133
153
  }
@@ -1,4 +1,3 @@
1
- import { z } from 'zod';
2
1
  import { config } from 'dotenv';
3
2
  import fetch, { RequestInit } from 'node-fetch';
4
3
  import { HttpsProxyAgent } from 'https-proxy-agent';
@@ -21,25 +20,33 @@ const accessMessage =
21
20
  const emptyOutputMessage =
22
21
  'stdout: Empty. Ensure you\'re writing output explicitly.\n';
23
22
 
24
- const CodeExecutionToolSchema = z.object({
25
- lang: z
26
- .enum([
27
- 'py',
28
- 'js',
29
- 'ts',
30
- 'c',
31
- 'cpp',
32
- 'java',
33
- 'php',
34
- 'rs',
35
- 'go',
36
- 'd',
37
- 'f90',
38
- 'r',
39
- ])
40
- .describe('The programming language or runtime to execute the code in.'),
41
- code: z.string()
42
- .describe(`The complete, self-contained code to execute, without any truncation or minimization.
23
+ const SUPPORTED_LANGUAGES = [
24
+ 'py',
25
+ 'js',
26
+ 'ts',
27
+ 'c',
28
+ 'cpp',
29
+ 'java',
30
+ 'php',
31
+ 'rs',
32
+ 'go',
33
+ 'd',
34
+ 'f90',
35
+ 'r',
36
+ ] as const;
37
+
38
+ const CodeExecutionToolSchema = {
39
+ type: 'object',
40
+ properties: {
41
+ lang: {
42
+ type: 'string',
43
+ enum: SUPPORTED_LANGUAGES,
44
+ description:
45
+ 'The programming language or runtime to execute the code in.',
46
+ },
47
+ code: {
48
+ type: 'string',
49
+ description: `The complete, self-contained code to execute, without any truncation or minimization.
43
50
  - The environment is stateless; variables and imports don't persist between executions.
44
51
  - Generated files from previous executions are automatically available in "/mnt/data/".
45
52
  - Files from previous executions are automatically available and can be modified in place.
@@ -50,21 +57,26 @@ const CodeExecutionToolSchema = z.object({
50
57
  - py: Matplotlib: Use \`plt.savefig()\` to save plots as files.
51
58
  - js: use the \`console\` or \`process\` methods for all outputs.
52
59
  - r: IMPORTANT: No X11 display available. ALL graphics MUST use Cairo library (library(Cairo)).
53
- - Other languages: use appropriate output functions.`),
54
- args: z
55
- .array(z.string())
56
- .optional()
57
- .describe(
58
- 'Additional arguments to execute the code with. This should only be used if the input code requires additional arguments to run.'
59
- ),
60
- });
60
+ - Other languages: use appropriate output functions.`,
61
+ },
62
+ args: {
63
+ type: 'array',
64
+ items: { type: 'string' },
65
+ description:
66
+ 'Additional arguments to execute the code with. This should only be used if the input code requires additional arguments to run.',
67
+ },
68
+ },
69
+ required: ['lang', 'code'],
70
+ } as const;
61
71
 
62
72
  const baseEndpoint = getCodeBaseURL();
63
73
  const EXEC_ENDPOINT = `${baseEndpoint}/exec`;
64
74
 
75
+ type SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];
76
+
65
77
  function createCodeExecutionTool(
66
78
  params: t.CodeExecutionToolParams = {}
67
- ): DynamicStructuredTool<typeof CodeExecutionToolSchema> {
79
+ ): DynamicStructuredTool {
68
80
  const apiKey =
69
81
  params[EnvVar.CODE_API_KEY] ??
70
82
  params.apiKey ??
@@ -83,8 +95,13 @@ Usage:
83
95
  - NEVER use this tool to execute malicious code.
84
96
  `.trim();
85
97
 
86
- return tool<typeof CodeExecutionToolSchema>(
87
- async ({ lang, code, ...rest }, config) => {
98
+ return tool(
99
+ async (rawInput, config) => {
100
+ const { lang, code, ...rest } = rawInput as {
101
+ lang: SupportedLanguage;
102
+ code: string;
103
+ args?: string[];
104
+ };
88
105
  /**
89
106
  * Extract session context from config.toolCall (injected by ToolNode).
90
107
  * - session_id: For API to associate with previous session
@@ -1,5 +1,4 @@
1
1
  // src/tools/ProgrammaticToolCalling.ts
2
- import { z } from 'zod';
3
2
  import { config } from 'dotenv';
4
3
  import fetch, { RequestInit } from 'node-fetch';
5
4
  import { HttpsProxyAgent } from 'https-proxy-agent';
@@ -33,12 +32,13 @@ const DEFAULT_TIMEOUT = 60000;
33
32
  // Schema
34
33
  // ============================================================================
35
34
 
36
- const ProgrammaticToolCallingSchema = z.object({
37
- code: z
38
- .string()
39
- .min(1)
40
- .describe(
41
- `Python code that calls tools programmatically. Tools are available as async functions.
35
+ const ProgrammaticToolCallingSchema = {
36
+ type: 'object',
37
+ properties: {
38
+ code: {
39
+ type: 'string',
40
+ minLength: 1,
41
+ description: `Python code that calls tools programmatically. Tools are available as async functions.
42
42
 
43
43
  CRITICAL - STATELESS EXECUTION:
44
44
  Each call is a fresh Python interpreter. Variables, imports, and data do NOT persist between calls.
@@ -66,19 +66,19 @@ Rules:
66
66
  - Just write code with await—auto-wrapped in async context
67
67
  - DO NOT define async def main() or call asyncio.run()
68
68
  - Tools are pre-defined—DO NOT write function definitions
69
- - Only print() output returns to the model`
70
- ),
71
- timeout: z
72
- .number()
73
- .int()
74
- .min(1000)
75
- .max(300000)
76
- .optional()
77
- .default(DEFAULT_TIMEOUT)
78
- .describe(
79
- 'Maximum execution time in milliseconds. Default: 60 seconds. Max: 5 minutes.'
80
- ),
81
- });
69
+ - Only print() output returns to the model`,
70
+ },
71
+ timeout: {
72
+ type: 'integer',
73
+ minimum: 1000,
74
+ maximum: 300000,
75
+ default: DEFAULT_TIMEOUT,
76
+ description:
77
+ 'Maximum execution time in milliseconds. Default: 60 seconds. Max: 5 minutes.',
78
+ },
79
+ },
80
+ required: ['code'],
81
+ } as const;
82
82
 
83
83
  // ============================================================================
84
84
  // Helper Functions
@@ -576,7 +576,7 @@ export function formatCompletedResponse(
576
576
  */
577
577
  export function createProgrammaticToolCallingTool(
578
578
  initParams: t.ProgrammaticToolCallingParams = {}
579
- ): DynamicStructuredTool<typeof ProgrammaticToolCallingSchema> {
579
+ ): DynamicStructuredTool {
580
580
  const apiKey =
581
581
  (initParams[EnvVar.CODE_API_KEY] as string | undefined) ??
582
582
  initParams.apiKey ??
@@ -616,8 +616,9 @@ Example (complete pipeline):
616
616
  data = await query_db(sql="..."); df = process(data); await save_to_sheet(data=df); print("Done")
617
617
  `.trim();
618
618
 
619
- return tool<typeof ProgrammaticToolCallingSchema>(
620
- async (params, config) => {
619
+ return tool(
620
+ async (rawParams, config) => {
621
+ const params = rawParams as { code: string; timeout?: number };
621
622
  const { code, timeout = DEFAULT_TIMEOUT } = params;
622
623
 
623
624
  // Extra params injected by ToolNode (follows web_search pattern)