@agentforge/patterns 0.4.0 → 0.5.0

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,84 @@ 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 taskResult = {
2084
+ assignmentId: currentAssignment.id,
2085
+ workerId,
2086
+ result: response,
2087
+ completedAt: Date.now(),
2088
+ success: true,
2089
+ metadata: {
2090
+ agent_type: "react",
2091
+ iterations: result.iteration || 0
2092
+ }
2093
+ };
2094
+ return {
2095
+ completedTasks: [taskResult],
2096
+ currentAgent: "supervisor",
2097
+ status: "routing"
2098
+ };
2099
+ } catch (error) {
2100
+ console.error(`[ReActWrapper:${workerId}] Error:`, error);
2101
+ const currentAssignment = state.activeAssignments.find(
2102
+ (assignment) => assignment.workerId === workerId
2103
+ );
2104
+ if (currentAssignment) {
2105
+ const errorResult = {
2106
+ assignmentId: currentAssignment.id,
2107
+ workerId,
2108
+ success: false,
2109
+ result: "",
2110
+ error: error instanceof Error ? error.message : "Unknown error in ReAct agent",
2111
+ completedAt: Date.now()
2112
+ };
2113
+ return {
2114
+ completedTasks: [errorResult],
2115
+ currentAgent: "supervisor",
2116
+ status: "routing"
2117
+ };
2118
+ }
2119
+ return {
2120
+ status: "failed",
2121
+ error: error instanceof Error ? error.message : `Unknown error in ReAct wrapper for ${workerId}`
2122
+ };
2123
+ }
2124
+ };
2125
+ }
2126
+
2049
2127
  // src/multi-agent/nodes.ts
2050
2128
  var import_messages5 = require("@langchain/core/messages");
2051
2129
  var import_core7 = require("@agentforge/core");
@@ -2139,7 +2217,8 @@ function createWorkerNode(config) {
2139
2217
  tools = [],
2140
2218
  systemPrompt,
2141
2219
  verbose = false,
2142
- executeFn
2220
+ executeFn,
2221
+ agent
2143
2222
  } = config;
2144
2223
  return async (state) => {
2145
2224
  try {
@@ -2159,10 +2238,26 @@ function createWorkerNode(config) {
2159
2238
  };
2160
2239
  }
2161
2240
  if (executeFn) {
2241
+ if (verbose) {
2242
+ console.log(`[Worker:${id}] Using custom executeFn`);
2243
+ }
2162
2244
  return await executeFn(state);
2163
2245
  }
2246
+ if (agent) {
2247
+ if (isReActAgent(agent)) {
2248
+ if (verbose) {
2249
+ console.log(`[Worker:${id}] Using ReAct agent (auto-wrapped)`);
2250
+ }
2251
+ const wrappedFn = wrapReActAgent(id, agent, verbose);
2252
+ return await wrappedFn(state);
2253
+ } else {
2254
+ console.warn(`[Worker:${id}] Agent provided but does not appear to be a ReAct agent. Falling back to default execution.`);
2255
+ }
2256
+ }
2164
2257
  if (!model) {
2165
- throw new Error(`Worker ${id} requires either a model or custom execution function`);
2258
+ throw new Error(
2259
+ `Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
2260
+ );
2166
2261
  }
2167
2262
  const defaultSystemPrompt = `You are a specialized worker agent with the following capabilities:
2168
2263
  Skills: ${capabilities.skills.join(", ")}
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,84 @@ 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 taskResult = {
1985
+ assignmentId: currentAssignment.id,
1986
+ workerId,
1987
+ result: response,
1988
+ completedAt: Date.now(),
1989
+ success: true,
1990
+ metadata: {
1991
+ agent_type: "react",
1992
+ iterations: result.iteration || 0
1993
+ }
1994
+ };
1995
+ return {
1996
+ completedTasks: [taskResult],
1997
+ currentAgent: "supervisor",
1998
+ status: "routing"
1999
+ };
2000
+ } catch (error) {
2001
+ console.error(`[ReActWrapper:${workerId}] Error:`, error);
2002
+ const currentAssignment = state.activeAssignments.find(
2003
+ (assignment) => assignment.workerId === workerId
2004
+ );
2005
+ if (currentAssignment) {
2006
+ const errorResult = {
2007
+ assignmentId: currentAssignment.id,
2008
+ workerId,
2009
+ success: false,
2010
+ result: "",
2011
+ error: error instanceof Error ? error.message : "Unknown error in ReAct agent",
2012
+ completedAt: Date.now()
2013
+ };
2014
+ return {
2015
+ completedTasks: [errorResult],
2016
+ currentAgent: "supervisor",
2017
+ status: "routing"
2018
+ };
2019
+ }
2020
+ return {
2021
+ status: "failed",
2022
+ error: error instanceof Error ? error.message : `Unknown error in ReAct wrapper for ${workerId}`
2023
+ };
2024
+ }
2025
+ };
2026
+ }
2027
+
1950
2028
  // src/multi-agent/nodes.ts
1951
2029
  import { HumanMessage as HumanMessage5, SystemMessage as SystemMessage5 } from "@langchain/core/messages";
1952
2030
  import { toLangChainTools as toLangChainTools2 } from "@agentforge/core";
@@ -2040,7 +2118,8 @@ function createWorkerNode(config) {
2040
2118
  tools = [],
2041
2119
  systemPrompt,
2042
2120
  verbose = false,
2043
- executeFn
2121
+ executeFn,
2122
+ agent
2044
2123
  } = config;
2045
2124
  return async (state) => {
2046
2125
  try {
@@ -2060,10 +2139,26 @@ function createWorkerNode(config) {
2060
2139
  };
2061
2140
  }
2062
2141
  if (executeFn) {
2142
+ if (verbose) {
2143
+ console.log(`[Worker:${id}] Using custom executeFn`);
2144
+ }
2063
2145
  return await executeFn(state);
2064
2146
  }
2147
+ if (agent) {
2148
+ if (isReActAgent(agent)) {
2149
+ if (verbose) {
2150
+ console.log(`[Worker:${id}] Using ReAct agent (auto-wrapped)`);
2151
+ }
2152
+ const wrappedFn = wrapReActAgent(id, agent, verbose);
2153
+ return await wrappedFn(state);
2154
+ } else {
2155
+ console.warn(`[Worker:${id}] Agent provided but does not appear to be a ReAct agent. Falling back to default execution.`);
2156
+ }
2157
+ }
2065
2158
  if (!model) {
2066
- throw new Error(`Worker ${id} requires either a model or custom execution function`);
2159
+ throw new Error(
2160
+ `Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
2161
+ );
2067
2162
  }
2068
2163
  const defaultSystemPrompt = `You are a specialized worker agent with the following capabilities:
2069
2164
  Skills: ${capabilities.skills.join(", ")}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentforge/patterns",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Agent patterns (ReAct, Planner-Executor) for AgentForge framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -16,6 +16,17 @@
16
16
  "files": [
17
17
  "dist"
18
18
  ],
19
+ "scripts": {
20
+ "build": "tsup src/index.ts --format esm,cjs --dts --clean",
21
+ "dev": "tsup src/index.ts --format esm,cjs --dts --watch",
22
+ "test": "vitest",
23
+ "test:coverage": "vitest --coverage",
24
+ "typecheck": "tsc --noEmit",
25
+ "lint": "eslint src",
26
+ "lint:fix": "eslint src --fix",
27
+ "format": "prettier --write \"src/**/*.ts\"",
28
+ "clean": "rm -rf dist *.tsbuildinfo"
29
+ },
19
30
  "keywords": [
20
31
  "agents",
21
32
  "ai",
@@ -36,10 +47,10 @@
36
47
  "url": "https://github.com/TVScoundrel/agentforge/issues"
37
48
  },
38
49
  "dependencies": {
50
+ "@agentforge/core": "workspace:*",
39
51
  "@langchain/core": "^1.1.0",
40
52
  "@langchain/langgraph": "^1.0.0",
41
- "zod": "^3.23.8",
42
- "@agentforge/core": "0.4.0"
53
+ "zod": "^3.23.8"
43
54
  },
44
55
  "devDependencies": {
45
56
  "@eslint/js": "^9.17.0",
@@ -54,16 +65,5 @@
54
65
  "peerDependencies": {
55
66
  "@langchain/core": "^1.1.0",
56
67
  "@langchain/langgraph": "^1.0.0"
57
- },
58
- "scripts": {
59
- "build": "tsup src/index.ts --format esm,cjs --dts --clean",
60
- "dev": "tsup src/index.ts --format esm,cjs --dts --watch",
61
- "test": "vitest",
62
- "test:coverage": "vitest --coverage",
63
- "typecheck": "tsc --noEmit",
64
- "lint": "eslint src",
65
- "lint:fix": "eslint src --fix",
66
- "format": "prettier --write \"src/**/*.ts\"",
67
- "clean": "rm -rf dist *.tsbuildinfo"
68
68
  }
69
- }
69
+ }
package/LICENSE DELETED
@@ -1,22 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2024 Tom Van Schoor
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
22
-