@agentforge/patterns 0.6.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +867 -332
- package/dist/index.d.cts +96 -2
- package/dist/index.d.ts +96 -2
- package/dist/index.js +852 -321
- package/package.json +4 -3
package/dist/index.cjs
CHANGED
|
@@ -71,11 +71,14 @@ __export(index_exports, {
|
|
|
71
71
|
ToolCallSchema: () => ToolCallSchema,
|
|
72
72
|
ToolResultSchema: () => ToolResultSchema,
|
|
73
73
|
WorkerCapabilitiesSchema: () => WorkerCapabilitiesSchema,
|
|
74
|
+
buildDeduplicationMetrics: () => buildDeduplicationMetrics,
|
|
75
|
+
calculateDeduplicationSavings: () => calculateDeduplicationSavings,
|
|
74
76
|
createAggregatorNode: () => createAggregatorNode,
|
|
75
77
|
createExecutorNode: () => createExecutorNode,
|
|
76
78
|
createFinisherNode: () => createFinisherNode,
|
|
77
79
|
createGeneratorNode: () => createGeneratorNode,
|
|
78
80
|
createMultiAgentSystem: () => createMultiAgentSystem,
|
|
81
|
+
createPatternLogger: () => createPatternLogger,
|
|
79
82
|
createPlanExecuteAgent: () => createPlanExecuteAgent,
|
|
80
83
|
createPlannerNode: () => createPlannerNode,
|
|
81
84
|
createReActAgent: () => createReActAgent,
|
|
@@ -87,6 +90,7 @@ __export(index_exports, {
|
|
|
87
90
|
createReviserNode: () => createReviserNode,
|
|
88
91
|
createSupervisorNode: () => createSupervisorNode,
|
|
89
92
|
createWorkerNode: () => createWorkerNode,
|
|
93
|
+
generateToolCallCacheKey: () => generateToolCallCacheKey,
|
|
90
94
|
getRoutingStrategy: () => getRoutingStrategy,
|
|
91
95
|
llmBasedRouting: () => llmBasedRouting,
|
|
92
96
|
loadBalancedRouting: () => loadBalancedRouting,
|
|
@@ -121,7 +125,9 @@ var ToolResultSchema = import_zod.z.object({
|
|
|
121
125
|
toolCallId: import_zod.z.string(),
|
|
122
126
|
result: import_zod.z.any(),
|
|
123
127
|
error: import_zod.z.string().optional(),
|
|
124
|
-
timestamp: import_zod.z.number().optional()
|
|
128
|
+
timestamp: import_zod.z.number().optional(),
|
|
129
|
+
isDuplicate: import_zod.z.boolean().optional()
|
|
130
|
+
// Flag indicating this was a duplicate tool call
|
|
125
131
|
});
|
|
126
132
|
var ScratchpadEntrySchema = import_zod.z.object({
|
|
127
133
|
step: import_zod.z.number(),
|
|
@@ -132,15 +138,46 @@ var ScratchpadEntrySchema = import_zod.z.object({
|
|
|
132
138
|
});
|
|
133
139
|
|
|
134
140
|
// src/react/state.ts
|
|
135
|
-
var
|
|
141
|
+
var import_zod3 = require("zod");
|
|
136
142
|
var import_core = require("@agentforge/core");
|
|
143
|
+
|
|
144
|
+
// src/shared/state-fields.ts
|
|
145
|
+
var import_zod2 = require("zod");
|
|
146
|
+
var iterationField = {
|
|
147
|
+
schema: import_zod2.z.number().int().nonnegative(),
|
|
148
|
+
reducer: (left, right) => left + right,
|
|
149
|
+
default: () => 0,
|
|
150
|
+
description: "Current iteration number"
|
|
151
|
+
};
|
|
152
|
+
function maxIterationsField(defaultValue) {
|
|
153
|
+
return {
|
|
154
|
+
schema: import_zod2.z.number().int().positive(),
|
|
155
|
+
default: () => defaultValue,
|
|
156
|
+
description: "Maximum number of iterations allowed"
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
var errorField = {
|
|
160
|
+
schema: import_zod2.z.string().optional(),
|
|
161
|
+
description: "Error message if execution failed"
|
|
162
|
+
};
|
|
163
|
+
var responseField = {
|
|
164
|
+
schema: import_zod2.z.string().optional(),
|
|
165
|
+
description: "Final response after completion"
|
|
166
|
+
};
|
|
167
|
+
var inputField = {
|
|
168
|
+
schema: import_zod2.z.string(),
|
|
169
|
+
default: () => "",
|
|
170
|
+
description: "Original user input or query"
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// src/react/state.ts
|
|
137
174
|
var ReActStateConfig = {
|
|
138
175
|
/**
|
|
139
176
|
* Conversation messages
|
|
140
177
|
* Accumulates all messages in the conversation
|
|
141
178
|
*/
|
|
142
179
|
messages: {
|
|
143
|
-
schema:
|
|
180
|
+
schema: import_zod3.z.array(MessageSchema),
|
|
144
181
|
reducer: (left, right) => [...left, ...right],
|
|
145
182
|
default: () => [],
|
|
146
183
|
description: "Conversation message history"
|
|
@@ -150,7 +187,7 @@ var ReActStateConfig = {
|
|
|
150
187
|
* Accumulates all reasoning steps the agent takes
|
|
151
188
|
*/
|
|
152
189
|
thoughts: {
|
|
153
|
-
schema:
|
|
190
|
+
schema: import_zod3.z.array(ThoughtSchema),
|
|
154
191
|
reducer: (left, right) => [...left, ...right],
|
|
155
192
|
default: () => [],
|
|
156
193
|
description: "Agent reasoning steps"
|
|
@@ -160,7 +197,7 @@ var ReActStateConfig = {
|
|
|
160
197
|
* Accumulates all tool calls made by the agent
|
|
161
198
|
*/
|
|
162
199
|
actions: {
|
|
163
|
-
schema:
|
|
200
|
+
schema: import_zod3.z.array(ToolCallSchema),
|
|
164
201
|
reducer: (left, right) => [...left, ...right],
|
|
165
202
|
default: () => [],
|
|
166
203
|
description: "Tool calls made by the agent"
|
|
@@ -170,7 +207,7 @@ var ReActStateConfig = {
|
|
|
170
207
|
* Accumulates all observations from tool executions
|
|
171
208
|
*/
|
|
172
209
|
observations: {
|
|
173
|
-
schema:
|
|
210
|
+
schema: import_zod3.z.array(ToolResultSchema),
|
|
174
211
|
reducer: (left, right) => [...left, ...right],
|
|
175
212
|
default: () => [],
|
|
176
213
|
description: "Results from tool executions"
|
|
@@ -180,7 +217,7 @@ var ReActStateConfig = {
|
|
|
180
217
|
* Accumulates step-by-step reasoning process
|
|
181
218
|
*/
|
|
182
219
|
scratchpad: {
|
|
183
|
-
schema:
|
|
220
|
+
schema: import_zod3.z.array(ScratchpadEntrySchema),
|
|
184
221
|
reducer: (left, right) => [...left, ...right],
|
|
185
222
|
default: () => [],
|
|
186
223
|
description: "Intermediate reasoning scratchpad"
|
|
@@ -189,27 +226,19 @@ var ReActStateConfig = {
|
|
|
189
226
|
* Current iteration count
|
|
190
227
|
* Tracks how many thought-action-observation loops have been executed
|
|
191
228
|
*/
|
|
192
|
-
iteration:
|
|
193
|
-
schema: import_zod2.z.number(),
|
|
194
|
-
reducer: (left, right) => left + right,
|
|
195
|
-
default: () => 0,
|
|
196
|
-
description: "Current iteration count"
|
|
197
|
-
},
|
|
229
|
+
iteration: iterationField,
|
|
198
230
|
/**
|
|
199
231
|
* Whether the agent should continue iterating
|
|
200
232
|
*/
|
|
201
233
|
shouldContinue: {
|
|
202
|
-
schema:
|
|
234
|
+
schema: import_zod3.z.boolean().optional(),
|
|
203
235
|
default: () => true,
|
|
204
236
|
description: "Whether to continue the ReAct loop"
|
|
205
237
|
},
|
|
206
238
|
/**
|
|
207
239
|
* Final response (if any)
|
|
208
240
|
*/
|
|
209
|
-
response:
|
|
210
|
-
schema: import_zod2.z.string().optional(),
|
|
211
|
-
description: "Final response from the agent"
|
|
212
|
-
}
|
|
241
|
+
response: responseField
|
|
213
242
|
};
|
|
214
243
|
var ReActState = (0, import_core.createStateAnnotation)(ReActStateConfig);
|
|
215
244
|
|
|
@@ -245,14 +274,68 @@ function formatScratchpad(scratchpad) {
|
|
|
245
274
|
|
|
246
275
|
// src/react/nodes.ts
|
|
247
276
|
var import_messages = require("@langchain/core/messages");
|
|
277
|
+
var import_core3 = require("@agentforge/core");
|
|
278
|
+
|
|
279
|
+
// src/shared/deduplication.ts
|
|
248
280
|
var import_core2 = require("@agentforge/core");
|
|
281
|
+
function generateToolCallCacheKey(toolName, args) {
|
|
282
|
+
const sortedArgs = JSON.stringify(args, Object.keys(args || {}).sort());
|
|
283
|
+
return `${toolName}:${sortedArgs}`;
|
|
284
|
+
}
|
|
285
|
+
function createPatternLogger(name, defaultLevel = "info") {
|
|
286
|
+
const logLevel5 = process.env.LOG_LEVEL?.toLowerCase() || defaultLevel;
|
|
287
|
+
return (0, import_core2.createLogger)(name, { level: logLevel5 });
|
|
288
|
+
}
|
|
289
|
+
function calculateDeduplicationSavings(duplicatesSkipped, toolsExecuted) {
|
|
290
|
+
if (duplicatesSkipped === 0) {
|
|
291
|
+
return "0%";
|
|
292
|
+
}
|
|
293
|
+
const total = toolsExecuted + duplicatesSkipped;
|
|
294
|
+
return `${Math.round(duplicatesSkipped / total * 100)}%`;
|
|
295
|
+
}
|
|
296
|
+
function buildDeduplicationMetrics(toolsExecuted, duplicatesSkipped, totalObservations) {
|
|
297
|
+
return {
|
|
298
|
+
toolsExecuted,
|
|
299
|
+
duplicatesSkipped,
|
|
300
|
+
totalObservations,
|
|
301
|
+
deduplicationSavings: calculateDeduplicationSavings(duplicatesSkipped, toolsExecuted)
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// src/shared/error-handling.ts
|
|
306
|
+
function isGraphInterrupt(error) {
|
|
307
|
+
return error !== null && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt";
|
|
308
|
+
}
|
|
309
|
+
function handleNodeError(error, context, verbose = false) {
|
|
310
|
+
if (isGraphInterrupt(error)) {
|
|
311
|
+
throw error;
|
|
312
|
+
}
|
|
313
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
314
|
+
if (verbose) {
|
|
315
|
+
console.error(`[${context}] Error:`, errorMessage);
|
|
316
|
+
if (error instanceof Error && error.stack) {
|
|
317
|
+
console.error(`[${context}] Stack:`, error.stack);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return errorMessage;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/react/nodes.ts
|
|
324
|
+
var reasoningLogger = createPatternLogger("agentforge:patterns:react:reasoning");
|
|
325
|
+
var actionLogger = createPatternLogger("agentforge:patterns:react:action");
|
|
326
|
+
var observationLogger = createPatternLogger("agentforge:patterns:react:observation");
|
|
249
327
|
function createReasoningNode(llm, tools, systemPrompt, maxIterations, verbose = false) {
|
|
250
|
-
const langchainTools = (0,
|
|
328
|
+
const langchainTools = (0, import_core3.toLangChainTools)(tools);
|
|
251
329
|
const llmWithTools = llm.bindTools ? llm.bindTools(langchainTools) : llm;
|
|
252
330
|
return async (state) => {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
331
|
+
const currentIteration = state.iteration || 0;
|
|
332
|
+
const startTime = Date.now();
|
|
333
|
+
reasoningLogger.debug("Reasoning iteration started", {
|
|
334
|
+
iteration: currentIteration + 1,
|
|
335
|
+
maxIterations,
|
|
336
|
+
observationCount: state.observations?.length || 0,
|
|
337
|
+
hasActions: !!state.actions?.length
|
|
338
|
+
});
|
|
256
339
|
const stateMessages = state.messages || [];
|
|
257
340
|
const messages = [
|
|
258
341
|
new import_messages.SystemMessage(systemPrompt),
|
|
@@ -282,8 +365,15 @@ ${scratchpadText}`));
|
|
|
282
365
|
});
|
|
283
366
|
}
|
|
284
367
|
}
|
|
285
|
-
const currentIteration = state.iteration || 0;
|
|
286
368
|
const shouldContinue = toolCalls.length > 0 && currentIteration + 1 < maxIterations;
|
|
369
|
+
reasoningLogger.info("Reasoning complete", {
|
|
370
|
+
iteration: currentIteration + 1,
|
|
371
|
+
thoughtGenerated: !!thought,
|
|
372
|
+
actionCount: toolCalls.length,
|
|
373
|
+
shouldContinue,
|
|
374
|
+
isFinalResponse: toolCalls.length === 0,
|
|
375
|
+
duration: Date.now() - startTime
|
|
376
|
+
});
|
|
287
377
|
return {
|
|
288
378
|
messages: [{ role: "assistant", content: thought }],
|
|
289
379
|
thoughts: thought ? [{ content: thought, timestamp: Date.now() }] : [],
|
|
@@ -296,16 +386,71 @@ ${scratchpadText}`));
|
|
|
296
386
|
};
|
|
297
387
|
};
|
|
298
388
|
}
|
|
299
|
-
function createActionNode(tools, verbose = false) {
|
|
389
|
+
function createActionNode(tools, verbose = false, enableDeduplication = true) {
|
|
300
390
|
const toolMap = new Map(tools.map((tool) => [tool.metadata.name, tool]));
|
|
301
391
|
return async (state) => {
|
|
302
392
|
const actions = state.actions || [];
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
393
|
+
const allObservations = state.observations || [];
|
|
394
|
+
const iteration = state.iteration || 0;
|
|
395
|
+
const startTime = Date.now();
|
|
396
|
+
actionLogger.debug("Action node started", {
|
|
397
|
+
actionCount: actions.length,
|
|
398
|
+
iteration,
|
|
399
|
+
cacheEnabled: enableDeduplication
|
|
400
|
+
});
|
|
306
401
|
const recentActions = actions.slice(-10);
|
|
307
402
|
const observations = [];
|
|
403
|
+
const executionCache = /* @__PURE__ */ new Map();
|
|
404
|
+
let cacheSize = 0;
|
|
405
|
+
if (enableDeduplication) {
|
|
406
|
+
for (const observation of allObservations) {
|
|
407
|
+
const correspondingAction = actions.find((a) => a.id === observation.toolCallId);
|
|
408
|
+
if (correspondingAction) {
|
|
409
|
+
const cacheKey = generateToolCallCacheKey(correspondingAction.name, correspondingAction.arguments);
|
|
410
|
+
executionCache.set(cacheKey, observation);
|
|
411
|
+
cacheSize++;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
if (cacheSize > 0) {
|
|
415
|
+
actionLogger.debug("Deduplication cache built", {
|
|
416
|
+
cacheSize,
|
|
417
|
+
totalObservations: allObservations.length
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
let duplicatesSkipped = 0;
|
|
422
|
+
let toolsExecuted = 0;
|
|
308
423
|
for (const action of recentActions) {
|
|
424
|
+
const existingObservation = allObservations.find((obs) => obs.toolCallId === action.id);
|
|
425
|
+
if (existingObservation) {
|
|
426
|
+
actionLogger.debug("Skipping already-processed action", {
|
|
427
|
+
toolName: action.name,
|
|
428
|
+
toolCallId: action.id,
|
|
429
|
+
iteration
|
|
430
|
+
});
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
if (enableDeduplication) {
|
|
434
|
+
const cacheKey = generateToolCallCacheKey(action.name, action.arguments);
|
|
435
|
+
const cachedResult = executionCache.get(cacheKey);
|
|
436
|
+
if (cachedResult) {
|
|
437
|
+
duplicatesSkipped++;
|
|
438
|
+
actionLogger.info("Duplicate tool call prevented", {
|
|
439
|
+
toolName: action.name,
|
|
440
|
+
arguments: action.arguments,
|
|
441
|
+
iteration,
|
|
442
|
+
cacheHit: true
|
|
443
|
+
});
|
|
444
|
+
observations.push({
|
|
445
|
+
toolCallId: action.id,
|
|
446
|
+
result: cachedResult.result,
|
|
447
|
+
error: cachedResult.error,
|
|
448
|
+
timestamp: Date.now(),
|
|
449
|
+
isDuplicate: true
|
|
450
|
+
});
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
309
454
|
const tool = toolMap.get(action.name);
|
|
310
455
|
if (!tool) {
|
|
311
456
|
observations.push({
|
|
@@ -317,31 +462,48 @@ function createActionNode(tools, verbose = false) {
|
|
|
317
462
|
continue;
|
|
318
463
|
}
|
|
319
464
|
try {
|
|
465
|
+
const startTime2 = Date.now();
|
|
320
466
|
const result = await tool.execute(action.arguments);
|
|
321
|
-
|
|
467
|
+
const executionTime = Date.now() - startTime2;
|
|
468
|
+
toolsExecuted++;
|
|
469
|
+
actionLogger.debug("Tool executed successfully", {
|
|
470
|
+
toolName: action.name,
|
|
471
|
+
executionTime,
|
|
472
|
+
iteration
|
|
473
|
+
});
|
|
474
|
+
const observation = {
|
|
322
475
|
toolCallId: action.id,
|
|
323
476
|
result,
|
|
324
477
|
timestamp: Date.now()
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
|
|
478
|
+
};
|
|
479
|
+
observations.push(observation);
|
|
480
|
+
if (enableDeduplication) {
|
|
481
|
+
const cacheKey = generateToolCallCacheKey(action.name, action.arguments);
|
|
482
|
+
executionCache.set(cacheKey, observation);
|
|
328
483
|
}
|
|
329
484
|
} catch (error) {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
485
|
+
const errorMessage = handleNodeError(error, `action:${action.name}`, false);
|
|
486
|
+
actionLogger.error("Tool execution failed", {
|
|
487
|
+
toolName: action.name,
|
|
488
|
+
error: errorMessage,
|
|
489
|
+
iteration
|
|
490
|
+
});
|
|
334
491
|
observations.push({
|
|
335
492
|
toolCallId: action.id,
|
|
336
493
|
result: null,
|
|
337
494
|
error: errorMessage,
|
|
338
495
|
timestamp: Date.now()
|
|
339
496
|
});
|
|
340
|
-
if (verbose) {
|
|
341
|
-
console.error(`[action] Tool '${action.name}' failed:`, errorMessage);
|
|
342
|
-
}
|
|
343
497
|
}
|
|
344
498
|
}
|
|
499
|
+
if (duplicatesSkipped > 0 || toolsExecuted > 0) {
|
|
500
|
+
const metrics = buildDeduplicationMetrics(toolsExecuted, duplicatesSkipped, observations.length);
|
|
501
|
+
actionLogger.info("Action node complete", {
|
|
502
|
+
iteration,
|
|
503
|
+
...metrics,
|
|
504
|
+
duration: Date.now() - startTime
|
|
505
|
+
});
|
|
506
|
+
}
|
|
345
507
|
return {
|
|
346
508
|
observations
|
|
347
509
|
};
|
|
@@ -352,9 +514,11 @@ function createObservationNode(verbose = false) {
|
|
|
352
514
|
const observations = state.observations || [];
|
|
353
515
|
const thoughts = state.thoughts || [];
|
|
354
516
|
const actions = state.actions || [];
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
517
|
+
const iteration = state.iteration || 0;
|
|
518
|
+
observationLogger.debug("Processing observations", {
|
|
519
|
+
observationCount: observations.length,
|
|
520
|
+
iteration
|
|
521
|
+
});
|
|
358
522
|
const recentObservations = observations.slice(-10);
|
|
359
523
|
const currentStep = state.iteration;
|
|
360
524
|
const latestThought = thoughts[thoughts.length - 1]?.content || "";
|
|
@@ -380,6 +544,11 @@ function createObservationNode(verbose = false) {
|
|
|
380
544
|
name: latestActions.find((a) => a.id === obs.toolCallId)?.name
|
|
381
545
|
};
|
|
382
546
|
});
|
|
547
|
+
observationLogger.debug("Observation node complete", {
|
|
548
|
+
iteration,
|
|
549
|
+
scratchpadUpdated: true,
|
|
550
|
+
messageCount: observationMessages.length
|
|
551
|
+
});
|
|
383
552
|
return {
|
|
384
553
|
scratchpad: [scratchpadEntry],
|
|
385
554
|
messages: observationMessages
|
|
@@ -389,7 +558,7 @@ function createObservationNode(verbose = false) {
|
|
|
389
558
|
|
|
390
559
|
// src/react/agent.ts
|
|
391
560
|
var import_langgraph = require("@langchain/langgraph");
|
|
392
|
-
var
|
|
561
|
+
var import_core4 = require("@agentforge/core");
|
|
393
562
|
function createReActAgent(config, options) {
|
|
394
563
|
const {
|
|
395
564
|
model,
|
|
@@ -398,13 +567,15 @@ function createReActAgent(config, options) {
|
|
|
398
567
|
maxIterations = 10,
|
|
399
568
|
returnIntermediateSteps = false,
|
|
400
569
|
stopCondition,
|
|
401
|
-
checkpointer
|
|
570
|
+
checkpointer,
|
|
571
|
+
enableDeduplication = true
|
|
572
|
+
// Enable by default
|
|
402
573
|
} = config;
|
|
403
574
|
const {
|
|
404
575
|
verbose = false,
|
|
405
576
|
nodeNames = {}
|
|
406
577
|
} = options || {};
|
|
407
|
-
const toolArray = tools instanceof
|
|
578
|
+
const toolArray = tools instanceof import_core4.ToolRegistry ? tools.getAll() : tools;
|
|
408
579
|
const REASONING_NODE = nodeNames.reasoning || "reasoning";
|
|
409
580
|
const ACTION_NODE = nodeNames.action || "action";
|
|
410
581
|
const OBSERVATION_NODE = nodeNames.observation || "observation";
|
|
@@ -415,7 +586,7 @@ function createReActAgent(config, options) {
|
|
|
415
586
|
maxIterations,
|
|
416
587
|
verbose
|
|
417
588
|
);
|
|
418
|
-
const actionNode = createActionNode(toolArray, verbose);
|
|
589
|
+
const actionNode = createActionNode(toolArray, verbose, enableDeduplication);
|
|
419
590
|
const observationNode = createObservationNode(verbose);
|
|
420
591
|
const shouldContinue = (state) => {
|
|
421
592
|
if (stopCondition && stopCondition(state)) {
|
|
@@ -550,34 +721,34 @@ function createReActAgentBuilder() {
|
|
|
550
721
|
var import_langgraph2 = require("@langchain/langgraph");
|
|
551
722
|
|
|
552
723
|
// src/plan-execute/state.ts
|
|
553
|
-
var
|
|
554
|
-
var
|
|
724
|
+
var import_zod5 = require("zod");
|
|
725
|
+
var import_core5 = require("@agentforge/core");
|
|
555
726
|
|
|
556
727
|
// src/plan-execute/schemas.ts
|
|
557
|
-
var
|
|
558
|
-
var PlanStepSchema =
|
|
728
|
+
var import_zod4 = require("zod");
|
|
729
|
+
var PlanStepSchema = import_zod4.z.object({
|
|
559
730
|
/**
|
|
560
731
|
* Unique identifier for the step
|
|
561
732
|
*/
|
|
562
|
-
id:
|
|
733
|
+
id: import_zod4.z.string().describe("Unique identifier for the step"),
|
|
563
734
|
/**
|
|
564
735
|
* Description of what this step should accomplish
|
|
565
736
|
*/
|
|
566
|
-
description:
|
|
737
|
+
description: import_zod4.z.string().describe("Description of what this step should accomplish"),
|
|
567
738
|
/**
|
|
568
739
|
* Optional dependencies on other steps (by ID)
|
|
569
740
|
*/
|
|
570
|
-
dependencies:
|
|
741
|
+
dependencies: import_zod4.z.array(import_zod4.z.string()).optional().describe("IDs of steps that must complete before this one"),
|
|
571
742
|
/**
|
|
572
743
|
* Optional tool to use for this step
|
|
573
744
|
*/
|
|
574
|
-
tool:
|
|
745
|
+
tool: import_zod4.z.string().optional().describe("Name of the tool to use for this step"),
|
|
575
746
|
/**
|
|
576
747
|
* Optional arguments for the tool
|
|
577
748
|
*/
|
|
578
|
-
args:
|
|
749
|
+
args: import_zod4.z.record(import_zod4.z.any()).optional().describe("Arguments to pass to the tool")
|
|
579
750
|
});
|
|
580
|
-
var CompletedStepSchema =
|
|
751
|
+
var CompletedStepSchema = import_zod4.z.object({
|
|
581
752
|
/**
|
|
582
753
|
* The step that was executed
|
|
583
754
|
*/
|
|
@@ -585,53 +756,53 @@ var CompletedStepSchema = import_zod3.z.object({
|
|
|
585
756
|
/**
|
|
586
757
|
* The result of executing the step
|
|
587
758
|
*/
|
|
588
|
-
result:
|
|
759
|
+
result: import_zod4.z.any().describe("The result of executing the step"),
|
|
589
760
|
/**
|
|
590
761
|
* Whether the step succeeded
|
|
591
762
|
*/
|
|
592
|
-
success:
|
|
763
|
+
success: import_zod4.z.boolean().describe("Whether the step succeeded"),
|
|
593
764
|
/**
|
|
594
765
|
* Optional error message if the step failed
|
|
595
766
|
*/
|
|
596
|
-
error:
|
|
767
|
+
error: import_zod4.z.string().optional().describe("Error message if the step failed"),
|
|
597
768
|
/**
|
|
598
769
|
* Timestamp when the step was completed
|
|
599
770
|
*/
|
|
600
|
-
timestamp:
|
|
771
|
+
timestamp: import_zod4.z.string().datetime().describe("ISO timestamp when the step was completed")
|
|
601
772
|
});
|
|
602
|
-
var PlanSchema =
|
|
773
|
+
var PlanSchema = import_zod4.z.object({
|
|
603
774
|
/**
|
|
604
775
|
* List of steps in the plan
|
|
605
776
|
*/
|
|
606
|
-
steps:
|
|
777
|
+
steps: import_zod4.z.array(PlanStepSchema).describe("List of steps in the plan"),
|
|
607
778
|
/**
|
|
608
779
|
* Overall goal of the plan
|
|
609
780
|
*/
|
|
610
|
-
goal:
|
|
781
|
+
goal: import_zod4.z.string().describe("Overall goal of the plan"),
|
|
611
782
|
/**
|
|
612
783
|
* Timestamp when the plan was created
|
|
613
784
|
*/
|
|
614
|
-
createdAt:
|
|
785
|
+
createdAt: import_zod4.z.string().datetime().describe("ISO timestamp when the plan was created"),
|
|
615
786
|
/**
|
|
616
787
|
* Optional confidence score (0-1)
|
|
617
788
|
*/
|
|
618
|
-
confidence:
|
|
789
|
+
confidence: import_zod4.z.number().min(0).max(1).optional().describe("Confidence score for the plan (0-1)")
|
|
619
790
|
});
|
|
620
|
-
var ReplanDecisionSchema =
|
|
791
|
+
var ReplanDecisionSchema = import_zod4.z.object({
|
|
621
792
|
/**
|
|
622
793
|
* Whether to replan
|
|
623
794
|
*/
|
|
624
|
-
shouldReplan:
|
|
795
|
+
shouldReplan: import_zod4.z.boolean().describe("Whether to replan based on current results"),
|
|
625
796
|
/**
|
|
626
797
|
* Reason for the decision
|
|
627
798
|
*/
|
|
628
|
-
reason:
|
|
799
|
+
reason: import_zod4.z.string().describe("Reason for the replan decision"),
|
|
629
800
|
/**
|
|
630
801
|
* Optional new goal if replanning
|
|
631
802
|
*/
|
|
632
|
-
newGoal:
|
|
803
|
+
newGoal: import_zod4.z.string().optional().describe("Updated goal if replanning")
|
|
633
804
|
});
|
|
634
|
-
var ExecutionStatusSchema =
|
|
805
|
+
var ExecutionStatusSchema = import_zod4.z.enum([
|
|
635
806
|
"planning",
|
|
636
807
|
"executing",
|
|
637
808
|
"replanning",
|
|
@@ -644,11 +815,7 @@ var PlanExecuteStateConfig = {
|
|
|
644
815
|
/**
|
|
645
816
|
* Original user input/query
|
|
646
817
|
*/
|
|
647
|
-
input:
|
|
648
|
-
schema: import_zod4.z.string(),
|
|
649
|
-
default: () => "",
|
|
650
|
-
description: "Original user input or query"
|
|
651
|
-
},
|
|
818
|
+
input: inputField,
|
|
652
819
|
/**
|
|
653
820
|
* The current plan
|
|
654
821
|
*/
|
|
@@ -661,7 +828,7 @@ var PlanExecuteStateConfig = {
|
|
|
661
828
|
* Accumulates all completed steps
|
|
662
829
|
*/
|
|
663
830
|
pastSteps: {
|
|
664
|
-
schema:
|
|
831
|
+
schema: import_zod5.z.array(CompletedStepSchema),
|
|
665
832
|
reducer: (left, right) => [...left, ...right],
|
|
666
833
|
default: () => [],
|
|
667
834
|
description: "Completed steps with their results"
|
|
@@ -670,7 +837,7 @@ var PlanExecuteStateConfig = {
|
|
|
670
837
|
* Index of the current step being executed
|
|
671
838
|
*/
|
|
672
839
|
currentStepIndex: {
|
|
673
|
-
schema:
|
|
840
|
+
schema: import_zod5.z.number().int().nonnegative().optional(),
|
|
674
841
|
description: "Index of the current step being executed"
|
|
675
842
|
},
|
|
676
843
|
/**
|
|
@@ -684,36 +851,21 @@ var PlanExecuteStateConfig = {
|
|
|
684
851
|
/**
|
|
685
852
|
* Final response
|
|
686
853
|
*/
|
|
687
|
-
response:
|
|
688
|
-
schema: import_zod4.z.string().optional(),
|
|
689
|
-
description: "Final response after plan execution"
|
|
690
|
-
},
|
|
854
|
+
response: responseField,
|
|
691
855
|
/**
|
|
692
856
|
* Error message if execution failed
|
|
693
857
|
*/
|
|
694
|
-
error:
|
|
695
|
-
schema: import_zod4.z.string().optional(),
|
|
696
|
-
description: "Error message if execution failed"
|
|
697
|
-
},
|
|
858
|
+
error: errorField,
|
|
698
859
|
/**
|
|
699
860
|
* Iteration counter for replanning
|
|
700
861
|
*/
|
|
701
|
-
iteration:
|
|
702
|
-
schema: import_zod4.z.number().int().nonnegative(),
|
|
703
|
-
reducer: (left, right) => left + right,
|
|
704
|
-
default: () => 0,
|
|
705
|
-
description: "Number of planning iterations"
|
|
706
|
-
},
|
|
862
|
+
iteration: iterationField,
|
|
707
863
|
/**
|
|
708
864
|
* Maximum iterations allowed
|
|
709
865
|
*/
|
|
710
|
-
maxIterations:
|
|
711
|
-
schema: import_zod4.z.number().int().positive(),
|
|
712
|
-
default: () => 5,
|
|
713
|
-
description: "Maximum number of planning iterations allowed"
|
|
714
|
-
}
|
|
866
|
+
maxIterations: maxIterationsField(5)
|
|
715
867
|
};
|
|
716
|
-
var PlanExecuteState = (0,
|
|
868
|
+
var PlanExecuteState = (0, import_core5.createStateAnnotation)(PlanExecuteStateConfig);
|
|
717
869
|
|
|
718
870
|
// src/plan-execute/nodes.ts
|
|
719
871
|
var import_messages2 = require("@langchain/core/messages");
|
|
@@ -778,6 +930,9 @@ var REMAINING_STEP_TEMPLATE = `Step {stepNumber}: {description}
|
|
|
778
930
|
{dependencies}`;
|
|
779
931
|
|
|
780
932
|
// src/plan-execute/nodes.ts
|
|
933
|
+
var plannerLogger = createPatternLogger("agentforge:patterns:plan-execute:planner");
|
|
934
|
+
var executorLogger = createPatternLogger("agentforge:patterns:plan-execute:executor");
|
|
935
|
+
var replannerLogger = createPatternLogger("agentforge:patterns:plan-execute:replanner");
|
|
781
936
|
function createPlannerNode(config) {
|
|
782
937
|
const {
|
|
783
938
|
model,
|
|
@@ -786,7 +941,13 @@ function createPlannerNode(config) {
|
|
|
786
941
|
includeToolDescriptions = false
|
|
787
942
|
} = config;
|
|
788
943
|
return async (state) => {
|
|
944
|
+
const startTime = Date.now();
|
|
789
945
|
try {
|
|
946
|
+
plannerLogger.debug("Planning started", {
|
|
947
|
+
input: state.input?.substring(0, 100),
|
|
948
|
+
maxSteps,
|
|
949
|
+
includeToolDescriptions
|
|
950
|
+
});
|
|
790
951
|
let toolDescriptions = "";
|
|
791
952
|
if (includeToolDescriptions) {
|
|
792
953
|
toolDescriptions = "";
|
|
@@ -811,6 +972,12 @@ function createPlannerNode(config) {
|
|
|
811
972
|
} catch (parseError) {
|
|
812
973
|
throw new Error(`Failed to parse plan from LLM response: ${parseError}`);
|
|
813
974
|
}
|
|
975
|
+
plannerLogger.info("Plan created", {
|
|
976
|
+
stepCount: plan.steps.length,
|
|
977
|
+
goal: plan.goal.substring(0, 100),
|
|
978
|
+
confidence: plan.confidence,
|
|
979
|
+
duration: Date.now() - startTime
|
|
980
|
+
});
|
|
814
981
|
return {
|
|
815
982
|
plan,
|
|
816
983
|
status: "executing",
|
|
@@ -818,6 +985,10 @@ function createPlannerNode(config) {
|
|
|
818
985
|
iteration: 1
|
|
819
986
|
};
|
|
820
987
|
} catch (error) {
|
|
988
|
+
plannerLogger.error("Planning failed", {
|
|
989
|
+
error: error instanceof Error ? error.message : String(error),
|
|
990
|
+
duration: Date.now() - startTime
|
|
991
|
+
});
|
|
821
992
|
return {
|
|
822
993
|
status: "failed",
|
|
823
994
|
error: error instanceof Error ? error.message : "Unknown error in planner"
|
|
@@ -830,11 +1001,18 @@ function createExecutorNode(config) {
|
|
|
830
1001
|
tools,
|
|
831
1002
|
model,
|
|
832
1003
|
parallel = false,
|
|
833
|
-
stepTimeout = 3e4
|
|
1004
|
+
stepTimeout = 3e4,
|
|
1005
|
+
enableDeduplication = true
|
|
834
1006
|
} = config;
|
|
835
1007
|
return async (state) => {
|
|
1008
|
+
const { plan, currentStepIndex = 0, pastSteps = [], iteration = 0 } = state;
|
|
836
1009
|
try {
|
|
837
|
-
|
|
1010
|
+
executorLogger.debug("Executor node executing", {
|
|
1011
|
+
currentStepIndex,
|
|
1012
|
+
totalSteps: plan?.steps?.length || 0,
|
|
1013
|
+
iteration,
|
|
1014
|
+
deduplicationEnabled: enableDeduplication
|
|
1015
|
+
});
|
|
838
1016
|
if (!plan || !plan.steps || plan.steps.length === 0) {
|
|
839
1017
|
return {
|
|
840
1018
|
status: "completed"
|
|
@@ -853,32 +1031,80 @@ function createExecutorNode(config) {
|
|
|
853
1031
|
throw new Error(`Unmet dependencies for step ${currentStep.id}: ${unmetDependencies.join(", ")}`);
|
|
854
1032
|
}
|
|
855
1033
|
}
|
|
1034
|
+
const executionCache = /* @__PURE__ */ new Map();
|
|
1035
|
+
let cacheSize = 0;
|
|
1036
|
+
if (enableDeduplication && currentStep.tool) {
|
|
1037
|
+
for (const pastStep of pastSteps) {
|
|
1038
|
+
if (pastStep.step.tool) {
|
|
1039
|
+
const cacheKey = generateToolCallCacheKey(pastStep.step.tool, pastStep.step.args || {});
|
|
1040
|
+
executionCache.set(cacheKey, pastStep);
|
|
1041
|
+
cacheSize++;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
if (cacheSize > 0) {
|
|
1045
|
+
executorLogger.debug("Deduplication cache built", {
|
|
1046
|
+
cacheSize,
|
|
1047
|
+
pastStepsCount: pastSteps.length
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
856
1051
|
let result;
|
|
857
1052
|
let success = true;
|
|
858
1053
|
let error;
|
|
1054
|
+
let isDuplicate = false;
|
|
859
1055
|
try {
|
|
860
1056
|
if (currentStep.tool) {
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
1057
|
+
if (enableDeduplication) {
|
|
1058
|
+
const cacheKey = generateToolCallCacheKey(currentStep.tool, currentStep.args || {});
|
|
1059
|
+
const cachedStep = executionCache.get(cacheKey);
|
|
1060
|
+
if (cachedStep) {
|
|
1061
|
+
isDuplicate = true;
|
|
1062
|
+
result = cachedStep.result;
|
|
1063
|
+
success = cachedStep.success;
|
|
1064
|
+
error = cachedStep.error;
|
|
1065
|
+
executorLogger.info("Duplicate step execution prevented", {
|
|
1066
|
+
stepId: currentStep.id,
|
|
1067
|
+
toolName: currentStep.tool,
|
|
1068
|
+
arguments: currentStep.args,
|
|
1069
|
+
iteration,
|
|
1070
|
+
cacheHit: true
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
if (!isDuplicate) {
|
|
1075
|
+
const tool = tools.find((t) => t.metadata.name === currentStep.tool);
|
|
1076
|
+
if (!tool) {
|
|
1077
|
+
throw new Error(`Tool not found: ${currentStep.tool}`);
|
|
1078
|
+
}
|
|
1079
|
+
const startTime = Date.now();
|
|
1080
|
+
const timeoutPromise = new Promise(
|
|
1081
|
+
(_, reject) => setTimeout(() => reject(new Error("Step execution timeout")), stepTimeout)
|
|
1082
|
+
);
|
|
1083
|
+
result = await Promise.race([
|
|
1084
|
+
tool.execute(currentStep.args || {}),
|
|
1085
|
+
timeoutPromise
|
|
1086
|
+
]);
|
|
1087
|
+
const executionTime = Date.now() - startTime;
|
|
1088
|
+
executorLogger.debug("Step executed successfully", {
|
|
1089
|
+
stepId: currentStep.id,
|
|
1090
|
+
toolName: currentStep.tool,
|
|
1091
|
+
executionTime,
|
|
1092
|
+
iteration
|
|
1093
|
+
});
|
|
864
1094
|
}
|
|
865
|
-
const timeoutPromise = new Promise(
|
|
866
|
-
(_, reject) => setTimeout(() => reject(new Error("Step execution timeout")), stepTimeout)
|
|
867
|
-
);
|
|
868
|
-
result = await Promise.race([
|
|
869
|
-
tool.execute(currentStep.args || {}),
|
|
870
|
-
timeoutPromise
|
|
871
|
-
]);
|
|
872
1095
|
} else {
|
|
873
1096
|
result = { message: "Step completed without tool execution" };
|
|
874
1097
|
}
|
|
875
1098
|
} catch (execError) {
|
|
876
|
-
|
|
877
|
-
throw execError;
|
|
878
|
-
}
|
|
1099
|
+
error = handleNodeError(execError, `executor:${currentStep.description}`, false);
|
|
879
1100
|
success = false;
|
|
880
|
-
error = execError instanceof Error ? execError.message : "Unknown execution error";
|
|
881
1101
|
result = null;
|
|
1102
|
+
executorLogger.warn("Step execution failed", {
|
|
1103
|
+
stepId: currentStep.id,
|
|
1104
|
+
toolName: currentStep.tool,
|
|
1105
|
+
error,
|
|
1106
|
+
iteration
|
|
1107
|
+
});
|
|
882
1108
|
}
|
|
883
1109
|
const completedStep = {
|
|
884
1110
|
step: currentStep,
|
|
@@ -887,11 +1113,23 @@ function createExecutorNode(config) {
|
|
|
887
1113
|
error,
|
|
888
1114
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
889
1115
|
};
|
|
1116
|
+
executorLogger.info("Executor node complete", {
|
|
1117
|
+
stepId: currentStep.id,
|
|
1118
|
+
stepIndex: currentStepIndex,
|
|
1119
|
+
totalSteps: plan.steps.length,
|
|
1120
|
+
success,
|
|
1121
|
+
isDuplicate,
|
|
1122
|
+
iteration
|
|
1123
|
+
});
|
|
890
1124
|
return {
|
|
891
1125
|
pastSteps: [completedStep],
|
|
892
1126
|
currentStepIndex: currentStepIndex + 1
|
|
893
1127
|
};
|
|
894
1128
|
} catch (error) {
|
|
1129
|
+
executorLogger.error("Executor node failed", {
|
|
1130
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
1131
|
+
iteration
|
|
1132
|
+
});
|
|
895
1133
|
return {
|
|
896
1134
|
status: "failed",
|
|
897
1135
|
error: error instanceof Error ? error.message : "Unknown error in executor"
|
|
@@ -906,11 +1144,18 @@ function createReplannerNode(config) {
|
|
|
906
1144
|
systemPrompt = DEFAULT_REPLANNER_SYSTEM_PROMPT
|
|
907
1145
|
} = config;
|
|
908
1146
|
return async (state) => {
|
|
1147
|
+
const startTime = Date.now();
|
|
909
1148
|
try {
|
|
910
1149
|
const { plan, pastSteps = [], currentStepIndex = 0 } = state;
|
|
911
1150
|
if (!plan) {
|
|
912
1151
|
return { status: "failed", error: "No plan available for replanning" };
|
|
913
1152
|
}
|
|
1153
|
+
replannerLogger.debug("Evaluating replanning", {
|
|
1154
|
+
completedSteps: pastSteps.length,
|
|
1155
|
+
remainingSteps: plan.steps.length - currentStepIndex,
|
|
1156
|
+
successfulSteps: pastSteps.filter((ps) => ps.success).length,
|
|
1157
|
+
failedSteps: pastSteps.filter((ps) => !ps.success).length
|
|
1158
|
+
});
|
|
914
1159
|
const completedStepsText = pastSteps.map(
|
|
915
1160
|
(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}`)
|
|
916
1161
|
).join("\n\n");
|
|
@@ -932,17 +1177,30 @@ function createReplannerNode(config) {
|
|
|
932
1177
|
throw new Error(`Failed to parse replan decision from LLM response: ${parseError}`);
|
|
933
1178
|
}
|
|
934
1179
|
if (decision.shouldReplan) {
|
|
1180
|
+
replannerLogger.info("Replanning triggered", {
|
|
1181
|
+
reason: decision.reason,
|
|
1182
|
+
newGoal: decision.newGoal?.substring(0, 100),
|
|
1183
|
+
duration: Date.now() - startTime
|
|
1184
|
+
});
|
|
935
1185
|
return {
|
|
936
1186
|
status: "planning",
|
|
937
1187
|
input: decision.newGoal || plan.goal,
|
|
938
1188
|
iteration: 1
|
|
939
1189
|
};
|
|
940
1190
|
} else {
|
|
1191
|
+
replannerLogger.info("Continuing with current plan", {
|
|
1192
|
+
reason: decision.reason,
|
|
1193
|
+
duration: Date.now() - startTime
|
|
1194
|
+
});
|
|
941
1195
|
return {
|
|
942
1196
|
status: "executing"
|
|
943
1197
|
};
|
|
944
1198
|
}
|
|
945
1199
|
} catch (error) {
|
|
1200
|
+
replannerLogger.error("Replanning evaluation failed", {
|
|
1201
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1202
|
+
duration: Date.now() - startTime
|
|
1203
|
+
});
|
|
946
1204
|
return {
|
|
947
1205
|
status: "failed",
|
|
948
1206
|
error: error instanceof Error ? error.message : "Unknown error in replanner"
|
|
@@ -1042,46 +1300,46 @@ function createPlanExecuteAgent(config) {
|
|
|
1042
1300
|
}
|
|
1043
1301
|
|
|
1044
1302
|
// src/reflection/state.ts
|
|
1045
|
-
var
|
|
1046
|
-
var
|
|
1303
|
+
var import_zod7 = require("zod");
|
|
1304
|
+
var import_core6 = require("@agentforge/core");
|
|
1047
1305
|
|
|
1048
1306
|
// src/reflection/schemas.ts
|
|
1049
|
-
var
|
|
1050
|
-
var ReflectionSchema =
|
|
1307
|
+
var import_zod6 = require("zod");
|
|
1308
|
+
var ReflectionSchema = import_zod6.z.object({
|
|
1051
1309
|
/**
|
|
1052
1310
|
* The critique or feedback on the current response
|
|
1053
1311
|
*/
|
|
1054
|
-
critique:
|
|
1312
|
+
critique: import_zod6.z.string().describe("Critique or feedback on the current response"),
|
|
1055
1313
|
/**
|
|
1056
1314
|
* Specific issues identified
|
|
1057
1315
|
*/
|
|
1058
|
-
issues:
|
|
1316
|
+
issues: import_zod6.z.array(import_zod6.z.string()).describe("Specific issues or problems identified"),
|
|
1059
1317
|
/**
|
|
1060
1318
|
* Suggestions for improvement
|
|
1061
1319
|
*/
|
|
1062
|
-
suggestions:
|
|
1320
|
+
suggestions: import_zod6.z.array(import_zod6.z.string()).describe("Suggestions for improving the response"),
|
|
1063
1321
|
/**
|
|
1064
1322
|
* Quality score (0-10)
|
|
1065
1323
|
*/
|
|
1066
|
-
score:
|
|
1324
|
+
score: import_zod6.z.number().min(0).max(10).optional().describe("Quality score from 0 to 10"),
|
|
1067
1325
|
/**
|
|
1068
1326
|
* Whether the response meets quality standards
|
|
1069
1327
|
*/
|
|
1070
|
-
meetsStandards:
|
|
1328
|
+
meetsStandards: import_zod6.z.boolean().describe("Whether the response meets quality standards"),
|
|
1071
1329
|
/**
|
|
1072
1330
|
* Timestamp of the reflection
|
|
1073
1331
|
*/
|
|
1074
|
-
timestamp:
|
|
1332
|
+
timestamp: import_zod6.z.date().optional().describe("When this reflection was created")
|
|
1075
1333
|
});
|
|
1076
|
-
var RevisionSchema =
|
|
1334
|
+
var RevisionSchema = import_zod6.z.object({
|
|
1077
1335
|
/**
|
|
1078
1336
|
* The revised content
|
|
1079
1337
|
*/
|
|
1080
|
-
content:
|
|
1338
|
+
content: import_zod6.z.string().describe("The revised content"),
|
|
1081
1339
|
/**
|
|
1082
1340
|
* Which iteration this revision is from
|
|
1083
1341
|
*/
|
|
1084
|
-
iteration:
|
|
1342
|
+
iteration: import_zod6.z.number().int().nonnegative().describe("Iteration number"),
|
|
1085
1343
|
/**
|
|
1086
1344
|
* The reflection that prompted this revision
|
|
1087
1345
|
*/
|
|
@@ -1089,9 +1347,9 @@ var RevisionSchema = import_zod5.z.object({
|
|
|
1089
1347
|
/**
|
|
1090
1348
|
* Timestamp of the revision
|
|
1091
1349
|
*/
|
|
1092
|
-
timestamp:
|
|
1350
|
+
timestamp: import_zod6.z.date().optional().describe("When this revision was created")
|
|
1093
1351
|
});
|
|
1094
|
-
var ReflectionStatusSchema =
|
|
1352
|
+
var ReflectionStatusSchema = import_zod6.z.enum([
|
|
1095
1353
|
"generating",
|
|
1096
1354
|
// Initial generation
|
|
1097
1355
|
"reflecting",
|
|
@@ -1103,25 +1361,25 @@ var ReflectionStatusSchema = import_zod5.z.enum([
|
|
|
1103
1361
|
"failed"
|
|
1104
1362
|
// Max iterations reached without meeting standards
|
|
1105
1363
|
]);
|
|
1106
|
-
var QualityCriteriaSchema =
|
|
1364
|
+
var QualityCriteriaSchema = import_zod6.z.object({
|
|
1107
1365
|
/**
|
|
1108
1366
|
* Minimum quality score required (0-10)
|
|
1109
1367
|
*/
|
|
1110
|
-
minScore:
|
|
1368
|
+
minScore: import_zod6.z.number().min(0).max(10).default(7).describe("Minimum quality score required"),
|
|
1111
1369
|
/**
|
|
1112
1370
|
* Specific criteria to evaluate
|
|
1113
1371
|
*/
|
|
1114
|
-
criteria:
|
|
1372
|
+
criteria: import_zod6.z.array(import_zod6.z.string()).optional().describe("Specific criteria to evaluate"),
|
|
1115
1373
|
/**
|
|
1116
1374
|
* Whether all criteria must be met
|
|
1117
1375
|
*/
|
|
1118
|
-
requireAll:
|
|
1376
|
+
requireAll: import_zod6.z.boolean().default(true).describe("Whether all criteria must be met")
|
|
1119
1377
|
});
|
|
1120
|
-
var ReflectionConfigSchema =
|
|
1378
|
+
var ReflectionConfigSchema = import_zod6.z.object({
|
|
1121
1379
|
/**
|
|
1122
1380
|
* Maximum number of reflection iterations
|
|
1123
1381
|
*/
|
|
1124
|
-
maxIterations:
|
|
1382
|
+
maxIterations: import_zod6.z.number().int().positive().default(3).describe("Maximum reflection iterations"),
|
|
1125
1383
|
/**
|
|
1126
1384
|
* Quality criteria for completion
|
|
1127
1385
|
*/
|
|
@@ -1129,7 +1387,7 @@ var ReflectionConfigSchema = import_zod5.z.object({
|
|
|
1129
1387
|
/**
|
|
1130
1388
|
* Whether to include previous reflections in context
|
|
1131
1389
|
*/
|
|
1132
|
-
includeHistory:
|
|
1390
|
+
includeHistory: import_zod6.z.boolean().default(true).describe("Include previous reflections in context")
|
|
1133
1391
|
});
|
|
1134
1392
|
|
|
1135
1393
|
// src/reflection/state.ts
|
|
@@ -1137,16 +1395,12 @@ var ReflectionStateConfig = {
|
|
|
1137
1395
|
/**
|
|
1138
1396
|
* Original user input/task
|
|
1139
1397
|
*/
|
|
1140
|
-
input:
|
|
1141
|
-
schema: import_zod6.z.string(),
|
|
1142
|
-
default: () => "",
|
|
1143
|
-
description: "Original user input or task"
|
|
1144
|
-
},
|
|
1398
|
+
input: inputField,
|
|
1145
1399
|
/**
|
|
1146
1400
|
* Current response/output
|
|
1147
1401
|
*/
|
|
1148
1402
|
currentResponse: {
|
|
1149
|
-
schema:
|
|
1403
|
+
schema: import_zod7.z.string().optional(),
|
|
1150
1404
|
description: "Current response or output"
|
|
1151
1405
|
},
|
|
1152
1406
|
/**
|
|
@@ -1154,7 +1408,7 @@ var ReflectionStateConfig = {
|
|
|
1154
1408
|
* Accumulates all reflections
|
|
1155
1409
|
*/
|
|
1156
1410
|
reflections: {
|
|
1157
|
-
schema:
|
|
1411
|
+
schema: import_zod7.z.array(ReflectionSchema),
|
|
1158
1412
|
reducer: (left, right) => [...left, ...right],
|
|
1159
1413
|
default: () => [],
|
|
1160
1414
|
description: "History of all reflections and critiques"
|
|
@@ -1164,7 +1418,7 @@ var ReflectionStateConfig = {
|
|
|
1164
1418
|
* Accumulates all revisions
|
|
1165
1419
|
*/
|
|
1166
1420
|
revisions: {
|
|
1167
|
-
schema:
|
|
1421
|
+
schema: import_zod7.z.array(RevisionSchema),
|
|
1168
1422
|
reducer: (left, right) => [...left, ...right],
|
|
1169
1423
|
default: () => [],
|
|
1170
1424
|
description: "History of all revisions"
|
|
@@ -1172,12 +1426,7 @@ var ReflectionStateConfig = {
|
|
|
1172
1426
|
/**
|
|
1173
1427
|
* Current iteration number
|
|
1174
1428
|
*/
|
|
1175
|
-
iteration:
|
|
1176
|
-
schema: import_zod6.z.number().int().nonnegative(),
|
|
1177
|
-
reducer: (left, right) => left + right,
|
|
1178
|
-
default: () => 0,
|
|
1179
|
-
description: "Current iteration number"
|
|
1180
|
-
},
|
|
1429
|
+
iteration: iterationField,
|
|
1181
1430
|
/**
|
|
1182
1431
|
* Current status
|
|
1183
1432
|
*/
|
|
@@ -1196,27 +1445,17 @@ var ReflectionStateConfig = {
|
|
|
1196
1445
|
/**
|
|
1197
1446
|
* Maximum iterations allowed
|
|
1198
1447
|
*/
|
|
1199
|
-
maxIterations:
|
|
1200
|
-
schema: import_zod6.z.number().int().positive(),
|
|
1201
|
-
default: () => 3,
|
|
1202
|
-
description: "Maximum number of reflection iterations allowed"
|
|
1203
|
-
},
|
|
1448
|
+
maxIterations: maxIterationsField(3),
|
|
1204
1449
|
/**
|
|
1205
1450
|
* Final response (when completed)
|
|
1206
1451
|
*/
|
|
1207
|
-
response:
|
|
1208
|
-
schema: import_zod6.z.string().optional(),
|
|
1209
|
-
description: "Final response after reflection process"
|
|
1210
|
-
},
|
|
1452
|
+
response: responseField,
|
|
1211
1453
|
/**
|
|
1212
1454
|
* Error message if failed
|
|
1213
1455
|
*/
|
|
1214
|
-
error:
|
|
1215
|
-
schema: import_zod6.z.string().optional(),
|
|
1216
|
-
description: "Error message if reflection failed"
|
|
1217
|
-
}
|
|
1456
|
+
error: errorField
|
|
1218
1457
|
};
|
|
1219
|
-
var ReflectionState = (0,
|
|
1458
|
+
var ReflectionState = (0, import_core6.createStateAnnotation)(ReflectionStateConfig);
|
|
1220
1459
|
|
|
1221
1460
|
// src/reflection/prompts.ts
|
|
1222
1461
|
var DEFAULT_GENERATOR_SYSTEM_PROMPT = `You are an expert content generator. Your task is to create high-quality responses to user requests.
|
|
@@ -1320,6 +1559,9 @@ var REVISION_ENTRY_TEMPLATE = `Iteration {iteration}:
|
|
|
1320
1559
|
|
|
1321
1560
|
// src/reflection/nodes.ts
|
|
1322
1561
|
var import_messages3 = require("@langchain/core/messages");
|
|
1562
|
+
var generatorLogger = createPatternLogger("agentforge:patterns:reflection:generator");
|
|
1563
|
+
var reflectorLogger = createPatternLogger("agentforge:patterns:reflection:reflector");
|
|
1564
|
+
var reviserLogger = createPatternLogger("agentforge:patterns:reflection:reviser");
|
|
1323
1565
|
function createGeneratorNode(config) {
|
|
1324
1566
|
const {
|
|
1325
1567
|
model,
|
|
@@ -1327,10 +1569,13 @@ function createGeneratorNode(config) {
|
|
|
1327
1569
|
verbose = false
|
|
1328
1570
|
} = config;
|
|
1329
1571
|
return async (state) => {
|
|
1572
|
+
const startTime = Date.now();
|
|
1330
1573
|
try {
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1574
|
+
generatorLogger.debug("Generating response", {
|
|
1575
|
+
attempt: state.iteration + 1,
|
|
1576
|
+
hasFeedback: state.reflections.length > 0,
|
|
1577
|
+
hasExistingResponse: !!state.currentResponse
|
|
1578
|
+
});
|
|
1334
1579
|
let context = "";
|
|
1335
1580
|
if (state.iteration > 0 && state.reflections.length > 0) {
|
|
1336
1581
|
const lastReflection = state.reflections[state.reflections.length - 1];
|
|
@@ -1345,19 +1590,27 @@ ${lastReflection.critique}`;
|
|
|
1345
1590
|
];
|
|
1346
1591
|
const response = await model.invoke(messages);
|
|
1347
1592
|
const content = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1593
|
+
generatorLogger.info("Response generated", {
|
|
1594
|
+
attempt: state.iteration + 1,
|
|
1595
|
+
responseLength: content.length,
|
|
1596
|
+
isRevision: state.iteration > 0,
|
|
1597
|
+
duration: Date.now() - startTime
|
|
1598
|
+
});
|
|
1351
1599
|
return {
|
|
1352
1600
|
currentResponse: content,
|
|
1353
1601
|
status: "reflecting",
|
|
1354
1602
|
iteration: 1
|
|
1355
1603
|
};
|
|
1356
1604
|
} catch (error) {
|
|
1357
|
-
|
|
1605
|
+
const errorMessage = handleNodeError(error, "generator", false);
|
|
1606
|
+
generatorLogger.error("Response generation failed", {
|
|
1607
|
+
attempt: state.iteration + 1,
|
|
1608
|
+
error: errorMessage,
|
|
1609
|
+
duration: Date.now() - startTime
|
|
1610
|
+
});
|
|
1358
1611
|
return {
|
|
1359
1612
|
status: "failed",
|
|
1360
|
-
error:
|
|
1613
|
+
error: errorMessage
|
|
1361
1614
|
};
|
|
1362
1615
|
}
|
|
1363
1616
|
};
|
|
@@ -1370,10 +1623,13 @@ function createReflectorNode(config) {
|
|
|
1370
1623
|
verbose = false
|
|
1371
1624
|
} = config;
|
|
1372
1625
|
return async (state) => {
|
|
1626
|
+
const startTime = Date.now();
|
|
1373
1627
|
try {
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1628
|
+
reflectorLogger.debug("Reflecting on response", {
|
|
1629
|
+
attempt: state.iteration,
|
|
1630
|
+
responseLength: state.currentResponse?.length || 0,
|
|
1631
|
+
hasCriteria: !!(qualityCriteria || state.qualityCriteria)
|
|
1632
|
+
});
|
|
1377
1633
|
if (!state.currentResponse) {
|
|
1378
1634
|
throw new Error("No current response to reflect on");
|
|
1379
1635
|
}
|
|
@@ -1423,19 +1679,28 @@ function createReflectorNode(config) {
|
|
|
1423
1679
|
meetsStandards: false
|
|
1424
1680
|
};
|
|
1425
1681
|
}
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1682
|
+
reflectorLogger.info("Reflection complete", {
|
|
1683
|
+
attempt: state.iteration,
|
|
1684
|
+
score: reflection.score,
|
|
1685
|
+
meetsStandards: reflection.meetsStandards,
|
|
1686
|
+
issueCount: reflection.issues.length,
|
|
1687
|
+
suggestionCount: reflection.suggestions.length,
|
|
1688
|
+
duration: Date.now() - startTime
|
|
1689
|
+
});
|
|
1430
1690
|
return {
|
|
1431
1691
|
reflections: [reflection],
|
|
1432
1692
|
status: reflection.meetsStandards ? "completed" : "revising"
|
|
1433
1693
|
};
|
|
1434
1694
|
} catch (error) {
|
|
1435
|
-
|
|
1695
|
+
const errorMessage = handleNodeError(error, "reflector", false);
|
|
1696
|
+
reflectorLogger.error("Reflection failed", {
|
|
1697
|
+
attempt: state.iteration,
|
|
1698
|
+
error: errorMessage,
|
|
1699
|
+
duration: Date.now() - startTime
|
|
1700
|
+
});
|
|
1436
1701
|
return {
|
|
1437
1702
|
status: "failed",
|
|
1438
|
-
error:
|
|
1703
|
+
error: errorMessage
|
|
1439
1704
|
};
|
|
1440
1705
|
}
|
|
1441
1706
|
};
|
|
@@ -1447,10 +1712,8 @@ function createReviserNode(config) {
|
|
|
1447
1712
|
verbose = false
|
|
1448
1713
|
} = config;
|
|
1449
1714
|
return async (state) => {
|
|
1715
|
+
const startTime = Date.now();
|
|
1450
1716
|
try {
|
|
1451
|
-
if (verbose) {
|
|
1452
|
-
console.log("[Reviser] Revising response...");
|
|
1453
|
-
}
|
|
1454
1717
|
if (!state.currentResponse) {
|
|
1455
1718
|
throw new Error("No current response to revise");
|
|
1456
1719
|
}
|
|
@@ -1458,6 +1721,12 @@ function createReviserNode(config) {
|
|
|
1458
1721
|
throw new Error("No reflections to base revision on");
|
|
1459
1722
|
}
|
|
1460
1723
|
const lastReflection = state.reflections[state.reflections.length - 1];
|
|
1724
|
+
reviserLogger.debug("Revising response", {
|
|
1725
|
+
attempt: state.iteration,
|
|
1726
|
+
previousScore: lastReflection.score,
|
|
1727
|
+
issueCount: lastReflection.issues.length,
|
|
1728
|
+
suggestionCount: lastReflection.suggestions.length
|
|
1729
|
+
});
|
|
1461
1730
|
let historySection = "";
|
|
1462
1731
|
if (state.revisions.length > 0) {
|
|
1463
1732
|
const revisionsText = state.revisions.map(
|
|
@@ -1474,14 +1743,17 @@ ${revisionsText}`;
|
|
|
1474
1743
|
];
|
|
1475
1744
|
const response = await model.invoke(messages);
|
|
1476
1745
|
const content = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
1477
|
-
if (verbose) {
|
|
1478
|
-
console.log("[Reviser] Created revision:", content.substring(0, 100) + "...");
|
|
1479
|
-
}
|
|
1480
1746
|
const revision = {
|
|
1481
1747
|
content,
|
|
1482
1748
|
iteration: state.iteration,
|
|
1483
1749
|
basedOn: lastReflection
|
|
1484
1750
|
};
|
|
1751
|
+
reviserLogger.info("Revision complete", {
|
|
1752
|
+
attempt: state.iteration,
|
|
1753
|
+
revisionLength: content.length,
|
|
1754
|
+
basedOnScore: lastReflection.score,
|
|
1755
|
+
duration: Date.now() - startTime
|
|
1756
|
+
});
|
|
1485
1757
|
return {
|
|
1486
1758
|
currentResponse: content,
|
|
1487
1759
|
revisions: [revision],
|
|
@@ -1489,10 +1761,15 @@ ${revisionsText}`;
|
|
|
1489
1761
|
iteration: 1
|
|
1490
1762
|
};
|
|
1491
1763
|
} catch (error) {
|
|
1492
|
-
|
|
1764
|
+
const errorMessage = handleNodeError(error, "reviser", false);
|
|
1765
|
+
reviserLogger.error("Revision failed", {
|
|
1766
|
+
attempt: state.iteration,
|
|
1767
|
+
error: errorMessage,
|
|
1768
|
+
duration: Date.now() - startTime
|
|
1769
|
+
});
|
|
1493
1770
|
return {
|
|
1494
1771
|
status: "failed",
|
|
1495
|
-
error:
|
|
1772
|
+
error: errorMessage
|
|
1496
1773
|
};
|
|
1497
1774
|
}
|
|
1498
1775
|
};
|
|
@@ -1578,13 +1855,13 @@ function createReflectionAgent(config) {
|
|
|
1578
1855
|
}
|
|
1579
1856
|
|
|
1580
1857
|
// src/multi-agent/state.ts
|
|
1581
|
-
var
|
|
1582
|
-
var
|
|
1858
|
+
var import_zod9 = require("zod");
|
|
1859
|
+
var import_core7 = require("@agentforge/core");
|
|
1583
1860
|
|
|
1584
1861
|
// src/multi-agent/schemas.ts
|
|
1585
|
-
var
|
|
1586
|
-
var AgentRoleSchema =
|
|
1587
|
-
var MessageTypeSchema =
|
|
1862
|
+
var import_zod8 = require("zod");
|
|
1863
|
+
var AgentRoleSchema = import_zod8.z.enum(["supervisor", "worker"]);
|
|
1864
|
+
var MessageTypeSchema = import_zod8.z.enum([
|
|
1588
1865
|
"user_input",
|
|
1589
1866
|
// Initial user message
|
|
1590
1867
|
"task_assignment",
|
|
@@ -1598,11 +1875,11 @@ var MessageTypeSchema = import_zod7.z.enum([
|
|
|
1598
1875
|
"completion"
|
|
1599
1876
|
// Final completion message
|
|
1600
1877
|
]);
|
|
1601
|
-
var AgentMessageSchema =
|
|
1878
|
+
var AgentMessageSchema = import_zod8.z.object({
|
|
1602
1879
|
/**
|
|
1603
1880
|
* Unique identifier for the message
|
|
1604
1881
|
*/
|
|
1605
|
-
id:
|
|
1882
|
+
id: import_zod8.z.string().describe("Unique message identifier"),
|
|
1606
1883
|
/**
|
|
1607
1884
|
* Type of message
|
|
1608
1885
|
*/
|
|
@@ -1610,25 +1887,25 @@ var AgentMessageSchema = import_zod7.z.object({
|
|
|
1610
1887
|
/**
|
|
1611
1888
|
* Agent that sent the message
|
|
1612
1889
|
*/
|
|
1613
|
-
from:
|
|
1890
|
+
from: import_zod8.z.string().describe("Agent identifier that sent the message"),
|
|
1614
1891
|
/**
|
|
1615
1892
|
* Agent(s) that should receive the message
|
|
1616
1893
|
*/
|
|
1617
|
-
to:
|
|
1894
|
+
to: import_zod8.z.union([import_zod8.z.string(), import_zod8.z.array(import_zod8.z.string())]).describe("Target agent(s)"),
|
|
1618
1895
|
/**
|
|
1619
1896
|
* Message content
|
|
1620
1897
|
*/
|
|
1621
|
-
content:
|
|
1898
|
+
content: import_zod8.z.string().describe("Message content"),
|
|
1622
1899
|
/**
|
|
1623
1900
|
* Optional metadata
|
|
1624
1901
|
*/
|
|
1625
|
-
metadata:
|
|
1902
|
+
metadata: import_zod8.z.record(import_zod8.z.any()).optional().describe("Additional message metadata"),
|
|
1626
1903
|
/**
|
|
1627
1904
|
* Timestamp when message was created
|
|
1628
1905
|
*/
|
|
1629
|
-
timestamp:
|
|
1906
|
+
timestamp: import_zod8.z.number().describe("Timestamp when message was created")
|
|
1630
1907
|
});
|
|
1631
|
-
var RoutingStrategySchema =
|
|
1908
|
+
var RoutingStrategySchema = import_zod8.z.enum([
|
|
1632
1909
|
"llm-based",
|
|
1633
1910
|
// LLM decides which agent to route to
|
|
1634
1911
|
"rule-based",
|
|
@@ -1640,25 +1917,25 @@ var RoutingStrategySchema = import_zod7.z.enum([
|
|
|
1640
1917
|
"load-balanced"
|
|
1641
1918
|
// Route based on agent workload
|
|
1642
1919
|
]);
|
|
1643
|
-
var RoutingDecisionSchema =
|
|
1920
|
+
var RoutingDecisionSchema = import_zod8.z.object({
|
|
1644
1921
|
/**
|
|
1645
1922
|
* Target agent to route to (single agent routing)
|
|
1646
1923
|
* @deprecated Use targetAgents for parallel routing support
|
|
1647
1924
|
*/
|
|
1648
|
-
targetAgent:
|
|
1925
|
+
targetAgent: import_zod8.z.string().nullable().default(null).describe("Agent to route the task to (single routing)"),
|
|
1649
1926
|
/**
|
|
1650
1927
|
* Target agents to route to (parallel routing)
|
|
1651
1928
|
* When multiple agents are specified, they execute in parallel
|
|
1652
1929
|
*/
|
|
1653
|
-
targetAgents:
|
|
1930
|
+
targetAgents: import_zod8.z.array(import_zod8.z.string()).nullable().default(null).describe("Agents to route the task to (parallel routing)"),
|
|
1654
1931
|
/**
|
|
1655
1932
|
* Reasoning for the routing decision
|
|
1656
1933
|
*/
|
|
1657
|
-
reasoning:
|
|
1934
|
+
reasoning: import_zod8.z.string().default("").describe("Explanation for routing decision"),
|
|
1658
1935
|
/**
|
|
1659
1936
|
* Confidence in the routing decision (0-1)
|
|
1660
1937
|
*/
|
|
1661
|
-
confidence:
|
|
1938
|
+
confidence: import_zod8.z.number().min(0).max(1).default(0.8).describe("Confidence score"),
|
|
1662
1939
|
/**
|
|
1663
1940
|
* Strategy used for routing
|
|
1664
1941
|
*/
|
|
@@ -1666,86 +1943,86 @@ var RoutingDecisionSchema = import_zod7.z.object({
|
|
|
1666
1943
|
/**
|
|
1667
1944
|
* Timestamp of the routing decision
|
|
1668
1945
|
*/
|
|
1669
|
-
timestamp:
|
|
1946
|
+
timestamp: import_zod8.z.number().default(() => Date.now()).describe("Timestamp of the decision")
|
|
1670
1947
|
}).refine(
|
|
1671
1948
|
(data) => data.targetAgent || data.targetAgents && data.targetAgents.length > 0,
|
|
1672
1949
|
{ message: "Either targetAgent or targetAgents must be provided" }
|
|
1673
1950
|
);
|
|
1674
|
-
var WorkerCapabilitiesSchema =
|
|
1951
|
+
var WorkerCapabilitiesSchema = import_zod8.z.object({
|
|
1675
1952
|
/**
|
|
1676
1953
|
* Skills/capabilities the agent has
|
|
1677
1954
|
*/
|
|
1678
|
-
skills:
|
|
1955
|
+
skills: import_zod8.z.array(import_zod8.z.string()).describe("List of agent skills"),
|
|
1679
1956
|
/**
|
|
1680
1957
|
* Tools available to the agent
|
|
1681
1958
|
*/
|
|
1682
|
-
tools:
|
|
1959
|
+
tools: import_zod8.z.array(import_zod8.z.string()).describe("List of tool names available to agent"),
|
|
1683
1960
|
/**
|
|
1684
1961
|
* Whether the agent is currently available
|
|
1685
1962
|
*/
|
|
1686
|
-
available:
|
|
1963
|
+
available: import_zod8.z.boolean().default(true).describe("Whether agent is available"),
|
|
1687
1964
|
/**
|
|
1688
1965
|
* Current workload (number of active tasks)
|
|
1689
1966
|
*/
|
|
1690
|
-
currentWorkload:
|
|
1967
|
+
currentWorkload: import_zod8.z.number().int().nonnegative().default(0).describe("Current number of active tasks")
|
|
1691
1968
|
});
|
|
1692
|
-
var TaskAssignmentSchema =
|
|
1969
|
+
var TaskAssignmentSchema = import_zod8.z.object({
|
|
1693
1970
|
/**
|
|
1694
1971
|
* Unique assignment identifier
|
|
1695
1972
|
*/
|
|
1696
|
-
id:
|
|
1973
|
+
id: import_zod8.z.string().describe("Unique assignment identifier"),
|
|
1697
1974
|
/**
|
|
1698
1975
|
* Worker ID assigned to the task
|
|
1699
1976
|
*/
|
|
1700
|
-
workerId:
|
|
1977
|
+
workerId: import_zod8.z.string().describe("Worker identifier assigned to task"),
|
|
1701
1978
|
/**
|
|
1702
1979
|
* Task description
|
|
1703
1980
|
*/
|
|
1704
|
-
task:
|
|
1981
|
+
task: import_zod8.z.string().describe("Description of the task"),
|
|
1705
1982
|
/**
|
|
1706
1983
|
* Task priority (1-10, higher is more urgent)
|
|
1707
1984
|
*/
|
|
1708
|
-
priority:
|
|
1985
|
+
priority: import_zod8.z.number().int().min(1).max(10).default(5).describe("Task priority"),
|
|
1709
1986
|
/**
|
|
1710
1987
|
* Timestamp when task was assigned
|
|
1711
1988
|
*/
|
|
1712
|
-
assignedAt:
|
|
1989
|
+
assignedAt: import_zod8.z.number().describe("Timestamp when task was assigned"),
|
|
1713
1990
|
/**
|
|
1714
1991
|
* Optional deadline for task completion
|
|
1715
1992
|
*/
|
|
1716
|
-
deadline:
|
|
1993
|
+
deadline: import_zod8.z.number().optional().describe("Optional task deadline timestamp")
|
|
1717
1994
|
});
|
|
1718
|
-
var TaskResultSchema =
|
|
1995
|
+
var TaskResultSchema = import_zod8.z.object({
|
|
1719
1996
|
/**
|
|
1720
1997
|
* Assignment identifier
|
|
1721
1998
|
*/
|
|
1722
|
-
assignmentId:
|
|
1999
|
+
assignmentId: import_zod8.z.string().describe("Assignment identifier"),
|
|
1723
2000
|
/**
|
|
1724
2001
|
* Worker that completed the task
|
|
1725
2002
|
*/
|
|
1726
|
-
workerId:
|
|
2003
|
+
workerId: import_zod8.z.string().describe("Worker that completed the task"),
|
|
1727
2004
|
/**
|
|
1728
2005
|
* Whether the task succeeded
|
|
1729
2006
|
*/
|
|
1730
|
-
success:
|
|
2007
|
+
success: import_zod8.z.boolean().describe("Whether the task succeeded"),
|
|
1731
2008
|
/**
|
|
1732
2009
|
* Task result/output
|
|
1733
2010
|
*/
|
|
1734
|
-
result:
|
|
2011
|
+
result: import_zod8.z.string().describe("Task result or output"),
|
|
1735
2012
|
/**
|
|
1736
2013
|
* Optional error message if task failed
|
|
1737
2014
|
*/
|
|
1738
|
-
error:
|
|
2015
|
+
error: import_zod8.z.string().optional().describe("Error message if task failed"),
|
|
1739
2016
|
/**
|
|
1740
2017
|
* Timestamp when task was completed
|
|
1741
2018
|
*/
|
|
1742
|
-
completedAt:
|
|
2019
|
+
completedAt: import_zod8.z.number().describe("Timestamp when task was completed"),
|
|
1743
2020
|
/**
|
|
1744
2021
|
* Optional metadata about execution
|
|
1745
2022
|
*/
|
|
1746
|
-
metadata:
|
|
2023
|
+
metadata: import_zod8.z.record(import_zod8.z.any()).optional().describe("Execution metadata")
|
|
1747
2024
|
});
|
|
1748
|
-
var MultiAgentStatusSchema =
|
|
2025
|
+
var MultiAgentStatusSchema = import_zod8.z.enum([
|
|
1749
2026
|
"initializing",
|
|
1750
2027
|
// System is initializing
|
|
1751
2028
|
"routing",
|
|
@@ -1761,27 +2038,27 @@ var MultiAgentStatusSchema = import_zod7.z.enum([
|
|
|
1761
2038
|
"failed"
|
|
1762
2039
|
// Task failed
|
|
1763
2040
|
]);
|
|
1764
|
-
var HandoffRequestSchema =
|
|
2041
|
+
var HandoffRequestSchema = import_zod8.z.object({
|
|
1765
2042
|
/**
|
|
1766
2043
|
* Agent requesting the handoff
|
|
1767
2044
|
*/
|
|
1768
|
-
from:
|
|
2045
|
+
from: import_zod8.z.string().describe("Agent requesting handoff"),
|
|
1769
2046
|
/**
|
|
1770
2047
|
* Target agent for handoff
|
|
1771
2048
|
*/
|
|
1772
|
-
to:
|
|
2049
|
+
to: import_zod8.z.string().describe("Target agent for handoff"),
|
|
1773
2050
|
/**
|
|
1774
2051
|
* Reason for handoff
|
|
1775
2052
|
*/
|
|
1776
|
-
reason:
|
|
2053
|
+
reason: import_zod8.z.string().describe("Reason for requesting handoff"),
|
|
1777
2054
|
/**
|
|
1778
2055
|
* Context to pass to next agent
|
|
1779
2056
|
*/
|
|
1780
|
-
context:
|
|
2057
|
+
context: import_zod8.z.any().describe("Context to pass to next agent"),
|
|
1781
2058
|
/**
|
|
1782
2059
|
* Timestamp of handoff request
|
|
1783
2060
|
*/
|
|
1784
|
-
timestamp:
|
|
2061
|
+
timestamp: import_zod8.z.string().datetime().describe("ISO timestamp of handoff request")
|
|
1785
2062
|
});
|
|
1786
2063
|
|
|
1787
2064
|
// src/multi-agent/state.ts
|
|
@@ -1789,17 +2066,13 @@ var MultiAgentStateConfig = {
|
|
|
1789
2066
|
/**
|
|
1790
2067
|
* Original user input/query
|
|
1791
2068
|
*/
|
|
1792
|
-
input:
|
|
1793
|
-
schema: import_zod8.z.string(),
|
|
1794
|
-
default: () => "",
|
|
1795
|
-
description: "Original user input or query"
|
|
1796
|
-
},
|
|
2069
|
+
input: inputField,
|
|
1797
2070
|
/**
|
|
1798
2071
|
* All messages in the multi-agent conversation
|
|
1799
2072
|
* Accumulates all messages between agents
|
|
1800
2073
|
*/
|
|
1801
2074
|
messages: {
|
|
1802
|
-
schema:
|
|
2075
|
+
schema: import_zod9.z.array(AgentMessageSchema),
|
|
1803
2076
|
reducer: (left, right) => [...left, ...right],
|
|
1804
2077
|
default: () => [],
|
|
1805
2078
|
description: "All messages in the multi-agent conversation"
|
|
@@ -1808,7 +2081,7 @@ var MultiAgentStateConfig = {
|
|
|
1808
2081
|
* Available worker agents and their capabilities
|
|
1809
2082
|
*/
|
|
1810
2083
|
workers: {
|
|
1811
|
-
schema:
|
|
2084
|
+
schema: import_zod9.z.record(import_zod9.z.string(), WorkerCapabilitiesSchema),
|
|
1812
2085
|
reducer: (left, right) => ({
|
|
1813
2086
|
...left,
|
|
1814
2087
|
...right
|
|
@@ -1820,7 +2093,7 @@ var MultiAgentStateConfig = {
|
|
|
1820
2093
|
* Current active agent
|
|
1821
2094
|
*/
|
|
1822
2095
|
currentAgent: {
|
|
1823
|
-
schema:
|
|
2096
|
+
schema: import_zod9.z.string().optional(),
|
|
1824
2097
|
description: "Identifier of the currently active agent"
|
|
1825
2098
|
},
|
|
1826
2099
|
/**
|
|
@@ -1828,7 +2101,7 @@ var MultiAgentStateConfig = {
|
|
|
1828
2101
|
* Accumulates all routing decisions
|
|
1829
2102
|
*/
|
|
1830
2103
|
routingHistory: {
|
|
1831
|
-
schema:
|
|
2104
|
+
schema: import_zod9.z.array(RoutingDecisionSchema),
|
|
1832
2105
|
reducer: (left, right) => [...left, ...right],
|
|
1833
2106
|
default: () => [],
|
|
1834
2107
|
description: "History of routing decisions"
|
|
@@ -1837,7 +2110,7 @@ var MultiAgentStateConfig = {
|
|
|
1837
2110
|
* Active task assignments
|
|
1838
2111
|
*/
|
|
1839
2112
|
activeAssignments: {
|
|
1840
|
-
schema:
|
|
2113
|
+
schema: import_zod9.z.array(TaskAssignmentSchema),
|
|
1841
2114
|
reducer: (left, right) => [...left, ...right],
|
|
1842
2115
|
default: () => [],
|
|
1843
2116
|
description: "Currently active task assignments"
|
|
@@ -1847,7 +2120,7 @@ var MultiAgentStateConfig = {
|
|
|
1847
2120
|
* Accumulates all completed tasks
|
|
1848
2121
|
*/
|
|
1849
2122
|
completedTasks: {
|
|
1850
|
-
schema:
|
|
2123
|
+
schema: import_zod9.z.array(TaskResultSchema),
|
|
1851
2124
|
reducer: (left, right) => [...left, ...right],
|
|
1852
2125
|
default: () => [],
|
|
1853
2126
|
description: "Completed task results"
|
|
@@ -1857,7 +2130,7 @@ var MultiAgentStateConfig = {
|
|
|
1857
2130
|
* Accumulates all handoff requests
|
|
1858
2131
|
*/
|
|
1859
2132
|
handoffs: {
|
|
1860
|
-
schema:
|
|
2133
|
+
schema: import_zod9.z.array(HandoffRequestSchema),
|
|
1861
2134
|
reducer: (left, right) => [...left, ...right],
|
|
1862
2135
|
default: () => [],
|
|
1863
2136
|
description: "Handoff requests between agents"
|
|
@@ -1873,44 +2146,40 @@ var MultiAgentStateConfig = {
|
|
|
1873
2146
|
/**
|
|
1874
2147
|
* Iteration counter
|
|
1875
2148
|
*/
|
|
1876
|
-
iteration:
|
|
1877
|
-
schema: import_zod8.z.number().int().nonnegative(),
|
|
1878
|
-
reducer: (left, right) => left + right,
|
|
1879
|
-
default: () => 0,
|
|
1880
|
-
description: "Current iteration number"
|
|
1881
|
-
},
|
|
2149
|
+
iteration: iterationField,
|
|
1882
2150
|
/**
|
|
1883
2151
|
* Maximum iterations allowed
|
|
1884
2152
|
*/
|
|
1885
|
-
maxIterations:
|
|
1886
|
-
schema: import_zod8.z.number().int().positive(),
|
|
1887
|
-
default: () => 10,
|
|
1888
|
-
description: "Maximum number of iterations allowed"
|
|
1889
|
-
},
|
|
2153
|
+
maxIterations: maxIterationsField(10),
|
|
1890
2154
|
/**
|
|
1891
2155
|
* Final aggregated response
|
|
1892
2156
|
*/
|
|
1893
|
-
response:
|
|
1894
|
-
schema: import_zod8.z.string().optional(),
|
|
1895
|
-
description: "Final aggregated response"
|
|
1896
|
-
},
|
|
2157
|
+
response: responseField,
|
|
1897
2158
|
/**
|
|
1898
2159
|
* Error message if execution failed
|
|
1899
2160
|
*/
|
|
1900
|
-
error:
|
|
1901
|
-
schema: import_zod8.z.string().optional(),
|
|
1902
|
-
description: "Error message if execution failed"
|
|
1903
|
-
}
|
|
2161
|
+
error: errorField
|
|
1904
2162
|
};
|
|
1905
|
-
var MultiAgentState = (0,
|
|
2163
|
+
var MultiAgentState = (0, import_core7.createStateAnnotation)(MultiAgentStateConfig);
|
|
1906
2164
|
|
|
1907
2165
|
// src/multi-agent/routing.ts
|
|
1908
2166
|
var import_messages4 = require("@langchain/core/messages");
|
|
2167
|
+
var import_core8 = require("@agentforge/core");
|
|
2168
|
+
var logLevel = process.env.LOG_LEVEL?.toLowerCase() || import_core8.LogLevel.INFO;
|
|
2169
|
+
var logger = (0, import_core8.createLogger)("multi-agent:routing", { level: logLevel });
|
|
1909
2170
|
async function executeTools(toolCalls, tools) {
|
|
1910
2171
|
const results = [];
|
|
2172
|
+
logger.debug("Executing tools", {
|
|
2173
|
+
toolCallCount: toolCalls.length,
|
|
2174
|
+
toolNames: toolCalls.map((tc) => tc.name)
|
|
2175
|
+
});
|
|
1911
2176
|
for (const toolCall of toolCalls) {
|
|
1912
2177
|
const tool = tools.find((t) => t.metadata.name === toolCall.name);
|
|
1913
2178
|
if (!tool) {
|
|
2179
|
+
logger.warn("Tool not found", {
|
|
2180
|
+
toolName: toolCall.name,
|
|
2181
|
+
availableTools: tools.map((t) => t.metadata.name)
|
|
2182
|
+
});
|
|
1914
2183
|
results.push(new import_messages4.ToolMessage({
|
|
1915
2184
|
content: `Error: Tool '${toolCall.name}' not found`,
|
|
1916
2185
|
tool_call_id: toolCall.id
|
|
@@ -1918,19 +2187,41 @@ async function executeTools(toolCalls, tools) {
|
|
|
1918
2187
|
continue;
|
|
1919
2188
|
}
|
|
1920
2189
|
try {
|
|
2190
|
+
logger.debug("Executing tool", {
|
|
2191
|
+
toolName: toolCall.name,
|
|
2192
|
+
args: toolCall.args
|
|
2193
|
+
});
|
|
1921
2194
|
const result = await tool.execute(toolCall.args);
|
|
1922
2195
|
const content = typeof result === "string" ? result : JSON.stringify(result);
|
|
2196
|
+
logger.debug("Tool execution successful", {
|
|
2197
|
+
toolName: toolCall.name,
|
|
2198
|
+
resultLength: content.length
|
|
2199
|
+
});
|
|
1923
2200
|
results.push(new import_messages4.ToolMessage({
|
|
1924
2201
|
content,
|
|
1925
2202
|
tool_call_id: toolCall.id
|
|
1926
2203
|
}));
|
|
1927
2204
|
} catch (error) {
|
|
2205
|
+
logger.error("Tool execution failed", {
|
|
2206
|
+
toolName: toolCall.name,
|
|
2207
|
+
error: error.message
|
|
2208
|
+
});
|
|
1928
2209
|
results.push(new import_messages4.ToolMessage({
|
|
1929
2210
|
content: `Error executing tool: ${error.message}`,
|
|
1930
2211
|
tool_call_id: toolCall.id
|
|
1931
2212
|
}));
|
|
1932
2213
|
}
|
|
1933
2214
|
}
|
|
2215
|
+
logger.debug("Tool execution complete", {
|
|
2216
|
+
successCount: results.filter((r) => {
|
|
2217
|
+
const content = typeof r.content === "string" ? r.content : JSON.stringify(r.content);
|
|
2218
|
+
return !content.startsWith("Error");
|
|
2219
|
+
}).length,
|
|
2220
|
+
errorCount: results.filter((r) => {
|
|
2221
|
+
const content = typeof r.content === "string" ? r.content : JSON.stringify(r.content);
|
|
2222
|
+
return content.startsWith("Error");
|
|
2223
|
+
}).length
|
|
2224
|
+
});
|
|
1934
2225
|
return results;
|
|
1935
2226
|
}
|
|
1936
2227
|
var DEFAULT_SUPERVISOR_SYSTEM_PROMPT = `You are a supervisor agent responsible for routing tasks to specialized worker agents.
|
|
@@ -1968,6 +2259,10 @@ Choose parallel routing when the task benefits from multiple perspectives or dat
|
|
|
1968
2259
|
var llmBasedRouting = {
|
|
1969
2260
|
name: "llm-based",
|
|
1970
2261
|
async route(state, config) {
|
|
2262
|
+
logger.info("Starting LLM-based routing", {
|
|
2263
|
+
iteration: state.iteration,
|
|
2264
|
+
availableWorkers: Object.keys(state.workers).length
|
|
2265
|
+
});
|
|
1971
2266
|
if (!config.model) {
|
|
1972
2267
|
throw new Error("LLM-based routing requires a model to be configured");
|
|
1973
2268
|
}
|
|
@@ -1980,8 +2275,20 @@ var llmBasedRouting = {
|
|
|
1980
2275
|
const available = caps.available ? "available" : "busy";
|
|
1981
2276
|
return `- ${id}: Skills: [${skills}], Tools: [${tools2}], Status: ${available}, Workload: ${caps.currentWorkload}`;
|
|
1982
2277
|
}).join("\n");
|
|
2278
|
+
logger.debug("Worker capabilities", {
|
|
2279
|
+
workers: Object.entries(state.workers).map(([id, caps]) => ({
|
|
2280
|
+
id,
|
|
2281
|
+
skills: caps.skills,
|
|
2282
|
+
available: caps.available,
|
|
2283
|
+
workload: caps.currentWorkload
|
|
2284
|
+
}))
|
|
2285
|
+
});
|
|
1983
2286
|
const lastMessage = state.messages[state.messages.length - 1];
|
|
1984
2287
|
const taskContext = lastMessage?.content || state.input;
|
|
2288
|
+
logger.debug("Task context", {
|
|
2289
|
+
taskLength: taskContext.length,
|
|
2290
|
+
taskPreview: taskContext.substring(0, 100)
|
|
2291
|
+
});
|
|
1985
2292
|
const userPrompt = `Current task: ${taskContext}
|
|
1986
2293
|
|
|
1987
2294
|
Available workers:
|
|
@@ -1991,6 +2298,11 @@ Select the best worker(s) for this task and explain your reasoning.`;
|
|
|
1991
2298
|
const conversationHistory = [];
|
|
1992
2299
|
let attempt = 0;
|
|
1993
2300
|
while (attempt < maxRetries) {
|
|
2301
|
+
logger.debug("LLM routing attempt", {
|
|
2302
|
+
attempt: attempt + 1,
|
|
2303
|
+
maxRetries,
|
|
2304
|
+
conversationHistoryLength: conversationHistory.length
|
|
2305
|
+
});
|
|
1994
2306
|
const messages = [
|
|
1995
2307
|
new import_messages4.SystemMessage(systemPrompt),
|
|
1996
2308
|
new import_messages4.HumanMessage(userPrompt),
|
|
@@ -1998,6 +2310,10 @@ Select the best worker(s) for this task and explain your reasoning.`;
|
|
|
1998
2310
|
];
|
|
1999
2311
|
const response = await config.model.invoke(messages);
|
|
2000
2312
|
if (response.tool_calls && response.tool_calls.length > 0) {
|
|
2313
|
+
logger.info("LLM requested tool calls", {
|
|
2314
|
+
toolCount: response.tool_calls.length,
|
|
2315
|
+
toolNames: response.tool_calls.map((tc) => tc.name)
|
|
2316
|
+
});
|
|
2001
2317
|
if (tools.length === 0) {
|
|
2002
2318
|
throw new Error("LLM requested tool calls but no tools are configured");
|
|
2003
2319
|
}
|
|
@@ -2007,24 +2323,42 @@ Select the best worker(s) for this task and explain your reasoning.`;
|
|
|
2007
2323
|
...toolResults
|
|
2008
2324
|
);
|
|
2009
2325
|
attempt++;
|
|
2326
|
+
logger.debug("Retrying routing with tool results", { attempt });
|
|
2010
2327
|
continue;
|
|
2011
2328
|
}
|
|
2329
|
+
logger.debug("Parsing routing decision from LLM response");
|
|
2012
2330
|
let decision;
|
|
2013
2331
|
if (response && typeof response === "object" && ("targetAgent" in response || "targetAgents" in response)) {
|
|
2332
|
+
logger.debug("Response is structured output", {
|
|
2333
|
+
hasTargetAgent: "targetAgent" in response,
|
|
2334
|
+
hasTargetAgents: "targetAgents" in response
|
|
2335
|
+
});
|
|
2014
2336
|
decision = response;
|
|
2015
2337
|
} else if (response.content) {
|
|
2016
2338
|
if (typeof response.content === "string") {
|
|
2017
2339
|
try {
|
|
2018
2340
|
decision = JSON.parse(response.content);
|
|
2341
|
+
logger.debug("Parsed JSON from string response");
|
|
2019
2342
|
} catch (error) {
|
|
2343
|
+
logger.error("Failed to parse routing decision", {
|
|
2344
|
+
content: response.content,
|
|
2345
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2346
|
+
});
|
|
2020
2347
|
throw new Error(`Failed to parse routing decision from LLM. Expected JSON but got: ${response.content}`);
|
|
2021
2348
|
}
|
|
2022
2349
|
} else if (typeof response.content === "object") {
|
|
2350
|
+
logger.debug("Response content is already an object");
|
|
2023
2351
|
decision = response.content;
|
|
2024
2352
|
} else {
|
|
2353
|
+
logger.error("Unexpected response content type", {
|
|
2354
|
+
type: typeof response.content
|
|
2355
|
+
});
|
|
2025
2356
|
throw new Error(`Unexpected response content type: ${typeof response.content}`);
|
|
2026
2357
|
}
|
|
2027
2358
|
} else {
|
|
2359
|
+
logger.error("Unexpected response format", {
|
|
2360
|
+
response: JSON.stringify(response)
|
|
2361
|
+
});
|
|
2028
2362
|
throw new Error(`Unexpected response format: ${JSON.stringify(response)}`);
|
|
2029
2363
|
}
|
|
2030
2364
|
const result = {
|
|
@@ -2035,20 +2369,41 @@ Select the best worker(s) for this task and explain your reasoning.`;
|
|
|
2035
2369
|
strategy: "llm-based",
|
|
2036
2370
|
timestamp: Date.now()
|
|
2037
2371
|
};
|
|
2372
|
+
logger.info("LLM routing decision made", {
|
|
2373
|
+
targetAgent: result.targetAgent,
|
|
2374
|
+
targetAgents: result.targetAgents,
|
|
2375
|
+
isParallel: result.targetAgents && result.targetAgents.length > 1,
|
|
2376
|
+
confidence: result.confidence,
|
|
2377
|
+
reasoning: result.reasoning
|
|
2378
|
+
});
|
|
2038
2379
|
return result;
|
|
2039
2380
|
}
|
|
2381
|
+
logger.error("Max tool retries exceeded", { maxRetries });
|
|
2040
2382
|
throw new Error(`Max tool retries (${maxRetries}) exceeded without routing decision`);
|
|
2041
2383
|
}
|
|
2042
2384
|
};
|
|
2043
2385
|
var roundRobinRouting = {
|
|
2044
2386
|
name: "round-robin",
|
|
2045
2387
|
async route(state, config) {
|
|
2388
|
+
logger.info("Starting round-robin routing", {
|
|
2389
|
+
iteration: state.iteration
|
|
2390
|
+
});
|
|
2046
2391
|
const availableWorkers = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id]) => id);
|
|
2392
|
+
logger.debug("Available workers for round-robin", {
|
|
2393
|
+
count: availableWorkers.length,
|
|
2394
|
+
workers: availableWorkers
|
|
2395
|
+
});
|
|
2047
2396
|
if (availableWorkers.length === 0) {
|
|
2397
|
+
logger.error("No available workers for round-robin routing");
|
|
2048
2398
|
throw new Error("No available workers for round-robin routing");
|
|
2049
2399
|
}
|
|
2050
2400
|
const lastRoutingIndex = state.routingHistory.length % availableWorkers.length;
|
|
2051
2401
|
const targetAgent = availableWorkers[lastRoutingIndex];
|
|
2402
|
+
logger.info("Round-robin routing decision", {
|
|
2403
|
+
targetAgent,
|
|
2404
|
+
index: lastRoutingIndex + 1,
|
|
2405
|
+
totalWorkers: availableWorkers.length
|
|
2406
|
+
});
|
|
2052
2407
|
return {
|
|
2053
2408
|
targetAgent,
|
|
2054
2409
|
targetAgents: null,
|
|
@@ -2062,8 +2417,15 @@ var roundRobinRouting = {
|
|
|
2062
2417
|
var skillBasedRouting = {
|
|
2063
2418
|
name: "skill-based",
|
|
2064
2419
|
async route(state, config) {
|
|
2420
|
+
logger.info("Starting skill-based routing", {
|
|
2421
|
+
iteration: state.iteration
|
|
2422
|
+
});
|
|
2065
2423
|
const lastMessage = state.messages[state.messages.length - 1];
|
|
2066
2424
|
const taskContent = (lastMessage?.content || state.input).toLowerCase();
|
|
2425
|
+
logger.debug("Task content for skill matching", {
|
|
2426
|
+
taskLength: taskContent.length,
|
|
2427
|
+
taskPreview: taskContent.substring(0, 100)
|
|
2428
|
+
});
|
|
2067
2429
|
const workerScores = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => {
|
|
2068
2430
|
const skillMatches = caps.skills.filter(
|
|
2069
2431
|
(skill) => taskContent.includes(skill.toLowerCase())
|
|
@@ -2074,11 +2436,20 @@ var skillBasedRouting = {
|
|
|
2074
2436
|
const score = skillMatches * 2 + toolMatches;
|
|
2075
2437
|
return { id, score, skills: caps.skills, tools: caps.tools };
|
|
2076
2438
|
}).filter((w) => w.score > 0).sort((a, b) => b.score - a.score);
|
|
2439
|
+
logger.debug("Worker skill scores", {
|
|
2440
|
+
scoredWorkers: workerScores.map((w) => ({ id: w.id, score: w.score }))
|
|
2441
|
+
});
|
|
2077
2442
|
if (workerScores.length === 0) {
|
|
2443
|
+
logger.warn("No skill matches found, using fallback");
|
|
2078
2444
|
const firstAvailable = Object.entries(state.workers).find(([_, caps]) => caps.available);
|
|
2079
2445
|
if (!firstAvailable) {
|
|
2446
|
+
logger.error("No available workers for skill-based routing");
|
|
2080
2447
|
throw new Error("No available workers for skill-based routing");
|
|
2081
2448
|
}
|
|
2449
|
+
logger.info("Skill-based routing fallback decision", {
|
|
2450
|
+
targetAgent: firstAvailable[0],
|
|
2451
|
+
confidence: 0.5
|
|
2452
|
+
});
|
|
2082
2453
|
return {
|
|
2083
2454
|
targetAgent: firstAvailable[0],
|
|
2084
2455
|
targetAgents: null,
|
|
@@ -2090,6 +2461,12 @@ var skillBasedRouting = {
|
|
|
2090
2461
|
}
|
|
2091
2462
|
const best = workerScores[0];
|
|
2092
2463
|
const confidence = Math.min(best.score / 5, 1);
|
|
2464
|
+
logger.info("Skill-based routing decision", {
|
|
2465
|
+
targetAgent: best.id,
|
|
2466
|
+
score: best.score,
|
|
2467
|
+
confidence,
|
|
2468
|
+
matchedSkills: best.skills
|
|
2469
|
+
});
|
|
2093
2470
|
return {
|
|
2094
2471
|
targetAgent: best.id,
|
|
2095
2472
|
targetAgents: null,
|
|
@@ -2103,13 +2480,26 @@ var skillBasedRouting = {
|
|
|
2103
2480
|
var loadBalancedRouting = {
|
|
2104
2481
|
name: "load-balanced",
|
|
2105
2482
|
async route(state, config) {
|
|
2483
|
+
logger.info("Starting load-balanced routing", {
|
|
2484
|
+
iteration: state.iteration
|
|
2485
|
+
});
|
|
2106
2486
|
const availableWorkers = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => ({ id, workload: caps.currentWorkload })).sort((a, b) => a.workload - b.workload);
|
|
2487
|
+
logger.debug("Worker workloads", {
|
|
2488
|
+
workers: availableWorkers.map((w) => ({ id: w.id, workload: w.workload }))
|
|
2489
|
+
});
|
|
2107
2490
|
if (availableWorkers.length === 0) {
|
|
2491
|
+
logger.error("No available workers for load-balanced routing");
|
|
2108
2492
|
throw new Error("No available workers for load-balanced routing");
|
|
2109
2493
|
}
|
|
2110
2494
|
const targetWorker = availableWorkers[0];
|
|
2111
2495
|
const avgWorkload = availableWorkers.reduce((sum, w) => sum + w.workload, 0) / availableWorkers.length;
|
|
2112
2496
|
const confidence = targetWorker.workload === 0 ? 1 : Math.max(0.5, 1 - targetWorker.workload / (avgWorkload * 2));
|
|
2497
|
+
logger.info("Load-balanced routing decision", {
|
|
2498
|
+
targetAgent: targetWorker.id,
|
|
2499
|
+
workload: targetWorker.workload,
|
|
2500
|
+
avgWorkload: avgWorkload.toFixed(1),
|
|
2501
|
+
confidence
|
|
2502
|
+
});
|
|
2113
2503
|
return {
|
|
2114
2504
|
targetAgent: targetWorker.id,
|
|
2115
2505
|
targetAgents: null,
|
|
@@ -2123,13 +2513,24 @@ var loadBalancedRouting = {
|
|
|
2123
2513
|
var ruleBasedRouting = {
|
|
2124
2514
|
name: "rule-based",
|
|
2125
2515
|
async route(state, config) {
|
|
2516
|
+
logger.info("Starting rule-based routing", {
|
|
2517
|
+
iteration: state.iteration
|
|
2518
|
+
});
|
|
2126
2519
|
if (!config.routingFn) {
|
|
2520
|
+
logger.error("Rule-based routing requires a custom routing function");
|
|
2127
2521
|
throw new Error("Rule-based routing requires a custom routing function");
|
|
2128
2522
|
}
|
|
2129
|
-
|
|
2523
|
+
const decision = await config.routingFn(state);
|
|
2524
|
+
logger.info("Rule-based routing decision", {
|
|
2525
|
+
targetAgent: decision.targetAgent,
|
|
2526
|
+
targetAgents: decision.targetAgents,
|
|
2527
|
+
confidence: decision.confidence
|
|
2528
|
+
});
|
|
2529
|
+
return decision;
|
|
2130
2530
|
}
|
|
2131
2531
|
};
|
|
2132
2532
|
function getRoutingStrategy(name) {
|
|
2533
|
+
logger.debug("Getting routing strategy", { name });
|
|
2133
2534
|
switch (name) {
|
|
2134
2535
|
case "llm-based":
|
|
2135
2536
|
return llmBasedRouting;
|
|
@@ -2142,14 +2543,15 @@ function getRoutingStrategy(name) {
|
|
|
2142
2543
|
case "rule-based":
|
|
2143
2544
|
return ruleBasedRouting;
|
|
2144
2545
|
default:
|
|
2546
|
+
logger.error("Unknown routing strategy", { name });
|
|
2145
2547
|
throw new Error(`Unknown routing strategy: ${name}`);
|
|
2146
2548
|
}
|
|
2147
2549
|
}
|
|
2148
2550
|
|
|
2149
2551
|
// src/multi-agent/utils.ts
|
|
2150
|
-
var
|
|
2151
|
-
var
|
|
2152
|
-
var
|
|
2552
|
+
var import_core9 = require("@agentforge/core");
|
|
2553
|
+
var logLevel2 = process.env.LOG_LEVEL?.toLowerCase() || import_core9.LogLevel.INFO;
|
|
2554
|
+
var logger2 = (0, import_core9.createLogger)("multi-agent", { level: logLevel2 });
|
|
2153
2555
|
function isReActAgent(obj) {
|
|
2154
2556
|
return obj && typeof obj === "object" && typeof obj.invoke === "function" && typeof obj.stream === "function" && // Additional check to ensure it's not just any object with invoke/stream
|
|
2155
2557
|
(obj.constructor?.name === "CompiledGraph" || obj.constructor?.name === "CompiledStateGraph");
|
|
@@ -2157,9 +2559,9 @@ function isReActAgent(obj) {
|
|
|
2157
2559
|
function wrapReActAgent(workerId, agent, verbose = false) {
|
|
2158
2560
|
return async (state, config) => {
|
|
2159
2561
|
try {
|
|
2160
|
-
|
|
2562
|
+
logger2.debug("Wrapping ReAct agent execution", { workerId });
|
|
2161
2563
|
const task = state.messages[state.messages.length - 1]?.content || state.input;
|
|
2162
|
-
|
|
2564
|
+
logger2.debug("Extracted task", {
|
|
2163
2565
|
workerId,
|
|
2164
2566
|
taskPreview: task.substring(0, 100) + (task.length > 100 ? "..." : "")
|
|
2165
2567
|
});
|
|
@@ -2167,7 +2569,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2167
2569
|
(assignment) => assignment.workerId === workerId && !state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
|
|
2168
2570
|
);
|
|
2169
2571
|
if (!currentAssignment) {
|
|
2170
|
-
|
|
2572
|
+
logger2.debug("No active assignment found", { workerId });
|
|
2171
2573
|
return {};
|
|
2172
2574
|
}
|
|
2173
2575
|
const result = await agent.invoke(
|
|
@@ -2178,14 +2580,14 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2178
2580
|
// Pass through the config for checkpointing and interrupt support
|
|
2179
2581
|
);
|
|
2180
2582
|
const response = result.messages?.[result.messages.length - 1]?.content || "No response";
|
|
2181
|
-
|
|
2583
|
+
logger2.debug("Received response from ReAct agent", {
|
|
2182
2584
|
workerId,
|
|
2183
2585
|
responsePreview: response.substring(0, 100) + (response.length > 100 ? "..." : "")
|
|
2184
2586
|
});
|
|
2185
2587
|
const toolsUsed = result.actions?.map((action) => action.name).filter(Boolean) || [];
|
|
2186
2588
|
const uniqueTools = [...new Set(toolsUsed)];
|
|
2187
2589
|
if (uniqueTools.length > 0) {
|
|
2188
|
-
|
|
2590
|
+
logger2.debug("Tools used by ReAct agent", { workerId, tools: uniqueTools });
|
|
2189
2591
|
}
|
|
2190
2592
|
const taskResult = {
|
|
2191
2593
|
assignmentId: currentAssignment.id,
|
|
@@ -2203,14 +2605,10 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2203
2605
|
completedTasks: [taskResult]
|
|
2204
2606
|
};
|
|
2205
2607
|
} catch (error) {
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
throw error;
|
|
2209
|
-
}
|
|
2210
|
-
logger.error("Error in ReAct agent execution", {
|
|
2608
|
+
const errorMessage = handleNodeError(error, `react-agent:${workerId}`, false);
|
|
2609
|
+
logger2.error("Error in ReAct agent execution", {
|
|
2211
2610
|
workerId,
|
|
2212
|
-
error:
|
|
2213
|
-
stack: error instanceof Error ? error.stack : void 0
|
|
2611
|
+
error: errorMessage
|
|
2214
2612
|
});
|
|
2215
2613
|
const currentAssignment = state.activeAssignments.find(
|
|
2216
2614
|
(assignment) => assignment.workerId === workerId
|
|
@@ -2221,7 +2619,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2221
2619
|
workerId,
|
|
2222
2620
|
success: false,
|
|
2223
2621
|
result: "",
|
|
2224
|
-
error:
|
|
2622
|
+
error: errorMessage,
|
|
2225
2623
|
completedAt: Date.now()
|
|
2226
2624
|
};
|
|
2227
2625
|
return {
|
|
@@ -2232,7 +2630,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2232
2630
|
}
|
|
2233
2631
|
return {
|
|
2234
2632
|
status: "failed",
|
|
2235
|
-
error:
|
|
2633
|
+
error: errorMessage
|
|
2236
2634
|
};
|
|
2237
2635
|
}
|
|
2238
2636
|
};
|
|
@@ -2240,7 +2638,9 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2240
2638
|
|
|
2241
2639
|
// src/multi-agent/nodes.ts
|
|
2242
2640
|
var import_messages5 = require("@langchain/core/messages");
|
|
2243
|
-
var
|
|
2641
|
+
var import_core10 = require("@agentforge/core");
|
|
2642
|
+
var logLevel3 = process.env.LOG_LEVEL?.toLowerCase() || import_core10.LogLevel.INFO;
|
|
2643
|
+
var logger3 = (0, import_core10.createLogger)("multi-agent:nodes", { level: logLevel3 });
|
|
2244
2644
|
var DEFAULT_AGGREGATOR_SYSTEM_PROMPT = `You are an aggregator agent responsible for combining results from multiple worker agents.
|
|
2245
2645
|
|
|
2246
2646
|
Your job is to:
|
|
@@ -2258,13 +2658,19 @@ function createSupervisorNode(config) {
|
|
|
2258
2658
|
} = config;
|
|
2259
2659
|
return async (state) => {
|
|
2260
2660
|
try {
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2661
|
+
logger3.info("Supervisor node executing", {
|
|
2662
|
+
iteration: state.iteration,
|
|
2663
|
+
maxIterations,
|
|
2664
|
+
activeAssignments: state.activeAssignments.length,
|
|
2665
|
+
completedTasks: state.completedTasks.length
|
|
2666
|
+
});
|
|
2667
|
+
logger3.debug(`Routing iteration ${state.iteration}/${maxIterations}`);
|
|
2264
2668
|
if (state.iteration >= maxIterations) {
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2669
|
+
logger3.warn("Max iterations reached", {
|
|
2670
|
+
iteration: state.iteration,
|
|
2671
|
+
maxIterations
|
|
2672
|
+
});
|
|
2673
|
+
logger3.debug("Max iterations reached, moving to aggregation");
|
|
2268
2674
|
return {
|
|
2269
2675
|
status: "aggregating",
|
|
2270
2676
|
currentAgent: "aggregator"
|
|
@@ -2273,27 +2679,55 @@ function createSupervisorNode(config) {
|
|
|
2273
2679
|
const allCompleted = state.activeAssignments.every(
|
|
2274
2680
|
(assignment) => state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
|
|
2275
2681
|
);
|
|
2682
|
+
logger3.debug("Checking task completion", {
|
|
2683
|
+
activeAssignments: state.activeAssignments.length,
|
|
2684
|
+
completedTasks: state.completedTasks.length,
|
|
2685
|
+
allCompleted
|
|
2686
|
+
});
|
|
2276
2687
|
if (allCompleted && state.activeAssignments.length > 0) {
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
}
|
|
2688
|
+
logger3.info("All tasks completed, moving to aggregation", {
|
|
2689
|
+
completedCount: state.completedTasks.length
|
|
2690
|
+
});
|
|
2691
|
+
logger3.debug("All tasks completed, moving to aggregation");
|
|
2280
2692
|
return {
|
|
2281
2693
|
status: "aggregating",
|
|
2282
2694
|
currentAgent: "aggregator"
|
|
2283
2695
|
};
|
|
2284
2696
|
}
|
|
2697
|
+
logger3.debug("Getting routing strategy", { strategy });
|
|
2285
2698
|
const routingImpl = getRoutingStrategy(strategy);
|
|
2286
2699
|
const decision = await routingImpl.route(state, config);
|
|
2287
2700
|
const targetAgents = decision.targetAgents && decision.targetAgents.length > 0 ? decision.targetAgents : decision.targetAgent ? [decision.targetAgent] : [];
|
|
2701
|
+
logger3.debug("Target agents determined", {
|
|
2702
|
+
targetAgents,
|
|
2703
|
+
isParallel: targetAgents.length > 1,
|
|
2704
|
+
decision: {
|
|
2705
|
+
reasoning: decision.reasoning,
|
|
2706
|
+
confidence: decision.confidence
|
|
2707
|
+
}
|
|
2708
|
+
});
|
|
2288
2709
|
if (targetAgents.length === 0) {
|
|
2710
|
+
logger3.error("No target agents specified in routing decision");
|
|
2289
2711
|
throw new Error("Routing decision must specify at least one target agent");
|
|
2290
2712
|
}
|
|
2291
|
-
if (
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
}
|
|
2713
|
+
if (targetAgents.length === 1) {
|
|
2714
|
+
logger3.info("Routing to single agent", {
|
|
2715
|
+
targetAgent: targetAgents[0],
|
|
2716
|
+
reasoning: decision.reasoning,
|
|
2717
|
+
confidence: decision.confidence
|
|
2718
|
+
});
|
|
2719
|
+
} else {
|
|
2720
|
+
logger3.info("Routing to multiple agents in parallel", {
|
|
2721
|
+
targetAgents,
|
|
2722
|
+
count: targetAgents.length,
|
|
2723
|
+
reasoning: decision.reasoning,
|
|
2724
|
+
confidence: decision.confidence
|
|
2725
|
+
});
|
|
2726
|
+
}
|
|
2727
|
+
if (targetAgents.length === 1) {
|
|
2728
|
+
logger3.debug(`Routing to ${targetAgents[0]}: ${decision.reasoning}`);
|
|
2729
|
+
} else {
|
|
2730
|
+
logger3.debug(`Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`);
|
|
2297
2731
|
}
|
|
2298
2732
|
const task = state.messages[state.messages.length - 1]?.content || state.input;
|
|
2299
2733
|
const assignments = targetAgents.map((workerId) => ({
|
|
@@ -2303,6 +2737,14 @@ function createSupervisorNode(config) {
|
|
|
2303
2737
|
priority: 5,
|
|
2304
2738
|
assignedAt: Date.now()
|
|
2305
2739
|
}));
|
|
2740
|
+
logger3.debug("Created task assignments", {
|
|
2741
|
+
assignmentCount: assignments.length,
|
|
2742
|
+
assignments: assignments.map((a) => ({
|
|
2743
|
+
id: a.id,
|
|
2744
|
+
workerId: a.workerId,
|
|
2745
|
+
taskLength: a.task.length
|
|
2746
|
+
}))
|
|
2747
|
+
});
|
|
2306
2748
|
const messages = assignments.map((assignment) => ({
|
|
2307
2749
|
id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
2308
2750
|
from: "supervisor",
|
|
@@ -2315,6 +2757,12 @@ function createSupervisorNode(config) {
|
|
|
2315
2757
|
priority: assignment.priority
|
|
2316
2758
|
}
|
|
2317
2759
|
}));
|
|
2760
|
+
logger3.info("Supervisor routing complete", {
|
|
2761
|
+
currentAgent: targetAgents.join(","),
|
|
2762
|
+
status: "executing",
|
|
2763
|
+
assignmentCount: assignments.length,
|
|
2764
|
+
nextIteration: state.iteration + 1
|
|
2765
|
+
});
|
|
2318
2766
|
return {
|
|
2319
2767
|
currentAgent: targetAgents.join(","),
|
|
2320
2768
|
// Store all agents (for backward compat)
|
|
@@ -2326,7 +2774,11 @@ function createSupervisorNode(config) {
|
|
|
2326
2774
|
iteration: state.iteration + 1
|
|
2327
2775
|
};
|
|
2328
2776
|
} catch (error) {
|
|
2329
|
-
|
|
2777
|
+
logger3.error("Supervisor node error", {
|
|
2778
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2779
|
+
stack: error instanceof Error ? error.stack : void 0,
|
|
2780
|
+
iteration: state.iteration
|
|
2781
|
+
});
|
|
2330
2782
|
return {
|
|
2331
2783
|
status: "failed",
|
|
2332
2784
|
error: error instanceof Error ? error.message : "Unknown error in supervisor"
|
|
@@ -2347,40 +2799,52 @@ function createWorkerNode(config) {
|
|
|
2347
2799
|
} = config;
|
|
2348
2800
|
return async (state, runConfig) => {
|
|
2349
2801
|
try {
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2802
|
+
logger3.info("Worker node executing", {
|
|
2803
|
+
workerId: id,
|
|
2804
|
+
iteration: state.iteration,
|
|
2805
|
+
activeAssignments: state.activeAssignments.length
|
|
2806
|
+
});
|
|
2353
2807
|
const currentAssignment = state.activeAssignments.find(
|
|
2354
2808
|
(assignment) => assignment.workerId === id && !state.completedTasks.some((task) => task.assignmentId === assignment.id)
|
|
2355
2809
|
);
|
|
2356
2810
|
if (!currentAssignment) {
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2811
|
+
logger3.debug("No active assignment found for worker", {
|
|
2812
|
+
workerId: id,
|
|
2813
|
+
totalActiveAssignments: state.activeAssignments.length,
|
|
2814
|
+
completedTasks: state.completedTasks.length
|
|
2815
|
+
});
|
|
2360
2816
|
return {};
|
|
2361
2817
|
}
|
|
2818
|
+
logger3.info("Worker processing assignment", {
|
|
2819
|
+
workerId: id,
|
|
2820
|
+
assignmentId: currentAssignment.id,
|
|
2821
|
+
taskLength: currentAssignment.task.length,
|
|
2822
|
+
taskPreview: currentAssignment.task.substring(0, 100)
|
|
2823
|
+
});
|
|
2362
2824
|
if (executeFn) {
|
|
2363
|
-
|
|
2364
|
-
console.log(`[Worker:${id}] Using custom executeFn`);
|
|
2365
|
-
}
|
|
2825
|
+
logger3.debug("Using custom execution function", { workerId: id });
|
|
2366
2826
|
return await executeFn(state, runConfig);
|
|
2367
2827
|
}
|
|
2368
2828
|
if (agent) {
|
|
2369
2829
|
if (isReActAgent(agent)) {
|
|
2370
|
-
|
|
2371
|
-
console.log(`[Worker:${id}] Using ReAct agent (auto-wrapped)`);
|
|
2372
|
-
}
|
|
2830
|
+
logger3.debug("Using ReAct agent", { workerId: id });
|
|
2373
2831
|
const wrappedFn = wrapReActAgent(id, agent, verbose);
|
|
2374
2832
|
return await wrappedFn(state, runConfig);
|
|
2375
2833
|
} else {
|
|
2376
|
-
|
|
2834
|
+
logger3.warn("Agent provided but not a ReAct agent, falling back", { workerId: id });
|
|
2377
2835
|
}
|
|
2378
2836
|
}
|
|
2379
2837
|
if (!model) {
|
|
2838
|
+
logger3.error("Worker missing required configuration", { workerId: id });
|
|
2380
2839
|
throw new Error(
|
|
2381
2840
|
`Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
|
|
2382
2841
|
);
|
|
2383
2842
|
}
|
|
2843
|
+
logger3.debug("Using default LLM execution", {
|
|
2844
|
+
workerId: id,
|
|
2845
|
+
hasTools: tools.length > 0,
|
|
2846
|
+
toolCount: tools.length
|
|
2847
|
+
});
|
|
2384
2848
|
const defaultSystemPrompt = `You are a specialized worker agent with the following capabilities:
|
|
2385
2849
|
Skills: ${capabilities.skills.join(", ")}
|
|
2386
2850
|
Tools: ${capabilities.tools.join(", ")}
|
|
@@ -2392,14 +2856,23 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
|
|
|
2392
2856
|
];
|
|
2393
2857
|
let modelToUse = model;
|
|
2394
2858
|
if (tools.length > 0 && model.bindTools) {
|
|
2395
|
-
|
|
2859
|
+
logger3.debug("Binding tools to model", {
|
|
2860
|
+
workerId: id,
|
|
2861
|
+
toolCount: tools.length,
|
|
2862
|
+
toolNames: tools.map((t) => t.metadata.name)
|
|
2863
|
+
});
|
|
2864
|
+
const langchainTools = (0, import_core10.toLangChainTools)(tools);
|
|
2396
2865
|
modelToUse = model.bindTools(langchainTools);
|
|
2397
2866
|
}
|
|
2867
|
+
logger3.debug("Invoking LLM", { workerId: id });
|
|
2398
2868
|
const response = await modelToUse.invoke(messages);
|
|
2399
2869
|
const result = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2870
|
+
logger3.info("Worker task completed", {
|
|
2871
|
+
workerId: id,
|
|
2872
|
+
assignmentId: currentAssignment.id,
|
|
2873
|
+
resultLength: result.length,
|
|
2874
|
+
resultPreview: result.substring(0, 100)
|
|
2875
|
+
});
|
|
2403
2876
|
const taskResult = {
|
|
2404
2877
|
assignmentId: currentAssignment.id,
|
|
2405
2878
|
workerId: id,
|
|
@@ -2429,26 +2902,35 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
|
|
|
2429
2902
|
currentWorkload: Math.max(0, capabilities.currentWorkload - 1)
|
|
2430
2903
|
}
|
|
2431
2904
|
};
|
|
2905
|
+
logger3.debug("Worker state update", {
|
|
2906
|
+
workerId: id,
|
|
2907
|
+
newWorkload: updatedWorkers[id].currentWorkload
|
|
2908
|
+
});
|
|
2432
2909
|
return {
|
|
2433
2910
|
completedTasks: [taskResult],
|
|
2434
2911
|
messages: [message],
|
|
2435
2912
|
workers: updatedWorkers
|
|
2436
2913
|
};
|
|
2437
2914
|
} catch (error) {
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2915
|
+
const errorMessage = handleNodeError(error, `worker:${id}`, false);
|
|
2916
|
+
logger3.error("Worker node error", {
|
|
2917
|
+
workerId: id,
|
|
2918
|
+
error: errorMessage
|
|
2919
|
+
});
|
|
2442
2920
|
const currentAssignment = state.activeAssignments.find(
|
|
2443
2921
|
(assignment) => assignment.workerId === id
|
|
2444
2922
|
);
|
|
2445
2923
|
if (currentAssignment) {
|
|
2924
|
+
logger3.warn("Creating error result for assignment", {
|
|
2925
|
+
workerId: id,
|
|
2926
|
+
assignmentId: currentAssignment.id
|
|
2927
|
+
});
|
|
2446
2928
|
const errorResult = {
|
|
2447
2929
|
assignmentId: currentAssignment.id,
|
|
2448
2930
|
workerId: id,
|
|
2449
2931
|
success: false,
|
|
2450
2932
|
result: "",
|
|
2451
|
-
error:
|
|
2933
|
+
error: errorMessage,
|
|
2452
2934
|
completedAt: Date.now()
|
|
2453
2935
|
};
|
|
2454
2936
|
return {
|
|
@@ -2457,9 +2939,10 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
|
|
|
2457
2939
|
status: "routing"
|
|
2458
2940
|
};
|
|
2459
2941
|
}
|
|
2942
|
+
logger3.error("No assignment found for error handling", { workerId: id });
|
|
2460
2943
|
return {
|
|
2461
2944
|
status: "failed",
|
|
2462
|
-
error:
|
|
2945
|
+
error: errorMessage
|
|
2463
2946
|
};
|
|
2464
2947
|
}
|
|
2465
2948
|
};
|
|
@@ -2473,29 +2956,44 @@ function createAggregatorNode(config = {}) {
|
|
|
2473
2956
|
} = config;
|
|
2474
2957
|
return async (state) => {
|
|
2475
2958
|
try {
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2959
|
+
logger3.info("Aggregator node executing", {
|
|
2960
|
+
completedTasks: state.completedTasks.length,
|
|
2961
|
+
successfulTasks: state.completedTasks.filter((t) => t.success).length,
|
|
2962
|
+
failedTasks: state.completedTasks.filter((t) => !t.success).length
|
|
2963
|
+
});
|
|
2964
|
+
logger3.debug("Combining results from workers");
|
|
2479
2965
|
if (aggregateFn) {
|
|
2966
|
+
logger3.debug("Using custom aggregation function");
|
|
2480
2967
|
const response2 = await aggregateFn(state);
|
|
2968
|
+
logger3.info("Custom aggregation complete", {
|
|
2969
|
+
responseLength: response2.length
|
|
2970
|
+
});
|
|
2481
2971
|
return {
|
|
2482
2972
|
response: response2,
|
|
2483
2973
|
status: "completed"
|
|
2484
2974
|
};
|
|
2485
2975
|
}
|
|
2486
2976
|
if (state.completedTasks.length === 0) {
|
|
2977
|
+
logger3.warn("No completed tasks to aggregate");
|
|
2487
2978
|
return {
|
|
2488
2979
|
response: "No tasks were completed.",
|
|
2489
2980
|
status: "completed"
|
|
2490
2981
|
};
|
|
2491
2982
|
}
|
|
2492
2983
|
if (!model) {
|
|
2984
|
+
logger3.debug("No model provided, concatenating results");
|
|
2493
2985
|
const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
|
|
2986
|
+
logger3.info("Simple concatenation complete", {
|
|
2987
|
+
resultLength: combinedResults.length
|
|
2988
|
+
});
|
|
2494
2989
|
return {
|
|
2495
2990
|
response: combinedResults || "No successful results to aggregate.",
|
|
2496
2991
|
status: "completed"
|
|
2497
2992
|
};
|
|
2498
2993
|
}
|
|
2994
|
+
logger3.debug("Using LLM for intelligent aggregation", {
|
|
2995
|
+
taskCount: state.completedTasks.length
|
|
2996
|
+
});
|
|
2499
2997
|
const taskResults = state.completedTasks.map((task, idx) => {
|
|
2500
2998
|
const status = task.success ? "\u2713" : "\u2717";
|
|
2501
2999
|
const result = task.success ? task.result : `Error: ${task.error}`;
|
|
@@ -2512,17 +3010,24 @@ Please synthesize these results into a comprehensive response that addresses the
|
|
|
2512
3010
|
new import_messages5.SystemMessage(systemPrompt),
|
|
2513
3011
|
new import_messages5.HumanMessage(userPrompt)
|
|
2514
3012
|
];
|
|
3013
|
+
logger3.debug("Invoking aggregation LLM");
|
|
2515
3014
|
const response = await model.invoke(messages);
|
|
2516
3015
|
const aggregatedResponse = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
3016
|
+
logger3.info("Aggregation complete", {
|
|
3017
|
+
responseLength: aggregatedResponse.length,
|
|
3018
|
+
responsePreview: aggregatedResponse.substring(0, 100)
|
|
3019
|
+
});
|
|
3020
|
+
logger3.debug("Aggregation complete");
|
|
2520
3021
|
return {
|
|
2521
3022
|
response: aggregatedResponse,
|
|
2522
3023
|
status: "completed"
|
|
2523
3024
|
};
|
|
2524
3025
|
} catch (error) {
|
|
2525
|
-
|
|
3026
|
+
logger3.error("Aggregator node error", {
|
|
3027
|
+
error: error instanceof Error ? error.message : String(error),
|
|
3028
|
+
stack: error instanceof Error ? error.stack : void 0,
|
|
3029
|
+
completedTasks: state.completedTasks.length
|
|
3030
|
+
});
|
|
2526
3031
|
return {
|
|
2527
3032
|
status: "failed",
|
|
2528
3033
|
error: error instanceof Error ? error.message : "Unknown error in aggregator"
|
|
@@ -2533,7 +3038,9 @@ Please synthesize these results into a comprehensive response that addresses the
|
|
|
2533
3038
|
|
|
2534
3039
|
// src/multi-agent/agent.ts
|
|
2535
3040
|
var import_langgraph4 = require("@langchain/langgraph");
|
|
2536
|
-
var
|
|
3041
|
+
var import_core11 = require("@agentforge/core");
|
|
3042
|
+
var logLevel4 = process.env.LOG_LEVEL?.toLowerCase() || import_core11.LogLevel.INFO;
|
|
3043
|
+
var logger4 = (0, import_core11.createLogger)("multi-agent:system", { level: logLevel4 });
|
|
2537
3044
|
function createMultiAgentSystem(config) {
|
|
2538
3045
|
const {
|
|
2539
3046
|
supervisor,
|
|
@@ -2551,7 +3058,7 @@ function createMultiAgentSystem(config) {
|
|
|
2551
3058
|
configuredModel = configuredModel.withStructuredOutput(RoutingDecisionSchema);
|
|
2552
3059
|
}
|
|
2553
3060
|
if (supervisor.tools && supervisor.tools.length > 0) {
|
|
2554
|
-
const langchainTools = (0,
|
|
3061
|
+
const langchainTools = (0, import_core11.toLangChainTools)(supervisor.tools);
|
|
2555
3062
|
configuredModel = configuredModel.bindTools(langchainTools);
|
|
2556
3063
|
}
|
|
2557
3064
|
supervisorConfig.model = configuredModel;
|
|
@@ -2575,25 +3082,49 @@ function createMultiAgentSystem(config) {
|
|
|
2575
3082
|
});
|
|
2576
3083
|
workflow.addNode("aggregator", aggregatorNode);
|
|
2577
3084
|
const supervisorRouter = (state) => {
|
|
3085
|
+
logger4.debug("Supervisor router executing", {
|
|
3086
|
+
status: state.status,
|
|
3087
|
+
currentAgent: state.currentAgent,
|
|
3088
|
+
iteration: state.iteration
|
|
3089
|
+
});
|
|
2578
3090
|
if (state.status === "completed" || state.status === "failed") {
|
|
3091
|
+
logger4.info("Supervisor router: ending workflow", { status: state.status });
|
|
2579
3092
|
return import_langgraph4.END;
|
|
2580
3093
|
}
|
|
2581
3094
|
if (state.status === "aggregating") {
|
|
3095
|
+
logger4.info("Supervisor router: routing to aggregator");
|
|
2582
3096
|
return "aggregator";
|
|
2583
3097
|
}
|
|
2584
3098
|
if (state.currentAgent && state.currentAgent !== "supervisor") {
|
|
2585
3099
|
if (state.currentAgent.includes(",")) {
|
|
2586
3100
|
const agents = state.currentAgent.split(",").map((a) => a.trim());
|
|
3101
|
+
logger4.info("Supervisor router: parallel routing", {
|
|
3102
|
+
agents,
|
|
3103
|
+
count: agents.length
|
|
3104
|
+
});
|
|
2587
3105
|
return agents;
|
|
2588
3106
|
}
|
|
3107
|
+
logger4.info("Supervisor router: single agent routing", {
|
|
3108
|
+
targetAgent: state.currentAgent
|
|
3109
|
+
});
|
|
2589
3110
|
return state.currentAgent;
|
|
2590
3111
|
}
|
|
3112
|
+
logger4.debug("Supervisor router: staying at supervisor");
|
|
2591
3113
|
return "supervisor";
|
|
2592
3114
|
};
|
|
2593
3115
|
const workerRouter = (state) => {
|
|
3116
|
+
logger4.debug("Worker router executing", {
|
|
3117
|
+
iteration: state.iteration,
|
|
3118
|
+
completedTasks: state.completedTasks.length
|
|
3119
|
+
});
|
|
3120
|
+
logger4.debug("Worker router: returning to supervisor");
|
|
2594
3121
|
return "supervisor";
|
|
2595
3122
|
};
|
|
2596
3123
|
const aggregatorRouter = (state) => {
|
|
3124
|
+
logger4.info("Aggregator router: ending workflow", {
|
|
3125
|
+
completedTasks: state.completedTasks.length,
|
|
3126
|
+
status: state.status
|
|
3127
|
+
});
|
|
2597
3128
|
return import_langgraph4.END;
|
|
2598
3129
|
};
|
|
2599
3130
|
workflow.setEntryPoint("supervisor");
|
|
@@ -2787,11 +3318,14 @@ function registerWorkers(system, workers) {
|
|
|
2787
3318
|
ToolCallSchema,
|
|
2788
3319
|
ToolResultSchema,
|
|
2789
3320
|
WorkerCapabilitiesSchema,
|
|
3321
|
+
buildDeduplicationMetrics,
|
|
3322
|
+
calculateDeduplicationSavings,
|
|
2790
3323
|
createAggregatorNode,
|
|
2791
3324
|
createExecutorNode,
|
|
2792
3325
|
createFinisherNode,
|
|
2793
3326
|
createGeneratorNode,
|
|
2794
3327
|
createMultiAgentSystem,
|
|
3328
|
+
createPatternLogger,
|
|
2795
3329
|
createPlanExecuteAgent,
|
|
2796
3330
|
createPlannerNode,
|
|
2797
3331
|
createReActAgent,
|
|
@@ -2803,6 +3337,7 @@ function registerWorkers(system, workers) {
|
|
|
2803
3337
|
createReviserNode,
|
|
2804
3338
|
createSupervisorNode,
|
|
2805
3339
|
createWorkerNode,
|
|
3340
|
+
generateToolCallCacheKey,
|
|
2806
3341
|
getRoutingStrategy,
|
|
2807
3342
|
llmBasedRouting,
|
|
2808
3343
|
loadBalancedRouting,
|