@agentforge/patterns 0.4.1 → 0.5.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 CHANGED
@@ -2046,6 +2046,90 @@ function getRoutingStrategy(name) {
2046
2046
  }
2047
2047
  }
2048
2048
 
2049
+ // src/multi-agent/utils.ts
2050
+ function isReActAgent(obj) {
2051
+ 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
2052
+ (obj.constructor?.name === "CompiledGraph" || obj.constructor?.name === "CompiledStateGraph");
2053
+ }
2054
+ function wrapReActAgent(workerId, agent, verbose = false) {
2055
+ return async (state) => {
2056
+ try {
2057
+ if (verbose) {
2058
+ console.log(`[ReActWrapper:${workerId}] Wrapping ReAct agent execution`);
2059
+ }
2060
+ const task = state.messages[state.messages.length - 1]?.content || state.input;
2061
+ if (verbose) {
2062
+ console.log(`[ReActWrapper:${workerId}] Task:`, task.substring(0, 100) + "...");
2063
+ }
2064
+ const currentAssignment = state.activeAssignments.find(
2065
+ (assignment) => assignment.workerId === workerId && !state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
2066
+ );
2067
+ if (!currentAssignment) {
2068
+ if (verbose) {
2069
+ console.log(`[ReActWrapper:${workerId}] No active assignment found`);
2070
+ }
2071
+ return {
2072
+ currentAgent: "supervisor",
2073
+ status: "routing"
2074
+ };
2075
+ }
2076
+ const result = await agent.invoke({
2077
+ messages: [{ role: "user", content: task }]
2078
+ });
2079
+ const response = result.messages?.[result.messages.length - 1]?.content || "No response";
2080
+ if (verbose) {
2081
+ console.log(`[ReActWrapper:${workerId}] Response:`, response.substring(0, 100) + "...");
2082
+ }
2083
+ const toolsUsed = result.actions?.map((action) => action.name).filter(Boolean) || [];
2084
+ const uniqueTools = [...new Set(toolsUsed)];
2085
+ if (verbose && uniqueTools.length > 0) {
2086
+ console.log(`[ReActWrapper:${workerId}] Tools used:`, uniqueTools.join(", "));
2087
+ }
2088
+ const taskResult = {
2089
+ assignmentId: currentAssignment.id,
2090
+ workerId,
2091
+ result: response,
2092
+ completedAt: Date.now(),
2093
+ success: true,
2094
+ metadata: {
2095
+ agent_type: "react",
2096
+ iterations: result.iteration || 0,
2097
+ tools_used: uniqueTools
2098
+ }
2099
+ };
2100
+ return {
2101
+ completedTasks: [taskResult],
2102
+ currentAgent: "supervisor",
2103
+ status: "routing"
2104
+ };
2105
+ } catch (error) {
2106
+ console.error(`[ReActWrapper:${workerId}] Error:`, error);
2107
+ const currentAssignment = state.activeAssignments.find(
2108
+ (assignment) => assignment.workerId === workerId
2109
+ );
2110
+ if (currentAssignment) {
2111
+ const errorResult = {
2112
+ assignmentId: currentAssignment.id,
2113
+ workerId,
2114
+ success: false,
2115
+ result: "",
2116
+ error: error instanceof Error ? error.message : "Unknown error in ReAct agent",
2117
+ completedAt: Date.now()
2118
+ };
2119
+ return {
2120
+ completedTasks: [errorResult],
2121
+ currentAgent: "supervisor",
2122
+ status: "routing"
2123
+ };
2124
+ }
2125
+ return {
2126
+ status: "failed",
2127
+ error: error instanceof Error ? error.message : `Unknown error in ReAct wrapper for ${workerId}`
2128
+ };
2129
+ }
2130
+ };
2131
+ }
2132
+
2049
2133
  // src/multi-agent/nodes.ts
2050
2134
  var import_messages5 = require("@langchain/core/messages");
2051
2135
  var import_core7 = require("@agentforge/core");
@@ -2139,7 +2223,8 @@ function createWorkerNode(config) {
2139
2223
  tools = [],
2140
2224
  systemPrompt,
2141
2225
  verbose = false,
2142
- executeFn
2226
+ executeFn,
2227
+ agent
2143
2228
  } = config;
2144
2229
  return async (state) => {
2145
2230
  try {
@@ -2159,10 +2244,26 @@ function createWorkerNode(config) {
2159
2244
  };
2160
2245
  }
2161
2246
  if (executeFn) {
2247
+ if (verbose) {
2248
+ console.log(`[Worker:${id}] Using custom executeFn`);
2249
+ }
2162
2250
  return await executeFn(state);
2163
2251
  }
2252
+ if (agent) {
2253
+ if (isReActAgent(agent)) {
2254
+ if (verbose) {
2255
+ console.log(`[Worker:${id}] Using ReAct agent (auto-wrapped)`);
2256
+ }
2257
+ const wrappedFn = wrapReActAgent(id, agent, verbose);
2258
+ return await wrappedFn(state);
2259
+ } else {
2260
+ console.warn(`[Worker:${id}] Agent provided but does not appear to be a ReAct agent. Falling back to default execution.`);
2261
+ }
2262
+ }
2164
2263
  if (!model) {
2165
- throw new Error(`Worker ${id} requires either a model or custom execution function`);
2264
+ throw new Error(
2265
+ `Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
2266
+ );
2166
2267
  }
2167
2268
  const defaultSystemPrompt = `You are a specialized worker agent with the following capabilities:
2168
2269
  Skills: ${capabilities.skills.join(", ")}
@@ -2386,6 +2487,17 @@ function createMultiAgentSystem(config) {
2386
2487
  };
2387
2488
  return originalInvoke(mergedInput, config2);
2388
2489
  };
2490
+ const originalStream = compiled.stream.bind(compiled);
2491
+ compiled.stream = async function(input, config2) {
2492
+ const mergedInput = {
2493
+ ...input,
2494
+ workers: {
2495
+ ...workerCapabilities,
2496
+ ...input.workers || {}
2497
+ }
2498
+ };
2499
+ return originalStream(mergedInput, config2);
2500
+ };
2389
2501
  return compiled;
2390
2502
  }
2391
2503
  var MultiAgentSystemBuilder = class {
package/dist/index.d.cts CHANGED
@@ -2313,8 +2313,34 @@ interface WorkerConfig {
2313
2313
  verbose?: boolean;
2314
2314
  /**
2315
2315
  * Custom execution function
2316
+ *
2317
+ * If provided, this function will be used to execute tasks for this worker.
2318
+ * Takes precedence over the `agent` property.
2316
2319
  */
2317
2320
  executeFn?: (state: MultiAgentStateType) => Promise<Partial<MultiAgentStateType>>;
2321
+ /**
2322
+ * ReAct agent instance
2323
+ *
2324
+ * If provided, the Multi-Agent pattern will automatically wrap this ReAct agent
2325
+ * to work as a worker. The agent should be a compiled LangGraph StateGraph
2326
+ * (e.g., created with `createReActAgent()`).
2327
+ *
2328
+ * Note: `executeFn` takes precedence over `agent` if both are provided.
2329
+ *
2330
+ * @example
2331
+ * ```typescript
2332
+ * const hrAgent = createReActAgent({ model, tools, systemPrompt });
2333
+ *
2334
+ * const system = createMultiAgentSystem({
2335
+ * workers: [{
2336
+ * id: 'hr',
2337
+ * capabilities: { skills: ['hr'], ... },
2338
+ * agent: hrAgent, // Automatically wrapped!
2339
+ * }]
2340
+ * });
2341
+ * ```
2342
+ */
2343
+ agent?: CompiledStateGraph<any, any>;
2318
2344
  }
2319
2345
  /**
2320
2346
  * Configuration for the aggregator node
package/dist/index.d.ts CHANGED
@@ -2313,8 +2313,34 @@ interface WorkerConfig {
2313
2313
  verbose?: boolean;
2314
2314
  /**
2315
2315
  * Custom execution function
2316
+ *
2317
+ * If provided, this function will be used to execute tasks for this worker.
2318
+ * Takes precedence over the `agent` property.
2316
2319
  */
2317
2320
  executeFn?: (state: MultiAgentStateType) => Promise<Partial<MultiAgentStateType>>;
2321
+ /**
2322
+ * ReAct agent instance
2323
+ *
2324
+ * If provided, the Multi-Agent pattern will automatically wrap this ReAct agent
2325
+ * to work as a worker. The agent should be a compiled LangGraph StateGraph
2326
+ * (e.g., created with `createReActAgent()`).
2327
+ *
2328
+ * Note: `executeFn` takes precedence over `agent` if both are provided.
2329
+ *
2330
+ * @example
2331
+ * ```typescript
2332
+ * const hrAgent = createReActAgent({ model, tools, systemPrompt });
2333
+ *
2334
+ * const system = createMultiAgentSystem({
2335
+ * workers: [{
2336
+ * id: 'hr',
2337
+ * capabilities: { skills: ['hr'], ... },
2338
+ * agent: hrAgent, // Automatically wrapped!
2339
+ * }]
2340
+ * });
2341
+ * ```
2342
+ */
2343
+ agent?: CompiledStateGraph<any, any>;
2318
2344
  }
2319
2345
  /**
2320
2346
  * Configuration for the aggregator node
package/dist/index.js CHANGED
@@ -1947,6 +1947,90 @@ function getRoutingStrategy(name) {
1947
1947
  }
1948
1948
  }
1949
1949
 
1950
+ // src/multi-agent/utils.ts
1951
+ function isReActAgent(obj) {
1952
+ 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
1953
+ (obj.constructor?.name === "CompiledGraph" || obj.constructor?.name === "CompiledStateGraph");
1954
+ }
1955
+ function wrapReActAgent(workerId, agent, verbose = false) {
1956
+ return async (state) => {
1957
+ try {
1958
+ if (verbose) {
1959
+ console.log(`[ReActWrapper:${workerId}] Wrapping ReAct agent execution`);
1960
+ }
1961
+ const task = state.messages[state.messages.length - 1]?.content || state.input;
1962
+ if (verbose) {
1963
+ console.log(`[ReActWrapper:${workerId}] Task:`, task.substring(0, 100) + "...");
1964
+ }
1965
+ const currentAssignment = state.activeAssignments.find(
1966
+ (assignment) => assignment.workerId === workerId && !state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
1967
+ );
1968
+ if (!currentAssignment) {
1969
+ if (verbose) {
1970
+ console.log(`[ReActWrapper:${workerId}] No active assignment found`);
1971
+ }
1972
+ return {
1973
+ currentAgent: "supervisor",
1974
+ status: "routing"
1975
+ };
1976
+ }
1977
+ const result = await agent.invoke({
1978
+ messages: [{ role: "user", content: task }]
1979
+ });
1980
+ const response = result.messages?.[result.messages.length - 1]?.content || "No response";
1981
+ if (verbose) {
1982
+ console.log(`[ReActWrapper:${workerId}] Response:`, response.substring(0, 100) + "...");
1983
+ }
1984
+ const toolsUsed = result.actions?.map((action) => action.name).filter(Boolean) || [];
1985
+ const uniqueTools = [...new Set(toolsUsed)];
1986
+ if (verbose && uniqueTools.length > 0) {
1987
+ console.log(`[ReActWrapper:${workerId}] Tools used:`, uniqueTools.join(", "));
1988
+ }
1989
+ const taskResult = {
1990
+ assignmentId: currentAssignment.id,
1991
+ workerId,
1992
+ result: response,
1993
+ completedAt: Date.now(),
1994
+ success: true,
1995
+ metadata: {
1996
+ agent_type: "react",
1997
+ iterations: result.iteration || 0,
1998
+ tools_used: uniqueTools
1999
+ }
2000
+ };
2001
+ return {
2002
+ completedTasks: [taskResult],
2003
+ currentAgent: "supervisor",
2004
+ status: "routing"
2005
+ };
2006
+ } catch (error) {
2007
+ console.error(`[ReActWrapper:${workerId}] Error:`, error);
2008
+ const currentAssignment = state.activeAssignments.find(
2009
+ (assignment) => assignment.workerId === workerId
2010
+ );
2011
+ if (currentAssignment) {
2012
+ const errorResult = {
2013
+ assignmentId: currentAssignment.id,
2014
+ workerId,
2015
+ success: false,
2016
+ result: "",
2017
+ error: error instanceof Error ? error.message : "Unknown error in ReAct agent",
2018
+ completedAt: Date.now()
2019
+ };
2020
+ return {
2021
+ completedTasks: [errorResult],
2022
+ currentAgent: "supervisor",
2023
+ status: "routing"
2024
+ };
2025
+ }
2026
+ return {
2027
+ status: "failed",
2028
+ error: error instanceof Error ? error.message : `Unknown error in ReAct wrapper for ${workerId}`
2029
+ };
2030
+ }
2031
+ };
2032
+ }
2033
+
1950
2034
  // src/multi-agent/nodes.ts
1951
2035
  import { HumanMessage as HumanMessage5, SystemMessage as SystemMessage5 } from "@langchain/core/messages";
1952
2036
  import { toLangChainTools as toLangChainTools2 } from "@agentforge/core";
@@ -2040,7 +2124,8 @@ function createWorkerNode(config) {
2040
2124
  tools = [],
2041
2125
  systemPrompt,
2042
2126
  verbose = false,
2043
- executeFn
2127
+ executeFn,
2128
+ agent
2044
2129
  } = config;
2045
2130
  return async (state) => {
2046
2131
  try {
@@ -2060,10 +2145,26 @@ function createWorkerNode(config) {
2060
2145
  };
2061
2146
  }
2062
2147
  if (executeFn) {
2148
+ if (verbose) {
2149
+ console.log(`[Worker:${id}] Using custom executeFn`);
2150
+ }
2063
2151
  return await executeFn(state);
2064
2152
  }
2153
+ if (agent) {
2154
+ if (isReActAgent(agent)) {
2155
+ if (verbose) {
2156
+ console.log(`[Worker:${id}] Using ReAct agent (auto-wrapped)`);
2157
+ }
2158
+ const wrappedFn = wrapReActAgent(id, agent, verbose);
2159
+ return await wrappedFn(state);
2160
+ } else {
2161
+ console.warn(`[Worker:${id}] Agent provided but does not appear to be a ReAct agent. Falling back to default execution.`);
2162
+ }
2163
+ }
2065
2164
  if (!model) {
2066
- throw new Error(`Worker ${id} requires either a model or custom execution function`);
2165
+ throw new Error(
2166
+ `Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
2167
+ );
2067
2168
  }
2068
2169
  const defaultSystemPrompt = `You are a specialized worker agent with the following capabilities:
2069
2170
  Skills: ${capabilities.skills.join(", ")}
@@ -2287,6 +2388,17 @@ function createMultiAgentSystem(config) {
2287
2388
  };
2288
2389
  return originalInvoke(mergedInput, config2);
2289
2390
  };
2391
+ const originalStream = compiled.stream.bind(compiled);
2392
+ compiled.stream = async function(input, config2) {
2393
+ const mergedInput = {
2394
+ ...input,
2395
+ workers: {
2396
+ ...workerCapabilities,
2397
+ ...input.workers || {}
2398
+ }
2399
+ };
2400
+ return originalStream(mergedInput, config2);
2401
+ };
2290
2402
  return compiled;
2291
2403
  }
2292
2404
  var MultiAgentSystemBuilder = class {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentforge/patterns",
3
- "version": "0.4.1",
3
+ "version": "0.5.1",
4
4
  "description": "Agent patterns (ReAct, Planner-Executor) for AgentForge framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",