@librechat/agents 3.3.2 → 3.3.4
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.
- package/dist/cjs/agents/AgentContext.cjs +2 -1
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +342 -109
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +3 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/langfuse.cjs +6 -0
- package/dist/cjs/langfuse.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +79 -6
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +37 -10
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/google/utils/common.cjs +19 -7
- package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
- package/dist/cjs/llm/invoke.cjs +64 -3
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +41 -5
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs +25 -4
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/index.cjs +4 -1
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +19 -2
- package/dist/cjs/messages/cache.cjs +54 -33
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/messages/contextPruning.cjs +17 -43
- package/dist/cjs/messages/contextPruning.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +315 -23
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +218 -60
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/index.cjs +1 -1
- package/dist/cjs/messages/prune.cjs +503 -127
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/stream.cjs +7 -3
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +28 -14
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +54 -41
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/toolOutputReferences.cjs +6 -0
- package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -1
- package/dist/cjs/utils/index.cjs +1 -1
- package/dist/cjs/utils/llm.cjs +1 -1
- package/dist/cjs/utils/llm.cjs.map +1 -1
- package/dist/cjs/utils/tokens.cjs +307 -20
- package/dist/cjs/utils/tokens.cjs.map +1 -1
- package/dist/cjs/utils/toolContent.cjs +1514 -0
- package/dist/cjs/utils/toolContent.cjs.map +1 -0
- package/dist/esm/agents/AgentContext.mjs +2 -1
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +349 -116
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +3 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/langfuse.mjs +6 -0
- package/dist/esm/langfuse.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +79 -6
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs +37 -10
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/google/utils/common.mjs +19 -7
- package/dist/esm/llm/google/utils/common.mjs.map +1 -1
- package/dist/esm/llm/invoke.mjs +65 -5
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +41 -5
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs +25 -4
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/index.mjs +4 -1
- package/dist/esm/llm/openrouter/index.mjs.map +1 -1
- package/dist/esm/main.mjs +7 -7
- package/dist/esm/messages/cache.mjs +55 -34
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/messages/contextPruning.mjs +17 -43
- package/dist/esm/messages/contextPruning.mjs.map +1 -1
- package/dist/esm/messages/core.mjs +305 -24
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +218 -61
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/index.mjs +1 -1
- package/dist/esm/messages/prune.mjs +500 -128
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/stream.mjs +7 -3
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +28 -14
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +54 -41
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/toolOutputReferences.mjs +6 -0
- package/dist/esm/tools/toolOutputReferences.mjs.map +1 -1
- package/dist/esm/utils/index.mjs +1 -1
- package/dist/esm/utils/llm.mjs +1 -1
- package/dist/esm/utils/llm.mjs.map +1 -1
- package/dist/esm/utils/tokens.mjs +307 -21
- package/dist/esm/utils/tokens.mjs.map +1 -1
- package/dist/esm/utils/toolContent.mjs +1503 -0
- package/dist/esm/utils/toolContent.mjs.map +1 -0
- package/dist/types/graphs/Graph.d.ts +1 -1
- package/dist/types/llm/invoke.d.ts +27 -1
- package/dist/types/llm/openai/index.d.ts +4 -0
- package/dist/types/llm/openai/utils/index.d.ts +2 -0
- package/dist/types/llm/openrouter/index.d.ts +5 -0
- package/dist/types/messages/contextPruning.d.ts +2 -1
- package/dist/types/messages/core.d.ts +36 -0
- package/dist/types/messages/format.d.ts +6 -0
- package/dist/types/messages/prune.d.ts +16 -12
- package/dist/types/types/stream.d.ts +1 -1
- package/dist/types/utils/tokens.d.ts +7 -0
- package/dist/types/utils/toolContent.d.ts +107 -0
- package/package.json +13 -9
- package/src/__tests__/stream.eagerEventExecution.test.ts +74 -0
- package/src/agents/AgentContext.ts +1 -0
- package/src/graphs/Graph.ts +698 -200
- package/src/graphs/MultiAgentGraph.ts +6 -1
- package/src/graphs/__tests__/Graph.contextOverflow.test.ts +1128 -4
- package/src/langfuse.ts +15 -0
- package/src/llm/anthropic/utils/message_inputs.ts +158 -12
- package/src/llm/anthropic/utils/tool-id-normalization.test.ts +109 -0
- package/src/llm/bedrock/utils/message_inputs.test.ts +49 -1
- package/src/llm/bedrock/utils/message_inputs.ts +86 -13
- package/src/llm/google/utils/common.test.ts +75 -9
- package/src/llm/google/utils/common.ts +34 -3
- package/src/llm/invoke.test.ts +488 -0
- package/src/llm/invoke.ts +196 -2
- package/src/llm/openai/index.ts +106 -6
- package/src/llm/openai/utils/index.ts +109 -53
- package/src/llm/openai/utils/messages.test.ts +330 -1
- package/src/llm/openrouter/index.ts +12 -2
- package/src/llm/openrouter/reasoning.test.ts +314 -0
- package/src/messages/__tests__/observationMasking.test.ts +93 -2
- package/src/messages/cache.tail.test.ts +193 -0
- package/src/messages/cache.test.ts +113 -0
- package/src/messages/cache.ts +92 -60
- package/src/messages/contextPruning.test.ts +184 -0
- package/src/messages/contextPruning.ts +49 -42
- package/src/messages/core.ts +653 -40
- package/src/messages/ensureThinkingBlock.test.ts +49 -1
- package/src/messages/foldToollessToolBlocks.test.ts +163 -5
- package/src/messages/format.ts +363 -91
- package/src/messages/formatAgentMessages.test.ts +604 -14
- package/src/messages/formatAgentMessages.tools.test.ts +103 -0
- package/src/messages/prune.ts +996 -183
- package/src/specs/langfuse-callbacks.test.ts +203 -1
- package/src/specs/prune.test.ts +1083 -6
- package/src/specs/summarization-unit.test.ts +105 -0
- package/src/specs/tokens.test.ts +609 -32
- package/src/stream.ts +23 -15
- package/src/summarization/__tests__/node.test.ts +77 -0
- package/src/summarization/node.ts +46 -18
- package/src/tools/ToolNode.ts +147 -68
- package/src/tools/__tests__/ToolNode.onResultCompletion.test.ts +148 -1
- package/src/tools/__tests__/ToolNode.outputReferences.test.ts +112 -3
- package/src/tools/__tests__/annotateMessagesForLLM.test.ts +32 -0
- package/src/tools/__tests__/directToolHooks.test.ts +255 -7
- package/src/tools/toolOutputReferences.ts +6 -0
- package/src/types/stream.ts +1 -1
- package/src/utils/llm.test.ts +18 -0
- package/src/utils/llm.ts +4 -1
- package/src/utils/tokens.ts +586 -31
- package/src/utils/toolContent.test.ts +1432 -0
- package/src/utils/toolContent.ts +2707 -0
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import "../common/enum.mjs";
|
|
2
2
|
import "../common/index.mjs";
|
|
3
|
+
import { calculateMaxToolResultChars, truncateToolResultContent } from "../utils/truncation.mjs";
|
|
4
|
+
import { cloneToolMessageWithContent, compactToolContent, getToolContentCharLength, isComputerCallOutputMessage, serializeStructuredValueBounded, serializeToolContentBounded } from "../utils/toolContent.mjs";
|
|
5
|
+
import { hasUnsafeStructuredSerialization } from "../utils/tokens.mjs";
|
|
3
6
|
import { toLangChainContent } from "./langchain.mjs";
|
|
4
|
-
import { calculateMaxToolResultChars, truncateToolInput, truncateToolResultContent } from "../utils/truncation.mjs";
|
|
5
7
|
import { resolveContextPruningSettings } from "./contextPruningSettings.mjs";
|
|
6
8
|
import { applyContextPruning } from "./contextPruning.mjs";
|
|
7
9
|
import { AIMessage, ToolMessage } from "@langchain/core/messages";
|
|
10
|
+
import { isProxy } from "node:util/types";
|
|
8
11
|
//#region src/messages/prune.ts
|
|
9
12
|
function sumTokenCounts(tokenMap, count) {
|
|
10
13
|
let total = 0;
|
|
@@ -13,6 +16,8 @@ function sumTokenCounts(tokenMap, count) {
|
|
|
13
16
|
}
|
|
14
17
|
/** Default fraction of the token budget reserved as headroom (5 %). */
|
|
15
18
|
const DEFAULT_RESERVE_RATIO = .05;
|
|
19
|
+
/** Provider framing reserved for the assistant reply label. */
|
|
20
|
+
const REPLY_PRIMER_TOKENS = 3;
|
|
16
21
|
/** Context pressure at which observation masking and context fading activate. */
|
|
17
22
|
const PRESSURE_THRESHOLD_MASKING = .8;
|
|
18
23
|
/** Pressure band thresholds paired with budget factors for progressive context fading. */
|
|
@@ -55,7 +60,8 @@ function clampCalibrationRatio(ratio) {
|
|
|
55
60
|
return Math.max(CALIBRATION_RATIO_MIN, Math.min(5, ratio));
|
|
56
61
|
}
|
|
57
62
|
function getToolCallIds(message) {
|
|
58
|
-
|
|
63
|
+
const messageRole = message.role;
|
|
64
|
+
if (message.getType() !== "ai" && messageRole !== "assistant") return /* @__PURE__ */ new Set();
|
|
59
65
|
const ids = /* @__PURE__ */ new Set();
|
|
60
66
|
const aiMessage = message;
|
|
61
67
|
for (const toolCall of aiMessage.tool_calls ?? []) if (typeof toolCall.id === "string" && toolCall.id.length > 0) ids.add(toolCall.id);
|
|
@@ -64,8 +70,26 @@ function getToolCallIds(message) {
|
|
|
64
70
|
const record = part;
|
|
65
71
|
if ((record.type === "tool_use" || record.type === "tool_call") && typeof record.id === "string" && record.id.length > 0) ids.add(record.id);
|
|
66
72
|
}
|
|
73
|
+
const toolOutputSource = getResponsesToolOutputSource(message);
|
|
74
|
+
if (toolOutputSource != null) for (const item of toolOutputSource.items) {
|
|
75
|
+
if (item == null || typeof item !== "object") continue;
|
|
76
|
+
const record = item;
|
|
77
|
+
if ((record.type === "function_call" || record.type === "custom_tool_call" || record.type === "computer_call") && typeof record.call_id === "string" && record.call_id !== "") ids.add(record.call_id);
|
|
78
|
+
}
|
|
67
79
|
return ids;
|
|
68
80
|
}
|
|
81
|
+
function getResponsesToolOutputSource(message) {
|
|
82
|
+
const responseOutput = readPropertyWithoutAccessors(message.response_metadata, "output");
|
|
83
|
+
if (!responseOutput.accessor && Array.isArray(responseOutput.value) && !isProxy(responseOutput.value) && responseOutput.value.length > 0) return {
|
|
84
|
+
source: "response_metadata",
|
|
85
|
+
items: responseOutput.value
|
|
86
|
+
};
|
|
87
|
+
const fallbackOutput = readPropertyWithoutAccessors(message.additional_kwargs, "tool_outputs");
|
|
88
|
+
if (!fallbackOutput.accessor && Array.isArray(fallbackOutput.value) && !isProxy(fallbackOutput.value)) return {
|
|
89
|
+
source: "tool_outputs",
|
|
90
|
+
items: fallbackOutput.value
|
|
91
|
+
};
|
|
92
|
+
}
|
|
69
93
|
function getToolResultId(message) {
|
|
70
94
|
if (message.getType() !== "tool") return null;
|
|
71
95
|
const toolMessage = message;
|
|
@@ -109,7 +133,8 @@ function repairOrphanedToolMessages({ context, allMessages, tokenCounter, indexT
|
|
|
109
133
|
repairedContext.push(message);
|
|
110
134
|
continue;
|
|
111
135
|
}
|
|
112
|
-
|
|
136
|
+
const messageRole = message.role;
|
|
137
|
+
if (message.getType() === "ai" || messageRole === "assistant") {
|
|
113
138
|
const toolCallIds = getToolCallIds(message);
|
|
114
139
|
if (toolCallIds.size > 0) {
|
|
115
140
|
let hasOrphanToolCalls = false;
|
|
@@ -153,22 +178,33 @@ function repairOrphanedToolMessages({ context, allMessages, tokenCounter, indexT
|
|
|
153
178
|
* Returns null if the message has no content left after stripping.
|
|
154
179
|
*/
|
|
155
180
|
function stripOrphanToolUseBlocks(message, presentToolResultIds) {
|
|
156
|
-
const
|
|
181
|
+
const aiMessage = message;
|
|
182
|
+
const keptToolCalls = (aiMessage.tool_calls ?? []).filter((tc) => typeof tc.id === "string" && presentToolResultIds.has(tc.id));
|
|
157
183
|
let keptContent;
|
|
158
|
-
if (Array.isArray(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
184
|
+
if (Array.isArray(aiMessage.content)) keptContent = aiMessage.content.filter((block) => {
|
|
185
|
+
if (typeof block !== "object") return true;
|
|
186
|
+
const record = block;
|
|
187
|
+
if ((record.type === "tool_use" || record.type === "tool_call") && typeof record.id === "string") return presentToolResultIds.has(record.id);
|
|
188
|
+
return true;
|
|
189
|
+
});
|
|
190
|
+
else keptContent = aiMessage.content;
|
|
191
|
+
const toolOutputSource = getResponsesToolOutputSource(message);
|
|
192
|
+
const keptToolOutputs = toolOutputSource?.items.filter((item) => {
|
|
193
|
+
if (item == null || typeof item !== "object") return true;
|
|
194
|
+
const record = item;
|
|
195
|
+
if (record.type !== "function_call" && record.type !== "custom_tool_call" && record.type !== "computer_call") return true;
|
|
196
|
+
return typeof record.call_id === "string" && presentToolResultIds.has(record.call_id);
|
|
197
|
+
}) ?? [];
|
|
198
|
+
if (keptToolCalls.length === 0 && Array.isArray(keptContent) && keptContent.length === 0 && keptToolOutputs.length === 0) return null;
|
|
199
|
+
let responseMetadata = message.response_metadata;
|
|
200
|
+
let additionalKwargs = message.additional_kwargs;
|
|
201
|
+
if (toolOutputSource?.source === "response_metadata") responseMetadata = cloneWithProjectedProperties(responseMetadata, { output: keptToolOutputs });
|
|
202
|
+
else if (toolOutputSource?.source === "tool_outputs") additionalKwargs = cloneWithProjectedProperties(additionalKwargs, { tool_outputs: keptToolOutputs });
|
|
203
|
+
return cloneWithProjectedProperties(message, {
|
|
170
204
|
content: toLangChainContent(keptContent),
|
|
171
|
-
tool_calls: keptToolCalls.length > 0 ? keptToolCalls : void 0
|
|
205
|
+
tool_calls: keptToolCalls.length > 0 ? keptToolCalls : void 0,
|
|
206
|
+
response_metadata: responseMetadata,
|
|
207
|
+
additional_kwargs: additionalKwargs
|
|
172
208
|
});
|
|
173
209
|
}
|
|
174
210
|
/**
|
|
@@ -186,11 +222,12 @@ function stripOrphanToolUseBlocks(message, presentToolResultIds) {
|
|
|
186
222
|
* Includes a fast-path: if every tool_call has a matching tool_result and
|
|
187
223
|
* vice-versa, the original array is returned immediately with zero allocation.
|
|
188
224
|
*/
|
|
189
|
-
function sanitizeOrphanToolBlocks(messages) {
|
|
225
|
+
function sanitizeOrphanToolBlocks(messages, onMessageCloned) {
|
|
190
226
|
const allToolCallIds = /* @__PURE__ */ new Set();
|
|
191
227
|
const allToolResultIds = /* @__PURE__ */ new Set();
|
|
192
228
|
for (const msg of messages) {
|
|
193
229
|
const msgAny = msg;
|
|
230
|
+
if (typeof msg.getType === "function") for (const id of getToolCallIds(msg)) allToolCallIds.add(id);
|
|
194
231
|
const toolCalls = msgAny.tool_calls;
|
|
195
232
|
if (Array.isArray(toolCalls)) {
|
|
196
233
|
for (const tc of toolCalls) if (typeof tc.id === "string" && tc.id.length > 0 && !tc.id.startsWith("srvtoolu_")) allToolCallIds.add(tc.id);
|
|
@@ -227,6 +264,7 @@ function sanitizeOrphanToolBlocks(messages) {
|
|
|
227
264
|
const stripped = stripOrphanToolUseBlocks(msg, allToolResultIds);
|
|
228
265
|
if (stripped != null) {
|
|
229
266
|
strippedAiIndices.add(result.length);
|
|
267
|
+
onMessageCloned?.(msg, stripped);
|
|
230
268
|
result.push(stripped);
|
|
231
269
|
}
|
|
232
270
|
continue;
|
|
@@ -242,6 +280,7 @@ function sanitizeOrphanToolBlocks(messages) {
|
|
|
242
280
|
const patched = Object.create(Object.getPrototypeOf(msg), Object.getOwnPropertyDescriptors(msg));
|
|
243
281
|
patched.tool_calls = keptToolCalls.length > 0 ? keptToolCalls : [];
|
|
244
282
|
patched.content = keptContent;
|
|
283
|
+
onMessageCloned?.(msg, patched);
|
|
245
284
|
result.push(patched);
|
|
246
285
|
continue;
|
|
247
286
|
}
|
|
@@ -536,26 +575,21 @@ function maskConsumedToolResults(params) {
|
|
|
536
575
|
for (let c = 0; c < count; c++) {
|
|
537
576
|
const i = consumedIndices[c];
|
|
538
577
|
const message = messages[i];
|
|
578
|
+
if (isComputerCallOutputMessage(message)) continue;
|
|
539
579
|
const content = message.content;
|
|
540
|
-
if (typeof content !== "string") continue;
|
|
541
580
|
let maxChars;
|
|
542
581
|
if (totalBudgetChars > 0) {
|
|
543
582
|
const share = (.2 + .8 * (count > 1 ? c / (count - 1) : 1)) / (count > 1 ? .6 * count : 1) * totalBudgetChars;
|
|
544
583
|
maxChars = Math.max(MASKED_RESULT_MAX_CHARS, Math.floor(share));
|
|
545
584
|
} else maxChars = MASKED_RESULT_MAX_CHARS;
|
|
546
|
-
|
|
585
|
+
const compacted = compactToolContent(content, maxChars);
|
|
586
|
+
if (!compacted.changed) continue;
|
|
547
587
|
if (params.originalContentStore && !params.originalContentStore.has(i)) {
|
|
548
|
-
|
|
549
|
-
|
|
588
|
+
const original = serializeToolContentBounded(content, ORIGINAL_CONTENT_MAX_CHARS);
|
|
589
|
+
params.originalContentStore.set(i, original);
|
|
590
|
+
if (params.onContentStored) params.onContentStored(i, original);
|
|
550
591
|
}
|
|
551
|
-
const cloned =
|
|
552
|
-
content: truncateToolResultContent(content, maxChars),
|
|
553
|
-
tool_call_id: message.tool_call_id,
|
|
554
|
-
name: message.name,
|
|
555
|
-
id: message.id,
|
|
556
|
-
additional_kwargs: message.additional_kwargs,
|
|
557
|
-
response_metadata: message.response_metadata
|
|
558
|
-
});
|
|
592
|
+
const cloned = cloneToolMessageWithContent(message, compacted.content);
|
|
559
593
|
messages[i] = cloned;
|
|
560
594
|
indexTokenCountMap[i] = tokenCounter(cloned);
|
|
561
595
|
maskedCount++;
|
|
@@ -578,23 +612,15 @@ function preFlightTruncateToolResults(params) {
|
|
|
578
612
|
const baseMaxChars = calculateMaxToolResultChars(maxContextTokens);
|
|
579
613
|
let truncatedCount = 0;
|
|
580
614
|
const toolIndices = [];
|
|
581
|
-
for (let i = 0; i < messages.length; i++) if (messages[i].getType() === "tool") toolIndices.push(i);
|
|
615
|
+
for (let i = 0; i < messages.length; i++) if (messages[i].getType() === "tool" && !isComputerCallOutputMessage(messages[i])) toolIndices.push(i);
|
|
582
616
|
for (let t = 0; t < toolIndices.length; t++) {
|
|
583
617
|
const i = toolIndices[t];
|
|
584
618
|
const message = messages[i];
|
|
585
619
|
const content = message.content;
|
|
586
|
-
if (typeof content !== "string") continue;
|
|
587
620
|
const recencyFactor = .2 + .8 * (toolIndices.length > 1 ? t / (toolIndices.length - 1) : 1);
|
|
588
|
-
const
|
|
589
|
-
if (
|
|
590
|
-
const cloned =
|
|
591
|
-
content: truncateToolResultContent(content, maxChars),
|
|
592
|
-
tool_call_id: message.tool_call_id,
|
|
593
|
-
name: message.name,
|
|
594
|
-
id: message.id,
|
|
595
|
-
additional_kwargs: message.additional_kwargs,
|
|
596
|
-
response_metadata: message.response_metadata
|
|
597
|
-
});
|
|
621
|
+
const compacted = compactToolContent(content, Math.max(200, Math.floor(baseMaxChars * recencyFactor)));
|
|
622
|
+
if (!compacted.changed) continue;
|
|
623
|
+
const cloned = cloneToolMessageWithContent(message, compacted.content);
|
|
598
624
|
messages[i] = cloned;
|
|
599
625
|
indexTokenCountMap[i] = tokenCounter(cloned);
|
|
600
626
|
truncatedCount++;
|
|
@@ -614,46 +640,385 @@ function preFlightTruncateToolResults(params) {
|
|
|
614
640
|
*
|
|
615
641
|
* @returns The number of AI messages that had tool_use inputs truncated.
|
|
616
642
|
*/
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
643
|
+
const HARD_MAX_TOOL_CALL_INPUT_CHARS = 2e5;
|
|
644
|
+
const MIN_JSON_VALUE_CHARS = 4;
|
|
645
|
+
const ACCESSOR_INPUT_PLACEHOLDER = "[Property accessor omitted]";
|
|
646
|
+
function normalizeToolInputLimit(maxChars) {
|
|
647
|
+
if (!Number.isFinite(maxChars)) return HARD_MAX_TOOL_CALL_INPUT_CHARS;
|
|
648
|
+
return Math.max(MIN_JSON_VALUE_CHARS, Math.floor(maxChars));
|
|
649
|
+
}
|
|
650
|
+
function readPropertyWithoutAccessors(value, key) {
|
|
651
|
+
let current = value;
|
|
652
|
+
for (let depth = 0; current != null && depth < 100; depth++) {
|
|
653
|
+
if (isProxy(current)) return {
|
|
654
|
+
found: true,
|
|
655
|
+
own: current === value,
|
|
656
|
+
accessor: true
|
|
657
|
+
};
|
|
658
|
+
let descriptor;
|
|
659
|
+
try {
|
|
660
|
+
descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
661
|
+
} catch {
|
|
636
662
|
return {
|
|
637
|
-
|
|
638
|
-
|
|
663
|
+
found: true,
|
|
664
|
+
own: current === value,
|
|
665
|
+
accessor: true
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
if (descriptor != null) {
|
|
669
|
+
if (!("value" in descriptor)) return {
|
|
670
|
+
found: true,
|
|
671
|
+
own: current === value,
|
|
672
|
+
accessor: true
|
|
639
673
|
};
|
|
640
|
-
});
|
|
641
|
-
if (!state.changed) continue;
|
|
642
|
-
const aiMsg = message;
|
|
643
|
-
const newToolCalls = (aiMsg.tool_calls ?? []).map((tc) => {
|
|
644
|
-
const serializedArgs = JSON.stringify(tc.args);
|
|
645
|
-
if (serializedArgs.length <= maxInputChars) return tc;
|
|
646
674
|
return {
|
|
647
|
-
|
|
648
|
-
|
|
675
|
+
found: true,
|
|
676
|
+
own: current === value,
|
|
677
|
+
accessor: false,
|
|
678
|
+
value: descriptor.value
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
try {
|
|
682
|
+
current = Object.getPrototypeOf(current);
|
|
683
|
+
} catch {
|
|
684
|
+
return {
|
|
685
|
+
found: true,
|
|
686
|
+
own: false,
|
|
687
|
+
accessor: true
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
return current == null ? {
|
|
692
|
+
found: false,
|
|
693
|
+
own: false,
|
|
694
|
+
accessor: false
|
|
695
|
+
} : {
|
|
696
|
+
found: true,
|
|
697
|
+
own: false,
|
|
698
|
+
accessor: true
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
function cloneWithProjectedProperties(value, changes) {
|
|
702
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
703
|
+
for (const [key, projectedValue] of Object.entries(changes)) {
|
|
704
|
+
const descriptor = descriptors[key];
|
|
705
|
+
descriptors[key] = {
|
|
706
|
+
configurable: descriptor?.configurable ?? true,
|
|
707
|
+
enumerable: descriptor?.enumerable ?? true,
|
|
708
|
+
value: projectedValue,
|
|
709
|
+
writable: descriptor?.writable ?? true
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
return Object.create(Object.getPrototypeOf(value), descriptors);
|
|
713
|
+
}
|
|
714
|
+
function createBoundedTruncationValue(preview, originalChars, maxChars) {
|
|
715
|
+
const normalizedMaxChars = normalizeToolInputLimit(maxChars);
|
|
716
|
+
if (JSON.stringify({
|
|
717
|
+
_truncated: "",
|
|
718
|
+
_originalChars: originalChars
|
|
719
|
+
}).length > normalizedMaxChars) return null;
|
|
720
|
+
let low = 0;
|
|
721
|
+
let high = Math.min(preview.length, normalizedMaxChars);
|
|
722
|
+
while (low < high) {
|
|
723
|
+
const next = Math.ceil((low + high) / 2);
|
|
724
|
+
const candidate = {
|
|
725
|
+
_truncated: preview.slice(0, next),
|
|
726
|
+
_originalChars: originalChars
|
|
727
|
+
};
|
|
728
|
+
if (JSON.stringify(candidate).length <= normalizedMaxChars) low = next;
|
|
729
|
+
else high = next - 1;
|
|
730
|
+
}
|
|
731
|
+
return {
|
|
732
|
+
_truncated: low < preview.length && low >= 13 ? preview.slice(0, low - 13) + "… [truncated]" : preview.slice(0, low),
|
|
733
|
+
_originalChars: originalChars
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
function projectToolInputWithinLimit(input, maxChars) {
|
|
737
|
+
const normalizedMaxChars = normalizeToolInputLimit(maxChars);
|
|
738
|
+
const serialized = serializeStructuredValueBounded(input, normalizedMaxChars);
|
|
739
|
+
if (serialized.truncated) return {
|
|
740
|
+
value: createBoundedTruncationValue(serialized.content, serialized.originalChars, normalizedMaxChars),
|
|
741
|
+
changed: true
|
|
742
|
+
};
|
|
743
|
+
if (!hasUnsafeStructuredSerialization(input)) return {
|
|
744
|
+
value: input,
|
|
745
|
+
changed: false
|
|
746
|
+
};
|
|
747
|
+
try {
|
|
748
|
+
return {
|
|
749
|
+
value: JSON.parse(serialized.content),
|
|
750
|
+
changed: true
|
|
751
|
+
};
|
|
752
|
+
} catch {
|
|
753
|
+
return {
|
|
754
|
+
value: createBoundedTruncationValue(serialized.content, serialized.originalChars, normalizedMaxChars),
|
|
755
|
+
changed: true
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Serializes one structured tool-call input as valid, bounded JSON without
|
|
761
|
+
* invoking user-defined accessors or `toJSON`.
|
|
762
|
+
*/
|
|
763
|
+
function serializeToolCallInput(input, maxChars = HARD_MAX_TOOL_CALL_INPUT_CHARS) {
|
|
764
|
+
const normalizedMaxChars = normalizeToolInputLimit(maxChars);
|
|
765
|
+
const serialized = serializeStructuredValueBounded(projectToolInputWithinLimit(input, normalizedMaxChars).value, normalizedMaxChars);
|
|
766
|
+
if (!serialized.truncated) return serialized.content === "undefined" ? "null" : serialized.content;
|
|
767
|
+
const fallback = createBoundedTruncationValue(serialized.content, serialized.originalChars, normalizedMaxChars);
|
|
768
|
+
return JSON.stringify(fallback);
|
|
769
|
+
}
|
|
770
|
+
function projectKnownInputProperty(value, key, maxChars, changes) {
|
|
771
|
+
const property = readPropertyWithoutAccessors(value, key);
|
|
772
|
+
if (!property.found) return;
|
|
773
|
+
const projected = property.accessor ? {
|
|
774
|
+
value: ACCESSOR_INPUT_PLACEHOLDER,
|
|
775
|
+
changed: true
|
|
776
|
+
} : projectToolInputWithinLimit(property.value, maxChars);
|
|
777
|
+
if (projected.changed || !property.own) changes[key] = projected.value;
|
|
778
|
+
}
|
|
779
|
+
function getStringProperty(value, key) {
|
|
780
|
+
const property = readPropertyWithoutAccessors(value, key);
|
|
781
|
+
return !property.accessor && typeof property.value === "string" ? property.value : void 0;
|
|
782
|
+
}
|
|
783
|
+
function projectInlineToolInput(block, maxChars) {
|
|
784
|
+
const type = getStringProperty(block, "type");
|
|
785
|
+
if (type !== "tool_use" && type !== "tool_call") return block;
|
|
786
|
+
const changes = {};
|
|
787
|
+
projectKnownInputProperty(block, "input", maxChars, changes);
|
|
788
|
+
projectKnownInputProperty(block, "args", maxChars, changes);
|
|
789
|
+
const nestedProperty = readPropertyWithoutAccessors(block, "tool_call");
|
|
790
|
+
if (nestedProperty.found && !nestedProperty.accessor && nestedProperty.value != null && typeof nestedProperty.value === "object") {
|
|
791
|
+
const nestedChanges = {};
|
|
792
|
+
projectKnownInputProperty(nestedProperty.value, "args", maxChars, nestedChanges);
|
|
793
|
+
if (Object.keys(nestedChanges).length > 0) changes.tool_call = cloneWithProjectedProperties(nestedProperty.value, nestedChanges);
|
|
794
|
+
else if (!nestedProperty.own) changes.tool_call = nestedProperty.value;
|
|
795
|
+
} else if (nestedProperty.accessor) changes.tool_call = { args: ACCESSOR_INPUT_PLACEHOLDER };
|
|
796
|
+
return Object.keys(changes).length > 0 ? cloneWithProjectedProperties(block, changes) : block;
|
|
797
|
+
}
|
|
798
|
+
function projectSerializedArguments(value, maxChars) {
|
|
799
|
+
const normalizedMaxChars = normalizeToolInputLimit(maxChars);
|
|
800
|
+
if (typeof value === "string" && value.length <= normalizedMaxChars) return {
|
|
801
|
+
value,
|
|
802
|
+
changed: false
|
|
803
|
+
};
|
|
804
|
+
return {
|
|
805
|
+
value: serializeToolCallInput(value, normalizedMaxChars),
|
|
806
|
+
changed: true
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
function selectProjectedSerializedArguments(property, canonical, maxChars) {
|
|
810
|
+
if (canonical != null) return {
|
|
811
|
+
value: canonical,
|
|
812
|
+
changed: property.value !== canonical
|
|
813
|
+
};
|
|
814
|
+
if (property.accessor) return {
|
|
815
|
+
value: serializeToolCallInput(ACCESSOR_INPUT_PLACEHOLDER, maxChars),
|
|
816
|
+
changed: true
|
|
817
|
+
};
|
|
818
|
+
return projectSerializedArguments(property.value, maxChars);
|
|
819
|
+
}
|
|
820
|
+
function projectRawOpenAIToolCalls(rawToolCalls, maxChars, canonicalArguments) {
|
|
821
|
+
if (!Array.isArray(rawToolCalls)) return {
|
|
822
|
+
value: rawToolCalls,
|
|
823
|
+
changed: false
|
|
824
|
+
};
|
|
825
|
+
const state = { changed: false };
|
|
826
|
+
const projected = rawToolCalls.map((rawToolCall) => {
|
|
827
|
+
if (rawToolCall == null || typeof rawToolCall !== "object") return rawToolCall;
|
|
828
|
+
const functionProperty = readPropertyWithoutAccessors(rawToolCall, "function");
|
|
829
|
+
if (!functionProperty.found || functionProperty.accessor || functionProperty.value == null || typeof functionProperty.value !== "object") return rawToolCall;
|
|
830
|
+
const argsProperty = readPropertyWithoutAccessors(functionProperty.value, "arguments");
|
|
831
|
+
if (!argsProperty.found) return rawToolCall;
|
|
832
|
+
const callId = getStringProperty(rawToolCall, "id");
|
|
833
|
+
const projectedArgs = selectProjectedSerializedArguments(argsProperty, callId != null ? canonicalArguments.get(callId) : void 0, maxChars);
|
|
834
|
+
if (!projectedArgs.changed && functionProperty.own && argsProperty.own) return rawToolCall;
|
|
835
|
+
state.changed = true;
|
|
836
|
+
return cloneWithProjectedProperties(rawToolCall, { function: cloneWithProjectedProperties(functionProperty.value, { arguments: projectedArgs.value }) });
|
|
837
|
+
});
|
|
838
|
+
return {
|
|
839
|
+
value: state.changed ? projected : rawToolCalls,
|
|
840
|
+
changed: state.changed
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
function projectLegacyFunctionCall(property, maxChars) {
|
|
844
|
+
if (!property.found) return {
|
|
845
|
+
value: property.value,
|
|
846
|
+
changed: false
|
|
847
|
+
};
|
|
848
|
+
if (property.own && !property.accessor && property.value === void 0) return {
|
|
849
|
+
value: void 0,
|
|
850
|
+
changed: false
|
|
851
|
+
};
|
|
852
|
+
if (property.accessor || !property.own || property.value == null || typeof property.value !== "object") return {
|
|
853
|
+
value: void 0,
|
|
854
|
+
changed: true
|
|
855
|
+
};
|
|
856
|
+
const nameProperty = readPropertyWithoutAccessors(property.value, "name");
|
|
857
|
+
const argsProperty = readPropertyWithoutAccessors(property.value, "arguments");
|
|
858
|
+
if (nameProperty.accessor || !nameProperty.own || typeof nameProperty.value !== "string" || nameProperty.value === "" || nameProperty.value.length > normalizeToolInputLimit(maxChars) || !argsProperty.found || argsProperty.accessor || !argsProperty.own) return {
|
|
859
|
+
value: void 0,
|
|
860
|
+
changed: true
|
|
861
|
+
};
|
|
862
|
+
try {
|
|
863
|
+
const prototype = Object.getPrototypeOf(property.value);
|
|
864
|
+
const enumerableKeys = Object.keys(property.value);
|
|
865
|
+
if ((prototype === Object.prototype || prototype === null) && enumerableKeys.length === 2 && enumerableKeys.includes("name") && enumerableKeys.includes("arguments") && typeof argsProperty.value === "string" && argsProperty.value.length <= normalizeToolInputLimit(maxChars) && !hasUnsafeStructuredSerialization(property.value)) return {
|
|
866
|
+
value: property.value,
|
|
867
|
+
changed: false
|
|
868
|
+
};
|
|
869
|
+
} catch {
|
|
870
|
+
return {
|
|
871
|
+
value: void 0,
|
|
872
|
+
changed: true
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
const projectedArgs = selectProjectedSerializedArguments(argsProperty, void 0, maxChars);
|
|
876
|
+
return {
|
|
877
|
+
value: {
|
|
878
|
+
name: nameProperty.value,
|
|
879
|
+
arguments: projectedArgs.value
|
|
880
|
+
},
|
|
881
|
+
changed: true
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
function projectResponsesOutput(output, maxChars, canonicalArguments, canonicalInputs) {
|
|
885
|
+
if (!Array.isArray(output)) return {
|
|
886
|
+
value: output,
|
|
887
|
+
changed: false
|
|
888
|
+
};
|
|
889
|
+
const state = { changed: false };
|
|
890
|
+
const projected = output.map((item) => {
|
|
891
|
+
if (item == null || typeof item !== "object") return item;
|
|
892
|
+
const type = getStringProperty(item, "type");
|
|
893
|
+
if (type !== "function_call" && type !== "custom_tool_call" && type !== "computer_call") return item;
|
|
894
|
+
let inputKey = "action";
|
|
895
|
+
if (type === "function_call") inputKey = "arguments";
|
|
896
|
+
else if (type === "custom_tool_call") inputKey = "input";
|
|
897
|
+
const inputProperty = readPropertyWithoutAccessors(item, inputKey);
|
|
898
|
+
if (!inputProperty.found) return item;
|
|
899
|
+
const callId = getStringProperty(item, "call_id") ?? getStringProperty(item, "id");
|
|
900
|
+
let projectedInput;
|
|
901
|
+
if (type === "function_call") projectedInput = selectProjectedSerializedArguments(inputProperty, callId != null ? canonicalArguments.get(callId) : void 0, maxChars);
|
|
902
|
+
else {
|
|
903
|
+
const canonicalArgs = callId != null ? canonicalInputs.get(callId) : void 0;
|
|
904
|
+
const canonicalProperty = canonicalArgs != null && typeof canonicalArgs === "object" ? readPropertyWithoutAccessors(canonicalArgs, inputKey) : void 0;
|
|
905
|
+
let source = inputProperty.accessor ? ACCESSOR_INPUT_PLACEHOLDER : inputProperty.value;
|
|
906
|
+
if (canonicalProperty != null && canonicalProperty.found) source = canonicalProperty.accessor ? ACCESSOR_INPUT_PLACEHOLDER : canonicalProperty.value;
|
|
907
|
+
if (type === "custom_tool_call" && typeof source === "string" && source.length <= normalizeToolInputLimit(maxChars)) projectedInput = {
|
|
908
|
+
value: source,
|
|
909
|
+
changed: !inputProperty.own || inputProperty.value !== source
|
|
649
910
|
};
|
|
911
|
+
else if (type === "custom_tool_call") {
|
|
912
|
+
const value = typeof source === "string" ? truncateToolResultContent(source, normalizeToolInputLimit(maxChars)) : serializeToolCallInput(source, maxChars);
|
|
913
|
+
projectedInput = {
|
|
914
|
+
value,
|
|
915
|
+
changed: !inputProperty.own || inputProperty.value !== value
|
|
916
|
+
};
|
|
917
|
+
} else {
|
|
918
|
+
const projected = projectToolInputWithinLimit(source, maxChars);
|
|
919
|
+
projectedInput = {
|
|
920
|
+
value: projected.value,
|
|
921
|
+
changed: projected.changed || !inputProperty.own || inputProperty.value !== projected.value
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
if (!projectedInput.changed && inputProperty.own) return item;
|
|
926
|
+
state.changed = true;
|
|
927
|
+
return cloneWithProjectedProperties(item, { [inputKey]: projectedInput.value });
|
|
928
|
+
});
|
|
929
|
+
return {
|
|
930
|
+
value: state.changed ? projected : output,
|
|
931
|
+
changed: state.changed
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
/** Per-input cap: 15% of context at ~4 chars/token, never above 200K chars. */
|
|
935
|
+
function calculateMaxToolCallInputChars(maxContextTokens) {
|
|
936
|
+
if (maxContextTokens == null || maxContextTokens <= 0) return HARD_MAX_TOOL_CALL_INPUT_CHARS;
|
|
937
|
+
return Math.max(MIN_JSON_VALUE_CHARS, Math.min(Math.floor(maxContextTokens * .15) * 4, HARD_MAX_TOOL_CALL_INPUT_CHARS));
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* Projects historical tool-call inputs into a provider-safe bounded form.
|
|
941
|
+
* Returns the original array when no message changes and otherwise clones only
|
|
942
|
+
* the array and AI messages whose inline input or `tool_calls` args changed.
|
|
943
|
+
*/
|
|
944
|
+
function projectToolCallInputs(messages, maxInputChars) {
|
|
945
|
+
const normalizedMaxInputChars = normalizeToolInputLimit(maxInputChars);
|
|
946
|
+
let projectedMessages;
|
|
947
|
+
for (let i = 0; i < messages.length; i++) {
|
|
948
|
+
const message = messages[i];
|
|
949
|
+
const messageRole = message.role;
|
|
950
|
+
if (message.getType() !== "ai" && messageRole !== "assistant") continue;
|
|
951
|
+
const aiMessage = message;
|
|
952
|
+
let projectedContent = aiMessage.content;
|
|
953
|
+
let contentChanged = false;
|
|
954
|
+
if (Array.isArray(aiMessage.content)) {
|
|
955
|
+
const originalContent = aiMessage.content;
|
|
956
|
+
const mappedContent = originalContent.map((block) => projectInlineToolInput(block, normalizedMaxInputChars));
|
|
957
|
+
contentChanged = mappedContent.some((block, blockIndex) => block !== originalContent[blockIndex]);
|
|
958
|
+
projectedContent = toLangChainContent(mappedContent);
|
|
959
|
+
}
|
|
960
|
+
const originalToolCalls = aiMessage.tool_calls ?? [];
|
|
961
|
+
const projectedToolCalls = originalToolCalls.map((toolCall) => {
|
|
962
|
+
const argsProperty = readPropertyWithoutAccessors(toolCall, "args");
|
|
963
|
+
const projected = argsProperty.accessor ? {
|
|
964
|
+
value: ACCESSOR_INPUT_PLACEHOLDER,
|
|
965
|
+
changed: true
|
|
966
|
+
} : projectToolInputWithinLimit(argsProperty.value, normalizedMaxInputChars);
|
|
967
|
+
if (argsProperty.own && !projected.changed) return toolCall;
|
|
968
|
+
return cloneWithProjectedProperties(toolCall, { args: projected.value });
|
|
650
969
|
});
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
970
|
+
const toolCallsChanged = projectedToolCalls.some((toolCall, toolCallIndex) => toolCall !== originalToolCalls[toolCallIndex]);
|
|
971
|
+
const rawAdditionalToolCallsProperty = readPropertyWithoutAccessors(aiMessage.additional_kwargs, "tool_calls");
|
|
972
|
+
const rawAdditionalToolCalls = rawAdditionalToolCallsProperty.accessor ? void 0 : rawAdditionalToolCallsProperty.value;
|
|
973
|
+
const rawResponsesOutputProperty = readPropertyWithoutAccessors(aiMessage.response_metadata, "output");
|
|
974
|
+
const rawResponsesOutput = rawResponsesOutputProperty.accessor ? void 0 : rawResponsesOutputProperty.value;
|
|
975
|
+
const canonicalArguments = /* @__PURE__ */ new Map();
|
|
976
|
+
const canonicalInputs = /* @__PURE__ */ new Map();
|
|
977
|
+
if (Array.isArray(rawAdditionalToolCalls) || Array.isArray(rawResponsesOutput)) for (const toolCall of projectedToolCalls) {
|
|
978
|
+
const id = getStringProperty(toolCall, "id");
|
|
979
|
+
if (id == null) continue;
|
|
980
|
+
const args = readPropertyWithoutAccessors(toolCall, "args");
|
|
981
|
+
const canonicalInput = args.accessor ? ACCESSOR_INPUT_PLACEHOLDER : args.value;
|
|
982
|
+
canonicalInputs.set(id, canonicalInput);
|
|
983
|
+
canonicalArguments.set(id, serializeToolCallInput(canonicalInput, normalizedMaxInputChars));
|
|
984
|
+
}
|
|
985
|
+
const additionalKwargsChanges = rawAdditionalToolCallsProperty.accessor ? { tool_calls: void 0 } : {};
|
|
986
|
+
const rawToolCalls = projectRawOpenAIToolCalls(rawAdditionalToolCalls, normalizedMaxInputChars, canonicalArguments);
|
|
987
|
+
if (rawToolCalls.changed) additionalKwargsChanges.tool_calls = rawToolCalls.value;
|
|
988
|
+
const legacyFunctionCall = projectLegacyFunctionCall(readPropertyWithoutAccessors(aiMessage.additional_kwargs, "function_call"), normalizedMaxInputChars);
|
|
989
|
+
if (legacyFunctionCall.changed) additionalKwargsChanges.function_call = legacyFunctionCall.value;
|
|
990
|
+
const additionalKwargsChanged = Object.keys(additionalKwargsChanges).length > 0;
|
|
991
|
+
let projectedAdditionalKwargs = aiMessage.additional_kwargs;
|
|
992
|
+
if (additionalKwargsChanged) projectedAdditionalKwargs = isProxy(aiMessage.additional_kwargs) ? { ...additionalKwargsChanges } : cloneWithProjectedProperties(aiMessage.additional_kwargs, additionalKwargsChanges);
|
|
993
|
+
let projectedResponseMetadata = aiMessage.response_metadata;
|
|
994
|
+
let responseMetadataChanged = rawResponsesOutputProperty.accessor;
|
|
995
|
+
const responsesOutput = projectResponsesOutput(rawResponsesOutput, normalizedMaxInputChars, canonicalArguments, canonicalInputs);
|
|
996
|
+
if (responsesOutput.changed || rawResponsesOutputProperty.accessor) {
|
|
997
|
+
const output = rawResponsesOutputProperty.accessor ? void 0 : responsesOutput.value;
|
|
998
|
+
projectedResponseMetadata = isProxy(aiMessage.response_metadata) ? { output } : cloneWithProjectedProperties(aiMessage.response_metadata, { output });
|
|
999
|
+
responseMetadataChanged = true;
|
|
1000
|
+
}
|
|
1001
|
+
if (!contentChanged && !toolCallsChanged && !additionalKwargsChanged && !responseMetadataChanged) continue;
|
|
1002
|
+
const projectedMessage = cloneWithProjectedProperties(aiMessage, {
|
|
1003
|
+
content: projectedContent,
|
|
1004
|
+
tool_calls: projectedToolCalls.length > 0 ? projectedToolCalls : [],
|
|
1005
|
+
additional_kwargs: projectedAdditionalKwargs,
|
|
1006
|
+
response_metadata: projectedResponseMetadata
|
|
655
1007
|
});
|
|
656
|
-
|
|
1008
|
+
projectedMessages ??= [...messages];
|
|
1009
|
+
projectedMessages[i] = projectedMessage;
|
|
1010
|
+
}
|
|
1011
|
+
return projectedMessages ?? messages;
|
|
1012
|
+
}
|
|
1013
|
+
function preFlightTruncateToolCallInputs(params) {
|
|
1014
|
+
const { messages, maxContextTokens, indexTokenCountMap, tokenCounter } = params;
|
|
1015
|
+
const projected = projectToolCallInputs(messages, calculateMaxToolCallInputChars(maxContextTokens));
|
|
1016
|
+
if (projected === messages) return 0;
|
|
1017
|
+
let truncatedCount = 0;
|
|
1018
|
+
for (let i = 0; i < messages.length; i++) {
|
|
1019
|
+
if (projected[i] === messages[i]) continue;
|
|
1020
|
+
messages[i] = projected[i];
|
|
1021
|
+
indexTokenCountMap[i] = tokenCounter(projected[i]);
|
|
657
1022
|
truncatedCount++;
|
|
658
1023
|
}
|
|
659
1024
|
return truncatedCount;
|
|
@@ -664,6 +1029,7 @@ function createPruneMessages(factoryParams) {
|
|
|
664
1029
|
let lastCutOffIndex = 0;
|
|
665
1030
|
let totalTokens = 0;
|
|
666
1031
|
for (const key in indexTokenCountMap) totalTokens += indexTokenCountMap[key] ?? 0;
|
|
1032
|
+
const reconciledToolMessages = /* @__PURE__ */ new WeakSet();
|
|
667
1033
|
let runThinkingStartIndex = -1;
|
|
668
1034
|
/** Cumulative raw tiktoken tokens we've sent to the provider (messages only,
|
|
669
1035
|
* excludes instruction overhead and new outputs not yet seen by provider). */
|
|
@@ -676,6 +1042,7 @@ function createPruneMessages(factoryParams) {
|
|
|
676
1042
|
/** Best observed instruction overhead from a near-zero variance turn.
|
|
677
1043
|
* Self-seeds from provider observations within the run. */
|
|
678
1044
|
let bestInstructionOverhead;
|
|
1045
|
+
const reconciledLegacyAiMessages = /* @__PURE__ */ new WeakSet();
|
|
679
1046
|
let bestVarianceAbs = Infinity;
|
|
680
1047
|
/** Local estimate at the time bestInstructionOverhead was observed.
|
|
681
1048
|
* Used to invalidate the cached overhead when instructions change
|
|
@@ -696,12 +1063,13 @@ function createPruneMessages(factoryParams) {
|
|
|
696
1063
|
const emptyInstructionTokens = factoryParams.getInstructionTokens?.() ?? 0;
|
|
697
1064
|
const emptyReserveRatio = factoryParams.reserveRatio ?? .05;
|
|
698
1065
|
const emptyBudget = factoryParams.maxTokens - (emptyReserveRatio > 0 && emptyReserveRatio < 1 ? Math.round(factoryParams.maxTokens * emptyReserveRatio) : 0);
|
|
1066
|
+
const emptyReplyPrimerTokens = Math.round(3 * calibrationRatio);
|
|
699
1067
|
return {
|
|
700
1068
|
context: [],
|
|
701
1069
|
indexTokenCountMap,
|
|
702
1070
|
messagesToRefine: [],
|
|
703
1071
|
prePruneContextTokens: 0,
|
|
704
|
-
remainingContextTokens: Math.max(0, emptyBudget - emptyInstructionTokens),
|
|
1072
|
+
remainingContextTokens: Math.max(0, emptyBudget - emptyInstructionTokens - emptyReplyPrimerTokens),
|
|
705
1073
|
calibrationRatio,
|
|
706
1074
|
resolvedInstructionOverhead: bestInstructionOverhead,
|
|
707
1075
|
contextBudget: emptyBudget,
|
|
@@ -731,6 +1099,45 @@ function createPruneMessages(factoryParams) {
|
|
|
731
1099
|
let currentUsage;
|
|
732
1100
|
if (params.usageMetadata && (checkValidNumber(params.usageMetadata.input_tokens) || checkValidNumber(params.usageMetadata.input_token_details) && (checkValidNumber(params.usageMetadata.input_token_details.cache_creation) || checkValidNumber(params.usageMetadata.input_token_details.cache_read))) && checkValidNumber(params.usageMetadata.output_tokens)) currentUsage = calculateTotalTokens(params.usageMetadata);
|
|
733
1101
|
const newOutputs = /* @__PURE__ */ new Set();
|
|
1102
|
+
const applyReconciledCount = (index, cachedCount, reconciledCount) => {
|
|
1103
|
+
if (cachedCount === void 0) {
|
|
1104
|
+
indexTokenCountMap[index] = reconciledCount;
|
|
1105
|
+
totalTokens += reconciledCount;
|
|
1106
|
+
if (index >= lastTurnStartIndex) newOutputs.add(index);
|
|
1107
|
+
return;
|
|
1108
|
+
}
|
|
1109
|
+
if (reconciledCount <= cachedCount) return;
|
|
1110
|
+
indexTokenCountMap[index] = reconciledCount;
|
|
1111
|
+
totalTokens += reconciledCount - cachedCount;
|
|
1112
|
+
if (index >= lastTurnStartIndex) newOutputs.add(index);
|
|
1113
|
+
};
|
|
1114
|
+
for (let i = 0; i < params.messages.length; i++) {
|
|
1115
|
+
let message = params.messages[i];
|
|
1116
|
+
const cachedCount = indexTokenCountMap[i];
|
|
1117
|
+
const messageType = message.getType();
|
|
1118
|
+
const messageRole = message.role;
|
|
1119
|
+
if ((messageType === "ai" || messageRole === "assistant" ? readPropertyWithoutAccessors(message.additional_kwargs, "function_call") : void 0)?.found === true && !reconciledLegacyAiMessages.has(message)) {
|
|
1120
|
+
const [projected] = projectToolCallInputs([message], calculateMaxToolCallInputChars(factoryParams.maxTokens));
|
|
1121
|
+
if (projected !== message) {
|
|
1122
|
+
message = projected;
|
|
1123
|
+
params.messages[i] = message;
|
|
1124
|
+
}
|
|
1125
|
+
reconciledLegacyAiMessages.add(message);
|
|
1126
|
+
if (cachedCount !== void 0 || i < lastTurnStartIndex) applyReconciledCount(i, cachedCount, factoryParams.tokenCounter(message));
|
|
1127
|
+
continue;
|
|
1128
|
+
}
|
|
1129
|
+
if (messageType !== "tool" || reconciledToolMessages.has(message)) continue;
|
|
1130
|
+
if (!isComputerCallOutputMessage(message)) {
|
|
1131
|
+
const normalized = compactToolContent(message.content, factoryParams.maxToolResultChars ?? 4e5);
|
|
1132
|
+
if (normalized.changed) {
|
|
1133
|
+
message = cloneToolMessageWithContent(message, normalized.content);
|
|
1134
|
+
params.messages[i] = message;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
const reconciledCount = factoryParams.tokenCounter(message);
|
|
1138
|
+
reconciledToolMessages.add(message);
|
|
1139
|
+
applyReconciledCount(i, cachedCount, reconciledCount);
|
|
1140
|
+
}
|
|
734
1141
|
let outputTokensAssigned = false;
|
|
735
1142
|
for (let i = lastTurnStartIndex; i < params.messages.length; i++) {
|
|
736
1143
|
const message = params.messages[i];
|
|
@@ -912,12 +1319,13 @@ function createPruneMessages(factoryParams) {
|
|
|
912
1319
|
tokensSaved: preTruncationTotalTokens - totalTokens
|
|
913
1320
|
});
|
|
914
1321
|
lastTurnStartIndex = params.messages.length;
|
|
915
|
-
|
|
1322
|
+
const calibratedReplyPrimerTokens = Math.round(3 * calibrationRatio);
|
|
1323
|
+
if (lastCutOffIndex === 0 && calibratedTotalTokens + calibratedReplyPrimerTokens + currentInstructionTokens <= pruningBudget) return {
|
|
916
1324
|
context: params.messages,
|
|
917
1325
|
indexTokenCountMap,
|
|
918
1326
|
messagesToRefine: [],
|
|
919
1327
|
prePruneContextTokens: calibratedTotalTokens,
|
|
920
|
-
remainingContextTokens: pruningBudget - calibratedTotalTokens - currentInstructionTokens,
|
|
1328
|
+
remainingContextTokens: pruningBudget - calibratedTotalTokens - calibratedReplyPrimerTokens - currentInstructionTokens,
|
|
921
1329
|
contextPressure,
|
|
922
1330
|
originalToolContent: originalToolContent.size > 0 ? originalToolContent : void 0,
|
|
923
1331
|
newOriginalToolContent,
|
|
@@ -1027,60 +1435,24 @@ function createPruneMessages(factoryParams) {
|
|
|
1027
1435
|
let emergencyTruncatedCount = 0;
|
|
1028
1436
|
for (let i = 0; i < emergencyMessages.length; i++) {
|
|
1029
1437
|
const message = emergencyMessages[i];
|
|
1030
|
-
if (message.getType() === "tool") {
|
|
1438
|
+
if (message.getType() === "tool" && !isComputerCallOutputMessage(message)) {
|
|
1031
1439
|
const content = message.content;
|
|
1032
|
-
if (
|
|
1033
|
-
const
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
name: message.name,
|
|
1037
|
-
id: message.id,
|
|
1038
|
-
additional_kwargs: message.additional_kwargs,
|
|
1039
|
-
response_metadata: message.response_metadata
|
|
1040
|
-
});
|
|
1440
|
+
if (getToolContentCharLength(content) > emergencyMaxChars) {
|
|
1441
|
+
const compacted = compactToolContent(content, emergencyMaxChars);
|
|
1442
|
+
if (!compacted.changed) continue;
|
|
1443
|
+
const cloned = cloneToolMessageWithContent(message, compacted.content);
|
|
1041
1444
|
emergencyMessages[i] = cloned;
|
|
1042
1445
|
indexTokenCountMap[i] = factoryParams.tokenCounter(cloned);
|
|
1043
1446
|
emergencyTruncatedCount++;
|
|
1044
1447
|
}
|
|
1045
1448
|
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
return false;
|
|
1054
|
-
})) {
|
|
1055
|
-
const newContent = contentBlocks.map((block) => {
|
|
1056
|
-
if (typeof block !== "object") return block;
|
|
1057
|
-
const record = block;
|
|
1058
|
-
if ((record.type === "tool_use" || record.type === "tool_call") && record.input != null) {
|
|
1059
|
-
const serialized = typeof record.input === "string" ? record.input : JSON.stringify(record.input);
|
|
1060
|
-
if (serialized.length > emergencyMaxChars) return {
|
|
1061
|
-
...record,
|
|
1062
|
-
input: truncateToolInput(serialized, emergencyMaxChars)
|
|
1063
|
-
};
|
|
1064
|
-
}
|
|
1065
|
-
return block;
|
|
1066
|
-
});
|
|
1067
|
-
const newToolCalls = (aiMsg.tool_calls ?? []).map((tc) => {
|
|
1068
|
-
const serializedArgs = JSON.stringify(tc.args);
|
|
1069
|
-
if (serializedArgs.length > emergencyMaxChars) return {
|
|
1070
|
-
...tc,
|
|
1071
|
-
args: truncateToolInput(serializedArgs, emergencyMaxChars)
|
|
1072
|
-
};
|
|
1073
|
-
return tc;
|
|
1074
|
-
});
|
|
1075
|
-
emergencyMessages[i] = new AIMessage({
|
|
1076
|
-
...aiMsg,
|
|
1077
|
-
content: toLangChainContent(newContent),
|
|
1078
|
-
tool_calls: newToolCalls.length > 0 ? newToolCalls : void 0
|
|
1079
|
-
});
|
|
1080
|
-
indexTokenCountMap[i] = factoryParams.tokenCounter(emergencyMessages[i]);
|
|
1081
|
-
emergencyTruncatedCount++;
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1449
|
+
}
|
|
1450
|
+
const projectedToolInputs = projectToolCallInputs(emergencyMessages, emergencyMaxChars);
|
|
1451
|
+
if (projectedToolInputs !== emergencyMessages) for (let i = 0; i < emergencyMessages.length; i++) {
|
|
1452
|
+
if (projectedToolInputs[i] === emergencyMessages[i]) continue;
|
|
1453
|
+
emergencyMessages[i] = projectedToolInputs[i];
|
|
1454
|
+
indexTokenCountMap[i] = factoryParams.tokenCounter(projectedToolInputs[i]);
|
|
1455
|
+
emergencyTruncatedCount++;
|
|
1084
1456
|
}
|
|
1085
1457
|
factoryParams.log?.("info", "Emergency truncation complete");
|
|
1086
1458
|
factoryParams.log?.("debug", "Emergency truncation details", {
|
|
@@ -1141,6 +1513,6 @@ function createPruneMessages(factoryParams) {
|
|
|
1141
1513
|
};
|
|
1142
1514
|
}
|
|
1143
1515
|
//#endregion
|
|
1144
|
-
export { CALIBRATION_RATIO_MAX, CALIBRATION_RATIO_MIN, DEFAULT_RESERVE_RATIO, ORIGINAL_CONTENT_MAX_CHARS, calculateTotalTokens, checkValidNumber, clampCalibrationRatio, createPruneMessages, enforceOriginalContentCap, getMessagesWithinTokenLimit, maskConsumedToolResults, preFlightTruncateToolCallInputs, preFlightTruncateToolResults, repairOrphanedToolMessages, sanitizeOrphanToolBlocks };
|
|
1516
|
+
export { CALIBRATION_RATIO_MAX, CALIBRATION_RATIO_MIN, DEFAULT_RESERVE_RATIO, ORIGINAL_CONTENT_MAX_CHARS, REPLY_PRIMER_TOKENS, calculateMaxToolCallInputChars, calculateTotalTokens, checkValidNumber, clampCalibrationRatio, createPruneMessages, enforceOriginalContentCap, getMessagesWithinTokenLimit, maskConsumedToolResults, preFlightTruncateToolCallInputs, preFlightTruncateToolResults, projectToolCallInputs, repairOrphanedToolMessages, sanitizeOrphanToolBlocks, serializeToolCallInput };
|
|
1145
1517
|
|
|
1146
1518
|
//# sourceMappingURL=prune.mjs.map
|