@agentforge/patterns 0.16.88 → 0.16.90
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 +51 -42
- package/dist/index.d.cts +15 -196
- package/dist/index.d.ts +15 -196
- package/dist/index.js +51 -42
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -1140,32 +1140,17 @@ var plannerLogger = createPatternLogger("agentforge:patterns:plan-execute:planne
|
|
|
1140
1140
|
var executorLogger = createPatternLogger("agentforge:patterns:plan-execute:executor");
|
|
1141
1141
|
var replannerLogger = createPatternLogger("agentforge:patterns:plan-execute:replanner");
|
|
1142
1142
|
|
|
1143
|
-
// src/plan-execute/
|
|
1143
|
+
// src/plan-execute/model-response.ts
|
|
1144
1144
|
function hasTextContentPart(value) {
|
|
1145
1145
|
return typeof value === "object" && value !== null && "text" in value && typeof value.text === "string";
|
|
1146
1146
|
}
|
|
1147
|
-
function
|
|
1147
|
+
function stringifyModelContent(value) {
|
|
1148
1148
|
try {
|
|
1149
1149
|
const serialized = JSON.stringify(value);
|
|
1150
|
-
|
|
1151
|
-
return "undefined";
|
|
1152
|
-
}
|
|
1153
|
-
return serialized;
|
|
1154
|
-
} catch (error) {
|
|
1155
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
1156
|
-
return `[${fallbackLabel}: ${reason}]`;
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
function toJsonSafeValue(value) {
|
|
1160
|
-
try {
|
|
1161
|
-
const serialized = JSON.stringify(value);
|
|
1162
|
-
if (serialized === void 0) {
|
|
1163
|
-
return void 0;
|
|
1164
|
-
}
|
|
1165
|
-
return JSON.parse(serialized);
|
|
1150
|
+
return serialized === void 0 ? "undefined" : serialized;
|
|
1166
1151
|
} catch (error) {
|
|
1167
1152
|
const reason = error instanceof Error ? error.message : String(error);
|
|
1168
|
-
return `[Unserializable
|
|
1153
|
+
return `[Unserializable model content: ${reason}]`;
|
|
1169
1154
|
}
|
|
1170
1155
|
}
|
|
1171
1156
|
function normalizeModelContent(content) {
|
|
@@ -1183,10 +1168,15 @@ function normalizeModelContent(content) {
|
|
|
1183
1168
|
return textParts.join("\n");
|
|
1184
1169
|
}
|
|
1185
1170
|
}
|
|
1186
|
-
return
|
|
1171
|
+
return stringifyModelContent(content);
|
|
1187
1172
|
}
|
|
1188
|
-
function
|
|
1189
|
-
|
|
1173
|
+
function parseModelResponse(content, context) {
|
|
1174
|
+
const normalized = normalizeModelContent(content);
|
|
1175
|
+
try {
|
|
1176
|
+
return JSON.parse(normalized);
|
|
1177
|
+
} catch (parseError) {
|
|
1178
|
+
throw new Error(`Failed to parse ${context} from LLM response: ${parseError}`);
|
|
1179
|
+
}
|
|
1190
1180
|
}
|
|
1191
1181
|
|
|
1192
1182
|
// src/plan-execute/planner-node.ts
|
|
@@ -1215,19 +1205,13 @@ function createPlannerNode(config) {
|
|
|
1215
1205
|
new import_messages2.HumanMessage(userPrompt)
|
|
1216
1206
|
];
|
|
1217
1207
|
const response = await model.invoke(messages);
|
|
1218
|
-
const
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1226
|
-
confidence: parsed.confidence
|
|
1227
|
-
};
|
|
1228
|
-
} catch (parseError) {
|
|
1229
|
-
throw new Error(`Failed to parse plan from LLM response: ${parseError}`);
|
|
1230
|
-
}
|
|
1208
|
+
const parsed = parseModelResponse(response.content, "plan");
|
|
1209
|
+
const plan = {
|
|
1210
|
+
steps: parsed.steps.slice(0, maxSteps),
|
|
1211
|
+
goal: parsed.goal || state.input || "",
|
|
1212
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1213
|
+
confidence: parsed.confidence
|
|
1214
|
+
};
|
|
1231
1215
|
plannerLogger.info("Plan created", {
|
|
1232
1216
|
stepCount: plan.steps.length,
|
|
1233
1217
|
goal: plan.goal.substring(0, 100),
|
|
@@ -1418,6 +1402,37 @@ function createExecutorNode(config) {
|
|
|
1418
1402
|
|
|
1419
1403
|
// src/plan-execute/replanner-node.ts
|
|
1420
1404
|
var import_messages3 = require("@langchain/core/messages");
|
|
1405
|
+
|
|
1406
|
+
// src/plan-execute/serialization.ts
|
|
1407
|
+
function stringifyWithFallback(value, fallbackLabel) {
|
|
1408
|
+
try {
|
|
1409
|
+
const serialized = JSON.stringify(value);
|
|
1410
|
+
if (serialized === void 0) {
|
|
1411
|
+
return "undefined";
|
|
1412
|
+
}
|
|
1413
|
+
return serialized;
|
|
1414
|
+
} catch (error) {
|
|
1415
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1416
|
+
return `[${fallbackLabel}: ${reason}]`;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
function toJsonSafeValue(value) {
|
|
1420
|
+
try {
|
|
1421
|
+
const serialized = JSON.stringify(value);
|
|
1422
|
+
if (serialized === void 0) {
|
|
1423
|
+
return void 0;
|
|
1424
|
+
}
|
|
1425
|
+
return JSON.parse(serialized);
|
|
1426
|
+
} catch (error) {
|
|
1427
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1428
|
+
return `[Unserializable step result: ${reason}]`;
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
function serializePlanExecuteResult(result) {
|
|
1432
|
+
return stringifyWithFallback(result, "Unserializable step result");
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
// src/plan-execute/replanner-node.ts
|
|
1421
1436
|
function createReplannerNode(config) {
|
|
1422
1437
|
const {
|
|
1423
1438
|
model,
|
|
@@ -1455,13 +1470,7 @@ function createReplannerNode(config) {
|
|
|
1455
1470
|
new import_messages3.HumanMessage(userPrompt)
|
|
1456
1471
|
];
|
|
1457
1472
|
const response = await model.invoke(messages);
|
|
1458
|
-
const
|
|
1459
|
-
let decision;
|
|
1460
|
-
try {
|
|
1461
|
-
decision = JSON.parse(content);
|
|
1462
|
-
} catch (parseError) {
|
|
1463
|
-
throw new Error(`Failed to parse replan decision from LLM response: ${parseError}`);
|
|
1464
|
-
}
|
|
1473
|
+
const decision = parseModelResponse(response.content, "replan decision");
|
|
1465
1474
|
if (decision.shouldReplan) {
|
|
1466
1475
|
replannerLogger.info("Replanning triggered", {
|
|
1467
1476
|
reason: decision.reason,
|
package/dist/index.d.cts
CHANGED
|
@@ -2973,249 +2973,68 @@ type MultiAgentStateType = {
|
|
|
2973
2973
|
error?: string;
|
|
2974
2974
|
};
|
|
2975
2975
|
|
|
2976
|
-
/**
|
|
2977
|
-
* Type Definitions for Multi-Agent Coordination Pattern
|
|
2978
|
-
*
|
|
2979
|
-
* This module defines TypeScript types for the Multi-Agent pattern.
|
|
2980
|
-
*
|
|
2981
|
-
* @module patterns/multi-agent/types
|
|
2982
|
-
*/
|
|
2983
|
-
|
|
2984
|
-
type WorkerTool = Tool<never, unknown>;
|
|
2985
|
-
/**
|
|
2986
|
-
* Runtime config passed to worker execution functions.
|
|
2987
|
-
*
|
|
2988
|
-
* Includes LangGraph's RunnableConfig while remaining open to caller-defined
|
|
2989
|
-
* keys used by integrations.
|
|
2990
|
-
*/
|
|
2991
|
-
type WorkerExecutionConfig = RunnableConfig | Record<string, unknown>;
|
|
2992
|
-
/**
|
|
2993
|
-
* Configuration for the supervisor node
|
|
2994
|
-
*/
|
|
2995
2976
|
interface SupervisorConfig {
|
|
2996
|
-
/**
|
|
2997
|
-
* Language model for routing decisions (used for LLM-based routing)
|
|
2998
|
-
*/
|
|
2999
2977
|
model?: BaseChatModel;
|
|
3000
|
-
/**
|
|
3001
|
-
* Routing strategy to use
|
|
3002
|
-
*/
|
|
3003
2978
|
strategy: RoutingStrategy;
|
|
3004
|
-
/**
|
|
3005
|
-
* System prompt for the supervisor (LLM-based routing only)
|
|
3006
|
-
*/
|
|
3007
2979
|
systemPrompt?: string;
|
|
3008
|
-
/**
|
|
3009
|
-
* Custom routing function (for rule-based routing)
|
|
3010
|
-
*/
|
|
3011
2980
|
routingFn?: (state: MultiAgentStateType) => Promise<RoutingDecision>;
|
|
3012
|
-
/**
|
|
3013
|
-
* Whether to include verbose logging
|
|
3014
|
-
*/
|
|
3015
2981
|
verbose?: boolean;
|
|
3016
|
-
/**
|
|
3017
|
-
* Maximum number of routing iterations
|
|
3018
|
-
*/
|
|
3019
2982
|
maxIterations?: number;
|
|
3020
2983
|
/**
|
|
3021
|
-
* Maximum number of tool call retries before requiring routing decision
|
|
3022
|
-
*
|
|
3023
|
-
*
|
|
3024
|
-
*
|
|
2984
|
+
* Maximum number of tool call retries before requiring a routing decision.
|
|
2985
|
+
* Prevents infinite loops where the supervisor keeps calling tools without
|
|
2986
|
+
* making a routing decision.
|
|
3025
2987
|
* @default 3
|
|
3026
2988
|
*/
|
|
3027
2989
|
maxToolRetries?: number;
|
|
3028
2990
|
}
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
2991
|
+
|
|
2992
|
+
type WorkerExecutionConfig = RunnableConfig | Record<string, unknown>;
|
|
2993
|
+
type WorkerTool = Tool<never, unknown>;
|
|
3032
2994
|
interface WorkerConfig {
|
|
3033
|
-
/**
|
|
3034
|
-
* Unique identifier for this worker
|
|
3035
|
-
*/
|
|
3036
2995
|
id: string;
|
|
3037
|
-
/**
|
|
3038
|
-
* Worker capabilities
|
|
3039
|
-
*/
|
|
3040
2996
|
capabilities: WorkerCapabilities;
|
|
3041
|
-
/**
|
|
3042
|
-
* Language model for the worker
|
|
3043
|
-
*/
|
|
3044
2997
|
model?: BaseChatModel;
|
|
3045
|
-
/**
|
|
3046
|
-
* Available tools for this worker
|
|
3047
|
-
*/
|
|
3048
2998
|
tools?: WorkerTool[];
|
|
3049
|
-
/**
|
|
3050
|
-
* System prompt for the worker
|
|
3051
|
-
*/
|
|
3052
2999
|
systemPrompt?: string;
|
|
3053
|
-
/**
|
|
3054
|
-
* Whether to include verbose logging
|
|
3055
|
-
*/
|
|
3056
3000
|
verbose?: boolean;
|
|
3057
3001
|
/**
|
|
3058
|
-
* Custom execution function
|
|
3059
|
-
*
|
|
3060
|
-
* If provided, this function will be used to execute tasks for this worker.
|
|
3061
|
-
* Takes precedence over the `agent` property.
|
|
3062
|
-
*
|
|
3063
|
-
* The config parameter contains LangGraph runtime configuration including
|
|
3064
|
-
* thread_id for checkpointing, which is required for interrupt functionality.
|
|
3002
|
+
* Custom execution function. When provided, this takes precedence over
|
|
3003
|
+
* the `agent` property.
|
|
3065
3004
|
*/
|
|
3066
3005
|
executeFn?: (state: MultiAgentStateType, config?: WorkerExecutionConfig) => Promise<Partial<MultiAgentStateType>>;
|
|
3067
3006
|
/**
|
|
3068
|
-
* ReAct agent instance
|
|
3069
|
-
*
|
|
3070
|
-
* If provided, the Multi-Agent pattern will automatically wrap this ReAct agent
|
|
3071
|
-
* to work as a worker. The agent should be a compiled LangGraph StateGraph
|
|
3072
|
-
* (e.g., created with `createReActAgent()`).
|
|
3073
|
-
*
|
|
3074
|
-
* Note: `executeFn` takes precedence over `agent` if both are provided.
|
|
3075
|
-
*
|
|
3076
|
-
* @example
|
|
3077
|
-
* ```typescript
|
|
3078
|
-
* const hrAgent = createReActAgent({ model, tools, systemPrompt });
|
|
3079
|
-
*
|
|
3080
|
-
* const system = createMultiAgentSystem({
|
|
3081
|
-
* workers: [{
|
|
3082
|
-
* id: 'hr',
|
|
3083
|
-
* capabilities: { skills: ['hr'], ... },
|
|
3084
|
-
* agent: hrAgent, // Automatically wrapped!
|
|
3085
|
-
* }]
|
|
3086
|
-
* });
|
|
3087
|
-
* ```
|
|
3007
|
+
* ReAct agent instance. `executeFn` takes precedence when both are set.
|
|
3088
3008
|
*/
|
|
3089
3009
|
agent?: CompiledStateGraph<unknown, unknown>;
|
|
3090
3010
|
}
|
|
3091
|
-
|
|
3092
|
-
* Configuration for the aggregator node
|
|
3093
|
-
*/
|
|
3011
|
+
|
|
3094
3012
|
interface AggregatorConfig {
|
|
3095
|
-
/**
|
|
3096
|
-
* Language model for aggregation (optional)
|
|
3097
|
-
*/
|
|
3098
3013
|
model?: BaseChatModel;
|
|
3099
|
-
/**
|
|
3100
|
-
* System prompt for aggregation
|
|
3101
|
-
*/
|
|
3102
3014
|
systemPrompt?: string;
|
|
3103
|
-
/**
|
|
3104
|
-
* Custom aggregation function
|
|
3105
|
-
*/
|
|
3106
3015
|
aggregateFn?: (state: MultiAgentStateType) => Promise<string>;
|
|
3107
|
-
/**
|
|
3108
|
-
* Whether to include verbose logging
|
|
3109
|
-
*/
|
|
3110
3016
|
verbose?: boolean;
|
|
3111
3017
|
}
|
|
3112
|
-
/**
|
|
3113
|
-
* Configuration for the multi-agent system
|
|
3114
|
-
*/
|
|
3115
3018
|
interface MultiAgentSystemConfig {
|
|
3116
|
-
/**
|
|
3117
|
-
* Supervisor configuration
|
|
3118
|
-
*/
|
|
3119
3019
|
supervisor: SupervisorConfig;
|
|
3120
|
-
/**
|
|
3121
|
-
* Worker configurations
|
|
3122
|
-
*/
|
|
3123
3020
|
workers: WorkerConfig[];
|
|
3124
|
-
/**
|
|
3125
|
-
* Aggregator configuration (optional)
|
|
3126
|
-
*/
|
|
3127
3021
|
aggregator?: AggregatorConfig;
|
|
3128
|
-
/**
|
|
3129
|
-
* Maximum iterations for the entire system
|
|
3130
|
-
*/
|
|
3131
3022
|
maxIterations?: number;
|
|
3132
|
-
/**
|
|
3133
|
-
* Whether to include verbose logging
|
|
3134
|
-
*/
|
|
3135
3023
|
verbose?: boolean;
|
|
3136
3024
|
/**
|
|
3137
|
-
* Optional checkpointer for state persistence
|
|
3138
|
-
*
|
|
3139
|
-
*
|
|
3140
|
-
*
|
|
3141
|
-
* When worker agents are configured with `checkpointer: true`, they automatically use
|
|
3142
|
-
* separate checkpoint namespaces to enable proper handling of nested graph interrupts.
|
|
3143
|
-
*
|
|
3144
|
-
* The namespace format is: `{parent_thread_id}:worker:{workerId}`
|
|
3145
|
-
*
|
|
3146
|
-
* For example, if the parent thread ID is `thread_abc123` and the worker ID is `hr`,
|
|
3147
|
-
* the worker's checkpoint namespace will be `thread_abc123:worker:hr`.
|
|
3148
|
-
*
|
|
3149
|
-
* This allows worker agents to use the `askHuman` tool without causing infinite loops,
|
|
3150
|
-
* as each worker's state is saved and resumed independently.
|
|
3151
|
-
*
|
|
3152
|
-
* @example
|
|
3153
|
-
* Basic usage with checkpointer:
|
|
3154
|
-
* ```typescript
|
|
3155
|
-
* import { MemorySaver } from '@langchain/langgraph';
|
|
3156
|
-
*
|
|
3157
|
-
* const checkpointer = new MemorySaver();
|
|
3158
|
-
* const system = createMultiAgentSystem({
|
|
3159
|
-
* supervisor: { strategy: 'skill-based', model },
|
|
3160
|
-
* workers: [...],
|
|
3161
|
-
* checkpointer
|
|
3162
|
-
* });
|
|
3163
|
-
* ```
|
|
3164
|
-
*
|
|
3165
|
-
* @example
|
|
3166
|
-
* Worker agents with nested graph interrupts:
|
|
3167
|
-
* ```typescript
|
|
3168
|
-
* import { MemorySaver } from '@langchain/langgraph';
|
|
3169
|
-
* import { createReActAgent } from '@agentforge/patterns';
|
|
3170
|
-
* import { createAskHumanTool } from '@agentforge/tools';
|
|
3171
|
-
*
|
|
3172
|
-
* // Create worker agent with checkpointer: true
|
|
3173
|
-
* const hrAgent = createReActAgent({
|
|
3174
|
-
* model,
|
|
3175
|
-
* tools: [createAskHumanTool(), ...hrTools],
|
|
3176
|
-
* checkpointer: true // Use parent's checkpointer with separate namespace
|
|
3177
|
-
* });
|
|
3178
|
-
*
|
|
3179
|
-
* // Create multi-agent system with checkpointer
|
|
3180
|
-
* const system = createMultiAgentSystem({
|
|
3181
|
-
* supervisor: { strategy: 'skill-based', model },
|
|
3182
|
-
* workers: [{
|
|
3183
|
-
* id: 'hr',
|
|
3184
|
-
* capabilities: { skills: ['hr'], ... },
|
|
3185
|
-
* agent: hrAgent
|
|
3186
|
-
* }],
|
|
3187
|
-
* checkpointer: new MemorySaver()
|
|
3188
|
-
* });
|
|
3189
|
-
*
|
|
3190
|
-
* // When hrAgent calls askHuman, it will use checkpoint namespace:
|
|
3191
|
-
* // thread_abc123:worker:hr
|
|
3192
|
-
* ```
|
|
3025
|
+
* Optional checkpointer for state persistence and human-in-the-loop flows.
|
|
3026
|
+
* Worker agents use isolated namespaces in the form
|
|
3027
|
+
* `{parent_thread_id}:worker:{workerId}` so nested interrupts can resume
|
|
3028
|
+
* without looping through the parent graph.
|
|
3193
3029
|
*/
|
|
3194
3030
|
checkpointer?: BaseCheckpointSaver;
|
|
3195
3031
|
}
|
|
3196
|
-
|
|
3197
|
-
* Node type for multi-agent graph
|
|
3198
|
-
*/
|
|
3032
|
+
|
|
3199
3033
|
type MultiAgentNode = 'supervisor' | 'aggregator' | string;
|
|
3200
|
-
/**
|
|
3201
|
-
* Route type for multi-agent graph
|
|
3202
|
-
*/
|
|
3203
3034
|
type MultiAgentRoute = 'continue' | 'aggregate' | 'end' | string | string[];
|
|
3204
|
-
/**
|
|
3205
|
-
* Router function type
|
|
3206
|
-
*/
|
|
3207
3035
|
type MultiAgentRouter = (state: MultiAgentStateType) => MultiAgentRoute;
|
|
3208
|
-
/**
|
|
3209
|
-
* Routing strategy implementation
|
|
3210
|
-
*/
|
|
3211
3036
|
interface RoutingStrategyImpl {
|
|
3212
|
-
/**
|
|
3213
|
-
* Name of the strategy
|
|
3214
|
-
*/
|
|
3215
3037
|
name: RoutingStrategy;
|
|
3216
|
-
/**
|
|
3217
|
-
* Execute the routing strategy
|
|
3218
|
-
*/
|
|
3219
3038
|
route: (state: MultiAgentStateType, config: SupervisorConfig) => Promise<RoutingDecision>;
|
|
3220
3039
|
}
|
|
3221
3040
|
|
package/dist/index.d.ts
CHANGED
|
@@ -2973,249 +2973,68 @@ type MultiAgentStateType = {
|
|
|
2973
2973
|
error?: string;
|
|
2974
2974
|
};
|
|
2975
2975
|
|
|
2976
|
-
/**
|
|
2977
|
-
* Type Definitions for Multi-Agent Coordination Pattern
|
|
2978
|
-
*
|
|
2979
|
-
* This module defines TypeScript types for the Multi-Agent pattern.
|
|
2980
|
-
*
|
|
2981
|
-
* @module patterns/multi-agent/types
|
|
2982
|
-
*/
|
|
2983
|
-
|
|
2984
|
-
type WorkerTool = Tool<never, unknown>;
|
|
2985
|
-
/**
|
|
2986
|
-
* Runtime config passed to worker execution functions.
|
|
2987
|
-
*
|
|
2988
|
-
* Includes LangGraph's RunnableConfig while remaining open to caller-defined
|
|
2989
|
-
* keys used by integrations.
|
|
2990
|
-
*/
|
|
2991
|
-
type WorkerExecutionConfig = RunnableConfig | Record<string, unknown>;
|
|
2992
|
-
/**
|
|
2993
|
-
* Configuration for the supervisor node
|
|
2994
|
-
*/
|
|
2995
2976
|
interface SupervisorConfig {
|
|
2996
|
-
/**
|
|
2997
|
-
* Language model for routing decisions (used for LLM-based routing)
|
|
2998
|
-
*/
|
|
2999
2977
|
model?: BaseChatModel;
|
|
3000
|
-
/**
|
|
3001
|
-
* Routing strategy to use
|
|
3002
|
-
*/
|
|
3003
2978
|
strategy: RoutingStrategy;
|
|
3004
|
-
/**
|
|
3005
|
-
* System prompt for the supervisor (LLM-based routing only)
|
|
3006
|
-
*/
|
|
3007
2979
|
systemPrompt?: string;
|
|
3008
|
-
/**
|
|
3009
|
-
* Custom routing function (for rule-based routing)
|
|
3010
|
-
*/
|
|
3011
2980
|
routingFn?: (state: MultiAgentStateType) => Promise<RoutingDecision>;
|
|
3012
|
-
/**
|
|
3013
|
-
* Whether to include verbose logging
|
|
3014
|
-
*/
|
|
3015
2981
|
verbose?: boolean;
|
|
3016
|
-
/**
|
|
3017
|
-
* Maximum number of routing iterations
|
|
3018
|
-
*/
|
|
3019
2982
|
maxIterations?: number;
|
|
3020
2983
|
/**
|
|
3021
|
-
* Maximum number of tool call retries before requiring routing decision
|
|
3022
|
-
*
|
|
3023
|
-
*
|
|
3024
|
-
*
|
|
2984
|
+
* Maximum number of tool call retries before requiring a routing decision.
|
|
2985
|
+
* Prevents infinite loops where the supervisor keeps calling tools without
|
|
2986
|
+
* making a routing decision.
|
|
3025
2987
|
* @default 3
|
|
3026
2988
|
*/
|
|
3027
2989
|
maxToolRetries?: number;
|
|
3028
2990
|
}
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
2991
|
+
|
|
2992
|
+
type WorkerExecutionConfig = RunnableConfig | Record<string, unknown>;
|
|
2993
|
+
type WorkerTool = Tool<never, unknown>;
|
|
3032
2994
|
interface WorkerConfig {
|
|
3033
|
-
/**
|
|
3034
|
-
* Unique identifier for this worker
|
|
3035
|
-
*/
|
|
3036
2995
|
id: string;
|
|
3037
|
-
/**
|
|
3038
|
-
* Worker capabilities
|
|
3039
|
-
*/
|
|
3040
2996
|
capabilities: WorkerCapabilities;
|
|
3041
|
-
/**
|
|
3042
|
-
* Language model for the worker
|
|
3043
|
-
*/
|
|
3044
2997
|
model?: BaseChatModel;
|
|
3045
|
-
/**
|
|
3046
|
-
* Available tools for this worker
|
|
3047
|
-
*/
|
|
3048
2998
|
tools?: WorkerTool[];
|
|
3049
|
-
/**
|
|
3050
|
-
* System prompt for the worker
|
|
3051
|
-
*/
|
|
3052
2999
|
systemPrompt?: string;
|
|
3053
|
-
/**
|
|
3054
|
-
* Whether to include verbose logging
|
|
3055
|
-
*/
|
|
3056
3000
|
verbose?: boolean;
|
|
3057
3001
|
/**
|
|
3058
|
-
* Custom execution function
|
|
3059
|
-
*
|
|
3060
|
-
* If provided, this function will be used to execute tasks for this worker.
|
|
3061
|
-
* Takes precedence over the `agent` property.
|
|
3062
|
-
*
|
|
3063
|
-
* The config parameter contains LangGraph runtime configuration including
|
|
3064
|
-
* thread_id for checkpointing, which is required for interrupt functionality.
|
|
3002
|
+
* Custom execution function. When provided, this takes precedence over
|
|
3003
|
+
* the `agent` property.
|
|
3065
3004
|
*/
|
|
3066
3005
|
executeFn?: (state: MultiAgentStateType, config?: WorkerExecutionConfig) => Promise<Partial<MultiAgentStateType>>;
|
|
3067
3006
|
/**
|
|
3068
|
-
* ReAct agent instance
|
|
3069
|
-
*
|
|
3070
|
-
* If provided, the Multi-Agent pattern will automatically wrap this ReAct agent
|
|
3071
|
-
* to work as a worker. The agent should be a compiled LangGraph StateGraph
|
|
3072
|
-
* (e.g., created with `createReActAgent()`).
|
|
3073
|
-
*
|
|
3074
|
-
* Note: `executeFn` takes precedence over `agent` if both are provided.
|
|
3075
|
-
*
|
|
3076
|
-
* @example
|
|
3077
|
-
* ```typescript
|
|
3078
|
-
* const hrAgent = createReActAgent({ model, tools, systemPrompt });
|
|
3079
|
-
*
|
|
3080
|
-
* const system = createMultiAgentSystem({
|
|
3081
|
-
* workers: [{
|
|
3082
|
-
* id: 'hr',
|
|
3083
|
-
* capabilities: { skills: ['hr'], ... },
|
|
3084
|
-
* agent: hrAgent, // Automatically wrapped!
|
|
3085
|
-
* }]
|
|
3086
|
-
* });
|
|
3087
|
-
* ```
|
|
3007
|
+
* ReAct agent instance. `executeFn` takes precedence when both are set.
|
|
3088
3008
|
*/
|
|
3089
3009
|
agent?: CompiledStateGraph<unknown, unknown>;
|
|
3090
3010
|
}
|
|
3091
|
-
|
|
3092
|
-
* Configuration for the aggregator node
|
|
3093
|
-
*/
|
|
3011
|
+
|
|
3094
3012
|
interface AggregatorConfig {
|
|
3095
|
-
/**
|
|
3096
|
-
* Language model for aggregation (optional)
|
|
3097
|
-
*/
|
|
3098
3013
|
model?: BaseChatModel;
|
|
3099
|
-
/**
|
|
3100
|
-
* System prompt for aggregation
|
|
3101
|
-
*/
|
|
3102
3014
|
systemPrompt?: string;
|
|
3103
|
-
/**
|
|
3104
|
-
* Custom aggregation function
|
|
3105
|
-
*/
|
|
3106
3015
|
aggregateFn?: (state: MultiAgentStateType) => Promise<string>;
|
|
3107
|
-
/**
|
|
3108
|
-
* Whether to include verbose logging
|
|
3109
|
-
*/
|
|
3110
3016
|
verbose?: boolean;
|
|
3111
3017
|
}
|
|
3112
|
-
/**
|
|
3113
|
-
* Configuration for the multi-agent system
|
|
3114
|
-
*/
|
|
3115
3018
|
interface MultiAgentSystemConfig {
|
|
3116
|
-
/**
|
|
3117
|
-
* Supervisor configuration
|
|
3118
|
-
*/
|
|
3119
3019
|
supervisor: SupervisorConfig;
|
|
3120
|
-
/**
|
|
3121
|
-
* Worker configurations
|
|
3122
|
-
*/
|
|
3123
3020
|
workers: WorkerConfig[];
|
|
3124
|
-
/**
|
|
3125
|
-
* Aggregator configuration (optional)
|
|
3126
|
-
*/
|
|
3127
3021
|
aggregator?: AggregatorConfig;
|
|
3128
|
-
/**
|
|
3129
|
-
* Maximum iterations for the entire system
|
|
3130
|
-
*/
|
|
3131
3022
|
maxIterations?: number;
|
|
3132
|
-
/**
|
|
3133
|
-
* Whether to include verbose logging
|
|
3134
|
-
*/
|
|
3135
3023
|
verbose?: boolean;
|
|
3136
3024
|
/**
|
|
3137
|
-
* Optional checkpointer for state persistence
|
|
3138
|
-
*
|
|
3139
|
-
*
|
|
3140
|
-
*
|
|
3141
|
-
* When worker agents are configured with `checkpointer: true`, they automatically use
|
|
3142
|
-
* separate checkpoint namespaces to enable proper handling of nested graph interrupts.
|
|
3143
|
-
*
|
|
3144
|
-
* The namespace format is: `{parent_thread_id}:worker:{workerId}`
|
|
3145
|
-
*
|
|
3146
|
-
* For example, if the parent thread ID is `thread_abc123` and the worker ID is `hr`,
|
|
3147
|
-
* the worker's checkpoint namespace will be `thread_abc123:worker:hr`.
|
|
3148
|
-
*
|
|
3149
|
-
* This allows worker agents to use the `askHuman` tool without causing infinite loops,
|
|
3150
|
-
* as each worker's state is saved and resumed independently.
|
|
3151
|
-
*
|
|
3152
|
-
* @example
|
|
3153
|
-
* Basic usage with checkpointer:
|
|
3154
|
-
* ```typescript
|
|
3155
|
-
* import { MemorySaver } from '@langchain/langgraph';
|
|
3156
|
-
*
|
|
3157
|
-
* const checkpointer = new MemorySaver();
|
|
3158
|
-
* const system = createMultiAgentSystem({
|
|
3159
|
-
* supervisor: { strategy: 'skill-based', model },
|
|
3160
|
-
* workers: [...],
|
|
3161
|
-
* checkpointer
|
|
3162
|
-
* });
|
|
3163
|
-
* ```
|
|
3164
|
-
*
|
|
3165
|
-
* @example
|
|
3166
|
-
* Worker agents with nested graph interrupts:
|
|
3167
|
-
* ```typescript
|
|
3168
|
-
* import { MemorySaver } from '@langchain/langgraph';
|
|
3169
|
-
* import { createReActAgent } from '@agentforge/patterns';
|
|
3170
|
-
* import { createAskHumanTool } from '@agentforge/tools';
|
|
3171
|
-
*
|
|
3172
|
-
* // Create worker agent with checkpointer: true
|
|
3173
|
-
* const hrAgent = createReActAgent({
|
|
3174
|
-
* model,
|
|
3175
|
-
* tools: [createAskHumanTool(), ...hrTools],
|
|
3176
|
-
* checkpointer: true // Use parent's checkpointer with separate namespace
|
|
3177
|
-
* });
|
|
3178
|
-
*
|
|
3179
|
-
* // Create multi-agent system with checkpointer
|
|
3180
|
-
* const system = createMultiAgentSystem({
|
|
3181
|
-
* supervisor: { strategy: 'skill-based', model },
|
|
3182
|
-
* workers: [{
|
|
3183
|
-
* id: 'hr',
|
|
3184
|
-
* capabilities: { skills: ['hr'], ... },
|
|
3185
|
-
* agent: hrAgent
|
|
3186
|
-
* }],
|
|
3187
|
-
* checkpointer: new MemorySaver()
|
|
3188
|
-
* });
|
|
3189
|
-
*
|
|
3190
|
-
* // When hrAgent calls askHuman, it will use checkpoint namespace:
|
|
3191
|
-
* // thread_abc123:worker:hr
|
|
3192
|
-
* ```
|
|
3025
|
+
* Optional checkpointer for state persistence and human-in-the-loop flows.
|
|
3026
|
+
* Worker agents use isolated namespaces in the form
|
|
3027
|
+
* `{parent_thread_id}:worker:{workerId}` so nested interrupts can resume
|
|
3028
|
+
* without looping through the parent graph.
|
|
3193
3029
|
*/
|
|
3194
3030
|
checkpointer?: BaseCheckpointSaver;
|
|
3195
3031
|
}
|
|
3196
|
-
|
|
3197
|
-
* Node type for multi-agent graph
|
|
3198
|
-
*/
|
|
3032
|
+
|
|
3199
3033
|
type MultiAgentNode = 'supervisor' | 'aggregator' | string;
|
|
3200
|
-
/**
|
|
3201
|
-
* Route type for multi-agent graph
|
|
3202
|
-
*/
|
|
3203
3034
|
type MultiAgentRoute = 'continue' | 'aggregate' | 'end' | string | string[];
|
|
3204
|
-
/**
|
|
3205
|
-
* Router function type
|
|
3206
|
-
*/
|
|
3207
3035
|
type MultiAgentRouter = (state: MultiAgentStateType) => MultiAgentRoute;
|
|
3208
|
-
/**
|
|
3209
|
-
* Routing strategy implementation
|
|
3210
|
-
*/
|
|
3211
3036
|
interface RoutingStrategyImpl {
|
|
3212
|
-
/**
|
|
3213
|
-
* Name of the strategy
|
|
3214
|
-
*/
|
|
3215
3037
|
name: RoutingStrategy;
|
|
3216
|
-
/**
|
|
3217
|
-
* Execute the routing strategy
|
|
3218
|
-
*/
|
|
3219
3038
|
route: (state: MultiAgentStateType, config: SupervisorConfig) => Promise<RoutingDecision>;
|
|
3220
3039
|
}
|
|
3221
3040
|
|
package/dist/index.js
CHANGED
|
@@ -1042,32 +1042,17 @@ var plannerLogger = createPatternLogger("agentforge:patterns:plan-execute:planne
|
|
|
1042
1042
|
var executorLogger = createPatternLogger("agentforge:patterns:plan-execute:executor");
|
|
1043
1043
|
var replannerLogger = createPatternLogger("agentforge:patterns:plan-execute:replanner");
|
|
1044
1044
|
|
|
1045
|
-
// src/plan-execute/
|
|
1045
|
+
// src/plan-execute/model-response.ts
|
|
1046
1046
|
function hasTextContentPart(value) {
|
|
1047
1047
|
return typeof value === "object" && value !== null && "text" in value && typeof value.text === "string";
|
|
1048
1048
|
}
|
|
1049
|
-
function
|
|
1049
|
+
function stringifyModelContent(value) {
|
|
1050
1050
|
try {
|
|
1051
1051
|
const serialized = JSON.stringify(value);
|
|
1052
|
-
|
|
1053
|
-
return "undefined";
|
|
1054
|
-
}
|
|
1055
|
-
return serialized;
|
|
1056
|
-
} catch (error) {
|
|
1057
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
1058
|
-
return `[${fallbackLabel}: ${reason}]`;
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
function toJsonSafeValue(value) {
|
|
1062
|
-
try {
|
|
1063
|
-
const serialized = JSON.stringify(value);
|
|
1064
|
-
if (serialized === void 0) {
|
|
1065
|
-
return void 0;
|
|
1066
|
-
}
|
|
1067
|
-
return JSON.parse(serialized);
|
|
1052
|
+
return serialized === void 0 ? "undefined" : serialized;
|
|
1068
1053
|
} catch (error) {
|
|
1069
1054
|
const reason = error instanceof Error ? error.message : String(error);
|
|
1070
|
-
return `[Unserializable
|
|
1055
|
+
return `[Unserializable model content: ${reason}]`;
|
|
1071
1056
|
}
|
|
1072
1057
|
}
|
|
1073
1058
|
function normalizeModelContent(content) {
|
|
@@ -1085,10 +1070,15 @@ function normalizeModelContent(content) {
|
|
|
1085
1070
|
return textParts.join("\n");
|
|
1086
1071
|
}
|
|
1087
1072
|
}
|
|
1088
|
-
return
|
|
1073
|
+
return stringifyModelContent(content);
|
|
1089
1074
|
}
|
|
1090
|
-
function
|
|
1091
|
-
|
|
1075
|
+
function parseModelResponse(content, context) {
|
|
1076
|
+
const normalized = normalizeModelContent(content);
|
|
1077
|
+
try {
|
|
1078
|
+
return JSON.parse(normalized);
|
|
1079
|
+
} catch (parseError) {
|
|
1080
|
+
throw new Error(`Failed to parse ${context} from LLM response: ${parseError}`);
|
|
1081
|
+
}
|
|
1092
1082
|
}
|
|
1093
1083
|
|
|
1094
1084
|
// src/plan-execute/planner-node.ts
|
|
@@ -1117,19 +1107,13 @@ function createPlannerNode(config) {
|
|
|
1117
1107
|
new HumanMessage2(userPrompt)
|
|
1118
1108
|
];
|
|
1119
1109
|
const response = await model.invoke(messages);
|
|
1120
|
-
const
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1128
|
-
confidence: parsed.confidence
|
|
1129
|
-
};
|
|
1130
|
-
} catch (parseError) {
|
|
1131
|
-
throw new Error(`Failed to parse plan from LLM response: ${parseError}`);
|
|
1132
|
-
}
|
|
1110
|
+
const parsed = parseModelResponse(response.content, "plan");
|
|
1111
|
+
const plan = {
|
|
1112
|
+
steps: parsed.steps.slice(0, maxSteps),
|
|
1113
|
+
goal: parsed.goal || state.input || "",
|
|
1114
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1115
|
+
confidence: parsed.confidence
|
|
1116
|
+
};
|
|
1133
1117
|
plannerLogger.info("Plan created", {
|
|
1134
1118
|
stepCount: plan.steps.length,
|
|
1135
1119
|
goal: plan.goal.substring(0, 100),
|
|
@@ -1320,6 +1304,37 @@ function createExecutorNode(config) {
|
|
|
1320
1304
|
|
|
1321
1305
|
// src/plan-execute/replanner-node.ts
|
|
1322
1306
|
import { HumanMessage as HumanMessage3, SystemMessage as SystemMessage3 } from "@langchain/core/messages";
|
|
1307
|
+
|
|
1308
|
+
// src/plan-execute/serialization.ts
|
|
1309
|
+
function stringifyWithFallback(value, fallbackLabel) {
|
|
1310
|
+
try {
|
|
1311
|
+
const serialized = JSON.stringify(value);
|
|
1312
|
+
if (serialized === void 0) {
|
|
1313
|
+
return "undefined";
|
|
1314
|
+
}
|
|
1315
|
+
return serialized;
|
|
1316
|
+
} catch (error) {
|
|
1317
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1318
|
+
return `[${fallbackLabel}: ${reason}]`;
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
function toJsonSafeValue(value) {
|
|
1322
|
+
try {
|
|
1323
|
+
const serialized = JSON.stringify(value);
|
|
1324
|
+
if (serialized === void 0) {
|
|
1325
|
+
return void 0;
|
|
1326
|
+
}
|
|
1327
|
+
return JSON.parse(serialized);
|
|
1328
|
+
} catch (error) {
|
|
1329
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1330
|
+
return `[Unserializable step result: ${reason}]`;
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
function serializePlanExecuteResult(result) {
|
|
1334
|
+
return stringifyWithFallback(result, "Unserializable step result");
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
// src/plan-execute/replanner-node.ts
|
|
1323
1338
|
function createReplannerNode(config) {
|
|
1324
1339
|
const {
|
|
1325
1340
|
model,
|
|
@@ -1357,13 +1372,7 @@ function createReplannerNode(config) {
|
|
|
1357
1372
|
new HumanMessage3(userPrompt)
|
|
1358
1373
|
];
|
|
1359
1374
|
const response = await model.invoke(messages);
|
|
1360
|
-
const
|
|
1361
|
-
let decision;
|
|
1362
|
-
try {
|
|
1363
|
-
decision = JSON.parse(content);
|
|
1364
|
-
} catch (parseError) {
|
|
1365
|
-
throw new Error(`Failed to parse replan decision from LLM response: ${parseError}`);
|
|
1366
|
-
}
|
|
1375
|
+
const decision = parseModelResponse(response.content, "replan decision");
|
|
1367
1376
|
if (decision.shouldReplan) {
|
|
1368
1377
|
replannerLogger.info("Replanning triggered", {
|
|
1369
1378
|
reason: decision.reason,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentforge/patterns",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.90",
|
|
4
4
|
"description": "Production-ready agent workflow patterns for TypeScript including ReAct and Planner-Executor, built on LangGraph.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -41,13 +41,13 @@
|
|
|
41
41
|
"url": "https://github.com/TVScoundrel/agentforge/issues"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@agentforge/core": "0.16.
|
|
44
|
+
"@agentforge/core": "0.16.90",
|
|
45
45
|
"@langchain/core": "^1.1.17",
|
|
46
46
|
"@langchain/langgraph": "^1.1.2",
|
|
47
47
|
"zod": "^3.23.8"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@agentforge/testing": "0.16.
|
|
50
|
+
"@agentforge/testing": "0.16.90",
|
|
51
51
|
"@eslint/js": "^9.17.0",
|
|
52
52
|
"@types/node": "^22.10.2",
|
|
53
53
|
"eslint": "^9.17.0",
|