@librechat/agents 2.4.321 → 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 (266) 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 +61 -13
  51. package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
  52. package/dist/cjs/tools/search/format.cjs +9 -3
  53. package/dist/cjs/tools/search/format.cjs.map +1 -1
  54. package/dist/cjs/tools/search/rerankers.cjs +35 -50
  55. package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
  56. package/dist/cjs/tools/search/schema.cjs +70 -0
  57. package/dist/cjs/tools/search/schema.cjs.map +1 -0
  58. package/dist/cjs/tools/search/search.cjs +145 -38
  59. package/dist/cjs/tools/search/search.cjs.map +1 -1
  60. package/dist/cjs/tools/search/tool.cjs +165 -48
  61. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  62. package/dist/cjs/tools/search/utils.cjs +34 -5
  63. package/dist/cjs/tools/search/utils.cjs.map +1 -1
  64. package/dist/cjs/utils/events.cjs +31 -0
  65. package/dist/cjs/utils/events.cjs.map +1 -0
  66. package/dist/cjs/utils/title.cjs +57 -21
  67. package/dist/cjs/utils/title.cjs.map +1 -1
  68. package/dist/cjs/utils/tokens.cjs +54 -7
  69. package/dist/cjs/utils/tokens.cjs.map +1 -1
  70. package/dist/esm/agents/AgentContext.mjs +216 -0
  71. package/dist/esm/agents/AgentContext.mjs.map +1 -0
  72. package/dist/esm/common/enum.mjs +15 -6
  73. package/dist/esm/common/enum.mjs.map +1 -1
  74. package/dist/esm/events.mjs +10 -6
  75. package/dist/esm/events.mjs.map +1 -1
  76. package/dist/esm/graphs/Graph.mjs +311 -214
  77. package/dist/esm/graphs/Graph.mjs.map +1 -1
  78. package/dist/esm/graphs/MultiAgentGraph.mjs +320 -0
  79. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -0
  80. package/dist/esm/llm/anthropic/index.mjs +54 -9
  81. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  82. package/dist/esm/llm/anthropic/types.mjs.map +1 -1
  83. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +52 -6
  84. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  85. package/dist/esm/llm/anthropic/utils/message_outputs.mjs +22 -2
  86. package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
  87. package/dist/esm/llm/anthropic/utils/tools.mjs +27 -0
  88. package/dist/esm/llm/anthropic/utils/tools.mjs.map +1 -0
  89. package/dist/esm/llm/google/index.mjs +142 -0
  90. package/dist/esm/llm/google/index.mjs.map +1 -0
  91. package/dist/esm/llm/google/utils/common.mjs +471 -0
  92. package/dist/esm/llm/google/utils/common.mjs.map +1 -0
  93. package/dist/esm/llm/ollama/index.mjs +65 -0
  94. package/dist/esm/llm/ollama/index.mjs.map +1 -0
  95. package/dist/esm/llm/ollama/utils.mjs +155 -0
  96. package/dist/esm/llm/ollama/utils.mjs.map +1 -0
  97. package/dist/esm/llm/openai/index.mjs +388 -4
  98. package/dist/esm/llm/openai/index.mjs.map +1 -1
  99. package/dist/esm/llm/openai/utils/index.mjs +666 -0
  100. package/dist/esm/llm/openai/utils/index.mjs.map +1 -0
  101. package/dist/esm/llm/providers.mjs +5 -5
  102. package/dist/esm/llm/providers.mjs.map +1 -1
  103. package/dist/esm/llm/text.mjs +14 -3
  104. package/dist/esm/llm/text.mjs.map +1 -1
  105. package/dist/esm/llm/vertexai/index.mjs +328 -0
  106. package/dist/esm/llm/vertexai/index.mjs.map +1 -0
  107. package/dist/esm/main.mjs +6 -5
  108. package/dist/esm/main.mjs.map +1 -1
  109. package/dist/esm/run.mjs +121 -83
  110. package/dist/esm/run.mjs.map +1 -1
  111. package/dist/esm/stream.mjs +87 -54
  112. package/dist/esm/stream.mjs.map +1 -1
  113. package/dist/esm/tools/ToolNode.mjs +10 -4
  114. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  115. package/dist/esm/tools/handlers.mjs +119 -15
  116. package/dist/esm/tools/handlers.mjs.map +1 -1
  117. package/dist/esm/tools/search/anthropic.mjs +37 -0
  118. package/dist/esm/tools/search/anthropic.mjs.map +1 -0
  119. package/dist/esm/tools/search/firecrawl.mjs +61 -13
  120. package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
  121. package/dist/esm/tools/search/format.mjs +10 -4
  122. package/dist/esm/tools/search/format.mjs.map +1 -1
  123. package/dist/esm/tools/search/rerankers.mjs +35 -50
  124. package/dist/esm/tools/search/rerankers.mjs.map +1 -1
  125. package/dist/esm/tools/search/schema.mjs +61 -0
  126. package/dist/esm/tools/search/schema.mjs.map +1 -0
  127. package/dist/esm/tools/search/search.mjs +146 -39
  128. package/dist/esm/tools/search/search.mjs.map +1 -1
  129. package/dist/esm/tools/search/tool.mjs +164 -47
  130. package/dist/esm/tools/search/tool.mjs.map +1 -1
  131. package/dist/esm/tools/search/utils.mjs +33 -6
  132. package/dist/esm/tools/search/utils.mjs.map +1 -1
  133. package/dist/esm/utils/events.mjs +29 -0
  134. package/dist/esm/utils/events.mjs.map +1 -0
  135. package/dist/esm/utils/title.mjs +57 -22
  136. package/dist/esm/utils/title.mjs.map +1 -1
  137. package/dist/esm/utils/tokens.mjs +54 -8
  138. package/dist/esm/utils/tokens.mjs.map +1 -1
  139. package/dist/types/agents/AgentContext.d.ts +91 -0
  140. package/dist/types/common/enum.d.ts +15 -6
  141. package/dist/types/events.d.ts +5 -4
  142. package/dist/types/graphs/Graph.d.ts +64 -67
  143. package/dist/types/graphs/MultiAgentGraph.d.ts +37 -0
  144. package/dist/types/graphs/index.d.ts +1 -0
  145. package/dist/types/llm/anthropic/index.d.ts +11 -0
  146. package/dist/types/llm/anthropic/types.d.ts +9 -3
  147. package/dist/types/llm/anthropic/utils/message_inputs.d.ts +1 -1
  148. package/dist/types/llm/anthropic/utils/output_parsers.d.ts +4 -4
  149. package/dist/types/llm/anthropic/utils/tools.d.ts +3 -0
  150. package/dist/types/llm/google/index.d.ts +13 -0
  151. package/dist/types/llm/google/types.d.ts +32 -0
  152. package/dist/types/llm/google/utils/common.d.ts +19 -0
  153. package/dist/types/llm/google/utils/tools.d.ts +10 -0
  154. package/dist/types/llm/google/utils/zod_to_genai_parameters.d.ts +14 -0
  155. package/dist/types/llm/ollama/index.d.ts +7 -0
  156. package/dist/types/llm/ollama/utils.d.ts +7 -0
  157. package/dist/types/llm/openai/index.d.ts +72 -3
  158. package/dist/types/llm/openai/types.d.ts +10 -0
  159. package/dist/types/llm/openai/utils/index.d.ts +20 -0
  160. package/dist/types/llm/text.d.ts +1 -1
  161. package/dist/types/llm/vertexai/index.d.ts +293 -0
  162. package/dist/types/messages/reducer.d.ts +9 -0
  163. package/dist/types/run.d.ts +19 -12
  164. package/dist/types/scripts/ant_web_search.d.ts +1 -0
  165. package/dist/types/scripts/args.d.ts +2 -1
  166. package/dist/types/scripts/handoff-test.d.ts +1 -0
  167. package/dist/types/scripts/multi-agent-conditional.d.ts +1 -0
  168. package/dist/types/scripts/multi-agent-parallel.d.ts +1 -0
  169. package/dist/types/scripts/multi-agent-sequence.d.ts +1 -0
  170. package/dist/types/scripts/multi-agent-test.d.ts +1 -0
  171. package/dist/types/stream.d.ts +10 -3
  172. package/dist/types/tools/CodeExecutor.d.ts +2 -2
  173. package/dist/types/tools/ToolNode.d.ts +1 -1
  174. package/dist/types/tools/handlers.d.ts +17 -4
  175. package/dist/types/tools/search/anthropic.d.ts +16 -0
  176. package/dist/types/tools/search/firecrawl.d.ts +16 -0
  177. package/dist/types/tools/search/rerankers.d.ts +8 -5
  178. package/dist/types/tools/search/schema.d.ts +16 -0
  179. package/dist/types/tools/search/tool.d.ts +13 -0
  180. package/dist/types/tools/search/types.d.ts +64 -9
  181. package/dist/types/tools/search/utils.d.ts +9 -2
  182. package/dist/types/types/graph.d.ts +95 -15
  183. package/dist/types/types/llm.d.ts +24 -10
  184. package/dist/types/types/run.d.ts +46 -8
  185. package/dist/types/types/stream.d.ts +16 -2
  186. package/dist/types/types/tools.d.ts +1 -1
  187. package/dist/types/utils/events.d.ts +6 -0
  188. package/dist/types/utils/title.d.ts +2 -1
  189. package/dist/types/utils/tokens.d.ts +24 -0
  190. package/package.json +35 -18
  191. package/src/agents/AgentContext.ts +315 -0
  192. package/src/common/enum.ts +14 -5
  193. package/src/events.ts +24 -13
  194. package/src/graphs/Graph.ts +495 -312
  195. package/src/graphs/MultiAgentGraph.ts +381 -0
  196. package/src/graphs/index.ts +2 -1
  197. package/src/llm/anthropic/Jacob_Lee_Resume_2023.pdf +0 -0
  198. package/src/llm/anthropic/index.ts +78 -13
  199. package/src/llm/anthropic/llm.spec.ts +491 -115
  200. package/src/llm/anthropic/types.ts +39 -3
  201. package/src/llm/anthropic/utils/message_inputs.ts +67 -11
  202. package/src/llm/anthropic/utils/message_outputs.ts +21 -2
  203. package/src/llm/anthropic/utils/output_parsers.ts +25 -6
  204. package/src/llm/anthropic/utils/tools.ts +29 -0
  205. package/src/llm/google/index.ts +218 -0
  206. package/src/llm/google/types.ts +43 -0
  207. package/src/llm/google/utils/common.ts +646 -0
  208. package/src/llm/google/utils/tools.ts +160 -0
  209. package/src/llm/google/utils/zod_to_genai_parameters.ts +86 -0
  210. package/src/llm/ollama/index.ts +89 -0
  211. package/src/llm/ollama/utils.ts +193 -0
  212. package/src/llm/openai/index.ts +600 -14
  213. package/src/llm/openai/types.ts +24 -0
  214. package/src/llm/openai/utils/index.ts +912 -0
  215. package/src/llm/openai/utils/isReasoningModel.test.ts +90 -0
  216. package/src/llm/providers.ts +10 -9
  217. package/src/llm/text.ts +26 -7
  218. package/src/llm/vertexai/index.ts +360 -0
  219. package/src/messages/reducer.ts +80 -0
  220. package/src/run.ts +181 -112
  221. package/src/scripts/ant_web_search.ts +158 -0
  222. package/src/scripts/args.ts +12 -8
  223. package/src/scripts/cli4.ts +29 -21
  224. package/src/scripts/cli5.ts +29 -21
  225. package/src/scripts/code_exec.ts +54 -23
  226. package/src/scripts/code_exec_files.ts +48 -17
  227. package/src/scripts/code_exec_simple.ts +46 -27
  228. package/src/scripts/handoff-test.ts +135 -0
  229. package/src/scripts/image.ts +52 -20
  230. package/src/scripts/multi-agent-conditional.ts +220 -0
  231. package/src/scripts/multi-agent-example-output.md +110 -0
  232. package/src/scripts/multi-agent-parallel.ts +337 -0
  233. package/src/scripts/multi-agent-sequence.ts +212 -0
  234. package/src/scripts/multi-agent-test.ts +186 -0
  235. package/src/scripts/search.ts +4 -12
  236. package/src/scripts/simple.ts +25 -10
  237. package/src/scripts/tools.ts +48 -18
  238. package/src/specs/anthropic.simple.test.ts +150 -34
  239. package/src/specs/azure.simple.test.ts +325 -0
  240. package/src/specs/openai.simple.test.ts +140 -33
  241. package/src/specs/openrouter.simple.test.ts +107 -0
  242. package/src/specs/prune.test.ts +4 -9
  243. package/src/specs/reasoning.test.ts +80 -44
  244. package/src/specs/token-memoization.test.ts +39 -0
  245. package/src/stream.test.ts +94 -0
  246. package/src/stream.ts +139 -60
  247. package/src/tools/ToolNode.ts +21 -7
  248. package/src/tools/handlers.ts +192 -18
  249. package/src/tools/search/anthropic.ts +51 -0
  250. package/src/tools/search/firecrawl.ts +78 -24
  251. package/src/tools/search/format.ts +10 -5
  252. package/src/tools/search/rerankers.ts +50 -62
  253. package/src/tools/search/schema.ts +63 -0
  254. package/src/tools/search/search.ts +167 -34
  255. package/src/tools/search/tool.ts +222 -46
  256. package/src/tools/search/types.ts +65 -10
  257. package/src/tools/search/utils.ts +37 -5
  258. package/src/types/graph.ts +272 -103
  259. package/src/types/llm.ts +25 -12
  260. package/src/types/run.ts +51 -13
  261. package/src/types/stream.ts +22 -1
  262. package/src/types/tools.ts +16 -10
  263. package/src/utils/events.ts +32 -0
  264. package/src/utils/llmConfig.ts +20 -8
  265. package/src/utils/title.ts +104 -30
  266. package/src/utils/tokens.ts +69 -10
@@ -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,12 +131,24 @@ 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 &&
@@ -97,7 +156,7 @@ export class ChatModelStreamHandler implements t.EventHandler {
97
156
  chunk.tool_calls.every((tc) => tc.id != null && tc.id !== '')
98
157
  ) {
99
158
  hasToolCalls = true;
100
- handleToolCalls(chunk.tool_calls, metadata, graph);
159
+ await handleToolCalls(chunk.tool_calls, metadata, graph);
101
160
  }
102
161
 
103
162
  const hasToolCallChunks =
@@ -106,18 +165,16 @@ export class ChatModelStreamHandler implements t.EventHandler {
106
165
  typeof content === 'undefined' ||
107
166
  !content.length ||
108
167
  (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
168
 
169
+ /** Set a preliminary message ID if found in empty chunk */
170
+ const isEmptyChunk = isEmptyContent && !hasToolCallChunks;
171
+ if (
172
+ isEmptyChunk &&
173
+ (chunk.id ?? '') !== '' &&
174
+ !graph.prelimMessageIdsByStepKey.has(chunk.id ?? '')
175
+ ) {
118
176
  const stepKey = graph.getStepKey(metadata);
119
- graph.prelimMessageIdsByStepKey.set(stepKey, chunkId);
120
- return;
177
+ graph.prelimMessageIdsByStepKey.set(stepKey, chunk.id ?? '');
121
178
  } else if (isEmptyChunk) {
122
179
  return;
123
180
  }
@@ -130,7 +187,7 @@ export class ChatModelStreamHandler implements t.EventHandler {
130
187
  chunk.tool_call_chunks.length &&
131
188
  typeof chunk.tool_call_chunks[0]?.index === 'number'
132
189
  ) {
133
- handleToolCallChunks({
190
+ await handleToolCallChunks({
134
191
  graph,
135
192
  stepKey,
136
193
  toolCallChunks: chunk.tool_call_chunks,
@@ -143,7 +200,7 @@ export class ChatModelStreamHandler implements t.EventHandler {
143
200
 
144
201
  const message_id = getMessageId(stepKey, graph) ?? '';
145
202
  if (message_id) {
146
- graph.dispatchRunStep(stepKey, {
203
+ await graph.dispatchRunStep(stepKey, {
147
204
  type: StepTypes.MESSAGE_CREATION,
148
205
  message_creation: {
149
206
  message_id,
@@ -181,8 +238,8 @@ hasToolCallChunks: ${hasToolCallChunks}
181
238
  ) {
182
239
  return;
183
240
  } else if (typeof content === 'string') {
184
- if (graph.currentTokenType === ContentTypes.TEXT) {
185
- graph.dispatchMessageDelta(stepId, {
241
+ if (agentContext.currentTokenType === ContentTypes.TEXT) {
242
+ await graph.dispatchMessageDelta(stepId, {
186
243
  content: [
187
244
  {
188
245
  type: ContentTypes.TEXT,
@@ -190,10 +247,10 @@ hasToolCallChunks: ${hasToolCallChunks}
190
247
  },
191
248
  ],
192
249
  });
193
- } else if (graph.currentTokenType === 'think_and_text') {
250
+ } else if (agentContext.currentTokenType === 'think_and_text') {
194
251
  const { text, thinking } = parseThinkingContent(content);
195
252
  if (thinking) {
196
- graph.dispatchReasoningDelta(stepId, {
253
+ await graph.dispatchReasoningDelta(stepId, {
197
254
  content: [
198
255
  {
199
256
  type: ContentTypes.THINK,
@@ -203,11 +260,11 @@ hasToolCallChunks: ${hasToolCallChunks}
203
260
  });
204
261
  }
205
262
  if (text) {
206
- graph.currentTokenType = ContentTypes.TEXT;
207
- graph.tokenTypeSwitch = 'content';
263
+ agentContext.currentTokenType = ContentTypes.TEXT;
264
+ agentContext.tokenTypeSwitch = 'content';
208
265
  const newStepKey = graph.getStepKey(metadata);
209
266
  const message_id = getMessageId(newStepKey, graph) ?? '';
210
- graph.dispatchRunStep(newStepKey, {
267
+ await graph.dispatchRunStep(newStepKey, {
211
268
  type: StepTypes.MESSAGE_CREATION,
212
269
  message_creation: {
213
270
  message_id,
@@ -215,7 +272,7 @@ hasToolCallChunks: ${hasToolCallChunks}
215
272
  });
216
273
 
217
274
  const newStepId = graph.getStepIdByKey(newStepKey);
218
- graph.dispatchMessageDelta(newStepId, {
275
+ await graph.dispatchMessageDelta(newStepId, {
219
276
  content: [
220
277
  {
221
278
  type: ContentTypes.TEXT,
@@ -225,7 +282,7 @@ hasToolCallChunks: ${hasToolCallChunks}
225
282
  });
226
283
  }
227
284
  } else {
228
- graph.dispatchReasoningDelta(stepId, {
285
+ await graph.dispatchReasoningDelta(stepId, {
229
286
  content: [
230
287
  {
231
288
  type: ContentTypes.THINK,
@@ -237,82 +294,97 @@ hasToolCallChunks: ${hasToolCallChunks}
237
294
  } else if (
238
295
  content.every((c) => c.type?.startsWith(ContentTypes.TEXT) ?? false)
239
296
  ) {
240
- graph.dispatchMessageDelta(stepId, {
297
+ await graph.dispatchMessageDelta(stepId, {
241
298
  content,
242
299
  });
243
300
  } else if (
244
301
  content.every(
245
302
  (c) =>
246
303
  (c.type?.startsWith(ContentTypes.THINKING) ?? false) ||
304
+ (c.type?.startsWith(ContentTypes.REASONING) ?? false) ||
247
305
  (c.type?.startsWith(ContentTypes.REASONING_CONTENT) ?? false)
248
306
  )
249
307
  ) {
250
- graph.dispatchReasoningDelta(stepId, {
308
+ await graph.dispatchReasoningDelta(stepId, {
251
309
  content: content.map((c) => ({
252
310
  type: ContentTypes.THINK,
253
311
  think:
254
312
  (c as t.ThinkingContentText).thinking ??
313
+ (c as Partial<t.GoogleReasoningContentText>).reasoning ??
255
314
  (c as Partial<t.BedrockReasoningContentText>).reasoningText?.text ??
256
315
  '',
257
316
  })),
258
317
  });
259
318
  }
260
319
  }
261
- handleReasoning(chunk: Partial<AIMessageChunk>, graph: Graph): void {
262
- let reasoning_content = chunk.additional_kwargs?.[graph.reasoningKey] as
263
- | string
264
- | undefined;
320
+ handleReasoning(
321
+ chunk: Partial<AIMessageChunk>,
322
+ agentContext: AgentContext
323
+ ): void {
324
+ let reasoning_content = chunk.additional_kwargs?.[
325
+ agentContext.reasoningKey
326
+ ] as string | Partial<ChatOpenAIReasoningSummary> | undefined;
265
327
  if (
266
328
  Array.isArray(chunk.content) &&
267
- (chunk.content[0]?.type === 'thinking' ||
268
- chunk.content[0]?.type === 'reasoning_content')
329
+ (chunk.content[0]?.type === ContentTypes.THINKING ||
330
+ chunk.content[0]?.type === ContentTypes.REASONING ||
331
+ chunk.content[0]?.type === ContentTypes.REASONING_CONTENT)
332
+ ) {
333
+ reasoning_content = 'valid';
334
+ } else if (
335
+ (agentContext.provider === Providers.OPENAI ||
336
+ agentContext.provider === Providers.AZURE) &&
337
+ reasoning_content != null &&
338
+ typeof reasoning_content !== 'string' &&
339
+ reasoning_content.summary?.[0]?.text != null &&
340
+ reasoning_content.summary[0].text
269
341
  ) {
270
342
  reasoning_content = 'valid';
271
343
  }
272
344
  if (
273
345
  reasoning_content != null &&
274
- reasoning_content &&
346
+ reasoning_content !== '' &&
275
347
  (chunk.content == null ||
276
348
  chunk.content === '' ||
277
349
  reasoning_content === 'valid')
278
350
  ) {
279
- graph.currentTokenType = ContentTypes.THINK;
280
- graph.tokenTypeSwitch = 'reasoning';
351
+ agentContext.currentTokenType = ContentTypes.THINK;
352
+ agentContext.tokenTypeSwitch = 'reasoning';
281
353
  return;
282
354
  } else if (
283
- graph.tokenTypeSwitch === 'reasoning' &&
284
- graph.currentTokenType !== ContentTypes.TEXT &&
285
- chunk.content != null &&
286
- chunk.content !== ''
355
+ agentContext.tokenTypeSwitch === 'reasoning' &&
356
+ agentContext.currentTokenType !== ContentTypes.TEXT &&
357
+ ((chunk.content != null && chunk.content !== '') ||
358
+ (chunk.tool_calls?.length ?? 0) > 0)
287
359
  ) {
288
- graph.currentTokenType = ContentTypes.TEXT;
289
- graph.tokenTypeSwitch = 'content';
360
+ agentContext.currentTokenType = ContentTypes.TEXT;
361
+ agentContext.tokenTypeSwitch = 'content';
290
362
  } else if (
291
363
  chunk.content != null &&
292
364
  typeof chunk.content === 'string' &&
293
365
  chunk.content.includes('<think>') &&
294
366
  chunk.content.includes('</think>')
295
367
  ) {
296
- graph.currentTokenType = 'think_and_text';
297
- graph.tokenTypeSwitch = 'content';
368
+ agentContext.currentTokenType = 'think_and_text';
369
+ agentContext.tokenTypeSwitch = 'content';
298
370
  } else if (
299
371
  chunk.content != null &&
300
372
  typeof chunk.content === 'string' &&
301
373
  chunk.content.includes('<think>')
302
374
  ) {
303
- graph.currentTokenType = ContentTypes.THINK;
304
- graph.tokenTypeSwitch = 'content';
375
+ agentContext.currentTokenType = ContentTypes.THINK;
376
+ agentContext.tokenTypeSwitch = 'content';
305
377
  } else if (
306
- graph.lastToken != null &&
307
- graph.lastToken.includes('</think>')
378
+ agentContext.lastToken != null &&
379
+ agentContext.lastToken.includes('</think>')
308
380
  ) {
309
- graph.currentTokenType = ContentTypes.TEXT;
310
- graph.tokenTypeSwitch = 'content';
381
+ agentContext.currentTokenType = ContentTypes.TEXT;
382
+ agentContext.tokenTypeSwitch = 'content';
311
383
  }
312
384
  if (typeof chunk.content !== 'string') {
313
385
  return;
314
386
  }
315
- graph.lastToken = chunk.content;
387
+ agentContext.lastToken = chunk.content;
316
388
  }
317
389
  }
318
390
 
@@ -402,12 +474,19 @@ export function createContentAggregator(): t.ContentAggregatorResult {
402
474
 
403
475
  const toolCallArgs = (contentPart.tool_call as t.ToolCallPart).args;
404
476
  /** When args are a valid object, they are likely already invoked */
405
- const args =
477
+ let args =
406
478
  finalUpdate ||
407
479
  typeof existingContent?.tool_call?.args === 'object' ||
408
480
  typeof toolCallArgs === 'object'
409
481
  ? contentPart.tool_call.args
410
482
  : (existingContent?.tool_call?.args ?? '') + (toolCallArgs ?? '');
483
+ if (
484
+ finalUpdate &&
485
+ args == null &&
486
+ existingContent?.tool_call?.args != null
487
+ ) {
488
+ args = existingContent.tool_call.args;
489
+ }
411
490
 
412
491
  const id =
413
492
  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
  }