@agentforge/patterns 0.16.31 → 0.16.33
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/index.cjs +267 -170
- package/dist/index.d.cts +15 -32
- package/dist/index.d.ts +15 -32
- package/dist/index.js +267 -170
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -411,9 +411,9 @@ function stringifyActionArguments(arguments_) {
|
|
|
411
411
|
function getLatestThought(thoughts) {
|
|
412
412
|
return thoughts[thoughts.length - 1]?.content ?? "";
|
|
413
413
|
}
|
|
414
|
-
function debugIfVerbose(
|
|
414
|
+
function debugIfVerbose(logger5, verbose, message, data) {
|
|
415
415
|
if (verbose) {
|
|
416
|
-
|
|
416
|
+
logger5.debug(message, data);
|
|
417
417
|
}
|
|
418
418
|
}
|
|
419
419
|
|
|
@@ -2391,8 +2391,9 @@ var MultiAgentStateConfig = {
|
|
|
2391
2391
|
};
|
|
2392
2392
|
var MultiAgentState = (0, import_core7.createStateAnnotation)(MultiAgentStateConfig);
|
|
2393
2393
|
|
|
2394
|
-
// src/multi-agent/routing.ts
|
|
2394
|
+
// src/multi-agent/routing-internal/llm-routing.ts
|
|
2395
2395
|
var import_messages5 = require("@langchain/core/messages");
|
|
2396
|
+
var logger = createPatternLogger("agentforge:patterns:multi-agent:routing");
|
|
2396
2397
|
var DEFAULT_SUPERVISOR_SYSTEM_PROMPT = `You are a supervisor agent responsible for routing tasks to specialized worker agents.
|
|
2397
2398
|
|
|
2398
2399
|
Your job is to:
|
|
@@ -2425,6 +2426,84 @@ For PARALLEL multi-worker routing:
|
|
|
2425
2426
|
}
|
|
2426
2427
|
|
|
2427
2428
|
Choose parallel routing when the task benefits from multiple perspectives or data sources.`;
|
|
2429
|
+
function hasStructuredOutput(model) {
|
|
2430
|
+
return typeof model.withStructuredOutput === "function";
|
|
2431
|
+
}
|
|
2432
|
+
function isRecord(value) {
|
|
2433
|
+
return typeof value === "object" && value !== null;
|
|
2434
|
+
}
|
|
2435
|
+
function isContentCarrier(value) {
|
|
2436
|
+
return isRecord(value) && "content" in value;
|
|
2437
|
+
}
|
|
2438
|
+
function serializeRoutingContent(content) {
|
|
2439
|
+
if (typeof content === "string") {
|
|
2440
|
+
return content;
|
|
2441
|
+
}
|
|
2442
|
+
if (Array.isArray(content)) {
|
|
2443
|
+
const textParts = content.flatMap((part) => {
|
|
2444
|
+
if (typeof part === "string") {
|
|
2445
|
+
return [part];
|
|
2446
|
+
}
|
|
2447
|
+
if (isRecord(part) && typeof part.text === "string") {
|
|
2448
|
+
return [part.text];
|
|
2449
|
+
}
|
|
2450
|
+
return [];
|
|
2451
|
+
});
|
|
2452
|
+
if (textParts.length > 0) {
|
|
2453
|
+
return textParts.join("\n");
|
|
2454
|
+
}
|
|
2455
|
+
return JSON.stringify(content);
|
|
2456
|
+
}
|
|
2457
|
+
return JSON.stringify(content);
|
|
2458
|
+
}
|
|
2459
|
+
function normalizeRoutingDecisionInput(decision) {
|
|
2460
|
+
if (isContentCarrier(decision)) {
|
|
2461
|
+
return JSON.parse(serializeRoutingContent(decision.content));
|
|
2462
|
+
}
|
|
2463
|
+
return decision;
|
|
2464
|
+
}
|
|
2465
|
+
function parseRoutingDecision(decision) {
|
|
2466
|
+
try {
|
|
2467
|
+
return RoutingDecisionSchema.parse(normalizeRoutingDecisionInput(decision));
|
|
2468
|
+
} catch (error) {
|
|
2469
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2470
|
+
throw new Error(`Invalid LLM routing decision: ${message}`);
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
function finalizeLlmRoutingDecision(decision) {
|
|
2474
|
+
const parsed = parseRoutingDecision(decision);
|
|
2475
|
+
return {
|
|
2476
|
+
targetAgent: parsed.targetAgent,
|
|
2477
|
+
targetAgents: parsed.targetAgents,
|
|
2478
|
+
reasoning: parsed.reasoning,
|
|
2479
|
+
confidence: parsed.confidence,
|
|
2480
|
+
strategy: "llm-based",
|
|
2481
|
+
timestamp: Date.now()
|
|
2482
|
+
};
|
|
2483
|
+
}
|
|
2484
|
+
async function invokeStructuredRoutingDecision(model, messages) {
|
|
2485
|
+
let structuredModel;
|
|
2486
|
+
try {
|
|
2487
|
+
structuredModel = model.withStructuredOutput(RoutingDecisionSchema);
|
|
2488
|
+
} catch (error) {
|
|
2489
|
+
logger.warn("Structured output unavailable, using direct routing fallback", {
|
|
2490
|
+
strategy: "llm-based",
|
|
2491
|
+
fallback: "direct-model-invoke",
|
|
2492
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2493
|
+
});
|
|
2494
|
+
const decision2 = await model.invoke(messages);
|
|
2495
|
+
return finalizeLlmRoutingDecision(decision2);
|
|
2496
|
+
}
|
|
2497
|
+
const decision = await structuredModel.invoke(messages);
|
|
2498
|
+
return finalizeLlmRoutingDecision(decision);
|
|
2499
|
+
}
|
|
2500
|
+
async function invokeRoutingDecision(model, messages) {
|
|
2501
|
+
if (hasStructuredOutput(model)) {
|
|
2502
|
+
return invokeStructuredRoutingDecision(model, messages);
|
|
2503
|
+
}
|
|
2504
|
+
const decision = await model.invoke(messages);
|
|
2505
|
+
return finalizeLlmRoutingDecision(decision);
|
|
2506
|
+
}
|
|
2428
2507
|
var llmBasedRouting = {
|
|
2429
2508
|
name: "llm-based",
|
|
2430
2509
|
async route(state, config) {
|
|
@@ -2450,64 +2529,119 @@ Select the best worker(s) for this task and explain your reasoning.`;
|
|
|
2450
2529
|
new import_messages5.SystemMessage(systemPrompt),
|
|
2451
2530
|
new import_messages5.HumanMessage(userPrompt)
|
|
2452
2531
|
];
|
|
2453
|
-
|
|
2532
|
+
return invokeRoutingDecision(config.model, messages);
|
|
2533
|
+
}
|
|
2534
|
+
};
|
|
2535
|
+
|
|
2536
|
+
// src/multi-agent/routing-internal/load-balanced-routing.ts
|
|
2537
|
+
function getAvailableWorkerLoads(state) {
|
|
2538
|
+
return Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => ({ id, workload: caps.currentWorkload })).sort((a, b) => a.workload - b.workload);
|
|
2539
|
+
}
|
|
2540
|
+
var loadBalancedRouting = {
|
|
2541
|
+
name: "load-balanced",
|
|
2542
|
+
async route(state, _config) {
|
|
2543
|
+
const availableWorkers = getAvailableWorkerLoads(state);
|
|
2544
|
+
if (availableWorkers.length === 0) {
|
|
2545
|
+
throw new Error("No available workers for load-balanced routing");
|
|
2546
|
+
}
|
|
2547
|
+
const targetWorker = availableWorkers[0];
|
|
2548
|
+
const avgWorkload = availableWorkers.reduce((sum, worker) => sum + worker.workload, 0) / availableWorkers.length;
|
|
2549
|
+
const confidence = targetWorker.workload === 0 ? 1 : Math.max(0.5, 1 - targetWorker.workload / (avgWorkload * 2));
|
|
2454
2550
|
return {
|
|
2455
|
-
targetAgent:
|
|
2456
|
-
targetAgents:
|
|
2457
|
-
reasoning:
|
|
2458
|
-
confidence
|
|
2459
|
-
strategy: "
|
|
2551
|
+
targetAgent: targetWorker.id,
|
|
2552
|
+
targetAgents: null,
|
|
2553
|
+
reasoning: `Lowest workload: ${targetWorker.workload} tasks (avg: ${avgWorkload.toFixed(1)})`,
|
|
2554
|
+
confidence,
|
|
2555
|
+
strategy: "load-balanced",
|
|
2460
2556
|
timestamp: Date.now()
|
|
2461
2557
|
};
|
|
2462
2558
|
}
|
|
2463
2559
|
};
|
|
2560
|
+
|
|
2561
|
+
// src/multi-agent/routing-internal/worker-selection.ts
|
|
2562
|
+
function getAvailableWorkerIds(state) {
|
|
2563
|
+
return Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id]) => id);
|
|
2564
|
+
}
|
|
2565
|
+
function getTaskContent(state) {
|
|
2566
|
+
const lastMessage = state.messages[state.messages.length - 1];
|
|
2567
|
+
return String(lastMessage?.content || state.input);
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
// src/multi-agent/routing-internal/round-robin-routing.ts
|
|
2571
|
+
function createRoundRobinDecision(targetAgent, position, workerCount) {
|
|
2572
|
+
return {
|
|
2573
|
+
targetAgent,
|
|
2574
|
+
targetAgents: null,
|
|
2575
|
+
reasoning: `Round-robin selection: worker ${position} of ${workerCount}`,
|
|
2576
|
+
confidence: 1,
|
|
2577
|
+
strategy: "round-robin",
|
|
2578
|
+
timestamp: Date.now()
|
|
2579
|
+
};
|
|
2580
|
+
}
|
|
2464
2581
|
var roundRobinRouting = {
|
|
2465
2582
|
name: "round-robin",
|
|
2466
|
-
async route(state,
|
|
2467
|
-
const availableWorkers =
|
|
2583
|
+
async route(state, _config) {
|
|
2584
|
+
const availableWorkers = getAvailableWorkerIds(state);
|
|
2468
2585
|
if (availableWorkers.length === 0) {
|
|
2469
2586
|
throw new Error("No available workers for round-robin routing");
|
|
2470
2587
|
}
|
|
2471
2588
|
const lastRoutingIndex = state.routingHistory.length % availableWorkers.length;
|
|
2472
|
-
|
|
2589
|
+
return createRoundRobinDecision(
|
|
2590
|
+
availableWorkers[lastRoutingIndex],
|
|
2591
|
+
lastRoutingIndex + 1,
|
|
2592
|
+
availableWorkers.length
|
|
2593
|
+
);
|
|
2594
|
+
}
|
|
2595
|
+
};
|
|
2596
|
+
|
|
2597
|
+
// src/multi-agent/routing-internal/rule-based-routing.ts
|
|
2598
|
+
var ruleBasedRouting = {
|
|
2599
|
+
name: "rule-based",
|
|
2600
|
+
async route(state, config) {
|
|
2601
|
+
if (!config.routingFn) {
|
|
2602
|
+
throw new Error("Rule-based routing requires a custom routing function");
|
|
2603
|
+
}
|
|
2604
|
+
return config.routingFn(state);
|
|
2605
|
+
}
|
|
2606
|
+
};
|
|
2607
|
+
|
|
2608
|
+
// src/multi-agent/routing-internal/skill-based-routing.ts
|
|
2609
|
+
function scoreWorkers(state, taskContent) {
|
|
2610
|
+
return Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => {
|
|
2611
|
+
const skillMatches = caps.skills.filter(
|
|
2612
|
+
(skill) => taskContent.includes(skill.toLowerCase())
|
|
2613
|
+
).length;
|
|
2614
|
+
const toolMatches = caps.tools.filter(
|
|
2615
|
+
(tool) => taskContent.includes(tool.toLowerCase())
|
|
2616
|
+
).length;
|
|
2473
2617
|
return {
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
confidence: 1,
|
|
2478
|
-
strategy: "round-robin",
|
|
2479
|
-
timestamp: Date.now()
|
|
2618
|
+
id,
|
|
2619
|
+
score: skillMatches * 2 + toolMatches,
|
|
2620
|
+
skills: caps.skills
|
|
2480
2621
|
};
|
|
2622
|
+
}).filter((worker) => worker.score > 0).sort((a, b) => b.score - a.score);
|
|
2623
|
+
}
|
|
2624
|
+
function createFallbackDecision(state) {
|
|
2625
|
+
const firstAvailable = Object.entries(state.workers).find(([_, caps]) => caps.available);
|
|
2626
|
+
if (!firstAvailable) {
|
|
2627
|
+
throw new Error("No available workers for skill-based routing");
|
|
2481
2628
|
}
|
|
2482
|
-
|
|
2629
|
+
return {
|
|
2630
|
+
targetAgent: firstAvailable[0],
|
|
2631
|
+
targetAgents: null,
|
|
2632
|
+
reasoning: "No skill matches found, using first available worker",
|
|
2633
|
+
confidence: 0.5,
|
|
2634
|
+
strategy: "skill-based",
|
|
2635
|
+
timestamp: Date.now()
|
|
2636
|
+
};
|
|
2637
|
+
}
|
|
2483
2638
|
var skillBasedRouting = {
|
|
2484
2639
|
name: "skill-based",
|
|
2485
|
-
async route(state,
|
|
2486
|
-
const
|
|
2487
|
-
const
|
|
2488
|
-
const workerScores = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => {
|
|
2489
|
-
const skillMatches = caps.skills.filter(
|
|
2490
|
-
(skill) => taskContent.includes(skill.toLowerCase())
|
|
2491
|
-
).length;
|
|
2492
|
-
const toolMatches = caps.tools.filter(
|
|
2493
|
-
(tool) => taskContent.includes(tool.toLowerCase())
|
|
2494
|
-
).length;
|
|
2495
|
-
const score = skillMatches * 2 + toolMatches;
|
|
2496
|
-
return { id, score, skills: caps.skills, tools: caps.tools };
|
|
2497
|
-
}).filter((w) => w.score > 0).sort((a, b) => b.score - a.score);
|
|
2640
|
+
async route(state, _config) {
|
|
2641
|
+
const taskContent = getTaskContent(state).toLowerCase();
|
|
2642
|
+
const workerScores = scoreWorkers(state, taskContent);
|
|
2498
2643
|
if (workerScores.length === 0) {
|
|
2499
|
-
|
|
2500
|
-
if (!firstAvailable) {
|
|
2501
|
-
throw new Error("No available workers for skill-based routing");
|
|
2502
|
-
}
|
|
2503
|
-
return {
|
|
2504
|
-
targetAgent: firstAvailable[0],
|
|
2505
|
-
targetAgents: null,
|
|
2506
|
-
reasoning: "No skill matches found, using first available worker",
|
|
2507
|
-
confidence: 0.5,
|
|
2508
|
-
strategy: "skill-based",
|
|
2509
|
-
timestamp: Date.now()
|
|
2510
|
-
};
|
|
2644
|
+
return createFallbackDecision(state);
|
|
2511
2645
|
}
|
|
2512
2646
|
const best = workerScores[0];
|
|
2513
2647
|
const confidence = Math.min(best.score / 5, 1);
|
|
@@ -2521,69 +2655,39 @@ var skillBasedRouting = {
|
|
|
2521
2655
|
};
|
|
2522
2656
|
}
|
|
2523
2657
|
};
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
const avgWorkload = availableWorkers.reduce((sum, w) => sum + w.workload, 0) / availableWorkers.length;
|
|
2533
|
-
const confidence = targetWorker.workload === 0 ? 1 : Math.max(0.5, 1 - targetWorker.workload / (avgWorkload * 2));
|
|
2534
|
-
return {
|
|
2535
|
-
targetAgent: targetWorker.id,
|
|
2536
|
-
targetAgents: null,
|
|
2537
|
-
reasoning: `Lowest workload: ${targetWorker.workload} tasks (avg: ${avgWorkload.toFixed(1)})`,
|
|
2538
|
-
confidence,
|
|
2539
|
-
strategy: "load-balanced",
|
|
2540
|
-
timestamp: Date.now()
|
|
2541
|
-
};
|
|
2542
|
-
}
|
|
2543
|
-
};
|
|
2544
|
-
var ruleBasedRouting = {
|
|
2545
|
-
name: "rule-based",
|
|
2546
|
-
async route(state, config) {
|
|
2547
|
-
if (!config.routingFn) {
|
|
2548
|
-
throw new Error("Rule-based routing requires a custom routing function");
|
|
2549
|
-
}
|
|
2550
|
-
return await config.routingFn(state);
|
|
2551
|
-
}
|
|
2658
|
+
|
|
2659
|
+
// src/multi-agent/routing.ts
|
|
2660
|
+
var routingStrategies = {
|
|
2661
|
+
"llm-based": llmBasedRouting,
|
|
2662
|
+
"round-robin": roundRobinRouting,
|
|
2663
|
+
"skill-based": skillBasedRouting,
|
|
2664
|
+
"load-balanced": loadBalancedRouting,
|
|
2665
|
+
"rule-based": ruleBasedRouting
|
|
2552
2666
|
};
|
|
2553
2667
|
function getRoutingStrategy(name) {
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
return llmBasedRouting;
|
|
2557
|
-
case "round-robin":
|
|
2558
|
-
return roundRobinRouting;
|
|
2559
|
-
case "skill-based":
|
|
2560
|
-
return skillBasedRouting;
|
|
2561
|
-
case "load-balanced":
|
|
2562
|
-
return loadBalancedRouting;
|
|
2563
|
-
case "rule-based":
|
|
2564
|
-
return ruleBasedRouting;
|
|
2565
|
-
default:
|
|
2566
|
-
throw new Error(`Unknown routing strategy: ${name}`);
|
|
2668
|
+
if (!Object.hasOwn(routingStrategies, name)) {
|
|
2669
|
+
throw new Error(`Unknown routing strategy: ${name}`);
|
|
2567
2670
|
}
|
|
2671
|
+
return routingStrategies[name];
|
|
2568
2672
|
}
|
|
2569
2673
|
|
|
2570
2674
|
// src/multi-agent/utils.ts
|
|
2571
|
-
var
|
|
2572
|
-
function
|
|
2675
|
+
var logger2 = createPatternLogger("agentforge:patterns:multi-agent:utils");
|
|
2676
|
+
function isRecord2(value) {
|
|
2573
2677
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2574
2678
|
}
|
|
2575
2679
|
function toRunnableConfig(config) {
|
|
2576
|
-
if (!
|
|
2680
|
+
if (!isRecord2(config)) {
|
|
2577
2681
|
return void 0;
|
|
2578
2682
|
}
|
|
2579
2683
|
return config;
|
|
2580
2684
|
}
|
|
2581
2685
|
function getReActResultShape(value) {
|
|
2582
|
-
if (!
|
|
2686
|
+
if (!isRecord2(value)) {
|
|
2583
2687
|
return {};
|
|
2584
2688
|
}
|
|
2585
|
-
const messages = Array.isArray(value.messages) ? value.messages.filter((message) =>
|
|
2586
|
-
const actions = Array.isArray(value.actions) ? value.actions.filter((action) =>
|
|
2689
|
+
const messages = Array.isArray(value.messages) ? value.messages.filter((message) => isRecord2(message)) : void 0;
|
|
2690
|
+
const actions = Array.isArray(value.actions) ? value.actions.filter((action) => isRecord2(action)) : void 0;
|
|
2587
2691
|
return {
|
|
2588
2692
|
messages,
|
|
2589
2693
|
actions,
|
|
@@ -2602,7 +2706,7 @@ function safeSerializeContent(content) {
|
|
|
2602
2706
|
if (typeof part === "string") {
|
|
2603
2707
|
return part;
|
|
2604
2708
|
}
|
|
2605
|
-
if (
|
|
2709
|
+
if (isRecord2(part) && typeof part.text === "string" && part.text.length > 0) {
|
|
2606
2710
|
return part.text;
|
|
2607
2711
|
}
|
|
2608
2712
|
try {
|
|
@@ -2647,17 +2751,17 @@ function isReActAgent(obj) {
|
|
|
2647
2751
|
function wrapReActAgent(workerId, agent, verbose = false) {
|
|
2648
2752
|
return async (state, config) => {
|
|
2649
2753
|
try {
|
|
2650
|
-
|
|
2754
|
+
logger2.debug("Wrapping ReAct agent execution", { workerId });
|
|
2651
2755
|
const runnableConfig = toRunnableConfig(config);
|
|
2652
2756
|
const currentAssignment = state.activeAssignments.find(
|
|
2653
2757
|
(assignment) => assignment.workerId === workerId && !state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
|
|
2654
2758
|
);
|
|
2655
2759
|
if (!currentAssignment) {
|
|
2656
|
-
|
|
2760
|
+
logger2.debug("No active assignment found", { workerId });
|
|
2657
2761
|
return {};
|
|
2658
2762
|
}
|
|
2659
2763
|
const task = currentAssignment.task;
|
|
2660
|
-
|
|
2764
|
+
logger2.debug("Extracted task from assignment", {
|
|
2661
2765
|
workerId,
|
|
2662
2766
|
assignmentId: currentAssignment.id,
|
|
2663
2767
|
taskPreview: task.substring(0, 100) + (task.length > 100 ? "..." : "")
|
|
@@ -2670,7 +2774,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2670
2774
|
thread_id: workerThreadId
|
|
2671
2775
|
}
|
|
2672
2776
|
} : runnableConfig;
|
|
2673
|
-
|
|
2777
|
+
logger2.debug("Invoking ReAct agent with worker-specific config", {
|
|
2674
2778
|
workerId,
|
|
2675
2779
|
...runnableConfig?.configurable?.thread_id !== void 0 ? { parentThreadId: String(runnableConfig.configurable.thread_id) } : {},
|
|
2676
2780
|
...workerThreadId ? { workerThreadId } : {},
|
|
@@ -2685,13 +2789,13 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2685
2789
|
);
|
|
2686
2790
|
const resultShape = getReActResultShape(result);
|
|
2687
2791
|
const response = extractResponse(resultShape);
|
|
2688
|
-
|
|
2792
|
+
logger2.debug("Received response from ReAct agent", {
|
|
2689
2793
|
workerId,
|
|
2690
2794
|
responsePreview: response.substring(0, 100) + (response.length > 100 ? "..." : "")
|
|
2691
2795
|
});
|
|
2692
2796
|
const uniqueTools = extractToolsUsed(resultShape);
|
|
2693
2797
|
if (uniqueTools.length > 0) {
|
|
2694
|
-
|
|
2798
|
+
logger2.debug("Tools used by ReAct agent", { workerId, tools: uniqueTools });
|
|
2695
2799
|
}
|
|
2696
2800
|
const taskResult = {
|
|
2697
2801
|
assignmentId: currentAssignment.id,
|
|
@@ -2710,7 +2814,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2710
2814
|
};
|
|
2711
2815
|
} catch (error) {
|
|
2712
2816
|
const errorMessage = handleNodeError(error, `react-agent:${workerId}`, verbose);
|
|
2713
|
-
|
|
2817
|
+
logger2.error("Error in ReAct agent execution", {
|
|
2714
2818
|
workerId,
|
|
2715
2819
|
error: errorMessage
|
|
2716
2820
|
});
|
|
@@ -2743,7 +2847,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2743
2847
|
// src/multi-agent/nodes/shared.ts
|
|
2744
2848
|
var import_messages6 = require("@langchain/core/messages");
|
|
2745
2849
|
var import_core8 = require("@agentforge/core");
|
|
2746
|
-
var
|
|
2850
|
+
var logger3 = createPatternLogger("agentforge:patterns:multi-agent:nodes");
|
|
2747
2851
|
function createGeneratedId(prefix) {
|
|
2748
2852
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
2749
2853
|
}
|
|
@@ -2795,7 +2899,7 @@ function serializeModelContent(content) {
|
|
|
2795
2899
|
const error = new Error(
|
|
2796
2900
|
"Failed to serialize model content: JSON.stringify returned undefined"
|
|
2797
2901
|
);
|
|
2798
|
-
|
|
2902
|
+
logger3.error("Model content serialization failed", {
|
|
2799
2903
|
errorMessage: error.message,
|
|
2800
2904
|
contentType: content === null ? "null" : typeof content
|
|
2801
2905
|
});
|
|
@@ -2804,7 +2908,7 @@ function serializeModelContent(content) {
|
|
|
2804
2908
|
return serialized;
|
|
2805
2909
|
} catch (error) {
|
|
2806
2910
|
const normalizedError = error instanceof Error ? error : new Error("Unknown error during model content serialization");
|
|
2807
|
-
|
|
2911
|
+
logger3.error("Model content serialization threw an error", {
|
|
2808
2912
|
errorMessage: normalizedError.message,
|
|
2809
2913
|
contentType: content === null ? "null" : typeof content
|
|
2810
2914
|
});
|
|
@@ -2847,16 +2951,16 @@ function createAggregatorNode(config = {}) {
|
|
|
2847
2951
|
} = config;
|
|
2848
2952
|
return async (state) => {
|
|
2849
2953
|
try {
|
|
2850
|
-
|
|
2954
|
+
logger3.info("Aggregator node executing", {
|
|
2851
2955
|
completedTasks: state.completedTasks.length,
|
|
2852
2956
|
successfulTasks: state.completedTasks.filter((task) => task.success).length,
|
|
2853
2957
|
failedTasks: state.completedTasks.filter((task) => !task.success).length
|
|
2854
2958
|
});
|
|
2855
|
-
|
|
2959
|
+
logger3.debug("Combining results from workers");
|
|
2856
2960
|
if (aggregateFn) {
|
|
2857
|
-
|
|
2961
|
+
logger3.debug("Using custom aggregation function");
|
|
2858
2962
|
const response2 = await aggregateFn(state);
|
|
2859
|
-
|
|
2963
|
+
logger3.info("Custom aggregation complete", {
|
|
2860
2964
|
responseLength: response2.length
|
|
2861
2965
|
});
|
|
2862
2966
|
return {
|
|
@@ -2865,16 +2969,16 @@ function createAggregatorNode(config = {}) {
|
|
|
2865
2969
|
};
|
|
2866
2970
|
}
|
|
2867
2971
|
if (state.completedTasks.length === 0) {
|
|
2868
|
-
|
|
2972
|
+
logger3.warn("No completed tasks to aggregate");
|
|
2869
2973
|
return {
|
|
2870
2974
|
response: "No tasks were completed.",
|
|
2871
2975
|
status: "completed"
|
|
2872
2976
|
};
|
|
2873
2977
|
}
|
|
2874
2978
|
if (!model) {
|
|
2875
|
-
|
|
2979
|
+
logger3.debug("No model provided, concatenating results");
|
|
2876
2980
|
const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
|
|
2877
|
-
|
|
2981
|
+
logger3.info("Simple concatenation complete", {
|
|
2878
2982
|
resultLength: combinedResults.length
|
|
2879
2983
|
});
|
|
2880
2984
|
return {
|
|
@@ -2882,27 +2986,27 @@ function createAggregatorNode(config = {}) {
|
|
|
2882
2986
|
status: "completed"
|
|
2883
2987
|
};
|
|
2884
2988
|
}
|
|
2885
|
-
|
|
2989
|
+
logger3.debug("Using LLM for intelligent aggregation", {
|
|
2886
2990
|
taskCount: state.completedTasks.length
|
|
2887
2991
|
});
|
|
2888
2992
|
const messages = createPromptMessages(systemPrompt, createAggregationPrompt(state));
|
|
2889
|
-
|
|
2993
|
+
logger3.debug("Invoking aggregation LLM");
|
|
2890
2994
|
const response = await model.invoke(messages);
|
|
2891
2995
|
const aggregatedResponse = serializeModelContent(response.content);
|
|
2892
|
-
|
|
2996
|
+
logger3.info("Aggregation complete", {
|
|
2893
2997
|
responseLength: aggregatedResponse.length
|
|
2894
2998
|
});
|
|
2895
|
-
|
|
2999
|
+
logger3.debug("Aggregation response details", {
|
|
2896
3000
|
responseLength: aggregatedResponse.length
|
|
2897
3001
|
});
|
|
2898
|
-
|
|
3002
|
+
logger3.debug("Aggregation complete");
|
|
2899
3003
|
return {
|
|
2900
3004
|
response: aggregatedResponse,
|
|
2901
3005
|
status: "completed"
|
|
2902
3006
|
};
|
|
2903
3007
|
} catch (error) {
|
|
2904
3008
|
const errorMessage = handleNodeError(error, "aggregator", false);
|
|
2905
|
-
|
|
3009
|
+
logger3.error("Aggregator node error", {
|
|
2906
3010
|
error: errorMessage,
|
|
2907
3011
|
...error instanceof Error && error.stack ? { stack: error.stack } : {},
|
|
2908
3012
|
completedTasks: state.completedTasks.length
|
|
@@ -2926,7 +3030,7 @@ function incrementAssignedWorkerLoads(state, assignments) {
|
|
|
2926
3030
|
for (const assignment of assignments) {
|
|
2927
3031
|
const worker = updatedWorkers[assignment.workerId];
|
|
2928
3032
|
if (!worker) {
|
|
2929
|
-
|
|
3033
|
+
logger3.error("Worker not found in state", {
|
|
2930
3034
|
workerId: assignment.workerId,
|
|
2931
3035
|
availableWorkers: Object.keys(updatedWorkers)
|
|
2932
3036
|
});
|
|
@@ -2945,45 +3049,45 @@ function createSupervisorNode(config) {
|
|
|
2945
3049
|
const { strategy, maxIterations = 10 } = config;
|
|
2946
3050
|
return async (state) => {
|
|
2947
3051
|
try {
|
|
2948
|
-
|
|
3052
|
+
logger3.info("Supervisor node executing", {
|
|
2949
3053
|
iteration: state.iteration,
|
|
2950
3054
|
maxIterations,
|
|
2951
3055
|
activeAssignments: state.activeAssignments.length,
|
|
2952
3056
|
completedTasks: state.completedTasks.length
|
|
2953
3057
|
});
|
|
2954
|
-
|
|
3058
|
+
logger3.debug(`Routing iteration ${state.iteration}/${maxIterations}`);
|
|
2955
3059
|
if (state.iteration >= maxIterations) {
|
|
2956
|
-
|
|
3060
|
+
logger3.warn("Max iterations reached", {
|
|
2957
3061
|
iteration: state.iteration,
|
|
2958
3062
|
maxIterations
|
|
2959
3063
|
});
|
|
2960
|
-
|
|
3064
|
+
logger3.debug("Max iterations reached, moving to aggregation");
|
|
2961
3065
|
return {
|
|
2962
3066
|
status: "aggregating",
|
|
2963
3067
|
currentAgent: "aggregator"
|
|
2964
3068
|
};
|
|
2965
3069
|
}
|
|
2966
3070
|
const allCompleted = allAssignmentsCompleted(state);
|
|
2967
|
-
|
|
3071
|
+
logger3.debug("Checking task completion", {
|
|
2968
3072
|
activeAssignments: state.activeAssignments.length,
|
|
2969
3073
|
completedTasks: state.completedTasks.length,
|
|
2970
3074
|
allCompleted
|
|
2971
3075
|
});
|
|
2972
3076
|
if (allCompleted && state.activeAssignments.length > 0) {
|
|
2973
|
-
|
|
3077
|
+
logger3.info("All tasks completed, moving to aggregation", {
|
|
2974
3078
|
completedCount: state.completedTasks.length
|
|
2975
3079
|
});
|
|
2976
|
-
|
|
3080
|
+
logger3.debug("All tasks completed, moving to aggregation");
|
|
2977
3081
|
return {
|
|
2978
3082
|
status: "aggregating",
|
|
2979
3083
|
currentAgent: "aggregator"
|
|
2980
3084
|
};
|
|
2981
3085
|
}
|
|
2982
|
-
|
|
3086
|
+
logger3.debug("Getting routing strategy", { strategy });
|
|
2983
3087
|
const routingImpl = getRoutingStrategy(strategy);
|
|
2984
3088
|
const decision = await routingImpl.route(state, config);
|
|
2985
3089
|
const targetAgents = decision.targetAgents && decision.targetAgents.length > 0 ? decision.targetAgents : decision.targetAgent ? [decision.targetAgent] : [];
|
|
2986
|
-
|
|
3090
|
+
logger3.debug("Target agents determined", {
|
|
2987
3091
|
targetAgents,
|
|
2988
3092
|
isParallel: targetAgents.length > 1,
|
|
2989
3093
|
decision: {
|
|
@@ -2992,30 +3096,30 @@ function createSupervisorNode(config) {
|
|
|
2992
3096
|
}
|
|
2993
3097
|
});
|
|
2994
3098
|
if (targetAgents.length === 0) {
|
|
2995
|
-
|
|
3099
|
+
logger3.error("No target agents specified in routing decision");
|
|
2996
3100
|
throw new Error("Routing decision must specify at least one target agent");
|
|
2997
3101
|
}
|
|
2998
3102
|
if (targetAgents.length === 1) {
|
|
2999
|
-
|
|
3103
|
+
logger3.info("Routing to single agent", {
|
|
3000
3104
|
targetAgent: targetAgents[0],
|
|
3001
3105
|
reasoning: decision.reasoning,
|
|
3002
3106
|
confidence: decision.confidence
|
|
3003
3107
|
});
|
|
3004
|
-
|
|
3108
|
+
logger3.debug(`Routing to ${targetAgents[0]}: ${decision.reasoning}`);
|
|
3005
3109
|
} else {
|
|
3006
|
-
|
|
3110
|
+
logger3.info("Routing to multiple agents in parallel", {
|
|
3007
3111
|
targetAgents,
|
|
3008
3112
|
count: targetAgents.length,
|
|
3009
3113
|
reasoning: decision.reasoning,
|
|
3010
3114
|
confidence: decision.confidence
|
|
3011
3115
|
});
|
|
3012
|
-
|
|
3116
|
+
logger3.debug(
|
|
3013
3117
|
`Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`
|
|
3014
3118
|
);
|
|
3015
3119
|
}
|
|
3016
3120
|
const task = getLatestTaskContent(state);
|
|
3017
3121
|
const assignments = createTaskAssignments(targetAgents, task);
|
|
3018
|
-
|
|
3122
|
+
logger3.debug("Created task assignments", {
|
|
3019
3123
|
assignmentCount: assignments.length,
|
|
3020
3124
|
assignments: assignments.map((assignment) => ({
|
|
3021
3125
|
id: assignment.id,
|
|
@@ -3025,7 +3129,7 @@ function createSupervisorNode(config) {
|
|
|
3025
3129
|
});
|
|
3026
3130
|
const messages = createAssignmentMessages(assignments);
|
|
3027
3131
|
const updatedWorkers = incrementAssignedWorkerLoads(state, assignments);
|
|
3028
|
-
|
|
3132
|
+
logger3.info("Supervisor routing complete", {
|
|
3029
3133
|
currentAgent: targetAgents.join(","),
|
|
3030
3134
|
status: "executing",
|
|
3031
3135
|
assignmentCount: assignments.length,
|
|
@@ -3043,7 +3147,7 @@ function createSupervisorNode(config) {
|
|
|
3043
3147
|
};
|
|
3044
3148
|
} catch (error) {
|
|
3045
3149
|
const errorMessage = handleNodeError(error, "supervisor", false);
|
|
3046
|
-
|
|
3150
|
+
logger3.error("Supervisor node error", {
|
|
3047
3151
|
error: errorMessage,
|
|
3048
3152
|
...error instanceof Error && error.stack ? { stack: error.stack } : {},
|
|
3049
3153
|
iteration: state.iteration
|
|
@@ -3071,7 +3175,7 @@ async function invokeWorkerModel(model, config, assignment) {
|
|
|
3071
3175
|
);
|
|
3072
3176
|
let modelToUse = model;
|
|
3073
3177
|
if (config.tools && config.tools.length > 0 && model.bindTools) {
|
|
3074
|
-
|
|
3178
|
+
logger3.debug("Binding tools to model", {
|
|
3075
3179
|
workerId: config.id,
|
|
3076
3180
|
toolCount: config.tools.length,
|
|
3077
3181
|
toolNames: config.tools.map((tool) => tool.metadata.name)
|
|
@@ -3080,15 +3184,15 @@ async function invokeWorkerModel(model, config, assignment) {
|
|
|
3080
3184
|
convertWorkerToolsForLangChain(config.tools)
|
|
3081
3185
|
);
|
|
3082
3186
|
}
|
|
3083
|
-
|
|
3187
|
+
logger3.debug("Invoking LLM", { workerId: config.id });
|
|
3084
3188
|
const response = await modelToUse.invoke(messages);
|
|
3085
3189
|
const result = serializeModelContent(response.content);
|
|
3086
|
-
|
|
3190
|
+
logger3.info("Worker task completed", {
|
|
3087
3191
|
workerId: config.id,
|
|
3088
3192
|
assignmentId: assignment.id,
|
|
3089
3193
|
resultLength: result.length
|
|
3090
3194
|
});
|
|
3091
|
-
|
|
3195
|
+
logger3.debug("Worker result details", {
|
|
3092
3196
|
workerId: config.id,
|
|
3093
3197
|
assignmentId: assignment.id,
|
|
3094
3198
|
resultLength: result.length
|
|
@@ -3123,7 +3227,7 @@ async function invokeWorkerModel(model, config, assignment) {
|
|
|
3123
3227
|
function getStateWorkerOrThrow(state, workerId) {
|
|
3124
3228
|
const currentWorker = state.workers[workerId];
|
|
3125
3229
|
if (!currentWorker) {
|
|
3126
|
-
|
|
3230
|
+
logger3.error("Attempted to decrement workload for unknown worker", {
|
|
3127
3231
|
workerId,
|
|
3128
3232
|
availableWorkers: Object.keys(state.workers)
|
|
3129
3233
|
});
|
|
@@ -3139,7 +3243,7 @@ function resolvePreviousWorkload(workerId, currentWorker, workerFromExecution) {
|
|
|
3139
3243
|
if (typeof currentWorker.currentWorkload === "number" && Number.isFinite(currentWorker.currentWorkload)) {
|
|
3140
3244
|
return currentWorker.currentWorkload;
|
|
3141
3245
|
}
|
|
3142
|
-
|
|
3246
|
+
logger3.error("Worker workload is not a valid number; cannot decrement", {
|
|
3143
3247
|
workerId,
|
|
3144
3248
|
workloadFromState: currentWorker.currentWorkload,
|
|
3145
3249
|
...typeof workloadFromWorker === "number" ? { workloadFromWorker } : {}
|
|
@@ -3174,7 +3278,7 @@ function mergeWorkersWithDecrement(state, workerId, executionResult) {
|
|
|
3174
3278
|
...baseWorkers,
|
|
3175
3279
|
[workerId]: updatedWorker
|
|
3176
3280
|
};
|
|
3177
|
-
|
|
3281
|
+
logger3.debug("Worker workload decremented", {
|
|
3178
3282
|
workerId,
|
|
3179
3283
|
previousWorkload,
|
|
3180
3284
|
newWorkload: updatedWorkers[workerId].currentWorkload,
|
|
@@ -3192,7 +3296,7 @@ function decrementWorkerOnError(state, workerId) {
|
|
|
3192
3296
|
currentWorkload: Math.max(0, previousWorkload - 1)
|
|
3193
3297
|
}
|
|
3194
3298
|
};
|
|
3195
|
-
|
|
3299
|
+
logger3.debug("Worker workload decremented (error path)", {
|
|
3196
3300
|
workerId,
|
|
3197
3301
|
previousWorkload,
|
|
3198
3302
|
newWorkload: updatedWorkers[workerId].currentWorkload
|
|
@@ -3200,7 +3304,7 @@ function decrementWorkerOnError(state, workerId) {
|
|
|
3200
3304
|
return updatedWorkers;
|
|
3201
3305
|
}
|
|
3202
3306
|
function createErrorTaskResult(assignment, workerId, errorMessage) {
|
|
3203
|
-
|
|
3307
|
+
logger3.warn("Creating error result for assignment", {
|
|
3204
3308
|
workerId,
|
|
3205
3309
|
assignmentId: assignment.id
|
|
3206
3310
|
});
|
|
@@ -3217,47 +3321,47 @@ function createWorkerNode(config) {
|
|
|
3217
3321
|
const { id, model, executeFn, agent } = config;
|
|
3218
3322
|
return async (state, runConfig) => {
|
|
3219
3323
|
try {
|
|
3220
|
-
|
|
3324
|
+
logger3.info("Worker node executing", {
|
|
3221
3325
|
workerId: id,
|
|
3222
3326
|
iteration: state.iteration,
|
|
3223
3327
|
activeAssignments: state.activeAssignments.length
|
|
3224
3328
|
});
|
|
3225
3329
|
const currentAssignment = findCurrentAssignment(state, id);
|
|
3226
3330
|
if (!currentAssignment) {
|
|
3227
|
-
|
|
3331
|
+
logger3.debug("No active assignment found for worker", {
|
|
3228
3332
|
workerId: id,
|
|
3229
3333
|
totalActiveAssignments: state.activeAssignments.length,
|
|
3230
3334
|
completedTasks: state.completedTasks.length
|
|
3231
3335
|
});
|
|
3232
3336
|
return {};
|
|
3233
3337
|
}
|
|
3234
|
-
|
|
3338
|
+
logger3.info("Worker processing assignment", {
|
|
3235
3339
|
workerId: id,
|
|
3236
3340
|
assignmentId: currentAssignment.id,
|
|
3237
3341
|
taskLength: currentAssignment.task.length
|
|
3238
3342
|
});
|
|
3239
|
-
|
|
3343
|
+
logger3.debug("Worker assignment details", {
|
|
3240
3344
|
workerId: id,
|
|
3241
3345
|
assignmentId: currentAssignment.id,
|
|
3242
3346
|
taskLength: currentAssignment.task.length
|
|
3243
3347
|
});
|
|
3244
3348
|
let executionResult;
|
|
3245
3349
|
if (executeFn) {
|
|
3246
|
-
|
|
3350
|
+
logger3.debug("Using custom execution function", { workerId: id });
|
|
3247
3351
|
executionResult = await executeFn(state, runConfig);
|
|
3248
3352
|
} else if (agent && isReActAgent(agent)) {
|
|
3249
|
-
|
|
3353
|
+
logger3.debug("Using ReAct agent", { workerId: id });
|
|
3250
3354
|
const wrappedFn = wrapReActAgent(id, agent, config.verbose ?? false);
|
|
3251
3355
|
executionResult = await wrappedFn(state, runConfig);
|
|
3252
3356
|
} else if (model) {
|
|
3253
|
-
|
|
3357
|
+
logger3.debug("Using default LLM execution", {
|
|
3254
3358
|
workerId: id,
|
|
3255
3359
|
hasTools: (config.tools ?? []).length > 0,
|
|
3256
3360
|
toolCount: (config.tools ?? []).length
|
|
3257
3361
|
});
|
|
3258
3362
|
executionResult = await invokeWorkerModel(model, config, currentAssignment);
|
|
3259
3363
|
} else {
|
|
3260
|
-
|
|
3364
|
+
logger3.error("Worker missing required configuration", { workerId: id });
|
|
3261
3365
|
throw new Error(
|
|
3262
3366
|
`Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
|
|
3263
3367
|
);
|
|
@@ -3269,7 +3373,7 @@ function createWorkerNode(config) {
|
|
|
3269
3373
|
};
|
|
3270
3374
|
} catch (error) {
|
|
3271
3375
|
const errorMessage = handleNodeError(error, `worker:${id}`, false);
|
|
3272
|
-
|
|
3376
|
+
logger3.error("Worker node error", {
|
|
3273
3377
|
workerId: id,
|
|
3274
3378
|
error: errorMessage
|
|
3275
3379
|
});
|
|
@@ -3278,7 +3382,7 @@ function createWorkerNode(config) {
|
|
|
3278
3382
|
updatedWorkers = decrementWorkerOnError(state, id);
|
|
3279
3383
|
} catch (workloadError) {
|
|
3280
3384
|
const workloadErrorMessage = workloadError instanceof Error ? workloadError.message : String(workloadError);
|
|
3281
|
-
|
|
3385
|
+
logger3.error("Worker error handling failed", {
|
|
3282
3386
|
workerId: id,
|
|
3283
3387
|
error: workloadErrorMessage
|
|
3284
3388
|
});
|
|
@@ -3298,7 +3402,7 @@ function createWorkerNode(config) {
|
|
|
3298
3402
|
workers: updatedWorkers
|
|
3299
3403
|
};
|
|
3300
3404
|
}
|
|
3301
|
-
|
|
3405
|
+
logger3.error("No assignment found for error handling", { workerId: id });
|
|
3302
3406
|
return {
|
|
3303
3407
|
status: "failed",
|
|
3304
3408
|
error: errorMessage,
|
|
@@ -3310,7 +3414,7 @@ function createWorkerNode(config) {
|
|
|
3310
3414
|
|
|
3311
3415
|
// src/multi-agent/agent.ts
|
|
3312
3416
|
var import_langgraph4 = require("@langchain/langgraph");
|
|
3313
|
-
var
|
|
3417
|
+
var logger4 = createPatternLogger("agentforge:patterns:multi-agent:system");
|
|
3314
3418
|
function getToolName(tool) {
|
|
3315
3419
|
if (!tool || typeof tool !== "object") {
|
|
3316
3420
|
return "unknown";
|
|
@@ -3335,13 +3439,6 @@ function createMultiAgentSystem(config) {
|
|
|
3335
3439
|
} = config;
|
|
3336
3440
|
const workflow = new import_langgraph4.StateGraph(MultiAgentState);
|
|
3337
3441
|
const supervisorConfig = { ...supervisor, maxIterations, verbose };
|
|
3338
|
-
if (supervisor.model) {
|
|
3339
|
-
let configuredModel = supervisor.model;
|
|
3340
|
-
if (supervisor.strategy === "llm-based") {
|
|
3341
|
-
configuredModel = configuredModel.withStructuredOutput(RoutingDecisionSchema);
|
|
3342
|
-
}
|
|
3343
|
-
supervisorConfig.model = configuredModel;
|
|
3344
|
-
}
|
|
3345
3442
|
const supervisorNode = createSupervisorNode(supervisorConfig);
|
|
3346
3443
|
workflow.addNode("supervisor", supervisorNode);
|
|
3347
3444
|
const workerIds = [];
|
|
@@ -3361,46 +3458,46 @@ function createMultiAgentSystem(config) {
|
|
|
3361
3458
|
});
|
|
3362
3459
|
workflow.addNode("aggregator", aggregatorNode);
|
|
3363
3460
|
const supervisorRouter = (state) => {
|
|
3364
|
-
|
|
3461
|
+
logger4.debug("Supervisor router executing", {
|
|
3365
3462
|
status: state.status,
|
|
3366
3463
|
...state.currentAgent ? { currentAgent: state.currentAgent } : {},
|
|
3367
3464
|
iteration: state.iteration
|
|
3368
3465
|
});
|
|
3369
3466
|
if (state.status === "completed" || state.status === "failed") {
|
|
3370
|
-
|
|
3467
|
+
logger4.info("Supervisor router: ending workflow", { status: state.status });
|
|
3371
3468
|
return import_langgraph4.END;
|
|
3372
3469
|
}
|
|
3373
3470
|
if (state.status === "aggregating") {
|
|
3374
|
-
|
|
3471
|
+
logger4.info("Supervisor router: routing to aggregator");
|
|
3375
3472
|
return "aggregator";
|
|
3376
3473
|
}
|
|
3377
3474
|
if (state.currentAgent && state.currentAgent !== "supervisor") {
|
|
3378
3475
|
if (state.currentAgent.includes(",")) {
|
|
3379
3476
|
const agents = state.currentAgent.split(",").map((a) => a.trim());
|
|
3380
|
-
|
|
3477
|
+
logger4.info("Supervisor router: parallel routing", {
|
|
3381
3478
|
agents,
|
|
3382
3479
|
count: agents.length
|
|
3383
3480
|
});
|
|
3384
3481
|
return agents;
|
|
3385
3482
|
}
|
|
3386
|
-
|
|
3483
|
+
logger4.info("Supervisor router: single agent routing", {
|
|
3387
3484
|
targetAgent: state.currentAgent
|
|
3388
3485
|
});
|
|
3389
3486
|
return state.currentAgent;
|
|
3390
3487
|
}
|
|
3391
|
-
|
|
3488
|
+
logger4.debug("Supervisor router: staying at supervisor");
|
|
3392
3489
|
return "supervisor";
|
|
3393
3490
|
};
|
|
3394
3491
|
const workerRouter = (state) => {
|
|
3395
|
-
|
|
3492
|
+
logger4.debug("Worker router executing", {
|
|
3396
3493
|
iteration: state.iteration,
|
|
3397
3494
|
completedTasks: state.completedTasks.length
|
|
3398
3495
|
});
|
|
3399
|
-
|
|
3496
|
+
logger4.debug("Worker router: returning to supervisor");
|
|
3400
3497
|
return "supervisor";
|
|
3401
3498
|
};
|
|
3402
3499
|
const aggregatorRouter = (state) => {
|
|
3403
|
-
|
|
3500
|
+
logger4.info("Aggregator router: ending workflow", {
|
|
3404
3501
|
completedTasks: state.completedTasks.length,
|
|
3405
3502
|
status: state.status
|
|
3406
3503
|
});
|