@agentforge/patterns 0.6.2 → 0.6.4
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 +783 -166
- package/dist/index.d.cts +168 -26
- package/dist/index.d.ts +168 -26
- package/dist/index.js +769 -156
- package/package.json +1 -1
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(),
|
|
@@ -245,14 +251,50 @@ function formatScratchpad(scratchpad) {
|
|
|
245
251
|
|
|
246
252
|
// src/react/nodes.ts
|
|
247
253
|
var import_messages = require("@langchain/core/messages");
|
|
254
|
+
var import_core3 = require("@agentforge/core");
|
|
255
|
+
|
|
256
|
+
// src/shared/deduplication.ts
|
|
248
257
|
var import_core2 = require("@agentforge/core");
|
|
258
|
+
function generateToolCallCacheKey(toolName, args) {
|
|
259
|
+
const sortedArgs = JSON.stringify(args, Object.keys(args || {}).sort());
|
|
260
|
+
return `${toolName}:${sortedArgs}`;
|
|
261
|
+
}
|
|
262
|
+
function createPatternLogger(name, defaultLevel = "info") {
|
|
263
|
+
const logLevel5 = process.env.LOG_LEVEL?.toLowerCase() || defaultLevel;
|
|
264
|
+
return (0, import_core2.createLogger)(name, { level: logLevel5 });
|
|
265
|
+
}
|
|
266
|
+
function calculateDeduplicationSavings(duplicatesSkipped, toolsExecuted) {
|
|
267
|
+
if (duplicatesSkipped === 0) {
|
|
268
|
+
return "0%";
|
|
269
|
+
}
|
|
270
|
+
const total = toolsExecuted + duplicatesSkipped;
|
|
271
|
+
return `${Math.round(duplicatesSkipped / total * 100)}%`;
|
|
272
|
+
}
|
|
273
|
+
function buildDeduplicationMetrics(toolsExecuted, duplicatesSkipped, totalObservations) {
|
|
274
|
+
return {
|
|
275
|
+
toolsExecuted,
|
|
276
|
+
duplicatesSkipped,
|
|
277
|
+
totalObservations,
|
|
278
|
+
deduplicationSavings: calculateDeduplicationSavings(duplicatesSkipped, toolsExecuted)
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// src/react/nodes.ts
|
|
283
|
+
var reasoningLogger = createPatternLogger("agentforge:patterns:react:reasoning");
|
|
284
|
+
var actionLogger = createPatternLogger("agentforge:patterns:react:action");
|
|
285
|
+
var observationLogger = createPatternLogger("agentforge:patterns:react:observation");
|
|
249
286
|
function createReasoningNode(llm, tools, systemPrompt, maxIterations, verbose = false) {
|
|
250
|
-
const langchainTools = (0,
|
|
287
|
+
const langchainTools = (0, import_core3.toLangChainTools)(tools);
|
|
251
288
|
const llmWithTools = llm.bindTools ? llm.bindTools(langchainTools) : llm;
|
|
252
289
|
return async (state) => {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
290
|
+
const currentIteration = state.iteration || 0;
|
|
291
|
+
const startTime = Date.now();
|
|
292
|
+
reasoningLogger.debug("Reasoning iteration started", {
|
|
293
|
+
iteration: currentIteration + 1,
|
|
294
|
+
maxIterations,
|
|
295
|
+
observationCount: state.observations?.length || 0,
|
|
296
|
+
hasActions: !!state.actions?.length
|
|
297
|
+
});
|
|
256
298
|
const stateMessages = state.messages || [];
|
|
257
299
|
const messages = [
|
|
258
300
|
new import_messages.SystemMessage(systemPrompt),
|
|
@@ -282,8 +324,15 @@ ${scratchpadText}`));
|
|
|
282
324
|
});
|
|
283
325
|
}
|
|
284
326
|
}
|
|
285
|
-
const currentIteration = state.iteration || 0;
|
|
286
327
|
const shouldContinue = toolCalls.length > 0 && currentIteration + 1 < maxIterations;
|
|
328
|
+
reasoningLogger.info("Reasoning complete", {
|
|
329
|
+
iteration: currentIteration + 1,
|
|
330
|
+
thoughtGenerated: !!thought,
|
|
331
|
+
actionCount: toolCalls.length,
|
|
332
|
+
shouldContinue,
|
|
333
|
+
isFinalResponse: toolCalls.length === 0,
|
|
334
|
+
duration: Date.now() - startTime
|
|
335
|
+
});
|
|
287
336
|
return {
|
|
288
337
|
messages: [{ role: "assistant", content: thought }],
|
|
289
338
|
thoughts: thought ? [{ content: thought, timestamp: Date.now() }] : [],
|
|
@@ -296,16 +345,71 @@ ${scratchpadText}`));
|
|
|
296
345
|
};
|
|
297
346
|
};
|
|
298
347
|
}
|
|
299
|
-
function createActionNode(tools, verbose = false) {
|
|
348
|
+
function createActionNode(tools, verbose = false, enableDeduplication = true) {
|
|
300
349
|
const toolMap = new Map(tools.map((tool) => [tool.metadata.name, tool]));
|
|
301
350
|
return async (state) => {
|
|
302
351
|
const actions = state.actions || [];
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
352
|
+
const allObservations = state.observations || [];
|
|
353
|
+
const iteration = state.iteration || 0;
|
|
354
|
+
const startTime = Date.now();
|
|
355
|
+
actionLogger.debug("Action node started", {
|
|
356
|
+
actionCount: actions.length,
|
|
357
|
+
iteration,
|
|
358
|
+
cacheEnabled: enableDeduplication
|
|
359
|
+
});
|
|
306
360
|
const recentActions = actions.slice(-10);
|
|
307
361
|
const observations = [];
|
|
362
|
+
const executionCache = /* @__PURE__ */ new Map();
|
|
363
|
+
let cacheSize = 0;
|
|
364
|
+
if (enableDeduplication) {
|
|
365
|
+
for (const observation of allObservations) {
|
|
366
|
+
const correspondingAction = actions.find((a) => a.id === observation.toolCallId);
|
|
367
|
+
if (correspondingAction) {
|
|
368
|
+
const cacheKey = generateToolCallCacheKey(correspondingAction.name, correspondingAction.arguments);
|
|
369
|
+
executionCache.set(cacheKey, observation);
|
|
370
|
+
cacheSize++;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (cacheSize > 0) {
|
|
374
|
+
actionLogger.debug("Deduplication cache built", {
|
|
375
|
+
cacheSize,
|
|
376
|
+
totalObservations: allObservations.length
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
let duplicatesSkipped = 0;
|
|
381
|
+
let toolsExecuted = 0;
|
|
308
382
|
for (const action of recentActions) {
|
|
383
|
+
const existingObservation = allObservations.find((obs) => obs.toolCallId === action.id);
|
|
384
|
+
if (existingObservation) {
|
|
385
|
+
actionLogger.debug("Skipping already-processed action", {
|
|
386
|
+
toolName: action.name,
|
|
387
|
+
toolCallId: action.id,
|
|
388
|
+
iteration
|
|
389
|
+
});
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
if (enableDeduplication) {
|
|
393
|
+
const cacheKey = generateToolCallCacheKey(action.name, action.arguments);
|
|
394
|
+
const cachedResult = executionCache.get(cacheKey);
|
|
395
|
+
if (cachedResult) {
|
|
396
|
+
duplicatesSkipped++;
|
|
397
|
+
actionLogger.info("Duplicate tool call prevented", {
|
|
398
|
+
toolName: action.name,
|
|
399
|
+
arguments: action.arguments,
|
|
400
|
+
iteration,
|
|
401
|
+
cacheHit: true
|
|
402
|
+
});
|
|
403
|
+
observations.push({
|
|
404
|
+
toolCallId: action.id,
|
|
405
|
+
result: cachedResult.result,
|
|
406
|
+
error: cachedResult.error,
|
|
407
|
+
timestamp: Date.now(),
|
|
408
|
+
isDuplicate: true
|
|
409
|
+
});
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
309
413
|
const tool = toolMap.get(action.name);
|
|
310
414
|
if (!tool) {
|
|
311
415
|
observations.push({
|
|
@@ -317,31 +421,51 @@ function createActionNode(tools, verbose = false) {
|
|
|
317
421
|
continue;
|
|
318
422
|
}
|
|
319
423
|
try {
|
|
424
|
+
const startTime2 = Date.now();
|
|
320
425
|
const result = await tool.execute(action.arguments);
|
|
321
|
-
|
|
426
|
+
const executionTime = Date.now() - startTime2;
|
|
427
|
+
toolsExecuted++;
|
|
428
|
+
actionLogger.debug("Tool executed successfully", {
|
|
429
|
+
toolName: action.name,
|
|
430
|
+
executionTime,
|
|
431
|
+
iteration
|
|
432
|
+
});
|
|
433
|
+
const observation = {
|
|
322
434
|
toolCallId: action.id,
|
|
323
435
|
result,
|
|
324
436
|
timestamp: Date.now()
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
|
|
437
|
+
};
|
|
438
|
+
observations.push(observation);
|
|
439
|
+
if (enableDeduplication) {
|
|
440
|
+
const cacheKey = generateToolCallCacheKey(action.name, action.arguments);
|
|
441
|
+
executionCache.set(cacheKey, observation);
|
|
328
442
|
}
|
|
329
443
|
} catch (error) {
|
|
330
444
|
if (error && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt") {
|
|
331
445
|
throw error;
|
|
332
446
|
}
|
|
333
447
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
448
|
+
actionLogger.error("Tool execution failed", {
|
|
449
|
+
toolName: action.name,
|
|
450
|
+
error: errorMessage,
|
|
451
|
+
iteration
|
|
452
|
+
});
|
|
334
453
|
observations.push({
|
|
335
454
|
toolCallId: action.id,
|
|
336
455
|
result: null,
|
|
337
456
|
error: errorMessage,
|
|
338
457
|
timestamp: Date.now()
|
|
339
458
|
});
|
|
340
|
-
if (verbose) {
|
|
341
|
-
console.error(`[action] Tool '${action.name}' failed:`, errorMessage);
|
|
342
|
-
}
|
|
343
459
|
}
|
|
344
460
|
}
|
|
461
|
+
if (duplicatesSkipped > 0 || toolsExecuted > 0) {
|
|
462
|
+
const metrics = buildDeduplicationMetrics(toolsExecuted, duplicatesSkipped, observations.length);
|
|
463
|
+
actionLogger.info("Action node complete", {
|
|
464
|
+
iteration,
|
|
465
|
+
...metrics,
|
|
466
|
+
duration: Date.now() - startTime
|
|
467
|
+
});
|
|
468
|
+
}
|
|
345
469
|
return {
|
|
346
470
|
observations
|
|
347
471
|
};
|
|
@@ -352,9 +476,11 @@ function createObservationNode(verbose = false) {
|
|
|
352
476
|
const observations = state.observations || [];
|
|
353
477
|
const thoughts = state.thoughts || [];
|
|
354
478
|
const actions = state.actions || [];
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
479
|
+
const iteration = state.iteration || 0;
|
|
480
|
+
observationLogger.debug("Processing observations", {
|
|
481
|
+
observationCount: observations.length,
|
|
482
|
+
iteration
|
|
483
|
+
});
|
|
358
484
|
const recentObservations = observations.slice(-10);
|
|
359
485
|
const currentStep = state.iteration;
|
|
360
486
|
const latestThought = thoughts[thoughts.length - 1]?.content || "";
|
|
@@ -380,6 +506,11 @@ function createObservationNode(verbose = false) {
|
|
|
380
506
|
name: latestActions.find((a) => a.id === obs.toolCallId)?.name
|
|
381
507
|
};
|
|
382
508
|
});
|
|
509
|
+
observationLogger.debug("Observation node complete", {
|
|
510
|
+
iteration,
|
|
511
|
+
scratchpadUpdated: true,
|
|
512
|
+
messageCount: observationMessages.length
|
|
513
|
+
});
|
|
383
514
|
return {
|
|
384
515
|
scratchpad: [scratchpadEntry],
|
|
385
516
|
messages: observationMessages
|
|
@@ -389,7 +520,7 @@ function createObservationNode(verbose = false) {
|
|
|
389
520
|
|
|
390
521
|
// src/react/agent.ts
|
|
391
522
|
var import_langgraph = require("@langchain/langgraph");
|
|
392
|
-
var
|
|
523
|
+
var import_core4 = require("@agentforge/core");
|
|
393
524
|
function createReActAgent(config, options) {
|
|
394
525
|
const {
|
|
395
526
|
model,
|
|
@@ -398,13 +529,15 @@ function createReActAgent(config, options) {
|
|
|
398
529
|
maxIterations = 10,
|
|
399
530
|
returnIntermediateSteps = false,
|
|
400
531
|
stopCondition,
|
|
401
|
-
checkpointer
|
|
532
|
+
checkpointer,
|
|
533
|
+
enableDeduplication = true
|
|
534
|
+
// Enable by default
|
|
402
535
|
} = config;
|
|
403
536
|
const {
|
|
404
537
|
verbose = false,
|
|
405
538
|
nodeNames = {}
|
|
406
539
|
} = options || {};
|
|
407
|
-
const toolArray = tools instanceof
|
|
540
|
+
const toolArray = tools instanceof import_core4.ToolRegistry ? tools.getAll() : tools;
|
|
408
541
|
const REASONING_NODE = nodeNames.reasoning || "reasoning";
|
|
409
542
|
const ACTION_NODE = nodeNames.action || "action";
|
|
410
543
|
const OBSERVATION_NODE = nodeNames.observation || "observation";
|
|
@@ -415,7 +548,7 @@ function createReActAgent(config, options) {
|
|
|
415
548
|
maxIterations,
|
|
416
549
|
verbose
|
|
417
550
|
);
|
|
418
|
-
const actionNode = createActionNode(toolArray, verbose);
|
|
551
|
+
const actionNode = createActionNode(toolArray, verbose, enableDeduplication);
|
|
419
552
|
const observationNode = createObservationNode(verbose);
|
|
420
553
|
const shouldContinue = (state) => {
|
|
421
554
|
if (stopCondition && stopCondition(state)) {
|
|
@@ -551,7 +684,7 @@ var import_langgraph2 = require("@langchain/langgraph");
|
|
|
551
684
|
|
|
552
685
|
// src/plan-execute/state.ts
|
|
553
686
|
var import_zod4 = require("zod");
|
|
554
|
-
var
|
|
687
|
+
var import_core5 = require("@agentforge/core");
|
|
555
688
|
|
|
556
689
|
// src/plan-execute/schemas.ts
|
|
557
690
|
var import_zod3 = require("zod");
|
|
@@ -713,7 +846,7 @@ var PlanExecuteStateConfig = {
|
|
|
713
846
|
description: "Maximum number of planning iterations allowed"
|
|
714
847
|
}
|
|
715
848
|
};
|
|
716
|
-
var PlanExecuteState = (0,
|
|
849
|
+
var PlanExecuteState = (0, import_core5.createStateAnnotation)(PlanExecuteStateConfig);
|
|
717
850
|
|
|
718
851
|
// src/plan-execute/nodes.ts
|
|
719
852
|
var import_messages2 = require("@langchain/core/messages");
|
|
@@ -778,6 +911,9 @@ var REMAINING_STEP_TEMPLATE = `Step {stepNumber}: {description}
|
|
|
778
911
|
{dependencies}`;
|
|
779
912
|
|
|
780
913
|
// src/plan-execute/nodes.ts
|
|
914
|
+
var plannerLogger = createPatternLogger("agentforge:patterns:plan-execute:planner");
|
|
915
|
+
var executorLogger = createPatternLogger("agentforge:patterns:plan-execute:executor");
|
|
916
|
+
var replannerLogger = createPatternLogger("agentforge:patterns:plan-execute:replanner");
|
|
781
917
|
function createPlannerNode(config) {
|
|
782
918
|
const {
|
|
783
919
|
model,
|
|
@@ -786,7 +922,13 @@ function createPlannerNode(config) {
|
|
|
786
922
|
includeToolDescriptions = false
|
|
787
923
|
} = config;
|
|
788
924
|
return async (state) => {
|
|
925
|
+
const startTime = Date.now();
|
|
789
926
|
try {
|
|
927
|
+
plannerLogger.debug("Planning started", {
|
|
928
|
+
input: state.input?.substring(0, 100),
|
|
929
|
+
maxSteps,
|
|
930
|
+
includeToolDescriptions
|
|
931
|
+
});
|
|
790
932
|
let toolDescriptions = "";
|
|
791
933
|
if (includeToolDescriptions) {
|
|
792
934
|
toolDescriptions = "";
|
|
@@ -811,6 +953,12 @@ function createPlannerNode(config) {
|
|
|
811
953
|
} catch (parseError) {
|
|
812
954
|
throw new Error(`Failed to parse plan from LLM response: ${parseError}`);
|
|
813
955
|
}
|
|
956
|
+
plannerLogger.info("Plan created", {
|
|
957
|
+
stepCount: plan.steps.length,
|
|
958
|
+
goal: plan.goal.substring(0, 100),
|
|
959
|
+
confidence: plan.confidence,
|
|
960
|
+
duration: Date.now() - startTime
|
|
961
|
+
});
|
|
814
962
|
return {
|
|
815
963
|
plan,
|
|
816
964
|
status: "executing",
|
|
@@ -818,6 +966,10 @@ function createPlannerNode(config) {
|
|
|
818
966
|
iteration: 1
|
|
819
967
|
};
|
|
820
968
|
} catch (error) {
|
|
969
|
+
plannerLogger.error("Planning failed", {
|
|
970
|
+
error: error instanceof Error ? error.message : String(error),
|
|
971
|
+
duration: Date.now() - startTime
|
|
972
|
+
});
|
|
821
973
|
return {
|
|
822
974
|
status: "failed",
|
|
823
975
|
error: error instanceof Error ? error.message : "Unknown error in planner"
|
|
@@ -830,11 +982,18 @@ function createExecutorNode(config) {
|
|
|
830
982
|
tools,
|
|
831
983
|
model,
|
|
832
984
|
parallel = false,
|
|
833
|
-
stepTimeout = 3e4
|
|
985
|
+
stepTimeout = 3e4,
|
|
986
|
+
enableDeduplication = true
|
|
834
987
|
} = config;
|
|
835
988
|
return async (state) => {
|
|
989
|
+
const { plan, currentStepIndex = 0, pastSteps = [], iteration = 0 } = state;
|
|
836
990
|
try {
|
|
837
|
-
|
|
991
|
+
executorLogger.debug("Executor node executing", {
|
|
992
|
+
currentStepIndex,
|
|
993
|
+
totalSteps: plan?.steps?.length || 0,
|
|
994
|
+
iteration,
|
|
995
|
+
deduplicationEnabled: enableDeduplication
|
|
996
|
+
});
|
|
838
997
|
if (!plan || !plan.steps || plan.steps.length === 0) {
|
|
839
998
|
return {
|
|
840
999
|
status: "completed"
|
|
@@ -853,22 +1012,67 @@ function createExecutorNode(config) {
|
|
|
853
1012
|
throw new Error(`Unmet dependencies for step ${currentStep.id}: ${unmetDependencies.join(", ")}`);
|
|
854
1013
|
}
|
|
855
1014
|
}
|
|
1015
|
+
const executionCache = /* @__PURE__ */ new Map();
|
|
1016
|
+
let cacheSize = 0;
|
|
1017
|
+
if (enableDeduplication && currentStep.tool) {
|
|
1018
|
+
for (const pastStep of pastSteps) {
|
|
1019
|
+
if (pastStep.step.tool) {
|
|
1020
|
+
const cacheKey = generateToolCallCacheKey(pastStep.step.tool, pastStep.step.args || {});
|
|
1021
|
+
executionCache.set(cacheKey, pastStep);
|
|
1022
|
+
cacheSize++;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
if (cacheSize > 0) {
|
|
1026
|
+
executorLogger.debug("Deduplication cache built", {
|
|
1027
|
+
cacheSize,
|
|
1028
|
+
pastStepsCount: pastSteps.length
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
856
1032
|
let result;
|
|
857
1033
|
let success = true;
|
|
858
1034
|
let error;
|
|
1035
|
+
let isDuplicate = false;
|
|
859
1036
|
try {
|
|
860
1037
|
if (currentStep.tool) {
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
1038
|
+
if (enableDeduplication) {
|
|
1039
|
+
const cacheKey = generateToolCallCacheKey(currentStep.tool, currentStep.args || {});
|
|
1040
|
+
const cachedStep = executionCache.get(cacheKey);
|
|
1041
|
+
if (cachedStep) {
|
|
1042
|
+
isDuplicate = true;
|
|
1043
|
+
result = cachedStep.result;
|
|
1044
|
+
success = cachedStep.success;
|
|
1045
|
+
error = cachedStep.error;
|
|
1046
|
+
executorLogger.info("Duplicate step execution prevented", {
|
|
1047
|
+
stepId: currentStep.id,
|
|
1048
|
+
toolName: currentStep.tool,
|
|
1049
|
+
arguments: currentStep.args,
|
|
1050
|
+
iteration,
|
|
1051
|
+
cacheHit: true
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
if (!isDuplicate) {
|
|
1056
|
+
const tool = tools.find((t) => t.metadata.name === currentStep.tool);
|
|
1057
|
+
if (!tool) {
|
|
1058
|
+
throw new Error(`Tool not found: ${currentStep.tool}`);
|
|
1059
|
+
}
|
|
1060
|
+
const startTime = Date.now();
|
|
1061
|
+
const timeoutPromise = new Promise(
|
|
1062
|
+
(_, reject) => setTimeout(() => reject(new Error("Step execution timeout")), stepTimeout)
|
|
1063
|
+
);
|
|
1064
|
+
result = await Promise.race([
|
|
1065
|
+
tool.execute(currentStep.args || {}),
|
|
1066
|
+
timeoutPromise
|
|
1067
|
+
]);
|
|
1068
|
+
const executionTime = Date.now() - startTime;
|
|
1069
|
+
executorLogger.debug("Step executed successfully", {
|
|
1070
|
+
stepId: currentStep.id,
|
|
1071
|
+
toolName: currentStep.tool,
|
|
1072
|
+
executionTime,
|
|
1073
|
+
iteration
|
|
1074
|
+
});
|
|
864
1075
|
}
|
|
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
1076
|
} else {
|
|
873
1077
|
result = { message: "Step completed without tool execution" };
|
|
874
1078
|
}
|
|
@@ -879,6 +1083,12 @@ function createExecutorNode(config) {
|
|
|
879
1083
|
success = false;
|
|
880
1084
|
error = execError instanceof Error ? execError.message : "Unknown execution error";
|
|
881
1085
|
result = null;
|
|
1086
|
+
executorLogger.warn("Step execution failed", {
|
|
1087
|
+
stepId: currentStep.id,
|
|
1088
|
+
toolName: currentStep.tool,
|
|
1089
|
+
error,
|
|
1090
|
+
iteration
|
|
1091
|
+
});
|
|
882
1092
|
}
|
|
883
1093
|
const completedStep = {
|
|
884
1094
|
step: currentStep,
|
|
@@ -887,11 +1097,23 @@ function createExecutorNode(config) {
|
|
|
887
1097
|
error,
|
|
888
1098
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
889
1099
|
};
|
|
1100
|
+
executorLogger.info("Executor node complete", {
|
|
1101
|
+
stepId: currentStep.id,
|
|
1102
|
+
stepIndex: currentStepIndex,
|
|
1103
|
+
totalSteps: plan.steps.length,
|
|
1104
|
+
success,
|
|
1105
|
+
isDuplicate,
|
|
1106
|
+
iteration
|
|
1107
|
+
});
|
|
890
1108
|
return {
|
|
891
1109
|
pastSteps: [completedStep],
|
|
892
1110
|
currentStepIndex: currentStepIndex + 1
|
|
893
1111
|
};
|
|
894
1112
|
} catch (error) {
|
|
1113
|
+
executorLogger.error("Executor node failed", {
|
|
1114
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
1115
|
+
iteration
|
|
1116
|
+
});
|
|
895
1117
|
return {
|
|
896
1118
|
status: "failed",
|
|
897
1119
|
error: error instanceof Error ? error.message : "Unknown error in executor"
|
|
@@ -906,11 +1128,18 @@ function createReplannerNode(config) {
|
|
|
906
1128
|
systemPrompt = DEFAULT_REPLANNER_SYSTEM_PROMPT
|
|
907
1129
|
} = config;
|
|
908
1130
|
return async (state) => {
|
|
1131
|
+
const startTime = Date.now();
|
|
909
1132
|
try {
|
|
910
1133
|
const { plan, pastSteps = [], currentStepIndex = 0 } = state;
|
|
911
1134
|
if (!plan) {
|
|
912
1135
|
return { status: "failed", error: "No plan available for replanning" };
|
|
913
1136
|
}
|
|
1137
|
+
replannerLogger.debug("Evaluating replanning", {
|
|
1138
|
+
completedSteps: pastSteps.length,
|
|
1139
|
+
remainingSteps: plan.steps.length - currentStepIndex,
|
|
1140
|
+
successfulSteps: pastSteps.filter((ps) => ps.success).length,
|
|
1141
|
+
failedSteps: pastSteps.filter((ps) => !ps.success).length
|
|
1142
|
+
});
|
|
914
1143
|
const completedStepsText = pastSteps.map(
|
|
915
1144
|
(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
1145
|
).join("\n\n");
|
|
@@ -932,17 +1161,30 @@ function createReplannerNode(config) {
|
|
|
932
1161
|
throw new Error(`Failed to parse replan decision from LLM response: ${parseError}`);
|
|
933
1162
|
}
|
|
934
1163
|
if (decision.shouldReplan) {
|
|
1164
|
+
replannerLogger.info("Replanning triggered", {
|
|
1165
|
+
reason: decision.reason,
|
|
1166
|
+
newGoal: decision.newGoal?.substring(0, 100),
|
|
1167
|
+
duration: Date.now() - startTime
|
|
1168
|
+
});
|
|
935
1169
|
return {
|
|
936
1170
|
status: "planning",
|
|
937
1171
|
input: decision.newGoal || plan.goal,
|
|
938
1172
|
iteration: 1
|
|
939
1173
|
};
|
|
940
1174
|
} else {
|
|
1175
|
+
replannerLogger.info("Continuing with current plan", {
|
|
1176
|
+
reason: decision.reason,
|
|
1177
|
+
duration: Date.now() - startTime
|
|
1178
|
+
});
|
|
941
1179
|
return {
|
|
942
1180
|
status: "executing"
|
|
943
1181
|
};
|
|
944
1182
|
}
|
|
945
1183
|
} catch (error) {
|
|
1184
|
+
replannerLogger.error("Replanning evaluation failed", {
|
|
1185
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1186
|
+
duration: Date.now() - startTime
|
|
1187
|
+
});
|
|
946
1188
|
return {
|
|
947
1189
|
status: "failed",
|
|
948
1190
|
error: error instanceof Error ? error.message : "Unknown error in replanner"
|
|
@@ -1043,7 +1285,7 @@ function createPlanExecuteAgent(config) {
|
|
|
1043
1285
|
|
|
1044
1286
|
// src/reflection/state.ts
|
|
1045
1287
|
var import_zod6 = require("zod");
|
|
1046
|
-
var
|
|
1288
|
+
var import_core6 = require("@agentforge/core");
|
|
1047
1289
|
|
|
1048
1290
|
// src/reflection/schemas.ts
|
|
1049
1291
|
var import_zod5 = require("zod");
|
|
@@ -1216,7 +1458,7 @@ var ReflectionStateConfig = {
|
|
|
1216
1458
|
description: "Error message if reflection failed"
|
|
1217
1459
|
}
|
|
1218
1460
|
};
|
|
1219
|
-
var ReflectionState = (0,
|
|
1461
|
+
var ReflectionState = (0, import_core6.createStateAnnotation)(ReflectionStateConfig);
|
|
1220
1462
|
|
|
1221
1463
|
// src/reflection/prompts.ts
|
|
1222
1464
|
var DEFAULT_GENERATOR_SYSTEM_PROMPT = `You are an expert content generator. Your task is to create high-quality responses to user requests.
|
|
@@ -1320,6 +1562,9 @@ var REVISION_ENTRY_TEMPLATE = `Iteration {iteration}:
|
|
|
1320
1562
|
|
|
1321
1563
|
// src/reflection/nodes.ts
|
|
1322
1564
|
var import_messages3 = require("@langchain/core/messages");
|
|
1565
|
+
var generatorLogger = createPatternLogger("agentforge:patterns:reflection:generator");
|
|
1566
|
+
var reflectorLogger = createPatternLogger("agentforge:patterns:reflection:reflector");
|
|
1567
|
+
var reviserLogger = createPatternLogger("agentforge:patterns:reflection:reviser");
|
|
1323
1568
|
function createGeneratorNode(config) {
|
|
1324
1569
|
const {
|
|
1325
1570
|
model,
|
|
@@ -1327,10 +1572,13 @@ function createGeneratorNode(config) {
|
|
|
1327
1572
|
verbose = false
|
|
1328
1573
|
} = config;
|
|
1329
1574
|
return async (state) => {
|
|
1575
|
+
const startTime = Date.now();
|
|
1330
1576
|
try {
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1577
|
+
generatorLogger.debug("Generating response", {
|
|
1578
|
+
attempt: state.iteration + 1,
|
|
1579
|
+
hasFeedback: state.reflections.length > 0,
|
|
1580
|
+
hasExistingResponse: !!state.currentResponse
|
|
1581
|
+
});
|
|
1334
1582
|
let context = "";
|
|
1335
1583
|
if (state.iteration > 0 && state.reflections.length > 0) {
|
|
1336
1584
|
const lastReflection = state.reflections[state.reflections.length - 1];
|
|
@@ -1345,16 +1593,23 @@ ${lastReflection.critique}`;
|
|
|
1345
1593
|
];
|
|
1346
1594
|
const response = await model.invoke(messages);
|
|
1347
1595
|
const content = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1596
|
+
generatorLogger.info("Response generated", {
|
|
1597
|
+
attempt: state.iteration + 1,
|
|
1598
|
+
responseLength: content.length,
|
|
1599
|
+
isRevision: state.iteration > 0,
|
|
1600
|
+
duration: Date.now() - startTime
|
|
1601
|
+
});
|
|
1351
1602
|
return {
|
|
1352
1603
|
currentResponse: content,
|
|
1353
1604
|
status: "reflecting",
|
|
1354
1605
|
iteration: 1
|
|
1355
1606
|
};
|
|
1356
1607
|
} catch (error) {
|
|
1357
|
-
|
|
1608
|
+
generatorLogger.error("Response generation failed", {
|
|
1609
|
+
attempt: state.iteration + 1,
|
|
1610
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1611
|
+
duration: Date.now() - startTime
|
|
1612
|
+
});
|
|
1358
1613
|
return {
|
|
1359
1614
|
status: "failed",
|
|
1360
1615
|
error: error instanceof Error ? error.message : "Unknown error in generator"
|
|
@@ -1370,10 +1625,13 @@ function createReflectorNode(config) {
|
|
|
1370
1625
|
verbose = false
|
|
1371
1626
|
} = config;
|
|
1372
1627
|
return async (state) => {
|
|
1628
|
+
const startTime = Date.now();
|
|
1373
1629
|
try {
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1630
|
+
reflectorLogger.debug("Reflecting on response", {
|
|
1631
|
+
attempt: state.iteration,
|
|
1632
|
+
responseLength: state.currentResponse?.length || 0,
|
|
1633
|
+
hasCriteria: !!(qualityCriteria || state.qualityCriteria)
|
|
1634
|
+
});
|
|
1377
1635
|
if (!state.currentResponse) {
|
|
1378
1636
|
throw new Error("No current response to reflect on");
|
|
1379
1637
|
}
|
|
@@ -1423,16 +1681,24 @@ function createReflectorNode(config) {
|
|
|
1423
1681
|
meetsStandards: false
|
|
1424
1682
|
};
|
|
1425
1683
|
}
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1684
|
+
reflectorLogger.info("Reflection complete", {
|
|
1685
|
+
attempt: state.iteration,
|
|
1686
|
+
score: reflection.score,
|
|
1687
|
+
meetsStandards: reflection.meetsStandards,
|
|
1688
|
+
issueCount: reflection.issues.length,
|
|
1689
|
+
suggestionCount: reflection.suggestions.length,
|
|
1690
|
+
duration: Date.now() - startTime
|
|
1691
|
+
});
|
|
1430
1692
|
return {
|
|
1431
1693
|
reflections: [reflection],
|
|
1432
1694
|
status: reflection.meetsStandards ? "completed" : "revising"
|
|
1433
1695
|
};
|
|
1434
1696
|
} catch (error) {
|
|
1435
|
-
|
|
1697
|
+
reflectorLogger.error("Reflection failed", {
|
|
1698
|
+
attempt: state.iteration,
|
|
1699
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1700
|
+
duration: Date.now() - startTime
|
|
1701
|
+
});
|
|
1436
1702
|
return {
|
|
1437
1703
|
status: "failed",
|
|
1438
1704
|
error: error instanceof Error ? error.message : "Unknown error in reflector"
|
|
@@ -1447,10 +1713,8 @@ function createReviserNode(config) {
|
|
|
1447
1713
|
verbose = false
|
|
1448
1714
|
} = config;
|
|
1449
1715
|
return async (state) => {
|
|
1716
|
+
const startTime = Date.now();
|
|
1450
1717
|
try {
|
|
1451
|
-
if (verbose) {
|
|
1452
|
-
console.log("[Reviser] Revising response...");
|
|
1453
|
-
}
|
|
1454
1718
|
if (!state.currentResponse) {
|
|
1455
1719
|
throw new Error("No current response to revise");
|
|
1456
1720
|
}
|
|
@@ -1458,6 +1722,12 @@ function createReviserNode(config) {
|
|
|
1458
1722
|
throw new Error("No reflections to base revision on");
|
|
1459
1723
|
}
|
|
1460
1724
|
const lastReflection = state.reflections[state.reflections.length - 1];
|
|
1725
|
+
reviserLogger.debug("Revising response", {
|
|
1726
|
+
attempt: state.iteration,
|
|
1727
|
+
previousScore: lastReflection.score,
|
|
1728
|
+
issueCount: lastReflection.issues.length,
|
|
1729
|
+
suggestionCount: lastReflection.suggestions.length
|
|
1730
|
+
});
|
|
1461
1731
|
let historySection = "";
|
|
1462
1732
|
if (state.revisions.length > 0) {
|
|
1463
1733
|
const revisionsText = state.revisions.map(
|
|
@@ -1474,14 +1744,17 @@ ${revisionsText}`;
|
|
|
1474
1744
|
];
|
|
1475
1745
|
const response = await model.invoke(messages);
|
|
1476
1746
|
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
1747
|
const revision = {
|
|
1481
1748
|
content,
|
|
1482
1749
|
iteration: state.iteration,
|
|
1483
1750
|
basedOn: lastReflection
|
|
1484
1751
|
};
|
|
1752
|
+
reviserLogger.info("Revision complete", {
|
|
1753
|
+
attempt: state.iteration,
|
|
1754
|
+
revisionLength: content.length,
|
|
1755
|
+
basedOnScore: lastReflection.score,
|
|
1756
|
+
duration: Date.now() - startTime
|
|
1757
|
+
});
|
|
1485
1758
|
return {
|
|
1486
1759
|
currentResponse: content,
|
|
1487
1760
|
revisions: [revision],
|
|
@@ -1489,7 +1762,11 @@ ${revisionsText}`;
|
|
|
1489
1762
|
iteration: 1
|
|
1490
1763
|
};
|
|
1491
1764
|
} catch (error) {
|
|
1492
|
-
|
|
1765
|
+
reviserLogger.error("Revision failed", {
|
|
1766
|
+
attempt: state.iteration,
|
|
1767
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1768
|
+
duration: Date.now() - startTime
|
|
1769
|
+
});
|
|
1493
1770
|
return {
|
|
1494
1771
|
status: "failed",
|
|
1495
1772
|
error: error instanceof Error ? error.message : "Unknown error in reviser"
|
|
@@ -1579,7 +1856,7 @@ function createReflectionAgent(config) {
|
|
|
1579
1856
|
|
|
1580
1857
|
// src/multi-agent/state.ts
|
|
1581
1858
|
var import_zod8 = require("zod");
|
|
1582
|
-
var
|
|
1859
|
+
var import_core7 = require("@agentforge/core");
|
|
1583
1860
|
|
|
1584
1861
|
// src/multi-agent/schemas.ts
|
|
1585
1862
|
var import_zod7 = require("zod");
|
|
@@ -1642,26 +1919,35 @@ var RoutingStrategySchema = import_zod7.z.enum([
|
|
|
1642
1919
|
]);
|
|
1643
1920
|
var RoutingDecisionSchema = import_zod7.z.object({
|
|
1644
1921
|
/**
|
|
1645
|
-
* Target agent to route to
|
|
1922
|
+
* Target agent to route to (single agent routing)
|
|
1923
|
+
* @deprecated Use targetAgents for parallel routing support
|
|
1924
|
+
*/
|
|
1925
|
+
targetAgent: import_zod7.z.string().nullable().default(null).describe("Agent to route the task to (single routing)"),
|
|
1926
|
+
/**
|
|
1927
|
+
* Target agents to route to (parallel routing)
|
|
1928
|
+
* When multiple agents are specified, they execute in parallel
|
|
1646
1929
|
*/
|
|
1647
|
-
|
|
1930
|
+
targetAgents: import_zod7.z.array(import_zod7.z.string()).nullable().default(null).describe("Agents to route the task to (parallel routing)"),
|
|
1648
1931
|
/**
|
|
1649
1932
|
* Reasoning for the routing decision
|
|
1650
1933
|
*/
|
|
1651
|
-
reasoning: import_zod7.z.string().
|
|
1934
|
+
reasoning: import_zod7.z.string().default("").describe("Explanation for routing decision"),
|
|
1652
1935
|
/**
|
|
1653
1936
|
* Confidence in the routing decision (0-1)
|
|
1654
1937
|
*/
|
|
1655
|
-
confidence: import_zod7.z.number().min(0).max(1).
|
|
1938
|
+
confidence: import_zod7.z.number().min(0).max(1).default(0.8).describe("Confidence score"),
|
|
1656
1939
|
/**
|
|
1657
1940
|
* Strategy used for routing
|
|
1658
1941
|
*/
|
|
1659
|
-
strategy: RoutingStrategySchema.describe("Strategy used for this decision"),
|
|
1942
|
+
strategy: RoutingStrategySchema.default("llm-based").describe("Strategy used for this decision"),
|
|
1660
1943
|
/**
|
|
1661
1944
|
* Timestamp of the routing decision
|
|
1662
1945
|
*/
|
|
1663
|
-
timestamp: import_zod7.z.number().
|
|
1664
|
-
})
|
|
1946
|
+
timestamp: import_zod7.z.number().default(() => Date.now()).describe("Timestamp of the decision")
|
|
1947
|
+
}).refine(
|
|
1948
|
+
(data) => data.targetAgent || data.targetAgents && data.targetAgents.length > 0,
|
|
1949
|
+
{ message: "Either targetAgent or targetAgents must be provided" }
|
|
1950
|
+
);
|
|
1665
1951
|
var WorkerCapabilitiesSchema = import_zod7.z.object({
|
|
1666
1952
|
/**
|
|
1667
1953
|
* Skills/capabilities the agent has
|
|
@@ -1893,15 +2179,26 @@ var MultiAgentStateConfig = {
|
|
|
1893
2179
|
description: "Error message if execution failed"
|
|
1894
2180
|
}
|
|
1895
2181
|
};
|
|
1896
|
-
var MultiAgentState = (0,
|
|
2182
|
+
var MultiAgentState = (0, import_core7.createStateAnnotation)(MultiAgentStateConfig);
|
|
1897
2183
|
|
|
1898
2184
|
// src/multi-agent/routing.ts
|
|
1899
2185
|
var import_messages4 = require("@langchain/core/messages");
|
|
2186
|
+
var import_core8 = require("@agentforge/core");
|
|
2187
|
+
var logLevel = process.env.LOG_LEVEL?.toLowerCase() || import_core8.LogLevel.INFO;
|
|
2188
|
+
var logger = (0, import_core8.createLogger)("multi-agent:routing", { level: logLevel });
|
|
1900
2189
|
async function executeTools(toolCalls, tools) {
|
|
1901
2190
|
const results = [];
|
|
2191
|
+
logger.debug("Executing tools", {
|
|
2192
|
+
toolCallCount: toolCalls.length,
|
|
2193
|
+
toolNames: toolCalls.map((tc) => tc.name)
|
|
2194
|
+
});
|
|
1902
2195
|
for (const toolCall of toolCalls) {
|
|
1903
2196
|
const tool = tools.find((t) => t.metadata.name === toolCall.name);
|
|
1904
2197
|
if (!tool) {
|
|
2198
|
+
logger.warn("Tool not found", {
|
|
2199
|
+
toolName: toolCall.name,
|
|
2200
|
+
availableTools: tools.map((t) => t.metadata.name)
|
|
2201
|
+
});
|
|
1905
2202
|
results.push(new import_messages4.ToolMessage({
|
|
1906
2203
|
content: `Error: Tool '${toolCall.name}' not found`,
|
|
1907
2204
|
tool_call_id: toolCall.id
|
|
@@ -1909,19 +2206,41 @@ async function executeTools(toolCalls, tools) {
|
|
|
1909
2206
|
continue;
|
|
1910
2207
|
}
|
|
1911
2208
|
try {
|
|
2209
|
+
logger.debug("Executing tool", {
|
|
2210
|
+
toolName: toolCall.name,
|
|
2211
|
+
args: toolCall.args
|
|
2212
|
+
});
|
|
1912
2213
|
const result = await tool.execute(toolCall.args);
|
|
1913
2214
|
const content = typeof result === "string" ? result : JSON.stringify(result);
|
|
2215
|
+
logger.debug("Tool execution successful", {
|
|
2216
|
+
toolName: toolCall.name,
|
|
2217
|
+
resultLength: content.length
|
|
2218
|
+
});
|
|
1914
2219
|
results.push(new import_messages4.ToolMessage({
|
|
1915
2220
|
content,
|
|
1916
2221
|
tool_call_id: toolCall.id
|
|
1917
2222
|
}));
|
|
1918
2223
|
} catch (error) {
|
|
2224
|
+
logger.error("Tool execution failed", {
|
|
2225
|
+
toolName: toolCall.name,
|
|
2226
|
+
error: error.message
|
|
2227
|
+
});
|
|
1919
2228
|
results.push(new import_messages4.ToolMessage({
|
|
1920
2229
|
content: `Error executing tool: ${error.message}`,
|
|
1921
2230
|
tool_call_id: toolCall.id
|
|
1922
2231
|
}));
|
|
1923
2232
|
}
|
|
1924
2233
|
}
|
|
2234
|
+
logger.debug("Tool execution complete", {
|
|
2235
|
+
successCount: results.filter((r) => {
|
|
2236
|
+
const content = typeof r.content === "string" ? r.content : JSON.stringify(r.content);
|
|
2237
|
+
return !content.startsWith("Error");
|
|
2238
|
+
}).length,
|
|
2239
|
+
errorCount: results.filter((r) => {
|
|
2240
|
+
const content = typeof r.content === "string" ? r.content : JSON.stringify(r.content);
|
|
2241
|
+
return content.startsWith("Error");
|
|
2242
|
+
}).length
|
|
2243
|
+
});
|
|
1925
2244
|
return results;
|
|
1926
2245
|
}
|
|
1927
2246
|
var DEFAULT_SUPERVISOR_SYSTEM_PROMPT = `You are a supervisor agent responsible for routing tasks to specialized worker agents.
|
|
@@ -1929,19 +2248,40 @@ var DEFAULT_SUPERVISOR_SYSTEM_PROMPT = `You are a supervisor agent responsible f
|
|
|
1929
2248
|
Your job is to:
|
|
1930
2249
|
1. Analyze the current task and context
|
|
1931
2250
|
2. Review available worker capabilities
|
|
1932
|
-
3. Select the most appropriate worker for the task
|
|
2251
|
+
3. Select the most appropriate worker(s) for the task
|
|
1933
2252
|
4. Provide clear reasoning for your decision
|
|
1934
2253
|
|
|
1935
|
-
|
|
2254
|
+
**IMPORTANT: You can route to MULTIPLE workers for parallel execution when:**
|
|
2255
|
+
- The task requires information from multiple domains (e.g., code + documentation)
|
|
2256
|
+
- Multiple workers have complementary expertise
|
|
2257
|
+
- Parallel execution would provide a more comprehensive answer
|
|
2258
|
+
|
|
2259
|
+
**Response Format:**
|
|
2260
|
+
|
|
2261
|
+
For SINGLE worker routing:
|
|
1936
2262
|
{
|
|
1937
2263
|
"targetAgent": "worker_id",
|
|
1938
2264
|
"reasoning": "explanation of why this worker is best suited",
|
|
1939
2265
|
"confidence": 0.0-1.0,
|
|
1940
2266
|
"strategy": "llm-based"
|
|
1941
|
-
}
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
For PARALLEL multi-worker routing:
|
|
2270
|
+
{
|
|
2271
|
+
"targetAgents": ["worker_id_1", "worker_id_2", ...],
|
|
2272
|
+
"reasoning": "explanation of why these workers should work in parallel",
|
|
2273
|
+
"confidence": 0.0-1.0,
|
|
2274
|
+
"strategy": "llm-based"
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
Choose parallel routing when the task benefits from multiple perspectives or data sources.`;
|
|
1942
2278
|
var llmBasedRouting = {
|
|
1943
2279
|
name: "llm-based",
|
|
1944
2280
|
async route(state, config) {
|
|
2281
|
+
logger.info("Starting LLM-based routing", {
|
|
2282
|
+
iteration: state.iteration,
|
|
2283
|
+
availableWorkers: Object.keys(state.workers).length
|
|
2284
|
+
});
|
|
1945
2285
|
if (!config.model) {
|
|
1946
2286
|
throw new Error("LLM-based routing requires a model to be configured");
|
|
1947
2287
|
}
|
|
@@ -1954,17 +2294,34 @@ var llmBasedRouting = {
|
|
|
1954
2294
|
const available = caps.available ? "available" : "busy";
|
|
1955
2295
|
return `- ${id}: Skills: [${skills}], Tools: [${tools2}], Status: ${available}, Workload: ${caps.currentWorkload}`;
|
|
1956
2296
|
}).join("\n");
|
|
2297
|
+
logger.debug("Worker capabilities", {
|
|
2298
|
+
workers: Object.entries(state.workers).map(([id, caps]) => ({
|
|
2299
|
+
id,
|
|
2300
|
+
skills: caps.skills,
|
|
2301
|
+
available: caps.available,
|
|
2302
|
+
workload: caps.currentWorkload
|
|
2303
|
+
}))
|
|
2304
|
+
});
|
|
1957
2305
|
const lastMessage = state.messages[state.messages.length - 1];
|
|
1958
2306
|
const taskContext = lastMessage?.content || state.input;
|
|
2307
|
+
logger.debug("Task context", {
|
|
2308
|
+
taskLength: taskContext.length,
|
|
2309
|
+
taskPreview: taskContext.substring(0, 100)
|
|
2310
|
+
});
|
|
1959
2311
|
const userPrompt = `Current task: ${taskContext}
|
|
1960
2312
|
|
|
1961
2313
|
Available workers:
|
|
1962
2314
|
${workerInfo}
|
|
1963
2315
|
|
|
1964
|
-
Select the best worker for this task and explain your reasoning.`;
|
|
2316
|
+
Select the best worker(s) for this task and explain your reasoning.`;
|
|
1965
2317
|
const conversationHistory = [];
|
|
1966
2318
|
let attempt = 0;
|
|
1967
2319
|
while (attempt < maxRetries) {
|
|
2320
|
+
logger.debug("LLM routing attempt", {
|
|
2321
|
+
attempt: attempt + 1,
|
|
2322
|
+
maxRetries,
|
|
2323
|
+
conversationHistoryLength: conversationHistory.length
|
|
2324
|
+
});
|
|
1968
2325
|
const messages = [
|
|
1969
2326
|
new import_messages4.SystemMessage(systemPrompt),
|
|
1970
2327
|
new import_messages4.HumanMessage(userPrompt),
|
|
@@ -1972,6 +2329,10 @@ Select the best worker for this task and explain your reasoning.`;
|
|
|
1972
2329
|
];
|
|
1973
2330
|
const response = await config.model.invoke(messages);
|
|
1974
2331
|
if (response.tool_calls && response.tool_calls.length > 0) {
|
|
2332
|
+
logger.info("LLM requested tool calls", {
|
|
2333
|
+
toolCount: response.tool_calls.length,
|
|
2334
|
+
toolNames: response.tool_calls.map((tc) => tc.name)
|
|
2335
|
+
});
|
|
1975
2336
|
if (tools.length === 0) {
|
|
1976
2337
|
throw new Error("LLM requested tool calls but no tools are configured");
|
|
1977
2338
|
}
|
|
@@ -1981,36 +2342,90 @@ Select the best worker for this task and explain your reasoning.`;
|
|
|
1981
2342
|
...toolResults
|
|
1982
2343
|
);
|
|
1983
2344
|
attempt++;
|
|
2345
|
+
logger.debug("Retrying routing with tool results", { attempt });
|
|
1984
2346
|
continue;
|
|
1985
2347
|
}
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
2348
|
+
logger.debug("Parsing routing decision from LLM response");
|
|
2349
|
+
let decision;
|
|
2350
|
+
if (response && typeof response === "object" && ("targetAgent" in response || "targetAgents" in response)) {
|
|
2351
|
+
logger.debug("Response is structured output", {
|
|
2352
|
+
hasTargetAgent: "targetAgent" in response,
|
|
2353
|
+
hasTargetAgents: "targetAgents" in response
|
|
2354
|
+
});
|
|
2355
|
+
decision = response;
|
|
2356
|
+
} else if (response.content) {
|
|
2357
|
+
if (typeof response.content === "string") {
|
|
2358
|
+
try {
|
|
2359
|
+
decision = JSON.parse(response.content);
|
|
2360
|
+
logger.debug("Parsed JSON from string response");
|
|
2361
|
+
} catch (error) {
|
|
2362
|
+
logger.error("Failed to parse routing decision", {
|
|
2363
|
+
content: response.content,
|
|
2364
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2365
|
+
});
|
|
2366
|
+
throw new Error(`Failed to parse routing decision from LLM. Expected JSON but got: ${response.content}`);
|
|
2367
|
+
}
|
|
2368
|
+
} else if (typeof response.content === "object") {
|
|
2369
|
+
logger.debug("Response content is already an object");
|
|
2370
|
+
decision = response.content;
|
|
2371
|
+
} else {
|
|
2372
|
+
logger.error("Unexpected response content type", {
|
|
2373
|
+
type: typeof response.content
|
|
2374
|
+
});
|
|
2375
|
+
throw new Error(`Unexpected response content type: ${typeof response.content}`);
|
|
2376
|
+
}
|
|
2377
|
+
} else {
|
|
2378
|
+
logger.error("Unexpected response format", {
|
|
2379
|
+
response: JSON.stringify(response)
|
|
2380
|
+
});
|
|
2381
|
+
throw new Error(`Unexpected response format: ${JSON.stringify(response)}`);
|
|
1998
2382
|
}
|
|
2383
|
+
const result = {
|
|
2384
|
+
targetAgent: decision.targetAgent,
|
|
2385
|
+
targetAgents: decision.targetAgents,
|
|
2386
|
+
reasoning: decision.reasoning,
|
|
2387
|
+
confidence: decision.confidence,
|
|
2388
|
+
strategy: "llm-based",
|
|
2389
|
+
timestamp: Date.now()
|
|
2390
|
+
};
|
|
2391
|
+
logger.info("LLM routing decision made", {
|
|
2392
|
+
targetAgent: result.targetAgent,
|
|
2393
|
+
targetAgents: result.targetAgents,
|
|
2394
|
+
isParallel: result.targetAgents && result.targetAgents.length > 1,
|
|
2395
|
+
confidence: result.confidence,
|
|
2396
|
+
reasoning: result.reasoning
|
|
2397
|
+
});
|
|
2398
|
+
return result;
|
|
1999
2399
|
}
|
|
2400
|
+
logger.error("Max tool retries exceeded", { maxRetries });
|
|
2000
2401
|
throw new Error(`Max tool retries (${maxRetries}) exceeded without routing decision`);
|
|
2001
2402
|
}
|
|
2002
2403
|
};
|
|
2003
2404
|
var roundRobinRouting = {
|
|
2004
2405
|
name: "round-robin",
|
|
2005
2406
|
async route(state, config) {
|
|
2407
|
+
logger.info("Starting round-robin routing", {
|
|
2408
|
+
iteration: state.iteration
|
|
2409
|
+
});
|
|
2006
2410
|
const availableWorkers = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id]) => id);
|
|
2411
|
+
logger.debug("Available workers for round-robin", {
|
|
2412
|
+
count: availableWorkers.length,
|
|
2413
|
+
workers: availableWorkers
|
|
2414
|
+
});
|
|
2007
2415
|
if (availableWorkers.length === 0) {
|
|
2416
|
+
logger.error("No available workers for round-robin routing");
|
|
2008
2417
|
throw new Error("No available workers for round-robin routing");
|
|
2009
2418
|
}
|
|
2010
2419
|
const lastRoutingIndex = state.routingHistory.length % availableWorkers.length;
|
|
2011
2420
|
const targetAgent = availableWorkers[lastRoutingIndex];
|
|
2421
|
+
logger.info("Round-robin routing decision", {
|
|
2422
|
+
targetAgent,
|
|
2423
|
+
index: lastRoutingIndex + 1,
|
|
2424
|
+
totalWorkers: availableWorkers.length
|
|
2425
|
+
});
|
|
2012
2426
|
return {
|
|
2013
2427
|
targetAgent,
|
|
2428
|
+
targetAgents: null,
|
|
2014
2429
|
reasoning: `Round-robin selection: worker ${lastRoutingIndex + 1} of ${availableWorkers.length}`,
|
|
2015
2430
|
confidence: 1,
|
|
2016
2431
|
strategy: "round-robin",
|
|
@@ -2021,8 +2436,15 @@ var roundRobinRouting = {
|
|
|
2021
2436
|
var skillBasedRouting = {
|
|
2022
2437
|
name: "skill-based",
|
|
2023
2438
|
async route(state, config) {
|
|
2439
|
+
logger.info("Starting skill-based routing", {
|
|
2440
|
+
iteration: state.iteration
|
|
2441
|
+
});
|
|
2024
2442
|
const lastMessage = state.messages[state.messages.length - 1];
|
|
2025
2443
|
const taskContent = (lastMessage?.content || state.input).toLowerCase();
|
|
2444
|
+
logger.debug("Task content for skill matching", {
|
|
2445
|
+
taskLength: taskContent.length,
|
|
2446
|
+
taskPreview: taskContent.substring(0, 100)
|
|
2447
|
+
});
|
|
2026
2448
|
const workerScores = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => {
|
|
2027
2449
|
const skillMatches = caps.skills.filter(
|
|
2028
2450
|
(skill) => taskContent.includes(skill.toLowerCase())
|
|
@@ -2033,13 +2455,23 @@ var skillBasedRouting = {
|
|
|
2033
2455
|
const score = skillMatches * 2 + toolMatches;
|
|
2034
2456
|
return { id, score, skills: caps.skills, tools: caps.tools };
|
|
2035
2457
|
}).filter((w) => w.score > 0).sort((a, b) => b.score - a.score);
|
|
2458
|
+
logger.debug("Worker skill scores", {
|
|
2459
|
+
scoredWorkers: workerScores.map((w) => ({ id: w.id, score: w.score }))
|
|
2460
|
+
});
|
|
2036
2461
|
if (workerScores.length === 0) {
|
|
2462
|
+
logger.warn("No skill matches found, using fallback");
|
|
2037
2463
|
const firstAvailable = Object.entries(state.workers).find(([_, caps]) => caps.available);
|
|
2038
2464
|
if (!firstAvailable) {
|
|
2465
|
+
logger.error("No available workers for skill-based routing");
|
|
2039
2466
|
throw new Error("No available workers for skill-based routing");
|
|
2040
2467
|
}
|
|
2468
|
+
logger.info("Skill-based routing fallback decision", {
|
|
2469
|
+
targetAgent: firstAvailable[0],
|
|
2470
|
+
confidence: 0.5
|
|
2471
|
+
});
|
|
2041
2472
|
return {
|
|
2042
2473
|
targetAgent: firstAvailable[0],
|
|
2474
|
+
targetAgents: null,
|
|
2043
2475
|
reasoning: "No skill matches found, using first available worker",
|
|
2044
2476
|
confidence: 0.5,
|
|
2045
2477
|
strategy: "skill-based",
|
|
@@ -2048,8 +2480,15 @@ var skillBasedRouting = {
|
|
|
2048
2480
|
}
|
|
2049
2481
|
const best = workerScores[0];
|
|
2050
2482
|
const confidence = Math.min(best.score / 5, 1);
|
|
2483
|
+
logger.info("Skill-based routing decision", {
|
|
2484
|
+
targetAgent: best.id,
|
|
2485
|
+
score: best.score,
|
|
2486
|
+
confidence,
|
|
2487
|
+
matchedSkills: best.skills
|
|
2488
|
+
});
|
|
2051
2489
|
return {
|
|
2052
2490
|
targetAgent: best.id,
|
|
2491
|
+
targetAgents: null,
|
|
2053
2492
|
reasoning: `Best skill match with score ${best.score} (skills: ${best.skills.join(", ")})`,
|
|
2054
2493
|
confidence,
|
|
2055
2494
|
strategy: "skill-based",
|
|
@@ -2060,15 +2499,29 @@ var skillBasedRouting = {
|
|
|
2060
2499
|
var loadBalancedRouting = {
|
|
2061
2500
|
name: "load-balanced",
|
|
2062
2501
|
async route(state, config) {
|
|
2502
|
+
logger.info("Starting load-balanced routing", {
|
|
2503
|
+
iteration: state.iteration
|
|
2504
|
+
});
|
|
2063
2505
|
const availableWorkers = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => ({ id, workload: caps.currentWorkload })).sort((a, b) => a.workload - b.workload);
|
|
2506
|
+
logger.debug("Worker workloads", {
|
|
2507
|
+
workers: availableWorkers.map((w) => ({ id: w.id, workload: w.workload }))
|
|
2508
|
+
});
|
|
2064
2509
|
if (availableWorkers.length === 0) {
|
|
2510
|
+
logger.error("No available workers for load-balanced routing");
|
|
2065
2511
|
throw new Error("No available workers for load-balanced routing");
|
|
2066
2512
|
}
|
|
2067
2513
|
const targetWorker = availableWorkers[0];
|
|
2068
2514
|
const avgWorkload = availableWorkers.reduce((sum, w) => sum + w.workload, 0) / availableWorkers.length;
|
|
2069
2515
|
const confidence = targetWorker.workload === 0 ? 1 : Math.max(0.5, 1 - targetWorker.workload / (avgWorkload * 2));
|
|
2516
|
+
logger.info("Load-balanced routing decision", {
|
|
2517
|
+
targetAgent: targetWorker.id,
|
|
2518
|
+
workload: targetWorker.workload,
|
|
2519
|
+
avgWorkload: avgWorkload.toFixed(1),
|
|
2520
|
+
confidence
|
|
2521
|
+
});
|
|
2070
2522
|
return {
|
|
2071
2523
|
targetAgent: targetWorker.id,
|
|
2524
|
+
targetAgents: null,
|
|
2072
2525
|
reasoning: `Lowest workload: ${targetWorker.workload} tasks (avg: ${avgWorkload.toFixed(1)})`,
|
|
2073
2526
|
confidence,
|
|
2074
2527
|
strategy: "load-balanced",
|
|
@@ -2079,13 +2532,24 @@ var loadBalancedRouting = {
|
|
|
2079
2532
|
var ruleBasedRouting = {
|
|
2080
2533
|
name: "rule-based",
|
|
2081
2534
|
async route(state, config) {
|
|
2535
|
+
logger.info("Starting rule-based routing", {
|
|
2536
|
+
iteration: state.iteration
|
|
2537
|
+
});
|
|
2082
2538
|
if (!config.routingFn) {
|
|
2539
|
+
logger.error("Rule-based routing requires a custom routing function");
|
|
2083
2540
|
throw new Error("Rule-based routing requires a custom routing function");
|
|
2084
2541
|
}
|
|
2085
|
-
|
|
2542
|
+
const decision = await config.routingFn(state);
|
|
2543
|
+
logger.info("Rule-based routing decision", {
|
|
2544
|
+
targetAgent: decision.targetAgent,
|
|
2545
|
+
targetAgents: decision.targetAgents,
|
|
2546
|
+
confidence: decision.confidence
|
|
2547
|
+
});
|
|
2548
|
+
return decision;
|
|
2086
2549
|
}
|
|
2087
2550
|
};
|
|
2088
2551
|
function getRoutingStrategy(name) {
|
|
2552
|
+
logger.debug("Getting routing strategy", { name });
|
|
2089
2553
|
switch (name) {
|
|
2090
2554
|
case "llm-based":
|
|
2091
2555
|
return llmBasedRouting;
|
|
@@ -2098,14 +2562,15 @@ function getRoutingStrategy(name) {
|
|
|
2098
2562
|
case "rule-based":
|
|
2099
2563
|
return ruleBasedRouting;
|
|
2100
2564
|
default:
|
|
2565
|
+
logger.error("Unknown routing strategy", { name });
|
|
2101
2566
|
throw new Error(`Unknown routing strategy: ${name}`);
|
|
2102
2567
|
}
|
|
2103
2568
|
}
|
|
2104
2569
|
|
|
2105
2570
|
// src/multi-agent/utils.ts
|
|
2106
|
-
var
|
|
2107
|
-
var
|
|
2108
|
-
var
|
|
2571
|
+
var import_core9 = require("@agentforge/core");
|
|
2572
|
+
var logLevel2 = process.env.LOG_LEVEL?.toLowerCase() || import_core9.LogLevel.INFO;
|
|
2573
|
+
var logger2 = (0, import_core9.createLogger)("multi-agent", { level: logLevel2 });
|
|
2109
2574
|
function isReActAgent(obj) {
|
|
2110
2575
|
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
|
|
2111
2576
|
(obj.constructor?.name === "CompiledGraph" || obj.constructor?.name === "CompiledStateGraph");
|
|
@@ -2113,9 +2578,9 @@ function isReActAgent(obj) {
|
|
|
2113
2578
|
function wrapReActAgent(workerId, agent, verbose = false) {
|
|
2114
2579
|
return async (state, config) => {
|
|
2115
2580
|
try {
|
|
2116
|
-
|
|
2581
|
+
logger2.debug("Wrapping ReAct agent execution", { workerId });
|
|
2117
2582
|
const task = state.messages[state.messages.length - 1]?.content || state.input;
|
|
2118
|
-
|
|
2583
|
+
logger2.debug("Extracted task", {
|
|
2119
2584
|
workerId,
|
|
2120
2585
|
taskPreview: task.substring(0, 100) + (task.length > 100 ? "..." : "")
|
|
2121
2586
|
});
|
|
@@ -2123,11 +2588,8 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2123
2588
|
(assignment) => assignment.workerId === workerId && !state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
|
|
2124
2589
|
);
|
|
2125
2590
|
if (!currentAssignment) {
|
|
2126
|
-
|
|
2127
|
-
return {
|
|
2128
|
-
currentAgent: "supervisor",
|
|
2129
|
-
status: "routing"
|
|
2130
|
-
};
|
|
2591
|
+
logger2.debug("No active assignment found", { workerId });
|
|
2592
|
+
return {};
|
|
2131
2593
|
}
|
|
2132
2594
|
const result = await agent.invoke(
|
|
2133
2595
|
{
|
|
@@ -2137,14 +2599,14 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2137
2599
|
// Pass through the config for checkpointing and interrupt support
|
|
2138
2600
|
);
|
|
2139
2601
|
const response = result.messages?.[result.messages.length - 1]?.content || "No response";
|
|
2140
|
-
|
|
2602
|
+
logger2.debug("Received response from ReAct agent", {
|
|
2141
2603
|
workerId,
|
|
2142
2604
|
responsePreview: response.substring(0, 100) + (response.length > 100 ? "..." : "")
|
|
2143
2605
|
});
|
|
2144
2606
|
const toolsUsed = result.actions?.map((action) => action.name).filter(Boolean) || [];
|
|
2145
2607
|
const uniqueTools = [...new Set(toolsUsed)];
|
|
2146
2608
|
if (uniqueTools.length > 0) {
|
|
2147
|
-
|
|
2609
|
+
logger2.debug("Tools used by ReAct agent", { workerId, tools: uniqueTools });
|
|
2148
2610
|
}
|
|
2149
2611
|
const taskResult = {
|
|
2150
2612
|
assignmentId: currentAssignment.id,
|
|
@@ -2159,16 +2621,14 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2159
2621
|
}
|
|
2160
2622
|
};
|
|
2161
2623
|
return {
|
|
2162
|
-
completedTasks: [taskResult]
|
|
2163
|
-
currentAgent: "supervisor",
|
|
2164
|
-
status: "routing"
|
|
2624
|
+
completedTasks: [taskResult]
|
|
2165
2625
|
};
|
|
2166
2626
|
} catch (error) {
|
|
2167
2627
|
if (error && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt") {
|
|
2168
|
-
|
|
2628
|
+
logger2.debug("GraphInterrupt detected - re-throwing", { workerId });
|
|
2169
2629
|
throw error;
|
|
2170
2630
|
}
|
|
2171
|
-
|
|
2631
|
+
logger2.error("Error in ReAct agent execution", {
|
|
2172
2632
|
workerId,
|
|
2173
2633
|
error: error instanceof Error ? error.message : String(error),
|
|
2174
2634
|
stack: error instanceof Error ? error.stack : void 0
|
|
@@ -2201,7 +2661,9 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2201
2661
|
|
|
2202
2662
|
// src/multi-agent/nodes.ts
|
|
2203
2663
|
var import_messages5 = require("@langchain/core/messages");
|
|
2204
|
-
var
|
|
2664
|
+
var import_core10 = require("@agentforge/core");
|
|
2665
|
+
var logLevel3 = process.env.LOG_LEVEL?.toLowerCase() || import_core10.LogLevel.INFO;
|
|
2666
|
+
var logger3 = (0, import_core10.createLogger)("multi-agent:nodes", { level: logLevel3 });
|
|
2205
2667
|
var DEFAULT_AGGREGATOR_SYSTEM_PROMPT = `You are an aggregator agent responsible for combining results from multiple worker agents.
|
|
2206
2668
|
|
|
2207
2669
|
Your job is to:
|
|
@@ -2219,46 +2681,97 @@ function createSupervisorNode(config) {
|
|
|
2219
2681
|
} = config;
|
|
2220
2682
|
return async (state) => {
|
|
2221
2683
|
try {
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2684
|
+
logger3.info("Supervisor node executing", {
|
|
2685
|
+
iteration: state.iteration,
|
|
2686
|
+
maxIterations,
|
|
2687
|
+
activeAssignments: state.activeAssignments.length,
|
|
2688
|
+
completedTasks: state.completedTasks.length
|
|
2689
|
+
});
|
|
2690
|
+
logger3.debug(`Routing iteration ${state.iteration}/${maxIterations}`);
|
|
2225
2691
|
if (state.iteration >= maxIterations) {
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2692
|
+
logger3.warn("Max iterations reached", {
|
|
2693
|
+
iteration: state.iteration,
|
|
2694
|
+
maxIterations
|
|
2695
|
+
});
|
|
2696
|
+
logger3.debug("Max iterations reached, moving to aggregation");
|
|
2229
2697
|
return {
|
|
2230
2698
|
status: "aggregating",
|
|
2231
2699
|
currentAgent: "aggregator"
|
|
2232
2700
|
};
|
|
2233
2701
|
}
|
|
2234
2702
|
const allCompleted = state.activeAssignments.every(
|
|
2235
|
-
(
|
|
2703
|
+
(assignment) => state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
|
|
2236
2704
|
);
|
|
2705
|
+
logger3.debug("Checking task completion", {
|
|
2706
|
+
activeAssignments: state.activeAssignments.length,
|
|
2707
|
+
completedTasks: state.completedTasks.length,
|
|
2708
|
+
allCompleted
|
|
2709
|
+
});
|
|
2237
2710
|
if (allCompleted && state.activeAssignments.length > 0) {
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
}
|
|
2711
|
+
logger3.info("All tasks completed, moving to aggregation", {
|
|
2712
|
+
completedCount: state.completedTasks.length
|
|
2713
|
+
});
|
|
2714
|
+
logger3.debug("All tasks completed, moving to aggregation");
|
|
2241
2715
|
return {
|
|
2242
2716
|
status: "aggregating",
|
|
2243
2717
|
currentAgent: "aggregator"
|
|
2244
2718
|
};
|
|
2245
2719
|
}
|
|
2720
|
+
logger3.debug("Getting routing strategy", { strategy });
|
|
2246
2721
|
const routingImpl = getRoutingStrategy(strategy);
|
|
2247
2722
|
const decision = await routingImpl.route(state, config);
|
|
2248
|
-
|
|
2249
|
-
|
|
2723
|
+
const targetAgents = decision.targetAgents && decision.targetAgents.length > 0 ? decision.targetAgents : decision.targetAgent ? [decision.targetAgent] : [];
|
|
2724
|
+
logger3.debug("Target agents determined", {
|
|
2725
|
+
targetAgents,
|
|
2726
|
+
isParallel: targetAgents.length > 1,
|
|
2727
|
+
decision: {
|
|
2728
|
+
reasoning: decision.reasoning,
|
|
2729
|
+
confidence: decision.confidence
|
|
2730
|
+
}
|
|
2731
|
+
});
|
|
2732
|
+
if (targetAgents.length === 0) {
|
|
2733
|
+
logger3.error("No target agents specified in routing decision");
|
|
2734
|
+
throw new Error("Routing decision must specify at least one target agent");
|
|
2250
2735
|
}
|
|
2251
|
-
|
|
2736
|
+
if (targetAgents.length === 1) {
|
|
2737
|
+
logger3.info("Routing to single agent", {
|
|
2738
|
+
targetAgent: targetAgents[0],
|
|
2739
|
+
reasoning: decision.reasoning,
|
|
2740
|
+
confidence: decision.confidence
|
|
2741
|
+
});
|
|
2742
|
+
} else {
|
|
2743
|
+
logger3.info("Routing to multiple agents in parallel", {
|
|
2744
|
+
targetAgents,
|
|
2745
|
+
count: targetAgents.length,
|
|
2746
|
+
reasoning: decision.reasoning,
|
|
2747
|
+
confidence: decision.confidence
|
|
2748
|
+
});
|
|
2749
|
+
}
|
|
2750
|
+
if (targetAgents.length === 1) {
|
|
2751
|
+
logger3.debug(`Routing to ${targetAgents[0]}: ${decision.reasoning}`);
|
|
2752
|
+
} else {
|
|
2753
|
+
logger3.debug(`Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`);
|
|
2754
|
+
}
|
|
2755
|
+
const task = state.messages[state.messages.length - 1]?.content || state.input;
|
|
2756
|
+
const assignments = targetAgents.map((workerId) => ({
|
|
2252
2757
|
id: `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
2253
|
-
workerId
|
|
2254
|
-
task
|
|
2758
|
+
workerId,
|
|
2759
|
+
task,
|
|
2255
2760
|
priority: 5,
|
|
2256
2761
|
assignedAt: Date.now()
|
|
2257
|
-
};
|
|
2258
|
-
|
|
2762
|
+
}));
|
|
2763
|
+
logger3.debug("Created task assignments", {
|
|
2764
|
+
assignmentCount: assignments.length,
|
|
2765
|
+
assignments: assignments.map((a) => ({
|
|
2766
|
+
id: a.id,
|
|
2767
|
+
workerId: a.workerId,
|
|
2768
|
+
taskLength: a.task.length
|
|
2769
|
+
}))
|
|
2770
|
+
});
|
|
2771
|
+
const messages = assignments.map((assignment) => ({
|
|
2259
2772
|
id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
2260
2773
|
from: "supervisor",
|
|
2261
|
-
to: [
|
|
2774
|
+
to: [assignment.workerId],
|
|
2262
2775
|
type: "task_assignment",
|
|
2263
2776
|
content: assignment.task,
|
|
2264
2777
|
timestamp: Date.now(),
|
|
@@ -2266,17 +2779,29 @@ function createSupervisorNode(config) {
|
|
|
2266
2779
|
assignmentId: assignment.id,
|
|
2267
2780
|
priority: assignment.priority
|
|
2268
2781
|
}
|
|
2269
|
-
};
|
|
2782
|
+
}));
|
|
2783
|
+
logger3.info("Supervisor routing complete", {
|
|
2784
|
+
currentAgent: targetAgents.join(","),
|
|
2785
|
+
status: "executing",
|
|
2786
|
+
assignmentCount: assignments.length,
|
|
2787
|
+
nextIteration: state.iteration + 1
|
|
2788
|
+
});
|
|
2270
2789
|
return {
|
|
2271
|
-
currentAgent:
|
|
2790
|
+
currentAgent: targetAgents.join(","),
|
|
2791
|
+
// Store all agents (for backward compat)
|
|
2272
2792
|
status: "executing",
|
|
2273
2793
|
routingHistory: [decision],
|
|
2274
|
-
activeAssignments:
|
|
2275
|
-
|
|
2794
|
+
activeAssignments: assignments,
|
|
2795
|
+
// Multiple assignments for parallel execution!
|
|
2796
|
+
messages,
|
|
2276
2797
|
iteration: state.iteration + 1
|
|
2277
2798
|
};
|
|
2278
2799
|
} catch (error) {
|
|
2279
|
-
|
|
2800
|
+
logger3.error("Supervisor node error", {
|
|
2801
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2802
|
+
stack: error instanceof Error ? error.stack : void 0,
|
|
2803
|
+
iteration: state.iteration
|
|
2804
|
+
});
|
|
2280
2805
|
return {
|
|
2281
2806
|
status: "failed",
|
|
2282
2807
|
error: error instanceof Error ? error.message : "Unknown error in supervisor"
|
|
@@ -2297,43 +2822,52 @@ function createWorkerNode(config) {
|
|
|
2297
2822
|
} = config;
|
|
2298
2823
|
return async (state, runConfig) => {
|
|
2299
2824
|
try {
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2825
|
+
logger3.info("Worker node executing", {
|
|
2826
|
+
workerId: id,
|
|
2827
|
+
iteration: state.iteration,
|
|
2828
|
+
activeAssignments: state.activeAssignments.length
|
|
2829
|
+
});
|
|
2303
2830
|
const currentAssignment = state.activeAssignments.find(
|
|
2304
2831
|
(assignment) => assignment.workerId === id && !state.completedTasks.some((task) => task.assignmentId === assignment.id)
|
|
2305
2832
|
);
|
|
2306
2833
|
if (!currentAssignment) {
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
};
|
|
2834
|
+
logger3.debug("No active assignment found for worker", {
|
|
2835
|
+
workerId: id,
|
|
2836
|
+
totalActiveAssignments: state.activeAssignments.length,
|
|
2837
|
+
completedTasks: state.completedTasks.length
|
|
2838
|
+
});
|
|
2839
|
+
return {};
|
|
2314
2840
|
}
|
|
2841
|
+
logger3.info("Worker processing assignment", {
|
|
2842
|
+
workerId: id,
|
|
2843
|
+
assignmentId: currentAssignment.id,
|
|
2844
|
+
taskLength: currentAssignment.task.length,
|
|
2845
|
+
taskPreview: currentAssignment.task.substring(0, 100)
|
|
2846
|
+
});
|
|
2315
2847
|
if (executeFn) {
|
|
2316
|
-
|
|
2317
|
-
console.log(`[Worker:${id}] Using custom executeFn`);
|
|
2318
|
-
}
|
|
2848
|
+
logger3.debug("Using custom execution function", { workerId: id });
|
|
2319
2849
|
return await executeFn(state, runConfig);
|
|
2320
2850
|
}
|
|
2321
2851
|
if (agent) {
|
|
2322
2852
|
if (isReActAgent(agent)) {
|
|
2323
|
-
|
|
2324
|
-
console.log(`[Worker:${id}] Using ReAct agent (auto-wrapped)`);
|
|
2325
|
-
}
|
|
2853
|
+
logger3.debug("Using ReAct agent", { workerId: id });
|
|
2326
2854
|
const wrappedFn = wrapReActAgent(id, agent, verbose);
|
|
2327
2855
|
return await wrappedFn(state, runConfig);
|
|
2328
2856
|
} else {
|
|
2329
|
-
|
|
2857
|
+
logger3.warn("Agent provided but not a ReAct agent, falling back", { workerId: id });
|
|
2330
2858
|
}
|
|
2331
2859
|
}
|
|
2332
2860
|
if (!model) {
|
|
2861
|
+
logger3.error("Worker missing required configuration", { workerId: id });
|
|
2333
2862
|
throw new Error(
|
|
2334
2863
|
`Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
|
|
2335
2864
|
);
|
|
2336
2865
|
}
|
|
2866
|
+
logger3.debug("Using default LLM execution", {
|
|
2867
|
+
workerId: id,
|
|
2868
|
+
hasTools: tools.length > 0,
|
|
2869
|
+
toolCount: tools.length
|
|
2870
|
+
});
|
|
2337
2871
|
const defaultSystemPrompt = `You are a specialized worker agent with the following capabilities:
|
|
2338
2872
|
Skills: ${capabilities.skills.join(", ")}
|
|
2339
2873
|
Tools: ${capabilities.tools.join(", ")}
|
|
@@ -2345,14 +2879,23 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
|
|
|
2345
2879
|
];
|
|
2346
2880
|
let modelToUse = model;
|
|
2347
2881
|
if (tools.length > 0 && model.bindTools) {
|
|
2348
|
-
|
|
2882
|
+
logger3.debug("Binding tools to model", {
|
|
2883
|
+
workerId: id,
|
|
2884
|
+
toolCount: tools.length,
|
|
2885
|
+
toolNames: tools.map((t) => t.metadata.name)
|
|
2886
|
+
});
|
|
2887
|
+
const langchainTools = (0, import_core10.toLangChainTools)(tools);
|
|
2349
2888
|
modelToUse = model.bindTools(langchainTools);
|
|
2350
2889
|
}
|
|
2890
|
+
logger3.debug("Invoking LLM", { workerId: id });
|
|
2351
2891
|
const response = await modelToUse.invoke(messages);
|
|
2352
2892
|
const result = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2893
|
+
logger3.info("Worker task completed", {
|
|
2894
|
+
workerId: id,
|
|
2895
|
+
assignmentId: currentAssignment.id,
|
|
2896
|
+
resultLength: result.length,
|
|
2897
|
+
resultPreview: result.substring(0, 100)
|
|
2898
|
+
});
|
|
2356
2899
|
const taskResult = {
|
|
2357
2900
|
assignmentId: currentAssignment.id,
|
|
2358
2901
|
workerId: id,
|
|
@@ -2382,22 +2925,33 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
|
|
|
2382
2925
|
currentWorkload: Math.max(0, capabilities.currentWorkload - 1)
|
|
2383
2926
|
}
|
|
2384
2927
|
};
|
|
2928
|
+
logger3.debug("Worker state update", {
|
|
2929
|
+
workerId: id,
|
|
2930
|
+
newWorkload: updatedWorkers[id].currentWorkload
|
|
2931
|
+
});
|
|
2385
2932
|
return {
|
|
2386
2933
|
completedTasks: [taskResult],
|
|
2387
2934
|
messages: [message],
|
|
2388
|
-
workers: updatedWorkers
|
|
2389
|
-
currentAgent: "supervisor",
|
|
2390
|
-
status: "routing"
|
|
2935
|
+
workers: updatedWorkers
|
|
2391
2936
|
};
|
|
2392
2937
|
} catch (error) {
|
|
2393
2938
|
if (error && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt") {
|
|
2939
|
+
logger3.info("GraphInterrupt detected, re-throwing", { workerId: id });
|
|
2394
2940
|
throw error;
|
|
2395
2941
|
}
|
|
2396
|
-
|
|
2942
|
+
logger3.error("Worker node error", {
|
|
2943
|
+
workerId: id,
|
|
2944
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2945
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
2946
|
+
});
|
|
2397
2947
|
const currentAssignment = state.activeAssignments.find(
|
|
2398
2948
|
(assignment) => assignment.workerId === id
|
|
2399
2949
|
);
|
|
2400
2950
|
if (currentAssignment) {
|
|
2951
|
+
logger3.warn("Creating error result for assignment", {
|
|
2952
|
+
workerId: id,
|
|
2953
|
+
assignmentId: currentAssignment.id
|
|
2954
|
+
});
|
|
2401
2955
|
const errorResult = {
|
|
2402
2956
|
assignmentId: currentAssignment.id,
|
|
2403
2957
|
workerId: id,
|
|
@@ -2412,6 +2966,7 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
|
|
|
2412
2966
|
status: "routing"
|
|
2413
2967
|
};
|
|
2414
2968
|
}
|
|
2969
|
+
logger3.error("No assignment found for error handling", { workerId: id });
|
|
2415
2970
|
return {
|
|
2416
2971
|
status: "failed",
|
|
2417
2972
|
error: error instanceof Error ? error.message : `Unknown error in worker ${id}`
|
|
@@ -2428,29 +2983,44 @@ function createAggregatorNode(config = {}) {
|
|
|
2428
2983
|
} = config;
|
|
2429
2984
|
return async (state) => {
|
|
2430
2985
|
try {
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2986
|
+
logger3.info("Aggregator node executing", {
|
|
2987
|
+
completedTasks: state.completedTasks.length,
|
|
2988
|
+
successfulTasks: state.completedTasks.filter((t) => t.success).length,
|
|
2989
|
+
failedTasks: state.completedTasks.filter((t) => !t.success).length
|
|
2990
|
+
});
|
|
2991
|
+
logger3.debug("Combining results from workers");
|
|
2434
2992
|
if (aggregateFn) {
|
|
2993
|
+
logger3.debug("Using custom aggregation function");
|
|
2435
2994
|
const response2 = await aggregateFn(state);
|
|
2995
|
+
logger3.info("Custom aggregation complete", {
|
|
2996
|
+
responseLength: response2.length
|
|
2997
|
+
});
|
|
2436
2998
|
return {
|
|
2437
2999
|
response: response2,
|
|
2438
3000
|
status: "completed"
|
|
2439
3001
|
};
|
|
2440
3002
|
}
|
|
2441
3003
|
if (state.completedTasks.length === 0) {
|
|
3004
|
+
logger3.warn("No completed tasks to aggregate");
|
|
2442
3005
|
return {
|
|
2443
3006
|
response: "No tasks were completed.",
|
|
2444
3007
|
status: "completed"
|
|
2445
3008
|
};
|
|
2446
3009
|
}
|
|
2447
3010
|
if (!model) {
|
|
3011
|
+
logger3.debug("No model provided, concatenating results");
|
|
2448
3012
|
const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
|
|
3013
|
+
logger3.info("Simple concatenation complete", {
|
|
3014
|
+
resultLength: combinedResults.length
|
|
3015
|
+
});
|
|
2449
3016
|
return {
|
|
2450
3017
|
response: combinedResults || "No successful results to aggregate.",
|
|
2451
3018
|
status: "completed"
|
|
2452
3019
|
};
|
|
2453
3020
|
}
|
|
3021
|
+
logger3.debug("Using LLM for intelligent aggregation", {
|
|
3022
|
+
taskCount: state.completedTasks.length
|
|
3023
|
+
});
|
|
2454
3024
|
const taskResults = state.completedTasks.map((task, idx) => {
|
|
2455
3025
|
const status = task.success ? "\u2713" : "\u2717";
|
|
2456
3026
|
const result = task.success ? task.result : `Error: ${task.error}`;
|
|
@@ -2467,17 +3037,24 @@ Please synthesize these results into a comprehensive response that addresses the
|
|
|
2467
3037
|
new import_messages5.SystemMessage(systemPrompt),
|
|
2468
3038
|
new import_messages5.HumanMessage(userPrompt)
|
|
2469
3039
|
];
|
|
3040
|
+
logger3.debug("Invoking aggregation LLM");
|
|
2470
3041
|
const response = await model.invoke(messages);
|
|
2471
3042
|
const aggregatedResponse = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
3043
|
+
logger3.info("Aggregation complete", {
|
|
3044
|
+
responseLength: aggregatedResponse.length,
|
|
3045
|
+
responsePreview: aggregatedResponse.substring(0, 100)
|
|
3046
|
+
});
|
|
3047
|
+
logger3.debug("Aggregation complete");
|
|
2475
3048
|
return {
|
|
2476
3049
|
response: aggregatedResponse,
|
|
2477
3050
|
status: "completed"
|
|
2478
3051
|
};
|
|
2479
3052
|
} catch (error) {
|
|
2480
|
-
|
|
3053
|
+
logger3.error("Aggregator node error", {
|
|
3054
|
+
error: error instanceof Error ? error.message : String(error),
|
|
3055
|
+
stack: error instanceof Error ? error.stack : void 0,
|
|
3056
|
+
completedTasks: state.completedTasks.length
|
|
3057
|
+
});
|
|
2481
3058
|
return {
|
|
2482
3059
|
status: "failed",
|
|
2483
3060
|
error: error instanceof Error ? error.message : "Unknown error in aggregator"
|
|
@@ -2488,7 +3065,9 @@ Please synthesize these results into a comprehensive response that addresses the
|
|
|
2488
3065
|
|
|
2489
3066
|
// src/multi-agent/agent.ts
|
|
2490
3067
|
var import_langgraph4 = require("@langchain/langgraph");
|
|
2491
|
-
var
|
|
3068
|
+
var import_core11 = require("@agentforge/core");
|
|
3069
|
+
var logLevel4 = process.env.LOG_LEVEL?.toLowerCase() || import_core11.LogLevel.INFO;
|
|
3070
|
+
var logger4 = (0, import_core11.createLogger)("multi-agent:system", { level: logLevel4 });
|
|
2492
3071
|
function createMultiAgentSystem(config) {
|
|
2493
3072
|
const {
|
|
2494
3073
|
supervisor,
|
|
@@ -2500,10 +3079,16 @@ function createMultiAgentSystem(config) {
|
|
|
2500
3079
|
} = config;
|
|
2501
3080
|
const workflow = new import_langgraph4.StateGraph(MultiAgentState);
|
|
2502
3081
|
let supervisorConfig = { ...supervisor, maxIterations, verbose };
|
|
2503
|
-
if (supervisor.model
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
3082
|
+
if (supervisor.model) {
|
|
3083
|
+
let configuredModel = supervisor.model;
|
|
3084
|
+
if (supervisor.strategy === "llm-based") {
|
|
3085
|
+
configuredModel = configuredModel.withStructuredOutput(RoutingDecisionSchema);
|
|
3086
|
+
}
|
|
3087
|
+
if (supervisor.tools && supervisor.tools.length > 0) {
|
|
3088
|
+
const langchainTools = (0, import_core11.toLangChainTools)(supervisor.tools);
|
|
3089
|
+
configuredModel = configuredModel.bindTools(langchainTools);
|
|
3090
|
+
}
|
|
3091
|
+
supervisorConfig.model = configuredModel;
|
|
2507
3092
|
}
|
|
2508
3093
|
const supervisorNode = createSupervisorNode(supervisorConfig);
|
|
2509
3094
|
workflow.addNode("supervisor", supervisorNode);
|
|
@@ -2524,21 +3109,49 @@ function createMultiAgentSystem(config) {
|
|
|
2524
3109
|
});
|
|
2525
3110
|
workflow.addNode("aggregator", aggregatorNode);
|
|
2526
3111
|
const supervisorRouter = (state) => {
|
|
3112
|
+
logger4.debug("Supervisor router executing", {
|
|
3113
|
+
status: state.status,
|
|
3114
|
+
currentAgent: state.currentAgent,
|
|
3115
|
+
iteration: state.iteration
|
|
3116
|
+
});
|
|
2527
3117
|
if (state.status === "completed" || state.status === "failed") {
|
|
3118
|
+
logger4.info("Supervisor router: ending workflow", { status: state.status });
|
|
2528
3119
|
return import_langgraph4.END;
|
|
2529
3120
|
}
|
|
2530
3121
|
if (state.status === "aggregating") {
|
|
3122
|
+
logger4.info("Supervisor router: routing to aggregator");
|
|
2531
3123
|
return "aggregator";
|
|
2532
3124
|
}
|
|
2533
3125
|
if (state.currentAgent && state.currentAgent !== "supervisor") {
|
|
3126
|
+
if (state.currentAgent.includes(",")) {
|
|
3127
|
+
const agents = state.currentAgent.split(",").map((a) => a.trim());
|
|
3128
|
+
logger4.info("Supervisor router: parallel routing", {
|
|
3129
|
+
agents,
|
|
3130
|
+
count: agents.length
|
|
3131
|
+
});
|
|
3132
|
+
return agents;
|
|
3133
|
+
}
|
|
3134
|
+
logger4.info("Supervisor router: single agent routing", {
|
|
3135
|
+
targetAgent: state.currentAgent
|
|
3136
|
+
});
|
|
2534
3137
|
return state.currentAgent;
|
|
2535
3138
|
}
|
|
3139
|
+
logger4.debug("Supervisor router: staying at supervisor");
|
|
2536
3140
|
return "supervisor";
|
|
2537
3141
|
};
|
|
2538
3142
|
const workerRouter = (state) => {
|
|
3143
|
+
logger4.debug("Worker router executing", {
|
|
3144
|
+
iteration: state.iteration,
|
|
3145
|
+
completedTasks: state.completedTasks.length
|
|
3146
|
+
});
|
|
3147
|
+
logger4.debug("Worker router: returning to supervisor");
|
|
2539
3148
|
return "supervisor";
|
|
2540
3149
|
};
|
|
2541
3150
|
const aggregatorRouter = (state) => {
|
|
3151
|
+
logger4.info("Aggregator router: ending workflow", {
|
|
3152
|
+
completedTasks: state.completedTasks.length,
|
|
3153
|
+
status: state.status
|
|
3154
|
+
});
|
|
2542
3155
|
return import_langgraph4.END;
|
|
2543
3156
|
};
|
|
2544
3157
|
workflow.setEntryPoint("supervisor");
|
|
@@ -2732,11 +3345,14 @@ function registerWorkers(system, workers) {
|
|
|
2732
3345
|
ToolCallSchema,
|
|
2733
3346
|
ToolResultSchema,
|
|
2734
3347
|
WorkerCapabilitiesSchema,
|
|
3348
|
+
buildDeduplicationMetrics,
|
|
3349
|
+
calculateDeduplicationSavings,
|
|
2735
3350
|
createAggregatorNode,
|
|
2736
3351
|
createExecutorNode,
|
|
2737
3352
|
createFinisherNode,
|
|
2738
3353
|
createGeneratorNode,
|
|
2739
3354
|
createMultiAgentSystem,
|
|
3355
|
+
createPatternLogger,
|
|
2740
3356
|
createPlanExecuteAgent,
|
|
2741
3357
|
createPlannerNode,
|
|
2742
3358
|
createReActAgent,
|
|
@@ -2748,6 +3364,7 @@ function registerWorkers(system, workers) {
|
|
|
2748
3364
|
createReviserNode,
|
|
2749
3365
|
createSupervisorNode,
|
|
2750
3366
|
createWorkerNode,
|
|
3367
|
+
generateToolCallCacheKey,
|
|
2751
3368
|
getRoutingStrategy,
|
|
2752
3369
|
llmBasedRouting,
|
|
2753
3370
|
loadBalancedRouting,
|