@librechat/agents 2.4.322 → 3.0.0-rc1

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 (258) 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 +14 -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 -212
  8. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  9. package/dist/cjs/graphs/MultiAgentGraph.cjs +322 -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 +389 -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 +120 -81
  41. package/dist/cjs/run.cjs.map +1 -1
  42. package/dist/cjs/stream.cjs +85 -51
  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 +15 -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 -214
  75. package/dist/esm/graphs/Graph.mjs.map +1 -1
  76. package/dist/esm/graphs/MultiAgentGraph.mjs +320 -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 +388 -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 +121 -83
  108. package/dist/esm/run.mjs.map +1 -1
  109. package/dist/esm/stream.mjs +87 -54
  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 +15 -6
  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 +37 -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 +72 -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/scripts/ant_web_search.d.ts +1 -0
  161. package/dist/types/scripts/args.d.ts +2 -1
  162. package/dist/types/scripts/handoff-test.d.ts +1 -0
  163. package/dist/types/scripts/multi-agent-conditional.d.ts +1 -0
  164. package/dist/types/scripts/multi-agent-parallel.d.ts +1 -0
  165. package/dist/types/scripts/multi-agent-sequence.d.ts +1 -0
  166. package/dist/types/scripts/multi-agent-test.d.ts +1 -0
  167. package/dist/types/stream.d.ts +10 -3
  168. package/dist/types/tools/CodeExecutor.d.ts +2 -2
  169. package/dist/types/tools/ToolNode.d.ts +1 -1
  170. package/dist/types/tools/handlers.d.ts +17 -4
  171. package/dist/types/tools/search/anthropic.d.ts +16 -0
  172. package/dist/types/tools/search/firecrawl.d.ts +15 -0
  173. package/dist/types/tools/search/rerankers.d.ts +0 -1
  174. package/dist/types/tools/search/types.d.ts +30 -9
  175. package/dist/types/types/graph.d.ts +95 -15
  176. package/dist/types/types/llm.d.ts +24 -10
  177. package/dist/types/types/run.d.ts +46 -8
  178. package/dist/types/types/stream.d.ts +16 -2
  179. package/dist/types/types/tools.d.ts +1 -1
  180. package/dist/types/utils/events.d.ts +6 -0
  181. package/dist/types/utils/title.d.ts +2 -1
  182. package/dist/types/utils/tokens.d.ts +24 -0
  183. package/package.json +33 -17
  184. package/src/agents/AgentContext.ts +315 -0
  185. package/src/common/enum.ts +14 -5
  186. package/src/events.ts +24 -13
  187. package/src/graphs/Graph.ts +495 -312
  188. package/src/graphs/MultiAgentGraph.ts +381 -0
  189. package/src/graphs/index.ts +2 -1
  190. package/src/llm/anthropic/Jacob_Lee_Resume_2023.pdf +0 -0
  191. package/src/llm/anthropic/index.ts +78 -13
  192. package/src/llm/anthropic/llm.spec.ts +491 -115
  193. package/src/llm/anthropic/types.ts +39 -3
  194. package/src/llm/anthropic/utils/message_inputs.ts +67 -11
  195. package/src/llm/anthropic/utils/message_outputs.ts +21 -2
  196. package/src/llm/anthropic/utils/output_parsers.ts +25 -6
  197. package/src/llm/anthropic/utils/tools.ts +29 -0
  198. package/src/llm/google/index.ts +218 -0
  199. package/src/llm/google/types.ts +43 -0
  200. package/src/llm/google/utils/common.ts +646 -0
  201. package/src/llm/google/utils/tools.ts +160 -0
  202. package/src/llm/google/utils/zod_to_genai_parameters.ts +86 -0
  203. package/src/llm/ollama/index.ts +89 -0
  204. package/src/llm/ollama/utils.ts +193 -0
  205. package/src/llm/openai/index.ts +600 -14
  206. package/src/llm/openai/types.ts +24 -0
  207. package/src/llm/openai/utils/index.ts +912 -0
  208. package/src/llm/openai/utils/isReasoningModel.test.ts +90 -0
  209. package/src/llm/providers.ts +10 -9
  210. package/src/llm/text.ts +26 -7
  211. package/src/llm/vertexai/index.ts +360 -0
  212. package/src/messages/reducer.ts +80 -0
  213. package/src/run.ts +181 -112
  214. package/src/scripts/ant_web_search.ts +158 -0
  215. package/src/scripts/args.ts +12 -8
  216. package/src/scripts/cli4.ts +29 -21
  217. package/src/scripts/cli5.ts +29 -21
  218. package/src/scripts/code_exec.ts +54 -23
  219. package/src/scripts/code_exec_files.ts +48 -17
  220. package/src/scripts/code_exec_simple.ts +46 -27
  221. package/src/scripts/handoff-test.ts +135 -0
  222. package/src/scripts/image.ts +52 -20
  223. package/src/scripts/multi-agent-conditional.ts +220 -0
  224. package/src/scripts/multi-agent-example-output.md +110 -0
  225. package/src/scripts/multi-agent-parallel.ts +337 -0
  226. package/src/scripts/multi-agent-sequence.ts +212 -0
  227. package/src/scripts/multi-agent-test.ts +186 -0
  228. package/src/scripts/search.ts +1 -9
  229. package/src/scripts/simple.ts +25 -10
  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 +139 -60
  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 +272 -103
  251. package/src/types/llm.ts +25 -12
  252. package/src/types/run.ts +51 -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
@@ -0,0 +1,135 @@
1
+ import { config } from 'dotenv';
2
+ config();
3
+
4
+ import { z } from 'zod';
5
+ import { tool } from '@langchain/core/tools';
6
+ import { ChatAnthropic } from '@langchain/anthropic';
7
+ import { createReactAgent } from '@langchain/langgraph/prebuilt';
8
+ import {
9
+ StateGraph,
10
+ MessagesAnnotation,
11
+ Command,
12
+ START,
13
+ getCurrentTaskInput,
14
+ END,
15
+ } from '@langchain/langgraph';
16
+ import { ToolMessage } from '@langchain/core/messages';
17
+
18
+ interface CreateHandoffToolParams {
19
+ agentName: string;
20
+ description?: string;
21
+ }
22
+
23
+ const createHandoffTool = ({
24
+ agentName,
25
+ description,
26
+ }: CreateHandoffToolParams) => {
27
+ const toolName = `transfer_to_${agentName}`;
28
+ const toolDescription = description || `Ask agent '${agentName}' for help`;
29
+
30
+ const handoffTool = tool(
31
+ async (_, config) => {
32
+ const toolMessage = new ToolMessage({
33
+ content: `Successfully transferred to ${agentName}`,
34
+ name: toolName,
35
+ tool_call_id: config.toolCall.id,
36
+ });
37
+
38
+ // inject the current agent state
39
+ const state =
40
+ getCurrentTaskInput() as (typeof MessagesAnnotation)['State'];
41
+ return new Command({
42
+ goto: agentName,
43
+ update: { messages: state.messages.concat(toolMessage) },
44
+ graph: Command.PARENT,
45
+ });
46
+ },
47
+ {
48
+ name: toolName,
49
+ schema: z.object({}),
50
+ description: toolDescription,
51
+ }
52
+ );
53
+
54
+ return handoffTool;
55
+ };
56
+
57
+ const bookHotel = tool(
58
+ async (input: { hotel_name: string }) => {
59
+ return `Successfully booked a stay at ${input.hotel_name}.`;
60
+ },
61
+ {
62
+ name: 'book_hotel',
63
+ description: 'Book a hotel',
64
+ schema: z.object({
65
+ hotel_name: z.string().describe('The name of the hotel to book'),
66
+ }),
67
+ }
68
+ );
69
+
70
+ const bookFlight = tool(
71
+ async (input: { from_airport: string; to_airport: string }) => {
72
+ return `Successfully booked a flight from ${input.from_airport} to ${input.to_airport}.`;
73
+ },
74
+ {
75
+ name: 'book_flight',
76
+ description: 'Book a flight',
77
+ schema: z.object({
78
+ from_airport: z.string().describe('The departure airport code'),
79
+ to_airport: z.string().describe('The arrival airport code'),
80
+ }),
81
+ }
82
+ );
83
+
84
+ const transferToHotelAssistant = createHandoffTool({
85
+ agentName: 'hotel_assistant',
86
+ description: 'Transfer user to the hotel-booking assistant.',
87
+ });
88
+
89
+ const transferToFlightAssistant = createHandoffTool({
90
+ agentName: 'flight_assistant',
91
+ description: 'Transfer user to the flight-booking assistant.',
92
+ });
93
+
94
+ const llm = new ChatAnthropic({
95
+ modelName: 'claude-3-5-sonnet-latest',
96
+ apiKey: process.env.ANTHROPIC_API_KEY,
97
+ });
98
+
99
+ const flightAssistant = createReactAgent({
100
+ llm,
101
+ tools: [bookFlight, transferToHotelAssistant],
102
+ prompt: 'You are a flight booking assistant',
103
+ name: 'flight_assistant',
104
+ });
105
+
106
+ const hotelAssistant = createReactAgent({
107
+ llm,
108
+ tools: [bookHotel, transferToFlightAssistant],
109
+ prompt: 'You are a hotel booking assistant',
110
+ name: 'hotel_assistant',
111
+ });
112
+
113
+ const multiAgentGraph = new StateGraph(MessagesAnnotation)
114
+ .addNode('flight_assistant', flightAssistant, {
115
+ ends: ['hotel_assistant', END],
116
+ })
117
+ .addNode('hotel_assistant', hotelAssistant, {
118
+ ends: ['flight_assistant', END],
119
+ })
120
+ .addEdge(START, 'flight_assistant')
121
+ .compile();
122
+
123
+ const stream = await multiAgentGraph.stream({
124
+ messages: [
125
+ {
126
+ role: 'user',
127
+ content: 'book a flight from BOS to JFK and a stay at McKittrick Hotel',
128
+ },
129
+ ],
130
+ });
131
+
132
+ for await (const chunk of stream) {
133
+ console.log(chunk);
134
+ console.log('\n');
135
+ }
@@ -5,14 +5,17 @@ import { HumanMessage, AIMessage, BaseMessage } from '@langchain/core/messages';
5
5
  import type { RunnableConfig } from '@langchain/core/runnables';
6
6
  import type * as t from '@/types';
7
7
  import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
8
- import { ToolEndHandler, ModelEndHandler, createMetadataAggregator } from '@/events';
8
+ import {
9
+ ToolEndHandler,
10
+ ModelEndHandler,
11
+ createMetadataAggregator,
12
+ } from '@/events';
9
13
  import { fetchRandomImageTool, fetchRandomImageURL } from '@/tools/example';
10
14
  import { getLLMConfig } from '@/utils/llmConfig';
11
15
  import { getArgs } from '@/scripts/args';
12
16
  import { GraphEvents } from '@/common';
13
17
  import { Run } from '@/run';
14
18
 
15
-
16
19
  const conversationHistory: BaseMessage[] = [];
17
20
 
18
21
  async function testCodeExecution(): Promise<void> {
@@ -23,38 +26,57 @@ async function testCodeExecution(): Promise<void> {
23
26
  [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),
24
27
  [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
25
28
  [GraphEvents.ON_RUN_STEP_COMPLETED]: {
26
- handle: (event: GraphEvents.ON_RUN_STEP_COMPLETED, data: t.StreamEventData): void => {
29
+ handle: (
30
+ event: GraphEvents.ON_RUN_STEP_COMPLETED,
31
+ data: t.StreamEventData
32
+ ): void => {
27
33
  console.log('====== ON_RUN_STEP_COMPLETED ======');
28
34
  console.dir(data, { depth: null });
29
- aggregateContent({ event, data: data as unknown as { result: t.ToolEndEvent } });
30
- }
35
+ aggregateContent({
36
+ event,
37
+ data: data as unknown as { result: t.ToolEndEvent },
38
+ });
39
+ },
31
40
  },
32
41
  [GraphEvents.ON_RUN_STEP]: {
33
- handle: (event: GraphEvents.ON_RUN_STEP, data: t.StreamEventData): void => {
42
+ handle: (
43
+ event: GraphEvents.ON_RUN_STEP,
44
+ data: t.StreamEventData
45
+ ): void => {
34
46
  console.log('====== ON_RUN_STEP ======');
35
47
  console.dir(data, { depth: null });
36
48
  aggregateContent({ event, data: data as t.RunStep });
37
- }
49
+ },
38
50
  },
39
51
  [GraphEvents.ON_RUN_STEP_DELTA]: {
40
- handle: (event: GraphEvents.ON_RUN_STEP_DELTA, data: t.StreamEventData): void => {
52
+ handle: (
53
+ event: GraphEvents.ON_RUN_STEP_DELTA,
54
+ data: t.StreamEventData
55
+ ): void => {
41
56
  console.log('====== ON_RUN_STEP_DELTA ======');
42
57
  console.dir(data, { depth: null });
43
58
  aggregateContent({ event, data: data as t.RunStepDeltaEvent });
44
- }
59
+ },
45
60
  },
46
61
  [GraphEvents.ON_MESSAGE_DELTA]: {
47
- handle: (event: GraphEvents.ON_MESSAGE_DELTA, data: t.StreamEventData): void => {
62
+ handle: (
63
+ event: GraphEvents.ON_MESSAGE_DELTA,
64
+ data: t.StreamEventData
65
+ ): void => {
48
66
  console.log('====== ON_MESSAGE_DELTA ======');
49
67
  console.dir(data, { depth: null });
50
68
  aggregateContent({ event, data: data as t.MessageDeltaEvent });
51
- }
69
+ },
52
70
  },
53
71
  [GraphEvents.TOOL_START]: {
54
- handle: (_event: string, data: t.StreamEventData, metadata?: Record<string, unknown>): void => {
72
+ handle: (
73
+ _event: string,
74
+ data: t.StreamEventData,
75
+ metadata?: Record<string, unknown>
76
+ ): void => {
55
77
  console.log('====== TOOL_START ======');
56
78
  console.dir(data, { depth: null });
57
- }
79
+ },
58
80
  },
59
81
  };
60
82
 
@@ -67,14 +89,19 @@ async function testCodeExecution(): Promise<void> {
67
89
  llmConfig,
68
90
  tools: [fetchRandomImageTool],
69
91
  // tools: [fetchRandomImageURL],
70
- instructions: 'You are a friendly AI assistant with internet capabilities. Always address the user by their name.',
92
+ instructions:
93
+ 'You are a friendly AI assistant with internet capabilities. Always address the user by their name.',
71
94
  additional_instructions: `The user's name is ${userName} and they are located in ${location}.`,
72
95
  },
73
96
  returnContent: true,
74
97
  customHandlers,
75
98
  });
76
99
 
77
- const config: Partial<RunnableConfig> & { version: 'v1' | 'v2'; run_id?: string; streamMode: string } = {
100
+ const config: Partial<RunnableConfig> & {
101
+ version: 'v1' | 'v2';
102
+ run_id?: string;
103
+ streamMode: string;
104
+ } = {
78
105
  configurable: {
79
106
  provider,
80
107
  thread_id: 'conversation-num-1',
@@ -109,7 +136,9 @@ async function testCodeExecution(): Promise<void> {
109
136
  inputs = {
110
137
  messages: conversationHistory,
111
138
  };
112
- const finalContentParts2 = await run.processStream(inputs, config, { keepContent: true });
139
+ const finalContentParts2 = await run.processStream(inputs, config, {
140
+ keepContent: true,
141
+ });
113
142
  const finalMessages2 = run.getRunMessages();
114
143
  if (finalMessages2) {
115
144
  conversationHistory.push(...finalMessages2);
@@ -119,12 +148,15 @@ async function testCodeExecution(): Promise<void> {
119
148
 
120
149
  const { handleLLMEnd, collected } = createMetadataAggregator();
121
150
  const titleResult = await run.generateTitle({
151
+ provider,
122
152
  inputText: userMessage2,
123
153
  contentParts,
124
154
  chainOptions: {
125
- callbacks: [{
126
- handleLLMEnd,
127
- }],
155
+ callbacks: [
156
+ {
157
+ handleLLMEnd,
158
+ },
159
+ ],
128
160
  },
129
161
  });
130
162
  console.log('Generated Title:', titleResult);
@@ -143,4 +175,4 @@ testCodeExecution().catch((err) => {
143
175
  console.log('Conversation history:');
144
176
  console.dir(conversationHistory, { depth: null });
145
177
  process.exit(1);
146
- });
178
+ });
@@ -0,0 +1,220 @@
1
+ import { config } from 'dotenv';
2
+ config();
3
+
4
+ import { HumanMessage, BaseMessage } from '@langchain/core/messages';
5
+ import { Run } from '@/run';
6
+ import { Providers, GraphEvents } from '@/common';
7
+ import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
8
+ import { ToolEndHandler, ModelEndHandler } from '@/events';
9
+ import type * as t from '@/types';
10
+
11
+ const conversationHistory: BaseMessage[] = [];
12
+
13
+ /**
14
+ * Example of conditional multi-agent system
15
+ *
16
+ * Graph structure:
17
+ * START -> classifier
18
+ * classifier -> technical_expert (if technical question)
19
+ * classifier -> business_expert (if business question)
20
+ * classifier -> general_assistant (otherwise)
21
+ * [all experts] -> END
22
+ */
23
+ async function testConditionalMultiAgent() {
24
+ console.log('Testing Conditional Multi-Agent System...\n');
25
+
26
+ // Set up content aggregator
27
+ const { contentParts, aggregateContent } = createContentAggregator();
28
+
29
+ // Define specialized agents
30
+ const agents: t.AgentInputs[] = [
31
+ {
32
+ agentId: 'classifier',
33
+ provider: Providers.ANTHROPIC,
34
+ clientOptions: {
35
+ modelName: 'claude-3-5-sonnet-latest',
36
+ apiKey: process.env.ANTHROPIC_API_KEY,
37
+ },
38
+ instructions:
39
+ 'You are a query classifier. Analyze user questions and determine if they are technical, business-related, or general.',
40
+ maxContextTokens: 8000,
41
+ },
42
+ {
43
+ agentId: 'technical_expert',
44
+ provider: Providers.ANTHROPIC,
45
+ clientOptions: {
46
+ modelName: 'claude-3-5-sonnet-latest',
47
+ apiKey: process.env.ANTHROPIC_API_KEY,
48
+ },
49
+ instructions:
50
+ 'You are a technical expert. Provide detailed technical answers about programming, systems, and technology.',
51
+ maxContextTokens: 8000,
52
+ },
53
+ {
54
+ agentId: 'business_expert',
55
+ provider: Providers.ANTHROPIC,
56
+ clientOptions: {
57
+ modelName: 'claude-3-5-sonnet-latest',
58
+ apiKey: process.env.ANTHROPIC_API_KEY,
59
+ },
60
+ instructions:
61
+ 'You are a business expert. Provide insights on business strategy, operations, and management.',
62
+ maxContextTokens: 8000,
63
+ },
64
+ {
65
+ agentId: 'general_assistant',
66
+ provider: Providers.ANTHROPIC,
67
+ clientOptions: {
68
+ modelName: 'claude-3-5-sonnet-latest',
69
+ apiKey: process.env.ANTHROPIC_API_KEY,
70
+ },
71
+ instructions:
72
+ 'You are a helpful general assistant. Answer questions on a wide range of topics.',
73
+ maxContextTokens: 8000,
74
+ },
75
+ ];
76
+
77
+ // Define conditional edges
78
+ // These create handoff tools with conditional routing logic
79
+ const edges: t.GraphEdge[] = [
80
+ {
81
+ from: 'classifier',
82
+ to: ['technical_expert', 'business_expert', 'general_assistant'],
83
+ description: 'Route to appropriate expert based on query type',
84
+ condition: (state: t.BaseGraphState) => {
85
+ // Simple keyword-based routing for demo
86
+ // In a real system, this would use the classifier's analysis
87
+ const lastMessage = state.messages[state.messages.length - 1];
88
+ const content = lastMessage.content?.toString().toLowerCase() || '';
89
+
90
+ if (
91
+ content.includes('code') ||
92
+ content.includes('programming') ||
93
+ content.includes('technical')
94
+ ) {
95
+ return 'technical_expert';
96
+ } else if (
97
+ content.includes('business') ||
98
+ content.includes('strategy') ||
99
+ content.includes('market')
100
+ ) {
101
+ return 'business_expert';
102
+ } else {
103
+ return 'general_assistant';
104
+ }
105
+ },
106
+ },
107
+ ];
108
+
109
+ // Track selected expert
110
+ let selectedExpert = '';
111
+
112
+ // Create custom handlers
113
+ const customHandlers = {
114
+ [GraphEvents.TOOL_END]: new ToolEndHandler(),
115
+ [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),
116
+ [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
117
+ [GraphEvents.ON_RUN_STEP_COMPLETED]: {
118
+ handle: (
119
+ event: GraphEvents.ON_RUN_STEP_COMPLETED,
120
+ data: t.StreamEventData
121
+ ): void => {
122
+ aggregateContent({
123
+ event,
124
+ data: data as unknown as { result: t.ToolEndEvent },
125
+ });
126
+ },
127
+ },
128
+ [GraphEvents.ON_RUN_STEP]: {
129
+ handle: (
130
+ event: GraphEvents.ON_RUN_STEP,
131
+ data: t.StreamEventData
132
+ ): void => {
133
+ const runStepData = data as any;
134
+ if (runStepData?.name && runStepData.name !== 'classifier') {
135
+ selectedExpert = runStepData.name;
136
+ console.log(`Routing to: ${selectedExpert}`);
137
+ }
138
+ aggregateContent({ event, data: data as t.RunStep });
139
+ },
140
+ },
141
+ [GraphEvents.ON_RUN_STEP_DELTA]: {
142
+ handle: (
143
+ event: GraphEvents.ON_RUN_STEP_DELTA,
144
+ data: t.StreamEventData
145
+ ): void => {
146
+ aggregateContent({ event, data: data as t.RunStepDeltaEvent });
147
+ },
148
+ },
149
+ [GraphEvents.ON_MESSAGE_DELTA]: {
150
+ handle: (
151
+ event: GraphEvents.ON_MESSAGE_DELTA,
152
+ data: t.StreamEventData
153
+ ): void => {
154
+ aggregateContent({ event, data: data as t.MessageDeltaEvent });
155
+ },
156
+ },
157
+ };
158
+
159
+ // Create multi-agent run configuration
160
+ const runConfig: t.RunConfig = {
161
+ runId: `conditional-multi-agent-${Date.now()}`,
162
+ graphConfig: {
163
+ type: 'multi-agent',
164
+ agents,
165
+ edges,
166
+ },
167
+ customHandlers,
168
+ returnContent: true,
169
+ };
170
+
171
+ try {
172
+ // Create and execute the run
173
+ const run = await Run.create(runConfig);
174
+
175
+ // Test with different types of questions
176
+ const testQuestions = [
177
+ 'How do I implement a binary search tree in Python?',
178
+ 'What are the key strategies for market expansion?',
179
+ 'What is the capital of France?',
180
+ ];
181
+
182
+ const config = {
183
+ configurable: {
184
+ thread_id: 'conditional-conversation-1',
185
+ },
186
+ streamMode: 'values',
187
+ version: 'v2' as const,
188
+ };
189
+
190
+ for (const question of testQuestions) {
191
+ console.log(`\n--- Processing: "${question}" ---\n`);
192
+
193
+ // Reset for each question
194
+ selectedExpert = '';
195
+ conversationHistory.length = 0;
196
+ conversationHistory.push(new HumanMessage(question));
197
+
198
+ // Process with streaming
199
+ const inputs = {
200
+ messages: conversationHistory,
201
+ };
202
+
203
+ const finalContentParts = await run.processStream(inputs, config);
204
+ const finalMessages = run.getRunMessages();
205
+
206
+ if (finalMessages) {
207
+ conversationHistory.push(...finalMessages);
208
+ }
209
+
210
+ console.log(`\n\nExpert used: ${selectedExpert}`);
211
+ console.log('Content parts:', contentParts.length);
212
+ console.log('---');
213
+ }
214
+ } catch (error) {
215
+ console.error('Error in conditional multi-agent test:', error);
216
+ }
217
+ }
218
+
219
+ // Run the test
220
+ testConditionalMultiAgent();
@@ -0,0 +1,110 @@
1
+ # Multi-Agent Test Scripts - Example Output
2
+
3
+ ## multi-agent-test.ts
4
+
5
+ ```
6
+ Testing Multi-Agent Handoff System...
7
+
8
+ Invoking multi-agent graph...
9
+
10
+ ====== ON_RUN_STEP ======
11
+ { name: 'flight_assistant', type: 'agent', ... }
12
+ [flight_assistant] Starting...
13
+
14
+ ====== CHAT_MODEL_STREAM ======
15
+ I'll help you book a flight from Boston to New York...
16
+
17
+ ====== TOOL_START ======
18
+ { name: 'transfer_to_hotel_assistant', ... }
19
+
20
+ ====== ON_RUN_STEP_COMPLETED ======
21
+ [flight_assistant] Completed
22
+
23
+ ====== ON_RUN_STEP ======
24
+ { name: 'hotel_assistant', type: 'agent', ... }
25
+ [hotel_assistant] Starting...
26
+
27
+ ====== CHAT_MODEL_STREAM ======
28
+ Great! I'll help you find a hotel near Times Square...
29
+
30
+ Final content parts:
31
+ [
32
+ { type: 'text', text: "I'll help you book a flight..." },
33
+ { type: 'tool_use', name: 'transfer_to_hotel_assistant', ... },
34
+ { type: 'text', text: "Great! I'll help you find a hotel..." }
35
+ ]
36
+ ```
37
+
38
+ ## multi-agent-parallel.ts
39
+
40
+ ```
41
+ Testing Parallel Multi-Agent System (Fan-in/Fan-out)...
42
+
43
+ Invoking parallel multi-agent graph...
44
+
45
+ ====== ON_RUN_STEP ======
46
+ [researcher] Starting analysis...
47
+
48
+ ====== ON_RUN_STEP ======
49
+ [analyst1] Starting analysis...
50
+ [analyst2] Starting analysis...
51
+ [analyst3] Starting analysis...
52
+
53
+ ====== CHAT_MODEL_STREAM ======
54
+ [analyst1] From a financial perspective...
55
+ [analyst2] From a technical perspective...
56
+ [analyst3] From a market perspective...
57
+
58
+ ====== ON_RUN_STEP_COMPLETED ======
59
+ [analyst1] Completed analysis
60
+ [analyst2] Completed analysis
61
+ [analyst3] Completed analysis
62
+
63
+ ====== ON_RUN_STEP ======
64
+ [summarizer] Starting analysis...
65
+
66
+ Final content parts: 5 parts
67
+ ```
68
+
69
+ ## multi-agent-conditional.ts
70
+
71
+ ```
72
+ Testing Conditional Multi-Agent System...
73
+
74
+ --- Processing: "How do I implement a binary search tree in Python?" ---
75
+
76
+ ====== ON_RUN_STEP ======
77
+ { name: 'classifier', type: 'agent', ... }
78
+
79
+ ====== ON_RUN_STEP ======
80
+ { name: 'technical_expert', type: 'agent', ... }
81
+ Routing to: technical_expert
82
+
83
+ ====== CHAT_MODEL_STREAM ======
84
+ To implement a binary search tree in Python, you'll need to create a Node class...
85
+
86
+ Expert used: technical_expert
87
+ Content parts: 2
88
+ ---
89
+
90
+ --- Processing: "What are the key strategies for market expansion?" ---
91
+
92
+ Routing to: business_expert
93
+ ...
94
+
95
+ --- Processing: "What is the capital of France?" ---
96
+
97
+ Routing to: general_assistant
98
+ ...
99
+ ```
100
+
101
+ ## Key Features of Updated Scripts
102
+
103
+ 1. **Proper Event Handling**: All GraphEvents are properly handled with custom handlers
104
+ 2. **Content Aggregation**: Using `createContentAggregator()` to collect all content parts
105
+ 3. **Stream Output**: Real-time token streaming via `ChatModelStreamHandler`
106
+ 4. **Debug Logging**: Comprehensive event logging for debugging multi-agent flows
107
+ 5. **Conversation History**: Proper tracking of messages across agent interactions
108
+ 6. **Return Content**: `returnContent: true` ensures content parts are returned
109
+
110
+ The scripts now follow the same patterns as `simple.ts` and `tools.ts`, providing consistent behavior and comprehensive event handling across all multi-agent scenarios.