@agentforge/patterns 0.1.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/README.md +489 -0
- package/dist/index.cjs +2564 -0
- package/dist/index.d.cts +2600 -0
- package/dist/index.d.ts +2600 -0
- package/dist/index.js +2464 -0
- package/package.json +69 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2564 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AgentMessageSchema: () => AgentMessageSchema,
|
|
24
|
+
AgentRoleSchema: () => AgentRoleSchema,
|
|
25
|
+
CompletedStepSchema: () => CompletedStepSchema,
|
|
26
|
+
DEFAULT_AGGREGATOR_SYSTEM_PROMPT: () => DEFAULT_AGGREGATOR_SYSTEM_PROMPT,
|
|
27
|
+
DEFAULT_GENERATOR_SYSTEM_PROMPT: () => DEFAULT_GENERATOR_SYSTEM_PROMPT,
|
|
28
|
+
DEFAULT_PLANNER_SYSTEM_PROMPT: () => DEFAULT_PLANNER_SYSTEM_PROMPT,
|
|
29
|
+
DEFAULT_REACT_SYSTEM_PROMPT: () => DEFAULT_REACT_SYSTEM_PROMPT,
|
|
30
|
+
DEFAULT_REFLECTOR_SYSTEM_PROMPT: () => DEFAULT_REFLECTOR_SYSTEM_PROMPT,
|
|
31
|
+
DEFAULT_REPLANNER_SYSTEM_PROMPT: () => DEFAULT_REPLANNER_SYSTEM_PROMPT,
|
|
32
|
+
DEFAULT_REVISER_SYSTEM_PROMPT: () => DEFAULT_REVISER_SYSTEM_PROMPT,
|
|
33
|
+
DEFAULT_SUPERVISOR_SYSTEM_PROMPT: () => DEFAULT_SUPERVISOR_SYSTEM_PROMPT,
|
|
34
|
+
ExecutionStatusSchema: () => ExecutionStatusSchema,
|
|
35
|
+
GENERATION_PROMPT_TEMPLATE: () => GENERATION_PROMPT_TEMPLATE,
|
|
36
|
+
HandoffRequestSchema: () => HandoffRequestSchema,
|
|
37
|
+
MessageSchema: () => MessageSchema,
|
|
38
|
+
MessageTypeSchema: () => MessageTypeSchema,
|
|
39
|
+
MultiAgentState: () => MultiAgentState,
|
|
40
|
+
MultiAgentStateConfig: () => MultiAgentStateConfig,
|
|
41
|
+
MultiAgentStatusSchema: () => MultiAgentStatusSchema,
|
|
42
|
+
MultiAgentSystemBuilder: () => MultiAgentSystemBuilder,
|
|
43
|
+
PLANNING_PROMPT_TEMPLATE: () => PLANNING_PROMPT_TEMPLATE,
|
|
44
|
+
PlanExecuteState: () => PlanExecuteState,
|
|
45
|
+
PlanExecuteStateConfig: () => PlanExecuteStateConfig,
|
|
46
|
+
PlanSchema: () => PlanSchema,
|
|
47
|
+
PlanStepSchema: () => PlanStepSchema,
|
|
48
|
+
QUALITY_CRITERIA_TEMPLATE: () => QUALITY_CRITERIA_TEMPLATE,
|
|
49
|
+
QualityCriteriaSchema: () => QualityCriteriaSchema,
|
|
50
|
+
REFLECTION_ENTRY_TEMPLATE: () => REFLECTION_ENTRY_TEMPLATE,
|
|
51
|
+
REFLECTION_HISTORY_TEMPLATE: () => REFLECTION_HISTORY_TEMPLATE,
|
|
52
|
+
REFLECTION_PROMPT_TEMPLATE: () => REFLECTION_PROMPT_TEMPLATE,
|
|
53
|
+
REPLANNING_PROMPT_TEMPLATE: () => REPLANNING_PROMPT_TEMPLATE,
|
|
54
|
+
REVISION_ENTRY_TEMPLATE: () => REVISION_ENTRY_TEMPLATE,
|
|
55
|
+
REVISION_PROMPT_TEMPLATE: () => REVISION_PROMPT_TEMPLATE,
|
|
56
|
+
ReActAgentBuilder: () => ReActAgentBuilder,
|
|
57
|
+
ReActState: () => ReActState,
|
|
58
|
+
ReflectionConfigSchema: () => ReflectionConfigSchema,
|
|
59
|
+
ReflectionSchema: () => ReflectionSchema,
|
|
60
|
+
ReflectionState: () => ReflectionState,
|
|
61
|
+
ReflectionStateConfig: () => ReflectionStateConfig,
|
|
62
|
+
ReflectionStatusSchema: () => ReflectionStatusSchema,
|
|
63
|
+
ReplanDecisionSchema: () => ReplanDecisionSchema,
|
|
64
|
+
RevisionSchema: () => RevisionSchema,
|
|
65
|
+
RoutingDecisionSchema: () => RoutingDecisionSchema,
|
|
66
|
+
RoutingStrategySchema: () => RoutingStrategySchema,
|
|
67
|
+
ScratchpadEntrySchema: () => ScratchpadEntrySchema,
|
|
68
|
+
TaskAssignmentSchema: () => TaskAssignmentSchema,
|
|
69
|
+
TaskResultSchema: () => TaskResultSchema,
|
|
70
|
+
ThoughtSchema: () => ThoughtSchema,
|
|
71
|
+
ToolCallSchema: () => ToolCallSchema,
|
|
72
|
+
ToolResultSchema: () => ToolResultSchema,
|
|
73
|
+
WorkerCapabilitiesSchema: () => WorkerCapabilitiesSchema,
|
|
74
|
+
createAggregatorNode: () => createAggregatorNode,
|
|
75
|
+
createExecutorNode: () => createExecutorNode,
|
|
76
|
+
createFinisherNode: () => createFinisherNode,
|
|
77
|
+
createGeneratorNode: () => createGeneratorNode,
|
|
78
|
+
createMultiAgentSystem: () => createMultiAgentSystem,
|
|
79
|
+
createPlanExecuteAgent: () => createPlanExecuteAgent,
|
|
80
|
+
createPlannerNode: () => createPlannerNode,
|
|
81
|
+
createReActAgent: () => createReActAgent,
|
|
82
|
+
createReActAgentBuilder: () => createReActAgentBuilder,
|
|
83
|
+
createReflectionAgent: () => createReflectionAgent,
|
|
84
|
+
createReflectionFinisherNode: () => createFinisherNode2,
|
|
85
|
+
createReflectorNode: () => createReflectorNode,
|
|
86
|
+
createReplannerNode: () => createReplannerNode,
|
|
87
|
+
createReviserNode: () => createReviserNode,
|
|
88
|
+
createSupervisorNode: () => createSupervisorNode,
|
|
89
|
+
createWorkerNode: () => createWorkerNode,
|
|
90
|
+
getRoutingStrategy: () => getRoutingStrategy,
|
|
91
|
+
llmBasedRouting: () => llmBasedRouting,
|
|
92
|
+
loadBalancedRouting: () => loadBalancedRouting,
|
|
93
|
+
registerWorkers: () => registerWorkers,
|
|
94
|
+
roundRobinRouting: () => roundRobinRouting,
|
|
95
|
+
ruleBasedRouting: () => ruleBasedRouting,
|
|
96
|
+
skillBasedRouting: () => skillBasedRouting
|
|
97
|
+
});
|
|
98
|
+
module.exports = __toCommonJS(index_exports);
|
|
99
|
+
|
|
100
|
+
// src/react/schemas.ts
|
|
101
|
+
var import_zod = require("zod");
|
|
102
|
+
var MessageRoleSchema = import_zod.z.enum(["system", "user", "assistant", "tool"]);
|
|
103
|
+
var MessageSchema = import_zod.z.object({
|
|
104
|
+
role: MessageRoleSchema,
|
|
105
|
+
content: import_zod.z.string(),
|
|
106
|
+
name: import_zod.z.string().optional(),
|
|
107
|
+
metadata: import_zod.z.record(import_zod.z.any()).optional()
|
|
108
|
+
});
|
|
109
|
+
var ThoughtSchema = import_zod.z.object({
|
|
110
|
+
content: import_zod.z.string(),
|
|
111
|
+
timestamp: import_zod.z.number().optional(),
|
|
112
|
+
metadata: import_zod.z.record(import_zod.z.any()).optional()
|
|
113
|
+
});
|
|
114
|
+
var ToolCallSchema = import_zod.z.object({
|
|
115
|
+
id: import_zod.z.string(),
|
|
116
|
+
name: import_zod.z.string(),
|
|
117
|
+
arguments: import_zod.z.record(import_zod.z.any()),
|
|
118
|
+
timestamp: import_zod.z.number().optional()
|
|
119
|
+
});
|
|
120
|
+
var ToolResultSchema = import_zod.z.object({
|
|
121
|
+
toolCallId: import_zod.z.string(),
|
|
122
|
+
result: import_zod.z.any(),
|
|
123
|
+
error: import_zod.z.string().optional(),
|
|
124
|
+
timestamp: import_zod.z.number().optional()
|
|
125
|
+
});
|
|
126
|
+
var ScratchpadEntrySchema = import_zod.z.object({
|
|
127
|
+
step: import_zod.z.number(),
|
|
128
|
+
thought: import_zod.z.string().optional(),
|
|
129
|
+
action: import_zod.z.string().optional(),
|
|
130
|
+
observation: import_zod.z.string().optional(),
|
|
131
|
+
timestamp: import_zod.z.number().optional()
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// src/react/state.ts
|
|
135
|
+
var import_zod2 = require("zod");
|
|
136
|
+
var import_core = require("@agentforge/core");
|
|
137
|
+
var ReActStateConfig = {
|
|
138
|
+
/**
|
|
139
|
+
* Conversation messages
|
|
140
|
+
* Accumulates all messages in the conversation
|
|
141
|
+
*/
|
|
142
|
+
messages: {
|
|
143
|
+
schema: import_zod2.z.array(MessageSchema),
|
|
144
|
+
reducer: (left, right) => [...left, ...right],
|
|
145
|
+
default: () => [],
|
|
146
|
+
description: "Conversation message history"
|
|
147
|
+
},
|
|
148
|
+
/**
|
|
149
|
+
* Reasoning thoughts
|
|
150
|
+
* Accumulates all reasoning steps the agent takes
|
|
151
|
+
*/
|
|
152
|
+
thoughts: {
|
|
153
|
+
schema: import_zod2.z.array(ThoughtSchema),
|
|
154
|
+
reducer: (left, right) => [...left, ...right],
|
|
155
|
+
default: () => [],
|
|
156
|
+
description: "Agent reasoning steps"
|
|
157
|
+
},
|
|
158
|
+
/**
|
|
159
|
+
* Tool calls (actions)
|
|
160
|
+
* Accumulates all tool calls made by the agent
|
|
161
|
+
*/
|
|
162
|
+
actions: {
|
|
163
|
+
schema: import_zod2.z.array(ToolCallSchema),
|
|
164
|
+
reducer: (left, right) => [...left, ...right],
|
|
165
|
+
default: () => [],
|
|
166
|
+
description: "Tool calls made by the agent"
|
|
167
|
+
},
|
|
168
|
+
/**
|
|
169
|
+
* Tool results (observations)
|
|
170
|
+
* Accumulates all observations from tool executions
|
|
171
|
+
*/
|
|
172
|
+
observations: {
|
|
173
|
+
schema: import_zod2.z.array(ToolResultSchema),
|
|
174
|
+
reducer: (left, right) => [...left, ...right],
|
|
175
|
+
default: () => [],
|
|
176
|
+
description: "Results from tool executions"
|
|
177
|
+
},
|
|
178
|
+
/**
|
|
179
|
+
* Scratchpad for intermediate reasoning
|
|
180
|
+
* Accumulates step-by-step reasoning process
|
|
181
|
+
*/
|
|
182
|
+
scratchpad: {
|
|
183
|
+
schema: import_zod2.z.array(ScratchpadEntrySchema),
|
|
184
|
+
reducer: (left, right) => [...left, ...right],
|
|
185
|
+
default: () => [],
|
|
186
|
+
description: "Intermediate reasoning scratchpad"
|
|
187
|
+
},
|
|
188
|
+
/**
|
|
189
|
+
* Current iteration count
|
|
190
|
+
* Tracks how many thought-action-observation loops have been executed
|
|
191
|
+
*/
|
|
192
|
+
iteration: {
|
|
193
|
+
schema: import_zod2.z.number(),
|
|
194
|
+
reducer: (left, right) => left + right,
|
|
195
|
+
default: () => 0,
|
|
196
|
+
description: "Current iteration count"
|
|
197
|
+
},
|
|
198
|
+
/**
|
|
199
|
+
* Whether the agent should continue iterating
|
|
200
|
+
*/
|
|
201
|
+
shouldContinue: {
|
|
202
|
+
schema: import_zod2.z.boolean().optional(),
|
|
203
|
+
default: () => true,
|
|
204
|
+
description: "Whether to continue the ReAct loop"
|
|
205
|
+
},
|
|
206
|
+
/**
|
|
207
|
+
* Final response (if any)
|
|
208
|
+
*/
|
|
209
|
+
response: {
|
|
210
|
+
schema: import_zod2.z.string().optional(),
|
|
211
|
+
description: "Final response from the agent"
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
var ReActState = (0, import_core.createStateAnnotation)(ReActStateConfig);
|
|
215
|
+
|
|
216
|
+
// src/react/prompts.ts
|
|
217
|
+
var DEFAULT_REACT_SYSTEM_PROMPT = `You are a helpful assistant that uses tools to solve problems.
|
|
218
|
+
|
|
219
|
+
You follow the ReAct (Reasoning and Action) pattern:
|
|
220
|
+
1. THINK: Reason about what to do next
|
|
221
|
+
2. ACT: Use a tool or provide a final answer
|
|
222
|
+
3. OBSERVE: Examine the tool's result
|
|
223
|
+
4. REPEAT: Continue until you can answer the user's question
|
|
224
|
+
|
|
225
|
+
When you need to use a tool:
|
|
226
|
+
- Think carefully about which tool to use
|
|
227
|
+
- Provide the correct arguments
|
|
228
|
+
- Wait for the observation before proceeding
|
|
229
|
+
|
|
230
|
+
When you have enough information:
|
|
231
|
+
- Provide a clear, complete answer
|
|
232
|
+
- Cite the tools you used if relevant`;
|
|
233
|
+
function formatScratchpad(scratchpad) {
|
|
234
|
+
if (scratchpad.length === 0) {
|
|
235
|
+
return "No previous steps.";
|
|
236
|
+
}
|
|
237
|
+
return scratchpad.map((entry) => {
|
|
238
|
+
const parts = [`Step ${entry.step}:`];
|
|
239
|
+
if (entry.thought) parts.push(` Thought: ${entry.thought}`);
|
|
240
|
+
if (entry.action) parts.push(` Action: ${entry.action}`);
|
|
241
|
+
if (entry.observation) parts.push(` Observation: ${entry.observation}`);
|
|
242
|
+
return parts.join("\n");
|
|
243
|
+
}).join("\n\n");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// src/react/nodes.ts
|
|
247
|
+
var import_messages = require("@langchain/core/messages");
|
|
248
|
+
var import_core2 = require("@agentforge/core");
|
|
249
|
+
function createReasoningNode(llm, tools, systemPrompt, maxIterations, verbose = false) {
|
|
250
|
+
const langchainTools = (0, import_core2.toLangChainTools)(tools);
|
|
251
|
+
const llmWithTools = llm.bindTools ? llm.bindTools(langchainTools) : llm;
|
|
252
|
+
return async (state) => {
|
|
253
|
+
if (verbose) {
|
|
254
|
+
console.log(`[reasoning] Iteration ${state.iteration + 1}/${maxIterations}`);
|
|
255
|
+
}
|
|
256
|
+
const stateMessages = state.messages || [];
|
|
257
|
+
const messages = [
|
|
258
|
+
new import_messages.SystemMessage(systemPrompt),
|
|
259
|
+
...stateMessages.map((msg) => {
|
|
260
|
+
if (msg.role === "user") return new import_messages.HumanMessage(msg.content);
|
|
261
|
+
if (msg.role === "assistant") return new import_messages.AIMessage(msg.content);
|
|
262
|
+
if (msg.role === "system") return new import_messages.SystemMessage(msg.content);
|
|
263
|
+
return new import_messages.HumanMessage(msg.content);
|
|
264
|
+
})
|
|
265
|
+
];
|
|
266
|
+
const scratchpad = state.scratchpad || [];
|
|
267
|
+
if (scratchpad.length > 0) {
|
|
268
|
+
const scratchpadText = formatScratchpad(scratchpad);
|
|
269
|
+
messages.push(new import_messages.SystemMessage(`Previous steps:
|
|
270
|
+
${scratchpadText}`));
|
|
271
|
+
}
|
|
272
|
+
const response = await llmWithTools.invoke(messages);
|
|
273
|
+
const thought = typeof response.content === "string" ? response.content : "";
|
|
274
|
+
const toolCalls = [];
|
|
275
|
+
if (response.tool_calls && response.tool_calls.length > 0) {
|
|
276
|
+
for (const toolCall of response.tool_calls) {
|
|
277
|
+
toolCalls.push({
|
|
278
|
+
id: toolCall.id || `call_${Date.now()}_${Math.random()}`,
|
|
279
|
+
name: toolCall.name,
|
|
280
|
+
arguments: toolCall.args || {},
|
|
281
|
+
timestamp: Date.now()
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
const currentIteration = state.iteration || 0;
|
|
286
|
+
const shouldContinue = toolCalls.length > 0 && currentIteration + 1 < maxIterations;
|
|
287
|
+
return {
|
|
288
|
+
messages: [{ role: "assistant", content: thought }],
|
|
289
|
+
thoughts: thought ? [{ content: thought, timestamp: Date.now() }] : [],
|
|
290
|
+
actions: toolCalls,
|
|
291
|
+
iteration: 1,
|
|
292
|
+
// Increment iteration
|
|
293
|
+
shouldContinue,
|
|
294
|
+
response: toolCalls.length === 0 ? thought : void 0
|
|
295
|
+
// Final response if no tool calls
|
|
296
|
+
};
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
function createActionNode(tools, verbose = false) {
|
|
300
|
+
const toolMap = new Map(tools.map((tool) => [tool.metadata.name, tool]));
|
|
301
|
+
return async (state) => {
|
|
302
|
+
const actions = state.actions || [];
|
|
303
|
+
if (verbose) {
|
|
304
|
+
console.log(`[action] Executing ${actions.length} tool calls`);
|
|
305
|
+
}
|
|
306
|
+
const recentActions = actions.slice(-10);
|
|
307
|
+
const observations = [];
|
|
308
|
+
for (const action of recentActions) {
|
|
309
|
+
const tool = toolMap.get(action.name);
|
|
310
|
+
if (!tool) {
|
|
311
|
+
observations.push({
|
|
312
|
+
toolCallId: action.id,
|
|
313
|
+
result: null,
|
|
314
|
+
error: `Tool '${action.name}' not found`,
|
|
315
|
+
timestamp: Date.now()
|
|
316
|
+
});
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
try {
|
|
320
|
+
const result = await tool.execute(action.arguments);
|
|
321
|
+
observations.push({
|
|
322
|
+
toolCallId: action.id,
|
|
323
|
+
result,
|
|
324
|
+
timestamp: Date.now()
|
|
325
|
+
});
|
|
326
|
+
if (verbose) {
|
|
327
|
+
console.log(`[action] Tool '${action.name}' executed successfully`);
|
|
328
|
+
}
|
|
329
|
+
} catch (error) {
|
|
330
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
331
|
+
observations.push({
|
|
332
|
+
toolCallId: action.id,
|
|
333
|
+
result: null,
|
|
334
|
+
error: errorMessage,
|
|
335
|
+
timestamp: Date.now()
|
|
336
|
+
});
|
|
337
|
+
if (verbose) {
|
|
338
|
+
console.error(`[action] Tool '${action.name}' failed:`, errorMessage);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return {
|
|
343
|
+
observations
|
|
344
|
+
};
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
function createObservationNode(verbose = false) {
|
|
348
|
+
return async (state) => {
|
|
349
|
+
const observations = state.observations || [];
|
|
350
|
+
const thoughts = state.thoughts || [];
|
|
351
|
+
const actions = state.actions || [];
|
|
352
|
+
if (verbose) {
|
|
353
|
+
console.log(`[observation] Processing ${observations.length} observations`);
|
|
354
|
+
}
|
|
355
|
+
const recentObservations = observations.slice(-10);
|
|
356
|
+
const currentStep = state.iteration;
|
|
357
|
+
const latestThought = thoughts[thoughts.length - 1]?.content || "";
|
|
358
|
+
const latestActions = actions.slice(-10);
|
|
359
|
+
const latestObservations = recentObservations;
|
|
360
|
+
const scratchpadEntry = {
|
|
361
|
+
step: currentStep,
|
|
362
|
+
thought: latestThought,
|
|
363
|
+
action: latestActions.map((a) => `${a.name}(${JSON.stringify(a.arguments)})`).join(", "),
|
|
364
|
+
observation: latestObservations.map((obs) => {
|
|
365
|
+
if (obs.error) {
|
|
366
|
+
return `Error: ${obs.error}`;
|
|
367
|
+
}
|
|
368
|
+
return typeof obs.result === "string" ? obs.result : JSON.stringify(obs.result);
|
|
369
|
+
}).join("; "),
|
|
370
|
+
timestamp: Date.now()
|
|
371
|
+
};
|
|
372
|
+
const observationMessages = latestObservations.map((obs) => {
|
|
373
|
+
const content = obs.error ? `Error: ${obs.error}` : typeof obs.result === "string" ? obs.result : JSON.stringify(obs.result, null, 2);
|
|
374
|
+
return {
|
|
375
|
+
role: "tool",
|
|
376
|
+
content,
|
|
377
|
+
name: latestActions.find((a) => a.id === obs.toolCallId)?.name
|
|
378
|
+
};
|
|
379
|
+
});
|
|
380
|
+
return {
|
|
381
|
+
scratchpad: [scratchpadEntry],
|
|
382
|
+
messages: observationMessages
|
|
383
|
+
};
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// src/react/agent.ts
|
|
388
|
+
var import_langgraph = require("@langchain/langgraph");
|
|
389
|
+
var import_core3 = require("@agentforge/core");
|
|
390
|
+
function createReActAgent(config, options) {
|
|
391
|
+
const {
|
|
392
|
+
llm,
|
|
393
|
+
tools,
|
|
394
|
+
systemPrompt = DEFAULT_REACT_SYSTEM_PROMPT,
|
|
395
|
+
maxIterations = 10,
|
|
396
|
+
returnIntermediateSteps = false,
|
|
397
|
+
stopCondition
|
|
398
|
+
} = config;
|
|
399
|
+
const {
|
|
400
|
+
verbose = false,
|
|
401
|
+
nodeNames = {}
|
|
402
|
+
} = options || {};
|
|
403
|
+
const toolArray = tools instanceof import_core3.ToolRegistry ? tools.getAll() : tools;
|
|
404
|
+
const REASONING_NODE = nodeNames.reasoning || "reasoning";
|
|
405
|
+
const ACTION_NODE = nodeNames.action || "action";
|
|
406
|
+
const OBSERVATION_NODE = nodeNames.observation || "observation";
|
|
407
|
+
const reasoningNode = createReasoningNode(
|
|
408
|
+
llm,
|
|
409
|
+
toolArray,
|
|
410
|
+
systemPrompt,
|
|
411
|
+
maxIterations,
|
|
412
|
+
verbose
|
|
413
|
+
);
|
|
414
|
+
const actionNode = createActionNode(toolArray, verbose);
|
|
415
|
+
const observationNode = createObservationNode(verbose);
|
|
416
|
+
const shouldContinue = (state) => {
|
|
417
|
+
if (stopCondition && stopCondition(state)) {
|
|
418
|
+
return import_langgraph.END;
|
|
419
|
+
}
|
|
420
|
+
if (state.iteration >= maxIterations) {
|
|
421
|
+
return import_langgraph.END;
|
|
422
|
+
}
|
|
423
|
+
if (state.response) {
|
|
424
|
+
return import_langgraph.END;
|
|
425
|
+
}
|
|
426
|
+
if (state.shouldContinue === false) {
|
|
427
|
+
return import_langgraph.END;
|
|
428
|
+
}
|
|
429
|
+
return ACTION_NODE;
|
|
430
|
+
};
|
|
431
|
+
const workflow = new import_langgraph.StateGraph(ReActState).addNode(REASONING_NODE, reasoningNode).addNode(ACTION_NODE, actionNode).addNode(OBSERVATION_NODE, observationNode).addEdge("__start__", REASONING_NODE).addConditionalEdges(REASONING_NODE, shouldContinue).addEdge(ACTION_NODE, OBSERVATION_NODE).addEdge(OBSERVATION_NODE, REASONING_NODE);
|
|
432
|
+
return workflow.compile();
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// src/react/builder.ts
|
|
436
|
+
var ReActAgentBuilder = class {
|
|
437
|
+
config = {};
|
|
438
|
+
options = {};
|
|
439
|
+
/**
|
|
440
|
+
* Set the language model (required)
|
|
441
|
+
*
|
|
442
|
+
* @param llm - LangChain chat model to use for reasoning
|
|
443
|
+
*/
|
|
444
|
+
withLLM(llm) {
|
|
445
|
+
this.config.llm = llm;
|
|
446
|
+
return this;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Set the tools (required)
|
|
450
|
+
*
|
|
451
|
+
* @param tools - Tool registry or array of tools
|
|
452
|
+
*/
|
|
453
|
+
withTools(tools) {
|
|
454
|
+
this.config.tools = tools;
|
|
455
|
+
return this;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Set the system prompt (optional)
|
|
459
|
+
*
|
|
460
|
+
* @param systemPrompt - System prompt for the agent
|
|
461
|
+
*/
|
|
462
|
+
withSystemPrompt(systemPrompt) {
|
|
463
|
+
this.config.systemPrompt = systemPrompt;
|
|
464
|
+
return this;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Set the maximum iterations (optional, default: 10)
|
|
468
|
+
*
|
|
469
|
+
* @param maxIterations - Maximum number of thought-action loops
|
|
470
|
+
*/
|
|
471
|
+
withMaxIterations(maxIterations) {
|
|
472
|
+
this.config.maxIterations = maxIterations;
|
|
473
|
+
return this;
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Set whether to return intermediate steps (optional, default: false)
|
|
477
|
+
*
|
|
478
|
+
* @param returnIntermediateSteps - Whether to include reasoning steps in output
|
|
479
|
+
*/
|
|
480
|
+
withReturnIntermediateSteps(returnIntermediateSteps) {
|
|
481
|
+
this.config.returnIntermediateSteps = returnIntermediateSteps;
|
|
482
|
+
return this;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Set a custom stop condition (optional)
|
|
486
|
+
*
|
|
487
|
+
* @param stopCondition - Function that determines when to stop the agent
|
|
488
|
+
*/
|
|
489
|
+
withStopCondition(stopCondition) {
|
|
490
|
+
this.config.stopCondition = stopCondition;
|
|
491
|
+
return this;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Enable verbose logging (optional, default: false)
|
|
495
|
+
*
|
|
496
|
+
* @param verbose - Whether to enable verbose logging
|
|
497
|
+
*/
|
|
498
|
+
withVerbose(verbose) {
|
|
499
|
+
this.options.verbose = verbose;
|
|
500
|
+
return this;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Set custom node names (optional)
|
|
504
|
+
*
|
|
505
|
+
* @param nodeNames - Custom names for nodes (for debugging/observability)
|
|
506
|
+
*/
|
|
507
|
+
withNodeNames(nodeNames) {
|
|
508
|
+
this.options.nodeNames = nodeNames;
|
|
509
|
+
return this;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Build the ReAct agent
|
|
513
|
+
*
|
|
514
|
+
* @returns A compiled LangGraph StateGraph
|
|
515
|
+
* @throws Error if required configuration is missing
|
|
516
|
+
*/
|
|
517
|
+
build() {
|
|
518
|
+
if (!this.config.llm) {
|
|
519
|
+
throw new Error("ReActAgentBuilder: llm is required. Use withLLM() to set it.");
|
|
520
|
+
}
|
|
521
|
+
if (!this.config.tools) {
|
|
522
|
+
throw new Error("ReActAgentBuilder: tools are required. Use withTools() to set them.");
|
|
523
|
+
}
|
|
524
|
+
const finalConfig = {
|
|
525
|
+
llm: this.config.llm,
|
|
526
|
+
tools: this.config.tools,
|
|
527
|
+
systemPrompt: this.config.systemPrompt || DEFAULT_REACT_SYSTEM_PROMPT,
|
|
528
|
+
maxIterations: this.config.maxIterations ?? 10,
|
|
529
|
+
returnIntermediateSteps: this.config.returnIntermediateSteps ?? false,
|
|
530
|
+
stopCondition: this.config.stopCondition
|
|
531
|
+
};
|
|
532
|
+
return createReActAgent(finalConfig, this.options);
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
function createReActAgentBuilder() {
|
|
536
|
+
return new ReActAgentBuilder();
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// src/plan-execute/agent.ts
|
|
540
|
+
var import_langgraph2 = require("@langchain/langgraph");
|
|
541
|
+
|
|
542
|
+
// src/plan-execute/state.ts
|
|
543
|
+
var import_zod4 = require("zod");
|
|
544
|
+
var import_core4 = require("@agentforge/core");
|
|
545
|
+
|
|
546
|
+
// src/plan-execute/schemas.ts
|
|
547
|
+
var import_zod3 = require("zod");
|
|
548
|
+
var PlanStepSchema = import_zod3.z.object({
|
|
549
|
+
/**
|
|
550
|
+
* Unique identifier for the step
|
|
551
|
+
*/
|
|
552
|
+
id: import_zod3.z.string().describe("Unique identifier for the step"),
|
|
553
|
+
/**
|
|
554
|
+
* Description of what this step should accomplish
|
|
555
|
+
*/
|
|
556
|
+
description: import_zod3.z.string().describe("Description of what this step should accomplish"),
|
|
557
|
+
/**
|
|
558
|
+
* Optional dependencies on other steps (by ID)
|
|
559
|
+
*/
|
|
560
|
+
dependencies: import_zod3.z.array(import_zod3.z.string()).optional().describe("IDs of steps that must complete before this one"),
|
|
561
|
+
/**
|
|
562
|
+
* Optional tool to use for this step
|
|
563
|
+
*/
|
|
564
|
+
tool: import_zod3.z.string().optional().describe("Name of the tool to use for this step"),
|
|
565
|
+
/**
|
|
566
|
+
* Optional arguments for the tool
|
|
567
|
+
*/
|
|
568
|
+
args: import_zod3.z.record(import_zod3.z.any()).optional().describe("Arguments to pass to the tool")
|
|
569
|
+
});
|
|
570
|
+
var CompletedStepSchema = import_zod3.z.object({
|
|
571
|
+
/**
|
|
572
|
+
* The step that was executed
|
|
573
|
+
*/
|
|
574
|
+
step: PlanStepSchema.describe("The step that was executed"),
|
|
575
|
+
/**
|
|
576
|
+
* The result of executing the step
|
|
577
|
+
*/
|
|
578
|
+
result: import_zod3.z.any().describe("The result of executing the step"),
|
|
579
|
+
/**
|
|
580
|
+
* Whether the step succeeded
|
|
581
|
+
*/
|
|
582
|
+
success: import_zod3.z.boolean().describe("Whether the step succeeded"),
|
|
583
|
+
/**
|
|
584
|
+
* Optional error message if the step failed
|
|
585
|
+
*/
|
|
586
|
+
error: import_zod3.z.string().optional().describe("Error message if the step failed"),
|
|
587
|
+
/**
|
|
588
|
+
* Timestamp when the step was completed
|
|
589
|
+
*/
|
|
590
|
+
timestamp: import_zod3.z.string().datetime().describe("ISO timestamp when the step was completed")
|
|
591
|
+
});
|
|
592
|
+
var PlanSchema = import_zod3.z.object({
|
|
593
|
+
/**
|
|
594
|
+
* List of steps in the plan
|
|
595
|
+
*/
|
|
596
|
+
steps: import_zod3.z.array(PlanStepSchema).describe("List of steps in the plan"),
|
|
597
|
+
/**
|
|
598
|
+
* Overall goal of the plan
|
|
599
|
+
*/
|
|
600
|
+
goal: import_zod3.z.string().describe("Overall goal of the plan"),
|
|
601
|
+
/**
|
|
602
|
+
* Timestamp when the plan was created
|
|
603
|
+
*/
|
|
604
|
+
createdAt: import_zod3.z.string().datetime().describe("ISO timestamp when the plan was created"),
|
|
605
|
+
/**
|
|
606
|
+
* Optional confidence score (0-1)
|
|
607
|
+
*/
|
|
608
|
+
confidence: import_zod3.z.number().min(0).max(1).optional().describe("Confidence score for the plan (0-1)")
|
|
609
|
+
});
|
|
610
|
+
var ReplanDecisionSchema = import_zod3.z.object({
|
|
611
|
+
/**
|
|
612
|
+
* Whether to replan
|
|
613
|
+
*/
|
|
614
|
+
shouldReplan: import_zod3.z.boolean().describe("Whether to replan based on current results"),
|
|
615
|
+
/**
|
|
616
|
+
* Reason for the decision
|
|
617
|
+
*/
|
|
618
|
+
reason: import_zod3.z.string().describe("Reason for the replan decision"),
|
|
619
|
+
/**
|
|
620
|
+
* Optional new goal if replanning
|
|
621
|
+
*/
|
|
622
|
+
newGoal: import_zod3.z.string().optional().describe("Updated goal if replanning")
|
|
623
|
+
});
|
|
624
|
+
var ExecutionStatusSchema = import_zod3.z.enum([
|
|
625
|
+
"planning",
|
|
626
|
+
"executing",
|
|
627
|
+
"replanning",
|
|
628
|
+
"completed",
|
|
629
|
+
"failed"
|
|
630
|
+
]).describe("Current status of the plan execution");
|
|
631
|
+
|
|
632
|
+
// src/plan-execute/state.ts
|
|
633
|
+
var PlanExecuteStateConfig = {
|
|
634
|
+
/**
|
|
635
|
+
* Original user input/query
|
|
636
|
+
*/
|
|
637
|
+
input: {
|
|
638
|
+
schema: import_zod4.z.string(),
|
|
639
|
+
default: () => "",
|
|
640
|
+
description: "Original user input or query"
|
|
641
|
+
},
|
|
642
|
+
/**
|
|
643
|
+
* The current plan
|
|
644
|
+
*/
|
|
645
|
+
plan: {
|
|
646
|
+
schema: PlanSchema.optional(),
|
|
647
|
+
description: "The current execution plan"
|
|
648
|
+
},
|
|
649
|
+
/**
|
|
650
|
+
* Completed steps with their results
|
|
651
|
+
* Accumulates all completed steps
|
|
652
|
+
*/
|
|
653
|
+
pastSteps: {
|
|
654
|
+
schema: import_zod4.z.array(CompletedStepSchema),
|
|
655
|
+
reducer: (left, right) => [...left, ...right],
|
|
656
|
+
default: () => [],
|
|
657
|
+
description: "Completed steps with their results"
|
|
658
|
+
},
|
|
659
|
+
/**
|
|
660
|
+
* Index of the current step being executed
|
|
661
|
+
*/
|
|
662
|
+
currentStepIndex: {
|
|
663
|
+
schema: import_zod4.z.number().int().nonnegative().optional(),
|
|
664
|
+
description: "Index of the current step being executed"
|
|
665
|
+
},
|
|
666
|
+
/**
|
|
667
|
+
* Current execution status
|
|
668
|
+
*/
|
|
669
|
+
status: {
|
|
670
|
+
schema: ExecutionStatusSchema,
|
|
671
|
+
default: () => "planning",
|
|
672
|
+
description: "Current execution status"
|
|
673
|
+
},
|
|
674
|
+
/**
|
|
675
|
+
* Final response
|
|
676
|
+
*/
|
|
677
|
+
response: {
|
|
678
|
+
schema: import_zod4.z.string().optional(),
|
|
679
|
+
description: "Final response after plan execution"
|
|
680
|
+
},
|
|
681
|
+
/**
|
|
682
|
+
* Error message if execution failed
|
|
683
|
+
*/
|
|
684
|
+
error: {
|
|
685
|
+
schema: import_zod4.z.string().optional(),
|
|
686
|
+
description: "Error message if execution failed"
|
|
687
|
+
},
|
|
688
|
+
/**
|
|
689
|
+
* Iteration counter for replanning
|
|
690
|
+
*/
|
|
691
|
+
iteration: {
|
|
692
|
+
schema: import_zod4.z.number().int().nonnegative(),
|
|
693
|
+
reducer: (left, right) => left + right,
|
|
694
|
+
default: () => 0,
|
|
695
|
+
description: "Number of planning iterations"
|
|
696
|
+
},
|
|
697
|
+
/**
|
|
698
|
+
* Maximum iterations allowed
|
|
699
|
+
*/
|
|
700
|
+
maxIterations: {
|
|
701
|
+
schema: import_zod4.z.number().int().positive(),
|
|
702
|
+
default: () => 5,
|
|
703
|
+
description: "Maximum number of planning iterations allowed"
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
var PlanExecuteState = (0, import_core4.createStateAnnotation)(PlanExecuteStateConfig);
|
|
707
|
+
|
|
708
|
+
// src/plan-execute/nodes.ts
|
|
709
|
+
var import_messages2 = require("@langchain/core/messages");
|
|
710
|
+
|
|
711
|
+
// src/plan-execute/prompts.ts
|
|
712
|
+
var DEFAULT_PLANNER_SYSTEM_PROMPT = `You are an expert planning assistant. Your job is to create a detailed, step-by-step plan to accomplish the user's goal.
|
|
713
|
+
|
|
714
|
+
Guidelines for creating plans:
|
|
715
|
+
1. Break down complex tasks into clear, actionable steps
|
|
716
|
+
2. Each step should have a specific, measurable outcome
|
|
717
|
+
3. Identify dependencies between steps
|
|
718
|
+
4. Consider which tools are needed for each step
|
|
719
|
+
5. Keep the plan focused and efficient
|
|
720
|
+
6. Aim for 3-7 steps for most tasks
|
|
721
|
+
|
|
722
|
+
Output your plan as a JSON object with the following structure:
|
|
723
|
+
{
|
|
724
|
+
"goal": "The overall goal",
|
|
725
|
+
"steps": [
|
|
726
|
+
{
|
|
727
|
+
"id": "step-1",
|
|
728
|
+
"description": "What this step accomplishes",
|
|
729
|
+
"tool": "tool_name (optional)",
|
|
730
|
+
"args": {"key": "value (optional)"},
|
|
731
|
+
"dependencies": ["step-id (optional)"]
|
|
732
|
+
}
|
|
733
|
+
],
|
|
734
|
+
"confidence": 0.9
|
|
735
|
+
}`;
|
|
736
|
+
var DEFAULT_REPLANNER_SYSTEM_PROMPT = `You are an expert replanning assistant. Your job is to decide whether the current plan needs to be adjusted based on the results so far.
|
|
737
|
+
|
|
738
|
+
Consider:
|
|
739
|
+
1. Have the completed steps achieved their intended outcomes?
|
|
740
|
+
2. Are there any unexpected results that require plan changes?
|
|
741
|
+
3. Is the original goal still achievable with the current plan?
|
|
742
|
+
4. Would a different approach be more effective?
|
|
743
|
+
|
|
744
|
+
Output your decision as a JSON object:
|
|
745
|
+
{
|
|
746
|
+
"shouldReplan": true/false,
|
|
747
|
+
"reason": "Explanation for the decision",
|
|
748
|
+
"newGoal": "Updated goal if replanning (optional)"
|
|
749
|
+
}`;
|
|
750
|
+
var PLANNING_PROMPT_TEMPLATE = `User Goal: {input}
|
|
751
|
+
|
|
752
|
+
{toolDescriptions}
|
|
753
|
+
|
|
754
|
+
Create a step-by-step plan to accomplish this goal.`;
|
|
755
|
+
var REPLANNING_PROMPT_TEMPLATE = `Original Goal: {goal}
|
|
756
|
+
|
|
757
|
+
Completed Steps:
|
|
758
|
+
{completedSteps}
|
|
759
|
+
|
|
760
|
+
Current Plan:
|
|
761
|
+
{remainingSteps}
|
|
762
|
+
|
|
763
|
+
Based on the results so far, should we continue with the current plan or replan?`;
|
|
764
|
+
var COMPLETED_STEP_TEMPLATE = `Step {stepNumber}: {description}
|
|
765
|
+
Result: {result}
|
|
766
|
+
Status: {status}`;
|
|
767
|
+
var REMAINING_STEP_TEMPLATE = `Step {stepNumber}: {description}
|
|
768
|
+
{dependencies}`;
|
|
769
|
+
|
|
770
|
+
// src/plan-execute/nodes.ts
|
|
771
|
+
function createPlannerNode(config) {
|
|
772
|
+
const {
|
|
773
|
+
llm,
|
|
774
|
+
systemPrompt = DEFAULT_PLANNER_SYSTEM_PROMPT,
|
|
775
|
+
maxSteps = 7,
|
|
776
|
+
includeToolDescriptions = false
|
|
777
|
+
} = config;
|
|
778
|
+
return async (state) => {
|
|
779
|
+
try {
|
|
780
|
+
let toolDescriptions = "";
|
|
781
|
+
if (includeToolDescriptions) {
|
|
782
|
+
toolDescriptions = "";
|
|
783
|
+
}
|
|
784
|
+
const userPrompt = PLANNING_PROMPT_TEMPLATE.replace("{input}", state.input || "").replace("{toolDescriptions}", toolDescriptions);
|
|
785
|
+
const messages = [
|
|
786
|
+
new import_messages2.SystemMessage(systemPrompt),
|
|
787
|
+
new import_messages2.HumanMessage(userPrompt)
|
|
788
|
+
];
|
|
789
|
+
const response = await llm.invoke(messages);
|
|
790
|
+
const content = response.content.toString();
|
|
791
|
+
let plan;
|
|
792
|
+
try {
|
|
793
|
+
const parsed = JSON.parse(content);
|
|
794
|
+
plan = {
|
|
795
|
+
steps: parsed.steps.slice(0, maxSteps),
|
|
796
|
+
// Limit to maxSteps
|
|
797
|
+
goal: parsed.goal || state.input || "",
|
|
798
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
799
|
+
confidence: parsed.confidence
|
|
800
|
+
};
|
|
801
|
+
} catch (parseError) {
|
|
802
|
+
throw new Error(`Failed to parse plan from LLM response: ${parseError}`);
|
|
803
|
+
}
|
|
804
|
+
return {
|
|
805
|
+
plan,
|
|
806
|
+
status: "executing",
|
|
807
|
+
currentStepIndex: 0,
|
|
808
|
+
iteration: 1
|
|
809
|
+
};
|
|
810
|
+
} catch (error) {
|
|
811
|
+
return {
|
|
812
|
+
status: "failed",
|
|
813
|
+
error: error instanceof Error ? error.message : "Unknown error in planner"
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
function createExecutorNode(config) {
|
|
819
|
+
const {
|
|
820
|
+
tools,
|
|
821
|
+
llm,
|
|
822
|
+
parallel = false,
|
|
823
|
+
stepTimeout = 3e4
|
|
824
|
+
} = config;
|
|
825
|
+
return async (state) => {
|
|
826
|
+
try {
|
|
827
|
+
const { plan, currentStepIndex = 0, pastSteps = [] } = state;
|
|
828
|
+
if (!plan || !plan.steps || plan.steps.length === 0) {
|
|
829
|
+
return {
|
|
830
|
+
status: "completed"
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
if (currentStepIndex >= plan.steps.length) {
|
|
834
|
+
return {
|
|
835
|
+
status: "completed"
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
const currentStep = plan.steps[currentStepIndex];
|
|
839
|
+
if (currentStep.dependencies && currentStep.dependencies.length > 0) {
|
|
840
|
+
const completedStepIds = new Set(pastSteps.map((ps) => ps.step.id));
|
|
841
|
+
const unmetDependencies = currentStep.dependencies.filter((dep) => !completedStepIds.has(dep));
|
|
842
|
+
if (unmetDependencies.length > 0) {
|
|
843
|
+
throw new Error(`Unmet dependencies for step ${currentStep.id}: ${unmetDependencies.join(", ")}`);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
let result;
|
|
847
|
+
let success = true;
|
|
848
|
+
let error;
|
|
849
|
+
try {
|
|
850
|
+
if (currentStep.tool) {
|
|
851
|
+
const tool = tools.find((t) => t.metadata.name === currentStep.tool);
|
|
852
|
+
if (!tool) {
|
|
853
|
+
throw new Error(`Tool not found: ${currentStep.tool}`);
|
|
854
|
+
}
|
|
855
|
+
const timeoutPromise = new Promise(
|
|
856
|
+
(_, reject) => setTimeout(() => reject(new Error("Step execution timeout")), stepTimeout)
|
|
857
|
+
);
|
|
858
|
+
result = await Promise.race([
|
|
859
|
+
tool.execute(currentStep.args || {}),
|
|
860
|
+
timeoutPromise
|
|
861
|
+
]);
|
|
862
|
+
} else {
|
|
863
|
+
result = { message: "Step completed without tool execution" };
|
|
864
|
+
}
|
|
865
|
+
} catch (execError) {
|
|
866
|
+
success = false;
|
|
867
|
+
error = execError instanceof Error ? execError.message : "Unknown execution error";
|
|
868
|
+
result = null;
|
|
869
|
+
}
|
|
870
|
+
const completedStep = {
|
|
871
|
+
step: currentStep,
|
|
872
|
+
result,
|
|
873
|
+
success,
|
|
874
|
+
error,
|
|
875
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
876
|
+
};
|
|
877
|
+
return {
|
|
878
|
+
pastSteps: [completedStep],
|
|
879
|
+
currentStepIndex: currentStepIndex + 1
|
|
880
|
+
};
|
|
881
|
+
} catch (error) {
|
|
882
|
+
return {
|
|
883
|
+
status: "failed",
|
|
884
|
+
error: error instanceof Error ? error.message : "Unknown error in executor"
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
function createReplannerNode(config) {
|
|
890
|
+
const {
|
|
891
|
+
llm,
|
|
892
|
+
replanThreshold = 0.7,
|
|
893
|
+
systemPrompt = DEFAULT_REPLANNER_SYSTEM_PROMPT
|
|
894
|
+
} = config;
|
|
895
|
+
return async (state) => {
|
|
896
|
+
try {
|
|
897
|
+
const { plan, pastSteps = [], currentStepIndex = 0 } = state;
|
|
898
|
+
if (!plan) {
|
|
899
|
+
return { status: "failed", error: "No plan available for replanning" };
|
|
900
|
+
}
|
|
901
|
+
const completedStepsText = pastSteps.map(
|
|
902
|
+
(ps, idx) => COMPLETED_STEP_TEMPLATE.replace("{stepNumber}", String(idx + 1)).replace("{description}", ps.step.description).replace("{result}", JSON.stringify(ps.result)).replace("{status}", ps.success ? "Success" : `Failed: ${ps.error}`)
|
|
903
|
+
).join("\n\n");
|
|
904
|
+
const remainingSteps = plan.steps.slice(currentStepIndex);
|
|
905
|
+
const remainingStepsText = remainingSteps.map(
|
|
906
|
+
(step, idx) => REMAINING_STEP_TEMPLATE.replace("{stepNumber}", String(currentStepIndex + idx + 1)).replace("{description}", step.description).replace("{dependencies}", step.dependencies ? `Dependencies: ${step.dependencies.join(", ")}` : "")
|
|
907
|
+
).join("\n\n");
|
|
908
|
+
const userPrompt = REPLANNING_PROMPT_TEMPLATE.replace("{goal}", plan.goal).replace("{completedSteps}", completedStepsText || "None").replace("{remainingSteps}", remainingStepsText || "None");
|
|
909
|
+
const messages = [
|
|
910
|
+
new import_messages2.SystemMessage(systemPrompt),
|
|
911
|
+
new import_messages2.HumanMessage(userPrompt)
|
|
912
|
+
];
|
|
913
|
+
const response = await llm.invoke(messages);
|
|
914
|
+
const content = response.content.toString();
|
|
915
|
+
let decision;
|
|
916
|
+
try {
|
|
917
|
+
decision = JSON.parse(content);
|
|
918
|
+
} catch (parseError) {
|
|
919
|
+
throw new Error(`Failed to parse replan decision from LLM response: ${parseError}`);
|
|
920
|
+
}
|
|
921
|
+
if (decision.shouldReplan) {
|
|
922
|
+
return {
|
|
923
|
+
status: "planning",
|
|
924
|
+
input: decision.newGoal || plan.goal,
|
|
925
|
+
iteration: 1
|
|
926
|
+
};
|
|
927
|
+
} else {
|
|
928
|
+
return {
|
|
929
|
+
status: "executing"
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
} catch (error) {
|
|
933
|
+
return {
|
|
934
|
+
status: "failed",
|
|
935
|
+
error: error instanceof Error ? error.message : "Unknown error in replanner"
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
function createFinisherNode() {
|
|
941
|
+
return async (state) => {
|
|
942
|
+
const results = state.pastSteps?.map((ps) => ({
|
|
943
|
+
step: ps.step.description,
|
|
944
|
+
result: ps.result,
|
|
945
|
+
success: ps.success
|
|
946
|
+
})) || [];
|
|
947
|
+
const response = JSON.stringify({
|
|
948
|
+
goal: state.plan?.goal || state.input,
|
|
949
|
+
results,
|
|
950
|
+
totalSteps: state.pastSteps?.length || 0,
|
|
951
|
+
successfulSteps: state.pastSteps?.filter((ps) => ps.success).length || 0
|
|
952
|
+
}, null, 2);
|
|
953
|
+
return {
|
|
954
|
+
status: "completed",
|
|
955
|
+
response
|
|
956
|
+
};
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
// src/plan-execute/agent.ts
|
|
961
|
+
function createPlanExecuteAgent(config) {
|
|
962
|
+
const {
|
|
963
|
+
planner,
|
|
964
|
+
executor,
|
|
965
|
+
replanner,
|
|
966
|
+
maxIterations = 5,
|
|
967
|
+
verbose = false
|
|
968
|
+
} = config;
|
|
969
|
+
const plannerNode = createPlannerNode(planner);
|
|
970
|
+
const executorNode = createExecutorNode(executor);
|
|
971
|
+
const finisherNode = createFinisherNode();
|
|
972
|
+
const replannerNode = replanner ? createReplannerNode(replanner) : async (state) => ({ status: "executing" });
|
|
973
|
+
const workflow = new import_langgraph2.StateGraph(PlanExecuteState).addNode("planner", plannerNode).addNode("executor", executorNode).addNode("finisher", finisherNode).addNode("replanner", replannerNode);
|
|
974
|
+
const routeAfterExecutor = (state) => {
|
|
975
|
+
if (state.status === "failed") {
|
|
976
|
+
return "error";
|
|
977
|
+
}
|
|
978
|
+
if (state.status === "completed") {
|
|
979
|
+
return "finish";
|
|
980
|
+
}
|
|
981
|
+
const allStepsCompleted = state.currentStepIndex !== void 0 && state.plan?.steps && state.currentStepIndex >= state.plan.steps.length;
|
|
982
|
+
if (allStepsCompleted) {
|
|
983
|
+
return "finish";
|
|
984
|
+
}
|
|
985
|
+
if (replanner && state.iteration < maxIterations) {
|
|
986
|
+
const recentSteps = state.pastSteps?.slice(-3) || [];
|
|
987
|
+
const hasFailures = recentSteps.some((step) => !step.success);
|
|
988
|
+
if (hasFailures) {
|
|
989
|
+
return "replan";
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
return "execute";
|
|
993
|
+
};
|
|
994
|
+
const routeAfterReplanner = (state) => {
|
|
995
|
+
if (state.status === "failed") {
|
|
996
|
+
return "error";
|
|
997
|
+
}
|
|
998
|
+
if (state.status === "planning") {
|
|
999
|
+
return "replan";
|
|
1000
|
+
}
|
|
1001
|
+
return "execute";
|
|
1002
|
+
};
|
|
1003
|
+
workflow.addEdge("__start__", "planner");
|
|
1004
|
+
workflow.addEdge("planner", "executor");
|
|
1005
|
+
workflow.addConditionalEdges(
|
|
1006
|
+
"executor",
|
|
1007
|
+
routeAfterExecutor,
|
|
1008
|
+
{
|
|
1009
|
+
execute: "executor",
|
|
1010
|
+
// Loop back to execute next step
|
|
1011
|
+
replan: "replanner",
|
|
1012
|
+
finish: "finisher",
|
|
1013
|
+
error: import_langgraph2.END
|
|
1014
|
+
}
|
|
1015
|
+
);
|
|
1016
|
+
workflow.addEdge("finisher", import_langgraph2.END);
|
|
1017
|
+
workflow.addConditionalEdges(
|
|
1018
|
+
"replanner",
|
|
1019
|
+
routeAfterReplanner,
|
|
1020
|
+
{
|
|
1021
|
+
replan: "planner",
|
|
1022
|
+
execute: "executor",
|
|
1023
|
+
error: import_langgraph2.END,
|
|
1024
|
+
finish: import_langgraph2.END
|
|
1025
|
+
}
|
|
1026
|
+
);
|
|
1027
|
+
return workflow.compile();
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
// src/reflection/state.ts
|
|
1031
|
+
var import_zod6 = require("zod");
|
|
1032
|
+
var import_core5 = require("@agentforge/core");
|
|
1033
|
+
|
|
1034
|
+
// src/reflection/schemas.ts
|
|
1035
|
+
var import_zod5 = require("zod");
|
|
1036
|
+
var ReflectionSchema = import_zod5.z.object({
|
|
1037
|
+
/**
|
|
1038
|
+
* The critique or feedback on the current response
|
|
1039
|
+
*/
|
|
1040
|
+
critique: import_zod5.z.string().describe("Critique or feedback on the current response"),
|
|
1041
|
+
/**
|
|
1042
|
+
* Specific issues identified
|
|
1043
|
+
*/
|
|
1044
|
+
issues: import_zod5.z.array(import_zod5.z.string()).describe("Specific issues or problems identified"),
|
|
1045
|
+
/**
|
|
1046
|
+
* Suggestions for improvement
|
|
1047
|
+
*/
|
|
1048
|
+
suggestions: import_zod5.z.array(import_zod5.z.string()).describe("Suggestions for improving the response"),
|
|
1049
|
+
/**
|
|
1050
|
+
* Quality score (0-10)
|
|
1051
|
+
*/
|
|
1052
|
+
score: import_zod5.z.number().min(0).max(10).optional().describe("Quality score from 0 to 10"),
|
|
1053
|
+
/**
|
|
1054
|
+
* Whether the response meets quality standards
|
|
1055
|
+
*/
|
|
1056
|
+
meetsStandards: import_zod5.z.boolean().describe("Whether the response meets quality standards"),
|
|
1057
|
+
/**
|
|
1058
|
+
* Timestamp of the reflection
|
|
1059
|
+
*/
|
|
1060
|
+
timestamp: import_zod5.z.date().optional().describe("When this reflection was created")
|
|
1061
|
+
});
|
|
1062
|
+
var RevisionSchema = import_zod5.z.object({
|
|
1063
|
+
/**
|
|
1064
|
+
* The revised content
|
|
1065
|
+
*/
|
|
1066
|
+
content: import_zod5.z.string().describe("The revised content"),
|
|
1067
|
+
/**
|
|
1068
|
+
* Which iteration this revision is from
|
|
1069
|
+
*/
|
|
1070
|
+
iteration: import_zod5.z.number().int().nonnegative().describe("Iteration number"),
|
|
1071
|
+
/**
|
|
1072
|
+
* The reflection that prompted this revision
|
|
1073
|
+
*/
|
|
1074
|
+
basedOn: ReflectionSchema.optional().describe("The reflection that prompted this revision"),
|
|
1075
|
+
/**
|
|
1076
|
+
* Timestamp of the revision
|
|
1077
|
+
*/
|
|
1078
|
+
timestamp: import_zod5.z.date().optional().describe("When this revision was created")
|
|
1079
|
+
});
|
|
1080
|
+
var ReflectionStatusSchema = import_zod5.z.enum([
|
|
1081
|
+
"generating",
|
|
1082
|
+
// Initial generation
|
|
1083
|
+
"reflecting",
|
|
1084
|
+
// Critiquing current response
|
|
1085
|
+
"revising",
|
|
1086
|
+
// Improving based on critique
|
|
1087
|
+
"completed",
|
|
1088
|
+
// Quality threshold met
|
|
1089
|
+
"failed"
|
|
1090
|
+
// Max iterations reached without meeting standards
|
|
1091
|
+
]);
|
|
1092
|
+
var QualityCriteriaSchema = import_zod5.z.object({
|
|
1093
|
+
/**
|
|
1094
|
+
* Minimum quality score required (0-10)
|
|
1095
|
+
*/
|
|
1096
|
+
minScore: import_zod5.z.number().min(0).max(10).default(7).describe("Minimum quality score required"),
|
|
1097
|
+
/**
|
|
1098
|
+
* Specific criteria to evaluate
|
|
1099
|
+
*/
|
|
1100
|
+
criteria: import_zod5.z.array(import_zod5.z.string()).optional().describe("Specific criteria to evaluate"),
|
|
1101
|
+
/**
|
|
1102
|
+
* Whether all criteria must be met
|
|
1103
|
+
*/
|
|
1104
|
+
requireAll: import_zod5.z.boolean().default(true).describe("Whether all criteria must be met")
|
|
1105
|
+
});
|
|
1106
|
+
var ReflectionConfigSchema = import_zod5.z.object({
|
|
1107
|
+
/**
|
|
1108
|
+
* Maximum number of reflection iterations
|
|
1109
|
+
*/
|
|
1110
|
+
maxIterations: import_zod5.z.number().int().positive().default(3).describe("Maximum reflection iterations"),
|
|
1111
|
+
/**
|
|
1112
|
+
* Quality criteria for completion
|
|
1113
|
+
*/
|
|
1114
|
+
qualityCriteria: QualityCriteriaSchema.optional().describe("Quality criteria for completion"),
|
|
1115
|
+
/**
|
|
1116
|
+
* Whether to include previous reflections in context
|
|
1117
|
+
*/
|
|
1118
|
+
includeHistory: import_zod5.z.boolean().default(true).describe("Include previous reflections in context")
|
|
1119
|
+
});
|
|
1120
|
+
|
|
1121
|
+
// src/reflection/state.ts
|
|
1122
|
+
var ReflectionStateConfig = {
|
|
1123
|
+
/**
|
|
1124
|
+
* Original user input/task
|
|
1125
|
+
*/
|
|
1126
|
+
input: {
|
|
1127
|
+
schema: import_zod6.z.string(),
|
|
1128
|
+
default: () => "",
|
|
1129
|
+
description: "Original user input or task"
|
|
1130
|
+
},
|
|
1131
|
+
/**
|
|
1132
|
+
* Current response/output
|
|
1133
|
+
*/
|
|
1134
|
+
currentResponse: {
|
|
1135
|
+
schema: import_zod6.z.string().optional(),
|
|
1136
|
+
description: "Current response or output"
|
|
1137
|
+
},
|
|
1138
|
+
/**
|
|
1139
|
+
* History of all reflections/critiques
|
|
1140
|
+
* Accumulates all reflections
|
|
1141
|
+
*/
|
|
1142
|
+
reflections: {
|
|
1143
|
+
schema: import_zod6.z.array(ReflectionSchema),
|
|
1144
|
+
reducer: (left, right) => [...left, ...right],
|
|
1145
|
+
default: () => [],
|
|
1146
|
+
description: "History of all reflections and critiques"
|
|
1147
|
+
},
|
|
1148
|
+
/**
|
|
1149
|
+
* History of all revisions
|
|
1150
|
+
* Accumulates all revisions
|
|
1151
|
+
*/
|
|
1152
|
+
revisions: {
|
|
1153
|
+
schema: import_zod6.z.array(RevisionSchema),
|
|
1154
|
+
reducer: (left, right) => [...left, ...right],
|
|
1155
|
+
default: () => [],
|
|
1156
|
+
description: "History of all revisions"
|
|
1157
|
+
},
|
|
1158
|
+
/**
|
|
1159
|
+
* Current iteration number
|
|
1160
|
+
*/
|
|
1161
|
+
iteration: {
|
|
1162
|
+
schema: import_zod6.z.number().int().nonnegative(),
|
|
1163
|
+
reducer: (left, right) => left + right,
|
|
1164
|
+
default: () => 0,
|
|
1165
|
+
description: "Current iteration number"
|
|
1166
|
+
},
|
|
1167
|
+
/**
|
|
1168
|
+
* Current status
|
|
1169
|
+
*/
|
|
1170
|
+
status: {
|
|
1171
|
+
schema: ReflectionStatusSchema,
|
|
1172
|
+
default: () => "generating",
|
|
1173
|
+
description: "Current reflection status"
|
|
1174
|
+
},
|
|
1175
|
+
/**
|
|
1176
|
+
* Quality criteria for completion
|
|
1177
|
+
*/
|
|
1178
|
+
qualityCriteria: {
|
|
1179
|
+
schema: QualityCriteriaSchema.optional(),
|
|
1180
|
+
description: "Quality criteria for determining completion"
|
|
1181
|
+
},
|
|
1182
|
+
/**
|
|
1183
|
+
* Maximum iterations allowed
|
|
1184
|
+
*/
|
|
1185
|
+
maxIterations: {
|
|
1186
|
+
schema: import_zod6.z.number().int().positive(),
|
|
1187
|
+
default: () => 3,
|
|
1188
|
+
description: "Maximum number of reflection iterations allowed"
|
|
1189
|
+
},
|
|
1190
|
+
/**
|
|
1191
|
+
* Final response (when completed)
|
|
1192
|
+
*/
|
|
1193
|
+
response: {
|
|
1194
|
+
schema: import_zod6.z.string().optional(),
|
|
1195
|
+
description: "Final response after reflection process"
|
|
1196
|
+
},
|
|
1197
|
+
/**
|
|
1198
|
+
* Error message if failed
|
|
1199
|
+
*/
|
|
1200
|
+
error: {
|
|
1201
|
+
schema: import_zod6.z.string().optional(),
|
|
1202
|
+
description: "Error message if reflection failed"
|
|
1203
|
+
}
|
|
1204
|
+
};
|
|
1205
|
+
var ReflectionState = (0, import_core5.createStateAnnotation)(ReflectionStateConfig);
|
|
1206
|
+
|
|
1207
|
+
// src/reflection/prompts.ts
|
|
1208
|
+
var DEFAULT_GENERATOR_SYSTEM_PROMPT = `You are an expert content generator. Your task is to create high-quality responses to user requests.
|
|
1209
|
+
|
|
1210
|
+
Focus on:
|
|
1211
|
+
- Clarity and coherence
|
|
1212
|
+
- Accuracy and correctness
|
|
1213
|
+
- Completeness and thoroughness
|
|
1214
|
+
- Appropriate tone and style
|
|
1215
|
+
|
|
1216
|
+
Generate the best possible response to the user's request.`;
|
|
1217
|
+
var DEFAULT_REFLECTOR_SYSTEM_PROMPT = `You are an expert critic and reviewer. Your task is to provide constructive feedback on responses.
|
|
1218
|
+
|
|
1219
|
+
Evaluate the response based on:
|
|
1220
|
+
- Clarity: Is it easy to understand?
|
|
1221
|
+
- Accuracy: Is the information correct?
|
|
1222
|
+
- Completeness: Does it fully address the request?
|
|
1223
|
+
- Quality: Is it well-written and professional?
|
|
1224
|
+
|
|
1225
|
+
Provide specific, actionable feedback for improvement.`;
|
|
1226
|
+
var DEFAULT_REVISER_SYSTEM_PROMPT = `You are an expert editor and reviser. Your task is to improve responses based on feedback.
|
|
1227
|
+
|
|
1228
|
+
Focus on:
|
|
1229
|
+
- Addressing all identified issues
|
|
1230
|
+
- Implementing suggested improvements
|
|
1231
|
+
- Maintaining the core message
|
|
1232
|
+
- Enhancing overall quality
|
|
1233
|
+
|
|
1234
|
+
Create an improved version that addresses the critique.`;
|
|
1235
|
+
var GENERATION_PROMPT_TEMPLATE = `Please generate a response to the following request:
|
|
1236
|
+
|
|
1237
|
+
{input}
|
|
1238
|
+
|
|
1239
|
+
{context}
|
|
1240
|
+
|
|
1241
|
+
Provide a high-quality, complete response.`;
|
|
1242
|
+
var REFLECTION_PROMPT_TEMPLATE = `Please review and critique the following response:
|
|
1243
|
+
|
|
1244
|
+
Original Request:
|
|
1245
|
+
{input}
|
|
1246
|
+
|
|
1247
|
+
Current Response:
|
|
1248
|
+
{currentResponse}
|
|
1249
|
+
|
|
1250
|
+
{criteria}
|
|
1251
|
+
|
|
1252
|
+
{history}
|
|
1253
|
+
|
|
1254
|
+
Provide a detailed critique including:
|
|
1255
|
+
1. What works well
|
|
1256
|
+
2. Specific issues or problems
|
|
1257
|
+
3. Suggestions for improvement
|
|
1258
|
+
4. A quality score (0-10)
|
|
1259
|
+
5. Whether it meets quality standards
|
|
1260
|
+
|
|
1261
|
+
Format your response as JSON with the following structure:
|
|
1262
|
+
{
|
|
1263
|
+
"critique": "overall assessment",
|
|
1264
|
+
"issues": ["issue 1", "issue 2"],
|
|
1265
|
+
"suggestions": ["suggestion 1", "suggestion 2"],
|
|
1266
|
+
"score": 8,
|
|
1267
|
+
"meetsStandards": true
|
|
1268
|
+
}`;
|
|
1269
|
+
var REVISION_PROMPT_TEMPLATE = `Please revise the following response based on the critique:
|
|
1270
|
+
|
|
1271
|
+
Original Request:
|
|
1272
|
+
{input}
|
|
1273
|
+
|
|
1274
|
+
Current Response:
|
|
1275
|
+
{currentResponse}
|
|
1276
|
+
|
|
1277
|
+
Critique:
|
|
1278
|
+
{critique}
|
|
1279
|
+
|
|
1280
|
+
Issues to Address:
|
|
1281
|
+
{issues}
|
|
1282
|
+
|
|
1283
|
+
Suggestions:
|
|
1284
|
+
{suggestions}
|
|
1285
|
+
|
|
1286
|
+
{history}
|
|
1287
|
+
|
|
1288
|
+
Create an improved version that addresses all the feedback while maintaining the core message.`;
|
|
1289
|
+
var QUALITY_CRITERIA_TEMPLATE = `Evaluate against these specific criteria:
|
|
1290
|
+
{criteria}
|
|
1291
|
+
|
|
1292
|
+
Minimum required score: {minScore}/10
|
|
1293
|
+
{requireAll}`;
|
|
1294
|
+
var REFLECTION_HISTORY_TEMPLATE = `Previous Reflections:
|
|
1295
|
+
{reflections}
|
|
1296
|
+
|
|
1297
|
+
Previous Revisions:
|
|
1298
|
+
{revisions}`;
|
|
1299
|
+
var REFLECTION_ENTRY_TEMPLATE = `Iteration {iteration}:
|
|
1300
|
+
Critique: {critique}
|
|
1301
|
+
Score: {score}/10
|
|
1302
|
+
Issues: {issues}
|
|
1303
|
+
Suggestions: {suggestions}`;
|
|
1304
|
+
var REVISION_ENTRY_TEMPLATE = `Iteration {iteration}:
|
|
1305
|
+
{content}`;
|
|
1306
|
+
|
|
1307
|
+
// src/reflection/nodes.ts
|
|
1308
|
+
var import_messages3 = require("@langchain/core/messages");
|
|
1309
|
+
function createGeneratorNode(config) {
|
|
1310
|
+
const {
|
|
1311
|
+
llm,
|
|
1312
|
+
systemPrompt = DEFAULT_GENERATOR_SYSTEM_PROMPT,
|
|
1313
|
+
verbose = false
|
|
1314
|
+
} = config;
|
|
1315
|
+
return async (state) => {
|
|
1316
|
+
try {
|
|
1317
|
+
if (verbose) {
|
|
1318
|
+
console.log("[Generator] Generating initial response...");
|
|
1319
|
+
}
|
|
1320
|
+
let context = "";
|
|
1321
|
+
if (state.iteration > 0 && state.reflections.length > 0) {
|
|
1322
|
+
const lastReflection = state.reflections[state.reflections.length - 1];
|
|
1323
|
+
context = `
|
|
1324
|
+
Previous feedback to consider:
|
|
1325
|
+
${lastReflection.critique}`;
|
|
1326
|
+
}
|
|
1327
|
+
const userPrompt = GENERATION_PROMPT_TEMPLATE.replace("{input}", state.input || "").replace("{context}", context);
|
|
1328
|
+
const messages = [
|
|
1329
|
+
new import_messages3.SystemMessage(systemPrompt),
|
|
1330
|
+
new import_messages3.HumanMessage(userPrompt)
|
|
1331
|
+
];
|
|
1332
|
+
const response = await llm.invoke(messages);
|
|
1333
|
+
const content = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
1334
|
+
if (verbose) {
|
|
1335
|
+
console.log("[Generator] Generated response:", content.substring(0, 100) + "...");
|
|
1336
|
+
}
|
|
1337
|
+
return {
|
|
1338
|
+
currentResponse: content,
|
|
1339
|
+
status: "reflecting",
|
|
1340
|
+
iteration: 1
|
|
1341
|
+
};
|
|
1342
|
+
} catch (error) {
|
|
1343
|
+
console.error("[Generator] Error:", error);
|
|
1344
|
+
return {
|
|
1345
|
+
status: "failed",
|
|
1346
|
+
error: error instanceof Error ? error.message : "Unknown error in generator"
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1351
|
+
function createReflectorNode(config) {
|
|
1352
|
+
const {
|
|
1353
|
+
llm,
|
|
1354
|
+
systemPrompt = DEFAULT_REFLECTOR_SYSTEM_PROMPT,
|
|
1355
|
+
qualityCriteria,
|
|
1356
|
+
verbose = false
|
|
1357
|
+
} = config;
|
|
1358
|
+
return async (state) => {
|
|
1359
|
+
try {
|
|
1360
|
+
if (verbose) {
|
|
1361
|
+
console.log("[Reflector] Reflecting on response...");
|
|
1362
|
+
}
|
|
1363
|
+
if (!state.currentResponse) {
|
|
1364
|
+
throw new Error("No current response to reflect on");
|
|
1365
|
+
}
|
|
1366
|
+
let criteriaSection = "";
|
|
1367
|
+
const criteria = qualityCriteria || state.qualityCriteria;
|
|
1368
|
+
if (criteria) {
|
|
1369
|
+
const criteriaList = criteria.criteria?.join("\n- ") || "";
|
|
1370
|
+
criteriaSection = QUALITY_CRITERIA_TEMPLATE.replace("{criteria}", criteriaList ? `- ${criteriaList}` : "General quality standards").replace("{minScore}", criteria.minScore?.toString() || "7").replace("{requireAll}", criteria.requireAll ? "All criteria must be met." : "Meet as many criteria as possible.");
|
|
1371
|
+
}
|
|
1372
|
+
let historySection = "";
|
|
1373
|
+
if (state.reflections.length > 0 || state.revisions.length > 0) {
|
|
1374
|
+
const reflectionsText = state.reflections.map(
|
|
1375
|
+
(r, idx) => REFLECTION_ENTRY_TEMPLATE.replace("{iteration}", (idx + 1).toString()).replace("{critique}", r.critique).replace("{score}", r.score?.toString() || "N/A").replace("{issues}", r.issues.join(", ")).replace("{suggestions}", r.suggestions.join(", "))
|
|
1376
|
+
).join("\n\n");
|
|
1377
|
+
const revisionsText = state.revisions.map(
|
|
1378
|
+
(r) => REVISION_ENTRY_TEMPLATE.replace("{iteration}", r.iteration.toString()).replace("{content}", r.content.substring(0, 200) + "...")
|
|
1379
|
+
).join("\n\n");
|
|
1380
|
+
historySection = REFLECTION_HISTORY_TEMPLATE.replace("{reflections}", reflectionsText || "None").replace("{revisions}", revisionsText || "None");
|
|
1381
|
+
}
|
|
1382
|
+
const userPrompt = REFLECTION_PROMPT_TEMPLATE.replace("{input}", state.input || "").replace("{currentResponse}", state.currentResponse).replace("{criteria}", criteriaSection).replace("{history}", historySection);
|
|
1383
|
+
const messages = [
|
|
1384
|
+
new import_messages3.SystemMessage(systemPrompt),
|
|
1385
|
+
new import_messages3.HumanMessage(userPrompt)
|
|
1386
|
+
];
|
|
1387
|
+
const response = await llm.invoke(messages);
|
|
1388
|
+
const content = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
1389
|
+
let reflection;
|
|
1390
|
+
try {
|
|
1391
|
+
const jsonMatch = content.match(/\{[\s\S]*\}/);
|
|
1392
|
+
if (jsonMatch) {
|
|
1393
|
+
reflection = JSON.parse(jsonMatch[0]);
|
|
1394
|
+
} else {
|
|
1395
|
+
reflection = {
|
|
1396
|
+
critique: content,
|
|
1397
|
+
issues: [],
|
|
1398
|
+
suggestions: [],
|
|
1399
|
+
score: 5,
|
|
1400
|
+
meetsStandards: false
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
} catch (parseError) {
|
|
1404
|
+
reflection = {
|
|
1405
|
+
critique: content,
|
|
1406
|
+
issues: [],
|
|
1407
|
+
suggestions: [],
|
|
1408
|
+
score: 5,
|
|
1409
|
+
meetsStandards: false
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
if (verbose) {
|
|
1413
|
+
console.log("[Reflector] Reflection score:", reflection.score);
|
|
1414
|
+
console.log("[Reflector] Meets standards:", reflection.meetsStandards);
|
|
1415
|
+
}
|
|
1416
|
+
return {
|
|
1417
|
+
reflections: [reflection],
|
|
1418
|
+
status: reflection.meetsStandards ? "completed" : "revising"
|
|
1419
|
+
};
|
|
1420
|
+
} catch (error) {
|
|
1421
|
+
console.error("[Reflector] Error:", error);
|
|
1422
|
+
return {
|
|
1423
|
+
status: "failed",
|
|
1424
|
+
error: error instanceof Error ? error.message : "Unknown error in reflector"
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
};
|
|
1428
|
+
}
|
|
1429
|
+
function createReviserNode(config) {
|
|
1430
|
+
const {
|
|
1431
|
+
llm,
|
|
1432
|
+
systemPrompt = DEFAULT_REVISER_SYSTEM_PROMPT,
|
|
1433
|
+
verbose = false
|
|
1434
|
+
} = config;
|
|
1435
|
+
return async (state) => {
|
|
1436
|
+
try {
|
|
1437
|
+
if (verbose) {
|
|
1438
|
+
console.log("[Reviser] Revising response...");
|
|
1439
|
+
}
|
|
1440
|
+
if (!state.currentResponse) {
|
|
1441
|
+
throw new Error("No current response to revise");
|
|
1442
|
+
}
|
|
1443
|
+
if (state.reflections.length === 0) {
|
|
1444
|
+
throw new Error("No reflections to base revision on");
|
|
1445
|
+
}
|
|
1446
|
+
const lastReflection = state.reflections[state.reflections.length - 1];
|
|
1447
|
+
let historySection = "";
|
|
1448
|
+
if (state.revisions.length > 0) {
|
|
1449
|
+
const revisionsText = state.revisions.map(
|
|
1450
|
+
(r) => REVISION_ENTRY_TEMPLATE.replace("{iteration}", r.iteration.toString()).replace("{content}", r.content.substring(0, 200) + "...")
|
|
1451
|
+
).join("\n\n");
|
|
1452
|
+
historySection = `
|
|
1453
|
+
Previous Revisions:
|
|
1454
|
+
${revisionsText}`;
|
|
1455
|
+
}
|
|
1456
|
+
const userPrompt = REVISION_PROMPT_TEMPLATE.replace("{input}", state.input || "").replace("{currentResponse}", state.currentResponse).replace("{critique}", lastReflection.critique).replace("{issues}", lastReflection.issues.join("\n- ")).replace("{suggestions}", lastReflection.suggestions.join("\n- ")).replace("{history}", historySection);
|
|
1457
|
+
const messages = [
|
|
1458
|
+
new import_messages3.SystemMessage(systemPrompt),
|
|
1459
|
+
new import_messages3.HumanMessage(userPrompt)
|
|
1460
|
+
];
|
|
1461
|
+
const response = await llm.invoke(messages);
|
|
1462
|
+
const content = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
1463
|
+
if (verbose) {
|
|
1464
|
+
console.log("[Reviser] Created revision:", content.substring(0, 100) + "...");
|
|
1465
|
+
}
|
|
1466
|
+
const revision = {
|
|
1467
|
+
content,
|
|
1468
|
+
iteration: state.iteration,
|
|
1469
|
+
basedOn: lastReflection
|
|
1470
|
+
};
|
|
1471
|
+
return {
|
|
1472
|
+
currentResponse: content,
|
|
1473
|
+
revisions: [revision],
|
|
1474
|
+
status: "reflecting",
|
|
1475
|
+
iteration: 1
|
|
1476
|
+
};
|
|
1477
|
+
} catch (error) {
|
|
1478
|
+
console.error("[Reviser] Error:", error);
|
|
1479
|
+
return {
|
|
1480
|
+
status: "failed",
|
|
1481
|
+
error: error instanceof Error ? error.message : "Unknown error in reviser"
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
function createFinisherNode2() {
|
|
1487
|
+
return async (state) => {
|
|
1488
|
+
return {
|
|
1489
|
+
status: "completed",
|
|
1490
|
+
response: state.currentResponse
|
|
1491
|
+
};
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
// src/reflection/agent.ts
|
|
1496
|
+
var import_langgraph3 = require("@langchain/langgraph");
|
|
1497
|
+
function createReflectionAgent(config) {
|
|
1498
|
+
const {
|
|
1499
|
+
generator,
|
|
1500
|
+
reflector,
|
|
1501
|
+
reviser,
|
|
1502
|
+
maxIterations = 3,
|
|
1503
|
+
qualityCriteria,
|
|
1504
|
+
verbose = false
|
|
1505
|
+
} = config;
|
|
1506
|
+
const generatorNode = createGeneratorNode({ ...generator, verbose });
|
|
1507
|
+
const reflectorNode = createReflectorNode({ ...reflector, qualityCriteria, verbose });
|
|
1508
|
+
const reviserNode = createReviserNode({ ...reviser, verbose });
|
|
1509
|
+
const finisherNode = createFinisherNode2();
|
|
1510
|
+
const routeAfterGenerator = (state) => {
|
|
1511
|
+
if (state.status === "failed") {
|
|
1512
|
+
return "error";
|
|
1513
|
+
}
|
|
1514
|
+
return "reflect";
|
|
1515
|
+
};
|
|
1516
|
+
const routeAfterReflector = (state) => {
|
|
1517
|
+
if (state.status === "failed") {
|
|
1518
|
+
return "error";
|
|
1519
|
+
}
|
|
1520
|
+
if (state.status === "completed") {
|
|
1521
|
+
return "finish";
|
|
1522
|
+
}
|
|
1523
|
+
if (state.iteration >= maxIterations) {
|
|
1524
|
+
return "finish";
|
|
1525
|
+
}
|
|
1526
|
+
return "revise";
|
|
1527
|
+
};
|
|
1528
|
+
const routeAfterReviser = (state) => {
|
|
1529
|
+
if (state.status === "failed") {
|
|
1530
|
+
return "error";
|
|
1531
|
+
}
|
|
1532
|
+
if (state.iteration >= maxIterations) {
|
|
1533
|
+
return "finish";
|
|
1534
|
+
}
|
|
1535
|
+
return "reflect";
|
|
1536
|
+
};
|
|
1537
|
+
const workflow = new import_langgraph3.StateGraph(ReflectionState).addNode("generator", generatorNode).addNode("reflector", reflectorNode).addNode("reviser", reviserNode).addNode("finisher", finisherNode);
|
|
1538
|
+
workflow.addEdge("__start__", "generator").addConditionalEdges(
|
|
1539
|
+
"generator",
|
|
1540
|
+
routeAfterGenerator,
|
|
1541
|
+
{
|
|
1542
|
+
reflect: "reflector",
|
|
1543
|
+
error: import_langgraph3.END
|
|
1544
|
+
}
|
|
1545
|
+
).addConditionalEdges(
|
|
1546
|
+
"reflector",
|
|
1547
|
+
routeAfterReflector,
|
|
1548
|
+
{
|
|
1549
|
+
revise: "reviser",
|
|
1550
|
+
finish: "finisher",
|
|
1551
|
+
error: import_langgraph3.END
|
|
1552
|
+
}
|
|
1553
|
+
).addConditionalEdges(
|
|
1554
|
+
"reviser",
|
|
1555
|
+
routeAfterReviser,
|
|
1556
|
+
{
|
|
1557
|
+
reflect: "reflector",
|
|
1558
|
+
finish: "finisher",
|
|
1559
|
+
error: import_langgraph3.END
|
|
1560
|
+
}
|
|
1561
|
+
).addEdge("finisher", import_langgraph3.END);
|
|
1562
|
+
return workflow.compile();
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
// src/multi-agent/state.ts
|
|
1566
|
+
var import_zod8 = require("zod");
|
|
1567
|
+
var import_core6 = require("@agentforge/core");
|
|
1568
|
+
|
|
1569
|
+
// src/multi-agent/schemas.ts
|
|
1570
|
+
var import_zod7 = require("zod");
|
|
1571
|
+
var AgentRoleSchema = import_zod7.z.enum(["supervisor", "worker"]);
|
|
1572
|
+
var MessageTypeSchema = import_zod7.z.enum([
|
|
1573
|
+
"user_input",
|
|
1574
|
+
// Initial user message
|
|
1575
|
+
"task_assignment",
|
|
1576
|
+
// Supervisor assigns task to worker
|
|
1577
|
+
"task_result",
|
|
1578
|
+
// Worker returns result to supervisor
|
|
1579
|
+
"handoff",
|
|
1580
|
+
// Worker hands off to another worker
|
|
1581
|
+
"error",
|
|
1582
|
+
// Error message
|
|
1583
|
+
"completion"
|
|
1584
|
+
// Final completion message
|
|
1585
|
+
]);
|
|
1586
|
+
var AgentMessageSchema = import_zod7.z.object({
|
|
1587
|
+
/**
|
|
1588
|
+
* Unique identifier for the message
|
|
1589
|
+
*/
|
|
1590
|
+
id: import_zod7.z.string().describe("Unique message identifier"),
|
|
1591
|
+
/**
|
|
1592
|
+
* Type of message
|
|
1593
|
+
*/
|
|
1594
|
+
type: MessageTypeSchema.describe("Type of message"),
|
|
1595
|
+
/**
|
|
1596
|
+
* Agent that sent the message
|
|
1597
|
+
*/
|
|
1598
|
+
from: import_zod7.z.string().describe("Agent identifier that sent the message"),
|
|
1599
|
+
/**
|
|
1600
|
+
* Agent(s) that should receive the message
|
|
1601
|
+
*/
|
|
1602
|
+
to: import_zod7.z.union([import_zod7.z.string(), import_zod7.z.array(import_zod7.z.string())]).describe("Target agent(s)"),
|
|
1603
|
+
/**
|
|
1604
|
+
* Message content
|
|
1605
|
+
*/
|
|
1606
|
+
content: import_zod7.z.string().describe("Message content"),
|
|
1607
|
+
/**
|
|
1608
|
+
* Optional metadata
|
|
1609
|
+
*/
|
|
1610
|
+
metadata: import_zod7.z.record(import_zod7.z.any()).optional().describe("Additional message metadata"),
|
|
1611
|
+
/**
|
|
1612
|
+
* Timestamp when message was created
|
|
1613
|
+
*/
|
|
1614
|
+
timestamp: import_zod7.z.number().describe("Timestamp when message was created")
|
|
1615
|
+
});
|
|
1616
|
+
var RoutingStrategySchema = import_zod7.z.enum([
|
|
1617
|
+
"llm-based",
|
|
1618
|
+
// LLM decides which agent to route to
|
|
1619
|
+
"rule-based",
|
|
1620
|
+
// Predefined rules determine routing
|
|
1621
|
+
"round-robin",
|
|
1622
|
+
// Distribute tasks evenly across agents
|
|
1623
|
+
"skill-based",
|
|
1624
|
+
// Route based on agent capabilities
|
|
1625
|
+
"load-balanced"
|
|
1626
|
+
// Route based on agent workload
|
|
1627
|
+
]);
|
|
1628
|
+
var RoutingDecisionSchema = import_zod7.z.object({
|
|
1629
|
+
/**
|
|
1630
|
+
* Target agent to route to
|
|
1631
|
+
*/
|
|
1632
|
+
targetAgent: import_zod7.z.string().describe("Agent to route the task to"),
|
|
1633
|
+
/**
|
|
1634
|
+
* Reasoning for the routing decision
|
|
1635
|
+
*/
|
|
1636
|
+
reasoning: import_zod7.z.string().optional().describe("Explanation for routing decision"),
|
|
1637
|
+
/**
|
|
1638
|
+
* Confidence in the routing decision (0-1)
|
|
1639
|
+
*/
|
|
1640
|
+
confidence: import_zod7.z.number().min(0).max(1).optional().describe("Confidence score"),
|
|
1641
|
+
/**
|
|
1642
|
+
* Strategy used for routing
|
|
1643
|
+
*/
|
|
1644
|
+
strategy: RoutingStrategySchema.describe("Strategy used for this decision"),
|
|
1645
|
+
/**
|
|
1646
|
+
* Timestamp of the routing decision
|
|
1647
|
+
*/
|
|
1648
|
+
timestamp: import_zod7.z.number().optional().describe("Timestamp of the decision")
|
|
1649
|
+
});
|
|
1650
|
+
var WorkerCapabilitiesSchema = import_zod7.z.object({
|
|
1651
|
+
/**
|
|
1652
|
+
* Skills/capabilities the agent has
|
|
1653
|
+
*/
|
|
1654
|
+
skills: import_zod7.z.array(import_zod7.z.string()).describe("List of agent skills"),
|
|
1655
|
+
/**
|
|
1656
|
+
* Tools available to the agent
|
|
1657
|
+
*/
|
|
1658
|
+
tools: import_zod7.z.array(import_zod7.z.string()).describe("List of tool names available to agent"),
|
|
1659
|
+
/**
|
|
1660
|
+
* Whether the agent is currently available
|
|
1661
|
+
*/
|
|
1662
|
+
available: import_zod7.z.boolean().default(true).describe("Whether agent is available"),
|
|
1663
|
+
/**
|
|
1664
|
+
* Current workload (number of active tasks)
|
|
1665
|
+
*/
|
|
1666
|
+
currentWorkload: import_zod7.z.number().int().nonnegative().default(0).describe("Current number of active tasks")
|
|
1667
|
+
});
|
|
1668
|
+
var TaskAssignmentSchema = import_zod7.z.object({
|
|
1669
|
+
/**
|
|
1670
|
+
* Unique assignment identifier
|
|
1671
|
+
*/
|
|
1672
|
+
id: import_zod7.z.string().describe("Unique assignment identifier"),
|
|
1673
|
+
/**
|
|
1674
|
+
* Worker ID assigned to the task
|
|
1675
|
+
*/
|
|
1676
|
+
workerId: import_zod7.z.string().describe("Worker identifier assigned to task"),
|
|
1677
|
+
/**
|
|
1678
|
+
* Task description
|
|
1679
|
+
*/
|
|
1680
|
+
task: import_zod7.z.string().describe("Description of the task"),
|
|
1681
|
+
/**
|
|
1682
|
+
* Task priority (1-10, higher is more urgent)
|
|
1683
|
+
*/
|
|
1684
|
+
priority: import_zod7.z.number().int().min(1).max(10).default(5).describe("Task priority"),
|
|
1685
|
+
/**
|
|
1686
|
+
* Timestamp when task was assigned
|
|
1687
|
+
*/
|
|
1688
|
+
assignedAt: import_zod7.z.number().describe("Timestamp when task was assigned"),
|
|
1689
|
+
/**
|
|
1690
|
+
* Optional deadline for task completion
|
|
1691
|
+
*/
|
|
1692
|
+
deadline: import_zod7.z.number().optional().describe("Optional task deadline timestamp")
|
|
1693
|
+
});
|
|
1694
|
+
var TaskResultSchema = import_zod7.z.object({
|
|
1695
|
+
/**
|
|
1696
|
+
* Assignment identifier
|
|
1697
|
+
*/
|
|
1698
|
+
assignmentId: import_zod7.z.string().describe("Assignment identifier"),
|
|
1699
|
+
/**
|
|
1700
|
+
* Worker that completed the task
|
|
1701
|
+
*/
|
|
1702
|
+
workerId: import_zod7.z.string().describe("Worker that completed the task"),
|
|
1703
|
+
/**
|
|
1704
|
+
* Whether the task succeeded
|
|
1705
|
+
*/
|
|
1706
|
+
success: import_zod7.z.boolean().describe("Whether the task succeeded"),
|
|
1707
|
+
/**
|
|
1708
|
+
* Task result/output
|
|
1709
|
+
*/
|
|
1710
|
+
result: import_zod7.z.string().describe("Task result or output"),
|
|
1711
|
+
/**
|
|
1712
|
+
* Optional error message if task failed
|
|
1713
|
+
*/
|
|
1714
|
+
error: import_zod7.z.string().optional().describe("Error message if task failed"),
|
|
1715
|
+
/**
|
|
1716
|
+
* Timestamp when task was completed
|
|
1717
|
+
*/
|
|
1718
|
+
completedAt: import_zod7.z.number().describe("Timestamp when task was completed"),
|
|
1719
|
+
/**
|
|
1720
|
+
* Optional metadata about execution
|
|
1721
|
+
*/
|
|
1722
|
+
metadata: import_zod7.z.record(import_zod7.z.any()).optional().describe("Execution metadata")
|
|
1723
|
+
});
|
|
1724
|
+
var MultiAgentStatusSchema = import_zod7.z.enum([
|
|
1725
|
+
"initializing",
|
|
1726
|
+
// System is initializing
|
|
1727
|
+
"routing",
|
|
1728
|
+
// Supervisor is routing the task
|
|
1729
|
+
"executing",
|
|
1730
|
+
// Worker is executing the task
|
|
1731
|
+
"coordinating",
|
|
1732
|
+
// Multiple workers are coordinating
|
|
1733
|
+
"aggregating",
|
|
1734
|
+
// Results are being aggregated
|
|
1735
|
+
"completed",
|
|
1736
|
+
// Task is completed
|
|
1737
|
+
"failed"
|
|
1738
|
+
// Task failed
|
|
1739
|
+
]);
|
|
1740
|
+
var HandoffRequestSchema = import_zod7.z.object({
|
|
1741
|
+
/**
|
|
1742
|
+
* Agent requesting the handoff
|
|
1743
|
+
*/
|
|
1744
|
+
from: import_zod7.z.string().describe("Agent requesting handoff"),
|
|
1745
|
+
/**
|
|
1746
|
+
* Target agent for handoff
|
|
1747
|
+
*/
|
|
1748
|
+
to: import_zod7.z.string().describe("Target agent for handoff"),
|
|
1749
|
+
/**
|
|
1750
|
+
* Reason for handoff
|
|
1751
|
+
*/
|
|
1752
|
+
reason: import_zod7.z.string().describe("Reason for requesting handoff"),
|
|
1753
|
+
/**
|
|
1754
|
+
* Context to pass to next agent
|
|
1755
|
+
*/
|
|
1756
|
+
context: import_zod7.z.any().describe("Context to pass to next agent"),
|
|
1757
|
+
/**
|
|
1758
|
+
* Timestamp of handoff request
|
|
1759
|
+
*/
|
|
1760
|
+
timestamp: import_zod7.z.string().datetime().describe("ISO timestamp of handoff request")
|
|
1761
|
+
});
|
|
1762
|
+
|
|
1763
|
+
// src/multi-agent/state.ts
|
|
1764
|
+
var MultiAgentStateConfig = {
|
|
1765
|
+
/**
|
|
1766
|
+
* Original user input/query
|
|
1767
|
+
*/
|
|
1768
|
+
input: {
|
|
1769
|
+
schema: import_zod8.z.string(),
|
|
1770
|
+
default: () => "",
|
|
1771
|
+
description: "Original user input or query"
|
|
1772
|
+
},
|
|
1773
|
+
/**
|
|
1774
|
+
* All messages in the multi-agent conversation
|
|
1775
|
+
* Accumulates all messages between agents
|
|
1776
|
+
*/
|
|
1777
|
+
messages: {
|
|
1778
|
+
schema: import_zod8.z.array(AgentMessageSchema),
|
|
1779
|
+
reducer: (left, right) => [...left, ...right],
|
|
1780
|
+
default: () => [],
|
|
1781
|
+
description: "All messages in the multi-agent conversation"
|
|
1782
|
+
},
|
|
1783
|
+
/**
|
|
1784
|
+
* Available worker agents and their capabilities
|
|
1785
|
+
*/
|
|
1786
|
+
workers: {
|
|
1787
|
+
schema: import_zod8.z.record(import_zod8.z.string(), WorkerCapabilitiesSchema),
|
|
1788
|
+
reducer: (left, right) => ({
|
|
1789
|
+
...left,
|
|
1790
|
+
...right
|
|
1791
|
+
}),
|
|
1792
|
+
default: () => ({}),
|
|
1793
|
+
description: "Available worker agents and their capabilities"
|
|
1794
|
+
},
|
|
1795
|
+
/**
|
|
1796
|
+
* Current active agent
|
|
1797
|
+
*/
|
|
1798
|
+
currentAgent: {
|
|
1799
|
+
schema: import_zod8.z.string().optional(),
|
|
1800
|
+
description: "Identifier of the currently active agent"
|
|
1801
|
+
},
|
|
1802
|
+
/**
|
|
1803
|
+
* Routing decisions made by the supervisor
|
|
1804
|
+
* Accumulates all routing decisions
|
|
1805
|
+
*/
|
|
1806
|
+
routingHistory: {
|
|
1807
|
+
schema: import_zod8.z.array(RoutingDecisionSchema),
|
|
1808
|
+
reducer: (left, right) => [...left, ...right],
|
|
1809
|
+
default: () => [],
|
|
1810
|
+
description: "History of routing decisions"
|
|
1811
|
+
},
|
|
1812
|
+
/**
|
|
1813
|
+
* Active task assignments
|
|
1814
|
+
*/
|
|
1815
|
+
activeAssignments: {
|
|
1816
|
+
schema: import_zod8.z.array(TaskAssignmentSchema),
|
|
1817
|
+
reducer: (left, right) => [...left, ...right],
|
|
1818
|
+
default: () => [],
|
|
1819
|
+
description: "Currently active task assignments"
|
|
1820
|
+
},
|
|
1821
|
+
/**
|
|
1822
|
+
* Completed task results
|
|
1823
|
+
* Accumulates all completed tasks
|
|
1824
|
+
*/
|
|
1825
|
+
completedTasks: {
|
|
1826
|
+
schema: import_zod8.z.array(TaskResultSchema),
|
|
1827
|
+
reducer: (left, right) => [...left, ...right],
|
|
1828
|
+
default: () => [],
|
|
1829
|
+
description: "Completed task results"
|
|
1830
|
+
},
|
|
1831
|
+
/**
|
|
1832
|
+
* Handoff requests between agents
|
|
1833
|
+
* Accumulates all handoff requests
|
|
1834
|
+
*/
|
|
1835
|
+
handoffs: {
|
|
1836
|
+
schema: import_zod8.z.array(HandoffRequestSchema),
|
|
1837
|
+
reducer: (left, right) => [...left, ...right],
|
|
1838
|
+
default: () => [],
|
|
1839
|
+
description: "Handoff requests between agents"
|
|
1840
|
+
},
|
|
1841
|
+
/**
|
|
1842
|
+
* Current execution status
|
|
1843
|
+
*/
|
|
1844
|
+
status: {
|
|
1845
|
+
schema: MultiAgentStatusSchema,
|
|
1846
|
+
default: () => "initializing",
|
|
1847
|
+
description: "Current multi-agent execution status"
|
|
1848
|
+
},
|
|
1849
|
+
/**
|
|
1850
|
+
* Iteration counter
|
|
1851
|
+
*/
|
|
1852
|
+
iteration: {
|
|
1853
|
+
schema: import_zod8.z.number().int().nonnegative(),
|
|
1854
|
+
reducer: (left, right) => left + right,
|
|
1855
|
+
default: () => 0,
|
|
1856
|
+
description: "Current iteration number"
|
|
1857
|
+
},
|
|
1858
|
+
/**
|
|
1859
|
+
* Maximum iterations allowed
|
|
1860
|
+
*/
|
|
1861
|
+
maxIterations: {
|
|
1862
|
+
schema: import_zod8.z.number().int().positive(),
|
|
1863
|
+
default: () => 10,
|
|
1864
|
+
description: "Maximum number of iterations allowed"
|
|
1865
|
+
},
|
|
1866
|
+
/**
|
|
1867
|
+
* Final aggregated response
|
|
1868
|
+
*/
|
|
1869
|
+
response: {
|
|
1870
|
+
schema: import_zod8.z.string().optional(),
|
|
1871
|
+
description: "Final aggregated response"
|
|
1872
|
+
},
|
|
1873
|
+
/**
|
|
1874
|
+
* Error message if execution failed
|
|
1875
|
+
*/
|
|
1876
|
+
error: {
|
|
1877
|
+
schema: import_zod8.z.string().optional(),
|
|
1878
|
+
description: "Error message if execution failed"
|
|
1879
|
+
}
|
|
1880
|
+
};
|
|
1881
|
+
var MultiAgentState = (0, import_core6.createStateAnnotation)(MultiAgentStateConfig);
|
|
1882
|
+
|
|
1883
|
+
// src/multi-agent/routing.ts
|
|
1884
|
+
var import_messages4 = require("@langchain/core/messages");
|
|
1885
|
+
var DEFAULT_SUPERVISOR_SYSTEM_PROMPT = `You are a supervisor agent responsible for routing tasks to specialized worker agents.
|
|
1886
|
+
|
|
1887
|
+
Your job is to:
|
|
1888
|
+
1. Analyze the current task and context
|
|
1889
|
+
2. Review available worker capabilities
|
|
1890
|
+
3. Select the most appropriate worker for the task
|
|
1891
|
+
4. Provide clear reasoning for your decision
|
|
1892
|
+
|
|
1893
|
+
Respond with a JSON object containing:
|
|
1894
|
+
{
|
|
1895
|
+
"targetAgent": "worker_id",
|
|
1896
|
+
"reasoning": "explanation of why this worker is best suited",
|
|
1897
|
+
"confidence": 0.0-1.0,
|
|
1898
|
+
"strategy": "llm-based"
|
|
1899
|
+
}`;
|
|
1900
|
+
var llmBasedRouting = {
|
|
1901
|
+
name: "llm-based",
|
|
1902
|
+
async route(state, config) {
|
|
1903
|
+
if (!config.llm) {
|
|
1904
|
+
throw new Error("LLM-based routing requires an LLM to be configured");
|
|
1905
|
+
}
|
|
1906
|
+
const systemPrompt = config.systemPrompt || DEFAULT_SUPERVISOR_SYSTEM_PROMPT;
|
|
1907
|
+
const workerInfo = Object.entries(state.workers).map(([id, caps]) => {
|
|
1908
|
+
const skills = caps.skills.join(", ");
|
|
1909
|
+
const tools = caps.tools.join(", ");
|
|
1910
|
+
const available = caps.available ? "available" : "busy";
|
|
1911
|
+
return `- ${id}: Skills: [${skills}], Tools: [${tools}], Status: ${available}, Workload: ${caps.currentWorkload}`;
|
|
1912
|
+
}).join("\n");
|
|
1913
|
+
const lastMessage = state.messages[state.messages.length - 1];
|
|
1914
|
+
const taskContext = lastMessage?.content || state.input;
|
|
1915
|
+
const userPrompt = `Current task: ${taskContext}
|
|
1916
|
+
|
|
1917
|
+
Available workers:
|
|
1918
|
+
${workerInfo}
|
|
1919
|
+
|
|
1920
|
+
Select the best worker for this task and explain your reasoning.`;
|
|
1921
|
+
const messages = [
|
|
1922
|
+
new import_messages4.SystemMessage(systemPrompt),
|
|
1923
|
+
new import_messages4.HumanMessage(userPrompt)
|
|
1924
|
+
];
|
|
1925
|
+
const response = await config.llm.invoke(messages);
|
|
1926
|
+
const content = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
1927
|
+
try {
|
|
1928
|
+
const decision = JSON.parse(content);
|
|
1929
|
+
return {
|
|
1930
|
+
targetAgent: decision.targetAgent,
|
|
1931
|
+
reasoning: decision.reasoning,
|
|
1932
|
+
confidence: decision.confidence,
|
|
1933
|
+
strategy: "llm-based",
|
|
1934
|
+
timestamp: Date.now()
|
|
1935
|
+
};
|
|
1936
|
+
} catch (error) {
|
|
1937
|
+
throw new Error(`Failed to parse routing decision from LLM: ${error}`);
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
};
|
|
1941
|
+
var roundRobinRouting = {
|
|
1942
|
+
name: "round-robin",
|
|
1943
|
+
async route(state, config) {
|
|
1944
|
+
const availableWorkers = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id]) => id);
|
|
1945
|
+
if (availableWorkers.length === 0) {
|
|
1946
|
+
throw new Error("No available workers for round-robin routing");
|
|
1947
|
+
}
|
|
1948
|
+
const lastRoutingIndex = state.routingHistory.length % availableWorkers.length;
|
|
1949
|
+
const targetAgent = availableWorkers[lastRoutingIndex];
|
|
1950
|
+
return {
|
|
1951
|
+
targetAgent,
|
|
1952
|
+
reasoning: `Round-robin selection: worker ${lastRoutingIndex + 1} of ${availableWorkers.length}`,
|
|
1953
|
+
confidence: 1,
|
|
1954
|
+
strategy: "round-robin",
|
|
1955
|
+
timestamp: Date.now()
|
|
1956
|
+
};
|
|
1957
|
+
}
|
|
1958
|
+
};
|
|
1959
|
+
var skillBasedRouting = {
|
|
1960
|
+
name: "skill-based",
|
|
1961
|
+
async route(state, config) {
|
|
1962
|
+
const lastMessage = state.messages[state.messages.length - 1];
|
|
1963
|
+
const taskContent = (lastMessage?.content || state.input).toLowerCase();
|
|
1964
|
+
const workerScores = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => {
|
|
1965
|
+
const skillMatches = caps.skills.filter(
|
|
1966
|
+
(skill) => taskContent.includes(skill.toLowerCase())
|
|
1967
|
+
).length;
|
|
1968
|
+
const toolMatches = caps.tools.filter(
|
|
1969
|
+
(tool) => taskContent.includes(tool.toLowerCase())
|
|
1970
|
+
).length;
|
|
1971
|
+
const score = skillMatches * 2 + toolMatches;
|
|
1972
|
+
return { id, score, skills: caps.skills, tools: caps.tools };
|
|
1973
|
+
}).filter((w) => w.score > 0).sort((a, b) => b.score - a.score);
|
|
1974
|
+
if (workerScores.length === 0) {
|
|
1975
|
+
const firstAvailable = Object.entries(state.workers).find(([_, caps]) => caps.available);
|
|
1976
|
+
if (!firstAvailable) {
|
|
1977
|
+
throw new Error("No available workers for skill-based routing");
|
|
1978
|
+
}
|
|
1979
|
+
return {
|
|
1980
|
+
targetAgent: firstAvailable[0],
|
|
1981
|
+
reasoning: "No skill matches found, using first available worker",
|
|
1982
|
+
confidence: 0.5,
|
|
1983
|
+
strategy: "skill-based",
|
|
1984
|
+
timestamp: Date.now()
|
|
1985
|
+
};
|
|
1986
|
+
}
|
|
1987
|
+
const best = workerScores[0];
|
|
1988
|
+
const confidence = Math.min(best.score / 5, 1);
|
|
1989
|
+
return {
|
|
1990
|
+
targetAgent: best.id,
|
|
1991
|
+
reasoning: `Best skill match with score ${best.score} (skills: ${best.skills.join(", ")})`,
|
|
1992
|
+
confidence,
|
|
1993
|
+
strategy: "skill-based",
|
|
1994
|
+
timestamp: Date.now()
|
|
1995
|
+
};
|
|
1996
|
+
}
|
|
1997
|
+
};
|
|
1998
|
+
var loadBalancedRouting = {
|
|
1999
|
+
name: "load-balanced",
|
|
2000
|
+
async route(state, config) {
|
|
2001
|
+
const availableWorkers = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => ({ id, workload: caps.currentWorkload })).sort((a, b) => a.workload - b.workload);
|
|
2002
|
+
if (availableWorkers.length === 0) {
|
|
2003
|
+
throw new Error("No available workers for load-balanced routing");
|
|
2004
|
+
}
|
|
2005
|
+
const targetWorker = availableWorkers[0];
|
|
2006
|
+
const avgWorkload = availableWorkers.reduce((sum, w) => sum + w.workload, 0) / availableWorkers.length;
|
|
2007
|
+
const confidence = targetWorker.workload === 0 ? 1 : Math.max(0.5, 1 - targetWorker.workload / (avgWorkload * 2));
|
|
2008
|
+
return {
|
|
2009
|
+
targetAgent: targetWorker.id,
|
|
2010
|
+
reasoning: `Lowest workload: ${targetWorker.workload} tasks (avg: ${avgWorkload.toFixed(1)})`,
|
|
2011
|
+
confidence,
|
|
2012
|
+
strategy: "load-balanced",
|
|
2013
|
+
timestamp: Date.now()
|
|
2014
|
+
};
|
|
2015
|
+
}
|
|
2016
|
+
};
|
|
2017
|
+
var ruleBasedRouting = {
|
|
2018
|
+
name: "rule-based",
|
|
2019
|
+
async route(state, config) {
|
|
2020
|
+
if (!config.routingFn) {
|
|
2021
|
+
throw new Error("Rule-based routing requires a custom routing function");
|
|
2022
|
+
}
|
|
2023
|
+
return await config.routingFn(state);
|
|
2024
|
+
}
|
|
2025
|
+
};
|
|
2026
|
+
function getRoutingStrategy(name) {
|
|
2027
|
+
switch (name) {
|
|
2028
|
+
case "llm-based":
|
|
2029
|
+
return llmBasedRouting;
|
|
2030
|
+
case "round-robin":
|
|
2031
|
+
return roundRobinRouting;
|
|
2032
|
+
case "skill-based":
|
|
2033
|
+
return skillBasedRouting;
|
|
2034
|
+
case "load-balanced":
|
|
2035
|
+
return loadBalancedRouting;
|
|
2036
|
+
case "rule-based":
|
|
2037
|
+
return ruleBasedRouting;
|
|
2038
|
+
default:
|
|
2039
|
+
throw new Error(`Unknown routing strategy: ${name}`);
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
// src/multi-agent/nodes.ts
|
|
2044
|
+
var import_messages5 = require("@langchain/core/messages");
|
|
2045
|
+
var import_core7 = require("@agentforge/core");
|
|
2046
|
+
var DEFAULT_AGGREGATOR_SYSTEM_PROMPT = `You are an aggregator agent responsible for combining results from multiple worker agents.
|
|
2047
|
+
|
|
2048
|
+
Your job is to:
|
|
2049
|
+
1. Review all completed task results
|
|
2050
|
+
2. Synthesize the information into a coherent response
|
|
2051
|
+
3. Ensure all aspects of the original query are addressed
|
|
2052
|
+
4. Provide a clear, comprehensive final answer
|
|
2053
|
+
|
|
2054
|
+
Be concise but thorough in your aggregation.`;
|
|
2055
|
+
function createSupervisorNode(config) {
|
|
2056
|
+
const {
|
|
2057
|
+
strategy,
|
|
2058
|
+
verbose = false,
|
|
2059
|
+
maxIterations = 10
|
|
2060
|
+
} = config;
|
|
2061
|
+
return async (state) => {
|
|
2062
|
+
try {
|
|
2063
|
+
if (verbose) {
|
|
2064
|
+
console.log(`[Supervisor] Routing iteration ${state.iteration}/${maxIterations}`);
|
|
2065
|
+
}
|
|
2066
|
+
if (state.iteration >= maxIterations) {
|
|
2067
|
+
if (verbose) {
|
|
2068
|
+
console.log("[Supervisor] Max iterations reached, moving to aggregation");
|
|
2069
|
+
}
|
|
2070
|
+
return {
|
|
2071
|
+
status: "aggregating",
|
|
2072
|
+
currentAgent: "aggregator"
|
|
2073
|
+
};
|
|
2074
|
+
}
|
|
2075
|
+
const allCompleted = state.activeAssignments.every(
|
|
2076
|
+
(assignment2) => state.completedTasks.some((task) => task.assignmentId === assignment2.id)
|
|
2077
|
+
);
|
|
2078
|
+
if (allCompleted && state.activeAssignments.length > 0) {
|
|
2079
|
+
if (verbose) {
|
|
2080
|
+
console.log("[Supervisor] All tasks completed, moving to aggregation");
|
|
2081
|
+
}
|
|
2082
|
+
return {
|
|
2083
|
+
status: "aggregating",
|
|
2084
|
+
currentAgent: "aggregator"
|
|
2085
|
+
};
|
|
2086
|
+
}
|
|
2087
|
+
const routingImpl = getRoutingStrategy(strategy);
|
|
2088
|
+
const decision = await routingImpl.route(state, config);
|
|
2089
|
+
if (verbose) {
|
|
2090
|
+
console.log(`[Supervisor] Routing to ${decision.targetAgent}: ${decision.reasoning}`);
|
|
2091
|
+
}
|
|
2092
|
+
const assignment = {
|
|
2093
|
+
id: `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
2094
|
+
workerId: decision.targetAgent,
|
|
2095
|
+
task: state.messages[state.messages.length - 1]?.content || state.input,
|
|
2096
|
+
priority: 5,
|
|
2097
|
+
assignedAt: Date.now()
|
|
2098
|
+
};
|
|
2099
|
+
const message = {
|
|
2100
|
+
id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
2101
|
+
from: "supervisor",
|
|
2102
|
+
to: [decision.targetAgent],
|
|
2103
|
+
type: "task_assignment",
|
|
2104
|
+
content: assignment.task,
|
|
2105
|
+
timestamp: Date.now(),
|
|
2106
|
+
metadata: {
|
|
2107
|
+
assignmentId: assignment.id,
|
|
2108
|
+
priority: assignment.priority
|
|
2109
|
+
}
|
|
2110
|
+
};
|
|
2111
|
+
return {
|
|
2112
|
+
currentAgent: decision.targetAgent,
|
|
2113
|
+
status: "executing",
|
|
2114
|
+
routingHistory: [decision],
|
|
2115
|
+
activeAssignments: [assignment],
|
|
2116
|
+
messages: [message],
|
|
2117
|
+
iteration: state.iteration + 1
|
|
2118
|
+
};
|
|
2119
|
+
} catch (error) {
|
|
2120
|
+
console.error("[Supervisor] Error:", error);
|
|
2121
|
+
return {
|
|
2122
|
+
status: "failed",
|
|
2123
|
+
error: error instanceof Error ? error.message : "Unknown error in supervisor"
|
|
2124
|
+
};
|
|
2125
|
+
}
|
|
2126
|
+
};
|
|
2127
|
+
}
|
|
2128
|
+
function createWorkerNode(config) {
|
|
2129
|
+
const {
|
|
2130
|
+
id,
|
|
2131
|
+
capabilities,
|
|
2132
|
+
llm,
|
|
2133
|
+
tools = [],
|
|
2134
|
+
systemPrompt,
|
|
2135
|
+
verbose = false,
|
|
2136
|
+
executeFn
|
|
2137
|
+
} = config;
|
|
2138
|
+
return async (state) => {
|
|
2139
|
+
try {
|
|
2140
|
+
if (verbose) {
|
|
2141
|
+
console.log(`[Worker:${id}] Executing task`);
|
|
2142
|
+
}
|
|
2143
|
+
const currentAssignment = state.activeAssignments.find(
|
|
2144
|
+
(assignment) => assignment.workerId === id && !state.completedTasks.some((task) => task.assignmentId === assignment.id)
|
|
2145
|
+
);
|
|
2146
|
+
if (!currentAssignment) {
|
|
2147
|
+
if (verbose) {
|
|
2148
|
+
console.log(`[Worker:${id}] No active assignment found`);
|
|
2149
|
+
}
|
|
2150
|
+
return {
|
|
2151
|
+
currentAgent: "supervisor",
|
|
2152
|
+
status: "routing"
|
|
2153
|
+
};
|
|
2154
|
+
}
|
|
2155
|
+
if (executeFn) {
|
|
2156
|
+
return await executeFn(state);
|
|
2157
|
+
}
|
|
2158
|
+
if (!llm) {
|
|
2159
|
+
throw new Error(`Worker ${id} requires either an LLM or custom execution function`);
|
|
2160
|
+
}
|
|
2161
|
+
const defaultSystemPrompt = `You are a specialized worker agent with the following capabilities:
|
|
2162
|
+
Skills: ${capabilities.skills.join(", ")}
|
|
2163
|
+
Tools: ${capabilities.tools.join(", ")}
|
|
2164
|
+
|
|
2165
|
+
Execute the assigned task using your skills and tools. Provide a clear, actionable result.`;
|
|
2166
|
+
const messages = [
|
|
2167
|
+
new import_messages5.SystemMessage(systemPrompt || defaultSystemPrompt),
|
|
2168
|
+
new import_messages5.HumanMessage(currentAssignment.task)
|
|
2169
|
+
];
|
|
2170
|
+
let llmToUse = llm;
|
|
2171
|
+
if (tools.length > 0 && llm.bindTools) {
|
|
2172
|
+
const langchainTools = (0, import_core7.toLangChainTools)(tools);
|
|
2173
|
+
llmToUse = llm.bindTools(langchainTools);
|
|
2174
|
+
}
|
|
2175
|
+
const response = await llmToUse.invoke(messages);
|
|
2176
|
+
const result = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
2177
|
+
if (verbose) {
|
|
2178
|
+
console.log(`[Worker:${id}] Task completed:`, result.substring(0, 100) + "...");
|
|
2179
|
+
}
|
|
2180
|
+
const taskResult = {
|
|
2181
|
+
assignmentId: currentAssignment.id,
|
|
2182
|
+
workerId: id,
|
|
2183
|
+
success: true,
|
|
2184
|
+
result,
|
|
2185
|
+
completedAt: Date.now(),
|
|
2186
|
+
metadata: {
|
|
2187
|
+
skills_used: capabilities.skills
|
|
2188
|
+
}
|
|
2189
|
+
};
|
|
2190
|
+
const message = {
|
|
2191
|
+
id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
2192
|
+
from: id,
|
|
2193
|
+
to: ["supervisor"],
|
|
2194
|
+
type: "task_result",
|
|
2195
|
+
content: result,
|
|
2196
|
+
timestamp: Date.now(),
|
|
2197
|
+
metadata: {
|
|
2198
|
+
assignmentId: currentAssignment.id,
|
|
2199
|
+
success: true
|
|
2200
|
+
}
|
|
2201
|
+
};
|
|
2202
|
+
const updatedWorkers = {
|
|
2203
|
+
...state.workers,
|
|
2204
|
+
[id]: {
|
|
2205
|
+
...capabilities,
|
|
2206
|
+
currentWorkload: Math.max(0, capabilities.currentWorkload - 1)
|
|
2207
|
+
}
|
|
2208
|
+
};
|
|
2209
|
+
return {
|
|
2210
|
+
completedTasks: [taskResult],
|
|
2211
|
+
messages: [message],
|
|
2212
|
+
workers: updatedWorkers,
|
|
2213
|
+
currentAgent: "supervisor",
|
|
2214
|
+
status: "routing"
|
|
2215
|
+
};
|
|
2216
|
+
} catch (error) {
|
|
2217
|
+
console.error(`[Worker:${id}] Error:`, error);
|
|
2218
|
+
const currentAssignment = state.activeAssignments.find(
|
|
2219
|
+
(assignment) => assignment.workerId === id
|
|
2220
|
+
);
|
|
2221
|
+
if (currentAssignment) {
|
|
2222
|
+
const errorResult = {
|
|
2223
|
+
assignmentId: currentAssignment.id,
|
|
2224
|
+
workerId: id,
|
|
2225
|
+
success: false,
|
|
2226
|
+
result: "",
|
|
2227
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
2228
|
+
completedAt: Date.now()
|
|
2229
|
+
};
|
|
2230
|
+
return {
|
|
2231
|
+
completedTasks: [errorResult],
|
|
2232
|
+
currentAgent: "supervisor",
|
|
2233
|
+
status: "routing"
|
|
2234
|
+
};
|
|
2235
|
+
}
|
|
2236
|
+
return {
|
|
2237
|
+
status: "failed",
|
|
2238
|
+
error: error instanceof Error ? error.message : `Unknown error in worker ${id}`
|
|
2239
|
+
};
|
|
2240
|
+
}
|
|
2241
|
+
};
|
|
2242
|
+
}
|
|
2243
|
+
function createAggregatorNode(config = {}) {
|
|
2244
|
+
const {
|
|
2245
|
+
llm,
|
|
2246
|
+
systemPrompt = DEFAULT_AGGREGATOR_SYSTEM_PROMPT,
|
|
2247
|
+
aggregateFn,
|
|
2248
|
+
verbose = false
|
|
2249
|
+
} = config;
|
|
2250
|
+
return async (state) => {
|
|
2251
|
+
try {
|
|
2252
|
+
if (verbose) {
|
|
2253
|
+
console.log("[Aggregator] Combining results from workers");
|
|
2254
|
+
}
|
|
2255
|
+
if (aggregateFn) {
|
|
2256
|
+
const response2 = await aggregateFn(state);
|
|
2257
|
+
return {
|
|
2258
|
+
response: response2,
|
|
2259
|
+
status: "completed"
|
|
2260
|
+
};
|
|
2261
|
+
}
|
|
2262
|
+
if (state.completedTasks.length === 0) {
|
|
2263
|
+
return {
|
|
2264
|
+
response: "No tasks were completed.",
|
|
2265
|
+
status: "completed"
|
|
2266
|
+
};
|
|
2267
|
+
}
|
|
2268
|
+
if (!llm) {
|
|
2269
|
+
const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
|
|
2270
|
+
return {
|
|
2271
|
+
response: combinedResults || "No successful results to aggregate.",
|
|
2272
|
+
status: "completed"
|
|
2273
|
+
};
|
|
2274
|
+
}
|
|
2275
|
+
const taskResults = state.completedTasks.map((task, idx) => {
|
|
2276
|
+
const status = task.success ? "\u2713" : "\u2717";
|
|
2277
|
+
const result = task.success ? task.result : `Error: ${task.error}`;
|
|
2278
|
+
return `${idx + 1}. [${status}] Worker ${task.workerId}:
|
|
2279
|
+
${result}`;
|
|
2280
|
+
}).join("\n\n");
|
|
2281
|
+
const userPrompt = `Original query: ${state.input}
|
|
2282
|
+
|
|
2283
|
+
Worker results:
|
|
2284
|
+
${taskResults}
|
|
2285
|
+
|
|
2286
|
+
Please synthesize these results into a comprehensive response that addresses the original query.`;
|
|
2287
|
+
const messages = [
|
|
2288
|
+
new import_messages5.SystemMessage(systemPrompt),
|
|
2289
|
+
new import_messages5.HumanMessage(userPrompt)
|
|
2290
|
+
];
|
|
2291
|
+
const response = await llm.invoke(messages);
|
|
2292
|
+
const aggregatedResponse = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
2293
|
+
if (verbose) {
|
|
2294
|
+
console.log("[Aggregator] Aggregation complete");
|
|
2295
|
+
}
|
|
2296
|
+
return {
|
|
2297
|
+
response: aggregatedResponse,
|
|
2298
|
+
status: "completed"
|
|
2299
|
+
};
|
|
2300
|
+
} catch (error) {
|
|
2301
|
+
console.error("[Aggregator] Error:", error);
|
|
2302
|
+
return {
|
|
2303
|
+
status: "failed",
|
|
2304
|
+
error: error instanceof Error ? error.message : "Unknown error in aggregator"
|
|
2305
|
+
};
|
|
2306
|
+
}
|
|
2307
|
+
};
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
// src/multi-agent/agent.ts
|
|
2311
|
+
var import_langgraph4 = require("@langchain/langgraph");
|
|
2312
|
+
function createMultiAgentSystem(config) {
|
|
2313
|
+
const {
|
|
2314
|
+
supervisor,
|
|
2315
|
+
workers,
|
|
2316
|
+
aggregator,
|
|
2317
|
+
maxIterations = 10,
|
|
2318
|
+
verbose = false
|
|
2319
|
+
} = config;
|
|
2320
|
+
const workflow = new import_langgraph4.StateGraph(MultiAgentState);
|
|
2321
|
+
const supervisorNode = createSupervisorNode({
|
|
2322
|
+
...supervisor,
|
|
2323
|
+
maxIterations,
|
|
2324
|
+
verbose
|
|
2325
|
+
});
|
|
2326
|
+
workflow.addNode("supervisor", supervisorNode);
|
|
2327
|
+
const workerIds = [];
|
|
2328
|
+
const workerCapabilities = {};
|
|
2329
|
+
for (const workerConfig of workers) {
|
|
2330
|
+
const workerNode = createWorkerNode({
|
|
2331
|
+
...workerConfig,
|
|
2332
|
+
verbose
|
|
2333
|
+
});
|
|
2334
|
+
workflow.addNode(workerConfig.id, workerNode);
|
|
2335
|
+
workerIds.push(workerConfig.id);
|
|
2336
|
+
workerCapabilities[workerConfig.id] = workerConfig.capabilities;
|
|
2337
|
+
}
|
|
2338
|
+
const aggregatorNode = createAggregatorNode({
|
|
2339
|
+
...aggregator,
|
|
2340
|
+
verbose
|
|
2341
|
+
});
|
|
2342
|
+
workflow.addNode("aggregator", aggregatorNode);
|
|
2343
|
+
const supervisorRouter = (state) => {
|
|
2344
|
+
if (state.status === "completed" || state.status === "failed") {
|
|
2345
|
+
return import_langgraph4.END;
|
|
2346
|
+
}
|
|
2347
|
+
if (state.status === "aggregating") {
|
|
2348
|
+
return "aggregator";
|
|
2349
|
+
}
|
|
2350
|
+
if (state.currentAgent && state.currentAgent !== "supervisor") {
|
|
2351
|
+
return state.currentAgent;
|
|
2352
|
+
}
|
|
2353
|
+
return "supervisor";
|
|
2354
|
+
};
|
|
2355
|
+
const workerRouter = (state) => {
|
|
2356
|
+
return "supervisor";
|
|
2357
|
+
};
|
|
2358
|
+
const aggregatorRouter = (state) => {
|
|
2359
|
+
return import_langgraph4.END;
|
|
2360
|
+
};
|
|
2361
|
+
workflow.setEntryPoint("supervisor");
|
|
2362
|
+
workflow.addConditionalEdges("supervisor", supervisorRouter, [
|
|
2363
|
+
"aggregator",
|
|
2364
|
+
import_langgraph4.END,
|
|
2365
|
+
...workerIds
|
|
2366
|
+
]);
|
|
2367
|
+
for (const workerId of workerIds) {
|
|
2368
|
+
workflow.addConditionalEdges(workerId, workerRouter, ["supervisor"]);
|
|
2369
|
+
}
|
|
2370
|
+
workflow.addConditionalEdges("aggregator", aggregatorRouter, [import_langgraph4.END]);
|
|
2371
|
+
const compiled = workflow.compile();
|
|
2372
|
+
const originalInvoke = compiled.invoke.bind(compiled);
|
|
2373
|
+
compiled.invoke = async function(input, config2) {
|
|
2374
|
+
const mergedInput = {
|
|
2375
|
+
...input,
|
|
2376
|
+
workers: {
|
|
2377
|
+
...workerCapabilities,
|
|
2378
|
+
...input.workers || {}
|
|
2379
|
+
}
|
|
2380
|
+
};
|
|
2381
|
+
return originalInvoke(mergedInput, config2);
|
|
2382
|
+
};
|
|
2383
|
+
return compiled;
|
|
2384
|
+
}
|
|
2385
|
+
var MultiAgentSystemBuilder = class {
|
|
2386
|
+
config;
|
|
2387
|
+
additionalWorkers = [];
|
|
2388
|
+
compiled = false;
|
|
2389
|
+
constructor(config) {
|
|
2390
|
+
this.config = {
|
|
2391
|
+
...config,
|
|
2392
|
+
workers: config.workers || []
|
|
2393
|
+
};
|
|
2394
|
+
}
|
|
2395
|
+
/**
|
|
2396
|
+
* Register workers with the system builder
|
|
2397
|
+
*
|
|
2398
|
+
* @param workers - Array of worker configurations
|
|
2399
|
+
* @returns this builder for chaining
|
|
2400
|
+
*
|
|
2401
|
+
* @example
|
|
2402
|
+
* ```typescript
|
|
2403
|
+
* const builder = new MultiAgentSystemBuilder({
|
|
2404
|
+
* supervisor: { llm, strategy: 'skill-based' },
|
|
2405
|
+
* aggregator: { llm },
|
|
2406
|
+
* });
|
|
2407
|
+
*
|
|
2408
|
+
* builder.registerWorkers([
|
|
2409
|
+
* {
|
|
2410
|
+
* name: 'math_worker',
|
|
2411
|
+
* capabilities: ['math', 'calculations'],
|
|
2412
|
+
* tools: [calculatorTool],
|
|
2413
|
+
* },
|
|
2414
|
+
* ]);
|
|
2415
|
+
*
|
|
2416
|
+
* const system = builder.build();
|
|
2417
|
+
* ```
|
|
2418
|
+
*/
|
|
2419
|
+
registerWorkers(workers) {
|
|
2420
|
+
if (this.compiled) {
|
|
2421
|
+
throw new Error("Cannot register workers after the system has been compiled");
|
|
2422
|
+
}
|
|
2423
|
+
for (const worker of workers) {
|
|
2424
|
+
this.additionalWorkers.push({
|
|
2425
|
+
id: worker.name,
|
|
2426
|
+
capabilities: {
|
|
2427
|
+
skills: worker.capabilities,
|
|
2428
|
+
tools: worker.tools?.map((t) => t.name || "unknown") || [],
|
|
2429
|
+
available: true,
|
|
2430
|
+
currentWorkload: 0
|
|
2431
|
+
},
|
|
2432
|
+
llm: worker.llm || this.config.supervisor.llm,
|
|
2433
|
+
tools: worker.tools,
|
|
2434
|
+
systemPrompt: worker.systemPrompt
|
|
2435
|
+
});
|
|
2436
|
+
}
|
|
2437
|
+
return this;
|
|
2438
|
+
}
|
|
2439
|
+
/**
|
|
2440
|
+
* Build and compile the multi-agent system
|
|
2441
|
+
*
|
|
2442
|
+
* @returns Compiled LangGraph workflow
|
|
2443
|
+
*/
|
|
2444
|
+
build() {
|
|
2445
|
+
if (this.compiled) {
|
|
2446
|
+
throw new Error("System has already been compiled");
|
|
2447
|
+
}
|
|
2448
|
+
const allWorkers = [...this.config.workers, ...this.additionalWorkers];
|
|
2449
|
+
if (allWorkers.length === 0) {
|
|
2450
|
+
throw new Error("At least one worker must be registered before building the system");
|
|
2451
|
+
}
|
|
2452
|
+
this.compiled = true;
|
|
2453
|
+
return createMultiAgentSystem({
|
|
2454
|
+
...this.config,
|
|
2455
|
+
workers: allWorkers
|
|
2456
|
+
});
|
|
2457
|
+
}
|
|
2458
|
+
};
|
|
2459
|
+
function registerWorkers(system, workers) {
|
|
2460
|
+
console.warn(
|
|
2461
|
+
"[AgentForge] registerWorkers() on a compiled system only updates worker capabilities in state.\nIt does NOT add worker nodes to the graph. Use MultiAgentSystemBuilder for proper worker registration.\nSee: https://github.com/agentforge/agentforge/blob/main/packages/patterns/docs/multi-agent-pattern.md"
|
|
2462
|
+
);
|
|
2463
|
+
if (!system._workerRegistry) {
|
|
2464
|
+
system._workerRegistry = {};
|
|
2465
|
+
}
|
|
2466
|
+
for (const worker of workers) {
|
|
2467
|
+
system._workerRegistry[worker.name] = {
|
|
2468
|
+
skills: worker.capabilities,
|
|
2469
|
+
tools: worker.tools?.map((t) => t.name || "unknown") || [],
|
|
2470
|
+
available: true,
|
|
2471
|
+
currentWorkload: 0
|
|
2472
|
+
};
|
|
2473
|
+
}
|
|
2474
|
+
if (!system._originalInvoke) {
|
|
2475
|
+
system._originalInvoke = system.invoke.bind(system);
|
|
2476
|
+
system.invoke = async function(input, config) {
|
|
2477
|
+
const mergedInput = {
|
|
2478
|
+
...input,
|
|
2479
|
+
workers: {
|
|
2480
|
+
...system._workerRegistry || {},
|
|
2481
|
+
...input.workers || {}
|
|
2482
|
+
}
|
|
2483
|
+
};
|
|
2484
|
+
return system._originalInvoke(mergedInput, config);
|
|
2485
|
+
};
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
2489
|
+
0 && (module.exports = {
|
|
2490
|
+
AgentMessageSchema,
|
|
2491
|
+
AgentRoleSchema,
|
|
2492
|
+
CompletedStepSchema,
|
|
2493
|
+
DEFAULT_AGGREGATOR_SYSTEM_PROMPT,
|
|
2494
|
+
DEFAULT_GENERATOR_SYSTEM_PROMPT,
|
|
2495
|
+
DEFAULT_PLANNER_SYSTEM_PROMPT,
|
|
2496
|
+
DEFAULT_REACT_SYSTEM_PROMPT,
|
|
2497
|
+
DEFAULT_REFLECTOR_SYSTEM_PROMPT,
|
|
2498
|
+
DEFAULT_REPLANNER_SYSTEM_PROMPT,
|
|
2499
|
+
DEFAULT_REVISER_SYSTEM_PROMPT,
|
|
2500
|
+
DEFAULT_SUPERVISOR_SYSTEM_PROMPT,
|
|
2501
|
+
ExecutionStatusSchema,
|
|
2502
|
+
GENERATION_PROMPT_TEMPLATE,
|
|
2503
|
+
HandoffRequestSchema,
|
|
2504
|
+
MessageSchema,
|
|
2505
|
+
MessageTypeSchema,
|
|
2506
|
+
MultiAgentState,
|
|
2507
|
+
MultiAgentStateConfig,
|
|
2508
|
+
MultiAgentStatusSchema,
|
|
2509
|
+
MultiAgentSystemBuilder,
|
|
2510
|
+
PLANNING_PROMPT_TEMPLATE,
|
|
2511
|
+
PlanExecuteState,
|
|
2512
|
+
PlanExecuteStateConfig,
|
|
2513
|
+
PlanSchema,
|
|
2514
|
+
PlanStepSchema,
|
|
2515
|
+
QUALITY_CRITERIA_TEMPLATE,
|
|
2516
|
+
QualityCriteriaSchema,
|
|
2517
|
+
REFLECTION_ENTRY_TEMPLATE,
|
|
2518
|
+
REFLECTION_HISTORY_TEMPLATE,
|
|
2519
|
+
REFLECTION_PROMPT_TEMPLATE,
|
|
2520
|
+
REPLANNING_PROMPT_TEMPLATE,
|
|
2521
|
+
REVISION_ENTRY_TEMPLATE,
|
|
2522
|
+
REVISION_PROMPT_TEMPLATE,
|
|
2523
|
+
ReActAgentBuilder,
|
|
2524
|
+
ReActState,
|
|
2525
|
+
ReflectionConfigSchema,
|
|
2526
|
+
ReflectionSchema,
|
|
2527
|
+
ReflectionState,
|
|
2528
|
+
ReflectionStateConfig,
|
|
2529
|
+
ReflectionStatusSchema,
|
|
2530
|
+
ReplanDecisionSchema,
|
|
2531
|
+
RevisionSchema,
|
|
2532
|
+
RoutingDecisionSchema,
|
|
2533
|
+
RoutingStrategySchema,
|
|
2534
|
+
ScratchpadEntrySchema,
|
|
2535
|
+
TaskAssignmentSchema,
|
|
2536
|
+
TaskResultSchema,
|
|
2537
|
+
ThoughtSchema,
|
|
2538
|
+
ToolCallSchema,
|
|
2539
|
+
ToolResultSchema,
|
|
2540
|
+
WorkerCapabilitiesSchema,
|
|
2541
|
+
createAggregatorNode,
|
|
2542
|
+
createExecutorNode,
|
|
2543
|
+
createFinisherNode,
|
|
2544
|
+
createGeneratorNode,
|
|
2545
|
+
createMultiAgentSystem,
|
|
2546
|
+
createPlanExecuteAgent,
|
|
2547
|
+
createPlannerNode,
|
|
2548
|
+
createReActAgent,
|
|
2549
|
+
createReActAgentBuilder,
|
|
2550
|
+
createReflectionAgent,
|
|
2551
|
+
createReflectionFinisherNode,
|
|
2552
|
+
createReflectorNode,
|
|
2553
|
+
createReplannerNode,
|
|
2554
|
+
createReviserNode,
|
|
2555
|
+
createSupervisorNode,
|
|
2556
|
+
createWorkerNode,
|
|
2557
|
+
getRoutingStrategy,
|
|
2558
|
+
llmBasedRouting,
|
|
2559
|
+
loadBalancedRouting,
|
|
2560
|
+
registerWorkers,
|
|
2561
|
+
roundRobinRouting,
|
|
2562
|
+
ruleBasedRouting,
|
|
2563
|
+
skillBasedRouting
|
|
2564
|
+
});
|