@librechat/agents 3.1.85 → 3.1.87
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/README.md +69 -0
- package/dist/cjs/agents/AgentContext.cjs +7 -2
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/events.cjs +23 -0
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +133 -18
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/index.cjs +251 -53
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/init.cjs +1 -5
- package/dist/cjs/llm/init.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +113 -24
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/index.cjs +3 -1
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +18 -5
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/openai/index.cjs +253 -0
- package/dist/cjs/openai/index.cjs.map +1 -0
- package/dist/cjs/responses/index.cjs +448 -0
- package/dist/cjs/responses/index.cjs.map +1 -0
- package/dist/cjs/run.cjs +108 -7
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/AgentSession.cjs +1057 -0
- package/dist/cjs/session/AgentSession.cjs.map +1 -0
- package/dist/cjs/session/JsonlSessionStore.cjs +425 -0
- package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -0
- package/dist/cjs/session/handlers.cjs +221 -0
- package/dist/cjs/session/handlers.cjs.map +1 -0
- package/dist/cjs/session/ids.cjs +22 -0
- package/dist/cjs/session/ids.cjs.map +1 -0
- package/dist/cjs/session/messageSerialization.cjs +179 -0
- package/dist/cjs/session/messageSerialization.cjs.map +1 -0
- package/dist/cjs/stream.cjs +472 -11
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +1 -1
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +177 -59
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/eagerEventExecution.cjs +113 -0
- package/dist/cjs/tools/eagerEventExecution.cjs.map +1 -0
- package/dist/cjs/tools/handlers.cjs +1 -1
- package/dist/cjs/tools/handlers.cjs.map +1 -1
- package/dist/cjs/tools/streamedToolCallSeals.cjs +42 -0
- package/dist/cjs/tools/streamedToolCallSeals.cjs.map +1 -0
- package/dist/esm/agents/AgentContext.mjs +7 -2
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/events.mjs +23 -1
- package/dist/esm/events.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +133 -18
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/llm/anthropic/index.mjs +251 -53
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/init.mjs +1 -5
- package/dist/esm/llm/init.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +113 -25
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/index.mjs +4 -2
- package/dist/esm/llm/openrouter/index.mjs.map +1 -1
- package/dist/esm/main.mjs +5 -1
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/openai/index.mjs +246 -0
- package/dist/esm/openai/index.mjs.map +1 -0
- package/dist/esm/responses/index.mjs +440 -0
- package/dist/esm/responses/index.mjs.map +1 -0
- package/dist/esm/run.mjs +108 -7
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/AgentSession.mjs +1054 -0
- package/dist/esm/session/AgentSession.mjs.map +1 -0
- package/dist/esm/session/JsonlSessionStore.mjs +422 -0
- package/dist/esm/session/JsonlSessionStore.mjs.map +1 -0
- package/dist/esm/session/handlers.mjs +219 -0
- package/dist/esm/session/handlers.mjs.map +1 -0
- package/dist/esm/session/ids.mjs +17 -0
- package/dist/esm/session/ids.mjs.map +1 -0
- package/dist/esm/session/messageSerialization.mjs +173 -0
- package/dist/esm/session/messageSerialization.mjs.map +1 -0
- package/dist/esm/stream.mjs +473 -12
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +1 -1
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +177 -59
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/eagerEventExecution.mjs +107 -0
- package/dist/esm/tools/eagerEventExecution.mjs.map +1 -0
- package/dist/esm/tools/handlers.mjs +1 -1
- package/dist/esm/tools/handlers.mjs.map +1 -1
- package/dist/esm/tools/streamedToolCallSeals.mjs +36 -0
- package/dist/esm/tools/streamedToolCallSeals.mjs.map +1 -0
- package/dist/types/events.d.ts +1 -0
- package/dist/types/graphs/Graph.d.ts +24 -9
- package/dist/types/index.d.ts +1 -0
- package/dist/types/llm/openai/index.d.ts +1 -0
- package/dist/types/openai/index.d.ts +75 -0
- package/dist/types/responses/index.d.ts +97 -0
- package/dist/types/run.d.ts +2 -0
- package/dist/types/session/AgentSession.d.ts +32 -0
- package/dist/types/session/JsonlSessionStore.d.ts +67 -0
- package/dist/types/session/handlers.d.ts +8 -0
- package/dist/types/session/ids.d.ts +4 -0
- package/dist/types/session/index.d.ts +5 -0
- package/dist/types/session/messageSerialization.d.ts +7 -0
- package/dist/types/session/types.d.ts +191 -0
- package/dist/types/tools/ToolNode.d.ts +12 -1
- package/dist/types/tools/eagerEventExecution.d.ts +23 -0
- package/dist/types/tools/streamedToolCallSeals.d.ts +13 -0
- package/dist/types/types/hitl.d.ts +4 -0
- package/dist/types/types/run.d.ts +11 -1
- package/dist/types/types/tools.d.ts +36 -0
- package/package.json +19 -2
- package/src/__tests__/stream.eagerEventExecution.test.ts +2458 -0
- package/src/agents/AgentContext.ts +7 -2
- package/src/agents/__tests__/AgentContext.test.ts +254 -5
- package/src/events.ts +29 -0
- package/src/graphs/Graph.ts +224 -50
- package/src/graphs/MultiAgentGraph.ts +1 -1
- package/src/graphs/__tests__/composition.smoke.test.ts +30 -0
- package/src/index.ts +3 -0
- package/src/llm/anthropic/index.ts +356 -84
- package/src/llm/anthropic/llm.spec.ts +64 -0
- package/src/llm/custom-chat-models.smoke.test.ts +175 -4
- package/src/llm/openai/contentBlocks.test.ts +35 -0
- package/src/llm/openai/deepseek.test.ts +201 -2
- package/src/llm/openai/index.ts +171 -26
- package/src/llm/openai/utils/index.ts +22 -0
- package/src/llm/openrouter/index.ts +4 -2
- package/src/openai/__tests__/openai.test.ts +337 -0
- package/src/openai/index.ts +404 -0
- package/src/responses/__tests__/responses.test.ts +652 -0
- package/src/responses/index.ts +677 -0
- package/src/run.ts +158 -8
- package/src/scripts/compare_pi_vs_ours.ts +592 -173
- package/src/scripts/session_live.ts +548 -0
- package/src/session/AgentSession.ts +1432 -0
- package/src/session/JsonlSessionStore.ts +572 -0
- package/src/session/__tests__/JsonlSessionStore.test.ts +1410 -0
- package/src/session/__tests__/handlers.test.ts +161 -0
- package/src/session/handlers.ts +272 -0
- package/src/session/ids.ts +17 -0
- package/src/session/index.ts +44 -0
- package/src/session/messageSerialization.ts +207 -0
- package/src/session/types.ts +275 -0
- package/src/specs/custom-event-await.test.ts +89 -0
- package/src/specs/summarization.test.ts +1 -1
- package/src/stream.ts +755 -48
- package/src/summarization/node.ts +1 -1
- package/src/tools/ToolNode.ts +299 -126
- package/src/tools/__tests__/ToolNode.eagerEventExecution.test.ts +373 -0
- package/src/tools/__tests__/handlers.test.ts +2 -1
- package/src/tools/__tests__/hitl.test.ts +206 -110
- package/src/tools/eagerEventExecution.ts +153 -0
- package/src/tools/handlers.ts +8 -4
- package/src/tools/streamedToolCallSeals.ts +57 -0
- package/src/types/hitl.ts +4 -0
- package/src/types/run.ts +11 -0
- package/src/types/tools.ts +36 -0
- package/dist/cjs/llm/text.cjs +0 -69
- package/dist/cjs/llm/text.cjs.map +0 -1
- package/dist/esm/llm/text.mjs +0 -67
- package/dist/esm/llm/text.mjs.map +0 -1
package/dist/cjs/stream.cjs
CHANGED
|
@@ -5,9 +5,12 @@ var handlers = require('./tools/handlers.cjs');
|
|
|
5
5
|
require('./messages/core.cjs');
|
|
6
6
|
var ids = require('./messages/ids.cjs');
|
|
7
7
|
require('@langchain/core/messages');
|
|
8
|
-
require('
|
|
8
|
+
var events = require('./utils/events.cjs');
|
|
9
9
|
require('uuid');
|
|
10
|
+
var eagerEventExecution = require('./tools/eagerEventExecution.cjs');
|
|
11
|
+
var streamedToolCallSeals = require('./tools/streamedToolCallSeals.cjs');
|
|
10
12
|
|
|
13
|
+
const LOCAL_CODING_BUNDLE_NAME_SET = new Set(_enum.LOCAL_CODING_BUNDLE_NAMES);
|
|
11
14
|
/**
|
|
12
15
|
* Parses content to extract thinking sections enclosed in <think> tags using string operations
|
|
13
16
|
* @param content The content to parse
|
|
@@ -55,6 +58,428 @@ function getNonEmptyValue(possibleValues) {
|
|
|
55
58
|
}
|
|
56
59
|
return undefined;
|
|
57
60
|
}
|
|
61
|
+
function isBatchSensitiveToolExecution(graph) {
|
|
62
|
+
return (graph.hookRegistry != null ||
|
|
63
|
+
graph.humanInTheLoop?.enabled === true ||
|
|
64
|
+
graph.toolOutputReferences?.enabled === true);
|
|
65
|
+
}
|
|
66
|
+
function isDirectGraphTool(name, agentContext) {
|
|
67
|
+
if (name.startsWith(_enum.Constants.LC_TRANSFER_TO_)) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
return (agentContext?.graphTools?.some((tool) => 'name' in tool && tool.name === name) === true);
|
|
71
|
+
}
|
|
72
|
+
function isDirectLocalTool(name, graph) {
|
|
73
|
+
if (graph.toolExecution?.engine !== 'local') {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
if (graph.toolExecution.local?.includeCodingTools === false) {
|
|
77
|
+
return _enum.CODE_EXECUTION_TOOLS.has(name);
|
|
78
|
+
}
|
|
79
|
+
return LOCAL_CODING_BUNDLE_NAME_SET.has(name);
|
|
80
|
+
}
|
|
81
|
+
function toCodeEnvFile(file, execSessionId) {
|
|
82
|
+
const base = {
|
|
83
|
+
id: file.id,
|
|
84
|
+
resource_id: file.resource_id ?? file.id,
|
|
85
|
+
name: file.name,
|
|
86
|
+
storage_session_id: file.storage_session_id ?? execSessionId,
|
|
87
|
+
};
|
|
88
|
+
const kind = file.kind ?? 'user';
|
|
89
|
+
if (kind === 'skill' && file.version != null) {
|
|
90
|
+
return { ...base, kind: 'skill', version: file.version };
|
|
91
|
+
}
|
|
92
|
+
if (kind === 'agent') {
|
|
93
|
+
return { ...base, kind: 'agent' };
|
|
94
|
+
}
|
|
95
|
+
return { ...base, kind: 'user' };
|
|
96
|
+
}
|
|
97
|
+
function getCodeSessionContext(graph, name) {
|
|
98
|
+
if (!_enum.CODE_EXECUTION_TOOLS.has(name) &&
|
|
99
|
+
name !== _enum.Constants.SKILL_TOOL &&
|
|
100
|
+
name !== _enum.Constants.READ_FILE) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
const codeSession = graph.sessions.get(_enum.Constants.EXECUTE_CODE);
|
|
104
|
+
if (codeSession?.session_id == null || codeSession.session_id === '') {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
session_id: codeSession.session_id,
|
|
109
|
+
files: codeSession.files?.map((file) => toCodeEnvFile(file, codeSession.session_id)),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function isEagerToolExecutionEnabledForBatch(args) {
|
|
113
|
+
const { graph, metadata, agentContext } = args;
|
|
114
|
+
if (graph.eagerEventToolExecution?.enabled !== true) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
if ((agentContext?.toolDefinitions?.length ?? 0) === 0) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
if (isBatchSensitiveToolExecution(graph)) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
if (metadata?.[_enum.Constants.PROGRAMMATIC_TOOL_CALLING] === true ||
|
|
124
|
+
metadata?.[_enum.Constants.BASH_PROGRAMMATIC_TOOL_CALLING] === true) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
if (graph.handlerRegistry?.getHandler(_enum.GraphEvents.ON_TOOL_EXECUTE) == null) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
function hasFinalToolCallSignal(chunk) {
|
|
133
|
+
const metadata = chunk.response_metadata;
|
|
134
|
+
const finishReason = metadata?.finish_reason ??
|
|
135
|
+
metadata?.finishReason ??
|
|
136
|
+
metadata?.stop_reason ??
|
|
137
|
+
metadata?.stopReason;
|
|
138
|
+
return finishReason === 'tool_calls' || finishReason === 'tool_use';
|
|
139
|
+
}
|
|
140
|
+
function canPrestartSequentialStreamedToolChunks(agentContext) {
|
|
141
|
+
return (agentContext?.provider === _enum.Providers.ANTHROPIC ||
|
|
142
|
+
agentContext?.provider === _enum.Providers.MOONSHOT);
|
|
143
|
+
}
|
|
144
|
+
function hasExplicitStreamedToolCallSeals(chunk) {
|
|
145
|
+
return (streamedToolCallSeals.getStreamedToolCallAdapter(chunk.response_metadata) != null);
|
|
146
|
+
}
|
|
147
|
+
function hasDirectToolCallInBatch(args) {
|
|
148
|
+
const { graph, agentContext, toolCalls } = args;
|
|
149
|
+
return toolCalls.some((toolCall) => toolCall.name !== '' &&
|
|
150
|
+
(isDirectGraphTool(toolCall.name, agentContext) ||
|
|
151
|
+
isDirectLocalTool(toolCall.name, graph)));
|
|
152
|
+
}
|
|
153
|
+
function hasPotentialDirectToolInStreamContext(args) {
|
|
154
|
+
const { graph, agentContext } = args;
|
|
155
|
+
if (graph.toolExecution?.engine === 'local') {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
if ((agentContext?.graphTools?.length ?? 0) > 0) {
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
return (agentContext?.toolDefinitions?.some((toolDefinition) => toolDefinition.name.startsWith(_enum.Constants.LC_TRANSFER_TO_)) === true);
|
|
162
|
+
}
|
|
163
|
+
function createEagerToolExecutionPlan(args) {
|
|
164
|
+
const { graph, metadata, agentContext, toolCalls, skipExisting = false, } = args;
|
|
165
|
+
if (!isEagerToolExecutionEnabledForBatch({
|
|
166
|
+
graph,
|
|
167
|
+
metadata,
|
|
168
|
+
agentContext,
|
|
169
|
+
})) {
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
if (hasDirectToolCallInBatch({ graph, agentContext, toolCalls })) {
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
const candidateToolCalls = skipExisting
|
|
176
|
+
? toolCalls.filter((toolCall) => {
|
|
177
|
+
if (toolCall.id == null || toolCall.id === '') {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
return !graph.eagerEventToolExecutions.has(toolCall.id);
|
|
181
|
+
})
|
|
182
|
+
: toolCalls;
|
|
183
|
+
if (candidateToolCalls.length === 0) {
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
// Eager execution must preserve ToolNode batch semantics exactly for every
|
|
187
|
+
// unstarted call. If any candidate cannot be planned, fall back for that
|
|
188
|
+
// candidate set.
|
|
189
|
+
if (candidateToolCalls.some((toolCall) => toolCall.id == null ||
|
|
190
|
+
toolCall.id === '' ||
|
|
191
|
+
toolCall.name === '' ||
|
|
192
|
+
(!skipExisting && graph.eagerEventToolExecutions.has(toolCall.id)))) {
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
const plan = eagerEventExecution.buildToolExecutionRequestPlan({
|
|
196
|
+
toolCalls: candidateToolCalls.map((toolCall) => ({
|
|
197
|
+
id: toolCall.id,
|
|
198
|
+
name: toolCall.name,
|
|
199
|
+
args: toolCall.args,
|
|
200
|
+
stepId: graph.toolCallStepIds.get(toolCall.id) ?? '',
|
|
201
|
+
codeSessionContext: getCodeSessionContext(graph, toolCall.name),
|
|
202
|
+
})),
|
|
203
|
+
usageCount: graph.getEagerEventToolUsageCount(agentContext?.agentId),
|
|
204
|
+
});
|
|
205
|
+
if (plan == null) {
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
return plan.requests.map((request) => ({
|
|
209
|
+
id: request.id,
|
|
210
|
+
toolName: request.name,
|
|
211
|
+
coercedArgs: request.args,
|
|
212
|
+
request,
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
215
|
+
function startEagerToolExecutions(args) {
|
|
216
|
+
const { graph, metadata, agentContext, toolCalls, skipExisting } = args;
|
|
217
|
+
const entries = createEagerToolExecutionPlan({
|
|
218
|
+
graph,
|
|
219
|
+
metadata,
|
|
220
|
+
agentContext,
|
|
221
|
+
toolCalls,
|
|
222
|
+
skipExisting,
|
|
223
|
+
});
|
|
224
|
+
if (entries == null || entries.length === 0) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const promise = new Promise((resolve, reject) => {
|
|
228
|
+
let dispatchSettled = false;
|
|
229
|
+
let resultSettled = false;
|
|
230
|
+
let settledResults;
|
|
231
|
+
const maybeResolve = () => {
|
|
232
|
+
if (dispatchSettled && resultSettled) {
|
|
233
|
+
resolve(settledResults ?? []);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
const batchRequest = {
|
|
237
|
+
toolCalls: entries.map((entry) => entry.request),
|
|
238
|
+
userId: graph.config?.configurable?.user_id,
|
|
239
|
+
agentId: agentContext?.agentId,
|
|
240
|
+
configurable: graph.config?.configurable,
|
|
241
|
+
metadata,
|
|
242
|
+
resolve: (results) => {
|
|
243
|
+
resultSettled = true;
|
|
244
|
+
settledResults = results;
|
|
245
|
+
maybeResolve();
|
|
246
|
+
},
|
|
247
|
+
reject,
|
|
248
|
+
};
|
|
249
|
+
void events.safeDispatchCustomEvent(_enum.GraphEvents.ON_TOOL_EXECUTE, batchRequest, graph.config)
|
|
250
|
+
.then(() => {
|
|
251
|
+
dispatchSettled = true;
|
|
252
|
+
maybeResolve();
|
|
253
|
+
})
|
|
254
|
+
.catch(reject);
|
|
255
|
+
}).then((results) => ({ results }), (error) => ({
|
|
256
|
+
error: eagerEventExecution.normalizeError(error),
|
|
257
|
+
}));
|
|
258
|
+
for (const entry of entries) {
|
|
259
|
+
graph.eagerEventToolExecutions.set(entry.id, {
|
|
260
|
+
toolCallId: entry.id,
|
|
261
|
+
toolName: entry.toolName,
|
|
262
|
+
args: entry.coercedArgs,
|
|
263
|
+
request: entry.request,
|
|
264
|
+
promise,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function getEagerToolChunkKey(stepKey, toolCallChunk) {
|
|
269
|
+
let chunkKey;
|
|
270
|
+
if (typeof toolCallChunk.index === 'number') {
|
|
271
|
+
chunkKey = String(toolCallChunk.index);
|
|
272
|
+
}
|
|
273
|
+
else if (toolCallChunk.id != null && toolCallChunk.id !== '') {
|
|
274
|
+
chunkKey = toolCallChunk.id;
|
|
275
|
+
}
|
|
276
|
+
if (chunkKey == null) {
|
|
277
|
+
return undefined;
|
|
278
|
+
}
|
|
279
|
+
return `${stepKey}\u0000${chunkKey}`;
|
|
280
|
+
}
|
|
281
|
+
function getEagerToolChunkIndex(toolCallChunk) {
|
|
282
|
+
return typeof toolCallChunk.index === 'number'
|
|
283
|
+
? toolCallChunk.index
|
|
284
|
+
: undefined;
|
|
285
|
+
}
|
|
286
|
+
function pruneEagerToolCallChunkStates(args) {
|
|
287
|
+
const { graph, stepKey, toolCallIds, clearStep = false } = args;
|
|
288
|
+
const prefix = `${stepKey}\u0000`;
|
|
289
|
+
for (const [key, state] of graph.eagerEventToolCallChunks) {
|
|
290
|
+
if (!key.startsWith(prefix)) {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (clearStep ||
|
|
294
|
+
(state.id != null && toolCallIds?.has(state.id) === true)) {
|
|
295
|
+
graph.eagerEventToolCallChunks.delete(key);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
function isEagerToolChunkStateComplete(state) {
|
|
300
|
+
return (state.id != null &&
|
|
301
|
+
state.id !== '' &&
|
|
302
|
+
state.name != null &&
|
|
303
|
+
state.name !== '' &&
|
|
304
|
+
eagerEventExecution.coerceRecordArgs(state.argsText) != null);
|
|
305
|
+
}
|
|
306
|
+
function mergeToolCallArgsText(existing, incoming) {
|
|
307
|
+
if (incoming === '') {
|
|
308
|
+
return existing;
|
|
309
|
+
}
|
|
310
|
+
if (existing === '') {
|
|
311
|
+
return incoming;
|
|
312
|
+
}
|
|
313
|
+
if (incoming === existing) {
|
|
314
|
+
try {
|
|
315
|
+
JSON.parse(incoming);
|
|
316
|
+
return incoming;
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
return `${existing}${incoming}`;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (incoming.startsWith(existing)) {
|
|
323
|
+
return incoming;
|
|
324
|
+
}
|
|
325
|
+
if (existing.startsWith(incoming)) {
|
|
326
|
+
return existing;
|
|
327
|
+
}
|
|
328
|
+
try {
|
|
329
|
+
JSON.parse(existing);
|
|
330
|
+
JSON.parse(incoming);
|
|
331
|
+
return incoming;
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
// Fall through to delta concatenation.
|
|
335
|
+
}
|
|
336
|
+
for (let overlap = Math.min(existing.length, incoming.length); overlap >= 8; overlap -= 1) {
|
|
337
|
+
if (existing.endsWith(incoming.slice(0, overlap))) {
|
|
338
|
+
return `${existing}${incoming.slice(overlap)}`;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return `${existing}${incoming}`;
|
|
342
|
+
}
|
|
343
|
+
function recordEagerToolCallChunks(args) {
|
|
344
|
+
const { graph, stepKey, toolCallChunks } = args;
|
|
345
|
+
if (toolCallChunks == null || toolCallChunks.length === 0) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
// Streamed args can be cumulative and parseable before the provider has
|
|
349
|
+
// sealed the call. Recording stays separate from dispatch so the boundary
|
|
350
|
+
// logic can wait for either a later tool index or the final tool-call signal.
|
|
351
|
+
for (const toolCallChunk of toolCallChunks) {
|
|
352
|
+
const key = getEagerToolChunkKey(stepKey, toolCallChunk);
|
|
353
|
+
if (key == null) {
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
const incomingId = toolCallChunk.id != null && toolCallChunk.id !== ''
|
|
357
|
+
? toolCallChunk.id
|
|
358
|
+
: undefined;
|
|
359
|
+
const incomingName = toolCallChunk.name != null && toolCallChunk.name !== ''
|
|
360
|
+
? toolCallChunk.name
|
|
361
|
+
: undefined;
|
|
362
|
+
const previous = graph.eagerEventToolCallChunks.get(key);
|
|
363
|
+
const shouldReset = previous != null &&
|
|
364
|
+
((incomingId != null &&
|
|
365
|
+
previous.id != null &&
|
|
366
|
+
incomingId !== previous.id) ||
|
|
367
|
+
(incomingName != null &&
|
|
368
|
+
previous.name != null &&
|
|
369
|
+
incomingName !== previous.name));
|
|
370
|
+
const existing = previous == null || shouldReset
|
|
371
|
+
? {
|
|
372
|
+
argsText: '',
|
|
373
|
+
}
|
|
374
|
+
: previous;
|
|
375
|
+
const id = incomingId ?? existing.id;
|
|
376
|
+
const name = incomingName ?? existing.name;
|
|
377
|
+
const incomingArgs = toolCallChunk.args ?? '';
|
|
378
|
+
const isRepeatedObservedFragment = incomingArgs !== '' &&
|
|
379
|
+
incomingArgs.length > 1 &&
|
|
380
|
+
incomingArgs === existing.lastArgsFragment;
|
|
381
|
+
const argsText = isRepeatedObservedFragment
|
|
382
|
+
? existing.argsText
|
|
383
|
+
: mergeToolCallArgsText(existing.argsText, incomingArgs);
|
|
384
|
+
const next = {
|
|
385
|
+
id,
|
|
386
|
+
name,
|
|
387
|
+
argsText,
|
|
388
|
+
index: getEagerToolChunkIndex(toolCallChunk) ?? existing.index,
|
|
389
|
+
lastArgsFragment: incomingArgs !== '' ? incomingArgs : existing.lastArgsFragment,
|
|
390
|
+
};
|
|
391
|
+
graph.eagerEventToolCallChunks.set(key, next);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
function getStreamedReadyToolCalls(args) {
|
|
395
|
+
const { graph, stepKey, toolCallChunks, seal, allowSequentialSeal = false, sealAll = false, } = args;
|
|
396
|
+
const currentIndices = new Set();
|
|
397
|
+
for (const toolCallChunk of toolCallChunks ?? []) {
|
|
398
|
+
const index = getEagerToolChunkIndex(toolCallChunk);
|
|
399
|
+
if (index != null) {
|
|
400
|
+
currentIndices.add(index);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
const highestCurrentIndex = currentIndices.size > 0 ? Math.max(...currentIndices) : undefined;
|
|
404
|
+
const prefix = `${stepKey}\u0000`;
|
|
405
|
+
const readyEntries = [];
|
|
406
|
+
for (const [key, state] of graph.eagerEventToolCallChunks) {
|
|
407
|
+
if (!key.startsWith(prefix)) {
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
if (state.id != null && graph.eagerEventToolExecutions.has(state.id)) {
|
|
411
|
+
graph.eagerEventToolCallChunks.delete(key);
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
if (!isEagerToolChunkStateComplete(state)) {
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
const isSealedByLaterChunk = allowSequentialSeal &&
|
|
418
|
+
highestCurrentIndex != null &&
|
|
419
|
+
state.index != null &&
|
|
420
|
+
state.index < highestCurrentIndex &&
|
|
421
|
+
!currentIndices.has(state.index);
|
|
422
|
+
const isSealedExplicitly = seal?.kind === 'single' &&
|
|
423
|
+
((seal.id != null && state.id === seal.id) ||
|
|
424
|
+
(seal.index != null && state.index === seal.index));
|
|
425
|
+
if (sealAll ||
|
|
426
|
+
seal?.kind === 'all' ||
|
|
427
|
+
isSealedByLaterChunk ||
|
|
428
|
+
isSealedExplicitly) {
|
|
429
|
+
readyEntries.push({ key, state });
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
pruneEagerToolCallChunkStates({
|
|
433
|
+
graph,
|
|
434
|
+
stepKey,
|
|
435
|
+
toolCallIds: new Set(readyEntries
|
|
436
|
+
.map(({ state }) => state.id)
|
|
437
|
+
.filter((id) => id != null && id !== '')),
|
|
438
|
+
});
|
|
439
|
+
if (sealAll) {
|
|
440
|
+
pruneEagerToolCallChunkStates({ graph, stepKey, clearStep: true });
|
|
441
|
+
}
|
|
442
|
+
return readyEntries
|
|
443
|
+
.sort((left, right) => (left.state.index ?? 0) - (right.state.index ?? 0))
|
|
444
|
+
.flatMap(({ state }) => {
|
|
445
|
+
const args = eagerEventExecution.coerceRecordArgs(state.argsText);
|
|
446
|
+
if (args == null) {
|
|
447
|
+
return [];
|
|
448
|
+
}
|
|
449
|
+
return [
|
|
450
|
+
{
|
|
451
|
+
id: state.id,
|
|
452
|
+
name: state.name ?? '',
|
|
453
|
+
args,
|
|
454
|
+
},
|
|
455
|
+
];
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
function startReadyStreamedEagerToolExecutions(args) {
|
|
459
|
+
const { graph, metadata, agentContext, stepKey, toolCallChunks, seal, allowSequentialSeal, sealAll, } = args;
|
|
460
|
+
if (hasPotentialDirectToolInStreamContext({ graph, agentContext }) ||
|
|
461
|
+
!isEagerToolExecutionEnabledForBatch({ graph, metadata, agentContext })) {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
const toolCalls = getStreamedReadyToolCalls({
|
|
465
|
+
graph,
|
|
466
|
+
stepKey,
|
|
467
|
+
toolCallChunks,
|
|
468
|
+
seal,
|
|
469
|
+
allowSequentialSeal,
|
|
470
|
+
sealAll,
|
|
471
|
+
});
|
|
472
|
+
if (toolCalls.length === 0) {
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
startEagerToolExecutions({
|
|
476
|
+
graph,
|
|
477
|
+
metadata,
|
|
478
|
+
agentContext,
|
|
479
|
+
toolCalls,
|
|
480
|
+
skipExisting: true,
|
|
481
|
+
});
|
|
482
|
+
}
|
|
58
483
|
function getChunkContent({ chunk, provider, reasoningKey, }) {
|
|
59
484
|
if ((provider === _enum.Providers.OPENAI || provider === _enum.Providers.AZURE) &&
|
|
60
485
|
chunk?.additional_kwargs?.reasoning?.summary?.[0]?.text != null &&
|
|
@@ -115,7 +540,9 @@ class ChatModelStreamHandler {
|
|
|
115
540
|
return;
|
|
116
541
|
}
|
|
117
542
|
this.handleReasoning(chunk, agentContext);
|
|
543
|
+
const stepKey = graph.getStepKey(metadata);
|
|
118
544
|
let hasToolCalls = false;
|
|
545
|
+
const hasToolCallChunks = (chunk.tool_call_chunks && chunk.tool_call_chunks.length > 0) ?? false;
|
|
119
546
|
if (chunk.tool_calls &&
|
|
120
547
|
chunk.tool_calls.length > 0 &&
|
|
121
548
|
chunk.tool_calls.every((tc) => tc.id != null &&
|
|
@@ -124,8 +551,19 @@ class ChatModelStreamHandler {
|
|
|
124
551
|
tc.name !== '')) {
|
|
125
552
|
hasToolCalls = true;
|
|
126
553
|
await handlers.handleToolCalls(chunk.tool_calls, metadata, graph);
|
|
554
|
+
if (hasFinalToolCallSignal(chunk)) {
|
|
555
|
+
startEagerToolExecutions({
|
|
556
|
+
graph,
|
|
557
|
+
metadata,
|
|
558
|
+
agentContext,
|
|
559
|
+
toolCalls: chunk.tool_calls,
|
|
560
|
+
skipExisting: true,
|
|
561
|
+
});
|
|
562
|
+
if (!hasToolCallChunks) {
|
|
563
|
+
pruneEagerToolCallChunkStates({ graph, stepKey, clearStep: true });
|
|
564
|
+
}
|
|
565
|
+
}
|
|
127
566
|
}
|
|
128
|
-
const hasToolCallChunks = (chunk.tool_call_chunks && chunk.tool_call_chunks.length > 0) ?? false;
|
|
129
567
|
const isEmptyContent = typeof content === 'undefined' ||
|
|
130
568
|
!content.length ||
|
|
131
569
|
(typeof content === 'string' && !content);
|
|
@@ -134,23 +572,45 @@ class ChatModelStreamHandler {
|
|
|
134
572
|
if (isEmptyChunk &&
|
|
135
573
|
(chunk.id ?? '') !== '' &&
|
|
136
574
|
!graph.prelimMessageIdsByStepKey.has(chunk.id ?? '')) {
|
|
137
|
-
const stepKey = graph.getStepKey(metadata);
|
|
138
575
|
graph.prelimMessageIdsByStepKey.set(stepKey, chunk.id ?? '');
|
|
139
576
|
}
|
|
140
577
|
else if (isEmptyChunk) {
|
|
141
578
|
return;
|
|
142
579
|
}
|
|
143
|
-
const stepKey = graph.getStepKey(metadata);
|
|
144
580
|
if (hasToolCallChunks &&
|
|
145
581
|
chunk.tool_call_chunks &&
|
|
146
582
|
chunk.tool_call_chunks.length &&
|
|
147
583
|
typeof chunk.tool_call_chunks[0]?.index === 'number') {
|
|
584
|
+
const streamedToolCallSeal = streamedToolCallSeals.getStreamedToolCallSeal(chunk.response_metadata);
|
|
585
|
+
const allowSequentialSeal = canPrestartSequentialStreamedToolChunks(agentContext);
|
|
586
|
+
const canStreamEager = (allowSequentialSeal || hasExplicitStreamedToolCallSeals(chunk)) &&
|
|
587
|
+
!hasPotentialDirectToolInStreamContext({ graph, agentContext }) &&
|
|
588
|
+
isEagerToolExecutionEnabledForBatch({ graph, metadata, agentContext });
|
|
589
|
+
if (canStreamEager) {
|
|
590
|
+
recordEagerToolCallChunks({
|
|
591
|
+
graph,
|
|
592
|
+
stepKey,
|
|
593
|
+
toolCallChunks: chunk.tool_call_chunks,
|
|
594
|
+
});
|
|
595
|
+
}
|
|
148
596
|
await handlers.handleToolCallChunks({
|
|
149
597
|
graph,
|
|
150
598
|
stepKey,
|
|
151
599
|
toolCallChunks: chunk.tool_call_chunks,
|
|
152
600
|
metadata,
|
|
153
601
|
});
|
|
602
|
+
if (canStreamEager) {
|
|
603
|
+
startReadyStreamedEagerToolExecutions({
|
|
604
|
+
graph,
|
|
605
|
+
metadata,
|
|
606
|
+
agentContext,
|
|
607
|
+
stepKey,
|
|
608
|
+
toolCallChunks: chunk.tool_call_chunks,
|
|
609
|
+
seal: streamedToolCallSeal,
|
|
610
|
+
allowSequentialSeal,
|
|
611
|
+
sealAll: hasFinalToolCallSignal(chunk),
|
|
612
|
+
});
|
|
613
|
+
}
|
|
154
614
|
}
|
|
155
615
|
if (isEmptyContent) {
|
|
156
616
|
return;
|
|
@@ -201,7 +661,7 @@ hasToolCallChunks: ${hasToolCallChunks}
|
|
|
201
661
|
text: content,
|
|
202
662
|
},
|
|
203
663
|
],
|
|
204
|
-
});
|
|
664
|
+
}, metadata);
|
|
205
665
|
}
|
|
206
666
|
else if (agentContext.currentTokenType === 'think_and_text') {
|
|
207
667
|
const { text, thinking } = parseThinkingContent(content);
|
|
@@ -213,7 +673,7 @@ hasToolCallChunks: ${hasToolCallChunks}
|
|
|
213
673
|
think: thinking,
|
|
214
674
|
},
|
|
215
675
|
],
|
|
216
|
-
});
|
|
676
|
+
}, metadata);
|
|
217
677
|
}
|
|
218
678
|
if (text) {
|
|
219
679
|
agentContext.currentTokenType = _enum.ContentTypes.TEXT;
|
|
@@ -234,7 +694,7 @@ hasToolCallChunks: ${hasToolCallChunks}
|
|
|
234
694
|
text: text,
|
|
235
695
|
},
|
|
236
696
|
],
|
|
237
|
-
});
|
|
697
|
+
}, metadata);
|
|
238
698
|
}
|
|
239
699
|
}
|
|
240
700
|
else {
|
|
@@ -245,13 +705,13 @@ hasToolCallChunks: ${hasToolCallChunks}
|
|
|
245
705
|
think: content,
|
|
246
706
|
},
|
|
247
707
|
],
|
|
248
|
-
});
|
|
708
|
+
}, metadata);
|
|
249
709
|
}
|
|
250
710
|
}
|
|
251
711
|
else if (content.every((c) => c.type?.startsWith(_enum.ContentTypes.TEXT) ?? false)) {
|
|
252
712
|
await graph.dispatchMessageDelta(stepId, {
|
|
253
713
|
content,
|
|
254
|
-
});
|
|
714
|
+
}, metadata);
|
|
255
715
|
}
|
|
256
716
|
else if (content.every((c) => (c.type?.startsWith(_enum.ContentTypes.THINKING) ?? false) ||
|
|
257
717
|
(c.type?.startsWith(_enum.ContentTypes.REASONING) ?? false) ||
|
|
@@ -262,10 +722,11 @@ hasToolCallChunks: ${hasToolCallChunks}
|
|
|
262
722
|
type: _enum.ContentTypes.THINK,
|
|
263
723
|
think: c.thinking ??
|
|
264
724
|
c.reasoning ??
|
|
265
|
-
c.reasoningText
|
|
725
|
+
c.reasoningText
|
|
726
|
+
?.text ??
|
|
266
727
|
'',
|
|
267
728
|
})),
|
|
268
|
-
});
|
|
729
|
+
}, metadata);
|
|
269
730
|
}
|
|
270
731
|
}
|
|
271
732
|
handleReasoning(chunk, agentContext) {
|