@librechat/agents 3.0.0-rc1 → 3.0.0-rc10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/common/enum.cjs +1 -0
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +0 -1
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +229 -44
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +33 -0
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/run.cjs +28 -15
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/stream.cjs +1 -1
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/esm/common/enum.mjs +1 -0
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +0 -1
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +230 -45
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +33 -0
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/run.mjs +28 -15
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/stream.mjs +1 -1
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/types/common/enum.d.ts +2 -1
- package/dist/types/graphs/MultiAgentGraph.d.ts +12 -2
- package/dist/types/llm/openai/index.d.ts +10 -0
- package/dist/types/run.d.ts +1 -1
- package/dist/types/types/graph.d.ts +38 -4
- package/dist/types/types/llm.d.ts +1 -0
- package/dist/types/types/run.d.ts +5 -1
- package/package.json +10 -2
- package/src/common/enum.ts +1 -0
- package/src/graphs/Graph.ts +0 -1
- package/src/graphs/MultiAgentGraph.ts +267 -50
- package/src/llm/openai/index.ts +41 -0
- package/src/run.ts +38 -27
- package/src/scripts/multi-agent-chain.ts +278 -0
- package/src/scripts/multi-agent-document-review-chain.ts +197 -0
- package/src/scripts/multi-agent-hybrid-flow.ts +310 -0
- package/src/scripts/multi-agent-parallel.ts +27 -23
- package/src/scripts/multi-agent-supervisor.ts +362 -0
- package/src/scripts/test-custom-prompt-key.ts +145 -0
- package/src/scripts/test-handoff-input.ts +170 -0
- package/src/scripts/test-multi-agent-list-handoff.ts +261 -0
- package/src/scripts/test-tools-before-handoff.ts +233 -0
- package/src/stream.ts +4 -1
- package/src/types/graph.ts +51 -5
- package/src/types/llm.ts +1 -0
- package/src/types/run.ts +6 -1
- package/dist/types/scripts/abort.d.ts +0 -1
- package/dist/types/scripts/ant_web_search.d.ts +0 -1
- package/dist/types/scripts/args.d.ts +0 -7
- package/dist/types/scripts/caching.d.ts +0 -1
- package/dist/types/scripts/cli.d.ts +0 -1
- package/dist/types/scripts/cli2.d.ts +0 -1
- package/dist/types/scripts/cli3.d.ts +0 -1
- package/dist/types/scripts/cli4.d.ts +0 -1
- package/dist/types/scripts/cli5.d.ts +0 -1
- package/dist/types/scripts/code_exec.d.ts +0 -1
- package/dist/types/scripts/code_exec_files.d.ts +0 -1
- package/dist/types/scripts/code_exec_simple.d.ts +0 -1
- package/dist/types/scripts/content.d.ts +0 -1
- package/dist/types/scripts/empty_input.d.ts +0 -1
- package/dist/types/scripts/handoff-test.d.ts +0 -1
- package/dist/types/scripts/image.d.ts +0 -1
- package/dist/types/scripts/memory.d.ts +0 -1
- package/dist/types/scripts/multi-agent-conditional.d.ts +0 -1
- package/dist/types/scripts/multi-agent-parallel.d.ts +0 -1
- package/dist/types/scripts/multi-agent-sequence.d.ts +0 -1
- package/dist/types/scripts/multi-agent-test.d.ts +0 -1
- package/dist/types/scripts/search.d.ts +0 -1
- package/dist/types/scripts/simple.d.ts +0 -1
- package/dist/types/scripts/stream.d.ts +0 -1
- package/dist/types/scripts/thinking.d.ts +0 -1
- package/dist/types/scripts/tools.d.ts +0 -1
- package/dist/types/specs/spec.utils.d.ts +0 -1
- package/src/scripts/multi-agent-example-output.md +0 -110
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { tool } from '@langchain/core/tools';
|
|
3
|
-
import {
|
|
3
|
+
import { PromptTemplate } from '@langchain/core/prompts';
|
|
4
|
+
import { ToolMessage, getBufferString, HumanMessage } from '@langchain/core/messages';
|
|
4
5
|
import { getCurrentTaskInput, Command, Annotation, messagesStateReducer, StateGraph, END, START } from '@langchain/langgraph';
|
|
5
6
|
import { StandardGraph } from './Graph.mjs';
|
|
7
|
+
import { Constants } from '../common/enum.mjs';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* MultiAgentGraph extends StandardGraph to support dynamic multi-agent workflows
|
|
9
|
-
* with handoffs, fan-in/fan-out, and other composable patterns
|
|
11
|
+
* with handoffs, fan-in/fan-out, and other composable patterns.
|
|
12
|
+
*
|
|
13
|
+
* Key behavior:
|
|
14
|
+
* - Agents with ONLY handoff edges: Can dynamically route to any handoff destination
|
|
15
|
+
* - Agents with ONLY direct edges: Always follow their direct edges
|
|
16
|
+
* - Agents with BOTH: Use Command for exclusive routing (handoff OR direct, not both)
|
|
17
|
+
* - If handoff occurs: Only the handoff destination executes
|
|
18
|
+
* - If no handoff: Direct edges execute (potentially in parallel)
|
|
19
|
+
*
|
|
20
|
+
* This enables the common pattern where an agent either delegates (handoff)
|
|
21
|
+
* OR continues its workflow (direct edges), but not both simultaneously.
|
|
10
22
|
*/
|
|
11
23
|
class MultiAgentGraph extends StandardGraph {
|
|
12
24
|
edges;
|
|
@@ -100,13 +112,6 @@ class MultiAgentGraph extends StandardGraph {
|
|
|
100
112
|
agentContext.tools = [];
|
|
101
113
|
}
|
|
102
114
|
agentContext.tools.push(...handoffTools);
|
|
103
|
-
// Update tool map
|
|
104
|
-
for (const tool of handoffTools) {
|
|
105
|
-
if (!agentContext.toolMap) {
|
|
106
|
-
agentContext.toolMap = new Map();
|
|
107
|
-
}
|
|
108
|
-
agentContext.toolMap.set(tool.name, tool);
|
|
109
|
-
}
|
|
110
115
|
}
|
|
111
116
|
}
|
|
112
117
|
/**
|
|
@@ -115,19 +120,23 @@ class MultiAgentGraph extends StandardGraph {
|
|
|
115
120
|
createHandoffToolsForEdge(edge) {
|
|
116
121
|
const tools = [];
|
|
117
122
|
const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];
|
|
118
|
-
|
|
123
|
+
/** If there's a condition, create a single conditional handoff tool */
|
|
119
124
|
if (edge.condition != null) {
|
|
120
125
|
const toolName = 'conditional_transfer';
|
|
121
126
|
const toolDescription = edge.description ?? 'Conditionally transfer control based on state';
|
|
122
|
-
|
|
127
|
+
/** Check if we have a prompt for handoff input */
|
|
128
|
+
const hasHandoffInput = edge.prompt != null && typeof edge.prompt === 'string';
|
|
129
|
+
const handoffInputDescription = hasHandoffInput ? edge.prompt : undefined;
|
|
130
|
+
const promptKey = edge.promptKey ?? 'instructions';
|
|
131
|
+
tools.push(tool(async (input, config) => {
|
|
123
132
|
const state = getCurrentTaskInput();
|
|
124
133
|
const toolCallId = config?.toolCall?.id ??
|
|
125
134
|
'unknown';
|
|
126
|
-
|
|
135
|
+
/** Evaluated condition */
|
|
127
136
|
const result = edge.condition(state);
|
|
128
137
|
let destination;
|
|
129
138
|
if (typeof result === 'boolean') {
|
|
130
|
-
|
|
139
|
+
/** If true, use first destination; if false, don't transfer */
|
|
131
140
|
if (!result)
|
|
132
141
|
return null;
|
|
133
142
|
destination = destinations[0];
|
|
@@ -136,11 +145,17 @@ class MultiAgentGraph extends StandardGraph {
|
|
|
136
145
|
destination = result;
|
|
137
146
|
}
|
|
138
147
|
else {
|
|
139
|
-
|
|
148
|
+
/** Array of destinations - for now, use the first */
|
|
140
149
|
destination = Array.isArray(result) ? result[0] : destinations[0];
|
|
141
150
|
}
|
|
151
|
+
let content = `Conditionally transferred to ${destination}`;
|
|
152
|
+
if (hasHandoffInput &&
|
|
153
|
+
promptKey in input &&
|
|
154
|
+
input[promptKey] != null) {
|
|
155
|
+
content += `\n\n${promptKey.charAt(0).toUpperCase() + promptKey.slice(1)}: ${input[promptKey]}`;
|
|
156
|
+
}
|
|
142
157
|
const toolMessage = new ToolMessage({
|
|
143
|
-
content
|
|
158
|
+
content,
|
|
144
159
|
name: toolName,
|
|
145
160
|
tool_call_id: toolCallId,
|
|
146
161
|
});
|
|
@@ -151,20 +166,39 @@ class MultiAgentGraph extends StandardGraph {
|
|
|
151
166
|
});
|
|
152
167
|
}, {
|
|
153
168
|
name: toolName,
|
|
154
|
-
schema:
|
|
169
|
+
schema: hasHandoffInput
|
|
170
|
+
? z.object({
|
|
171
|
+
[promptKey]: z
|
|
172
|
+
.string()
|
|
173
|
+
.optional()
|
|
174
|
+
.describe(handoffInputDescription),
|
|
175
|
+
})
|
|
176
|
+
: z.object({}),
|
|
155
177
|
description: toolDescription,
|
|
156
178
|
}));
|
|
157
179
|
}
|
|
158
180
|
else {
|
|
159
|
-
|
|
181
|
+
/** Create individual tools for each destination */
|
|
160
182
|
for (const destination of destinations) {
|
|
161
|
-
const toolName =
|
|
183
|
+
const toolName = `${Constants.LC_TRANSFER_TO_}${destination}`;
|
|
162
184
|
const toolDescription = edge.description ?? `Transfer control to agent '${destination}'`;
|
|
163
|
-
|
|
185
|
+
/** Check if we have a prompt for handoff input */
|
|
186
|
+
const hasHandoffInput = edge.prompt != null && typeof edge.prompt === 'string';
|
|
187
|
+
const handoffInputDescription = hasHandoffInput
|
|
188
|
+
? edge.prompt
|
|
189
|
+
: undefined;
|
|
190
|
+
const promptKey = edge.promptKey ?? 'instructions';
|
|
191
|
+
tools.push(tool(async (input, config) => {
|
|
164
192
|
const toolCallId = config?.toolCall?.id ??
|
|
165
193
|
'unknown';
|
|
194
|
+
let content = `Successfully transferred to ${destination}`;
|
|
195
|
+
if (hasHandoffInput &&
|
|
196
|
+
promptKey in input &&
|
|
197
|
+
input[promptKey] != null) {
|
|
198
|
+
content += `\n\n${promptKey.charAt(0).toUpperCase() + promptKey.slice(1)}: ${input[promptKey]}`;
|
|
199
|
+
}
|
|
166
200
|
const toolMessage = new ToolMessage({
|
|
167
|
-
content
|
|
201
|
+
content,
|
|
168
202
|
name: toolName,
|
|
169
203
|
tool_call_id: toolCallId,
|
|
170
204
|
});
|
|
@@ -176,7 +210,14 @@ class MultiAgentGraph extends StandardGraph {
|
|
|
176
210
|
});
|
|
177
211
|
}, {
|
|
178
212
|
name: toolName,
|
|
179
|
-
schema:
|
|
213
|
+
schema: hasHandoffInput
|
|
214
|
+
? z.object({
|
|
215
|
+
[promptKey]: z
|
|
216
|
+
.string()
|
|
217
|
+
.optional()
|
|
218
|
+
.describe(handoffInputDescription),
|
|
219
|
+
})
|
|
220
|
+
: z.object({}),
|
|
180
221
|
description: toolDescription,
|
|
181
222
|
}));
|
|
182
223
|
}
|
|
@@ -187,7 +228,7 @@ class MultiAgentGraph extends StandardGraph {
|
|
|
187
228
|
* Create a complete agent subgraph (similar to createReactAgent)
|
|
188
229
|
*/
|
|
189
230
|
createAgentSubgraph(agentId) {
|
|
190
|
-
|
|
231
|
+
/** This is essentially the same as `createAgentNode` from `StandardGraph` */
|
|
191
232
|
return this.createAgentNode(agentId);
|
|
192
233
|
}
|
|
193
234
|
/**
|
|
@@ -206,6 +247,12 @@ class MultiAgentGraph extends StandardGraph {
|
|
|
206
247
|
},
|
|
207
248
|
default: () => [],
|
|
208
249
|
}),
|
|
250
|
+
/** Channel for passing filtered messages to agents when excludeResults is true */
|
|
251
|
+
agentMessages: Annotation({
|
|
252
|
+
/** Replaces state entirely */
|
|
253
|
+
reducer: (a, b) => b,
|
|
254
|
+
default: () => [],
|
|
255
|
+
}),
|
|
209
256
|
});
|
|
210
257
|
const builder = new StateGraph(StateAnnotation);
|
|
211
258
|
// Add all agents as complete subgraphs
|
|
@@ -229,17 +276,105 @@ class MultiAgentGraph extends StandardGraph {
|
|
|
229
276
|
dests.forEach((dest) => directDestinations.add(dest));
|
|
230
277
|
}
|
|
231
278
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const
|
|
279
|
+
/** Check if this agent has BOTH handoff and direct edges */
|
|
280
|
+
const hasHandoffEdges = handoffDestinations.size > 0;
|
|
281
|
+
const hasDirectEdges = directDestinations.size > 0;
|
|
282
|
+
const needsCommandRouting = hasHandoffEdges && hasDirectEdges;
|
|
283
|
+
/** Collect all possible destinations for this agent */
|
|
284
|
+
const allDestinations = new Set([
|
|
285
|
+
...handoffDestinations,
|
|
286
|
+
...directDestinations,
|
|
287
|
+
]);
|
|
235
288
|
if (handoffDestinations.size > 0 || directDestinations.size === 0) {
|
|
236
|
-
|
|
289
|
+
allDestinations.add(END);
|
|
237
290
|
}
|
|
238
|
-
|
|
291
|
+
/** Agent subgraph (includes agent + tools) */
|
|
239
292
|
const agentSubgraph = this.createAgentSubgraph(agentId);
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
293
|
+
/** Wrapper function that handles agentMessages channel and conditional routing */
|
|
294
|
+
const agentWrapper = async (state) => {
|
|
295
|
+
let result;
|
|
296
|
+
if (state.agentMessages != null && state.agentMessages.length > 0) {
|
|
297
|
+
/**
|
|
298
|
+
* When using agentMessages (excludeResults=true), we need to update
|
|
299
|
+
* the token map to account for the new prompt message
|
|
300
|
+
*/
|
|
301
|
+
const agentContext = this.agentContexts.get(agentId);
|
|
302
|
+
if (agentContext && agentContext.tokenCounter) {
|
|
303
|
+
// The agentMessages contains:
|
|
304
|
+
// 1. Filtered messages (0 to startIndex) - already have token counts
|
|
305
|
+
// 2. New prompt message - needs token counting
|
|
306
|
+
const freshTokenMap = {};
|
|
307
|
+
// Copy existing token counts for filtered messages (0 to startIndex)
|
|
308
|
+
for (let i = 0; i < this.startIndex; i++) {
|
|
309
|
+
const tokenCount = agentContext.indexTokenCountMap[i];
|
|
310
|
+
if (tokenCount !== undefined) {
|
|
311
|
+
freshTokenMap[i] = tokenCount;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
// Calculate tokens only for the new prompt message (last message)
|
|
315
|
+
const promptMessageIndex = state.agentMessages.length - 1;
|
|
316
|
+
if (promptMessageIndex >= this.startIndex) {
|
|
317
|
+
const promptMessage = state.agentMessages[promptMessageIndex];
|
|
318
|
+
freshTokenMap[promptMessageIndex] =
|
|
319
|
+
agentContext.tokenCounter(promptMessage);
|
|
320
|
+
}
|
|
321
|
+
// Update the agent's token map with instructions added
|
|
322
|
+
agentContext.updateTokenMapWithInstructions(freshTokenMap);
|
|
323
|
+
}
|
|
324
|
+
/** Temporary state with messages replaced by `agentMessages` */
|
|
325
|
+
const transformedState = {
|
|
326
|
+
...state,
|
|
327
|
+
messages: state.agentMessages,
|
|
328
|
+
};
|
|
329
|
+
result = await agentSubgraph.invoke(transformedState);
|
|
330
|
+
result = {
|
|
331
|
+
...result,
|
|
332
|
+
/** Clear agentMessages for next agent */
|
|
333
|
+
agentMessages: [],
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
result = await agentSubgraph.invoke(state);
|
|
338
|
+
}
|
|
339
|
+
/** If agent has both handoff and direct edges, use Command for exclusive routing */
|
|
340
|
+
if (needsCommandRouting) {
|
|
341
|
+
/** Check if a handoff occurred */
|
|
342
|
+
const lastMessage = result.messages[result.messages.length - 1];
|
|
343
|
+
if (lastMessage != null &&
|
|
344
|
+
lastMessage.getType() === 'tool' &&
|
|
345
|
+
typeof lastMessage.name === 'string' &&
|
|
346
|
+
lastMessage.name.startsWith(Constants.LC_TRANSFER_TO_)) {
|
|
347
|
+
/** Handoff occurred - extract destination and navigate there exclusively */
|
|
348
|
+
const handoffDest = lastMessage.name.replace(Constants.LC_TRANSFER_TO_, '');
|
|
349
|
+
return new Command({
|
|
350
|
+
update: result,
|
|
351
|
+
goto: handoffDest,
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
/** No handoff - proceed with direct edges */
|
|
356
|
+
const directDests = Array.from(directDestinations);
|
|
357
|
+
if (directDests.length === 1) {
|
|
358
|
+
return new Command({
|
|
359
|
+
update: result,
|
|
360
|
+
goto: directDests[0],
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
else if (directDests.length > 1) {
|
|
364
|
+
/** Multiple direct destinations - they'll run in parallel */
|
|
365
|
+
return new Command({
|
|
366
|
+
update: result,
|
|
367
|
+
goto: directDests,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
/** No special routing needed - return state normally */
|
|
373
|
+
return result;
|
|
374
|
+
};
|
|
375
|
+
/** Wrapped agent as a node with its possible destinations */
|
|
376
|
+
builder.addNode(agentId, agentWrapper, {
|
|
377
|
+
ends: Array.from(allDestinations),
|
|
243
378
|
});
|
|
244
379
|
}
|
|
245
380
|
// Add starting edges for all starting nodes
|
|
@@ -248,7 +383,8 @@ class MultiAgentGraph extends StandardGraph {
|
|
|
248
383
|
/** @ts-ignore */
|
|
249
384
|
builder.addEdge(START, startNode);
|
|
250
385
|
}
|
|
251
|
-
/**
|
|
386
|
+
/**
|
|
387
|
+
* Add direct edges for automatic transitions
|
|
252
388
|
* Group edges by destination to handle fan-in scenarios
|
|
253
389
|
*/
|
|
254
390
|
const edgesByDestination = new Map();
|
|
@@ -263,30 +399,66 @@ class MultiAgentGraph extends StandardGraph {
|
|
|
263
399
|
}
|
|
264
400
|
for (const [destination, edges] of edgesByDestination) {
|
|
265
401
|
/** Checks if this is a fan-in scenario with prompt instructions */
|
|
266
|
-
const edgesWithPrompt = edges.filter((edge) => edge.
|
|
402
|
+
const edgesWithPrompt = edges.filter((edge) => edge.prompt != null && edge.prompt !== '');
|
|
267
403
|
if (edgesWithPrompt.length > 0) {
|
|
268
|
-
|
|
404
|
+
/**
|
|
405
|
+
* Single wrapper node for destination (Fan-in with prompt)
|
|
406
|
+
*/
|
|
269
407
|
const wrapperNodeId = `fan_in_${destination}_prompt`;
|
|
270
|
-
|
|
271
|
-
|
|
408
|
+
/**
|
|
409
|
+
* First edge's `prompt`
|
|
410
|
+
* (they should all be the same for fan-in)
|
|
411
|
+
*/
|
|
412
|
+
const prompt = edgesWithPrompt[0].prompt;
|
|
413
|
+
/**
|
|
414
|
+
* First edge's `excludeResults` flag
|
|
415
|
+
* (they should all be the same for fan-in)
|
|
416
|
+
*/
|
|
417
|
+
const excludeResults = edgesWithPrompt[0].excludeResults;
|
|
272
418
|
builder.addNode(wrapperNodeId, async (state) => {
|
|
273
419
|
let promptText;
|
|
274
|
-
|
|
275
|
-
|
|
420
|
+
let effectiveExcludeResults = excludeResults;
|
|
421
|
+
if (typeof prompt === 'function') {
|
|
422
|
+
promptText = await prompt(state.messages, this.startIndex);
|
|
276
423
|
}
|
|
277
|
-
else {
|
|
278
|
-
|
|
424
|
+
else if (prompt != null) {
|
|
425
|
+
if (prompt.includes('{results}')) {
|
|
426
|
+
const resultsMessages = state.messages.slice(this.startIndex);
|
|
427
|
+
const resultsString = getBufferString(resultsMessages);
|
|
428
|
+
const promptTemplate = PromptTemplate.fromTemplate(prompt);
|
|
429
|
+
const result = await promptTemplate.invoke({
|
|
430
|
+
results: resultsString,
|
|
431
|
+
});
|
|
432
|
+
promptText = result.value;
|
|
433
|
+
effectiveExcludeResults =
|
|
434
|
+
excludeResults !== false && promptText !== '';
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
promptText = prompt;
|
|
438
|
+
}
|
|
279
439
|
}
|
|
280
440
|
if (promptText != null && promptText !== '') {
|
|
281
|
-
|
|
441
|
+
if (effectiveExcludeResults == null ||
|
|
442
|
+
effectiveExcludeResults === false) {
|
|
443
|
+
return {
|
|
444
|
+
messages: [new HumanMessage(promptText)],
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
/** When `excludeResults` is true, use agentMessages channel
|
|
448
|
+
* to pass filtered messages + prompt to the destination agent
|
|
449
|
+
*/
|
|
450
|
+
const filteredMessages = state.messages.slice(0, this.startIndex);
|
|
282
451
|
return {
|
|
283
|
-
messages: [
|
|
452
|
+
messages: [new HumanMessage(promptText)],
|
|
453
|
+
agentMessages: messagesStateReducer(filteredMessages, [
|
|
454
|
+
new HumanMessage(promptText),
|
|
455
|
+
]),
|
|
284
456
|
};
|
|
285
457
|
}
|
|
286
|
-
|
|
458
|
+
/** No prompt needed, return empty update */
|
|
287
459
|
return {};
|
|
288
460
|
});
|
|
289
|
-
|
|
461
|
+
/** Add edges from all sources to the wrapper, then wrapper to destination */
|
|
290
462
|
for (const edge of edges) {
|
|
291
463
|
const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
|
|
292
464
|
for (const source of sources) {
|
|
@@ -295,16 +467,29 @@ class MultiAgentGraph extends StandardGraph {
|
|
|
295
467
|
builder.addEdge(source, wrapperNodeId);
|
|
296
468
|
}
|
|
297
469
|
}
|
|
298
|
-
|
|
470
|
+
/** Single edge from wrapper to destination */
|
|
299
471
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
300
472
|
/** @ts-ignore */
|
|
301
473
|
builder.addEdge(wrapperNodeId, destination);
|
|
302
474
|
}
|
|
303
475
|
else {
|
|
304
|
-
|
|
476
|
+
/** No prompt instructions, add direct edges (skip if source uses Command routing) */
|
|
305
477
|
for (const edge of edges) {
|
|
306
478
|
const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
|
|
307
479
|
for (const source of sources) {
|
|
480
|
+
/** Check if this source node has both handoff and direct edges */
|
|
481
|
+
const sourceHandoffEdges = this.handoffEdges.filter((e) => {
|
|
482
|
+
const eSources = Array.isArray(e.from) ? e.from : [e.from];
|
|
483
|
+
return eSources.includes(source);
|
|
484
|
+
});
|
|
485
|
+
const sourceDirectEdges = this.directEdges.filter((e) => {
|
|
486
|
+
const eSources = Array.isArray(e.from) ? e.from : [e.from];
|
|
487
|
+
return eSources.includes(source);
|
|
488
|
+
});
|
|
489
|
+
/** Skip adding edge if source uses Command routing (has both types) */
|
|
490
|
+
if (sourceHandoffEdges.length > 0 && sourceDirectEdges.length > 0) {
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
308
493
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
309
494
|
/** @ts-ignore */
|
|
310
495
|
builder.addEdge(source, destination);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultiAgentGraph.mjs","sources":["../../../src/graphs/MultiAgentGraph.ts"],"sourcesContent":["import { z } from 'zod';\nimport { tool } from '@langchain/core/tools';\nimport { ToolMessage, HumanMessage } from '@langchain/core/messages';\nimport {\n END,\n START,\n Command,\n StateGraph,\n Annotation,\n getCurrentTaskInput,\n messagesStateReducer,\n} from '@langchain/langgraph';\nimport type { ToolRunnableConfig } from '@langchain/core/tools';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type * as t from '@/types';\nimport { StandardGraph } from './Graph';\n\n/**\n * MultiAgentGraph extends StandardGraph to support dynamic multi-agent workflows\n * with handoffs, fan-in/fan-out, and other composable patterns\n */\nexport class MultiAgentGraph extends StandardGraph {\n private edges: t.GraphEdge[];\n private startingNodes: Set<string> = new Set();\n private directEdges: t.GraphEdge[] = [];\n private handoffEdges: t.GraphEdge[] = [];\n\n constructor(input: t.MultiAgentGraphInput) {\n super(input);\n this.edges = input.edges;\n this.categorizeEdges();\n this.analyzeGraph();\n this.createHandoffTools();\n }\n\n /**\n * Categorize edges into handoff and direct types\n */\n private categorizeEdges(): void {\n for (const edge of this.edges) {\n // Default behavior: edges with conditions or explicit 'handoff' type are handoff edges\n // Edges with explicit 'direct' type or multi-destination without conditions are direct edges\n if (edge.edgeType === 'direct') {\n this.directEdges.push(edge);\n } else if (edge.edgeType === 'handoff' || edge.condition != null) {\n this.handoffEdges.push(edge);\n } else {\n // Default: single-to-single edges are handoff, single-to-multiple are direct\n const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];\n const sources = Array.isArray(edge.from) ? edge.from : [edge.from];\n\n if (sources.length === 1 && destinations.length > 1) {\n // Fan-out pattern defaults to direct\n this.directEdges.push(edge);\n } else {\n // Everything else defaults to handoff\n this.handoffEdges.push(edge);\n }\n }\n }\n }\n\n /**\n * Analyze graph structure to determine starting nodes and connections\n */\n private analyzeGraph(): void {\n const hasIncomingEdge = new Set<string>();\n\n // Track all nodes that have incoming edges\n for (const edge of this.edges) {\n const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];\n destinations.forEach((dest) => hasIncomingEdge.add(dest));\n }\n\n // Starting nodes are those without incoming edges\n for (const agentId of this.agentContexts.keys()) {\n if (!hasIncomingEdge.has(agentId)) {\n this.startingNodes.add(agentId);\n }\n }\n\n // If no starting nodes found, use the first agent\n if (this.startingNodes.size === 0 && this.agentContexts.size > 0) {\n this.startingNodes.add(this.agentContexts.keys().next().value!);\n }\n }\n\n /**\n * Create handoff tools for agents based on handoff edges only\n */\n private createHandoffTools(): void {\n // Group handoff edges by source agent(s)\n const handoffsByAgent = new Map<string, t.GraphEdge[]>();\n\n // Only process handoff edges for tool creation\n for (const edge of this.handoffEdges) {\n const sources = Array.isArray(edge.from) ? edge.from : [edge.from];\n sources.forEach((source) => {\n if (!handoffsByAgent.has(source)) {\n handoffsByAgent.set(source, []);\n }\n handoffsByAgent.get(source)!.push(edge);\n });\n }\n\n // Create handoff tools for each agent\n for (const [agentId, edges] of handoffsByAgent) {\n const agentContext = this.agentContexts.get(agentId);\n if (!agentContext) continue;\n\n // Create handoff tools for this agent's outgoing edges\n const handoffTools: t.GenericTool[] = [];\n for (const edge of edges) {\n handoffTools.push(...this.createHandoffToolsForEdge(edge));\n }\n\n // Add handoff tools to the agent's existing tools\n if (!agentContext.tools) {\n agentContext.tools = [];\n }\n agentContext.tools.push(...handoffTools);\n\n // Update tool map\n for (const tool of handoffTools) {\n if (!agentContext.toolMap) {\n agentContext.toolMap = new Map();\n }\n agentContext.toolMap.set(tool.name, tool);\n }\n }\n }\n\n /**\n * Create handoff tools for an edge (handles multiple destinations)\n */\n private createHandoffToolsForEdge(edge: t.GraphEdge): t.GenericTool[] {\n const tools: t.GenericTool[] = [];\n const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];\n\n // If there's a condition, create a single conditional handoff tool\n if (edge.condition != null) {\n const toolName = 'conditional_transfer';\n const toolDescription =\n edge.description ?? 'Conditionally transfer control based on state';\n\n tools.push(\n tool(\n async (_, config) => {\n const state = getCurrentTaskInput() as t.BaseGraphState;\n const toolCallId =\n (config as ToolRunnableConfig | undefined)?.toolCall?.id ??\n 'unknown';\n\n // Evaluate condition\n const result = edge.condition!(state);\n let destination: string;\n\n if (typeof result === 'boolean') {\n // If true, use first destination; if false, don't transfer\n if (!result) return null;\n destination = destinations[0];\n } else if (typeof result === 'string') {\n destination = result;\n } else {\n // Array of destinations - for now, use the first\n destination = Array.isArray(result) ? result[0] : destinations[0];\n }\n\n const toolMessage = new ToolMessage({\n content: `Conditionally transferred to ${destination}`,\n name: toolName,\n tool_call_id: toolCallId,\n });\n\n return new Command({\n goto: destination,\n update: { messages: state.messages.concat(toolMessage) },\n graph: Command.PARENT,\n });\n },\n {\n name: toolName,\n schema: z.object({}),\n description: toolDescription,\n }\n )\n );\n } else {\n // Create individual tools for each destination\n for (const destination of destinations) {\n const toolName = `transfer_to_${destination}`;\n const toolDescription =\n edge.description ?? `Transfer control to agent '${destination}'`;\n\n tools.push(\n tool(\n async (_, config) => {\n const toolCallId =\n (config as ToolRunnableConfig | undefined)?.toolCall?.id ??\n 'unknown';\n const toolMessage = new ToolMessage({\n content: `Successfully transferred to ${destination}`,\n name: toolName,\n tool_call_id: toolCallId,\n });\n\n const state = getCurrentTaskInput() as t.BaseGraphState;\n\n return new Command({\n goto: destination,\n update: { messages: state.messages.concat(toolMessage) },\n graph: Command.PARENT,\n });\n },\n {\n name: toolName,\n schema: z.object({}),\n description: toolDescription,\n }\n )\n );\n }\n }\n\n return tools;\n }\n\n /**\n * Create a complete agent subgraph (similar to createReactAgent)\n */\n private createAgentSubgraph(agentId: string): t.CompiledAgentWorfklow {\n // This is essentially the same as createAgentNode from StandardGraph\n return this.createAgentNode(agentId);\n }\n\n /**\n * Create the multi-agent workflow with dynamic handoffs\n */\n override createWorkflow(): t.CompiledStateWorkflow {\n const StateAnnotation = Annotation.Root({\n messages: Annotation<BaseMessage[]>({\n reducer: (a, b) => {\n if (!a.length) {\n this.startIndex = a.length + b.length;\n }\n const result = messagesStateReducer(a, b);\n this.messages = result;\n return result;\n },\n default: () => [],\n }),\n });\n\n const builder = new StateGraph(StateAnnotation);\n\n // Add all agents as complete subgraphs\n for (const [agentId] of this.agentContexts) {\n // Get all possible destinations for this agent\n const handoffDestinations = new Set<string>();\n const directDestinations = new Set<string>();\n\n // Check handoff edges for destinations\n for (const edge of this.handoffEdges) {\n const sources = Array.isArray(edge.from) ? edge.from : [edge.from];\n if (sources.includes(agentId) === true) {\n const dests = Array.isArray(edge.to) ? edge.to : [edge.to];\n dests.forEach((dest) => handoffDestinations.add(dest));\n }\n }\n\n // Check direct edges for destinations\n for (const edge of this.directEdges) {\n const sources = Array.isArray(edge.from) ? edge.from : [edge.from];\n if (sources.includes(agentId) === true) {\n const dests = Array.isArray(edge.to) ? edge.to : [edge.to];\n dests.forEach((dest) => directDestinations.add(dest));\n }\n }\n\n // If agent has handoff destinations, add END to possible ends\n // If agent only has direct destinations, it naturally ends without explicit END\n const destinations = new Set([...handoffDestinations]);\n if (handoffDestinations.size > 0 || directDestinations.size === 0) {\n destinations.add(END);\n }\n\n // Create the agent subgraph (includes agent + tools)\n const agentSubgraph = this.createAgentSubgraph(agentId);\n\n // Add the agent as a node with its possible destinations\n builder.addNode(agentId, agentSubgraph, {\n ends: Array.from(destinations),\n });\n }\n\n // Add starting edges for all starting nodes\n for (const startNode of this.startingNodes) {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n /** @ts-ignore */\n builder.addEdge(START, startNode);\n }\n\n /** Add direct edges for automatic transitions\n * Group edges by destination to handle fan-in scenarios\n */\n const edgesByDestination = new Map<string, t.GraphEdge[]>();\n\n for (const edge of this.directEdges) {\n const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];\n for (const destination of destinations) {\n if (!edgesByDestination.has(destination)) {\n edgesByDestination.set(destination, []);\n }\n edgesByDestination.get(destination)!.push(edge);\n }\n }\n\n for (const [destination, edges] of edgesByDestination) {\n /** Checks if this is a fan-in scenario with prompt instructions */\n const edgesWithPrompt = edges.filter(\n (edge) =>\n edge.promptInstructions != null && edge.promptInstructions !== ''\n );\n\n if (edgesWithPrompt.length > 0) {\n // Fan-in with prompt: create a single wrapper node for this destination\n const wrapperNodeId = `fan_in_${destination}_prompt`;\n\n // Use the first edge's prompt instructions (they should all be the same for fan-in)\n const promptInstructions = edgesWithPrompt[0].promptInstructions;\n\n builder.addNode(wrapperNodeId, async (state: t.BaseGraphState) => {\n let promptText: string | undefined;\n\n if (typeof promptInstructions === 'function') {\n promptText = promptInstructions(state.messages);\n } else {\n promptText = promptInstructions;\n }\n\n if (promptText != null && promptText !== '') {\n // Return state with the prompt message added\n return {\n messages: [...state.messages, new HumanMessage(promptText)],\n };\n }\n\n // No prompt needed, return empty update\n return {};\n });\n\n // Add edges from all sources to the wrapper, then wrapper to destination\n for (const edge of edges) {\n const sources = Array.isArray(edge.from) ? edge.from : [edge.from];\n for (const source of sources) {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n /** @ts-ignore */\n builder.addEdge(source, wrapperNodeId);\n }\n }\n\n // Single edge from wrapper to destination\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n /** @ts-ignore */\n builder.addEdge(wrapperNodeId, destination);\n } else {\n // No prompt instructions, add direct edges\n for (const edge of edges) {\n const sources = Array.isArray(edge.from) ? edge.from : [edge.from];\n for (const source of sources) {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n /** @ts-ignore */\n builder.addEdge(source, destination);\n }\n }\n }\n }\n\n return builder.compile(this.compileOptions as unknown as never);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MultiAgentGraph.mjs","sources":["../../../src/graphs/MultiAgentGraph.ts"],"sourcesContent":["import { z } from 'zod';\nimport { tool } from '@langchain/core/tools';\nimport { PromptTemplate } from '@langchain/core/prompts';\nimport {\n ToolMessage,\n HumanMessage,\n getBufferString,\n} from '@langchain/core/messages';\nimport {\n END,\n START,\n Command,\n StateGraph,\n Annotation,\n getCurrentTaskInput,\n messagesStateReducer,\n} from '@langchain/langgraph';\nimport type { ToolRunnableConfig } from '@langchain/core/tools';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type * as t from '@/types';\nimport { StandardGraph } from './Graph';\nimport { Constants } from '@/common';\n\n/**\n * MultiAgentGraph extends StandardGraph to support dynamic multi-agent workflows\n * with handoffs, fan-in/fan-out, and other composable patterns.\n *\n * Key behavior:\n * - Agents with ONLY handoff edges: Can dynamically route to any handoff destination\n * - Agents with ONLY direct edges: Always follow their direct edges\n * - Agents with BOTH: Use Command for exclusive routing (handoff OR direct, not both)\n * - If handoff occurs: Only the handoff destination executes\n * - If no handoff: Direct edges execute (potentially in parallel)\n *\n * This enables the common pattern where an agent either delegates (handoff)\n * OR continues its workflow (direct edges), but not both simultaneously.\n */\nexport class MultiAgentGraph extends StandardGraph {\n private edges: t.GraphEdge[];\n private startingNodes: Set<string> = new Set();\n private directEdges: t.GraphEdge[] = [];\n private handoffEdges: t.GraphEdge[] = [];\n\n constructor(input: t.MultiAgentGraphInput) {\n super(input);\n this.edges = input.edges;\n this.categorizeEdges();\n this.analyzeGraph();\n this.createHandoffTools();\n }\n\n /**\n * Categorize edges into handoff and direct types\n */\n private categorizeEdges(): void {\n for (const edge of this.edges) {\n // Default behavior: edges with conditions or explicit 'handoff' type are handoff edges\n // Edges with explicit 'direct' type or multi-destination without conditions are direct edges\n if (edge.edgeType === 'direct') {\n this.directEdges.push(edge);\n } else if (edge.edgeType === 'handoff' || edge.condition != null) {\n this.handoffEdges.push(edge);\n } else {\n // Default: single-to-single edges are handoff, single-to-multiple are direct\n const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];\n const sources = Array.isArray(edge.from) ? edge.from : [edge.from];\n\n if (sources.length === 1 && destinations.length > 1) {\n // Fan-out pattern defaults to direct\n this.directEdges.push(edge);\n } else {\n // Everything else defaults to handoff\n this.handoffEdges.push(edge);\n }\n }\n }\n }\n\n /**\n * Analyze graph structure to determine starting nodes and connections\n */\n private analyzeGraph(): void {\n const hasIncomingEdge = new Set<string>();\n\n // Track all nodes that have incoming edges\n for (const edge of this.edges) {\n const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];\n destinations.forEach((dest) => hasIncomingEdge.add(dest));\n }\n\n // Starting nodes are those without incoming edges\n for (const agentId of this.agentContexts.keys()) {\n if (!hasIncomingEdge.has(agentId)) {\n this.startingNodes.add(agentId);\n }\n }\n\n // If no starting nodes found, use the first agent\n if (this.startingNodes.size === 0 && this.agentContexts.size > 0) {\n this.startingNodes.add(this.agentContexts.keys().next().value!);\n }\n }\n\n /**\n * Create handoff tools for agents based on handoff edges only\n */\n private createHandoffTools(): void {\n // Group handoff edges by source agent(s)\n const handoffsByAgent = new Map<string, t.GraphEdge[]>();\n\n // Only process handoff edges for tool creation\n for (const edge of this.handoffEdges) {\n const sources = Array.isArray(edge.from) ? edge.from : [edge.from];\n sources.forEach((source) => {\n if (!handoffsByAgent.has(source)) {\n handoffsByAgent.set(source, []);\n }\n handoffsByAgent.get(source)!.push(edge);\n });\n }\n\n // Create handoff tools for each agent\n for (const [agentId, edges] of handoffsByAgent) {\n const agentContext = this.agentContexts.get(agentId);\n if (!agentContext) continue;\n\n // Create handoff tools for this agent's outgoing edges\n const handoffTools: t.GenericTool[] = [];\n for (const edge of edges) {\n handoffTools.push(...this.createHandoffToolsForEdge(edge));\n }\n\n // Add handoff tools to the agent's existing tools\n if (!agentContext.tools) {\n agentContext.tools = [];\n }\n agentContext.tools.push(...handoffTools);\n }\n }\n\n /**\n * Create handoff tools for an edge (handles multiple destinations)\n */\n private createHandoffToolsForEdge(edge: t.GraphEdge): t.GenericTool[] {\n const tools: t.GenericTool[] = [];\n const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];\n\n /** If there's a condition, create a single conditional handoff tool */\n if (edge.condition != null) {\n const toolName = 'conditional_transfer';\n const toolDescription =\n edge.description ?? 'Conditionally transfer control based on state';\n\n /** Check if we have a prompt for handoff input */\n const hasHandoffInput =\n edge.prompt != null && typeof edge.prompt === 'string';\n const handoffInputDescription = hasHandoffInput ? edge.prompt : undefined;\n const promptKey = edge.promptKey ?? 'instructions';\n\n tools.push(\n tool(\n async (input: Record<string, unknown>, config) => {\n const state = getCurrentTaskInput() as t.BaseGraphState;\n const toolCallId =\n (config as ToolRunnableConfig | undefined)?.toolCall?.id ??\n 'unknown';\n\n /** Evaluated condition */\n const result = edge.condition!(state);\n let destination: string;\n\n if (typeof result === 'boolean') {\n /** If true, use first destination; if false, don't transfer */\n if (!result) return null;\n destination = destinations[0];\n } else if (typeof result === 'string') {\n destination = result;\n } else {\n /** Array of destinations - for now, use the first */\n destination = Array.isArray(result) ? result[0] : destinations[0];\n }\n\n let content = `Conditionally transferred to ${destination}`;\n if (\n hasHandoffInput &&\n promptKey in input &&\n input[promptKey] != null\n ) {\n content += `\\n\\n${promptKey.charAt(0).toUpperCase() + promptKey.slice(1)}: ${input[promptKey]}`;\n }\n\n const toolMessage = new ToolMessage({\n content,\n name: toolName,\n tool_call_id: toolCallId,\n });\n\n return new Command({\n goto: destination,\n update: { messages: state.messages.concat(toolMessage) },\n graph: Command.PARENT,\n });\n },\n {\n name: toolName,\n schema: hasHandoffInput\n ? z.object({\n [promptKey]: z\n .string()\n .optional()\n .describe(handoffInputDescription as string),\n })\n : z.object({}),\n description: toolDescription,\n }\n )\n );\n } else {\n /** Create individual tools for each destination */\n for (const destination of destinations) {\n const toolName = `${Constants.LC_TRANSFER_TO_}${destination}`;\n const toolDescription =\n edge.description ?? `Transfer control to agent '${destination}'`;\n\n /** Check if we have a prompt for handoff input */\n const hasHandoffInput =\n edge.prompt != null && typeof edge.prompt === 'string';\n const handoffInputDescription = hasHandoffInput\n ? edge.prompt\n : undefined;\n const promptKey = edge.promptKey ?? 'instructions';\n\n tools.push(\n tool(\n async (input: Record<string, unknown>, config) => {\n const toolCallId =\n (config as ToolRunnableConfig | undefined)?.toolCall?.id ??\n 'unknown';\n\n let content = `Successfully transferred to ${destination}`;\n if (\n hasHandoffInput &&\n promptKey in input &&\n input[promptKey] != null\n ) {\n content += `\\n\\n${promptKey.charAt(0).toUpperCase() + promptKey.slice(1)}: ${input[promptKey]}`;\n }\n\n const toolMessage = new ToolMessage({\n content,\n name: toolName,\n tool_call_id: toolCallId,\n });\n\n const state = getCurrentTaskInput() as t.BaseGraphState;\n\n return new Command({\n goto: destination,\n update: { messages: state.messages.concat(toolMessage) },\n graph: Command.PARENT,\n });\n },\n {\n name: toolName,\n schema: hasHandoffInput\n ? z.object({\n [promptKey]: z\n .string()\n .optional()\n .describe(handoffInputDescription as string),\n })\n : z.object({}),\n description: toolDescription,\n }\n )\n );\n }\n }\n\n return tools;\n }\n\n /**\n * Create a complete agent subgraph (similar to createReactAgent)\n */\n private createAgentSubgraph(agentId: string): t.CompiledAgentWorfklow {\n /** This is essentially the same as `createAgentNode` from `StandardGraph` */\n return this.createAgentNode(agentId);\n }\n\n /**\n * Create the multi-agent workflow with dynamic handoffs\n */\n override createWorkflow(): t.CompiledMultiAgentWorkflow {\n const StateAnnotation = Annotation.Root({\n messages: Annotation<BaseMessage[]>({\n reducer: (a, b) => {\n if (!a.length) {\n this.startIndex = a.length + b.length;\n }\n const result = messagesStateReducer(a, b);\n this.messages = result;\n return result;\n },\n default: () => [],\n }),\n /** Channel for passing filtered messages to agents when excludeResults is true */\n agentMessages: Annotation<BaseMessage[]>({\n /** Replaces state entirely */\n reducer: (a, b) => b,\n default: () => [],\n }),\n });\n\n const builder = new StateGraph(StateAnnotation);\n\n // Add all agents as complete subgraphs\n for (const [agentId] of this.agentContexts) {\n // Get all possible destinations for this agent\n const handoffDestinations = new Set<string>();\n const directDestinations = new Set<string>();\n\n // Check handoff edges for destinations\n for (const edge of this.handoffEdges) {\n const sources = Array.isArray(edge.from) ? edge.from : [edge.from];\n if (sources.includes(agentId) === true) {\n const dests = Array.isArray(edge.to) ? edge.to : [edge.to];\n dests.forEach((dest) => handoffDestinations.add(dest));\n }\n }\n\n // Check direct edges for destinations\n for (const edge of this.directEdges) {\n const sources = Array.isArray(edge.from) ? edge.from : [edge.from];\n if (sources.includes(agentId) === true) {\n const dests = Array.isArray(edge.to) ? edge.to : [edge.to];\n dests.forEach((dest) => directDestinations.add(dest));\n }\n }\n\n /** Check if this agent has BOTH handoff and direct edges */\n const hasHandoffEdges = handoffDestinations.size > 0;\n const hasDirectEdges = directDestinations.size > 0;\n const needsCommandRouting = hasHandoffEdges && hasDirectEdges;\n\n /** Collect all possible destinations for this agent */\n const allDestinations = new Set([\n ...handoffDestinations,\n ...directDestinations,\n ]);\n if (handoffDestinations.size > 0 || directDestinations.size === 0) {\n allDestinations.add(END);\n }\n\n /** Agent subgraph (includes agent + tools) */\n const agentSubgraph = this.createAgentSubgraph(agentId);\n\n /** Wrapper function that handles agentMessages channel and conditional routing */\n const agentWrapper = async (\n state: t.MultiAgentGraphState\n ): Promise<t.MultiAgentGraphState | Command> => {\n let result: t.MultiAgentGraphState;\n\n if (state.agentMessages != null && state.agentMessages.length > 0) {\n /**\n * When using agentMessages (excludeResults=true), we need to update\n * the token map to account for the new prompt message\n */\n const agentContext = this.agentContexts.get(agentId);\n if (agentContext && agentContext.tokenCounter) {\n // The agentMessages contains:\n // 1. Filtered messages (0 to startIndex) - already have token counts\n // 2. New prompt message - needs token counting\n\n const freshTokenMap: Record<string, number> = {};\n\n // Copy existing token counts for filtered messages (0 to startIndex)\n for (let i = 0; i < this.startIndex; i++) {\n const tokenCount = agentContext.indexTokenCountMap[i];\n if (tokenCount !== undefined) {\n freshTokenMap[i] = tokenCount;\n }\n }\n\n // Calculate tokens only for the new prompt message (last message)\n const promptMessageIndex = state.agentMessages.length - 1;\n if (promptMessageIndex >= this.startIndex) {\n const promptMessage = state.agentMessages[promptMessageIndex];\n freshTokenMap[promptMessageIndex] =\n agentContext.tokenCounter(promptMessage);\n }\n\n // Update the agent's token map with instructions added\n agentContext.updateTokenMapWithInstructions(freshTokenMap);\n }\n\n /** Temporary state with messages replaced by `agentMessages` */\n const transformedState: t.MultiAgentGraphState = {\n ...state,\n messages: state.agentMessages,\n };\n result = await agentSubgraph.invoke(transformedState);\n result = {\n ...result,\n /** Clear agentMessages for next agent */\n agentMessages: [],\n };\n } else {\n result = await agentSubgraph.invoke(state);\n }\n\n /** If agent has both handoff and direct edges, use Command for exclusive routing */\n if (needsCommandRouting) {\n /** Check if a handoff occurred */\n const lastMessage = result.messages[\n result.messages.length - 1\n ] as BaseMessage | null;\n if (\n lastMessage != null &&\n lastMessage.getType() === 'tool' &&\n typeof lastMessage.name === 'string' &&\n lastMessage.name.startsWith(Constants.LC_TRANSFER_TO_)\n ) {\n /** Handoff occurred - extract destination and navigate there exclusively */\n const handoffDest = lastMessage.name.replace(\n Constants.LC_TRANSFER_TO_,\n ''\n );\n return new Command({\n update: result,\n goto: handoffDest,\n });\n } else {\n /** No handoff - proceed with direct edges */\n const directDests = Array.from(directDestinations);\n if (directDests.length === 1) {\n return new Command({\n update: result,\n goto: directDests[0],\n });\n } else if (directDests.length > 1) {\n /** Multiple direct destinations - they'll run in parallel */\n return new Command({\n update: result,\n goto: directDests,\n });\n }\n }\n }\n\n /** No special routing needed - return state normally */\n return result;\n };\n\n /** Wrapped agent as a node with its possible destinations */\n builder.addNode(agentId, agentWrapper, {\n ends: Array.from(allDestinations),\n });\n }\n\n // Add starting edges for all starting nodes\n for (const startNode of this.startingNodes) {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n /** @ts-ignore */\n builder.addEdge(START, startNode);\n }\n\n /**\n * Add direct edges for automatic transitions\n * Group edges by destination to handle fan-in scenarios\n */\n const edgesByDestination = new Map<string, t.GraphEdge[]>();\n\n for (const edge of this.directEdges) {\n const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];\n for (const destination of destinations) {\n if (!edgesByDestination.has(destination)) {\n edgesByDestination.set(destination, []);\n }\n edgesByDestination.get(destination)!.push(edge);\n }\n }\n\n for (const [destination, edges] of edgesByDestination) {\n /** Checks if this is a fan-in scenario with prompt instructions */\n const edgesWithPrompt = edges.filter(\n (edge) => edge.prompt != null && edge.prompt !== ''\n );\n\n if (edgesWithPrompt.length > 0) {\n /**\n * Single wrapper node for destination (Fan-in with prompt)\n */\n const wrapperNodeId = `fan_in_${destination}_prompt`;\n /**\n * First edge's `prompt`\n * (they should all be the same for fan-in)\n */\n const prompt = edgesWithPrompt[0].prompt;\n /**\n * First edge's `excludeResults` flag\n * (they should all be the same for fan-in)\n */\n const excludeResults = edgesWithPrompt[0].excludeResults;\n\n builder.addNode(wrapperNodeId, async (state: t.BaseGraphState) => {\n let promptText: string | undefined;\n let effectiveExcludeResults = excludeResults;\n\n if (typeof prompt === 'function') {\n promptText = await prompt(state.messages, this.startIndex);\n } else if (prompt != null) {\n if (prompt.includes('{results}')) {\n const resultsMessages = state.messages.slice(this.startIndex);\n const resultsString = getBufferString(resultsMessages);\n const promptTemplate = PromptTemplate.fromTemplate(prompt);\n const result = await promptTemplate.invoke({\n results: resultsString,\n });\n promptText = result.value;\n effectiveExcludeResults =\n excludeResults !== false && promptText !== '';\n } else {\n promptText = prompt;\n }\n }\n\n if (promptText != null && promptText !== '') {\n if (\n effectiveExcludeResults == null ||\n effectiveExcludeResults === false\n ) {\n return {\n messages: [new HumanMessage(promptText)],\n };\n }\n\n /** When `excludeResults` is true, use agentMessages channel\n * to pass filtered messages + prompt to the destination agent\n */\n const filteredMessages = state.messages.slice(0, this.startIndex);\n return {\n messages: [new HumanMessage(promptText)],\n agentMessages: messagesStateReducer(filteredMessages, [\n new HumanMessage(promptText),\n ]),\n };\n }\n\n /** No prompt needed, return empty update */\n return {};\n });\n\n /** Add edges from all sources to the wrapper, then wrapper to destination */\n for (const edge of edges) {\n const sources = Array.isArray(edge.from) ? edge.from : [edge.from];\n for (const source of sources) {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n /** @ts-ignore */\n builder.addEdge(source, wrapperNodeId);\n }\n }\n\n /** Single edge from wrapper to destination */\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n /** @ts-ignore */\n builder.addEdge(wrapperNodeId, destination);\n } else {\n /** No prompt instructions, add direct edges (skip if source uses Command routing) */\n for (const edge of edges) {\n const sources = Array.isArray(edge.from) ? edge.from : [edge.from];\n for (const source of sources) {\n /** Check if this source node has both handoff and direct edges */\n const sourceHandoffEdges = this.handoffEdges.filter((e) => {\n const eSources = Array.isArray(e.from) ? e.from : [e.from];\n return eSources.includes(source);\n });\n const sourceDirectEdges = this.directEdges.filter((e) => {\n const eSources = Array.isArray(e.from) ? e.from : [e.from];\n return eSources.includes(source);\n });\n\n /** Skip adding edge if source uses Command routing (has both types) */\n if (sourceHandoffEdges.length > 0 && sourceDirectEdges.length > 0) {\n continue;\n }\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n /** @ts-ignore */\n builder.addEdge(source, destination);\n }\n }\n }\n }\n\n return builder.compile(this.compileOptions as unknown as never);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,9 @@ import '@langchain/core/utils/function_calling';
|
|
|
6
6
|
import { ChatDeepSeek as ChatDeepSeek$1 } from '@langchain/deepseek';
|
|
7
7
|
import { getEndpoint, AzureChatOpenAI as AzureChatOpenAI$1, ChatOpenAI as ChatOpenAI$1, OpenAIClient } from '@langchain/openai';
|
|
8
8
|
import { isReasoningModel, _convertMessagesToOpenAIResponsesParams, _convertOpenAIResponsesDeltaToBaseMessageChunk, _convertMessagesToOpenAIParams } from './utils/index.mjs';
|
|
9
|
+
import '../../common/enum.mjs';
|
|
10
|
+
import { sleep } from '../../utils/run.mjs';
|
|
11
|
+
import 'js-tiktoken/lite';
|
|
9
12
|
|
|
10
13
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
11
14
|
const iife = (fn) => fn();
|
|
@@ -104,6 +107,11 @@ class CustomAzureOpenAIClient extends AzureOpenAI {
|
|
|
104
107
|
}
|
|
105
108
|
/** @ts-expect-error We are intentionally overriding `getReasoningParams` */
|
|
106
109
|
class ChatOpenAI extends ChatOpenAI$1 {
|
|
110
|
+
_lc_stream_delay;
|
|
111
|
+
constructor(fields) {
|
|
112
|
+
super(fields);
|
|
113
|
+
this._lc_stream_delay = fields?._lc_stream_delay;
|
|
114
|
+
}
|
|
107
115
|
get exposedClient() {
|
|
108
116
|
return this.client;
|
|
109
117
|
}
|
|
@@ -171,6 +179,9 @@ class ChatOpenAI extends ChatOpenAI$1 {
|
|
|
171
179
|
if (chunk == null)
|
|
172
180
|
continue;
|
|
173
181
|
yield chunk;
|
|
182
|
+
if (this._lc_stream_delay != null) {
|
|
183
|
+
await sleep(this._lc_stream_delay);
|
|
184
|
+
}
|
|
174
185
|
await runManager?.handleLLMNewToken(chunk.text || '', undefined, undefined, undefined, undefined, { chunk });
|
|
175
186
|
}
|
|
176
187
|
return;
|
|
@@ -235,6 +246,9 @@ class ChatOpenAI extends ChatOpenAI$1 {
|
|
|
235
246
|
generationInfo,
|
|
236
247
|
});
|
|
237
248
|
yield generationChunk;
|
|
249
|
+
if (this._lc_stream_delay != null) {
|
|
250
|
+
await sleep(this._lc_stream_delay);
|
|
251
|
+
}
|
|
238
252
|
await runManager?.handleLLMNewToken(generationChunk.text || '', newTokenIndices, undefined, undefined, undefined, { chunk: generationChunk });
|
|
239
253
|
}
|
|
240
254
|
if (usage) {
|
|
@@ -275,6 +289,9 @@ class ChatOpenAI extends ChatOpenAI$1 {
|
|
|
275
289
|
text: '',
|
|
276
290
|
});
|
|
277
291
|
yield generationChunk;
|
|
292
|
+
if (this._lc_stream_delay != null) {
|
|
293
|
+
await sleep(this._lc_stream_delay);
|
|
294
|
+
}
|
|
278
295
|
}
|
|
279
296
|
if (options.signal?.aborted === true) {
|
|
280
297
|
throw new Error('AbortError');
|
|
@@ -283,6 +300,11 @@ class ChatOpenAI extends ChatOpenAI$1 {
|
|
|
283
300
|
}
|
|
284
301
|
/** @ts-expect-error We are intentionally overriding `getReasoningParams` */
|
|
285
302
|
class AzureChatOpenAI extends AzureChatOpenAI$1 {
|
|
303
|
+
_lc_stream_delay;
|
|
304
|
+
constructor(fields) {
|
|
305
|
+
super(fields);
|
|
306
|
+
this._lc_stream_delay = fields?._lc_stream_delay;
|
|
307
|
+
}
|
|
286
308
|
get exposedClient() {
|
|
287
309
|
return this.client;
|
|
288
310
|
}
|
|
@@ -379,6 +401,9 @@ class AzureChatOpenAI extends AzureChatOpenAI$1 {
|
|
|
379
401
|
if (chunk == null)
|
|
380
402
|
continue;
|
|
381
403
|
yield chunk;
|
|
404
|
+
if (this._lc_stream_delay != null) {
|
|
405
|
+
await sleep(this._lc_stream_delay);
|
|
406
|
+
}
|
|
382
407
|
await runManager?.handleLLMNewToken(chunk.text || '', undefined, undefined, undefined, undefined, { chunk });
|
|
383
408
|
}
|
|
384
409
|
return;
|
|
@@ -413,8 +438,10 @@ class ChatDeepSeek extends ChatDeepSeek$1 {
|
|
|
413
438
|
}
|
|
414
439
|
}
|
|
415
440
|
class ChatXAI extends ChatXAI$1 {
|
|
441
|
+
_lc_stream_delay;
|
|
416
442
|
constructor(fields) {
|
|
417
443
|
super(fields);
|
|
444
|
+
this._lc_stream_delay = fields?._lc_stream_delay;
|
|
418
445
|
const customBaseURL = fields?.configuration?.baseURL ?? fields?.clientConfig?.baseURL;
|
|
419
446
|
if (customBaseURL != null && customBaseURL) {
|
|
420
447
|
this.clientConfig = {
|
|
@@ -516,6 +543,9 @@ class ChatXAI extends ChatXAI$1 {
|
|
|
516
543
|
generationInfo,
|
|
517
544
|
});
|
|
518
545
|
yield generationChunk;
|
|
546
|
+
if (this._lc_stream_delay != null) {
|
|
547
|
+
await sleep(this._lc_stream_delay);
|
|
548
|
+
}
|
|
519
549
|
await runManager?.handleLLMNewToken(generationChunk.text || '', newTokenIndices, undefined, undefined, undefined, { chunk: generationChunk });
|
|
520
550
|
}
|
|
521
551
|
if (usage) {
|
|
@@ -580,6 +610,9 @@ class ChatXAI extends ChatXAI$1 {
|
|
|
580
610
|
text: '',
|
|
581
611
|
});
|
|
582
612
|
yield generationChunk;
|
|
613
|
+
if (this._lc_stream_delay != null) {
|
|
614
|
+
await sleep(this._lc_stream_delay);
|
|
615
|
+
}
|
|
583
616
|
}
|
|
584
617
|
if (options.signal?.aborted === true) {
|
|
585
618
|
throw new Error('AbortError');
|