@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.cjs
CHANGED
|
@@ -2679,16 +2679,82 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2679
2679
|
};
|
|
2680
2680
|
}
|
|
2681
2681
|
|
|
2682
|
-
// src/multi-agent/nodes.ts
|
|
2682
|
+
// src/multi-agent/nodes/shared.ts
|
|
2683
2683
|
var import_messages5 = require("@langchain/core/messages");
|
|
2684
2684
|
var import_core8 = require("@agentforge/core");
|
|
2685
2685
|
var logger2 = createPatternLogger("agentforge:patterns:multi-agent:nodes");
|
|
2686
|
+
function createGeneratedId(prefix) {
|
|
2687
|
+
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
2688
|
+
}
|
|
2689
|
+
function createTaskAssignmentId() {
|
|
2690
|
+
return `task_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
2691
|
+
}
|
|
2692
|
+
function getLatestTaskContent(state) {
|
|
2693
|
+
return state.messages[state.messages.length - 1]?.content || state.input;
|
|
2694
|
+
}
|
|
2695
|
+
function createTaskAssignments(workerIds, task) {
|
|
2696
|
+
return workerIds.map((workerId) => ({
|
|
2697
|
+
id: createTaskAssignmentId(),
|
|
2698
|
+
workerId,
|
|
2699
|
+
task,
|
|
2700
|
+
priority: 5,
|
|
2701
|
+
assignedAt: Date.now()
|
|
2702
|
+
}));
|
|
2703
|
+
}
|
|
2704
|
+
function createAssignmentMessages(assignments) {
|
|
2705
|
+
return assignments.map((assignment) => ({
|
|
2706
|
+
id: createGeneratedId("msg"),
|
|
2707
|
+
from: "supervisor",
|
|
2708
|
+
to: [assignment.workerId],
|
|
2709
|
+
type: "task_assignment",
|
|
2710
|
+
content: assignment.task,
|
|
2711
|
+
timestamp: Date.now(),
|
|
2712
|
+
metadata: {
|
|
2713
|
+
assignmentId: assignment.id,
|
|
2714
|
+
priority: assignment.priority
|
|
2715
|
+
}
|
|
2716
|
+
}));
|
|
2717
|
+
}
|
|
2718
|
+
function findCurrentAssignment(state, workerId) {
|
|
2719
|
+
return state.activeAssignments.find(
|
|
2720
|
+
(assignment) => assignment.workerId === workerId && !state.completedTasks.some((task) => task.assignmentId === assignment.id)
|
|
2721
|
+
);
|
|
2722
|
+
}
|
|
2686
2723
|
function convertWorkerToolsForLangChain(tools) {
|
|
2687
2724
|
const safeTools = tools ?? [];
|
|
2688
|
-
return (0, import_core8.toLangChainTools)(
|
|
2689
|
-
safeTools
|
|
2690
|
-
);
|
|
2725
|
+
return (0, import_core8.toLangChainTools)(safeTools);
|
|
2691
2726
|
}
|
|
2727
|
+
function serializeModelContent(content) {
|
|
2728
|
+
if (typeof content === "string") {
|
|
2729
|
+
return content;
|
|
2730
|
+
}
|
|
2731
|
+
try {
|
|
2732
|
+
const serialized = JSON.stringify(content);
|
|
2733
|
+
if (typeof serialized !== "string") {
|
|
2734
|
+
const error = new Error(
|
|
2735
|
+
"Failed to serialize model content: JSON.stringify returned undefined"
|
|
2736
|
+
);
|
|
2737
|
+
logger2.error("Model content serialization failed", {
|
|
2738
|
+
errorMessage: error.message,
|
|
2739
|
+
contentType: content === null ? "null" : typeof content
|
|
2740
|
+
});
|
|
2741
|
+
throw error;
|
|
2742
|
+
}
|
|
2743
|
+
return serialized;
|
|
2744
|
+
} catch (error) {
|
|
2745
|
+
const normalizedError = error instanceof Error ? error : new Error("Unknown error during model content serialization");
|
|
2746
|
+
logger2.error("Model content serialization threw an error", {
|
|
2747
|
+
errorMessage: normalizedError.message,
|
|
2748
|
+
contentType: content === null ? "null" : typeof content
|
|
2749
|
+
});
|
|
2750
|
+
throw normalizedError;
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
function createPromptMessages(systemPrompt, task) {
|
|
2754
|
+
return [new import_messages5.SystemMessage(systemPrompt), new import_messages5.HumanMessage(task)];
|
|
2755
|
+
}
|
|
2756
|
+
|
|
2757
|
+
// src/multi-agent/nodes/aggregator.ts
|
|
2692
2758
|
var DEFAULT_AGGREGATOR_SYSTEM_PROMPT = `You are an aggregator agent responsible for combining results from multiple worker agents.
|
|
2693
2759
|
|
|
2694
2760
|
Your job is to:
|
|
@@ -2698,12 +2764,124 @@ Your job is to:
|
|
|
2698
2764
|
4. Provide a clear, comprehensive final answer
|
|
2699
2765
|
|
|
2700
2766
|
Be concise but thorough in your aggregation.`;
|
|
2701
|
-
function
|
|
2767
|
+
function createAggregationPrompt(state) {
|
|
2768
|
+
const taskResults = state.completedTasks.map((task, idx) => {
|
|
2769
|
+
const status = task.success ? "\u2713" : "\u2717";
|
|
2770
|
+
const result = task.success ? task.result : `Error: ${task.error}`;
|
|
2771
|
+
return `${idx + 1}. [${status}] Worker ${task.workerId}:
|
|
2772
|
+
${result}`;
|
|
2773
|
+
}).join("\n\n");
|
|
2774
|
+
return `Original query: ${state.input}
|
|
2775
|
+
|
|
2776
|
+
Worker results:
|
|
2777
|
+
${taskResults}
|
|
2778
|
+
|
|
2779
|
+
Please synthesize these results into a comprehensive response that addresses the original query.`;
|
|
2780
|
+
}
|
|
2781
|
+
function createAggregatorNode(config = {}) {
|
|
2702
2782
|
const {
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2783
|
+
model,
|
|
2784
|
+
systemPrompt = DEFAULT_AGGREGATOR_SYSTEM_PROMPT,
|
|
2785
|
+
aggregateFn
|
|
2706
2786
|
} = config;
|
|
2787
|
+
return async (state) => {
|
|
2788
|
+
try {
|
|
2789
|
+
logger2.info("Aggregator node executing", {
|
|
2790
|
+
completedTasks: state.completedTasks.length,
|
|
2791
|
+
successfulTasks: state.completedTasks.filter((task) => task.success).length,
|
|
2792
|
+
failedTasks: state.completedTasks.filter((task) => !task.success).length
|
|
2793
|
+
});
|
|
2794
|
+
logger2.debug("Combining results from workers");
|
|
2795
|
+
if (aggregateFn) {
|
|
2796
|
+
logger2.debug("Using custom aggregation function");
|
|
2797
|
+
const response2 = await aggregateFn(state);
|
|
2798
|
+
logger2.info("Custom aggregation complete", {
|
|
2799
|
+
responseLength: response2.length
|
|
2800
|
+
});
|
|
2801
|
+
return {
|
|
2802
|
+
response: response2,
|
|
2803
|
+
status: "completed"
|
|
2804
|
+
};
|
|
2805
|
+
}
|
|
2806
|
+
if (state.completedTasks.length === 0) {
|
|
2807
|
+
logger2.warn("No completed tasks to aggregate");
|
|
2808
|
+
return {
|
|
2809
|
+
response: "No tasks were completed.",
|
|
2810
|
+
status: "completed"
|
|
2811
|
+
};
|
|
2812
|
+
}
|
|
2813
|
+
if (!model) {
|
|
2814
|
+
logger2.debug("No model provided, concatenating results");
|
|
2815
|
+
const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
|
|
2816
|
+
logger2.info("Simple concatenation complete", {
|
|
2817
|
+
resultLength: combinedResults.length
|
|
2818
|
+
});
|
|
2819
|
+
return {
|
|
2820
|
+
response: combinedResults || "No successful results to aggregate.",
|
|
2821
|
+
status: "completed"
|
|
2822
|
+
};
|
|
2823
|
+
}
|
|
2824
|
+
logger2.debug("Using LLM for intelligent aggregation", {
|
|
2825
|
+
taskCount: state.completedTasks.length
|
|
2826
|
+
});
|
|
2827
|
+
const messages = createPromptMessages(systemPrompt, createAggregationPrompt(state));
|
|
2828
|
+
logger2.debug("Invoking aggregation LLM");
|
|
2829
|
+
const response = await model.invoke(messages);
|
|
2830
|
+
const aggregatedResponse = serializeModelContent(response.content);
|
|
2831
|
+
logger2.info("Aggregation complete", {
|
|
2832
|
+
responseLength: aggregatedResponse.length
|
|
2833
|
+
});
|
|
2834
|
+
logger2.debug("Aggregation response details", {
|
|
2835
|
+
responseLength: aggregatedResponse.length
|
|
2836
|
+
});
|
|
2837
|
+
logger2.debug("Aggregation complete");
|
|
2838
|
+
return {
|
|
2839
|
+
response: aggregatedResponse,
|
|
2840
|
+
status: "completed"
|
|
2841
|
+
};
|
|
2842
|
+
} catch (error) {
|
|
2843
|
+
const errorMessage = handleNodeError(error, "aggregator", false);
|
|
2844
|
+
logger2.error("Aggregator node error", {
|
|
2845
|
+
error: errorMessage,
|
|
2846
|
+
...error instanceof Error && error.stack ? { stack: error.stack } : {},
|
|
2847
|
+
completedTasks: state.completedTasks.length
|
|
2848
|
+
});
|
|
2849
|
+
return {
|
|
2850
|
+
status: "failed",
|
|
2851
|
+
error: errorMessage
|
|
2852
|
+
};
|
|
2853
|
+
}
|
|
2854
|
+
};
|
|
2855
|
+
}
|
|
2856
|
+
|
|
2857
|
+
// src/multi-agent/nodes/supervisor.ts
|
|
2858
|
+
function allAssignmentsCompleted(state) {
|
|
2859
|
+
return state.activeAssignments.every(
|
|
2860
|
+
(assignment) => state.completedTasks.some((task) => task.assignmentId === assignment.id)
|
|
2861
|
+
);
|
|
2862
|
+
}
|
|
2863
|
+
function incrementAssignedWorkerLoads(state, assignments) {
|
|
2864
|
+
const updatedWorkers = { ...state.workers };
|
|
2865
|
+
for (const assignment of assignments) {
|
|
2866
|
+
const worker = updatedWorkers[assignment.workerId];
|
|
2867
|
+
if (!worker) {
|
|
2868
|
+
logger2.error("Worker not found in state", {
|
|
2869
|
+
workerId: assignment.workerId,
|
|
2870
|
+
availableWorkers: Object.keys(updatedWorkers)
|
|
2871
|
+
});
|
|
2872
|
+
throw new Error(
|
|
2873
|
+
`Worker ${assignment.workerId} not found in state.workers. Available workers: ${Object.keys(updatedWorkers).join(", ")}`
|
|
2874
|
+
);
|
|
2875
|
+
}
|
|
2876
|
+
updatedWorkers[assignment.workerId] = {
|
|
2877
|
+
...worker,
|
|
2878
|
+
currentWorkload: worker.currentWorkload + 1
|
|
2879
|
+
};
|
|
2880
|
+
}
|
|
2881
|
+
return updatedWorkers;
|
|
2882
|
+
}
|
|
2883
|
+
function createSupervisorNode(config) {
|
|
2884
|
+
const { strategy, maxIterations = 10 } = config;
|
|
2707
2885
|
return async (state) => {
|
|
2708
2886
|
try {
|
|
2709
2887
|
logger2.info("Supervisor node executing", {
|
|
@@ -2724,9 +2902,7 @@ function createSupervisorNode(config) {
|
|
|
2724
2902
|
currentAgent: "aggregator"
|
|
2725
2903
|
};
|
|
2726
2904
|
}
|
|
2727
|
-
const allCompleted = state
|
|
2728
|
-
(assignment) => state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
|
|
2729
|
-
);
|
|
2905
|
+
const allCompleted = allAssignmentsCompleted(state);
|
|
2730
2906
|
logger2.debug("Checking task completion", {
|
|
2731
2907
|
activeAssignments: state.activeAssignments.length,
|
|
2732
2908
|
completedTasks: state.completedTasks.length,
|
|
@@ -2764,6 +2940,7 @@ function createSupervisorNode(config) {
|
|
|
2764
2940
|
reasoning: decision.reasoning,
|
|
2765
2941
|
confidence: decision.confidence
|
|
2766
2942
|
});
|
|
2943
|
+
logger2.debug(`Routing to ${targetAgents[0]}: ${decision.reasoning}`);
|
|
2767
2944
|
} else {
|
|
2768
2945
|
logger2.info("Routing to multiple agents in parallel", {
|
|
2769
2946
|
targetAgents,
|
|
@@ -2771,57 +2948,22 @@ function createSupervisorNode(config) {
|
|
|
2771
2948
|
reasoning: decision.reasoning,
|
|
2772
2949
|
confidence: decision.confidence
|
|
2773
2950
|
});
|
|
2951
|
+
logger2.debug(
|
|
2952
|
+
`Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`
|
|
2953
|
+
);
|
|
2774
2954
|
}
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
} else {
|
|
2778
|
-
logger2.debug(`Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`);
|
|
2779
|
-
}
|
|
2780
|
-
const task = state.messages[state.messages.length - 1]?.content || state.input;
|
|
2781
|
-
const assignments = targetAgents.map((workerId) => ({
|
|
2782
|
-
id: `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
2783
|
-
workerId,
|
|
2784
|
-
task,
|
|
2785
|
-
priority: 5,
|
|
2786
|
-
assignedAt: Date.now()
|
|
2787
|
-
}));
|
|
2955
|
+
const task = getLatestTaskContent(state);
|
|
2956
|
+
const assignments = createTaskAssignments(targetAgents, task);
|
|
2788
2957
|
logger2.debug("Created task assignments", {
|
|
2789
2958
|
assignmentCount: assignments.length,
|
|
2790
|
-
assignments: assignments.map((
|
|
2791
|
-
id:
|
|
2792
|
-
workerId:
|
|
2793
|
-
taskLength:
|
|
2959
|
+
assignments: assignments.map((assignment) => ({
|
|
2960
|
+
id: assignment.id,
|
|
2961
|
+
workerId: assignment.workerId,
|
|
2962
|
+
taskLength: assignment.task.length
|
|
2794
2963
|
}))
|
|
2795
2964
|
});
|
|
2796
|
-
const messages = assignments
|
|
2797
|
-
|
|
2798
|
-
from: "supervisor",
|
|
2799
|
-
to: [assignment.workerId],
|
|
2800
|
-
type: "task_assignment",
|
|
2801
|
-
content: assignment.task,
|
|
2802
|
-
timestamp: Date.now(),
|
|
2803
|
-
metadata: {
|
|
2804
|
-
assignmentId: assignment.id,
|
|
2805
|
-
priority: assignment.priority
|
|
2806
|
-
}
|
|
2807
|
-
}));
|
|
2808
|
-
const updatedWorkers = { ...state.workers };
|
|
2809
|
-
for (const assignment of assignments) {
|
|
2810
|
-
const worker = updatedWorkers[assignment.workerId];
|
|
2811
|
-
if (!worker) {
|
|
2812
|
-
logger2.error("Worker not found in state", {
|
|
2813
|
-
workerId: assignment.workerId,
|
|
2814
|
-
availableWorkers: Object.keys(updatedWorkers)
|
|
2815
|
-
});
|
|
2816
|
-
throw new Error(
|
|
2817
|
-
`Worker ${assignment.workerId} not found in state.workers. Available workers: ${Object.keys(updatedWorkers).join(", ")}`
|
|
2818
|
-
);
|
|
2819
|
-
}
|
|
2820
|
-
updatedWorkers[assignment.workerId] = {
|
|
2821
|
-
...worker,
|
|
2822
|
-
currentWorkload: worker.currentWorkload + 1
|
|
2823
|
-
};
|
|
2824
|
-
}
|
|
2965
|
+
const messages = createAssignmentMessages(assignments);
|
|
2966
|
+
const updatedWorkers = incrementAssignedWorkerLoads(state, assignments);
|
|
2825
2967
|
logger2.info("Supervisor routing complete", {
|
|
2826
2968
|
currentAgent: targetAgents.join(","),
|
|
2827
2969
|
status: "executing",
|
|
@@ -2831,41 +2973,187 @@ function createSupervisorNode(config) {
|
|
|
2831
2973
|
});
|
|
2832
2974
|
return {
|
|
2833
2975
|
currentAgent: targetAgents.join(","),
|
|
2834
|
-
// Store all agents (for backward compat)
|
|
2835
2976
|
status: "executing",
|
|
2836
2977
|
routingHistory: [decision],
|
|
2837
2978
|
activeAssignments: assignments,
|
|
2838
|
-
// Multiple assignments for parallel execution!
|
|
2839
2979
|
messages,
|
|
2840
2980
|
workers: updatedWorkers,
|
|
2841
|
-
// Include updated workload
|
|
2842
|
-
// Add 1 to iteration counter (uses additive reducer)
|
|
2843
2981
|
iteration: 1
|
|
2844
2982
|
};
|
|
2845
2983
|
} catch (error) {
|
|
2984
|
+
const errorMessage = handleNodeError(error, "supervisor", false);
|
|
2846
2985
|
logger2.error("Supervisor node error", {
|
|
2847
|
-
error:
|
|
2986
|
+
error: errorMessage,
|
|
2848
2987
|
...error instanceof Error && error.stack ? { stack: error.stack } : {},
|
|
2849
2988
|
iteration: state.iteration
|
|
2850
2989
|
});
|
|
2851
2990
|
return {
|
|
2852
2991
|
status: "failed",
|
|
2853
|
-
error:
|
|
2992
|
+
error: errorMessage
|
|
2854
2993
|
};
|
|
2855
2994
|
}
|
|
2856
2995
|
};
|
|
2857
2996
|
}
|
|
2997
|
+
|
|
2998
|
+
// src/multi-agent/nodes/worker.ts
|
|
2999
|
+
function buildDefaultSystemPrompt(config) {
|
|
3000
|
+
return `You are a specialized worker agent with the following capabilities:
|
|
3001
|
+
Skills: ${config.capabilities.skills.join(", ")}
|
|
3002
|
+
Tools: ${config.capabilities.tools.join(", ")}
|
|
3003
|
+
|
|
3004
|
+
Execute the assigned task using your skills and tools. Provide a clear, actionable result.`;
|
|
3005
|
+
}
|
|
3006
|
+
async function invokeWorkerModel(model, config, assignment) {
|
|
3007
|
+
const messages = createPromptMessages(
|
|
3008
|
+
config.systemPrompt || buildDefaultSystemPrompt(config),
|
|
3009
|
+
assignment.task
|
|
3010
|
+
);
|
|
3011
|
+
let modelToUse = model;
|
|
3012
|
+
if (config.tools && config.tools.length > 0 && model.bindTools) {
|
|
3013
|
+
logger2.debug("Binding tools to model", {
|
|
3014
|
+
workerId: config.id,
|
|
3015
|
+
toolCount: config.tools.length,
|
|
3016
|
+
toolNames: config.tools.map((tool) => tool.metadata.name)
|
|
3017
|
+
});
|
|
3018
|
+
modelToUse = model.bindTools(
|
|
3019
|
+
convertWorkerToolsForLangChain(config.tools)
|
|
3020
|
+
);
|
|
3021
|
+
}
|
|
3022
|
+
logger2.debug("Invoking LLM", { workerId: config.id });
|
|
3023
|
+
const response = await modelToUse.invoke(messages);
|
|
3024
|
+
const result = serializeModelContent(response.content);
|
|
3025
|
+
logger2.info("Worker task completed", {
|
|
3026
|
+
workerId: config.id,
|
|
3027
|
+
assignmentId: assignment.id,
|
|
3028
|
+
resultLength: result.length
|
|
3029
|
+
});
|
|
3030
|
+
logger2.debug("Worker result details", {
|
|
3031
|
+
workerId: config.id,
|
|
3032
|
+
assignmentId: assignment.id,
|
|
3033
|
+
resultLength: result.length
|
|
3034
|
+
});
|
|
3035
|
+
const taskResult = {
|
|
3036
|
+
assignmentId: assignment.id,
|
|
3037
|
+
workerId: config.id,
|
|
3038
|
+
success: true,
|
|
3039
|
+
result,
|
|
3040
|
+
completedAt: Date.now(),
|
|
3041
|
+
metadata: {
|
|
3042
|
+
skills_used: config.capabilities.skills
|
|
3043
|
+
}
|
|
3044
|
+
};
|
|
3045
|
+
const message = {
|
|
3046
|
+
id: createGeneratedId("msg"),
|
|
3047
|
+
from: config.id,
|
|
3048
|
+
to: ["supervisor"],
|
|
3049
|
+
type: "task_result",
|
|
3050
|
+
content: result,
|
|
3051
|
+
timestamp: Date.now(),
|
|
3052
|
+
metadata: {
|
|
3053
|
+
assignmentId: assignment.id,
|
|
3054
|
+
success: true
|
|
3055
|
+
}
|
|
3056
|
+
};
|
|
3057
|
+
return {
|
|
3058
|
+
completedTasks: [taskResult],
|
|
3059
|
+
messages: [message]
|
|
3060
|
+
};
|
|
3061
|
+
}
|
|
3062
|
+
function getStateWorkerOrThrow(state, workerId) {
|
|
3063
|
+
const currentWorker = state.workers[workerId];
|
|
3064
|
+
if (!currentWorker) {
|
|
3065
|
+
logger2.error("Attempted to decrement workload for unknown worker", {
|
|
3066
|
+
workerId,
|
|
3067
|
+
availableWorkers: Object.keys(state.workers)
|
|
3068
|
+
});
|
|
3069
|
+
throw new Error(`Worker "${workerId}" not found when decrementing workload.`);
|
|
3070
|
+
}
|
|
3071
|
+
return currentWorker;
|
|
3072
|
+
}
|
|
3073
|
+
function resolvePreviousWorkload(workerId, currentWorker, workerFromExecution) {
|
|
3074
|
+
const workloadFromWorker = workerFromExecution?.currentWorkload;
|
|
3075
|
+
if (typeof workloadFromWorker === "number" && Number.isFinite(workloadFromWorker)) {
|
|
3076
|
+
return workloadFromWorker;
|
|
3077
|
+
}
|
|
3078
|
+
if (typeof currentWorker.currentWorkload === "number" && Number.isFinite(currentWorker.currentWorkload)) {
|
|
3079
|
+
return currentWorker.currentWorkload;
|
|
3080
|
+
}
|
|
3081
|
+
logger2.error("Worker workload is not a valid number; cannot decrement", {
|
|
3082
|
+
workerId,
|
|
3083
|
+
workloadFromState: currentWorker.currentWorkload,
|
|
3084
|
+
...typeof workloadFromWorker === "number" ? { workloadFromWorker } : {}
|
|
3085
|
+
});
|
|
3086
|
+
throw new Error(
|
|
3087
|
+
`Worker "${workerId}" does not have a valid numeric currentWorkload to decrement.`
|
|
3088
|
+
);
|
|
3089
|
+
}
|
|
3090
|
+
function mergeWorkersWithDecrement(state, workerId, executionResult) {
|
|
3091
|
+
const currentWorker = getStateWorkerOrThrow(state, workerId);
|
|
3092
|
+
const executionResultWorkers = executionResult.workers || {};
|
|
3093
|
+
const baseWorkers = {
|
|
3094
|
+
...state.workers,
|
|
3095
|
+
...executionResultWorkers
|
|
3096
|
+
};
|
|
3097
|
+
const workerFromExecution = executionResultWorkers[workerId];
|
|
3098
|
+
const previousWorkload = resolvePreviousWorkload(
|
|
3099
|
+
workerId,
|
|
3100
|
+
currentWorker,
|
|
3101
|
+
workerFromExecution
|
|
3102
|
+
);
|
|
3103
|
+
const hasWorkerOverride = typeof workerFromExecution === "object" && workerFromExecution !== null;
|
|
3104
|
+
const updatedWorker = hasWorkerOverride ? {
|
|
3105
|
+
...currentWorker,
|
|
3106
|
+
...workerFromExecution,
|
|
3107
|
+
currentWorkload: Math.max(0, previousWorkload - 1)
|
|
3108
|
+
} : {
|
|
3109
|
+
...currentWorker,
|
|
3110
|
+
currentWorkload: Math.max(0, previousWorkload - 1)
|
|
3111
|
+
};
|
|
3112
|
+
const updatedWorkers = {
|
|
3113
|
+
...baseWorkers,
|
|
3114
|
+
[workerId]: updatedWorker
|
|
3115
|
+
};
|
|
3116
|
+
logger2.debug("Worker workload decremented", {
|
|
3117
|
+
workerId,
|
|
3118
|
+
previousWorkload,
|
|
3119
|
+
newWorkload: updatedWorkers[workerId].currentWorkload,
|
|
3120
|
+
hadExecutionResultWorkers: !!executionResult.workers
|
|
3121
|
+
});
|
|
3122
|
+
return updatedWorkers;
|
|
3123
|
+
}
|
|
3124
|
+
function decrementWorkerOnError(state, workerId) {
|
|
3125
|
+
const currentWorker = getStateWorkerOrThrow(state, workerId);
|
|
3126
|
+
const previousWorkload = resolvePreviousWorkload(workerId, currentWorker);
|
|
3127
|
+
const updatedWorkers = {
|
|
3128
|
+
...state.workers,
|
|
3129
|
+
[workerId]: {
|
|
3130
|
+
...currentWorker,
|
|
3131
|
+
currentWorkload: Math.max(0, previousWorkload - 1)
|
|
3132
|
+
}
|
|
3133
|
+
};
|
|
3134
|
+
logger2.debug("Worker workload decremented (error path)", {
|
|
3135
|
+
workerId,
|
|
3136
|
+
previousWorkload,
|
|
3137
|
+
newWorkload: updatedWorkers[workerId].currentWorkload
|
|
3138
|
+
});
|
|
3139
|
+
return updatedWorkers;
|
|
3140
|
+
}
|
|
3141
|
+
function createErrorTaskResult(assignment, workerId, errorMessage) {
|
|
3142
|
+
logger2.warn("Creating error result for assignment", {
|
|
3143
|
+
workerId,
|
|
3144
|
+
assignmentId: assignment.id
|
|
3145
|
+
});
|
|
3146
|
+
return {
|
|
3147
|
+
assignmentId: assignment.id,
|
|
3148
|
+
workerId,
|
|
3149
|
+
success: false,
|
|
3150
|
+
result: "",
|
|
3151
|
+
error: errorMessage,
|
|
3152
|
+
completedAt: Date.now()
|
|
3153
|
+
};
|
|
3154
|
+
}
|
|
2858
3155
|
function createWorkerNode(config) {
|
|
2859
|
-
const {
|
|
2860
|
-
id,
|
|
2861
|
-
capabilities,
|
|
2862
|
-
model,
|
|
2863
|
-
tools = [],
|
|
2864
|
-
systemPrompt,
|
|
2865
|
-
verbose = false,
|
|
2866
|
-
executeFn,
|
|
2867
|
-
agent
|
|
2868
|
-
} = config;
|
|
3156
|
+
const { id, model, executeFn, agent } = config;
|
|
2869
3157
|
return async (state, runConfig) => {
|
|
2870
3158
|
try {
|
|
2871
3159
|
logger2.info("Worker node executing", {
|
|
@@ -2873,9 +3161,7 @@ function createWorkerNode(config) {
|
|
|
2873
3161
|
iteration: state.iteration,
|
|
2874
3162
|
activeAssignments: state.activeAssignments.length
|
|
2875
3163
|
});
|
|
2876
|
-
const currentAssignment = state
|
|
2877
|
-
(assignment) => assignment.workerId === id && !state.completedTasks.some((task) => task.assignmentId === assignment.id)
|
|
2878
|
-
);
|
|
3164
|
+
const currentAssignment = findCurrentAssignment(state, id);
|
|
2879
3165
|
if (!currentAssignment) {
|
|
2880
3166
|
logger2.debug("No active assignment found for worker", {
|
|
2881
3167
|
workerId: id,
|
|
@@ -2887,105 +3173,35 @@ function createWorkerNode(config) {
|
|
|
2887
3173
|
logger2.info("Worker processing assignment", {
|
|
2888
3174
|
workerId: id,
|
|
2889
3175
|
assignmentId: currentAssignment.id,
|
|
2890
|
-
taskLength: currentAssignment.task.length
|
|
2891
|
-
|
|
3176
|
+
taskLength: currentAssignment.task.length
|
|
3177
|
+
});
|
|
3178
|
+
logger2.debug("Worker assignment details", {
|
|
3179
|
+
workerId: id,
|
|
3180
|
+
assignmentId: currentAssignment.id,
|
|
3181
|
+
taskLength: currentAssignment.task.length
|
|
2892
3182
|
});
|
|
2893
|
-
async function executeWithLLM() {
|
|
2894
|
-
logger2.debug("Using default LLM execution", {
|
|
2895
|
-
workerId: id,
|
|
2896
|
-
hasTools: tools.length > 0,
|
|
2897
|
-
toolCount: tools.length
|
|
2898
|
-
});
|
|
2899
|
-
const defaultSystemPrompt = `You are a specialized worker agent with the following capabilities:
|
|
2900
|
-
Skills: ${capabilities.skills.join(", ")}
|
|
2901
|
-
Tools: ${capabilities.tools.join(", ")}
|
|
2902
|
-
|
|
2903
|
-
Execute the assigned task using your skills and tools. Provide a clear, actionable result.`;
|
|
2904
|
-
const messages = [
|
|
2905
|
-
new import_messages5.SystemMessage(systemPrompt || defaultSystemPrompt),
|
|
2906
|
-
new import_messages5.HumanMessage(currentAssignment.task)
|
|
2907
|
-
];
|
|
2908
|
-
let modelToUse = model;
|
|
2909
|
-
if (tools.length > 0 && model.bindTools) {
|
|
2910
|
-
logger2.debug("Binding tools to model", {
|
|
2911
|
-
workerId: id,
|
|
2912
|
-
toolCount: tools.length,
|
|
2913
|
-
toolNames: tools.map((t) => t.metadata.name)
|
|
2914
|
-
});
|
|
2915
|
-
const langchainTools = convertWorkerToolsForLangChain(tools);
|
|
2916
|
-
modelToUse = model.bindTools(langchainTools);
|
|
2917
|
-
}
|
|
2918
|
-
logger2.debug("Invoking LLM", { workerId: id });
|
|
2919
|
-
const response = await modelToUse.invoke(messages);
|
|
2920
|
-
const result = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
2921
|
-
logger2.info("Worker task completed", {
|
|
2922
|
-
workerId: id,
|
|
2923
|
-
assignmentId: currentAssignment.id,
|
|
2924
|
-
resultLength: result.length,
|
|
2925
|
-
resultPreview: result.substring(0, 100)
|
|
2926
|
-
});
|
|
2927
|
-
const taskResult = {
|
|
2928
|
-
assignmentId: currentAssignment.id,
|
|
2929
|
-
workerId: id,
|
|
2930
|
-
success: true,
|
|
2931
|
-
result,
|
|
2932
|
-
completedAt: Date.now(),
|
|
2933
|
-
metadata: {
|
|
2934
|
-
skills_used: capabilities.skills
|
|
2935
|
-
}
|
|
2936
|
-
};
|
|
2937
|
-
const message = {
|
|
2938
|
-
id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
2939
|
-
from: id,
|
|
2940
|
-
to: ["supervisor"],
|
|
2941
|
-
type: "task_result",
|
|
2942
|
-
content: result,
|
|
2943
|
-
timestamp: Date.now(),
|
|
2944
|
-
metadata: {
|
|
2945
|
-
assignmentId: currentAssignment.id,
|
|
2946
|
-
success: true
|
|
2947
|
-
}
|
|
2948
|
-
};
|
|
2949
|
-
return {
|
|
2950
|
-
completedTasks: [taskResult],
|
|
2951
|
-
messages: [message]
|
|
2952
|
-
};
|
|
2953
|
-
}
|
|
2954
3183
|
let executionResult;
|
|
2955
3184
|
if (executeFn) {
|
|
2956
3185
|
logger2.debug("Using custom execution function", { workerId: id });
|
|
2957
3186
|
executionResult = await executeFn(state, runConfig);
|
|
2958
3187
|
} else if (agent && isReActAgent(agent)) {
|
|
2959
3188
|
logger2.debug("Using ReAct agent", { workerId: id });
|
|
2960
|
-
const wrappedFn = wrapReActAgent(id, agent, verbose);
|
|
3189
|
+
const wrappedFn = wrapReActAgent(id, agent, config.verbose ?? false);
|
|
2961
3190
|
executionResult = await wrappedFn(state, runConfig);
|
|
2962
3191
|
} else if (model) {
|
|
2963
|
-
|
|
3192
|
+
logger2.debug("Using default LLM execution", {
|
|
3193
|
+
workerId: id,
|
|
3194
|
+
hasTools: (config.tools ?? []).length > 0,
|
|
3195
|
+
toolCount: (config.tools ?? []).length
|
|
3196
|
+
});
|
|
3197
|
+
executionResult = await invokeWorkerModel(model, config, currentAssignment);
|
|
2964
3198
|
} else {
|
|
2965
3199
|
logger2.error("Worker missing required configuration", { workerId: id });
|
|
2966
3200
|
throw new Error(
|
|
2967
3201
|
`Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
|
|
2968
3202
|
);
|
|
2969
3203
|
}
|
|
2970
|
-
const
|
|
2971
|
-
const baseWorkers = {
|
|
2972
|
-
...state.workers,
|
|
2973
|
-
...executionResult.workers || {}
|
|
2974
|
-
};
|
|
2975
|
-
const workerToUpdate = baseWorkers[id] || currentWorker;
|
|
2976
|
-
const updatedWorkers = {
|
|
2977
|
-
...baseWorkers,
|
|
2978
|
-
[id]: {
|
|
2979
|
-
...workerToUpdate,
|
|
2980
|
-
currentWorkload: Math.max(0, workerToUpdate.currentWorkload - 1)
|
|
2981
|
-
}
|
|
2982
|
-
};
|
|
2983
|
-
logger2.debug("Worker workload decremented", {
|
|
2984
|
-
workerId: id,
|
|
2985
|
-
previousWorkload: workerToUpdate.currentWorkload,
|
|
2986
|
-
newWorkload: updatedWorkers[id].currentWorkload,
|
|
2987
|
-
hadExecutionResultWorkers: !!executionResult.workers
|
|
2988
|
-
});
|
|
3204
|
+
const updatedWorkers = mergeWorkersWithDecrement(state, id, executionResult);
|
|
2989
3205
|
return {
|
|
2990
3206
|
...executionResult,
|
|
2991
3207
|
workers: updatedWorkers
|
|
@@ -2996,41 +3212,29 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
|
|
|
2996
3212
|
workerId: id,
|
|
2997
3213
|
error: errorMessage
|
|
2998
3214
|
});
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3215
|
+
let updatedWorkers;
|
|
3216
|
+
try {
|
|
3217
|
+
updatedWorkers = decrementWorkerOnError(state, id);
|
|
3218
|
+
} catch (workloadError) {
|
|
3219
|
+
const workloadErrorMessage = workloadError instanceof Error ? workloadError.message : String(workloadError);
|
|
3220
|
+
logger2.error("Worker error handling failed", {
|
|
3221
|
+
workerId: id,
|
|
3222
|
+
error: workloadErrorMessage
|
|
3223
|
+
});
|
|
3224
|
+
return {
|
|
3225
|
+
status: "failed",
|
|
3226
|
+
error: `${errorMessage}. ${workloadErrorMessage}`
|
|
3227
|
+
};
|
|
3228
|
+
}
|
|
3012
3229
|
const currentAssignment = state.activeAssignments.find(
|
|
3013
3230
|
(assignment) => assignment.workerId === id
|
|
3014
3231
|
);
|
|
3015
3232
|
if (currentAssignment) {
|
|
3016
|
-
logger2.warn("Creating error result for assignment", {
|
|
3017
|
-
workerId: id,
|
|
3018
|
-
assignmentId: currentAssignment.id
|
|
3019
|
-
});
|
|
3020
|
-
const errorResult = {
|
|
3021
|
-
assignmentId: currentAssignment.id,
|
|
3022
|
-
workerId: id,
|
|
3023
|
-
success: false,
|
|
3024
|
-
result: "",
|
|
3025
|
-
error: errorMessage,
|
|
3026
|
-
completedAt: Date.now()
|
|
3027
|
-
};
|
|
3028
3233
|
return {
|
|
3029
|
-
completedTasks: [
|
|
3234
|
+
completedTasks: [createErrorTaskResult(currentAssignment, id, errorMessage)],
|
|
3030
3235
|
currentAgent: "supervisor",
|
|
3031
3236
|
status: "routing",
|
|
3032
3237
|
workers: updatedWorkers
|
|
3033
|
-
// Include workload update
|
|
3034
3238
|
};
|
|
3035
3239
|
}
|
|
3036
3240
|
logger2.error("No assignment found for error handling", { workerId: id });
|
|
@@ -3038,95 +3242,6 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
|
|
|
3038
3242
|
status: "failed",
|
|
3039
3243
|
error: errorMessage,
|
|
3040
3244
|
workers: updatedWorkers
|
|
3041
|
-
// Include workload update even on failure
|
|
3042
|
-
};
|
|
3043
|
-
}
|
|
3044
|
-
};
|
|
3045
|
-
}
|
|
3046
|
-
function createAggregatorNode(config = {}) {
|
|
3047
|
-
const {
|
|
3048
|
-
model,
|
|
3049
|
-
systemPrompt = DEFAULT_AGGREGATOR_SYSTEM_PROMPT,
|
|
3050
|
-
aggregateFn,
|
|
3051
|
-
verbose = false
|
|
3052
|
-
} = config;
|
|
3053
|
-
return async (state) => {
|
|
3054
|
-
try {
|
|
3055
|
-
logger2.info("Aggregator node executing", {
|
|
3056
|
-
completedTasks: state.completedTasks.length,
|
|
3057
|
-
successfulTasks: state.completedTasks.filter((t) => t.success).length,
|
|
3058
|
-
failedTasks: state.completedTasks.filter((t) => !t.success).length
|
|
3059
|
-
});
|
|
3060
|
-
logger2.debug("Combining results from workers");
|
|
3061
|
-
if (aggregateFn) {
|
|
3062
|
-
logger2.debug("Using custom aggregation function");
|
|
3063
|
-
const response2 = await aggregateFn(state);
|
|
3064
|
-
logger2.info("Custom aggregation complete", {
|
|
3065
|
-
responseLength: response2.length
|
|
3066
|
-
});
|
|
3067
|
-
return {
|
|
3068
|
-
response: response2,
|
|
3069
|
-
status: "completed"
|
|
3070
|
-
};
|
|
3071
|
-
}
|
|
3072
|
-
if (state.completedTasks.length === 0) {
|
|
3073
|
-
logger2.warn("No completed tasks to aggregate");
|
|
3074
|
-
return {
|
|
3075
|
-
response: "No tasks were completed.",
|
|
3076
|
-
status: "completed"
|
|
3077
|
-
};
|
|
3078
|
-
}
|
|
3079
|
-
if (!model) {
|
|
3080
|
-
logger2.debug("No model provided, concatenating results");
|
|
3081
|
-
const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
|
|
3082
|
-
logger2.info("Simple concatenation complete", {
|
|
3083
|
-
resultLength: combinedResults.length
|
|
3084
|
-
});
|
|
3085
|
-
return {
|
|
3086
|
-
response: combinedResults || "No successful results to aggregate.",
|
|
3087
|
-
status: "completed"
|
|
3088
|
-
};
|
|
3089
|
-
}
|
|
3090
|
-
logger2.debug("Using LLM for intelligent aggregation", {
|
|
3091
|
-
taskCount: state.completedTasks.length
|
|
3092
|
-
});
|
|
3093
|
-
const taskResults = state.completedTasks.map((task, idx) => {
|
|
3094
|
-
const status = task.success ? "\u2713" : "\u2717";
|
|
3095
|
-
const result = task.success ? task.result : `Error: ${task.error}`;
|
|
3096
|
-
return `${idx + 1}. [${status}] Worker ${task.workerId}:
|
|
3097
|
-
${result}`;
|
|
3098
|
-
}).join("\n\n");
|
|
3099
|
-
const userPrompt = `Original query: ${state.input}
|
|
3100
|
-
|
|
3101
|
-
Worker results:
|
|
3102
|
-
${taskResults}
|
|
3103
|
-
|
|
3104
|
-
Please synthesize these results into a comprehensive response that addresses the original query.`;
|
|
3105
|
-
const messages = [
|
|
3106
|
-
new import_messages5.SystemMessage(systemPrompt),
|
|
3107
|
-
new import_messages5.HumanMessage(userPrompt)
|
|
3108
|
-
];
|
|
3109
|
-
logger2.debug("Invoking aggregation LLM");
|
|
3110
|
-
const response = await model.invoke(messages);
|
|
3111
|
-
const aggregatedResponse = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
3112
|
-
logger2.info("Aggregation complete", {
|
|
3113
|
-
responseLength: aggregatedResponse.length,
|
|
3114
|
-
responsePreview: aggregatedResponse.substring(0, 100)
|
|
3115
|
-
});
|
|
3116
|
-
logger2.debug("Aggregation complete");
|
|
3117
|
-
return {
|
|
3118
|
-
response: aggregatedResponse,
|
|
3119
|
-
status: "completed"
|
|
3120
|
-
};
|
|
3121
|
-
} catch (error) {
|
|
3122
|
-
logger2.error("Aggregator node error", {
|
|
3123
|
-
error: error instanceof Error ? error.message : String(error),
|
|
3124
|
-
...error instanceof Error && error.stack ? { stack: error.stack } : {},
|
|
3125
|
-
completedTasks: state.completedTasks.length
|
|
3126
|
-
});
|
|
3127
|
-
return {
|
|
3128
|
-
status: "failed",
|
|
3129
|
-
error: error instanceof Error ? error.message : "Unknown error in aggregator"
|
|
3130
3245
|
};
|
|
3131
3246
|
}
|
|
3132
3247
|
};
|