@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
@@ -1,36 +1,40 @@
1
1
  import { AzureOpenAI as AzureOpenAIClient } from 'openai';
2
2
  import { ChatXAI as OriginalChatXAI } from '@langchain/xai';
3
3
  import { ChatGenerationChunk } from '@langchain/core/outputs';
4
- import { AIMessage, AIMessageChunk } from '@langchain/core/messages';
4
+ import {
5
+ AIMessage,
6
+ AIMessageChunk,
7
+ isAIMessage,
8
+ } from '@langchain/core/messages';
5
9
  import { ToolDefinition } from '@langchain/core/language_models/base';
6
- import { isLangChainTool } from '@langchain/core/utils/function_calling';
10
+ import {
11
+ convertToOpenAITool,
12
+ isLangChainTool,
13
+ } from '@langchain/core/utils/function_calling';
7
14
  import { ChatDeepSeek as OriginalChatDeepSeek } from '@langchain/deepseek';
8
15
  import { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';
9
16
  import {
10
17
  getEndpoint,
11
18
  OpenAIClient,
12
- formatToOpenAITool,
19
+ getHeadersWithUserAgent,
13
20
  ChatOpenAI as OriginalChatOpenAI,
21
+ ChatOpenAIResponses as OriginalChatOpenAIResponses,
22
+ ChatOpenAICompletions as OriginalChatOpenAICompletions,
14
23
  AzureChatOpenAI as OriginalAzureChatOpenAI,
24
+ AzureChatOpenAIResponses as OriginalAzureChatOpenAIResponses,
25
+ AzureChatOpenAICompletions as OriginalAzureChatOpenAICompletions,
15
26
  } from '@langchain/openai';
27
+ import type { HeaderValue, HeadersLike } from './types';
16
28
  import type {
17
- OpenAIChatCallOptions,
18
- OpenAIRoleEnum,
19
- HeaderValue,
20
- HeadersLike,
21
- } from './types';
29
+ BaseMessage,
30
+ BaseMessageChunk,
31
+ UsageMetadata,
32
+ } from '@langchain/core/messages';
22
33
  import type { BindToolsInput } from '@langchain/core/language_models/chat_models';
23
- import type { BaseMessage, UsageMetadata } from '@langchain/core/messages';
24
- import type { ChatResult, ChatGeneration } from '@langchain/core/outputs';
34
+ import type { ChatGeneration, ChatResult } from '@langchain/core/outputs';
25
35
  import type { ChatXAIInput } from '@langchain/xai';
26
36
  import type * as t from '@langchain/openai';
27
- import {
28
- isReasoningModel,
29
- _convertMessagesToOpenAIParams,
30
- _convertMessagesToOpenAIResponsesParams,
31
- _convertOpenAIResponsesDeltaToBaseMessageChunk,
32
- type ResponseReturnStreamEvents,
33
- } from './utils';
37
+ import { isReasoningModel, _convertMessagesToOpenAIParams } from './utils';
34
38
  import { sleep } from '@/utils';
35
39
 
36
40
  // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
@@ -79,10 +83,274 @@ export function normalizeHeaders(
79
83
  return Object.fromEntries(output.entries());
80
84
  }
81
85
 
86
+ type OpenAICoreRequestOptions = OpenAIClient.RequestOptions;
82
87
  type OpenAICompletionParam =
83
88
  OpenAIClient.Chat.Completions.ChatCompletionMessageParam;
89
+ type OpenAIClientConfig = NonNullable<
90
+ ConstructorParameters<typeof OpenAIClient>[0]
91
+ >;
92
+ type LibreChatOpenAIFields = t.ChatOpenAIFields & {
93
+ _lc_stream_delay?: number;
94
+ includeReasoningContent?: boolean;
95
+ includeReasoningDetails?: boolean;
96
+ convertReasoningDetailsToContent?: boolean;
97
+ };
98
+ type LibreChatAzureOpenAIFields = t.AzureOpenAIInput & {
99
+ _lc_stream_delay?: number;
100
+ };
101
+ type ReasoningCallOptions = {
102
+ reasoning?: OpenAIClient.Reasoning;
103
+ reasoningEffort?: OpenAIClient.Reasoning['effort'];
104
+ };
105
+ type OpenAIDeltaWithLibreChatFields = Record<string, unknown> & {
106
+ reasoning?: unknown;
107
+ reasoning_details?: unknown;
108
+ provider_specific_fields?: unknown;
109
+ };
110
+ type OpenAIClientOwner = {
111
+ client?: OpenAIClient;
112
+ clientConfig: OpenAIClientConfig;
113
+ timeout?: number;
114
+ };
115
+ type AbortableOpenAIClient = CustomOpenAIClient | CustomAzureOpenAIClient;
116
+ type OpenAIClientDelegate = {
117
+ client?: AbortableOpenAIClient;
118
+ _getClientOptions(
119
+ options: OpenAICoreRequestOptions | undefined
120
+ ): OpenAICoreRequestOptions;
121
+ };
122
+ type OpenAIChatCompletion = OpenAIClient.Chat.Completions.ChatCompletion;
123
+ type OpenAIChatCompletionChunk =
124
+ OpenAIClient.Chat.Completions.ChatCompletionChunk;
125
+ type OpenAIChatCompletionStreamItem =
126
+ | OpenAIChatCompletionChunk
127
+ | {
128
+ event: string;
129
+ data?: unknown;
130
+ };
131
+ type OpenAIChatCompletionRequest =
132
+ | OpenAIClient.Chat.ChatCompletionCreateParamsStreaming
133
+ | OpenAIClient.Chat.ChatCompletionCreateParamsNonStreaming;
134
+ type OpenAIChatCompletionResult =
135
+ | AsyncIterable<OpenAIChatCompletionChunk>
136
+ | OpenAIChatCompletion;
137
+ type OpenAIChatCompletionRetry = (
138
+ request: OpenAIChatCompletionRequest,
139
+ requestOptions?: OpenAICoreRequestOptions
140
+ ) => Promise<
141
+ AsyncIterable<OpenAIChatCompletionStreamItem> | OpenAIChatCompletion
142
+ >;
143
+
144
+ function getExposedOpenAIClient(
145
+ completions: OpenAIClientDelegate,
146
+ responses: OpenAIClientDelegate,
147
+ preferResponses: boolean
148
+ ): AbortableOpenAIClient {
149
+ const responsesClient = responses.client;
150
+ if (responsesClient?.abortHandler != null) {
151
+ return responsesClient;
152
+ }
153
+ const completionsClient = completions.client;
154
+ if (completionsClient?.abortHandler != null) {
155
+ return completionsClient;
156
+ }
84
157
 
85
- type OpenAICoreRequestOptions = OpenAIClient.RequestOptions;
158
+ const delegate = preferResponses ? responses : completions;
159
+ delegate._getClientOptions(undefined);
160
+ return delegate.client as AbortableOpenAIClient;
161
+ }
162
+
163
+ function getReasoningParams(
164
+ baseReasoning: OpenAIClient.Reasoning | undefined,
165
+ options?: ReasoningCallOptions
166
+ ): OpenAIClient.Reasoning | undefined {
167
+ let reasoning: OpenAIClient.Reasoning | undefined;
168
+ if (baseReasoning !== undefined) {
169
+ reasoning = {
170
+ ...reasoning,
171
+ ...baseReasoning,
172
+ };
173
+ }
174
+ if (options?.reasoning !== undefined) {
175
+ reasoning = {
176
+ ...reasoning,
177
+ ...options.reasoning,
178
+ };
179
+ }
180
+ if (
181
+ options?.reasoningEffort !== undefined &&
182
+ reasoning?.effort === undefined
183
+ ) {
184
+ reasoning = {
185
+ ...reasoning,
186
+ effort: options.reasoningEffort,
187
+ };
188
+ }
189
+ return reasoning;
190
+ }
191
+
192
+ function getGatedReasoningParams(
193
+ model: string,
194
+ baseReasoning: OpenAIClient.Reasoning | undefined,
195
+ options?: ReasoningCallOptions
196
+ ): OpenAIClient.Reasoning | undefined {
197
+ if (!isReasoningModel(model)) {
198
+ return;
199
+ }
200
+ return getReasoningParams(baseReasoning, options);
201
+ }
202
+
203
+ function isObject(value: unknown): value is object {
204
+ return typeof value === 'object' && value !== null;
205
+ }
206
+
207
+ function isOpenAIChatCompletionChunk(
208
+ value: unknown
209
+ ): value is OpenAIChatCompletionChunk {
210
+ if (!isObject(value)) {
211
+ return false;
212
+ }
213
+
214
+ // Intentionally loose: downstream handlers already tolerate empty choices.
215
+ const { choices } = value as { choices?: unknown };
216
+ return Array.isArray(choices);
217
+ }
218
+
219
+ function getOpenAIChatCompletionChunk(
220
+ value: OpenAIChatCompletionStreamItem
221
+ ): OpenAIChatCompletionChunk | undefined {
222
+ if (isOpenAIChatCompletionChunk(value)) {
223
+ return value;
224
+ }
225
+
226
+ const { data } = value;
227
+ if (isOpenAIChatCompletionChunk(data)) {
228
+ return data;
229
+ }
230
+
231
+ return undefined;
232
+ }
233
+
234
+ async function* filterOpenAIChatCompletionStream(
235
+ stream: AsyncIterable<OpenAIChatCompletionStreamItem>
236
+ ): AsyncGenerator<OpenAIChatCompletionChunk> {
237
+ for await (const item of stream) {
238
+ const chunk = getOpenAIChatCompletionChunk(item);
239
+ if (chunk == null) {
240
+ continue;
241
+ }
242
+ yield chunk;
243
+ }
244
+ }
245
+
246
+ async function completionWithFilteredOpenAIStream(
247
+ request: OpenAIChatCompletionRequest,
248
+ requestOptions: OpenAICoreRequestOptions | undefined,
249
+ completionWithRetry: OpenAIChatCompletionRetry
250
+ ): Promise<OpenAIChatCompletionResult> {
251
+ if (request.stream !== true) {
252
+ return (await completionWithRetry(
253
+ request,
254
+ requestOptions
255
+ )) as OpenAIChatCompletion;
256
+ }
257
+
258
+ const stream = await completionWithRetry(request, requestOptions);
259
+ return filterOpenAIChatCompletionStream(
260
+ stream as AsyncIterable<OpenAIChatCompletionStreamItem>
261
+ );
262
+ }
263
+
264
+ function attachLibreChatDeltaFields(
265
+ chunk: BaseMessageChunk,
266
+ delta: Record<string, unknown>
267
+ ): BaseMessageChunk {
268
+ if (!AIMessageChunk.isInstance(chunk)) {
269
+ return chunk;
270
+ }
271
+
272
+ const libreChatDelta = delta as OpenAIDeltaWithLibreChatFields;
273
+ if (
274
+ libreChatDelta.reasoning != null &&
275
+ chunk.additional_kwargs.reasoning_content == null
276
+ ) {
277
+ chunk.additional_kwargs.reasoning_content = libreChatDelta.reasoning;
278
+ }
279
+ if (libreChatDelta.reasoning_details != null) {
280
+ chunk.additional_kwargs.reasoning_details =
281
+ libreChatDelta.reasoning_details;
282
+ }
283
+ if (libreChatDelta.provider_specific_fields != null) {
284
+ chunk.additional_kwargs.provider_specific_fields =
285
+ libreChatDelta.provider_specific_fields;
286
+ }
287
+ return chunk;
288
+ }
289
+
290
+ function attachLibreChatMessageFields(
291
+ message: BaseMessage,
292
+ rawMessage: Record<string, unknown>
293
+ ): BaseMessage {
294
+ if (!isAIMessage(message)) {
295
+ return message;
296
+ }
297
+ if (
298
+ rawMessage.reasoning != null &&
299
+ message.additional_kwargs.reasoning_content == null
300
+ ) {
301
+ message.additional_kwargs.reasoning_content = rawMessage.reasoning;
302
+ }
303
+ if (rawMessage.reasoning_details != null) {
304
+ message.additional_kwargs.reasoning_details = rawMessage.reasoning_details;
305
+ }
306
+ if (rawMessage.provider_specific_fields != null) {
307
+ message.additional_kwargs.provider_specific_fields =
308
+ rawMessage.provider_specific_fields;
309
+ }
310
+ return message;
311
+ }
312
+
313
+ function getCustomOpenAIClientOptions(
314
+ owner: OpenAIClientOwner,
315
+ options?: OpenAICoreRequestOptions
316
+ ): OpenAICoreRequestOptions {
317
+ if (!(owner.client as OpenAIClient | undefined)) {
318
+ const openAIEndpointConfig: t.OpenAIEndpointConfig = {
319
+ baseURL: owner.clientConfig.baseURL,
320
+ };
321
+
322
+ const endpoint = getEndpoint(openAIEndpointConfig);
323
+ const params = {
324
+ ...owner.clientConfig,
325
+ baseURL: endpoint,
326
+ timeout: owner.timeout,
327
+ maxRetries: 0,
328
+ };
329
+ if (params.baseURL == null) {
330
+ delete params.baseURL;
331
+ }
332
+
333
+ params.defaultHeaders = getHeadersWithUserAgent(params.defaultHeaders);
334
+ owner.client = new CustomOpenAIClient(params);
335
+ }
336
+ const requestOptions = {
337
+ ...owner.clientConfig,
338
+ ...options,
339
+ } as OpenAICoreRequestOptions;
340
+ return requestOptions;
341
+ }
342
+
343
+ async function* delayStreamChunks<T>(
344
+ chunks: AsyncGenerator<T>,
345
+ delay?: number
346
+ ): AsyncGenerator<T> {
347
+ for await (const chunk of chunks) {
348
+ yield chunk;
349
+ if (delay != null) {
350
+ await sleep(delay);
351
+ }
352
+ }
353
+ }
86
354
 
87
355
  function createAbortHandler(controller: AbortController): () => void {
88
356
  return function (): void {
@@ -113,7 +381,7 @@ export function _convertToOpenAITool(
113
381
  let toolDef: OpenAIClient.ChatCompletionTool | undefined;
114
382
 
115
383
  if (isLangChainTool(tool)) {
116
- toolDef = formatToOpenAITool(tool);
384
+ toolDef = convertToOpenAITool(tool);
117
385
  } else {
118
386
  toolDef = tool as ToolDefinition;
119
387
  }
@@ -195,134 +463,261 @@ export class CustomAzureOpenAIClient extends AzureOpenAIClient {
195
463
  }
196
464
  }
197
465
 
198
- /** @ts-expect-error We are intentionally overriding `getReasoningParams` */
199
- export class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
200
- _lc_stream_delay?: number;
466
+ class LibreChatOpenAICompletions extends OriginalChatOpenAICompletions {
467
+ private includeReasoningContent?: boolean;
468
+ private includeReasoningDetails?: boolean;
469
+ private convertReasoningDetailsToContent?: boolean;
201
470
 
202
- constructor(
203
- fields?: t.ChatOpenAICallOptions & {
204
- _lc_stream_delay?: number;
205
- } & t.OpenAIChatInput['modelKwargs']
206
- ) {
471
+ constructor(fields?: LibreChatOpenAIFields) {
207
472
  super(fields);
208
- this._lc_stream_delay = fields?._lc_stream_delay;
473
+ this.includeReasoningContent = fields?.includeReasoningContent;
474
+ this.includeReasoningDetails = fields?.includeReasoningDetails;
475
+ this.convertReasoningDetailsToContent =
476
+ fields?.convertReasoningDetailsToContent;
209
477
  }
210
478
 
211
- public get exposedClient(): CustomOpenAIClient {
212
- return this.client;
213
- }
214
- static lc_name(): string {
215
- return 'LibreChatOpenAI';
479
+ protected _getReasoningParams(
480
+ options?: this['ParsedCallOptions']
481
+ ): OpenAIClient.Reasoning | undefined {
482
+ return getReasoningParams(this.reasoning, options);
216
483
  }
217
- protected _getClientOptions(
484
+
485
+ _getClientOptions(
218
486
  options?: OpenAICoreRequestOptions
219
487
  ): OpenAICoreRequestOptions {
220
- if (!(this.client as OpenAIClient | undefined)) {
221
- const openAIEndpointConfig: t.OpenAIEndpointConfig = {
222
- baseURL: this.clientConfig.baseURL,
223
- };
224
-
225
- const endpoint = getEndpoint(openAIEndpointConfig);
226
- const params = {
227
- ...this.clientConfig,
228
- baseURL: endpoint,
229
- timeout: this.timeout,
230
- maxRetries: 0,
231
- };
232
- if (params.baseURL == null) {
233
- delete params.baseURL;
234
- }
235
-
236
- this.client = new CustomOpenAIClient(params);
237
- }
238
- const requestOptions = {
239
- ...this.clientConfig,
240
- ...options,
241
- } as OpenAICoreRequestOptions;
242
- return requestOptions;
488
+ return getCustomOpenAIClientOptions(this, options);
243
489
  }
244
490
 
245
- /**
246
- * Returns backwards compatible reasoning parameters from constructor params and call options
247
- * @internal
248
- */
249
- getReasoningParams(
250
- options?: this['ParsedCallOptions']
251
- ): OpenAIClient.Reasoning | undefined {
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
- }
491
+ async completionWithRetry(
492
+ request: OpenAIClient.Chat.ChatCompletionCreateParamsStreaming,
493
+ requestOptions?: OpenAICoreRequestOptions
494
+ ): Promise<AsyncIterable<OpenAIChatCompletionChunk>>;
495
+ async completionWithRetry(
496
+ request: OpenAIClient.Chat.ChatCompletionCreateParamsNonStreaming,
497
+ requestOptions?: OpenAICoreRequestOptions
498
+ ): Promise<OpenAIChatCompletion>;
499
+ async completionWithRetry(
500
+ request:
501
+ | OpenAIClient.Chat.ChatCompletionCreateParamsStreaming
502
+ | OpenAIClient.Chat.ChatCompletionCreateParamsNonStreaming,
503
+ requestOptions?: OpenAICoreRequestOptions
504
+ ): Promise<AsyncIterable<OpenAIChatCompletionChunk> | OpenAIChatCompletion> {
505
+ return completionWithFilteredOpenAIStream(
506
+ request,
507
+ requestOptions,
508
+ super.completionWithRetry.bind(this) as OpenAIChatCompletionRetry
509
+ );
510
+ }
266
511
 
267
- return reasoning;
512
+ protected _convertCompletionsDeltaToBaseMessageChunk(
513
+ delta: Record<string, unknown>,
514
+ rawResponse: OpenAIClient.Chat.Completions.ChatCompletionChunk,
515
+ defaultRole?: OpenAIClient.Chat.ChatCompletionRole
516
+ ): BaseMessageChunk {
517
+ return attachLibreChatDeltaFields(
518
+ super._convertCompletionsDeltaToBaseMessageChunk(
519
+ delta,
520
+ rawResponse,
521
+ defaultRole
522
+ ),
523
+ delta
524
+ );
268
525
  }
269
526
 
270
- protected _getReasoningParams(
271
- options?: this['ParsedCallOptions']
272
- ): OpenAIClient.Reasoning | undefined {
273
- return this.getReasoningParams(options);
527
+ protected _convertCompletionsMessageToBaseMessage(
528
+ message: OpenAIClient.ChatCompletionMessage,
529
+ rawResponse: OpenAIClient.ChatCompletion
530
+ ): BaseMessage {
531
+ return attachLibreChatMessageFields(
532
+ super._convertCompletionsMessageToBaseMessage(message, rawResponse),
533
+ message as unknown as Record<string, unknown>
534
+ );
274
535
  }
275
536
 
276
- async *_streamResponseChunks(
537
+ async _generate(
277
538
  messages: BaseMessage[],
278
539
  options: this['ParsedCallOptions'],
279
540
  runManager?: CallbackManagerForLLMRun
280
- ): AsyncGenerator<ChatGenerationChunk> {
281
- if (!this._useResponseApi(options)) {
282
- return yield* this._streamResponseChunks2(messages, options, runManager);
541
+ ): Promise<ChatResult> {
542
+ if (
543
+ this.includeReasoningContent !== true &&
544
+ this.includeReasoningDetails !== true
545
+ ) {
546
+ return super._generate(messages, options, runManager);
283
547
  }
284
- const streamIterable = await this.responseApiWithRetry(
548
+
549
+ options.signal?.throwIfAborted();
550
+ const usageMetadata: Partial<UsageMetadata> = {};
551
+ const params = this.invocationParams(options);
552
+ const messagesMapped = _convertMessagesToOpenAIParams(
553
+ messages,
554
+ this.model,
285
555
  {
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
556
+ includeReasoningContent: this.includeReasoningContent,
557
+ includeReasoningDetails: this.includeReasoningDetails,
558
+ convertReasoningDetailsToContent: this.convertReasoningDetailsToContent,
559
+ }
295
560
  );
296
561
 
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);
562
+ if (params.stream === true) {
563
+ const stream = this._streamResponseChunks(messages, options, runManager);
564
+ const finalChunks = new Map<number, ChatGenerationChunk>();
565
+ for await (const chunk of stream) {
566
+ chunk.message.response_metadata = {
567
+ ...chunk.generationInfo,
568
+ ...chunk.message.response_metadata,
569
+ };
570
+ const index =
571
+ typeof chunk.generationInfo?.completion === 'number'
572
+ ? chunk.generationInfo.completion
573
+ : 0;
574
+ const existingChunk = finalChunks.get(index);
575
+ if (existingChunk == null) {
576
+ finalChunks.set(index, chunk);
577
+ } else {
578
+ finalChunks.set(index, existingChunk.concat(chunk));
579
+ }
305
580
  }
306
- await runManager?.handleLLMNewToken(
307
- chunk.text || '',
308
- undefined,
309
- undefined,
310
- undefined,
311
- undefined,
312
- { chunk }
581
+ const generations = Array.from(finalChunks.entries())
582
+ .sort(([aKey], [bKey]) => aKey - bKey)
583
+ .map(([, value]) => value);
584
+ const { functions, function_call } = this.invocationParams(options);
585
+ const promptTokenUsage = await this._getEstimatedTokenCountFromPrompt(
586
+ messages,
587
+ functions,
588
+ function_call
313
589
  );
590
+ const completionTokenUsage =
591
+ await this._getNumTokensFromGenerations(generations);
592
+ usageMetadata.input_tokens = promptTokenUsage;
593
+ usageMetadata.output_tokens = completionTokenUsage;
594
+ usageMetadata.total_tokens = promptTokenUsage + completionTokenUsage;
595
+ return {
596
+ generations,
597
+ llmOutput: {
598
+ estimatedTokenUsage: {
599
+ promptTokens: usageMetadata.input_tokens,
600
+ completionTokens: usageMetadata.output_tokens,
601
+ totalTokens: usageMetadata.total_tokens,
602
+ },
603
+ },
604
+ };
314
605
  }
315
606
 
316
- return;
607
+ const data = await this.completionWithRetry(
608
+ {
609
+ ...params,
610
+ stream: false,
611
+ messages: messagesMapped,
612
+ },
613
+ {
614
+ signal: options.signal,
615
+ ...options.options,
616
+ }
617
+ );
618
+ const {
619
+ completion_tokens: completionTokens,
620
+ prompt_tokens: promptTokens,
621
+ total_tokens: totalTokens,
622
+ prompt_tokens_details: promptTokensDetails,
623
+ completion_tokens_details: completionTokensDetails,
624
+ } = data.usage ?? {};
625
+
626
+ if (completionTokens != null) {
627
+ usageMetadata.output_tokens =
628
+ (usageMetadata.output_tokens ?? 0) + completionTokens;
629
+ }
630
+ if (promptTokens != null) {
631
+ usageMetadata.input_tokens =
632
+ (usageMetadata.input_tokens ?? 0) + promptTokens;
633
+ }
634
+ if (totalTokens != null) {
635
+ usageMetadata.total_tokens =
636
+ (usageMetadata.total_tokens ?? 0) + totalTokens;
637
+ }
638
+ if (
639
+ promptTokensDetails?.audio_tokens != null ||
640
+ promptTokensDetails?.cached_tokens != null
641
+ ) {
642
+ usageMetadata.input_token_details = {
643
+ ...(promptTokensDetails.audio_tokens != null && {
644
+ audio: promptTokensDetails.audio_tokens,
645
+ }),
646
+ ...(promptTokensDetails.cached_tokens != null && {
647
+ cache_read: promptTokensDetails.cached_tokens,
648
+ }),
649
+ };
650
+ }
651
+ if (
652
+ completionTokensDetails?.audio_tokens != null ||
653
+ completionTokensDetails?.reasoning_tokens != null
654
+ ) {
655
+ usageMetadata.output_token_details = {
656
+ ...(completionTokensDetails.audio_tokens != null && {
657
+ audio: completionTokensDetails.audio_tokens,
658
+ }),
659
+ ...(completionTokensDetails.reasoning_tokens != null && {
660
+ reasoning: completionTokensDetails.reasoning_tokens,
661
+ }),
662
+ };
663
+ }
664
+
665
+ const generations: ChatGeneration[] = [];
666
+ for (const part of data.choices) {
667
+ const generation: ChatGeneration = {
668
+ text: part.message.content ?? '',
669
+ message: this._convertCompletionsMessageToBaseMessage(
670
+ part.message,
671
+ data
672
+ ),
673
+ };
674
+ generation.generationInfo = {
675
+ finish_reason: part.finish_reason,
676
+ ...(part.logprobs ? { logprobs: part.logprobs } : {}),
677
+ };
678
+ if (isAIMessage(generation.message)) {
679
+ generation.message.usage_metadata = usageMetadata as UsageMetadata;
680
+ }
681
+ generation.message = new AIMessage(
682
+ Object.fromEntries(
683
+ Object.entries(generation.message).filter(
684
+ ([key]) => !key.startsWith('lc_')
685
+ )
686
+ )
687
+ );
688
+ generations.push(generation);
689
+ }
690
+ return {
691
+ generations,
692
+ llmOutput: {
693
+ tokenUsage: {
694
+ promptTokens: usageMetadata.input_tokens,
695
+ completionTokens: usageMetadata.output_tokens,
696
+ totalTokens: usageMetadata.total_tokens,
697
+ },
698
+ },
699
+ };
317
700
  }
318
701
 
319
- async *_streamResponseChunks2(
702
+ async *_streamResponseChunks(
320
703
  messages: BaseMessage[],
321
704
  options: this['ParsedCallOptions'],
322
705
  runManager?: CallbackManagerForLLMRun
323
706
  ): AsyncGenerator<ChatGenerationChunk> {
707
+ if (
708
+ this.includeReasoningContent !== true &&
709
+ this.includeReasoningDetails !== true
710
+ ) {
711
+ yield* super._streamResponseChunks(messages, options, runManager);
712
+ return;
713
+ }
714
+
324
715
  const messagesMapped: OpenAICompletionParam[] =
325
- _convertMessagesToOpenAIParams(messages, this.model);
716
+ _convertMessagesToOpenAIParams(messages, this.model, {
717
+ includeReasoningContent: this.includeReasoningContent,
718
+ includeReasoningDetails: this.includeReasoningDetails,
719
+ convertReasoningDetailsToContent: this.convertReasoningDetailsToContent,
720
+ });
326
721
 
327
722
  const params = {
328
723
  ...this.invocationParams(options, {
@@ -331,43 +726,42 @@ export class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
331
726
  messages: messagesMapped,
332
727
  stream: true as const,
333
728
  };
334
- let defaultRole: OpenAIRoleEnum | undefined;
729
+ let defaultRole: OpenAIClient.Chat.ChatCompletionRole | undefined;
335
730
 
336
731
  const streamIterable = await this.completionWithRetry(params, options);
337
732
  let usage: OpenAIClient.Completions.CompletionUsage | undefined;
338
733
  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) {
734
+ if (options.signal?.aborted === true) {
735
+ return;
736
+ }
737
+ type StreamChoice = Omit<
738
+ OpenAIClient.Chat.Completions.ChatCompletionChunk.Choice,
739
+ 'delta'
740
+ > & {
741
+ delta?: OpenAIClient.Chat.Completions.ChatCompletionChunk.Choice['delta'];
742
+ };
743
+ const choices = data.choices as StreamChoice[] | undefined;
744
+ const choice = choices?.[0];
745
+ if (data.usage != null) {
343
746
  usage = data.usage;
344
747
  }
345
- if (!choice) {
748
+ if (choice == null) {
346
749
  continue;
347
750
  }
348
751
 
349
752
  const { delta } = choice;
350
- if (!delta) {
753
+ if (delta == null) {
351
754
  continue;
352
755
  }
353
- const chunk = this._convertOpenAIDeltaToBaseMessageChunk(
354
- delta,
756
+ const chunk = this._convertCompletionsDeltaToBaseMessageChunk(
757
+ delta as unknown as Record<string, unknown>,
355
758
  data,
356
759
  defaultRole
357
760
  );
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
- if ('provider_specific_fields' in delta) {
364
- chunk.additional_kwargs.provider_specific_fields =
365
- delta.provider_specific_fields;
366
- }
367
761
  defaultRole = delta.role ?? defaultRole;
368
762
  const newTokenIndices = {
369
763
  prompt: options.promptIndex ?? 0,
370
- completion: choice.index ?? 0,
764
+ completion: choice.index,
371
765
  };
372
766
  if (typeof chunk.content !== 'string') {
373
767
  // eslint-disable-next-line no-console
@@ -376,17 +770,14 @@ export class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
376
770
  );
377
771
  continue;
378
772
  }
379
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
380
- const generationInfo: Record<string, any> = { ...newTokenIndices };
773
+ const generationInfo: Record<string, unknown> = { ...newTokenIndices };
381
774
  if (choice.finish_reason != null) {
382
775
  generationInfo.finish_reason = choice.finish_reason;
383
- // Only include system fingerprint in the last chunk for now
384
- // to avoid concatenation issues
385
776
  generationInfo.system_fingerprint = data.system_fingerprint;
386
777
  generationInfo.model_name = data.model;
387
778
  generationInfo.service_tier = data.service_tier;
388
779
  }
389
- if (this.logprobs == true) {
780
+ if (this.logprobs === true) {
390
781
  generationInfo.logprobs = choice.logprobs;
391
782
  }
392
783
  const generationChunk = new ChatGenerationChunk({
@@ -395,11 +786,8 @@ export class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
395
786
  generationInfo,
396
787
  });
397
788
  yield generationChunk;
398
- if (this._lc_stream_delay != null) {
399
- await sleep(this._lc_stream_delay);
400
- }
401
789
  await runManager?.handleLLMNewToken(
402
- generationChunk.text || '',
790
+ generationChunk.text,
403
791
  newTokenIndices,
404
792
  undefined,
405
793
  undefined,
@@ -427,9 +815,7 @@ export class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
427
815
  const generationChunk = new ChatGenerationChunk({
428
816
  message: new AIMessageChunk({
429
817
  content: '',
430
- response_metadata: {
431
- usage: { ...usage },
432
- },
818
+ response_metadata: { usage: { ...usage } },
433
819
  usage_metadata: {
434
820
  input_tokens: usage.prompt_tokens,
435
821
  output_tokens: usage.completion_tokens,
@@ -445,9 +831,17 @@ export class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
445
831
  text: '',
446
832
  });
447
833
  yield generationChunk;
448
- if (this._lc_stream_delay != null) {
449
- await sleep(this._lc_stream_delay);
450
- }
834
+ await runManager?.handleLLMNewToken(
835
+ generationChunk.text,
836
+ {
837
+ prompt: 0,
838
+ completion: 0,
839
+ },
840
+ undefined,
841
+ undefined,
842
+ undefined,
843
+ { chunk: generationChunk }
844
+ );
451
845
  }
452
846
  if (options.signal?.aborted === true) {
453
847
  throw new Error('AbortError');
@@ -455,57 +849,28 @@ export class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
455
849
  }
456
850
  }
457
851
 
458
- /** @ts-expect-error We are intentionally overriding `getReasoningParams` */
459
- export class AzureChatOpenAI extends OriginalAzureChatOpenAI {
460
- _lc_stream_delay?: number;
461
-
462
- constructor(fields?: t.AzureOpenAIInput & { _lc_stream_delay?: number }) {
463
- super(fields);
464
- this._lc_stream_delay = fields?._lc_stream_delay;
852
+ class LibreChatOpenAIResponses extends OriginalChatOpenAIResponses {
853
+ protected _getReasoningParams(
854
+ options?: this['ParsedCallOptions']
855
+ ): OpenAIClient.Reasoning | undefined {
856
+ return getReasoningParams(this.reasoning, options);
465
857
  }
466
858
 
467
- public get exposedClient(): CustomOpenAIClient {
468
- return this.client;
469
- }
470
- static lc_name(): 'LibreChatAzureOpenAI' {
471
- return 'LibreChatAzureOpenAI';
859
+ _getClientOptions(
860
+ options?: OpenAICoreRequestOptions
861
+ ): OpenAICoreRequestOptions {
862
+ return getCustomOpenAIClientOptions(this, options);
472
863
  }
473
- /**
474
- * Returns backwards compatible reasoning parameters from constructor params and call options
475
- * @internal
476
- */
477
- getReasoningParams(
864
+ }
865
+
866
+ class LibreChatAzureOpenAICompletions extends OriginalAzureChatOpenAICompletions {
867
+ protected _getReasoningParams(
478
868
  options?: this['ParsedCallOptions']
479
869
  ): OpenAIClient.Reasoning | undefined {
480
- if (!isReasoningModel(this.model)) {
481
- return;
482
- }
870
+ return getGatedReasoningParams(this.model, this.reasoning, options);
871
+ }
483
872
 
484
- // apply options in reverse order of importance -- newer options supersede older options
485
- let reasoning: OpenAIClient.Reasoning | undefined;
486
- if (this.reasoning !== undefined) {
487
- reasoning = {
488
- ...reasoning,
489
- ...this.reasoning,
490
- };
491
- }
492
- if (options?.reasoning !== undefined) {
493
- reasoning = {
494
- ...reasoning,
495
- ...options.reasoning,
496
- };
497
- }
498
-
499
- return reasoning;
500
- }
501
-
502
- protected _getReasoningParams(
503
- options?: this['ParsedCallOptions']
504
- ): OpenAIClient.Reasoning | undefined {
505
- return this.getReasoningParams(options);
506
- }
507
-
508
- protected _getClientOptions(
873
+ _getClientOptions(
509
874
  options: OpenAICoreRequestOptions | undefined
510
875
  ): OpenAICoreRequestOptions {
511
876
  if (!(this.client as unknown as AzureOpenAIClient | undefined)) {
@@ -567,162 +932,133 @@ export class AzureChatOpenAI extends OriginalAzureChatOpenAI {
567
932
  }
568
933
  return requestOptions;
569
934
  }
570
- async *_streamResponseChunks(
571
- messages: BaseMessage[],
572
- options: this['ParsedCallOptions'],
573
- runManager?: CallbackManagerForLLMRun
574
- ): AsyncGenerator<ChatGenerationChunk> {
575
- if (!this._useResponseApi(options)) {
576
- return yield* super._streamResponseChunks(messages, options, runManager);
577
- }
578
- const streamIterable = await this.responseApiWithRetry(
579
- {
580
- ...this.invocationParams<'responses'>(options, { streaming: true }),
581
- input: _convertMessagesToOpenAIResponsesParams(
582
- messages,
583
- this.model,
584
- this.zdrEnabled
585
- ),
586
- stream: true,
587
- },
588
- options
589
- );
590
-
591
- for await (const data of streamIterable) {
592
- const chunk = _convertOpenAIResponsesDeltaToBaseMessageChunk(
593
- data as ResponseReturnStreamEvents
594
- );
595
- if (chunk == null) continue;
596
- yield chunk;
597
- if (this._lc_stream_delay != null) {
598
- await sleep(this._lc_stream_delay);
599
- }
600
- await runManager?.handleLLMNewToken(
601
- chunk.text || '',
602
- undefined,
603
- undefined,
604
- undefined,
605
- undefined,
606
- { chunk }
607
- );
608
- }
609
935
 
610
- return;
936
+ async completionWithRetry(
937
+ request: OpenAIClient.Chat.ChatCompletionCreateParamsStreaming,
938
+ requestOptions?: OpenAICoreRequestOptions
939
+ ): Promise<AsyncIterable<OpenAIChatCompletionChunk>>;
940
+ async completionWithRetry(
941
+ request: OpenAIClient.Chat.ChatCompletionCreateParamsNonStreaming,
942
+ requestOptions?: OpenAICoreRequestOptions
943
+ ): Promise<OpenAIChatCompletion>;
944
+ async completionWithRetry(
945
+ request:
946
+ | OpenAIClient.Chat.ChatCompletionCreateParamsStreaming
947
+ | OpenAIClient.Chat.ChatCompletionCreateParamsNonStreaming,
948
+ requestOptions?: OpenAICoreRequestOptions
949
+ ): Promise<AsyncIterable<OpenAIChatCompletionChunk> | OpenAIChatCompletion> {
950
+ return completionWithFilteredOpenAIStream(
951
+ request,
952
+ requestOptions,
953
+ super.completionWithRetry.bind(this) as OpenAIChatCompletionRetry
954
+ );
611
955
  }
612
956
  }
613
- export class ChatDeepSeek extends OriginalChatDeepSeek {
614
- public get exposedClient(): CustomOpenAIClient {
615
- return this.client;
616
- }
617
- static lc_name(): 'LibreChatDeepSeek' {
618
- return 'LibreChatDeepSeek';
619
- }
620
957
 
621
- protected _convertMessages(messages: BaseMessage[]): OpenAICompletionParam[] {
622
- return _convertMessagesToOpenAIParams(messages, this.model, {
623
- includeReasoningContent: true,
624
- });
958
+ class LibreChatAzureOpenAIResponses extends OriginalAzureChatOpenAIResponses {
959
+ protected _getReasoningParams(
960
+ options?: this['ParsedCallOptions']
961
+ ): OpenAIClient.Reasoning | undefined {
962
+ return getGatedReasoningParams(this.model, this.reasoning, options);
625
963
  }
626
964
 
627
- async _generate(
628
- messages: BaseMessage[],
629
- options: this['ParsedCallOptions'] | undefined,
630
- runManager?: CallbackManagerForLLMRun
631
- ): Promise<ChatResult> {
632
- const params = this.invocationParams(options);
965
+ _getClientOptions(
966
+ options: OpenAICoreRequestOptions | undefined
967
+ ): OpenAICoreRequestOptions {
968
+ if (!(this.client as unknown as AzureOpenAIClient | undefined)) {
969
+ const openAIEndpointConfig: t.OpenAIEndpointConfig = {
970
+ azureOpenAIApiDeploymentName: this.azureOpenAIApiDeploymentName,
971
+ azureOpenAIApiInstanceName: this.azureOpenAIApiInstanceName,
972
+ azureOpenAIApiKey: this.azureOpenAIApiKey,
973
+ azureOpenAIBasePath: this.azureOpenAIBasePath,
974
+ azureADTokenProvider: this.azureADTokenProvider,
975
+ baseURL: this.clientConfig.baseURL,
976
+ };
633
977
 
634
- if (params.stream === true) {
635
- return super._generate(messages, options ?? {}, runManager);
636
- }
978
+ const endpoint = getEndpoint(openAIEndpointConfig);
637
979
 
638
- const messagesMapped = this._convertMessages(messages);
639
- const data = await this.completionWithRetry(
640
- {
641
- ...params,
642
- stream: false,
643
- messages: messagesMapped,
644
- },
645
- {
646
- signal: options?.signal,
647
- ...options?.options,
980
+ const params = {
981
+ ...this.clientConfig,
982
+ baseURL: endpoint,
983
+ timeout: this.timeout,
984
+ maxRetries: 0,
985
+ };
986
+
987
+ if (!this.azureADTokenProvider) {
988
+ params.apiKey = openAIEndpointConfig.azureOpenAIApiKey;
648
989
  }
649
- );
650
990
 
651
- const { completion_tokens, prompt_tokens, total_tokens } = data.usage ?? {};
991
+ if (params.baseURL == null) {
992
+ delete params.baseURL;
993
+ }
652
994
 
653
- const generations = [];
654
- for (const part of data.choices ?? []) {
655
- const text = part.message.content ?? '';
656
- const generation: ChatGeneration = {
657
- text: typeof text === 'string' ? text : '',
658
- message: this._convertResponseToMessage(part, data),
659
- };
660
- generation.generationInfo = {
661
- ...(part.finish_reason != null
662
- ? { finish_reason: part.finish_reason }
663
- : {}),
664
- ...(part.logprobs ? { logprobs: part.logprobs } : {}),
995
+ const defaultHeaders = normalizeHeaders(params.defaultHeaders);
996
+ params.defaultHeaders = {
997
+ ...params.defaultHeaders,
998
+ 'User-Agent':
999
+ defaultHeaders['User-Agent'] != null
1000
+ ? `${defaultHeaders['User-Agent']}: librechat-azure-openai-v2`
1001
+ : 'librechat-azure-openai-v2',
665
1002
  };
666
- generations.push(generation);
1003
+
1004
+ this.client = new CustomAzureOpenAIClient({
1005
+ apiVersion: this.azureOpenAIApiVersion,
1006
+ azureADTokenProvider: this.azureADTokenProvider,
1007
+ ...(params as t.AzureOpenAIInput),
1008
+ }) as unknown as CustomOpenAIClient;
667
1009
  }
668
1010
 
669
- return {
670
- generations,
671
- llmOutput: {
672
- tokenUsage: {
673
- completionTokens: completion_tokens,
674
- promptTokens: prompt_tokens,
675
- totalTokens: total_tokens,
676
- },
677
- },
678
- };
1011
+ const requestOptions = {
1012
+ ...this.clientConfig,
1013
+ ...options,
1014
+ } as OpenAICoreRequestOptions;
1015
+ if (this.azureOpenAIApiKey != null) {
1016
+ requestOptions.headers = {
1017
+ 'api-key': this.azureOpenAIApiKey,
1018
+ ...requestOptions.headers,
1019
+ };
1020
+ requestOptions.query = {
1021
+ 'api-version': this.azureOpenAIApiVersion,
1022
+ ...requestOptions.query,
1023
+ };
1024
+ }
1025
+ return requestOptions;
679
1026
  }
1027
+ }
680
1028
 
681
- protected _convertResponseToMessage(
682
- choice: OpenAIClient.Chat.Completions.ChatCompletion.Choice,
683
- data: OpenAIClient.Chat.Completions.ChatCompletion
684
- ): AIMessage {
685
- const { message } = choice;
686
- const rawToolCalls = message.tool_calls;
687
- const toolCalls = rawToolCalls?.map((tc) => ({
688
- id: tc.id,
689
- name: tc.function.name,
690
- args: JSON.parse(tc.function.arguments || '{}'),
691
- type: 'tool_call' as const,
692
- }));
693
-
694
- const additional_kwargs: Record<string, unknown> = {};
695
- if (rawToolCalls) {
696
- additional_kwargs.tool_calls = rawToolCalls;
697
- }
698
- if (
699
- 'reasoning_content' in message &&
700
- message.reasoning_content != null &&
701
- message.reasoning_content !== ''
702
- ) {
703
- additional_kwargs.reasoning_content = message.reasoning_content;
704
- }
1029
+ function withLibreChatOpenAIFields(
1030
+ fields?: LibreChatOpenAIFields
1031
+ ): LibreChatOpenAIFields {
1032
+ const nextFields = fields ?? {};
1033
+ return {
1034
+ ...nextFields,
1035
+ completions:
1036
+ nextFields.completions ?? new LibreChatOpenAICompletions(nextFields),
1037
+ responses: nextFields.responses ?? new LibreChatOpenAIResponses(nextFields),
1038
+ };
1039
+ }
705
1040
 
706
- return new AIMessage({
707
- content: message.content ?? '',
708
- tool_calls: toolCalls,
709
- additional_kwargs,
710
- usage_metadata: data.usage
711
- ? {
712
- input_tokens: data.usage.prompt_tokens,
713
- output_tokens: data.usage.completion_tokens,
714
- total_tokens: data.usage.total_tokens,
715
- }
716
- : undefined,
717
- response_metadata: {
718
- model_name: data.model,
719
- system_fingerprint: data.system_fingerprint,
720
- finish_reason: choice.finish_reason,
721
- },
722
- });
1041
+ export class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
1042
+ _lc_stream_delay?: number;
1043
+
1044
+ constructor(
1045
+ fields?: LibreChatOpenAIFields & t.OpenAIChatInput['modelKwargs']
1046
+ ) {
1047
+ super(withLibreChatOpenAIFields(fields));
1048
+ this._lc_stream_delay = fields?._lc_stream_delay;
723
1049
  }
724
1050
 
725
- protected _getClientOptions(
1051
+ public get exposedClient(): CustomOpenAIClient {
1052
+ return getExposedOpenAIClient(
1053
+ this.completions as OpenAIClientDelegate,
1054
+ this.responses as OpenAIClientDelegate,
1055
+ this._useResponsesApi(undefined)
1056
+ ) as CustomOpenAIClient;
1057
+ }
1058
+ static lc_name(): string {
1059
+ return 'LibreChatOpenAI';
1060
+ }
1061
+ _getClientOptions(
726
1062
  options?: OpenAICoreRequestOptions
727
1063
  ): OpenAICoreRequestOptions {
728
1064
  if (!(this.client as OpenAIClient | undefined)) {
@@ -750,130 +1086,199 @@ export class ChatDeepSeek extends OriginalChatDeepSeek {
750
1086
  return requestOptions;
751
1087
  }
752
1088
 
1089
+ /**
1090
+ * Returns backwards compatible reasoning parameters from constructor params and call options
1091
+ * @internal
1092
+ */
1093
+ getReasoningParams(
1094
+ options?: this['ParsedCallOptions']
1095
+ ): OpenAIClient.Reasoning | undefined {
1096
+ return getReasoningParams(this.reasoning, options);
1097
+ }
1098
+
1099
+ protected _getReasoningParams(
1100
+ options?: this['ParsedCallOptions']
1101
+ ): OpenAIClient.Reasoning | undefined {
1102
+ return this.getReasoningParams(options);
1103
+ }
1104
+
753
1105
  async *_streamResponseChunks(
754
1106
  messages: BaseMessage[],
755
1107
  options: this['ParsedCallOptions'],
756
1108
  runManager?: CallbackManagerForLLMRun
757
1109
  ): AsyncGenerator<ChatGenerationChunk> {
758
- const messagesMapped: OpenAICompletionParam[] =
759
- _convertMessagesToOpenAIParams(messages, this.model, {
760
- includeReasoningContent: true,
761
- });
1110
+ yield* delayStreamChunks(
1111
+ super._streamResponseChunks(messages, options, runManager),
1112
+ this._lc_stream_delay
1113
+ );
1114
+ }
1115
+ }
762
1116
 
763
- const params = {
764
- ...this.invocationParams(options, {
765
- streaming: true,
766
- }),
767
- messages: messagesMapped,
768
- stream: true as const,
769
- };
770
- let defaultRole: OpenAIRoleEnum | undefined;
1117
+ export class AzureChatOpenAI extends OriginalAzureChatOpenAI {
1118
+ _lc_stream_delay?: number;
771
1119
 
772
- const streamIterable = await this.completionWithRetry(params, options);
773
- let usage: OpenAIClient.Completions.CompletionUsage | undefined;
774
- for await (const data of streamIterable) {
775
- const choice = data.choices[0] as
776
- | Partial<OpenAIClient.Chat.Completions.ChatCompletionChunk.Choice>
777
- | undefined;
778
- if (data.usage) {
779
- usage = data.usage;
780
- }
781
- if (!choice) {
782
- continue;
783
- }
1120
+ constructor(fields?: LibreChatAzureOpenAIFields) {
1121
+ super(fields);
1122
+ this.completions = new LibreChatAzureOpenAICompletions(fields);
1123
+ this.responses = new LibreChatAzureOpenAIResponses(fields);
1124
+ this._lc_stream_delay = fields?._lc_stream_delay;
1125
+ }
784
1126
 
785
- const { delta } = choice;
786
- if (!delta) {
787
- continue;
788
- }
789
- const chunk = this._convertOpenAIDeltaToBaseMessageChunk(
790
- delta,
791
- data,
792
- defaultRole
793
- );
794
- if ('reasoning_content' in delta) {
795
- chunk.additional_kwargs.reasoning_content = delta.reasoning_content;
796
- }
797
- defaultRole = delta.role ?? defaultRole;
798
- const newTokenIndices = {
799
- prompt: (options as OpenAIChatCallOptions).promptIndex ?? 0,
800
- completion: choice.index ?? 0,
1127
+ public get exposedClient(): CustomOpenAIClient {
1128
+ return getExposedOpenAIClient(
1129
+ this.completions as OpenAIClientDelegate,
1130
+ this.responses as OpenAIClientDelegate,
1131
+ this._useResponsesApi(undefined)
1132
+ ) as CustomOpenAIClient;
1133
+ }
1134
+ static lc_name(): 'LibreChatAzureOpenAI' {
1135
+ return 'LibreChatAzureOpenAI';
1136
+ }
1137
+ /**
1138
+ * Returns backwards compatible reasoning parameters from constructor params and call options
1139
+ * @internal
1140
+ */
1141
+ getReasoningParams(
1142
+ options?: this['ParsedCallOptions']
1143
+ ): OpenAIClient.Reasoning | undefined {
1144
+ return getGatedReasoningParams(this.model, this.reasoning, options);
1145
+ }
1146
+
1147
+ protected _getReasoningParams(
1148
+ options?: this['ParsedCallOptions']
1149
+ ): OpenAIClient.Reasoning | undefined {
1150
+ return this.getReasoningParams(options);
1151
+ }
1152
+
1153
+ _getClientOptions(
1154
+ options: OpenAICoreRequestOptions | undefined
1155
+ ): OpenAICoreRequestOptions {
1156
+ if (!(this.client as unknown as AzureOpenAIClient | undefined)) {
1157
+ const openAIEndpointConfig: t.OpenAIEndpointConfig = {
1158
+ azureOpenAIApiDeploymentName: this.azureOpenAIApiDeploymentName,
1159
+ azureOpenAIApiInstanceName: this.azureOpenAIApiInstanceName,
1160
+ azureOpenAIApiKey: this.azureOpenAIApiKey,
1161
+ azureOpenAIBasePath: this.azureOpenAIBasePath,
1162
+ azureADTokenProvider: this.azureADTokenProvider,
1163
+ baseURL: this.clientConfig.baseURL,
801
1164
  };
802
- if (typeof chunk.content !== 'string') {
803
- // eslint-disable-next-line no-console
804
- console.log(
805
- '[WARNING]: Received non-string content from OpenAI. This is currently not supported.'
806
- );
807
- continue;
808
- }
809
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
810
- const generationInfo: Record<string, any> = { ...newTokenIndices };
811
- if (choice.finish_reason != null) {
812
- generationInfo.finish_reason = choice.finish_reason;
813
- generationInfo.system_fingerprint = data.system_fingerprint;
814
- generationInfo.model_name = data.model;
815
- generationInfo.service_tier = data.service_tier;
1165
+
1166
+ const endpoint = getEndpoint(openAIEndpointConfig);
1167
+
1168
+ const params = {
1169
+ ...this.clientConfig,
1170
+ baseURL: endpoint,
1171
+ timeout: this.timeout,
1172
+ maxRetries: 0,
1173
+ };
1174
+
1175
+ if (!this.azureADTokenProvider) {
1176
+ params.apiKey = openAIEndpointConfig.azureOpenAIApiKey;
816
1177
  }
817
- if (this.logprobs == true) {
818
- generationInfo.logprobs = choice.logprobs;
1178
+
1179
+ if (params.baseURL == null) {
1180
+ delete params.baseURL;
819
1181
  }
820
- const generationChunk = new ChatGenerationChunk({
821
- message: chunk,
822
- text: chunk.content,
823
- generationInfo,
824
- });
825
- yield generationChunk;
826
- await runManager?.handleLLMNewToken(
827
- generationChunk.text || '',
828
- newTokenIndices,
829
- undefined,
830
- undefined,
831
- undefined,
832
- { chunk: generationChunk }
833
- );
1182
+
1183
+ const defaultHeaders = normalizeHeaders(params.defaultHeaders);
1184
+ params.defaultHeaders = {
1185
+ ...params.defaultHeaders,
1186
+ 'User-Agent':
1187
+ defaultHeaders['User-Agent'] != null
1188
+ ? `${defaultHeaders['User-Agent']}: librechat-azure-openai-v2`
1189
+ : 'librechat-azure-openai-v2',
1190
+ };
1191
+
1192
+ this.client = new CustomAzureOpenAIClient({
1193
+ apiVersion: this.azureOpenAIApiVersion,
1194
+ azureADTokenProvider: this.azureADTokenProvider,
1195
+ ...(params as t.AzureOpenAIInput),
1196
+ }) as unknown as CustomOpenAIClient;
834
1197
  }
835
- if (usage) {
836
- const inputTokenDetails = {
837
- ...(usage.prompt_tokens_details?.audio_tokens != null && {
838
- audio: usage.prompt_tokens_details.audio_tokens,
839
- }),
840
- ...(usage.prompt_tokens_details?.cached_tokens != null && {
841
- cache_read: usage.prompt_tokens_details.cached_tokens,
842
- }),
1198
+
1199
+ const requestOptions = {
1200
+ ...this.clientConfig,
1201
+ ...options,
1202
+ } as OpenAICoreRequestOptions;
1203
+ if (this.azureOpenAIApiKey != null) {
1204
+ requestOptions.headers = {
1205
+ 'api-key': this.azureOpenAIApiKey,
1206
+ ...requestOptions.headers,
843
1207
  };
844
- const outputTokenDetails = {
845
- ...(usage.completion_tokens_details?.audio_tokens != null && {
846
- audio: usage.completion_tokens_details.audio_tokens,
847
- }),
848
- ...(usage.completion_tokens_details?.reasoning_tokens != null && {
849
- reasoning: usage.completion_tokens_details.reasoning_tokens,
850
- }),
1208
+ requestOptions.query = {
1209
+ 'api-version': this.azureOpenAIApiVersion,
1210
+ ...requestOptions.query,
851
1211
  };
852
- const generationChunk = new ChatGenerationChunk({
853
- message: new AIMessageChunk({
854
- content: '',
855
- response_metadata: {
856
- usage: { ...usage },
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
1212
  }
874
- if (options.signal?.aborted === true) {
875
- throw new Error('AbortError');
1213
+ return requestOptions;
1214
+ }
1215
+ async *_streamResponseChunks(
1216
+ messages: BaseMessage[],
1217
+ options: this['ParsedCallOptions'],
1218
+ runManager?: CallbackManagerForLLMRun
1219
+ ): AsyncGenerator<ChatGenerationChunk> {
1220
+ yield* delayStreamChunks(
1221
+ super._streamResponseChunks(messages, options, runManager),
1222
+ this._lc_stream_delay
1223
+ );
1224
+ }
1225
+ }
1226
+ export class ChatDeepSeek extends OriginalChatDeepSeek {
1227
+ _lc_stream_delay?: number;
1228
+
1229
+ constructor(
1230
+ fields?: ConstructorParameters<typeof OriginalChatDeepSeek>[0] & {
1231
+ _lc_stream_delay?: number;
876
1232
  }
1233
+ ) {
1234
+ super(fields);
1235
+ this._lc_stream_delay = fields?._lc_stream_delay;
1236
+ }
1237
+
1238
+ public get exposedClient(): CustomOpenAIClient {
1239
+ return this.client;
1240
+ }
1241
+ static lc_name(): 'LibreChatDeepSeek' {
1242
+ return 'LibreChatDeepSeek';
1243
+ }
1244
+
1245
+ _getClientOptions(
1246
+ options?: OpenAICoreRequestOptions
1247
+ ): OpenAICoreRequestOptions {
1248
+ if (!(this.client as OpenAIClient | undefined)) {
1249
+ const openAIEndpointConfig: t.OpenAIEndpointConfig = {
1250
+ baseURL: this.clientConfig.baseURL,
1251
+ };
1252
+
1253
+ const endpoint = getEndpoint(openAIEndpointConfig);
1254
+ const params = {
1255
+ ...this.clientConfig,
1256
+ baseURL: endpoint,
1257
+ timeout: this.timeout,
1258
+ maxRetries: 0,
1259
+ };
1260
+ if (params.baseURL == null) {
1261
+ delete params.baseURL;
1262
+ }
1263
+
1264
+ this.client = new CustomOpenAIClient(params);
1265
+ }
1266
+ const requestOptions = {
1267
+ ...this.clientConfig,
1268
+ ...options,
1269
+ } as OpenAICoreRequestOptions;
1270
+ return requestOptions;
1271
+ }
1272
+
1273
+ async *_streamResponseChunks(
1274
+ messages: BaseMessage[],
1275
+ options: this['ParsedCallOptions'],
1276
+ runManager?: CallbackManagerForLLMRun
1277
+ ): AsyncGenerator<ChatGenerationChunk> {
1278
+ yield* delayStreamChunks(
1279
+ super._streamResponseChunks(messages, options, runManager),
1280
+ this._lc_stream_delay
1281
+ );
877
1282
  }
878
1283
  }
879
1284
 
@@ -896,242 +1301,17 @@ export interface XAIUsageMetadata
896
1301
  }
897
1302
 
898
1303
  export class ChatMoonshot extends ChatOpenAI {
899
- static lc_name(): 'LibreChatMoonshot' {
900
- return 'LibreChatMoonshot';
901
- }
902
-
903
- protected _convertMessages(messages: BaseMessage[]): OpenAICompletionParam[] {
904
- return _convertMessagesToOpenAIParams(messages, this.model, {
1304
+ constructor(
1305
+ fields?: LibreChatOpenAIFields & t.OpenAIChatInput['modelKwargs']
1306
+ ) {
1307
+ super({
1308
+ ...fields,
905
1309
  includeReasoningContent: true,
906
1310
  });
907
1311
  }
908
1312
 
909
- async _generate(
910
- messages: BaseMessage[],
911
- options: this['ParsedCallOptions'],
912
- runManager?: CallbackManagerForLLMRun
913
- ): Promise<ChatResult> {
914
- const params = this.invocationParams(options);
915
-
916
- if (params.stream === true) {
917
- return super._generate(messages, options, runManager);
918
- }
919
-
920
- const messagesMapped = this._convertMessages(messages);
921
- const data = await this.completionWithRetry(
922
- {
923
- ...params,
924
- stream: false,
925
- messages: messagesMapped,
926
- },
927
- {
928
- signal: options.signal,
929
- ...options.options,
930
- }
931
- );
932
-
933
- const { completion_tokens, prompt_tokens, total_tokens } = data.usage ?? {};
934
-
935
- const generations = [];
936
- for (const part of data.choices ?? []) {
937
- const text = part.message.content ?? '';
938
- const generation: ChatGeneration = {
939
- text: typeof text === 'string' ? text : '',
940
- message: this._convertResponseToMessage(part, data),
941
- };
942
- generation.generationInfo = {
943
- ...(part.finish_reason ? { finish_reason: part.finish_reason } : {}),
944
- ...(part.logprobs ? { logprobs: part.logprobs } : {}),
945
- };
946
- generations.push(generation);
947
- }
948
-
949
- return {
950
- generations,
951
- llmOutput: {
952
- tokenUsage: {
953
- completionTokens: completion_tokens,
954
- promptTokens: prompt_tokens,
955
- totalTokens: total_tokens,
956
- },
957
- },
958
- };
959
- }
960
-
961
- protected _convertResponseToMessage(
962
- choice: OpenAIClient.Chat.Completions.ChatCompletion.Choice,
963
- data: OpenAIClient.Chat.Completions.ChatCompletion
964
- ): AIMessage {
965
- const { message } = choice;
966
- const rawToolCalls = message.tool_calls;
967
- const toolCalls = rawToolCalls?.map((tc) => ({
968
- id: tc.id,
969
- name: tc.function.name,
970
- args: JSON.parse(tc.function.arguments || '{}'),
971
- type: 'tool_call' as const,
972
- }));
973
-
974
- const additional_kwargs: Record<string, unknown> = {};
975
- if (rawToolCalls) {
976
- additional_kwargs.tool_calls = rawToolCalls;
977
- }
978
- if (
979
- 'reasoning_content' in message &&
980
- message.reasoning_content != null &&
981
- message.reasoning_content !== ''
982
- ) {
983
- additional_kwargs.reasoning_content = message.reasoning_content;
984
- }
985
-
986
- return new AIMessage({
987
- content: message.content ?? '',
988
- tool_calls: toolCalls,
989
- additional_kwargs,
990
- usage_metadata: data.usage
991
- ? {
992
- input_tokens: data.usage.prompt_tokens,
993
- output_tokens: data.usage.completion_tokens,
994
- total_tokens: data.usage.total_tokens,
995
- }
996
- : undefined,
997
- response_metadata: {
998
- model_name: data.model,
999
- system_fingerprint: data.system_fingerprint,
1000
- finish_reason: choice.finish_reason,
1001
- },
1002
- });
1003
- }
1004
-
1005
- async *_streamResponseChunks(
1006
- messages: BaseMessage[],
1007
- options: this['ParsedCallOptions'],
1008
- runManager?: CallbackManagerForLLMRun
1009
- ): AsyncGenerator<ChatGenerationChunk> {
1010
- const messagesMapped: OpenAICompletionParam[] =
1011
- _convertMessagesToOpenAIParams(messages, this.model, {
1012
- includeReasoningContent: true,
1013
- });
1014
-
1015
- const params = {
1016
- ...this.invocationParams(options, {
1017
- streaming: true,
1018
- }),
1019
- messages: messagesMapped,
1020
- stream: true as const,
1021
- };
1022
- let defaultRole: OpenAIRoleEnum | undefined;
1023
-
1024
- const streamIterable = await this.completionWithRetry(params, options);
1025
- let usage: OpenAIClient.Completions.CompletionUsage | undefined;
1026
- for await (const data of streamIterable) {
1027
- const choice = data.choices[0] as
1028
- | Partial<OpenAIClient.Chat.Completions.ChatCompletionChunk.Choice>
1029
- | undefined;
1030
- if (data.usage) {
1031
- usage = data.usage;
1032
- }
1033
- if (!choice) {
1034
- continue;
1035
- }
1036
-
1037
- const { delta } = choice;
1038
- if (!delta) {
1039
- continue;
1040
- }
1041
- const chunk = this._convertOpenAIDeltaToBaseMessageChunk(
1042
- delta,
1043
- data,
1044
- defaultRole
1045
- );
1046
- if ('reasoning_content' in delta) {
1047
- chunk.additional_kwargs.reasoning_content = delta.reasoning_content;
1048
- }
1049
- defaultRole = delta.role ?? defaultRole;
1050
- const newTokenIndices = {
1051
- prompt: (options as OpenAIChatCallOptions).promptIndex ?? 0,
1052
- completion: choice.index ?? 0,
1053
- };
1054
- if (typeof chunk.content !== 'string') {
1055
- // eslint-disable-next-line no-console
1056
- console.log(
1057
- '[WARNING]: Received non-string content from OpenAI. This is currently not supported.'
1058
- );
1059
- continue;
1060
- }
1061
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1062
- const generationInfo: Record<string, any> = { ...newTokenIndices };
1063
- if (choice.finish_reason != null) {
1064
- generationInfo.finish_reason = choice.finish_reason;
1065
- generationInfo.system_fingerprint = data.system_fingerprint;
1066
- generationInfo.model_name = data.model;
1067
- generationInfo.service_tier = data.service_tier;
1068
- }
1069
- if (this.logprobs == true) {
1070
- generationInfo.logprobs = choice.logprobs;
1071
- }
1072
- const generationChunk = new ChatGenerationChunk({
1073
- message: chunk,
1074
- text: chunk.content,
1075
- generationInfo,
1076
- });
1077
- yield generationChunk;
1078
- if (this._lc_stream_delay != null) {
1079
- await sleep(this._lc_stream_delay);
1080
- }
1081
- await runManager?.handleLLMNewToken(
1082
- generationChunk.text || '',
1083
- newTokenIndices,
1084
- undefined,
1085
- undefined,
1086
- undefined,
1087
- { chunk: generationChunk }
1088
- );
1089
- }
1090
- if (usage) {
1091
- const inputTokenDetails = {
1092
- ...(usage.prompt_tokens_details?.audio_tokens != null && {
1093
- audio: usage.prompt_tokens_details.audio_tokens,
1094
- }),
1095
- ...(usage.prompt_tokens_details?.cached_tokens != null && {
1096
- cache_read: usage.prompt_tokens_details.cached_tokens,
1097
- }),
1098
- };
1099
- const outputTokenDetails = {
1100
- ...(usage.completion_tokens_details?.audio_tokens != null && {
1101
- audio: usage.completion_tokens_details.audio_tokens,
1102
- }),
1103
- ...(usage.completion_tokens_details?.reasoning_tokens != null && {
1104
- reasoning: usage.completion_tokens_details.reasoning_tokens,
1105
- }),
1106
- };
1107
- const generationChunk = new ChatGenerationChunk({
1108
- message: new AIMessageChunk({
1109
- content: '',
1110
- response_metadata: {
1111
- usage: { ...usage },
1112
- },
1113
- usage_metadata: {
1114
- input_tokens: usage.prompt_tokens,
1115
- output_tokens: usage.completion_tokens,
1116
- total_tokens: usage.total_tokens,
1117
- ...(Object.keys(inputTokenDetails).length > 0 && {
1118
- input_token_details: inputTokenDetails,
1119
- }),
1120
- ...(Object.keys(outputTokenDetails).length > 0 && {
1121
- output_token_details: outputTokenDetails,
1122
- }),
1123
- },
1124
- }),
1125
- text: '',
1126
- });
1127
- yield generationChunk;
1128
- if (this._lc_stream_delay != null) {
1129
- await sleep(this._lc_stream_delay);
1130
- }
1131
- }
1132
- if (options.signal?.aborted === true) {
1133
- throw new Error('AbortError');
1134
- }
1313
+ static lc_name(): 'LibreChatMoonshot' {
1314
+ return 'LibreChatMoonshot';
1135
1315
  }
1136
1316
  }
1137
1317
 
@@ -1168,7 +1348,7 @@ export class ChatXAI extends OriginalChatXAI {
1168
1348
  return this.client;
1169
1349
  }
1170
1350
 
1171
- protected _getClientOptions(
1351
+ _getClientOptions(
1172
1352
  options?: OpenAICoreRequestOptions
1173
1353
  ): OpenAICoreRequestOptions {
1174
1354
  if (!(this.client as OpenAIClient | undefined)) {
@@ -1201,166 +1381,9 @@ export class ChatXAI extends OriginalChatXAI {
1201
1381
  options: this['ParsedCallOptions'],
1202
1382
  runManager?: CallbackManagerForLLMRun
1203
1383
  ): AsyncGenerator<ChatGenerationChunk> {
1204
- const messagesMapped: OpenAICompletionParam[] =
1205
- _convertMessagesToOpenAIParams(messages, this.model);
1206
-
1207
- const params = {
1208
- ...this.invocationParams(options, {
1209
- streaming: true,
1210
- }),
1211
- messages: messagesMapped,
1212
- stream: true as const,
1213
- };
1214
- let defaultRole: OpenAIRoleEnum | undefined;
1215
-
1216
- const streamIterable = await this.completionWithRetry(params, options);
1217
- let usage: OpenAIClient.Completions.CompletionUsage | undefined;
1218
- for await (const data of streamIterable) {
1219
- const choice = data.choices[0] as
1220
- | Partial<OpenAIClient.Chat.Completions.ChatCompletionChunk.Choice>
1221
- | undefined;
1222
- if (data.usage) {
1223
- usage = data.usage;
1224
- }
1225
- if (!choice) {
1226
- continue;
1227
- }
1228
-
1229
- const { delta } = choice;
1230
- if (!delta) {
1231
- continue;
1232
- }
1233
- const chunk = this._convertOpenAIDeltaToBaseMessageChunk(
1234
- delta,
1235
- data,
1236
- defaultRole
1237
- );
1238
- if (chunk.usage_metadata != null) {
1239
- chunk.usage_metadata = {
1240
- input_tokens:
1241
- (chunk.usage_metadata as Partial<UsageMetadata>).input_tokens ?? 0,
1242
- output_tokens:
1243
- (chunk.usage_metadata as Partial<UsageMetadata>).output_tokens ?? 0,
1244
- total_tokens:
1245
- (chunk.usage_metadata as Partial<UsageMetadata>).total_tokens ?? 0,
1246
- };
1247
- }
1248
- if ('reasoning_content' in delta) {
1249
- chunk.additional_kwargs.reasoning_content = delta.reasoning_content;
1250
- }
1251
- defaultRole = delta.role ?? defaultRole;
1252
- const newTokenIndices = {
1253
- prompt: (options as OpenAIChatCallOptions).promptIndex ?? 0,
1254
- completion: choice.index ?? 0,
1255
- };
1256
- if (typeof chunk.content !== 'string') {
1257
- // eslint-disable-next-line no-console
1258
- console.log(
1259
- '[WARNING]: Received non-string content from OpenAI. This is currently not supported.'
1260
- );
1261
- continue;
1262
- }
1263
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1264
- const generationInfo: Record<string, any> = { ...newTokenIndices };
1265
- if (choice.finish_reason != null) {
1266
- generationInfo.finish_reason = choice.finish_reason;
1267
- // Only include system fingerprint in the last chunk for now
1268
- // to avoid concatenation issues
1269
- generationInfo.system_fingerprint = data.system_fingerprint;
1270
- generationInfo.model_name = data.model;
1271
- generationInfo.service_tier = data.service_tier;
1272
- }
1273
- if (this.logprobs == true) {
1274
- generationInfo.logprobs = choice.logprobs;
1275
- }
1276
- const generationChunk = new ChatGenerationChunk({
1277
- message: chunk,
1278
- text: chunk.content,
1279
- generationInfo,
1280
- });
1281
- yield generationChunk;
1282
- if (this._lc_stream_delay != null) {
1283
- await sleep(this._lc_stream_delay);
1284
- }
1285
- await runManager?.handleLLMNewToken(
1286
- generationChunk.text || '',
1287
- newTokenIndices,
1288
- undefined,
1289
- undefined,
1290
- undefined,
1291
- { chunk: generationChunk }
1292
- );
1293
- }
1294
- if (usage) {
1295
- // Type assertion for xAI-specific usage structure
1296
- const xaiUsage = usage as XAIUsageMetadata;
1297
- const inputTokenDetails = {
1298
- // Standard OpenAI fields
1299
- ...(usage.prompt_tokens_details?.audio_tokens != null && {
1300
- audio: usage.prompt_tokens_details.audio_tokens,
1301
- }),
1302
- ...(usage.prompt_tokens_details?.cached_tokens != null && {
1303
- cache_read: usage.prompt_tokens_details.cached_tokens,
1304
- }),
1305
- // Add xAI-specific prompt token details if they exist
1306
- ...(xaiUsage.prompt_tokens_details?.text_tokens != null && {
1307
- text: xaiUsage.prompt_tokens_details.text_tokens,
1308
- }),
1309
- ...(xaiUsage.prompt_tokens_details?.image_tokens != null && {
1310
- image: xaiUsage.prompt_tokens_details.image_tokens,
1311
- }),
1312
- };
1313
- const outputTokenDetails = {
1314
- // Standard OpenAI fields
1315
- ...(usage.completion_tokens_details?.audio_tokens != null && {
1316
- audio: usage.completion_tokens_details.audio_tokens,
1317
- }),
1318
- ...(usage.completion_tokens_details?.reasoning_tokens != null && {
1319
- reasoning: usage.completion_tokens_details.reasoning_tokens,
1320
- }),
1321
- // Add xAI-specific completion token details if they exist
1322
- ...(xaiUsage.completion_tokens_details?.accepted_prediction_tokens !=
1323
- null && {
1324
- accepted_prediction:
1325
- xaiUsage.completion_tokens_details.accepted_prediction_tokens,
1326
- }),
1327
- ...(xaiUsage.completion_tokens_details?.rejected_prediction_tokens !=
1328
- null && {
1329
- rejected_prediction:
1330
- xaiUsage.completion_tokens_details.rejected_prediction_tokens,
1331
- }),
1332
- };
1333
- const generationChunk = new ChatGenerationChunk({
1334
- message: new AIMessageChunk({
1335
- content: '',
1336
- response_metadata: {
1337
- usage: { ...usage },
1338
- // Include xAI-specific metadata if it exists
1339
- ...(xaiUsage.num_sources_used != null && {
1340
- num_sources_used: xaiUsage.num_sources_used,
1341
- }),
1342
- },
1343
- usage_metadata: {
1344
- input_tokens: usage.prompt_tokens,
1345
- output_tokens: usage.completion_tokens,
1346
- total_tokens: usage.total_tokens,
1347
- ...(Object.keys(inputTokenDetails).length > 0 && {
1348
- input_token_details: inputTokenDetails,
1349
- }),
1350
- ...(Object.keys(outputTokenDetails).length > 0 && {
1351
- output_token_details: outputTokenDetails,
1352
- }),
1353
- },
1354
- }),
1355
- text: '',
1356
- });
1357
- yield generationChunk;
1358
- if (this._lc_stream_delay != null) {
1359
- await sleep(this._lc_stream_delay);
1360
- }
1361
- }
1362
- if (options.signal?.aborted === true) {
1363
- throw new Error('AbortError');
1364
- }
1384
+ yield* delayStreamChunks(
1385
+ super._streamResponseChunks(messages, options, runManager),
1386
+ this._lc_stream_delay
1387
+ );
1365
1388
  }
1366
1389
  }