@librechat/agents 4.0.2 → 4.0.3

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 (156) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +3 -2
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/decisions/structuredChat.cjs +2 -8
  4. package/dist/cjs/decisions/structuredChat.cjs.map +1 -1
  5. package/dist/cjs/decisions/structuredOutput.cjs +18 -0
  6. package/dist/cjs/decisions/structuredOutput.cjs.map +1 -0
  7. package/dist/cjs/graphs/Graph.cjs +25 -9
  8. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  9. package/dist/cjs/llm/google/index.cjs +1 -1
  10. package/dist/cjs/llm/invoke.cjs +34 -12
  11. package/dist/cjs/llm/invoke.cjs.map +1 -1
  12. package/dist/cjs/llm/openai/index.cjs +7 -0
  13. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  14. package/dist/cjs/llm/providerTextBoundary.cjs +355 -0
  15. package/dist/cjs/llm/providerTextBoundary.cjs.map +1 -0
  16. package/dist/cjs/llm/providerTextChunk.cjs +14 -0
  17. package/dist/cjs/llm/providerTextChunk.cjs.map +1 -0
  18. package/dist/cjs/llm/providerTextControls.cjs +236 -0
  19. package/dist/cjs/llm/providerTextControls.cjs.map +1 -0
  20. package/dist/cjs/llm/stream/smoother.cjs +2 -1
  21. package/dist/cjs/llm/stream/smoother.cjs.map +1 -1
  22. package/dist/cjs/main.cjs +10 -1
  23. package/dist/cjs/protection/providerText.cjs +224 -0
  24. package/dist/cjs/protection/providerText.cjs.map +1 -0
  25. package/dist/cjs/protection/providerTextInput.cjs +25 -0
  26. package/dist/cjs/protection/providerTextInput.cjs.map +1 -0
  27. package/dist/cjs/run.cjs +7 -1
  28. package/dist/cjs/run.cjs.map +1 -1
  29. package/dist/cjs/stream.cjs +42 -14
  30. package/dist/cjs/stream.cjs.map +1 -1
  31. package/dist/cjs/summarization/node.cjs +26 -8
  32. package/dist/cjs/summarization/node.cjs.map +1 -1
  33. package/dist/cjs/tools/SubagentTool.cjs +11 -3
  34. package/dist/cjs/tools/SubagentTool.cjs.map +1 -1
  35. package/dist/cjs/tools/ToolNode.cjs +6 -5
  36. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  37. package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs +2 -2
  38. package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs.map +1 -1
  39. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +66 -16
  40. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  41. package/dist/cjs/tools/subagent/SubagentReplay.cjs +4 -2
  42. package/dist/cjs/tools/subagent/SubagentReplay.cjs.map +1 -1
  43. package/dist/cjs/tools/subagent/childGraphConfig.cjs +3 -1
  44. package/dist/cjs/tools/subagent/childGraphConfig.cjs.map +1 -1
  45. package/dist/cjs/tools/subagent/diagnostics.cjs +48 -5
  46. package/dist/cjs/tools/subagent/diagnostics.cjs.map +1 -1
  47. package/dist/cjs/tools/subagent/hostArgs.cjs +214 -0
  48. package/dist/cjs/tools/subagent/hostArgs.cjs.map +1 -0
  49. package/dist/cjs/tools/subagent/index.cjs +1 -0
  50. package/dist/cjs/tools/toolErrorContent.cjs +1 -1
  51. package/dist/esm/agents/AgentContext.mjs +3 -2
  52. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  53. package/dist/esm/decisions/structuredChat.mjs +2 -8
  54. package/dist/esm/decisions/structuredChat.mjs.map +1 -1
  55. package/dist/esm/decisions/structuredOutput.mjs +18 -0
  56. package/dist/esm/decisions/structuredOutput.mjs.map +1 -0
  57. package/dist/esm/graphs/Graph.mjs +25 -9
  58. package/dist/esm/graphs/Graph.mjs.map +1 -1
  59. package/dist/esm/llm/google/index.mjs +1 -1
  60. package/dist/esm/llm/invoke.mjs +34 -12
  61. package/dist/esm/llm/invoke.mjs.map +1 -1
  62. package/dist/esm/llm/openai/index.mjs +7 -0
  63. package/dist/esm/llm/openai/index.mjs.map +1 -1
  64. package/dist/esm/llm/providerTextBoundary.mjs +355 -0
  65. package/dist/esm/llm/providerTextBoundary.mjs.map +1 -0
  66. package/dist/esm/llm/providerTextChunk.mjs +13 -0
  67. package/dist/esm/llm/providerTextChunk.mjs.map +1 -0
  68. package/dist/esm/llm/providerTextControls.mjs +230 -0
  69. package/dist/esm/llm/providerTextControls.mjs.map +1 -0
  70. package/dist/esm/llm/stream/smoother.mjs +2 -1
  71. package/dist/esm/llm/stream/smoother.mjs.map +1 -1
  72. package/dist/esm/main.mjs +5 -3
  73. package/dist/esm/protection/providerText.mjs +218 -0
  74. package/dist/esm/protection/providerText.mjs.map +1 -0
  75. package/dist/esm/protection/providerTextInput.mjs +24 -0
  76. package/dist/esm/protection/providerTextInput.mjs.map +1 -0
  77. package/dist/esm/run.mjs +7 -1
  78. package/dist/esm/run.mjs.map +1 -1
  79. package/dist/esm/stream.mjs +42 -14
  80. package/dist/esm/stream.mjs.map +1 -1
  81. package/dist/esm/summarization/node.mjs +26 -8
  82. package/dist/esm/summarization/node.mjs.map +1 -1
  83. package/dist/esm/tools/SubagentTool.mjs +11 -3
  84. package/dist/esm/tools/SubagentTool.mjs.map +1 -1
  85. package/dist/esm/tools/ToolNode.mjs +6 -5
  86. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  87. package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs +2 -2
  88. package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs.map +1 -1
  89. package/dist/esm/tools/subagent/SubagentExecutor.mjs +66 -16
  90. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  91. package/dist/esm/tools/subagent/SubagentReplay.mjs +4 -2
  92. package/dist/esm/tools/subagent/SubagentReplay.mjs.map +1 -1
  93. package/dist/esm/tools/subagent/childGraphConfig.mjs +3 -1
  94. package/dist/esm/tools/subagent/childGraphConfig.mjs.map +1 -1
  95. package/dist/esm/tools/subagent/diagnostics.mjs +46 -6
  96. package/dist/esm/tools/subagent/diagnostics.mjs.map +1 -1
  97. package/dist/esm/tools/subagent/hostArgs.mjs +207 -0
  98. package/dist/esm/tools/subagent/hostArgs.mjs.map +1 -0
  99. package/dist/esm/tools/subagent/index.mjs +1 -0
  100. package/dist/esm/tools/toolErrorContent.mjs +1 -1
  101. package/dist/types/decisions/structuredChat.d.ts +3 -2
  102. package/dist/types/decisions/structuredOutput.d.ts +4 -0
  103. package/dist/types/graphs/Graph.d.ts +6 -3
  104. package/dist/types/index.d.ts +2 -0
  105. package/dist/types/llm/invoke.d.ts +4 -1
  106. package/dist/types/llm/providerTextBoundary.d.ts +4 -0
  107. package/dist/types/llm/providerTextChunk.d.ts +7 -0
  108. package/dist/types/llm/providerTextControls.d.ts +18 -0
  109. package/dist/types/protection/providerText.d.ts +77 -0
  110. package/dist/types/protection/providerTextInput.d.ts +3 -0
  111. package/dist/types/run.d.ts +1 -0
  112. package/dist/types/tools/BashExecutor.d.ts +4 -0
  113. package/dist/types/tools/CodeExecutor.d.ts +4 -0
  114. package/dist/types/tools/ReadFile.d.ts +4 -0
  115. package/dist/types/tools/SkillTool.d.ts +4 -0
  116. package/dist/types/tools/SubagentTool.d.ts +2 -0
  117. package/dist/types/tools/ToolSearch.d.ts +4 -0
  118. package/dist/types/tools/search/schema.d.ts +4 -0
  119. package/dist/types/tools/subagent/SubagentExecutionRegistry.d.ts +2 -0
  120. package/dist/types/tools/subagent/SubagentExecutor.d.ts +10 -2
  121. package/dist/types/tools/subagent/SubagentReplay.d.ts +3 -1
  122. package/dist/types/tools/subagent/diagnostics.d.ts +27 -3
  123. package/dist/types/tools/subagent/hostArgs.d.ts +57 -0
  124. package/dist/types/tools/subagent/index.d.ts +3 -2
  125. package/dist/types/types/graph.d.ts +36 -0
  126. package/dist/types/types/run.d.ts +4 -2
  127. package/dist/types/types/tools.d.ts +2 -0
  128. package/package.json +4 -4
  129. package/src/agents/AgentContext.ts +3 -2
  130. package/src/decisions/structuredChat.ts +9 -15
  131. package/src/decisions/structuredOutput.ts +25 -0
  132. package/src/graphs/Graph.ts +53 -9
  133. package/src/index.ts +7 -0
  134. package/src/llm/invoke.ts +48 -7
  135. package/src/llm/openai/index.ts +8 -0
  136. package/src/llm/providerTextBoundary.ts +446 -0
  137. package/src/llm/providerTextChunk.ts +15 -0
  138. package/src/llm/providerTextControls.ts +173 -0
  139. package/src/llm/stream/smoother.ts +4 -2
  140. package/src/protection/providerText.ts +325 -0
  141. package/src/protection/providerTextInput.ts +21 -0
  142. package/src/run.ts +9 -0
  143. package/src/stream.ts +33 -7
  144. package/src/summarization/node.ts +32 -14
  145. package/src/tools/SubagentTool.ts +14 -3
  146. package/src/tools/ToolNode.ts +7 -3
  147. package/src/tools/subagent/SubagentExecutionRegistry.ts +6 -2
  148. package/src/tools/subagent/SubagentExecutor.ts +145 -18
  149. package/src/tools/subagent/SubagentReplay.ts +10 -3
  150. package/src/tools/subagent/childGraphConfig.ts +12 -1
  151. package/src/tools/subagent/diagnostics.ts +98 -6
  152. package/src/tools/subagent/hostArgs.ts +413 -0
  153. package/src/tools/subagent/index.ts +9 -0
  154. package/src/types/graph.ts +41 -0
  155. package/src/types/run.ts +4 -2
  156. package/src/types/tools.ts +6 -1
@@ -0,0 +1,25 @@
1
+ import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
2
+
3
+ export type DecisionOutputMethod = 'jsonSchema' | 'functionCalling';
4
+
5
+ /** Binds only verified enforcement paths; a model profile alone is not a strict-output guarantee. */
6
+ export function withDecisionStructuredOutput(
7
+ model: BaseChatModel,
8
+ schema: Record<string, unknown>,
9
+ method: DecisionOutputMethod
10
+ ): ReturnType<BaseChatModel['withStructuredOutput']> {
11
+ const options = {
12
+ name: 'DecideQuestions',
13
+ method,
14
+ includeRaw: true as const,
15
+ };
16
+ const adapter = model._llmType();
17
+ if (
18
+ adapter === 'openai' ||
19
+ adapter === 'azure_openai' ||
20
+ (adapter === 'anthropic' && method === 'functionCalling')
21
+ ) {
22
+ return model.withStructuredOutput(schema, { ...options, strict: true });
23
+ }
24
+ throw new Error('unverified strict decision model adapter');
25
+ }
@@ -131,6 +131,10 @@ import {
131
131
  resolveLangfuseConfig,
132
132
  resolveToolOutputTracingConfig,
133
133
  } from '@/langfuseConfig';
134
+ import {
135
+ ProviderTextProtectionError,
136
+ validateProviderTextProtection,
137
+ } from '@/protection/providerText';
134
138
  import {
135
139
  annotateMessagesForLLM,
136
140
  ToolOutputReferenceRegistry,
@@ -139,6 +143,10 @@ import {
139
143
  InvalidModelToolCallError,
140
144
  snapshotAcceptedModelResponse,
141
145
  } from './acceptedModelResponse';
146
+ import {
147
+ collectSubagentHostArgNames,
148
+ pickSubagentHostArgInput,
149
+ } from '@/tools/subagent/hostArgs';
142
150
  import {
143
151
  prepareProviderRequest,
144
152
  usesNativeOpenAIResponses,
@@ -1378,6 +1386,8 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1378
1386
  * these on every streamed chunk event.
1379
1387
  */
1380
1388
  streamLimits: ResolvedStreamLimits;
1389
+ /** Default-off mandatory provider prose release. */
1390
+ providerTextProtection?: t.StandardGraphInput['providerTextProtection'];
1381
1391
  /**
1382
1392
  * Cumulative streamed argument bytes per in-flight tool call, keyed by
1383
1393
  * generation key + chunk index (see `resolveGenerationKey`). Per-run
@@ -1423,25 +1433,31 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1423
1433
  * attempt lease; spans resets on purpose. */
1424
1434
  activeStreamLimitGenerations?: Set<string>;
1425
1435
 
1426
- /** The stream-limit error behind an already-fired breaker, whether this
1436
+ /** The safety error behind an already-fired breaker, whether this
1427
1437
  * graph's own controller tripped or a parent run's breaker arrived through
1428
1438
  * the composed constructor signal (child graphs own separate controllers).
1429
1439
  * Providers can translate either abort into a generic error, and recovery
1430
1440
  * paths must not run in that state. */
1431
1441
  protected resolveTrippedBreakerReason(
1432
1442
  breakerSignal: AbortSignal = this.breakerAbort.signal
1433
- ): StreamLimitExceededError | PreparedSubagentError | undefined {
1443
+ ):
1444
+ | StreamLimitExceededError
1445
+ | PreparedSubagentError
1446
+ | ProviderTextProtectionError
1447
+ | undefined {
1434
1448
  if (
1435
1449
  breakerSignal.aborted &&
1436
1450
  (breakerSignal.reason instanceof StreamLimitExceededError ||
1437
- breakerSignal.reason instanceof PreparedSubagentError)
1451
+ breakerSignal.reason instanceof PreparedSubagentError ||
1452
+ breakerSignal.reason instanceof ProviderTextProtectionError)
1438
1453
  ) {
1439
1454
  return breakerSignal.reason;
1440
1455
  }
1441
1456
  if (
1442
1457
  this.signal?.aborted === true &&
1443
1458
  (this.signal.reason instanceof StreamLimitExceededError ||
1444
- this.signal.reason instanceof PreparedSubagentError)
1459
+ this.signal.reason instanceof PreparedSubagentError ||
1460
+ this.signal.reason instanceof ProviderTextProtectionError)
1445
1461
  ) {
1446
1462
  return this.signal.reason;
1447
1463
  }
@@ -1551,6 +1567,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1551
1567
  onSubagentResolutionFailure,
1552
1568
  preemption,
1553
1569
  streamLimits,
1570
+ providerTextProtection,
1554
1571
  toolExecution,
1555
1572
  clientDelegatedToolNames,
1556
1573
  }: t.StandardGraphInput,
@@ -1580,8 +1597,14 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1580
1597
  this.onSubagentResolutionFailure = onSubagentResolutionFailure;
1581
1598
  this.preemption = preemption;
1582
1599
  this.streamLimits = resolveStreamLimits(streamLimits);
1600
+ if (providerTextProtection != null)
1601
+ validateProviderTextProtection(providerTextProtection);
1602
+ this.providerTextProtection = providerTextProtection;
1583
1603
  this.toolExecution = toolExecution;
1584
- if (clientDelegatedToolNames != null && clientDelegatedToolNames.length > 0) {
1604
+ if (
1605
+ clientDelegatedToolNames != null &&
1606
+ clientDelegatedToolNames.length > 0
1607
+ ) {
1585
1608
  if (agents.length !== 1) {
1586
1609
  throw new Error('Client tool delegation requires a single-agent graph');
1587
1610
  }
@@ -4404,7 +4427,12 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
4404
4427
  * succeeding fallback would resolve a run the public contract says
4405
4428
  * must reject. Rethrow before any recovery path.
4406
4429
  */
4407
- if (primaryError instanceof InvalidModelToolCallError) {
4430
+ if (
4431
+ primaryError instanceof InvalidModelToolCallError ||
4432
+ primaryError instanceof ProviderTextProtectionError
4433
+ ) {
4434
+ if (primaryError instanceof ProviderTextProtectionError)
4435
+ attemptBreaker.abort(primaryError);
4408
4436
  throw primaryError;
4409
4437
  }
4410
4438
  if (
@@ -4747,7 +4775,12 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
4747
4775
  })
4748
4776
  );
4749
4777
  } catch (fallbackError) {
4750
- if (fallbackError instanceof InvalidModelToolCallError) {
4778
+ if (
4779
+ fallbackError instanceof InvalidModelToolCallError ||
4780
+ fallbackError instanceof ProviderTextProtectionError
4781
+ ) {
4782
+ if (fallbackError instanceof ProviderTextProtectionError)
4783
+ attemptBreaker.abort(fallbackError);
4751
4784
  throw fallbackError;
4752
4785
  }
4753
4786
  if (
@@ -5347,6 +5380,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
5347
5380
  subagentContext: this.subagentContext,
5348
5381
  onResolutionFailure: this.onSubagentResolutionFailure,
5349
5382
  streamLimits: this.streamLimits,
5383
+ providerTextProtection: this.providerTextProtection,
5350
5384
  humanInTheLoop: this.humanInTheLoop,
5351
5385
  checkpointer: this.compileOptions?.checkpointer,
5352
5386
  maxDepth: effectiveSubagentDepth,
@@ -5361,6 +5395,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
5361
5395
  ): GraphFactory => snapshotChildGraphFactory(parentHandlerRegistry),
5362
5396
  });
5363
5397
  this.registerSubagentExecutor(executor);
5398
+ const hostArgNames = collectSubagentHostArgNames(executableConfigs);
5364
5399
 
5365
5400
  const subagentTool = tool(
5366
5401
  async (rawInput, config) => {
@@ -5370,6 +5405,10 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
5370
5405
  subagent_thread_id?: string;
5371
5406
  run_in_background?: boolean;
5372
5407
  };
5408
+ const hostArgInput = pickSubagentHostArgInput(input, hostArgNames);
5409
+ if (!hostArgInput.ok) {
5410
+ return hostArgInput.message;
5411
+ }
5373
5412
  const description =
5374
5413
  typeof input.description === 'string' &&
5375
5414
  input.description.trim().length > 0
@@ -5426,6 +5465,9 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
5426
5465
  parentConfigurable: config.configurable as
5427
5466
  | Record<string, unknown>
5428
5467
  | undefined,
5468
+ ...(hostArgInput.hostArgs == null
5469
+ ? {}
5470
+ : { hostArgs: hostArgInput.hostArgs }),
5429
5471
  };
5430
5472
  if (input.run_in_background === true) {
5431
5473
  return executor.executeInBackground({
@@ -5556,8 +5598,10 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
5556
5598
  const delegatedNames = this.clientDelegatedToolNames;
5557
5599
  if (delegatedNames != null && delegatedNames.size > 0) {
5558
5600
  const { messages } = state as t.BaseGraphState;
5559
- const last = messages[messages.length - 1] as AIMessageChunk | undefined;
5560
- const calls = last?.getType() === 'ai' ? last.tool_calls ?? [] : [];
5601
+ const last = messages[messages.length - 1] as
5602
+ | AIMessageChunk
5603
+ | undefined;
5604
+ const calls = last?.getType() === 'ai' ? (last.tool_calls ?? []) : [];
5561
5605
  if (calls.some((call) => delegatedNames.has(call.name))) {
5562
5606
  if (
5563
5607
  calls.some(
package/src/index.ts CHANGED
@@ -121,3 +121,10 @@ export type {
121
121
  } from './llm/streamLimits';
122
122
  export * from './decisions';
123
123
  export * from './rerank';
124
+
125
+ export { PROVIDER_TEXT_PROTECTION_VERSION, ProviderTextProtectionError } from './protection/providerText';
126
+ export type {
127
+ ProviderTextProtection,
128
+ ProviderTextProtectionResult,
129
+ ProviderTextProtectionErrorCode,
130
+ } from './protection/providerText';
package/src/llm/invoke.ts CHANGED
@@ -14,6 +14,7 @@ import type { ToolCall } from '@langchain/core/messages/tool';
14
14
  import type { BaseMessage } from '@langchain/core/messages';
15
15
  import type { ToolOutputReferenceRegistry } from '@/tools/toolOutputReferences';
16
16
  import type { PreparedProviderRequest } from '@/llm/prepareProviderRequest';
17
+ import type { ProviderTextProtection } from '@/protection/providerText';
17
18
  import type { ContextOverflowContext } from '@/utils/errors';
18
19
  import type { StreamLimitState } from '@/llm/streamLimits';
19
20
  import type { PreemptAction } from '@/llm/preempt';
@@ -28,6 +29,12 @@ import {
28
29
  STREAM_LIMIT_REDISPATCH_KEY,
29
30
  STREAM_LIMIT_ATTEMPT_KEY,
30
31
  } from '@/llm/streamLimits';
32
+ import {
33
+ validateProviderTextProtection,
34
+ ProviderTextProtectionError,
35
+ createProviderTextRestartCancellation,
36
+ isProviderTextRestartCancellation,
37
+ } from '@/protection/providerText';
31
38
  import {
32
39
  inspectProviderMessageProjection,
33
40
  ProviderMessageProjectionInvariantError,
@@ -56,6 +63,7 @@ import {
56
63
  } from '@/llm/providers';
57
64
  import { ChatModelStreamHandler, dispatchesChatModelStream } from '@/stream';
58
65
  import { Constants, ContentTypes, GraphEvents, Providers } from '@/common';
66
+ import { withProviderTextBoundary } from '@/llm/providerTextBoundary';
59
67
  import { PreparedSubagentError } from '@/tools/preparedSubagents';
60
68
  import { assertNotTruncatedToolCall } from '@/llm/truncation';
61
69
  import { resolveClientOptionsModel } from '@/llm/request';
@@ -623,6 +631,7 @@ function appendStreamChunk({
623
631
  */
624
632
  interface AttemptInvokeCommonParams {
625
633
  context?: InvokeContext;
634
+ providerTextProtection?: ProviderTextProtection;
626
635
  onChunk?: OnChunk;
627
636
  /**
628
637
  * The agent lane this attempt belongs to, as the model node names it. Only
@@ -697,6 +706,9 @@ export async function attemptInvoke(
697
706
  params: AttemptInvokeParams,
698
707
  config?: RunnableConfig
699
708
  ): Promise<Partial<t.BaseGraphState>> {
709
+ const policy =
710
+ params.providerTextProtection ?? params.context?.providerTextProtection;
711
+ if (policy != null) validateProviderTextProtection(policy);
700
712
  const provider = resolveAttemptProvider(params);
701
713
  const providerStampedConfig: RunnableConfig = {
702
714
  ...config,
@@ -707,7 +719,14 @@ export async function attemptInvoke(
707
719
  resolvedProvider: provider,
708
720
  },
709
721
  };
710
- const request = resolveAttemptRequest(params, providerStampedConfig);
722
+ const preparedRequest = resolveAttemptRequest(params, providerStampedConfig);
723
+ const request =
724
+ policy == null
725
+ ? preparedRequest
726
+ : {
727
+ ...preparedRequest,
728
+ model: withProviderTextBoundary(preparedRequest.model, policy, provider),
729
+ };
711
730
  const configuredModel =
712
731
  providerStampedConfig.metadata?.[Constants.INVOKED_MODEL];
713
732
  const modelId =
@@ -763,6 +782,7 @@ export async function attemptInvoke(
763
782
  context: params.context,
764
783
  onChunk: params.onChunk,
765
784
  preemptAgentId: params.preemptAgentId,
785
+ providerTextProtection: policy,
766
786
  },
767
787
  stampedConfig
768
788
  );
@@ -775,6 +795,16 @@ export async function attemptInvoke(
775
795
  prepared?.finish(preparedAttempt, calls);
776
796
  return result;
777
797
  } catch (error) {
798
+ if (policy != null && config?.signal?.aborted === true) {
799
+ const reason: unknown = config.signal.reason;
800
+ if (reason instanceof StreamLimitExceededError || reason instanceof PreparedSubagentError) {
801
+ prepared?.finish(preparedAttempt, undefined, reason);
802
+ throw reason;
803
+ }
804
+ const cancelled = new ProviderTextProtectionError(reason instanceof ProviderTextProtectionError ? reason.code : 'cancelled');
805
+ prepared?.finish(preparedAttempt, undefined, cancelled);
806
+ throw cancelled;
807
+ }
778
808
  prepared?.finish(preparedAttempt, undefined, error);
779
809
  throw error;
780
810
  } finally {
@@ -799,9 +829,10 @@ async function attemptInvokeBody(
799
829
  context,
800
830
  onChunk,
801
831
  preemptAgentId,
832
+ providerTextProtection,
802
833
  }: Pick<
803
834
  AttemptInvokeCommonParams,
804
- 'context' | 'onChunk' | 'preemptAgentId'
835
+ 'context' | 'onChunk' | 'preemptAgentId' | 'providerTextProtection'
805
836
  > & {
806
837
  request: PreparedProviderRequest;
807
838
  },
@@ -1068,7 +1099,7 @@ async function attemptInvokeBody(
1068
1099
  notePreemptRestartedRun(sealedRunId, finalChunk);
1069
1100
  }
1070
1101
  restartRoute = 'aborted';
1071
- restartController.abort();
1102
+ restartController.abort(providerTextProtection == null ? undefined : createProviderTextRestartCancellation());
1072
1103
  return true;
1073
1104
  };
1074
1105
  const unsubscribeWake =
@@ -1085,7 +1116,8 @@ async function attemptInvokeBody(
1085
1116
  if (
1086
1117
  signal?.aborted === true &&
1087
1118
  (signal.reason instanceof StreamLimitExceededError ||
1088
- signal.reason instanceof PreparedSubagentError)
1119
+ signal.reason instanceof PreparedSubagentError ||
1120
+ signal.reason instanceof ProviderTextProtectionError)
1089
1121
  ) {
1090
1122
  throw signal.reason;
1091
1123
  }
@@ -1334,6 +1366,7 @@ async function attemptInvokeBody(
1334
1366
  restartRoute === 'aborted' &&
1335
1367
  !(error instanceof StreamLimitExceededError) &&
1336
1368
  !(error instanceof PreparedSubagentError) &&
1369
+ (!(error instanceof ProviderTextProtectionError) || isProviderTextRestartCancellation(error)) &&
1337
1370
  config.signal?.aborted !== true;
1338
1371
  if (!ownAbort) {
1339
1372
  throw error;
@@ -1561,6 +1594,7 @@ export async function tryFallbackProviders({
1561
1594
  context,
1562
1595
  onChunk,
1563
1596
  streamLimitState,
1597
+ providerTextProtection,
1564
1598
  preemptAgentId,
1565
1599
  overflowContext,
1566
1600
  prepareProviderRequest: prepareFallbackRequest,
@@ -1576,6 +1610,7 @@ export async function tryFallbackProviders({
1576
1610
  /** Accounting-lease owner forwarded to each fallback attempt (see
1577
1611
  * `AttemptInvokeParams.streamLimitState`). */
1578
1612
  streamLimitState?: StreamLimitState;
1613
+ providerTextProtection?: ProviderTextProtection;
1579
1614
  /** Forwarded so a fallback-served attempt records a discarded turn in the
1580
1615
  * SAME lane the primary would have (see
1581
1616
  * `AttemptInvokeParams.preemptAgentId`). Dropping it here would leave the
@@ -1610,6 +1645,7 @@ export async function tryFallbackProviders({
1610
1645
  config?: RunnableConfig;
1611
1646
  }) => BaseMessage[] | Promise<BaseMessage[]>;
1612
1647
  }): Promise<Partial<t.BaseGraphState> | undefined> {
1648
+ if (primaryError instanceof ProviderTextProtectionError) throw primaryError;
1613
1649
  const isOverflow = (
1614
1650
  error: unknown,
1615
1651
  contextOverride = overflowContext
@@ -1686,7 +1722,8 @@ export async function tryFallbackProviders({
1686
1722
  if (
1687
1723
  config?.signal?.aborted === true &&
1688
1724
  (config.signal.reason instanceof StreamLimitExceededError ||
1689
- config.signal.reason instanceof PreparedSubagentError)
1725
+ config.signal.reason instanceof PreparedSubagentError ||
1726
+ config.signal.reason instanceof ProviderTextProtectionError)
1690
1727
  ) {
1691
1728
  throw config.signal.reason;
1692
1729
  }
@@ -1697,6 +1734,7 @@ export async function tryFallbackProviders({
1697
1734
  context,
1698
1735
  onChunk,
1699
1736
  streamLimitState,
1737
+ providerTextProtection,
1700
1738
  preemptAgentId,
1701
1739
  },
1702
1740
  fbConfig
@@ -1710,6 +1748,7 @@ export async function tryFallbackProviders({
1710
1748
  context,
1711
1749
  onChunk,
1712
1750
  streamLimitState,
1751
+ providerTextProtection,
1713
1752
  preemptAgentId,
1714
1753
  },
1715
1754
  fbConfig
@@ -1723,7 +1762,8 @@ export async function tryFallbackProviders({
1723
1762
  if (
1724
1763
  e instanceof StreamLimitExceededError ||
1725
1764
  e instanceof PreparedSubagentError ||
1726
- e instanceof InvalidModelToolCallError
1765
+ e instanceof InvalidModelToolCallError ||
1766
+ e instanceof ProviderTextProtectionError
1727
1767
  ) {
1728
1768
  throw e;
1729
1769
  }
@@ -1734,7 +1774,8 @@ export async function tryFallbackProviders({
1734
1774
  if (
1735
1775
  config?.signal?.aborted === true &&
1736
1776
  (config.signal.reason instanceof StreamLimitExceededError ||
1737
- config.signal.reason instanceof PreparedSubagentError)
1777
+ config.signal.reason instanceof PreparedSubagentError ||
1778
+ config.signal.reason instanceof ProviderTextProtectionError)
1738
1779
  ) {
1739
1780
  throw config.signal.reason;
1740
1781
  }
@@ -63,6 +63,7 @@ import {
63
63
  hasToolCallChunks,
64
64
  getReasoningKwargsText,
65
65
  } from '@/llm/stream/chunkAdapters';
66
+ import { registerProviderTextChunkConstructor } from '@/llm/providerTextChunk';
66
67
  import { isReasoningModel, _convertMessagesToOpenAIParams } from './utils';
67
68
  import { smoothStream, resolveStreamDelay } from '@/llm/stream/smoother';
68
69
  import { INTENT_ARG, isIntentLabelProperty } from '@/tools/intentArg';
@@ -855,6 +856,8 @@ class ResponsesReplayAIMessageChunk extends AIMessageChunk {
855
856
  }
856
857
  }
857
858
 
859
+ registerProviderTextChunkConstructor(ResponsesReplayAIMessageChunk);
860
+
858
861
  function makeResponsesReplayAggregationSafe(
859
862
  chunk: ChatGenerationChunk
860
863
  ): ChatGenerationChunk {
@@ -2923,6 +2926,11 @@ export class AzureChatOpenAI extends OriginalAzureChatOpenAI {
2923
2926
  super(fields);
2924
2927
  this.completions = new LibreChatAzureOpenAICompletions(fields);
2925
2928
  this.responses = new LibreChatAzureOpenAIResponses(fields);
2929
+ this.fields = {
2930
+ ...this.fields,
2931
+ completions: this.completions,
2932
+ responses: this.responses,
2933
+ };
2926
2934
  this._lc_stream_delay = resolveStreamDelay(fields?._lc_stream_delay);
2927
2935
  this.firstPartyEndpoint = fields?.firstPartyEndpoint;
2928
2936
  }