@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,94 @@
1
+ import type { AIMessageChunk } from '@langchain/core/messages';
2
+ import type { ChatOpenAIReasoningSummary } from '@langchain/openai';
3
+ import { getChunkContent } from './stream';
4
+ import { Providers } from '@/common';
5
+
6
+ describe('getChunkContent', () => {
7
+ it('should handle reasoning content for OpenAI/Azure providers', () => {
8
+ const chunk: Partial<AIMessageChunk> = {
9
+ content: 'Regular content',
10
+ additional_kwargs: {
11
+ reasoning: {
12
+ summary: [{ text: 'Reasoning summary text' }],
13
+ } as Partial<ChatOpenAIReasoningSummary>,
14
+ },
15
+ };
16
+
17
+ const result = getChunkContent({
18
+ chunk,
19
+ provider: Providers.OPENAI,
20
+ reasoningKey: 'reasoning',
21
+ });
22
+
23
+ expect(result).toBe('Reasoning summary text');
24
+ });
25
+
26
+ it('should fallback to reasoningKey when no OpenAI reasoning summary', () => {
27
+ const chunk: Partial<AIMessageChunk> = {
28
+ content: 'Regular content',
29
+ additional_kwargs: {
30
+ reasoning_content: 'Reasoning from key',
31
+ },
32
+ };
33
+
34
+ const result = getChunkContent({
35
+ chunk,
36
+ reasoningKey: 'reasoning_content',
37
+ });
38
+
39
+ expect(result).toBe('Reasoning from key');
40
+ });
41
+
42
+ it('should fallback to chunk.content when reasoningKey value is null or undefined', () => {
43
+ const chunk: Partial<AIMessageChunk> = {
44
+ content: 'Fallback content',
45
+ additional_kwargs: {
46
+ reasoning_content: null,
47
+ },
48
+ };
49
+
50
+ const result = getChunkContent({
51
+ chunk,
52
+ reasoningKey: 'reasoning_content',
53
+ });
54
+
55
+ expect(result).toBe('Fallback content');
56
+ });
57
+
58
+ it('should fallback to chunk.content when reasoningKey value is empty string', () => {
59
+ const chunk: Partial<AIMessageChunk> = {
60
+ content: ' can',
61
+ additional_kwargs: {
62
+ reasoning_content: '',
63
+ },
64
+ };
65
+
66
+ const result = getChunkContent({
67
+ chunk,
68
+ reasoningKey: 'reasoning_content',
69
+ });
70
+
71
+ expect(result).toBe(' can');
72
+ });
73
+
74
+ it('should return undefined when no content is available', () => {
75
+ const chunk: Partial<AIMessageChunk> = {
76
+ additional_kwargs: {},
77
+ };
78
+
79
+ const result = getChunkContent({
80
+ chunk,
81
+ reasoningKey: 'reasoning',
82
+ });
83
+
84
+ expect(result).toBeUndefined();
85
+ });
86
+
87
+ it('should handle missing chunk gracefully', () => {
88
+ const result = getChunkContent({
89
+ reasoningKey: 'reasoning',
90
+ });
91
+
92
+ expect(result).toBeUndefined();
93
+ });
94
+ });
package/src/stream.ts CHANGED
@@ -1,10 +1,22 @@
1
1
  // src/stream.ts
2
+ import type { ChatOpenAIReasoningSummary } from '@langchain/openai';
2
3
  import type { AIMessageChunk } from '@langchain/core/messages';
3
4
  import type { ToolCall } from '@langchain/core/messages/tool';
4
- import type { Graph } from '@/graphs';
5
+ import type { AgentContext } from '@/agents/AgentContext';
6
+ import type { StandardGraph } from '@/graphs';
5
7
  import type * as t from '@/types';
6
- import { StepTypes, ContentTypes, GraphEvents, ToolCallTypes } from '@/common';
7
- import { handleToolCalls, handleToolCallChunks } from '@/tools/handlers';
8
+ import {
9
+ ToolCallTypes,
10
+ ContentTypes,
11
+ GraphEvents,
12
+ StepTypes,
13
+ Providers,
14
+ } from '@/common';
15
+ import {
16
+ handleServerToolResult,
17
+ handleToolCallChunks,
18
+ handleToolCalls,
19
+ } from '@/tools/handlers';
8
20
  import { getMessageId } from '@/messages';
9
21
 
10
22
  /**
@@ -66,13 +78,48 @@ function getNonEmptyValue(possibleValues: string[]): string | undefined {
66
78
  }
67
79
  return undefined;
68
80
  }
81
+
82
+ export function getChunkContent({
83
+ chunk,
84
+ provider,
85
+ reasoningKey,
86
+ }: {
87
+ chunk?: Partial<AIMessageChunk>;
88
+ provider?: Providers;
89
+ reasoningKey: 'reasoning_content' | 'reasoning';
90
+ }): string | t.MessageContentComplex[] | undefined {
91
+ if (
92
+ (provider === Providers.OPENAI || provider === Providers.AZURE) &&
93
+ (
94
+ chunk?.additional_kwargs?.reasoning as
95
+ | Partial<ChatOpenAIReasoningSummary>
96
+ | undefined
97
+ )?.summary?.[0]?.text != null &&
98
+ ((
99
+ chunk?.additional_kwargs?.reasoning as
100
+ | Partial<ChatOpenAIReasoningSummary>
101
+ | undefined
102
+ )?.summary?.[0]?.text?.length ?? 0) > 0
103
+ ) {
104
+ return (
105
+ chunk?.additional_kwargs?.reasoning as
106
+ | Partial<ChatOpenAIReasoningSummary>
107
+ | undefined
108
+ )?.summary?.[0]?.text;
109
+ }
110
+ return (
111
+ ((chunk?.additional_kwargs?.[reasoningKey] as string | undefined) ?? '') ||
112
+ chunk?.content
113
+ );
114
+ }
115
+
69
116
  export class ChatModelStreamHandler implements t.EventHandler {
70
- handle(
117
+ async handle(
71
118
  event: string,
72
119
  data: t.StreamEventData,
73
120
  metadata?: Record<string, unknown>,
74
- graph?: Graph
75
- ): void {
121
+ graph?: StandardGraph
122
+ ): Promise<void> {
76
123
  if (!graph) {
77
124
  throw new Error('Graph not found');
78
125
  }
@@ -84,20 +131,35 @@ export class ChatModelStreamHandler implements t.EventHandler {
84
131
  return;
85
132
  }
86
133
 
87
- const chunk = data.chunk as Partial<AIMessageChunk>;
88
- const content =
89
- (chunk.additional_kwargs?.[graph.reasoningKey] as string | undefined) ??
90
- chunk.content;
91
- this.handleReasoning(chunk, graph);
134
+ const agentContext = graph.getAgentContext(metadata);
92
135
 
136
+ const chunk = data.chunk as Partial<AIMessageChunk>;
137
+ const content = getChunkContent({
138
+ chunk,
139
+ reasoningKey: agentContext.reasoningKey,
140
+ provider: agentContext.provider,
141
+ });
142
+ const skipHandling = await handleServerToolResult({
143
+ graph,
144
+ content,
145
+ metadata,
146
+ agentContext,
147
+ });
148
+ if (skipHandling) {
149
+ return;
150
+ }
151
+ this.handleReasoning(chunk, agentContext);
93
152
  let hasToolCalls = false;
94
153
  if (
95
154
  chunk.tool_calls &&
96
155
  chunk.tool_calls.length > 0 &&
97
- chunk.tool_calls.every((tc) => tc.id != null && tc.id !== '')
156
+ chunk.tool_calls.every(
157
+ (tc) =>
158
+ tc.id != null && tc.id !== '' && tc.name != null && tc.name !== ''
159
+ )
98
160
  ) {
99
161
  hasToolCalls = true;
100
- handleToolCalls(chunk.tool_calls, metadata, graph);
162
+ await handleToolCalls(chunk.tool_calls, metadata, graph);
101
163
  }
102
164
 
103
165
  const hasToolCallChunks =
@@ -106,18 +168,16 @@ export class ChatModelStreamHandler implements t.EventHandler {
106
168
  typeof content === 'undefined' ||
107
169
  !content.length ||
108
170
  (typeof content === 'string' && !content);
109
- const isEmptyChunk = isEmptyContent && !hasToolCallChunks;
110
- const chunkId = chunk.id ?? '';
111
- if (isEmptyChunk && chunkId && chunkId.startsWith('msg')) {
112
- if (graph.messageIdsByStepKey.has(chunkId)) {
113
- return;
114
- } else if (graph.prelimMessageIdsByStepKey.has(chunkId)) {
115
- return;
116
- }
117
171
 
172
+ /** Set a preliminary message ID if found in empty chunk */
173
+ const isEmptyChunk = isEmptyContent && !hasToolCallChunks;
174
+ if (
175
+ isEmptyChunk &&
176
+ (chunk.id ?? '') !== '' &&
177
+ !graph.prelimMessageIdsByStepKey.has(chunk.id ?? '')
178
+ ) {
118
179
  const stepKey = graph.getStepKey(metadata);
119
- graph.prelimMessageIdsByStepKey.set(stepKey, chunkId);
120
- return;
180
+ graph.prelimMessageIdsByStepKey.set(stepKey, chunk.id ?? '');
121
181
  } else if (isEmptyChunk) {
122
182
  return;
123
183
  }
@@ -130,7 +190,7 @@ export class ChatModelStreamHandler implements t.EventHandler {
130
190
  chunk.tool_call_chunks.length &&
131
191
  typeof chunk.tool_call_chunks[0]?.index === 'number'
132
192
  ) {
133
- handleToolCallChunks({
193
+ await handleToolCallChunks({
134
194
  graph,
135
195
  stepKey,
136
196
  toolCallChunks: chunk.tool_call_chunks,
@@ -143,7 +203,7 @@ export class ChatModelStreamHandler implements t.EventHandler {
143
203
 
144
204
  const message_id = getMessageId(stepKey, graph) ?? '';
145
205
  if (message_id) {
146
- graph.dispatchRunStep(stepKey, {
206
+ await graph.dispatchRunStep(stepKey, {
147
207
  type: StepTypes.MESSAGE_CREATION,
148
208
  message_creation: {
149
209
  message_id,
@@ -181,8 +241,8 @@ hasToolCallChunks: ${hasToolCallChunks}
181
241
  ) {
182
242
  return;
183
243
  } else if (typeof content === 'string') {
184
- if (graph.currentTokenType === ContentTypes.TEXT) {
185
- graph.dispatchMessageDelta(stepId, {
244
+ if (agentContext.currentTokenType === ContentTypes.TEXT) {
245
+ await graph.dispatchMessageDelta(stepId, {
186
246
  content: [
187
247
  {
188
248
  type: ContentTypes.TEXT,
@@ -190,10 +250,10 @@ hasToolCallChunks: ${hasToolCallChunks}
190
250
  },
191
251
  ],
192
252
  });
193
- } else if (graph.currentTokenType === 'think_and_text') {
253
+ } else if (agentContext.currentTokenType === 'think_and_text') {
194
254
  const { text, thinking } = parseThinkingContent(content);
195
255
  if (thinking) {
196
- graph.dispatchReasoningDelta(stepId, {
256
+ await graph.dispatchReasoningDelta(stepId, {
197
257
  content: [
198
258
  {
199
259
  type: ContentTypes.THINK,
@@ -203,11 +263,11 @@ hasToolCallChunks: ${hasToolCallChunks}
203
263
  });
204
264
  }
205
265
  if (text) {
206
- graph.currentTokenType = ContentTypes.TEXT;
207
- graph.tokenTypeSwitch = 'content';
266
+ agentContext.currentTokenType = ContentTypes.TEXT;
267
+ agentContext.tokenTypeSwitch = 'content';
208
268
  const newStepKey = graph.getStepKey(metadata);
209
269
  const message_id = getMessageId(newStepKey, graph) ?? '';
210
- graph.dispatchRunStep(newStepKey, {
270
+ await graph.dispatchRunStep(newStepKey, {
211
271
  type: StepTypes.MESSAGE_CREATION,
212
272
  message_creation: {
213
273
  message_id,
@@ -215,7 +275,7 @@ hasToolCallChunks: ${hasToolCallChunks}
215
275
  });
216
276
 
217
277
  const newStepId = graph.getStepIdByKey(newStepKey);
218
- graph.dispatchMessageDelta(newStepId, {
278
+ await graph.dispatchMessageDelta(newStepId, {
219
279
  content: [
220
280
  {
221
281
  type: ContentTypes.TEXT,
@@ -225,7 +285,7 @@ hasToolCallChunks: ${hasToolCallChunks}
225
285
  });
226
286
  }
227
287
  } else {
228
- graph.dispatchReasoningDelta(stepId, {
288
+ await graph.dispatchReasoningDelta(stepId, {
229
289
  content: [
230
290
  {
231
291
  type: ContentTypes.THINK,
@@ -237,82 +297,97 @@ hasToolCallChunks: ${hasToolCallChunks}
237
297
  } else if (
238
298
  content.every((c) => c.type?.startsWith(ContentTypes.TEXT) ?? false)
239
299
  ) {
240
- graph.dispatchMessageDelta(stepId, {
300
+ await graph.dispatchMessageDelta(stepId, {
241
301
  content,
242
302
  });
243
303
  } else if (
244
304
  content.every(
245
305
  (c) =>
246
306
  (c.type?.startsWith(ContentTypes.THINKING) ?? false) ||
307
+ (c.type?.startsWith(ContentTypes.REASONING) ?? false) ||
247
308
  (c.type?.startsWith(ContentTypes.REASONING_CONTENT) ?? false)
248
309
  )
249
310
  ) {
250
- graph.dispatchReasoningDelta(stepId, {
311
+ await graph.dispatchReasoningDelta(stepId, {
251
312
  content: content.map((c) => ({
252
313
  type: ContentTypes.THINK,
253
314
  think:
254
315
  (c as t.ThinkingContentText).thinking ??
316
+ (c as Partial<t.GoogleReasoningContentText>).reasoning ??
255
317
  (c as Partial<t.BedrockReasoningContentText>).reasoningText?.text ??
256
318
  '',
257
319
  })),
258
320
  });
259
321
  }
260
322
  }
261
- handleReasoning(chunk: Partial<AIMessageChunk>, graph: Graph): void {
262
- let reasoning_content = chunk.additional_kwargs?.[graph.reasoningKey] as
263
- | string
264
- | undefined;
323
+ handleReasoning(
324
+ chunk: Partial<AIMessageChunk>,
325
+ agentContext: AgentContext
326
+ ): void {
327
+ let reasoning_content = chunk.additional_kwargs?.[
328
+ agentContext.reasoningKey
329
+ ] as string | Partial<ChatOpenAIReasoningSummary> | undefined;
265
330
  if (
266
331
  Array.isArray(chunk.content) &&
267
- (chunk.content[0]?.type === 'thinking' ||
268
- chunk.content[0]?.type === 'reasoning_content')
332
+ (chunk.content[0]?.type === ContentTypes.THINKING ||
333
+ chunk.content[0]?.type === ContentTypes.REASONING ||
334
+ chunk.content[0]?.type === ContentTypes.REASONING_CONTENT)
335
+ ) {
336
+ reasoning_content = 'valid';
337
+ } else if (
338
+ (agentContext.provider === Providers.OPENAI ||
339
+ agentContext.provider === Providers.AZURE) &&
340
+ reasoning_content != null &&
341
+ typeof reasoning_content !== 'string' &&
342
+ reasoning_content.summary?.[0]?.text != null &&
343
+ reasoning_content.summary[0].text
269
344
  ) {
270
345
  reasoning_content = 'valid';
271
346
  }
272
347
  if (
273
348
  reasoning_content != null &&
274
- reasoning_content &&
349
+ reasoning_content !== '' &&
275
350
  (chunk.content == null ||
276
351
  chunk.content === '' ||
277
352
  reasoning_content === 'valid')
278
353
  ) {
279
- graph.currentTokenType = ContentTypes.THINK;
280
- graph.tokenTypeSwitch = 'reasoning';
354
+ agentContext.currentTokenType = ContentTypes.THINK;
355
+ agentContext.tokenTypeSwitch = 'reasoning';
281
356
  return;
282
357
  } else if (
283
- graph.tokenTypeSwitch === 'reasoning' &&
284
- graph.currentTokenType !== ContentTypes.TEXT &&
285
- chunk.content != null &&
286
- chunk.content !== ''
358
+ agentContext.tokenTypeSwitch === 'reasoning' &&
359
+ agentContext.currentTokenType !== ContentTypes.TEXT &&
360
+ ((chunk.content != null && chunk.content !== '') ||
361
+ (chunk.tool_calls?.length ?? 0) > 0)
287
362
  ) {
288
- graph.currentTokenType = ContentTypes.TEXT;
289
- graph.tokenTypeSwitch = 'content';
363
+ agentContext.currentTokenType = ContentTypes.TEXT;
364
+ agentContext.tokenTypeSwitch = 'content';
290
365
  } else if (
291
366
  chunk.content != null &&
292
367
  typeof chunk.content === 'string' &&
293
368
  chunk.content.includes('<think>') &&
294
369
  chunk.content.includes('</think>')
295
370
  ) {
296
- graph.currentTokenType = 'think_and_text';
297
- graph.tokenTypeSwitch = 'content';
371
+ agentContext.currentTokenType = 'think_and_text';
372
+ agentContext.tokenTypeSwitch = 'content';
298
373
  } else if (
299
374
  chunk.content != null &&
300
375
  typeof chunk.content === 'string' &&
301
376
  chunk.content.includes('<think>')
302
377
  ) {
303
- graph.currentTokenType = ContentTypes.THINK;
304
- graph.tokenTypeSwitch = 'content';
378
+ agentContext.currentTokenType = ContentTypes.THINK;
379
+ agentContext.tokenTypeSwitch = 'content';
305
380
  } else if (
306
- graph.lastToken != null &&
307
- graph.lastToken.includes('</think>')
381
+ agentContext.lastToken != null &&
382
+ agentContext.lastToken.includes('</think>')
308
383
  ) {
309
- graph.currentTokenType = ContentTypes.TEXT;
310
- graph.tokenTypeSwitch = 'content';
384
+ agentContext.currentTokenType = ContentTypes.TEXT;
385
+ agentContext.tokenTypeSwitch = 'content';
311
386
  }
312
387
  if (typeof chunk.content !== 'string') {
313
388
  return;
314
389
  }
315
- graph.lastToken = chunk.content;
390
+ agentContext.lastToken = chunk.content;
316
391
  }
317
392
  }
318
393
 
@@ -402,12 +477,19 @@ export function createContentAggregator(): t.ContentAggregatorResult {
402
477
 
403
478
  const toolCallArgs = (contentPart.tool_call as t.ToolCallPart).args;
404
479
  /** When args are a valid object, they are likely already invoked */
405
- const args =
480
+ let args =
406
481
  finalUpdate ||
407
482
  typeof existingContent?.tool_call?.args === 'object' ||
408
483
  typeof toolCallArgs === 'object'
409
484
  ? contentPart.tool_call.args
410
485
  : (existingContent?.tool_call?.args ?? '') + (toolCallArgs ?? '');
486
+ if (
487
+ finalUpdate &&
488
+ args == null &&
489
+ existingContent?.tool_call?.args != null
490
+ ) {
491
+ args = existingContent.tool_call.args;
492
+ }
411
493
 
412
494
  const id =
413
495
  getNonEmptyValue([
@@ -13,7 +13,6 @@ import type { BaseMessage, AIMessage } from '@langchain/core/messages';
13
13
  import type { StructuredToolInterface } from '@langchain/core/tools';
14
14
  import type * as t from '@/types';
15
15
  import { RunnableCallable } from '@/utils';
16
- import { GraphNodeKeys } from '@/common';
17
16
 
18
17
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
18
  export class ToolNode<T = any> extends RunnableCallable<T, T> {
@@ -138,18 +137,33 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
138
137
  }
139
138
  }
140
139
 
141
- export function toolsCondition(
142
- state: BaseMessage[] | typeof MessagesAnnotation.State
143
- ): 'tools' | typeof END {
144
- const message = Array.isArray(state)
140
+ function areToolCallsInvoked(
141
+ message: AIMessage,
142
+ invokedToolIds?: Set<string>
143
+ ): boolean {
144
+ if (!invokedToolIds || invokedToolIds.size === 0) return false;
145
+ return (
146
+ message.tool_calls?.every(
147
+ (toolCall) => toolCall.id != null && invokedToolIds.has(toolCall.id)
148
+ ) ?? false
149
+ );
150
+ }
151
+
152
+ export function toolsCondition<T extends string>(
153
+ state: BaseMessage[] | typeof MessagesAnnotation.State,
154
+ toolNode: T,
155
+ invokedToolIds?: Set<string>
156
+ ): T | typeof END {
157
+ const message: AIMessage = Array.isArray(state)
145
158
  ? state[state.length - 1]
146
159
  : state.messages[state.messages.length - 1];
147
160
 
148
161
  if (
149
162
  'tool_calls' in message &&
150
- ((message as AIMessage).tool_calls?.length ?? 0) > 0
163
+ (message.tool_calls?.length ?? 0) > 0 &&
164
+ !areToolCallsInvoked(message, invokedToolIds)
151
165
  ) {
152
- return GraphNodeKeys.TOOLS;
166
+ return toolNode;
153
167
  } else {
154
168
  return END;
155
169
  }