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