@agentforge/patterns 0.16.1 → 0.16.3
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 +391 -276
- package/dist/index.d.cts +27 -40
- package/dist/index.d.ts +27 -40
- package/dist/index.js +391 -276
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -2581,16 +2581,82 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2581
2581
|
};
|
|
2582
2582
|
}
|
|
2583
2583
|
|
|
2584
|
-
// src/multi-agent/nodes.ts
|
|
2584
|
+
// src/multi-agent/nodes/shared.ts
|
|
2585
2585
|
import { HumanMessage as HumanMessage5, SystemMessage as SystemMessage5 } from "@langchain/core/messages";
|
|
2586
2586
|
import { toLangChainTools as toLangChainTools2 } from "@agentforge/core";
|
|
2587
2587
|
var logger2 = createPatternLogger("agentforge:patterns:multi-agent:nodes");
|
|
2588
|
+
function createGeneratedId(prefix) {
|
|
2589
|
+
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
2590
|
+
}
|
|
2591
|
+
function createTaskAssignmentId() {
|
|
2592
|
+
return `task_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
2593
|
+
}
|
|
2594
|
+
function getLatestTaskContent(state) {
|
|
2595
|
+
return state.messages[state.messages.length - 1]?.content || state.input;
|
|
2596
|
+
}
|
|
2597
|
+
function createTaskAssignments(workerIds, task) {
|
|
2598
|
+
return workerIds.map((workerId) => ({
|
|
2599
|
+
id: createTaskAssignmentId(),
|
|
2600
|
+
workerId,
|
|
2601
|
+
task,
|
|
2602
|
+
priority: 5,
|
|
2603
|
+
assignedAt: Date.now()
|
|
2604
|
+
}));
|
|
2605
|
+
}
|
|
2606
|
+
function createAssignmentMessages(assignments) {
|
|
2607
|
+
return assignments.map((assignment) => ({
|
|
2608
|
+
id: createGeneratedId("msg"),
|
|
2609
|
+
from: "supervisor",
|
|
2610
|
+
to: [assignment.workerId],
|
|
2611
|
+
type: "task_assignment",
|
|
2612
|
+
content: assignment.task,
|
|
2613
|
+
timestamp: Date.now(),
|
|
2614
|
+
metadata: {
|
|
2615
|
+
assignmentId: assignment.id,
|
|
2616
|
+
priority: assignment.priority
|
|
2617
|
+
}
|
|
2618
|
+
}));
|
|
2619
|
+
}
|
|
2620
|
+
function findCurrentAssignment(state, workerId) {
|
|
2621
|
+
return state.activeAssignments.find(
|
|
2622
|
+
(assignment) => assignment.workerId === workerId && !state.completedTasks.some((task) => task.assignmentId === assignment.id)
|
|
2623
|
+
);
|
|
2624
|
+
}
|
|
2588
2625
|
function convertWorkerToolsForLangChain(tools) {
|
|
2589
2626
|
const safeTools = tools ?? [];
|
|
2590
|
-
return toLangChainTools2(
|
|
2591
|
-
safeTools
|
|
2592
|
-
);
|
|
2627
|
+
return toLangChainTools2(safeTools);
|
|
2593
2628
|
}
|
|
2629
|
+
function serializeModelContent(content) {
|
|
2630
|
+
if (typeof content === "string") {
|
|
2631
|
+
return content;
|
|
2632
|
+
}
|
|
2633
|
+
try {
|
|
2634
|
+
const serialized = JSON.stringify(content);
|
|
2635
|
+
if (typeof serialized !== "string") {
|
|
2636
|
+
const error = new Error(
|
|
2637
|
+
"Failed to serialize model content: JSON.stringify returned undefined"
|
|
2638
|
+
);
|
|
2639
|
+
logger2.error("Model content serialization failed", {
|
|
2640
|
+
errorMessage: error.message,
|
|
2641
|
+
contentType: content === null ? "null" : typeof content
|
|
2642
|
+
});
|
|
2643
|
+
throw error;
|
|
2644
|
+
}
|
|
2645
|
+
return serialized;
|
|
2646
|
+
} catch (error) {
|
|
2647
|
+
const normalizedError = error instanceof Error ? error : new Error("Unknown error during model content serialization");
|
|
2648
|
+
logger2.error("Model content serialization threw an error", {
|
|
2649
|
+
errorMessage: normalizedError.message,
|
|
2650
|
+
contentType: content === null ? "null" : typeof content
|
|
2651
|
+
});
|
|
2652
|
+
throw normalizedError;
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
function createPromptMessages(systemPrompt, task) {
|
|
2656
|
+
return [new SystemMessage5(systemPrompt), new HumanMessage5(task)];
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
// src/multi-agent/nodes/aggregator.ts
|
|
2594
2660
|
var DEFAULT_AGGREGATOR_SYSTEM_PROMPT = `You are an aggregator agent responsible for combining results from multiple worker agents.
|
|
2595
2661
|
|
|
2596
2662
|
Your job is to:
|
|
@@ -2600,12 +2666,124 @@ Your job is to:
|
|
|
2600
2666
|
4. Provide a clear, comprehensive final answer
|
|
2601
2667
|
|
|
2602
2668
|
Be concise but thorough in your aggregation.`;
|
|
2603
|
-
function
|
|
2669
|
+
function createAggregationPrompt(state) {
|
|
2670
|
+
const taskResults = state.completedTasks.map((task, idx) => {
|
|
2671
|
+
const status = task.success ? "\u2713" : "\u2717";
|
|
2672
|
+
const result = task.success ? task.result : `Error: ${task.error}`;
|
|
2673
|
+
return `${idx + 1}. [${status}] Worker ${task.workerId}:
|
|
2674
|
+
${result}`;
|
|
2675
|
+
}).join("\n\n");
|
|
2676
|
+
return `Original query: ${state.input}
|
|
2677
|
+
|
|
2678
|
+
Worker results:
|
|
2679
|
+
${taskResults}
|
|
2680
|
+
|
|
2681
|
+
Please synthesize these results into a comprehensive response that addresses the original query.`;
|
|
2682
|
+
}
|
|
2683
|
+
function createAggregatorNode(config = {}) {
|
|
2604
2684
|
const {
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2685
|
+
model,
|
|
2686
|
+
systemPrompt = DEFAULT_AGGREGATOR_SYSTEM_PROMPT,
|
|
2687
|
+
aggregateFn
|
|
2608
2688
|
} = config;
|
|
2689
|
+
return async (state) => {
|
|
2690
|
+
try {
|
|
2691
|
+
logger2.info("Aggregator node executing", {
|
|
2692
|
+
completedTasks: state.completedTasks.length,
|
|
2693
|
+
successfulTasks: state.completedTasks.filter((task) => task.success).length,
|
|
2694
|
+
failedTasks: state.completedTasks.filter((task) => !task.success).length
|
|
2695
|
+
});
|
|
2696
|
+
logger2.debug("Combining results from workers");
|
|
2697
|
+
if (aggregateFn) {
|
|
2698
|
+
logger2.debug("Using custom aggregation function");
|
|
2699
|
+
const response2 = await aggregateFn(state);
|
|
2700
|
+
logger2.info("Custom aggregation complete", {
|
|
2701
|
+
responseLength: response2.length
|
|
2702
|
+
});
|
|
2703
|
+
return {
|
|
2704
|
+
response: response2,
|
|
2705
|
+
status: "completed"
|
|
2706
|
+
};
|
|
2707
|
+
}
|
|
2708
|
+
if (state.completedTasks.length === 0) {
|
|
2709
|
+
logger2.warn("No completed tasks to aggregate");
|
|
2710
|
+
return {
|
|
2711
|
+
response: "No tasks were completed.",
|
|
2712
|
+
status: "completed"
|
|
2713
|
+
};
|
|
2714
|
+
}
|
|
2715
|
+
if (!model) {
|
|
2716
|
+
logger2.debug("No model provided, concatenating results");
|
|
2717
|
+
const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
|
|
2718
|
+
logger2.info("Simple concatenation complete", {
|
|
2719
|
+
resultLength: combinedResults.length
|
|
2720
|
+
});
|
|
2721
|
+
return {
|
|
2722
|
+
response: combinedResults || "No successful results to aggregate.",
|
|
2723
|
+
status: "completed"
|
|
2724
|
+
};
|
|
2725
|
+
}
|
|
2726
|
+
logger2.debug("Using LLM for intelligent aggregation", {
|
|
2727
|
+
taskCount: state.completedTasks.length
|
|
2728
|
+
});
|
|
2729
|
+
const messages = createPromptMessages(systemPrompt, createAggregationPrompt(state));
|
|
2730
|
+
logger2.debug("Invoking aggregation LLM");
|
|
2731
|
+
const response = await model.invoke(messages);
|
|
2732
|
+
const aggregatedResponse = serializeModelContent(response.content);
|
|
2733
|
+
logger2.info("Aggregation complete", {
|
|
2734
|
+
responseLength: aggregatedResponse.length
|
|
2735
|
+
});
|
|
2736
|
+
logger2.debug("Aggregation response details", {
|
|
2737
|
+
responseLength: aggregatedResponse.length
|
|
2738
|
+
});
|
|
2739
|
+
logger2.debug("Aggregation complete");
|
|
2740
|
+
return {
|
|
2741
|
+
response: aggregatedResponse,
|
|
2742
|
+
status: "completed"
|
|
2743
|
+
};
|
|
2744
|
+
} catch (error) {
|
|
2745
|
+
const errorMessage = handleNodeError(error, "aggregator", false);
|
|
2746
|
+
logger2.error("Aggregator node error", {
|
|
2747
|
+
error: errorMessage,
|
|
2748
|
+
...error instanceof Error && error.stack ? { stack: error.stack } : {},
|
|
2749
|
+
completedTasks: state.completedTasks.length
|
|
2750
|
+
});
|
|
2751
|
+
return {
|
|
2752
|
+
status: "failed",
|
|
2753
|
+
error: errorMessage
|
|
2754
|
+
};
|
|
2755
|
+
}
|
|
2756
|
+
};
|
|
2757
|
+
}
|
|
2758
|
+
|
|
2759
|
+
// src/multi-agent/nodes/supervisor.ts
|
|
2760
|
+
function allAssignmentsCompleted(state) {
|
|
2761
|
+
return state.activeAssignments.every(
|
|
2762
|
+
(assignment) => state.completedTasks.some((task) => task.assignmentId === assignment.id)
|
|
2763
|
+
);
|
|
2764
|
+
}
|
|
2765
|
+
function incrementAssignedWorkerLoads(state, assignments) {
|
|
2766
|
+
const updatedWorkers = { ...state.workers };
|
|
2767
|
+
for (const assignment of assignments) {
|
|
2768
|
+
const worker = updatedWorkers[assignment.workerId];
|
|
2769
|
+
if (!worker) {
|
|
2770
|
+
logger2.error("Worker not found in state", {
|
|
2771
|
+
workerId: assignment.workerId,
|
|
2772
|
+
availableWorkers: Object.keys(updatedWorkers)
|
|
2773
|
+
});
|
|
2774
|
+
throw new Error(
|
|
2775
|
+
`Worker ${assignment.workerId} not found in state.workers. Available workers: ${Object.keys(updatedWorkers).join(", ")}`
|
|
2776
|
+
);
|
|
2777
|
+
}
|
|
2778
|
+
updatedWorkers[assignment.workerId] = {
|
|
2779
|
+
...worker,
|
|
2780
|
+
currentWorkload: worker.currentWorkload + 1
|
|
2781
|
+
};
|
|
2782
|
+
}
|
|
2783
|
+
return updatedWorkers;
|
|
2784
|
+
}
|
|
2785
|
+
function createSupervisorNode(config) {
|
|
2786
|
+
const { strategy, maxIterations = 10 } = config;
|
|
2609
2787
|
return async (state) => {
|
|
2610
2788
|
try {
|
|
2611
2789
|
logger2.info("Supervisor node executing", {
|
|
@@ -2626,9 +2804,7 @@ function createSupervisorNode(config) {
|
|
|
2626
2804
|
currentAgent: "aggregator"
|
|
2627
2805
|
};
|
|
2628
2806
|
}
|
|
2629
|
-
const allCompleted = state
|
|
2630
|
-
(assignment) => state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
|
|
2631
|
-
);
|
|
2807
|
+
const allCompleted = allAssignmentsCompleted(state);
|
|
2632
2808
|
logger2.debug("Checking task completion", {
|
|
2633
2809
|
activeAssignments: state.activeAssignments.length,
|
|
2634
2810
|
completedTasks: state.completedTasks.length,
|
|
@@ -2666,6 +2842,7 @@ function createSupervisorNode(config) {
|
|
|
2666
2842
|
reasoning: decision.reasoning,
|
|
2667
2843
|
confidence: decision.confidence
|
|
2668
2844
|
});
|
|
2845
|
+
logger2.debug(`Routing to ${targetAgents[0]}: ${decision.reasoning}`);
|
|
2669
2846
|
} else {
|
|
2670
2847
|
logger2.info("Routing to multiple agents in parallel", {
|
|
2671
2848
|
targetAgents,
|
|
@@ -2673,57 +2850,22 @@ function createSupervisorNode(config) {
|
|
|
2673
2850
|
reasoning: decision.reasoning,
|
|
2674
2851
|
confidence: decision.confidence
|
|
2675
2852
|
});
|
|
2853
|
+
logger2.debug(
|
|
2854
|
+
`Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`
|
|
2855
|
+
);
|
|
2676
2856
|
}
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
} else {
|
|
2680
|
-
logger2.debug(`Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`);
|
|
2681
|
-
}
|
|
2682
|
-
const task = state.messages[state.messages.length - 1]?.content || state.input;
|
|
2683
|
-
const assignments = targetAgents.map((workerId) => ({
|
|
2684
|
-
id: `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
2685
|
-
workerId,
|
|
2686
|
-
task,
|
|
2687
|
-
priority: 5,
|
|
2688
|
-
assignedAt: Date.now()
|
|
2689
|
-
}));
|
|
2857
|
+
const task = getLatestTaskContent(state);
|
|
2858
|
+
const assignments = createTaskAssignments(targetAgents, task);
|
|
2690
2859
|
logger2.debug("Created task assignments", {
|
|
2691
2860
|
assignmentCount: assignments.length,
|
|
2692
|
-
assignments: assignments.map((
|
|
2693
|
-
id:
|
|
2694
|
-
workerId:
|
|
2695
|
-
taskLength:
|
|
2861
|
+
assignments: assignments.map((assignment) => ({
|
|
2862
|
+
id: assignment.id,
|
|
2863
|
+
workerId: assignment.workerId,
|
|
2864
|
+
taskLength: assignment.task.length
|
|
2696
2865
|
}))
|
|
2697
2866
|
});
|
|
2698
|
-
const messages = assignments
|
|
2699
|
-
|
|
2700
|
-
from: "supervisor",
|
|
2701
|
-
to: [assignment.workerId],
|
|
2702
|
-
type: "task_assignment",
|
|
2703
|
-
content: assignment.task,
|
|
2704
|
-
timestamp: Date.now(),
|
|
2705
|
-
metadata: {
|
|
2706
|
-
assignmentId: assignment.id,
|
|
2707
|
-
priority: assignment.priority
|
|
2708
|
-
}
|
|
2709
|
-
}));
|
|
2710
|
-
const updatedWorkers = { ...state.workers };
|
|
2711
|
-
for (const assignment of assignments) {
|
|
2712
|
-
const worker = updatedWorkers[assignment.workerId];
|
|
2713
|
-
if (!worker) {
|
|
2714
|
-
logger2.error("Worker not found in state", {
|
|
2715
|
-
workerId: assignment.workerId,
|
|
2716
|
-
availableWorkers: Object.keys(updatedWorkers)
|
|
2717
|
-
});
|
|
2718
|
-
throw new Error(
|
|
2719
|
-
`Worker ${assignment.workerId} not found in state.workers. Available workers: ${Object.keys(updatedWorkers).join(", ")}`
|
|
2720
|
-
);
|
|
2721
|
-
}
|
|
2722
|
-
updatedWorkers[assignment.workerId] = {
|
|
2723
|
-
...worker,
|
|
2724
|
-
currentWorkload: worker.currentWorkload + 1
|
|
2725
|
-
};
|
|
2726
|
-
}
|
|
2867
|
+
const messages = createAssignmentMessages(assignments);
|
|
2868
|
+
const updatedWorkers = incrementAssignedWorkerLoads(state, assignments);
|
|
2727
2869
|
logger2.info("Supervisor routing complete", {
|
|
2728
2870
|
currentAgent: targetAgents.join(","),
|
|
2729
2871
|
status: "executing",
|
|
@@ -2733,41 +2875,187 @@ function createSupervisorNode(config) {
|
|
|
2733
2875
|
});
|
|
2734
2876
|
return {
|
|
2735
2877
|
currentAgent: targetAgents.join(","),
|
|
2736
|
-
// Store all agents (for backward compat)
|
|
2737
2878
|
status: "executing",
|
|
2738
2879
|
routingHistory: [decision],
|
|
2739
2880
|
activeAssignments: assignments,
|
|
2740
|
-
// Multiple assignments for parallel execution!
|
|
2741
2881
|
messages,
|
|
2742
2882
|
workers: updatedWorkers,
|
|
2743
|
-
// Include updated workload
|
|
2744
|
-
// Add 1 to iteration counter (uses additive reducer)
|
|
2745
2883
|
iteration: 1
|
|
2746
2884
|
};
|
|
2747
2885
|
} catch (error) {
|
|
2886
|
+
const errorMessage = handleNodeError(error, "supervisor", false);
|
|
2748
2887
|
logger2.error("Supervisor node error", {
|
|
2749
|
-
error:
|
|
2888
|
+
error: errorMessage,
|
|
2750
2889
|
...error instanceof Error && error.stack ? { stack: error.stack } : {},
|
|
2751
2890
|
iteration: state.iteration
|
|
2752
2891
|
});
|
|
2753
2892
|
return {
|
|
2754
2893
|
status: "failed",
|
|
2755
|
-
error:
|
|
2894
|
+
error: errorMessage
|
|
2756
2895
|
};
|
|
2757
2896
|
}
|
|
2758
2897
|
};
|
|
2759
2898
|
}
|
|
2899
|
+
|
|
2900
|
+
// src/multi-agent/nodes/worker.ts
|
|
2901
|
+
function buildDefaultSystemPrompt(config) {
|
|
2902
|
+
return `You are a specialized worker agent with the following capabilities:
|
|
2903
|
+
Skills: ${config.capabilities.skills.join(", ")}
|
|
2904
|
+
Tools: ${config.capabilities.tools.join(", ")}
|
|
2905
|
+
|
|
2906
|
+
Execute the assigned task using your skills and tools. Provide a clear, actionable result.`;
|
|
2907
|
+
}
|
|
2908
|
+
async function invokeWorkerModel(model, config, assignment) {
|
|
2909
|
+
const messages = createPromptMessages(
|
|
2910
|
+
config.systemPrompt || buildDefaultSystemPrompt(config),
|
|
2911
|
+
assignment.task
|
|
2912
|
+
);
|
|
2913
|
+
let modelToUse = model;
|
|
2914
|
+
if (config.tools && config.tools.length > 0 && model.bindTools) {
|
|
2915
|
+
logger2.debug("Binding tools to model", {
|
|
2916
|
+
workerId: config.id,
|
|
2917
|
+
toolCount: config.tools.length,
|
|
2918
|
+
toolNames: config.tools.map((tool) => tool.metadata.name)
|
|
2919
|
+
});
|
|
2920
|
+
modelToUse = model.bindTools(
|
|
2921
|
+
convertWorkerToolsForLangChain(config.tools)
|
|
2922
|
+
);
|
|
2923
|
+
}
|
|
2924
|
+
logger2.debug("Invoking LLM", { workerId: config.id });
|
|
2925
|
+
const response = await modelToUse.invoke(messages);
|
|
2926
|
+
const result = serializeModelContent(response.content);
|
|
2927
|
+
logger2.info("Worker task completed", {
|
|
2928
|
+
workerId: config.id,
|
|
2929
|
+
assignmentId: assignment.id,
|
|
2930
|
+
resultLength: result.length
|
|
2931
|
+
});
|
|
2932
|
+
logger2.debug("Worker result details", {
|
|
2933
|
+
workerId: config.id,
|
|
2934
|
+
assignmentId: assignment.id,
|
|
2935
|
+
resultLength: result.length
|
|
2936
|
+
});
|
|
2937
|
+
const taskResult = {
|
|
2938
|
+
assignmentId: assignment.id,
|
|
2939
|
+
workerId: config.id,
|
|
2940
|
+
success: true,
|
|
2941
|
+
result,
|
|
2942
|
+
completedAt: Date.now(),
|
|
2943
|
+
metadata: {
|
|
2944
|
+
skills_used: config.capabilities.skills
|
|
2945
|
+
}
|
|
2946
|
+
};
|
|
2947
|
+
const message = {
|
|
2948
|
+
id: createGeneratedId("msg"),
|
|
2949
|
+
from: config.id,
|
|
2950
|
+
to: ["supervisor"],
|
|
2951
|
+
type: "task_result",
|
|
2952
|
+
content: result,
|
|
2953
|
+
timestamp: Date.now(),
|
|
2954
|
+
metadata: {
|
|
2955
|
+
assignmentId: assignment.id,
|
|
2956
|
+
success: true
|
|
2957
|
+
}
|
|
2958
|
+
};
|
|
2959
|
+
return {
|
|
2960
|
+
completedTasks: [taskResult],
|
|
2961
|
+
messages: [message]
|
|
2962
|
+
};
|
|
2963
|
+
}
|
|
2964
|
+
function getStateWorkerOrThrow(state, workerId) {
|
|
2965
|
+
const currentWorker = state.workers[workerId];
|
|
2966
|
+
if (!currentWorker) {
|
|
2967
|
+
logger2.error("Attempted to decrement workload for unknown worker", {
|
|
2968
|
+
workerId,
|
|
2969
|
+
availableWorkers: Object.keys(state.workers)
|
|
2970
|
+
});
|
|
2971
|
+
throw new Error(`Worker "${workerId}" not found when decrementing workload.`);
|
|
2972
|
+
}
|
|
2973
|
+
return currentWorker;
|
|
2974
|
+
}
|
|
2975
|
+
function resolvePreviousWorkload(workerId, currentWorker, workerFromExecution) {
|
|
2976
|
+
const workloadFromWorker = workerFromExecution?.currentWorkload;
|
|
2977
|
+
if (typeof workloadFromWorker === "number" && Number.isFinite(workloadFromWorker)) {
|
|
2978
|
+
return workloadFromWorker;
|
|
2979
|
+
}
|
|
2980
|
+
if (typeof currentWorker.currentWorkload === "number" && Number.isFinite(currentWorker.currentWorkload)) {
|
|
2981
|
+
return currentWorker.currentWorkload;
|
|
2982
|
+
}
|
|
2983
|
+
logger2.error("Worker workload is not a valid number; cannot decrement", {
|
|
2984
|
+
workerId,
|
|
2985
|
+
workloadFromState: currentWorker.currentWorkload,
|
|
2986
|
+
...typeof workloadFromWorker === "number" ? { workloadFromWorker } : {}
|
|
2987
|
+
});
|
|
2988
|
+
throw new Error(
|
|
2989
|
+
`Worker "${workerId}" does not have a valid numeric currentWorkload to decrement.`
|
|
2990
|
+
);
|
|
2991
|
+
}
|
|
2992
|
+
function mergeWorkersWithDecrement(state, workerId, executionResult) {
|
|
2993
|
+
const currentWorker = getStateWorkerOrThrow(state, workerId);
|
|
2994
|
+
const executionResultWorkers = executionResult.workers || {};
|
|
2995
|
+
const baseWorkers = {
|
|
2996
|
+
...state.workers,
|
|
2997
|
+
...executionResultWorkers
|
|
2998
|
+
};
|
|
2999
|
+
const workerFromExecution = executionResultWorkers[workerId];
|
|
3000
|
+
const previousWorkload = resolvePreviousWorkload(
|
|
3001
|
+
workerId,
|
|
3002
|
+
currentWorker,
|
|
3003
|
+
workerFromExecution
|
|
3004
|
+
);
|
|
3005
|
+
const hasWorkerOverride = typeof workerFromExecution === "object" && workerFromExecution !== null;
|
|
3006
|
+
const updatedWorker = hasWorkerOverride ? {
|
|
3007
|
+
...currentWorker,
|
|
3008
|
+
...workerFromExecution,
|
|
3009
|
+
currentWorkload: Math.max(0, previousWorkload - 1)
|
|
3010
|
+
} : {
|
|
3011
|
+
...currentWorker,
|
|
3012
|
+
currentWorkload: Math.max(0, previousWorkload - 1)
|
|
3013
|
+
};
|
|
3014
|
+
const updatedWorkers = {
|
|
3015
|
+
...baseWorkers,
|
|
3016
|
+
[workerId]: updatedWorker
|
|
3017
|
+
};
|
|
3018
|
+
logger2.debug("Worker workload decremented", {
|
|
3019
|
+
workerId,
|
|
3020
|
+
previousWorkload,
|
|
3021
|
+
newWorkload: updatedWorkers[workerId].currentWorkload,
|
|
3022
|
+
hadExecutionResultWorkers: !!executionResult.workers
|
|
3023
|
+
});
|
|
3024
|
+
return updatedWorkers;
|
|
3025
|
+
}
|
|
3026
|
+
function decrementWorkerOnError(state, workerId) {
|
|
3027
|
+
const currentWorker = getStateWorkerOrThrow(state, workerId);
|
|
3028
|
+
const previousWorkload = resolvePreviousWorkload(workerId, currentWorker);
|
|
3029
|
+
const updatedWorkers = {
|
|
3030
|
+
...state.workers,
|
|
3031
|
+
[workerId]: {
|
|
3032
|
+
...currentWorker,
|
|
3033
|
+
currentWorkload: Math.max(0, previousWorkload - 1)
|
|
3034
|
+
}
|
|
3035
|
+
};
|
|
3036
|
+
logger2.debug("Worker workload decremented (error path)", {
|
|
3037
|
+
workerId,
|
|
3038
|
+
previousWorkload,
|
|
3039
|
+
newWorkload: updatedWorkers[workerId].currentWorkload
|
|
3040
|
+
});
|
|
3041
|
+
return updatedWorkers;
|
|
3042
|
+
}
|
|
3043
|
+
function createErrorTaskResult(assignment, workerId, errorMessage) {
|
|
3044
|
+
logger2.warn("Creating error result for assignment", {
|
|
3045
|
+
workerId,
|
|
3046
|
+
assignmentId: assignment.id
|
|
3047
|
+
});
|
|
3048
|
+
return {
|
|
3049
|
+
assignmentId: assignment.id,
|
|
3050
|
+
workerId,
|
|
3051
|
+
success: false,
|
|
3052
|
+
result: "",
|
|
3053
|
+
error: errorMessage,
|
|
3054
|
+
completedAt: Date.now()
|
|
3055
|
+
};
|
|
3056
|
+
}
|
|
2760
3057
|
function createWorkerNode(config) {
|
|
2761
|
-
const {
|
|
2762
|
-
id,
|
|
2763
|
-
capabilities,
|
|
2764
|
-
model,
|
|
2765
|
-
tools = [],
|
|
2766
|
-
systemPrompt,
|
|
2767
|
-
verbose = false,
|
|
2768
|
-
executeFn,
|
|
2769
|
-
agent
|
|
2770
|
-
} = config;
|
|
3058
|
+
const { id, model, executeFn, agent } = config;
|
|
2771
3059
|
return async (state, runConfig) => {
|
|
2772
3060
|
try {
|
|
2773
3061
|
logger2.info("Worker node executing", {
|
|
@@ -2775,9 +3063,7 @@ function createWorkerNode(config) {
|
|
|
2775
3063
|
iteration: state.iteration,
|
|
2776
3064
|
activeAssignments: state.activeAssignments.length
|
|
2777
3065
|
});
|
|
2778
|
-
const currentAssignment = state
|
|
2779
|
-
(assignment) => assignment.workerId === id && !state.completedTasks.some((task) => task.assignmentId === assignment.id)
|
|
2780
|
-
);
|
|
3066
|
+
const currentAssignment = findCurrentAssignment(state, id);
|
|
2781
3067
|
if (!currentAssignment) {
|
|
2782
3068
|
logger2.debug("No active assignment found for worker", {
|
|
2783
3069
|
workerId: id,
|
|
@@ -2789,105 +3075,35 @@ function createWorkerNode(config) {
|
|
|
2789
3075
|
logger2.info("Worker processing assignment", {
|
|
2790
3076
|
workerId: id,
|
|
2791
3077
|
assignmentId: currentAssignment.id,
|
|
2792
|
-
taskLength: currentAssignment.task.length
|
|
2793
|
-
|
|
3078
|
+
taskLength: currentAssignment.task.length
|
|
3079
|
+
});
|
|
3080
|
+
logger2.debug("Worker assignment details", {
|
|
3081
|
+
workerId: id,
|
|
3082
|
+
assignmentId: currentAssignment.id,
|
|
3083
|
+
taskLength: currentAssignment.task.length
|
|
2794
3084
|
});
|
|
2795
|
-
async function executeWithLLM() {
|
|
2796
|
-
logger2.debug("Using default LLM execution", {
|
|
2797
|
-
workerId: id,
|
|
2798
|
-
hasTools: tools.length > 0,
|
|
2799
|
-
toolCount: tools.length
|
|
2800
|
-
});
|
|
2801
|
-
const defaultSystemPrompt = `You are a specialized worker agent with the following capabilities:
|
|
2802
|
-
Skills: ${capabilities.skills.join(", ")}
|
|
2803
|
-
Tools: ${capabilities.tools.join(", ")}
|
|
2804
|
-
|
|
2805
|
-
Execute the assigned task using your skills and tools. Provide a clear, actionable result.`;
|
|
2806
|
-
const messages = [
|
|
2807
|
-
new SystemMessage5(systemPrompt || defaultSystemPrompt),
|
|
2808
|
-
new HumanMessage5(currentAssignment.task)
|
|
2809
|
-
];
|
|
2810
|
-
let modelToUse = model;
|
|
2811
|
-
if (tools.length > 0 && model.bindTools) {
|
|
2812
|
-
logger2.debug("Binding tools to model", {
|
|
2813
|
-
workerId: id,
|
|
2814
|
-
toolCount: tools.length,
|
|
2815
|
-
toolNames: tools.map((t) => t.metadata.name)
|
|
2816
|
-
});
|
|
2817
|
-
const langchainTools = convertWorkerToolsForLangChain(tools);
|
|
2818
|
-
modelToUse = model.bindTools(langchainTools);
|
|
2819
|
-
}
|
|
2820
|
-
logger2.debug("Invoking LLM", { workerId: id });
|
|
2821
|
-
const response = await modelToUse.invoke(messages);
|
|
2822
|
-
const result = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
2823
|
-
logger2.info("Worker task completed", {
|
|
2824
|
-
workerId: id,
|
|
2825
|
-
assignmentId: currentAssignment.id,
|
|
2826
|
-
resultLength: result.length,
|
|
2827
|
-
resultPreview: result.substring(0, 100)
|
|
2828
|
-
});
|
|
2829
|
-
const taskResult = {
|
|
2830
|
-
assignmentId: currentAssignment.id,
|
|
2831
|
-
workerId: id,
|
|
2832
|
-
success: true,
|
|
2833
|
-
result,
|
|
2834
|
-
completedAt: Date.now(),
|
|
2835
|
-
metadata: {
|
|
2836
|
-
skills_used: capabilities.skills
|
|
2837
|
-
}
|
|
2838
|
-
};
|
|
2839
|
-
const message = {
|
|
2840
|
-
id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
2841
|
-
from: id,
|
|
2842
|
-
to: ["supervisor"],
|
|
2843
|
-
type: "task_result",
|
|
2844
|
-
content: result,
|
|
2845
|
-
timestamp: Date.now(),
|
|
2846
|
-
metadata: {
|
|
2847
|
-
assignmentId: currentAssignment.id,
|
|
2848
|
-
success: true
|
|
2849
|
-
}
|
|
2850
|
-
};
|
|
2851
|
-
return {
|
|
2852
|
-
completedTasks: [taskResult],
|
|
2853
|
-
messages: [message]
|
|
2854
|
-
};
|
|
2855
|
-
}
|
|
2856
3085
|
let executionResult;
|
|
2857
3086
|
if (executeFn) {
|
|
2858
3087
|
logger2.debug("Using custom execution function", { workerId: id });
|
|
2859
3088
|
executionResult = await executeFn(state, runConfig);
|
|
2860
3089
|
} else if (agent && isReActAgent(agent)) {
|
|
2861
3090
|
logger2.debug("Using ReAct agent", { workerId: id });
|
|
2862
|
-
const wrappedFn = wrapReActAgent(id, agent, verbose);
|
|
3091
|
+
const wrappedFn = wrapReActAgent(id, agent, config.verbose ?? false);
|
|
2863
3092
|
executionResult = await wrappedFn(state, runConfig);
|
|
2864
3093
|
} else if (model) {
|
|
2865
|
-
|
|
3094
|
+
logger2.debug("Using default LLM execution", {
|
|
3095
|
+
workerId: id,
|
|
3096
|
+
hasTools: (config.tools ?? []).length > 0,
|
|
3097
|
+
toolCount: (config.tools ?? []).length
|
|
3098
|
+
});
|
|
3099
|
+
executionResult = await invokeWorkerModel(model, config, currentAssignment);
|
|
2866
3100
|
} else {
|
|
2867
3101
|
logger2.error("Worker missing required configuration", { workerId: id });
|
|
2868
3102
|
throw new Error(
|
|
2869
3103
|
`Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
|
|
2870
3104
|
);
|
|
2871
3105
|
}
|
|
2872
|
-
const
|
|
2873
|
-
const baseWorkers = {
|
|
2874
|
-
...state.workers,
|
|
2875
|
-
...executionResult.workers || {}
|
|
2876
|
-
};
|
|
2877
|
-
const workerToUpdate = baseWorkers[id] || currentWorker;
|
|
2878
|
-
const updatedWorkers = {
|
|
2879
|
-
...baseWorkers,
|
|
2880
|
-
[id]: {
|
|
2881
|
-
...workerToUpdate,
|
|
2882
|
-
currentWorkload: Math.max(0, workerToUpdate.currentWorkload - 1)
|
|
2883
|
-
}
|
|
2884
|
-
};
|
|
2885
|
-
logger2.debug("Worker workload decremented", {
|
|
2886
|
-
workerId: id,
|
|
2887
|
-
previousWorkload: workerToUpdate.currentWorkload,
|
|
2888
|
-
newWorkload: updatedWorkers[id].currentWorkload,
|
|
2889
|
-
hadExecutionResultWorkers: !!executionResult.workers
|
|
2890
|
-
});
|
|
3106
|
+
const updatedWorkers = mergeWorkersWithDecrement(state, id, executionResult);
|
|
2891
3107
|
return {
|
|
2892
3108
|
...executionResult,
|
|
2893
3109
|
workers: updatedWorkers
|
|
@@ -2898,41 +3114,29 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
|
|
|
2898
3114
|
workerId: id,
|
|
2899
3115
|
error: errorMessage
|
|
2900
3116
|
});
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
3117
|
+
let updatedWorkers;
|
|
3118
|
+
try {
|
|
3119
|
+
updatedWorkers = decrementWorkerOnError(state, id);
|
|
3120
|
+
} catch (workloadError) {
|
|
3121
|
+
const workloadErrorMessage = workloadError instanceof Error ? workloadError.message : String(workloadError);
|
|
3122
|
+
logger2.error("Worker error handling failed", {
|
|
3123
|
+
workerId: id,
|
|
3124
|
+
error: workloadErrorMessage
|
|
3125
|
+
});
|
|
3126
|
+
return {
|
|
3127
|
+
status: "failed",
|
|
3128
|
+
error: `${errorMessage}. ${workloadErrorMessage}`
|
|
3129
|
+
};
|
|
3130
|
+
}
|
|
2914
3131
|
const currentAssignment = state.activeAssignments.find(
|
|
2915
3132
|
(assignment) => assignment.workerId === id
|
|
2916
3133
|
);
|
|
2917
3134
|
if (currentAssignment) {
|
|
2918
|
-
logger2.warn("Creating error result for assignment", {
|
|
2919
|
-
workerId: id,
|
|
2920
|
-
assignmentId: currentAssignment.id
|
|
2921
|
-
});
|
|
2922
|
-
const errorResult = {
|
|
2923
|
-
assignmentId: currentAssignment.id,
|
|
2924
|
-
workerId: id,
|
|
2925
|
-
success: false,
|
|
2926
|
-
result: "",
|
|
2927
|
-
error: errorMessage,
|
|
2928
|
-
completedAt: Date.now()
|
|
2929
|
-
};
|
|
2930
3135
|
return {
|
|
2931
|
-
completedTasks: [
|
|
3136
|
+
completedTasks: [createErrorTaskResult(currentAssignment, id, errorMessage)],
|
|
2932
3137
|
currentAgent: "supervisor",
|
|
2933
3138
|
status: "routing",
|
|
2934
3139
|
workers: updatedWorkers
|
|
2935
|
-
// Include workload update
|
|
2936
3140
|
};
|
|
2937
3141
|
}
|
|
2938
3142
|
logger2.error("No assignment found for error handling", { workerId: id });
|
|
@@ -2940,95 +3144,6 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
|
|
|
2940
3144
|
status: "failed",
|
|
2941
3145
|
error: errorMessage,
|
|
2942
3146
|
workers: updatedWorkers
|
|
2943
|
-
// Include workload update even on failure
|
|
2944
|
-
};
|
|
2945
|
-
}
|
|
2946
|
-
};
|
|
2947
|
-
}
|
|
2948
|
-
function createAggregatorNode(config = {}) {
|
|
2949
|
-
const {
|
|
2950
|
-
model,
|
|
2951
|
-
systemPrompt = DEFAULT_AGGREGATOR_SYSTEM_PROMPT,
|
|
2952
|
-
aggregateFn,
|
|
2953
|
-
verbose = false
|
|
2954
|
-
} = config;
|
|
2955
|
-
return async (state) => {
|
|
2956
|
-
try {
|
|
2957
|
-
logger2.info("Aggregator node executing", {
|
|
2958
|
-
completedTasks: state.completedTasks.length,
|
|
2959
|
-
successfulTasks: state.completedTasks.filter((t) => t.success).length,
|
|
2960
|
-
failedTasks: state.completedTasks.filter((t) => !t.success).length
|
|
2961
|
-
});
|
|
2962
|
-
logger2.debug("Combining results from workers");
|
|
2963
|
-
if (aggregateFn) {
|
|
2964
|
-
logger2.debug("Using custom aggregation function");
|
|
2965
|
-
const response2 = await aggregateFn(state);
|
|
2966
|
-
logger2.info("Custom aggregation complete", {
|
|
2967
|
-
responseLength: response2.length
|
|
2968
|
-
});
|
|
2969
|
-
return {
|
|
2970
|
-
response: response2,
|
|
2971
|
-
status: "completed"
|
|
2972
|
-
};
|
|
2973
|
-
}
|
|
2974
|
-
if (state.completedTasks.length === 0) {
|
|
2975
|
-
logger2.warn("No completed tasks to aggregate");
|
|
2976
|
-
return {
|
|
2977
|
-
response: "No tasks were completed.",
|
|
2978
|
-
status: "completed"
|
|
2979
|
-
};
|
|
2980
|
-
}
|
|
2981
|
-
if (!model) {
|
|
2982
|
-
logger2.debug("No model provided, concatenating results");
|
|
2983
|
-
const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
|
|
2984
|
-
logger2.info("Simple concatenation complete", {
|
|
2985
|
-
resultLength: combinedResults.length
|
|
2986
|
-
});
|
|
2987
|
-
return {
|
|
2988
|
-
response: combinedResults || "No successful results to aggregate.",
|
|
2989
|
-
status: "completed"
|
|
2990
|
-
};
|
|
2991
|
-
}
|
|
2992
|
-
logger2.debug("Using LLM for intelligent aggregation", {
|
|
2993
|
-
taskCount: state.completedTasks.length
|
|
2994
|
-
});
|
|
2995
|
-
const taskResults = state.completedTasks.map((task, idx) => {
|
|
2996
|
-
const status = task.success ? "\u2713" : "\u2717";
|
|
2997
|
-
const result = task.success ? task.result : `Error: ${task.error}`;
|
|
2998
|
-
return `${idx + 1}. [${status}] Worker ${task.workerId}:
|
|
2999
|
-
${result}`;
|
|
3000
|
-
}).join("\n\n");
|
|
3001
|
-
const userPrompt = `Original query: ${state.input}
|
|
3002
|
-
|
|
3003
|
-
Worker results:
|
|
3004
|
-
${taskResults}
|
|
3005
|
-
|
|
3006
|
-
Please synthesize these results into a comprehensive response that addresses the original query.`;
|
|
3007
|
-
const messages = [
|
|
3008
|
-
new SystemMessage5(systemPrompt),
|
|
3009
|
-
new HumanMessage5(userPrompt)
|
|
3010
|
-
];
|
|
3011
|
-
logger2.debug("Invoking aggregation LLM");
|
|
3012
|
-
const response = await model.invoke(messages);
|
|
3013
|
-
const aggregatedResponse = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
3014
|
-
logger2.info("Aggregation complete", {
|
|
3015
|
-
responseLength: aggregatedResponse.length,
|
|
3016
|
-
responsePreview: aggregatedResponse.substring(0, 100)
|
|
3017
|
-
});
|
|
3018
|
-
logger2.debug("Aggregation complete");
|
|
3019
|
-
return {
|
|
3020
|
-
response: aggregatedResponse,
|
|
3021
|
-
status: "completed"
|
|
3022
|
-
};
|
|
3023
|
-
} catch (error) {
|
|
3024
|
-
logger2.error("Aggregator node error", {
|
|
3025
|
-
error: error instanceof Error ? error.message : String(error),
|
|
3026
|
-
...error instanceof Error && error.stack ? { stack: error.stack } : {},
|
|
3027
|
-
completedTasks: state.completedTasks.length
|
|
3028
|
-
});
|
|
3029
|
-
return {
|
|
3030
|
-
status: "failed",
|
|
3031
|
-
error: error instanceof Error ? error.message : "Unknown error in aggregator"
|
|
3032
3147
|
};
|
|
3033
3148
|
}
|
|
3034
3149
|
};
|