@librechat/agents 3.6.15 → 3.7.0

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 (180) hide show
  1. package/README.md +41 -0
  2. package/dist/cjs/_virtual/_rolldown/runtime.cjs +10 -0
  3. package/dist/cjs/agents/AgentContext.cjs +1 -1
  4. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  5. package/dist/cjs/graphs/Graph.cjs +13 -13
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/lazyRequire.cjs +33 -0
  8. package/dist/cjs/lazyRequire.cjs.map +1 -0
  9. package/dist/cjs/llm/anthropic/index.cjs +13 -0
  10. package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
  11. package/dist/cjs/llm/bedrock/index.cjs +10 -1
  12. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  13. package/dist/cjs/llm/bedrock/toolCache.cjs +3 -2
  14. package/dist/cjs/llm/bedrock/toolCache.cjs.map +1 -1
  15. package/dist/cjs/llm/google/index.cjs +10 -1
  16. package/dist/cjs/llm/google/index.cjs.map +1 -1
  17. package/dist/cjs/llm/init.cjs +7 -4
  18. package/dist/cjs/llm/init.cjs.map +1 -1
  19. package/dist/cjs/llm/invoke.cjs +9 -2
  20. package/dist/cjs/llm/invoke.cjs.map +1 -1
  21. package/dist/cjs/llm/mistral/index.cjs +9 -0
  22. package/dist/cjs/llm/mistral/index.cjs.map +1 -1
  23. package/dist/cjs/llm/openai/index.cjs +41 -1
  24. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  25. package/dist/cjs/llm/openrouter/index.cjs +12 -3
  26. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  27. package/dist/cjs/llm/openrouter/toolCache.cjs +3 -2
  28. package/dist/cjs/llm/openrouter/toolCache.cjs.map +1 -1
  29. package/dist/cjs/llm/prepareProviderRequest.cjs +6 -3
  30. package/dist/cjs/llm/prepareProviderRequest.cjs.map +1 -1
  31. package/dist/cjs/llm/providerRegistry.cjs +81 -0
  32. package/dist/cjs/llm/providerRegistry.cjs.map +1 -0
  33. package/dist/cjs/llm/providers.cjs +37 -28
  34. package/dist/cjs/llm/providers.cjs.map +1 -1
  35. package/dist/cjs/llm/providers.eager.cjs +21 -0
  36. package/dist/cjs/llm/providers.eager.cjs.map +1 -0
  37. package/dist/cjs/llm/request.cjs +6 -4
  38. package/dist/cjs/llm/request.cjs.map +1 -1
  39. package/dist/cjs/llm/truncation.cjs +2 -1
  40. package/dist/cjs/llm/truncation.cjs.map +1 -1
  41. package/dist/cjs/llm/vertexai/index.cjs +17 -0
  42. package/dist/cjs/llm/vertexai/index.cjs.map +1 -1
  43. package/dist/cjs/main.cjs +19 -18
  44. package/dist/cjs/messages/core.cjs +9 -4
  45. package/dist/cjs/messages/core.cjs.map +1 -1
  46. package/dist/cjs/messages/format.cjs.map +1 -1
  47. package/dist/cjs/messages/prune.cjs +50 -12
  48. package/dist/cjs/messages/prune.cjs.map +1 -1
  49. package/dist/cjs/provider-registration.cjs +4 -0
  50. package/dist/cjs/run.cjs +9 -4
  51. package/dist/cjs/run.cjs.map +1 -1
  52. package/dist/cjs/stream.cjs +1 -1
  53. package/dist/cjs/stream.cjs.map +1 -1
  54. package/dist/cjs/summarization/node.cjs +2 -2
  55. package/dist/cjs/summarization/node.cjs.map +1 -1
  56. package/dist/cjs/tools/Calculator.cjs +7 -4
  57. package/dist/cjs/tools/Calculator.cjs.map +1 -1
  58. package/dist/cjs/tools/CodeExecutor.cjs +1 -1
  59. package/dist/cjs/utils/llm.cjs +22 -4
  60. package/dist/cjs/utils/llm.cjs.map +1 -1
  61. package/dist/esm/_virtual/_rolldown/runtime.mjs +14 -0
  62. package/dist/esm/agents/AgentContext.mjs +1 -1
  63. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  64. package/dist/esm/graphs/Graph.mjs +13 -13
  65. package/dist/esm/graphs/Graph.mjs.map +1 -1
  66. package/dist/esm/lazyRequire.mjs +30 -0
  67. package/dist/esm/lazyRequire.mjs.map +1 -0
  68. package/dist/esm/llm/anthropic/index.mjs +6 -1
  69. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  70. package/dist/esm/llm/bedrock/index.mjs +4 -2
  71. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  72. package/dist/esm/llm/bedrock/toolCache.mjs +3 -2
  73. package/dist/esm/llm/bedrock/toolCache.mjs.map +1 -1
  74. package/dist/esm/llm/google/index.mjs +4 -2
  75. package/dist/esm/llm/google/index.mjs.map +1 -1
  76. package/dist/esm/llm/init.mjs +8 -5
  77. package/dist/esm/llm/init.mjs.map +1 -1
  78. package/dist/esm/llm/invoke.mjs +9 -2
  79. package/dist/esm/llm/invoke.mjs.map +1 -1
  80. package/dist/esm/llm/mistral/index.mjs +3 -1
  81. package/dist/esm/llm/mistral/index.mjs.map +1 -1
  82. package/dist/esm/llm/openai/index.mjs +31 -9
  83. package/dist/esm/llm/openai/index.mjs.map +1 -1
  84. package/dist/esm/llm/openrouter/index.mjs +3 -1
  85. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  86. package/dist/esm/llm/openrouter/toolCache.mjs +3 -2
  87. package/dist/esm/llm/openrouter/toolCache.mjs.map +1 -1
  88. package/dist/esm/llm/prepareProviderRequest.mjs +7 -4
  89. package/dist/esm/llm/prepareProviderRequest.mjs.map +1 -1
  90. package/dist/esm/llm/providerRegistry.mjs +76 -0
  91. package/dist/esm/llm/providerRegistry.mjs.map +1 -0
  92. package/dist/esm/llm/providers.eager.mjs +22 -0
  93. package/dist/esm/llm/providers.eager.mjs.map +1 -0
  94. package/dist/esm/llm/providers.mjs +38 -28
  95. package/dist/esm/llm/providers.mjs.map +1 -1
  96. package/dist/esm/llm/request.mjs +6 -4
  97. package/dist/esm/llm/request.mjs.map +1 -1
  98. package/dist/esm/llm/truncation.mjs +2 -1
  99. package/dist/esm/llm/truncation.mjs.map +1 -1
  100. package/dist/esm/llm/vertexai/index.mjs +8 -1
  101. package/dist/esm/llm/vertexai/index.mjs.map +1 -1
  102. package/dist/esm/main.mjs +12 -13
  103. package/dist/esm/messages/core.mjs +9 -5
  104. package/dist/esm/messages/core.mjs.map +1 -1
  105. package/dist/esm/messages/format.mjs.map +1 -1
  106. package/dist/esm/messages/prune.mjs +50 -13
  107. package/dist/esm/messages/prune.mjs.map +1 -1
  108. package/dist/esm/provider-registration.mjs +3 -0
  109. package/dist/esm/run.mjs +10 -5
  110. package/dist/esm/run.mjs.map +1 -1
  111. package/dist/esm/stream.mjs +1 -1
  112. package/dist/esm/stream.mjs.map +1 -1
  113. package/dist/esm/summarization/node.mjs +2 -2
  114. package/dist/esm/summarization/node.mjs.map +1 -1
  115. package/dist/esm/tools/Calculator.mjs +7 -2
  116. package/dist/esm/tools/Calculator.mjs.map +1 -1
  117. package/dist/esm/tools/CodeExecutor.mjs +1 -1
  118. package/dist/esm/utils/llm.mjs +21 -5
  119. package/dist/esm/utils/llm.mjs.map +1 -1
  120. package/dist/types/agents/AgentContext.d.ts +5 -5
  121. package/dist/types/index.d.ts +5 -3
  122. package/dist/types/lazyRequire.d.ts +8 -0
  123. package/dist/types/llm/contextOverflowRecovery.d.ts +2 -2
  124. package/dist/types/llm/init.d.ts +21 -14
  125. package/dist/types/llm/invoke.d.ts +5 -6
  126. package/dist/types/llm/prepareProviderRequest.d.ts +5 -6
  127. package/dist/types/llm/providerRegistry.d.ts +22 -0
  128. package/dist/types/llm/providers.d.ts +4 -5
  129. package/dist/types/llm/providers.eager.d.ts +1 -0
  130. package/dist/types/llm/request.d.ts +2 -3
  131. package/dist/types/llm/truncation.d.ts +2 -2
  132. package/dist/types/messages/core.d.ts +3 -2
  133. package/dist/types/messages/format.d.ts +3 -3
  134. package/dist/types/messages/prune.d.ts +8 -6
  135. package/dist/types/provider-registration.d.ts +15 -0
  136. package/dist/types/stream.d.ts +1 -2
  137. package/dist/types/summarization/node.d.ts +1 -2
  138. package/dist/types/types/activityLabel.d.ts +3 -4
  139. package/dist/types/types/graph.d.ts +4 -5
  140. package/dist/types/types/llm.d.ts +54 -13
  141. package/dist/types/types/reasoningLabel.d.ts +2 -3
  142. package/dist/types/types/run.d.ts +3 -3
  143. package/dist/types/types/summarize.d.ts +2 -2
  144. package/dist/types/utils/errors.d.ts +3 -3
  145. package/dist/types/utils/llm.d.ts +14 -4
  146. package/dist/types/utils/llmConfig.d.ts +2 -2
  147. package/package.json +66 -1
  148. package/src/agents/AgentContext.ts +27 -26
  149. package/src/graphs/Graph.ts +16 -22
  150. package/src/index.ts +8 -3
  151. package/src/lazyRequire.ts +67 -0
  152. package/src/llm/bedrock/toolCache.ts +6 -2
  153. package/src/llm/contextOverflowRecovery.ts +2 -2
  154. package/src/llm/init.ts +55 -25
  155. package/src/llm/invoke.ts +36 -21
  156. package/src/llm/openrouter/toolCache.ts +8 -2
  157. package/src/llm/prepareProviderRequest.ts +19 -18
  158. package/src/llm/providerRegistry.ts +174 -0
  159. package/src/llm/providers.eager.ts +24 -0
  160. package/src/llm/providers.ts +99 -42
  161. package/src/llm/request.ts +10 -6
  162. package/src/llm/truncation.ts +9 -3
  163. package/src/messages/core.ts +18 -13
  164. package/src/messages/format.ts +14 -16
  165. package/src/messages/prune.ts +108 -28
  166. package/src/provider-registration.ts +29 -0
  167. package/src/run.ts +15 -7
  168. package/src/session/AgentSession.ts +1 -1
  169. package/src/stream.ts +7 -8
  170. package/src/summarization/node.ts +7 -7
  171. package/src/tools/Calculator.ts +9 -2
  172. package/src/types/activityLabel.ts +3 -4
  173. package/src/types/graph.ts +5 -5
  174. package/src/types/llm.ts +95 -13
  175. package/src/types/reasoningLabel.ts +2 -3
  176. package/src/types/run.ts +3 -3
  177. package/src/types/summarize.ts +2 -2
  178. package/src/utils/errors.ts +3 -3
  179. package/src/utils/llm.ts +61 -15
  180. package/src/utils/llmConfig.ts +4 -4
package/src/llm/invoke.ts CHANGED
@@ -13,14 +13,9 @@ import type { ToolCall } from '@langchain/core/messages/tool';
13
13
  import type { BaseMessage } from '@langchain/core/messages';
14
14
  import type { ToolOutputReferenceRegistry } from '@/tools/toolOutputReferences';
15
15
  import type { PreparedProviderRequest } from '@/llm/prepareProviderRequest';
16
- import type { StreamLimitState } from '@/llm/streamLimits';
17
16
  import type { ContextOverflowContext } from '@/utils/errors';
17
+ import type { StreamLimitState } from '@/llm/streamLimits';
18
18
  import type * as t from '@/types';
19
- import {
20
- modifyDeltaProperties,
21
- } from '@/messages';
22
- import { ChatModelStreamHandler, dispatchesChatModelStream } from '@/stream';
23
- import { Constants, ContentTypes, GraphEvents, Providers } from '@/common';
24
19
  import {
25
20
  enforceStreamLimitsForWireChunk,
26
21
  registerActiveStreamLimitGeneration,
@@ -35,11 +30,17 @@ import {
35
30
  assertPreparedProviderRequestFor,
36
31
  prepareProviderRequest,
37
32
  } from '@/llm/prepareProviderRequest';
33
+ import {
34
+ getProviderFamily,
35
+ providerUsesManualToolStream,
36
+ } from '@/llm/providers';
37
+ import { ChatModelStreamHandler, dispatchesChatModelStream } from '@/stream';
38
+ import { Constants, ContentTypes, GraphEvents, Providers } from '@/common';
38
39
  import { assertNotTruncatedToolCall } from '@/llm/truncation';
39
- import { manualToolStreamProviders } from '@/llm/providers';
40
40
  import { safeDispatchCustomEvent } from '@/utils/events';
41
41
  import { getContextOverflowInfo } from '@/utils/errors';
42
42
  import { appendCallbacks } from '@/utils/callbacks';
43
+ import { modifyDeltaProperties } from '@/messages';
43
44
  import { canSealPreempt } from '@/llm/preempt';
44
45
  import { initializeModel } from '@/llm/init';
45
46
 
@@ -105,6 +106,19 @@ export type OnChunk = (
105
106
  /** Unique per-model-attempt sequence; see the stamp in `attemptInvoke`. */
106
107
  let streamLimitAttemptSeq = 0;
107
108
 
109
+ function getManualToolStreamNormalizationProvider(
110
+ provider: t.ProviderName
111
+ ): t.ProviderName {
112
+ const family = getProviderFamily(provider);
113
+ if (family === 'anthropic') {
114
+ return Providers.ANTHROPIC;
115
+ }
116
+ if (family === 'bedrock') {
117
+ return Providers.BEDROCK;
118
+ }
119
+ return provider;
120
+ }
121
+
108
122
  /**
109
123
  * The registered handler that owns content-part dispatch, if any.
110
124
  *
@@ -136,7 +150,7 @@ function removeOpenRouterFinalReasoningReplayContent({
136
150
  }: {
137
151
  current?: AIMessageChunk;
138
152
  next: AIMessageChunk;
139
- provider: Providers;
153
+ provider: t.ProviderName;
140
154
  }): AIMessageChunk {
141
155
  const content = getOpenRouterFinalReasoningContent({
142
156
  current,
@@ -161,7 +175,7 @@ function getOpenRouterFinalReasoningContent({
161
175
  }: {
162
176
  current?: AIMessageChunk;
163
177
  next: AIMessageChunk;
164
- provider: Providers;
178
+ provider: t.ProviderName;
165
179
  }): string | undefined {
166
180
  if (
167
181
  provider !== Providers.OPENROUTER ||
@@ -197,7 +211,7 @@ function getStreamHandlingChunk({
197
211
  }: {
198
212
  current?: AIMessageChunk;
199
213
  next: AIMessageChunk;
200
- provider: Providers;
214
+ provider: t.ProviderName;
201
215
  }): AIMessageChunk | undefined {
202
216
  const content = getOpenRouterFinalReasoningContent({
203
217
  current,
@@ -473,7 +487,7 @@ function appendStreamChunk({
473
487
  }: {
474
488
  current?: AIMessageChunk;
475
489
  next: AIMessageChunk;
476
- provider: Providers;
490
+ provider: t.ProviderName;
477
491
  }): AIMessageChunk {
478
492
  if (current == null) {
479
493
  return next;
@@ -514,11 +528,11 @@ type AttemptInvokeParams = AttemptInvokeCommonParams &
514
528
  request?: never;
515
529
  model: t.ChatModel;
516
530
  messages: BaseMessage[];
517
- provider: Providers;
531
+ provider: t.ProviderName;
518
532
  }
519
533
  );
520
534
 
521
- function resolveAttemptProvider(params: AttemptInvokeParams): Providers {
535
+ function resolveAttemptProvider(params: AttemptInvokeParams): t.ProviderName {
522
536
  if (params.request != null) {
523
537
  return params.request.provider;
524
538
  }
@@ -586,9 +600,7 @@ export async function attemptInvoke(
586
600
  : undefined;
587
601
  const generationKey =
588
602
  leaseTarget != null
589
- ? resolveGenerationKey(
590
- stampedConfig.metadata as Record<string, unknown>
591
- )
603
+ ? resolveGenerationKey(stampedConfig.metadata as Record<string, unknown>)
592
604
  : undefined;
593
605
  if (leaseTarget != null && generationKey != null) {
594
606
  registerActiveStreamLimitGeneration(leaseTarget, generationKey);
@@ -809,8 +821,11 @@ async function attemptInvokeBody(
809
821
  }
810
822
  }
811
823
 
812
- if (manualToolStreamProviders.has(provider)) {
813
- finalChunk = modifyDeltaProperties(provider, finalChunk);
824
+ if (providerUsesManualToolStream(provider)) {
825
+ finalChunk = modifyDeltaProperties(
826
+ getManualToolStreamNormalizationProvider(provider),
827
+ finalChunk
828
+ );
814
829
  }
815
830
 
816
831
  if (preempted && finalChunk != null) {
@@ -857,7 +872,7 @@ async function attemptInvokeBody(
857
872
  * differ, which is the whole reason a fallback exists.
858
873
  */
859
874
  export interface FallbackErrorContext {
860
- provider: Providers;
875
+ provider: t.ProviderName;
861
876
  clientOptions?: t.ClientOptions;
862
877
  maxContextTokens?: number;
863
878
  }
@@ -968,7 +983,7 @@ export async function tryFallbackProviders({
968
983
  prepareProviderRequest?: (input: {
969
984
  model: t.ChatModel;
970
985
  messages: BaseMessage[];
971
- provider: Providers;
986
+ provider: t.ProviderName;
972
987
  clientOptions?: t.ClientOptions;
973
988
  maxContextTokens?: number;
974
989
  config?: RunnableConfig;
@@ -977,7 +992,7 @@ export async function tryFallbackProviders({
977
992
  prepareProviderMessages?: (input: {
978
993
  model: t.ChatModel;
979
994
  messages: BaseMessage[];
980
- provider: Providers;
995
+ provider: t.ProviderName;
981
996
  clientOptions?: t.ClientOptions;
982
997
  maxContextTokens?: number;
983
998
  config?: RunnableConfig;
@@ -5,7 +5,13 @@ import {
5
5
  buildAnthropicCacheControl,
6
6
  type PromptCacheTtl,
7
7
  } from '@/messages/cache';
8
- import { _convertToOpenAITool } from '@/llm/openai';
8
+ import { requireInternalModule } from '@/lazyRequire';
9
+
10
+ /** Loads with the first OpenRouter request rather than alongside every graph. */
11
+ const convertToOpenAITool = (
12
+ tool: BindToolsInput
13
+ ): OpenAIClient.ChatCompletionTool =>
14
+ requireInternalModule<typeof import('@/llm/openai')>('llm/openai/index')._convertToOpenAITool(tool);
9
15
 
10
16
  type OpenRouterCacheControl = { type: 'ephemeral'; ttl?: '1h' };
11
17
 
@@ -38,7 +44,7 @@ function hasDeferredMarker(tool: unknown): boolean {
38
44
  }
39
45
 
40
46
  function toOpenRouterTool(tool: unknown): OpenRouterToolWithCacheControl {
41
- const converted = _convertToOpenAITool(
47
+ const converted = convertToOpenAITool(
42
48
  tool as BindToolsInput
43
49
  ) as OpenRouterToolWithCacheControl;
44
50
 
@@ -14,7 +14,6 @@ import {
14
14
  } from '@/messages/core';
15
15
  import {
16
16
  coalesceAdjacentUserTurns,
17
- strictAlternationProviders,
18
17
  appendPredecessorHandoffCue,
19
18
  removePredecessorHandoffCue,
20
19
  } from '@/messages';
@@ -23,8 +22,10 @@ import {
23
22
  stripBedrockCacheControl,
24
23
  } from '@/messages/cache';
25
24
  import { annotateMessagesForLLM } from '@/tools/toolOutputReferences';
26
- import { Providers } from '@/common';
25
+ import { providerRequiresStrictAlternation } from '@/llm/providers';
27
26
  import { isAnthropicLike, isOpenAILike } from '@/utils/llm';
27
+ import { getProviderFamily } from '@/llm/providerRegistry';
28
+ import { Providers } from '@/common';
28
29
 
29
30
  const preparedProviderRequestBrand = Symbol('PreparedProviderRequest');
30
31
 
@@ -43,7 +44,7 @@ export interface ProviderPayloadMeasurement {
43
44
  export interface PreparedProviderRequest {
44
45
  readonly model: t.ChatModel;
45
46
  readonly modelId?: string;
46
- readonly provider: Providers;
47
+ readonly provider: t.ProviderName;
47
48
  readonly projectionMode: ProviderMessageProjectionMode;
48
49
  readonly messages: BaseMessage[];
49
50
  readonly measurement?: ProviderPayloadMeasurement;
@@ -63,7 +64,7 @@ export interface ProviderRequestContext {
63
64
  export interface PrepareProviderRequestParams {
64
65
  model: t.ChatModel;
65
66
  messages: BaseMessage[];
66
- provider: Providers;
67
+ provider: t.ProviderName;
67
68
  context?: ProviderRequestContext;
68
69
  config?: RunnableConfig;
69
70
  maxToolResultChars?: number;
@@ -72,7 +73,7 @@ export interface PrepareProviderRequestParams {
72
73
 
73
74
  export function usesNativeOpenAIResponses(
74
75
  model: t.ChatModel,
75
- provider: Providers,
76
+ provider: t.ProviderName,
76
77
  callOptions?: unknown
77
78
  ): boolean {
78
79
  if (!isOpenAILike(provider)) {
@@ -142,7 +143,7 @@ export function usesNativeOpenAIResponses(
142
143
 
143
144
  function resolveProviderMessageProjectionMode(
144
145
  model: t.ChatModel,
145
- provider: Providers,
146
+ provider: t.ProviderName,
146
147
  callOptions?: unknown
147
148
  ): ProviderMessageProjectionMode {
148
149
  return usesNativeOpenAIResponses(model, provider, callOptions)
@@ -153,18 +154,16 @@ function resolveProviderMessageProjectionMode(
153
154
  interface ProjectMessagesForProviderParams {
154
155
  model: t.ChatModel;
155
156
  messages: BaseMessage[];
156
- provider: Providers;
157
+ provider: t.ProviderName;
157
158
  maxToolResultChars?: number;
158
159
  callOptions?: unknown;
159
160
  }
160
161
 
161
- function projectMessagesForProviderMode({
162
- messages,
163
- provider,
164
- maxToolResultChars,
165
- }: ProjectMessagesForProviderParams,
166
- projectionMode: ProviderMessageProjectionMode
162
+ function projectMessagesForProviderMode(
163
+ { messages, provider, maxToolResultChars }: ProjectMessagesForProviderParams,
164
+ projectionMode: ProviderMessageProjectionMode
167
165
  ): BaseMessage[] {
166
+ const providerFamily = getProviderFamily(provider);
168
167
  const nativeOpenAIResponses = projectionMode === 'openai-responses';
169
168
  const providerInputMessages = projectToolStreamContentForProvider(
170
169
  messages,
@@ -197,7 +196,7 @@ projectionMode: ProviderMessageProjectionMode
197
196
  )
198
197
  );
199
198
  }
200
- if (provider === Providers.ANTHROPIC) {
199
+ if (provider === Providers.ANTHROPIC || providerFamily === 'anthropic') {
201
200
  return projectComputerCallOutputsToText(
202
201
  projectSingleTextToolOutputsToText(
203
202
  stripBedrockCacheControl(providerInputMessages),
@@ -205,7 +204,7 @@ projectionMode: ProviderMessageProjectionMode
205
204
  )
206
205
  );
207
206
  }
208
- if (provider === Providers.BEDROCK) {
207
+ if (provider === Providers.BEDROCK || providerFamily === 'bedrock') {
209
208
  return stripAnthropicCacheControl(
210
209
  projectComputerCallOutputsToText(
211
210
  projectCacheControlledToolOutputsToText(
@@ -310,7 +309,7 @@ export function prepareProviderRequest({
310
309
  : (message): boolean => isRunProduced.call(context, message)
311
310
  )
312
311
  : removePredecessorHandoffCue(annotated);
313
- const preparedMessages = strictAlternationProviders.has(provider)
312
+ const preparedMessages = providerRequiresStrictAlternation(provider)
314
313
  ? coalesceAdjacentUserTurns(cued)
315
314
  : cued;
316
315
 
@@ -332,7 +331,7 @@ export function prepareProviderRequest({
332
331
  export function assertPreparedProviderRequestFor(
333
332
  request: PreparedProviderRequest,
334
333
  model: t.ChatModel,
335
- provider: Providers,
334
+ provider: t.ProviderName,
336
335
  config?: RunnableConfig
337
336
  ): void {
338
337
  if (
@@ -344,7 +343,9 @@ export function assertPreparedProviderRequestFor(
344
343
  throw new Error('Prepared provider request does not match serving model');
345
344
  }
346
345
  if (request.provider !== provider) {
347
- throw new Error('Prepared provider request does not match serving provider');
346
+ throw new Error(
347
+ 'Prepared provider request does not match serving provider'
348
+ );
348
349
  }
349
350
  if (
350
351
  request.projectionMode !==
@@ -0,0 +1,174 @@
1
+ import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
2
+ import type {
3
+ ProviderFamily,
4
+ ProviderRegistrationOptions,
5
+ } from '../provider-registration';
6
+ import type { ProviderModelConstructor, ProviderName } from '@/types';
7
+
8
+ type ProviderModelClass = new (config: never) => BaseChatModel;
9
+
10
+ interface StoredProviderRegistration {
11
+ model?: ProviderModelClass;
12
+ /** Built-ins defer their provider SDK import until the first model request;
13
+ * the resolved class is validated once and memoized into `model`. */
14
+ loadModel?: () => ProviderModelClass;
15
+ family: ProviderFamily;
16
+ manualToolStream: boolean;
17
+ strictAlternation: boolean;
18
+ owner: symbol;
19
+ }
20
+
21
+ interface ProviderRegistryGlobal {
22
+ [key: symbol]: Map<ProviderName, StoredProviderRegistration> | undefined;
23
+ }
24
+
25
+ const PROVIDER_REGISTRY_KEY = Symbol.for(
26
+ '@librechat/agents:providerRegistry:v1'
27
+ );
28
+ const providerRegistryGlobal = globalThis as ProviderRegistryGlobal;
29
+ const registeredProviders =
30
+ providerRegistryGlobal[PROVIDER_REGISTRY_KEY] ??
31
+ new Map<ProviderName, StoredProviderRegistration>();
32
+ providerRegistryGlobal[PROVIDER_REGISTRY_KEY] = registeredProviders;
33
+ const builtInProviders = new Map<ProviderName, StoredProviderRegistration>();
34
+
35
+ function normalizeProvider(provider: ProviderName): ProviderName {
36
+ if (typeof provider !== 'string' || provider.trim() === '') {
37
+ throw new TypeError('LLM provider name must be a non-empty string');
38
+ }
39
+ if (provider !== provider.trim()) {
40
+ throw new TypeError(
41
+ 'LLM provider name must not have surrounding whitespace'
42
+ );
43
+ }
44
+ return provider;
45
+ }
46
+
47
+ function isConstructible<T extends abstract new (...args: never[]) => object>(
48
+ value: T
49
+ ): boolean {
50
+ try {
51
+ Reflect.construct(String, [], value);
52
+ return true;
53
+ } catch {
54
+ return false;
55
+ }
56
+ }
57
+
58
+ function createRegistration<
59
+ TOptions extends object,
60
+ TModel extends BaseChatModel,
61
+ >(
62
+ provider: ProviderName,
63
+ options: ProviderRegistrationOptions<TOptions, TModel>
64
+ ): StoredProviderRegistration {
65
+ if (typeof options.model !== 'function' || !isConstructible(options.model)) {
66
+ throw new TypeError(`LLM provider constructor is invalid: ${provider}`);
67
+ }
68
+ return {
69
+ model: options.model,
70
+ family: options.family ?? 'generic',
71
+ manualToolStream: options.manualToolStream ?? false,
72
+ strictAlternation: options.strictAlternation ?? false,
73
+ owner: Symbol(provider),
74
+ };
75
+ }
76
+
77
+ function getRegistration(
78
+ provider: ProviderName
79
+ ): StoredProviderRegistration | undefined {
80
+ return builtInProviders.get(provider) ?? registeredProviders.get(provider);
81
+ }
82
+
83
+ /** Registers one host provider until the returned disposer is called. */
84
+ export function registerProvider<
85
+ TOptions extends object,
86
+ TModel extends BaseChatModel,
87
+ >(options: ProviderRegistrationOptions<TOptions, TModel>): () => void {
88
+ const provider = normalizeProvider(options.provider);
89
+ if (getRegistration(provider) != null) {
90
+ throw new Error(`LLM provider already registered: ${provider}`);
91
+ }
92
+
93
+ const registration = createRegistration(provider, options);
94
+ registeredProviders.set(provider, registration);
95
+
96
+ return (): void => {
97
+ if (registeredProviders.get(provider)?.owner === registration.owner) {
98
+ registeredProviders.delete(provider);
99
+ }
100
+ };
101
+ }
102
+
103
+ /** Initializes one built-in for the current package module graph. */
104
+ export function registerBuiltInProvider<
105
+ TOptions extends object,
106
+ TModel extends BaseChatModel,
107
+ >(options: ProviderRegistrationOptions<TOptions, TModel>): void {
108
+ const provider = normalizeProvider(options.provider);
109
+ if (builtInProviders.has(provider)) {
110
+ throw new Error(`LLM provider already registered: ${provider}`);
111
+ }
112
+ builtInProviders.set(provider, createRegistration(provider, options));
113
+ }
114
+
115
+ interface BuiltInProviderLoaderOptions {
116
+ provider: ProviderName;
117
+ loadModel: () => ProviderModelClass;
118
+ family?: ProviderFamily;
119
+ manualToolStream?: boolean;
120
+ strictAlternation?: boolean;
121
+ }
122
+
123
+ /** Registers a built-in whose provider SDK loads on first use, not at import time. */
124
+ export function registerBuiltInProviderLoader(
125
+ options: BuiltInProviderLoaderOptions
126
+ ): void {
127
+ const provider = normalizeProvider(options.provider);
128
+ if (builtInProviders.has(provider)) {
129
+ throw new Error(`LLM provider already registered: ${provider}`);
130
+ }
131
+ builtInProviders.set(provider, {
132
+ loadModel: options.loadModel,
133
+ family: options.family ?? 'generic',
134
+ manualToolStream: options.manualToolStream ?? false,
135
+ strictAlternation: options.strictAlternation ?? false,
136
+ owner: Symbol(provider),
137
+ });
138
+ }
139
+
140
+ export function getRegisteredChatModelClass<P extends ProviderName>(
141
+ provider: P
142
+ ): ProviderModelConstructor<P> {
143
+ const registration = getRegistration(provider);
144
+ if (!registration) {
145
+ throw new Error(`Unsupported LLM provider: ${provider}`);
146
+ }
147
+ if (registration.model == null && registration.loadModel != null) {
148
+ const loaded = registration.loadModel();
149
+ if (typeof loaded !== 'function' || !isConstructible(loaded)) {
150
+ throw new TypeError(`LLM provider constructor is invalid: ${provider}`);
151
+ }
152
+ registration.model = loaded;
153
+ }
154
+ if (registration.model == null) {
155
+ throw new Error(`Unsupported LLM provider: ${provider}`);
156
+ }
157
+ return registration.model as ProviderModelConstructor<P>;
158
+ }
159
+
160
+ export function getProviderFamily(
161
+ provider: ProviderName
162
+ ): ProviderFamily | undefined {
163
+ return getRegistration(provider)?.family;
164
+ }
165
+
166
+ export function providerUsesManualToolStream(provider: ProviderName): boolean {
167
+ return getRegistration(provider)?.manualToolStream ?? false;
168
+ }
169
+
170
+ export function providerRequiresStrictAlternation(
171
+ provider: ProviderName
172
+ ): boolean {
173
+ return getRegistration(provider)?.strictAlternation ?? false;
174
+ }
@@ -0,0 +1,24 @@
1
+ import * as openai from '@/llm/openai';
2
+ import * as google from '@/llm/google';
3
+ import * as bedrock from '@/llm/bedrock';
4
+ import * as mistral from '@/llm/mistral';
5
+ import * as vertexai from '@/llm/vertexai';
6
+ import * as anthropic from '@/llm/anthropic';
7
+ import * as openrouter from '@/llm/openrouter';
8
+ import { registerSourceModeModules } from '@/lazyRequire';
9
+
10
+ /**
11
+ * Source-mode counterpart of the lazy provider loading the built package uses: commands
12
+ * that run the TypeScript directly import this module first, so every lazily loadable
13
+ * provider module is loaded through the active ESM loader — one module graph, one set of
14
+ * class identities — and registered with the lazy-require seam.
15
+ */
16
+ registerSourceModeModules({
17
+ 'llm/openai/index': openai,
18
+ 'llm/google/index': google,
19
+ 'llm/bedrock/index': bedrock,
20
+ 'llm/mistral/index': mistral,
21
+ 'llm/vertexai/index': vertexai,
22
+ 'llm/anthropic/index': anthropic,
23
+ 'llm/openrouter/index': openrouter,
24
+ });
@@ -1,52 +1,109 @@
1
- // src/llm/providers.ts
2
- import { CustomChatMistralAI } from '@/llm/mistral';
3
- import type {
4
- ChatModelConstructorMap,
5
- ProviderOptionsMap,
6
- ChatModelMap,
7
- } from '@/types';
1
+ import type { ProviderModelConstructor, ProviderName } from '@/types';
2
+ import { requireInternalModule } from '@/lazyRequire';
3
+ import type { ProviderFamily } from '../provider-registration';
4
+ import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
8
5
  import {
9
- AzureChatOpenAI,
10
- ChatDeepSeek,
11
- ChatMoonshot,
12
- ChatOpenAI,
13
- ChatXAI,
14
- } from '@/llm/openai';
15
- import { CustomChatGoogleGenerativeAI } from '@/llm/google';
16
- import { CustomChatBedrockConverse } from '@/llm/bedrock';
17
- import { CustomAnthropic } from '@/llm/anthropic';
18
- import { ChatOpenRouter } from '@/llm/openrouter';
19
- import { ChatVertexAI } from '@/llm/vertexai';
6
+ getRegisteredChatModelClass,
7
+ registerBuiltInProviderLoader,
8
+ } from '@/llm/providerRegistry';
20
9
  import { Providers } from '@/common';
21
10
 
22
- export const llmProviders: Partial<ChatModelConstructorMap> = {
23
- [Providers.XAI]: ChatXAI,
24
- [Providers.OPENAI]: ChatOpenAI,
25
- [Providers.AZURE]: AzureChatOpenAI,
26
- [Providers.VERTEXAI]: ChatVertexAI,
27
- [Providers.DEEPSEEK]: ChatDeepSeek,
28
- [Providers.MISTRALAI]: CustomChatMistralAI,
29
- [Providers.MISTRAL]: CustomChatMistralAI,
30
- [Providers.ANTHROPIC]: CustomAnthropic,
31
- [Providers.OPENROUTER]: ChatOpenRouter,
32
- [Providers.BEDROCK]: CustomChatBedrockConverse,
33
- // [Providers.ANTHROPIC]: ChatAnthropic,
34
- [Providers.GOOGLE]: CustomChatGoogleGenerativeAI,
35
- [Providers.MOONSHOT]: ChatMoonshot,
11
+ /**
12
+ * Built-in provider SDKs load on first model request, not at import time: eagerly
13
+ * importing every provider cost hundreds of milliseconds of boot in hosts that
14
+ * configure one or two of them. `createRequire` keeps resolution synchronous and
15
+ * correct from both the CJS and ESM builds.
16
+ */
17
+
18
+ type ProviderModelClass = new (config: never) => BaseChatModel;
19
+
20
+ type BuiltInProviderTraits = {
21
+ family: ProviderFamily;
22
+ manualToolStream?: boolean;
23
+ strictAlternation?: boolean;
36
24
  };
37
25
 
38
- export const manualToolStreamProviders = new Set<Providers | string>([
26
+ function initializeBuiltInProvider(
27
+ provider: Providers,
28
+ loadModel: () => ProviderModelClass,
29
+ traits: BuiltInProviderTraits
30
+ ): void {
31
+ registerBuiltInProviderLoader({
32
+ provider,
33
+ loadModel,
34
+ ...traits,
35
+ });
36
+ }
37
+
38
+ const fromOpenAI =
39
+ (name: string) => (): ProviderModelClass =>
40
+ (requireInternalModule<Record<string, ProviderModelClass>>('llm/openai/index'))[
41
+ name
42
+ ];
43
+
44
+ initializeBuiltInProvider(Providers.XAI, fromOpenAI('ChatXAI'), { family: 'openai' });
45
+ initializeBuiltInProvider(Providers.OPENAI, fromOpenAI('ChatOpenAI'), { family: 'openai' });
46
+ initializeBuiltInProvider(Providers.AZURE, fromOpenAI('AzureChatOpenAI'), {
47
+ family: 'openai',
48
+ });
49
+ initializeBuiltInProvider(
50
+ Providers.VERTEXAI,
51
+ () => (requireInternalModule<typeof import('@/llm/vertexai')>('llm/vertexai/index')).ChatVertexAI,
52
+ { family: 'google' }
53
+ );
54
+ initializeBuiltInProvider(Providers.DEEPSEEK, fromOpenAI('ChatDeepSeek'), {
55
+ family: 'openai',
56
+ });
57
+ const loadMistral = (): ProviderModelClass =>
58
+ (requireInternalModule<typeof import('@/llm/mistral')>('llm/mistral/index')).CustomChatMistralAI;
59
+ initializeBuiltInProvider(Providers.MISTRALAI, loadMistral, {
60
+ family: 'mistral',
61
+ strictAlternation: true,
62
+ });
63
+ initializeBuiltInProvider(Providers.MISTRAL, loadMistral, {
64
+ family: 'mistral',
65
+ strictAlternation: true,
66
+ });
67
+ initializeBuiltInProvider(
39
68
  Providers.ANTHROPIC,
69
+ () => (requireInternalModule<typeof import('@/llm/anthropic')>('llm/anthropic/index')).CustomAnthropic,
70
+ { family: 'anthropic', manualToolStream: true }
71
+ );
72
+ initializeBuiltInProvider(
73
+ Providers.OPENROUTER,
74
+ () =>
75
+ (requireInternalModule<typeof import('@/llm/openrouter')>('llm/openrouter/index')).ChatOpenRouter,
76
+ { family: 'openai' }
77
+ );
78
+ initializeBuiltInProvider(
40
79
  Providers.BEDROCK,
41
- ]);
80
+ () =>
81
+ (requireInternalModule<typeof import('@/llm/bedrock')>('llm/bedrock/index'))
82
+ .CustomChatBedrockConverse,
83
+ { family: 'bedrock', manualToolStream: true, strictAlternation: true }
84
+ );
85
+ initializeBuiltInProvider(
86
+ Providers.GOOGLE,
87
+ () =>
88
+ (requireInternalModule<typeof import('@/llm/google')>('llm/google/index'))
89
+ .CustomChatGoogleGenerativeAI,
90
+ { family: 'google' }
91
+ );
92
+ initializeBuiltInProvider(Providers.MOONSHOT, fromOpenAI('ChatMoonshot'), {
93
+ family: 'generic',
94
+ });
42
95
 
43
- export const getChatModelClass = <P extends Providers>(
96
+ export const getChatModelClass = <P extends ProviderName>(
44
97
  provider: P
45
- ): new (config: ProviderOptionsMap[P]) => ChatModelMap[P] => {
46
- const ChatModelClass = llmProviders[provider];
47
- if (!ChatModelClass) {
48
- throw new Error(`Unsupported LLM provider: ${provider}`);
49
- }
98
+ ): ProviderModelConstructor<P> => getRegisteredChatModelClass(provider);
50
99
 
51
- return ChatModelClass;
52
- };
100
+ export {
101
+ getProviderFamily,
102
+ providerRequiresStrictAlternation,
103
+ providerUsesManualToolStream,
104
+ registerProvider,
105
+ } from '@/llm/providerRegistry';
106
+ export type {
107
+ ProviderFamily,
108
+ ProviderRegistrationOptions,
109
+ } from '../provider-registration';