@agentforge/patterns 0.15.8 → 0.15.10
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 +121 -79
- package/dist/index.js +127 -80
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -343,54 +343,101 @@ function handleNodeError(error, context, verbose = false) {
|
|
|
343
343
|
var reasoningLogger = createPatternLogger("agentforge:patterns:react:reasoning");
|
|
344
344
|
var actionLogger = createPatternLogger("agentforge:patterns:react:action");
|
|
345
345
|
var observationLogger = createPatternLogger("agentforge:patterns:react:observation");
|
|
346
|
+
function normalizeConversationMessage(message) {
|
|
347
|
+
switch (message.role) {
|
|
348
|
+
case "user":
|
|
349
|
+
return new import_messages.HumanMessage(message.content);
|
|
350
|
+
case "assistant":
|
|
351
|
+
return new import_messages.AIMessage(message.content);
|
|
352
|
+
case "system":
|
|
353
|
+
return new import_messages.SystemMessage(message.content);
|
|
354
|
+
case "tool":
|
|
355
|
+
if (!message.tool_call_id) {
|
|
356
|
+
reasoningLogger.warn(
|
|
357
|
+
"Tool message missing tool_call_id; falling back to human message",
|
|
358
|
+
message.name ? { name: message.name } : void 0
|
|
359
|
+
);
|
|
360
|
+
return new import_messages.HumanMessage(message.content);
|
|
361
|
+
}
|
|
362
|
+
return new import_messages.ToolMessage({
|
|
363
|
+
content: message.content,
|
|
364
|
+
tool_call_id: message.tool_call_id,
|
|
365
|
+
name: message.name
|
|
366
|
+
});
|
|
367
|
+
default:
|
|
368
|
+
return new import_messages.HumanMessage(message.content);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
function buildReasoningMessages(systemPrompt, stateMessages, scratchpad) {
|
|
372
|
+
const messages = [
|
|
373
|
+
new import_messages.SystemMessage(systemPrompt),
|
|
374
|
+
...stateMessages.map(normalizeConversationMessage)
|
|
375
|
+
];
|
|
376
|
+
if (scratchpad.length > 0) {
|
|
377
|
+
messages.push(new import_messages.SystemMessage(`Previous steps:
|
|
378
|
+
${formatScratchpad(scratchpad)}`));
|
|
379
|
+
}
|
|
380
|
+
return messages;
|
|
381
|
+
}
|
|
382
|
+
function extractToolCalls(response) {
|
|
383
|
+
if (!response.tool_calls || response.tool_calls.length === 0) {
|
|
384
|
+
return [];
|
|
385
|
+
}
|
|
386
|
+
return response.tool_calls.map((toolCall) => ({
|
|
387
|
+
id: toolCall.id || `call_${Date.now()}_${Math.random()}`,
|
|
388
|
+
name: toolCall.name,
|
|
389
|
+
arguments: toolCall.args ?? {},
|
|
390
|
+
timestamp: Date.now()
|
|
391
|
+
}));
|
|
392
|
+
}
|
|
393
|
+
function formatObservationContent(observation) {
|
|
394
|
+
if (observation.error) {
|
|
395
|
+
return `Error: ${observation.error}`;
|
|
396
|
+
}
|
|
397
|
+
return stringifyObservationResult(observation.result, 2);
|
|
398
|
+
}
|
|
399
|
+
function formatActionSummary(actions) {
|
|
400
|
+
return actions.map((action) => `${action.name}(${JSON.stringify(action.arguments)})`).join(", ");
|
|
401
|
+
}
|
|
402
|
+
function formatObservationSummary(observations) {
|
|
403
|
+
return observations.map((observation) => {
|
|
404
|
+
if (observation.error) {
|
|
405
|
+
return `Error: ${observation.error}`;
|
|
406
|
+
}
|
|
407
|
+
return stringifyObservationResult(observation.result);
|
|
408
|
+
}).join("; ");
|
|
409
|
+
}
|
|
410
|
+
function stringifyObservationResult(result, space) {
|
|
411
|
+
if (typeof result === "string") {
|
|
412
|
+
return result;
|
|
413
|
+
}
|
|
414
|
+
const stringified = JSON.stringify(result, null, space);
|
|
415
|
+
return stringified ?? String(result);
|
|
416
|
+
}
|
|
417
|
+
function getLatestThought(thoughts) {
|
|
418
|
+
return thoughts[thoughts.length - 1]?.content ?? "";
|
|
419
|
+
}
|
|
420
|
+
function debugIfVerbose(logger4, verbose, message, data) {
|
|
421
|
+
if (verbose) {
|
|
422
|
+
logger4.debug(message, data);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
346
425
|
function createReasoningNode(llm, tools, systemPrompt, maxIterations, verbose = false) {
|
|
347
426
|
const langchainTools = (0, import_core3.toLangChainTools)(tools);
|
|
348
427
|
const llmWithTools = llm.bindTools ? llm.bindTools(langchainTools) : llm;
|
|
349
428
|
return async (state) => {
|
|
350
429
|
const currentIteration = state.iteration || 0;
|
|
351
430
|
const startTime = Date.now();
|
|
352
|
-
reasoningLogger
|
|
431
|
+
debugIfVerbose(reasoningLogger, verbose, "Reasoning iteration started", {
|
|
353
432
|
iteration: currentIteration + 1,
|
|
354
433
|
maxIterations,
|
|
355
|
-
observationCount: state.observations
|
|
356
|
-
hasActions:
|
|
434
|
+
observationCount: state.observations.length,
|
|
435
|
+
hasActions: state.actions.length > 0
|
|
357
436
|
});
|
|
358
|
-
const
|
|
359
|
-
const messages = [
|
|
360
|
-
new import_messages.SystemMessage(systemPrompt),
|
|
361
|
-
...stateMessages.map((msg) => {
|
|
362
|
-
if (msg.role === "user") return new import_messages.HumanMessage(msg.content);
|
|
363
|
-
if (msg.role === "assistant") return new import_messages.AIMessage(msg.content);
|
|
364
|
-
if (msg.role === "system") return new import_messages.SystemMessage(msg.content);
|
|
365
|
-
if (msg.role === "tool") {
|
|
366
|
-
return new import_messages.ToolMessage({
|
|
367
|
-
content: msg.content,
|
|
368
|
-
tool_call_id: msg.tool_call_id,
|
|
369
|
-
name: msg.name
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
return new import_messages.HumanMessage(msg.content);
|
|
373
|
-
})
|
|
374
|
-
];
|
|
375
|
-
const scratchpad = state.scratchpad || [];
|
|
376
|
-
if (scratchpad.length > 0) {
|
|
377
|
-
const scratchpadText = formatScratchpad(scratchpad);
|
|
378
|
-
messages.push(new import_messages.SystemMessage(`Previous steps:
|
|
379
|
-
${scratchpadText}`));
|
|
380
|
-
}
|
|
437
|
+
const messages = buildReasoningMessages(systemPrompt, state.messages, state.scratchpad);
|
|
381
438
|
const response = await llmWithTools.invoke(messages);
|
|
382
439
|
const thought = typeof response.content === "string" ? response.content : "";
|
|
383
|
-
const toolCalls =
|
|
384
|
-
if (response.tool_calls && response.tool_calls.length > 0) {
|
|
385
|
-
for (const toolCall of response.tool_calls) {
|
|
386
|
-
toolCalls.push({
|
|
387
|
-
id: toolCall.id || `call_${Date.now()}_${Math.random()}`,
|
|
388
|
-
name: toolCall.name,
|
|
389
|
-
arguments: toolCall.args || {},
|
|
390
|
-
timestamp: Date.now()
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
}
|
|
440
|
+
const toolCalls = extractToolCalls(response);
|
|
394
441
|
const shouldContinue = toolCalls.length > 0 && currentIteration + 1 < maxIterations;
|
|
395
442
|
reasoningLogger.info("Reasoning complete", {
|
|
396
443
|
iteration: currentIteration + 1,
|
|
@@ -415,11 +462,11 @@ ${scratchpadText}`));
|
|
|
415
462
|
function createActionNode(tools, verbose = false, enableDeduplication = true) {
|
|
416
463
|
const toolMap = new Map(tools.map((tool) => [tool.metadata.name, tool]));
|
|
417
464
|
return async (state) => {
|
|
418
|
-
const actions = state.actions
|
|
419
|
-
const allObservations = state.observations
|
|
465
|
+
const actions = state.actions;
|
|
466
|
+
const allObservations = state.observations;
|
|
420
467
|
const iteration = state.iteration || 0;
|
|
421
468
|
const startTime = Date.now();
|
|
422
|
-
actionLogger
|
|
469
|
+
debugIfVerbose(actionLogger, verbose, "Action node started", {
|
|
423
470
|
actionCount: actions.length,
|
|
424
471
|
iteration,
|
|
425
472
|
cacheEnabled: enableDeduplication
|
|
@@ -438,7 +485,7 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
|
|
|
438
485
|
}
|
|
439
486
|
}
|
|
440
487
|
if (cacheSize > 0) {
|
|
441
|
-
actionLogger
|
|
488
|
+
debugIfVerbose(actionLogger, verbose, "Deduplication cache built", {
|
|
442
489
|
cacheSize,
|
|
443
490
|
totalObservations: allObservations.length
|
|
444
491
|
});
|
|
@@ -449,7 +496,7 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
|
|
|
449
496
|
for (const action of recentActions) {
|
|
450
497
|
const existingObservation = allObservations.find((obs) => obs.toolCallId === action.id);
|
|
451
498
|
if (existingObservation) {
|
|
452
|
-
actionLogger
|
|
499
|
+
debugIfVerbose(actionLogger, verbose, "Skipping already-processed action", {
|
|
453
500
|
toolName: action.name,
|
|
454
501
|
toolCallId: action.id,
|
|
455
502
|
iteration
|
|
@@ -492,7 +539,7 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
|
|
|
492
539
|
const result = await tool.invoke(action.arguments);
|
|
493
540
|
const executionTime = Date.now() - startTime2;
|
|
494
541
|
toolsExecuted++;
|
|
495
|
-
actionLogger
|
|
542
|
+
debugIfVerbose(actionLogger, verbose, "Tool executed successfully", {
|
|
496
543
|
toolName: action.name,
|
|
497
544
|
executionTime,
|
|
498
545
|
iteration
|
|
@@ -508,7 +555,7 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
|
|
|
508
555
|
executionCache.set(cacheKey, observation);
|
|
509
556
|
}
|
|
510
557
|
} catch (error) {
|
|
511
|
-
const errorMessage = handleNodeError(error, `action:${action.name}`,
|
|
558
|
+
const errorMessage = handleNodeError(error, `action:${action.name}`, verbose);
|
|
512
559
|
actionLogger.error("Tool execution failed", {
|
|
513
560
|
toolName: action.name,
|
|
514
561
|
error: errorMessage,
|
|
@@ -537,39 +584,31 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
|
|
|
537
584
|
}
|
|
538
585
|
function createObservationNode(verbose = false, returnIntermediateSteps = false) {
|
|
539
586
|
return async (state) => {
|
|
540
|
-
const observations = state.observations
|
|
541
|
-
const thoughts = state.thoughts
|
|
542
|
-
const actions = state.actions
|
|
587
|
+
const observations = state.observations;
|
|
588
|
+
const thoughts = state.thoughts;
|
|
589
|
+
const actions = state.actions;
|
|
543
590
|
const iteration = state.iteration || 0;
|
|
544
|
-
observationLogger
|
|
591
|
+
debugIfVerbose(observationLogger, verbose, "Processing observations", {
|
|
545
592
|
observationCount: observations.length,
|
|
546
593
|
iteration
|
|
547
594
|
});
|
|
548
595
|
const recentObservations = observations.slice(-10);
|
|
549
596
|
const latestActions = actions.slice(-10);
|
|
550
|
-
const
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
// Include tool_call_id for proper ToolMessage construction
|
|
558
|
-
};
|
|
559
|
-
});
|
|
597
|
+
const actionNamesById = new Map(latestActions.map((action) => [action.id, action.name]));
|
|
598
|
+
const observationMessages = recentObservations.map((observation) => ({
|
|
599
|
+
role: "tool",
|
|
600
|
+
content: formatObservationContent(observation),
|
|
601
|
+
name: actionNamesById.get(observation.toolCallId),
|
|
602
|
+
tool_call_id: observation.toolCallId
|
|
603
|
+
}));
|
|
560
604
|
const scratchpadEntries = returnIntermediateSteps ? [{
|
|
561
605
|
step: state.iteration,
|
|
562
|
-
thought: thoughts
|
|
563
|
-
action: latestActions
|
|
564
|
-
observation: recentObservations
|
|
565
|
-
if (obs.error) {
|
|
566
|
-
return `Error: ${obs.error}`;
|
|
567
|
-
}
|
|
568
|
-
return typeof obs.result === "string" ? obs.result : JSON.stringify(obs.result);
|
|
569
|
-
}).join("; "),
|
|
606
|
+
thought: getLatestThought(thoughts),
|
|
607
|
+
action: formatActionSummary(latestActions),
|
|
608
|
+
observation: formatObservationSummary(recentObservations),
|
|
570
609
|
timestamp: Date.now()
|
|
571
610
|
}] : [];
|
|
572
|
-
observationLogger
|
|
611
|
+
debugIfVerbose(observationLogger, verbose, "Observation node complete", {
|
|
573
612
|
iteration,
|
|
574
613
|
scratchpadUpdated: returnIntermediateSteps,
|
|
575
614
|
messageCount: observationMessages.length
|
|
@@ -1039,7 +1078,7 @@ function createPlannerNode(config) {
|
|
|
1039
1078
|
plannerLogger.info("Plan created", {
|
|
1040
1079
|
stepCount: plan.steps.length,
|
|
1041
1080
|
goal: plan.goal.substring(0, 100),
|
|
1042
|
-
confidence: plan.confidence,
|
|
1081
|
+
...plan.confidence !== void 0 ? { confidence: plan.confidence } : {},
|
|
1043
1082
|
duration: Date.now() - startTime
|
|
1044
1083
|
});
|
|
1045
1084
|
return {
|
|
@@ -1129,7 +1168,10 @@ function createExecutorNode(config) {
|
|
|
1129
1168
|
executorLogger.info("Duplicate step execution prevented", {
|
|
1130
1169
|
stepId: currentStep.id,
|
|
1131
1170
|
toolName: currentStep.tool,
|
|
1132
|
-
|
|
1171
|
+
...currentStep.args ? {
|
|
1172
|
+
argumentKeys: Object.keys(currentStep.args),
|
|
1173
|
+
argumentCount: Object.keys(currentStep.args).length
|
|
1174
|
+
} : {},
|
|
1133
1175
|
iteration,
|
|
1134
1176
|
cacheHit: true
|
|
1135
1177
|
});
|
|
@@ -1165,7 +1207,7 @@ function createExecutorNode(config) {
|
|
|
1165
1207
|
result = null;
|
|
1166
1208
|
executorLogger.warn("Step execution failed", {
|
|
1167
1209
|
stepId: currentStep.id,
|
|
1168
|
-
toolName: currentStep.tool,
|
|
1210
|
+
...currentStep.tool ? { toolName: currentStep.tool } : {},
|
|
1169
1211
|
error,
|
|
1170
1212
|
iteration
|
|
1171
1213
|
});
|
|
@@ -1243,7 +1285,7 @@ function createReplannerNode(config) {
|
|
|
1243
1285
|
if (decision.shouldReplan) {
|
|
1244
1286
|
replannerLogger.info("Replanning triggered", {
|
|
1245
1287
|
reason: decision.reason,
|
|
1246
|
-
newGoal: decision.newGoal
|
|
1288
|
+
...decision.newGoal ? { newGoal: decision.newGoal.substring(0, 100) } : {},
|
|
1247
1289
|
duration: Date.now() - startTime
|
|
1248
1290
|
});
|
|
1249
1291
|
return {
|
|
@@ -1745,7 +1787,7 @@ function createReflectorNode(config) {
|
|
|
1745
1787
|
}
|
|
1746
1788
|
reflectorLogger.info("Reflection complete", {
|
|
1747
1789
|
attempt: state.iteration,
|
|
1748
|
-
score: reflection.score,
|
|
1790
|
+
...reflection.score !== void 0 ? { score: reflection.score } : {},
|
|
1749
1791
|
meetsStandards: reflection.meetsStandards,
|
|
1750
1792
|
issueCount: reflection.issues.length,
|
|
1751
1793
|
suggestionCount: reflection.suggestions.length,
|
|
@@ -1787,7 +1829,7 @@ function createReviserNode(config) {
|
|
|
1787
1829
|
const lastReflection = state.reflections[state.reflections.length - 1];
|
|
1788
1830
|
reviserLogger.debug("Revising response", {
|
|
1789
1831
|
attempt: state.iteration,
|
|
1790
|
-
previousScore: lastReflection.score,
|
|
1832
|
+
...lastReflection.score !== void 0 ? { previousScore: lastReflection.score } : {},
|
|
1791
1833
|
issueCount: lastReflection.issues.length,
|
|
1792
1834
|
suggestionCount: lastReflection.suggestions.length
|
|
1793
1835
|
});
|
|
@@ -1815,7 +1857,7 @@ ${revisionsText}`;
|
|
|
1815
1857
|
reviserLogger.info("Revision complete", {
|
|
1816
1858
|
attempt: state.iteration,
|
|
1817
1859
|
revisionLength: content.length,
|
|
1818
|
-
basedOnScore: lastReflection.score,
|
|
1860
|
+
...lastReflection.score !== void 0 ? { basedOnScore: lastReflection.score } : {},
|
|
1819
1861
|
duration: Date.now() - startTime
|
|
1820
1862
|
});
|
|
1821
1863
|
return {
|
|
@@ -2507,8 +2549,8 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2507
2549
|
} : runnableConfig;
|
|
2508
2550
|
logger.debug("Invoking ReAct agent with worker-specific config", {
|
|
2509
2551
|
workerId,
|
|
2510
|
-
parentThreadId: runnableConfig
|
|
2511
|
-
workerThreadId,
|
|
2552
|
+
...runnableConfig?.configurable?.thread_id !== void 0 ? { parentThreadId: String(runnableConfig.configurable.thread_id) } : {},
|
|
2553
|
+
...workerThreadId ? { workerThreadId } : {},
|
|
2512
2554
|
hasConfig: !!workerConfig
|
|
2513
2555
|
});
|
|
2514
2556
|
const result = await agent.invoke(
|
|
@@ -2741,7 +2783,7 @@ function createSupervisorNode(config) {
|
|
|
2741
2783
|
} catch (error) {
|
|
2742
2784
|
logger2.error("Supervisor node error", {
|
|
2743
2785
|
error: error instanceof Error ? error.message : String(error),
|
|
2744
|
-
|
|
2786
|
+
...error instanceof Error && error.stack ? { stack: error.stack } : {},
|
|
2745
2787
|
iteration: state.iteration
|
|
2746
2788
|
});
|
|
2747
2789
|
return {
|
|
@@ -3017,7 +3059,7 @@ Please synthesize these results into a comprehensive response that addresses the
|
|
|
3017
3059
|
} catch (error) {
|
|
3018
3060
|
logger2.error("Aggregator node error", {
|
|
3019
3061
|
error: error instanceof Error ? error.message : String(error),
|
|
3020
|
-
|
|
3062
|
+
...error instanceof Error && error.stack ? { stack: error.stack } : {},
|
|
3021
3063
|
completedTasks: state.completedTasks.length
|
|
3022
3064
|
});
|
|
3023
3065
|
return {
|
|
@@ -3083,7 +3125,7 @@ function createMultiAgentSystem(config) {
|
|
|
3083
3125
|
const supervisorRouter = (state) => {
|
|
3084
3126
|
logger3.debug("Supervisor router executing", {
|
|
3085
3127
|
status: state.status,
|
|
3086
|
-
currentAgent: state.currentAgent,
|
|
3128
|
+
...state.currentAgent ? { currentAgent: state.currentAgent } : {},
|
|
3087
3129
|
iteration: state.iteration
|
|
3088
3130
|
});
|
|
3089
3131
|
if (state.status === "completed" || state.status === "failed") {
|
package/dist/index.js
CHANGED
|
@@ -171,7 +171,12 @@ function formatScratchpad(scratchpad) {
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
// src/react/nodes.ts
|
|
174
|
-
import {
|
|
174
|
+
import {
|
|
175
|
+
HumanMessage,
|
|
176
|
+
AIMessage,
|
|
177
|
+
SystemMessage,
|
|
178
|
+
ToolMessage
|
|
179
|
+
} from "@langchain/core/messages";
|
|
175
180
|
import { toLangChainTools } from "@agentforge/core";
|
|
176
181
|
|
|
177
182
|
// src/shared/deduplication.ts
|
|
@@ -240,54 +245,101 @@ function handleNodeError(error, context, verbose = false) {
|
|
|
240
245
|
var reasoningLogger = createPatternLogger("agentforge:patterns:react:reasoning");
|
|
241
246
|
var actionLogger = createPatternLogger("agentforge:patterns:react:action");
|
|
242
247
|
var observationLogger = createPatternLogger("agentforge:patterns:react:observation");
|
|
248
|
+
function normalizeConversationMessage(message) {
|
|
249
|
+
switch (message.role) {
|
|
250
|
+
case "user":
|
|
251
|
+
return new HumanMessage(message.content);
|
|
252
|
+
case "assistant":
|
|
253
|
+
return new AIMessage(message.content);
|
|
254
|
+
case "system":
|
|
255
|
+
return new SystemMessage(message.content);
|
|
256
|
+
case "tool":
|
|
257
|
+
if (!message.tool_call_id) {
|
|
258
|
+
reasoningLogger.warn(
|
|
259
|
+
"Tool message missing tool_call_id; falling back to human message",
|
|
260
|
+
message.name ? { name: message.name } : void 0
|
|
261
|
+
);
|
|
262
|
+
return new HumanMessage(message.content);
|
|
263
|
+
}
|
|
264
|
+
return new ToolMessage({
|
|
265
|
+
content: message.content,
|
|
266
|
+
tool_call_id: message.tool_call_id,
|
|
267
|
+
name: message.name
|
|
268
|
+
});
|
|
269
|
+
default:
|
|
270
|
+
return new HumanMessage(message.content);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
function buildReasoningMessages(systemPrompt, stateMessages, scratchpad) {
|
|
274
|
+
const messages = [
|
|
275
|
+
new SystemMessage(systemPrompt),
|
|
276
|
+
...stateMessages.map(normalizeConversationMessage)
|
|
277
|
+
];
|
|
278
|
+
if (scratchpad.length > 0) {
|
|
279
|
+
messages.push(new SystemMessage(`Previous steps:
|
|
280
|
+
${formatScratchpad(scratchpad)}`));
|
|
281
|
+
}
|
|
282
|
+
return messages;
|
|
283
|
+
}
|
|
284
|
+
function extractToolCalls(response) {
|
|
285
|
+
if (!response.tool_calls || response.tool_calls.length === 0) {
|
|
286
|
+
return [];
|
|
287
|
+
}
|
|
288
|
+
return response.tool_calls.map((toolCall) => ({
|
|
289
|
+
id: toolCall.id || `call_${Date.now()}_${Math.random()}`,
|
|
290
|
+
name: toolCall.name,
|
|
291
|
+
arguments: toolCall.args ?? {},
|
|
292
|
+
timestamp: Date.now()
|
|
293
|
+
}));
|
|
294
|
+
}
|
|
295
|
+
function formatObservationContent(observation) {
|
|
296
|
+
if (observation.error) {
|
|
297
|
+
return `Error: ${observation.error}`;
|
|
298
|
+
}
|
|
299
|
+
return stringifyObservationResult(observation.result, 2);
|
|
300
|
+
}
|
|
301
|
+
function formatActionSummary(actions) {
|
|
302
|
+
return actions.map((action) => `${action.name}(${JSON.stringify(action.arguments)})`).join(", ");
|
|
303
|
+
}
|
|
304
|
+
function formatObservationSummary(observations) {
|
|
305
|
+
return observations.map((observation) => {
|
|
306
|
+
if (observation.error) {
|
|
307
|
+
return `Error: ${observation.error}`;
|
|
308
|
+
}
|
|
309
|
+
return stringifyObservationResult(observation.result);
|
|
310
|
+
}).join("; ");
|
|
311
|
+
}
|
|
312
|
+
function stringifyObservationResult(result, space) {
|
|
313
|
+
if (typeof result === "string") {
|
|
314
|
+
return result;
|
|
315
|
+
}
|
|
316
|
+
const stringified = JSON.stringify(result, null, space);
|
|
317
|
+
return stringified ?? String(result);
|
|
318
|
+
}
|
|
319
|
+
function getLatestThought(thoughts) {
|
|
320
|
+
return thoughts[thoughts.length - 1]?.content ?? "";
|
|
321
|
+
}
|
|
322
|
+
function debugIfVerbose(logger4, verbose, message, data) {
|
|
323
|
+
if (verbose) {
|
|
324
|
+
logger4.debug(message, data);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
243
327
|
function createReasoningNode(llm, tools, systemPrompt, maxIterations, verbose = false) {
|
|
244
328
|
const langchainTools = toLangChainTools(tools);
|
|
245
329
|
const llmWithTools = llm.bindTools ? llm.bindTools(langchainTools) : llm;
|
|
246
330
|
return async (state) => {
|
|
247
331
|
const currentIteration = state.iteration || 0;
|
|
248
332
|
const startTime = Date.now();
|
|
249
|
-
reasoningLogger
|
|
333
|
+
debugIfVerbose(reasoningLogger, verbose, "Reasoning iteration started", {
|
|
250
334
|
iteration: currentIteration + 1,
|
|
251
335
|
maxIterations,
|
|
252
|
-
observationCount: state.observations
|
|
253
|
-
hasActions:
|
|
336
|
+
observationCount: state.observations.length,
|
|
337
|
+
hasActions: state.actions.length > 0
|
|
254
338
|
});
|
|
255
|
-
const
|
|
256
|
-
const messages = [
|
|
257
|
-
new SystemMessage(systemPrompt),
|
|
258
|
-
...stateMessages.map((msg) => {
|
|
259
|
-
if (msg.role === "user") return new HumanMessage(msg.content);
|
|
260
|
-
if (msg.role === "assistant") return new AIMessage(msg.content);
|
|
261
|
-
if (msg.role === "system") return new SystemMessage(msg.content);
|
|
262
|
-
if (msg.role === "tool") {
|
|
263
|
-
return new ToolMessage({
|
|
264
|
-
content: msg.content,
|
|
265
|
-
tool_call_id: msg.tool_call_id,
|
|
266
|
-
name: msg.name
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
return new HumanMessage(msg.content);
|
|
270
|
-
})
|
|
271
|
-
];
|
|
272
|
-
const scratchpad = state.scratchpad || [];
|
|
273
|
-
if (scratchpad.length > 0) {
|
|
274
|
-
const scratchpadText = formatScratchpad(scratchpad);
|
|
275
|
-
messages.push(new SystemMessage(`Previous steps:
|
|
276
|
-
${scratchpadText}`));
|
|
277
|
-
}
|
|
339
|
+
const messages = buildReasoningMessages(systemPrompt, state.messages, state.scratchpad);
|
|
278
340
|
const response = await llmWithTools.invoke(messages);
|
|
279
341
|
const thought = typeof response.content === "string" ? response.content : "";
|
|
280
|
-
const toolCalls =
|
|
281
|
-
if (response.tool_calls && response.tool_calls.length > 0) {
|
|
282
|
-
for (const toolCall of response.tool_calls) {
|
|
283
|
-
toolCalls.push({
|
|
284
|
-
id: toolCall.id || `call_${Date.now()}_${Math.random()}`,
|
|
285
|
-
name: toolCall.name,
|
|
286
|
-
arguments: toolCall.args || {},
|
|
287
|
-
timestamp: Date.now()
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
}
|
|
342
|
+
const toolCalls = extractToolCalls(response);
|
|
291
343
|
const shouldContinue = toolCalls.length > 0 && currentIteration + 1 < maxIterations;
|
|
292
344
|
reasoningLogger.info("Reasoning complete", {
|
|
293
345
|
iteration: currentIteration + 1,
|
|
@@ -312,11 +364,11 @@ ${scratchpadText}`));
|
|
|
312
364
|
function createActionNode(tools, verbose = false, enableDeduplication = true) {
|
|
313
365
|
const toolMap = new Map(tools.map((tool) => [tool.metadata.name, tool]));
|
|
314
366
|
return async (state) => {
|
|
315
|
-
const actions = state.actions
|
|
316
|
-
const allObservations = state.observations
|
|
367
|
+
const actions = state.actions;
|
|
368
|
+
const allObservations = state.observations;
|
|
317
369
|
const iteration = state.iteration || 0;
|
|
318
370
|
const startTime = Date.now();
|
|
319
|
-
actionLogger
|
|
371
|
+
debugIfVerbose(actionLogger, verbose, "Action node started", {
|
|
320
372
|
actionCount: actions.length,
|
|
321
373
|
iteration,
|
|
322
374
|
cacheEnabled: enableDeduplication
|
|
@@ -335,7 +387,7 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
|
|
|
335
387
|
}
|
|
336
388
|
}
|
|
337
389
|
if (cacheSize > 0) {
|
|
338
|
-
actionLogger
|
|
390
|
+
debugIfVerbose(actionLogger, verbose, "Deduplication cache built", {
|
|
339
391
|
cacheSize,
|
|
340
392
|
totalObservations: allObservations.length
|
|
341
393
|
});
|
|
@@ -346,7 +398,7 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
|
|
|
346
398
|
for (const action of recentActions) {
|
|
347
399
|
const existingObservation = allObservations.find((obs) => obs.toolCallId === action.id);
|
|
348
400
|
if (existingObservation) {
|
|
349
|
-
actionLogger
|
|
401
|
+
debugIfVerbose(actionLogger, verbose, "Skipping already-processed action", {
|
|
350
402
|
toolName: action.name,
|
|
351
403
|
toolCallId: action.id,
|
|
352
404
|
iteration
|
|
@@ -389,7 +441,7 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
|
|
|
389
441
|
const result = await tool.invoke(action.arguments);
|
|
390
442
|
const executionTime = Date.now() - startTime2;
|
|
391
443
|
toolsExecuted++;
|
|
392
|
-
actionLogger
|
|
444
|
+
debugIfVerbose(actionLogger, verbose, "Tool executed successfully", {
|
|
393
445
|
toolName: action.name,
|
|
394
446
|
executionTime,
|
|
395
447
|
iteration
|
|
@@ -405,7 +457,7 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
|
|
|
405
457
|
executionCache.set(cacheKey, observation);
|
|
406
458
|
}
|
|
407
459
|
} catch (error) {
|
|
408
|
-
const errorMessage = handleNodeError(error, `action:${action.name}`,
|
|
460
|
+
const errorMessage = handleNodeError(error, `action:${action.name}`, verbose);
|
|
409
461
|
actionLogger.error("Tool execution failed", {
|
|
410
462
|
toolName: action.name,
|
|
411
463
|
error: errorMessage,
|
|
@@ -434,39 +486,31 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
|
|
|
434
486
|
}
|
|
435
487
|
function createObservationNode(verbose = false, returnIntermediateSteps = false) {
|
|
436
488
|
return async (state) => {
|
|
437
|
-
const observations = state.observations
|
|
438
|
-
const thoughts = state.thoughts
|
|
439
|
-
const actions = state.actions
|
|
489
|
+
const observations = state.observations;
|
|
490
|
+
const thoughts = state.thoughts;
|
|
491
|
+
const actions = state.actions;
|
|
440
492
|
const iteration = state.iteration || 0;
|
|
441
|
-
observationLogger
|
|
493
|
+
debugIfVerbose(observationLogger, verbose, "Processing observations", {
|
|
442
494
|
observationCount: observations.length,
|
|
443
495
|
iteration
|
|
444
496
|
});
|
|
445
497
|
const recentObservations = observations.slice(-10);
|
|
446
498
|
const latestActions = actions.slice(-10);
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
// Include tool_call_id for proper ToolMessage construction
|
|
455
|
-
};
|
|
456
|
-
});
|
|
499
|
+
const actionNamesById = new Map(latestActions.map((action) => [action.id, action.name]));
|
|
500
|
+
const observationMessages = recentObservations.map((observation) => ({
|
|
501
|
+
role: "tool",
|
|
502
|
+
content: formatObservationContent(observation),
|
|
503
|
+
name: actionNamesById.get(observation.toolCallId),
|
|
504
|
+
tool_call_id: observation.toolCallId
|
|
505
|
+
}));
|
|
457
506
|
const scratchpadEntries = returnIntermediateSteps ? [{
|
|
458
507
|
step: state.iteration,
|
|
459
|
-
thought: thoughts
|
|
460
|
-
action: latestActions
|
|
461
|
-
observation: recentObservations
|
|
462
|
-
if (obs.error) {
|
|
463
|
-
return `Error: ${obs.error}`;
|
|
464
|
-
}
|
|
465
|
-
return typeof obs.result === "string" ? obs.result : JSON.stringify(obs.result);
|
|
466
|
-
}).join("; "),
|
|
508
|
+
thought: getLatestThought(thoughts),
|
|
509
|
+
action: formatActionSummary(latestActions),
|
|
510
|
+
observation: formatObservationSummary(recentObservations),
|
|
467
511
|
timestamp: Date.now()
|
|
468
512
|
}] : [];
|
|
469
|
-
observationLogger
|
|
513
|
+
debugIfVerbose(observationLogger, verbose, "Observation node complete", {
|
|
470
514
|
iteration,
|
|
471
515
|
scratchpadUpdated: returnIntermediateSteps,
|
|
472
516
|
messageCount: observationMessages.length
|
|
@@ -936,7 +980,7 @@ function createPlannerNode(config) {
|
|
|
936
980
|
plannerLogger.info("Plan created", {
|
|
937
981
|
stepCount: plan.steps.length,
|
|
938
982
|
goal: plan.goal.substring(0, 100),
|
|
939
|
-
confidence: plan.confidence,
|
|
983
|
+
...plan.confidence !== void 0 ? { confidence: plan.confidence } : {},
|
|
940
984
|
duration: Date.now() - startTime
|
|
941
985
|
});
|
|
942
986
|
return {
|
|
@@ -1026,7 +1070,10 @@ function createExecutorNode(config) {
|
|
|
1026
1070
|
executorLogger.info("Duplicate step execution prevented", {
|
|
1027
1071
|
stepId: currentStep.id,
|
|
1028
1072
|
toolName: currentStep.tool,
|
|
1029
|
-
|
|
1073
|
+
...currentStep.args ? {
|
|
1074
|
+
argumentKeys: Object.keys(currentStep.args),
|
|
1075
|
+
argumentCount: Object.keys(currentStep.args).length
|
|
1076
|
+
} : {},
|
|
1030
1077
|
iteration,
|
|
1031
1078
|
cacheHit: true
|
|
1032
1079
|
});
|
|
@@ -1062,7 +1109,7 @@ function createExecutorNode(config) {
|
|
|
1062
1109
|
result = null;
|
|
1063
1110
|
executorLogger.warn("Step execution failed", {
|
|
1064
1111
|
stepId: currentStep.id,
|
|
1065
|
-
toolName: currentStep.tool,
|
|
1112
|
+
...currentStep.tool ? { toolName: currentStep.tool } : {},
|
|
1066
1113
|
error,
|
|
1067
1114
|
iteration
|
|
1068
1115
|
});
|
|
@@ -1140,7 +1187,7 @@ function createReplannerNode(config) {
|
|
|
1140
1187
|
if (decision.shouldReplan) {
|
|
1141
1188
|
replannerLogger.info("Replanning triggered", {
|
|
1142
1189
|
reason: decision.reason,
|
|
1143
|
-
newGoal: decision.newGoal
|
|
1190
|
+
...decision.newGoal ? { newGoal: decision.newGoal.substring(0, 100) } : {},
|
|
1144
1191
|
duration: Date.now() - startTime
|
|
1145
1192
|
});
|
|
1146
1193
|
return {
|
|
@@ -1642,7 +1689,7 @@ function createReflectorNode(config) {
|
|
|
1642
1689
|
}
|
|
1643
1690
|
reflectorLogger.info("Reflection complete", {
|
|
1644
1691
|
attempt: state.iteration,
|
|
1645
|
-
score: reflection.score,
|
|
1692
|
+
...reflection.score !== void 0 ? { score: reflection.score } : {},
|
|
1646
1693
|
meetsStandards: reflection.meetsStandards,
|
|
1647
1694
|
issueCount: reflection.issues.length,
|
|
1648
1695
|
suggestionCount: reflection.suggestions.length,
|
|
@@ -1684,7 +1731,7 @@ function createReviserNode(config) {
|
|
|
1684
1731
|
const lastReflection = state.reflections[state.reflections.length - 1];
|
|
1685
1732
|
reviserLogger.debug("Revising response", {
|
|
1686
1733
|
attempt: state.iteration,
|
|
1687
|
-
previousScore: lastReflection.score,
|
|
1734
|
+
...lastReflection.score !== void 0 ? { previousScore: lastReflection.score } : {},
|
|
1688
1735
|
issueCount: lastReflection.issues.length,
|
|
1689
1736
|
suggestionCount: lastReflection.suggestions.length
|
|
1690
1737
|
});
|
|
@@ -1712,7 +1759,7 @@ ${revisionsText}`;
|
|
|
1712
1759
|
reviserLogger.info("Revision complete", {
|
|
1713
1760
|
attempt: state.iteration,
|
|
1714
1761
|
revisionLength: content.length,
|
|
1715
|
-
basedOnScore: lastReflection.score,
|
|
1762
|
+
...lastReflection.score !== void 0 ? { basedOnScore: lastReflection.score } : {},
|
|
1716
1763
|
duration: Date.now() - startTime
|
|
1717
1764
|
});
|
|
1718
1765
|
return {
|
|
@@ -2404,8 +2451,8 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2404
2451
|
} : runnableConfig;
|
|
2405
2452
|
logger.debug("Invoking ReAct agent with worker-specific config", {
|
|
2406
2453
|
workerId,
|
|
2407
|
-
parentThreadId: runnableConfig
|
|
2408
|
-
workerThreadId,
|
|
2454
|
+
...runnableConfig?.configurable?.thread_id !== void 0 ? { parentThreadId: String(runnableConfig.configurable.thread_id) } : {},
|
|
2455
|
+
...workerThreadId ? { workerThreadId } : {},
|
|
2409
2456
|
hasConfig: !!workerConfig
|
|
2410
2457
|
});
|
|
2411
2458
|
const result = await agent.invoke(
|
|
@@ -2638,7 +2685,7 @@ function createSupervisorNode(config) {
|
|
|
2638
2685
|
} catch (error) {
|
|
2639
2686
|
logger2.error("Supervisor node error", {
|
|
2640
2687
|
error: error instanceof Error ? error.message : String(error),
|
|
2641
|
-
|
|
2688
|
+
...error instanceof Error && error.stack ? { stack: error.stack } : {},
|
|
2642
2689
|
iteration: state.iteration
|
|
2643
2690
|
});
|
|
2644
2691
|
return {
|
|
@@ -2914,7 +2961,7 @@ Please synthesize these results into a comprehensive response that addresses the
|
|
|
2914
2961
|
} catch (error) {
|
|
2915
2962
|
logger2.error("Aggregator node error", {
|
|
2916
2963
|
error: error instanceof Error ? error.message : String(error),
|
|
2917
|
-
|
|
2964
|
+
...error instanceof Error && error.stack ? { stack: error.stack } : {},
|
|
2918
2965
|
completedTasks: state.completedTasks.length
|
|
2919
2966
|
});
|
|
2920
2967
|
return {
|
|
@@ -2980,7 +3027,7 @@ function createMultiAgentSystem(config) {
|
|
|
2980
3027
|
const supervisorRouter = (state) => {
|
|
2981
3028
|
logger3.debug("Supervisor router executing", {
|
|
2982
3029
|
status: state.status,
|
|
2983
|
-
currentAgent: state.currentAgent,
|
|
3030
|
+
...state.currentAgent ? { currentAgent: state.currentAgent } : {},
|
|
2984
3031
|
iteration: state.iteration
|
|
2985
3032
|
});
|
|
2986
3033
|
if (state.status === "completed" || state.status === "failed") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentforge/patterns",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.10",
|
|
4
4
|
"description": "Production-ready agent workflow patterns for TypeScript including ReAct and Planner-Executor, built on LangGraph.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -41,13 +41,13 @@
|
|
|
41
41
|
"url": "https://github.com/TVScoundrel/agentforge/issues"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@agentforge/core": "0.15.
|
|
44
|
+
"@agentforge/core": "0.15.10",
|
|
45
45
|
"@langchain/core": "^1.1.17",
|
|
46
46
|
"@langchain/langgraph": "^1.1.2",
|
|
47
47
|
"zod": "^3.23.8"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@agentforge/testing": "0.15.
|
|
50
|
+
"@agentforge/testing": "0.15.10",
|
|
51
51
|
"@eslint/js": "^9.17.0",
|
|
52
52
|
"@types/node": "^22.10.2",
|
|
53
53
|
"eslint": "^9.17.0",
|