@librechat/agents 3.6.15 → 3.6.16

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 (117) hide show
  1. package/README.md +41 -0
  2. package/dist/cjs/agents/AgentContext.cjs +1 -1
  3. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  4. package/dist/cjs/graphs/Graph.cjs +9 -7
  5. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  6. package/dist/cjs/llm/init.cjs +1 -0
  7. package/dist/cjs/llm/init.cjs.map +1 -1
  8. package/dist/cjs/llm/invoke.cjs +9 -2
  9. package/dist/cjs/llm/invoke.cjs.map +1 -1
  10. package/dist/cjs/llm/openai/index.cjs +1 -1
  11. package/dist/cjs/llm/prepareProviderRequest.cjs +6 -3
  12. package/dist/cjs/llm/prepareProviderRequest.cjs.map +1 -1
  13. package/dist/cjs/llm/providerRegistry.cjs +69 -0
  14. package/dist/cjs/llm/providerRegistry.cjs.map +1 -0
  15. package/dist/cjs/llm/providers.cjs +38 -25
  16. package/dist/cjs/llm/providers.cjs.map +1 -1
  17. package/dist/cjs/llm/request.cjs +6 -4
  18. package/dist/cjs/llm/request.cjs.map +1 -1
  19. package/dist/cjs/llm/truncation.cjs +2 -1
  20. package/dist/cjs/llm/truncation.cjs.map +1 -1
  21. package/dist/cjs/main.cjs +18 -15
  22. package/dist/cjs/messages/format.cjs.map +1 -1
  23. package/dist/cjs/messages/prune.cjs +8 -4
  24. package/dist/cjs/messages/prune.cjs.map +1 -1
  25. package/dist/cjs/provider-registration.cjs +4 -0
  26. package/dist/cjs/run.cjs +2 -2
  27. package/dist/cjs/stream.cjs +1 -1
  28. package/dist/cjs/stream.cjs.map +1 -1
  29. package/dist/cjs/summarization/node.cjs +2 -2
  30. package/dist/cjs/summarization/node.cjs.map +1 -1
  31. package/dist/cjs/utils/llm.cjs +6 -4
  32. package/dist/cjs/utils/llm.cjs.map +1 -1
  33. package/dist/esm/agents/AgentContext.mjs +1 -1
  34. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  35. package/dist/esm/graphs/Graph.mjs +10 -8
  36. package/dist/esm/graphs/Graph.mjs.map +1 -1
  37. package/dist/esm/llm/init.mjs +1 -0
  38. package/dist/esm/llm/init.mjs.map +1 -1
  39. package/dist/esm/llm/invoke.mjs +9 -2
  40. package/dist/esm/llm/invoke.mjs.map +1 -1
  41. package/dist/esm/llm/openai/index.mjs +1 -1
  42. package/dist/esm/llm/prepareProviderRequest.mjs +7 -4
  43. package/dist/esm/llm/prepareProviderRequest.mjs.map +1 -1
  44. package/dist/esm/llm/providerRegistry.mjs +64 -0
  45. package/dist/esm/llm/providerRegistry.mjs.map +1 -0
  46. package/dist/esm/llm/providers.mjs +36 -22
  47. package/dist/esm/llm/providers.mjs.map +1 -1
  48. package/dist/esm/llm/request.mjs +6 -4
  49. package/dist/esm/llm/request.mjs.map +1 -1
  50. package/dist/esm/llm/truncation.mjs +2 -1
  51. package/dist/esm/llm/truncation.mjs.map +1 -1
  52. package/dist/esm/main.mjs +12 -10
  53. package/dist/esm/messages/format.mjs.map +1 -1
  54. package/dist/esm/messages/prune.mjs +8 -4
  55. package/dist/esm/messages/prune.mjs.map +1 -1
  56. package/dist/esm/provider-registration.mjs +3 -0
  57. package/dist/esm/run.mjs +2 -2
  58. package/dist/esm/stream.mjs +1 -1
  59. package/dist/esm/stream.mjs.map +1 -1
  60. package/dist/esm/summarization/node.mjs +2 -2
  61. package/dist/esm/summarization/node.mjs.map +1 -1
  62. package/dist/esm/utils/llm.mjs +6 -4
  63. package/dist/esm/utils/llm.mjs.map +1 -1
  64. package/dist/types/agents/AgentContext.d.ts +5 -5
  65. package/dist/types/index.d.ts +2 -0
  66. package/dist/types/llm/contextOverflowRecovery.d.ts +2 -2
  67. package/dist/types/llm/init.d.ts +21 -14
  68. package/dist/types/llm/invoke.d.ts +5 -6
  69. package/dist/types/llm/prepareProviderRequest.d.ts +5 -6
  70. package/dist/types/llm/providerRegistry.d.ts +11 -0
  71. package/dist/types/llm/providers.d.ts +4 -5
  72. package/dist/types/llm/request.d.ts +2 -3
  73. package/dist/types/llm/truncation.d.ts +2 -2
  74. package/dist/types/messages/core.d.ts +1 -2
  75. package/dist/types/messages/format.d.ts +3 -3
  76. package/dist/types/messages/prune.d.ts +3 -2
  77. package/dist/types/provider-registration.d.ts +15 -0
  78. package/dist/types/stream.d.ts +1 -2
  79. package/dist/types/summarization/node.d.ts +1 -2
  80. package/dist/types/types/activityLabel.d.ts +3 -4
  81. package/dist/types/types/graph.d.ts +4 -5
  82. package/dist/types/types/llm.d.ts +54 -13
  83. package/dist/types/types/reasoningLabel.d.ts +2 -3
  84. package/dist/types/types/run.d.ts +3 -3
  85. package/dist/types/types/summarize.d.ts +2 -2
  86. package/dist/types/utils/errors.d.ts +3 -3
  87. package/dist/types/utils/llm.d.ts +4 -4
  88. package/dist/types/utils/llmConfig.d.ts +2 -2
  89. package/package.json +9 -1
  90. package/src/agents/AgentContext.ts +27 -26
  91. package/src/graphs/Graph.ts +14 -14
  92. package/src/index.ts +5 -0
  93. package/src/llm/contextOverflowRecovery.ts +2 -2
  94. package/src/llm/init.ts +40 -17
  95. package/src/llm/invoke.ts +36 -21
  96. package/src/llm/prepareProviderRequest.ts +19 -18
  97. package/src/llm/providerRegistry.ts +134 -0
  98. package/src/llm/providers.ts +73 -30
  99. package/src/llm/request.ts +10 -6
  100. package/src/llm/truncation.ts +9 -3
  101. package/src/messages/core.ts +2 -2
  102. package/src/messages/format.ts +14 -16
  103. package/src/messages/prune.ts +22 -17
  104. package/src/provider-registration.ts +29 -0
  105. package/src/run.ts +1 -1
  106. package/src/session/AgentSession.ts +1 -1
  107. package/src/stream.ts +7 -8
  108. package/src/summarization/node.ts +7 -7
  109. package/src/types/activityLabel.ts +3 -4
  110. package/src/types/graph.ts +5 -5
  111. package/src/types/llm.ts +95 -13
  112. package/src/types/reasoningLabel.ts +2 -3
  113. package/src/types/run.ts +3 -3
  114. package/src/types/summarize.ts +2 -2
  115. package/src/utils/errors.ts +3 -3
  116. package/src/utils/llm.ts +22 -15
  117. package/src/utils/llmConfig.ts +4 -4
@@ -0,0 +1,134 @@
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
+ interface StoredProviderRegistration {
9
+ model: new (config: never) => BaseChatModel;
10
+ family: ProviderFamily;
11
+ manualToolStream: boolean;
12
+ strictAlternation: boolean;
13
+ owner: symbol;
14
+ }
15
+
16
+ interface ProviderRegistryGlobal {
17
+ [key: symbol]: Map<ProviderName, StoredProviderRegistration> | undefined;
18
+ }
19
+
20
+ const PROVIDER_REGISTRY_KEY = Symbol.for(
21
+ '@librechat/agents:providerRegistry:v1'
22
+ );
23
+ const providerRegistryGlobal = globalThis as ProviderRegistryGlobal;
24
+ const registeredProviders =
25
+ providerRegistryGlobal[PROVIDER_REGISTRY_KEY] ??
26
+ new Map<ProviderName, StoredProviderRegistration>();
27
+ providerRegistryGlobal[PROVIDER_REGISTRY_KEY] = registeredProviders;
28
+ const builtInProviders = new Map<ProviderName, StoredProviderRegistration>();
29
+
30
+ function normalizeProvider(provider: ProviderName): ProviderName {
31
+ if (typeof provider !== 'string' || provider.trim() === '') {
32
+ throw new TypeError('LLM provider name must be a non-empty string');
33
+ }
34
+ if (provider !== provider.trim()) {
35
+ throw new TypeError(
36
+ 'LLM provider name must not have surrounding whitespace'
37
+ );
38
+ }
39
+ return provider;
40
+ }
41
+
42
+ function isConstructible<T extends abstract new (...args: never[]) => object>(
43
+ value: T
44
+ ): boolean {
45
+ try {
46
+ Reflect.construct(String, [], value);
47
+ return true;
48
+ } catch {
49
+ return false;
50
+ }
51
+ }
52
+
53
+ function createRegistration<
54
+ TOptions extends object,
55
+ TModel extends BaseChatModel,
56
+ >(
57
+ provider: ProviderName,
58
+ options: ProviderRegistrationOptions<TOptions, TModel>
59
+ ): StoredProviderRegistration {
60
+ if (typeof options.model !== 'function' || !isConstructible(options.model)) {
61
+ throw new TypeError(`LLM provider constructor is invalid: ${provider}`);
62
+ }
63
+ return {
64
+ model: options.model,
65
+ family: options.family ?? 'generic',
66
+ manualToolStream: options.manualToolStream ?? false,
67
+ strictAlternation: options.strictAlternation ?? false,
68
+ owner: Symbol(provider),
69
+ };
70
+ }
71
+
72
+ function getRegistration(
73
+ provider: ProviderName
74
+ ): StoredProviderRegistration | undefined {
75
+ return builtInProviders.get(provider) ?? registeredProviders.get(provider);
76
+ }
77
+
78
+ /** Registers one host provider until the returned disposer is called. */
79
+ export function registerProvider<
80
+ TOptions extends object,
81
+ TModel extends BaseChatModel,
82
+ >(options: ProviderRegistrationOptions<TOptions, TModel>): () => void {
83
+ const provider = normalizeProvider(options.provider);
84
+ if (getRegistration(provider) != null) {
85
+ throw new Error(`LLM provider already registered: ${provider}`);
86
+ }
87
+
88
+ const registration = createRegistration(provider, options);
89
+ registeredProviders.set(provider, registration);
90
+
91
+ return (): void => {
92
+ if (registeredProviders.get(provider)?.owner === registration.owner) {
93
+ registeredProviders.delete(provider);
94
+ }
95
+ };
96
+ }
97
+
98
+ /** Initializes one built-in for the current package module graph. */
99
+ export function registerBuiltInProvider<
100
+ TOptions extends object,
101
+ TModel extends BaseChatModel,
102
+ >(options: ProviderRegistrationOptions<TOptions, TModel>): void {
103
+ const provider = normalizeProvider(options.provider);
104
+ if (builtInProviders.has(provider)) {
105
+ throw new Error(`LLM provider already registered: ${provider}`);
106
+ }
107
+ builtInProviders.set(provider, createRegistration(provider, options));
108
+ }
109
+
110
+ export function getRegisteredChatModelClass<P extends ProviderName>(
111
+ provider: P
112
+ ): ProviderModelConstructor<P> {
113
+ const registration = getRegistration(provider);
114
+ if (!registration) {
115
+ throw new Error(`Unsupported LLM provider: ${provider}`);
116
+ }
117
+ return registration.model as ProviderModelConstructor<P>;
118
+ }
119
+
120
+ export function getProviderFamily(
121
+ provider: ProviderName
122
+ ): ProviderFamily | undefined {
123
+ return getRegistration(provider)?.family;
124
+ }
125
+
126
+ export function providerUsesManualToolStream(provider: ProviderName): boolean {
127
+ return getRegistration(provider)?.manualToolStream ?? false;
128
+ }
129
+
130
+ export function providerRequiresStrictAlternation(
131
+ provider: ProviderName
132
+ ): boolean {
133
+ return getRegistration(provider)?.strictAlternation ?? false;
134
+ }
@@ -1,10 +1,9 @@
1
- // src/llm/providers.ts
2
- import { CustomChatMistralAI } from '@/llm/mistral';
3
1
  import type {
4
2
  ChatModelConstructorMap,
5
- ProviderOptionsMap,
6
- ChatModelMap,
3
+ ProviderModelConstructor,
4
+ ProviderName,
7
5
  } from '@/types';
6
+ import type { ProviderFamily } from '../provider-registration';
8
7
  import {
9
8
  AzureChatOpenAI,
10
9
  ChatDeepSeek,
@@ -12,41 +11,85 @@ import {
12
11
  ChatOpenAI,
13
12
  ChatXAI,
14
13
  } from '@/llm/openai';
14
+ import {
15
+ getRegisteredChatModelClass,
16
+ registerBuiltInProvider,
17
+ } from '@/llm/providerRegistry';
15
18
  import { CustomChatGoogleGenerativeAI } from '@/llm/google';
16
19
  import { CustomChatBedrockConverse } from '@/llm/bedrock';
20
+ import { CustomChatMistralAI } from '@/llm/mistral';
17
21
  import { CustomAnthropic } from '@/llm/anthropic';
18
22
  import { ChatOpenRouter } from '@/llm/openrouter';
19
23
  import { ChatVertexAI } from '@/llm/vertexai';
20
24
  import { Providers } from '@/common';
21
25
 
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,
26
+ type BuiltInProviderTraits = {
27
+ family: ProviderFamily;
28
+ manualToolStream?: boolean;
29
+ strictAlternation?: boolean;
36
30
  };
37
31
 
38
- export const manualToolStreamProviders = new Set<Providers | string>([
39
- Providers.ANTHROPIC,
40
- Providers.BEDROCK,
41
- ]);
32
+ function initializeBuiltInProvider<P extends Providers>(
33
+ provider: P,
34
+ model: ChatModelConstructorMap[P],
35
+ traits: BuiltInProviderTraits
36
+ ): void {
37
+ registerBuiltInProvider({
38
+ provider,
39
+ model,
40
+ ...traits,
41
+ });
42
+ }
42
43
 
43
- export const getChatModelClass = <P extends Providers>(
44
+ initializeBuiltInProvider(Providers.XAI, ChatXAI, { family: 'openai' });
45
+ initializeBuiltInProvider(Providers.OPENAI, ChatOpenAI, { family: 'openai' });
46
+ initializeBuiltInProvider(Providers.AZURE, AzureChatOpenAI, {
47
+ family: 'openai',
48
+ });
49
+ initializeBuiltInProvider(Providers.VERTEXAI, ChatVertexAI, {
50
+ family: 'google',
51
+ });
52
+ initializeBuiltInProvider(Providers.DEEPSEEK, ChatDeepSeek, {
53
+ family: 'openai',
54
+ });
55
+ initializeBuiltInProvider(Providers.MISTRALAI, CustomChatMistralAI, {
56
+ family: 'mistral',
57
+ strictAlternation: true,
58
+ });
59
+ initializeBuiltInProvider(Providers.MISTRAL, CustomChatMistralAI, {
60
+ family: 'mistral',
61
+ strictAlternation: true,
62
+ });
63
+ initializeBuiltInProvider(Providers.ANTHROPIC, CustomAnthropic, {
64
+ family: 'anthropic',
65
+ manualToolStream: true,
66
+ });
67
+ initializeBuiltInProvider(Providers.OPENROUTER, ChatOpenRouter, {
68
+ family: 'openai',
69
+ });
70
+ initializeBuiltInProvider(Providers.BEDROCK, CustomChatBedrockConverse, {
71
+ family: 'bedrock',
72
+ manualToolStream: true,
73
+ strictAlternation: true,
74
+ });
75
+ initializeBuiltInProvider(Providers.GOOGLE, CustomChatGoogleGenerativeAI, {
76
+ family: 'google',
77
+ });
78
+ initializeBuiltInProvider(Providers.MOONSHOT, ChatMoonshot, {
79
+ family: 'generic',
80
+ });
81
+
82
+ export const getChatModelClass = <P extends ProviderName>(
44
83
  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
- }
84
+ ): ProviderModelConstructor<P> => getRegisteredChatModelClass(provider);
50
85
 
51
- return ChatModelClass;
52
- };
86
+ export {
87
+ getProviderFamily,
88
+ providerRequiresStrictAlternation,
89
+ providerUsesManualToolStream,
90
+ registerProvider,
91
+ } from '@/llm/providerRegistry';
92
+ export type {
93
+ ProviderFamily,
94
+ ProviderRegistrationOptions,
95
+ } from '../provider-registration';
@@ -1,4 +1,5 @@
1
1
  import type * as t from '@/types';
2
+ import { getProviderFamily } from '@/llm/providerRegistry';
2
3
  import { Providers } from '@/common';
3
4
 
4
5
  /**
@@ -7,20 +8,21 @@ import { Providers } from '@/common';
7
8
  * and OpenAI-compat (modelKwargs.thinking).
8
9
  */
9
10
  export function isThinkingEnabled(
10
- provider: Providers,
11
+ provider: t.ProviderName,
11
12
  clientOptions?: t.ClientOptions
12
13
  ): boolean {
13
14
  if (!clientOptions) return false;
15
+ const family = getProviderFamily(provider);
14
16
 
15
17
  if (
16
- provider === Providers.ANTHROPIC &&
18
+ (provider === Providers.ANTHROPIC || family === 'anthropic') &&
17
19
  (clientOptions as t.AnthropicClientOptions).thinking != null
18
20
  ) {
19
21
  return true;
20
22
  }
21
23
 
22
24
  if (
23
- provider === Providers.BEDROCK &&
25
+ (provider === Providers.BEDROCK || family === 'bedrock') &&
24
26
  (clientOptions as t.BedrockAnthropicInput).additionalModelRequestFields?.[
25
27
  'thinking'
26
28
  ] != null
@@ -29,7 +31,7 @@ export function isThinkingEnabled(
29
31
  }
30
32
 
31
33
  if (
32
- provider === Providers.OPENAI &&
34
+ (provider === Providers.OPENAI || family === 'openai') &&
33
35
  (
34
36
  (clientOptions as t.OpenAIClientOptions).modelKwargs
35
37
  ?.thinking as t.AnthropicClientOptions['thinking']
@@ -47,9 +49,11 @@ export function isThinkingEnabled(
47
49
  * use `maxTokens`.
48
50
  */
49
51
  export function getMaxOutputTokensKey(
50
- provider: Providers | string
52
+ provider: t.ProviderName
51
53
  ): 'maxOutputTokens' | 'maxTokens' {
52
- return provider === Providers.GOOGLE || provider === Providers.VERTEXAI
54
+ return provider === Providers.GOOGLE ||
55
+ provider === Providers.VERTEXAI ||
56
+ getProviderFamily(provider) === 'google'
53
57
  ? 'maxOutputTokens'
54
58
  : 'maxTokens';
55
59
  }
@@ -1,4 +1,6 @@
1
1
  import type { AIMessageChunk, BaseMessage } from '@langchain/core/messages';
2
+ import type { ProviderName } from '@/types';
3
+ import { getProviderFamily } from '@/llm/providerRegistry';
2
4
  import { Providers } from '@/common';
3
5
 
4
6
  /**
@@ -7,7 +9,7 @@ import { Providers } from '@/common';
7
9
  * `functionCall` whole and seal it on arrival, so a `MAX_TOKENS` finish does
8
10
  * NOT imply the arguments were cut off — the truncation guard must skip them.
9
11
  */
10
- const ATOMIC_TOOL_CALL_ARG_PROVIDERS = new Set<Providers>([
12
+ const ATOMIC_TOOL_CALL_ARG_PROVIDERS = new Set<ProviderName>([
11
13
  Providers.GOOGLE,
12
14
  Providers.VERTEXAI,
13
15
  ]);
@@ -162,12 +164,16 @@ function collectToolCallNames(message: AIMessageChunk | BaseMessage): string[] {
162
164
  */
163
165
  export function assertNotTruncatedToolCall(
164
166
  message: AIMessageChunk | BaseMessage | undefined | null,
165
- provider?: Providers
167
+ provider?: ProviderName
166
168
  ): void {
167
169
  if (message == null) {
168
170
  return;
169
171
  }
170
- if (provider != null && ATOMIC_TOOL_CALL_ARG_PROVIDERS.has(provider)) {
172
+ if (
173
+ provider != null &&
174
+ (ATOMIC_TOOL_CALL_ARG_PROVIDERS.has(provider) ||
175
+ getProviderFamily(provider) === 'google')
176
+ ) {
171
177
  return;
172
178
  }
173
179
  const stopReason = getTruncationStopReason(message);
@@ -90,7 +90,7 @@ const modifyContent = ({
90
90
  messageType,
91
91
  content,
92
92
  }: {
93
- provider: Providers;
93
+ provider: t.ProviderName;
94
94
  messageType: string;
95
95
  content: t.ExtendedMessageContent[];
96
96
  }): (t.ExtendedMessageContent | null)[] => {
@@ -243,7 +243,7 @@ function hasReasoningContent(content: BaseMessage['content']): boolean {
243
243
  }
244
244
 
245
245
  export function modifyDeltaProperties(
246
- provider: Providers,
246
+ provider: t.ProviderName,
247
247
  obj?: AIMessageChunk
248
248
  ): AIMessageChunk | undefined {
249
249
  if (!obj || typeof obj !== 'object') return obj;
@@ -27,6 +27,7 @@ import type {
27
27
  ToolCallPart,
28
28
  TPayload,
29
29
  TMessage,
30
+ ProviderName,
30
31
  } from '@/types';
31
32
  import type {
32
33
  ProviderMessageAttribution,
@@ -366,7 +367,7 @@ export const formatFromLangChain = (
366
367
  interface FormatAssistantMessageOptions {
367
368
  preserveUnpairedServerToolUses?: boolean;
368
369
  preserveReasoningContent?: boolean;
369
- provider?: Providers;
370
+ provider?: ProviderName;
370
371
  sourceMessageId?: string;
371
372
  sourceContentPartOffset?: number;
372
373
  sourceContentPartIndices?: readonly SourceContentPartIndices[];
@@ -376,7 +377,7 @@ interface FormatAssistantMessageOptions {
376
377
  type SourceContentPartIndices = number | readonly number[];
377
378
 
378
379
  interface FormatAgentMessagesOptions {
379
- provider?: Providers;
380
+ provider?: ProviderName;
380
381
  /** Reconstruct hidden `reasoning_content` from `THINK` parts onto prior
381
382
  * tool-call messages. Explicit opt-in for OpenAI-compatible endpoints that
382
383
  * replay reasoning across turns; defaults to on for DeepSeek thinking-mode. */
@@ -478,10 +479,7 @@ function collectTrustedToolResultSourceContentPartIndices(
478
479
  let trusted: Set<number> | undefined;
479
480
  let previousPart: MessageContentComplex | null | undefined;
480
481
  for (let index = 0; index < content.length; index++) {
481
- const part = content[index] as
482
- | MessageContentComplex
483
- | null
484
- | undefined;
482
+ const part = content[index] as MessageContentComplex | null | undefined;
485
483
  if (part == null) {
486
484
  previousPart = part;
487
485
  continue;
@@ -509,13 +507,18 @@ function sourceContentPartIndicesAreTrustedToolResult(
509
507
  trustedToolSourceContentPartIndices: ReadonlySet<number> | undefined
510
508
  ): boolean {
511
509
  if (typeof sourceContentPartIndices === 'number') {
512
- return trustedToolSourceContentPartIndices?.has(sourceContentPartIndices) === true;
510
+ return (
511
+ trustedToolSourceContentPartIndices?.has(sourceContentPartIndices) ===
512
+ true
513
+ );
513
514
  }
514
515
  if (sourceContentPartIndices.length === 0) {
515
516
  return false;
516
517
  }
517
518
  for (const sourceContentPartIndex of sourceContentPartIndices) {
518
- if (trustedToolSourceContentPartIndices?.has(sourceContentPartIndex) !== true) {
519
+ if (
520
+ trustedToolSourceContentPartIndices?.has(sourceContentPartIndex) !== true
521
+ ) {
519
522
  return false;
520
523
  }
521
524
  }
@@ -1377,10 +1380,7 @@ function stampSourceMessageIdentity(
1377
1380
  },
1378
1381
  ];
1379
1382
  }
1380
- setProviderMessageProvenance(
1381
- message,
1382
- partsToStamp
1383
- );
1383
+ setProviderMessageProvenance(message, partsToStamp);
1384
1384
  if (sourceMessageId == null || derivedIndex !== 0) {
1385
1385
  return;
1386
1386
  }
@@ -2759,9 +2759,7 @@ function getSyntheticProviderContextProvenanceParts(
2759
2759
  ): ProviderMessageProvenancePart[] | null {
2760
2760
  /** Fold labels are generated context, while retained source bytes keep their
2761
2761
  * original attribution so downstream policy can still route them exactly. */
2762
- const parts: ProviderMessageProvenancePart[] = [
2763
- { attribution: 'synthetic' },
2764
- ];
2762
+ const parts: ProviderMessageProvenancePart[] = [{ attribution: 'synthetic' }];
2765
2763
  for (const source of sourceMessages) {
2766
2764
  const {
2767
2765
  message: sourceMessage,
@@ -3311,7 +3309,7 @@ function appendToolCalls(
3311
3309
  */
3312
3310
  export function ensureThinkingBlockInMessages(
3313
3311
  messages: BaseMessage[],
3314
- _provider: Providers,
3312
+ _provider: ProviderName,
3315
3313
  config?: RunnableConfig,
3316
3314
  runStartIndex?: number
3317
3315
  ): BaseMessage[] {
@@ -13,6 +13,7 @@ import type {
13
13
  } from '@/types/stream';
14
14
  import type { ContextPruningConfig } from '@/types/graph';
15
15
  import type { TokenCounter } from '@/types/run';
16
+ import type { ProviderName } from '@/types';
16
17
  import {
17
18
  cloneToolMessageWithContent,
18
19
  compactToolContent,
@@ -29,6 +30,7 @@ import {
29
30
  import { resolveContextPruningSettings } from './contextPruningSettings';
30
31
  import { hasUnsafeStructuredSerialization } from '@/utils/tokens';
31
32
  import { ContentTypes, Providers, Constants } from '@/common';
33
+ import { getProviderFamily } from '@/llm/providerRegistry';
32
34
  import { applyContextPruning } from './contextPruning';
33
35
  import { toLangChainContent } from './langchain';
34
36
 
@@ -130,7 +132,7 @@ export function clampCalibrationRatio(ratio: number): number {
130
132
  }
131
133
 
132
134
  export type PruneMessagesFactoryParams = {
133
- provider?: Providers;
135
+ provider?: ProviderName;
134
136
  maxTokens: number;
135
137
  /** Per-tool-result character cap applied while reconciling cached counts. */
136
138
  maxToolResultChars?: number;
@@ -2065,6 +2067,15 @@ type ThinkingBlocks = {
2065
2067
  };
2066
2068
 
2067
2069
  export function createPruneMessages(factoryParams: PruneMessagesFactoryParams) {
2070
+ const providerFamily =
2071
+ factoryParams.provider == null
2072
+ ? undefined
2073
+ : getProviderFamily(factoryParams.provider);
2074
+ const usesBedrockThinking =
2075
+ factoryParams.provider === Providers.BEDROCK ||
2076
+ providerFamily === 'bedrock';
2077
+ const usesOpenAIThinking =
2078
+ factoryParams.provider === Providers.OPENAI || providerFamily === 'openai';
2068
2079
  const indexTokenCountMap = { ...factoryParams.indexTokenCountMap };
2069
2080
  let lastTurnStartIndex = factoryParams.startIndex;
2070
2081
  let lastCutOffIndex = 0;
@@ -2152,10 +2163,7 @@ export function createPruneMessages(factoryParams: PruneMessagesFactoryParams) {
2152
2163
  };
2153
2164
  }
2154
2165
 
2155
- if (
2156
- factoryParams.provider === Providers.OPENAI &&
2157
- factoryParams.thinkingEnabled === true
2158
- ) {
2166
+ if (usesOpenAIThinking && factoryParams.thinkingEnabled === true) {
2159
2167
  for (let i = lastTurnStartIndex; i < params.messages.length; i++) {
2160
2168
  const m = params.messages[i];
2161
2169
  if (
@@ -2729,10 +2737,9 @@ export function createPruneMessages(factoryParams: PruneMessagesFactoryParams) {
2729
2737
  thinkingEnabled: factoryParams.thinkingEnabled,
2730
2738
  tokenCounter: factoryParams.tokenCounter,
2731
2739
  instructionTokens: rawSpaceInstructionTokens,
2732
- reasoningType:
2733
- factoryParams.provider === Providers.BEDROCK
2734
- ? ContentTypes.REASONING_CONTENT
2735
- : ContentTypes.THINKING,
2740
+ reasoningType: usesBedrockThinking
2741
+ ? ContentTypes.REASONING_CONTENT
2742
+ : ContentTypes.THINKING,
2736
2743
  thinkingStartIndex:
2737
2744
  factoryParams.thinkingEnabled === true
2738
2745
  ? runThinkingStartIndex
@@ -2826,10 +2833,9 @@ export function createPruneMessages(factoryParams: PruneMessagesFactoryParams) {
2826
2833
  thinkingEnabled: factoryParams.thinkingEnabled,
2827
2834
  tokenCounter: factoryParams.tokenCounter,
2828
2835
  instructionTokens: currentInstructionTokens,
2829
- reasoningType:
2830
- factoryParams.provider === Providers.BEDROCK
2831
- ? ContentTypes.REASONING_CONTENT
2832
- : ContentTypes.THINKING,
2836
+ reasoningType: usesBedrockThinking
2837
+ ? ContentTypes.REASONING_CONTENT
2838
+ : ContentTypes.THINKING,
2833
2839
  thinkingStartIndex:
2834
2840
  factoryParams.thinkingEnabled === true
2835
2841
  ? runThinkingStartIndex
@@ -2963,10 +2969,9 @@ export function createPruneMessages(factoryParams: PruneMessagesFactoryParams) {
2963
2969
  thinkingEnabled: factoryParams.thinkingEnabled,
2964
2970
  tokenCounter: factoryParams.tokenCounter,
2965
2971
  instructionTokens: currentInstructionTokens,
2966
- reasoningType:
2967
- factoryParams.provider === Providers.BEDROCK
2968
- ? ContentTypes.REASONING_CONTENT
2969
- : ContentTypes.THINKING,
2972
+ reasoningType: usesBedrockThinking
2973
+ ? ContentTypes.REASONING_CONTENT
2974
+ : ContentTypes.THINKING,
2970
2975
  thinkingStartIndex:
2971
2976
  factoryParams.thinkingEnabled === true
2972
2977
  ? runThinkingStartIndex
@@ -0,0 +1,29 @@
1
+ import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
2
+
3
+ declare const CUSTOM_PROVIDER_OPTIONS_TYPE: unique symbol;
4
+
5
+ /** Declaration-merge this map to type host-registered provider options. */
6
+ export interface CustomProviderOptionsMap {
7
+ readonly [CUSTOM_PROVIDER_OPTIONS_TYPE]?: never;
8
+ }
9
+
10
+ export type ProviderFamily =
11
+ | 'openai'
12
+ | 'anthropic'
13
+ | 'bedrock'
14
+ | 'google'
15
+ | 'mistral'
16
+ | 'generic';
17
+
18
+ export interface ProviderRegistrationOptions<
19
+ TOptions extends object,
20
+ TModel extends BaseChatModel,
21
+ > {
22
+ provider: string;
23
+ model: new (config: TOptions) => TModel;
24
+ family?: ProviderFamily;
25
+ manualToolStream?: boolean;
26
+ strictAlternation?: boolean;
27
+ }
28
+
29
+ export { registerProvider } from './llm/providers';
package/src/run.ts CHANGED
@@ -452,7 +452,7 @@ export class Run<_T extends t.BaseGraphState> {
452
452
  provider,
453
453
  clientOptions,
454
454
  agentId: 'default',
455
- };
455
+ } as t.AgentInputs;
456
456
  signal = legacySignal;
457
457
  }
458
458
 
@@ -588,7 +588,7 @@ function createAgentInputFromGraphConfig(
588
588
  provider,
589
589
  clientOptions,
590
590
  agentId: 'default',
591
- };
591
+ } as t.AgentInputs;
592
592
  }
593
593
  const summarizationConfig: t.SummarizationConfig = {
594
594
  ...(agent.summarizationConfig ?? {}),
package/src/stream.ts CHANGED
@@ -820,12 +820,11 @@ function startEagerToolExecutions(args: {
820
820
  toolCalls: entries.map((entry) => entry.request),
821
821
  userId: graph.config?.configurable?.user_id as string | undefined,
822
822
  agentId: agentContext?.agentId,
823
- callerCapabilityProjection:
824
- (
825
- agentContext as
826
- | Partial<Pick<AgentContext, 'getCallerCapabilityProjectionSnapshot'>>
827
- | undefined
828
- )?.getCallerCapabilityProjectionSnapshot?.(),
823
+ callerCapabilityProjection: (
824
+ agentContext as
825
+ | Partial<Pick<AgentContext, 'getCallerCapabilityProjectionSnapshot'>>
826
+ | undefined
827
+ )?.getCallerCapabilityProjectionSnapshot?.(),
829
828
  configurable: graph.config?.configurable as
830
829
  | Record<string, unknown>
831
830
  | undefined,
@@ -1327,7 +1326,7 @@ export function getChunkContent({
1327
1326
  reasoningKey,
1328
1327
  }: {
1329
1328
  chunk?: Partial<AIMessageChunk>;
1330
- provider?: Providers;
1329
+ provider?: t.ProviderName;
1331
1330
  reasoningKey: 'reasoning_content' | 'reasoning';
1332
1331
  }): string | t.MessageContentComplex[] | undefined {
1333
1332
  if (
@@ -1851,7 +1850,7 @@ export class ChatModelStreamHandler implements t.EventHandler {
1851
1850
 
1852
1851
  if (Array.isArray(content) && content.every(isTextContentPart)) {
1853
1852
  const contentGroups = splitAssistantTextContentByPhase(content);
1854
- const currentStepId = graph.stepKeyIds?.get(stepKey)?.at(-1);
1853
+ const currentStepId = graph.stepKeyIds.get(stepKey)?.at(-1);
1855
1854
  const currentStep =
1856
1855
  currentStepId == null ? undefined : graph.getRunStep(currentStepId);
1857
1856
  const currentPhase =