@librechat/agents 3.7.22 → 3.8.1
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 +13 -3
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +174 -53
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +15 -5
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/langfuse.cjs +48 -2
- package/dist/cjs/langfuse.cjs.map +1 -1
- package/dist/cjs/langfuseToolOutputTracing.cjs +42 -0
- package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
- package/dist/cjs/langfuseTraceShaping.cjs +7 -1
- package/dist/cjs/langfuseTraceShaping.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +5 -9
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +2 -7
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/contextPressureMeter.cjs +51 -10
- package/dist/cjs/llm/contextPressureMeter.cjs.map +1 -1
- package/dist/cjs/llm/init.cjs +1 -1
- package/dist/cjs/llm/invoke.cjs +2 -2
- package/dist/cjs/llm/openai/index.cjs +4 -3
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs +5 -4
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/llm/preempt.cjs +3 -2
- package/dist/cjs/llm/preempt.cjs.map +1 -1
- package/dist/cjs/llm/prepareProviderRequest.cjs +9 -7
- package/dist/cjs/llm/prepareProviderRequest.cjs.map +1 -1
- package/dist/cjs/llm/providers.cjs +1 -1
- package/dist/cjs/main.cjs +11 -5
- package/dist/cjs/messages/alternation.cjs +1 -5
- package/dist/cjs/messages/alternation.cjs.map +1 -1
- package/dist/cjs/messages/budget.cjs +206 -7
- package/dist/cjs/messages/budget.cjs.map +1 -1
- package/dist/cjs/messages/cache.cjs +3 -9
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +25 -37
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +131 -42
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/index.cjs +2 -0
- package/dist/cjs/messages/prune.cjs +12 -7
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/messages/reasoningTypes.cjs +21 -0
- package/dist/cjs/messages/reasoningTypes.cjs.map +1 -0
- package/dist/cjs/messages/recency.cjs +15 -94
- package/dist/cjs/messages/recency.cjs.map +1 -1
- package/dist/cjs/messages/toolHistoryProjection.cjs +243 -0
- package/dist/cjs/messages/toolHistoryProjection.cjs.map +1 -0
- package/dist/cjs/messages/toolResultTypes.cjs +145 -4
- package/dist/cjs/messages/toolResultTypes.cjs.map +1 -1
- package/dist/cjs/run.cjs +5 -4
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/AgentSession.cjs +6 -4
- package/dist/cjs/session/AgentSession.cjs.map +1 -1
- package/dist/cjs/session/JsonlSessionStore.cjs +3 -0
- package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -1
- package/dist/cjs/session/index.cjs +1 -1
- package/dist/cjs/session/sessionProjection.cjs +75 -0
- package/dist/cjs/session/sessionProjection.cjs.map +1 -0
- package/dist/cjs/stream.cjs +18 -17
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/index.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +18 -8
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/summarization/shared.cjs +9 -0
- package/dist/cjs/summarization/shared.cjs.map +1 -1
- package/dist/cjs/tools/ArtifactDelivery.cjs +27 -0
- package/dist/cjs/tools/ArtifactDelivery.cjs.map +1 -0
- package/dist/cjs/tools/BashExecutor.cjs +6 -1
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +6 -1
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +5 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/local/CompileCheckTool.cjs +1 -1
- package/dist/cjs/tools/local/LocalCodingTools.cjs +1 -1
- package/dist/cjs/utils/events.cjs +13 -0
- package/dist/cjs/utils/events.cjs.map +1 -1
- package/dist/cjs/utils/tokens.cjs +105 -0
- package/dist/cjs/utils/tokens.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +13 -3
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +176 -55
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +15 -5
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/langfuse.mjs +49 -4
- package/dist/esm/langfuse.mjs.map +1 -1
- package/dist/esm/langfuseToolOutputTracing.mjs +41 -1
- package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
- package/dist/esm/langfuseTraceShaping.mjs +7 -1
- package/dist/esm/langfuseTraceShaping.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +5 -9
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs +2 -7
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/contextPressureMeter.mjs +52 -11
- package/dist/esm/llm/contextPressureMeter.mjs.map +1 -1
- package/dist/esm/llm/init.mjs +1 -1
- package/dist/esm/llm/invoke.mjs +2 -2
- package/dist/esm/llm/openai/index.mjs +2 -1
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs +5 -4
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/llm/preempt.mjs +2 -1
- package/dist/esm/llm/preempt.mjs.map +1 -1
- package/dist/esm/llm/prepareProviderRequest.mjs +9 -7
- package/dist/esm/llm/prepareProviderRequest.mjs.map +1 -1
- package/dist/esm/llm/providers.mjs +1 -1
- package/dist/esm/main.mjs +10 -7
- package/dist/esm/messages/alternation.mjs +2 -6
- package/dist/esm/messages/alternation.mjs.map +1 -1
- package/dist/esm/messages/budget.mjs +206 -8
- package/dist/esm/messages/budget.mjs.map +1 -1
- package/dist/esm/messages/cache.mjs +3 -9
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/messages/core.mjs +23 -33
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +132 -43
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/index.mjs +2 -0
- package/dist/esm/messages/prune.mjs +12 -7
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/messages/reasoningTypes.mjs +20 -0
- package/dist/esm/messages/reasoningTypes.mjs.map +1 -0
- package/dist/esm/messages/recency.mjs +16 -95
- package/dist/esm/messages/recency.mjs.map +1 -1
- package/dist/esm/messages/toolHistoryProjection.mjs +237 -0
- package/dist/esm/messages/toolHistoryProjection.mjs.map +1 -0
- package/dist/esm/messages/toolResultTypes.mjs +141 -4
- package/dist/esm/messages/toolResultTypes.mjs.map +1 -1
- package/dist/esm/run.mjs +5 -4
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/AgentSession.mjs +6 -4
- package/dist/esm/session/AgentSession.mjs.map +1 -1
- package/dist/esm/session/JsonlSessionStore.mjs +3 -0
- package/dist/esm/session/JsonlSessionStore.mjs.map +1 -1
- package/dist/esm/session/index.mjs +1 -1
- package/dist/esm/session/sessionProjection.mjs +72 -0
- package/dist/esm/session/sessionProjection.mjs.map +1 -0
- package/dist/esm/stream.mjs +15 -14
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/index.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +19 -9
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/summarization/shared.mjs +9 -1
- package/dist/esm/summarization/shared.mjs.map +1 -1
- package/dist/esm/tools/ArtifactDelivery.mjs +26 -0
- package/dist/esm/tools/ArtifactDelivery.mjs.map +1 -0
- package/dist/esm/tools/BashExecutor.mjs +6 -1
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +6 -1
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +5 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/local/CompileCheckTool.mjs +1 -1
- package/dist/esm/tools/local/LocalCodingTools.mjs +1 -1
- package/dist/esm/utils/events.mjs +13 -0
- package/dist/esm/utils/events.mjs.map +1 -1
- package/dist/esm/utils/tokens.mjs +105 -1
- package/dist/esm/utils/tokens.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +13 -1
- package/dist/types/graphs/Graph.d.ts +27 -0
- package/dist/types/hooks/types.d.ts +4 -2
- package/dist/types/index.d.ts +1 -0
- package/dist/types/langfuse.d.ts +10 -0
- package/dist/types/langfuseToolOutputTracing.d.ts +2 -0
- package/dist/types/llm/contextPressureMeter.d.ts +2 -1
- package/dist/types/llm/prepareProviderRequest.d.ts +7 -1
- package/dist/types/messages/budget.d.ts +15 -10
- package/dist/types/messages/core.d.ts +8 -17
- package/dist/types/messages/format.d.ts +3 -2
- package/dist/types/messages/index.d.ts +1 -1
- package/dist/types/messages/prune.d.ts +1 -1
- package/dist/types/messages/reasoningTypes.d.ts +7 -0
- package/dist/types/messages/recency.d.ts +3 -0
- package/dist/types/messages/toolHistoryProjection.d.ts +65 -0
- package/dist/types/messages/toolResultTypes.d.ts +31 -1
- package/dist/types/session/sessionProjection.d.ts +10 -0
- package/dist/types/summarization/index.d.ts +2 -1
- package/dist/types/summarization/node.d.ts +1 -0
- package/dist/types/summarization/shared.d.ts +12 -0
- package/dist/types/tools/ArtifactDelivery.d.ts +4 -0
- package/dist/types/types/graph.d.ts +28 -0
- package/dist/types/types/run.d.ts +4 -0
- package/dist/types/types/summarize.d.ts +4 -1
- package/dist/types/types/tools.d.ts +11 -0
- package/dist/types/utils/tokens.d.ts +2 -1
- package/package.json +1 -1
- package/src/agents/AgentContext.ts +34 -3
- package/src/graphs/Graph.ts +465 -147
- package/src/graphs/MultiAgentGraph.ts +28 -6
- package/src/hooks/types.ts +4 -1
- package/src/index.ts +1 -0
- package/src/langfuse.ts +84 -11
- package/src/langfuseToolOutputTracing.ts +91 -0
- package/src/langfuseTraceShaping.ts +17 -4
- package/src/llm/anthropic/utils/message_inputs.ts +5 -10
- package/src/llm/bedrock/utils/message_inputs.ts +2 -8
- package/src/llm/contextPressureMeter.ts +80 -12
- package/src/llm/openai/utils/index.ts +5 -4
- package/src/llm/prepareProviderRequest.ts +21 -16
- package/src/messages/alternation.ts +2 -15
- package/src/messages/budget.ts +439 -23
- package/src/messages/cache.ts +8 -9
- package/src/messages/core.ts +65 -95
- package/src/messages/format.ts +262 -78
- package/src/messages/index.ts +1 -1
- package/src/messages/prune.ts +31 -12
- package/src/messages/reasoningTypes.ts +23 -0
- package/src/messages/recency.ts +35 -179
- package/src/messages/toolHistoryProjection.ts +460 -0
- package/src/messages/toolResultTypes.ts +331 -100
- package/src/run.ts +10 -2
- package/src/session/AgentSession.ts +33 -16
- package/src/session/JsonlSessionStore.ts +6 -0
- package/src/session/sessionProjection.ts +126 -0
- package/src/stream.ts +14 -19
- package/src/summarization/index.ts +2 -0
- package/src/summarization/node.ts +62 -13
- package/src/summarization/shared.ts +23 -0
- package/src/tools/ArtifactDelivery.ts +50 -0
- package/src/tools/BashExecutor.ts +18 -1
- package/src/tools/CodeExecutor.ts +18 -1
- package/src/tools/ProgrammaticToolCalling.ts +15 -1
- package/src/types/graph.ts +28 -0
- package/src/types/run.ts +4 -0
- package/src/types/summarize.ts +4 -1
- package/src/types/tools.ts +12 -0
- package/src/utils/events.ts +19 -0
- package/src/utils/tokens.ts +183 -0
|
@@ -1267,6 +1267,11 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
1267
1267
|
reducer: (_current, update) => update,
|
|
1268
1268
|
default: () => undefined,
|
|
1269
1269
|
}),
|
|
1270
|
+
/** Surfaced from the summarizing agent's subgraph on a summarize-only run. */
|
|
1271
|
+
manualSummary: Annotation<string | undefined>({
|
|
1272
|
+
reducer: (_current, update) => update,
|
|
1273
|
+
default: () => undefined,
|
|
1274
|
+
}),
|
|
1270
1275
|
runStepState: this.createRunStepStateAnnotation(),
|
|
1271
1276
|
});
|
|
1272
1277
|
|
|
@@ -1281,14 +1286,29 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
1281
1286
|
builder.addEdge(source, destination);
|
|
1282
1287
|
};
|
|
1283
1288
|
|
|
1289
|
+
/**
|
|
1290
|
+
* A summarize-only run compiles to the opted-in agent alone: no other
|
|
1291
|
+
* node, no handoff or direct edge, START → agent → END. Anything else
|
|
1292
|
+
* would run the summarizer twice (once from START, once through its
|
|
1293
|
+
* predecessor) or append routing prompts to the checkpoint it just
|
|
1294
|
+
* produced.
|
|
1295
|
+
*/
|
|
1296
|
+
const summarizeOnlyAgentId = this.summarizeOnlyAgentId;
|
|
1297
|
+
const agentIds =
|
|
1298
|
+
summarizeOnlyAgentId != null
|
|
1299
|
+
? [summarizeOnlyAgentId]
|
|
1300
|
+
: [...this.agentContexts.keys()];
|
|
1301
|
+
const handoffEdges = summarizeOnlyAgentId != null ? [] : this.handoffEdges;
|
|
1302
|
+
const directEdges = summarizeOnlyAgentId != null ? [] : this.directEdges;
|
|
1303
|
+
|
|
1284
1304
|
// Add all agents as complete subgraphs
|
|
1285
|
-
for (const
|
|
1305
|
+
for (const agentId of agentIds) {
|
|
1286
1306
|
// Get all possible destinations for this agent
|
|
1287
1307
|
const handoffDestinations = new Set<string>();
|
|
1288
1308
|
const directDestinations = new Set<string>();
|
|
1289
1309
|
|
|
1290
1310
|
// Check handoff edges for destinations
|
|
1291
|
-
for (const edge of
|
|
1311
|
+
for (const edge of handoffEdges) {
|
|
1292
1312
|
const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
|
|
1293
1313
|
if (sources.includes(agentId) === true) {
|
|
1294
1314
|
const dests = Array.isArray(edge.to) ? edge.to : [edge.to];
|
|
@@ -1297,7 +1317,7 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
1297
1317
|
}
|
|
1298
1318
|
|
|
1299
1319
|
// Check direct edges for destinations
|
|
1300
|
-
for (const edge of
|
|
1320
|
+
for (const edge of directEdges) {
|
|
1301
1321
|
const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
|
|
1302
1322
|
if (sources.includes(agentId) === true) {
|
|
1303
1323
|
const dests = Array.isArray(edge.to) ? edge.to : [edge.to];
|
|
@@ -1500,6 +1520,7 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
1500
1520
|
} else {
|
|
1501
1521
|
result = await agentSubgraph.invoke(state, memberConfig);
|
|
1502
1522
|
}
|
|
1523
|
+
result = this.propagateManualCompaction(result);
|
|
1503
1524
|
|
|
1504
1525
|
if (this.resultAgentId === agentId) {
|
|
1505
1526
|
result = {
|
|
@@ -1560,8 +1581,9 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
1560
1581
|
});
|
|
1561
1582
|
}
|
|
1562
1583
|
|
|
1563
|
-
|
|
1564
|
-
|
|
1584
|
+
const startingNodes =
|
|
1585
|
+
summarizeOnlyAgentId != null ? [summarizeOnlyAgentId] : this.startingNodes;
|
|
1586
|
+
for (const startNode of startingNodes) {
|
|
1565
1587
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1566
1588
|
/** @ts-ignore */
|
|
1567
1589
|
builder.addEdge(START, startNode);
|
|
@@ -1573,7 +1595,7 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
1573
1595
|
*/
|
|
1574
1596
|
const edgesByDestination = new Map<string, t.GraphEdge[]>();
|
|
1575
1597
|
|
|
1576
|
-
for (const edge of
|
|
1598
|
+
for (const edge of directEdges) {
|
|
1577
1599
|
const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];
|
|
1578
1600
|
for (const destination of destinations) {
|
|
1579
1601
|
if (!edgesByDestination.has(destination)) {
|
package/src/hooks/types.ts
CHANGED
|
@@ -252,13 +252,16 @@ export interface PreCompactHookInput extends BaseHookInput {
|
|
|
252
252
|
/**
|
|
253
253
|
* What triggered compaction. Matches `SummarizationTrigger.type` from the
|
|
254
254
|
* agent's summarization config. `'default'` means no trigger was
|
|
255
|
-
* configured and compaction fired because messages were pruned
|
|
255
|
+
* configured and compaction fired because messages were pruned;
|
|
256
|
+
* `'manual'` means the host asked for the summary outright and no trigger
|
|
257
|
+
* was evaluated.
|
|
256
258
|
*/
|
|
257
259
|
trigger:
|
|
258
260
|
| 'token_ratio'
|
|
259
261
|
| 'remaining_tokens'
|
|
260
262
|
| 'messages_to_refine'
|
|
261
263
|
| 'default'
|
|
264
|
+
| 'manual'
|
|
262
265
|
| (string & {});
|
|
263
266
|
}
|
|
264
267
|
|
package/src/index.ts
CHANGED
package/src/langfuse.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { tool } from '@langchain/core/tools';
|
|
1
2
|
import { CallbackHandler } from '@langfuse/langchain';
|
|
2
3
|
import { LangfuseOtelContextKeys } from '@langfuse/core';
|
|
3
4
|
import { AIMessage, AIMessageChunk } from '@langchain/core/messages';
|
|
@@ -18,6 +19,7 @@ import type {
|
|
|
18
19
|
LLMResult,
|
|
19
20
|
} from '@langchain/core/outputs';
|
|
20
21
|
import type { PropagateAttributesParams } from '@langfuse/tracing';
|
|
22
|
+
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
21
23
|
import type { Context, SpanContext } from '@opentelemetry/api';
|
|
22
24
|
import type { ResolvedLangfuseToolOutputTracingConfig } from '@/langfuseRuntimeContext';
|
|
23
25
|
import type * as t from '@/types';
|
|
@@ -39,10 +41,15 @@ import {
|
|
|
39
41
|
registerLangfuseManagedSpan,
|
|
40
42
|
resolveLangfuseDestinationKey,
|
|
41
43
|
} from '@/langfuseSpanRegistry';
|
|
44
|
+
import {
|
|
45
|
+
getToolObservationMetadata,
|
|
46
|
+
LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT,
|
|
47
|
+
} from '@/langfuseToolOutputTracing';
|
|
42
48
|
import {
|
|
43
49
|
readPreemptRestartedRun,
|
|
44
50
|
PREEMPT_RESTART_CONTROL_FLOW,
|
|
45
51
|
} from '@/llm/preempt';
|
|
52
|
+
import { filterCallbacks, findCallback } from '@/utils/callbacks';
|
|
46
53
|
import { isPresent, parseBooleanEnv } from '@/utils/misc';
|
|
47
54
|
|
|
48
55
|
export {
|
|
@@ -306,10 +313,10 @@ class ScopedLangfuseCallbackHandler extends CallbackHandler {
|
|
|
306
313
|
private deferredRootStarted = false;
|
|
307
314
|
private deferredRootOutcome:
|
|
308
315
|
| {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
316
|
+
type: 'end';
|
|
317
|
+
output: Parameters<CallbackHandler['handleChainEnd']>[0];
|
|
318
|
+
parentRunId?: string;
|
|
319
|
+
}
|
|
313
320
|
| { type: 'error'; error: Error; parentRunId?: string }
|
|
314
321
|
| undefined;
|
|
315
322
|
|
|
@@ -615,11 +622,7 @@ class ScopedLangfuseCallbackHandler extends CallbackHandler {
|
|
|
615
622
|
this.deferredRootStarted = false;
|
|
616
623
|
this.deferredRootOutcome = undefined;
|
|
617
624
|
if (outcome.type === 'error') {
|
|
618
|
-
await super.handleChainError(
|
|
619
|
-
outcome.error,
|
|
620
|
-
runId,
|
|
621
|
-
outcome.parentRunId
|
|
622
|
-
);
|
|
625
|
+
await super.handleChainError(outcome.error, runId, outcome.parentRunId);
|
|
623
626
|
return;
|
|
624
627
|
}
|
|
625
628
|
await super.handleChainEnd(outcome.output, runId, outcome.parentRunId);
|
|
@@ -895,6 +898,18 @@ function mergeLangfuseTags(
|
|
|
895
898
|
return merged.length > 0 ? [...new Set(merged)] : undefined;
|
|
896
899
|
}
|
|
897
900
|
|
|
901
|
+
/**
|
|
902
|
+
* The trace's user identity: the host-configured `langfuse.userId` when
|
|
903
|
+
* present, else the caller's (normally `configurable.user_id`).
|
|
904
|
+
*/
|
|
905
|
+
export function resolveLangfuseTraceUserId(
|
|
906
|
+
langfuse: t.LangfuseConfig | undefined,
|
|
907
|
+
userId: string | undefined
|
|
908
|
+
): string | undefined {
|
|
909
|
+
const configured = langfuse?.userId?.trim();
|
|
910
|
+
return isPresent(configured) ? configured : userId;
|
|
911
|
+
}
|
|
912
|
+
|
|
898
913
|
export function getLangfuseTraceName(
|
|
899
914
|
traceMetadata?: LangfuseTraceMetadata,
|
|
900
915
|
fallback: string = 'LibreChat Agent'
|
|
@@ -942,7 +957,7 @@ export function createLangfuseHandler({
|
|
|
942
957
|
return undefined;
|
|
943
958
|
}
|
|
944
959
|
return new ScopedLangfuseCallbackHandler({
|
|
945
|
-
userId,
|
|
960
|
+
userId: resolveLangfuseTraceUserId(langfuse, userId),
|
|
946
961
|
sessionId,
|
|
947
962
|
traceMetadata:
|
|
948
963
|
inheritTraceIdentity === true
|
|
@@ -972,7 +987,10 @@ function createPropagateAttributeParams({
|
|
|
972
987
|
inheritTraceIdentity,
|
|
973
988
|
}: LangfuseAttributeParams): PropagateAttributesParams {
|
|
974
989
|
return {
|
|
975
|
-
userId:
|
|
990
|
+
userId:
|
|
991
|
+
inheritTraceIdentity === true
|
|
992
|
+
? undefined
|
|
993
|
+
: resolveLangfuseTraceUserId(langfuse, userId),
|
|
976
994
|
sessionId: inheritTraceIdentity === true ? undefined : sessionId,
|
|
977
995
|
traceName: inheritTraceIdentity === true ? undefined : traceName,
|
|
978
996
|
tags: mergeLangfuseTags(tags, langfuse?.tags),
|
|
@@ -1008,6 +1026,61 @@ export function isLangfuseCallbackHandler(value: unknown): boolean {
|
|
|
1008
1026
|
return value instanceof CallbackHandler;
|
|
1009
1027
|
}
|
|
1010
1028
|
|
|
1029
|
+
/** Host results have passed host output filters. Record only reserved metadata,
|
|
1030
|
+
* not arguments, rows, errors, or attachments. These completion observations
|
|
1031
|
+
* do not measure tool latency; execution timing belongs in the metadata. */
|
|
1032
|
+
export async function traceHostToolResults(
|
|
1033
|
+
request: t.ToolExecuteBatchRequest,
|
|
1034
|
+
results: t.ToolExecuteResult[],
|
|
1035
|
+
config?: RunnableConfig
|
|
1036
|
+
): Promise<void> {
|
|
1037
|
+
if (findCallback(config?.callbacks, isLangfuseCallbackHandler) == null) {
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
const callbacks = filterCallbacks(
|
|
1041
|
+
config?.callbacks,
|
|
1042
|
+
isLangfuseCallbackHandler
|
|
1043
|
+
);
|
|
1044
|
+
const callsById = new Map(request.toolCalls.map((call) => [call.id, call]));
|
|
1045
|
+
for (const result of results) {
|
|
1046
|
+
const metadata = getToolObservationMetadata(result.artifact);
|
|
1047
|
+
const call = callsById.get(result.toolCallId);
|
|
1048
|
+
if (call == null || Object.keys(metadata).length === 0) {
|
|
1049
|
+
continue;
|
|
1050
|
+
}
|
|
1051
|
+
const failure =
|
|
1052
|
+
result.status === 'error'
|
|
1053
|
+
? new Error('Host tool execution failed')
|
|
1054
|
+
: undefined;
|
|
1055
|
+
const observation = tool(
|
|
1056
|
+
async () => {
|
|
1057
|
+
if (failure != null) {
|
|
1058
|
+
throw failure;
|
|
1059
|
+
}
|
|
1060
|
+
return LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT;
|
|
1061
|
+
},
|
|
1062
|
+
{
|
|
1063
|
+
name: call.name,
|
|
1064
|
+
description: 'Host tool execution metadata',
|
|
1065
|
+
schema: { type: 'object', properties: {} },
|
|
1066
|
+
}
|
|
1067
|
+
);
|
|
1068
|
+
try {
|
|
1069
|
+
await observation.invoke(
|
|
1070
|
+
{ type: 'tool_call', id: call.id, name: call.name, args: {} },
|
|
1071
|
+
{
|
|
1072
|
+
callbacks,
|
|
1073
|
+
metadata: { ...metadata, ...request.metadata },
|
|
1074
|
+
}
|
|
1075
|
+
);
|
|
1076
|
+
} catch (error) {
|
|
1077
|
+
if (failure == null || error !== failure) {
|
|
1078
|
+
throw error;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1011
1084
|
export async function disposeLangfuseHandler(value: unknown): Promise<void> {
|
|
1012
1085
|
if (value instanceof ScopedLangfuseCallbackHandler) {
|
|
1013
1086
|
await value.finishDeferredRoot();
|
|
@@ -24,6 +24,9 @@ import { resolveToolOutputTracingConfigForSpan } from '@/langfuseRuntimeScope';
|
|
|
24
24
|
|
|
25
25
|
export { LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT, resolveLangfuseConfig };
|
|
26
26
|
|
|
27
|
+
export const LANGFUSE_OBSERVATION_METADATA_ARTIFACT_KEY =
|
|
28
|
+
'librechatLangfuseObservationMetadata';
|
|
29
|
+
|
|
27
30
|
const LANGGRAPH_TOOL_NODE_PREFIX = 'tools=';
|
|
28
31
|
const SERVER_TOOL_RESULT_PREFIX = '{"serverToolResult":';
|
|
29
32
|
const SERVER_TOOL_RESULT_REPLAY_MARKER_KEY = 'librechatResponsesReplay';
|
|
@@ -52,6 +55,9 @@ type RedactionContext = {
|
|
|
52
55
|
};
|
|
53
56
|
|
|
54
57
|
const TOOL_OUTPUT_FIELD_KEYS = ['content', 'artifact'];
|
|
58
|
+
const MAX_OBSERVATION_METADATA_FIELDS = 32;
|
|
59
|
+
const MAX_OBSERVATION_METADATA_STRING_LENGTH = 200;
|
|
60
|
+
const OBSERVATION_METADATA_KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/;
|
|
55
61
|
|
|
56
62
|
type ResponsesReplayOutputDescriptor = {
|
|
57
63
|
nestedOutputFields?: Readonly<Record<string, readonly string[]>>;
|
|
@@ -744,6 +750,90 @@ export function classifyLangfuseToolNodeSpan(span: ReadableSpan): void {
|
|
|
744
750
|
classifyLangGraphToolNodeSpan((span as SpanWithAttributes).attributes);
|
|
745
751
|
}
|
|
746
752
|
|
|
753
|
+
function parseSerializedValue(value: unknown): unknown {
|
|
754
|
+
if (typeof value !== 'string') {
|
|
755
|
+
return value;
|
|
756
|
+
}
|
|
757
|
+
try {
|
|
758
|
+
return JSON.parse(value) as unknown;
|
|
759
|
+
} catch {
|
|
760
|
+
return undefined;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function getToolMessageArtifact(
|
|
765
|
+
value: unknown
|
|
766
|
+
): Record<string, unknown> | undefined {
|
|
767
|
+
const parsed = parseSerializedValue(value);
|
|
768
|
+
if (!isRecord(parsed)) {
|
|
769
|
+
return undefined;
|
|
770
|
+
}
|
|
771
|
+
if (isRecord(parsed.artifact)) {
|
|
772
|
+
return parsed.artifact;
|
|
773
|
+
}
|
|
774
|
+
if (isRecord(parsed.kwargs) && isRecord(parsed.kwargs.artifact)) {
|
|
775
|
+
return parsed.kwargs.artifact;
|
|
776
|
+
}
|
|
777
|
+
return undefined;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
export function getToolObservationMetadata(
|
|
781
|
+
artifact: unknown
|
|
782
|
+
): Record<string, string | number | boolean> {
|
|
783
|
+
// This reserved artifact field is for trusted host adapters. Never copy
|
|
784
|
+
// arbitrary tool or model output into it without an explicit allowlist.
|
|
785
|
+
const metadata = isRecord(artifact)
|
|
786
|
+
? artifact[LANGFUSE_OBSERVATION_METADATA_ARTIFACT_KEY]
|
|
787
|
+
: undefined;
|
|
788
|
+
const result: Record<string, string | number | boolean> = {};
|
|
789
|
+
if (!isRecord(metadata)) {
|
|
790
|
+
return result;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
let promoted = 0;
|
|
794
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
795
|
+
if (
|
|
796
|
+
promoted >= MAX_OBSERVATION_METADATA_FIELDS ||
|
|
797
|
+
!OBSERVATION_METADATA_KEY_PATTERN.test(key) ||
|
|
798
|
+
!(
|
|
799
|
+
typeof value === 'number' ||
|
|
800
|
+
typeof value === 'boolean' ||
|
|
801
|
+
(typeof value === 'string' &&
|
|
802
|
+
value.length <= MAX_OBSERVATION_METADATA_STRING_LENGTH)
|
|
803
|
+
)
|
|
804
|
+
) {
|
|
805
|
+
continue;
|
|
806
|
+
}
|
|
807
|
+
if (typeof value === 'number' && !Number.isFinite(value)) {
|
|
808
|
+
continue;
|
|
809
|
+
}
|
|
810
|
+
result[key] = value;
|
|
811
|
+
promoted += 1;
|
|
812
|
+
}
|
|
813
|
+
return result;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
function promoteToolObservationMetadata(
|
|
817
|
+
span: ReadableSpan,
|
|
818
|
+
attributes: Record<string, unknown>,
|
|
819
|
+
config: ResolvedLangfuseToolOutputTracingConfig
|
|
820
|
+
): void {
|
|
821
|
+
if (!isToolObservation(attributes) || !shouldRedactTool(span.name, config)) {
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
const metadata = getToolObservationMetadata(
|
|
825
|
+
getToolMessageArtifact(
|
|
826
|
+
attributes[LangfuseOtelSpanAttributes.OBSERVATION_OUTPUT]
|
|
827
|
+
)
|
|
828
|
+
);
|
|
829
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
830
|
+
const attributeKey = `${LangfuseOtelSpanAttributes.OBSERVATION_METADATA}.${key}`;
|
|
831
|
+
if (!Object.prototype.hasOwnProperty.call(attributes, attributeKey)) {
|
|
832
|
+
attributes[attributeKey] = value;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
747
837
|
function redactToolObservationOutput(
|
|
748
838
|
span: ReadableSpan,
|
|
749
839
|
attributes: Record<string, unknown>,
|
|
@@ -774,6 +864,7 @@ export function redactLangfuseSpanToolOutputs(
|
|
|
774
864
|
return;
|
|
775
865
|
}
|
|
776
866
|
|
|
867
|
+
promoteToolObservationMetadata(span, attributes, config);
|
|
777
868
|
redactToolObservationOutput(span, attributes, config);
|
|
778
869
|
|
|
779
870
|
for (const key of [
|
|
@@ -246,6 +246,18 @@ function findLastMessageText(value: unknown, role: string): string | undefined {
|
|
|
246
246
|
return undefined;
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
+
/**
|
|
250
|
+
* A summarize-only run answers with its summary, carried in state as
|
|
251
|
+
* `manualSummary`, rather than with an assistant message.
|
|
252
|
+
*/
|
|
253
|
+
function getManualSummary(value: unknown): string | undefined {
|
|
254
|
+
if (!isRecord(value) || typeof value.manualSummary !== 'string') {
|
|
255
|
+
return undefined;
|
|
256
|
+
}
|
|
257
|
+
const text = value.manualSummary.trim();
|
|
258
|
+
return text === '' ? undefined : text;
|
|
259
|
+
}
|
|
260
|
+
|
|
249
261
|
function normalizeToolCall(value: unknown): SerializedToolCall | undefined {
|
|
250
262
|
if (!isRecord(value)) {
|
|
251
263
|
return undefined;
|
|
@@ -534,10 +546,11 @@ function shapeConversationPayload(span: MutableSpan): void {
|
|
|
534
546
|
parseAttributeValue(span.attributes[inputKey]),
|
|
535
547
|
'user'
|
|
536
548
|
);
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
549
|
+
const output = parseAttributeValue(span.attributes[outputKey]);
|
|
550
|
+
/** A summarize-only run's summary is its answer; any assistant message
|
|
551
|
+
* still in state is an older reply the run retained, not its output. */
|
|
552
|
+
const answer =
|
|
553
|
+
getManualSummary(output) ?? findLastMessageText(output, 'assistant');
|
|
541
554
|
/** A generation that IS the trace root — a bare `model.invoke` with no
|
|
542
555
|
* wrapping chain, i.e. the activity-label path — is also the only
|
|
543
556
|
* observation carrying its own prompt: reducing its observation input
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
serializeStructuredValueBounded,
|
|
40
40
|
} from '@/utils/toolContent';
|
|
41
41
|
import { HARD_MAX_TOOL_RESULT_CHARS } from '@/utils/truncation';
|
|
42
|
+
import { isReasoningContentBlock } from '@/messages/reasoningTypes';
|
|
42
43
|
import { Constants } from '@/common';
|
|
43
44
|
|
|
44
45
|
type StandardTextBlock = Data.StandardTextBlock;
|
|
@@ -656,7 +657,6 @@ function _formatContent(message: BaseMessage) {
|
|
|
656
657
|
* cross-provider handoff (e.g. Bedrock → Anthropic) we drop them rather than
|
|
657
658
|
* forwarding an unusable block. The receiving model produces its own thinking.
|
|
658
659
|
*/
|
|
659
|
-
const foreignReasoningTypes = ['reasoning_content', 'reasoning', 'think'];
|
|
660
660
|
/**
|
|
661
661
|
* Google server-side tool blocks (`toolCall`/`toolResponse` parts from e.g.
|
|
662
662
|
* URL context or Google Search). Only Google can execute these and validate
|
|
@@ -1049,7 +1049,7 @@ function _formatContent(message: BaseMessage) {
|
|
|
1049
1049
|
};
|
|
1050
1050
|
} else if (
|
|
1051
1051
|
isAIMessage(message) &&
|
|
1052
|
-
|
|
1052
|
+
isReasoningContentBlock(contentPart)
|
|
1053
1053
|
) {
|
|
1054
1054
|
// Foreign reasoning on an ASSISTANT turn (Bedrock `reasoning_content`,
|
|
1055
1055
|
// Google `reasoning`, LibreChat `think`) carries provider-specific
|
|
@@ -1240,12 +1240,6 @@ function messagesHaveCacheControl(
|
|
|
1240
1240
|
);
|
|
1241
1241
|
}
|
|
1242
1242
|
|
|
1243
|
-
/** Anthropic rejects cache_control on these reasoning blocks. */
|
|
1244
|
-
const NON_CACHEABLE_PAYLOAD_BLOCK_TYPES = new Set([
|
|
1245
|
-
'thinking',
|
|
1246
|
-
'redacted_thinking',
|
|
1247
|
-
]);
|
|
1248
|
-
|
|
1249
1243
|
/**
|
|
1250
1244
|
* Place one ephemeral `cache_control` on the last cacheable block of the final
|
|
1251
1245
|
* message of an already-converted Anthropic payload. Used to re-anchor the tail
|
|
@@ -1287,8 +1281,9 @@ function reanchorTailCacheControl(
|
|
|
1287
1281
|
|
|
1288
1282
|
let anchor = -1;
|
|
1289
1283
|
for (let i = 0; i < content.length; i++) {
|
|
1290
|
-
const
|
|
1291
|
-
|
|
1284
|
+
const block = content[i] as { type?: string };
|
|
1285
|
+
const type = block.type;
|
|
1286
|
+
if (type == null || isReasoningContentBlock(block)) {
|
|
1292
1287
|
continue;
|
|
1293
1288
|
}
|
|
1294
1289
|
if (
|
|
@@ -30,6 +30,7 @@ import type {
|
|
|
30
30
|
} from '../types';
|
|
31
31
|
import { serializeStructuredValueBounded } from '@/utils/toolContent';
|
|
32
32
|
import { HARD_MAX_TOOL_RESULT_CHARS } from '@/utils/truncation';
|
|
33
|
+
import { isReasoningContentBlock } from '@/messages/reasoningTypes';
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
36
|
* Reasoning blocks from other providers, relative to Bedrock. Bedrock's native
|
|
@@ -37,13 +38,6 @@ import { HARD_MAX_TOOL_RESULT_CHARS } from '@/utils/truncation';
|
|
|
37
38
|
* signatures Bedrock cannot validate, so they are dropped on a cross-provider
|
|
38
39
|
* handoff (e.g. Anthropic → Bedrock) rather than crashing the conversion.
|
|
39
40
|
*/
|
|
40
|
-
const FOREIGN_REASONING_TYPES = [
|
|
41
|
-
'thinking',
|
|
42
|
-
'redacted_thinking',
|
|
43
|
-
'reasoning',
|
|
44
|
-
'think',
|
|
45
|
-
];
|
|
46
|
-
|
|
47
41
|
/**
|
|
48
42
|
* Google server-side tool blocks (`toolCall`/`toolResponse` parts from e.g.
|
|
49
43
|
* URL context or Google Search). Only Google can execute these and validate
|
|
@@ -797,7 +791,7 @@ function convertAIMessageToConverseMessage(msg: BaseMessage): BedrockMessage {
|
|
|
797
791
|
contentBlocks.push({
|
|
798
792
|
cachePoint,
|
|
799
793
|
} as BedrockContentBlock);
|
|
800
|
-
} else if (
|
|
794
|
+
} else if (isReasoningContentBlock(block)) {
|
|
801
795
|
// Reasoning from another provider (Anthropic `thinking`/
|
|
802
796
|
// `redacted_thinking`, Google `reasoning`, LibreChat `think`).
|
|
803
797
|
// Bedrock's native reasoning is `reasoning_content` (handled above); a
|
|
@@ -9,7 +9,8 @@ import {
|
|
|
9
9
|
REPLY_PRIMER_TOKENS,
|
|
10
10
|
isSyntheticProviderContextMessage,
|
|
11
11
|
} from '@/messages';
|
|
12
|
-
import {
|
|
12
|
+
import { createToolMessageUsageAccumulator } from '@/messages/budget';
|
|
13
|
+
import { readTokenMetadataProperty, apportionTokenCounts } from '@/utils';
|
|
13
14
|
|
|
14
15
|
interface ContextPressureUsage {
|
|
15
16
|
contextBudget?: number;
|
|
@@ -19,6 +20,7 @@ interface ContextPressureUsage {
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
interface ContextPressureMeterParams {
|
|
23
|
+
provider?: t.ProviderName;
|
|
22
24
|
tokenCounter?: t.TokenCounter;
|
|
23
25
|
tokenCountCache?: ExactTokenCountCache;
|
|
24
26
|
sourceMessages: BaseMessage[];
|
|
@@ -89,6 +91,21 @@ function readDataProperty(
|
|
|
89
91
|
: { own: true, safe: false };
|
|
90
92
|
}
|
|
91
93
|
|
|
94
|
+
function readCacheMetadataProperty(
|
|
95
|
+
owner: object,
|
|
96
|
+
property: PropertyKey,
|
|
97
|
+
path: string
|
|
98
|
+
): { safe: boolean; value?: unknown } {
|
|
99
|
+
try {
|
|
100
|
+
return {
|
|
101
|
+
safe: true,
|
|
102
|
+
value: readTokenMetadataProperty(owner, property, path),
|
|
103
|
+
};
|
|
104
|
+
} catch {
|
|
105
|
+
return { safe: false };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
92
109
|
function getStableTokenSurface(
|
|
93
110
|
message: BaseMessage
|
|
94
111
|
): StableTokenSurface | undefined {
|
|
@@ -105,10 +122,13 @@ function getStableTokenSurface(
|
|
|
105
122
|
}
|
|
106
123
|
const messageType = message.getType();
|
|
107
124
|
const roleProperty = readDataProperty(message, 'role');
|
|
108
|
-
|
|
125
|
+
const resolvedRoleProperty = roleProperty.own
|
|
126
|
+
? roleProperty
|
|
127
|
+
: readCacheMetadataProperty(message, 'role', 'message.role');
|
|
128
|
+
if (!resolvedRoleProperty.safe) {
|
|
109
129
|
return undefined;
|
|
110
130
|
}
|
|
111
|
-
const role =
|
|
131
|
+
const role = resolvedRoleProperty.value;
|
|
112
132
|
if (role != null && typeof role !== 'string') {
|
|
113
133
|
return undefined;
|
|
114
134
|
}
|
|
@@ -129,6 +149,14 @@ function getStableTokenSurface(
|
|
|
129
149
|
return undefined;
|
|
130
150
|
}
|
|
131
151
|
const additionalKwargs = rawAdditionalKwargs;
|
|
152
|
+
const rawToolCalls = readCacheMetadataProperty(
|
|
153
|
+
additionalKwargs,
|
|
154
|
+
'tool_calls',
|
|
155
|
+
'additional_kwargs.tool_calls'
|
|
156
|
+
);
|
|
157
|
+
if (!rawToolCalls.safe || rawToolCalls.value != null) {
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
132
160
|
const typeProperty = readDataProperty(additionalKwargs, 'type');
|
|
133
161
|
if (!typeProperty.safe) {
|
|
134
162
|
return undefined;
|
|
@@ -138,16 +166,16 @@ function getStableTokenSurface(
|
|
|
138
166
|
message as AIMessage,
|
|
139
167
|
'tool_calls'
|
|
140
168
|
);
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
(
|
|
144
|
-
) {
|
|
169
|
+
const resolvedToolCallsProperty = toolCallsProperty.own
|
|
170
|
+
? toolCallsProperty
|
|
171
|
+
: readCacheMetadataProperty(message, 'tool_calls', 'message.tool_calls');
|
|
172
|
+
if (!resolvedToolCallsProperty.safe) {
|
|
145
173
|
return undefined;
|
|
146
174
|
}
|
|
147
|
-
const toolCalls =
|
|
175
|
+
const toolCalls = resolvedToolCallsProperty.value;
|
|
148
176
|
if (
|
|
149
177
|
toolCalls != null &&
|
|
150
|
-
(
|
|
178
|
+
(isProxy(toolCalls) || !Array.isArray(toolCalls) || toolCalls.length > 0)
|
|
151
179
|
) {
|
|
152
180
|
return undefined;
|
|
153
181
|
}
|
|
@@ -230,6 +258,7 @@ function getProviderMessageOriginKey(message: BaseMessage): string | undefined {
|
|
|
230
258
|
|
|
231
259
|
/** Measures repeated provider projections while tokenizing each message object once. */
|
|
232
260
|
export function createContextPressureMeter({
|
|
261
|
+
provider,
|
|
233
262
|
tokenCounter,
|
|
234
263
|
tokenCountCache,
|
|
235
264
|
sourceMessages,
|
|
@@ -382,6 +411,22 @@ export function createContextPressureMeter({
|
|
|
382
411
|
? availableMessageTokens -
|
|
383
412
|
Math.min(availableMessageTokens, Math.max(0, baselineRemaining))
|
|
384
413
|
: undefined;
|
|
414
|
+
const toolUsage = createToolMessageUsageAccumulator(provider);
|
|
415
|
+
const toolUsageState: { error?: Error } = {};
|
|
416
|
+
const addToolUsage = (
|
|
417
|
+
message: BaseMessage,
|
|
418
|
+
getRawTokens: () => number
|
|
419
|
+
): void => {
|
|
420
|
+
if (toolUsageState.error != null) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
try {
|
|
424
|
+
toolUsage.add(message, getRawTokens);
|
|
425
|
+
} catch (error) {
|
|
426
|
+
toolUsageState.error =
|
|
427
|
+
error instanceof Error ? error : new Error(String(error));
|
|
428
|
+
}
|
|
429
|
+
};
|
|
385
430
|
|
|
386
431
|
let projectedMessageTokens: number;
|
|
387
432
|
if (
|
|
@@ -423,15 +468,18 @@ export function createContextPressureMeter({
|
|
|
423
468
|
const usedOrigins = new Set<number>();
|
|
424
469
|
for (const message of messages) {
|
|
425
470
|
const origin = origins.get(message);
|
|
471
|
+
let rawTokens: number | undefined;
|
|
472
|
+
const getRawTokens = (): number => (rawTokens ??= count(message));
|
|
473
|
+
addToolUsage(message, getRawTokens);
|
|
426
474
|
if (origin == null || usedOrigins.has(origin)) {
|
|
427
|
-
newRawTokens +=
|
|
475
|
+
newRawTokens += getRawTokens();
|
|
428
476
|
continue;
|
|
429
477
|
}
|
|
430
478
|
usedOrigins.add(origin);
|
|
431
479
|
const projectionDelta =
|
|
432
480
|
message === baseline[origin].message
|
|
433
481
|
? 0
|
|
434
|
-
:
|
|
482
|
+
: getRawTokens() - baseline[origin].rawTokens;
|
|
435
483
|
projectedMessageTokens += Math.max(
|
|
436
484
|
0,
|
|
437
485
|
attribution.attributedByOrigin[origin] +
|
|
@@ -442,16 +490,36 @@ export function createContextPressureMeter({
|
|
|
442
490
|
} else {
|
|
443
491
|
let rawTokens = REPLY_PRIMER_TOKENS;
|
|
444
492
|
for (const message of messages) {
|
|
445
|
-
|
|
493
|
+
const messageTokens = count(message);
|
|
494
|
+
addToolUsage(message, () => messageTokens);
|
|
495
|
+
rawTokens += messageTokens;
|
|
446
496
|
}
|
|
447
497
|
projectedMessageTokens = Math.round(rawTokens * usageRatio);
|
|
448
498
|
}
|
|
499
|
+
let measuredToolUsage:
|
|
500
|
+
| Pick<
|
|
501
|
+
t.TokenBudgetBreakdown,
|
|
502
|
+
'toolMessageTokens' | 'toolMessageTokenCounts'
|
|
503
|
+
>
|
|
504
|
+
| undefined;
|
|
505
|
+
try {
|
|
506
|
+
measuredToolUsage =
|
|
507
|
+
toolUsageState.error == null ? toolUsage.finish(usageRatio) : undefined;
|
|
508
|
+
} catch (error) {
|
|
509
|
+
toolUsageState.error =
|
|
510
|
+
error instanceof Error ? error : new Error(String(error));
|
|
511
|
+
measuredToolUsage = undefined;
|
|
512
|
+
}
|
|
449
513
|
return {
|
|
450
514
|
fits: projectedMessageTokens <= availableMessageTokens,
|
|
451
515
|
projectedMessageTokens,
|
|
452
516
|
availableMessageTokens,
|
|
453
517
|
contextBudget,
|
|
454
518
|
effectiveInstructionTokens,
|
|
519
|
+
...measuredToolUsage,
|
|
520
|
+
...(toolUsageState.error == null
|
|
521
|
+
? {}
|
|
522
|
+
: { toolMessageUsageError: toolUsageState.error }),
|
|
455
523
|
};
|
|
456
524
|
};
|
|
457
525
|
|