@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/src/stream.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/stream.ts
|
|
2
2
|
import type { ChatOpenAIReasoningSummary } from '@langchain/openai';
|
|
3
3
|
import type { AIMessageChunk } from '@langchain/core/messages';
|
|
4
|
-
import type { ToolCall } from '@langchain/core/messages/tool';
|
|
4
|
+
import type { ToolCall, ToolCallChunk } from '@langchain/core/messages/tool';
|
|
5
5
|
import type { AgentContext } from '@/agents/AgentContext';
|
|
6
6
|
import type { StandardGraph } from '@/graphs';
|
|
7
7
|
import type * as t from '@/types';
|
|
@@ -11,6 +11,9 @@ import {
|
|
|
11
11
|
GraphEvents,
|
|
12
12
|
StepTypes,
|
|
13
13
|
Providers,
|
|
14
|
+
Constants,
|
|
15
|
+
CODE_EXECUTION_TOOLS,
|
|
16
|
+
LOCAL_CODING_BUNDLE_NAMES,
|
|
14
17
|
} from '@/common';
|
|
15
18
|
import {
|
|
16
19
|
handleServerToolResult,
|
|
@@ -18,6 +21,21 @@ import {
|
|
|
18
21
|
handleToolCalls,
|
|
19
22
|
} from '@/tools/handlers';
|
|
20
23
|
import { getMessageId } from '@/messages';
|
|
24
|
+
import { safeDispatchCustomEvent } from '@/utils/events';
|
|
25
|
+
import {
|
|
26
|
+
buildToolExecutionRequestPlan,
|
|
27
|
+
coerceRecordArgs,
|
|
28
|
+
normalizeError,
|
|
29
|
+
} from '@/tools/eagerEventExecution';
|
|
30
|
+
import {
|
|
31
|
+
getStreamedToolCallSeal,
|
|
32
|
+
getStreamedToolCallAdapter,
|
|
33
|
+
type StreamedToolCallSeal,
|
|
34
|
+
} from '@/tools/streamedToolCallSeals';
|
|
35
|
+
|
|
36
|
+
const LOCAL_CODING_BUNDLE_NAME_SET: ReadonlySet<string> = new Set(
|
|
37
|
+
LOCAL_CODING_BUNDLE_NAMES
|
|
38
|
+
);
|
|
21
39
|
|
|
22
40
|
/**
|
|
23
41
|
* Parses content to extract thinking sections enclosed in <think> tags using string operations
|
|
@@ -79,6 +97,631 @@ function getNonEmptyValue(possibleValues: string[]): string | undefined {
|
|
|
79
97
|
return undefined;
|
|
80
98
|
}
|
|
81
99
|
|
|
100
|
+
function isBatchSensitiveToolExecution(graph: StandardGraph): boolean {
|
|
101
|
+
return (
|
|
102
|
+
graph.hookRegistry != null ||
|
|
103
|
+
graph.humanInTheLoop?.enabled === true ||
|
|
104
|
+
graph.toolOutputReferences?.enabled === true
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function isDirectGraphTool(
|
|
109
|
+
name: string,
|
|
110
|
+
agentContext: AgentContext | undefined
|
|
111
|
+
): boolean {
|
|
112
|
+
if (name.startsWith(Constants.LC_TRANSFER_TO_)) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
return (
|
|
116
|
+
(agentContext?.graphTools as t.GenericTool[] | undefined)?.some(
|
|
117
|
+
(tool) => 'name' in tool && tool.name === name
|
|
118
|
+
) === true
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function isDirectLocalTool(name: string, graph: StandardGraph): boolean {
|
|
123
|
+
if (graph.toolExecution?.engine !== 'local') {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
if (graph.toolExecution.local?.includeCodingTools === false) {
|
|
127
|
+
return CODE_EXECUTION_TOOLS.has(name);
|
|
128
|
+
}
|
|
129
|
+
return LOCAL_CODING_BUNDLE_NAME_SET.has(name);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function toCodeEnvFile(file: t.FileRef, execSessionId: string): t.CodeEnvFile {
|
|
133
|
+
const base = {
|
|
134
|
+
id: file.id,
|
|
135
|
+
resource_id: file.resource_id ?? file.id,
|
|
136
|
+
name: file.name,
|
|
137
|
+
storage_session_id: file.storage_session_id ?? execSessionId,
|
|
138
|
+
};
|
|
139
|
+
const kind = file.kind ?? 'user';
|
|
140
|
+
if (kind === 'skill' && file.version != null) {
|
|
141
|
+
return { ...base, kind: 'skill', version: file.version };
|
|
142
|
+
}
|
|
143
|
+
if (kind === 'agent') {
|
|
144
|
+
return { ...base, kind: 'agent' };
|
|
145
|
+
}
|
|
146
|
+
return { ...base, kind: 'user' };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function getCodeSessionContext(
|
|
150
|
+
graph: StandardGraph,
|
|
151
|
+
name: string
|
|
152
|
+
): t.ToolCallRequest['codeSessionContext'] | undefined {
|
|
153
|
+
if (
|
|
154
|
+
!CODE_EXECUTION_TOOLS.has(name) &&
|
|
155
|
+
name !== Constants.SKILL_TOOL &&
|
|
156
|
+
name !== Constants.READ_FILE
|
|
157
|
+
) {
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const codeSession = graph.sessions.get(Constants.EXECUTE_CODE) as
|
|
162
|
+
| t.CodeSessionContext
|
|
163
|
+
| undefined;
|
|
164
|
+
if (codeSession?.session_id == null || codeSession.session_id === '') {
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
session_id: codeSession.session_id,
|
|
170
|
+
files: codeSession.files?.map((file) =>
|
|
171
|
+
toCodeEnvFile(file, codeSession.session_id)
|
|
172
|
+
),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function isEagerToolExecutionEnabledForBatch(args: {
|
|
177
|
+
graph: StandardGraph;
|
|
178
|
+
metadata?: Record<string, unknown>;
|
|
179
|
+
agentContext?: AgentContext;
|
|
180
|
+
}): boolean {
|
|
181
|
+
const { graph, metadata, agentContext } = args;
|
|
182
|
+
if (graph.eagerEventToolExecution?.enabled !== true) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
if ((agentContext?.toolDefinitions?.length ?? 0) === 0) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
if (isBatchSensitiveToolExecution(graph)) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
if (
|
|
192
|
+
metadata?.[Constants.PROGRAMMATIC_TOOL_CALLING] === true ||
|
|
193
|
+
metadata?.[Constants.BASH_PROGRAMMATIC_TOOL_CALLING] === true
|
|
194
|
+
) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
if (graph.handlerRegistry?.getHandler(GraphEvents.ON_TOOL_EXECUTE) == null) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function hasFinalToolCallSignal(chunk: Partial<AIMessageChunk>): boolean {
|
|
204
|
+
const metadata = chunk.response_metadata as
|
|
205
|
+
| Record<string, unknown>
|
|
206
|
+
| undefined;
|
|
207
|
+
const finishReason =
|
|
208
|
+
metadata?.finish_reason ??
|
|
209
|
+
metadata?.finishReason ??
|
|
210
|
+
metadata?.stop_reason ??
|
|
211
|
+
metadata?.stopReason;
|
|
212
|
+
return finishReason === 'tool_calls' || finishReason === 'tool_use';
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function canPrestartSequentialStreamedToolChunks(
|
|
216
|
+
agentContext: AgentContext | undefined
|
|
217
|
+
): boolean {
|
|
218
|
+
return (
|
|
219
|
+
agentContext?.provider === Providers.ANTHROPIC ||
|
|
220
|
+
agentContext?.provider === Providers.MOONSHOT
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function hasExplicitStreamedToolCallSeals(
|
|
225
|
+
chunk: Partial<AIMessageChunk>
|
|
226
|
+
): boolean {
|
|
227
|
+
return (
|
|
228
|
+
getStreamedToolCallAdapter(
|
|
229
|
+
chunk.response_metadata as Record<string, unknown> | undefined
|
|
230
|
+
) != null
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function hasDirectToolCallInBatch(args: {
|
|
235
|
+
graph: StandardGraph;
|
|
236
|
+
agentContext?: AgentContext;
|
|
237
|
+
toolCalls: ToolCall[];
|
|
238
|
+
}): boolean {
|
|
239
|
+
const { graph, agentContext, toolCalls } = args;
|
|
240
|
+
return toolCalls.some(
|
|
241
|
+
(toolCall) =>
|
|
242
|
+
toolCall.name !== '' &&
|
|
243
|
+
(isDirectGraphTool(toolCall.name, agentContext) ||
|
|
244
|
+
isDirectLocalTool(toolCall.name, graph))
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function hasPotentialDirectToolInStreamContext(args: {
|
|
249
|
+
graph: StandardGraph;
|
|
250
|
+
agentContext?: AgentContext;
|
|
251
|
+
}): boolean {
|
|
252
|
+
const { graph, agentContext } = args;
|
|
253
|
+
if (graph.toolExecution?.engine === 'local') {
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
if ((agentContext?.graphTools?.length ?? 0) > 0) {
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
return (
|
|
260
|
+
agentContext?.toolDefinitions?.some((toolDefinition) =>
|
|
261
|
+
toolDefinition.name.startsWith(Constants.LC_TRANSFER_TO_)
|
|
262
|
+
) === true
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
type EagerToolExecutionEntry = {
|
|
267
|
+
id: string;
|
|
268
|
+
toolName: string;
|
|
269
|
+
coercedArgs: Record<string, unknown>;
|
|
270
|
+
request: t.ToolCallRequest;
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
function createEagerToolExecutionPlan(args: {
|
|
274
|
+
graph: StandardGraph;
|
|
275
|
+
metadata?: Record<string, unknown>;
|
|
276
|
+
agentContext?: AgentContext;
|
|
277
|
+
toolCalls: ToolCall[];
|
|
278
|
+
skipExisting?: boolean;
|
|
279
|
+
}): EagerToolExecutionEntry[] | undefined {
|
|
280
|
+
const {
|
|
281
|
+
graph,
|
|
282
|
+
metadata,
|
|
283
|
+
agentContext,
|
|
284
|
+
toolCalls,
|
|
285
|
+
skipExisting = false,
|
|
286
|
+
} = args;
|
|
287
|
+
if (
|
|
288
|
+
!isEagerToolExecutionEnabledForBatch({
|
|
289
|
+
graph,
|
|
290
|
+
metadata,
|
|
291
|
+
agentContext,
|
|
292
|
+
})
|
|
293
|
+
) {
|
|
294
|
+
return undefined;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (hasDirectToolCallInBatch({ graph, agentContext, toolCalls })) {
|
|
298
|
+
return undefined;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const candidateToolCalls = skipExisting
|
|
302
|
+
? toolCalls.filter((toolCall) => {
|
|
303
|
+
if (toolCall.id == null || toolCall.id === '') {
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
return !graph.eagerEventToolExecutions.has(toolCall.id);
|
|
307
|
+
})
|
|
308
|
+
: toolCalls;
|
|
309
|
+
if (candidateToolCalls.length === 0) {
|
|
310
|
+
return [];
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Eager execution must preserve ToolNode batch semantics exactly for every
|
|
314
|
+
// unstarted call. If any candidate cannot be planned, fall back for that
|
|
315
|
+
// candidate set.
|
|
316
|
+
if (
|
|
317
|
+
candidateToolCalls.some(
|
|
318
|
+
(toolCall) =>
|
|
319
|
+
toolCall.id == null ||
|
|
320
|
+
toolCall.id === '' ||
|
|
321
|
+
toolCall.name === '' ||
|
|
322
|
+
(!skipExisting && graph.eagerEventToolExecutions.has(toolCall.id))
|
|
323
|
+
)
|
|
324
|
+
) {
|
|
325
|
+
return undefined;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const plan = buildToolExecutionRequestPlan({
|
|
329
|
+
toolCalls: candidateToolCalls.map((toolCall) => ({
|
|
330
|
+
id: toolCall.id,
|
|
331
|
+
name: toolCall.name,
|
|
332
|
+
args: toolCall.args,
|
|
333
|
+
stepId: graph.toolCallStepIds.get(toolCall.id!) ?? '',
|
|
334
|
+
codeSessionContext: getCodeSessionContext(graph, toolCall.name),
|
|
335
|
+
})),
|
|
336
|
+
usageCount: graph.getEagerEventToolUsageCount(agentContext?.agentId),
|
|
337
|
+
});
|
|
338
|
+
if (plan == null) {
|
|
339
|
+
return undefined;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return plan.requests.map(
|
|
343
|
+
(request): EagerToolExecutionEntry => ({
|
|
344
|
+
id: request.id,
|
|
345
|
+
toolName: request.name,
|
|
346
|
+
coercedArgs: request.args,
|
|
347
|
+
request,
|
|
348
|
+
})
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function startEagerToolExecutions(args: {
|
|
353
|
+
graph: StandardGraph;
|
|
354
|
+
metadata?: Record<string, unknown>;
|
|
355
|
+
agentContext?: AgentContext;
|
|
356
|
+
toolCalls: ToolCall[];
|
|
357
|
+
skipExisting?: boolean;
|
|
358
|
+
}): void {
|
|
359
|
+
const { graph, metadata, agentContext, toolCalls, skipExisting } = args;
|
|
360
|
+
const entries = createEagerToolExecutionPlan({
|
|
361
|
+
graph,
|
|
362
|
+
metadata,
|
|
363
|
+
agentContext,
|
|
364
|
+
toolCalls,
|
|
365
|
+
skipExisting,
|
|
366
|
+
});
|
|
367
|
+
if (entries == null || entries.length === 0) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const promise: Promise<t.EagerEventToolExecutionOutcome> = new Promise<
|
|
372
|
+
t.ToolExecuteResult[]
|
|
373
|
+
>((resolve, reject) => {
|
|
374
|
+
let dispatchSettled = false;
|
|
375
|
+
let resultSettled = false;
|
|
376
|
+
let settledResults: t.ToolExecuteResult[] | undefined;
|
|
377
|
+
const maybeResolve = (): void => {
|
|
378
|
+
if (dispatchSettled && resultSettled) {
|
|
379
|
+
resolve(settledResults ?? []);
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
const batchRequest: t.ToolExecuteBatchRequest = {
|
|
383
|
+
toolCalls: entries.map((entry) => entry.request),
|
|
384
|
+
userId: graph.config?.configurable?.user_id as string | undefined,
|
|
385
|
+
agentId: agentContext?.agentId,
|
|
386
|
+
configurable: graph.config?.configurable as
|
|
387
|
+
| Record<string, unknown>
|
|
388
|
+
| undefined,
|
|
389
|
+
metadata,
|
|
390
|
+
resolve: (results): void => {
|
|
391
|
+
resultSettled = true;
|
|
392
|
+
settledResults = results;
|
|
393
|
+
maybeResolve();
|
|
394
|
+
},
|
|
395
|
+
reject,
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
void safeDispatchCustomEvent(
|
|
399
|
+
GraphEvents.ON_TOOL_EXECUTE,
|
|
400
|
+
batchRequest,
|
|
401
|
+
graph.config
|
|
402
|
+
)
|
|
403
|
+
.then(() => {
|
|
404
|
+
dispatchSettled = true;
|
|
405
|
+
maybeResolve();
|
|
406
|
+
})
|
|
407
|
+
.catch(reject);
|
|
408
|
+
}).then(
|
|
409
|
+
(results): t.EagerEventToolExecutionOutcome => ({ results }),
|
|
410
|
+
(error): t.EagerEventToolExecutionOutcome => ({
|
|
411
|
+
error: normalizeError(error),
|
|
412
|
+
})
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
for (const entry of entries) {
|
|
416
|
+
graph.eagerEventToolExecutions.set(entry.id, {
|
|
417
|
+
toolCallId: entry.id,
|
|
418
|
+
toolName: entry.toolName,
|
|
419
|
+
args: entry.coercedArgs,
|
|
420
|
+
request: entry.request,
|
|
421
|
+
promise,
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function getEagerToolChunkKey(
|
|
427
|
+
stepKey: string,
|
|
428
|
+
toolCallChunk: ToolCallChunk
|
|
429
|
+
): string | undefined {
|
|
430
|
+
let chunkKey: string | undefined;
|
|
431
|
+
if (typeof toolCallChunk.index === 'number') {
|
|
432
|
+
chunkKey = String(toolCallChunk.index);
|
|
433
|
+
} else if (toolCallChunk.id != null && toolCallChunk.id !== '') {
|
|
434
|
+
chunkKey = toolCallChunk.id;
|
|
435
|
+
}
|
|
436
|
+
if (chunkKey == null) {
|
|
437
|
+
return undefined;
|
|
438
|
+
}
|
|
439
|
+
return `${stepKey}\u0000${chunkKey}`;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function getEagerToolChunkIndex(
|
|
443
|
+
toolCallChunk: ToolCallChunk
|
|
444
|
+
): number | undefined {
|
|
445
|
+
return typeof toolCallChunk.index === 'number'
|
|
446
|
+
? toolCallChunk.index
|
|
447
|
+
: undefined;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function pruneEagerToolCallChunkStates(args: {
|
|
451
|
+
graph: StandardGraph;
|
|
452
|
+
stepKey: string;
|
|
453
|
+
toolCallIds?: ReadonlySet<string>;
|
|
454
|
+
clearStep?: boolean;
|
|
455
|
+
}): void {
|
|
456
|
+
const { graph, stepKey, toolCallIds, clearStep = false } = args;
|
|
457
|
+
const prefix = `${stepKey}\u0000`;
|
|
458
|
+
for (const [key, state] of graph.eagerEventToolCallChunks) {
|
|
459
|
+
if (!key.startsWith(prefix)) {
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
if (
|
|
463
|
+
clearStep ||
|
|
464
|
+
(state.id != null && toolCallIds?.has(state.id) === true)
|
|
465
|
+
) {
|
|
466
|
+
graph.eagerEventToolCallChunks.delete(key);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function isEagerToolChunkStateComplete(
|
|
472
|
+
state: t.EagerEventToolCallChunkState
|
|
473
|
+
): boolean {
|
|
474
|
+
return (
|
|
475
|
+
state.id != null &&
|
|
476
|
+
state.id !== '' &&
|
|
477
|
+
state.name != null &&
|
|
478
|
+
state.name !== '' &&
|
|
479
|
+
coerceRecordArgs(state.argsText) != null
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function mergeToolCallArgsText(existing: string, incoming: string): string {
|
|
484
|
+
if (incoming === '') {
|
|
485
|
+
return existing;
|
|
486
|
+
}
|
|
487
|
+
if (existing === '') {
|
|
488
|
+
return incoming;
|
|
489
|
+
}
|
|
490
|
+
if (incoming === existing) {
|
|
491
|
+
try {
|
|
492
|
+
JSON.parse(incoming);
|
|
493
|
+
return incoming;
|
|
494
|
+
} catch {
|
|
495
|
+
return `${existing}${incoming}`;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
if (incoming.startsWith(existing)) {
|
|
499
|
+
return incoming;
|
|
500
|
+
}
|
|
501
|
+
if (existing.startsWith(incoming)) {
|
|
502
|
+
return existing;
|
|
503
|
+
}
|
|
504
|
+
try {
|
|
505
|
+
JSON.parse(existing);
|
|
506
|
+
JSON.parse(incoming);
|
|
507
|
+
return incoming;
|
|
508
|
+
} catch {
|
|
509
|
+
// Fall through to delta concatenation.
|
|
510
|
+
}
|
|
511
|
+
for (
|
|
512
|
+
let overlap = Math.min(existing.length, incoming.length);
|
|
513
|
+
overlap >= 8;
|
|
514
|
+
overlap -= 1
|
|
515
|
+
) {
|
|
516
|
+
if (existing.endsWith(incoming.slice(0, overlap))) {
|
|
517
|
+
return `${existing}${incoming.slice(overlap)}`;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return `${existing}${incoming}`;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function recordEagerToolCallChunks(args: {
|
|
524
|
+
graph: StandardGraph;
|
|
525
|
+
stepKey: string;
|
|
526
|
+
toolCallChunks?: ToolCallChunk[];
|
|
527
|
+
}): void {
|
|
528
|
+
const { graph, stepKey, toolCallChunks } = args;
|
|
529
|
+
if (toolCallChunks == null || toolCallChunks.length === 0) {
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// Streamed args can be cumulative and parseable before the provider has
|
|
534
|
+
// sealed the call. Recording stays separate from dispatch so the boundary
|
|
535
|
+
// logic can wait for either a later tool index or the final tool-call signal.
|
|
536
|
+
for (const toolCallChunk of toolCallChunks) {
|
|
537
|
+
const key = getEagerToolChunkKey(stepKey, toolCallChunk);
|
|
538
|
+
if (key == null) {
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const incomingId =
|
|
543
|
+
toolCallChunk.id != null && toolCallChunk.id !== ''
|
|
544
|
+
? toolCallChunk.id
|
|
545
|
+
: undefined;
|
|
546
|
+
const incomingName =
|
|
547
|
+
toolCallChunk.name != null && toolCallChunk.name !== ''
|
|
548
|
+
? toolCallChunk.name
|
|
549
|
+
: undefined;
|
|
550
|
+
const previous = graph.eagerEventToolCallChunks.get(key);
|
|
551
|
+
const shouldReset =
|
|
552
|
+
previous != null &&
|
|
553
|
+
((incomingId != null &&
|
|
554
|
+
previous.id != null &&
|
|
555
|
+
incomingId !== previous.id) ||
|
|
556
|
+
(incomingName != null &&
|
|
557
|
+
previous.name != null &&
|
|
558
|
+
incomingName !== previous.name));
|
|
559
|
+
const existing =
|
|
560
|
+
previous == null || shouldReset
|
|
561
|
+
? {
|
|
562
|
+
argsText: '',
|
|
563
|
+
}
|
|
564
|
+
: previous;
|
|
565
|
+
const id = incomingId ?? existing.id;
|
|
566
|
+
const name = incomingName ?? existing.name;
|
|
567
|
+
const incomingArgs = toolCallChunk.args ?? '';
|
|
568
|
+
const isRepeatedObservedFragment =
|
|
569
|
+
incomingArgs !== '' &&
|
|
570
|
+
incomingArgs.length > 1 &&
|
|
571
|
+
incomingArgs === existing.lastArgsFragment;
|
|
572
|
+
const argsText = isRepeatedObservedFragment
|
|
573
|
+
? existing.argsText
|
|
574
|
+
: mergeToolCallArgsText(existing.argsText, incomingArgs);
|
|
575
|
+
const next = {
|
|
576
|
+
id,
|
|
577
|
+
name,
|
|
578
|
+
argsText,
|
|
579
|
+
index: getEagerToolChunkIndex(toolCallChunk) ?? existing.index,
|
|
580
|
+
lastArgsFragment:
|
|
581
|
+
incomingArgs !== '' ? incomingArgs : existing.lastArgsFragment,
|
|
582
|
+
};
|
|
583
|
+
graph.eagerEventToolCallChunks.set(key, next);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function getStreamedReadyToolCalls(args: {
|
|
588
|
+
graph: StandardGraph;
|
|
589
|
+
stepKey: string;
|
|
590
|
+
toolCallChunks?: ToolCallChunk[];
|
|
591
|
+
seal?: StreamedToolCallSeal;
|
|
592
|
+
allowSequentialSeal?: boolean;
|
|
593
|
+
sealAll?: boolean;
|
|
594
|
+
}): ToolCall[] {
|
|
595
|
+
const {
|
|
596
|
+
graph,
|
|
597
|
+
stepKey,
|
|
598
|
+
toolCallChunks,
|
|
599
|
+
seal,
|
|
600
|
+
allowSequentialSeal = false,
|
|
601
|
+
sealAll = false,
|
|
602
|
+
} = args;
|
|
603
|
+
const currentIndices = new Set<number>();
|
|
604
|
+
for (const toolCallChunk of toolCallChunks ?? []) {
|
|
605
|
+
const index = getEagerToolChunkIndex(toolCallChunk);
|
|
606
|
+
if (index != null) {
|
|
607
|
+
currentIndices.add(index);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
const highestCurrentIndex =
|
|
611
|
+
currentIndices.size > 0 ? Math.max(...currentIndices) : undefined;
|
|
612
|
+
const prefix = `${stepKey}\u0000`;
|
|
613
|
+
const readyEntries: Array<{
|
|
614
|
+
key: string;
|
|
615
|
+
state: t.EagerEventToolCallChunkState;
|
|
616
|
+
}> = [];
|
|
617
|
+
|
|
618
|
+
for (const [key, state] of graph.eagerEventToolCallChunks) {
|
|
619
|
+
if (!key.startsWith(prefix)) {
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
if (state.id != null && graph.eagerEventToolExecutions.has(state.id)) {
|
|
623
|
+
graph.eagerEventToolCallChunks.delete(key);
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
if (!isEagerToolChunkStateComplete(state)) {
|
|
627
|
+
continue;
|
|
628
|
+
}
|
|
629
|
+
const isSealedByLaterChunk =
|
|
630
|
+
allowSequentialSeal &&
|
|
631
|
+
highestCurrentIndex != null &&
|
|
632
|
+
state.index != null &&
|
|
633
|
+
state.index < highestCurrentIndex &&
|
|
634
|
+
!currentIndices.has(state.index);
|
|
635
|
+
const isSealedExplicitly =
|
|
636
|
+
seal?.kind === 'single' &&
|
|
637
|
+
((seal.id != null && state.id === seal.id) ||
|
|
638
|
+
(seal.index != null && state.index === seal.index));
|
|
639
|
+
if (
|
|
640
|
+
sealAll ||
|
|
641
|
+
seal?.kind === 'all' ||
|
|
642
|
+
isSealedByLaterChunk ||
|
|
643
|
+
isSealedExplicitly
|
|
644
|
+
) {
|
|
645
|
+
readyEntries.push({ key, state });
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
pruneEagerToolCallChunkStates({
|
|
650
|
+
graph,
|
|
651
|
+
stepKey,
|
|
652
|
+
toolCallIds: new Set(
|
|
653
|
+
readyEntries
|
|
654
|
+
.map(({ state }) => state.id)
|
|
655
|
+
.filter((id): id is string => id != null && id !== '')
|
|
656
|
+
),
|
|
657
|
+
});
|
|
658
|
+
if (sealAll) {
|
|
659
|
+
pruneEagerToolCallChunkStates({ graph, stepKey, clearStep: true });
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
return readyEntries
|
|
663
|
+
.sort((left, right) => (left.state.index ?? 0) - (right.state.index ?? 0))
|
|
664
|
+
.flatMap(({ state }) => {
|
|
665
|
+
const args = coerceRecordArgs(state.argsText);
|
|
666
|
+
if (args == null) {
|
|
667
|
+
return [];
|
|
668
|
+
}
|
|
669
|
+
return [
|
|
670
|
+
{
|
|
671
|
+
id: state.id,
|
|
672
|
+
name: state.name ?? '',
|
|
673
|
+
args,
|
|
674
|
+
},
|
|
675
|
+
];
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
function startReadyStreamedEagerToolExecutions(args: {
|
|
680
|
+
graph: StandardGraph;
|
|
681
|
+
metadata?: Record<string, unknown>;
|
|
682
|
+
agentContext?: AgentContext;
|
|
683
|
+
stepKey: string;
|
|
684
|
+
toolCallChunks?: ToolCallChunk[];
|
|
685
|
+
seal?: StreamedToolCallSeal;
|
|
686
|
+
allowSequentialSeal?: boolean;
|
|
687
|
+
sealAll?: boolean;
|
|
688
|
+
}): void {
|
|
689
|
+
const {
|
|
690
|
+
graph,
|
|
691
|
+
metadata,
|
|
692
|
+
agentContext,
|
|
693
|
+
stepKey,
|
|
694
|
+
toolCallChunks,
|
|
695
|
+
seal,
|
|
696
|
+
allowSequentialSeal,
|
|
697
|
+
sealAll,
|
|
698
|
+
} = args;
|
|
699
|
+
if (
|
|
700
|
+
hasPotentialDirectToolInStreamContext({ graph, agentContext }) ||
|
|
701
|
+
!isEagerToolExecutionEnabledForBatch({ graph, metadata, agentContext })
|
|
702
|
+
) {
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
const toolCalls = getStreamedReadyToolCalls({
|
|
706
|
+
graph,
|
|
707
|
+
stepKey,
|
|
708
|
+
toolCallChunks,
|
|
709
|
+
seal,
|
|
710
|
+
allowSequentialSeal,
|
|
711
|
+
sealAll,
|
|
712
|
+
});
|
|
713
|
+
if (toolCalls.length === 0) {
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
startEagerToolExecutions({
|
|
717
|
+
graph,
|
|
718
|
+
metadata,
|
|
719
|
+
agentContext,
|
|
720
|
+
toolCalls,
|
|
721
|
+
skipExisting: true,
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
|
|
82
725
|
export function getChunkContent({
|
|
83
726
|
chunk,
|
|
84
727
|
provider,
|
|
@@ -157,6 +800,7 @@ export class ChatModelStreamHandler implements t.EventHandler {
|
|
|
157
800
|
const agentContext = graph.getAgentContext(metadata);
|
|
158
801
|
|
|
159
802
|
const chunk = data.chunk as Partial<AIMessageChunk>;
|
|
803
|
+
|
|
160
804
|
const content = getChunkContent({
|
|
161
805
|
chunk,
|
|
162
806
|
reasoningKey: agentContext.reasoningKey,
|
|
@@ -172,7 +816,10 @@ export class ChatModelStreamHandler implements t.EventHandler {
|
|
|
172
816
|
return;
|
|
173
817
|
}
|
|
174
818
|
this.handleReasoning(chunk, agentContext);
|
|
819
|
+
const stepKey = graph.getStepKey(metadata);
|
|
175
820
|
let hasToolCalls = false;
|
|
821
|
+
const hasToolCallChunks =
|
|
822
|
+
(chunk.tool_call_chunks && chunk.tool_call_chunks.length > 0) ?? false;
|
|
176
823
|
if (
|
|
177
824
|
chunk.tool_calls &&
|
|
178
825
|
chunk.tool_calls.length > 0 &&
|
|
@@ -186,10 +833,20 @@ export class ChatModelStreamHandler implements t.EventHandler {
|
|
|
186
833
|
) {
|
|
187
834
|
hasToolCalls = true;
|
|
188
835
|
await handleToolCalls(chunk.tool_calls, metadata, graph);
|
|
836
|
+
if (hasFinalToolCallSignal(chunk)) {
|
|
837
|
+
startEagerToolExecutions({
|
|
838
|
+
graph,
|
|
839
|
+
metadata,
|
|
840
|
+
agentContext,
|
|
841
|
+
toolCalls: chunk.tool_calls,
|
|
842
|
+
skipExisting: true,
|
|
843
|
+
});
|
|
844
|
+
if (!hasToolCallChunks) {
|
|
845
|
+
pruneEagerToolCallChunkStates({ graph, stepKey, clearStep: true });
|
|
846
|
+
}
|
|
847
|
+
}
|
|
189
848
|
}
|
|
190
849
|
|
|
191
|
-
const hasToolCallChunks =
|
|
192
|
-
(chunk.tool_call_chunks && chunk.tool_call_chunks.length > 0) ?? false;
|
|
193
850
|
const isEmptyContent =
|
|
194
851
|
typeof content === 'undefined' ||
|
|
195
852
|
!content.length ||
|
|
@@ -202,26 +859,51 @@ export class ChatModelStreamHandler implements t.EventHandler {
|
|
|
202
859
|
(chunk.id ?? '') !== '' &&
|
|
203
860
|
!graph.prelimMessageIdsByStepKey.has(chunk.id ?? '')
|
|
204
861
|
) {
|
|
205
|
-
const stepKey = graph.getStepKey(metadata);
|
|
206
862
|
graph.prelimMessageIdsByStepKey.set(stepKey, chunk.id ?? '');
|
|
207
863
|
} else if (isEmptyChunk) {
|
|
208
864
|
return;
|
|
209
865
|
}
|
|
210
866
|
|
|
211
|
-
const stepKey = graph.getStepKey(metadata);
|
|
212
|
-
|
|
213
867
|
if (
|
|
214
868
|
hasToolCallChunks &&
|
|
215
869
|
chunk.tool_call_chunks &&
|
|
216
870
|
chunk.tool_call_chunks.length &&
|
|
217
871
|
typeof chunk.tool_call_chunks[0]?.index === 'number'
|
|
218
872
|
) {
|
|
873
|
+
const streamedToolCallSeal = getStreamedToolCallSeal(
|
|
874
|
+
chunk.response_metadata as Record<string, unknown> | undefined
|
|
875
|
+
);
|
|
876
|
+
const allowSequentialSeal =
|
|
877
|
+
canPrestartSequentialStreamedToolChunks(agentContext);
|
|
878
|
+
const canStreamEager =
|
|
879
|
+
(allowSequentialSeal || hasExplicitStreamedToolCallSeals(chunk)) &&
|
|
880
|
+
!hasPotentialDirectToolInStreamContext({ graph, agentContext }) &&
|
|
881
|
+
isEagerToolExecutionEnabledForBatch({ graph, metadata, agentContext });
|
|
882
|
+
if (canStreamEager) {
|
|
883
|
+
recordEagerToolCallChunks({
|
|
884
|
+
graph,
|
|
885
|
+
stepKey,
|
|
886
|
+
toolCallChunks: chunk.tool_call_chunks,
|
|
887
|
+
});
|
|
888
|
+
}
|
|
219
889
|
await handleToolCallChunks({
|
|
220
890
|
graph,
|
|
221
891
|
stepKey,
|
|
222
892
|
toolCallChunks: chunk.tool_call_chunks,
|
|
223
893
|
metadata,
|
|
224
894
|
});
|
|
895
|
+
if (canStreamEager) {
|
|
896
|
+
startReadyStreamedEagerToolExecutions({
|
|
897
|
+
graph,
|
|
898
|
+
metadata,
|
|
899
|
+
agentContext,
|
|
900
|
+
stepKey,
|
|
901
|
+
toolCallChunks: chunk.tool_call_chunks,
|
|
902
|
+
seal: streamedToolCallSeal,
|
|
903
|
+
allowSequentialSeal,
|
|
904
|
+
sealAll: hasFinalToolCallSignal(chunk),
|
|
905
|
+
});
|
|
906
|
+
}
|
|
225
907
|
}
|
|
226
908
|
|
|
227
909
|
if (isEmptyContent) {
|
|
@@ -273,25 +955,33 @@ hasToolCallChunks: ${hasToolCallChunks}
|
|
|
273
955
|
return;
|
|
274
956
|
} else if (typeof content === 'string') {
|
|
275
957
|
if (agentContext.currentTokenType === ContentTypes.TEXT) {
|
|
276
|
-
await graph.dispatchMessageDelta(
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
type: ContentTypes.TEXT,
|
|
280
|
-
text: content,
|
|
281
|
-
},
|
|
282
|
-
],
|
|
283
|
-
});
|
|
284
|
-
} else if (agentContext.currentTokenType === 'think_and_text') {
|
|
285
|
-
const { text, thinking } = parseThinkingContent(content);
|
|
286
|
-
if (thinking) {
|
|
287
|
-
await graph.dispatchReasoningDelta(stepId, {
|
|
958
|
+
await graph.dispatchMessageDelta(
|
|
959
|
+
stepId,
|
|
960
|
+
{
|
|
288
961
|
content: [
|
|
289
962
|
{
|
|
290
|
-
type: ContentTypes.
|
|
291
|
-
|
|
963
|
+
type: ContentTypes.TEXT,
|
|
964
|
+
text: content,
|
|
292
965
|
},
|
|
293
966
|
],
|
|
294
|
-
}
|
|
967
|
+
},
|
|
968
|
+
metadata
|
|
969
|
+
);
|
|
970
|
+
} else if (agentContext.currentTokenType === 'think_and_text') {
|
|
971
|
+
const { text, thinking } = parseThinkingContent(content);
|
|
972
|
+
if (thinking) {
|
|
973
|
+
await graph.dispatchReasoningDelta(
|
|
974
|
+
stepId,
|
|
975
|
+
{
|
|
976
|
+
content: [
|
|
977
|
+
{
|
|
978
|
+
type: ContentTypes.THINK,
|
|
979
|
+
think: thinking,
|
|
980
|
+
},
|
|
981
|
+
],
|
|
982
|
+
},
|
|
983
|
+
metadata
|
|
984
|
+
);
|
|
295
985
|
}
|
|
296
986
|
if (text) {
|
|
297
987
|
agentContext.currentTokenType = ContentTypes.TEXT;
|
|
@@ -310,31 +1000,43 @@ hasToolCallChunks: ${hasToolCallChunks}
|
|
|
310
1000
|
);
|
|
311
1001
|
|
|
312
1002
|
const newStepId = graph.getStepIdByKey(newStepKey);
|
|
313
|
-
await graph.dispatchMessageDelta(
|
|
1003
|
+
await graph.dispatchMessageDelta(
|
|
1004
|
+
newStepId,
|
|
1005
|
+
{
|
|
1006
|
+
content: [
|
|
1007
|
+
{
|
|
1008
|
+
type: ContentTypes.TEXT,
|
|
1009
|
+
text: text,
|
|
1010
|
+
},
|
|
1011
|
+
],
|
|
1012
|
+
},
|
|
1013
|
+
metadata
|
|
1014
|
+
);
|
|
1015
|
+
}
|
|
1016
|
+
} else {
|
|
1017
|
+
await graph.dispatchReasoningDelta(
|
|
1018
|
+
stepId,
|
|
1019
|
+
{
|
|
314
1020
|
content: [
|
|
315
1021
|
{
|
|
316
|
-
type: ContentTypes.
|
|
317
|
-
|
|
1022
|
+
type: ContentTypes.THINK,
|
|
1023
|
+
think: content,
|
|
318
1024
|
},
|
|
319
1025
|
],
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
await graph.dispatchReasoningDelta(stepId, {
|
|
324
|
-
content: [
|
|
325
|
-
{
|
|
326
|
-
type: ContentTypes.THINK,
|
|
327
|
-
think: content,
|
|
328
|
-
},
|
|
329
|
-
],
|
|
330
|
-
});
|
|
1026
|
+
},
|
|
1027
|
+
metadata
|
|
1028
|
+
);
|
|
331
1029
|
}
|
|
332
1030
|
} else if (
|
|
333
1031
|
content.every((c) => c.type?.startsWith(ContentTypes.TEXT) ?? false)
|
|
334
1032
|
) {
|
|
335
|
-
await graph.dispatchMessageDelta(
|
|
336
|
-
|
|
337
|
-
|
|
1033
|
+
await graph.dispatchMessageDelta(
|
|
1034
|
+
stepId,
|
|
1035
|
+
{
|
|
1036
|
+
content,
|
|
1037
|
+
},
|
|
1038
|
+
metadata
|
|
1039
|
+
);
|
|
338
1040
|
} else if (
|
|
339
1041
|
content.every(
|
|
340
1042
|
(c) =>
|
|
@@ -344,16 +1046,21 @@ hasToolCallChunks: ${hasToolCallChunks}
|
|
|
344
1046
|
c.type === 'redacted_thinking'
|
|
345
1047
|
)
|
|
346
1048
|
) {
|
|
347
|
-
await graph.dispatchReasoningDelta(
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
1049
|
+
await graph.dispatchReasoningDelta(
|
|
1050
|
+
stepId,
|
|
1051
|
+
{
|
|
1052
|
+
content: content.map((c) => ({
|
|
1053
|
+
type: ContentTypes.THINK,
|
|
1054
|
+
think:
|
|
1055
|
+
(c as t.ThinkingContentText).thinking ??
|
|
1056
|
+
(c as Partial<t.GoogleReasoningContentText>).reasoning ??
|
|
1057
|
+
(c as Partial<t.BedrockReasoningContentText>).reasoningText
|
|
1058
|
+
?.text ??
|
|
1059
|
+
'',
|
|
1060
|
+
})),
|
|
1061
|
+
},
|
|
1062
|
+
metadata
|
|
1063
|
+
);
|
|
357
1064
|
}
|
|
358
1065
|
}
|
|
359
1066
|
handleReasoning(
|