@langchain/langgraph 0.1.1 → 0.1.3

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 (46) hide show
  1. package/README.md +2 -2
  2. package/dist/constants.cjs +1 -0
  3. package/dist/constants.d.ts +1 -0
  4. package/dist/constants.js +1 -0
  5. package/dist/graph/annotation.cjs +78 -4
  6. package/dist/graph/annotation.d.ts +82 -5
  7. package/dist/graph/annotation.js +77 -2
  8. package/dist/graph/graph.cjs +14 -8
  9. package/dist/graph/graph.d.ts +14 -5
  10. package/dist/graph/graph.js +14 -8
  11. package/dist/graph/message.cjs +6 -0
  12. package/dist/graph/message.d.ts +6 -0
  13. package/dist/graph/message.js +6 -0
  14. package/dist/graph/messages_annotation.cjs +5 -0
  15. package/dist/graph/messages_annotation.d.ts +5 -0
  16. package/dist/graph/messages_annotation.js +5 -0
  17. package/dist/graph/state.cjs +84 -3
  18. package/dist/graph/state.d.ts +75 -5
  19. package/dist/graph/state.js +86 -5
  20. package/dist/prebuilt/agent_executor.cjs +1 -0
  21. package/dist/prebuilt/agent_executor.d.ts +2 -0
  22. package/dist/prebuilt/agent_executor.js +1 -0
  23. package/dist/prebuilt/chat_agent_executor.cjs +1 -0
  24. package/dist/prebuilt/chat_agent_executor.d.ts +2 -0
  25. package/dist/prebuilt/chat_agent_executor.js +1 -0
  26. package/dist/prebuilt/react_agent_executor.cjs +1 -1
  27. package/dist/prebuilt/react_agent_executor.js +2 -2
  28. package/dist/pregel/algo.cjs +3 -41
  29. package/dist/pregel/algo.d.ts +0 -5
  30. package/dist/pregel/algo.js +2 -39
  31. package/dist/pregel/debug.d.ts +1 -1
  32. package/dist/pregel/index.cjs +17 -30
  33. package/dist/pregel/index.d.ts +2 -0
  34. package/dist/pregel/index.js +18 -31
  35. package/dist/pregel/loop.cjs +5 -2
  36. package/dist/pregel/loop.js +5 -2
  37. package/dist/pregel/read.cjs +19 -1
  38. package/dist/pregel/read.d.ts +5 -0
  39. package/dist/pregel/read.js +19 -1
  40. package/dist/pregel/retry.cjs +147 -0
  41. package/dist/pregel/retry.d.ts +11 -0
  42. package/dist/pregel/retry.js +143 -0
  43. package/dist/pregel/types.d.ts +4 -2
  44. package/dist/pregel/utils.d.ts +26 -0
  45. package/dist/web.d.ts +1 -0
  46. package/package.json +6 -4
@@ -16,6 +16,7 @@ const algo_js_1 = require("./algo.cjs");
16
16
  const utils_js_1 = require("../utils.cjs");
17
17
  const utils_js_2 = require("./utils.cjs");
18
18
  const loop_js_1 = require("./loop.cjs");
19
+ const retry_js_1 = require("./retry.cjs");
19
20
  function isString(value) {
20
21
  return typeof value === "string";
21
22
  }
@@ -159,6 +160,12 @@ class Pregel extends runnables_1.Runnable {
159
160
  writable: true,
160
161
  value: void 0
161
162
  });
163
+ Object.defineProperty(this, "retryPolicy", {
164
+ enumerable: true,
165
+ configurable: true,
166
+ writable: true,
167
+ value: void 0
168
+ });
162
169
  let { streamMode } = fields;
163
170
  if (streamMode != null && !Array.isArray(streamMode)) {
164
171
  streamMode = [streamMode];
@@ -175,6 +182,7 @@ class Pregel extends runnables_1.Runnable {
175
182
  this.stepTimeout = fields.stepTimeout ?? this.stepTimeout;
176
183
  this.debug = fields.debug ?? this.debug;
177
184
  this.checkpointer = fields.checkpointer;
185
+ this.retryPolicy = fields.retryPolicy;
178
186
  if (this.autoValidate) {
179
187
  this.validate();
180
188
  }
@@ -344,12 +352,11 @@ class Pregel extends runnables_1.Runnable {
344
352
  writers.length > 1 ? runnables_1.RunnableSequence.from(writers) : writers[0],
345
353
  writes: [],
346
354
  triggers: [constants_js_1.INTERRUPT],
347
- config: undefined,
348
355
  id: (0, langgraph_checkpoint_1.uuid5)(constants_js_1.INTERRUPT, checkpoint.id),
349
356
  };
350
357
  // execute task
351
358
  await task.proc.invoke(task.input, (0, runnables_1.patchConfig)(config, {
352
- runName: `${this.name}UpdateState`,
359
+ runName: config.runName ?? `${this.getName()}UpdateState`,
353
360
  configurable: {
354
361
  [constants_js_1.CONFIG_KEY_SEND]: (items) => task.writes.push(...items),
355
362
  [constants_js_1.CONFIG_KEY_READ]: algo_js_1._localRead.bind(undefined, checkpoint, channels,
@@ -490,35 +497,15 @@ class Pregel extends runnables_1.Runnable {
490
497
  if (debug) {
491
498
  (0, debug_js_1.printStepTasks)(loop.step, loop.tasks);
492
499
  }
493
- // execute tasks, and wait for one to fail or all to finish.
494
- // each task is independent from all other concurrent tasks
495
- // yield updates/debug output as each task finishes
496
- const tasks = Object.fromEntries(loop.tasks
497
- .filter((task) => task.writes.length === 0)
498
- .map((pregelTask) => {
499
- return [
500
- pregelTask.id,
501
- async () => {
502
- let error;
503
- let result;
504
- try {
505
- result = await pregelTask.proc.invoke(pregelTask.input, pregelTask.config);
506
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
507
- }
508
- catch (e) {
509
- error = e;
510
- error.pregelTaskId = pregelTask.id;
511
- }
512
- return {
513
- task: pregelTask,
514
- result,
515
- error,
516
- };
517
- },
518
- ];
519
- }));
520
500
  try {
521
- for await (const task of (0, algo_js_1.executeTasks)(tasks, this.stepTimeout, config.signal)) {
501
+ // execute tasks, and wait for one to fail or all to finish.
502
+ // each task is independent from all other concurrent tasks
503
+ // yield updates/debug output as each task finishes
504
+ for await (const task of (0, retry_js_1.executeTasksWithRetry)(loop.tasks.filter((task) => task.writes.length === 0), {
505
+ stepTimeout: this.stepTimeout,
506
+ signal: config.signal,
507
+ retryPolicy: this.retryPolicy,
508
+ })) {
522
509
  loop.putWrites(task.id, task.writes);
523
510
  if (streamMode.includes("updates")) {
524
511
  yield* (0, utils_js_1.prefixGenerator)((0, io_js_1.mapOutputUpdates)(outputKeys, [task]), streamMode.length > 1 ? "updates" : undefined);
@@ -6,6 +6,7 @@ import { PregelNode } from "./read.js";
6
6
  import { ChannelWrite } from "./write.js";
7
7
  import { All, PregelInterface, PregelParams, StateSnapshot, StreamMode } from "./types.js";
8
8
  import { StrRecord } from "./algo.js";
9
+ import { RetryPolicy } from "./utils.js";
9
10
  type WriteValue = Runnable | RunnableFunc<unknown, unknown> | unknown;
10
11
  export declare class Channel {
11
12
  static subscribeTo(channels: string, options?: {
@@ -50,6 +51,7 @@ export declare class Pregel<Nn extends StrRecord<string, PregelNode>, Cc extends
50
51
  stepTimeout?: number;
51
52
  debug: boolean;
52
53
  checkpointer?: BaseCheckpointSaver;
54
+ retryPolicy?: RetryPolicy;
53
55
  constructor(fields: PregelParams<Nn, Cc>);
54
56
  validate(): this;
55
57
  get streamChannelsList(): Array<keyof Cc>;
@@ -9,10 +9,11 @@ import { mapDebugTaskResults, printStepCheckpoint, printStepTasks, printStepWrit
9
9
  import { ChannelWrite, PASSTHROUGH } from "./write.js";
10
10
  import { CONFIG_KEY_CHECKPOINTER, CONFIG_KEY_READ, CONFIG_KEY_SEND, ERROR, INTERRUPT, } from "../constants.js";
11
11
  import { GraphRecursionError, GraphValueError, InvalidUpdateError, } from "../errors.js";
12
- import { executeTasks, _prepareNextTasks, _localRead, _applyWrites, } from "./algo.js";
12
+ import { _prepareNextTasks, _localRead, _applyWrites, } from "./algo.js";
13
13
  import { prefixGenerator } from "../utils.js";
14
14
  import { _coerceToDict, getNewChannelVersions } from "./utils.js";
15
15
  import { PregelLoop } from "./loop.js";
16
+ import { executeTasksWithRetry } from "./retry.js";
16
17
  function isString(value) {
17
18
  return typeof value === "string";
18
19
  }
@@ -155,6 +156,12 @@ export class Pregel extends Runnable {
155
156
  writable: true,
156
157
  value: void 0
157
158
  });
159
+ Object.defineProperty(this, "retryPolicy", {
160
+ enumerable: true,
161
+ configurable: true,
162
+ writable: true,
163
+ value: void 0
164
+ });
158
165
  let { streamMode } = fields;
159
166
  if (streamMode != null && !Array.isArray(streamMode)) {
160
167
  streamMode = [streamMode];
@@ -171,6 +178,7 @@ export class Pregel extends Runnable {
171
178
  this.stepTimeout = fields.stepTimeout ?? this.stepTimeout;
172
179
  this.debug = fields.debug ?? this.debug;
173
180
  this.checkpointer = fields.checkpointer;
181
+ this.retryPolicy = fields.retryPolicy;
174
182
  if (this.autoValidate) {
175
183
  this.validate();
176
184
  }
@@ -340,12 +348,11 @@ export class Pregel extends Runnable {
340
348
  writers.length > 1 ? RunnableSequence.from(writers) : writers[0],
341
349
  writes: [],
342
350
  triggers: [INTERRUPT],
343
- config: undefined,
344
351
  id: uuid5(INTERRUPT, checkpoint.id),
345
352
  };
346
353
  // execute task
347
354
  await task.proc.invoke(task.input, patchConfig(config, {
348
- runName: `${this.name}UpdateState`,
355
+ runName: config.runName ?? `${this.getName()}UpdateState`,
349
356
  configurable: {
350
357
  [CONFIG_KEY_SEND]: (items) => task.writes.push(...items),
351
358
  [CONFIG_KEY_READ]: _localRead.bind(undefined, checkpoint, channels,
@@ -486,35 +493,15 @@ export class Pregel extends Runnable {
486
493
  if (debug) {
487
494
  printStepTasks(loop.step, loop.tasks);
488
495
  }
489
- // execute tasks, and wait for one to fail or all to finish.
490
- // each task is independent from all other concurrent tasks
491
- // yield updates/debug output as each task finishes
492
- const tasks = Object.fromEntries(loop.tasks
493
- .filter((task) => task.writes.length === 0)
494
- .map((pregelTask) => {
495
- return [
496
- pregelTask.id,
497
- async () => {
498
- let error;
499
- let result;
500
- try {
501
- result = await pregelTask.proc.invoke(pregelTask.input, pregelTask.config);
502
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
503
- }
504
- catch (e) {
505
- error = e;
506
- error.pregelTaskId = pregelTask.id;
507
- }
508
- return {
509
- task: pregelTask,
510
- result,
511
- error,
512
- };
513
- },
514
- ];
515
- }));
516
496
  try {
517
- for await (const task of executeTasks(tasks, this.stepTimeout, config.signal)) {
497
+ // execute tasks, and wait for one to fail or all to finish.
498
+ // each task is independent from all other concurrent tasks
499
+ // yield updates/debug output as each task finishes
500
+ for await (const task of executeTasksWithRetry(loop.tasks.filter((task) => task.writes.length === 0), {
501
+ stepTimeout: this.stepTimeout,
502
+ signal: config.signal,
503
+ retryPolicy: this.retryPolicy,
504
+ })) {
518
505
  loop.putWrites(task.id, task.writes);
519
506
  if (streamMode.includes("updates")) {
520
507
  yield* prefixGenerator(mapOutputUpdates(outputKeys, [task]), streamMode.length > 1 ? "updates" : undefined);
@@ -269,7 +269,7 @@ class PregelLoop {
269
269
  : (0, io_js_1.mapOutputValues)(outputKeys, writes, this.channels).next().value;
270
270
  await this._putCheckpoint({
271
271
  source: "loop",
272
- writes: metadataWrites,
272
+ writes: metadataWrites ?? null,
273
273
  });
274
274
  // after execution, check if we should interrupt
275
275
  if ((0, algo_js_1.shouldInterrupt)(this.checkpoint, interruptAfter, this.tasks)) {
@@ -377,7 +377,10 @@ class PregelLoop {
377
377
  },
378
378
  ]), this.checkpointerGetNextVersion);
379
379
  // save input checkpoint
380
- await this._putCheckpoint({ source: "input", writes: this.input });
380
+ await this._putCheckpoint({
381
+ source: "input",
382
+ writes: this.input ?? null,
383
+ });
381
384
  }
382
385
  // done with input
383
386
  this.input = isResuming ? INPUT_RESUMING : INPUT_DONE;
@@ -263,7 +263,7 @@ export class PregelLoop {
263
263
  : mapOutputValues(outputKeys, writes, this.channels).next().value;
264
264
  await this._putCheckpoint({
265
265
  source: "loop",
266
- writes: metadataWrites,
266
+ writes: metadataWrites ?? null,
267
267
  });
268
268
  // after execution, check if we should interrupt
269
269
  if (shouldInterrupt(this.checkpoint, interruptAfter, this.tasks)) {
@@ -371,7 +371,10 @@ export class PregelLoop {
371
371
  },
372
372
  ]), this.checkpointerGetNextVersion);
373
373
  // save input checkpoint
374
- await this._putCheckpoint({ source: "input", writes: this.input });
374
+ await this._putCheckpoint({
375
+ source: "input",
376
+ writes: this.input ?? null,
377
+ });
375
378
  }
376
379
  // done with input
377
380
  this.input = isResuming ? INPUT_RESUMING : INPUT_DONE;
@@ -63,7 +63,7 @@ const defaultRunnableBound =
63
63
  /* #__PURE__ */ new runnables_1.RunnablePassthrough();
64
64
  class PregelNode extends runnables_1.RunnableBinding {
65
65
  constructor(fields) {
66
- const { channels, triggers, mapper, writers, bound, kwargs } = fields;
66
+ const { channels, triggers, mapper, writers, bound, kwargs, metadata, retryPolicy, } = fields;
67
67
  const mergedTags = [
68
68
  ...(fields.config?.tags ? fields.config.tags : []),
69
69
  ...(fields.tags ? fields.tags : []),
@@ -121,12 +121,26 @@ class PregelNode extends runnables_1.RunnableBinding {
121
121
  writable: true,
122
122
  value: {}
123
123
  });
124
+ Object.defineProperty(this, "metadata", {
125
+ enumerable: true,
126
+ configurable: true,
127
+ writable: true,
128
+ value: {}
129
+ });
130
+ Object.defineProperty(this, "retryPolicy", {
131
+ enumerable: true,
132
+ configurable: true,
133
+ writable: true,
134
+ value: void 0
135
+ });
124
136
  this.channels = channels;
125
137
  this.triggers = triggers;
126
138
  this.mapper = mapper;
127
139
  this.writers = writers ?? this.writers;
128
140
  this.bound = bound ?? this.bound;
129
141
  this.kwargs = kwargs ?? this.kwargs;
142
+ this.metadata = metadata ?? this.metadata;
143
+ this.retryPolicy = retryPolicy;
130
144
  }
131
145
  getWriters() {
132
146
  const newWriters = [...this.writers];
@@ -188,6 +202,7 @@ class PregelNode extends runnables_1.RunnableBinding {
188
202
  bound: this.bound,
189
203
  kwargs: this.kwargs,
190
204
  config: this.config,
205
+ retryPolicy: this.retryPolicy,
191
206
  });
192
207
  }
193
208
  pipe(coerceable) {
@@ -200,6 +215,7 @@ class PregelNode extends runnables_1.RunnableBinding {
200
215
  bound: this.bound,
201
216
  config: this.config,
202
217
  kwargs: this.kwargs,
218
+ retryPolicy: this.retryPolicy,
203
219
  });
204
220
  }
205
221
  else if (this.bound === defaultRunnableBound) {
@@ -211,6 +227,7 @@ class PregelNode extends runnables_1.RunnableBinding {
211
227
  bound: (0, runnables_1._coerceToRunnable)(coerceable),
212
228
  config: this.config,
213
229
  kwargs: this.kwargs,
230
+ retryPolicy: this.retryPolicy,
214
231
  });
215
232
  }
216
233
  else {
@@ -222,6 +239,7 @@ class PregelNode extends runnables_1.RunnableBinding {
222
239
  bound: this.bound.pipe(coerceable),
223
240
  config: this.config,
224
241
  kwargs: this.kwargs,
242
+ retryPolicy: this.retryPolicy,
225
243
  });
226
244
  }
227
245
  }
@@ -1,5 +1,6 @@
1
1
  import { Runnable, RunnableBinding, RunnableBindingArgs, RunnableConfig, RunnableLike } from "@langchain/core/runnables";
2
2
  import { RunnableCallable } from "../utils.js";
3
+ import type { RetryPolicy } from "./utils.js";
3
4
  export declare class ChannelRead<RunInput = any> extends RunnableCallable {
4
5
  lc_graph_name: string;
5
6
  channel: string | Array<string>;
@@ -17,6 +18,8 @@ interface PregelNodeArgs<RunInput, RunOutput> extends Partial<RunnableBindingArg
17
18
  bound?: Runnable<RunInput, RunOutput>;
18
19
  kwargs?: Record<string, any>;
19
20
  config?: RunnableConfig;
21
+ metadata?: Record<string, unknown>;
22
+ retryPolicy?: RetryPolicy;
20
23
  }
21
24
  export type PregelNodeInputType = any;
22
25
  export type PregelNodeOutputType = any;
@@ -28,6 +31,8 @@ export declare class PregelNode<RunInput = PregelNodeInputType, RunOutput = Preg
28
31
  writers: Runnable[];
29
32
  bound: Runnable<RunInput, RunOutput>;
30
33
  kwargs: Record<string, any>;
34
+ metadata: Record<string, unknown>;
35
+ retryPolicy?: RetryPolicy;
31
36
  constructor(fields: PregelNodeArgs<RunInput, RunOutput>);
32
37
  getWriters(): Array<Runnable>;
33
38
  getNode(): Runnable<RunInput, RunOutput> | undefined;
@@ -59,7 +59,7 @@ const defaultRunnableBound =
59
59
  /* #__PURE__ */ new RunnablePassthrough();
60
60
  export class PregelNode extends RunnableBinding {
61
61
  constructor(fields) {
62
- const { channels, triggers, mapper, writers, bound, kwargs } = fields;
62
+ const { channels, triggers, mapper, writers, bound, kwargs, metadata, retryPolicy, } = fields;
63
63
  const mergedTags = [
64
64
  ...(fields.config?.tags ? fields.config.tags : []),
65
65
  ...(fields.tags ? fields.tags : []),
@@ -117,12 +117,26 @@ export class PregelNode extends RunnableBinding {
117
117
  writable: true,
118
118
  value: {}
119
119
  });
120
+ Object.defineProperty(this, "metadata", {
121
+ enumerable: true,
122
+ configurable: true,
123
+ writable: true,
124
+ value: {}
125
+ });
126
+ Object.defineProperty(this, "retryPolicy", {
127
+ enumerable: true,
128
+ configurable: true,
129
+ writable: true,
130
+ value: void 0
131
+ });
120
132
  this.channels = channels;
121
133
  this.triggers = triggers;
122
134
  this.mapper = mapper;
123
135
  this.writers = writers ?? this.writers;
124
136
  this.bound = bound ?? this.bound;
125
137
  this.kwargs = kwargs ?? this.kwargs;
138
+ this.metadata = metadata ?? this.metadata;
139
+ this.retryPolicy = retryPolicy;
126
140
  }
127
141
  getWriters() {
128
142
  const newWriters = [...this.writers];
@@ -184,6 +198,7 @@ export class PregelNode extends RunnableBinding {
184
198
  bound: this.bound,
185
199
  kwargs: this.kwargs,
186
200
  config: this.config,
201
+ retryPolicy: this.retryPolicy,
187
202
  });
188
203
  }
189
204
  pipe(coerceable) {
@@ -196,6 +211,7 @@ export class PregelNode extends RunnableBinding {
196
211
  bound: this.bound,
197
212
  config: this.config,
198
213
  kwargs: this.kwargs,
214
+ retryPolicy: this.retryPolicy,
199
215
  });
200
216
  }
201
217
  else if (this.bound === defaultRunnableBound) {
@@ -207,6 +223,7 @@ export class PregelNode extends RunnableBinding {
207
223
  bound: _coerceToRunnable(coerceable),
208
224
  config: this.config,
209
225
  kwargs: this.kwargs,
226
+ retryPolicy: this.retryPolicy,
210
227
  });
211
228
  }
212
229
  else {
@@ -218,6 +235,7 @@ export class PregelNode extends RunnableBinding {
218
235
  bound: this.bound.pipe(coerceable),
219
236
  config: this.config,
220
237
  kwargs: this.kwargs,
238
+ retryPolicy: this.retryPolicy,
221
239
  });
222
240
  }
223
241
  }
@@ -0,0 +1,147 @@
1
+ "use strict";
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;
4
+ const errors_js_1 = require("../errors.cjs");
5
+ exports.DEFAULT_INITIAL_INTERVAL = 500;
6
+ exports.DEFAULT_BACKOFF_FACTOR = 2;
7
+ exports.DEFAULT_MAX_INTERVAL = 128000;
8
+ exports.DEFAULT_MAX_RETRIES = 3;
9
+ const DEFAULT_STATUS_NO_RETRY = [
10
+ 400,
11
+ 401,
12
+ 402,
13
+ 403,
14
+ 404,
15
+ 405,
16
+ 406,
17
+ 407,
18
+ 409, // Conflict
19
+ ];
20
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
+ const DEFAULT_RETRY_ON_HANDLER = (error) => {
22
+ if (error.message.startsWith("Cancel") ||
23
+ error.message.startsWith("AbortError") ||
24
+ error.name === "AbortError") {
25
+ return false;
26
+ }
27
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
+ if (error?.code === "ECONNABORTED") {
29
+ return false;
30
+ }
31
+ const status =
32
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
+ error?.response?.status ?? error?.status;
34
+ if (status && DEFAULT_STATUS_NO_RETRY.includes(+status)) {
35
+ return false;
36
+ }
37
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
+ if (error?.error?.code === "insufficient_quota") {
39
+ return false;
40
+ }
41
+ return true;
42
+ };
43
+ async function* executeTasksWithRetry(
44
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
+ tasks, options
46
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
+ ) {
48
+ const { stepTimeout, retryPolicy } = options ?? {};
49
+ let signal = options?.signal;
50
+ // Start tasks
51
+ const executingTasksMap = Object.fromEntries(tasks.map((pregelTask) => {
52
+ return [pregelTask.id, _runWithRetry(pregelTask, retryPolicy)];
53
+ }));
54
+ if (stepTimeout && signal) {
55
+ if ("any" in AbortSignal) {
56
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
57
+ signal = AbortSignal.any([
58
+ signal,
59
+ AbortSignal.timeout(stepTimeout),
60
+ ]);
61
+ }
62
+ }
63
+ else if (stepTimeout) {
64
+ signal = AbortSignal.timeout(stepTimeout);
65
+ }
66
+ // Abort if signal is aborted
67
+ signal?.throwIfAborted();
68
+ let listener;
69
+ const signalPromise = new Promise((_resolve, reject) => {
70
+ listener = () => reject(new Error("Abort"));
71
+ signal?.addEventListener("abort", listener);
72
+ }).finally(() => signal?.removeEventListener("abort", listener));
73
+ while (Object.keys(executingTasksMap).length > 0) {
74
+ const { task, error } = await Promise.race([
75
+ ...Object.values(executingTasksMap),
76
+ signalPromise,
77
+ ]);
78
+ if (error !== undefined) {
79
+ // TODO: don't stop others if exception is interrupt
80
+ throw error;
81
+ }
82
+ yield task;
83
+ delete executingTasksMap[task.id];
84
+ }
85
+ }
86
+ exports.executeTasksWithRetry = executeTasksWithRetry;
87
+ async function _runWithRetry(
88
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
89
+ pregelTask, retryPolicy) {
90
+ const resolvedRetryPolicy = pregelTask.retry_policy ?? retryPolicy;
91
+ let interval = resolvedRetryPolicy !== undefined
92
+ ? resolvedRetryPolicy.initialInterval ?? exports.DEFAULT_INITIAL_INTERVAL
93
+ : 0;
94
+ let attempts = 0;
95
+ let error;
96
+ let result;
97
+ // eslint-disable-next-line no-constant-condition
98
+ while (true) {
99
+ // Modify writes in place to clear any previous retries
100
+ while (pregelTask.writes.length > 0) {
101
+ pregelTask.writes.pop();
102
+ }
103
+ error = undefined;
104
+ try {
105
+ result = await pregelTask.proc.invoke(pregelTask.input, pregelTask.config);
106
+ break;
107
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
108
+ }
109
+ catch (e) {
110
+ error = e;
111
+ error.pregelTaskId = pregelTask.id;
112
+ if (error.name === errors_js_1.GraphInterrupt.unminifiable_name) {
113
+ break;
114
+ }
115
+ if (resolvedRetryPolicy === undefined) {
116
+ break;
117
+ }
118
+ attempts += 1;
119
+ // check if we should give up
120
+ if (attempts >= (resolvedRetryPolicy.maxAttempts ?? exports.DEFAULT_MAX_RETRIES)) {
121
+ break;
122
+ }
123
+ const retryOn = resolvedRetryPolicy.retryOn ?? DEFAULT_RETRY_ON_HANDLER;
124
+ if (!retryOn(error)) {
125
+ break;
126
+ }
127
+ interval = Math.min(resolvedRetryPolicy.maxInterval ?? exports.DEFAULT_MAX_INTERVAL, interval * (resolvedRetryPolicy.backoffFactor ?? exports.DEFAULT_BACKOFF_FACTOR));
128
+ const intervalWithJitter = resolvedRetryPolicy.jitter
129
+ ? Math.floor(interval + Math.random() * 1000)
130
+ : interval;
131
+ // sleep before retrying
132
+ // eslint-disable-next-line no-promise-executor-return
133
+ await new Promise((resolve) => setTimeout(resolve, intervalWithJitter));
134
+ // log the retry
135
+ const errorName = error.name ??
136
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
137
+ error.constructor.unminifiable_name ??
138
+ error.constructor.name;
139
+ console.log(`Retrying task "${pregelTask.name}" after ${interval.toFixed(2)} seconds (attempt ${attempts}) after ${errorName}: ${error}`);
140
+ }
141
+ }
142
+ return {
143
+ task: pregelTask,
144
+ result,
145
+ error,
146
+ };
147
+ }
@@ -0,0 +1,11 @@
1
+ import { PregelExecutableTask } from "./types.js";
2
+ import type { RetryPolicy } from "./utils.js";
3
+ export declare const DEFAULT_INITIAL_INTERVAL = 500;
4
+ export declare const DEFAULT_BACKOFF_FACTOR = 2;
5
+ export declare const DEFAULT_MAX_INTERVAL = 128000;
6
+ export declare const DEFAULT_MAX_RETRIES = 3;
7
+ export declare function executeTasksWithRetry(tasks: PregelExecutableTask<any, any>[], options?: {
8
+ stepTimeout?: number;
9
+ signal?: AbortSignal;
10
+ retryPolicy?: RetryPolicy;
11
+ }): AsyncGenerator<PregelExecutableTask<any, any>>;