@agentforge/patterns 0.5.4 → 0.6.1
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 +117 -48
- package/dist/index.d.cts +38 -2
- package/dist/index.d.ts +38 -2
- package/dist/index.js +116 -47
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -327,6 +327,9 @@ function createActionNode(tools, verbose = false) {
|
|
|
327
327
|
console.log(`[action] Tool '${action.name}' executed successfully`);
|
|
328
328
|
}
|
|
329
329
|
} catch (error) {
|
|
330
|
+
if (error && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt") {
|
|
331
|
+
throw error;
|
|
332
|
+
}
|
|
330
333
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
331
334
|
observations.push({
|
|
332
335
|
toolCallId: action.id,
|
|
@@ -1891,6 +1894,33 @@ var MultiAgentState = (0, import_core6.createStateAnnotation)(MultiAgentStateCon
|
|
|
1891
1894
|
|
|
1892
1895
|
// src/multi-agent/routing.ts
|
|
1893
1896
|
var import_messages4 = require("@langchain/core/messages");
|
|
1897
|
+
async function executeTools(toolCalls, tools) {
|
|
1898
|
+
const results = [];
|
|
1899
|
+
for (const toolCall of toolCalls) {
|
|
1900
|
+
const tool = tools.find((t) => t.metadata.name === toolCall.name);
|
|
1901
|
+
if (!tool) {
|
|
1902
|
+
results.push(new import_messages4.ToolMessage({
|
|
1903
|
+
content: `Error: Tool '${toolCall.name}' not found`,
|
|
1904
|
+
tool_call_id: toolCall.id
|
|
1905
|
+
}));
|
|
1906
|
+
continue;
|
|
1907
|
+
}
|
|
1908
|
+
try {
|
|
1909
|
+
const result = await tool.execute(toolCall.args);
|
|
1910
|
+
const content = typeof result === "string" ? result : JSON.stringify(result);
|
|
1911
|
+
results.push(new import_messages4.ToolMessage({
|
|
1912
|
+
content,
|
|
1913
|
+
tool_call_id: toolCall.id
|
|
1914
|
+
}));
|
|
1915
|
+
} catch (error) {
|
|
1916
|
+
results.push(new import_messages4.ToolMessage({
|
|
1917
|
+
content: `Error executing tool: ${error.message}`,
|
|
1918
|
+
tool_call_id: toolCall.id
|
|
1919
|
+
}));
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
return results;
|
|
1923
|
+
}
|
|
1894
1924
|
var DEFAULT_SUPERVISOR_SYSTEM_PROMPT = `You are a supervisor agent responsible for routing tasks to specialized worker agents.
|
|
1895
1925
|
|
|
1896
1926
|
Your job is to:
|
|
@@ -1913,11 +1943,13 @@ var llmBasedRouting = {
|
|
|
1913
1943
|
throw new Error("LLM-based routing requires a model to be configured");
|
|
1914
1944
|
}
|
|
1915
1945
|
const systemPrompt = config.systemPrompt || DEFAULT_SUPERVISOR_SYSTEM_PROMPT;
|
|
1946
|
+
const maxRetries = config.maxToolRetries || 3;
|
|
1947
|
+
const tools = config.tools || [];
|
|
1916
1948
|
const workerInfo = Object.entries(state.workers).map(([id, caps]) => {
|
|
1917
1949
|
const skills = caps.skills.join(", ");
|
|
1918
|
-
const
|
|
1950
|
+
const tools2 = caps.tools.join(", ");
|
|
1919
1951
|
const available = caps.available ? "available" : "busy";
|
|
1920
|
-
return `- ${id}: Skills: [${skills}], Tools: [${
|
|
1952
|
+
return `- ${id}: Skills: [${skills}], Tools: [${tools2}], Status: ${available}, Workload: ${caps.currentWorkload}`;
|
|
1921
1953
|
}).join("\n");
|
|
1922
1954
|
const lastMessage = state.messages[state.messages.length - 1];
|
|
1923
1955
|
const taskContext = lastMessage?.content || state.input;
|
|
@@ -1927,24 +1959,42 @@ Available workers:
|
|
|
1927
1959
|
${workerInfo}
|
|
1928
1960
|
|
|
1929
1961
|
Select the best worker for this task and explain your reasoning.`;
|
|
1930
|
-
const
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1962
|
+
const conversationHistory = [];
|
|
1963
|
+
let attempt = 0;
|
|
1964
|
+
while (attempt < maxRetries) {
|
|
1965
|
+
const messages = [
|
|
1966
|
+
new import_messages4.SystemMessage(systemPrompt),
|
|
1967
|
+
new import_messages4.HumanMessage(userPrompt),
|
|
1968
|
+
...conversationHistory
|
|
1969
|
+
];
|
|
1970
|
+
const response = await config.model.invoke(messages);
|
|
1971
|
+
if (response.tool_calls && response.tool_calls.length > 0) {
|
|
1972
|
+
if (tools.length === 0) {
|
|
1973
|
+
throw new Error("LLM requested tool calls but no tools are configured");
|
|
1974
|
+
}
|
|
1975
|
+
const toolResults = await executeTools(response.tool_calls, tools);
|
|
1976
|
+
conversationHistory.push(
|
|
1977
|
+
new import_messages4.AIMessage({ content: response.content || "", tool_calls: response.tool_calls }),
|
|
1978
|
+
...toolResults
|
|
1979
|
+
);
|
|
1980
|
+
attempt++;
|
|
1981
|
+
continue;
|
|
1982
|
+
}
|
|
1983
|
+
const content = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
1984
|
+
try {
|
|
1985
|
+
const decision = JSON.parse(content);
|
|
1986
|
+
return {
|
|
1987
|
+
targetAgent: decision.targetAgent,
|
|
1988
|
+
reasoning: decision.reasoning,
|
|
1989
|
+
confidence: decision.confidence,
|
|
1990
|
+
strategy: "llm-based",
|
|
1991
|
+
timestamp: Date.now()
|
|
1992
|
+
};
|
|
1993
|
+
} catch (error) {
|
|
1994
|
+
throw new Error(`Failed to parse routing decision from LLM: ${error}`);
|
|
1995
|
+
}
|
|
1947
1996
|
}
|
|
1997
|
+
throw new Error(`Max tool retries (${maxRetries}) exceeded without routing decision`);
|
|
1948
1998
|
}
|
|
1949
1999
|
};
|
|
1950
2000
|
var roundRobinRouting = {
|
|
@@ -2050,43 +2100,48 @@ function getRoutingStrategy(name) {
|
|
|
2050
2100
|
}
|
|
2051
2101
|
|
|
2052
2102
|
// src/multi-agent/utils.ts
|
|
2103
|
+
var import_core7 = require("@agentforge/core");
|
|
2104
|
+
var logLevel = process.env.LOG_LEVEL?.toLowerCase() || import_core7.LogLevel.INFO;
|
|
2105
|
+
var logger = (0, import_core7.createLogger)("multi-agent", { level: logLevel });
|
|
2053
2106
|
function isReActAgent(obj) {
|
|
2054
2107
|
return obj && typeof obj === "object" && typeof obj.invoke === "function" && typeof obj.stream === "function" && // Additional check to ensure it's not just any object with invoke/stream
|
|
2055
2108
|
(obj.constructor?.name === "CompiledGraph" || obj.constructor?.name === "CompiledStateGraph");
|
|
2056
2109
|
}
|
|
2057
2110
|
function wrapReActAgent(workerId, agent, verbose = false) {
|
|
2058
|
-
return async (state) => {
|
|
2111
|
+
return async (state, config) => {
|
|
2059
2112
|
try {
|
|
2060
|
-
|
|
2061
|
-
console.log(`[ReActWrapper:${workerId}] Wrapping ReAct agent execution`);
|
|
2062
|
-
}
|
|
2113
|
+
logger.debug("Wrapping ReAct agent execution", { workerId });
|
|
2063
2114
|
const task = state.messages[state.messages.length - 1]?.content || state.input;
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2115
|
+
logger.debug("Extracted task", {
|
|
2116
|
+
workerId,
|
|
2117
|
+
taskPreview: task.substring(0, 100) + (task.length > 100 ? "..." : "")
|
|
2118
|
+
});
|
|
2067
2119
|
const currentAssignment = state.activeAssignments.find(
|
|
2068
2120
|
(assignment) => assignment.workerId === workerId && !state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
|
|
2069
2121
|
);
|
|
2070
2122
|
if (!currentAssignment) {
|
|
2071
|
-
|
|
2072
|
-
console.log(`[ReActWrapper:${workerId}] No active assignment found`);
|
|
2073
|
-
}
|
|
2123
|
+
logger.debug("No active assignment found", { workerId });
|
|
2074
2124
|
return {
|
|
2075
2125
|
currentAgent: "supervisor",
|
|
2076
2126
|
status: "routing"
|
|
2077
2127
|
};
|
|
2078
2128
|
}
|
|
2079
|
-
const result = await agent.invoke(
|
|
2080
|
-
|
|
2081
|
-
|
|
2129
|
+
const result = await agent.invoke(
|
|
2130
|
+
{
|
|
2131
|
+
messages: [{ role: "user", content: task }]
|
|
2132
|
+
},
|
|
2133
|
+
config
|
|
2134
|
+
// Pass through the config for checkpointing and interrupt support
|
|
2135
|
+
);
|
|
2082
2136
|
const response = result.messages?.[result.messages.length - 1]?.content || "No response";
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2137
|
+
logger.debug("Received response from ReAct agent", {
|
|
2138
|
+
workerId,
|
|
2139
|
+
responsePreview: response.substring(0, 100) + (response.length > 100 ? "..." : "")
|
|
2140
|
+
});
|
|
2086
2141
|
const toolsUsed = result.actions?.map((action) => action.name).filter(Boolean) || [];
|
|
2087
2142
|
const uniqueTools = [...new Set(toolsUsed)];
|
|
2088
|
-
if (
|
|
2089
|
-
|
|
2143
|
+
if (uniqueTools.length > 0) {
|
|
2144
|
+
logger.debug("Tools used by ReAct agent", { workerId, tools: uniqueTools });
|
|
2090
2145
|
}
|
|
2091
2146
|
const taskResult = {
|
|
2092
2147
|
assignmentId: currentAssignment.id,
|
|
@@ -2106,7 +2161,15 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2106
2161
|
status: "routing"
|
|
2107
2162
|
};
|
|
2108
2163
|
} catch (error) {
|
|
2109
|
-
|
|
2164
|
+
if (error && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt") {
|
|
2165
|
+
logger.debug("GraphInterrupt detected - re-throwing", { workerId });
|
|
2166
|
+
throw error;
|
|
2167
|
+
}
|
|
2168
|
+
logger.error("Error in ReAct agent execution", {
|
|
2169
|
+
workerId,
|
|
2170
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2171
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
2172
|
+
});
|
|
2110
2173
|
const currentAssignment = state.activeAssignments.find(
|
|
2111
2174
|
(assignment) => assignment.workerId === workerId
|
|
2112
2175
|
);
|
|
@@ -2135,7 +2198,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2135
2198
|
|
|
2136
2199
|
// src/multi-agent/nodes.ts
|
|
2137
2200
|
var import_messages5 = require("@langchain/core/messages");
|
|
2138
|
-
var
|
|
2201
|
+
var import_core8 = require("@agentforge/core");
|
|
2139
2202
|
var DEFAULT_AGGREGATOR_SYSTEM_PROMPT = `You are an aggregator agent responsible for combining results from multiple worker agents.
|
|
2140
2203
|
|
|
2141
2204
|
Your job is to:
|
|
@@ -2229,7 +2292,7 @@ function createWorkerNode(config) {
|
|
|
2229
2292
|
executeFn,
|
|
2230
2293
|
agent
|
|
2231
2294
|
} = config;
|
|
2232
|
-
return async (state) => {
|
|
2295
|
+
return async (state, runConfig) => {
|
|
2233
2296
|
try {
|
|
2234
2297
|
if (verbose) {
|
|
2235
2298
|
console.log(`[Worker:${id}] Executing task`);
|
|
@@ -2250,7 +2313,7 @@ function createWorkerNode(config) {
|
|
|
2250
2313
|
if (verbose) {
|
|
2251
2314
|
console.log(`[Worker:${id}] Using custom executeFn`);
|
|
2252
2315
|
}
|
|
2253
|
-
return await executeFn(state);
|
|
2316
|
+
return await executeFn(state, runConfig);
|
|
2254
2317
|
}
|
|
2255
2318
|
if (agent) {
|
|
2256
2319
|
if (isReActAgent(agent)) {
|
|
@@ -2258,7 +2321,7 @@ function createWorkerNode(config) {
|
|
|
2258
2321
|
console.log(`[Worker:${id}] Using ReAct agent (auto-wrapped)`);
|
|
2259
2322
|
}
|
|
2260
2323
|
const wrappedFn = wrapReActAgent(id, agent, verbose);
|
|
2261
|
-
return await wrappedFn(state);
|
|
2324
|
+
return await wrappedFn(state, runConfig);
|
|
2262
2325
|
} else {
|
|
2263
2326
|
console.warn(`[Worker:${id}] Agent provided but does not appear to be a ReAct agent. Falling back to default execution.`);
|
|
2264
2327
|
}
|
|
@@ -2279,7 +2342,7 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
|
|
|
2279
2342
|
];
|
|
2280
2343
|
let modelToUse = model;
|
|
2281
2344
|
if (tools.length > 0 && model.bindTools) {
|
|
2282
|
-
const langchainTools = (0,
|
|
2345
|
+
const langchainTools = (0, import_core8.toLangChainTools)(tools);
|
|
2283
2346
|
modelToUse = model.bindTools(langchainTools);
|
|
2284
2347
|
}
|
|
2285
2348
|
const response = await modelToUse.invoke(messages);
|
|
@@ -2324,6 +2387,9 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
|
|
|
2324
2387
|
status: "routing"
|
|
2325
2388
|
};
|
|
2326
2389
|
} catch (error) {
|
|
2390
|
+
if (error && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt") {
|
|
2391
|
+
throw error;
|
|
2392
|
+
}
|
|
2327
2393
|
console.error(`[Worker:${id}] Error:`, error);
|
|
2328
2394
|
const currentAssignment = state.activeAssignments.find(
|
|
2329
2395
|
(assignment) => assignment.workerId === id
|
|
@@ -2419,6 +2485,7 @@ Please synthesize these results into a comprehensive response that addresses the
|
|
|
2419
2485
|
|
|
2420
2486
|
// src/multi-agent/agent.ts
|
|
2421
2487
|
var import_langgraph4 = require("@langchain/langgraph");
|
|
2488
|
+
var import_core9 = require("@agentforge/core");
|
|
2422
2489
|
function createMultiAgentSystem(config) {
|
|
2423
2490
|
const {
|
|
2424
2491
|
supervisor,
|
|
@@ -2429,11 +2496,13 @@ function createMultiAgentSystem(config) {
|
|
|
2429
2496
|
checkpointer
|
|
2430
2497
|
} = config;
|
|
2431
2498
|
const workflow = new import_langgraph4.StateGraph(MultiAgentState);
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2499
|
+
let supervisorConfig = { ...supervisor, maxIterations, verbose };
|
|
2500
|
+
if (supervisor.model && supervisor.tools && supervisor.tools.length > 0) {
|
|
2501
|
+
const langchainTools = (0, import_core9.toLangChainTools)(supervisor.tools);
|
|
2502
|
+
const modelWithTools = supervisor.model.bindTools(langchainTools);
|
|
2503
|
+
supervisorConfig.model = modelWithTools;
|
|
2504
|
+
}
|
|
2505
|
+
const supervisorNode = createSupervisorNode(supervisorConfig);
|
|
2437
2506
|
workflow.addNode("supervisor", supervisorNode);
|
|
2438
2507
|
const workerIds = [];
|
|
2439
2508
|
const workerCapabilities = {};
|
package/dist/index.d.cts
CHANGED
|
@@ -2411,6 +2411,37 @@ interface SupervisorConfig {
|
|
|
2411
2411
|
* Maximum number of routing iterations
|
|
2412
2412
|
*/
|
|
2413
2413
|
maxIterations?: number;
|
|
2414
|
+
/**
|
|
2415
|
+
* Optional tools the supervisor can use during routing
|
|
2416
|
+
*
|
|
2417
|
+
* Enables the supervisor to gather additional information before making routing decisions.
|
|
2418
|
+
* Common use case: askHuman tool for clarifying ambiguous queries.
|
|
2419
|
+
*
|
|
2420
|
+
* Note: Only works with LLM-based routing strategy.
|
|
2421
|
+
*
|
|
2422
|
+
* @example
|
|
2423
|
+
* ```typescript
|
|
2424
|
+
* import { createAskHumanTool } from '@agentforge/tools';
|
|
2425
|
+
*
|
|
2426
|
+
* const system = createMultiAgentSystem({
|
|
2427
|
+
* supervisor: {
|
|
2428
|
+
* strategy: 'llm-based',
|
|
2429
|
+
* model: chatModel,
|
|
2430
|
+
* tools: [createAskHumanTool()],
|
|
2431
|
+
* },
|
|
2432
|
+
* // ...
|
|
2433
|
+
* });
|
|
2434
|
+
* ```
|
|
2435
|
+
*/
|
|
2436
|
+
tools?: Tool<any, any>[];
|
|
2437
|
+
/**
|
|
2438
|
+
* Maximum number of tool call retries before requiring routing decision
|
|
2439
|
+
*
|
|
2440
|
+
* Prevents infinite loops where the supervisor keeps calling tools without making a routing decision.
|
|
2441
|
+
*
|
|
2442
|
+
* @default 3
|
|
2443
|
+
*/
|
|
2444
|
+
maxToolRetries?: number;
|
|
2414
2445
|
}
|
|
2415
2446
|
/**
|
|
2416
2447
|
* Configuration for a worker agent node
|
|
@@ -2445,8 +2476,11 @@ interface WorkerConfig {
|
|
|
2445
2476
|
*
|
|
2446
2477
|
* If provided, this function will be used to execute tasks for this worker.
|
|
2447
2478
|
* Takes precedence over the `agent` property.
|
|
2479
|
+
*
|
|
2480
|
+
* The config parameter contains LangGraph runtime configuration including
|
|
2481
|
+
* thread_id for checkpointing, which is required for interrupt functionality.
|
|
2448
2482
|
*/
|
|
2449
|
-
executeFn?: (state: MultiAgentStateType) => Promise<Partial<MultiAgentStateType>>;
|
|
2483
|
+
executeFn?: (state: MultiAgentStateType, config?: any) => Promise<Partial<MultiAgentStateType>>;
|
|
2450
2484
|
/**
|
|
2451
2485
|
* ReAct agent instance
|
|
2452
2486
|
*
|
|
@@ -2576,6 +2610,8 @@ declare const DEFAULT_SUPERVISOR_SYSTEM_PROMPT = "You are a supervisor agent res
|
|
|
2576
2610
|
/**
|
|
2577
2611
|
* LLM-based routing strategy
|
|
2578
2612
|
* Uses an LLM to intelligently route tasks based on worker capabilities
|
|
2613
|
+
*
|
|
2614
|
+
* Supports tool calls (e.g., askHuman) for gathering additional information before routing.
|
|
2579
2615
|
*/
|
|
2580
2616
|
declare const llmBasedRouting: RoutingStrategyImpl;
|
|
2581
2617
|
/**
|
|
@@ -2622,7 +2658,7 @@ declare function createSupervisorNode(config: SupervisorConfig): (state: MultiAg
|
|
|
2622
2658
|
/**
|
|
2623
2659
|
* Create a worker agent node
|
|
2624
2660
|
*/
|
|
2625
|
-
declare function createWorkerNode(config: WorkerConfig): (state: MultiAgentStateType) => Promise<Partial<MultiAgentStateType>>;
|
|
2661
|
+
declare function createWorkerNode(config: WorkerConfig): (state: MultiAgentStateType, runConfig?: any) => Promise<Partial<MultiAgentStateType>>;
|
|
2626
2662
|
/**
|
|
2627
2663
|
* Create an aggregator node that combines worker results
|
|
2628
2664
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -2411,6 +2411,37 @@ interface SupervisorConfig {
|
|
|
2411
2411
|
* Maximum number of routing iterations
|
|
2412
2412
|
*/
|
|
2413
2413
|
maxIterations?: number;
|
|
2414
|
+
/**
|
|
2415
|
+
* Optional tools the supervisor can use during routing
|
|
2416
|
+
*
|
|
2417
|
+
* Enables the supervisor to gather additional information before making routing decisions.
|
|
2418
|
+
* Common use case: askHuman tool for clarifying ambiguous queries.
|
|
2419
|
+
*
|
|
2420
|
+
* Note: Only works with LLM-based routing strategy.
|
|
2421
|
+
*
|
|
2422
|
+
* @example
|
|
2423
|
+
* ```typescript
|
|
2424
|
+
* import { createAskHumanTool } from '@agentforge/tools';
|
|
2425
|
+
*
|
|
2426
|
+
* const system = createMultiAgentSystem({
|
|
2427
|
+
* supervisor: {
|
|
2428
|
+
* strategy: 'llm-based',
|
|
2429
|
+
* model: chatModel,
|
|
2430
|
+
* tools: [createAskHumanTool()],
|
|
2431
|
+
* },
|
|
2432
|
+
* // ...
|
|
2433
|
+
* });
|
|
2434
|
+
* ```
|
|
2435
|
+
*/
|
|
2436
|
+
tools?: Tool<any, any>[];
|
|
2437
|
+
/**
|
|
2438
|
+
* Maximum number of tool call retries before requiring routing decision
|
|
2439
|
+
*
|
|
2440
|
+
* Prevents infinite loops where the supervisor keeps calling tools without making a routing decision.
|
|
2441
|
+
*
|
|
2442
|
+
* @default 3
|
|
2443
|
+
*/
|
|
2444
|
+
maxToolRetries?: number;
|
|
2414
2445
|
}
|
|
2415
2446
|
/**
|
|
2416
2447
|
* Configuration for a worker agent node
|
|
@@ -2445,8 +2476,11 @@ interface WorkerConfig {
|
|
|
2445
2476
|
*
|
|
2446
2477
|
* If provided, this function will be used to execute tasks for this worker.
|
|
2447
2478
|
* Takes precedence over the `agent` property.
|
|
2479
|
+
*
|
|
2480
|
+
* The config parameter contains LangGraph runtime configuration including
|
|
2481
|
+
* thread_id for checkpointing, which is required for interrupt functionality.
|
|
2448
2482
|
*/
|
|
2449
|
-
executeFn?: (state: MultiAgentStateType) => Promise<Partial<MultiAgentStateType>>;
|
|
2483
|
+
executeFn?: (state: MultiAgentStateType, config?: any) => Promise<Partial<MultiAgentStateType>>;
|
|
2450
2484
|
/**
|
|
2451
2485
|
* ReAct agent instance
|
|
2452
2486
|
*
|
|
@@ -2576,6 +2610,8 @@ declare const DEFAULT_SUPERVISOR_SYSTEM_PROMPT = "You are a supervisor agent res
|
|
|
2576
2610
|
/**
|
|
2577
2611
|
* LLM-based routing strategy
|
|
2578
2612
|
* Uses an LLM to intelligently route tasks based on worker capabilities
|
|
2613
|
+
*
|
|
2614
|
+
* Supports tool calls (e.g., askHuman) for gathering additional information before routing.
|
|
2579
2615
|
*/
|
|
2580
2616
|
declare const llmBasedRouting: RoutingStrategyImpl;
|
|
2581
2617
|
/**
|
|
@@ -2622,7 +2658,7 @@ declare function createSupervisorNode(config: SupervisorConfig): (state: MultiAg
|
|
|
2622
2658
|
/**
|
|
2623
2659
|
* Create a worker agent node
|
|
2624
2660
|
*/
|
|
2625
|
-
declare function createWorkerNode(config: WorkerConfig): (state: MultiAgentStateType) => Promise<Partial<MultiAgentStateType>>;
|
|
2661
|
+
declare function createWorkerNode(config: WorkerConfig): (state: MultiAgentStateType, runConfig?: any) => Promise<Partial<MultiAgentStateType>>;
|
|
2626
2662
|
/**
|
|
2627
2663
|
* Create an aggregator node that combines worker results
|
|
2628
2664
|
*/
|
package/dist/index.js
CHANGED
|
@@ -228,6 +228,9 @@ function createActionNode(tools, verbose = false) {
|
|
|
228
228
|
console.log(`[action] Tool '${action.name}' executed successfully`);
|
|
229
229
|
}
|
|
230
230
|
} catch (error) {
|
|
231
|
+
if (error && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt") {
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
231
234
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
232
235
|
observations.push({
|
|
233
236
|
toolCallId: action.id,
|
|
@@ -1791,7 +1794,34 @@ var MultiAgentStateConfig = {
|
|
|
1791
1794
|
var MultiAgentState = createStateAnnotation4(MultiAgentStateConfig);
|
|
1792
1795
|
|
|
1793
1796
|
// src/multi-agent/routing.ts
|
|
1794
|
-
import { HumanMessage as HumanMessage4, SystemMessage as SystemMessage4 } from "@langchain/core/messages";
|
|
1797
|
+
import { HumanMessage as HumanMessage4, SystemMessage as SystemMessage4, AIMessage as AIMessage2, ToolMessage as ToolMessage2 } from "@langchain/core/messages";
|
|
1798
|
+
async function executeTools(toolCalls, tools) {
|
|
1799
|
+
const results = [];
|
|
1800
|
+
for (const toolCall of toolCalls) {
|
|
1801
|
+
const tool = tools.find((t) => t.metadata.name === toolCall.name);
|
|
1802
|
+
if (!tool) {
|
|
1803
|
+
results.push(new ToolMessage2({
|
|
1804
|
+
content: `Error: Tool '${toolCall.name}' not found`,
|
|
1805
|
+
tool_call_id: toolCall.id
|
|
1806
|
+
}));
|
|
1807
|
+
continue;
|
|
1808
|
+
}
|
|
1809
|
+
try {
|
|
1810
|
+
const result = await tool.execute(toolCall.args);
|
|
1811
|
+
const content = typeof result === "string" ? result : JSON.stringify(result);
|
|
1812
|
+
results.push(new ToolMessage2({
|
|
1813
|
+
content,
|
|
1814
|
+
tool_call_id: toolCall.id
|
|
1815
|
+
}));
|
|
1816
|
+
} catch (error) {
|
|
1817
|
+
results.push(new ToolMessage2({
|
|
1818
|
+
content: `Error executing tool: ${error.message}`,
|
|
1819
|
+
tool_call_id: toolCall.id
|
|
1820
|
+
}));
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
return results;
|
|
1824
|
+
}
|
|
1795
1825
|
var DEFAULT_SUPERVISOR_SYSTEM_PROMPT = `You are a supervisor agent responsible for routing tasks to specialized worker agents.
|
|
1796
1826
|
|
|
1797
1827
|
Your job is to:
|
|
@@ -1814,11 +1844,13 @@ var llmBasedRouting = {
|
|
|
1814
1844
|
throw new Error("LLM-based routing requires a model to be configured");
|
|
1815
1845
|
}
|
|
1816
1846
|
const systemPrompt = config.systemPrompt || DEFAULT_SUPERVISOR_SYSTEM_PROMPT;
|
|
1847
|
+
const maxRetries = config.maxToolRetries || 3;
|
|
1848
|
+
const tools = config.tools || [];
|
|
1817
1849
|
const workerInfo = Object.entries(state.workers).map(([id, caps]) => {
|
|
1818
1850
|
const skills = caps.skills.join(", ");
|
|
1819
|
-
const
|
|
1851
|
+
const tools2 = caps.tools.join(", ");
|
|
1820
1852
|
const available = caps.available ? "available" : "busy";
|
|
1821
|
-
return `- ${id}: Skills: [${skills}], Tools: [${
|
|
1853
|
+
return `- ${id}: Skills: [${skills}], Tools: [${tools2}], Status: ${available}, Workload: ${caps.currentWorkload}`;
|
|
1822
1854
|
}).join("\n");
|
|
1823
1855
|
const lastMessage = state.messages[state.messages.length - 1];
|
|
1824
1856
|
const taskContext = lastMessage?.content || state.input;
|
|
@@ -1828,24 +1860,42 @@ Available workers:
|
|
|
1828
1860
|
${workerInfo}
|
|
1829
1861
|
|
|
1830
1862
|
Select the best worker for this task and explain your reasoning.`;
|
|
1831
|
-
const
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1863
|
+
const conversationHistory = [];
|
|
1864
|
+
let attempt = 0;
|
|
1865
|
+
while (attempt < maxRetries) {
|
|
1866
|
+
const messages = [
|
|
1867
|
+
new SystemMessage4(systemPrompt),
|
|
1868
|
+
new HumanMessage4(userPrompt),
|
|
1869
|
+
...conversationHistory
|
|
1870
|
+
];
|
|
1871
|
+
const response = await config.model.invoke(messages);
|
|
1872
|
+
if (response.tool_calls && response.tool_calls.length > 0) {
|
|
1873
|
+
if (tools.length === 0) {
|
|
1874
|
+
throw new Error("LLM requested tool calls but no tools are configured");
|
|
1875
|
+
}
|
|
1876
|
+
const toolResults = await executeTools(response.tool_calls, tools);
|
|
1877
|
+
conversationHistory.push(
|
|
1878
|
+
new AIMessage2({ content: response.content || "", tool_calls: response.tool_calls }),
|
|
1879
|
+
...toolResults
|
|
1880
|
+
);
|
|
1881
|
+
attempt++;
|
|
1882
|
+
continue;
|
|
1883
|
+
}
|
|
1884
|
+
const content = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
1885
|
+
try {
|
|
1886
|
+
const decision = JSON.parse(content);
|
|
1887
|
+
return {
|
|
1888
|
+
targetAgent: decision.targetAgent,
|
|
1889
|
+
reasoning: decision.reasoning,
|
|
1890
|
+
confidence: decision.confidence,
|
|
1891
|
+
strategy: "llm-based",
|
|
1892
|
+
timestamp: Date.now()
|
|
1893
|
+
};
|
|
1894
|
+
} catch (error) {
|
|
1895
|
+
throw new Error(`Failed to parse routing decision from LLM: ${error}`);
|
|
1896
|
+
}
|
|
1848
1897
|
}
|
|
1898
|
+
throw new Error(`Max tool retries (${maxRetries}) exceeded without routing decision`);
|
|
1849
1899
|
}
|
|
1850
1900
|
};
|
|
1851
1901
|
var roundRobinRouting = {
|
|
@@ -1951,43 +2001,48 @@ function getRoutingStrategy(name) {
|
|
|
1951
2001
|
}
|
|
1952
2002
|
|
|
1953
2003
|
// src/multi-agent/utils.ts
|
|
2004
|
+
import { createLogger, LogLevel } from "@agentforge/core";
|
|
2005
|
+
var logLevel = process.env.LOG_LEVEL?.toLowerCase() || LogLevel.INFO;
|
|
2006
|
+
var logger = createLogger("multi-agent", { level: logLevel });
|
|
1954
2007
|
function isReActAgent(obj) {
|
|
1955
2008
|
return obj && typeof obj === "object" && typeof obj.invoke === "function" && typeof obj.stream === "function" && // Additional check to ensure it's not just any object with invoke/stream
|
|
1956
2009
|
(obj.constructor?.name === "CompiledGraph" || obj.constructor?.name === "CompiledStateGraph");
|
|
1957
2010
|
}
|
|
1958
2011
|
function wrapReActAgent(workerId, agent, verbose = false) {
|
|
1959
|
-
return async (state) => {
|
|
2012
|
+
return async (state, config) => {
|
|
1960
2013
|
try {
|
|
1961
|
-
|
|
1962
|
-
console.log(`[ReActWrapper:${workerId}] Wrapping ReAct agent execution`);
|
|
1963
|
-
}
|
|
2014
|
+
logger.debug("Wrapping ReAct agent execution", { workerId });
|
|
1964
2015
|
const task = state.messages[state.messages.length - 1]?.content || state.input;
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
2016
|
+
logger.debug("Extracted task", {
|
|
2017
|
+
workerId,
|
|
2018
|
+
taskPreview: task.substring(0, 100) + (task.length > 100 ? "..." : "")
|
|
2019
|
+
});
|
|
1968
2020
|
const currentAssignment = state.activeAssignments.find(
|
|
1969
2021
|
(assignment) => assignment.workerId === workerId && !state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
|
|
1970
2022
|
);
|
|
1971
2023
|
if (!currentAssignment) {
|
|
1972
|
-
|
|
1973
|
-
console.log(`[ReActWrapper:${workerId}] No active assignment found`);
|
|
1974
|
-
}
|
|
2024
|
+
logger.debug("No active assignment found", { workerId });
|
|
1975
2025
|
return {
|
|
1976
2026
|
currentAgent: "supervisor",
|
|
1977
2027
|
status: "routing"
|
|
1978
2028
|
};
|
|
1979
2029
|
}
|
|
1980
|
-
const result = await agent.invoke(
|
|
1981
|
-
|
|
1982
|
-
|
|
2030
|
+
const result = await agent.invoke(
|
|
2031
|
+
{
|
|
2032
|
+
messages: [{ role: "user", content: task }]
|
|
2033
|
+
},
|
|
2034
|
+
config
|
|
2035
|
+
// Pass through the config for checkpointing and interrupt support
|
|
2036
|
+
);
|
|
1983
2037
|
const response = result.messages?.[result.messages.length - 1]?.content || "No response";
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
2038
|
+
logger.debug("Received response from ReAct agent", {
|
|
2039
|
+
workerId,
|
|
2040
|
+
responsePreview: response.substring(0, 100) + (response.length > 100 ? "..." : "")
|
|
2041
|
+
});
|
|
1987
2042
|
const toolsUsed = result.actions?.map((action) => action.name).filter(Boolean) || [];
|
|
1988
2043
|
const uniqueTools = [...new Set(toolsUsed)];
|
|
1989
|
-
if (
|
|
1990
|
-
|
|
2044
|
+
if (uniqueTools.length > 0) {
|
|
2045
|
+
logger.debug("Tools used by ReAct agent", { workerId, tools: uniqueTools });
|
|
1991
2046
|
}
|
|
1992
2047
|
const taskResult = {
|
|
1993
2048
|
assignmentId: currentAssignment.id,
|
|
@@ -2007,7 +2062,15 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2007
2062
|
status: "routing"
|
|
2008
2063
|
};
|
|
2009
2064
|
} catch (error) {
|
|
2010
|
-
|
|
2065
|
+
if (error && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt") {
|
|
2066
|
+
logger.debug("GraphInterrupt detected - re-throwing", { workerId });
|
|
2067
|
+
throw error;
|
|
2068
|
+
}
|
|
2069
|
+
logger.error("Error in ReAct agent execution", {
|
|
2070
|
+
workerId,
|
|
2071
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2072
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
2073
|
+
});
|
|
2011
2074
|
const currentAssignment = state.activeAssignments.find(
|
|
2012
2075
|
(assignment) => assignment.workerId === workerId
|
|
2013
2076
|
);
|
|
@@ -2130,7 +2193,7 @@ function createWorkerNode(config) {
|
|
|
2130
2193
|
executeFn,
|
|
2131
2194
|
agent
|
|
2132
2195
|
} = config;
|
|
2133
|
-
return async (state) => {
|
|
2196
|
+
return async (state, runConfig) => {
|
|
2134
2197
|
try {
|
|
2135
2198
|
if (verbose) {
|
|
2136
2199
|
console.log(`[Worker:${id}] Executing task`);
|
|
@@ -2151,7 +2214,7 @@ function createWorkerNode(config) {
|
|
|
2151
2214
|
if (verbose) {
|
|
2152
2215
|
console.log(`[Worker:${id}] Using custom executeFn`);
|
|
2153
2216
|
}
|
|
2154
|
-
return await executeFn(state);
|
|
2217
|
+
return await executeFn(state, runConfig);
|
|
2155
2218
|
}
|
|
2156
2219
|
if (agent) {
|
|
2157
2220
|
if (isReActAgent(agent)) {
|
|
@@ -2159,7 +2222,7 @@ function createWorkerNode(config) {
|
|
|
2159
2222
|
console.log(`[Worker:${id}] Using ReAct agent (auto-wrapped)`);
|
|
2160
2223
|
}
|
|
2161
2224
|
const wrappedFn = wrapReActAgent(id, agent, verbose);
|
|
2162
|
-
return await wrappedFn(state);
|
|
2225
|
+
return await wrappedFn(state, runConfig);
|
|
2163
2226
|
} else {
|
|
2164
2227
|
console.warn(`[Worker:${id}] Agent provided but does not appear to be a ReAct agent. Falling back to default execution.`);
|
|
2165
2228
|
}
|
|
@@ -2225,6 +2288,9 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
|
|
|
2225
2288
|
status: "routing"
|
|
2226
2289
|
};
|
|
2227
2290
|
} catch (error) {
|
|
2291
|
+
if (error && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt") {
|
|
2292
|
+
throw error;
|
|
2293
|
+
}
|
|
2228
2294
|
console.error(`[Worker:${id}] Error:`, error);
|
|
2229
2295
|
const currentAssignment = state.activeAssignments.find(
|
|
2230
2296
|
(assignment) => assignment.workerId === id
|
|
@@ -2320,6 +2386,7 @@ Please synthesize these results into a comprehensive response that addresses the
|
|
|
2320
2386
|
|
|
2321
2387
|
// src/multi-agent/agent.ts
|
|
2322
2388
|
import { StateGraph as StateGraph4, END as END4 } from "@langchain/langgraph";
|
|
2389
|
+
import { toLangChainTools as toLangChainTools3 } from "@agentforge/core";
|
|
2323
2390
|
function createMultiAgentSystem(config) {
|
|
2324
2391
|
const {
|
|
2325
2392
|
supervisor,
|
|
@@ -2330,11 +2397,13 @@ function createMultiAgentSystem(config) {
|
|
|
2330
2397
|
checkpointer
|
|
2331
2398
|
} = config;
|
|
2332
2399
|
const workflow = new StateGraph4(MultiAgentState);
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2400
|
+
let supervisorConfig = { ...supervisor, maxIterations, verbose };
|
|
2401
|
+
if (supervisor.model && supervisor.tools && supervisor.tools.length > 0) {
|
|
2402
|
+
const langchainTools = toLangChainTools3(supervisor.tools);
|
|
2403
|
+
const modelWithTools = supervisor.model.bindTools(langchainTools);
|
|
2404
|
+
supervisorConfig.model = modelWithTools;
|
|
2405
|
+
}
|
|
2406
|
+
const supervisorNode = createSupervisorNode(supervisorConfig);
|
|
2338
2407
|
workflow.addNode("supervisor", supervisorNode);
|
|
2339
2408
|
const workerIds = [];
|
|
2340
2409
|
const workerCapabilities = {};
|