@librechat/agents 3.6.14 → 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 (127) 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/contextPressureMeter.cjs +8 -6
  7. package/dist/cjs/llm/contextPressureMeter.cjs.map +1 -1
  8. package/dist/cjs/llm/init.cjs +1 -0
  9. package/dist/cjs/llm/init.cjs.map +1 -1
  10. package/dist/cjs/llm/invoke.cjs +9 -2
  11. package/dist/cjs/llm/invoke.cjs.map +1 -1
  12. package/dist/cjs/llm/openai/index.cjs +1 -1
  13. package/dist/cjs/llm/prepareProviderRequest.cjs +6 -3
  14. package/dist/cjs/llm/prepareProviderRequest.cjs.map +1 -1
  15. package/dist/cjs/llm/providerRegistry.cjs +69 -0
  16. package/dist/cjs/llm/providerRegistry.cjs.map +1 -0
  17. package/dist/cjs/llm/providers.cjs +38 -25
  18. package/dist/cjs/llm/providers.cjs.map +1 -1
  19. package/dist/cjs/llm/request.cjs +6 -4
  20. package/dist/cjs/llm/request.cjs.map +1 -1
  21. package/dist/cjs/llm/truncation.cjs +2 -1
  22. package/dist/cjs/llm/truncation.cjs.map +1 -1
  23. package/dist/cjs/main.cjs +18 -15
  24. package/dist/cjs/messages/format.cjs.map +1 -1
  25. package/dist/cjs/messages/prune.cjs +8 -4
  26. package/dist/cjs/messages/prune.cjs.map +1 -1
  27. package/dist/cjs/provider-registration.cjs +4 -0
  28. package/dist/cjs/run.cjs +2 -2
  29. package/dist/cjs/stream.cjs +1 -1
  30. package/dist/cjs/stream.cjs.map +1 -1
  31. package/dist/cjs/summarization/node.cjs +2 -2
  32. package/dist/cjs/summarization/node.cjs.map +1 -1
  33. package/dist/cjs/utils/llm.cjs +6 -4
  34. package/dist/cjs/utils/llm.cjs.map +1 -1
  35. package/dist/cjs/utils/toolContent.cjs +2 -2
  36. package/dist/cjs/utils/toolContent.cjs.map +1 -1
  37. package/dist/esm/agents/AgentContext.mjs +1 -1
  38. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  39. package/dist/esm/graphs/Graph.mjs +10 -8
  40. package/dist/esm/graphs/Graph.mjs.map +1 -1
  41. package/dist/esm/llm/contextPressureMeter.mjs +8 -6
  42. package/dist/esm/llm/contextPressureMeter.mjs.map +1 -1
  43. package/dist/esm/llm/init.mjs +1 -0
  44. package/dist/esm/llm/init.mjs.map +1 -1
  45. package/dist/esm/llm/invoke.mjs +9 -2
  46. package/dist/esm/llm/invoke.mjs.map +1 -1
  47. package/dist/esm/llm/openai/index.mjs +1 -1
  48. package/dist/esm/llm/prepareProviderRequest.mjs +7 -4
  49. package/dist/esm/llm/prepareProviderRequest.mjs.map +1 -1
  50. package/dist/esm/llm/providerRegistry.mjs +64 -0
  51. package/dist/esm/llm/providerRegistry.mjs.map +1 -0
  52. package/dist/esm/llm/providers.mjs +36 -22
  53. package/dist/esm/llm/providers.mjs.map +1 -1
  54. package/dist/esm/llm/request.mjs +6 -4
  55. package/dist/esm/llm/request.mjs.map +1 -1
  56. package/dist/esm/llm/truncation.mjs +2 -1
  57. package/dist/esm/llm/truncation.mjs.map +1 -1
  58. package/dist/esm/main.mjs +12 -10
  59. package/dist/esm/messages/format.mjs.map +1 -1
  60. package/dist/esm/messages/prune.mjs +8 -4
  61. package/dist/esm/messages/prune.mjs.map +1 -1
  62. package/dist/esm/provider-registration.mjs +3 -0
  63. package/dist/esm/run.mjs +2 -2
  64. package/dist/esm/stream.mjs +1 -1
  65. package/dist/esm/stream.mjs.map +1 -1
  66. package/dist/esm/summarization/node.mjs +2 -2
  67. package/dist/esm/summarization/node.mjs.map +1 -1
  68. package/dist/esm/utils/llm.mjs +6 -4
  69. package/dist/esm/utils/llm.mjs.map +1 -1
  70. package/dist/esm/utils/toolContent.mjs +3 -3
  71. package/dist/esm/utils/toolContent.mjs.map +1 -1
  72. package/dist/types/agents/AgentContext.d.ts +5 -5
  73. package/dist/types/index.d.ts +2 -0
  74. package/dist/types/llm/contextOverflowRecovery.d.ts +2 -2
  75. package/dist/types/llm/init.d.ts +21 -14
  76. package/dist/types/llm/invoke.d.ts +5 -6
  77. package/dist/types/llm/prepareProviderRequest.d.ts +5 -6
  78. package/dist/types/llm/providerRegistry.d.ts +11 -0
  79. package/dist/types/llm/providers.d.ts +4 -5
  80. package/dist/types/llm/request.d.ts +2 -3
  81. package/dist/types/llm/truncation.d.ts +2 -2
  82. package/dist/types/messages/core.d.ts +1 -2
  83. package/dist/types/messages/format.d.ts +3 -3
  84. package/dist/types/messages/prune.d.ts +3 -2
  85. package/dist/types/provider-registration.d.ts +15 -0
  86. package/dist/types/stream.d.ts +1 -2
  87. package/dist/types/summarization/node.d.ts +1 -2
  88. package/dist/types/types/activityLabel.d.ts +3 -4
  89. package/dist/types/types/graph.d.ts +4 -5
  90. package/dist/types/types/llm.d.ts +54 -13
  91. package/dist/types/types/reasoningLabel.d.ts +2 -3
  92. package/dist/types/types/run.d.ts +3 -3
  93. package/dist/types/types/summarize.d.ts +2 -2
  94. package/dist/types/utils/errors.d.ts +3 -3
  95. package/dist/types/utils/llm.d.ts +4 -4
  96. package/dist/types/utils/llmConfig.d.ts +2 -2
  97. package/package.json +9 -1
  98. package/src/agents/AgentContext.ts +27 -26
  99. package/src/graphs/Graph.ts +14 -14
  100. package/src/index.ts +5 -0
  101. package/src/llm/contextOverflowRecovery.ts +2 -2
  102. package/src/llm/contextPressureMeter.ts +23 -13
  103. package/src/llm/init.ts +40 -17
  104. package/src/llm/invoke.ts +36 -21
  105. package/src/llm/prepareProviderRequest.ts +19 -18
  106. package/src/llm/providerRegistry.ts +134 -0
  107. package/src/llm/providers.ts +73 -30
  108. package/src/llm/request.ts +10 -6
  109. package/src/llm/truncation.ts +9 -3
  110. package/src/messages/core.ts +2 -2
  111. package/src/messages/format.ts +14 -16
  112. package/src/messages/prune.ts +22 -17
  113. package/src/provider-registration.ts +29 -0
  114. package/src/run.ts +1 -1
  115. package/src/session/AgentSession.ts +1 -1
  116. package/src/stream.ts +7 -8
  117. package/src/summarization/node.ts +7 -7
  118. package/src/types/activityLabel.ts +3 -4
  119. package/src/types/graph.ts +5 -5
  120. package/src/types/llm.ts +95 -13
  121. package/src/types/reasoningLabel.ts +2 -3
  122. package/src/types/run.ts +3 -3
  123. package/src/types/summarize.ts +2 -2
  124. package/src/utils/errors.ts +3 -3
  125. package/src/utils/llm.ts +22 -15
  126. package/src/utils/llmConfig.ts +4 -4
  127. package/src/utils/toolContent.ts +9 -3
@@ -1,6 +1,6 @@
1
1
  import { ChatMistralAI } from '@langchain/mistralai';
2
2
  import type { OpenAIChatInput, ChatOpenAIFields, AzureOpenAIInput, ClientOptions as OAIClientOptions } from '@langchain/openai';
3
- import type { BindToolsInput, BaseChatModelParams } from '@langchain/core/language_models/chat_models';
3
+ import type { BindToolsInput, BaseChatModel, BaseChatModelParams } from '@langchain/core/language_models/chat_models';
4
4
  import type { GoogleGenerativeAIChatInput } from '@langchain/google-genai';
5
5
  import type { ChatVertexAIInput } from '@langchain/google-vertexai';
6
6
  import type { ChatBedrockConverseInput } from '@langchain/aws';
@@ -12,6 +12,7 @@ import type { AnthropicInput } from '@langchain/anthropic';
12
12
  import type { Runnable } from '@langchain/core/runnables';
13
13
  import type { OpenAI as OpenAIClient } from 'openai';
14
14
  import type { ChatXAIInput } from '@langchain/xai';
15
+ import type { CustomProviderOptionsMap } from '../provider-registration';
15
16
  import type { ChatOpenRouterCallOptions } from '@/llm/openrouter';
16
17
  import type { PromptCacheTtl } from '@/messages/cache';
17
18
  import { AzureChatOpenAI, ChatDeepSeek, ChatMoonshot, ChatOpenAI, ChatXAI } from '@/llm/openai';
@@ -105,21 +106,57 @@ export type GoogleClientOptions = GoogleGenerativeAIChatInput & StreamSmoothingO
105
106
  };
106
107
  export type DeepSeekClientOptions = Partial<ChatDeepSeekInput> & StreamSmoothingOptions;
107
108
  export type XAIClientOptions = ChatXAIInput & StreamSmoothingOptions;
108
- export type ClientOptions = OpenAIClientOptions | AzureClientOptions | AnthropicClientOptions | MistralAIClientOptions | VertexAIClientOptions | BedrockConverseClientOptions | GoogleClientOptions | DeepSeekClientOptions | XAIClientOptions;
109
- export type SharedLLMConfig = {
110
- provider: Providers;
109
+ export type BuiltInClientOptions = OpenAIClientOptions | AzureClientOptions | AnthropicClientOptions | MistralAIClientOptions | VertexAIClientOptions | BedrockConverseClientOptions | GoogleClientOptions | DeepSeekClientOptions | XAIClientOptions;
110
+ type CustomProviderName = Extract<keyof CustomProviderOptionsMap, string>;
111
+ type LooseRuntimeProviderName = string & {
112
+ readonly __runtimeProviderName?: never;
113
+ };
114
+ export type ProviderName = keyof ProviderOptionsMap | CustomProviderName | LooseRuntimeProviderName;
115
+ declare const RUNTIME_PROVIDER_NAME: unique symbol;
116
+ /** A runtime provider without declaration-merged option types. */
117
+ export type RuntimeProviderName = string & {
118
+ readonly [RUNTIME_PROVIDER_NAME]: true;
119
+ };
120
+ export type ClientOptions = BuiltInClientOptions | CustomProviderOptionsMap[CustomProviderName];
121
+ export type SharedLLMConfig<P extends keyof ProviderOptionsMap | CustomProviderName | RuntimeProviderName = keyof ProviderOptionsMap> = {
122
+ provider: P;
123
+ model?: string;
111
124
  _lc_stream_delay?: number;
112
125
  };
113
- export interface FallbackConfig {
114
- provider: Providers;
115
- clientOptions?: ClientOptions;
126
+ type CustomProviderClientOptionsConfig<P extends CustomProviderName> = {
127
+ provider: P;
128
+ } & (object extends CustomProviderOptionsMap[P] ? {
129
+ clientOptions?: CustomProviderOptionsMap[P];
130
+ } : {
131
+ clientOptions: CustomProviderOptionsMap[P];
132
+ });
133
+ export type ProviderClientOptionsConfig = {
134
+ provider: keyof ProviderOptionsMap;
135
+ clientOptions?: BuiltInClientOptions;
136
+ } | {
137
+ [P in CustomProviderName]: CustomProviderClientOptionsConfig<P>;
138
+ }[CustomProviderName] | {
139
+ provider: RuntimeProviderName;
140
+ clientOptions: ClientOptions;
141
+ };
142
+ export type FallbackConfig = ProviderClientOptionsConfig & {
116
143
  /** Context window used to corroborate ambiguous fallback overflow errors. */
117
144
  maxContextTokens?: number;
118
- }
119
- export type LLMConfig = SharedLLMConfig & ClientOptions & {
145
+ };
146
+ type LLMConfigFor<P extends CustomProviderName> = SharedLLMConfig<P> & CustomProviderOptionsMap[P] & {
147
+ /** Optional provider fallbacks in order of attempt */
148
+ fallbacks?: FallbackConfig[];
149
+ };
150
+ export type BuiltInLLMConfig = SharedLLMConfig & BuiltInClientOptions & {
120
151
  /** Optional provider fallbacks in order of attempt */
121
152
  fallbacks?: FallbackConfig[];
122
153
  };
154
+ export type LLMConfig = BuiltInLLMConfig | {
155
+ [P in CustomProviderName]: LLMConfigFor<P>;
156
+ }[CustomProviderName] | (SharedLLMConfig<RuntimeProviderName> & ClientOptions & {
157
+ /** Optional provider fallbacks in order of attempt */
158
+ fallbacks?: FallbackConfig[];
159
+ });
123
160
  export type ProviderOptionsMap = {
124
161
  [Providers.AZURE]: AzureClientOptions;
125
162
  [Providers.OPENAI]: OpenAIClientOptions;
@@ -134,7 +171,7 @@ export type ProviderOptionsMap = {
134
171
  [Providers.XAI]: XAIClientOptions;
135
172
  [Providers.MOONSHOT]: OpenAIClientOptions;
136
173
  };
137
- export type ChatModelMap = {
174
+ export interface ChatModelMap {
138
175
  [Providers.XAI]: ChatXAI;
139
176
  [Providers.OPENAI]: ChatOpenAI;
140
177
  [Providers.AZURE]: AzureChatOpenAI;
@@ -147,11 +184,15 @@ export type ChatModelMap = {
147
184
  [Providers.BEDROCK]: CustomChatBedrockConverse;
148
185
  [Providers.GOOGLE]: CustomChatGoogleGenerativeAI;
149
186
  [Providers.MOONSHOT]: ChatMoonshot;
150
- };
187
+ }
188
+ export type ProviderOptionsFor<P extends ProviderName> = P extends keyof ProviderOptionsMap ? ProviderOptionsMap[P] : P extends CustomProviderName ? CustomProviderOptionsMap[P] : BuiltInClientOptions;
189
+ export type ProviderModelFor<P extends ProviderName> = P extends keyof ChatModelMap ? ChatModelMap[P] & BaseChatModel : BaseChatModel;
190
+ export type ProviderModelConstructor<P extends ProviderName> = new (config: ProviderOptionsFor<P>) => ProviderModelFor<P>;
151
191
  export type ChatModelConstructorMap = {
152
192
  [P in Providers]: new (config: ProviderOptionsMap[P]) => ChatModelMap[P];
153
193
  };
154
- export type ChatModelInstance = ChatModelMap[Providers];
155
- export type ModelWithTools = ChatModelInstance & {
194
+ export type ChatModelInstance = BaseChatModel;
195
+ export type ModelWithTools = BaseChatModel & {
156
196
  bindTools(tools: CommonToolType[]): Runnable;
157
197
  };
198
+ export type { CustomProviderOptionsMap } from '../provider-registration';
@@ -1,7 +1,6 @@
1
1
  import type { RunnableConfig } from '@langchain/core/runnables';
2
2
  import type { UsageMetadata } from '@langchain/core/messages';
3
- import type { ClientOptions } from '@/types/llm';
4
- import type { Providers } from '@/common';
3
+ import type { ClientOptions, ProviderName } from '@/types/llm';
5
4
  /** Lifecycle state of the visible reasoning snapshot being labeled. */
6
5
  export type ReasoningLabelStatus = 'streaming' | 'complete';
7
6
  /** Result of one reasoning-label revision. */
@@ -12,7 +11,7 @@ export type ReasoningLabelResult = {
12
11
  };
13
12
  /** Options for `Run.generateReasoningLabel`. */
14
13
  export type RunReasoningLabelOptions = {
15
- provider: Providers;
14
+ provider: ProviderName;
16
15
  clientOptions?: ClientOptions;
17
16
  /**
18
17
  * Complete user-visible reasoning accumulated for this step so far. Hidden
@@ -14,7 +14,7 @@ import type * as l from '@/types/llm';
14
14
  export type ZodObjectAny = z.ZodObject<any, any, any, any>;
15
15
  export type BaseGraphConfig = {
16
16
  llmConfig: l.LLMConfig;
17
- provider?: e.Providers;
17
+ provider?: l.ProviderName;
18
18
  clientOptions?: l.ClientOptions;
19
19
  /** Optional compile options for workflow.compile() */
20
20
  compileOptions?: g.CompileOptions;
@@ -34,7 +34,7 @@ export type SupervisedGraphConfig = BaseGraphConfig & {
34
34
  routingPolicies?: Array<{
35
35
  stage: string;
36
36
  agents?: string[];
37
- model?: e.Providers;
37
+ model?: l.ProviderName;
38
38
  parallel?: boolean;
39
39
  /** Optional simple condition on content/tools */
40
40
  when?: 'always' | 'has_tools' | 'no_tools' | {
@@ -55,7 +55,7 @@ export type SupervisedGraphConfig = BaseGraphConfig & {
55
55
  } & Omit<g.StandardGraphInput, 'provider' | 'clientOptions'>;
56
56
  export type RunTitleOptions = {
57
57
  inputText: string;
58
- provider: e.Providers;
58
+ provider: l.ProviderName;
59
59
  contentParts: (s.MessageContentComplex | undefined)[];
60
60
  titlePrompt?: string;
61
61
  skipLanguage?: boolean;
@@ -1,5 +1,5 @@
1
1
  import type { SummaryContentBlock } from '@/types/stream';
2
- import type { Providers } from '@/common';
2
+ import type { ProviderName } from '@/types/llm';
3
3
  export type SummarizationTrigger = {
4
4
  type: 'token_ratio' | 'remaining_tokens' | 'messages_to_refine' | (string & {});
5
5
  value: number;
@@ -28,7 +28,7 @@ export type RetainRecentConfig = {
28
28
  tokens?: number;
29
29
  };
30
30
  export type SummarizationConfig = {
31
- provider?: Providers;
31
+ provider?: ProviderName;
32
32
  model?: string;
33
33
  parameters?: Record<string, unknown>;
34
34
  prompt?: string;
@@ -1,4 +1,4 @@
1
- import type { Providers } from '@/common';
1
+ import type { ProviderName } from '@/types';
2
2
  /**
3
3
  * Why the request was rejected. Both kinds are fixed by shrinking the
4
4
  * prompt, which is what makes them recoverable; they are distinguished
@@ -33,10 +33,10 @@ export interface ContextOverflowInfo {
33
33
  promptTokens?: number;
34
34
  /** Which layer produced the verdict. Surfaced in logs and asserted in tests. */
35
35
  source: 'langchain' | 'pattern';
36
- provider?: Providers;
36
+ provider?: ProviderName;
37
37
  }
38
38
  export interface ContextOverflowContext {
39
- provider?: Providers;
39
+ provider?: ProviderName;
40
40
  /** Our own estimate of the prompt size for the call that failed. */
41
41
  estimatedPromptTokens?: number;
42
42
  /** The budget we believed applied when we built that prompt. */
@@ -1,7 +1,7 @@
1
- import { Providers } from '@/common';
2
- export declare function isOpenAILike(provider?: string | Providers): boolean;
3
- export declare function isGoogleLike(provider?: string | Providers): boolean;
1
+ import type { ProviderName } from '@/types';
2
+ export declare function isOpenAILike(provider?: ProviderName): boolean;
3
+ export declare function isGoogleLike(provider?: ProviderName): boolean;
4
4
  /** Returns true for native Anthropic or Bedrock running a Claude model. */
5
- export declare function isAnthropicLike(provider?: string | Providers, clientOptions?: {
5
+ export declare function isAnthropicLike(provider?: ProviderName, clientOptions?: {
6
6
  model?: string;
7
7
  }): boolean;
@@ -1,3 +1,3 @@
1
1
  import type * as t from '@/types';
2
- export declare const llmConfigs: Record<string, t.LLMConfig | undefined>;
3
- export declare function getLLMConfig(provider: string): t.LLMConfig;
2
+ export declare const llmConfigs: Record<string, t.BuiltInLLMConfig | undefined>;
3
+ export declare function getLLMConfig(provider: string): t.BuiltInLLMConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@librechat/agents",
3
- "version": "3.6.14",
3
+ "version": "3.6.16",
4
4
  "reova": {
5
5
  "enabled": true,
6
6
  "endpoint": "https://telemetry.reo.dev/data"
@@ -14,6 +14,11 @@
14
14
  "import": "./dist/esm/main.mjs",
15
15
  "require": "./dist/cjs/main.cjs"
16
16
  },
17
+ "./provider-registration": {
18
+ "types": "./dist/types/provider-registration.d.ts",
19
+ "import": "./dist/esm/provider-registration.mjs",
20
+ "require": "./dist/cjs/provider-registration.cjs"
21
+ },
17
22
  "./openai": {
18
23
  "types": "./dist/types/openai/index.d.ts",
19
24
  "import": "./dist/esm/openai/index.mjs",
@@ -77,6 +82,9 @@
77
82
  },
78
83
  "typesVersions": {
79
84
  "*": {
85
+ "provider-registration": [
86
+ "dist/types/provider-registration.d.ts"
87
+ ],
80
88
  "langchain": [
81
89
  "dist/types/langchain/index.d.ts"
82
90
  ],
@@ -7,8 +7,18 @@ import type {
7
7
  BaseMessageFields,
8
8
  } from '@langchain/core/messages';
9
9
  import type { RunnableConfig, Runnable } from '@langchain/core/runnables';
10
- import type * as t from '@/types';
11
10
  import type { ExactTokenCountCache } from '@/llm/contextPressureMeter';
11
+ import type * as t from '@/types';
12
+ import {
13
+ type CallerCapabilityProjection,
14
+ allowsToolCaller,
15
+ applyCallerCapabilityDefinitionOverrides,
16
+ createCallerCapabilityProjectionSnapshot,
17
+ isToolDefinitionActive,
18
+ isProgrammaticControlTool,
19
+ mergeCallerCapabilityDefinitions,
20
+ resolveCallerCapabilityProjection,
21
+ } from '@/tools/CallerCapabilities';
12
22
  import {
13
23
  addTailCacheControl,
14
24
  addCacheControlToStablePrefixMessages,
@@ -19,6 +29,12 @@ import {
19
29
  cloneMessage,
20
30
  type PromptCacheTtl,
21
31
  } from '@/messages/cache';
32
+ import {
33
+ isProgrammaticRunnerAutoBound,
34
+ isProgrammaticRunnerResolvedDirectly,
35
+ resolveLocalImplementationNames,
36
+ resolveLocalToolRegistry,
37
+ } from '@/tools/local/resolveLocalExecutionTools';
22
38
  import {
23
39
  DEFAULT_RESERVE_RATIO,
24
40
  ORIGINAL_CONTENT_MAX_CHARS,
@@ -33,27 +49,11 @@ import {
33
49
  Constants,
34
50
  Providers,
35
51
  } from '@/common';
36
- import {
37
- isProgrammaticRunnerAutoBound,
38
- isProgrammaticRunnerResolvedDirectly,
39
- resolveLocalImplementationNames,
40
- resolveLocalToolRegistry,
41
- } from '@/tools/local/resolveLocalExecutionTools';
42
- import {
43
- type CallerCapabilityProjection,
44
- allowsToolCaller,
45
- applyCallerCapabilityDefinitionOverrides,
46
- createCallerCapabilityProjectionSnapshot,
47
- isToolDefinitionActive,
48
- isProgrammaticControlTool,
49
- mergeCallerCapabilityDefinitions,
50
- resolveCallerCapabilityProjection,
51
- } from '@/tools/CallerCapabilities';
52
+ import { isTokenCounterCacheCompatible } from '@/llm/tokenCounterCacheCompatibility';
53
+ import { createExactTokenCountCache } from '@/llm/contextPressureMeter';
52
54
  import { createSchemaOnlyTools } from '@/tools/schema';
53
55
  import { apportionTokenCounts } from '@/utils/tokens';
54
56
  import { isThinkingEnabled } from '@/llm/request';
55
- import { createExactTokenCountCache } from '@/llm/contextPressureMeter';
56
- import { isTokenCounterCacheCompatible } from '@/llm/tokenCounterCacheCompatibility';
57
57
  import { toJsonSchema } from '@/utils/schema';
58
58
 
59
59
  type AgentSystemTextBlock = {
@@ -201,7 +201,7 @@ export class AgentContext {
201
201
  /** Human-readable name for this agent (used in handoff context). Falls back to agentId if not provided. */
202
202
  name?: string;
203
203
  /** Provider for this specific agent */
204
- provider: Providers;
204
+ provider: t.ProviderName;
205
205
  /** Client options for this agent */
206
206
  clientOptions?: t.ClientOptions;
207
207
  /** Per-agent Langfuse tracing configuration. */
@@ -433,7 +433,7 @@ export class AgentContext {
433
433
  agentId: string;
434
434
  codeSessionKey?: string;
435
435
  name?: string;
436
- provider: Providers;
436
+ provider: t.ProviderName;
437
437
  clientOptions?: t.ClientOptions;
438
438
  langfuse?: t.LangfuseConfig;
439
439
  maxContextTokens?: number;
@@ -570,7 +570,9 @@ export class AgentContext {
570
570
  directOnlyToolNames.length > 0
571
571
  ? `\nCall these tools directly; never list them in the \`tool_manifest\` or reference them inside ${programmaticRunnerNames}: ${directOnlyToolNames
572
572
  .map((name) => `\`${name}\``)
573
- .join(', ')}. Every ${programmaticRunnerNames} call must include a \`tool_manifest\` containing the exact registered names used by its code; the manifest is validated before execution starts.`
573
+ .join(
574
+ ', '
575
+ )}. Every ${programmaticRunnerNames} call must include a \`tool_manifest\` containing the exact registered names used by its code; the manifest is validated before execution starts.`
574
576
  : '';
575
577
  const boundary =
576
578
  '\n\n' +
@@ -658,9 +660,8 @@ export class AgentContext {
658
660
  this.toolExecution,
659
661
  this.toolDefinitions?.some((toolDef) => toolDef.name === name) === true
660
662
  ) ||
661
- this.graphTools?.some(
662
- (tool) => 'name' in tool && tool.name === name
663
- ) === true
663
+ this.graphTools?.some((tool) => 'name' in tool && tool.name === name) ===
664
+ true
664
665
  );
665
666
  }
666
667
 
@@ -1618,7 +1619,7 @@ export class AgentContext {
1618
1619
 
1619
1620
  /** Applies token calibration only when the observation came from this provider. */
1620
1621
  applyObservedOverflowCalibration(
1621
- provider: Providers | undefined,
1622
+ provider: t.ProviderName | undefined,
1622
1623
  observedCalibrationRatio: number | undefined
1623
1624
  ): void {
1624
1625
  if (
@@ -72,7 +72,6 @@ import {
72
72
  splitAtRecencyBoundary,
73
73
  convertInjectedMessages,
74
74
  coalesceAdjacentUserTurns,
75
- strictAlternationProviders,
76
75
  appendPredecessorHandoffCue,
77
76
  stampSyntheticProviderMessage,
78
77
  } from '@/messages';
@@ -98,14 +97,6 @@ import {
98
97
  joinKeys,
99
98
  sleep,
100
99
  } from '@/utils';
101
- import {
102
- attemptInvoke,
103
- tryFallbackProviders,
104
- getFallbackErrorContext,
105
- getFallbackOverflowCandidates,
106
- } from '@/llm/invoke';
107
- import { prepareProviderRequest } from '@/llm/prepareProviderRequest';
108
- import { createContextPressureMeter } from '@/llm/contextPressureMeter';
109
100
  import {
110
101
  resolveStreamLimits,
111
102
  StreamLimitExceededError,
@@ -130,6 +121,12 @@ import {
130
121
  planContextOverflowRecovery,
131
122
  translateRecoveryBudget,
132
123
  } from '@/llm/contextOverflowRecovery';
124
+ import {
125
+ attemptInvoke,
126
+ tryFallbackProviders,
127
+ getFallbackErrorContext,
128
+ getFallbackOverflowCandidates,
129
+ } from '@/llm/invoke';
133
130
  import {
134
131
  hasToolOutputTracingConfig,
135
132
  resolveLangfuseConfig,
@@ -159,8 +156,11 @@ import { createLocalCodingToolBundle } from '@/tools/local/LocalCodingTools';
159
156
  import { SUBAGENT_REPLAY_CONTROLLER } from '@/tools/subagent/SubagentReplay';
160
157
  import { applyGraphRuntimeConfig } from '@/graphs/applyGraphRuntimeConfig';
161
158
  import { partitionAndMarkBedrockToolCache } from '@/llm/bedrock/toolCache';
159
+ import { createContextPressureMeter } from '@/llm/contextPressureMeter';
162
160
  import { safeDispatchCustomEvent, emitAgentLog } from '@/utils/events';
161
+ import { prepareProviderRequest } from '@/llm/prepareProviderRequest';
163
162
  import { createCloudflareCodingToolBundle } from '@/tools/cloudflare';
163
+ import { providerRequiresStrictAlternation } from '@/llm/providers';
164
164
  import { buildSubagentToolParams } from '@/tools/SubagentTool';
165
165
  import { initializeLangfuseTracing } from '@/instrumentation';
166
166
  import { shouldTriggerSummarization } from '@/summarization';
@@ -324,7 +324,7 @@ function isGoogleServerSideToolMessageContentPart(
324
324
  }
325
325
 
326
326
  function hasGoogleServerSideToolDeltaContent(
327
- provider: Providers | undefined,
327
+ provider: t.ProviderName | undefined,
328
328
  content: t.MessageDelta['content']
329
329
  ): content is t.MessageContentComplex[] {
330
330
  return (
@@ -337,7 +337,7 @@ function hasGoogleServerSideToolDeltaContent(
337
337
  }
338
338
 
339
339
  function getMessageDeltaContent(
340
- provider: Providers | undefined,
340
+ provider: t.ProviderName | undefined,
341
341
  content: MessageContent | undefined
342
342
  ): t.MessageDelta['content'] | undefined {
343
343
  if (content == null) {
@@ -550,7 +550,7 @@ async function dispatchTextMessageContent({
550
550
  }: {
551
551
  graph: Graph<t.BaseGraphState>;
552
552
  stepKey: string;
553
- provider?: Providers;
553
+ provider?: t.ProviderName;
554
554
  content: t.MessageDelta['content'];
555
555
  metadata: Record<string, unknown>;
556
556
  }): Promise<boolean> {
@@ -3237,7 +3237,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
3237
3237
  info,
3238
3238
  }: {
3239
3239
  projection: ReturnType<typeof measureProviderPayload>;
3240
- provider?: Providers;
3240
+ provider?: t.ProviderName;
3241
3241
  info: string;
3242
3242
  }): ContextOverflowError => {
3243
3243
  const error = new ContextOverflowError(
@@ -3508,7 +3508,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
3508
3508
  * drop messages, so coalescing has to see its output, and it is the
3509
3509
  * last shaping step before the cache breakpoint is chosen.
3510
3510
  */
3511
- if (strictAlternationProviders.has(agentContext.provider)) {
3511
+ if (providerRequiresStrictAlternation(agentContext.provider)) {
3512
3512
  /**
3513
3513
  * Wrapped like every other provider transform: the merged message is
3514
3514
  * a NEW object, and without re-attachment the final pre-invoke
package/src/index.ts CHANGED
@@ -78,6 +78,11 @@ export type {
78
78
  ChatOpenRouterCallOptions,
79
79
  } from './llm/openrouter';
80
80
  export { getChatModelClass } from './llm/providers';
81
+ export { registerProvider } from './provider-registration';
82
+ export type {
83
+ ProviderFamily,
84
+ ProviderRegistrationOptions,
85
+ } from './provider-registration';
81
86
  export { CustomChatMistralAI } from './llm/mistral';
82
87
  export {
83
88
  smoothStream,
@@ -18,7 +18,7 @@
18
18
  * provider-space budget.
19
19
  */
20
20
  import type { ContextOverflowInfo } from '@/utils/errors';
21
- import type { Providers } from '@/common';
21
+ import type { ProviderName } from '@/types';
22
22
  import { getContextOverflowInfo } from '@/utils/errors';
23
23
 
24
24
  /** Fraction of the previous budget used when the provider named no ceiling. */
@@ -66,7 +66,7 @@ export interface OverflowRecoveryPlan {
66
66
 
67
67
  export interface OverflowRecoveryParams {
68
68
  error: unknown;
69
- provider: Providers;
69
+ provider: ProviderName;
70
70
  /** Budget in force when the rejected prompt was built. */
71
71
  maxContextTokens?: number;
72
72
  /** Our own estimate of the prompt we actually sent. */
@@ -35,7 +35,9 @@ interface ProviderPayloadMeasureOptions {
35
35
  }
36
36
 
37
37
  interface ProviderMessageBaseline {
38
- rawTokens: number;
38
+ message: BaseMessage;
39
+ /** Lazily tokenized: reading it costs a full count, so only read it on a changed projection. */
40
+ readonly rawTokens: number;
39
41
  accountingWeight: number;
40
42
  }
41
43
 
@@ -264,20 +266,27 @@ export function createContextPressureMeter({
264
266
  sourceIndices.set(sourceMessages[i], i);
265
267
  }
266
268
  baseline = retainedMessages.map((message, index) => {
267
- const rawTokens = count(message);
268
269
  const sourceIndex = sourceIndices.get(message);
269
270
  const indexedTokens =
270
271
  sourceIndex != null ? indexTokenCountMap[sourceIndex] : undefined;
271
- const accountingWeight =
272
+ const hasIndexedTokens =
272
273
  indexedTokens != null &&
273
274
  Number.isFinite(indexedTokens) &&
274
- indexedTokens >= 0
275
- ? indexedTokens
276
- : rawTokens;
275
+ indexedTokens >= 0;
276
+ const accountingWeight = hasIndexedTokens
277
+ ? indexedTokens
278
+ : count(message);
277
279
  if (!origins.has(message)) {
278
280
  origins.set(message, index);
279
281
  }
280
- return { rawTokens, accountingWeight };
282
+ return {
283
+ message,
284
+ /** Exact count is only needed as the subtrahend when a projection changed this message. */
285
+ get rawTokens(): number {
286
+ return count(message);
287
+ },
288
+ accountingWeight,
289
+ };
281
290
  });
282
291
  baselineWeights = {};
283
292
  for (let i = 0; i < baseline.length; i++) {
@@ -382,9 +391,7 @@ export function createContextPressureMeter({
382
391
  ) {
383
392
  let attribution = baselineAttributions.get(availableMessageTokens);
384
393
  if (attribution == null) {
385
- const replyPrimerTokens = Math.round(
386
- REPLY_PRIMER_TOKENS * usageRatio
387
- );
394
+ const replyPrimerTokens = Math.round(REPLY_PRIMER_TOKENS * usageRatio);
388
395
  const attributableTokens =
389
396
  totalBaselineWeight > 0
390
397
  ? Math.min(
@@ -415,17 +422,20 @@ export function createContextPressureMeter({
415
422
  let newRawTokens = 0;
416
423
  const usedOrigins = new Set<number>();
417
424
  for (const message of messages) {
418
- const rawTokens = count(message);
419
425
  const origin = origins.get(message);
420
426
  if (origin == null || usedOrigins.has(origin)) {
421
- newRawTokens += rawTokens;
427
+ newRawTokens += count(message);
422
428
  continue;
423
429
  }
424
430
  usedOrigins.add(origin);
431
+ const projectionDelta =
432
+ message === baseline[origin].message
433
+ ? 0
434
+ : count(message) - baseline[origin].rawTokens;
425
435
  projectedMessageTokens += Math.max(
426
436
  0,
427
437
  attribution.attributedByOrigin[origin] +
428
- Math.round((rawTokens - baseline[origin].rawTokens) * usageRatio)
438
+ Math.round(projectionDelta * usageRatio)
429
439
  );
430
440
  }
431
441
  projectedMessageTokens += Math.round(newRawTokens * usageRatio);
package/src/llm/init.ts CHANGED
@@ -6,29 +6,46 @@ import { getChatModelClass } from '@/llm/providers';
6
6
  import { isOpenAILike } from '@/utils';
7
7
  import { Providers } from '@/common';
8
8
 
9
+ type InitializeModelParams<P extends t.ProviderName> = {
10
+ provider: P;
11
+ tools?: t.GraphTools;
12
+ } & (
13
+ | {
14
+ override: t.ChatModelInstance;
15
+ clientOptions?: t.ProviderOptionsFor<P>;
16
+ }
17
+ | ([P] extends [keyof t.ProviderOptionsMap]
18
+ ? {
19
+ override?: t.ChatModelInstance;
20
+ clientOptions?: t.ProviderOptionsFor<P>;
21
+ }
22
+ : object extends t.ProviderOptionsFor<P>
23
+ ? {
24
+ override?: t.ChatModelInstance;
25
+ clientOptions?: t.ProviderOptionsFor<P>;
26
+ }
27
+ : {
28
+ override?: undefined;
29
+ clientOptions: t.ProviderOptionsFor<P>;
30
+ })
31
+ );
32
+
9
33
  /**
10
- * Creates a chat model instance for a given provider, applies provider-specific
11
- * field assignments, and optionally binds tools.
12
- *
13
- * This is the single entry point for model creation across the codebase — used
14
- * by both the agent graph (main LLM) and the summarization node (compaction LLM).
15
- * An optional `override` model can be passed to skip construction entirely
16
- * (useful for cached/reused model instances or test fakes).
34
+ * Creates a chat model instance for a given built-in or host-registered
35
+ * provider, applies provider-specific field assignments, and optionally binds
36
+ * tools.
17
37
  */
18
- export function initializeModel({
38
+ export function initializeModel<P extends t.ProviderName>({
19
39
  provider,
20
40
  clientOptions,
21
41
  tools,
22
42
  override,
23
- }: {
24
- provider: Providers;
25
- clientOptions?: t.ClientOptions;
26
- tools?: t.GraphTools;
27
- override?: t.ChatModelInstance;
28
- }): Runnable {
43
+ }: InitializeModelParams<P>): Runnable {
29
44
  const model =
30
45
  override ??
31
- new (getChatModelClass(provider))(clientOptions ?? ({} as never));
46
+ new (getChatModelClass(provider))(
47
+ (clientOptions ?? {}) as t.ProviderOptionsFor<P>
48
+ );
32
49
 
33
50
  if (
34
51
  isOpenAILike(provider) &&
@@ -56,8 +73,14 @@ export function initializeModel({
56
73
  }
57
74
 
58
75
  if (!tools || tools.length === 0) {
59
- return model as unknown as Runnable;
76
+ return model;
77
+ }
78
+
79
+ if (!('bindTools' in model) || typeof model.bindTools !== 'function') {
80
+ throw new TypeError(
81
+ `LLM provider does not support tool binding: ${provider}`
82
+ );
60
83
  }
61
84
 
62
- return (model as t.ModelWithTools).bindTools(tools);
85
+ return model.bindTools(tools);
63
86
  }