@librechat/agents 3.1.75 → 3.1.77-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/dist/cjs/graphs/Graph.cjs +22 -3
  2. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  3. package/dist/cjs/hitl/askUserQuestion.cjs +67 -0
  4. package/dist/cjs/hitl/askUserQuestion.cjs.map +1 -0
  5. package/dist/cjs/hooks/HookRegistry.cjs +54 -0
  6. package/dist/cjs/hooks/HookRegistry.cjs.map +1 -1
  7. package/dist/cjs/hooks/createToolPolicyHook.cjs +115 -0
  8. package/dist/cjs/hooks/createToolPolicyHook.cjs.map +1 -0
  9. package/dist/cjs/hooks/executeHooks.cjs +40 -1
  10. package/dist/cjs/hooks/executeHooks.cjs.map +1 -1
  11. package/dist/cjs/hooks/types.cjs +1 -0
  12. package/dist/cjs/hooks/types.cjs.map +1 -1
  13. package/dist/cjs/langchain/google-common.cjs +3 -0
  14. package/dist/cjs/langchain/google-common.cjs.map +1 -0
  15. package/dist/cjs/langchain/index.cjs +86 -0
  16. package/dist/cjs/langchain/index.cjs.map +1 -0
  17. package/dist/cjs/langchain/language_models/chat_models.cjs +3 -0
  18. package/dist/cjs/langchain/language_models/chat_models.cjs.map +1 -0
  19. package/dist/cjs/langchain/messages/tool.cjs +3 -0
  20. package/dist/cjs/langchain/messages/tool.cjs.map +1 -0
  21. package/dist/cjs/langchain/messages.cjs +51 -0
  22. package/dist/cjs/langchain/messages.cjs.map +1 -0
  23. package/dist/cjs/langchain/openai.cjs +3 -0
  24. package/dist/cjs/langchain/openai.cjs.map +1 -0
  25. package/dist/cjs/langchain/prompts.cjs +11 -0
  26. package/dist/cjs/langchain/prompts.cjs.map +1 -0
  27. package/dist/cjs/langchain/runnables.cjs +19 -0
  28. package/dist/cjs/langchain/runnables.cjs.map +1 -0
  29. package/dist/cjs/langchain/tools.cjs +23 -0
  30. package/dist/cjs/langchain/tools.cjs.map +1 -0
  31. package/dist/cjs/langchain/utils/env.cjs +11 -0
  32. package/dist/cjs/langchain/utils/env.cjs.map +1 -0
  33. package/dist/cjs/llm/anthropic/index.cjs +145 -52
  34. package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
  35. package/dist/cjs/llm/anthropic/types.cjs.map +1 -1
  36. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +21 -14
  37. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  38. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +84 -70
  39. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
  40. package/dist/cjs/llm/bedrock/index.cjs +1 -1
  41. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  42. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +213 -3
  43. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
  44. package/dist/cjs/llm/bedrock/utils/message_outputs.cjs +2 -1
  45. package/dist/cjs/llm/bedrock/utils/message_outputs.cjs.map +1 -1
  46. package/dist/cjs/llm/google/utils/common.cjs +5 -4
  47. package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
  48. package/dist/cjs/llm/openai/index.cjs +519 -655
  49. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  50. package/dist/cjs/llm/openai/utils/index.cjs +20 -458
  51. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  52. package/dist/cjs/llm/openrouter/index.cjs +57 -175
  53. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  54. package/dist/cjs/llm/vertexai/index.cjs +5 -3
  55. package/dist/cjs/llm/vertexai/index.cjs.map +1 -1
  56. package/dist/cjs/main.cjs +112 -3
  57. package/dist/cjs/main.cjs.map +1 -1
  58. package/dist/cjs/messages/cache.cjs +2 -1
  59. package/dist/cjs/messages/cache.cjs.map +1 -1
  60. package/dist/cjs/messages/core.cjs +7 -6
  61. package/dist/cjs/messages/core.cjs.map +1 -1
  62. package/dist/cjs/messages/format.cjs +73 -15
  63. package/dist/cjs/messages/format.cjs.map +1 -1
  64. package/dist/cjs/messages/langchain.cjs +26 -0
  65. package/dist/cjs/messages/langchain.cjs.map +1 -0
  66. package/dist/cjs/messages/prune.cjs +7 -6
  67. package/dist/cjs/messages/prune.cjs.map +1 -1
  68. package/dist/cjs/run.cjs +400 -42
  69. package/dist/cjs/run.cjs.map +1 -1
  70. package/dist/cjs/tools/ToolNode.cjs +556 -56
  71. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  72. package/dist/cjs/tools/search/search.cjs +55 -66
  73. package/dist/cjs/tools/search/search.cjs.map +1 -1
  74. package/dist/cjs/tools/search/tavily-scraper.cjs +189 -0
  75. package/dist/cjs/tools/search/tavily-scraper.cjs.map +1 -0
  76. package/dist/cjs/tools/search/tavily-search.cjs +372 -0
  77. package/dist/cjs/tools/search/tavily-search.cjs.map +1 -0
  78. package/dist/cjs/tools/search/tool.cjs +26 -4
  79. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  80. package/dist/cjs/tools/search/utils.cjs +10 -3
  81. package/dist/cjs/tools/search/utils.cjs.map +1 -1
  82. package/dist/esm/graphs/Graph.mjs +22 -3
  83. package/dist/esm/graphs/Graph.mjs.map +1 -1
  84. package/dist/esm/hitl/askUserQuestion.mjs +65 -0
  85. package/dist/esm/hitl/askUserQuestion.mjs.map +1 -0
  86. package/dist/esm/hooks/HookRegistry.mjs +54 -0
  87. package/dist/esm/hooks/HookRegistry.mjs.map +1 -1
  88. package/dist/esm/hooks/createToolPolicyHook.mjs +113 -0
  89. package/dist/esm/hooks/createToolPolicyHook.mjs.map +1 -0
  90. package/dist/esm/hooks/executeHooks.mjs +40 -1
  91. package/dist/esm/hooks/executeHooks.mjs.map +1 -1
  92. package/dist/esm/hooks/types.mjs +1 -0
  93. package/dist/esm/hooks/types.mjs.map +1 -1
  94. package/dist/esm/langchain/google-common.mjs +2 -0
  95. package/dist/esm/langchain/google-common.mjs.map +1 -0
  96. package/dist/esm/langchain/index.mjs +5 -0
  97. package/dist/esm/langchain/index.mjs.map +1 -0
  98. package/dist/esm/langchain/language_models/chat_models.mjs +2 -0
  99. package/dist/esm/langchain/language_models/chat_models.mjs.map +1 -0
  100. package/dist/esm/langchain/messages/tool.mjs +2 -0
  101. package/dist/esm/langchain/messages/tool.mjs.map +1 -0
  102. package/dist/esm/langchain/messages.mjs +2 -0
  103. package/dist/esm/langchain/messages.mjs.map +1 -0
  104. package/dist/esm/langchain/openai.mjs +2 -0
  105. package/dist/esm/langchain/openai.mjs.map +1 -0
  106. package/dist/esm/langchain/prompts.mjs +2 -0
  107. package/dist/esm/langchain/prompts.mjs.map +1 -0
  108. package/dist/esm/langchain/runnables.mjs +2 -0
  109. package/dist/esm/langchain/runnables.mjs.map +1 -0
  110. package/dist/esm/langchain/tools.mjs +2 -0
  111. package/dist/esm/langchain/tools.mjs.map +1 -0
  112. package/dist/esm/langchain/utils/env.mjs +2 -0
  113. package/dist/esm/langchain/utils/env.mjs.map +1 -0
  114. package/dist/esm/llm/anthropic/index.mjs +146 -54
  115. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  116. package/dist/esm/llm/anthropic/types.mjs.map +1 -1
  117. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +21 -14
  118. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  119. package/dist/esm/llm/anthropic/utils/message_outputs.mjs +84 -71
  120. package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
  121. package/dist/esm/llm/bedrock/index.mjs +1 -1
  122. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  123. package/dist/esm/llm/bedrock/utils/message_inputs.mjs +214 -4
  124. package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
  125. package/dist/esm/llm/bedrock/utils/message_outputs.mjs +2 -1
  126. package/dist/esm/llm/bedrock/utils/message_outputs.mjs.map +1 -1
  127. package/dist/esm/llm/google/utils/common.mjs +5 -4
  128. package/dist/esm/llm/google/utils/common.mjs.map +1 -1
  129. package/dist/esm/llm/openai/index.mjs +520 -656
  130. package/dist/esm/llm/openai/index.mjs.map +1 -1
  131. package/dist/esm/llm/openai/utils/index.mjs +23 -459
  132. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  133. package/dist/esm/llm/openrouter/index.mjs +57 -175
  134. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  135. package/dist/esm/llm/vertexai/index.mjs +5 -3
  136. package/dist/esm/llm/vertexai/index.mjs.map +1 -1
  137. package/dist/esm/main.mjs +7 -0
  138. package/dist/esm/main.mjs.map +1 -1
  139. package/dist/esm/messages/cache.mjs +2 -1
  140. package/dist/esm/messages/cache.mjs.map +1 -1
  141. package/dist/esm/messages/core.mjs +7 -6
  142. package/dist/esm/messages/core.mjs.map +1 -1
  143. package/dist/esm/messages/format.mjs +73 -15
  144. package/dist/esm/messages/format.mjs.map +1 -1
  145. package/dist/esm/messages/langchain.mjs +23 -0
  146. package/dist/esm/messages/langchain.mjs.map +1 -0
  147. package/dist/esm/messages/prune.mjs +7 -6
  148. package/dist/esm/messages/prune.mjs.map +1 -1
  149. package/dist/esm/run.mjs +400 -42
  150. package/dist/esm/run.mjs.map +1 -1
  151. package/dist/esm/tools/ToolNode.mjs +557 -57
  152. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  153. package/dist/esm/tools/search/search.mjs +55 -66
  154. package/dist/esm/tools/search/search.mjs.map +1 -1
  155. package/dist/esm/tools/search/tavily-scraper.mjs +186 -0
  156. package/dist/esm/tools/search/tavily-scraper.mjs.map +1 -0
  157. package/dist/esm/tools/search/tavily-search.mjs +370 -0
  158. package/dist/esm/tools/search/tavily-search.mjs.map +1 -0
  159. package/dist/esm/tools/search/tool.mjs +26 -4
  160. package/dist/esm/tools/search/tool.mjs.map +1 -1
  161. package/dist/esm/tools/search/utils.mjs +10 -3
  162. package/dist/esm/tools/search/utils.mjs.map +1 -1
  163. package/dist/types/graphs/Graph.d.ts +7 -0
  164. package/dist/types/hitl/askUserQuestion.d.ts +55 -0
  165. package/dist/types/hitl/index.d.ts +6 -0
  166. package/dist/types/hooks/HookRegistry.d.ts +58 -0
  167. package/dist/types/hooks/createToolPolicyHook.d.ts +87 -0
  168. package/dist/types/hooks/index.d.ts +4 -1
  169. package/dist/types/hooks/types.d.ts +109 -3
  170. package/dist/types/index.d.ts +10 -0
  171. package/dist/types/langchain/google-common.d.ts +1 -0
  172. package/dist/types/langchain/index.d.ts +8 -0
  173. package/dist/types/langchain/language_models/chat_models.d.ts +1 -0
  174. package/dist/types/langchain/messages/tool.d.ts +1 -0
  175. package/dist/types/langchain/messages.d.ts +2 -0
  176. package/dist/types/langchain/openai.d.ts +1 -0
  177. package/dist/types/langchain/prompts.d.ts +1 -0
  178. package/dist/types/langchain/runnables.d.ts +2 -0
  179. package/dist/types/langchain/tools.d.ts +2 -0
  180. package/dist/types/langchain/utils/env.d.ts +1 -0
  181. package/dist/types/llm/anthropic/index.d.ts +22 -9
  182. package/dist/types/llm/anthropic/types.d.ts +5 -1
  183. package/dist/types/llm/anthropic/utils/message_outputs.d.ts +13 -6
  184. package/dist/types/llm/anthropic/utils/output_parsers.d.ts +1 -1
  185. package/dist/types/llm/openai/index.d.ts +21 -24
  186. package/dist/types/llm/openrouter/index.d.ts +11 -9
  187. package/dist/types/llm/vertexai/index.d.ts +1 -0
  188. package/dist/types/messages/cache.d.ts +4 -1
  189. package/dist/types/messages/format.d.ts +4 -1
  190. package/dist/types/messages/langchain.d.ts +27 -0
  191. package/dist/types/run.d.ts +117 -1
  192. package/dist/types/tools/ToolNode.d.ts +26 -1
  193. package/dist/types/tools/search/tavily-scraper.d.ts +19 -0
  194. package/dist/types/tools/search/tavily-search.d.ts +4 -0
  195. package/dist/types/tools/search/types.d.ts +99 -5
  196. package/dist/types/tools/search/utils.d.ts +2 -2
  197. package/dist/types/types/graph.d.ts +23 -37
  198. package/dist/types/types/hitl.d.ts +272 -0
  199. package/dist/types/types/index.d.ts +1 -0
  200. package/dist/types/types/llm.d.ts +3 -3
  201. package/dist/types/types/run.d.ts +33 -0
  202. package/dist/types/types/stream.d.ts +1 -1
  203. package/dist/types/types/tools.d.ts +19 -0
  204. package/package.json +80 -17
  205. package/src/graphs/Graph.ts +33 -4
  206. package/src/graphs/__tests__/composition.smoke.test.ts +188 -0
  207. package/src/hitl/askUserQuestion.ts +72 -0
  208. package/src/hitl/index.ts +7 -0
  209. package/src/hooks/HookRegistry.ts +71 -0
  210. package/src/hooks/__tests__/createToolPolicyHook.test.ts +259 -0
  211. package/src/hooks/createToolPolicyHook.ts +184 -0
  212. package/src/hooks/executeHooks.ts +50 -1
  213. package/src/hooks/index.ts +6 -0
  214. package/src/hooks/types.ts +112 -0
  215. package/src/index.ts +22 -0
  216. package/src/langchain/google-common.ts +1 -0
  217. package/src/langchain/index.ts +8 -0
  218. package/src/langchain/language_models/chat_models.ts +1 -0
  219. package/src/langchain/messages/tool.ts +5 -0
  220. package/src/langchain/messages.ts +21 -0
  221. package/src/langchain/openai.ts +1 -0
  222. package/src/langchain/prompts.ts +1 -0
  223. package/src/langchain/runnables.ts +7 -0
  224. package/src/langchain/tools.ts +8 -0
  225. package/src/langchain/utils/env.ts +1 -0
  226. package/src/llm/anthropic/index.ts +252 -84
  227. package/src/llm/anthropic/llm.spec.ts +751 -102
  228. package/src/llm/anthropic/types.ts +9 -1
  229. package/src/llm/anthropic/utils/message_inputs.ts +37 -19
  230. package/src/llm/anthropic/utils/message_outputs.ts +119 -101
  231. package/src/llm/bedrock/index.ts +2 -2
  232. package/src/llm/bedrock/llm.spec.ts +341 -0
  233. package/src/llm/bedrock/utils/message_inputs.ts +303 -4
  234. package/src/llm/bedrock/utils/message_outputs.ts +2 -1
  235. package/src/llm/custom-chat-models.smoke.test.ts +836 -0
  236. package/src/llm/google/llm.spec.ts +339 -57
  237. package/src/llm/google/utils/common.ts +53 -48
  238. package/src/llm/openai/contentBlocks.test.ts +346 -0
  239. package/src/llm/openai/index.ts +856 -833
  240. package/src/llm/openai/utils/index.ts +107 -78
  241. package/src/llm/openai/utils/messages.test.ts +159 -0
  242. package/src/llm/openrouter/index.ts +124 -247
  243. package/src/llm/openrouter/reasoning.test.ts +8 -1
  244. package/src/llm/vertexai/index.ts +11 -5
  245. package/src/llm/vertexai/llm.spec.ts +28 -1
  246. package/src/messages/cache.test.ts +4 -3
  247. package/src/messages/cache.ts +3 -2
  248. package/src/messages/core.ts +16 -9
  249. package/src/messages/format.ts +96 -16
  250. package/src/messages/formatAgentMessages.test.ts +166 -1
  251. package/src/messages/langchain.ts +39 -0
  252. package/src/messages/prune.ts +12 -8
  253. package/src/run.ts +456 -47
  254. package/src/scripts/caching.ts +2 -3
  255. package/src/specs/summarization.test.ts +51 -58
  256. package/src/tools/ToolNode.ts +706 -63
  257. package/src/tools/__tests__/hitl.test.ts +3593 -0
  258. package/src/tools/search/search.ts +83 -73
  259. package/src/tools/search/tavily-scraper.ts +235 -0
  260. package/src/tools/search/tavily-search.ts +424 -0
  261. package/src/tools/search/tavily.test.ts +965 -0
  262. package/src/tools/search/tool.ts +36 -26
  263. package/src/tools/search/types.ts +133 -8
  264. package/src/tools/search/utils.ts +13 -5
  265. package/src/types/graph.ts +32 -87
  266. package/src/types/hitl.ts +303 -0
  267. package/src/types/index.ts +1 -0
  268. package/src/types/llm.ts +3 -3
  269. package/src/types/run.ts +33 -0
  270. package/src/types/stream.ts +1 -1
  271. package/src/types/tools.ts +19 -0
  272. package/src/utils/llmConfig.ts +1 -6
@@ -7,13 +7,19 @@ import {
7
7
  HumanMessage,
8
8
  SystemMessage,
9
9
  } from '@langchain/core/messages';
10
- import type { MessageContentImageUrl } from '@langchain/core/messages';
10
+ import type {
11
+ MessageContent,
12
+ MessageContentImageUrl,
13
+ } from '@langchain/core/messages';
11
14
  import type { ToolCall } from '@langchain/core/messages/tool';
12
15
  import type {
16
+ BedrockReasoningContentText,
13
17
  ExtendedMessageContent,
18
+ GoogleReasoningContentText,
14
19
  MessageContentComplex,
15
20
  ReasoningContentText,
16
21
  SummaryContentBlock,
22
+ ThinkingContentText,
17
23
  ToolCallContent,
18
24
  ToolCallPart,
19
25
  TPayload,
@@ -22,6 +28,7 @@ import type {
22
28
  import type { RunnableConfig } from '@langchain/core/runnables';
23
29
  import { emitAgentLog } from '@/utils/events';
24
30
  import { Providers, ContentTypes, Constants } from '@/common';
31
+ import { toLangChainContent, toLangChainMessageFields } from './langchain';
25
32
 
26
33
  interface MediaMessageParams {
27
34
  message: {
@@ -210,7 +217,7 @@ export const formatMessage = ({
210
217
  return mediaMessage;
211
218
  }
212
219
 
213
- return new HumanMessage(mediaMessage);
220
+ return new HumanMessage(toLangChainMessageFields(mediaMessage));
214
221
  }
215
222
 
216
223
  if (!langChain) {
@@ -218,11 +225,11 @@ export const formatMessage = ({
218
225
  }
219
226
 
220
227
  if (role === 'user') {
221
- return new HumanMessage(formattedMessage);
228
+ return new HumanMessage(toLangChainMessageFields(formattedMessage));
222
229
  } else if (role === 'assistant') {
223
- return new AIMessage(formattedMessage);
230
+ return new AIMessage(toLangChainMessageFields(formattedMessage));
224
231
  } else {
225
- return new SystemMessage(formattedMessage);
232
+ return new SystemMessage(toLangChainMessageFields(formattedMessage));
226
233
  }
227
234
  };
228
235
 
@@ -276,18 +283,86 @@ export const formatFromLangChain = (
276
283
  };
277
284
  };
278
285
 
286
+ interface FormatAssistantMessageOptions {
287
+ preserveReasoningContent?: boolean;
288
+ }
289
+
290
+ interface FormatAgentMessagesOptions {
291
+ provider?: Providers;
292
+ }
293
+
294
+ function extractReasoningContent(
295
+ part: MessageContentComplex | undefined | null
296
+ ): string {
297
+ if (part == null || typeof part !== 'object') {
298
+ return '';
299
+ }
300
+ if (part.type === ContentTypes.THINK) {
301
+ const think = (part as ReasoningContentText).think;
302
+ return typeof think === 'string' ? think : '';
303
+ }
304
+ if (part.type === ContentTypes.THINKING) {
305
+ const thinking = (part as ThinkingContentText).thinking;
306
+ return typeof thinking === 'string' ? thinking : '';
307
+ }
308
+ if (part.type === ContentTypes.REASONING) {
309
+ const reasoning = (part as GoogleReasoningContentText).reasoning;
310
+ return typeof reasoning === 'string' ? reasoning : '';
311
+ }
312
+ if (part.type === ContentTypes.REASONING_CONTENT) {
313
+ const reasoningText = (part as BedrockReasoningContentText).reasoningText;
314
+ return typeof reasoningText.text === 'string' ? reasoningText.text : '';
315
+ }
316
+ return '';
317
+ }
318
+
279
319
  /**
280
320
  * Helper function to format an assistant message
281
321
  * @param message The message to format
322
+ * @param options Optional formatting options
282
323
  * @returns Array of formatted messages
283
324
  */
284
325
  function formatAssistantMessage(
285
- message: Partial<TMessage>
326
+ message: Partial<TMessage>,
327
+ options?: FormatAssistantMessageOptions
286
328
  ): Array<AIMessage | ToolMessage> {
287
329
  const formattedMessages: Array<AIMessage | ToolMessage> = [];
288
330
  let currentContent: MessageContentComplex[] = [];
289
331
  let lastAIMessage: AIMessage | null = null;
290
332
  let hasReasoning = false;
333
+ let pendingReasoningContent = '';
334
+ const shouldPreserveReasoningContent =
335
+ options?.preserveReasoningContent === true;
336
+
337
+ const takePendingReasoningContent = (): string | undefined => {
338
+ if (!shouldPreserveReasoningContent || !pendingReasoningContent) {
339
+ return undefined;
340
+ }
341
+ const reasoningContent = pendingReasoningContent;
342
+ pendingReasoningContent = '';
343
+ return reasoningContent;
344
+ };
345
+
346
+ const createAIMessage = (content: MessageContent): AIMessage => {
347
+ const reasoningContent = takePendingReasoningContent();
348
+ return new AIMessage({
349
+ content,
350
+ ...(reasoningContent != null && {
351
+ additional_kwargs: { reasoning_content: reasoningContent },
352
+ }),
353
+ });
354
+ };
355
+
356
+ const attachPendingReasoningContent = (aiMessage: AIMessage): void => {
357
+ const reasoningContent = takePendingReasoningContent();
358
+ if (reasoningContent == null) {
359
+ return;
360
+ }
361
+ aiMessage.additional_kwargs.reasoning_content =
362
+ typeof aiMessage.additional_kwargs.reasoning_content === 'string'
363
+ ? `${aiMessage.additional_kwargs.reasoning_content}${reasoningContent}`
364
+ : reasoningContent;
365
+ };
291
366
 
292
367
  if (Array.isArray(message.content)) {
293
368
  for (const part of message.content as Array<
@@ -310,15 +385,13 @@ function formatAssistantMessage(
310
385
  }, '');
311
386
  content =
312
387
  `${content}\n${part[ContentTypes.TEXT] ?? part.text ?? ''}`.trim();
313
- lastAIMessage = new AIMessage({ content });
388
+ lastAIMessage = createAIMessage(content);
314
389
  formattedMessages.push(lastAIMessage);
315
390
  currentContent = [];
316
391
  continue;
317
392
  }
318
393
  // Create a new AIMessage with this text and prepare for tool calls
319
- lastAIMessage = new AIMessage({
320
- content: part.text != null ? part.text : '',
321
- });
394
+ lastAIMessage = createAIMessage(part.text != null ? part.text : '');
322
395
  formattedMessages.push(lastAIMessage);
323
396
  } else if (part.type === ContentTypes.TOOL_CALL) {
324
397
  // Skip malformed tool call entries without tool_call property
@@ -343,8 +416,10 @@ function formatAssistantMessage(
343
416
 
344
417
  if (!lastAIMessage) {
345
418
  // "Heal" the payload by creating an AIMessage to precede the tool call
346
- lastAIMessage = new AIMessage({ content: '' });
419
+ lastAIMessage = createAIMessage('');
347
420
  formattedMessages.push(lastAIMessage);
421
+ } else {
422
+ attachPendingReasoningContent(lastAIMessage);
348
423
  }
349
424
 
350
425
  const tool_call: ToolCallPart = _tool_call;
@@ -376,10 +451,12 @@ function formatAssistantMessage(
376
451
  } else if (
377
452
  part.type === ContentTypes.THINK ||
378
453
  part.type === ContentTypes.THINKING ||
454
+ part.type === ContentTypes.REASONING ||
379
455
  part.type === ContentTypes.REASONING_CONTENT ||
380
456
  part.type === 'redacted_thinking'
381
457
  ) {
382
458
  hasReasoning = true;
459
+ pendingReasoningContent += extractReasoningContent(part);
383
460
  continue;
384
461
  } else if (
385
462
  part.type === ContentTypes.ERROR ||
@@ -410,10 +487,10 @@ function formatAssistantMessage(
410
487
  .trim();
411
488
 
412
489
  if (content) {
413
- formattedMessages.push(new AIMessage({ content }));
490
+ formattedMessages.push(createAIMessage(content));
414
491
  }
415
492
  } else if (currentContent.length > 0) {
416
- formattedMessages.push(new AIMessage({ content: currentContent }));
493
+ formattedMessages.push(createAIMessage(toLangChainContent(currentContent)));
417
494
  }
418
495
 
419
496
  return formattedMessages;
@@ -829,7 +906,8 @@ export const formatAgentMessages = (
829
906
  /** Pre-resolved skill bodies keyed by skill name. When present, HumanMessages
830
907
  * are reconstructed after skill ToolMessages to restore skill instructions
831
908
  * that were only in LangGraph state during the original run. */
832
- skills?: Map<string, string>
909
+ skills?: Map<string, string>,
910
+ options?: FormatAgentMessagesOptions
833
911
  ): {
834
912
  messages: Array<HumanMessage | AIMessage | SystemMessage | ToolMessage>;
835
913
  indexTokenCountMap?: Record<number, number>;
@@ -1077,7 +1155,9 @@ export const formatAgentMessages = (
1077
1155
  }
1078
1156
  }
1079
1157
 
1080
- const formattedMessages = formatAssistantMessage(processedMessage);
1158
+ const formattedMessages = formatAssistantMessage(processedMessage, {
1159
+ preserveReasoningContent: options?.provider === Providers.DEEPSEEK,
1160
+ });
1081
1161
  if (sourceMessageId != null && sourceMessageId !== '') {
1082
1162
  for (const formattedMessage of formattedMessages) {
1083
1163
  formattedMessage.id = sourceMessageId;
@@ -1542,7 +1622,7 @@ export function ensureThinkingBlockInMessages(
1542
1622
  'ensureThinkingBlockInMessages: injecting [Previous agent context] HumanMessage' +
1543
1623
  ` (${parts.length} msgs at index ${i}, no thinking block in chain)`
1544
1624
  );
1545
- result.push(new HumanMessage({ content: parts }));
1625
+ result.push(new HumanMessage({ content: toLangChainContent(parts) }));
1546
1626
  i = j;
1547
1627
  } else {
1548
1628
  // Keep the message as is
@@ -6,7 +6,7 @@ import {
6
6
  } from '@langchain/core/messages';
7
7
  import type { MessageContentComplex, TPayload } from '@/types';
8
8
  import { formatAgentMessages } from './format';
9
- import { ContentTypes } from '@/common';
9
+ import { ContentTypes, Providers } from '@/common';
10
10
 
11
11
  describe('formatAgentMessages', () => {
12
12
  it('should format simple user and AI messages', () => {
@@ -967,6 +967,171 @@ describe('formatAgentMessages', () => {
967
967
  );
968
968
  });
969
969
 
970
+ it('should preserve hidden reasoning_content for DeepSeek assistant messages', () => {
971
+ const payload: TPayload = [
972
+ {
973
+ role: 'assistant',
974
+ content: [
975
+ {
976
+ type: ContentTypes.THINK,
977
+ [ContentTypes.THINK]: 'Need calculator.',
978
+ },
979
+ {
980
+ type: ContentTypes.TEXT,
981
+ [ContentTypes.TEXT]: 'Using calculator.',
982
+ tool_call_ids: ['call_1'],
983
+ },
984
+ {
985
+ type: ContentTypes.TOOL_CALL,
986
+ tool_call: {
987
+ id: 'call_1',
988
+ name: 'calculator',
989
+ args: '{"input":"127 * 453"}',
990
+ output: '57531',
991
+ },
992
+ },
993
+ {
994
+ type: ContentTypes.THINK,
995
+ [ContentTypes.THINK]: 'Calculator returned 57531.',
996
+ },
997
+ {
998
+ type: ContentTypes.TEXT,
999
+ [ContentTypes.TEXT]: '127 * 453 = 57531.',
1000
+ },
1001
+ ],
1002
+ },
1003
+ ];
1004
+
1005
+ const defaultResult = formatAgentMessages(payload);
1006
+ expect(
1007
+ (defaultResult.messages[0] as AIMessage).additional_kwargs
1008
+ .reasoning_content
1009
+ ).toBeUndefined();
1010
+
1011
+ const result = formatAgentMessages(
1012
+ payload,
1013
+ undefined,
1014
+ undefined,
1015
+ undefined,
1016
+ { provider: Providers.DEEPSEEK }
1017
+ );
1018
+
1019
+ expect(result.messages).toHaveLength(3);
1020
+ expect(result.messages[0]).toBeInstanceOf(AIMessage);
1021
+ expect(result.messages[1]).toBeInstanceOf(ToolMessage);
1022
+ expect(result.messages[2]).toBeInstanceOf(AIMessage);
1023
+
1024
+ const toolCallMessage = result.messages[0] as AIMessage;
1025
+ const finalMessage = result.messages[2] as AIMessage;
1026
+
1027
+ expect(toolCallMessage.content).toBe('Using calculator.');
1028
+ expect(toolCallMessage.tool_calls).toHaveLength(1);
1029
+ expect(toolCallMessage.additional_kwargs.reasoning_content).toBe(
1030
+ 'Need calculator.'
1031
+ );
1032
+ expect(finalMessage.content).toBe('127 * 453 = 57531.');
1033
+ expect(finalMessage.additional_kwargs.reasoning_content).toBe(
1034
+ 'Calculator returned 57531.'
1035
+ );
1036
+ });
1037
+
1038
+ it('should preserve DeepSeek reasoning from supported hidden content blocks', () => {
1039
+ const payload: TPayload = [
1040
+ {
1041
+ role: 'assistant',
1042
+ content: [
1043
+ {
1044
+ type: ContentTypes.THINK,
1045
+ [ContentTypes.THINK]: 'Think. ',
1046
+ },
1047
+ {
1048
+ type: ContentTypes.THINKING,
1049
+ thinking: 'Thinking. ',
1050
+ },
1051
+ {
1052
+ type: ContentTypes.REASONING,
1053
+ reasoning: 'Reasoning. ',
1054
+ },
1055
+ {
1056
+ type: ContentTypes.REASONING_CONTENT,
1057
+ reasoningText: { text: 'Reasoning content.' },
1058
+ },
1059
+ {
1060
+ type: ContentTypes.TEXT,
1061
+ [ContentTypes.TEXT]: 'Done.',
1062
+ },
1063
+ ],
1064
+ },
1065
+ ];
1066
+
1067
+ const result = formatAgentMessages(
1068
+ payload,
1069
+ undefined,
1070
+ undefined,
1071
+ undefined,
1072
+ { provider: Providers.DEEPSEEK }
1073
+ );
1074
+
1075
+ expect(result.messages).toHaveLength(1);
1076
+ expect(result.messages[0]).toBeInstanceOf(AIMessage);
1077
+ expect(result.messages[0].content).toBe('Done.');
1078
+ expect(
1079
+ (result.messages[0] as AIMessage).additional_kwargs.reasoning_content
1080
+ ).toBe('Think. Thinking. Reasoning. Reasoning content.');
1081
+ });
1082
+
1083
+ it('should attach later DeepSeek reasoning to an existing tool-call assistant message', () => {
1084
+ const payload: TPayload = [
1085
+ {
1086
+ role: 'assistant',
1087
+ content: [
1088
+ {
1089
+ type: ContentTypes.THINK,
1090
+ [ContentTypes.THINK]: 'Need calculator. ',
1091
+ },
1092
+ {
1093
+ type: ContentTypes.TEXT,
1094
+ [ContentTypes.TEXT]: 'Using calculator.',
1095
+ tool_call_ids: ['call_1'],
1096
+ },
1097
+ {
1098
+ type: ContentTypes.THINK,
1099
+ [ContentTypes.THINK]: 'Preparing tool call.',
1100
+ },
1101
+ {
1102
+ type: ContentTypes.TOOL_CALL,
1103
+ tool_call: {
1104
+ id: 'call_1',
1105
+ name: 'calculator',
1106
+ args: '{"input":"127 * 453"}',
1107
+ output: '57531',
1108
+ },
1109
+ },
1110
+ ],
1111
+ },
1112
+ ];
1113
+
1114
+ const result = formatAgentMessages(
1115
+ payload,
1116
+ undefined,
1117
+ undefined,
1118
+ undefined,
1119
+ { provider: Providers.DEEPSEEK }
1120
+ );
1121
+
1122
+ expect(result.messages).toHaveLength(2);
1123
+ expect(result.messages[0]).toBeInstanceOf(AIMessage);
1124
+ expect(result.messages[1]).toBeInstanceOf(ToolMessage);
1125
+
1126
+ const toolCallMessage = result.messages[0] as AIMessage;
1127
+
1128
+ expect(toolCallMessage.content).toBe('Using calculator.');
1129
+ expect(toolCallMessage.tool_calls).toHaveLength(1);
1130
+ expect(toolCallMessage.additional_kwargs.reasoning_content).toBe(
1131
+ 'Need calculator. Preparing tool call.'
1132
+ );
1133
+ });
1134
+
970
1135
  it('should strip thinking blocks and join TEXT parts as string', () => {
971
1136
  const payload = [
972
1137
  {
@@ -0,0 +1,39 @@
1
+ import type { MessageContent } from '@langchain/core/messages';
2
+ import type * as t from '@/types';
3
+
4
+ type LibreChatMessageContent =
5
+ | MessageContent
6
+ | string
7
+ | t.MessageContentComplex[]
8
+ | t.ExtendedMessageContent[];
9
+
10
+ type WithLangChainContent<T extends { content: LibreChatMessageContent }> =
11
+ Omit<T, 'content'> & {
12
+ content: MessageContent;
13
+ };
14
+
15
+ /**
16
+ * Bridges LibreChat's extended content blocks to LangChain 1.x MessageContent.
17
+ *
18
+ * LangChain 1.x narrowed message constructor types around ContentBlock, while
19
+ * LibreChat still carries provider-specific blocks through the same content
20
+ * field. This helper keeps the runtime shape unchanged during the dependency
21
+ * upgrade; tracking issue: https://github.com/danny-avila/agents/issues/130.
22
+ */
23
+ export function toLangChainContent(
24
+ content: LibreChatMessageContent
25
+ ): MessageContent {
26
+ return content as MessageContent;
27
+ }
28
+
29
+ /**
30
+ * Applies the same LangChain 1.x content bridge to message constructor fields.
31
+ *
32
+ * Keep this cast-only helper local to constructor boundaries so follow-up work
33
+ * can replace it with aligned content types or explicit conversion logic.
34
+ */
35
+ export function toLangChainMessageFields<
36
+ T extends { content: LibreChatMessageContent },
37
+ >(message: T): WithLangChainContent<T> {
38
+ return message as WithLangChainContent<T>;
39
+ }
@@ -19,6 +19,7 @@ import {
19
19
  import { resolveContextPruningSettings } from './contextPruningSettings';
20
20
  import { ContentTypes, Providers, Constants } from '@/common';
21
21
  import { applyContextPruning } from './contextPruning';
22
+ import { toLangChainContent } from './langchain';
22
23
 
23
24
  function sumTokenCounts(
24
25
  tokenMap: Record<string, number | undefined>,
@@ -343,7 +344,7 @@ function stripOrphanToolUseBlocks(
343
344
 
344
345
  return new AIMessage({
345
346
  ...message,
346
- content: keptContent,
347
+ content: toLangChainContent(keptContent),
347
348
  tool_calls: keptToolCalls.length > 0 ? keptToolCalls : undefined,
348
349
  });
349
350
  }
@@ -542,7 +543,7 @@ function addThinkingBlock(
542
543
  content.unshift(thinkingBlock);
543
544
  return new AIMessage({
544
545
  ...message,
545
- content,
546
+ content: toLangChainContent(content),
546
547
  });
547
548
  }
548
549
 
@@ -817,7 +818,7 @@ export function getMessagesWithinTokenLimit({
817
818
 
818
819
  thinkingStartIndex = originalLength - 1 - assistantIndex;
819
820
  const thinkingTokenCount = tokenCounter(
820
- new AIMessage({ content: [thinkingBlock] })
821
+ new AIMessage({ content: toLangChainContent([thinkingBlock]) })
821
822
  );
822
823
  const newRemainingCount = remainingContextTokens - thinkingTokenCount;
823
824
  const newMessage = addThinkingBlock(
@@ -856,7 +857,7 @@ export function getMessagesWithinTokenLimit({
856
857
  }
857
858
  }
858
859
 
859
- const firstMessage: AIMessage = newContext[newContext.length - 1];
860
+ const firstMessage = newContext[newContext.length - 1];
860
861
  const firstMessageType = newContext[newContext.length - 1].getType();
861
862
  if (firstMessageType === 'tool') {
862
863
  startType = ['ai', 'human'];
@@ -887,7 +888,10 @@ export function getMessagesWithinTokenLimit({
887
888
  }
888
889
 
889
890
  if (firstMessageType === 'ai') {
890
- const newMessage = addThinkingBlock(firstMessage, thinkingBlock);
891
+ const newMessage = addThinkingBlock(
892
+ firstMessage as AIMessage,
893
+ thinkingBlock
894
+ );
891
895
  newContext[newContext.length - 1] = newMessage;
892
896
  } else {
893
897
  newContext.push(thinkingMessage);
@@ -1178,7 +1182,7 @@ export function preFlightTruncateToolCallInputs(params: {
1178
1182
 
1179
1183
  messages[i] = new AIMessage({
1180
1184
  ...aiMsg,
1181
- content: newContent,
1185
+ content: toLangChainContent(newContent),
1182
1186
  tool_calls: newToolCalls.length > 0 ? newToolCalls : undefined,
1183
1187
  });
1184
1188
  indexTokenCountMap[i] = tokenCounter(messages[i]);
@@ -1290,7 +1294,7 @@ export function createPruneMessages(factoryParams: PruneMessagesFactoryParams) {
1290
1294
 
1291
1295
  params.messages[i] = new AIMessage({
1292
1296
  ...message,
1293
- content: [thinkingBlock],
1297
+ content: toLangChainContent([thinkingBlock]),
1294
1298
  additional_kwargs: {
1295
1299
  ...message.additional_kwargs,
1296
1300
  reasoning_content: undefined,
@@ -1966,7 +1970,7 @@ export function createPruneMessages(factoryParams: PruneMessagesFactoryParams) {
1966
1970
  });
1967
1971
  emergencyMessages[i] = new AIMessage({
1968
1972
  ...aiMsg,
1969
- content: newContent,
1973
+ content: toLangChainContent(newContent),
1970
1974
  tool_calls: newToolCalls.length > 0 ? newToolCalls : undefined,
1971
1975
  });
1972
1976
  indexTokenCountMap[i] = factoryParams.tokenCounter(