@librechat/agents 2.4.322 → 3.0.0-rc10

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 (279) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +218 -0
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -0
  3. package/dist/cjs/common/enum.cjs +15 -5
  4. package/dist/cjs/common/enum.cjs.map +1 -1
  5. package/dist/cjs/events.cjs +10 -6
  6. package/dist/cjs/events.cjs.map +1 -1
  7. package/dist/cjs/graphs/Graph.cjs +309 -213
  8. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  9. package/dist/cjs/graphs/MultiAgentGraph.cjs +507 -0
  10. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -0
  11. package/dist/cjs/llm/anthropic/index.cjs +54 -9
  12. package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
  13. package/dist/cjs/llm/anthropic/types.cjs.map +1 -1
  14. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +52 -6
  15. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  16. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +22 -2
  17. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
  18. package/dist/cjs/llm/anthropic/utils/tools.cjs +29 -0
  19. package/dist/cjs/llm/anthropic/utils/tools.cjs.map +1 -0
  20. package/dist/cjs/llm/google/index.cjs +144 -0
  21. package/dist/cjs/llm/google/index.cjs.map +1 -0
  22. package/dist/cjs/llm/google/utils/common.cjs +477 -0
  23. package/dist/cjs/llm/google/utils/common.cjs.map +1 -0
  24. package/dist/cjs/llm/ollama/index.cjs +67 -0
  25. package/dist/cjs/llm/ollama/index.cjs.map +1 -0
  26. package/dist/cjs/llm/ollama/utils.cjs +158 -0
  27. package/dist/cjs/llm/ollama/utils.cjs.map +1 -0
  28. package/dist/cjs/llm/openai/index.cjs +422 -3
  29. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  30. package/dist/cjs/llm/openai/utils/index.cjs +672 -0
  31. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -0
  32. package/dist/cjs/llm/providers.cjs +15 -15
  33. package/dist/cjs/llm/providers.cjs.map +1 -1
  34. package/dist/cjs/llm/text.cjs +14 -3
  35. package/dist/cjs/llm/text.cjs.map +1 -1
  36. package/dist/cjs/llm/vertexai/index.cjs +330 -0
  37. package/dist/cjs/llm/vertexai/index.cjs.map +1 -0
  38. package/dist/cjs/main.cjs +11 -0
  39. package/dist/cjs/main.cjs.map +1 -1
  40. package/dist/cjs/run.cjs +137 -85
  41. package/dist/cjs/run.cjs.map +1 -1
  42. package/dist/cjs/stream.cjs +86 -52
  43. package/dist/cjs/stream.cjs.map +1 -1
  44. package/dist/cjs/tools/ToolNode.cjs +10 -4
  45. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  46. package/dist/cjs/tools/handlers.cjs +119 -13
  47. package/dist/cjs/tools/handlers.cjs.map +1 -1
  48. package/dist/cjs/tools/search/anthropic.cjs +40 -0
  49. package/dist/cjs/tools/search/anthropic.cjs.map +1 -0
  50. package/dist/cjs/tools/search/firecrawl.cjs +55 -9
  51. package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
  52. package/dist/cjs/tools/search/format.cjs +6 -6
  53. package/dist/cjs/tools/search/format.cjs.map +1 -1
  54. package/dist/cjs/tools/search/rerankers.cjs +7 -29
  55. package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
  56. package/dist/cjs/tools/search/search.cjs +86 -16
  57. package/dist/cjs/tools/search/search.cjs.map +1 -1
  58. package/dist/cjs/tools/search/tool.cjs +4 -2
  59. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  60. package/dist/cjs/tools/search/utils.cjs +1 -1
  61. package/dist/cjs/tools/search/utils.cjs.map +1 -1
  62. package/dist/cjs/utils/events.cjs +31 -0
  63. package/dist/cjs/utils/events.cjs.map +1 -0
  64. package/dist/cjs/utils/title.cjs +57 -21
  65. package/dist/cjs/utils/title.cjs.map +1 -1
  66. package/dist/cjs/utils/tokens.cjs +54 -7
  67. package/dist/cjs/utils/tokens.cjs.map +1 -1
  68. package/dist/esm/agents/AgentContext.mjs +216 -0
  69. package/dist/esm/agents/AgentContext.mjs.map +1 -0
  70. package/dist/esm/common/enum.mjs +16 -6
  71. package/dist/esm/common/enum.mjs.map +1 -1
  72. package/dist/esm/events.mjs +10 -6
  73. package/dist/esm/events.mjs.map +1 -1
  74. package/dist/esm/graphs/Graph.mjs +311 -215
  75. package/dist/esm/graphs/Graph.mjs.map +1 -1
  76. package/dist/esm/graphs/MultiAgentGraph.mjs +505 -0
  77. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -0
  78. package/dist/esm/llm/anthropic/index.mjs +54 -9
  79. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  80. package/dist/esm/llm/anthropic/types.mjs.map +1 -1
  81. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +52 -6
  82. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  83. package/dist/esm/llm/anthropic/utils/message_outputs.mjs +22 -2
  84. package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
  85. package/dist/esm/llm/anthropic/utils/tools.mjs +27 -0
  86. package/dist/esm/llm/anthropic/utils/tools.mjs.map +1 -0
  87. package/dist/esm/llm/google/index.mjs +142 -0
  88. package/dist/esm/llm/google/index.mjs.map +1 -0
  89. package/dist/esm/llm/google/utils/common.mjs +471 -0
  90. package/dist/esm/llm/google/utils/common.mjs.map +1 -0
  91. package/dist/esm/llm/ollama/index.mjs +65 -0
  92. package/dist/esm/llm/ollama/index.mjs.map +1 -0
  93. package/dist/esm/llm/ollama/utils.mjs +155 -0
  94. package/dist/esm/llm/ollama/utils.mjs.map +1 -0
  95. package/dist/esm/llm/openai/index.mjs +421 -4
  96. package/dist/esm/llm/openai/index.mjs.map +1 -1
  97. package/dist/esm/llm/openai/utils/index.mjs +666 -0
  98. package/dist/esm/llm/openai/utils/index.mjs.map +1 -0
  99. package/dist/esm/llm/providers.mjs +5 -5
  100. package/dist/esm/llm/providers.mjs.map +1 -1
  101. package/dist/esm/llm/text.mjs +14 -3
  102. package/dist/esm/llm/text.mjs.map +1 -1
  103. package/dist/esm/llm/vertexai/index.mjs +328 -0
  104. package/dist/esm/llm/vertexai/index.mjs.map +1 -0
  105. package/dist/esm/main.mjs +6 -5
  106. package/dist/esm/main.mjs.map +1 -1
  107. package/dist/esm/run.mjs +138 -87
  108. package/dist/esm/run.mjs.map +1 -1
  109. package/dist/esm/stream.mjs +88 -55
  110. package/dist/esm/stream.mjs.map +1 -1
  111. package/dist/esm/tools/ToolNode.mjs +10 -4
  112. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  113. package/dist/esm/tools/handlers.mjs +119 -15
  114. package/dist/esm/tools/handlers.mjs.map +1 -1
  115. package/dist/esm/tools/search/anthropic.mjs +37 -0
  116. package/dist/esm/tools/search/anthropic.mjs.map +1 -0
  117. package/dist/esm/tools/search/firecrawl.mjs +55 -9
  118. package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
  119. package/dist/esm/tools/search/format.mjs +7 -7
  120. package/dist/esm/tools/search/format.mjs.map +1 -1
  121. package/dist/esm/tools/search/rerankers.mjs +7 -29
  122. package/dist/esm/tools/search/rerankers.mjs.map +1 -1
  123. package/dist/esm/tools/search/search.mjs +86 -16
  124. package/dist/esm/tools/search/search.mjs.map +1 -1
  125. package/dist/esm/tools/search/tool.mjs +4 -2
  126. package/dist/esm/tools/search/tool.mjs.map +1 -1
  127. package/dist/esm/tools/search/utils.mjs +1 -1
  128. package/dist/esm/tools/search/utils.mjs.map +1 -1
  129. package/dist/esm/utils/events.mjs +29 -0
  130. package/dist/esm/utils/events.mjs.map +1 -0
  131. package/dist/esm/utils/title.mjs +57 -22
  132. package/dist/esm/utils/title.mjs.map +1 -1
  133. package/dist/esm/utils/tokens.mjs +54 -8
  134. package/dist/esm/utils/tokens.mjs.map +1 -1
  135. package/dist/types/agents/AgentContext.d.ts +91 -0
  136. package/dist/types/common/enum.d.ts +17 -7
  137. package/dist/types/events.d.ts +5 -4
  138. package/dist/types/graphs/Graph.d.ts +64 -67
  139. package/dist/types/graphs/MultiAgentGraph.d.ts +47 -0
  140. package/dist/types/graphs/index.d.ts +1 -0
  141. package/dist/types/llm/anthropic/index.d.ts +11 -0
  142. package/dist/types/llm/anthropic/types.d.ts +9 -3
  143. package/dist/types/llm/anthropic/utils/message_inputs.d.ts +1 -1
  144. package/dist/types/llm/anthropic/utils/output_parsers.d.ts +4 -4
  145. package/dist/types/llm/anthropic/utils/tools.d.ts +3 -0
  146. package/dist/types/llm/google/index.d.ts +13 -0
  147. package/dist/types/llm/google/types.d.ts +32 -0
  148. package/dist/types/llm/google/utils/common.d.ts +19 -0
  149. package/dist/types/llm/google/utils/tools.d.ts +10 -0
  150. package/dist/types/llm/google/utils/zod_to_genai_parameters.d.ts +14 -0
  151. package/dist/types/llm/ollama/index.d.ts +7 -0
  152. package/dist/types/llm/ollama/utils.d.ts +7 -0
  153. package/dist/types/llm/openai/index.d.ts +82 -3
  154. package/dist/types/llm/openai/types.d.ts +10 -0
  155. package/dist/types/llm/openai/utils/index.d.ts +20 -0
  156. package/dist/types/llm/text.d.ts +1 -1
  157. package/dist/types/llm/vertexai/index.d.ts +293 -0
  158. package/dist/types/messages/reducer.d.ts +9 -0
  159. package/dist/types/run.d.ts +19 -12
  160. package/dist/types/stream.d.ts +10 -3
  161. package/dist/types/tools/CodeExecutor.d.ts +2 -2
  162. package/dist/types/tools/ToolNode.d.ts +1 -1
  163. package/dist/types/tools/handlers.d.ts +17 -4
  164. package/dist/types/tools/search/anthropic.d.ts +16 -0
  165. package/dist/types/tools/search/firecrawl.d.ts +15 -0
  166. package/dist/types/tools/search/rerankers.d.ts +0 -1
  167. package/dist/types/tools/search/types.d.ts +30 -9
  168. package/dist/types/types/graph.d.ts +129 -15
  169. package/dist/types/types/llm.d.ts +25 -10
  170. package/dist/types/types/run.d.ts +50 -8
  171. package/dist/types/types/stream.d.ts +16 -2
  172. package/dist/types/types/tools.d.ts +1 -1
  173. package/dist/types/utils/events.d.ts +6 -0
  174. package/dist/types/utils/title.d.ts +2 -1
  175. package/dist/types/utils/tokens.d.ts +24 -0
  176. package/package.json +41 -17
  177. package/src/agents/AgentContext.ts +315 -0
  178. package/src/common/enum.ts +15 -5
  179. package/src/events.ts +24 -13
  180. package/src/graphs/Graph.ts +495 -313
  181. package/src/graphs/MultiAgentGraph.ts +598 -0
  182. package/src/graphs/index.ts +2 -1
  183. package/src/llm/anthropic/Jacob_Lee_Resume_2023.pdf +0 -0
  184. package/src/llm/anthropic/index.ts +78 -13
  185. package/src/llm/anthropic/llm.spec.ts +491 -115
  186. package/src/llm/anthropic/types.ts +39 -3
  187. package/src/llm/anthropic/utils/message_inputs.ts +67 -11
  188. package/src/llm/anthropic/utils/message_outputs.ts +21 -2
  189. package/src/llm/anthropic/utils/output_parsers.ts +25 -6
  190. package/src/llm/anthropic/utils/tools.ts +29 -0
  191. package/src/llm/google/index.ts +218 -0
  192. package/src/llm/google/types.ts +43 -0
  193. package/src/llm/google/utils/common.ts +646 -0
  194. package/src/llm/google/utils/tools.ts +160 -0
  195. package/src/llm/google/utils/zod_to_genai_parameters.ts +86 -0
  196. package/src/llm/ollama/index.ts +89 -0
  197. package/src/llm/ollama/utils.ts +193 -0
  198. package/src/llm/openai/index.ts +641 -14
  199. package/src/llm/openai/types.ts +24 -0
  200. package/src/llm/openai/utils/index.ts +912 -0
  201. package/src/llm/openai/utils/isReasoningModel.test.ts +90 -0
  202. package/src/llm/providers.ts +10 -9
  203. package/src/llm/text.ts +26 -7
  204. package/src/llm/vertexai/index.ts +360 -0
  205. package/src/messages/reducer.ts +80 -0
  206. package/src/run.ts +196 -116
  207. package/src/scripts/ant_web_search.ts +158 -0
  208. package/src/scripts/args.ts +12 -8
  209. package/src/scripts/cli4.ts +29 -21
  210. package/src/scripts/cli5.ts +29 -21
  211. package/src/scripts/code_exec.ts +54 -23
  212. package/src/scripts/code_exec_files.ts +48 -17
  213. package/src/scripts/code_exec_simple.ts +46 -27
  214. package/src/scripts/handoff-test.ts +135 -0
  215. package/src/scripts/image.ts +52 -20
  216. package/src/scripts/multi-agent-chain.ts +278 -0
  217. package/src/scripts/multi-agent-conditional.ts +220 -0
  218. package/src/scripts/multi-agent-document-review-chain.ts +197 -0
  219. package/src/scripts/multi-agent-hybrid-flow.ts +310 -0
  220. package/src/scripts/multi-agent-parallel.ts +341 -0
  221. package/src/scripts/multi-agent-sequence.ts +212 -0
  222. package/src/scripts/multi-agent-supervisor.ts +362 -0
  223. package/src/scripts/multi-agent-test.ts +186 -0
  224. package/src/scripts/search.ts +1 -9
  225. package/src/scripts/simple.ts +25 -10
  226. package/src/scripts/test-custom-prompt-key.ts +145 -0
  227. package/src/scripts/test-handoff-input.ts +170 -0
  228. package/src/scripts/test-multi-agent-list-handoff.ts +261 -0
  229. package/src/scripts/test-tools-before-handoff.ts +233 -0
  230. package/src/scripts/tools.ts +48 -18
  231. package/src/specs/anthropic.simple.test.ts +150 -34
  232. package/src/specs/azure.simple.test.ts +325 -0
  233. package/src/specs/openai.simple.test.ts +140 -33
  234. package/src/specs/openrouter.simple.test.ts +107 -0
  235. package/src/specs/prune.test.ts +4 -9
  236. package/src/specs/reasoning.test.ts +80 -44
  237. package/src/specs/token-memoization.test.ts +39 -0
  238. package/src/stream.test.ts +94 -0
  239. package/src/stream.ts +143 -61
  240. package/src/tools/ToolNode.ts +21 -7
  241. package/src/tools/handlers.ts +192 -18
  242. package/src/tools/search/anthropic.ts +51 -0
  243. package/src/tools/search/firecrawl.ts +69 -20
  244. package/src/tools/search/format.ts +6 -8
  245. package/src/tools/search/rerankers.ts +7 -40
  246. package/src/tools/search/search.ts +97 -16
  247. package/src/tools/search/tool.ts +5 -2
  248. package/src/tools/search/types.ts +30 -10
  249. package/src/tools/search/utils.ts +1 -1
  250. package/src/types/graph.ts +318 -103
  251. package/src/types/llm.ts +26 -12
  252. package/src/types/run.ts +56 -13
  253. package/src/types/stream.ts +22 -1
  254. package/src/types/tools.ts +16 -10
  255. package/src/utils/events.ts +32 -0
  256. package/src/utils/llmConfig.ts +19 -7
  257. package/src/utils/title.ts +104 -30
  258. package/src/utils/tokens.ts +69 -10
  259. package/dist/types/scripts/abort.d.ts +0 -1
  260. package/dist/types/scripts/args.d.ts +0 -6
  261. package/dist/types/scripts/caching.d.ts +0 -1
  262. package/dist/types/scripts/cli.d.ts +0 -1
  263. package/dist/types/scripts/cli2.d.ts +0 -1
  264. package/dist/types/scripts/cli3.d.ts +0 -1
  265. package/dist/types/scripts/cli4.d.ts +0 -1
  266. package/dist/types/scripts/cli5.d.ts +0 -1
  267. package/dist/types/scripts/code_exec.d.ts +0 -1
  268. package/dist/types/scripts/code_exec_files.d.ts +0 -1
  269. package/dist/types/scripts/code_exec_simple.d.ts +0 -1
  270. package/dist/types/scripts/content.d.ts +0 -1
  271. package/dist/types/scripts/empty_input.d.ts +0 -1
  272. package/dist/types/scripts/image.d.ts +0 -1
  273. package/dist/types/scripts/memory.d.ts +0 -1
  274. package/dist/types/scripts/search.d.ts +0 -1
  275. package/dist/types/scripts/simple.d.ts +0 -1
  276. package/dist/types/scripts/stream.d.ts +0 -1
  277. package/dist/types/scripts/thinking.d.ts +0 -1
  278. package/dist/types/scripts/tools.d.ts +0 -1
  279. package/dist/types/specs/spec.utils.d.ts +0 -1
@@ -0,0 +1,261 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { config } from 'dotenv';
4
+ config();
5
+
6
+ import { HumanMessage, BaseMessage } from '@langchain/core/messages';
7
+ import { Run } from '@/run';
8
+ import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
9
+ import { Providers, GraphEvents, Constants } from '@/common';
10
+ import { ToolEndHandler, ModelEndHandler } from '@/events';
11
+ import type * as t from '@/types';
12
+
13
+ const conversationHistory: BaseMessage[] = [];
14
+
15
+ /**
16
+ * Test supervisor-based multi-agent system using a single edge with multiple destinations
17
+ *
18
+ * Instead of creating 5 separate edges, we use one edge with an array of destinations
19
+ * This should create handoff tools for all 5 specialists from a single edge definition
20
+ */
21
+ async function testSupervisorListHandoff() {
22
+ console.log('Testing Supervisor with List-Based Handoff Edge...\n');
23
+
24
+ // Set up content aggregator
25
+ const { contentParts, aggregateContent } = createContentAggregator();
26
+
27
+ // Track which specialist role was selected
28
+ let selectedRole = '';
29
+
30
+ // Create custom handlers
31
+ const customHandlers = {
32
+ [GraphEvents.TOOL_END]: new ToolEndHandler(),
33
+ [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),
34
+ [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
35
+ [GraphEvents.ON_RUN_STEP]: {
36
+ handle: (
37
+ event: GraphEvents.ON_RUN_STEP,
38
+ data: t.StreamEventData
39
+ ): void => {
40
+ const runStepData = data as any;
41
+ if (runStepData?.name) {
42
+ console.log(`\n[${runStepData.name}] Processing...`);
43
+ }
44
+ aggregateContent({ event, data: data as t.RunStep });
45
+ },
46
+ },
47
+ [GraphEvents.ON_RUN_STEP_COMPLETED]: {
48
+ handle: (
49
+ event: GraphEvents.ON_RUN_STEP_COMPLETED,
50
+ data: t.StreamEventData
51
+ ): void => {
52
+ aggregateContent({
53
+ event,
54
+ data: data as unknown as { result: t.ToolEndEvent },
55
+ });
56
+ },
57
+ },
58
+ [GraphEvents.ON_MESSAGE_DELTA]: {
59
+ handle: (
60
+ event: GraphEvents.ON_MESSAGE_DELTA,
61
+ data: t.StreamEventData
62
+ ): void => {
63
+ aggregateContent({ event, data: data as t.MessageDeltaEvent });
64
+ },
65
+ },
66
+ [GraphEvents.TOOL_START]: {
67
+ handle: (
68
+ _event: string,
69
+ data: t.StreamEventData,
70
+ metadata?: Record<string, unknown>
71
+ ): void => {
72
+ const toolData = data as any;
73
+ if (toolData?.name?.startsWith(Constants.LC_TRANSFER_TO_)) {
74
+ const specialist = toolData.name.replace(
75
+ Constants.LC_TRANSFER_TO_,
76
+ ''
77
+ );
78
+ console.log(`\n🔀 Transferring to ${specialist}...`);
79
+ selectedRole = specialist;
80
+ }
81
+ },
82
+ },
83
+ };
84
+
85
+ // Function to create the graph with a single edge to multiple specialists
86
+ function createSupervisorGraphWithListEdge(): t.RunConfig {
87
+ console.log(`\nCreating graph with supervisor and 5 specialist agents.`);
88
+ console.log(
89
+ 'Using a SINGLE edge with multiple destinations (list-based handoff).\n'
90
+ );
91
+
92
+ // Define the adaptive specialist configuration that will be reused
93
+ const specialistConfig = {
94
+ provider: Providers.ANTHROPIC,
95
+ clientOptions: {
96
+ modelName: 'claude-3-5-sonnet-latest',
97
+ apiKey: process.env.ANTHROPIC_API_KEY,
98
+ },
99
+ instructions: `You are an Adaptive Specialist. Your agent ID indicates your role:
100
+
101
+ - data_analyst: Focus on statistical analysis, metrics, ML evaluation, A/B testing
102
+ - security_expert: Focus on cybersecurity, vulnerability assessment, compliance
103
+ - product_designer: Focus on UX/UI design, user research, accessibility
104
+ - devops_engineer: Focus on CI/CD, infrastructure, cloud platforms, monitoring
105
+ - legal_advisor: Focus on licensing, privacy laws, contracts, regulatory compliance
106
+
107
+ The supervisor will provide specific instructions. Follow them while maintaining your expert perspective.`,
108
+ maxContextTokens: 8000,
109
+ };
110
+
111
+ // Create the graph with supervisor and all 5 specialists
112
+ const agents: t.AgentInputs[] = [
113
+ {
114
+ agentId: 'supervisor',
115
+ provider: Providers.ANTHROPIC,
116
+ clientOptions: {
117
+ modelName: 'claude-3-5-sonnet-latest',
118
+ apiKey: process.env.ANTHROPIC_API_KEY,
119
+ },
120
+ instructions: `You are a Task Supervisor with access to 5 specialist agents:
121
+ 1. transfer_to_data_analyst - For statistical analysis and metrics
122
+ 2. transfer_to_security_expert - For cybersecurity and vulnerability assessment
123
+ 3. transfer_to_product_designer - For UX/UI design
124
+ 4. transfer_to_devops_engineer - For infrastructure and deployment
125
+ 5. transfer_to_legal_advisor - For compliance and licensing
126
+
127
+ Your role is to:
128
+ 1. Analyze the incoming request
129
+ 2. Decide which specialist is best suited
130
+ 3. Use the appropriate transfer tool (e.g., transfer_to_data_analyst)
131
+ 4. Provide specific instructions to guide their work
132
+
133
+ Be specific about what you need from the specialist.`,
134
+ maxContextTokens: 8000,
135
+ },
136
+ // Include all 5 specialists with the same adaptive configuration
137
+ {
138
+ agentId: 'data_analyst',
139
+ ...specialistConfig,
140
+ },
141
+ {
142
+ agentId: 'security_expert',
143
+ ...specialistConfig,
144
+ },
145
+ {
146
+ agentId: 'product_designer',
147
+ ...specialistConfig,
148
+ },
149
+ {
150
+ agentId: 'devops_engineer',
151
+ ...specialistConfig,
152
+ },
153
+ {
154
+ agentId: 'legal_advisor',
155
+ ...specialistConfig,
156
+ },
157
+ ];
158
+
159
+ // Create a SINGLE edge from supervisor to ALL 5 specialists using a list
160
+ const edges: t.GraphEdge[] = [
161
+ {
162
+ from: 'supervisor',
163
+ to: [
164
+ 'data_analyst',
165
+ 'security_expert',
166
+ 'product_designer',
167
+ 'devops_engineer',
168
+ 'legal_advisor',
169
+ ],
170
+ description:
171
+ 'Transfer to appropriate specialist based on task requirements',
172
+ edgeType: 'handoff',
173
+ },
174
+ ];
175
+
176
+ return {
177
+ runId: `supervisor-list-handoff-${Date.now()}`,
178
+ graphConfig: {
179
+ type: 'multi-agent',
180
+ agents,
181
+ edges,
182
+ },
183
+ customHandlers,
184
+ returnContent: true,
185
+ };
186
+ }
187
+
188
+ try {
189
+ // Test with different queries
190
+ const testQueries = [
191
+ // 'How can we analyze user engagement metrics to improve our product?',
192
+ // 'What security measures should we implement for our new API?',
193
+ // 'Can you help design a better onboarding flow for our mobile app?',
194
+ // 'We need to set up a CI/CD pipeline for our microservices.',
195
+ 'What are the legal implications of using GPL-licensed code in our product?',
196
+ ];
197
+
198
+ const config = {
199
+ configurable: {
200
+ thread_id: 'supervisor-list-handoff-1',
201
+ },
202
+ streamMode: 'values',
203
+ version: 'v2' as const,
204
+ };
205
+
206
+ for (const query of testQueries) {
207
+ console.log(`\n${'='.repeat(60)}`);
208
+ console.log(`USER QUERY: "${query}"`);
209
+ console.log('='.repeat(60));
210
+
211
+ // Reset conversation
212
+ conversationHistory.length = 0;
213
+ conversationHistory.push(new HumanMessage(query));
214
+
215
+ // Create graph with supervisor having a single edge to multiple specialists
216
+ const runConfig = createSupervisorGraphWithListEdge();
217
+ const run = await Run.create(runConfig);
218
+
219
+ console.log('Processing request...');
220
+
221
+ // Process with streaming
222
+ const inputs = {
223
+ messages: conversationHistory,
224
+ };
225
+
226
+ const finalContentParts = await run.processStream(inputs, config);
227
+ const finalMessages = run.getRunMessages();
228
+
229
+ if (finalMessages) {
230
+ conversationHistory.push(...finalMessages);
231
+ }
232
+
233
+ // Show summary
234
+ console.log(`\n${'─'.repeat(60)}`);
235
+ console.log(`Graph structure:`);
236
+ console.log(`- Agents: 6 total (supervisor + 5 specialists)`);
237
+ console.log(`- Edges: 1 edge with multiple destinations`);
238
+ console.log(
239
+ `- Edge type: handoff (creates individual tools for each destination)`
240
+ );
241
+ console.log(
242
+ `- Result: Supervisor has 5 handoff tools from a single edge`
243
+ );
244
+ console.log('─'.repeat(60));
245
+ }
246
+
247
+ // Final summary
248
+ console.log(`\n${'='.repeat(60)}`);
249
+ console.log('TEST COMPLETE');
250
+ console.log('='.repeat(60));
251
+ console.log('\nThis test demonstrates that a single edge with multiple');
252
+ console.log('destinations in the "to" field creates individual handoff');
253
+ console.log('tools for each destination agent, achieving the same result');
254
+ console.log('as creating separate edges for each specialist.');
255
+ } catch (error) {
256
+ console.error('Error in supervisor list handoff test:', error);
257
+ }
258
+ }
259
+
260
+ // Run the test
261
+ testSupervisorListHandoff();
@@ -0,0 +1,233 @@
1
+ import { config } from 'dotenv';
2
+ config();
3
+
4
+ import { TavilySearch } from '@langchain/tavily';
5
+ import { HumanMessage, BaseMessage } from '@langchain/core/messages';
6
+ import { Run } from '@/run';
7
+ import { Providers, GraphEvents } from '@/common';
8
+ import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
9
+ import { ToolEndHandler, ModelEndHandler } from '@/events';
10
+ import type * as t from '@/types';
11
+
12
+ const conversationHistory: BaseMessage[] = [];
13
+
14
+ /**
15
+ * Test edge case: Agent performs 2 web searches before handing off
16
+ *
17
+ * This tests how the system behaves when an agent with handoff capabilities
18
+ * uses tools before transferring control to another agent.
19
+ */
20
+ async function testToolsBeforeHandoff() {
21
+ console.log('Testing Tools Before Handoff Edge Case...\n');
22
+
23
+ // Set up content aggregator
24
+ const { contentParts, aggregateContent } = createContentAggregator();
25
+
26
+ // Track tool calls and handoffs
27
+ let toolCallCount = 0;
28
+ let handoffOccurred = false;
29
+
30
+ // Create custom handlers
31
+ const customHandlers = {
32
+ [GraphEvents.TOOL_END]: new ToolEndHandler(undefined, (name?: string) => {
33
+ console.log(`\n✅ Tool completed: ${name}`);
34
+ return true;
35
+ }),
36
+ [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),
37
+ [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
38
+ [GraphEvents.ON_RUN_STEP]: {
39
+ handle: (
40
+ event: GraphEvents.ON_RUN_STEP,
41
+ data: t.StreamEventData
42
+ ): void => {
43
+ const runStepData = data as any;
44
+ if (runStepData?.name) {
45
+ console.log(`\n[${runStepData.name}] Processing...`);
46
+ }
47
+ aggregateContent({ event, data: data as t.RunStep });
48
+ },
49
+ },
50
+ [GraphEvents.ON_RUN_STEP_COMPLETED]: {
51
+ handle: (
52
+ event: GraphEvents.ON_RUN_STEP_COMPLETED,
53
+ data: t.StreamEventData
54
+ ): void => {
55
+ aggregateContent({
56
+ event,
57
+ data: data as unknown as { result: t.ToolEndEvent },
58
+ });
59
+ },
60
+ },
61
+ [GraphEvents.ON_MESSAGE_DELTA]: {
62
+ handle: (
63
+ event: GraphEvents.ON_MESSAGE_DELTA,
64
+ data: t.StreamEventData
65
+ ): void => {
66
+ // console.log('====== ON_MESSAGE_DELTA ======');
67
+ console.dir(data, { depth: null });
68
+ aggregateContent({ event, data: data as t.MessageDeltaEvent });
69
+ },
70
+ },
71
+ [GraphEvents.TOOL_START]: {
72
+ handle: (
73
+ _event: string,
74
+ data: t.StreamEventData,
75
+ metadata?: Record<string, unknown>
76
+ ): void => {
77
+ const toolData = data as any;
78
+ console.log(`\n🔧 Tool started:`);
79
+ console.dir({ toolData, metadata }, { depth: null });
80
+
81
+ if (toolData?.output?.name === 'tavily_search_results_json') {
82
+ toolCallCount++;
83
+ console.log(`📊 Search #${toolCallCount} initiated`);
84
+ } else if (toolData?.output?.name?.includes('transfer_to_')) {
85
+ handoffOccurred = true;
86
+ const specialist = toolData.name.replace('transfer_to_', '');
87
+ console.log(`\n🔀 Handoff initiated to: ${specialist}`);
88
+ }
89
+ },
90
+ },
91
+ };
92
+
93
+ // Create the graph with research agent and report writer
94
+ function createGraphWithToolsAndHandoff(): t.RunConfig {
95
+ const agents: t.AgentInputs[] = [
96
+ {
97
+ agentId: 'research_coordinator',
98
+ provider: Providers.OPENAI,
99
+ clientOptions: {
100
+ modelName: 'gpt-4.1-mini',
101
+ apiKey: process.env.OPENAI_API_KEY,
102
+ },
103
+ tools: [new TavilySearch({ maxResults: 3 })],
104
+ instructions: `You are a Research Coordinator with access to web search and a report writer specialist.
105
+
106
+ Your workflow MUST follow these steps IN ORDER:
107
+ 1. FIRST: Write an initial response acknowledging the request and outlining your research plan
108
+ - Explain what aspects you'll investigate
109
+ - Describe your search strategy
110
+ 2. SECOND: Conduct exactly 2 web searches to gather comprehensive information
111
+ - Search 1: Get general information about the topic
112
+ - Search 2: Get specific details, recent updates, or complementary data
113
+ - Note: Even if your searches are unsuccessful, you MUST still proceed to handoff after EXACTLY 2 searches
114
+ 3. FINALLY: After completing both searches, transfer to the report writer
115
+ - Provide the report writer with a summary of your findings
116
+
117
+ CRITICAL: You MUST write your initial response before ANY tool use. Then complete both searches before handoff.`,
118
+ maxContextTokens: 8000,
119
+ },
120
+ {
121
+ agentId: 'report_writer',
122
+ provider: Providers.OPENAI,
123
+ clientOptions: {
124
+ modelName: 'gpt-5-mini',
125
+ apiKey: process.env.OPENAI_API_KEY,
126
+ },
127
+ instructions: `You are a Report Writer specialist. Your role is to:
128
+ 1. Receive research findings from the Research Coordinator
129
+ 2. Create a well-structured, comprehensive report
130
+ 3. Include all key findings from the research
131
+ 4. Format the report with clear sections and bullet points
132
+ 5. Add a brief executive summary at the beginning
133
+
134
+ Focus on clarity, completeness, and professional presentation.`,
135
+ maxContextTokens: 8000,
136
+ },
137
+ ];
138
+
139
+ // Create edge from research coordinator to report writer
140
+ const edges: t.GraphEdge[] = [
141
+ {
142
+ from: 'research_coordinator',
143
+ to: 'report_writer',
144
+ description: 'Transfer to report writer after completing research',
145
+ edgeType: 'handoff',
146
+ },
147
+ ];
148
+
149
+ return {
150
+ runId: `tools-before-handoff-${Date.now()}`,
151
+ graphConfig: {
152
+ type: 'multi-agent',
153
+ agents,
154
+ edges,
155
+ },
156
+ customHandlers,
157
+ returnContent: true,
158
+ };
159
+ }
160
+
161
+ try {
162
+ // Single test query that requires research before report writing
163
+ const query = `Research the latest developments in quantum computing from 2025,
164
+ including major breakthroughs and commercial applications.
165
+ I need a comprehensive report with recent findings.`;
166
+
167
+ console.log('='.repeat(60));
168
+ console.log(`USER QUERY: "${query}"`);
169
+ console.log('='.repeat(60));
170
+
171
+ // Create the graph
172
+ const runConfig = createGraphWithToolsAndHandoff();
173
+ const run = await Run.create(runConfig);
174
+
175
+ console.log('\nExpected behavior:');
176
+ console.log('1. Research Coordinator writes initial response/plan');
177
+ console.log('2. Research Coordinator performs 2 web searches');
178
+ console.log('3. Research Coordinator hands off to Report Writer');
179
+ console.log('4. Report Writer creates final report\n');
180
+
181
+ // Process with streaming
182
+ conversationHistory.push(new HumanMessage(query));
183
+ const inputs = {
184
+ messages: conversationHistory,
185
+ };
186
+
187
+ const config = {
188
+ configurable: {
189
+ thread_id: 'tools-handoff-test-1',
190
+ },
191
+ streamMode: 'values',
192
+ version: 'v2' as const,
193
+ };
194
+
195
+ const finalContentParts = await run.processStream(inputs, config);
196
+ const finalMessages = run.getRunMessages();
197
+
198
+ if (finalMessages) {
199
+ conversationHistory.push(...finalMessages);
200
+ }
201
+
202
+ // Show results summary
203
+ console.log(`\n${'─'.repeat(60)}`);
204
+ console.log('EDGE CASE TEST RESULTS:');
205
+ console.log('─'.repeat(60));
206
+ console.log(`Tool calls before handoff: ${toolCallCount}`);
207
+ console.log(`Expected tool calls: 2`);
208
+ console.log(`Handoff occurred: ${handoffOccurred ? 'Yes ✅' : 'No ❌'}`);
209
+ console.log(
210
+ `Test status: ${toolCallCount === 2 && handoffOccurred ? 'PASSED ✅' : 'FAILED ❌'}`
211
+ );
212
+ console.log('─'.repeat(60));
213
+
214
+ // Display conversation history
215
+ console.log('\nConversation History:');
216
+ console.log('─'.repeat(60));
217
+ conversationHistory.forEach((msg, idx) => {
218
+ const role = msg.constructor.name.replace('Message', '');
219
+ console.log(`\n[${idx}] ${role}:`);
220
+ if (typeof msg.content === 'string') {
221
+ console.log(
222
+ msg.content.substring(0, 200) +
223
+ (msg.content.length > 200 ? '...' : '')
224
+ );
225
+ }
226
+ });
227
+ } catch (error) {
228
+ console.error('Error in tools-before-handoff test:', error);
229
+ }
230
+ }
231
+
232
+ // Run the test
233
+ testToolsBeforeHandoff();
@@ -8,7 +8,6 @@ import type * as t from '@/types';
8
8
  import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
9
9
  import { ToolEndHandler, ModelEndHandler } from '@/events';
10
10
 
11
-
12
11
  import { getArgs } from '@/scripts/args';
13
12
  import { Run } from '@/run';
14
13
  import { GraphEvents, Callback } from '@/common';
@@ -19,42 +18,73 @@ async function testStandardStreaming(): Promise<void> {
19
18
  const { userName, location, provider, currentDate } = await getArgs();
20
19
  const { contentParts, aggregateContent } = createContentAggregator();
21
20
  const customHandlers = {
22
- [GraphEvents.TOOL_END]: new ToolEndHandler(),
21
+ [GraphEvents.TOOL_END]: new ToolEndHandler(undefined, (name?: string) => {
22
+ return true;
23
+ }),
23
24
  [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),
24
25
  [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
25
26
  [GraphEvents.ON_RUN_STEP_COMPLETED]: {
26
- handle: (event: GraphEvents.ON_RUN_STEP_COMPLETED, data: t.StreamEventData): void => {
27
+ handle: (
28
+ event: GraphEvents.ON_RUN_STEP_COMPLETED,
29
+ data: t.StreamEventData
30
+ ): void => {
27
31
  console.log('====== ON_RUN_STEP_COMPLETED ======');
28
- // console.dir(data, { depth: null });
29
- aggregateContent({ event, data: data as unknown as { result: t.ToolEndEvent } });
30
- }
32
+ console.dir(data, { depth: null });
33
+ aggregateContent({
34
+ event,
35
+ data: data as unknown as { result: t.ToolEndEvent },
36
+ });
37
+ },
31
38
  },
32
39
  [GraphEvents.ON_RUN_STEP]: {
33
- handle: (event: GraphEvents.ON_RUN_STEP, data: t.StreamEventData): void => {
40
+ handle: (
41
+ event: GraphEvents.ON_RUN_STEP,
42
+ data: t.StreamEventData
43
+ ): void => {
34
44
  console.log('====== ON_RUN_STEP ======');
35
45
  console.dir(data, { depth: null });
36
46
  aggregateContent({ event, data: data as t.RunStep });
37
- }
47
+ },
38
48
  },
39
49
  [GraphEvents.ON_RUN_STEP_DELTA]: {
40
- handle: (event: GraphEvents.ON_RUN_STEP_DELTA, data: t.StreamEventData): void => {
50
+ handle: (
51
+ event: GraphEvents.ON_RUN_STEP_DELTA,
52
+ data: t.StreamEventData
53
+ ): void => {
41
54
  console.log('====== ON_RUN_STEP_DELTA ======');
42
55
  console.dir(data, { depth: null });
43
56
  aggregateContent({ event, data: data as t.RunStepDeltaEvent });
44
- }
57
+ },
45
58
  },
46
59
  [GraphEvents.ON_MESSAGE_DELTA]: {
47
- handle: (event: GraphEvents.ON_MESSAGE_DELTA, data: t.StreamEventData): void => {
60
+ handle: (
61
+ event: GraphEvents.ON_MESSAGE_DELTA,
62
+ data: t.StreamEventData
63
+ ): void => {
48
64
  console.log('====== ON_MESSAGE_DELTA ======');
49
65
  console.dir(data, { depth: null });
50
66
  aggregateContent({ event, data: data as t.MessageDeltaEvent });
51
- }
67
+ },
68
+ },
69
+ [GraphEvents.ON_REASONING_DELTA]: {
70
+ handle: (
71
+ event: GraphEvents.ON_REASONING_DELTA,
72
+ data: t.StreamEventData
73
+ ) => {
74
+ console.log('====== ON_REASONING_DELTA ======');
75
+ console.dir(data, { depth: null });
76
+ aggregateContent({ event, data: data as t.ReasoningDeltaEvent });
77
+ },
52
78
  },
53
79
  [GraphEvents.TOOL_START]: {
54
- handle: (_event: string, data: t.StreamEventData, metadata?: Record<string, unknown>): void => {
80
+ handle: (
81
+ _event: string,
82
+ data: t.StreamEventData,
83
+ metadata?: Record<string, unknown>
84
+ ): void => {
55
85
  console.log('====== TOOL_START ======');
56
- // console.dir(data, { depth: null });
57
- }
86
+ console.dir(data, { depth: null });
87
+ },
58
88
  },
59
89
  };
60
90
 
@@ -66,7 +96,8 @@ async function testStandardStreaming(): Promise<void> {
66
96
  type: 'standard',
67
97
  llmConfig,
68
98
  tools: [new TavilySearchResults()],
69
- instructions: 'You are a friendly AI assistant. Always address the user by their name.',
99
+ instructions:
100
+ 'You are a friendly AI assistant. Always address the user by their name.',
70
101
  additional_instructions: `The user's name is ${userName} and they are located in ${location}.`,
71
102
  },
72
103
  returnContent: true,
@@ -86,7 +117,6 @@ async function testStandardStreaming(): Promise<void> {
86
117
 
87
118
  const userMessage = `
88
119
  Make a search for the weather in ${location} today, which is ${currentDate}.
89
- Before making the search, please let me know what you're about to do, then immediately start searching without hesitation.
90
120
  Make sure to always refer to me by name, which is ${userName}.
91
121
  After giving me a thorough summary, tell me a joke about the weather forecast we went over.
92
122
  `;
@@ -102,7 +132,7 @@ async function testStandardStreaming(): Promise<void> {
102
132
  conversationHistory.push(...finalMessages);
103
133
  console.dir(conversationHistory, { depth: null });
104
134
  }
105
- // console.dir(finalContentParts, { depth: null });
135
+ console.dir(finalContentParts, { depth: null });
106
136
  console.log('\n\n====================\n\n');
107
137
  // console.dir(contentParts, { depth: null });
108
138
  }