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