@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
@@ -1,21 +1,128 @@
1
1
  import { AzureOpenAI as AzureOpenAIClient } from 'openai';
2
+ import { AIMessageChunk } from '@langchain/core/messages';
2
3
  import { ChatXAI as OriginalChatXAI } from '@langchain/xai';
4
+ import { ChatGenerationChunk } from '@langchain/core/outputs';
5
+ import { ToolDefinition } from '@langchain/core/language_models/base';
6
+ import { isLangChainTool } from '@langchain/core/utils/function_calling';
3
7
  import { ChatDeepSeek as OriginalChatDeepSeek } from '@langchain/deepseek';
8
+ import { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';
4
9
  import {
5
10
  getEndpoint,
6
11
  OpenAIClient,
12
+ formatToOpenAITool,
7
13
  ChatOpenAI as OriginalChatOpenAI,
8
14
  AzureChatOpenAI as OriginalAzureChatOpenAI,
9
15
  } from '@langchain/openai';
10
- import type { OpenAICoreRequestOptions } from 'node_modules/@langchain/deepseek/node_modules/@langchain/openai';
16
+ import type {
17
+ OpenAIChatCallOptions,
18
+ OpenAIRoleEnum,
19
+ HeaderValue,
20
+ HeadersLike,
21
+ } from './types';
22
+ import type { BindToolsInput } from '@langchain/core/language_models/chat_models';
23
+ import type { BaseMessage, UsageMetadata } from '@langchain/core/messages';
24
+ import type { ChatXAIInput } from '@langchain/xai';
11
25
  import type * as t from '@langchain/openai';
26
+ import {
27
+ isReasoningModel,
28
+ _convertMessagesToOpenAIParams,
29
+ _convertMessagesToOpenAIResponsesParams,
30
+ _convertOpenAIResponsesDeltaToBaseMessageChunk,
31
+ type ResponseReturnStreamEvents,
32
+ } from './utils';
33
+ import { sleep } from '@/utils';
34
+
35
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
36
+ const iife = <T>(fn: () => T) => fn();
37
+
38
+ export function isHeaders(headers: unknown): headers is Headers {
39
+ return (
40
+ typeof Headers !== 'undefined' &&
41
+ headers !== null &&
42
+ typeof headers === 'object' &&
43
+ Object.prototype.toString.call(headers) === '[object Headers]'
44
+ );
45
+ }
46
+
47
+ export function normalizeHeaders(
48
+ headers: HeadersLike
49
+ ): Record<string, HeaderValue | readonly HeaderValue[]> {
50
+ const output = iife(() => {
51
+ // If headers is a Headers instance
52
+ if (isHeaders(headers)) {
53
+ return headers;
54
+ }
55
+ // If headers is an array of [key, value] pairs
56
+ else if (Array.isArray(headers)) {
57
+ return new Headers(headers);
58
+ }
59
+ // If headers is a NullableHeaders-like object (has 'values' property that is a Headers)
60
+ else if (
61
+ typeof headers === 'object' &&
62
+ headers !== null &&
63
+ 'values' in headers &&
64
+ isHeaders(headers.values)
65
+ ) {
66
+ return headers.values;
67
+ }
68
+ // If headers is a plain object
69
+ else if (typeof headers === 'object' && headers !== null) {
70
+ const entries: [string, string][] = Object.entries(headers)
71
+ .filter(([, v]) => typeof v === 'string')
72
+ .map(([k, v]) => [k, v as string]);
73
+ return new Headers(entries);
74
+ }
75
+ return new Headers();
76
+ });
77
+
78
+ return Object.fromEntries(output.entries());
79
+ }
80
+
81
+ type OpenAICompletionParam =
82
+ OpenAIClient.Chat.Completions.ChatCompletionMessageParam;
83
+
84
+ type OpenAICoreRequestOptions = OpenAIClient.RequestOptions;
12
85
 
13
86
  function createAbortHandler(controller: AbortController): () => void {
14
87
  return function (): void {
15
88
  controller.abort();
16
89
  };
17
90
  }
91
+ /**
92
+ * Formats a tool in either OpenAI format, or LangChain structured tool format
93
+ * into an OpenAI tool format. If the tool is already in OpenAI format, return without
94
+ * any changes. If it is in LangChain structured tool format, convert it to OpenAI tool format
95
+ * using OpenAI's `zodFunction` util, falling back to `convertToOpenAIFunction` if the parameters
96
+ * returned from the `zodFunction` util are not defined.
97
+ *
98
+ * @param {BindToolsInput} tool The tool to convert to an OpenAI tool.
99
+ * @param {Object} [fields] Additional fields to add to the OpenAI tool.
100
+ * @returns {ToolDefinition} The inputted tool in OpenAI tool format.
101
+ */
102
+ export function _convertToOpenAITool(
103
+ tool: BindToolsInput,
104
+ fields?: {
105
+ /**
106
+ * If `true`, model output is guaranteed to exactly match the JSON Schema
107
+ * provided in the function definition.
108
+ */
109
+ strict?: boolean;
110
+ }
111
+ ): OpenAIClient.ChatCompletionTool {
112
+ let toolDef: OpenAIClient.ChatCompletionTool | undefined;
113
+
114
+ if (isLangChainTool(tool)) {
115
+ toolDef = formatToOpenAITool(tool);
116
+ } else {
117
+ toolDef = tool as ToolDefinition;
118
+ }
18
119
 
120
+ if (fields?.strict !== undefined) {
121
+ toolDef.function.strict = fields.strict;
122
+ }
123
+
124
+ return toolDef;
125
+ }
19
126
  export class CustomOpenAIClient extends OpenAIClient {
20
127
  abortHandler?: () => void;
21
128
  async fetchWithTimeout(
@@ -87,13 +194,25 @@ export class CustomAzureOpenAIClient extends AzureOpenAIClient {
87
194
  }
88
195
  }
89
196
 
197
+ /** @ts-expect-error We are intentionally overriding `getReasoningParams` */
90
198
  export class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
199
+ _lc_stream_delay?: number;
200
+
201
+ constructor(
202
+ fields?: t.ChatOpenAICallOptions & {
203
+ _lc_stream_delay?: number;
204
+ } & t.OpenAIChatInput['modelKwargs']
205
+ ) {
206
+ super(fields);
207
+ this._lc_stream_delay = fields?._lc_stream_delay;
208
+ }
209
+
91
210
  public get exposedClient(): CustomOpenAIClient {
92
211
  return this.client;
93
212
  }
94
213
  protected _getClientOptions(
95
- options?: t.OpenAICoreRequestOptions
96
- ): t.OpenAICoreRequestOptions {
214
+ options?: OpenAICoreRequestOptions
215
+ ): OpenAICoreRequestOptions {
97
216
  if (!(this.client as OpenAIClient | undefined)) {
98
217
  const openAIEndpointConfig: t.OpenAIEndpointConfig = {
99
218
  baseURL: this.clientConfig.baseURL,
@@ -115,19 +234,274 @@ export class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
115
234
  const requestOptions = {
116
235
  ...this.clientConfig,
117
236
  ...options,
118
- } as t.OpenAICoreRequestOptions;
237
+ } as OpenAICoreRequestOptions;
119
238
  return requestOptions;
120
239
  }
240
+
241
+ /**
242
+ * Returns backwards compatible reasoning parameters from constructor params and call options
243
+ * @internal
244
+ */
245
+ getReasoningParams(
246
+ options?: this['ParsedCallOptions']
247
+ ): OpenAIClient.Reasoning | undefined {
248
+ if (!isReasoningModel(this.model)) {
249
+ return;
250
+ }
251
+
252
+ // apply options in reverse order of importance -- newer options supersede older options
253
+ let reasoning: OpenAIClient.Reasoning | undefined;
254
+ if (this.reasoning !== undefined) {
255
+ reasoning = {
256
+ ...reasoning,
257
+ ...this.reasoning,
258
+ };
259
+ }
260
+ if (options?.reasoning !== undefined) {
261
+ reasoning = {
262
+ ...reasoning,
263
+ ...options.reasoning,
264
+ };
265
+ }
266
+
267
+ return reasoning;
268
+ }
269
+
270
+ protected _getReasoningParams(
271
+ options?: this['ParsedCallOptions']
272
+ ): OpenAIClient.Reasoning | undefined {
273
+ return this.getReasoningParams(options);
274
+ }
275
+
276
+ async *_streamResponseChunks(
277
+ messages: BaseMessage[],
278
+ options: this['ParsedCallOptions'],
279
+ runManager?: CallbackManagerForLLMRun
280
+ ): AsyncGenerator<ChatGenerationChunk> {
281
+ if (!this._useResponseApi(options)) {
282
+ return yield* this._streamResponseChunks2(messages, options, runManager);
283
+ }
284
+ const streamIterable = await this.responseApiWithRetry(
285
+ {
286
+ ...this.invocationParams<'responses'>(options, { streaming: true }),
287
+ input: _convertMessagesToOpenAIResponsesParams(
288
+ messages,
289
+ this.model,
290
+ this.zdrEnabled
291
+ ),
292
+ stream: true,
293
+ },
294
+ options
295
+ );
296
+
297
+ for await (const data of streamIterable) {
298
+ const chunk = _convertOpenAIResponsesDeltaToBaseMessageChunk(
299
+ data as ResponseReturnStreamEvents
300
+ );
301
+ if (chunk == null) continue;
302
+ yield chunk;
303
+ if (this._lc_stream_delay != null) {
304
+ await sleep(this._lc_stream_delay);
305
+ }
306
+ await runManager?.handleLLMNewToken(
307
+ chunk.text || '',
308
+ undefined,
309
+ undefined,
310
+ undefined,
311
+ undefined,
312
+ { chunk }
313
+ );
314
+ }
315
+
316
+ return;
317
+ }
318
+
319
+ async *_streamResponseChunks2(
320
+ messages: BaseMessage[],
321
+ options: this['ParsedCallOptions'],
322
+ runManager?: CallbackManagerForLLMRun
323
+ ): AsyncGenerator<ChatGenerationChunk> {
324
+ const messagesMapped: OpenAICompletionParam[] =
325
+ _convertMessagesToOpenAIParams(messages, this.model);
326
+
327
+ const params = {
328
+ ...this.invocationParams(options, {
329
+ streaming: true,
330
+ }),
331
+ messages: messagesMapped,
332
+ stream: true as const,
333
+ };
334
+ let defaultRole: OpenAIRoleEnum | undefined;
335
+
336
+ const streamIterable = await this.completionWithRetry(params, options);
337
+ let usage: OpenAIClient.Completions.CompletionUsage | undefined;
338
+ for await (const data of streamIterable) {
339
+ const choice = data.choices[0] as
340
+ | Partial<OpenAIClient.Chat.Completions.ChatCompletionChunk.Choice>
341
+ | undefined;
342
+ if (data.usage) {
343
+ usage = data.usage;
344
+ }
345
+ if (!choice) {
346
+ continue;
347
+ }
348
+
349
+ const { delta } = choice;
350
+ if (!delta) {
351
+ continue;
352
+ }
353
+ const chunk = this._convertOpenAIDeltaToBaseMessageChunk(
354
+ delta,
355
+ data,
356
+ defaultRole
357
+ );
358
+ if ('reasoning_content' in delta) {
359
+ chunk.additional_kwargs.reasoning_content = delta.reasoning_content;
360
+ } else if ('reasoning' in delta) {
361
+ chunk.additional_kwargs.reasoning_content = delta.reasoning;
362
+ }
363
+ defaultRole = delta.role ?? defaultRole;
364
+ const newTokenIndices = {
365
+ prompt: options.promptIndex ?? 0,
366
+ completion: choice.index ?? 0,
367
+ };
368
+ if (typeof chunk.content !== 'string') {
369
+ // eslint-disable-next-line no-console
370
+ console.log(
371
+ '[WARNING]: Received non-string content from OpenAI. This is currently not supported.'
372
+ );
373
+ continue;
374
+ }
375
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
376
+ const generationInfo: Record<string, any> = { ...newTokenIndices };
377
+ if (choice.finish_reason != null) {
378
+ generationInfo.finish_reason = choice.finish_reason;
379
+ // Only include system fingerprint in the last chunk for now
380
+ // to avoid concatenation issues
381
+ generationInfo.system_fingerprint = data.system_fingerprint;
382
+ generationInfo.model_name = data.model;
383
+ generationInfo.service_tier = data.service_tier;
384
+ }
385
+ if (this.logprobs == true) {
386
+ generationInfo.logprobs = choice.logprobs;
387
+ }
388
+ const generationChunk = new ChatGenerationChunk({
389
+ message: chunk,
390
+ text: chunk.content,
391
+ generationInfo,
392
+ });
393
+ yield generationChunk;
394
+ if (this._lc_stream_delay != null) {
395
+ await sleep(this._lc_stream_delay);
396
+ }
397
+ await runManager?.handleLLMNewToken(
398
+ generationChunk.text || '',
399
+ newTokenIndices,
400
+ undefined,
401
+ undefined,
402
+ undefined,
403
+ { chunk: generationChunk }
404
+ );
405
+ }
406
+ if (usage) {
407
+ const inputTokenDetails = {
408
+ ...(usage.prompt_tokens_details?.audio_tokens != null && {
409
+ audio: usage.prompt_tokens_details.audio_tokens,
410
+ }),
411
+ ...(usage.prompt_tokens_details?.cached_tokens != null && {
412
+ cache_read: usage.prompt_tokens_details.cached_tokens,
413
+ }),
414
+ };
415
+ const outputTokenDetails = {
416
+ ...(usage.completion_tokens_details?.audio_tokens != null && {
417
+ audio: usage.completion_tokens_details.audio_tokens,
418
+ }),
419
+ ...(usage.completion_tokens_details?.reasoning_tokens != null && {
420
+ reasoning: usage.completion_tokens_details.reasoning_tokens,
421
+ }),
422
+ };
423
+ const generationChunk = new ChatGenerationChunk({
424
+ message: new AIMessageChunk({
425
+ content: '',
426
+ response_metadata: {
427
+ usage: { ...usage },
428
+ },
429
+ usage_metadata: {
430
+ input_tokens: usage.prompt_tokens,
431
+ output_tokens: usage.completion_tokens,
432
+ total_tokens: usage.total_tokens,
433
+ ...(Object.keys(inputTokenDetails).length > 0 && {
434
+ input_token_details: inputTokenDetails,
435
+ }),
436
+ ...(Object.keys(outputTokenDetails).length > 0 && {
437
+ output_token_details: outputTokenDetails,
438
+ }),
439
+ },
440
+ }),
441
+ text: '',
442
+ });
443
+ yield generationChunk;
444
+ if (this._lc_stream_delay != null) {
445
+ await sleep(this._lc_stream_delay);
446
+ }
447
+ }
448
+ if (options.signal?.aborted === true) {
449
+ throw new Error('AbortError');
450
+ }
451
+ }
121
452
  }
122
453
 
454
+ /** @ts-expect-error We are intentionally overriding `getReasoningParams` */
123
455
  export class AzureChatOpenAI extends OriginalAzureChatOpenAI {
456
+ _lc_stream_delay?: number;
457
+
458
+ constructor(fields?: t.AzureOpenAIInput & { _lc_stream_delay?: number }) {
459
+ super(fields);
460
+ this._lc_stream_delay = fields?._lc_stream_delay;
461
+ }
462
+
124
463
  public get exposedClient(): CustomOpenAIClient {
125
464
  return this.client;
126
465
  }
466
+ /**
467
+ * Returns backwards compatible reasoning parameters from constructor params and call options
468
+ * @internal
469
+ */
470
+ getReasoningParams(
471
+ options?: this['ParsedCallOptions']
472
+ ): OpenAIClient.Reasoning | undefined {
473
+ if (!isReasoningModel(this.model)) {
474
+ return;
475
+ }
476
+
477
+ // apply options in reverse order of importance -- newer options supersede older options
478
+ let reasoning: OpenAIClient.Reasoning | undefined;
479
+ if (this.reasoning !== undefined) {
480
+ reasoning = {
481
+ ...reasoning,
482
+ ...this.reasoning,
483
+ };
484
+ }
485
+ if (options?.reasoning !== undefined) {
486
+ reasoning = {
487
+ ...reasoning,
488
+ ...options.reasoning,
489
+ };
490
+ }
491
+
492
+ return reasoning;
493
+ }
494
+
495
+ protected _getReasoningParams(
496
+ options?: this['ParsedCallOptions']
497
+ ): OpenAIClient.Reasoning | undefined {
498
+ return this.getReasoningParams(options);
499
+ }
500
+
127
501
  protected _getClientOptions(
128
- options: t.OpenAICoreRequestOptions | undefined
129
- ): t.OpenAICoreRequestOptions {
130
- if (!(this.client as AzureOpenAIClient | undefined)) {
502
+ options: OpenAICoreRequestOptions | undefined
503
+ ): OpenAICoreRequestOptions {
504
+ if (!(this.client as unknown as AzureOpenAIClient | undefined)) {
131
505
  const openAIEndpointConfig: t.OpenAIEndpointConfig = {
132
506
  azureOpenAIApiDeploymentName: this.azureOpenAIApiDeploymentName,
133
507
  azureOpenAIApiInstanceName: this.azureOpenAIApiInstanceName,
@@ -154,25 +528,26 @@ export class AzureChatOpenAI extends OriginalAzureChatOpenAI {
154
528
  delete params.baseURL;
155
529
  }
156
530
 
531
+ const defaultHeaders = normalizeHeaders(params.defaultHeaders);
157
532
  params.defaultHeaders = {
158
533
  ...params.defaultHeaders,
159
534
  'User-Agent':
160
- params.defaultHeaders?.['User-Agent'] != null
161
- ? `${params.defaultHeaders['User-Agent']}: langchainjs-azure-openai-v2`
162
- : 'langchainjs-azure-openai-v2',
535
+ defaultHeaders['User-Agent'] != null
536
+ ? `${defaultHeaders['User-Agent']}: librechat-azure-openai-v2`
537
+ : 'librechat-azure-openai-v2',
163
538
  };
164
539
 
165
540
  this.client = new CustomAzureOpenAIClient({
166
541
  apiVersion: this.azureOpenAIApiVersion,
167
542
  azureADTokenProvider: this.azureADTokenProvider,
168
- ...params,
169
- });
543
+ ...(params as t.AzureOpenAIInput),
544
+ }) as unknown as CustomOpenAIClient;
170
545
  }
171
546
 
172
547
  const requestOptions = {
173
548
  ...this.clientConfig,
174
549
  ...options,
175
- } as t.OpenAICoreRequestOptions;
550
+ } as OpenAICoreRequestOptions;
176
551
  if (this.azureOpenAIApiKey != null) {
177
552
  requestOptions.headers = {
178
553
  'api-key': this.azureOpenAIApiKey,
@@ -185,8 +560,49 @@ export class AzureChatOpenAI extends OriginalAzureChatOpenAI {
185
560
  }
186
561
  return requestOptions;
187
562
  }
188
- }
563
+ async *_streamResponseChunks(
564
+ messages: BaseMessage[],
565
+ options: this['ParsedCallOptions'],
566
+ runManager?: CallbackManagerForLLMRun
567
+ ): AsyncGenerator<ChatGenerationChunk> {
568
+ if (!this._useResponseApi(options)) {
569
+ return yield* super._streamResponseChunks(messages, options, runManager);
570
+ }
571
+ const streamIterable = await this.responseApiWithRetry(
572
+ {
573
+ ...this.invocationParams<'responses'>(options, { streaming: true }),
574
+ input: _convertMessagesToOpenAIResponsesParams(
575
+ messages,
576
+ this.model,
577
+ this.zdrEnabled
578
+ ),
579
+ stream: true,
580
+ },
581
+ options
582
+ );
583
+
584
+ for await (const data of streamIterable) {
585
+ const chunk = _convertOpenAIResponsesDeltaToBaseMessageChunk(
586
+ data as ResponseReturnStreamEvents
587
+ );
588
+ if (chunk == null) continue;
589
+ yield chunk;
590
+ if (this._lc_stream_delay != null) {
591
+ await sleep(this._lc_stream_delay);
592
+ }
593
+ await runManager?.handleLLMNewToken(
594
+ chunk.text || '',
595
+ undefined,
596
+ undefined,
597
+ undefined,
598
+ undefined,
599
+ { chunk }
600
+ );
601
+ }
189
602
 
603
+ return;
604
+ }
605
+ }
190
606
  export class ChatDeepSeek extends OriginalChatDeepSeek {
191
607
  public get exposedClient(): CustomOpenAIClient {
192
608
  return this.client;
@@ -220,10 +636,53 @@ export class ChatDeepSeek extends OriginalChatDeepSeek {
220
636
  }
221
637
  }
222
638
 
639
+ /** xAI-specific usage metadata type */
640
+ export interface XAIUsageMetadata
641
+ extends OpenAIClient.Completions.CompletionUsage {
642
+ prompt_tokens_details?: {
643
+ audio_tokens?: number;
644
+ cached_tokens?: number;
645
+ text_tokens?: number;
646
+ image_tokens?: number;
647
+ };
648
+ completion_tokens_details?: {
649
+ audio_tokens?: number;
650
+ reasoning_tokens?: number;
651
+ accepted_prediction_tokens?: number;
652
+ rejected_prediction_tokens?: number;
653
+ };
654
+ num_sources_used?: number;
655
+ }
656
+
223
657
  export class ChatXAI extends OriginalChatXAI {
658
+ _lc_stream_delay?: number;
659
+
660
+ constructor(
661
+ fields?: Partial<ChatXAIInput> & {
662
+ configuration?: { baseURL?: string };
663
+ clientConfig?: { baseURL?: string };
664
+ _lc_stream_delay?: number;
665
+ }
666
+ ) {
667
+ super(fields);
668
+ this._lc_stream_delay = fields?._lc_stream_delay;
669
+ const customBaseURL =
670
+ fields?.configuration?.baseURL ?? fields?.clientConfig?.baseURL;
671
+ if (customBaseURL != null && customBaseURL) {
672
+ this.clientConfig = {
673
+ ...this.clientConfig,
674
+ baseURL: customBaseURL,
675
+ };
676
+ // Reset the client to force recreation with new config
677
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
678
+ this.client = undefined as any;
679
+ }
680
+ }
681
+
224
682
  public get exposedClient(): CustomOpenAIClient {
225
683
  return this.client;
226
684
  }
685
+
227
686
  protected _getClientOptions(
228
687
  options?: OpenAICoreRequestOptions
229
688
  ): OpenAICoreRequestOptions {
@@ -251,4 +710,172 @@ export class ChatXAI extends OriginalChatXAI {
251
710
  } as OpenAICoreRequestOptions;
252
711
  return requestOptions;
253
712
  }
713
+
714
+ async *_streamResponseChunks(
715
+ messages: BaseMessage[],
716
+ options: this['ParsedCallOptions'],
717
+ runManager?: CallbackManagerForLLMRun
718
+ ): AsyncGenerator<ChatGenerationChunk> {
719
+ const messagesMapped: OpenAICompletionParam[] =
720
+ _convertMessagesToOpenAIParams(messages, this.model);
721
+
722
+ const params = {
723
+ ...this.invocationParams(options, {
724
+ streaming: true,
725
+ }),
726
+ messages: messagesMapped,
727
+ stream: true as const,
728
+ };
729
+ let defaultRole: OpenAIRoleEnum | undefined;
730
+
731
+ const streamIterable = await this.completionWithRetry(params, options);
732
+ let usage: OpenAIClient.Completions.CompletionUsage | undefined;
733
+ for await (const data of streamIterable) {
734
+ const choice = data.choices[0] as
735
+ | Partial<OpenAIClient.Chat.Completions.ChatCompletionChunk.Choice>
736
+ | undefined;
737
+ if (data.usage) {
738
+ usage = data.usage;
739
+ }
740
+ if (!choice) {
741
+ continue;
742
+ }
743
+
744
+ const { delta } = choice;
745
+ if (!delta) {
746
+ continue;
747
+ }
748
+ const chunk = this._convertOpenAIDeltaToBaseMessageChunk(
749
+ delta,
750
+ data,
751
+ defaultRole
752
+ );
753
+ if (chunk.usage_metadata != null) {
754
+ chunk.usage_metadata = {
755
+ input_tokens:
756
+ (chunk.usage_metadata as Partial<UsageMetadata>).input_tokens ?? 0,
757
+ output_tokens:
758
+ (chunk.usage_metadata as Partial<UsageMetadata>).output_tokens ?? 0,
759
+ total_tokens:
760
+ (chunk.usage_metadata as Partial<UsageMetadata>).total_tokens ?? 0,
761
+ };
762
+ }
763
+ if ('reasoning_content' in delta) {
764
+ chunk.additional_kwargs.reasoning_content = delta.reasoning_content;
765
+ }
766
+ defaultRole = delta.role ?? defaultRole;
767
+ const newTokenIndices = {
768
+ prompt: (options as OpenAIChatCallOptions).promptIndex ?? 0,
769
+ completion: choice.index ?? 0,
770
+ };
771
+ if (typeof chunk.content !== 'string') {
772
+ // eslint-disable-next-line no-console
773
+ console.log(
774
+ '[WARNING]: Received non-string content from OpenAI. This is currently not supported.'
775
+ );
776
+ continue;
777
+ }
778
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
779
+ const generationInfo: Record<string, any> = { ...newTokenIndices };
780
+ if (choice.finish_reason != null) {
781
+ generationInfo.finish_reason = choice.finish_reason;
782
+ // Only include system fingerprint in the last chunk for now
783
+ // to avoid concatenation issues
784
+ generationInfo.system_fingerprint = data.system_fingerprint;
785
+ generationInfo.model_name = data.model;
786
+ generationInfo.service_tier = data.service_tier;
787
+ }
788
+ if (this.logprobs == true) {
789
+ generationInfo.logprobs = choice.logprobs;
790
+ }
791
+ const generationChunk = new ChatGenerationChunk({
792
+ message: chunk,
793
+ text: chunk.content,
794
+ generationInfo,
795
+ });
796
+ yield generationChunk;
797
+ if (this._lc_stream_delay != null) {
798
+ await sleep(this._lc_stream_delay);
799
+ }
800
+ await runManager?.handleLLMNewToken(
801
+ generationChunk.text || '',
802
+ newTokenIndices,
803
+ undefined,
804
+ undefined,
805
+ undefined,
806
+ { chunk: generationChunk }
807
+ );
808
+ }
809
+ if (usage) {
810
+ // Type assertion for xAI-specific usage structure
811
+ const xaiUsage = usage as XAIUsageMetadata;
812
+ const inputTokenDetails = {
813
+ // Standard OpenAI fields
814
+ ...(usage.prompt_tokens_details?.audio_tokens != null && {
815
+ audio: usage.prompt_tokens_details.audio_tokens,
816
+ }),
817
+ ...(usage.prompt_tokens_details?.cached_tokens != null && {
818
+ cache_read: usage.prompt_tokens_details.cached_tokens,
819
+ }),
820
+ // Add xAI-specific prompt token details if they exist
821
+ ...(xaiUsage.prompt_tokens_details?.text_tokens != null && {
822
+ text: xaiUsage.prompt_tokens_details.text_tokens,
823
+ }),
824
+ ...(xaiUsage.prompt_tokens_details?.image_tokens != null && {
825
+ image: xaiUsage.prompt_tokens_details.image_tokens,
826
+ }),
827
+ };
828
+ const outputTokenDetails = {
829
+ // Standard OpenAI fields
830
+ ...(usage.completion_tokens_details?.audio_tokens != null && {
831
+ audio: usage.completion_tokens_details.audio_tokens,
832
+ }),
833
+ ...(usage.completion_tokens_details?.reasoning_tokens != null && {
834
+ reasoning: usage.completion_tokens_details.reasoning_tokens,
835
+ }),
836
+ // Add xAI-specific completion token details if they exist
837
+ ...(xaiUsage.completion_tokens_details?.accepted_prediction_tokens !=
838
+ null && {
839
+ accepted_prediction:
840
+ xaiUsage.completion_tokens_details.accepted_prediction_tokens,
841
+ }),
842
+ ...(xaiUsage.completion_tokens_details?.rejected_prediction_tokens !=
843
+ null && {
844
+ rejected_prediction:
845
+ xaiUsage.completion_tokens_details.rejected_prediction_tokens,
846
+ }),
847
+ };
848
+ const generationChunk = new ChatGenerationChunk({
849
+ message: new AIMessageChunk({
850
+ content: '',
851
+ response_metadata: {
852
+ usage: { ...usage },
853
+ // Include xAI-specific metadata if it exists
854
+ ...(xaiUsage.num_sources_used != null && {
855
+ num_sources_used: xaiUsage.num_sources_used,
856
+ }),
857
+ },
858
+ usage_metadata: {
859
+ input_tokens: usage.prompt_tokens,
860
+ output_tokens: usage.completion_tokens,
861
+ total_tokens: usage.total_tokens,
862
+ ...(Object.keys(inputTokenDetails).length > 0 && {
863
+ input_token_details: inputTokenDetails,
864
+ }),
865
+ ...(Object.keys(outputTokenDetails).length > 0 && {
866
+ output_token_details: outputTokenDetails,
867
+ }),
868
+ },
869
+ }),
870
+ text: '',
871
+ });
872
+ yield generationChunk;
873
+ if (this._lc_stream_delay != null) {
874
+ await sleep(this._lc_stream_delay);
875
+ }
876
+ }
877
+ if (options.signal?.aborted === true) {
878
+ throw new Error('AbortError');
879
+ }
880
+ }
254
881
  }