@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
@@ -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. */
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
  }
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;
@@ -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 !==