@agentforge/patterns 0.15.10 → 0.15.11

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 CHANGED
@@ -273,10 +273,12 @@ function formatScratchpad(scratchpad) {
273
273
  }).join("\n\n");
274
274
  }
275
275
 
276
- // src/react/nodes.ts
277
- var import_messages = require("@langchain/core/messages");
276
+ // src/react/nodes/reasoning.ts
278
277
  var import_core3 = require("@agentforge/core");
279
278
 
279
+ // src/react/nodes/shared.ts
280
+ var import_messages = require("@langchain/core/messages");
281
+
280
282
  // src/shared/deduplication.ts
281
283
  var import_core2 = require("@agentforge/core");
282
284
  function normalizeObject(obj) {
@@ -321,25 +323,7 @@ function buildDeduplicationMetrics(toolsExecuted, duplicatesSkipped, totalObserv
321
323
  };
322
324
  }
323
325
 
324
- // src/shared/error-handling.ts
325
- function isGraphInterrupt(error) {
326
- return error !== null && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt";
327
- }
328
- function handleNodeError(error, context, verbose = false) {
329
- if (isGraphInterrupt(error)) {
330
- throw error;
331
- }
332
- const errorMessage = error instanceof Error ? error.message : String(error);
333
- if (verbose) {
334
- console.error(`[${context}] Error:`, errorMessage);
335
- if (error instanceof Error && error.stack) {
336
- console.error(`[${context}] Stack:`, error.stack);
337
- }
338
- }
339
- return errorMessage;
340
- }
341
-
342
- // src/react/nodes.ts
326
+ // src/react/nodes/shared.ts
343
327
  var reasoningLogger = createPatternLogger("agentforge:patterns:react:reasoning");
344
328
  var actionLogger = createPatternLogger("agentforge:patterns:react:action");
345
329
  var observationLogger = createPatternLogger("agentforge:patterns:react:observation");
@@ -397,7 +381,7 @@ function formatObservationContent(observation) {
397
381
  return stringifyObservationResult(observation.result, 2);
398
382
  }
399
383
  function formatActionSummary(actions) {
400
- return actions.map((action) => `${action.name}(${JSON.stringify(action.arguments)})`).join(", ");
384
+ return actions.map((action) => `${action.name}(${stringifyActionArguments(action.arguments)})`).join(", ");
401
385
  }
402
386
  function formatObservationSummary(observations) {
403
387
  return observations.map((observation) => {
@@ -411,8 +395,15 @@ function stringifyObservationResult(result, space) {
411
395
  if (typeof result === "string") {
412
396
  return result;
413
397
  }
414
- const stringified = JSON.stringify(result, null, space);
415
- return stringified ?? String(result);
398
+ try {
399
+ const stringified = JSON.stringify(result, null, space);
400
+ return stringified ?? String(result);
401
+ } catch {
402
+ return String(result);
403
+ }
404
+ }
405
+ function stringifyActionArguments(arguments_) {
406
+ return stringifyObservationResult(arguments_);
416
407
  }
417
408
  function getLatestThought(thoughts) {
418
409
  return thoughts[thoughts.length - 1]?.content ?? "";
@@ -422,6 +413,8 @@ function debugIfVerbose(logger4, verbose, message, data) {
422
413
  logger4.debug(message, data);
423
414
  }
424
415
  }
416
+
417
+ // src/react/nodes/reasoning.ts
425
418
  function createReasoningNode(llm, tools, systemPrompt, maxIterations, verbose = false) {
426
419
  const langchainTools = (0, import_core3.toLangChainTools)(tools);
427
420
  const llmWithTools = llm.bindTools ? llm.bindTools(langchainTools) : llm;
@@ -452,15 +445,43 @@ function createReasoningNode(llm, tools, systemPrompt, maxIterations, verbose =
452
445
  thoughts: thought ? [{ content: thought, timestamp: Date.now() }] : [],
453
446
  actions: toolCalls,
454
447
  iteration: 1,
455
- // Add 1 to iteration counter (uses additive reducer)
456
448
  shouldContinue,
457
449
  response: toolCalls.length === 0 ? thought : void 0
458
- // Final response if no tool calls
459
450
  };
460
451
  };
461
452
  }
453
+
454
+ // src/shared/error-handling.ts
455
+ function isGraphInterrupt(error) {
456
+ return error !== null && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt";
457
+ }
458
+ function handleNodeError(error, context, verbose = false) {
459
+ if (isGraphInterrupt(error)) {
460
+ throw error;
461
+ }
462
+ const errorMessage = error instanceof Error ? error.message : String(error);
463
+ if (verbose) {
464
+ console.error(`[${context}] Error:`, errorMessage);
465
+ if (error instanceof Error && error.stack) {
466
+ console.error(`[${context}] Stack:`, error.stack);
467
+ }
468
+ }
469
+ return errorMessage;
470
+ }
471
+
472
+ // src/react/nodes/action.ts
462
473
  function createActionNode(tools, verbose = false, enableDeduplication = true) {
463
474
  const toolMap = new Map(tools.map((tool) => [tool.metadata.name, tool]));
475
+ function buildCacheKey(toolName, args) {
476
+ try {
477
+ return generateToolCallCacheKey(toolName, args);
478
+ } catch {
479
+ debugIfVerbose(actionLogger, verbose, "Skipping deduplication for unserializable tool call", {
480
+ toolName
481
+ });
482
+ return void 0;
483
+ }
484
+ }
464
485
  return async (state) => {
465
486
  const actions = state.actions;
466
487
  const allObservations = state.observations;
@@ -474,14 +495,23 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
474
495
  const recentActions = actions.slice(-10);
475
496
  const observations = [];
476
497
  const executionCache = /* @__PURE__ */ new Map();
498
+ const actionsById = new Map(actions.map((action) => [action.id, action]));
499
+ const observedToolCallIds = new Set(
500
+ allObservations.map((observation) => observation.toolCallId)
501
+ );
477
502
  let cacheSize = 0;
478
503
  if (enableDeduplication) {
479
504
  for (const observation of allObservations) {
480
- const correspondingAction = actions.find((a) => a.id === observation.toolCallId);
505
+ const correspondingAction = actionsById.get(observation.toolCallId);
481
506
  if (correspondingAction) {
482
- const cacheKey = generateToolCallCacheKey(correspondingAction.name, correspondingAction.arguments);
483
- executionCache.set(cacheKey, observation);
484
- cacheSize++;
507
+ const cacheKey = buildCacheKey(
508
+ correspondingAction.name,
509
+ correspondingAction.arguments
510
+ );
511
+ if (cacheKey) {
512
+ executionCache.set(cacheKey, observation);
513
+ cacheSize++;
514
+ }
485
515
  }
486
516
  }
487
517
  if (cacheSize > 0) {
@@ -494,8 +524,7 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
494
524
  let duplicatesSkipped = 0;
495
525
  let toolsExecuted = 0;
496
526
  for (const action of recentActions) {
497
- const existingObservation = allObservations.find((obs) => obs.toolCallId === action.id);
498
- if (existingObservation) {
527
+ if (observedToolCallIds.has(action.id)) {
499
528
  debugIfVerbose(actionLogger, verbose, "Skipping already-processed action", {
500
529
  toolName: action.name,
501
530
  toolCallId: action.id,
@@ -504,13 +533,13 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
504
533
  continue;
505
534
  }
506
535
  if (enableDeduplication) {
507
- const cacheKey = generateToolCallCacheKey(action.name, action.arguments);
508
- const cachedResult = executionCache.get(cacheKey);
536
+ const cacheKey = buildCacheKey(action.name, action.arguments);
537
+ const cachedResult = cacheKey ? executionCache.get(cacheKey) : void 0;
509
538
  if (cachedResult) {
510
539
  duplicatesSkipped++;
511
540
  actionLogger.info("Duplicate tool call prevented", {
512
541
  toolName: action.name,
513
- arguments: action.arguments,
542
+ argumentKeys: Object.keys(action.arguments),
514
543
  iteration,
515
544
  cacheHit: true
516
545
  });
@@ -535,9 +564,9 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
535
564
  continue;
536
565
  }
537
566
  try {
538
- const startTime2 = Date.now();
567
+ const toolStartTime = Date.now();
539
568
  const result = await tool.invoke(action.arguments);
540
- const executionTime = Date.now() - startTime2;
569
+ const executionTime = Date.now() - toolStartTime;
541
570
  toolsExecuted++;
542
571
  debugIfVerbose(actionLogger, verbose, "Tool executed successfully", {
543
572
  toolName: action.name,
@@ -551,8 +580,10 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
551
580
  };
552
581
  observations.push(observation);
553
582
  if (enableDeduplication) {
554
- const cacheKey = generateToolCallCacheKey(action.name, action.arguments);
555
- executionCache.set(cacheKey, observation);
583
+ const cacheKey = buildCacheKey(action.name, action.arguments);
584
+ if (cacheKey) {
585
+ executionCache.set(cacheKey, observation);
586
+ }
556
587
  }
557
588
  } catch (error) {
558
589
  const errorMessage = handleNodeError(error, `action:${action.name}`, verbose);
@@ -570,7 +601,11 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
570
601
  }
571
602
  }
572
603
  if (duplicatesSkipped > 0 || toolsExecuted > 0) {
573
- const metrics = buildDeduplicationMetrics(toolsExecuted, duplicatesSkipped, observations.length);
604
+ const metrics = buildDeduplicationMetrics(
605
+ toolsExecuted,
606
+ duplicatesSkipped,
607
+ observations.length
608
+ );
574
609
  actionLogger.info("Action node complete", {
575
610
  iteration,
576
611
  ...metrics,
@@ -582,6 +617,8 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
582
617
  };
583
618
  };
584
619
  }
620
+
621
+ // src/react/nodes/observation.ts
585
622
  function createObservationNode(verbose = false, returnIntermediateSteps = false) {
586
623
  return async (state) => {
587
624
  const observations = state.observations;
@@ -601,13 +638,15 @@ function createObservationNode(verbose = false, returnIntermediateSteps = false)
601
638
  name: actionNamesById.get(observation.toolCallId),
602
639
  tool_call_id: observation.toolCallId
603
640
  }));
604
- const scratchpadEntries = returnIntermediateSteps ? [{
605
- step: state.iteration,
606
- thought: getLatestThought(thoughts),
607
- action: formatActionSummary(latestActions),
608
- observation: formatObservationSummary(recentObservations),
609
- timestamp: Date.now()
610
- }] : [];
641
+ const scratchpadEntries = returnIntermediateSteps ? [
642
+ {
643
+ step: iteration,
644
+ thought: getLatestThought(thoughts),
645
+ action: formatActionSummary(latestActions),
646
+ observation: formatObservationSummary(recentObservations),
647
+ timestamp: Date.now()
648
+ }
649
+ ] : [];
611
650
  debugIfVerbose(observationLogger, verbose, "Observation node complete", {
612
651
  iteration,
613
652
  scratchpadUpdated: returnIntermediateSteps,
package/dist/index.js CHANGED
@@ -170,14 +170,16 @@ function formatScratchpad(scratchpad) {
170
170
  }).join("\n\n");
171
171
  }
172
172
 
173
- // src/react/nodes.ts
173
+ // src/react/nodes/reasoning.ts
174
+ import { toLangChainTools } from "@agentforge/core";
175
+
176
+ // src/react/nodes/shared.ts
174
177
  import {
175
178
  HumanMessage,
176
179
  AIMessage,
177
180
  SystemMessage,
178
181
  ToolMessage
179
182
  } from "@langchain/core/messages";
180
- import { toLangChainTools } from "@agentforge/core";
181
183
 
182
184
  // src/shared/deduplication.ts
183
185
  import { createLogger } from "@agentforge/core";
@@ -223,25 +225,7 @@ function buildDeduplicationMetrics(toolsExecuted, duplicatesSkipped, totalObserv
223
225
  };
224
226
  }
225
227
 
226
- // src/shared/error-handling.ts
227
- function isGraphInterrupt(error) {
228
- return error !== null && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt";
229
- }
230
- function handleNodeError(error, context, verbose = false) {
231
- if (isGraphInterrupt(error)) {
232
- throw error;
233
- }
234
- const errorMessage = error instanceof Error ? error.message : String(error);
235
- if (verbose) {
236
- console.error(`[${context}] Error:`, errorMessage);
237
- if (error instanceof Error && error.stack) {
238
- console.error(`[${context}] Stack:`, error.stack);
239
- }
240
- }
241
- return errorMessage;
242
- }
243
-
244
- // src/react/nodes.ts
228
+ // src/react/nodes/shared.ts
245
229
  var reasoningLogger = createPatternLogger("agentforge:patterns:react:reasoning");
246
230
  var actionLogger = createPatternLogger("agentforge:patterns:react:action");
247
231
  var observationLogger = createPatternLogger("agentforge:patterns:react:observation");
@@ -299,7 +283,7 @@ function formatObservationContent(observation) {
299
283
  return stringifyObservationResult(observation.result, 2);
300
284
  }
301
285
  function formatActionSummary(actions) {
302
- return actions.map((action) => `${action.name}(${JSON.stringify(action.arguments)})`).join(", ");
286
+ return actions.map((action) => `${action.name}(${stringifyActionArguments(action.arguments)})`).join(", ");
303
287
  }
304
288
  function formatObservationSummary(observations) {
305
289
  return observations.map((observation) => {
@@ -313,8 +297,15 @@ function stringifyObservationResult(result, space) {
313
297
  if (typeof result === "string") {
314
298
  return result;
315
299
  }
316
- const stringified = JSON.stringify(result, null, space);
317
- return stringified ?? String(result);
300
+ try {
301
+ const stringified = JSON.stringify(result, null, space);
302
+ return stringified ?? String(result);
303
+ } catch {
304
+ return String(result);
305
+ }
306
+ }
307
+ function stringifyActionArguments(arguments_) {
308
+ return stringifyObservationResult(arguments_);
318
309
  }
319
310
  function getLatestThought(thoughts) {
320
311
  return thoughts[thoughts.length - 1]?.content ?? "";
@@ -324,6 +315,8 @@ function debugIfVerbose(logger4, verbose, message, data) {
324
315
  logger4.debug(message, data);
325
316
  }
326
317
  }
318
+
319
+ // src/react/nodes/reasoning.ts
327
320
  function createReasoningNode(llm, tools, systemPrompt, maxIterations, verbose = false) {
328
321
  const langchainTools = toLangChainTools(tools);
329
322
  const llmWithTools = llm.bindTools ? llm.bindTools(langchainTools) : llm;
@@ -354,15 +347,43 @@ function createReasoningNode(llm, tools, systemPrompt, maxIterations, verbose =
354
347
  thoughts: thought ? [{ content: thought, timestamp: Date.now() }] : [],
355
348
  actions: toolCalls,
356
349
  iteration: 1,
357
- // Add 1 to iteration counter (uses additive reducer)
358
350
  shouldContinue,
359
351
  response: toolCalls.length === 0 ? thought : void 0
360
- // Final response if no tool calls
361
352
  };
362
353
  };
363
354
  }
355
+
356
+ // src/shared/error-handling.ts
357
+ function isGraphInterrupt(error) {
358
+ return error !== null && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt";
359
+ }
360
+ function handleNodeError(error, context, verbose = false) {
361
+ if (isGraphInterrupt(error)) {
362
+ throw error;
363
+ }
364
+ const errorMessage = error instanceof Error ? error.message : String(error);
365
+ if (verbose) {
366
+ console.error(`[${context}] Error:`, errorMessage);
367
+ if (error instanceof Error && error.stack) {
368
+ console.error(`[${context}] Stack:`, error.stack);
369
+ }
370
+ }
371
+ return errorMessage;
372
+ }
373
+
374
+ // src/react/nodes/action.ts
364
375
  function createActionNode(tools, verbose = false, enableDeduplication = true) {
365
376
  const toolMap = new Map(tools.map((tool) => [tool.metadata.name, tool]));
377
+ function buildCacheKey(toolName, args) {
378
+ try {
379
+ return generateToolCallCacheKey(toolName, args);
380
+ } catch {
381
+ debugIfVerbose(actionLogger, verbose, "Skipping deduplication for unserializable tool call", {
382
+ toolName
383
+ });
384
+ return void 0;
385
+ }
386
+ }
366
387
  return async (state) => {
367
388
  const actions = state.actions;
368
389
  const allObservations = state.observations;
@@ -376,14 +397,23 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
376
397
  const recentActions = actions.slice(-10);
377
398
  const observations = [];
378
399
  const executionCache = /* @__PURE__ */ new Map();
400
+ const actionsById = new Map(actions.map((action) => [action.id, action]));
401
+ const observedToolCallIds = new Set(
402
+ allObservations.map((observation) => observation.toolCallId)
403
+ );
379
404
  let cacheSize = 0;
380
405
  if (enableDeduplication) {
381
406
  for (const observation of allObservations) {
382
- const correspondingAction = actions.find((a) => a.id === observation.toolCallId);
407
+ const correspondingAction = actionsById.get(observation.toolCallId);
383
408
  if (correspondingAction) {
384
- const cacheKey = generateToolCallCacheKey(correspondingAction.name, correspondingAction.arguments);
385
- executionCache.set(cacheKey, observation);
386
- cacheSize++;
409
+ const cacheKey = buildCacheKey(
410
+ correspondingAction.name,
411
+ correspondingAction.arguments
412
+ );
413
+ if (cacheKey) {
414
+ executionCache.set(cacheKey, observation);
415
+ cacheSize++;
416
+ }
387
417
  }
388
418
  }
389
419
  if (cacheSize > 0) {
@@ -396,8 +426,7 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
396
426
  let duplicatesSkipped = 0;
397
427
  let toolsExecuted = 0;
398
428
  for (const action of recentActions) {
399
- const existingObservation = allObservations.find((obs) => obs.toolCallId === action.id);
400
- if (existingObservation) {
429
+ if (observedToolCallIds.has(action.id)) {
401
430
  debugIfVerbose(actionLogger, verbose, "Skipping already-processed action", {
402
431
  toolName: action.name,
403
432
  toolCallId: action.id,
@@ -406,13 +435,13 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
406
435
  continue;
407
436
  }
408
437
  if (enableDeduplication) {
409
- const cacheKey = generateToolCallCacheKey(action.name, action.arguments);
410
- const cachedResult = executionCache.get(cacheKey);
438
+ const cacheKey = buildCacheKey(action.name, action.arguments);
439
+ const cachedResult = cacheKey ? executionCache.get(cacheKey) : void 0;
411
440
  if (cachedResult) {
412
441
  duplicatesSkipped++;
413
442
  actionLogger.info("Duplicate tool call prevented", {
414
443
  toolName: action.name,
415
- arguments: action.arguments,
444
+ argumentKeys: Object.keys(action.arguments),
416
445
  iteration,
417
446
  cacheHit: true
418
447
  });
@@ -437,9 +466,9 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
437
466
  continue;
438
467
  }
439
468
  try {
440
- const startTime2 = Date.now();
469
+ const toolStartTime = Date.now();
441
470
  const result = await tool.invoke(action.arguments);
442
- const executionTime = Date.now() - startTime2;
471
+ const executionTime = Date.now() - toolStartTime;
443
472
  toolsExecuted++;
444
473
  debugIfVerbose(actionLogger, verbose, "Tool executed successfully", {
445
474
  toolName: action.name,
@@ -453,8 +482,10 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
453
482
  };
454
483
  observations.push(observation);
455
484
  if (enableDeduplication) {
456
- const cacheKey = generateToolCallCacheKey(action.name, action.arguments);
457
- executionCache.set(cacheKey, observation);
485
+ const cacheKey = buildCacheKey(action.name, action.arguments);
486
+ if (cacheKey) {
487
+ executionCache.set(cacheKey, observation);
488
+ }
458
489
  }
459
490
  } catch (error) {
460
491
  const errorMessage = handleNodeError(error, `action:${action.name}`, verbose);
@@ -472,7 +503,11 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
472
503
  }
473
504
  }
474
505
  if (duplicatesSkipped > 0 || toolsExecuted > 0) {
475
- const metrics = buildDeduplicationMetrics(toolsExecuted, duplicatesSkipped, observations.length);
506
+ const metrics = buildDeduplicationMetrics(
507
+ toolsExecuted,
508
+ duplicatesSkipped,
509
+ observations.length
510
+ );
476
511
  actionLogger.info("Action node complete", {
477
512
  iteration,
478
513
  ...metrics,
@@ -484,6 +519,8 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
484
519
  };
485
520
  };
486
521
  }
522
+
523
+ // src/react/nodes/observation.ts
487
524
  function createObservationNode(verbose = false, returnIntermediateSteps = false) {
488
525
  return async (state) => {
489
526
  const observations = state.observations;
@@ -503,13 +540,15 @@ function createObservationNode(verbose = false, returnIntermediateSteps = false)
503
540
  name: actionNamesById.get(observation.toolCallId),
504
541
  tool_call_id: observation.toolCallId
505
542
  }));
506
- const scratchpadEntries = returnIntermediateSteps ? [{
507
- step: state.iteration,
508
- thought: getLatestThought(thoughts),
509
- action: formatActionSummary(latestActions),
510
- observation: formatObservationSummary(recentObservations),
511
- timestamp: Date.now()
512
- }] : [];
543
+ const scratchpadEntries = returnIntermediateSteps ? [
544
+ {
545
+ step: iteration,
546
+ thought: getLatestThought(thoughts),
547
+ action: formatActionSummary(latestActions),
548
+ observation: formatObservationSummary(recentObservations),
549
+ timestamp: Date.now()
550
+ }
551
+ ] : [];
513
552
  debugIfVerbose(observationLogger, verbose, "Observation node complete", {
514
553
  iteration,
515
554
  scratchpadUpdated: returnIntermediateSteps,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentforge/patterns",
3
- "version": "0.15.10",
3
+ "version": "0.15.11",
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.10",
44
+ "@agentforge/core": "0.15.11",
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.10",
50
+ "@agentforge/testing": "0.15.11",
51
51
  "@eslint/js": "^9.17.0",
52
52
  "@types/node": "^22.10.2",
53
53
  "eslint": "^9.17.0",