@langchain/langgraph 0.2.40 → 0.2.42

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.
Files changed (86) hide show
  1. package/README.md +237 -154
  2. package/dist/channels/any_value.cjs +10 -10
  3. package/dist/channels/any_value.d.ts +1 -1
  4. package/dist/channels/any_value.js +10 -10
  5. package/dist/channels/ephemeral_value.cjs +10 -9
  6. package/dist/channels/ephemeral_value.d.ts +1 -1
  7. package/dist/channels/ephemeral_value.js +10 -9
  8. package/dist/channels/last_value.cjs +8 -7
  9. package/dist/channels/last_value.d.ts +1 -1
  10. package/dist/channels/last_value.js +8 -7
  11. package/dist/constants.cjs +33 -6
  12. package/dist/constants.d.ts +17 -2
  13. package/dist/constants.js +32 -5
  14. package/dist/errors.d.ts +3 -3
  15. package/dist/func/index.cjs +272 -0
  16. package/dist/func/index.d.ts +310 -0
  17. package/dist/func/index.js +267 -0
  18. package/dist/func/types.cjs +15 -0
  19. package/dist/func/types.d.ts +59 -0
  20. package/dist/func/types.js +11 -0
  21. package/dist/graph/graph.cjs +31 -35
  22. package/dist/graph/graph.d.ts +1 -5
  23. package/dist/graph/graph.js +1 -5
  24. package/dist/graph/index.cjs +1 -3
  25. package/dist/graph/index.d.ts +1 -1
  26. package/dist/graph/index.js +1 -1
  27. package/dist/graph/message.d.ts +1 -1
  28. package/dist/graph/state.cjs +17 -17
  29. package/dist/graph/state.d.ts +2 -1
  30. package/dist/graph/state.js +2 -2
  31. package/dist/index.cjs +8 -0
  32. package/dist/index.d.ts +3 -0
  33. package/dist/index.js +3 -0
  34. package/dist/interrupt.cjs +21 -34
  35. package/dist/interrupt.d.ts +1 -1
  36. package/dist/interrupt.js +22 -35
  37. package/dist/prebuilt/agent_executor.cjs +3 -3
  38. package/dist/prebuilt/agent_executor.d.ts +1 -1
  39. package/dist/prebuilt/agent_executor.js +1 -1
  40. package/dist/prebuilt/chat_agent_executor.cjs +3 -3
  41. package/dist/prebuilt/chat_agent_executor.d.ts +1 -1
  42. package/dist/prebuilt/chat_agent_executor.js +1 -1
  43. package/dist/prebuilt/react_agent_executor.cjs +79 -12
  44. package/dist/prebuilt/react_agent_executor.d.ts +35 -4
  45. package/dist/prebuilt/react_agent_executor.js +79 -13
  46. package/dist/prebuilt/tool_node.cjs +1 -2
  47. package/dist/prebuilt/tool_node.d.ts +1 -1
  48. package/dist/prebuilt/tool_node.js +1 -2
  49. package/dist/pregel/algo.cjs +121 -12
  50. package/dist/pregel/algo.d.ts +8 -6
  51. package/dist/pregel/algo.js +122 -13
  52. package/dist/pregel/call.cjs +77 -0
  53. package/dist/pregel/call.d.ts +15 -0
  54. package/dist/pregel/call.js +71 -0
  55. package/dist/pregel/index.cjs +59 -96
  56. package/dist/pregel/index.d.ts +1 -10
  57. package/dist/pregel/index.js +61 -98
  58. package/dist/pregel/io.cjs +6 -1
  59. package/dist/pregel/io.js +7 -2
  60. package/dist/pregel/loop.cjs +109 -75
  61. package/dist/pregel/loop.d.ts +17 -23
  62. package/dist/pregel/loop.js +110 -75
  63. package/dist/pregel/messages.d.ts +1 -1
  64. package/dist/pregel/retry.cjs +22 -50
  65. package/dist/pregel/retry.d.ts +6 -6
  66. package/dist/pregel/retry.js +22 -50
  67. package/dist/pregel/runner.cjs +275 -0
  68. package/dist/pregel/runner.d.ts +64 -0
  69. package/dist/pregel/runner.js +271 -0
  70. package/dist/pregel/stream.cjs +71 -0
  71. package/dist/pregel/stream.d.ts +17 -0
  72. package/dist/pregel/stream.js +67 -0
  73. package/dist/pregel/types.cjs +54 -0
  74. package/dist/pregel/types.d.ts +78 -6
  75. package/dist/pregel/types.js +51 -1
  76. package/dist/pregel/utils/config.cjs +26 -1
  77. package/dist/pregel/utils/config.d.ts +14 -0
  78. package/dist/pregel/utils/config.js +22 -0
  79. package/dist/pregel/write.d.ts +1 -1
  80. package/dist/utils.cjs +15 -1
  81. package/dist/utils.d.ts +3 -1
  82. package/dist/utils.js +12 -0
  83. package/dist/web.cjs +7 -5
  84. package/dist/web.d.ts +4 -4
  85. package/dist/web.js +3 -3
  86. package/package.json +8 -8
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.executeTasksWithRetry = exports.DEFAULT_MAX_RETRIES = exports.DEFAULT_MAX_INTERVAL = exports.DEFAULT_BACKOFF_FACTOR = exports.DEFAULT_INITIAL_INTERVAL = void 0;
3
+ exports._runWithRetry = exports.DEFAULT_MAX_RETRIES = exports.DEFAULT_MAX_INTERVAL = exports.DEFAULT_BACKOFF_FACTOR = exports.DEFAULT_INITIAL_INTERVAL = void 0;
4
4
  const constants_js_1 = require("../constants.cjs");
5
5
  const errors_js_1 = require("../errors.cjs");
6
+ const index_js_1 = require("./utils/index.cjs");
6
7
  exports.DEFAULT_INITIAL_INTERVAL = 500;
7
8
  exports.DEFAULT_BACKOFF_FACTOR = 2;
8
9
  exports.DEFAULT_MAX_INTERVAL = 128000;
@@ -41,47 +42,9 @@ const DEFAULT_RETRY_ON_HANDLER = (error) => {
41
42
  }
42
43
  return true;
43
44
  };
44
- async function* executeTasksWithRetry(
45
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
- tasks, options) {
47
- const { stepTimeout, retryPolicy } = options ?? {};
48
- let signal = options?.signal;
49
- // Start tasks
50
- const executingTasksMap = Object.fromEntries(tasks.map((pregelTask) => {
51
- return [pregelTask.id, _runWithRetry(pregelTask, retryPolicy)];
52
- }));
53
- if (stepTimeout && signal) {
54
- if ("any" in AbortSignal) {
55
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
56
- signal = AbortSignal.any([
57
- signal,
58
- AbortSignal.timeout(stepTimeout),
59
- ]);
60
- }
61
- }
62
- else if (stepTimeout) {
63
- signal = AbortSignal.timeout(stepTimeout);
64
- }
65
- // Abort if signal is aborted
66
- signal?.throwIfAborted();
67
- let listener;
68
- const signalPromise = new Promise((_resolve, reject) => {
69
- listener = () => reject(new Error("Abort"));
70
- signal?.addEventListener("abort", listener);
71
- }).finally(() => signal?.removeEventListener("abort", listener));
72
- while (Object.keys(executingTasksMap).length > 0) {
73
- const settledTask = await Promise.race([
74
- ...Object.values(executingTasksMap),
75
- signalPromise,
76
- ]);
77
- yield settledTask;
78
- delete executingTasksMap[settledTask.task.id];
79
- }
80
- }
81
- exports.executeTasksWithRetry = executeTasksWithRetry;
82
45
  async function _runWithRetry(
83
46
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
- pregelTask, retryPolicy) {
47
+ pregelTask, retryPolicy, configurable, signal) {
85
48
  const resolvedRetryPolicy = pregelTask.retry_policy ?? retryPolicy;
86
49
  let interval = resolvedRetryPolicy !== undefined
87
50
  ? resolvedRetryPolicy.initialInterval ?? exports.DEFAULT_INITIAL_INTERVAL
@@ -89,28 +52,34 @@ pregelTask, retryPolicy) {
89
52
  let attempts = 0;
90
53
  let error;
91
54
  let result;
55
+ let { config } = pregelTask;
56
+ if (configurable) {
57
+ config = (0, index_js_1.patchConfigurable)(config, configurable);
58
+ }
92
59
  // eslint-disable-next-line no-constant-condition
93
60
  while (true) {
94
- // Modify writes in place to clear any previous retries
95
- while (pregelTask.writes.length > 0) {
96
- pregelTask.writes.pop();
61
+ if (signal?.aborted) {
62
+ // no need to throw here - we'll throw from the runner, instead.
63
+ // there's just no point in retrying if the user has requested an abort.
64
+ break;
97
65
  }
66
+ // Clear any writes from previous attempts
67
+ pregelTask.writes.splice(0, pregelTask.writes.length);
98
68
  error = undefined;
99
69
  try {
100
- result = await pregelTask.proc.invoke(pregelTask.input, pregelTask.config);
70
+ result = await pregelTask.proc.invoke(pregelTask.input, config);
101
71
  break;
102
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
103
72
  }
104
73
  catch (e) {
105
74
  error = e;
106
75
  error.pregelTaskId = pregelTask.id;
107
76
  if ((0, errors_js_1.isParentCommand)(error)) {
108
- const ns = pregelTask.config?.configurable?.checkpoint_ns;
77
+ const ns = config?.configurable?.checkpoint_ns;
109
78
  const cmd = error.command;
110
79
  if (cmd.graph === ns) {
111
80
  // this command is for the current graph, handle it
112
81
  for (const writer of pregelTask.writers) {
113
- await writer.invoke(cmd, pregelTask.config);
82
+ await writer.invoke(cmd, config);
114
83
  }
115
84
  break;
116
85
  }
@@ -153,11 +122,13 @@ pregelTask, retryPolicy) {
153
122
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
154
123
  error.constructor.unminifiable_name ??
155
124
  error.constructor.name;
156
- console.log(`Retrying task "${pregelTask.name}" after ${interval.toFixed(2)}ms (attempt ${attempts}) after ${errorName}: ${error}`);
125
+ console.log(`Retrying task "${String(pregelTask.name)}" after ${interval.toFixed(2)}ms (attempt ${attempts}) after ${errorName}: ${error}`);
126
+ // signal subgraphs to resume (if available)
127
+ config = (0, index_js_1.patchConfigurable)(config, { [constants_js_1.CONFIG_KEY_RESUMING]: true });
157
128
  }
158
129
  finally {
159
130
  // Clear checkpoint_ns seen (for subgraph detection)
160
- const checkpointNs = pregelTask.config?.configurable?.checkpoint_ns;
131
+ const checkpointNs = config?.configurable?.checkpoint_ns;
161
132
  if (checkpointNs) {
162
133
  (0, errors_js_1.getSubgraphsSeenSet)().delete(checkpointNs);
163
134
  }
@@ -166,6 +137,7 @@ pregelTask, retryPolicy) {
166
137
  return {
167
138
  task: pregelTask,
168
139
  result,
169
- error,
140
+ error: error,
170
141
  };
171
142
  }
143
+ exports._runWithRetry = _runWithRetry;
@@ -1,5 +1,5 @@
1
1
  import { PregelExecutableTask } from "./types.js";
2
- import type { RetryPolicy } from "./utils/index.js";
2
+ import { type RetryPolicy } from "./utils/index.js";
3
3
  export declare const DEFAULT_INITIAL_INTERVAL = 500;
4
4
  export declare const DEFAULT_BACKOFF_FACTOR = 2;
5
5
  export declare const DEFAULT_MAX_INTERVAL = 128000;
@@ -8,8 +8,8 @@ export type SettledPregelTask = {
8
8
  task: PregelExecutableTask<any, any>;
9
9
  error: Error;
10
10
  };
11
- export declare function executeTasksWithRetry(tasks: PregelExecutableTask<any, any>[], options?: {
12
- stepTimeout?: number;
13
- signal?: AbortSignal;
14
- retryPolicy?: RetryPolicy;
15
- }): AsyncGenerator<SettledPregelTask>;
11
+ export declare function _runWithRetry<N extends PropertyKey, C extends PropertyKey>(pregelTask: PregelExecutableTask<N, C>, retryPolicy?: RetryPolicy, configurable?: Record<string, unknown>, signal?: AbortSignal): Promise<{
12
+ task: PregelExecutableTask<N, C>;
13
+ result: unknown;
14
+ error: Error | undefined;
15
+ }>;
@@ -1,5 +1,6 @@
1
- import { CHECKPOINT_NAMESPACE_SEPARATOR, Command } from "../constants.js";
1
+ import { CHECKPOINT_NAMESPACE_SEPARATOR, Command, CONFIG_KEY_RESUMING, } from "../constants.js";
2
2
  import { getSubgraphsSeenSet, isGraphBubbleUp, isParentCommand, } from "../errors.js";
3
+ import { patchConfigurable } from "./utils/index.js";
3
4
  export const DEFAULT_INITIAL_INTERVAL = 500;
4
5
  export const DEFAULT_BACKOFF_FACTOR = 2;
5
6
  export const DEFAULT_MAX_INTERVAL = 128000;
@@ -38,46 +39,9 @@ const DEFAULT_RETRY_ON_HANDLER = (error) => {
38
39
  }
39
40
  return true;
40
41
  };
41
- export async function* executeTasksWithRetry(
42
+ export async function _runWithRetry(
42
43
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
- tasks, options) {
44
- const { stepTimeout, retryPolicy } = options ?? {};
45
- let signal = options?.signal;
46
- // Start tasks
47
- const executingTasksMap = Object.fromEntries(tasks.map((pregelTask) => {
48
- return [pregelTask.id, _runWithRetry(pregelTask, retryPolicy)];
49
- }));
50
- if (stepTimeout && signal) {
51
- if ("any" in AbortSignal) {
52
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
- signal = AbortSignal.any([
54
- signal,
55
- AbortSignal.timeout(stepTimeout),
56
- ]);
57
- }
58
- }
59
- else if (stepTimeout) {
60
- signal = AbortSignal.timeout(stepTimeout);
61
- }
62
- // Abort if signal is aborted
63
- signal?.throwIfAborted();
64
- let listener;
65
- const signalPromise = new Promise((_resolve, reject) => {
66
- listener = () => reject(new Error("Abort"));
67
- signal?.addEventListener("abort", listener);
68
- }).finally(() => signal?.removeEventListener("abort", listener));
69
- while (Object.keys(executingTasksMap).length > 0) {
70
- const settledTask = await Promise.race([
71
- ...Object.values(executingTasksMap),
72
- signalPromise,
73
- ]);
74
- yield settledTask;
75
- delete executingTasksMap[settledTask.task.id];
76
- }
77
- }
78
- async function _runWithRetry(
79
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
80
- pregelTask, retryPolicy) {
44
+ pregelTask, retryPolicy, configurable, signal) {
81
45
  const resolvedRetryPolicy = pregelTask.retry_policy ?? retryPolicy;
82
46
  let interval = resolvedRetryPolicy !== undefined
83
47
  ? resolvedRetryPolicy.initialInterval ?? DEFAULT_INITIAL_INTERVAL
@@ -85,28 +49,34 @@ pregelTask, retryPolicy) {
85
49
  let attempts = 0;
86
50
  let error;
87
51
  let result;
52
+ let { config } = pregelTask;
53
+ if (configurable) {
54
+ config = patchConfigurable(config, configurable);
55
+ }
88
56
  // eslint-disable-next-line no-constant-condition
89
57
  while (true) {
90
- // Modify writes in place to clear any previous retries
91
- while (pregelTask.writes.length > 0) {
92
- pregelTask.writes.pop();
58
+ if (signal?.aborted) {
59
+ // no need to throw here - we'll throw from the runner, instead.
60
+ // there's just no point in retrying if the user has requested an abort.
61
+ break;
93
62
  }
63
+ // Clear any writes from previous attempts
64
+ pregelTask.writes.splice(0, pregelTask.writes.length);
94
65
  error = undefined;
95
66
  try {
96
- result = await pregelTask.proc.invoke(pregelTask.input, pregelTask.config);
67
+ result = await pregelTask.proc.invoke(pregelTask.input, config);
97
68
  break;
98
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
99
69
  }
100
70
  catch (e) {
101
71
  error = e;
102
72
  error.pregelTaskId = pregelTask.id;
103
73
  if (isParentCommand(error)) {
104
- const ns = pregelTask.config?.configurable?.checkpoint_ns;
74
+ const ns = config?.configurable?.checkpoint_ns;
105
75
  const cmd = error.command;
106
76
  if (cmd.graph === ns) {
107
77
  // this command is for the current graph, handle it
108
78
  for (const writer of pregelTask.writers) {
109
- await writer.invoke(cmd, pregelTask.config);
79
+ await writer.invoke(cmd, config);
110
80
  }
111
81
  break;
112
82
  }
@@ -149,11 +119,13 @@ pregelTask, retryPolicy) {
149
119
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
150
120
  error.constructor.unminifiable_name ??
151
121
  error.constructor.name;
152
- console.log(`Retrying task "${pregelTask.name}" after ${interval.toFixed(2)}ms (attempt ${attempts}) after ${errorName}: ${error}`);
122
+ console.log(`Retrying task "${String(pregelTask.name)}" after ${interval.toFixed(2)}ms (attempt ${attempts}) after ${errorName}: ${error}`);
123
+ // signal subgraphs to resume (if available)
124
+ config = patchConfigurable(config, { [CONFIG_KEY_RESUMING]: true });
153
125
  }
154
126
  finally {
155
127
  // Clear checkpoint_ns seen (for subgraph detection)
156
- const checkpointNs = pregelTask.config?.configurable?.checkpoint_ns;
128
+ const checkpointNs = config?.configurable?.checkpoint_ns;
157
129
  if (checkpointNs) {
158
130
  getSubgraphsSeenSet().delete(checkpointNs);
159
131
  }
@@ -162,6 +134,6 @@ pregelTask, retryPolicy) {
162
134
  return {
163
135
  task: pregelTask,
164
136
  result,
165
- error,
137
+ error: error,
166
138
  };
167
139
  }
@@ -0,0 +1,275 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PregelRunner = void 0;
4
+ const types_js_1 = require("./types.cjs");
5
+ const constants_js_1 = require("../constants.cjs");
6
+ const errors_js_1 = require("../errors.cjs");
7
+ const retry_js_1 = require("./retry.cjs");
8
+ /**
9
+ * Responsible for handling task execution on each tick of the {@link PregelLoop}.
10
+ */
11
+ class PregelRunner {
12
+ /**
13
+ * Construct a new PregelRunner, which executes tasks from the provided PregelLoop.
14
+ * @param loop - The PregelLoop that produces tasks for this runner to execute.
15
+ */
16
+ constructor({ loop, nodeFinished, }) {
17
+ Object.defineProperty(this, "nodeFinished", {
18
+ enumerable: true,
19
+ configurable: true,
20
+ writable: true,
21
+ value: void 0
22
+ });
23
+ Object.defineProperty(this, "loop", {
24
+ enumerable: true,
25
+ configurable: true,
26
+ writable: true,
27
+ value: void 0
28
+ });
29
+ this.loop = loop;
30
+ this.nodeFinished = nodeFinished;
31
+ }
32
+ /**
33
+ * Execute tasks from the current step of the PregelLoop.
34
+ *
35
+ * Note: this method does NOT call {@link PregelLoop}#tick. That must be handled externally.
36
+ * @param options - Options for the execution.
37
+ */
38
+ async tick(options = {}) {
39
+ const { timeout, signal, retryPolicy, onStepWrite } = options;
40
+ let graphInterrupt;
41
+ // Start task execution
42
+ const pendingTasks = Object.values(this.loop.tasks).filter((t) => t.writes.length === 0);
43
+ const taskStream = this._executeTasksWithRetry(pendingTasks, {
44
+ stepTimeout: timeout,
45
+ signal,
46
+ retryPolicy,
47
+ });
48
+ for await (const { task, error } of taskStream) {
49
+ graphInterrupt = this._commit(task, error) ?? graphInterrupt;
50
+ }
51
+ onStepWrite?.(this.loop.step, Object.values(this.loop.tasks)
52
+ .map((task) => task.writes)
53
+ .flat());
54
+ if (graphInterrupt) {
55
+ throw graphInterrupt;
56
+ }
57
+ }
58
+ /**
59
+ * Concurrently executes tasks with the requested retry policy, yielding a {@link SettledPregelTask} for each task as it completes.
60
+ * @param tasks - The tasks to execute.
61
+ * @param options - Options for the execution.
62
+ */
63
+ async *_executeTasksWithRetry(tasks, options) {
64
+ const { stepTimeout, retryPolicy } = options ?? {};
65
+ let signal = options?.signal;
66
+ const promiseAddedSymbol = Symbol.for("promiseAdded");
67
+ let addedPromiseSignal;
68
+ let addedPromiseWait;
69
+ function waitHandler(resolve) {
70
+ addedPromiseSignal = () => {
71
+ addedPromiseWait = new Promise(waitHandler);
72
+ resolve(promiseAddedSymbol);
73
+ };
74
+ }
75
+ addedPromiseWait = new Promise(waitHandler);
76
+ const executingTasksMap = {};
77
+ const writer = (task, writes, { calls } = {}) => {
78
+ if (writes.every(([channel]) => channel !== constants_js_1.PUSH)) {
79
+ return task.config?.configurable?.[constants_js_1.CONFIG_KEY_SEND]?.(writes) ?? [];
80
+ }
81
+ // Schedule PUSH tasks, collect promises
82
+ const scratchpad = task.config?.configurable?.[constants_js_1.CONFIG_KEY_SCRATCHPAD];
83
+ const rtn = {};
84
+ for (const [idx, write] of writes.entries()) {
85
+ const [channel] = write;
86
+ if (channel !== constants_js_1.PUSH) {
87
+ continue;
88
+ }
89
+ const wcall = calls?.[idx];
90
+ const cnt = scratchpad.callCounter;
91
+ scratchpad.callCounter += 1;
92
+ if (wcall == null) {
93
+ throw new Error("BUG: No call found");
94
+ }
95
+ const nextTask = this.loop.acceptPush(task, cnt, wcall);
96
+ if (!nextTask) {
97
+ continue;
98
+ }
99
+ // Check if this task is already running
100
+ const existingPromise = executingTasksMap[nextTask.id];
101
+ if (existingPromise !== undefined) {
102
+ // If the parent task was retried, the next task might already be running
103
+ rtn[idx] = existingPromise;
104
+ }
105
+ else if (nextTask.writes.length > 0) {
106
+ // If it already ran, return the result
107
+ const returns = nextTask.writes.filter(([c]) => c === constants_js_1.RETURN);
108
+ const errors = nextTask.writes.filter(([c]) => c === constants_js_1.ERROR);
109
+ if (returns.length > 0) {
110
+ // Task completed successfully
111
+ if (returns.length === 1) {
112
+ rtn[idx] = Promise.resolve(returns[0][1]);
113
+ }
114
+ else {
115
+ // should be unreachable
116
+ throw new Error(`BUG: multiple returns found for task ${nextTask.name}__${nextTask.id}`);
117
+ }
118
+ }
119
+ else if (errors.length > 0) {
120
+ if (errors.length === 1) {
121
+ const errorValue = errors[0][1];
122
+ // Task failed
123
+ const error =
124
+ // eslint-disable-next-line no-instanceof/no-instanceof
125
+ errorValue instanceof Error
126
+ ? errorValue
127
+ : new Error(String(errorValue));
128
+ rtn[idx] = Promise.reject(error);
129
+ }
130
+ else {
131
+ // the only way this should happen is if the task executes multiple times and writes aren't cleared
132
+ throw new Error(`BUG: multiple errors found for task ${nextTask.name}__${nextTask.id}`);
133
+ }
134
+ }
135
+ }
136
+ else {
137
+ // Schedule the next task with retry
138
+ const prom = (0, retry_js_1._runWithRetry)(nextTask, retryPolicy, {
139
+ [constants_js_1.CONFIG_KEY_SEND]: writer.bind(this, nextTask),
140
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
141
+ [constants_js_1.CONFIG_KEY_CALL]: call.bind(this, nextTask),
142
+ });
143
+ executingTasksMap[nextTask.id] = prom;
144
+ addedPromiseSignal();
145
+ rtn[idx] = prom.then(({ result, error }) => {
146
+ if (error) {
147
+ return Promise.reject(error);
148
+ }
149
+ return result;
150
+ });
151
+ }
152
+ }
153
+ return Object.values(rtn);
154
+ };
155
+ const call = (task, func, name, input, options = {}) => {
156
+ const result = writer(task, [[constants_js_1.PUSH, null]], {
157
+ calls: [
158
+ new types_js_1.Call({
159
+ func,
160
+ name,
161
+ input,
162
+ retry: options.retry,
163
+ callbacks: options.callbacks,
164
+ }),
165
+ ],
166
+ });
167
+ // eslint-disable-next-line no-instanceof/no-instanceof
168
+ if (result !== undefined) {
169
+ if (result.length === 1) {
170
+ return result[0];
171
+ }
172
+ return Promise.all(result);
173
+ }
174
+ return Promise.resolve();
175
+ };
176
+ if (stepTimeout && signal) {
177
+ if ("any" in AbortSignal) {
178
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
179
+ signal = AbortSignal.any([
180
+ signal,
181
+ AbortSignal.timeout(stepTimeout),
182
+ ]);
183
+ }
184
+ }
185
+ else if (stepTimeout) {
186
+ signal = AbortSignal.timeout(stepTimeout);
187
+ }
188
+ if (signal?.aborted) {
189
+ // note: don't use throwIfAborted here because it throws a DOMException,
190
+ // which isn't consistent with how we throw on abort below.
191
+ throw new Error("Abort");
192
+ }
193
+ // Start tasks
194
+ Object.assign(executingTasksMap, Object.fromEntries(tasks.map((pregelTask) => {
195
+ return [
196
+ pregelTask.id,
197
+ (0, retry_js_1._runWithRetry)(pregelTask, retryPolicy, {
198
+ [constants_js_1.CONFIG_KEY_SEND]: writer?.bind(this, pregelTask),
199
+ [constants_js_1.CONFIG_KEY_CALL]: call?.bind(this, pregelTask),
200
+ }).catch((error) => {
201
+ return { task: pregelTask, error };
202
+ }),
203
+ ];
204
+ })));
205
+ let listener;
206
+ const signalPromise = new Promise((_resolve, reject) => {
207
+ listener = () => reject(new Error("Abort"));
208
+ signal?.addEventListener("abort", listener);
209
+ }).finally(() => signal?.removeEventListener("abort", listener));
210
+ while (Object.keys(executingTasksMap).length > 0) {
211
+ const settledTask = await Promise.race([
212
+ ...Object.values(executingTasksMap),
213
+ signalPromise,
214
+ addedPromiseWait,
215
+ ]);
216
+ if (settledTask === promiseAddedSymbol) {
217
+ continue;
218
+ }
219
+ yield settledTask;
220
+ delete executingTasksMap[settledTask.task.id];
221
+ }
222
+ }
223
+ /**
224
+ * Determines what writes to apply based on whether the task completed successfully, and what type of error occurred.
225
+ *
226
+ * Throws an error if the error is a {@link GraphBubbleUp} error and {@link PregelLoop}#isNested is true.
227
+ *
228
+ * Note that in the case of a {@link GraphBubbleUp} error that is not a {@link GraphInterrupt}, like a {@link Command}, this method does not apply any writes.
229
+ *
230
+ * @param task - The task to commit.
231
+ * @param error - The error that occurred, if any.
232
+ * @returns The {@link GraphInterrupt} that occurred, if the user's code threw one.
233
+ */
234
+ _commit(task, error) {
235
+ let graphInterrupt;
236
+ if (error !== undefined) {
237
+ if ((0, errors_js_1.isGraphBubbleUp)(error)) {
238
+ if (this.loop.isNested) {
239
+ throw error;
240
+ }
241
+ if ((0, errors_js_1.isGraphInterrupt)(error)) {
242
+ graphInterrupt = error;
243
+ if (error.interrupts.length) {
244
+ const interrupts = error.interrupts.map((interrupt) => [constants_js_1.INTERRUPT, interrupt]);
245
+ const resumes = task.writes.filter((w) => w[0] === constants_js_1.RESUME);
246
+ if (resumes.length) {
247
+ interrupts.push(...resumes);
248
+ }
249
+ this.loop.putWrites(task.id, interrupts);
250
+ }
251
+ }
252
+ }
253
+ else {
254
+ this.loop.putWrites(task.id, [
255
+ [constants_js_1.ERROR, { message: error.message, name: error.name }],
256
+ ]);
257
+ throw error;
258
+ }
259
+ }
260
+ else {
261
+ if (this.nodeFinished &&
262
+ (task.config?.tags == null || !task.config.tags.includes(constants_js_1.TAG_HIDDEN))) {
263
+ this.nodeFinished(String(task.name));
264
+ }
265
+ if (task.writes.length === 0) {
266
+ // Add no writes marker
267
+ task.writes.push([constants_js_1.NO_WRITES, null]);
268
+ }
269
+ // Save task writes to checkpointer
270
+ this.loop.putWrites(task.id, task.writes);
271
+ }
272
+ return graphInterrupt;
273
+ }
274
+ }
275
+ exports.PregelRunner = PregelRunner;
@@ -0,0 +1,64 @@
1
+ import { PendingWrite } from "@langchain/langgraph-checkpoint";
2
+ import { RetryPolicy } from "./utils/index.js";
3
+ import { PregelLoop } from "./loop.js";
4
+ /**
5
+ * Options for the {@link PregelRunner#tick} method.
6
+ */
7
+ export type TickOptions = {
8
+ /**
9
+ * The deadline before which all tasks must be completed.
10
+ */
11
+ timeout?: number;
12
+ /**
13
+ * An optional {@link AbortSignal} to cancel processing of tasks.
14
+ */
15
+ signal?: AbortSignal;
16
+ /**
17
+ * The {@link RetryPolicy} to use for the tick.
18
+ */
19
+ retryPolicy?: RetryPolicy;
20
+ /**
21
+ * An optional callback to be called after all task writes are completed.
22
+ */
23
+ onStepWrite?: (step: number, writes: PendingWrite[]) => void;
24
+ };
25
+ /**
26
+ * Responsible for handling task execution on each tick of the {@link PregelLoop}.
27
+ */
28
+ export declare class PregelRunner {
29
+ private nodeFinished?;
30
+ private loop;
31
+ /**
32
+ * Construct a new PregelRunner, which executes tasks from the provided PregelLoop.
33
+ * @param loop - The PregelLoop that produces tasks for this runner to execute.
34
+ */
35
+ constructor({ loop, nodeFinished, }: {
36
+ loop: PregelLoop;
37
+ nodeFinished?: (id: string) => void;
38
+ });
39
+ /**
40
+ * Execute tasks from the current step of the PregelLoop.
41
+ *
42
+ * Note: this method does NOT call {@link PregelLoop}#tick. That must be handled externally.
43
+ * @param options - Options for the execution.
44
+ */
45
+ tick(options?: TickOptions): Promise<void>;
46
+ /**
47
+ * Concurrently executes tasks with the requested retry policy, yielding a {@link SettledPregelTask} for each task as it completes.
48
+ * @param tasks - The tasks to execute.
49
+ * @param options - Options for the execution.
50
+ */
51
+ private _executeTasksWithRetry;
52
+ /**
53
+ * Determines what writes to apply based on whether the task completed successfully, and what type of error occurred.
54
+ *
55
+ * Throws an error if the error is a {@link GraphBubbleUp} error and {@link PregelLoop}#isNested is true.
56
+ *
57
+ * Note that in the case of a {@link GraphBubbleUp} error that is not a {@link GraphInterrupt}, like a {@link Command}, this method does not apply any writes.
58
+ *
59
+ * @param task - The task to commit.
60
+ * @param error - The error that occurred, if any.
61
+ * @returns The {@link GraphInterrupt} that occurred, if the user's code threw one.
62
+ */
63
+ private _commit;
64
+ }