@librechat/agents 3.6.10 → 3.6.12

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 (64) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +63 -10
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/graphs/Graph.cjs +48 -119
  4. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  5. package/dist/cjs/llm/contextPressureMeter.cjs +132 -0
  6. package/dist/cjs/llm/contextPressureMeter.cjs.map +1 -0
  7. package/dist/cjs/llm/invoke.cjs +46 -75
  8. package/dist/cjs/llm/invoke.cjs.map +1 -1
  9. package/dist/cjs/llm/prepareProviderRequest.cjs +108 -0
  10. package/dist/cjs/llm/prepareProviderRequest.cjs.map +1 -0
  11. package/dist/cjs/main.cjs +4 -0
  12. package/dist/cjs/messages/content.cjs +5 -4
  13. package/dist/cjs/messages/content.cjs.map +1 -1
  14. package/dist/cjs/stream.cjs +1 -0
  15. package/dist/cjs/stream.cjs.map +1 -1
  16. package/dist/cjs/tools/CallerCapabilities.cjs +31 -0
  17. package/dist/cjs/tools/CallerCapabilities.cjs.map +1 -1
  18. package/dist/cjs/tools/ToolNode.cjs +23 -6
  19. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  20. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs +26 -2
  21. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs.map +1 -1
  22. package/dist/esm/agents/AgentContext.mjs +65 -12
  23. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  24. package/dist/esm/graphs/Graph.mjs +51 -122
  25. package/dist/esm/graphs/Graph.mjs.map +1 -1
  26. package/dist/esm/llm/contextPressureMeter.mjs +132 -0
  27. package/dist/esm/llm/contextPressureMeter.mjs.map +1 -0
  28. package/dist/esm/llm/invoke.mjs +48 -75
  29. package/dist/esm/llm/invoke.mjs.map +1 -1
  30. package/dist/esm/llm/prepareProviderRequest.mjs +105 -0
  31. package/dist/esm/llm/prepareProviderRequest.mjs.map +1 -0
  32. package/dist/esm/main.mjs +3 -2
  33. package/dist/esm/messages/content.mjs +5 -4
  34. package/dist/esm/messages/content.mjs.map +1 -1
  35. package/dist/esm/stream.mjs +1 -0
  36. package/dist/esm/stream.mjs.map +1 -1
  37. package/dist/esm/tools/CallerCapabilities.mjs +29 -1
  38. package/dist/esm/tools/CallerCapabilities.mjs.map +1 -1
  39. package/dist/esm/tools/ToolNode.mjs +24 -7
  40. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  41. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs +26 -4
  42. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs.map +1 -1
  43. package/dist/types/agents/AgentContext.d.ts +11 -0
  44. package/dist/types/index.d.ts +2 -0
  45. package/dist/types/llm/contextPressureMeter.d.ts +30 -0
  46. package/dist/types/llm/invoke.d.ts +31 -33
  47. package/dist/types/llm/prepareProviderRequest.d.ts +55 -0
  48. package/dist/types/tools/CallerCapabilities.d.ts +13 -0
  49. package/dist/types/tools/ToolNode.d.ts +9 -1
  50. package/dist/types/tools/local/resolveLocalExecutionTools.d.ts +6 -0
  51. package/dist/types/types/tools.d.ts +17 -0
  52. package/package.json +1 -1
  53. package/src/agents/AgentContext.ts +157 -15
  54. package/src/graphs/Graph.ts +75 -279
  55. package/src/index.ts +8 -0
  56. package/src/llm/contextPressureMeter.ts +284 -0
  57. package/src/llm/invoke.ts +136 -296
  58. package/src/llm/prepareProviderRequest.ts +357 -0
  59. package/src/messages/content.ts +3 -3
  60. package/src/stream.ts +6 -0
  61. package/src/tools/CallerCapabilities.ts +68 -0
  62. package/src/tools/ToolNode.ts +47 -4
  63. package/src/tools/local/resolveLocalExecutionTools.ts +77 -3
  64. package/src/types/tools.ts +18 -0
@@ -50,8 +50,8 @@ import {
50
50
  addBedrockTailCacheControl,
51
51
  projectArtifactPayload,
52
52
  formatContentStrings,
53
+ cloneMessage,
53
54
  CALIBRATION_RATIO_MAX,
54
- REPLY_PRIMER_TOKENS,
55
55
  createPruneMessages,
56
56
  projectToolCallInputs,
57
57
  calculateMaxToolCallInputChars,
@@ -74,7 +74,6 @@ import {
74
74
  coalesceAdjacentUserTurns,
75
75
  strictAlternationProviders,
76
76
  appendPredecessorHandoffCue,
77
- removePredecessorHandoffCue,
78
77
  stampSyntheticProviderMessage,
79
78
  } from '@/messages';
80
79
  import {
@@ -104,9 +103,9 @@ import {
104
103
  tryFallbackProviders,
105
104
  getFallbackErrorContext,
106
105
  getFallbackOverflowCandidates,
107
- projectMessagesForProvider,
108
- resolveServingModelId,
109
106
  } from '@/llm/invoke';
107
+ import { prepareProviderRequest } from '@/llm/prepareProviderRequest';
108
+ import { createContextPressureMeter } from '@/llm/contextPressureMeter';
110
109
  import {
111
110
  resolveStreamLimits,
112
111
  StreamLimitExceededError,
@@ -2868,6 +2867,9 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
2868
2867
  * the breakpoint and don't invalidate the prefix.
2869
2868
  */
2870
2869
  let toolsForBinding = rawToolsForBinding;
2870
+ const isDeferredTool = makeIsDeferred(
2871
+ agentContext.getEffectiveToolDefinitions()
2872
+ );
2871
2873
  if (
2872
2874
  agentContext.provider === Providers.ANTHROPIC &&
2873
2875
  (agentContext.clientOptions as t.AnthropicClientOptions | undefined)
@@ -2876,7 +2878,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
2876
2878
  toolsForBinding =
2877
2879
  partitionAndMarkAnthropicToolCache(
2878
2880
  rawToolsForBinding,
2879
- makeIsDeferred(agentContext.toolDefinitions),
2881
+ isDeferredTool,
2880
2882
  resolvePromptCacheTtl(
2881
2883
  (
2882
2884
  agentContext.clientOptions as
@@ -2896,7 +2898,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
2896
2898
  toolsForBinding =
2897
2899
  partitionAndMarkOpenRouterToolCache(
2898
2900
  rawToolsForBinding,
2899
- makeIsDeferred(agentContext.toolDefinitions),
2901
+ isDeferredTool,
2900
2902
  resolvePromptCacheTtl(
2901
2903
  (
2902
2904
  agentContext.clientOptions as
@@ -2923,7 +2925,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
2923
2925
  toolsForBinding =
2924
2926
  partitionAndMarkBedrockToolCache(
2925
2927
  rawToolsForBinding,
2926
- makeIsDeferred(agentContext.toolDefinitions)
2928
+ isDeferredTool
2927
2929
  ) ?? rawToolsForBinding;
2928
2930
  }
2929
2931
  }
@@ -3151,105 +3153,18 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
3151
3153
  * baseline, then attribute it across retained messages. Provider
3152
3154
  * transforms can shrink one message while expanding or adding another;
3153
3155
  * per-origin accounting prevents that unrelated shrink from canceling
3154
- * the expansion. Raw counts are frozen before in-place formatters run.
3155
- */
3156
- let providerMessageBaseline:
3157
- | Array<{ rawTokens: number; accountingWeight: number }>
3158
- | undefined;
3159
- const providerMessageOrigins = new WeakMap<BaseMessage, number>();
3160
- if (contextUsage != null && agentContext.tokenCounter != null) {
3161
- const sourceIndices = new WeakMap<BaseMessage, number>();
3162
- for (let i = 0; i < messages.length; i++) {
3163
- sourceIndices.set(messages[i], i);
3164
- }
3165
- providerMessageBaseline = messagesToUse.map((message, index) => {
3166
- const rawTokens = agentContext.tokenCounter!(message);
3167
- const sourceIndex = sourceIndices.get(message);
3168
- const indexedTokens =
3169
- sourceIndex != null
3170
- ? agentContext.indexTokenCountMap[sourceIndex]
3171
- : undefined;
3172
- const accountingWeight =
3173
- indexedTokens != null &&
3174
- Number.isFinite(indexedTokens) &&
3175
- indexedTokens >= 0
3176
- ? indexedTokens
3177
- : rawTokens;
3178
- if (!providerMessageOrigins.has(message)) {
3179
- providerMessageOrigins.set(message, index);
3180
- }
3181
- return { rawTokens, accountingWeight };
3182
- });
3183
- }
3184
-
3185
- const getProviderMessageOriginKey = (
3186
- message: BaseMessage
3187
- ): string | undefined => {
3188
- const type = message.getType();
3189
- if (
3190
- message instanceof ToolMessage &&
3191
- typeof message.tool_call_id === 'string' &&
3192
- message.tool_call_id.length > 0
3193
- ) {
3194
- return `tool:call:${message.tool_call_id}`;
3195
- }
3196
- if (typeof message.id === 'string' && message.id.length > 0) {
3197
- return `${type}:id:${message.id}`;
3198
- }
3199
- return undefined;
3200
- };
3201
-
3202
- /**
3203
- * Provider projections clone messages. Preserve their baseline origin
3204
- * without writing tracking metadata onto the wire. Synthetic fold
3205
- * messages intentionally remain unattributed and are charged in full.
3156
+ * the expansion. Exact counts are memoized across repeated projections.
3206
3157
  */
3207
- const trackProviderMessageOrigins = (
3208
- before: BaseMessage[],
3209
- after: BaseMessage[]
3210
- ): BaseMessage[] => {
3211
- if (providerMessageBaseline == null || before === after) {
3212
- return after;
3213
- }
3214
- if (before.length === after.length) {
3215
- for (let i = 0; i < after.length; i++) {
3216
- const origin = providerMessageOrigins.get(before[i]);
3217
- if (
3218
- origin != null &&
3219
- !providerMessageOrigins.has(after[i]) &&
3220
- before[i].getType() === after[i].getType() &&
3221
- !isSyntheticProviderContextMessage(after[i])
3222
- ) {
3223
- providerMessageOrigins.set(after[i], origin);
3224
- }
3225
- }
3226
- return after;
3227
- }
3228
-
3229
- const keyedOrigins = new Map<string, number | null>();
3230
- for (const message of before) {
3231
- const origin = providerMessageOrigins.get(message);
3232
- const key = getProviderMessageOriginKey(message);
3233
- if (origin == null || key == null) {
3234
- continue;
3235
- }
3236
- keyedOrigins.set(key, keyedOrigins.has(key) ? null : origin);
3237
- }
3238
- for (const message of after) {
3239
- if (
3240
- providerMessageOrigins.has(message) ||
3241
- isSyntheticProviderContextMessage(message)
3242
- ) {
3243
- continue;
3244
- }
3245
- const key = getProviderMessageOriginKey(message);
3246
- const origin = key != null ? keyedOrigins.get(key) : undefined;
3247
- if (origin != null) {
3248
- providerMessageOrigins.set(message, origin);
3249
- }
3250
- }
3251
- return after;
3252
- };
3158
+ const contextPressure = createContextPressureMeter({
3159
+ tokenCounter: agentContext.tokenCounter,
3160
+ sourceMessages: messages,
3161
+ retainedMessages: messagesToUse,
3162
+ indexTokenCountMap: agentContext.indexTokenCountMap,
3163
+ contextUsage,
3164
+ instructionTokens: agentContext.instructionTokens,
3165
+ calibrationRatio: agentContext.calibrationRatio,
3166
+ });
3167
+ const trackProviderMessageOrigins = contextPressure.trackProjection;
3253
3168
 
3254
3169
  if (agentContext.useLegacyContent) {
3255
3170
  const before = finalMessages;
@@ -3297,8 +3212,13 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
3297
3212
  typeof lastMessageX.content === 'string'
3298
3213
  ) {
3299
3214
  const trimmed = lastMessageX.content.trim();
3300
- finalMessages[finalMessages.length - 2].content =
3301
- trimmed.length > 0 ? [{ type: 'text' as const, text: trimmed }] : '';
3215
+ const before = finalMessages;
3216
+ finalMessages = [...before];
3217
+ finalMessages[finalMessages.length - 2] = cloneMessage(
3218
+ lastMessageX,
3219
+ trimmed.length > 0 ? [{ type: 'text' as const, text: trimmed }] : ''
3220
+ );
3221
+ finalMessages = trackProviderMessageOrigins(before, finalMessages);
3302
3222
  }
3303
3223
 
3304
3224
  const localProviderOverflowMeasurements = new WeakMap<
@@ -3308,123 +3228,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
3308
3228
  estimatedPromptTokens: number;
3309
3229
  }
3310
3230
  >();
3311
- const measureProviderPayload = (
3312
- candidate: BaseMessage[],
3313
- contextBudgetOverride?: number,
3314
- forceRawRecount = false
3315
- ): {
3316
- fits: boolean;
3317
- projectedMessageTokens?: number;
3318
- availableMessageTokens?: number;
3319
- contextBudget?: number;
3320
- effectiveInstructionTokens?: number;
3321
- } => {
3322
- const contextBudget =
3323
- contextBudgetOverride ?? contextUsage?.contextBudget;
3324
- const effectiveInstructionTokens =
3325
- contextUsage?.effectiveInstructionTokens ??
3326
- (forceRawRecount ? agentContext.instructionTokens : undefined);
3327
- if (
3328
- agentContext.tokenCounter == null ||
3329
- contextBudget == null ||
3330
- effectiveInstructionTokens == null
3331
- ) {
3332
- return { fits: true };
3333
- }
3334
- const availableMessageTokens = Math.max(
3335
- 0,
3336
- contextBudget - effectiveInstructionTokens
3337
- );
3338
- let usageRatio =
3339
- agentContext.calibrationRatio > 0 ? agentContext.calibrationRatio : 1;
3340
- if (
3341
- contextUsage?.calibrationRatio != null &&
3342
- contextUsage.calibrationRatio > 0
3343
- ) {
3344
- usageRatio = contextUsage.calibrationRatio;
3345
- }
3346
- if (forceRawRecount) {
3347
- usageRatio = Math.max(1, usageRatio);
3348
- }
3349
- const baselineRemaining = contextUsage?.remainingContextTokens;
3350
- const accountedMessageTokens =
3351
- !forceRawRecount &&
3352
- providerMessageBaseline != null &&
3353
- baselineRemaining != null &&
3354
- Number.isFinite(baselineRemaining)
3355
- ? availableMessageTokens -
3356
- Math.min(availableMessageTokens, Math.max(0, baselineRemaining))
3357
- : undefined;
3358
-
3359
- let projectedMessageTokens: number;
3360
- if (accountedMessageTokens != null && providerMessageBaseline != null) {
3361
- const replyPrimerTokens = Math.round(
3362
- REPLY_PRIMER_TOKENS * usageRatio
3363
- );
3364
- const rawWeights: Record<string, number> = {};
3365
- let totalWeight = 0;
3366
- for (let i = 0; i < providerMessageBaseline.length; i++) {
3367
- const weight = providerMessageBaseline[i].accountingWeight;
3368
- rawWeights[i] = weight;
3369
- totalWeight += weight;
3370
- }
3371
- const attributableTokens =
3372
- totalWeight > 0
3373
- ? Math.min(
3374
- Math.max(0, accountedMessageTokens - replyPrimerTokens),
3375
- Math.round(totalWeight * usageRatio)
3376
- )
3377
- : 0;
3378
- const apportionedTokens =
3379
- totalWeight > 0
3380
- ? apportionTokenCounts(
3381
- rawWeights,
3382
- attributableTokens / totalWeight,
3383
- attributableTokens
3384
- )
3385
- : {};
3386
- const attributedByOrigin = providerMessageBaseline.map(
3387
- (_, origin) => apportionedTokens[origin] || 0
3388
- );
3389
- projectedMessageTokens = Math.max(
3390
- replyPrimerTokens,
3391
- accountedMessageTokens - attributableTokens
3392
- );
3393
- let newRawTokens = 0;
3394
- const usedOrigins = new Set<number>();
3395
- for (const message of candidate) {
3396
- const rawTokens = agentContext.tokenCounter(message);
3397
- const origin = providerMessageOrigins.get(message);
3398
- if (origin == null || usedOrigins.has(origin)) {
3399
- newRawTokens += rawTokens;
3400
- continue;
3401
- }
3402
- usedOrigins.add(origin);
3403
- projectedMessageTokens += Math.max(
3404
- 0,
3405
- attributedByOrigin[origin] +
3406
- Math.round(
3407
- (rawTokens - providerMessageBaseline[origin].rawTokens) *
3408
- usageRatio
3409
- )
3410
- );
3411
- }
3412
- projectedMessageTokens += Math.round(newRawTokens * usageRatio);
3413
- } else {
3414
- let rawTokens = REPLY_PRIMER_TOKENS;
3415
- for (const message of candidate) {
3416
- rawTokens += agentContext.tokenCounter(message);
3417
- }
3418
- projectedMessageTokens = Math.round(rawTokens * usageRatio);
3419
- }
3420
- return {
3421
- fits: projectedMessageTokens <= availableMessageTokens,
3422
- projectedMessageTokens,
3423
- availableMessageTokens,
3424
- contextBudget,
3425
- effectiveInstructionTokens,
3426
- };
3427
- };
3231
+ const measureProviderPayload = contextPressure.measure;
3428
3232
 
3429
3233
  const createProviderPayloadOverflowError = ({
3430
3234
  projection,
@@ -3779,10 +3583,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
3779
3583
  finalMessages = trackProviderMessageOrigins(
3780
3584
  beforeSanitizeMessages,
3781
3585
  sanitizeOrphanToolBlocks(beforeSanitizeMessages, (source, clone) => {
3782
- const origin = providerMessageOrigins.get(source);
3783
- if (origin != null) {
3784
- providerMessageOrigins.set(clone, origin);
3785
- }
3586
+ contextPressure.trackClone(source, clone);
3786
3587
  })
3787
3588
  );
3788
3589
  if (finalMessages.length !== beforeSanitize) {
@@ -3855,23 +3656,30 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
3855
3656
 
3856
3657
  const fallbackBaseMessages = finalMessages;
3857
3658
  const beforeFinalProviderProjection = fallbackBaseMessages;
3858
- finalMessages = trackProviderMessageOrigins(
3859
- beforeFinalProviderProjection,
3860
- projectMessagesForProvider({
3861
- model: (this.overrideModel ?? model) as t.ChatModel,
3862
- messages: beforeFinalProviderProjection,
3863
- provider: agentContext.provider,
3864
- maxToolResultChars: maxProviderToolResultChars,
3865
- callOptions: config,
3866
- })
3867
- );
3659
+ const preparedRequest = prepareProviderRequest({
3660
+ model: (this.overrideModel ?? model) as t.ChatModel,
3661
+ messages: beforeFinalProviderProjection,
3662
+ provider: agentContext.provider,
3663
+ context: this,
3664
+ config,
3665
+ maxToolResultChars: maxProviderToolResultChars,
3666
+ measure: (preparedMessages) =>
3667
+ measureProviderPayload(
3668
+ trackProviderMessageOrigins(
3669
+ beforeFinalProviderProjection,
3670
+ preparedMessages
3671
+ )
3672
+ ),
3673
+ });
3674
+ finalMessages = preparedRequest.messages;
3868
3675
 
3869
3676
  /**
3870
3677
  * Prompt-cache placement and orphan sanitization are provider-wire
3871
3678
  * transforms too. Re-measure after both so no content added after the
3872
3679
  * earlier artifact/synthetic compaction decision can bypass the guard.
3873
3680
  */
3874
- finalProjection = measureProviderPayload(finalMessages);
3681
+ finalProjection =
3682
+ preparedRequest.measurement ?? measureProviderPayload(finalMessages);
3875
3683
  const preInvokeContextOverflowError = !finalProjection.fits
3876
3684
  ? createProviderPayloadOverflowError({
3877
3685
  projection: finalProjection,
@@ -4080,9 +3888,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
4080
3888
  () =>
4081
3889
  attemptInvoke(
4082
3890
  {
4083
- model: (this.overrideModel ?? model) as t.ChatModel,
4084
- messages: finalMessages,
4085
- provider: agentContext.provider,
3891
+ request: preparedRequest,
4086
3892
  context: this,
4087
3893
  },
4088
3894
  invokeConfig
@@ -4285,7 +4091,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
4285
4091
  estimatedPromptTokens: getEstimatedPromptTokens(contextUsage),
4286
4092
  maxContextTokens: agentContext.maxContextTokens,
4287
4093
  },
4288
- prepareProviderMessages: ({
4094
+ prepareProviderRequest: ({
4289
4095
  model: fallbackModel,
4290
4096
  messages: fallbackMessages,
4291
4097
  provider: fallbackProvider,
@@ -4297,36 +4103,6 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
4297
4103
  calculateMaxToolResultChars(
4298
4104
  fallbackMaxContextTokens ?? agentContext.maxContextTokens
4299
4105
  );
4300
- /**
4301
- * Serving-provider cue shaping BEFORE the fallback payload
4302
- * is measured: a Claude fallback behind a tolerant primary
4303
- * gains the cue inside the guarded projection (a prompt
4304
- * within the cue's cost of the fallback budget must take
4305
- * the recovery path, not ship oversized), and a tolerant
4306
- * fallback behind an Anthropic primary sheds the baked cue
4307
- * before it is measured against the tighter budget. The
4308
- * attemptInvoke funnel pass then finds nothing to change.
4309
- */
4310
- const cueShapedFallbackMessages = trackProviderMessageOrigins(
4311
- fallbackMessages,
4312
- isAnthropicLike(fallbackProvider, {
4313
- model: resolveServingModelId(fallbackModel),
4314
- })
4315
- ? appendPredecessorHandoffCue(fallbackMessages, (m) =>
4316
- this.isRunProducedMessage(m)
4317
- )
4318
- : removePredecessorHandoffCue(fallbackMessages)
4319
- );
4320
- const projectedFallbackMessages = trackProviderMessageOrigins(
4321
- cueShapedFallbackMessages,
4322
- projectMessagesForProvider({
4323
- model: fallbackModel,
4324
- messages: cueShapedFallbackMessages,
4325
- provider: fallbackProvider,
4326
- maxToolResultChars: fallbackToolResultChars,
4327
- callOptions: fallbackConfig,
4328
- })
4329
- );
4330
4106
  const primaryContextBudget = contextUsage?.contextBudget;
4331
4107
  const fallbackContextBudget =
4332
4108
  fallbackMaxContextTokens == null
@@ -4335,11 +4111,31 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
4335
4111
  primaryContextBudget ?? fallbackMaxContextTokens,
4336
4112
  fallbackMaxContextTokens
4337
4113
  );
4338
- const projection = measureProviderPayload(
4339
- projectedFallbackMessages,
4340
- fallbackContextBudget,
4341
- true
4342
- );
4114
+ const preparedFallbackRequest = prepareProviderRequest({
4115
+ model: fallbackModel,
4116
+ messages: fallbackMessages,
4117
+ provider: fallbackProvider,
4118
+ context: this,
4119
+ config: fallbackConfig,
4120
+ maxToolResultChars: fallbackToolResultChars,
4121
+ measure: (preparedMessages) =>
4122
+ measureProviderPayload(
4123
+ trackProviderMessageOrigins(
4124
+ fallbackMessages,
4125
+ preparedMessages
4126
+ ),
4127
+ {
4128
+ contextBudget: fallbackContextBudget,
4129
+ forceRawRecount: true,
4130
+ }
4131
+ ),
4132
+ });
4133
+ const projection =
4134
+ preparedFallbackRequest.measurement ??
4135
+ measureProviderPayload(preparedFallbackRequest.messages, {
4136
+ contextBudget: fallbackContextBudget,
4137
+ forceRawRecount: true,
4138
+ });
4343
4139
  if (!projection.fits) {
4344
4140
  throw createProviderPayloadOverflowError({
4345
4141
  projection,
@@ -4347,7 +4143,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
4347
4143
  info: 'Fallback provider message formatting exceeded the context budget before invocation.',
4348
4144
  });
4349
4145
  }
4350
- return projectedFallbackMessages;
4146
+ return preparedFallbackRequest;
4351
4147
  },
4352
4148
  })
4353
4149
  );
package/src/index.ts CHANGED
@@ -88,6 +88,14 @@ export type { SmoothItem, SmoothPiece } from './llm/stream/smoother';
88
88
  export { FakeChatModel, createFakeStreamingLLM } from './llm/fake';
89
89
  export { initializeModel } from './llm/init';
90
90
  export { attemptInvoke, tryFallbackProviders } from './llm/invoke';
91
+ export { prepareProviderRequest } from './llm/prepareProviderRequest';
92
+ export type {
93
+ PreparedProviderRequest,
94
+ PrepareProviderRequestParams,
95
+ ProviderMessageProjectionMode,
96
+ ProviderPayloadMeasurement,
97
+ ProviderRequestContext,
98
+ } from './llm/prepareProviderRequest';
91
99
  export { canSealPreempt } from './llm/preempt';
92
100
  export { isThinkingEnabled, getMaxOutputTokensKey } from './llm/request';
93
101
  export {