@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.js
CHANGED
|
@@ -313,9 +313,9 @@ function stringifyActionArguments(arguments_) {
|
|
|
313
313
|
function getLatestThought(thoughts) {
|
|
314
314
|
return thoughts[thoughts.length - 1]?.content ?? "";
|
|
315
315
|
}
|
|
316
|
-
function debugIfVerbose(
|
|
316
|
+
function debugIfVerbose(logger5, verbose, message, data) {
|
|
317
317
|
if (verbose) {
|
|
318
|
-
|
|
318
|
+
logger5.debug(message, data);
|
|
319
319
|
}
|
|
320
320
|
}
|
|
321
321
|
|
|
@@ -2293,8 +2293,9 @@ var MultiAgentStateConfig = {
|
|
|
2293
2293
|
};
|
|
2294
2294
|
var MultiAgentState = createStateAnnotation4(MultiAgentStateConfig);
|
|
2295
2295
|
|
|
2296
|
-
// src/multi-agent/routing.ts
|
|
2296
|
+
// src/multi-agent/routing-internal/llm-routing.ts
|
|
2297
2297
|
import { HumanMessage as HumanMessage5, SystemMessage as SystemMessage5 } from "@langchain/core/messages";
|
|
2298
|
+
var logger = createPatternLogger("agentforge:patterns:multi-agent:routing");
|
|
2298
2299
|
var DEFAULT_SUPERVISOR_SYSTEM_PROMPT = `You are a supervisor agent responsible for routing tasks to specialized worker agents.
|
|
2299
2300
|
|
|
2300
2301
|
Your job is to:
|
|
@@ -2327,6 +2328,84 @@ For PARALLEL multi-worker routing:
|
|
|
2327
2328
|
}
|
|
2328
2329
|
|
|
2329
2330
|
Choose parallel routing when the task benefits from multiple perspectives or data sources.`;
|
|
2331
|
+
function hasStructuredOutput(model) {
|
|
2332
|
+
return typeof model.withStructuredOutput === "function";
|
|
2333
|
+
}
|
|
2334
|
+
function isRecord(value) {
|
|
2335
|
+
return typeof value === "object" && value !== null;
|
|
2336
|
+
}
|
|
2337
|
+
function isContentCarrier(value) {
|
|
2338
|
+
return isRecord(value) && "content" in value;
|
|
2339
|
+
}
|
|
2340
|
+
function serializeRoutingContent(content) {
|
|
2341
|
+
if (typeof content === "string") {
|
|
2342
|
+
return content;
|
|
2343
|
+
}
|
|
2344
|
+
if (Array.isArray(content)) {
|
|
2345
|
+
const textParts = content.flatMap((part) => {
|
|
2346
|
+
if (typeof part === "string") {
|
|
2347
|
+
return [part];
|
|
2348
|
+
}
|
|
2349
|
+
if (isRecord(part) && typeof part.text === "string") {
|
|
2350
|
+
return [part.text];
|
|
2351
|
+
}
|
|
2352
|
+
return [];
|
|
2353
|
+
});
|
|
2354
|
+
if (textParts.length > 0) {
|
|
2355
|
+
return textParts.join("\n");
|
|
2356
|
+
}
|
|
2357
|
+
return JSON.stringify(content);
|
|
2358
|
+
}
|
|
2359
|
+
return JSON.stringify(content);
|
|
2360
|
+
}
|
|
2361
|
+
function normalizeRoutingDecisionInput(decision) {
|
|
2362
|
+
if (isContentCarrier(decision)) {
|
|
2363
|
+
return JSON.parse(serializeRoutingContent(decision.content));
|
|
2364
|
+
}
|
|
2365
|
+
return decision;
|
|
2366
|
+
}
|
|
2367
|
+
function parseRoutingDecision(decision) {
|
|
2368
|
+
try {
|
|
2369
|
+
return RoutingDecisionSchema.parse(normalizeRoutingDecisionInput(decision));
|
|
2370
|
+
} catch (error) {
|
|
2371
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2372
|
+
throw new Error(`Invalid LLM routing decision: ${message}`);
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
function finalizeLlmRoutingDecision(decision) {
|
|
2376
|
+
const parsed = parseRoutingDecision(decision);
|
|
2377
|
+
return {
|
|
2378
|
+
targetAgent: parsed.targetAgent,
|
|
2379
|
+
targetAgents: parsed.targetAgents,
|
|
2380
|
+
reasoning: parsed.reasoning,
|
|
2381
|
+
confidence: parsed.confidence,
|
|
2382
|
+
strategy: "llm-based",
|
|
2383
|
+
timestamp: Date.now()
|
|
2384
|
+
};
|
|
2385
|
+
}
|
|
2386
|
+
async function invokeStructuredRoutingDecision(model, messages) {
|
|
2387
|
+
let structuredModel;
|
|
2388
|
+
try {
|
|
2389
|
+
structuredModel = model.withStructuredOutput(RoutingDecisionSchema);
|
|
2390
|
+
} catch (error) {
|
|
2391
|
+
logger.warn("Structured output unavailable, using direct routing fallback", {
|
|
2392
|
+
strategy: "llm-based",
|
|
2393
|
+
fallback: "direct-model-invoke",
|
|
2394
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2395
|
+
});
|
|
2396
|
+
const decision2 = await model.invoke(messages);
|
|
2397
|
+
return finalizeLlmRoutingDecision(decision2);
|
|
2398
|
+
}
|
|
2399
|
+
const decision = await structuredModel.invoke(messages);
|
|
2400
|
+
return finalizeLlmRoutingDecision(decision);
|
|
2401
|
+
}
|
|
2402
|
+
async function invokeRoutingDecision(model, messages) {
|
|
2403
|
+
if (hasStructuredOutput(model)) {
|
|
2404
|
+
return invokeStructuredRoutingDecision(model, messages);
|
|
2405
|
+
}
|
|
2406
|
+
const decision = await model.invoke(messages);
|
|
2407
|
+
return finalizeLlmRoutingDecision(decision);
|
|
2408
|
+
}
|
|
2330
2409
|
var llmBasedRouting = {
|
|
2331
2410
|
name: "llm-based",
|
|
2332
2411
|
async route(state, config) {
|
|
@@ -2352,64 +2431,119 @@ Select the best worker(s) for this task and explain your reasoning.`;
|
|
|
2352
2431
|
new SystemMessage5(systemPrompt),
|
|
2353
2432
|
new HumanMessage5(userPrompt)
|
|
2354
2433
|
];
|
|
2355
|
-
|
|
2434
|
+
return invokeRoutingDecision(config.model, messages);
|
|
2435
|
+
}
|
|
2436
|
+
};
|
|
2437
|
+
|
|
2438
|
+
// src/multi-agent/routing-internal/load-balanced-routing.ts
|
|
2439
|
+
function getAvailableWorkerLoads(state) {
|
|
2440
|
+
return Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => ({ id, workload: caps.currentWorkload })).sort((a, b) => a.workload - b.workload);
|
|
2441
|
+
}
|
|
2442
|
+
var loadBalancedRouting = {
|
|
2443
|
+
name: "load-balanced",
|
|
2444
|
+
async route(state, _config) {
|
|
2445
|
+
const availableWorkers = getAvailableWorkerLoads(state);
|
|
2446
|
+
if (availableWorkers.length === 0) {
|
|
2447
|
+
throw new Error("No available workers for load-balanced routing");
|
|
2448
|
+
}
|
|
2449
|
+
const targetWorker = availableWorkers[0];
|
|
2450
|
+
const avgWorkload = availableWorkers.reduce((sum, worker) => sum + worker.workload, 0) / availableWorkers.length;
|
|
2451
|
+
const confidence = targetWorker.workload === 0 ? 1 : Math.max(0.5, 1 - targetWorker.workload / (avgWorkload * 2));
|
|
2356
2452
|
return {
|
|
2357
|
-
targetAgent:
|
|
2358
|
-
targetAgents:
|
|
2359
|
-
reasoning:
|
|
2360
|
-
confidence
|
|
2361
|
-
strategy: "
|
|
2453
|
+
targetAgent: targetWorker.id,
|
|
2454
|
+
targetAgents: null,
|
|
2455
|
+
reasoning: `Lowest workload: ${targetWorker.workload} tasks (avg: ${avgWorkload.toFixed(1)})`,
|
|
2456
|
+
confidence,
|
|
2457
|
+
strategy: "load-balanced",
|
|
2362
2458
|
timestamp: Date.now()
|
|
2363
2459
|
};
|
|
2364
2460
|
}
|
|
2365
2461
|
};
|
|
2462
|
+
|
|
2463
|
+
// src/multi-agent/routing-internal/worker-selection.ts
|
|
2464
|
+
function getAvailableWorkerIds(state) {
|
|
2465
|
+
return Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id]) => id);
|
|
2466
|
+
}
|
|
2467
|
+
function getTaskContent(state) {
|
|
2468
|
+
const lastMessage = state.messages[state.messages.length - 1];
|
|
2469
|
+
return String(lastMessage?.content || state.input);
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2472
|
+
// src/multi-agent/routing-internal/round-robin-routing.ts
|
|
2473
|
+
function createRoundRobinDecision(targetAgent, position, workerCount) {
|
|
2474
|
+
return {
|
|
2475
|
+
targetAgent,
|
|
2476
|
+
targetAgents: null,
|
|
2477
|
+
reasoning: `Round-robin selection: worker ${position} of ${workerCount}`,
|
|
2478
|
+
confidence: 1,
|
|
2479
|
+
strategy: "round-robin",
|
|
2480
|
+
timestamp: Date.now()
|
|
2481
|
+
};
|
|
2482
|
+
}
|
|
2366
2483
|
var roundRobinRouting = {
|
|
2367
2484
|
name: "round-robin",
|
|
2368
|
-
async route(state,
|
|
2369
|
-
const availableWorkers =
|
|
2485
|
+
async route(state, _config) {
|
|
2486
|
+
const availableWorkers = getAvailableWorkerIds(state);
|
|
2370
2487
|
if (availableWorkers.length === 0) {
|
|
2371
2488
|
throw new Error("No available workers for round-robin routing");
|
|
2372
2489
|
}
|
|
2373
2490
|
const lastRoutingIndex = state.routingHistory.length % availableWorkers.length;
|
|
2374
|
-
|
|
2491
|
+
return createRoundRobinDecision(
|
|
2492
|
+
availableWorkers[lastRoutingIndex],
|
|
2493
|
+
lastRoutingIndex + 1,
|
|
2494
|
+
availableWorkers.length
|
|
2495
|
+
);
|
|
2496
|
+
}
|
|
2497
|
+
};
|
|
2498
|
+
|
|
2499
|
+
// src/multi-agent/routing-internal/rule-based-routing.ts
|
|
2500
|
+
var ruleBasedRouting = {
|
|
2501
|
+
name: "rule-based",
|
|
2502
|
+
async route(state, config) {
|
|
2503
|
+
if (!config.routingFn) {
|
|
2504
|
+
throw new Error("Rule-based routing requires a custom routing function");
|
|
2505
|
+
}
|
|
2506
|
+
return config.routingFn(state);
|
|
2507
|
+
}
|
|
2508
|
+
};
|
|
2509
|
+
|
|
2510
|
+
// src/multi-agent/routing-internal/skill-based-routing.ts
|
|
2511
|
+
function scoreWorkers(state, taskContent) {
|
|
2512
|
+
return Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => {
|
|
2513
|
+
const skillMatches = caps.skills.filter(
|
|
2514
|
+
(skill) => taskContent.includes(skill.toLowerCase())
|
|
2515
|
+
).length;
|
|
2516
|
+
const toolMatches = caps.tools.filter(
|
|
2517
|
+
(tool) => taskContent.includes(tool.toLowerCase())
|
|
2518
|
+
).length;
|
|
2375
2519
|
return {
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
confidence: 1,
|
|
2380
|
-
strategy: "round-robin",
|
|
2381
|
-
timestamp: Date.now()
|
|
2520
|
+
id,
|
|
2521
|
+
score: skillMatches * 2 + toolMatches,
|
|
2522
|
+
skills: caps.skills
|
|
2382
2523
|
};
|
|
2524
|
+
}).filter((worker) => worker.score > 0).sort((a, b) => b.score - a.score);
|
|
2525
|
+
}
|
|
2526
|
+
function createFallbackDecision(state) {
|
|
2527
|
+
const firstAvailable = Object.entries(state.workers).find(([_, caps]) => caps.available);
|
|
2528
|
+
if (!firstAvailable) {
|
|
2529
|
+
throw new Error("No available workers for skill-based routing");
|
|
2383
2530
|
}
|
|
2384
|
-
|
|
2531
|
+
return {
|
|
2532
|
+
targetAgent: firstAvailable[0],
|
|
2533
|
+
targetAgents: null,
|
|
2534
|
+
reasoning: "No skill matches found, using first available worker",
|
|
2535
|
+
confidence: 0.5,
|
|
2536
|
+
strategy: "skill-based",
|
|
2537
|
+
timestamp: Date.now()
|
|
2538
|
+
};
|
|
2539
|
+
}
|
|
2385
2540
|
var skillBasedRouting = {
|
|
2386
2541
|
name: "skill-based",
|
|
2387
|
-
async route(state,
|
|
2388
|
-
const
|
|
2389
|
-
const
|
|
2390
|
-
const workerScores = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => {
|
|
2391
|
-
const skillMatches = caps.skills.filter(
|
|
2392
|
-
(skill) => taskContent.includes(skill.toLowerCase())
|
|
2393
|
-
).length;
|
|
2394
|
-
const toolMatches = caps.tools.filter(
|
|
2395
|
-
(tool) => taskContent.includes(tool.toLowerCase())
|
|
2396
|
-
).length;
|
|
2397
|
-
const score = skillMatches * 2 + toolMatches;
|
|
2398
|
-
return { id, score, skills: caps.skills, tools: caps.tools };
|
|
2399
|
-
}).filter((w) => w.score > 0).sort((a, b) => b.score - a.score);
|
|
2542
|
+
async route(state, _config) {
|
|
2543
|
+
const taskContent = getTaskContent(state).toLowerCase();
|
|
2544
|
+
const workerScores = scoreWorkers(state, taskContent);
|
|
2400
2545
|
if (workerScores.length === 0) {
|
|
2401
|
-
|
|
2402
|
-
if (!firstAvailable) {
|
|
2403
|
-
throw new Error("No available workers for skill-based routing");
|
|
2404
|
-
}
|
|
2405
|
-
return {
|
|
2406
|
-
targetAgent: firstAvailable[0],
|
|
2407
|
-
targetAgents: null,
|
|
2408
|
-
reasoning: "No skill matches found, using first available worker",
|
|
2409
|
-
confidence: 0.5,
|
|
2410
|
-
strategy: "skill-based",
|
|
2411
|
-
timestamp: Date.now()
|
|
2412
|
-
};
|
|
2546
|
+
return createFallbackDecision(state);
|
|
2413
2547
|
}
|
|
2414
2548
|
const best = workerScores[0];
|
|
2415
2549
|
const confidence = Math.min(best.score / 5, 1);
|
|
@@ -2423,69 +2557,39 @@ var skillBasedRouting = {
|
|
|
2423
2557
|
};
|
|
2424
2558
|
}
|
|
2425
2559
|
};
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
const avgWorkload = availableWorkers.reduce((sum, w) => sum + w.workload, 0) / availableWorkers.length;
|
|
2435
|
-
const confidence = targetWorker.workload === 0 ? 1 : Math.max(0.5, 1 - targetWorker.workload / (avgWorkload * 2));
|
|
2436
|
-
return {
|
|
2437
|
-
targetAgent: targetWorker.id,
|
|
2438
|
-
targetAgents: null,
|
|
2439
|
-
reasoning: `Lowest workload: ${targetWorker.workload} tasks (avg: ${avgWorkload.toFixed(1)})`,
|
|
2440
|
-
confidence,
|
|
2441
|
-
strategy: "load-balanced",
|
|
2442
|
-
timestamp: Date.now()
|
|
2443
|
-
};
|
|
2444
|
-
}
|
|
2445
|
-
};
|
|
2446
|
-
var ruleBasedRouting = {
|
|
2447
|
-
name: "rule-based",
|
|
2448
|
-
async route(state, config) {
|
|
2449
|
-
if (!config.routingFn) {
|
|
2450
|
-
throw new Error("Rule-based routing requires a custom routing function");
|
|
2451
|
-
}
|
|
2452
|
-
return await config.routingFn(state);
|
|
2453
|
-
}
|
|
2560
|
+
|
|
2561
|
+
// src/multi-agent/routing.ts
|
|
2562
|
+
var routingStrategies = {
|
|
2563
|
+
"llm-based": llmBasedRouting,
|
|
2564
|
+
"round-robin": roundRobinRouting,
|
|
2565
|
+
"skill-based": skillBasedRouting,
|
|
2566
|
+
"load-balanced": loadBalancedRouting,
|
|
2567
|
+
"rule-based": ruleBasedRouting
|
|
2454
2568
|
};
|
|
2455
2569
|
function getRoutingStrategy(name) {
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
return llmBasedRouting;
|
|
2459
|
-
case "round-robin":
|
|
2460
|
-
return roundRobinRouting;
|
|
2461
|
-
case "skill-based":
|
|
2462
|
-
return skillBasedRouting;
|
|
2463
|
-
case "load-balanced":
|
|
2464
|
-
return loadBalancedRouting;
|
|
2465
|
-
case "rule-based":
|
|
2466
|
-
return ruleBasedRouting;
|
|
2467
|
-
default:
|
|
2468
|
-
throw new Error(`Unknown routing strategy: ${name}`);
|
|
2570
|
+
if (!Object.hasOwn(routingStrategies, name)) {
|
|
2571
|
+
throw new Error(`Unknown routing strategy: ${name}`);
|
|
2469
2572
|
}
|
|
2573
|
+
return routingStrategies[name];
|
|
2470
2574
|
}
|
|
2471
2575
|
|
|
2472
2576
|
// src/multi-agent/utils.ts
|
|
2473
|
-
var
|
|
2474
|
-
function
|
|
2577
|
+
var logger2 = createPatternLogger("agentforge:patterns:multi-agent:utils");
|
|
2578
|
+
function isRecord2(value) {
|
|
2475
2579
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2476
2580
|
}
|
|
2477
2581
|
function toRunnableConfig(config) {
|
|
2478
|
-
if (!
|
|
2582
|
+
if (!isRecord2(config)) {
|
|
2479
2583
|
return void 0;
|
|
2480
2584
|
}
|
|
2481
2585
|
return config;
|
|
2482
2586
|
}
|
|
2483
2587
|
function getReActResultShape(value) {
|
|
2484
|
-
if (!
|
|
2588
|
+
if (!isRecord2(value)) {
|
|
2485
2589
|
return {};
|
|
2486
2590
|
}
|
|
2487
|
-
const messages = Array.isArray(value.messages) ? value.messages.filter((message) =>
|
|
2488
|
-
const actions = Array.isArray(value.actions) ? value.actions.filter((action) =>
|
|
2591
|
+
const messages = Array.isArray(value.messages) ? value.messages.filter((message) => isRecord2(message)) : void 0;
|
|
2592
|
+
const actions = Array.isArray(value.actions) ? value.actions.filter((action) => isRecord2(action)) : void 0;
|
|
2489
2593
|
return {
|
|
2490
2594
|
messages,
|
|
2491
2595
|
actions,
|
|
@@ -2504,7 +2608,7 @@ function safeSerializeContent(content) {
|
|
|
2504
2608
|
if (typeof part === "string") {
|
|
2505
2609
|
return part;
|
|
2506
2610
|
}
|
|
2507
|
-
if (
|
|
2611
|
+
if (isRecord2(part) && typeof part.text === "string" && part.text.length > 0) {
|
|
2508
2612
|
return part.text;
|
|
2509
2613
|
}
|
|
2510
2614
|
try {
|
|
@@ -2549,17 +2653,17 @@ function isReActAgent(obj) {
|
|
|
2549
2653
|
function wrapReActAgent(workerId, agent, verbose = false) {
|
|
2550
2654
|
return async (state, config) => {
|
|
2551
2655
|
try {
|
|
2552
|
-
|
|
2656
|
+
logger2.debug("Wrapping ReAct agent execution", { workerId });
|
|
2553
2657
|
const runnableConfig = toRunnableConfig(config);
|
|
2554
2658
|
const currentAssignment = state.activeAssignments.find(
|
|
2555
2659
|
(assignment) => assignment.workerId === workerId && !state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
|
|
2556
2660
|
);
|
|
2557
2661
|
if (!currentAssignment) {
|
|
2558
|
-
|
|
2662
|
+
logger2.debug("No active assignment found", { workerId });
|
|
2559
2663
|
return {};
|
|
2560
2664
|
}
|
|
2561
2665
|
const task = currentAssignment.task;
|
|
2562
|
-
|
|
2666
|
+
logger2.debug("Extracted task from assignment", {
|
|
2563
2667
|
workerId,
|
|
2564
2668
|
assignmentId: currentAssignment.id,
|
|
2565
2669
|
taskPreview: task.substring(0, 100) + (task.length > 100 ? "..." : "")
|
|
@@ -2572,7 +2676,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2572
2676
|
thread_id: workerThreadId
|
|
2573
2677
|
}
|
|
2574
2678
|
} : runnableConfig;
|
|
2575
|
-
|
|
2679
|
+
logger2.debug("Invoking ReAct agent with worker-specific config", {
|
|
2576
2680
|
workerId,
|
|
2577
2681
|
...runnableConfig?.configurable?.thread_id !== void 0 ? { parentThreadId: String(runnableConfig.configurable.thread_id) } : {},
|
|
2578
2682
|
...workerThreadId ? { workerThreadId } : {},
|
|
@@ -2587,13 +2691,13 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2587
2691
|
);
|
|
2588
2692
|
const resultShape = getReActResultShape(result);
|
|
2589
2693
|
const response = extractResponse(resultShape);
|
|
2590
|
-
|
|
2694
|
+
logger2.debug("Received response from ReAct agent", {
|
|
2591
2695
|
workerId,
|
|
2592
2696
|
responsePreview: response.substring(0, 100) + (response.length > 100 ? "..." : "")
|
|
2593
2697
|
});
|
|
2594
2698
|
const uniqueTools = extractToolsUsed(resultShape);
|
|
2595
2699
|
if (uniqueTools.length > 0) {
|
|
2596
|
-
|
|
2700
|
+
logger2.debug("Tools used by ReAct agent", { workerId, tools: uniqueTools });
|
|
2597
2701
|
}
|
|
2598
2702
|
const taskResult = {
|
|
2599
2703
|
assignmentId: currentAssignment.id,
|
|
@@ -2612,7 +2716,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2612
2716
|
};
|
|
2613
2717
|
} catch (error) {
|
|
2614
2718
|
const errorMessage = handleNodeError(error, `react-agent:${workerId}`, verbose);
|
|
2615
|
-
|
|
2719
|
+
logger2.error("Error in ReAct agent execution", {
|
|
2616
2720
|
workerId,
|
|
2617
2721
|
error: errorMessage
|
|
2618
2722
|
});
|
|
@@ -2645,7 +2749,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2645
2749
|
// src/multi-agent/nodes/shared.ts
|
|
2646
2750
|
import { HumanMessage as HumanMessage6, SystemMessage as SystemMessage6 } from "@langchain/core/messages";
|
|
2647
2751
|
import { toLangChainTools as toLangChainTools2 } from "@agentforge/core";
|
|
2648
|
-
var
|
|
2752
|
+
var logger3 = createPatternLogger("agentforge:patterns:multi-agent:nodes");
|
|
2649
2753
|
function createGeneratedId(prefix) {
|
|
2650
2754
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
2651
2755
|
}
|
|
@@ -2697,7 +2801,7 @@ function serializeModelContent(content) {
|
|
|
2697
2801
|
const error = new Error(
|
|
2698
2802
|
"Failed to serialize model content: JSON.stringify returned undefined"
|
|
2699
2803
|
);
|
|
2700
|
-
|
|
2804
|
+
logger3.error("Model content serialization failed", {
|
|
2701
2805
|
errorMessage: error.message,
|
|
2702
2806
|
contentType: content === null ? "null" : typeof content
|
|
2703
2807
|
});
|
|
@@ -2706,7 +2810,7 @@ function serializeModelContent(content) {
|
|
|
2706
2810
|
return serialized;
|
|
2707
2811
|
} catch (error) {
|
|
2708
2812
|
const normalizedError = error instanceof Error ? error : new Error("Unknown error during model content serialization");
|
|
2709
|
-
|
|
2813
|
+
logger3.error("Model content serialization threw an error", {
|
|
2710
2814
|
errorMessage: normalizedError.message,
|
|
2711
2815
|
contentType: content === null ? "null" : typeof content
|
|
2712
2816
|
});
|
|
@@ -2749,16 +2853,16 @@ function createAggregatorNode(config = {}) {
|
|
|
2749
2853
|
} = config;
|
|
2750
2854
|
return async (state) => {
|
|
2751
2855
|
try {
|
|
2752
|
-
|
|
2856
|
+
logger3.info("Aggregator node executing", {
|
|
2753
2857
|
completedTasks: state.completedTasks.length,
|
|
2754
2858
|
successfulTasks: state.completedTasks.filter((task) => task.success).length,
|
|
2755
2859
|
failedTasks: state.completedTasks.filter((task) => !task.success).length
|
|
2756
2860
|
});
|
|
2757
|
-
|
|
2861
|
+
logger3.debug("Combining results from workers");
|
|
2758
2862
|
if (aggregateFn) {
|
|
2759
|
-
|
|
2863
|
+
logger3.debug("Using custom aggregation function");
|
|
2760
2864
|
const response2 = await aggregateFn(state);
|
|
2761
|
-
|
|
2865
|
+
logger3.info("Custom aggregation complete", {
|
|
2762
2866
|
responseLength: response2.length
|
|
2763
2867
|
});
|
|
2764
2868
|
return {
|
|
@@ -2767,16 +2871,16 @@ function createAggregatorNode(config = {}) {
|
|
|
2767
2871
|
};
|
|
2768
2872
|
}
|
|
2769
2873
|
if (state.completedTasks.length === 0) {
|
|
2770
|
-
|
|
2874
|
+
logger3.warn("No completed tasks to aggregate");
|
|
2771
2875
|
return {
|
|
2772
2876
|
response: "No tasks were completed.",
|
|
2773
2877
|
status: "completed"
|
|
2774
2878
|
};
|
|
2775
2879
|
}
|
|
2776
2880
|
if (!model) {
|
|
2777
|
-
|
|
2881
|
+
logger3.debug("No model provided, concatenating results");
|
|
2778
2882
|
const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
|
|
2779
|
-
|
|
2883
|
+
logger3.info("Simple concatenation complete", {
|
|
2780
2884
|
resultLength: combinedResults.length
|
|
2781
2885
|
});
|
|
2782
2886
|
return {
|
|
@@ -2784,27 +2888,27 @@ function createAggregatorNode(config = {}) {
|
|
|
2784
2888
|
status: "completed"
|
|
2785
2889
|
};
|
|
2786
2890
|
}
|
|
2787
|
-
|
|
2891
|
+
logger3.debug("Using LLM for intelligent aggregation", {
|
|
2788
2892
|
taskCount: state.completedTasks.length
|
|
2789
2893
|
});
|
|
2790
2894
|
const messages = createPromptMessages(systemPrompt, createAggregationPrompt(state));
|
|
2791
|
-
|
|
2895
|
+
logger3.debug("Invoking aggregation LLM");
|
|
2792
2896
|
const response = await model.invoke(messages);
|
|
2793
2897
|
const aggregatedResponse = serializeModelContent(response.content);
|
|
2794
|
-
|
|
2898
|
+
logger3.info("Aggregation complete", {
|
|
2795
2899
|
responseLength: aggregatedResponse.length
|
|
2796
2900
|
});
|
|
2797
|
-
|
|
2901
|
+
logger3.debug("Aggregation response details", {
|
|
2798
2902
|
responseLength: aggregatedResponse.length
|
|
2799
2903
|
});
|
|
2800
|
-
|
|
2904
|
+
logger3.debug("Aggregation complete");
|
|
2801
2905
|
return {
|
|
2802
2906
|
response: aggregatedResponse,
|
|
2803
2907
|
status: "completed"
|
|
2804
2908
|
};
|
|
2805
2909
|
} catch (error) {
|
|
2806
2910
|
const errorMessage = handleNodeError(error, "aggregator", false);
|
|
2807
|
-
|
|
2911
|
+
logger3.error("Aggregator node error", {
|
|
2808
2912
|
error: errorMessage,
|
|
2809
2913
|
...error instanceof Error && error.stack ? { stack: error.stack } : {},
|
|
2810
2914
|
completedTasks: state.completedTasks.length
|
|
@@ -2828,7 +2932,7 @@ function incrementAssignedWorkerLoads(state, assignments) {
|
|
|
2828
2932
|
for (const assignment of assignments) {
|
|
2829
2933
|
const worker = updatedWorkers[assignment.workerId];
|
|
2830
2934
|
if (!worker) {
|
|
2831
|
-
|
|
2935
|
+
logger3.error("Worker not found in state", {
|
|
2832
2936
|
workerId: assignment.workerId,
|
|
2833
2937
|
availableWorkers: Object.keys(updatedWorkers)
|
|
2834
2938
|
});
|
|
@@ -2847,45 +2951,45 @@ function createSupervisorNode(config) {
|
|
|
2847
2951
|
const { strategy, maxIterations = 10 } = config;
|
|
2848
2952
|
return async (state) => {
|
|
2849
2953
|
try {
|
|
2850
|
-
|
|
2954
|
+
logger3.info("Supervisor node executing", {
|
|
2851
2955
|
iteration: state.iteration,
|
|
2852
2956
|
maxIterations,
|
|
2853
2957
|
activeAssignments: state.activeAssignments.length,
|
|
2854
2958
|
completedTasks: state.completedTasks.length
|
|
2855
2959
|
});
|
|
2856
|
-
|
|
2960
|
+
logger3.debug(`Routing iteration ${state.iteration}/${maxIterations}`);
|
|
2857
2961
|
if (state.iteration >= maxIterations) {
|
|
2858
|
-
|
|
2962
|
+
logger3.warn("Max iterations reached", {
|
|
2859
2963
|
iteration: state.iteration,
|
|
2860
2964
|
maxIterations
|
|
2861
2965
|
});
|
|
2862
|
-
|
|
2966
|
+
logger3.debug("Max iterations reached, moving to aggregation");
|
|
2863
2967
|
return {
|
|
2864
2968
|
status: "aggregating",
|
|
2865
2969
|
currentAgent: "aggregator"
|
|
2866
2970
|
};
|
|
2867
2971
|
}
|
|
2868
2972
|
const allCompleted = allAssignmentsCompleted(state);
|
|
2869
|
-
|
|
2973
|
+
logger3.debug("Checking task completion", {
|
|
2870
2974
|
activeAssignments: state.activeAssignments.length,
|
|
2871
2975
|
completedTasks: state.completedTasks.length,
|
|
2872
2976
|
allCompleted
|
|
2873
2977
|
});
|
|
2874
2978
|
if (allCompleted && state.activeAssignments.length > 0) {
|
|
2875
|
-
|
|
2979
|
+
logger3.info("All tasks completed, moving to aggregation", {
|
|
2876
2980
|
completedCount: state.completedTasks.length
|
|
2877
2981
|
});
|
|
2878
|
-
|
|
2982
|
+
logger3.debug("All tasks completed, moving to aggregation");
|
|
2879
2983
|
return {
|
|
2880
2984
|
status: "aggregating",
|
|
2881
2985
|
currentAgent: "aggregator"
|
|
2882
2986
|
};
|
|
2883
2987
|
}
|
|
2884
|
-
|
|
2988
|
+
logger3.debug("Getting routing strategy", { strategy });
|
|
2885
2989
|
const routingImpl = getRoutingStrategy(strategy);
|
|
2886
2990
|
const decision = await routingImpl.route(state, config);
|
|
2887
2991
|
const targetAgents = decision.targetAgents && decision.targetAgents.length > 0 ? decision.targetAgents : decision.targetAgent ? [decision.targetAgent] : [];
|
|
2888
|
-
|
|
2992
|
+
logger3.debug("Target agents determined", {
|
|
2889
2993
|
targetAgents,
|
|
2890
2994
|
isParallel: targetAgents.length > 1,
|
|
2891
2995
|
decision: {
|
|
@@ -2894,30 +2998,30 @@ function createSupervisorNode(config) {
|
|
|
2894
2998
|
}
|
|
2895
2999
|
});
|
|
2896
3000
|
if (targetAgents.length === 0) {
|
|
2897
|
-
|
|
3001
|
+
logger3.error("No target agents specified in routing decision");
|
|
2898
3002
|
throw new Error("Routing decision must specify at least one target agent");
|
|
2899
3003
|
}
|
|
2900
3004
|
if (targetAgents.length === 1) {
|
|
2901
|
-
|
|
3005
|
+
logger3.info("Routing to single agent", {
|
|
2902
3006
|
targetAgent: targetAgents[0],
|
|
2903
3007
|
reasoning: decision.reasoning,
|
|
2904
3008
|
confidence: decision.confidence
|
|
2905
3009
|
});
|
|
2906
|
-
|
|
3010
|
+
logger3.debug(`Routing to ${targetAgents[0]}: ${decision.reasoning}`);
|
|
2907
3011
|
} else {
|
|
2908
|
-
|
|
3012
|
+
logger3.info("Routing to multiple agents in parallel", {
|
|
2909
3013
|
targetAgents,
|
|
2910
3014
|
count: targetAgents.length,
|
|
2911
3015
|
reasoning: decision.reasoning,
|
|
2912
3016
|
confidence: decision.confidence
|
|
2913
3017
|
});
|
|
2914
|
-
|
|
3018
|
+
logger3.debug(
|
|
2915
3019
|
`Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`
|
|
2916
3020
|
);
|
|
2917
3021
|
}
|
|
2918
3022
|
const task = getLatestTaskContent(state);
|
|
2919
3023
|
const assignments = createTaskAssignments(targetAgents, task);
|
|
2920
|
-
|
|
3024
|
+
logger3.debug("Created task assignments", {
|
|
2921
3025
|
assignmentCount: assignments.length,
|
|
2922
3026
|
assignments: assignments.map((assignment) => ({
|
|
2923
3027
|
id: assignment.id,
|
|
@@ -2927,7 +3031,7 @@ function createSupervisorNode(config) {
|
|
|
2927
3031
|
});
|
|
2928
3032
|
const messages = createAssignmentMessages(assignments);
|
|
2929
3033
|
const updatedWorkers = incrementAssignedWorkerLoads(state, assignments);
|
|
2930
|
-
|
|
3034
|
+
logger3.info("Supervisor routing complete", {
|
|
2931
3035
|
currentAgent: targetAgents.join(","),
|
|
2932
3036
|
status: "executing",
|
|
2933
3037
|
assignmentCount: assignments.length,
|
|
@@ -2945,7 +3049,7 @@ function createSupervisorNode(config) {
|
|
|
2945
3049
|
};
|
|
2946
3050
|
} catch (error) {
|
|
2947
3051
|
const errorMessage = handleNodeError(error, "supervisor", false);
|
|
2948
|
-
|
|
3052
|
+
logger3.error("Supervisor node error", {
|
|
2949
3053
|
error: errorMessage,
|
|
2950
3054
|
...error instanceof Error && error.stack ? { stack: error.stack } : {},
|
|
2951
3055
|
iteration: state.iteration
|
|
@@ -2973,7 +3077,7 @@ async function invokeWorkerModel(model, config, assignment) {
|
|
|
2973
3077
|
);
|
|
2974
3078
|
let modelToUse = model;
|
|
2975
3079
|
if (config.tools && config.tools.length > 0 && model.bindTools) {
|
|
2976
|
-
|
|
3080
|
+
logger3.debug("Binding tools to model", {
|
|
2977
3081
|
workerId: config.id,
|
|
2978
3082
|
toolCount: config.tools.length,
|
|
2979
3083
|
toolNames: config.tools.map((tool) => tool.metadata.name)
|
|
@@ -2982,15 +3086,15 @@ async function invokeWorkerModel(model, config, assignment) {
|
|
|
2982
3086
|
convertWorkerToolsForLangChain(config.tools)
|
|
2983
3087
|
);
|
|
2984
3088
|
}
|
|
2985
|
-
|
|
3089
|
+
logger3.debug("Invoking LLM", { workerId: config.id });
|
|
2986
3090
|
const response = await modelToUse.invoke(messages);
|
|
2987
3091
|
const result = serializeModelContent(response.content);
|
|
2988
|
-
|
|
3092
|
+
logger3.info("Worker task completed", {
|
|
2989
3093
|
workerId: config.id,
|
|
2990
3094
|
assignmentId: assignment.id,
|
|
2991
3095
|
resultLength: result.length
|
|
2992
3096
|
});
|
|
2993
|
-
|
|
3097
|
+
logger3.debug("Worker result details", {
|
|
2994
3098
|
workerId: config.id,
|
|
2995
3099
|
assignmentId: assignment.id,
|
|
2996
3100
|
resultLength: result.length
|
|
@@ -3025,7 +3129,7 @@ async function invokeWorkerModel(model, config, assignment) {
|
|
|
3025
3129
|
function getStateWorkerOrThrow(state, workerId) {
|
|
3026
3130
|
const currentWorker = state.workers[workerId];
|
|
3027
3131
|
if (!currentWorker) {
|
|
3028
|
-
|
|
3132
|
+
logger3.error("Attempted to decrement workload for unknown worker", {
|
|
3029
3133
|
workerId,
|
|
3030
3134
|
availableWorkers: Object.keys(state.workers)
|
|
3031
3135
|
});
|
|
@@ -3041,7 +3145,7 @@ function resolvePreviousWorkload(workerId, currentWorker, workerFromExecution) {
|
|
|
3041
3145
|
if (typeof currentWorker.currentWorkload === "number" && Number.isFinite(currentWorker.currentWorkload)) {
|
|
3042
3146
|
return currentWorker.currentWorkload;
|
|
3043
3147
|
}
|
|
3044
|
-
|
|
3148
|
+
logger3.error("Worker workload is not a valid number; cannot decrement", {
|
|
3045
3149
|
workerId,
|
|
3046
3150
|
workloadFromState: currentWorker.currentWorkload,
|
|
3047
3151
|
...typeof workloadFromWorker === "number" ? { workloadFromWorker } : {}
|
|
@@ -3076,7 +3180,7 @@ function mergeWorkersWithDecrement(state, workerId, executionResult) {
|
|
|
3076
3180
|
...baseWorkers,
|
|
3077
3181
|
[workerId]: updatedWorker
|
|
3078
3182
|
};
|
|
3079
|
-
|
|
3183
|
+
logger3.debug("Worker workload decremented", {
|
|
3080
3184
|
workerId,
|
|
3081
3185
|
previousWorkload,
|
|
3082
3186
|
newWorkload: updatedWorkers[workerId].currentWorkload,
|
|
@@ -3094,7 +3198,7 @@ function decrementWorkerOnError(state, workerId) {
|
|
|
3094
3198
|
currentWorkload: Math.max(0, previousWorkload - 1)
|
|
3095
3199
|
}
|
|
3096
3200
|
};
|
|
3097
|
-
|
|
3201
|
+
logger3.debug("Worker workload decremented (error path)", {
|
|
3098
3202
|
workerId,
|
|
3099
3203
|
previousWorkload,
|
|
3100
3204
|
newWorkload: updatedWorkers[workerId].currentWorkload
|
|
@@ -3102,7 +3206,7 @@ function decrementWorkerOnError(state, workerId) {
|
|
|
3102
3206
|
return updatedWorkers;
|
|
3103
3207
|
}
|
|
3104
3208
|
function createErrorTaskResult(assignment, workerId, errorMessage) {
|
|
3105
|
-
|
|
3209
|
+
logger3.warn("Creating error result for assignment", {
|
|
3106
3210
|
workerId,
|
|
3107
3211
|
assignmentId: assignment.id
|
|
3108
3212
|
});
|
|
@@ -3119,47 +3223,47 @@ function createWorkerNode(config) {
|
|
|
3119
3223
|
const { id, model, executeFn, agent } = config;
|
|
3120
3224
|
return async (state, runConfig) => {
|
|
3121
3225
|
try {
|
|
3122
|
-
|
|
3226
|
+
logger3.info("Worker node executing", {
|
|
3123
3227
|
workerId: id,
|
|
3124
3228
|
iteration: state.iteration,
|
|
3125
3229
|
activeAssignments: state.activeAssignments.length
|
|
3126
3230
|
});
|
|
3127
3231
|
const currentAssignment = findCurrentAssignment(state, id);
|
|
3128
3232
|
if (!currentAssignment) {
|
|
3129
|
-
|
|
3233
|
+
logger3.debug("No active assignment found for worker", {
|
|
3130
3234
|
workerId: id,
|
|
3131
3235
|
totalActiveAssignments: state.activeAssignments.length,
|
|
3132
3236
|
completedTasks: state.completedTasks.length
|
|
3133
3237
|
});
|
|
3134
3238
|
return {};
|
|
3135
3239
|
}
|
|
3136
|
-
|
|
3240
|
+
logger3.info("Worker processing assignment", {
|
|
3137
3241
|
workerId: id,
|
|
3138
3242
|
assignmentId: currentAssignment.id,
|
|
3139
3243
|
taskLength: currentAssignment.task.length
|
|
3140
3244
|
});
|
|
3141
|
-
|
|
3245
|
+
logger3.debug("Worker assignment details", {
|
|
3142
3246
|
workerId: id,
|
|
3143
3247
|
assignmentId: currentAssignment.id,
|
|
3144
3248
|
taskLength: currentAssignment.task.length
|
|
3145
3249
|
});
|
|
3146
3250
|
let executionResult;
|
|
3147
3251
|
if (executeFn) {
|
|
3148
|
-
|
|
3252
|
+
logger3.debug("Using custom execution function", { workerId: id });
|
|
3149
3253
|
executionResult = await executeFn(state, runConfig);
|
|
3150
3254
|
} else if (agent && isReActAgent(agent)) {
|
|
3151
|
-
|
|
3255
|
+
logger3.debug("Using ReAct agent", { workerId: id });
|
|
3152
3256
|
const wrappedFn = wrapReActAgent(id, agent, config.verbose ?? false);
|
|
3153
3257
|
executionResult = await wrappedFn(state, runConfig);
|
|
3154
3258
|
} else if (model) {
|
|
3155
|
-
|
|
3259
|
+
logger3.debug("Using default LLM execution", {
|
|
3156
3260
|
workerId: id,
|
|
3157
3261
|
hasTools: (config.tools ?? []).length > 0,
|
|
3158
3262
|
toolCount: (config.tools ?? []).length
|
|
3159
3263
|
});
|
|
3160
3264
|
executionResult = await invokeWorkerModel(model, config, currentAssignment);
|
|
3161
3265
|
} else {
|
|
3162
|
-
|
|
3266
|
+
logger3.error("Worker missing required configuration", { workerId: id });
|
|
3163
3267
|
throw new Error(
|
|
3164
3268
|
`Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
|
|
3165
3269
|
);
|
|
@@ -3171,7 +3275,7 @@ function createWorkerNode(config) {
|
|
|
3171
3275
|
};
|
|
3172
3276
|
} catch (error) {
|
|
3173
3277
|
const errorMessage = handleNodeError(error, `worker:${id}`, false);
|
|
3174
|
-
|
|
3278
|
+
logger3.error("Worker node error", {
|
|
3175
3279
|
workerId: id,
|
|
3176
3280
|
error: errorMessage
|
|
3177
3281
|
});
|
|
@@ -3180,7 +3284,7 @@ function createWorkerNode(config) {
|
|
|
3180
3284
|
updatedWorkers = decrementWorkerOnError(state, id);
|
|
3181
3285
|
} catch (workloadError) {
|
|
3182
3286
|
const workloadErrorMessage = workloadError instanceof Error ? workloadError.message : String(workloadError);
|
|
3183
|
-
|
|
3287
|
+
logger3.error("Worker error handling failed", {
|
|
3184
3288
|
workerId: id,
|
|
3185
3289
|
error: workloadErrorMessage
|
|
3186
3290
|
});
|
|
@@ -3200,7 +3304,7 @@ function createWorkerNode(config) {
|
|
|
3200
3304
|
workers: updatedWorkers
|
|
3201
3305
|
};
|
|
3202
3306
|
}
|
|
3203
|
-
|
|
3307
|
+
logger3.error("No assignment found for error handling", { workerId: id });
|
|
3204
3308
|
return {
|
|
3205
3309
|
status: "failed",
|
|
3206
3310
|
error: errorMessage,
|
|
@@ -3212,7 +3316,7 @@ function createWorkerNode(config) {
|
|
|
3212
3316
|
|
|
3213
3317
|
// src/multi-agent/agent.ts
|
|
3214
3318
|
import { StateGraph as StateGraph4, END as END4 } from "@langchain/langgraph";
|
|
3215
|
-
var
|
|
3319
|
+
var logger4 = createPatternLogger("agentforge:patterns:multi-agent:system");
|
|
3216
3320
|
function getToolName(tool) {
|
|
3217
3321
|
if (!tool || typeof tool !== "object") {
|
|
3218
3322
|
return "unknown";
|
|
@@ -3237,13 +3341,6 @@ function createMultiAgentSystem(config) {
|
|
|
3237
3341
|
} = config;
|
|
3238
3342
|
const workflow = new StateGraph4(MultiAgentState);
|
|
3239
3343
|
const supervisorConfig = { ...supervisor, maxIterations, verbose };
|
|
3240
|
-
if (supervisor.model) {
|
|
3241
|
-
let configuredModel = supervisor.model;
|
|
3242
|
-
if (supervisor.strategy === "llm-based") {
|
|
3243
|
-
configuredModel = configuredModel.withStructuredOutput(RoutingDecisionSchema);
|
|
3244
|
-
}
|
|
3245
|
-
supervisorConfig.model = configuredModel;
|
|
3246
|
-
}
|
|
3247
3344
|
const supervisorNode = createSupervisorNode(supervisorConfig);
|
|
3248
3345
|
workflow.addNode("supervisor", supervisorNode);
|
|
3249
3346
|
const workerIds = [];
|
|
@@ -3263,46 +3360,46 @@ function createMultiAgentSystem(config) {
|
|
|
3263
3360
|
});
|
|
3264
3361
|
workflow.addNode("aggregator", aggregatorNode);
|
|
3265
3362
|
const supervisorRouter = (state) => {
|
|
3266
|
-
|
|
3363
|
+
logger4.debug("Supervisor router executing", {
|
|
3267
3364
|
status: state.status,
|
|
3268
3365
|
...state.currentAgent ? { currentAgent: state.currentAgent } : {},
|
|
3269
3366
|
iteration: state.iteration
|
|
3270
3367
|
});
|
|
3271
3368
|
if (state.status === "completed" || state.status === "failed") {
|
|
3272
|
-
|
|
3369
|
+
logger4.info("Supervisor router: ending workflow", { status: state.status });
|
|
3273
3370
|
return END4;
|
|
3274
3371
|
}
|
|
3275
3372
|
if (state.status === "aggregating") {
|
|
3276
|
-
|
|
3373
|
+
logger4.info("Supervisor router: routing to aggregator");
|
|
3277
3374
|
return "aggregator";
|
|
3278
3375
|
}
|
|
3279
3376
|
if (state.currentAgent && state.currentAgent !== "supervisor") {
|
|
3280
3377
|
if (state.currentAgent.includes(",")) {
|
|
3281
3378
|
const agents = state.currentAgent.split(",").map((a) => a.trim());
|
|
3282
|
-
|
|
3379
|
+
logger4.info("Supervisor router: parallel routing", {
|
|
3283
3380
|
agents,
|
|
3284
3381
|
count: agents.length
|
|
3285
3382
|
});
|
|
3286
3383
|
return agents;
|
|
3287
3384
|
}
|
|
3288
|
-
|
|
3385
|
+
logger4.info("Supervisor router: single agent routing", {
|
|
3289
3386
|
targetAgent: state.currentAgent
|
|
3290
3387
|
});
|
|
3291
3388
|
return state.currentAgent;
|
|
3292
3389
|
}
|
|
3293
|
-
|
|
3390
|
+
logger4.debug("Supervisor router: staying at supervisor");
|
|
3294
3391
|
return "supervisor";
|
|
3295
3392
|
};
|
|
3296
3393
|
const workerRouter = (state) => {
|
|
3297
|
-
|
|
3394
|
+
logger4.debug("Worker router executing", {
|
|
3298
3395
|
iteration: state.iteration,
|
|
3299
3396
|
completedTasks: state.completedTasks.length
|
|
3300
3397
|
});
|
|
3301
|
-
|
|
3398
|
+
logger4.debug("Worker router: returning to supervisor");
|
|
3302
3399
|
return "supervisor";
|
|
3303
3400
|
};
|
|
3304
3401
|
const aggregatorRouter = (state) => {
|
|
3305
|
-
|
|
3402
|
+
logger4.info("Aggregator router: ending workflow", {
|
|
3306
3403
|
completedTasks: state.completedTasks.length,
|
|
3307
3404
|
status: state.status
|
|
3308
3405
|
});
|