@agentforge/patterns 0.16.31 → 0.16.32
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 +160 -99
- package/dist/index.d.cts +0 -9
- package/dist/index.d.ts +0 -9
- package/dist/index.js +160 -99
- 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
|
|
|
@@ -2393,6 +2393,82 @@ var MultiAgentState = (0, import_core7.createStateAnnotation)(MultiAgentStateCon
|
|
|
2393
2393
|
|
|
2394
2394
|
// src/multi-agent/routing.ts
|
|
2395
2395
|
var import_messages5 = require("@langchain/core/messages");
|
|
2396
|
+
var logger = createPatternLogger("agentforge:patterns:multi-agent:routing");
|
|
2397
|
+
function hasStructuredOutput(model) {
|
|
2398
|
+
return typeof model.withStructuredOutput === "function";
|
|
2399
|
+
}
|
|
2400
|
+
function isRecord(value) {
|
|
2401
|
+
return typeof value === "object" && value !== null;
|
|
2402
|
+
}
|
|
2403
|
+
function isContentCarrier(value) {
|
|
2404
|
+
return isRecord(value) && "content" in value;
|
|
2405
|
+
}
|
|
2406
|
+
function serializeRoutingContent(content) {
|
|
2407
|
+
if (typeof content === "string") {
|
|
2408
|
+
return content;
|
|
2409
|
+
}
|
|
2410
|
+
if (Array.isArray(content)) {
|
|
2411
|
+
const textParts = content.flatMap((part) => {
|
|
2412
|
+
if (typeof part === "string") {
|
|
2413
|
+
return [part];
|
|
2414
|
+
}
|
|
2415
|
+
if (isRecord(part) && typeof part.text === "string") {
|
|
2416
|
+
return [part.text];
|
|
2417
|
+
}
|
|
2418
|
+
return [];
|
|
2419
|
+
});
|
|
2420
|
+
if (textParts.length > 0) {
|
|
2421
|
+
return textParts.join("\n");
|
|
2422
|
+
}
|
|
2423
|
+
return JSON.stringify(content);
|
|
2424
|
+
}
|
|
2425
|
+
return JSON.stringify(content);
|
|
2426
|
+
}
|
|
2427
|
+
function normalizeRoutingDecisionInput(decision) {
|
|
2428
|
+
if (isContentCarrier(decision)) {
|
|
2429
|
+
return JSON.parse(serializeRoutingContent(decision.content));
|
|
2430
|
+
}
|
|
2431
|
+
return decision;
|
|
2432
|
+
}
|
|
2433
|
+
function parseRoutingDecision(decision) {
|
|
2434
|
+
try {
|
|
2435
|
+
return RoutingDecisionSchema.parse(normalizeRoutingDecisionInput(decision));
|
|
2436
|
+
} catch (error) {
|
|
2437
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2438
|
+
throw new Error(`Invalid LLM routing decision: ${message}`);
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
function finalizeLlmRoutingDecision(decision) {
|
|
2442
|
+
const parsed = parseRoutingDecision(decision);
|
|
2443
|
+
return {
|
|
2444
|
+
targetAgent: parsed.targetAgent,
|
|
2445
|
+
targetAgents: parsed.targetAgents,
|
|
2446
|
+
reasoning: parsed.reasoning,
|
|
2447
|
+
confidence: parsed.confidence,
|
|
2448
|
+
strategy: "llm-based",
|
|
2449
|
+
timestamp: Date.now()
|
|
2450
|
+
};
|
|
2451
|
+
}
|
|
2452
|
+
async function invokeRoutingDecision(model, messages) {
|
|
2453
|
+
if (hasStructuredOutput(model)) {
|
|
2454
|
+
let structuredModel;
|
|
2455
|
+
try {
|
|
2456
|
+
structuredModel = model.withStructuredOutput(RoutingDecisionSchema);
|
|
2457
|
+
} catch (error) {
|
|
2458
|
+
logger.warn("Structured output unavailable, using direct routing fallback", {
|
|
2459
|
+
strategy: "llm-based",
|
|
2460
|
+
fallback: "direct-model-invoke",
|
|
2461
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2462
|
+
});
|
|
2463
|
+
const decision3 = await model.invoke(messages);
|
|
2464
|
+
return finalizeLlmRoutingDecision(decision3);
|
|
2465
|
+
}
|
|
2466
|
+
const decision2 = await structuredModel.invoke(messages);
|
|
2467
|
+
return finalizeLlmRoutingDecision(decision2);
|
|
2468
|
+
}
|
|
2469
|
+
const decision = await model.invoke(messages);
|
|
2470
|
+
return finalizeLlmRoutingDecision(decision);
|
|
2471
|
+
}
|
|
2396
2472
|
var DEFAULT_SUPERVISOR_SYSTEM_PROMPT = `You are a supervisor agent responsible for routing tasks to specialized worker agents.
|
|
2397
2473
|
|
|
2398
2474
|
Your job is to:
|
|
@@ -2450,20 +2526,12 @@ Select the best worker(s) for this task and explain your reasoning.`;
|
|
|
2450
2526
|
new import_messages5.SystemMessage(systemPrompt),
|
|
2451
2527
|
new import_messages5.HumanMessage(userPrompt)
|
|
2452
2528
|
];
|
|
2453
|
-
|
|
2454
|
-
return {
|
|
2455
|
-
targetAgent: decision.targetAgent,
|
|
2456
|
-
targetAgents: decision.targetAgents,
|
|
2457
|
-
reasoning: decision.reasoning,
|
|
2458
|
-
confidence: decision.confidence,
|
|
2459
|
-
strategy: "llm-based",
|
|
2460
|
-
timestamp: Date.now()
|
|
2461
|
-
};
|
|
2529
|
+
return await invokeRoutingDecision(config.model, messages);
|
|
2462
2530
|
}
|
|
2463
2531
|
};
|
|
2464
2532
|
var roundRobinRouting = {
|
|
2465
2533
|
name: "round-robin",
|
|
2466
|
-
async route(state,
|
|
2534
|
+
async route(state, _config) {
|
|
2467
2535
|
const availableWorkers = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id]) => id);
|
|
2468
2536
|
if (availableWorkers.length === 0) {
|
|
2469
2537
|
throw new Error("No available workers for round-robin routing");
|
|
@@ -2482,7 +2550,7 @@ var roundRobinRouting = {
|
|
|
2482
2550
|
};
|
|
2483
2551
|
var skillBasedRouting = {
|
|
2484
2552
|
name: "skill-based",
|
|
2485
|
-
async route(state,
|
|
2553
|
+
async route(state, _config) {
|
|
2486
2554
|
const lastMessage = state.messages[state.messages.length - 1];
|
|
2487
2555
|
const taskContent = (lastMessage?.content || state.input).toLowerCase();
|
|
2488
2556
|
const workerScores = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => {
|
|
@@ -2523,7 +2591,7 @@ var skillBasedRouting = {
|
|
|
2523
2591
|
};
|
|
2524
2592
|
var loadBalancedRouting = {
|
|
2525
2593
|
name: "load-balanced",
|
|
2526
|
-
async route(state,
|
|
2594
|
+
async route(state, _config) {
|
|
2527
2595
|
const availableWorkers = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => ({ id, workload: caps.currentWorkload })).sort((a, b) => a.workload - b.workload);
|
|
2528
2596
|
if (availableWorkers.length === 0) {
|
|
2529
2597
|
throw new Error("No available workers for load-balanced routing");
|
|
@@ -2568,22 +2636,22 @@ function getRoutingStrategy(name) {
|
|
|
2568
2636
|
}
|
|
2569
2637
|
|
|
2570
2638
|
// src/multi-agent/utils.ts
|
|
2571
|
-
var
|
|
2572
|
-
function
|
|
2639
|
+
var logger2 = createPatternLogger("agentforge:patterns:multi-agent:utils");
|
|
2640
|
+
function isRecord2(value) {
|
|
2573
2641
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2574
2642
|
}
|
|
2575
2643
|
function toRunnableConfig(config) {
|
|
2576
|
-
if (!
|
|
2644
|
+
if (!isRecord2(config)) {
|
|
2577
2645
|
return void 0;
|
|
2578
2646
|
}
|
|
2579
2647
|
return config;
|
|
2580
2648
|
}
|
|
2581
2649
|
function getReActResultShape(value) {
|
|
2582
|
-
if (!
|
|
2650
|
+
if (!isRecord2(value)) {
|
|
2583
2651
|
return {};
|
|
2584
2652
|
}
|
|
2585
|
-
const messages = Array.isArray(value.messages) ? value.messages.filter((message) =>
|
|
2586
|
-
const actions = Array.isArray(value.actions) ? value.actions.filter((action) =>
|
|
2653
|
+
const messages = Array.isArray(value.messages) ? value.messages.filter((message) => isRecord2(message)) : void 0;
|
|
2654
|
+
const actions = Array.isArray(value.actions) ? value.actions.filter((action) => isRecord2(action)) : void 0;
|
|
2587
2655
|
return {
|
|
2588
2656
|
messages,
|
|
2589
2657
|
actions,
|
|
@@ -2602,7 +2670,7 @@ function safeSerializeContent(content) {
|
|
|
2602
2670
|
if (typeof part === "string") {
|
|
2603
2671
|
return part;
|
|
2604
2672
|
}
|
|
2605
|
-
if (
|
|
2673
|
+
if (isRecord2(part) && typeof part.text === "string" && part.text.length > 0) {
|
|
2606
2674
|
return part.text;
|
|
2607
2675
|
}
|
|
2608
2676
|
try {
|
|
@@ -2647,17 +2715,17 @@ function isReActAgent(obj) {
|
|
|
2647
2715
|
function wrapReActAgent(workerId, agent, verbose = false) {
|
|
2648
2716
|
return async (state, config) => {
|
|
2649
2717
|
try {
|
|
2650
|
-
|
|
2718
|
+
logger2.debug("Wrapping ReAct agent execution", { workerId });
|
|
2651
2719
|
const runnableConfig = toRunnableConfig(config);
|
|
2652
2720
|
const currentAssignment = state.activeAssignments.find(
|
|
2653
2721
|
(assignment) => assignment.workerId === workerId && !state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
|
|
2654
2722
|
);
|
|
2655
2723
|
if (!currentAssignment) {
|
|
2656
|
-
|
|
2724
|
+
logger2.debug("No active assignment found", { workerId });
|
|
2657
2725
|
return {};
|
|
2658
2726
|
}
|
|
2659
2727
|
const task = currentAssignment.task;
|
|
2660
|
-
|
|
2728
|
+
logger2.debug("Extracted task from assignment", {
|
|
2661
2729
|
workerId,
|
|
2662
2730
|
assignmentId: currentAssignment.id,
|
|
2663
2731
|
taskPreview: task.substring(0, 100) + (task.length > 100 ? "..." : "")
|
|
@@ -2670,7 +2738,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2670
2738
|
thread_id: workerThreadId
|
|
2671
2739
|
}
|
|
2672
2740
|
} : runnableConfig;
|
|
2673
|
-
|
|
2741
|
+
logger2.debug("Invoking ReAct agent with worker-specific config", {
|
|
2674
2742
|
workerId,
|
|
2675
2743
|
...runnableConfig?.configurable?.thread_id !== void 0 ? { parentThreadId: String(runnableConfig.configurable.thread_id) } : {},
|
|
2676
2744
|
...workerThreadId ? { workerThreadId } : {},
|
|
@@ -2685,13 +2753,13 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2685
2753
|
);
|
|
2686
2754
|
const resultShape = getReActResultShape(result);
|
|
2687
2755
|
const response = extractResponse(resultShape);
|
|
2688
|
-
|
|
2756
|
+
logger2.debug("Received response from ReAct agent", {
|
|
2689
2757
|
workerId,
|
|
2690
2758
|
responsePreview: response.substring(0, 100) + (response.length > 100 ? "..." : "")
|
|
2691
2759
|
});
|
|
2692
2760
|
const uniqueTools = extractToolsUsed(resultShape);
|
|
2693
2761
|
if (uniqueTools.length > 0) {
|
|
2694
|
-
|
|
2762
|
+
logger2.debug("Tools used by ReAct agent", { workerId, tools: uniqueTools });
|
|
2695
2763
|
}
|
|
2696
2764
|
const taskResult = {
|
|
2697
2765
|
assignmentId: currentAssignment.id,
|
|
@@ -2710,7 +2778,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2710
2778
|
};
|
|
2711
2779
|
} catch (error) {
|
|
2712
2780
|
const errorMessage = handleNodeError(error, `react-agent:${workerId}`, verbose);
|
|
2713
|
-
|
|
2781
|
+
logger2.error("Error in ReAct agent execution", {
|
|
2714
2782
|
workerId,
|
|
2715
2783
|
error: errorMessage
|
|
2716
2784
|
});
|
|
@@ -2743,7 +2811,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2743
2811
|
// src/multi-agent/nodes/shared.ts
|
|
2744
2812
|
var import_messages6 = require("@langchain/core/messages");
|
|
2745
2813
|
var import_core8 = require("@agentforge/core");
|
|
2746
|
-
var
|
|
2814
|
+
var logger3 = createPatternLogger("agentforge:patterns:multi-agent:nodes");
|
|
2747
2815
|
function createGeneratedId(prefix) {
|
|
2748
2816
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
2749
2817
|
}
|
|
@@ -2795,7 +2863,7 @@ function serializeModelContent(content) {
|
|
|
2795
2863
|
const error = new Error(
|
|
2796
2864
|
"Failed to serialize model content: JSON.stringify returned undefined"
|
|
2797
2865
|
);
|
|
2798
|
-
|
|
2866
|
+
logger3.error("Model content serialization failed", {
|
|
2799
2867
|
errorMessage: error.message,
|
|
2800
2868
|
contentType: content === null ? "null" : typeof content
|
|
2801
2869
|
});
|
|
@@ -2804,7 +2872,7 @@ function serializeModelContent(content) {
|
|
|
2804
2872
|
return serialized;
|
|
2805
2873
|
} catch (error) {
|
|
2806
2874
|
const normalizedError = error instanceof Error ? error : new Error("Unknown error during model content serialization");
|
|
2807
|
-
|
|
2875
|
+
logger3.error("Model content serialization threw an error", {
|
|
2808
2876
|
errorMessage: normalizedError.message,
|
|
2809
2877
|
contentType: content === null ? "null" : typeof content
|
|
2810
2878
|
});
|
|
@@ -2847,16 +2915,16 @@ function createAggregatorNode(config = {}) {
|
|
|
2847
2915
|
} = config;
|
|
2848
2916
|
return async (state) => {
|
|
2849
2917
|
try {
|
|
2850
|
-
|
|
2918
|
+
logger3.info("Aggregator node executing", {
|
|
2851
2919
|
completedTasks: state.completedTasks.length,
|
|
2852
2920
|
successfulTasks: state.completedTasks.filter((task) => task.success).length,
|
|
2853
2921
|
failedTasks: state.completedTasks.filter((task) => !task.success).length
|
|
2854
2922
|
});
|
|
2855
|
-
|
|
2923
|
+
logger3.debug("Combining results from workers");
|
|
2856
2924
|
if (aggregateFn) {
|
|
2857
|
-
|
|
2925
|
+
logger3.debug("Using custom aggregation function");
|
|
2858
2926
|
const response2 = await aggregateFn(state);
|
|
2859
|
-
|
|
2927
|
+
logger3.info("Custom aggregation complete", {
|
|
2860
2928
|
responseLength: response2.length
|
|
2861
2929
|
});
|
|
2862
2930
|
return {
|
|
@@ -2865,16 +2933,16 @@ function createAggregatorNode(config = {}) {
|
|
|
2865
2933
|
};
|
|
2866
2934
|
}
|
|
2867
2935
|
if (state.completedTasks.length === 0) {
|
|
2868
|
-
|
|
2936
|
+
logger3.warn("No completed tasks to aggregate");
|
|
2869
2937
|
return {
|
|
2870
2938
|
response: "No tasks were completed.",
|
|
2871
2939
|
status: "completed"
|
|
2872
2940
|
};
|
|
2873
2941
|
}
|
|
2874
2942
|
if (!model) {
|
|
2875
|
-
|
|
2943
|
+
logger3.debug("No model provided, concatenating results");
|
|
2876
2944
|
const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
|
|
2877
|
-
|
|
2945
|
+
logger3.info("Simple concatenation complete", {
|
|
2878
2946
|
resultLength: combinedResults.length
|
|
2879
2947
|
});
|
|
2880
2948
|
return {
|
|
@@ -2882,27 +2950,27 @@ function createAggregatorNode(config = {}) {
|
|
|
2882
2950
|
status: "completed"
|
|
2883
2951
|
};
|
|
2884
2952
|
}
|
|
2885
|
-
|
|
2953
|
+
logger3.debug("Using LLM for intelligent aggregation", {
|
|
2886
2954
|
taskCount: state.completedTasks.length
|
|
2887
2955
|
});
|
|
2888
2956
|
const messages = createPromptMessages(systemPrompt, createAggregationPrompt(state));
|
|
2889
|
-
|
|
2957
|
+
logger3.debug("Invoking aggregation LLM");
|
|
2890
2958
|
const response = await model.invoke(messages);
|
|
2891
2959
|
const aggregatedResponse = serializeModelContent(response.content);
|
|
2892
|
-
|
|
2960
|
+
logger3.info("Aggregation complete", {
|
|
2893
2961
|
responseLength: aggregatedResponse.length
|
|
2894
2962
|
});
|
|
2895
|
-
|
|
2963
|
+
logger3.debug("Aggregation response details", {
|
|
2896
2964
|
responseLength: aggregatedResponse.length
|
|
2897
2965
|
});
|
|
2898
|
-
|
|
2966
|
+
logger3.debug("Aggregation complete");
|
|
2899
2967
|
return {
|
|
2900
2968
|
response: aggregatedResponse,
|
|
2901
2969
|
status: "completed"
|
|
2902
2970
|
};
|
|
2903
2971
|
} catch (error) {
|
|
2904
2972
|
const errorMessage = handleNodeError(error, "aggregator", false);
|
|
2905
|
-
|
|
2973
|
+
logger3.error("Aggregator node error", {
|
|
2906
2974
|
error: errorMessage,
|
|
2907
2975
|
...error instanceof Error && error.stack ? { stack: error.stack } : {},
|
|
2908
2976
|
completedTasks: state.completedTasks.length
|
|
@@ -2926,7 +2994,7 @@ function incrementAssignedWorkerLoads(state, assignments) {
|
|
|
2926
2994
|
for (const assignment of assignments) {
|
|
2927
2995
|
const worker = updatedWorkers[assignment.workerId];
|
|
2928
2996
|
if (!worker) {
|
|
2929
|
-
|
|
2997
|
+
logger3.error("Worker not found in state", {
|
|
2930
2998
|
workerId: assignment.workerId,
|
|
2931
2999
|
availableWorkers: Object.keys(updatedWorkers)
|
|
2932
3000
|
});
|
|
@@ -2945,45 +3013,45 @@ function createSupervisorNode(config) {
|
|
|
2945
3013
|
const { strategy, maxIterations = 10 } = config;
|
|
2946
3014
|
return async (state) => {
|
|
2947
3015
|
try {
|
|
2948
|
-
|
|
3016
|
+
logger3.info("Supervisor node executing", {
|
|
2949
3017
|
iteration: state.iteration,
|
|
2950
3018
|
maxIterations,
|
|
2951
3019
|
activeAssignments: state.activeAssignments.length,
|
|
2952
3020
|
completedTasks: state.completedTasks.length
|
|
2953
3021
|
});
|
|
2954
|
-
|
|
3022
|
+
logger3.debug(`Routing iteration ${state.iteration}/${maxIterations}`);
|
|
2955
3023
|
if (state.iteration >= maxIterations) {
|
|
2956
|
-
|
|
3024
|
+
logger3.warn("Max iterations reached", {
|
|
2957
3025
|
iteration: state.iteration,
|
|
2958
3026
|
maxIterations
|
|
2959
3027
|
});
|
|
2960
|
-
|
|
3028
|
+
logger3.debug("Max iterations reached, moving to aggregation");
|
|
2961
3029
|
return {
|
|
2962
3030
|
status: "aggregating",
|
|
2963
3031
|
currentAgent: "aggregator"
|
|
2964
3032
|
};
|
|
2965
3033
|
}
|
|
2966
3034
|
const allCompleted = allAssignmentsCompleted(state);
|
|
2967
|
-
|
|
3035
|
+
logger3.debug("Checking task completion", {
|
|
2968
3036
|
activeAssignments: state.activeAssignments.length,
|
|
2969
3037
|
completedTasks: state.completedTasks.length,
|
|
2970
3038
|
allCompleted
|
|
2971
3039
|
});
|
|
2972
3040
|
if (allCompleted && state.activeAssignments.length > 0) {
|
|
2973
|
-
|
|
3041
|
+
logger3.info("All tasks completed, moving to aggregation", {
|
|
2974
3042
|
completedCount: state.completedTasks.length
|
|
2975
3043
|
});
|
|
2976
|
-
|
|
3044
|
+
logger3.debug("All tasks completed, moving to aggregation");
|
|
2977
3045
|
return {
|
|
2978
3046
|
status: "aggregating",
|
|
2979
3047
|
currentAgent: "aggregator"
|
|
2980
3048
|
};
|
|
2981
3049
|
}
|
|
2982
|
-
|
|
3050
|
+
logger3.debug("Getting routing strategy", { strategy });
|
|
2983
3051
|
const routingImpl = getRoutingStrategy(strategy);
|
|
2984
3052
|
const decision = await routingImpl.route(state, config);
|
|
2985
3053
|
const targetAgents = decision.targetAgents && decision.targetAgents.length > 0 ? decision.targetAgents : decision.targetAgent ? [decision.targetAgent] : [];
|
|
2986
|
-
|
|
3054
|
+
logger3.debug("Target agents determined", {
|
|
2987
3055
|
targetAgents,
|
|
2988
3056
|
isParallel: targetAgents.length > 1,
|
|
2989
3057
|
decision: {
|
|
@@ -2992,30 +3060,30 @@ function createSupervisorNode(config) {
|
|
|
2992
3060
|
}
|
|
2993
3061
|
});
|
|
2994
3062
|
if (targetAgents.length === 0) {
|
|
2995
|
-
|
|
3063
|
+
logger3.error("No target agents specified in routing decision");
|
|
2996
3064
|
throw new Error("Routing decision must specify at least one target agent");
|
|
2997
3065
|
}
|
|
2998
3066
|
if (targetAgents.length === 1) {
|
|
2999
|
-
|
|
3067
|
+
logger3.info("Routing to single agent", {
|
|
3000
3068
|
targetAgent: targetAgents[0],
|
|
3001
3069
|
reasoning: decision.reasoning,
|
|
3002
3070
|
confidence: decision.confidence
|
|
3003
3071
|
});
|
|
3004
|
-
|
|
3072
|
+
logger3.debug(`Routing to ${targetAgents[0]}: ${decision.reasoning}`);
|
|
3005
3073
|
} else {
|
|
3006
|
-
|
|
3074
|
+
logger3.info("Routing to multiple agents in parallel", {
|
|
3007
3075
|
targetAgents,
|
|
3008
3076
|
count: targetAgents.length,
|
|
3009
3077
|
reasoning: decision.reasoning,
|
|
3010
3078
|
confidence: decision.confidence
|
|
3011
3079
|
});
|
|
3012
|
-
|
|
3080
|
+
logger3.debug(
|
|
3013
3081
|
`Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`
|
|
3014
3082
|
);
|
|
3015
3083
|
}
|
|
3016
3084
|
const task = getLatestTaskContent(state);
|
|
3017
3085
|
const assignments = createTaskAssignments(targetAgents, task);
|
|
3018
|
-
|
|
3086
|
+
logger3.debug("Created task assignments", {
|
|
3019
3087
|
assignmentCount: assignments.length,
|
|
3020
3088
|
assignments: assignments.map((assignment) => ({
|
|
3021
3089
|
id: assignment.id,
|
|
@@ -3025,7 +3093,7 @@ function createSupervisorNode(config) {
|
|
|
3025
3093
|
});
|
|
3026
3094
|
const messages = createAssignmentMessages(assignments);
|
|
3027
3095
|
const updatedWorkers = incrementAssignedWorkerLoads(state, assignments);
|
|
3028
|
-
|
|
3096
|
+
logger3.info("Supervisor routing complete", {
|
|
3029
3097
|
currentAgent: targetAgents.join(","),
|
|
3030
3098
|
status: "executing",
|
|
3031
3099
|
assignmentCount: assignments.length,
|
|
@@ -3043,7 +3111,7 @@ function createSupervisorNode(config) {
|
|
|
3043
3111
|
};
|
|
3044
3112
|
} catch (error) {
|
|
3045
3113
|
const errorMessage = handleNodeError(error, "supervisor", false);
|
|
3046
|
-
|
|
3114
|
+
logger3.error("Supervisor node error", {
|
|
3047
3115
|
error: errorMessage,
|
|
3048
3116
|
...error instanceof Error && error.stack ? { stack: error.stack } : {},
|
|
3049
3117
|
iteration: state.iteration
|
|
@@ -3071,7 +3139,7 @@ async function invokeWorkerModel(model, config, assignment) {
|
|
|
3071
3139
|
);
|
|
3072
3140
|
let modelToUse = model;
|
|
3073
3141
|
if (config.tools && config.tools.length > 0 && model.bindTools) {
|
|
3074
|
-
|
|
3142
|
+
logger3.debug("Binding tools to model", {
|
|
3075
3143
|
workerId: config.id,
|
|
3076
3144
|
toolCount: config.tools.length,
|
|
3077
3145
|
toolNames: config.tools.map((tool) => tool.metadata.name)
|
|
@@ -3080,15 +3148,15 @@ async function invokeWorkerModel(model, config, assignment) {
|
|
|
3080
3148
|
convertWorkerToolsForLangChain(config.tools)
|
|
3081
3149
|
);
|
|
3082
3150
|
}
|
|
3083
|
-
|
|
3151
|
+
logger3.debug("Invoking LLM", { workerId: config.id });
|
|
3084
3152
|
const response = await modelToUse.invoke(messages);
|
|
3085
3153
|
const result = serializeModelContent(response.content);
|
|
3086
|
-
|
|
3154
|
+
logger3.info("Worker task completed", {
|
|
3087
3155
|
workerId: config.id,
|
|
3088
3156
|
assignmentId: assignment.id,
|
|
3089
3157
|
resultLength: result.length
|
|
3090
3158
|
});
|
|
3091
|
-
|
|
3159
|
+
logger3.debug("Worker result details", {
|
|
3092
3160
|
workerId: config.id,
|
|
3093
3161
|
assignmentId: assignment.id,
|
|
3094
3162
|
resultLength: result.length
|
|
@@ -3123,7 +3191,7 @@ async function invokeWorkerModel(model, config, assignment) {
|
|
|
3123
3191
|
function getStateWorkerOrThrow(state, workerId) {
|
|
3124
3192
|
const currentWorker = state.workers[workerId];
|
|
3125
3193
|
if (!currentWorker) {
|
|
3126
|
-
|
|
3194
|
+
logger3.error("Attempted to decrement workload for unknown worker", {
|
|
3127
3195
|
workerId,
|
|
3128
3196
|
availableWorkers: Object.keys(state.workers)
|
|
3129
3197
|
});
|
|
@@ -3139,7 +3207,7 @@ function resolvePreviousWorkload(workerId, currentWorker, workerFromExecution) {
|
|
|
3139
3207
|
if (typeof currentWorker.currentWorkload === "number" && Number.isFinite(currentWorker.currentWorkload)) {
|
|
3140
3208
|
return currentWorker.currentWorkload;
|
|
3141
3209
|
}
|
|
3142
|
-
|
|
3210
|
+
logger3.error("Worker workload is not a valid number; cannot decrement", {
|
|
3143
3211
|
workerId,
|
|
3144
3212
|
workloadFromState: currentWorker.currentWorkload,
|
|
3145
3213
|
...typeof workloadFromWorker === "number" ? { workloadFromWorker } : {}
|
|
@@ -3174,7 +3242,7 @@ function mergeWorkersWithDecrement(state, workerId, executionResult) {
|
|
|
3174
3242
|
...baseWorkers,
|
|
3175
3243
|
[workerId]: updatedWorker
|
|
3176
3244
|
};
|
|
3177
|
-
|
|
3245
|
+
logger3.debug("Worker workload decremented", {
|
|
3178
3246
|
workerId,
|
|
3179
3247
|
previousWorkload,
|
|
3180
3248
|
newWorkload: updatedWorkers[workerId].currentWorkload,
|
|
@@ -3192,7 +3260,7 @@ function decrementWorkerOnError(state, workerId) {
|
|
|
3192
3260
|
currentWorkload: Math.max(0, previousWorkload - 1)
|
|
3193
3261
|
}
|
|
3194
3262
|
};
|
|
3195
|
-
|
|
3263
|
+
logger3.debug("Worker workload decremented (error path)", {
|
|
3196
3264
|
workerId,
|
|
3197
3265
|
previousWorkload,
|
|
3198
3266
|
newWorkload: updatedWorkers[workerId].currentWorkload
|
|
@@ -3200,7 +3268,7 @@ function decrementWorkerOnError(state, workerId) {
|
|
|
3200
3268
|
return updatedWorkers;
|
|
3201
3269
|
}
|
|
3202
3270
|
function createErrorTaskResult(assignment, workerId, errorMessage) {
|
|
3203
|
-
|
|
3271
|
+
logger3.warn("Creating error result for assignment", {
|
|
3204
3272
|
workerId,
|
|
3205
3273
|
assignmentId: assignment.id
|
|
3206
3274
|
});
|
|
@@ -3217,47 +3285,47 @@ function createWorkerNode(config) {
|
|
|
3217
3285
|
const { id, model, executeFn, agent } = config;
|
|
3218
3286
|
return async (state, runConfig) => {
|
|
3219
3287
|
try {
|
|
3220
|
-
|
|
3288
|
+
logger3.info("Worker node executing", {
|
|
3221
3289
|
workerId: id,
|
|
3222
3290
|
iteration: state.iteration,
|
|
3223
3291
|
activeAssignments: state.activeAssignments.length
|
|
3224
3292
|
});
|
|
3225
3293
|
const currentAssignment = findCurrentAssignment(state, id);
|
|
3226
3294
|
if (!currentAssignment) {
|
|
3227
|
-
|
|
3295
|
+
logger3.debug("No active assignment found for worker", {
|
|
3228
3296
|
workerId: id,
|
|
3229
3297
|
totalActiveAssignments: state.activeAssignments.length,
|
|
3230
3298
|
completedTasks: state.completedTasks.length
|
|
3231
3299
|
});
|
|
3232
3300
|
return {};
|
|
3233
3301
|
}
|
|
3234
|
-
|
|
3302
|
+
logger3.info("Worker processing assignment", {
|
|
3235
3303
|
workerId: id,
|
|
3236
3304
|
assignmentId: currentAssignment.id,
|
|
3237
3305
|
taskLength: currentAssignment.task.length
|
|
3238
3306
|
});
|
|
3239
|
-
|
|
3307
|
+
logger3.debug("Worker assignment details", {
|
|
3240
3308
|
workerId: id,
|
|
3241
3309
|
assignmentId: currentAssignment.id,
|
|
3242
3310
|
taskLength: currentAssignment.task.length
|
|
3243
3311
|
});
|
|
3244
3312
|
let executionResult;
|
|
3245
3313
|
if (executeFn) {
|
|
3246
|
-
|
|
3314
|
+
logger3.debug("Using custom execution function", { workerId: id });
|
|
3247
3315
|
executionResult = await executeFn(state, runConfig);
|
|
3248
3316
|
} else if (agent && isReActAgent(agent)) {
|
|
3249
|
-
|
|
3317
|
+
logger3.debug("Using ReAct agent", { workerId: id });
|
|
3250
3318
|
const wrappedFn = wrapReActAgent(id, agent, config.verbose ?? false);
|
|
3251
3319
|
executionResult = await wrappedFn(state, runConfig);
|
|
3252
3320
|
} else if (model) {
|
|
3253
|
-
|
|
3321
|
+
logger3.debug("Using default LLM execution", {
|
|
3254
3322
|
workerId: id,
|
|
3255
3323
|
hasTools: (config.tools ?? []).length > 0,
|
|
3256
3324
|
toolCount: (config.tools ?? []).length
|
|
3257
3325
|
});
|
|
3258
3326
|
executionResult = await invokeWorkerModel(model, config, currentAssignment);
|
|
3259
3327
|
} else {
|
|
3260
|
-
|
|
3328
|
+
logger3.error("Worker missing required configuration", { workerId: id });
|
|
3261
3329
|
throw new Error(
|
|
3262
3330
|
`Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
|
|
3263
3331
|
);
|
|
@@ -3269,7 +3337,7 @@ function createWorkerNode(config) {
|
|
|
3269
3337
|
};
|
|
3270
3338
|
} catch (error) {
|
|
3271
3339
|
const errorMessage = handleNodeError(error, `worker:${id}`, false);
|
|
3272
|
-
|
|
3340
|
+
logger3.error("Worker node error", {
|
|
3273
3341
|
workerId: id,
|
|
3274
3342
|
error: errorMessage
|
|
3275
3343
|
});
|
|
@@ -3278,7 +3346,7 @@ function createWorkerNode(config) {
|
|
|
3278
3346
|
updatedWorkers = decrementWorkerOnError(state, id);
|
|
3279
3347
|
} catch (workloadError) {
|
|
3280
3348
|
const workloadErrorMessage = workloadError instanceof Error ? workloadError.message : String(workloadError);
|
|
3281
|
-
|
|
3349
|
+
logger3.error("Worker error handling failed", {
|
|
3282
3350
|
workerId: id,
|
|
3283
3351
|
error: workloadErrorMessage
|
|
3284
3352
|
});
|
|
@@ -3298,7 +3366,7 @@ function createWorkerNode(config) {
|
|
|
3298
3366
|
workers: updatedWorkers
|
|
3299
3367
|
};
|
|
3300
3368
|
}
|
|
3301
|
-
|
|
3369
|
+
logger3.error("No assignment found for error handling", { workerId: id });
|
|
3302
3370
|
return {
|
|
3303
3371
|
status: "failed",
|
|
3304
3372
|
error: errorMessage,
|
|
@@ -3310,7 +3378,7 @@ function createWorkerNode(config) {
|
|
|
3310
3378
|
|
|
3311
3379
|
// src/multi-agent/agent.ts
|
|
3312
3380
|
var import_langgraph4 = require("@langchain/langgraph");
|
|
3313
|
-
var
|
|
3381
|
+
var logger4 = createPatternLogger("agentforge:patterns:multi-agent:system");
|
|
3314
3382
|
function getToolName(tool) {
|
|
3315
3383
|
if (!tool || typeof tool !== "object") {
|
|
3316
3384
|
return "unknown";
|
|
@@ -3335,13 +3403,6 @@ function createMultiAgentSystem(config) {
|
|
|
3335
3403
|
} = config;
|
|
3336
3404
|
const workflow = new import_langgraph4.StateGraph(MultiAgentState);
|
|
3337
3405
|
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
3406
|
const supervisorNode = createSupervisorNode(supervisorConfig);
|
|
3346
3407
|
workflow.addNode("supervisor", supervisorNode);
|
|
3347
3408
|
const workerIds = [];
|
|
@@ -3361,46 +3422,46 @@ function createMultiAgentSystem(config) {
|
|
|
3361
3422
|
});
|
|
3362
3423
|
workflow.addNode("aggregator", aggregatorNode);
|
|
3363
3424
|
const supervisorRouter = (state) => {
|
|
3364
|
-
|
|
3425
|
+
logger4.debug("Supervisor router executing", {
|
|
3365
3426
|
status: state.status,
|
|
3366
3427
|
...state.currentAgent ? { currentAgent: state.currentAgent } : {},
|
|
3367
3428
|
iteration: state.iteration
|
|
3368
3429
|
});
|
|
3369
3430
|
if (state.status === "completed" || state.status === "failed") {
|
|
3370
|
-
|
|
3431
|
+
logger4.info("Supervisor router: ending workflow", { status: state.status });
|
|
3371
3432
|
return import_langgraph4.END;
|
|
3372
3433
|
}
|
|
3373
3434
|
if (state.status === "aggregating") {
|
|
3374
|
-
|
|
3435
|
+
logger4.info("Supervisor router: routing to aggregator");
|
|
3375
3436
|
return "aggregator";
|
|
3376
3437
|
}
|
|
3377
3438
|
if (state.currentAgent && state.currentAgent !== "supervisor") {
|
|
3378
3439
|
if (state.currentAgent.includes(",")) {
|
|
3379
3440
|
const agents = state.currentAgent.split(",").map((a) => a.trim());
|
|
3380
|
-
|
|
3441
|
+
logger4.info("Supervisor router: parallel routing", {
|
|
3381
3442
|
agents,
|
|
3382
3443
|
count: agents.length
|
|
3383
3444
|
});
|
|
3384
3445
|
return agents;
|
|
3385
3446
|
}
|
|
3386
|
-
|
|
3447
|
+
logger4.info("Supervisor router: single agent routing", {
|
|
3387
3448
|
targetAgent: state.currentAgent
|
|
3388
3449
|
});
|
|
3389
3450
|
return state.currentAgent;
|
|
3390
3451
|
}
|
|
3391
|
-
|
|
3452
|
+
logger4.debug("Supervisor router: staying at supervisor");
|
|
3392
3453
|
return "supervisor";
|
|
3393
3454
|
};
|
|
3394
3455
|
const workerRouter = (state) => {
|
|
3395
|
-
|
|
3456
|
+
logger4.debug("Worker router executing", {
|
|
3396
3457
|
iteration: state.iteration,
|
|
3397
3458
|
completedTasks: state.completedTasks.length
|
|
3398
3459
|
});
|
|
3399
|
-
|
|
3460
|
+
logger4.debug("Worker router: returning to supervisor");
|
|
3400
3461
|
return "supervisor";
|
|
3401
3462
|
};
|
|
3402
3463
|
const aggregatorRouter = (state) => {
|
|
3403
|
-
|
|
3464
|
+
logger4.info("Aggregator router: ending workflow", {
|
|
3404
3465
|
completedTasks: state.completedTasks.length,
|
|
3405
3466
|
status: state.status
|
|
3406
3467
|
});
|