@librechat/agents 3.0.62 → 3.0.63
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/graphs/MultiAgentGraph.cjs +73 -33
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +9 -0
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +73 -33
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +9 -0
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/types/graphs/MultiAgentGraph.d.ts +3 -0
- package/package.json +1 -1
- package/src/graphs/MultiAgentGraph.ts +73 -32
- package/src/tools/ToolNode.ts +9 -0
|
@@ -420,6 +420,9 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
420
420
|
* Returns filtered messages with the transfer tool call/message removed, plus any instructions
|
|
421
421
|
* extracted from the transfer to be injected as a HumanMessage preamble.
|
|
422
422
|
*
|
|
423
|
+
* Supports both single handoffs (last message is the transfer) and parallel handoffs
|
|
424
|
+
* (multiple transfer ToolMessages, need to find the one targeting this agent).
|
|
425
|
+
*
|
|
423
426
|
* @param messages - Current state messages
|
|
424
427
|
* @param agentId - The agent ID to check for handoff reception
|
|
425
428
|
* @returns Object with filtered messages and extracted instructions, or null if not a handoff
|
|
@@ -430,35 +433,49 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
430
433
|
): { filteredMessages: BaseMessage[]; instructions: string | null } | null {
|
|
431
434
|
if (messages.length === 0) return null;
|
|
432
435
|
|
|
433
|
-
|
|
436
|
+
/**
|
|
437
|
+
* Search for a transfer ToolMessage targeting this agent.
|
|
438
|
+
* For parallel handoffs, multiple transfer messages may exist - find ours.
|
|
439
|
+
* Search backwards from the end to find the most recent transfer to this agent.
|
|
440
|
+
*/
|
|
441
|
+
let toolMessage: ToolMessage | null = null;
|
|
442
|
+
let toolMessageIndex = -1;
|
|
443
|
+
|
|
444
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
445
|
+
const msg = messages[i];
|
|
446
|
+
if (msg.getType() !== 'tool') continue;
|
|
434
447
|
|
|
435
|
-
|
|
436
|
-
|
|
448
|
+
const candidateMsg = msg as ToolMessage;
|
|
449
|
+
const toolName = candidateMsg.name;
|
|
437
450
|
|
|
438
|
-
|
|
439
|
-
const toolName = toolMessage.name;
|
|
451
|
+
if (typeof toolName !== 'string') continue;
|
|
440
452
|
|
|
441
|
-
|
|
453
|
+
/** Check for standard transfer pattern */
|
|
454
|
+
const isTransferMessage = toolName.startsWith(Constants.LC_TRANSFER_TO_);
|
|
455
|
+
const isConditionalTransfer = toolName === 'conditional_transfer';
|
|
442
456
|
|
|
443
|
-
|
|
444
|
-
const isTransferMessage = toolName.startsWith(Constants.LC_TRANSFER_TO_);
|
|
445
|
-
const isConditionalTransfer = toolName === 'conditional_transfer';
|
|
457
|
+
if (!isTransferMessage && !isConditionalTransfer) continue;
|
|
446
458
|
|
|
447
|
-
|
|
459
|
+
/** Extract destination from tool name or additional_kwargs */
|
|
460
|
+
let destinationAgent: string | null = null;
|
|
448
461
|
|
|
449
|
-
|
|
450
|
-
|
|
462
|
+
if (isTransferMessage) {
|
|
463
|
+
destinationAgent = toolName.replace(Constants.LC_TRANSFER_TO_, '');
|
|
464
|
+
} else if (isConditionalTransfer) {
|
|
465
|
+
const handoffDest = candidateMsg.additional_kwargs.handoff_destination;
|
|
466
|
+
destinationAgent = typeof handoffDest === 'string' ? handoffDest : null;
|
|
467
|
+
}
|
|
451
468
|
|
|
452
|
-
|
|
453
|
-
destinationAgent
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
469
|
+
/** Check if this transfer targets our agent */
|
|
470
|
+
if (destinationAgent === agentId) {
|
|
471
|
+
toolMessage = candidateMsg;
|
|
472
|
+
toolMessageIndex = i;
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
458
475
|
}
|
|
459
476
|
|
|
460
|
-
/**
|
|
461
|
-
if (
|
|
477
|
+
/** No transfer targeting this agent found */
|
|
478
|
+
if (toolMessage === null || toolMessageIndex < 0) return null;
|
|
462
479
|
|
|
463
480
|
/** Extract instructions from the ToolMessage content */
|
|
464
481
|
const contentStr =
|
|
@@ -472,35 +489,59 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
472
489
|
/** Get the tool_call_id to find and filter the AI message's tool call */
|
|
473
490
|
const toolCallId = toolMessage.tool_call_id;
|
|
474
491
|
|
|
475
|
-
/**
|
|
492
|
+
/**
|
|
493
|
+
* Collect all transfer tool_call_ids to filter out.
|
|
494
|
+
* For parallel handoffs, we filter ALL transfer messages (not just ours)
|
|
495
|
+
* to give the receiving agent a clean context without handoff noise.
|
|
496
|
+
*/
|
|
497
|
+
const transferToolCallIds = new Set<string>([toolCallId]);
|
|
498
|
+
for (const msg of messages) {
|
|
499
|
+
if (msg.getType() !== 'tool') continue;
|
|
500
|
+
const tm = msg as ToolMessage;
|
|
501
|
+
const tName = tm.name;
|
|
502
|
+
if (typeof tName !== 'string') continue;
|
|
503
|
+
if (
|
|
504
|
+
tName.startsWith(Constants.LC_TRANSFER_TO_) ||
|
|
505
|
+
tName === 'conditional_transfer'
|
|
506
|
+
) {
|
|
507
|
+
transferToolCallIds.add(tm.tool_call_id);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/** Filter out all transfer messages */
|
|
476
512
|
const filteredMessages: BaseMessage[] = [];
|
|
477
513
|
|
|
478
|
-
for (let i = 0; i < messages.length
|
|
479
|
-
/** Exclude the last message (ToolMessage) by iterating to length - 1 */
|
|
514
|
+
for (let i = 0; i < messages.length; i++) {
|
|
480
515
|
const msg = messages[i];
|
|
481
516
|
const msgType = msg.getType();
|
|
482
517
|
|
|
518
|
+
/** Skip transfer ToolMessages */
|
|
519
|
+
if (msgType === 'tool') {
|
|
520
|
+
const tm = msg as ToolMessage;
|
|
521
|
+
if (transferToolCallIds.has(tm.tool_call_id)) {
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
483
526
|
if (msgType === 'ai') {
|
|
484
|
-
/** Check if this AI message contains
|
|
527
|
+
/** Check if this AI message contains any transfer tool calls */
|
|
485
528
|
const aiMsg = msg as AIMessage | AIMessageChunk;
|
|
486
529
|
const toolCalls = aiMsg.tool_calls;
|
|
487
530
|
|
|
488
531
|
if (toolCalls && toolCalls.length > 0) {
|
|
489
|
-
|
|
490
|
-
|
|
532
|
+
/** Filter out all transfer tool calls */
|
|
533
|
+
const remainingToolCalls = toolCalls.filter(
|
|
534
|
+
(tc) => tc.id == null || !transferToolCallIds.has(tc.id)
|
|
491
535
|
);
|
|
492
536
|
|
|
493
|
-
|
|
494
|
-
/** This AI message has the transfer tool call - filter it out */
|
|
495
|
-
const remainingToolCalls = toolCalls.filter(
|
|
496
|
-
(tc) => tc.id !== toolCallId
|
|
497
|
-
);
|
|
537
|
+
const hasTransferCalls = remainingToolCalls.length < toolCalls.length;
|
|
498
538
|
|
|
539
|
+
if (hasTransferCalls) {
|
|
499
540
|
if (
|
|
500
541
|
remainingToolCalls.length > 0 ||
|
|
501
542
|
(typeof aiMsg.content === 'string' && aiMsg.content.trim())
|
|
502
543
|
) {
|
|
503
|
-
/** Keep the message but without
|
|
544
|
+
/** Keep the message but without transfer tool calls */
|
|
504
545
|
const filteredAiMsg = new AIMessage({
|
|
505
546
|
content: aiMsg.content,
|
|
506
547
|
tool_calls: remainingToolCalls,
|
package/src/tools/ToolNode.ts
CHANGED
|
@@ -288,6 +288,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
288
288
|
Array.isArray(output.goto) &&
|
|
289
289
|
output.goto.every((send): send is Send => isSend(send))
|
|
290
290
|
) {
|
|
291
|
+
/** Aggregate Send-based commands */
|
|
291
292
|
if (parentCommand) {
|
|
292
293
|
(parentCommand.goto as Send[]).push(...(output.goto as Send[]));
|
|
293
294
|
} else {
|
|
@@ -297,6 +298,14 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
297
298
|
});
|
|
298
299
|
}
|
|
299
300
|
} else {
|
|
301
|
+
/**
|
|
302
|
+
* Non-Send Commands (including handoff Commands with string goto)
|
|
303
|
+
* are passed through as-is. For single handoffs, this works correctly.
|
|
304
|
+
*
|
|
305
|
+
* Note: Parallel handoffs (LLM calling multiple transfer tools simultaneously)
|
|
306
|
+
* are not yet fully supported. For parallel agent execution, use direct edges
|
|
307
|
+
* with edgeType: 'direct' instead of handoff edges.
|
|
308
|
+
*/
|
|
300
309
|
combinedOutputs.push(output);
|
|
301
310
|
}
|
|
302
311
|
} else {
|