@librechat/agents 3.1.57 → 3.1.61
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 +326 -62
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/common/enum.cjs +13 -0
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/events.cjs +7 -27
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +303 -222
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +4 -4
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +6 -2
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/init.cjs +60 -0
- package/dist/cjs/llm/init.cjs.map +1 -0
- package/dist/cjs/llm/invoke.cjs +90 -0
- package/dist/cjs/llm/invoke.cjs.map +1 -0
- package/dist/cjs/llm/openai/index.cjs +2 -0
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/request.cjs +41 -0
- package/dist/cjs/llm/request.cjs.map +1 -0
- package/dist/cjs/main.cjs +40 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/cache.cjs +76 -89
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/messages/contextPruning.cjs +156 -0
- package/dist/cjs/messages/contextPruning.cjs.map +1 -0
- package/dist/cjs/messages/contextPruningSettings.cjs +53 -0
- package/dist/cjs/messages/contextPruningSettings.cjs.map +1 -0
- package/dist/cjs/messages/core.cjs +23 -37
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +156 -11
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/prune.cjs +1161 -49
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/messages/reducer.cjs +87 -0
- package/dist/cjs/messages/reducer.cjs.map +1 -0
- package/dist/cjs/run.cjs +81 -42
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/stream.cjs +54 -7
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/index.cjs +75 -0
- package/dist/cjs/summarization/index.cjs.map +1 -0
- package/dist/cjs/summarization/node.cjs +663 -0
- package/dist/cjs/summarization/node.cjs.map +1 -0
- package/dist/cjs/tools/ToolNode.cjs +16 -8
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/handlers.cjs +2 -0
- package/dist/cjs/tools/handlers.cjs.map +1 -1
- package/dist/cjs/utils/errors.cjs +115 -0
- package/dist/cjs/utils/errors.cjs.map +1 -0
- package/dist/cjs/utils/events.cjs +17 -0
- package/dist/cjs/utils/events.cjs.map +1 -1
- package/dist/cjs/utils/handlers.cjs +16 -0
- package/dist/cjs/utils/handlers.cjs.map +1 -1
- package/dist/cjs/utils/llm.cjs +10 -0
- package/dist/cjs/utils/llm.cjs.map +1 -1
- package/dist/cjs/utils/tokens.cjs +247 -14
- package/dist/cjs/utils/tokens.cjs.map +1 -1
- package/dist/cjs/utils/truncation.cjs +107 -0
- package/dist/cjs/utils/truncation.cjs.map +1 -0
- package/dist/esm/agents/AgentContext.mjs +325 -61
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/common/enum.mjs +13 -0
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/events.mjs +8 -28
- package/dist/esm/events.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +307 -226
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +4 -4
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs +6 -2
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/init.mjs +58 -0
- package/dist/esm/llm/init.mjs.map +1 -0
- package/dist/esm/llm/invoke.mjs +87 -0
- package/dist/esm/llm/invoke.mjs.map +1 -0
- package/dist/esm/llm/openai/index.mjs +2 -0
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/request.mjs +38 -0
- package/dist/esm/llm/request.mjs.map +1 -0
- package/dist/esm/main.mjs +13 -3
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/messages/cache.mjs +76 -89
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/messages/contextPruning.mjs +154 -0
- package/dist/esm/messages/contextPruning.mjs.map +1 -0
- package/dist/esm/messages/contextPruningSettings.mjs +50 -0
- package/dist/esm/messages/contextPruningSettings.mjs.map +1 -0
- package/dist/esm/messages/core.mjs +23 -37
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +156 -11
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/prune.mjs +1158 -52
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/messages/reducer.mjs +83 -0
- package/dist/esm/messages/reducer.mjs.map +1 -0
- package/dist/esm/run.mjs +82 -43
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/stream.mjs +54 -7
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/index.mjs +73 -0
- package/dist/esm/summarization/index.mjs.map +1 -0
- package/dist/esm/summarization/node.mjs +659 -0
- package/dist/esm/summarization/node.mjs.map +1 -0
- package/dist/esm/tools/ToolNode.mjs +16 -8
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/handlers.mjs +2 -0
- package/dist/esm/tools/handlers.mjs.map +1 -1
- package/dist/esm/utils/errors.mjs +111 -0
- package/dist/esm/utils/errors.mjs.map +1 -0
- package/dist/esm/utils/events.mjs +17 -1
- package/dist/esm/utils/events.mjs.map +1 -1
- package/dist/esm/utils/handlers.mjs +16 -0
- package/dist/esm/utils/handlers.mjs.map +1 -1
- package/dist/esm/utils/llm.mjs +10 -1
- package/dist/esm/utils/llm.mjs.map +1 -1
- package/dist/esm/utils/tokens.mjs +245 -15
- package/dist/esm/utils/tokens.mjs.map +1 -1
- package/dist/esm/utils/truncation.mjs +102 -0
- package/dist/esm/utils/truncation.mjs.map +1 -0
- package/dist/types/agents/AgentContext.d.ts +124 -6
- package/dist/types/common/enum.d.ts +14 -1
- package/dist/types/graphs/Graph.d.ts +22 -27
- package/dist/types/index.d.ts +5 -0
- package/dist/types/llm/init.d.ts +18 -0
- package/dist/types/llm/invoke.d.ts +48 -0
- package/dist/types/llm/request.d.ts +14 -0
- package/dist/types/messages/contextPruning.d.ts +42 -0
- package/dist/types/messages/contextPruningSettings.d.ts +44 -0
- package/dist/types/messages/core.d.ts +1 -1
- package/dist/types/messages/format.d.ts +17 -1
- package/dist/types/messages/index.d.ts +3 -0
- package/dist/types/messages/prune.d.ts +162 -1
- package/dist/types/messages/reducer.d.ts +18 -0
- package/dist/types/run.d.ts +12 -1
- package/dist/types/summarization/index.d.ts +20 -0
- package/dist/types/summarization/node.d.ts +29 -0
- package/dist/types/tools/ToolNode.d.ts +3 -1
- package/dist/types/types/graph.d.ts +44 -6
- package/dist/types/types/index.d.ts +1 -0
- package/dist/types/types/run.d.ts +30 -0
- package/dist/types/types/stream.d.ts +31 -4
- package/dist/types/types/summarize.d.ts +47 -0
- package/dist/types/types/tools.d.ts +7 -0
- package/dist/types/utils/errors.d.ts +28 -0
- package/dist/types/utils/events.d.ts +13 -0
- package/dist/types/utils/index.d.ts +2 -0
- package/dist/types/utils/llm.d.ts +4 -0
- package/dist/types/utils/tokens.d.ts +14 -1
- package/dist/types/utils/truncation.d.ts +49 -0
- package/package.json +3 -3
- package/src/agents/AgentContext.ts +388 -58
- package/src/agents/__tests__/AgentContext.test.ts +265 -5
- package/src/common/enum.ts +13 -0
- package/src/events.ts +9 -39
- package/src/graphs/Graph.ts +468 -331
- package/src/index.ts +7 -0
- package/src/llm/anthropic/llm.spec.ts +3 -3
- package/src/llm/anthropic/utils/message_inputs.ts +6 -4
- package/src/llm/bedrock/llm.spec.ts +1 -1
- package/src/llm/bedrock/utils/message_inputs.ts +6 -2
- package/src/llm/init.ts +63 -0
- package/src/llm/invoke.ts +144 -0
- package/src/llm/request.ts +55 -0
- package/src/messages/__tests__/observationMasking.test.ts +221 -0
- package/src/messages/cache.ts +77 -102
- package/src/messages/contextPruning.ts +191 -0
- package/src/messages/contextPruningSettings.ts +90 -0
- package/src/messages/core.ts +32 -53
- package/src/messages/ensureThinkingBlock.test.ts +39 -39
- package/src/messages/format.ts +227 -15
- package/src/messages/formatAgentMessages.test.ts +511 -1
- package/src/messages/index.ts +3 -0
- package/src/messages/prune.ts +1548 -62
- package/src/messages/reducer.ts +22 -0
- package/src/run.ts +104 -51
- package/src/scripts/bedrock-merge-test.ts +1 -1
- package/src/scripts/test-thinking-handoff-bedrock.ts +1 -1
- package/src/scripts/test-thinking-handoff.ts +1 -1
- package/src/scripts/thinking-bedrock.ts +1 -1
- package/src/scripts/thinking.ts +1 -1
- package/src/specs/anthropic.simple.test.ts +1 -1
- package/src/specs/multi-agent-summarization.test.ts +396 -0
- package/src/specs/prune.test.ts +1196 -23
- package/src/specs/summarization-unit.test.ts +868 -0
- package/src/specs/summarization.test.ts +3827 -0
- package/src/specs/summarize-prune.test.ts +376 -0
- package/src/specs/thinking-handoff.test.ts +10 -10
- package/src/specs/thinking-prune.test.ts +7 -4
- package/src/specs/token-accounting-e2e.test.ts +1034 -0
- package/src/specs/token-accounting-pipeline.test.ts +882 -0
- package/src/specs/token-distribution-edge-case.test.ts +25 -26
- package/src/splitStream.test.ts +42 -33
- package/src/stream.ts +64 -11
- package/src/summarization/__tests__/aggregator.test.ts +153 -0
- package/src/summarization/__tests__/node.test.ts +708 -0
- package/src/summarization/__tests__/trigger.test.ts +50 -0
- package/src/summarization/index.ts +102 -0
- package/src/summarization/node.ts +982 -0
- package/src/tools/ToolNode.ts +25 -3
- package/src/types/graph.ts +62 -7
- package/src/types/index.ts +1 -0
- package/src/types/run.ts +32 -0
- package/src/types/stream.ts +45 -5
- package/src/types/summarize.ts +58 -0
- package/src/types/tools.ts +7 -0
- package/src/utils/errors.ts +117 -0
- package/src/utils/events.ts +31 -0
- package/src/utils/handlers.ts +18 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/llm.ts +12 -0
- package/src/utils/tokens.ts +336 -18
- package/src/utils/truncation.ts +124 -0
- package/src/scripts/image.ts +0 -180
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var nanoid = require('nanoid');
|
|
4
|
-
var stream$1 = require('@langchain/core/utils/stream');
|
|
5
|
-
var googleVertexai = require('@langchain/google-vertexai');
|
|
6
|
-
var langgraph = require('@langchain/langgraph');
|
|
7
|
-
var runnables = require('@langchain/core/runnables');
|
|
8
4
|
var messages = require('@langchain/core/messages');
|
|
5
|
+
var langgraph = require('@langchain/langgraph');
|
|
9
6
|
var core = require('../messages/core.cjs');
|
|
10
7
|
var ids = require('../messages/ids.cjs');
|
|
11
8
|
var prune = require('../messages/prune.cjs');
|
|
@@ -13,25 +10,29 @@ var format = require('../messages/format.cjs');
|
|
|
13
10
|
var cache = require('../messages/cache.cjs');
|
|
14
11
|
var content = require('../messages/content.cjs');
|
|
15
12
|
var tools = require('../messages/tools.cjs');
|
|
13
|
+
var reducer = require('../messages/reducer.cjs');
|
|
16
14
|
var _enum = require('../common/enum.cjs');
|
|
17
15
|
var graph = require('../utils/graph.cjs');
|
|
18
16
|
var llm = require('../utils/llm.cjs');
|
|
19
|
-
var stream = require('../stream.cjs');
|
|
20
17
|
var handlers = require('../tools/handlers.cjs');
|
|
21
18
|
var run = require('../utils/run.cjs');
|
|
22
19
|
require('ai-tokenizer');
|
|
23
20
|
require('zod-to-json-schema');
|
|
24
|
-
var providers = require('../llm/providers.cjs');
|
|
25
21
|
var ToolNode = require('../tools/ToolNode.cjs');
|
|
26
|
-
var index = require('../llm/openai/index.cjs');
|
|
27
22
|
var events = require('../utils/events.cjs');
|
|
23
|
+
var invoke = require('../llm/invoke.cjs');
|
|
24
|
+
var index = require('../summarization/index.cjs');
|
|
25
|
+
var node = require('../summarization/node.cjs');
|
|
28
26
|
var schema = require('../tools/schema.cjs');
|
|
29
27
|
var AgentContext = require('../agents/AgentContext.cjs');
|
|
30
28
|
var fake = require('../llm/fake.cjs');
|
|
29
|
+
var request = require('../llm/request.cjs');
|
|
30
|
+
var init = require('../llm/init.cjs');
|
|
31
31
|
|
|
32
32
|
/* eslint-disable no-console */
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
const { AGENT, TOOLS, SUMMARIZE } = _enum.GraphNodeKeys;
|
|
34
|
+
/** Minimum relative variance before calibrated toolSchemaTokens overrides current value. */
|
|
35
|
+
const CALIBRATION_VARIANCE_THRESHOLD = 0.15;
|
|
35
36
|
class Graph {
|
|
36
37
|
messageStepHasToolCalls = new Map();
|
|
37
38
|
messageIdsByStepKey = new Map();
|
|
@@ -41,6 +42,12 @@ class Graph {
|
|
|
41
42
|
stepKeyIds = new Map();
|
|
42
43
|
contentIndexMap = new Map();
|
|
43
44
|
toolCallStepIds = new Map();
|
|
45
|
+
/**
|
|
46
|
+
* Step IDs that have been dispatched via handler registry directly
|
|
47
|
+
* (in dispatchRunStep). Used by the custom event callback to skip
|
|
48
|
+
* duplicate dispatch through the LangGraph callback chain.
|
|
49
|
+
*/
|
|
50
|
+
handlerDispatchedStepIds = new Set();
|
|
44
51
|
signal;
|
|
45
52
|
/** Set of invoked tool call IDs from non-message run steps completed mid-run, if any */
|
|
46
53
|
invokedToolIds;
|
|
@@ -76,16 +83,23 @@ class StandardGraph extends Graph {
|
|
|
76
83
|
/** Optional compile options passed into workflow.compile() */
|
|
77
84
|
compileOptions;
|
|
78
85
|
messages = [];
|
|
86
|
+
/** Cached run messages preserved before clearHeavyState() so getRunMessages() works after cleanup. */
|
|
87
|
+
cachedRunMessages;
|
|
79
88
|
runId;
|
|
89
|
+
/**
|
|
90
|
+
* Boundary between historical messages (loaded from conversation state)
|
|
91
|
+
* and messages produced during the current run. Set once in the state
|
|
92
|
+
* reducer when messages first arrive. Used by `getRunMessages()` and
|
|
93
|
+
* multi-agent message filtering — NOT for pruner token counting (the
|
|
94
|
+
* pruner maintains its own `lastTurnStartIndex` in its closure).
|
|
95
|
+
*/
|
|
80
96
|
startIndex = 0;
|
|
81
97
|
signal;
|
|
82
98
|
/** Map of agent contexts by agent ID */
|
|
83
99
|
agentContexts = new Map();
|
|
84
100
|
/** Default agent ID to use */
|
|
85
101
|
defaultAgentId;
|
|
86
|
-
constructor({
|
|
87
|
-
// parent-level graph inputs
|
|
88
|
-
runId, signal, agents, tokenCounter, indexTokenCountMap, }) {
|
|
102
|
+
constructor({ runId, signal, agents, tokenCounter, indexTokenCountMap, calibrationRatio, }) {
|
|
89
103
|
super();
|
|
90
104
|
this.runId = runId;
|
|
91
105
|
this.signal = signal;
|
|
@@ -94,6 +108,9 @@ class StandardGraph extends Graph {
|
|
|
94
108
|
}
|
|
95
109
|
for (const agentConfig of agents) {
|
|
96
110
|
const agentContext = AgentContext.AgentContext.fromConfig(agentConfig, tokenCounter, indexTokenCountMap);
|
|
111
|
+
if (calibrationRatio != null && calibrationRatio > 0) {
|
|
112
|
+
agentContext.calibrationRatio = calibrationRatio;
|
|
113
|
+
}
|
|
97
114
|
this.agentContexts.set(agentConfig.agentId, agentContext);
|
|
98
115
|
}
|
|
99
116
|
this.defaultAgentId = agents[0].agentId;
|
|
@@ -101,6 +118,7 @@ class StandardGraph extends Graph {
|
|
|
101
118
|
/* Init */
|
|
102
119
|
resetValues(keepContent) {
|
|
103
120
|
this.messages = [];
|
|
121
|
+
this.cachedRunMessages = undefined;
|
|
104
122
|
this.config = graph.resetIfNotEmpty(this.config, undefined);
|
|
105
123
|
if (keepContent !== true) {
|
|
106
124
|
this.contentData = graph.resetIfNotEmpty(this.contentData, []);
|
|
@@ -114,6 +132,7 @@ class StandardGraph extends Graph {
|
|
|
114
132
|
* a stale reference on 2nd+ processStream calls.
|
|
115
133
|
*/
|
|
116
134
|
this.toolCallStepIds.clear();
|
|
135
|
+
this.handlerDispatchedStepIds = graph.resetIfNotEmpty(this.handlerDispatchedStepIds, new Set());
|
|
117
136
|
this.messageIdsByStepKey = graph.resetIfNotEmpty(this.messageIdsByStepKey, new Map());
|
|
118
137
|
this.messageStepHasToolCalls = graph.resetIfNotEmpty(this.messageStepHasToolCalls, new Map());
|
|
119
138
|
this.prelimMessageIdsByStepKey = graph.resetIfNotEmpty(this.prelimMessageIdsByStepKey, new Map());
|
|
@@ -123,6 +142,7 @@ class StandardGraph extends Graph {
|
|
|
123
142
|
}
|
|
124
143
|
}
|
|
125
144
|
clearHeavyState() {
|
|
145
|
+
this.cachedRunMessages = this.messages.slice(this.startIndex);
|
|
126
146
|
super.clearHeavyState();
|
|
127
147
|
this.messages = [];
|
|
128
148
|
this.overrideModel = undefined;
|
|
@@ -153,6 +173,9 @@ class StandardGraph extends Graph {
|
|
|
153
173
|
else if (currentNode.startsWith(TOOLS)) {
|
|
154
174
|
agentId = currentNode.substring(TOOLS.length);
|
|
155
175
|
}
|
|
176
|
+
else if (currentNode.startsWith(SUMMARIZE)) {
|
|
177
|
+
agentId = currentNode.substring(SUMMARIZE.length);
|
|
178
|
+
}
|
|
156
179
|
const agentContext = this.agentContexts.get(agentId ?? '');
|
|
157
180
|
if (!agentContext) {
|
|
158
181
|
throw new Error(`No agent context found for agent ID ${agentId}`);
|
|
@@ -222,11 +245,26 @@ class StandardGraph extends Graph {
|
|
|
222
245
|
}
|
|
223
246
|
/* Misc.*/
|
|
224
247
|
getRunMessages() {
|
|
248
|
+
if (this.messages.length === 0 && this.cachedRunMessages != null) {
|
|
249
|
+
return this.cachedRunMessages;
|
|
250
|
+
}
|
|
225
251
|
return this.messages.slice(this.startIndex);
|
|
226
252
|
}
|
|
227
253
|
getContentParts() {
|
|
228
254
|
return core.convertMessagesToContent(this.messages.slice(this.startIndex));
|
|
229
255
|
}
|
|
256
|
+
getCalibrationRatio() {
|
|
257
|
+
const context = this.agentContexts.get(this.defaultAgentId);
|
|
258
|
+
return context?.calibrationRatio ?? 1;
|
|
259
|
+
}
|
|
260
|
+
getResolvedInstructionOverhead() {
|
|
261
|
+
const context = this.agentContexts.get(this.defaultAgentId);
|
|
262
|
+
return context?.resolvedInstructionOverhead;
|
|
263
|
+
}
|
|
264
|
+
getToolCount() {
|
|
265
|
+
const context = this.agentContexts.get(this.defaultAgentId);
|
|
266
|
+
return ((context?.tools?.length ?? 0) + (context?.toolDefinitions?.length ?? 0));
|
|
267
|
+
}
|
|
230
268
|
/**
|
|
231
269
|
* Get all run steps, optionally filtered by agent ID
|
|
232
270
|
*/
|
|
@@ -278,35 +316,6 @@ class StandardGraph extends Graph {
|
|
|
278
316
|
return contentPartAgentMap;
|
|
279
317
|
}
|
|
280
318
|
/* Graph */
|
|
281
|
-
createSystemRunnable({ provider, clientOptions, instructions, additional_instructions, }) {
|
|
282
|
-
let finalInstructions = instructions;
|
|
283
|
-
if (additional_instructions != null && additional_instructions !== '') {
|
|
284
|
-
finalInstructions =
|
|
285
|
-
finalInstructions != null && finalInstructions
|
|
286
|
-
? `${finalInstructions}\n\n${additional_instructions}`
|
|
287
|
-
: additional_instructions;
|
|
288
|
-
}
|
|
289
|
-
if (finalInstructions != null &&
|
|
290
|
-
finalInstructions &&
|
|
291
|
-
provider === _enum.Providers.ANTHROPIC &&
|
|
292
|
-
clientOptions.promptCache === true) {
|
|
293
|
-
finalInstructions = {
|
|
294
|
-
content: [
|
|
295
|
-
{
|
|
296
|
-
type: 'text',
|
|
297
|
-
text: instructions,
|
|
298
|
-
cache_control: { type: 'ephemeral' },
|
|
299
|
-
},
|
|
300
|
-
],
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
if (finalInstructions != null && finalInstructions !== '') {
|
|
304
|
-
const systemMessage = new messages.SystemMessage(finalInstructions);
|
|
305
|
-
return runnables.RunnableLambda.from((messages) => {
|
|
306
|
-
return [systemMessage, ...messages];
|
|
307
|
-
}).withConfig({ runName: 'prompt' });
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
319
|
initializeTools({ currentTools, currentToolMap, agentContext, }) {
|
|
311
320
|
const toolDefinitions = agentContext?.toolDefinitions;
|
|
312
321
|
const eventDrivenMode = toolDefinitions != null && toolDefinitions.length > 0;
|
|
@@ -336,6 +345,8 @@ class StandardGraph extends Graph {
|
|
|
336
345
|
toolCallStepIds: this.toolCallStepIds,
|
|
337
346
|
toolRegistry: agentContext?.toolRegistry,
|
|
338
347
|
directToolNames: directToolNames.size > 0 ? directToolNames : undefined,
|
|
348
|
+
maxContextTokens: agentContext?.maxContextTokens,
|
|
349
|
+
maxToolResultChars: agentContext?.maxToolResultChars,
|
|
339
350
|
errorHandler: (data, metadata) => StandardGraph.handleToolCallErrorStatic(this, data, metadata),
|
|
340
351
|
});
|
|
341
352
|
}
|
|
@@ -359,42 +370,10 @@ class StandardGraph extends Graph {
|
|
|
359
370
|
errorHandler: (data, metadata) => StandardGraph.handleToolCallErrorStatic(this, data, metadata),
|
|
360
371
|
toolRegistry: agentContext?.toolRegistry,
|
|
361
372
|
sessions: this.sessions,
|
|
373
|
+
maxContextTokens: agentContext?.maxContextTokens,
|
|
374
|
+
maxToolResultChars: agentContext?.maxToolResultChars,
|
|
362
375
|
});
|
|
363
376
|
}
|
|
364
|
-
initializeModel({ provider, tools, clientOptions, }) {
|
|
365
|
-
const ChatModelClass = providers.getChatModelClass(provider);
|
|
366
|
-
const model = new ChatModelClass(clientOptions ?? {});
|
|
367
|
-
if (llm.isOpenAILike(provider) &&
|
|
368
|
-
(model instanceof index.ChatOpenAI || model instanceof index.AzureChatOpenAI)) {
|
|
369
|
-
model.temperature = clientOptions
|
|
370
|
-
.temperature;
|
|
371
|
-
model.topP = clientOptions.topP;
|
|
372
|
-
model.frequencyPenalty = clientOptions
|
|
373
|
-
.frequencyPenalty;
|
|
374
|
-
model.presencePenalty = clientOptions
|
|
375
|
-
.presencePenalty;
|
|
376
|
-
model.n = clientOptions.n;
|
|
377
|
-
}
|
|
378
|
-
else if (provider === _enum.Providers.VERTEXAI &&
|
|
379
|
-
model instanceof googleVertexai.ChatVertexAI) {
|
|
380
|
-
model.temperature = clientOptions
|
|
381
|
-
.temperature;
|
|
382
|
-
model.topP = clientOptions.topP;
|
|
383
|
-
model.topK = clientOptions.topK;
|
|
384
|
-
model.topLogprobs = clientOptions
|
|
385
|
-
.topLogprobs;
|
|
386
|
-
model.frequencyPenalty = clientOptions
|
|
387
|
-
.frequencyPenalty;
|
|
388
|
-
model.presencePenalty = clientOptions
|
|
389
|
-
.presencePenalty;
|
|
390
|
-
model.maxOutputTokens = clientOptions
|
|
391
|
-
.maxOutputTokens;
|
|
392
|
-
}
|
|
393
|
-
if (!tools || tools.length === 0) {
|
|
394
|
-
return model;
|
|
395
|
-
}
|
|
396
|
-
return model.bindTools(tools);
|
|
397
|
-
}
|
|
398
377
|
overrideTestModel(responses, sleep, toolCalls) {
|
|
399
378
|
this.overrideModel = fake.createFakeStreamingLLM({
|
|
400
379
|
responses,
|
|
@@ -402,10 +381,6 @@ class StandardGraph extends Graph {
|
|
|
402
381
|
toolCalls,
|
|
403
382
|
});
|
|
404
383
|
}
|
|
405
|
-
getNewModel({ provider, clientOptions, }) {
|
|
406
|
-
const ChatModelClass = providers.getChatModelClass(provider);
|
|
407
|
-
return new ChatModelClass(clientOptions ?? {});
|
|
408
|
-
}
|
|
409
384
|
getUsageMetadata(finalMessage) {
|
|
410
385
|
if (finalMessage &&
|
|
411
386
|
'usage_metadata' in finalMessage &&
|
|
@@ -413,58 +388,6 @@ class StandardGraph extends Graph {
|
|
|
413
388
|
return finalMessage.usage_metadata;
|
|
414
389
|
}
|
|
415
390
|
}
|
|
416
|
-
/** Execute model invocation with streaming support */
|
|
417
|
-
async attemptInvoke({ currentModel, finalMessages, provider, tools: _tools, }, config) {
|
|
418
|
-
const model = this.overrideModel ?? currentModel;
|
|
419
|
-
if (!model) {
|
|
420
|
-
throw new Error('No model found');
|
|
421
|
-
}
|
|
422
|
-
if (model.stream) {
|
|
423
|
-
/**
|
|
424
|
-
* Process all model output through a local ChatModelStreamHandler in the
|
|
425
|
-
* graph execution context. Each chunk is awaited before the next one is
|
|
426
|
-
* consumed, so by the time the stream is exhausted every run step
|
|
427
|
-
* (MESSAGE_CREATION, TOOL_CALLS) has been created and toolCallStepIds is
|
|
428
|
-
* fully populated — the graph will not transition to ToolNode until this
|
|
429
|
-
* is done.
|
|
430
|
-
*
|
|
431
|
-
* This replaces the previous pattern where ChatModelStreamHandler lived
|
|
432
|
-
* in the for-await stream consumer (handler registry). That consumer
|
|
433
|
-
* runs concurrently with graph execution, so the graph could advance to
|
|
434
|
-
* ToolNode before the consumer had processed all events. By handling
|
|
435
|
-
* chunks here, inside the agent node, the race is eliminated.
|
|
436
|
-
*
|
|
437
|
-
* The for-await consumer no longer needs a ChatModelStreamHandler; its
|
|
438
|
-
* on_chat_model_stream events are simply ignored (no handler registered).
|
|
439
|
-
* The dispatched custom events (ON_RUN_STEP, ON_MESSAGE_DELTA, etc.)
|
|
440
|
-
* still reach the content aggregator and SSE handlers through the custom
|
|
441
|
-
* event callback in Run.createCustomEventCallback.
|
|
442
|
-
*/
|
|
443
|
-
const metadata = config?.metadata;
|
|
444
|
-
const streamHandler = new stream.ChatModelStreamHandler();
|
|
445
|
-
const stream$2 = await model.stream(finalMessages, config);
|
|
446
|
-
let finalChunk;
|
|
447
|
-
for await (const chunk of stream$2) {
|
|
448
|
-
await streamHandler.handle(_enum.GraphEvents.CHAT_MODEL_STREAM, { chunk }, metadata, this);
|
|
449
|
-
finalChunk = finalChunk ? stream$1.concat(finalChunk, chunk) : chunk;
|
|
450
|
-
}
|
|
451
|
-
if (providers.manualToolStreamProviders.has(provider)) {
|
|
452
|
-
finalChunk = core.modifyDeltaProperties(provider, finalChunk);
|
|
453
|
-
}
|
|
454
|
-
if ((finalChunk?.tool_calls?.length ?? 0) > 0) {
|
|
455
|
-
finalChunk.tool_calls = finalChunk.tool_calls?.filter((tool_call) => !!tool_call.name);
|
|
456
|
-
}
|
|
457
|
-
return { messages: [finalChunk] };
|
|
458
|
-
}
|
|
459
|
-
else {
|
|
460
|
-
/** Fallback for models without stream support. */
|
|
461
|
-
const finalMessage = await model.invoke(finalMessages, config);
|
|
462
|
-
if ((finalMessage.tool_calls?.length ?? 0) > 0) {
|
|
463
|
-
finalMessage.tool_calls = finalMessage.tool_calls?.filter((tool_call) => !!tool_call.name);
|
|
464
|
-
}
|
|
465
|
-
return { messages: [finalMessage] };
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
391
|
cleanupSignalListener(currentModel) {
|
|
469
392
|
if (!this.signal) {
|
|
470
393
|
return;
|
|
@@ -482,9 +405,6 @@ class StandardGraph extends Graph {
|
|
|
482
405
|
}
|
|
483
406
|
createCallModel(agentId = 'default') {
|
|
484
407
|
return async (state, config) => {
|
|
485
|
-
/**
|
|
486
|
-
* Get agent context - it must exist by this point
|
|
487
|
-
*/
|
|
488
408
|
const agentContext = this.agentContexts.get(agentId);
|
|
489
409
|
if (!agentContext) {
|
|
490
410
|
throw new Error(`Agent context not found for agentId: ${agentId}`);
|
|
@@ -493,14 +413,13 @@ class StandardGraph extends Graph {
|
|
|
493
413
|
throw new Error('No config provided');
|
|
494
414
|
}
|
|
495
415
|
const { messages: messages$1 } = state;
|
|
496
|
-
// Extract tool discoveries from current turn only (similar to formatArtifactPayload pattern)
|
|
497
416
|
const discoveredNames = tools.extractToolDiscoveries(messages$1);
|
|
498
417
|
if (discoveredNames.length > 0) {
|
|
499
418
|
agentContext.markToolsAsDiscovered(discoveredNames);
|
|
500
419
|
}
|
|
501
420
|
const toolsForBinding = agentContext.getToolsForBinding();
|
|
502
421
|
let model = this.overrideModel ??
|
|
503
|
-
|
|
422
|
+
init.initializeModel({
|
|
504
423
|
tools: toolsForBinding,
|
|
505
424
|
provider: agentContext.provider,
|
|
506
425
|
clientOptions: agentContext.clientOptions,
|
|
@@ -518,34 +437,97 @@ class StandardGraph extends Graph {
|
|
|
518
437
|
let messagesToUse = messages$1;
|
|
519
438
|
if (!agentContext.pruneMessages &&
|
|
520
439
|
agentContext.tokenCounter &&
|
|
521
|
-
agentContext.maxContextTokens != null
|
|
522
|
-
agentContext.indexTokenCountMap[0] != null) {
|
|
523
|
-
const isAnthropicWithThinking = (agentContext.provider === _enum.Providers.ANTHROPIC &&
|
|
524
|
-
agentContext.clientOptions.thinking !=
|
|
525
|
-
null) ||
|
|
526
|
-
(agentContext.provider === _enum.Providers.BEDROCK &&
|
|
527
|
-
agentContext.clientOptions
|
|
528
|
-
.additionalModelRequestFields?.['thinking'] != null) ||
|
|
529
|
-
(agentContext.provider === _enum.Providers.OPENAI &&
|
|
530
|
-
agentContext.clientOptions.modelKwargs
|
|
531
|
-
?.thinking?.type === 'enabled');
|
|
440
|
+
agentContext.maxContextTokens != null) {
|
|
532
441
|
agentContext.pruneMessages = prune.createPruneMessages({
|
|
533
|
-
startIndex: this.startIndex,
|
|
442
|
+
startIndex: agentContext.indexTokenCountMap[0] != null ? this.startIndex : 0,
|
|
534
443
|
provider: agentContext.provider,
|
|
535
444
|
tokenCounter: agentContext.tokenCounter,
|
|
536
445
|
maxTokens: agentContext.maxContextTokens,
|
|
537
|
-
thinkingEnabled:
|
|
446
|
+
thinkingEnabled: request.isThinkingEnabled(agentContext.provider, agentContext.clientOptions),
|
|
538
447
|
indexTokenCountMap: agentContext.indexTokenCountMap,
|
|
448
|
+
contextPruningConfig: agentContext.contextPruningConfig,
|
|
449
|
+
summarizationEnabled: agentContext.summarizationEnabled,
|
|
450
|
+
reserveRatio: agentContext.summarizationConfig?.reserveRatio,
|
|
451
|
+
calibrationRatio: agentContext.calibrationRatio,
|
|
452
|
+
getInstructionTokens: () => agentContext.instructionTokens,
|
|
453
|
+
log: (level, message, data) => {
|
|
454
|
+
events.emitAgentLog(config, level, 'prune', message, data, {
|
|
455
|
+
runId: this.runId,
|
|
456
|
+
agentId,
|
|
457
|
+
});
|
|
458
|
+
},
|
|
539
459
|
});
|
|
540
460
|
}
|
|
541
461
|
if (agentContext.pruneMessages) {
|
|
542
|
-
const { context, indexTokenCountMap } = agentContext.pruneMessages({
|
|
462
|
+
const { context, indexTokenCountMap, messagesToRefine, prePruneContextTokens, remainingContextTokens, originalToolContent, calibrationRatio, resolvedInstructionOverhead, } = agentContext.pruneMessages({
|
|
543
463
|
messages: messages$1,
|
|
544
464
|
usageMetadata: agentContext.currentUsage,
|
|
545
|
-
|
|
465
|
+
lastCallUsage: agentContext.lastCallUsage,
|
|
466
|
+
totalTokensFresh: agentContext.totalTokensFresh,
|
|
546
467
|
});
|
|
547
468
|
agentContext.indexTokenCountMap = indexTokenCountMap;
|
|
469
|
+
if (calibrationRatio != null && calibrationRatio > 0) {
|
|
470
|
+
agentContext.calibrationRatio = calibrationRatio;
|
|
471
|
+
}
|
|
472
|
+
if (resolvedInstructionOverhead != null) {
|
|
473
|
+
agentContext.resolvedInstructionOverhead =
|
|
474
|
+
resolvedInstructionOverhead;
|
|
475
|
+
const nonToolOverhead = agentContext.instructionTokens - agentContext.toolSchemaTokens;
|
|
476
|
+
const calibratedToolTokens = Math.max(0, resolvedInstructionOverhead - nonToolOverhead);
|
|
477
|
+
const currentToolTokens = agentContext.toolSchemaTokens;
|
|
478
|
+
const variance = currentToolTokens > 0
|
|
479
|
+
? Math.abs(calibratedToolTokens - currentToolTokens) /
|
|
480
|
+
currentToolTokens
|
|
481
|
+
: 1;
|
|
482
|
+
if (variance > CALIBRATION_VARIANCE_THRESHOLD) {
|
|
483
|
+
agentContext.toolSchemaTokens = calibratedToolTokens;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
548
486
|
messagesToUse = context;
|
|
487
|
+
const hasPrunedMessages = agentContext.summarizationEnabled === true &&
|
|
488
|
+
Array.isArray(messagesToRefine) &&
|
|
489
|
+
messagesToRefine.length > 0;
|
|
490
|
+
if (hasPrunedMessages) {
|
|
491
|
+
const shouldSkip = agentContext.shouldSkipSummarization(messages$1.length);
|
|
492
|
+
const triggerResult = !shouldSkip &&
|
|
493
|
+
index.shouldTriggerSummarization({
|
|
494
|
+
trigger: agentContext.summarizationConfig?.trigger,
|
|
495
|
+
maxContextTokens: agentContext.maxContextTokens,
|
|
496
|
+
prePruneContextTokens: prePruneContextTokens != null
|
|
497
|
+
? prePruneContextTokens + agentContext.instructionTokens
|
|
498
|
+
: undefined,
|
|
499
|
+
remainingContextTokens,
|
|
500
|
+
messagesToRefineCount: messagesToRefine.length,
|
|
501
|
+
});
|
|
502
|
+
if (triggerResult) {
|
|
503
|
+
if (originalToolContent != null && originalToolContent.size > 0) {
|
|
504
|
+
agentContext.pendingOriginalToolContent = originalToolContent;
|
|
505
|
+
}
|
|
506
|
+
events.emitAgentLog(config, 'info', 'graph', 'Summarization triggered', undefined, { runId: this.runId, agentId });
|
|
507
|
+
events.emitAgentLog(config, 'debug', 'graph', 'Summarization trigger details', {
|
|
508
|
+
totalMessages: messages$1.length,
|
|
509
|
+
remainingContextTokens: remainingContextTokens ?? 0,
|
|
510
|
+
summaryVersion: agentContext.summaryVersion + 1,
|
|
511
|
+
toolSchemaTokens: agentContext.toolSchemaTokens,
|
|
512
|
+
instructionTokens: agentContext.instructionTokens,
|
|
513
|
+
systemMessageTokens: agentContext.systemMessageTokens,
|
|
514
|
+
}, { runId: this.runId, agentId });
|
|
515
|
+
agentContext.markSummarizationTriggered(messages$1.length);
|
|
516
|
+
return {
|
|
517
|
+
summarizationRequest: {
|
|
518
|
+
remainingContextTokens: remainingContextTokens ?? 0,
|
|
519
|
+
agentId: agentId || agentContext.agentId,
|
|
520
|
+
},
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
if (shouldSkip) {
|
|
524
|
+
events.emitAgentLog(config, 'debug', 'graph', 'Summarization skipped — no new messages or per-run cap reached', {
|
|
525
|
+
messageCount: messages$1.length,
|
|
526
|
+
messagesToRefineCount: messagesToRefine.length,
|
|
527
|
+
contextLength: context.length,
|
|
528
|
+
}, { runId: this.runId, agentId });
|
|
529
|
+
}
|
|
530
|
+
}
|
|
549
531
|
}
|
|
550
532
|
let finalMessages = messagesToUse;
|
|
551
533
|
if (agentContext.useLegacyContent) {
|
|
@@ -557,26 +539,29 @@ class StandardGraph extends Graph {
|
|
|
557
539
|
const lastMessageY = finalMessages.length >= 1
|
|
558
540
|
? finalMessages[finalMessages.length - 1]
|
|
559
541
|
: null;
|
|
542
|
+
const anthropicLike = llm.isAnthropicLike(agentContext.provider, agentContext.clientOptions);
|
|
560
543
|
if (agentContext.provider === _enum.Providers.BEDROCK &&
|
|
561
544
|
lastMessageX instanceof messages.AIMessageChunk &&
|
|
562
545
|
lastMessageY instanceof messages.ToolMessage &&
|
|
563
546
|
typeof lastMessageX.content === 'string') {
|
|
564
|
-
|
|
547
|
+
const trimmed = lastMessageX.content.trim();
|
|
548
|
+
finalMessages[finalMessages.length - 2].content =
|
|
549
|
+
trimmed.length > 0 ? [{ type: 'text', text: trimmed }] : '';
|
|
565
550
|
}
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
else if (isLatestToolMessage &&
|
|
572
|
-
((llm.isOpenAILike(agentContext.provider) &&
|
|
551
|
+
if (lastMessageY instanceof messages.ToolMessage) {
|
|
552
|
+
if (anthropicLike) {
|
|
553
|
+
core.formatAnthropicArtifactContent(finalMessages);
|
|
554
|
+
}
|
|
555
|
+
else if ((llm.isOpenAILike(agentContext.provider) &&
|
|
573
556
|
agentContext.provider !== _enum.Providers.DEEPSEEK) ||
|
|
574
|
-
llm.isGoogleLike(agentContext.provider))
|
|
575
|
-
|
|
557
|
+
llm.isGoogleLike(agentContext.provider)) {
|
|
558
|
+
core.formatArtifactPayload(finalMessages);
|
|
559
|
+
}
|
|
576
560
|
}
|
|
577
561
|
if (agentContext.provider === _enum.Providers.ANTHROPIC) {
|
|
578
562
|
const anthropicOptions = agentContext.clientOptions;
|
|
579
|
-
if (anthropicOptions?.promptCache === true
|
|
563
|
+
if (anthropicOptions?.promptCache === true &&
|
|
564
|
+
!agentContext.systemRunnable) {
|
|
580
565
|
finalMessages = cache.addCacheControl(finalMessages);
|
|
581
566
|
}
|
|
582
567
|
}
|
|
@@ -586,19 +571,25 @@ class StandardGraph extends Graph {
|
|
|
586
571
|
finalMessages = cache.addBedrockCacheControl(finalMessages);
|
|
587
572
|
}
|
|
588
573
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
(agentContext.
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
574
|
+
if (request.isThinkingEnabled(agentContext.provider, agentContext.clientOptions)) {
|
|
575
|
+
finalMessages = format.ensureThinkingBlockInMessages(finalMessages, agentContext.provider, config);
|
|
576
|
+
}
|
|
577
|
+
// Intentionally broad: runs when the pruner wasn't used OR any post-pruning
|
|
578
|
+
// transform (addCacheControl, ensureThinkingBlock, etc.) reassigned finalMessages.
|
|
579
|
+
// sanitizeOrphanToolBlocks fast-paths to a Set diff check when no orphans exist,
|
|
580
|
+
// so the cost is negligible and this acts as a safety net for Anthropic/Bedrock.
|
|
581
|
+
const needsOrphanSanitize = anthropicLike &&
|
|
582
|
+
(!agentContext.pruneMessages || finalMessages !== messagesToUse);
|
|
583
|
+
if (needsOrphanSanitize) {
|
|
584
|
+
const beforeSanitize = finalMessages.length;
|
|
585
|
+
finalMessages = prune.sanitizeOrphanToolBlocks(finalMessages);
|
|
586
|
+
if (finalMessages.length !== beforeSanitize) {
|
|
587
|
+
events.emitAgentLog(config, 'warn', 'sanitize', 'Orphan tool blocks removed', {
|
|
588
|
+
before: beforeSanitize,
|
|
589
|
+
after: finalMessages.length,
|
|
590
|
+
dropped: beforeSanitize - finalMessages.length,
|
|
591
|
+
}, { runId: this.runId, agentId });
|
|
592
|
+
}
|
|
602
593
|
}
|
|
603
594
|
if (agentContext.lastStreamCall != null &&
|
|
604
595
|
agentContext.streamBuffer != null) {
|
|
@@ -610,52 +601,68 @@ class StandardGraph extends Graph {
|
|
|
610
601
|
}
|
|
611
602
|
}
|
|
612
603
|
agentContext.lastStreamCall = Date.now();
|
|
604
|
+
agentContext.markTokensStale();
|
|
613
605
|
let result;
|
|
614
606
|
const fallbacks = agentContext.clientOptions?.fallbacks ??
|
|
615
607
|
[];
|
|
616
|
-
if (finalMessages.length === 0
|
|
608
|
+
if (finalMessages.length === 0 &&
|
|
609
|
+
!agentContext.hasPendingCompactionSummary()) {
|
|
610
|
+
const budgetBreakdown = agentContext.getTokenBudgetBreakdown(messages$1);
|
|
611
|
+
const breakdown = agentContext.formatTokenBudgetBreakdown(messages$1);
|
|
612
|
+
const instructionsExceedBudget = budgetBreakdown.instructionTokens > budgetBreakdown.maxContextTokens;
|
|
613
|
+
let guidance;
|
|
614
|
+
if (instructionsExceedBudget) {
|
|
615
|
+
const toolPct = budgetBreakdown.toolSchemaTokens > 0
|
|
616
|
+
? Math.round((budgetBreakdown.toolSchemaTokens /
|
|
617
|
+
budgetBreakdown.instructionTokens) *
|
|
618
|
+
100)
|
|
619
|
+
: 0;
|
|
620
|
+
guidance =
|
|
621
|
+
toolPct > 50
|
|
622
|
+
? `Tool definitions consume ${budgetBreakdown.toolSchemaTokens} tokens (${toolPct}% of instructions) across ${budgetBreakdown.toolCount} tools, exceeding maxContextTokens (${budgetBreakdown.maxContextTokens}). Reduce the number of tools or increase maxContextTokens.`
|
|
623
|
+
: `Instructions (${budgetBreakdown.instructionTokens} tokens) exceed maxContextTokens (${budgetBreakdown.maxContextTokens}). Increase maxContextTokens or shorten the system prompt.`;
|
|
624
|
+
if (agentContext.summarizationEnabled === true) {
|
|
625
|
+
guidance +=
|
|
626
|
+
' Summarization was skipped because the summary would further increase the instruction overhead.';
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
guidance =
|
|
631
|
+
'Please increase the context window size or make your message shorter.';
|
|
632
|
+
}
|
|
633
|
+
events.emitAgentLog(config, 'error', 'graph', 'Empty messages after pruning', {
|
|
634
|
+
messageCount: messages$1.length,
|
|
635
|
+
instructionsExceedBudget,
|
|
636
|
+
breakdown,
|
|
637
|
+
}, { runId: this.runId, agentId });
|
|
617
638
|
throw new Error(JSON.stringify({
|
|
618
639
|
type: 'empty_messages',
|
|
619
|
-
info:
|
|
640
|
+
info: `Message pruning removed all messages as none fit in the context window. ${guidance}\n${breakdown}`,
|
|
620
641
|
}));
|
|
621
642
|
}
|
|
643
|
+
const invokeStart = Date.now();
|
|
644
|
+
const invokeMeta = { runId: this.runId, agentId };
|
|
645
|
+
events.emitAgentLog(config, 'debug', 'graph', 'Invoking LLM', {
|
|
646
|
+
messageCount: finalMessages.length,
|
|
647
|
+
provider: agentContext.provider,
|
|
648
|
+
}, invokeMeta, { force: true });
|
|
622
649
|
try {
|
|
623
|
-
result = await
|
|
624
|
-
|
|
625
|
-
finalMessages,
|
|
650
|
+
result = await invoke.attemptInvoke({
|
|
651
|
+
model: (this.overrideModel ?? model),
|
|
652
|
+
messages: finalMessages,
|
|
626
653
|
provider: agentContext.provider,
|
|
627
|
-
|
|
654
|
+
context: this,
|
|
628
655
|
}, config);
|
|
629
656
|
}
|
|
630
657
|
catch (primaryError) {
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
model = (!bindableTools || bindableTools.length === 0
|
|
640
|
-
? model
|
|
641
|
-
: model.bindTools(bindableTools));
|
|
642
|
-
result = await this.attemptInvoke({
|
|
643
|
-
currentModel: model,
|
|
644
|
-
finalMessages,
|
|
645
|
-
provider: fb.provider,
|
|
646
|
-
tools: agentContext.tools,
|
|
647
|
-
}, config);
|
|
648
|
-
lastError = undefined;
|
|
649
|
-
break;
|
|
650
|
-
}
|
|
651
|
-
catch (e) {
|
|
652
|
-
lastError = e;
|
|
653
|
-
continue;
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
if (lastError !== undefined) {
|
|
657
|
-
throw lastError;
|
|
658
|
-
}
|
|
658
|
+
result = await invoke.tryFallbackProviders({
|
|
659
|
+
fallbacks,
|
|
660
|
+
tools: agentContext.tools,
|
|
661
|
+
messages: finalMessages,
|
|
662
|
+
config,
|
|
663
|
+
primaryError,
|
|
664
|
+
context: this,
|
|
665
|
+
});
|
|
659
666
|
}
|
|
660
667
|
if (!result) {
|
|
661
668
|
throw new Error('No result after model invocation');
|
|
@@ -755,27 +762,53 @@ class StandardGraph extends Graph {
|
|
|
755
762
|
}
|
|
756
763
|
}
|
|
757
764
|
}
|
|
765
|
+
const invokeElapsed = ((Date.now() - invokeStart) / 1000).toFixed(2);
|
|
758
766
|
agentContext.currentUsage = this.getUsageMetadata(result.messages?.[0]);
|
|
767
|
+
if (agentContext.currentUsage) {
|
|
768
|
+
agentContext.updateLastCallUsage(agentContext.currentUsage);
|
|
769
|
+
events.emitAgentLog(config, 'debug', 'graph', `LLM call complete (${invokeElapsed}s)`, {
|
|
770
|
+
...agentContext.currentUsage,
|
|
771
|
+
elapsedSeconds: Number(invokeElapsed),
|
|
772
|
+
instructionTokens: agentContext.instructionTokens,
|
|
773
|
+
toolSchemaTokens: agentContext.toolSchemaTokens,
|
|
774
|
+
messageCount: finalMessages.length,
|
|
775
|
+
}, invokeMeta, { force: true });
|
|
776
|
+
}
|
|
777
|
+
else {
|
|
778
|
+
events.emitAgentLog(config, 'debug', 'graph', `LLM call complete (${invokeElapsed}s)`, {
|
|
779
|
+
elapsedSeconds: Number(invokeElapsed),
|
|
780
|
+
messageCount: finalMessages.length,
|
|
781
|
+
}, invokeMeta, { force: true });
|
|
782
|
+
}
|
|
759
783
|
this.cleanupSignalListener();
|
|
760
784
|
return result;
|
|
761
785
|
};
|
|
762
786
|
}
|
|
763
787
|
createAgentNode(agentId) {
|
|
788
|
+
const getConfig = () => this.config;
|
|
764
789
|
const agentContext = this.agentContexts.get(agentId);
|
|
765
790
|
if (!agentContext) {
|
|
766
791
|
throw new Error(`Agent context not found for agentId: ${agentId}`);
|
|
767
792
|
}
|
|
768
793
|
const agentNode = `${AGENT}${agentId}`;
|
|
769
794
|
const toolNode = `${TOOLS}${agentId}`;
|
|
795
|
+
const summarizeNode = `${SUMMARIZE}${agentId}`;
|
|
770
796
|
const routeMessage = (state, config) => {
|
|
771
797
|
this.config = config;
|
|
798
|
+
if (state.summarizationRequest != null) {
|
|
799
|
+
return summarizeNode;
|
|
800
|
+
}
|
|
772
801
|
return ToolNode.toolsCondition(state, toolNode, this.invokedToolIds);
|
|
773
802
|
};
|
|
774
803
|
const StateAnnotation = langgraph.Annotation.Root({
|
|
775
804
|
messages: langgraph.Annotation({
|
|
776
|
-
reducer:
|
|
805
|
+
reducer: reducer.messagesStateReducer,
|
|
777
806
|
default: () => [],
|
|
778
807
|
}),
|
|
808
|
+
summarizationRequest: langgraph.Annotation({
|
|
809
|
+
reducer: (_, b) => b,
|
|
810
|
+
default: () => undefined,
|
|
811
|
+
}),
|
|
779
812
|
});
|
|
780
813
|
const workflow = new langgraph.StateGraph(StateAnnotation)
|
|
781
814
|
.addNode(agentNode, this.createCallModel(agentId))
|
|
@@ -783,15 +816,54 @@ class StandardGraph extends Graph {
|
|
|
783
816
|
currentTools: agentContext.tools,
|
|
784
817
|
currentToolMap: agentContext.toolMap,
|
|
785
818
|
agentContext,
|
|
819
|
+
}))
|
|
820
|
+
.addNode(summarizeNode, node.createSummarizeNode({
|
|
821
|
+
agentContext,
|
|
822
|
+
graph: {
|
|
823
|
+
contentData: this.contentData,
|
|
824
|
+
contentIndexMap: this.contentIndexMap,
|
|
825
|
+
get config() {
|
|
826
|
+
return getConfig();
|
|
827
|
+
},
|
|
828
|
+
runId: this.runId,
|
|
829
|
+
isMultiAgent: this.isMultiAgentGraph(),
|
|
830
|
+
dispatchRunStep: async (runStep, nodeConfig) => {
|
|
831
|
+
this.contentData.push(runStep);
|
|
832
|
+
this.contentIndexMap.set(runStep.id, runStep.index);
|
|
833
|
+
const resolvedConfig = nodeConfig ?? this.config;
|
|
834
|
+
const handler = this.handlerRegistry?.getHandler(_enum.GraphEvents.ON_RUN_STEP);
|
|
835
|
+
if (handler) {
|
|
836
|
+
await handler.handle(_enum.GraphEvents.ON_RUN_STEP, runStep, resolvedConfig?.configurable, this);
|
|
837
|
+
this.handlerDispatchedStepIds.add(runStep.id);
|
|
838
|
+
}
|
|
839
|
+
if (resolvedConfig) {
|
|
840
|
+
await events.safeDispatchCustomEvent(_enum.GraphEvents.ON_RUN_STEP, runStep, resolvedConfig);
|
|
841
|
+
}
|
|
842
|
+
},
|
|
843
|
+
dispatchRunStepCompleted: async (stepId, result, nodeConfig) => {
|
|
844
|
+
const resolvedConfig = nodeConfig ?? this.config;
|
|
845
|
+
const runStep = this.contentData.find((s) => s.id === stepId);
|
|
846
|
+
const handler = this.handlerRegistry?.getHandler(_enum.GraphEvents.ON_RUN_STEP_COMPLETED);
|
|
847
|
+
if (handler) {
|
|
848
|
+
await handler.handle(_enum.GraphEvents.ON_RUN_STEP_COMPLETED, {
|
|
849
|
+
result: {
|
|
850
|
+
...result,
|
|
851
|
+
id: stepId,
|
|
852
|
+
index: runStep?.index ?? 0,
|
|
853
|
+
},
|
|
854
|
+
}, resolvedConfig?.configurable, this);
|
|
855
|
+
}
|
|
856
|
+
},
|
|
857
|
+
},
|
|
858
|
+
generateStepId: (stepKey) => this.generateStepId(stepKey),
|
|
786
859
|
}))
|
|
787
860
|
.addEdge(langgraph.START, agentNode)
|
|
788
861
|
.addConditionalEdges(agentNode, routeMessage)
|
|
862
|
+
.addEdge(summarizeNode, agentNode)
|
|
789
863
|
.addEdge(toolNode, agentContext.toolEnd ? langgraph.END : agentNode);
|
|
790
|
-
|
|
791
|
-
return workflow.compile(this.compileOptions);
|
|
864
|
+
return workflow.compile();
|
|
792
865
|
}
|
|
793
866
|
createWorkflow() {
|
|
794
|
-
/** Use the default (first) agent for now */
|
|
795
867
|
const agentNode = this.createAgentNode(this.defaultAgentId);
|
|
796
868
|
const StateAnnotation = langgraph.Annotation.Root({
|
|
797
869
|
messages: langgraph.Annotation({
|
|
@@ -799,7 +871,7 @@ class StandardGraph extends Graph {
|
|
|
799
871
|
if (!a.length) {
|
|
800
872
|
this.startIndex = a.length + b.length;
|
|
801
873
|
}
|
|
802
|
-
const result =
|
|
874
|
+
const result = reducer.messagesStateReducer(a, b);
|
|
803
875
|
this.messages = result;
|
|
804
876
|
return result;
|
|
805
877
|
},
|
|
@@ -809,7 +881,8 @@ class StandardGraph extends Graph {
|
|
|
809
881
|
const workflow = new langgraph.StateGraph(StateAnnotation)
|
|
810
882
|
.addNode(this.defaultAgentId, agentNode, { ends: [langgraph.END] })
|
|
811
883
|
.addEdge(langgraph.START, this.defaultAgentId)
|
|
812
|
-
|
|
884
|
+
// LangGraph compile() types are overly strict for opt-in options
|
|
885
|
+
.compile(this.compileOptions);
|
|
813
886
|
return workflow;
|
|
814
887
|
}
|
|
815
888
|
/**
|
|
@@ -860,18 +933,11 @@ class StandardGraph extends Graph {
|
|
|
860
933
|
if (runId) {
|
|
861
934
|
runStep.runId = runId;
|
|
862
935
|
}
|
|
863
|
-
/**
|
|
864
|
-
* Extract agentId and parallelGroupId from metadata
|
|
865
|
-
* Only set agentId for MultiAgentGraph (so frontend knows when to show agent labels)
|
|
866
|
-
*/
|
|
867
936
|
if (metadata) {
|
|
868
937
|
try {
|
|
869
938
|
const agentContext = this.getAgentContext(metadata);
|
|
870
939
|
if (this.isMultiAgentGraph() && agentContext.agentId) {
|
|
871
|
-
// Only include agentId for MultiAgentGraph - enables frontend to show agent labels
|
|
872
940
|
runStep.agentId = agentContext.agentId;
|
|
873
|
-
// Set group ID if this agent is part of a parallel group
|
|
874
|
-
// Group IDs are incrementing numbers (1, 2, 3...) reflecting execution order
|
|
875
941
|
const groupId = this.getParallelGroupIdForAgent(agentContext.agentId);
|
|
876
942
|
if (groupId != null) {
|
|
877
943
|
runStep.groupId = groupId;
|
|
@@ -884,6 +950,21 @@ class StandardGraph extends Graph {
|
|
|
884
950
|
}
|
|
885
951
|
this.contentData.push(runStep);
|
|
886
952
|
this.contentIndexMap.set(stepId, runStep.index);
|
|
953
|
+
// Primary dispatch: handler registry (reliable, always works).
|
|
954
|
+
// This mirrors how handleToolCallCompleted dispatches ON_RUN_STEP_COMPLETED
|
|
955
|
+
// via the handler registry, ensuring the event always reaches the handler
|
|
956
|
+
// even when LangGraph's callback system drops the custom event.
|
|
957
|
+
const handler = this.handlerRegistry?.getHandler(_enum.GraphEvents.ON_RUN_STEP);
|
|
958
|
+
if (handler) {
|
|
959
|
+
await handler.handle(_enum.GraphEvents.ON_RUN_STEP, runStep, metadata, this);
|
|
960
|
+
this.handlerDispatchedStepIds.add(stepId);
|
|
961
|
+
}
|
|
962
|
+
// Secondary dispatch: custom event for LangGraph callback chain
|
|
963
|
+
// (tracing, Langfuse, external consumers). May be silently dropped
|
|
964
|
+
// in some scenarios (stale run ID, subgraph callback propagation issues),
|
|
965
|
+
// but the primary dispatch above guarantees the event reaches the handler.
|
|
966
|
+
// The customEventCallback in run.ts skips events already dispatched above
|
|
967
|
+
// to prevent double handling.
|
|
887
968
|
await events.safeDispatchCustomEvent(_enum.GraphEvents.ON_RUN_STEP, runStep, this.config);
|
|
888
969
|
return stepId;
|
|
889
970
|
}
|