@langchain/langgraph 0.2.26 → 0.2.28
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/constants.cjs +33 -2
- package/dist/constants.d.ts +13 -4
- package/dist/constants.js +32 -1
- package/dist/errors.cjs +24 -1
- package/dist/errors.d.ts +8 -2
- package/dist/errors.js +21 -0
- package/dist/graph/graph.cjs +34 -8
- package/dist/graph/graph.d.ts +6 -5
- package/dist/graph/graph.js +35 -9
- package/dist/graph/state.cjs +76 -16
- package/dist/graph/state.d.ts +3 -1
- package/dist/graph/state.js +79 -19
- package/dist/prebuilt/index.d.ts +1 -1
- package/dist/prebuilt/index.js +1 -1
- package/dist/prebuilt/react_agent_executor.cjs +65 -46
- package/dist/prebuilt/react_agent_executor.d.ts +67 -13
- package/dist/prebuilt/react_agent_executor.js +66 -47
- package/dist/prebuilt/tool_node.cjs +7 -0
- package/dist/prebuilt/tool_node.js +7 -0
- package/dist/pregel/algo.cjs +32 -1
- package/dist/pregel/algo.d.ts +3 -2
- package/dist/pregel/algo.js +32 -1
- package/dist/pregel/index.cjs +66 -6
- package/dist/pregel/index.js +68 -8
- package/dist/pregel/io.cjs +35 -0
- package/dist/pregel/io.d.ts +3 -0
- package/dist/pregel/io.js +37 -2
- package/dist/pregel/loop.cjs +17 -16
- package/dist/pregel/loop.js +17 -16
- package/dist/pregel/retry.cjs +23 -0
- package/dist/pregel/retry.js +24 -1
- package/dist/pregel/types.d.ts +1 -0
- package/dist/pregel/write.cjs +27 -24
- package/dist/pregel/write.d.ts +1 -2
- package/dist/pregel/write.js +27 -24
- package/dist/web.d.ts +1 -1
- package/dist/web.js +1 -1
- package/package.json +1 -1
package/dist/pregel/algo.cjs
CHANGED
|
@@ -100,6 +100,21 @@ const IGNORE = new Set([constants_js_1.PUSH, constants_js_1.RESUME, constants_js
|
|
|
100
100
|
function _applyWrites(checkpoint, channels, tasks,
|
|
101
101
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
102
102
|
getNextVersion) {
|
|
103
|
+
// Sort tasks by first 3 path elements for deterministic order
|
|
104
|
+
// Later path parts (like task IDs) are ignored for sorting
|
|
105
|
+
tasks.sort((a, b) => {
|
|
106
|
+
const aPath = a.path?.slice(0, 3) || [];
|
|
107
|
+
const bPath = b.path?.slice(0, 3) || [];
|
|
108
|
+
// Compare each path element
|
|
109
|
+
for (let i = 0; i < Math.min(aPath.length, bPath.length); i += 1) {
|
|
110
|
+
if (aPath[i] < bPath[i])
|
|
111
|
+
return -1;
|
|
112
|
+
if (aPath[i] > bPath[i])
|
|
113
|
+
return 1;
|
|
114
|
+
}
|
|
115
|
+
// If one path is shorter, it comes first
|
|
116
|
+
return aPath.length - bPath.length;
|
|
117
|
+
});
|
|
103
118
|
// if no task has triggers this is applying writes from the null task only
|
|
104
119
|
// so we don't do anything other than update the channels written to
|
|
105
120
|
const bumpStep = tasks.some((task) => task.triggers.length > 0);
|
|
@@ -146,6 +161,7 @@ getNextVersion) {
|
|
|
146
161
|
// do nothing
|
|
147
162
|
}
|
|
148
163
|
else if (chan === constants_js_1.TASKS) {
|
|
164
|
+
// TODO: remove branch in 1.0
|
|
149
165
|
checkpoint.pending_sends.push({
|
|
150
166
|
node: val.node,
|
|
151
167
|
args: val.args,
|
|
@@ -214,6 +230,11 @@ getNextVersion) {
|
|
|
214
230
|
return pendingWritesByManaged;
|
|
215
231
|
}
|
|
216
232
|
exports._applyWrites = _applyWrites;
|
|
233
|
+
/**
|
|
234
|
+
* Prepare the set of tasks that will make up the next Pregel step.
|
|
235
|
+
* This is the union of all PUSH tasks (Sends) and PULL tasks (nodes triggered
|
|
236
|
+
* by edges).
|
|
237
|
+
*/
|
|
217
238
|
function _prepareNextTasks(checkpoint, pendingWrites, processes, channels, managed, config, forExecution, extra) {
|
|
218
239
|
const tasks = {};
|
|
219
240
|
// Consume pending packets
|
|
@@ -234,12 +255,18 @@ function _prepareNextTasks(checkpoint, pendingWrites, processes, channels, manag
|
|
|
234
255
|
return tasks;
|
|
235
256
|
}
|
|
236
257
|
exports._prepareNextTasks = _prepareNextTasks;
|
|
258
|
+
/**
|
|
259
|
+
* Prepares a single task for the next Pregel step, given a task path, which
|
|
260
|
+
* uniquely identifies a PUSH or PULL task within the graph.
|
|
261
|
+
*/
|
|
237
262
|
function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processes, channels, managed, config, forExecution, extra) {
|
|
238
263
|
const { step, checkpointer, manager } = extra;
|
|
239
264
|
const configurable = config.configurable ?? {};
|
|
240
265
|
const parentNamespace = configurable.checkpoint_ns ?? "";
|
|
241
266
|
if (taskPath[0] === constants_js_1.PUSH) {
|
|
242
|
-
const index = typeof taskPath[1] === "number"
|
|
267
|
+
const index = typeof taskPath[1] === "number"
|
|
268
|
+
? taskPath[1]
|
|
269
|
+
: parseInt(taskPath[1], 10);
|
|
243
270
|
if (index >= checkpoint.pending_sends.length) {
|
|
244
271
|
return undefined;
|
|
245
272
|
}
|
|
@@ -302,6 +329,7 @@ function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processes, chan
|
|
|
302
329
|
name: packet.node,
|
|
303
330
|
writes: writes,
|
|
304
331
|
triggers,
|
|
332
|
+
path: taskPath,
|
|
305
333
|
}, select_, fresh_),
|
|
306
334
|
[constants_js_1.CONFIG_KEY_CHECKPOINTER]: checkpointer ?? configurable[constants_js_1.CONFIG_KEY_CHECKPOINTER],
|
|
307
335
|
[constants_js_1.CONFIG_KEY_CHECKPOINT_MAP]: {
|
|
@@ -319,6 +347,7 @@ function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processes, chan
|
|
|
319
347
|
retry_policy: proc.retryPolicy,
|
|
320
348
|
id: taskId,
|
|
321
349
|
path: taskPath,
|
|
350
|
+
writers: proc.getWriters(),
|
|
322
351
|
};
|
|
323
352
|
}
|
|
324
353
|
}
|
|
@@ -405,6 +434,7 @@ function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processes, chan
|
|
|
405
434
|
name,
|
|
406
435
|
writes: writes,
|
|
407
436
|
triggers,
|
|
437
|
+
path: taskPath,
|
|
408
438
|
}, select_, fresh_),
|
|
409
439
|
[constants_js_1.CONFIG_KEY_CHECKPOINTER]: checkpointer ?? configurable[constants_js_1.CONFIG_KEY_CHECKPOINTER],
|
|
410
440
|
[constants_js_1.CONFIG_KEY_CHECKPOINT_MAP]: {
|
|
@@ -422,6 +452,7 @@ function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processes, chan
|
|
|
422
452
|
retry_policy: proc.retryPolicy,
|
|
423
453
|
id: taskId,
|
|
424
454
|
path: taskPath,
|
|
455
|
+
writers: proc.getWriters(),
|
|
425
456
|
};
|
|
426
457
|
}
|
|
427
458
|
}
|
package/dist/pregel/algo.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export type WritesProtocol<C = string> = {
|
|
|
15
15
|
name: string;
|
|
16
16
|
writes: PendingWrite<C>[];
|
|
17
17
|
triggers: string[];
|
|
18
|
+
path?: [string, ...(string | number)[]];
|
|
18
19
|
};
|
|
19
20
|
export declare const increment: (current?: number) => number;
|
|
20
21
|
export declare function shouldInterrupt<N extends PropertyKey, C extends PropertyKey>(checkpoint: Checkpoint, interruptNodes: All | N[], tasks: PregelExecutableTask<N, C>[]): boolean;
|
|
@@ -37,5 +38,5 @@ export type NextTaskExtraFieldsWithoutStore = NextTaskExtraFields & {
|
|
|
37
38
|
export declare function _prepareNextTasks<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel>>(checkpoint: ReadonlyCheckpoint, pendingWrites: [string, string, unknown][] | undefined, processes: Nn, channels: Cc, managed: ManagedValueMapping, config: RunnableConfig, forExecution: false, extra: NextTaskExtraFieldsWithoutStore): Record<string, PregelTaskDescription>;
|
|
38
39
|
export declare function _prepareNextTasks<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel>>(checkpoint: ReadonlyCheckpoint, pendingWrites: [string, string, unknown][] | undefined, processes: Nn, channels: Cc, managed: ManagedValueMapping, config: RunnableConfig, forExecution: true, extra: NextTaskExtraFieldsWithStore): Record<string, PregelExecutableTask<keyof Nn, keyof Cc>>;
|
|
39
40
|
export declare function _prepareSingleTask<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel>>(taskPath: [string, string | number], checkpoint: ReadonlyCheckpoint, pendingWrites: [string, string, unknown][] | undefined, processes: Nn, channels: Cc, managed: ManagedValueMapping, config: RunnableConfig, forExecution: false, extra: NextTaskExtraFields): PregelTaskDescription | undefined;
|
|
40
|
-
export declare function _prepareSingleTask<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel>>(taskPath: [string, string | number], checkpoint: ReadonlyCheckpoint, pendingWrites: [string, string, unknown][] | undefined, processes: Nn, channels: Cc, managed: ManagedValueMapping, config: RunnableConfig, forExecution: true, extra: NextTaskExtraFields): PregelExecutableTask<keyof Nn, keyof Cc> | undefined;
|
|
41
|
-
export declare function _prepareSingleTask<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel>>(taskPath: [string, string | number], checkpoint: ReadonlyCheckpoint, pendingWrites: [string, string, unknown][] | undefined, processes: Nn, channels: Cc, managed: ManagedValueMapping, config: RunnableConfig, forExecution: boolean, extra: NextTaskExtraFieldsWithStore): PregelTaskDescription | PregelExecutableTask<keyof Nn, keyof Cc> | undefined;
|
|
41
|
+
export declare function _prepareSingleTask<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel>>(taskPath: [string, ...(string | number)[]], checkpoint: ReadonlyCheckpoint, pendingWrites: [string, string, unknown][] | undefined, processes: Nn, channels: Cc, managed: ManagedValueMapping, config: RunnableConfig, forExecution: true, extra: NextTaskExtraFields): PregelExecutableTask<keyof Nn, keyof Cc> | undefined;
|
|
42
|
+
export declare function _prepareSingleTask<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel>>(taskPath: [string, ...(string | number)[]], checkpoint: ReadonlyCheckpoint, pendingWrites: [string, string, unknown][] | undefined, processes: Nn, channels: Cc, managed: ManagedValueMapping, config: RunnableConfig, forExecution: boolean, extra: NextTaskExtraFieldsWithStore): PregelTaskDescription | PregelExecutableTask<keyof Nn, keyof Cc> | undefined;
|
package/dist/pregel/algo.js
CHANGED
|
@@ -93,6 +93,21 @@ const IGNORE = new Set([PUSH, RESUME, INTERRUPT]);
|
|
|
93
93
|
export function _applyWrites(checkpoint, channels, tasks,
|
|
94
94
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
95
95
|
getNextVersion) {
|
|
96
|
+
// Sort tasks by first 3 path elements for deterministic order
|
|
97
|
+
// Later path parts (like task IDs) are ignored for sorting
|
|
98
|
+
tasks.sort((a, b) => {
|
|
99
|
+
const aPath = a.path?.slice(0, 3) || [];
|
|
100
|
+
const bPath = b.path?.slice(0, 3) || [];
|
|
101
|
+
// Compare each path element
|
|
102
|
+
for (let i = 0; i < Math.min(aPath.length, bPath.length); i += 1) {
|
|
103
|
+
if (aPath[i] < bPath[i])
|
|
104
|
+
return -1;
|
|
105
|
+
if (aPath[i] > bPath[i])
|
|
106
|
+
return 1;
|
|
107
|
+
}
|
|
108
|
+
// If one path is shorter, it comes first
|
|
109
|
+
return aPath.length - bPath.length;
|
|
110
|
+
});
|
|
96
111
|
// if no task has triggers this is applying writes from the null task only
|
|
97
112
|
// so we don't do anything other than update the channels written to
|
|
98
113
|
const bumpStep = tasks.some((task) => task.triggers.length > 0);
|
|
@@ -139,6 +154,7 @@ getNextVersion) {
|
|
|
139
154
|
// do nothing
|
|
140
155
|
}
|
|
141
156
|
else if (chan === TASKS) {
|
|
157
|
+
// TODO: remove branch in 1.0
|
|
142
158
|
checkpoint.pending_sends.push({
|
|
143
159
|
node: val.node,
|
|
144
160
|
args: val.args,
|
|
@@ -206,6 +222,11 @@ getNextVersion) {
|
|
|
206
222
|
// Return managed values writes to be applied externally
|
|
207
223
|
return pendingWritesByManaged;
|
|
208
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Prepare the set of tasks that will make up the next Pregel step.
|
|
227
|
+
* This is the union of all PUSH tasks (Sends) and PULL tasks (nodes triggered
|
|
228
|
+
* by edges).
|
|
229
|
+
*/
|
|
209
230
|
export function _prepareNextTasks(checkpoint, pendingWrites, processes, channels, managed, config, forExecution, extra) {
|
|
210
231
|
const tasks = {};
|
|
211
232
|
// Consume pending packets
|
|
@@ -225,12 +246,18 @@ export function _prepareNextTasks(checkpoint, pendingWrites, processes, channels
|
|
|
225
246
|
}
|
|
226
247
|
return tasks;
|
|
227
248
|
}
|
|
249
|
+
/**
|
|
250
|
+
* Prepares a single task for the next Pregel step, given a task path, which
|
|
251
|
+
* uniquely identifies a PUSH or PULL task within the graph.
|
|
252
|
+
*/
|
|
228
253
|
export function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processes, channels, managed, config, forExecution, extra) {
|
|
229
254
|
const { step, checkpointer, manager } = extra;
|
|
230
255
|
const configurable = config.configurable ?? {};
|
|
231
256
|
const parentNamespace = configurable.checkpoint_ns ?? "";
|
|
232
257
|
if (taskPath[0] === PUSH) {
|
|
233
|
-
const index = typeof taskPath[1] === "number"
|
|
258
|
+
const index = typeof taskPath[1] === "number"
|
|
259
|
+
? taskPath[1]
|
|
260
|
+
: parseInt(taskPath[1], 10);
|
|
234
261
|
if (index >= checkpoint.pending_sends.length) {
|
|
235
262
|
return undefined;
|
|
236
263
|
}
|
|
@@ -293,6 +320,7 @@ export function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processe
|
|
|
293
320
|
name: packet.node,
|
|
294
321
|
writes: writes,
|
|
295
322
|
triggers,
|
|
323
|
+
path: taskPath,
|
|
296
324
|
}, select_, fresh_),
|
|
297
325
|
[CONFIG_KEY_CHECKPOINTER]: checkpointer ?? configurable[CONFIG_KEY_CHECKPOINTER],
|
|
298
326
|
[CONFIG_KEY_CHECKPOINT_MAP]: {
|
|
@@ -310,6 +338,7 @@ export function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processe
|
|
|
310
338
|
retry_policy: proc.retryPolicy,
|
|
311
339
|
id: taskId,
|
|
312
340
|
path: taskPath,
|
|
341
|
+
writers: proc.getWriters(),
|
|
313
342
|
};
|
|
314
343
|
}
|
|
315
344
|
}
|
|
@@ -396,6 +425,7 @@ export function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processe
|
|
|
396
425
|
name,
|
|
397
426
|
writes: writes,
|
|
398
427
|
triggers,
|
|
428
|
+
path: taskPath,
|
|
399
429
|
}, select_, fresh_),
|
|
400
430
|
[CONFIG_KEY_CHECKPOINTER]: checkpointer ?? configurable[CONFIG_KEY_CHECKPOINTER],
|
|
401
431
|
[CONFIG_KEY_CHECKPOINT_MAP]: {
|
|
@@ -413,6 +443,7 @@ export function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processe
|
|
|
413
443
|
retry_policy: proc.retryPolicy,
|
|
414
444
|
id: taskId,
|
|
415
445
|
path: taskPath,
|
|
446
|
+
writers: proc.getWriters(),
|
|
416
447
|
};
|
|
417
448
|
}
|
|
418
449
|
}
|
package/dist/pregel/index.cjs
CHANGED
|
@@ -472,8 +472,13 @@ class Pregel extends runnables_1.Runnable {
|
|
|
472
472
|
let checkpointConfig = (0, utils_js_1.patchConfigurable)(config, {
|
|
473
473
|
checkpoint_ns: config.configurable?.checkpoint_ns ?? "",
|
|
474
474
|
});
|
|
475
|
+
let checkpointMetadata = config.metadata ?? {};
|
|
475
476
|
if (saved?.config.configurable) {
|
|
476
477
|
checkpointConfig = (0, utils_js_1.patchConfigurable)(config, saved.config.configurable);
|
|
478
|
+
checkpointMetadata = {
|
|
479
|
+
...saved.metadata,
|
|
480
|
+
...checkpointMetadata,
|
|
481
|
+
};
|
|
477
482
|
}
|
|
478
483
|
// Find last node that updated the state, if not provided
|
|
479
484
|
if (values == null && asNode === undefined) {
|
|
@@ -485,6 +490,54 @@ class Pregel extends runnables_1.Runnable {
|
|
|
485
490
|
}, {});
|
|
486
491
|
return (0, index_js_1.patchCheckpointMap)(nextConfig, saved ? saved.metadata : undefined);
|
|
487
492
|
}
|
|
493
|
+
// update channels
|
|
494
|
+
const channels = (0, base_js_1.emptyChannels)(this.channels, checkpoint);
|
|
495
|
+
// Pass `skipManaged: true` as managed values are not used/relevant in update state calls.
|
|
496
|
+
const { managed } = await this.prepareSpecs(config, { skipManaged: true });
|
|
497
|
+
if (values === null && asNode === "__end__") {
|
|
498
|
+
if (saved) {
|
|
499
|
+
// tasks for this checkpoint
|
|
500
|
+
const nextTasks = (0, algo_js_1._prepareNextTasks)(checkpoint, saved.pendingWrites || [], this.nodes, channels, managed, saved.config, true, {
|
|
501
|
+
step: (saved.metadata?.step ?? -1) + 1,
|
|
502
|
+
checkpointer: this.checkpointer || undefined,
|
|
503
|
+
store: this.store,
|
|
504
|
+
});
|
|
505
|
+
// apply null writes
|
|
506
|
+
const nullWrites = (saved.pendingWrites || [])
|
|
507
|
+
.filter((w) => w[0] === constants_js_1.NULL_TASK_ID)
|
|
508
|
+
.flatMap((w) => w.slice(1));
|
|
509
|
+
if (nullWrites.length > 0) {
|
|
510
|
+
(0, algo_js_1._applyWrites)(saved.checkpoint, channels, [
|
|
511
|
+
{
|
|
512
|
+
name: constants_js_1.INPUT,
|
|
513
|
+
writes: nullWrites,
|
|
514
|
+
triggers: [],
|
|
515
|
+
},
|
|
516
|
+
]);
|
|
517
|
+
}
|
|
518
|
+
// apply writes from tasks that already ran
|
|
519
|
+
for (const [taskId, k, v] of saved.pendingWrites || []) {
|
|
520
|
+
if ([constants_js_1.ERROR, constants_js_1.INTERRUPT, langgraph_checkpoint_1.SCHEDULED].includes(k)) {
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
if (!(taskId in nextTasks)) {
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
nextTasks[taskId].writes.push([k, v]);
|
|
527
|
+
}
|
|
528
|
+
// clear all current tasks
|
|
529
|
+
(0, algo_js_1._applyWrites)(checkpoint, channels, Object.values(nextTasks));
|
|
530
|
+
}
|
|
531
|
+
// save checkpoint
|
|
532
|
+
const nextConfig = await checkpointer.put(checkpointConfig, (0, base_js_1.createCheckpoint)(checkpoint, undefined, step), {
|
|
533
|
+
...checkpointMetadata,
|
|
534
|
+
source: "update",
|
|
535
|
+
step: step + 1,
|
|
536
|
+
writes: {},
|
|
537
|
+
parents: saved?.metadata?.parents ?? {},
|
|
538
|
+
}, {});
|
|
539
|
+
return (0, index_js_1.patchCheckpointMap)(nextConfig, saved ? saved.metadata : undefined);
|
|
540
|
+
}
|
|
488
541
|
if (values == null && asNode === "__copy__") {
|
|
489
542
|
const nextConfig = await checkpointer.put(saved?.parentConfig ?? checkpointConfig, (0, base_js_1.createCheckpoint)(checkpoint, undefined, step), {
|
|
490
543
|
source: "fork",
|
|
@@ -534,10 +587,6 @@ class Pregel extends runnables_1.Runnable {
|
|
|
534
587
|
if (this.nodes[asNode] === undefined) {
|
|
535
588
|
throw new errors_js_1.InvalidUpdateError(`Node "${asNode.toString()}" does not exist`);
|
|
536
589
|
}
|
|
537
|
-
// update channels
|
|
538
|
-
const channels = (0, base_js_1.emptyChannels)(this.channels, checkpoint);
|
|
539
|
-
// Pass `skipManaged: true` as managed values are not used/relevant in update state calls.
|
|
540
|
-
const { managed } = await this.prepareSpecs(config, { skipManaged: true });
|
|
541
590
|
// run all writers of the chosen node
|
|
542
591
|
const writers = this.nodes[asNode].getWriters();
|
|
543
592
|
if (!writers.length) {
|
|
@@ -553,6 +602,7 @@ class Pregel extends runnables_1.Runnable {
|
|
|
553
602
|
writes: [],
|
|
554
603
|
triggers: [constants_js_1.INTERRUPT],
|
|
555
604
|
id: (0, langgraph_checkpoint_1.uuid5)(constants_js_1.INTERRUPT, checkpoint.id),
|
|
605
|
+
writers: [],
|
|
556
606
|
};
|
|
557
607
|
// execute task
|
|
558
608
|
await task.proc.invoke(task.input, (0, runnables_1.patchConfig)({
|
|
@@ -568,8 +618,15 @@ class Pregel extends runnables_1.Runnable {
|
|
|
568
618
|
},
|
|
569
619
|
}));
|
|
570
620
|
// save task writes
|
|
571
|
-
|
|
572
|
-
|
|
621
|
+
// channel writes are saved to current checkpoint
|
|
622
|
+
// push writes are saved to next checkpoint
|
|
623
|
+
const [channelWrites, pushWrites] = [
|
|
624
|
+
task.writes.filter((w) => w[0] !== constants_js_1.PUSH),
|
|
625
|
+
task.writes.filter((w) => w[0] === constants_js_1.PUSH),
|
|
626
|
+
];
|
|
627
|
+
// save task writes
|
|
628
|
+
if (saved !== undefined && channelWrites.length > 0) {
|
|
629
|
+
await checkpointer.putWrites(checkpointConfig, channelWrites, task.id);
|
|
573
630
|
}
|
|
574
631
|
// apply to checkpoint
|
|
575
632
|
// TODO: Why does keyof StrRecord allow number and symbol?
|
|
@@ -581,6 +638,9 @@ class Pregel extends runnables_1.Runnable {
|
|
|
581
638
|
writes: { [asNode]: values },
|
|
582
639
|
parents: saved?.metadata?.parents ?? {},
|
|
583
640
|
}, newVersions);
|
|
641
|
+
if (pushWrites.length > 0) {
|
|
642
|
+
await checkpointer.putWrites(nextConfig, pushWrites, task.id);
|
|
643
|
+
}
|
|
584
644
|
return (0, index_js_1.patchCheckpointMap)(nextConfig, saved ? saved.metadata : undefined);
|
|
585
645
|
}
|
|
586
646
|
_defaults(config) {
|
package/dist/pregel/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/* eslint-disable no-param-reassign */
|
|
2
2
|
import { Runnable, RunnableSequence, getCallbackManagerForConfig, mergeConfigs, patchConfig, _coerceToRunnable, } from "@langchain/core/runnables";
|
|
3
|
-
import { compareChannelVersions, copyCheckpoint, emptyCheckpoint, uuid5, } from "@langchain/langgraph-checkpoint";
|
|
3
|
+
import { compareChannelVersions, copyCheckpoint, emptyCheckpoint, SCHEDULED, uuid5, } from "@langchain/langgraph-checkpoint";
|
|
4
4
|
import { createCheckpoint, emptyChannels, isBaseChannel, } from "../channels/base.js";
|
|
5
5
|
import { PregelNode } from "./read.js";
|
|
6
6
|
import { validateGraph, validateKeys } from "./validate.js";
|
|
7
7
|
import { readChannels } from "./io.js";
|
|
8
8
|
import { printStepCheckpoint, printStepTasks, printStepWrites, tasksWithWrites, } from "./debug.js";
|
|
9
9
|
import { ChannelWrite, PASSTHROUGH } from "./write.js";
|
|
10
|
-
import { CONFIG_KEY_CHECKPOINTER, CONFIG_KEY_READ, CONFIG_KEY_SEND, ERROR, INTERRUPT, CHECKPOINT_NAMESPACE_SEPARATOR, CHECKPOINT_NAMESPACE_END, CONFIG_KEY_STREAM, CONFIG_KEY_TASK_ID, } from "../constants.js";
|
|
10
|
+
import { CONFIG_KEY_CHECKPOINTER, CONFIG_KEY_READ, CONFIG_KEY_SEND, ERROR, INTERRUPT, CHECKPOINT_NAMESPACE_SEPARATOR, CHECKPOINT_NAMESPACE_END, CONFIG_KEY_STREAM, CONFIG_KEY_TASK_ID, NULL_TASK_ID, INPUT, PUSH, } from "../constants.js";
|
|
11
11
|
import { GraphRecursionError, GraphValueError, InvalidUpdateError, isGraphBubbleUp, isGraphInterrupt, } from "../errors.js";
|
|
12
12
|
import { _prepareNextTasks, _localRead, _applyWrites, } from "./algo.js";
|
|
13
13
|
import { _coerceToDict, getNewChannelVersions, patchCheckpointMap, } from "./utils/index.js";
|
|
@@ -468,8 +468,13 @@ export class Pregel extends Runnable {
|
|
|
468
468
|
let checkpointConfig = patchConfigurable(config, {
|
|
469
469
|
checkpoint_ns: config.configurable?.checkpoint_ns ?? "",
|
|
470
470
|
});
|
|
471
|
+
let checkpointMetadata = config.metadata ?? {};
|
|
471
472
|
if (saved?.config.configurable) {
|
|
472
473
|
checkpointConfig = patchConfigurable(config, saved.config.configurable);
|
|
474
|
+
checkpointMetadata = {
|
|
475
|
+
...saved.metadata,
|
|
476
|
+
...checkpointMetadata,
|
|
477
|
+
};
|
|
473
478
|
}
|
|
474
479
|
// Find last node that updated the state, if not provided
|
|
475
480
|
if (values == null && asNode === undefined) {
|
|
@@ -481,6 +486,54 @@ export class Pregel extends Runnable {
|
|
|
481
486
|
}, {});
|
|
482
487
|
return patchCheckpointMap(nextConfig, saved ? saved.metadata : undefined);
|
|
483
488
|
}
|
|
489
|
+
// update channels
|
|
490
|
+
const channels = emptyChannels(this.channels, checkpoint);
|
|
491
|
+
// Pass `skipManaged: true` as managed values are not used/relevant in update state calls.
|
|
492
|
+
const { managed } = await this.prepareSpecs(config, { skipManaged: true });
|
|
493
|
+
if (values === null && asNode === "__end__") {
|
|
494
|
+
if (saved) {
|
|
495
|
+
// tasks for this checkpoint
|
|
496
|
+
const nextTasks = _prepareNextTasks(checkpoint, saved.pendingWrites || [], this.nodes, channels, managed, saved.config, true, {
|
|
497
|
+
step: (saved.metadata?.step ?? -1) + 1,
|
|
498
|
+
checkpointer: this.checkpointer || undefined,
|
|
499
|
+
store: this.store,
|
|
500
|
+
});
|
|
501
|
+
// apply null writes
|
|
502
|
+
const nullWrites = (saved.pendingWrites || [])
|
|
503
|
+
.filter((w) => w[0] === NULL_TASK_ID)
|
|
504
|
+
.flatMap((w) => w.slice(1));
|
|
505
|
+
if (nullWrites.length > 0) {
|
|
506
|
+
_applyWrites(saved.checkpoint, channels, [
|
|
507
|
+
{
|
|
508
|
+
name: INPUT,
|
|
509
|
+
writes: nullWrites,
|
|
510
|
+
triggers: [],
|
|
511
|
+
},
|
|
512
|
+
]);
|
|
513
|
+
}
|
|
514
|
+
// apply writes from tasks that already ran
|
|
515
|
+
for (const [taskId, k, v] of saved.pendingWrites || []) {
|
|
516
|
+
if ([ERROR, INTERRUPT, SCHEDULED].includes(k)) {
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
if (!(taskId in nextTasks)) {
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
nextTasks[taskId].writes.push([k, v]);
|
|
523
|
+
}
|
|
524
|
+
// clear all current tasks
|
|
525
|
+
_applyWrites(checkpoint, channels, Object.values(nextTasks));
|
|
526
|
+
}
|
|
527
|
+
// save checkpoint
|
|
528
|
+
const nextConfig = await checkpointer.put(checkpointConfig, createCheckpoint(checkpoint, undefined, step), {
|
|
529
|
+
...checkpointMetadata,
|
|
530
|
+
source: "update",
|
|
531
|
+
step: step + 1,
|
|
532
|
+
writes: {},
|
|
533
|
+
parents: saved?.metadata?.parents ?? {},
|
|
534
|
+
}, {});
|
|
535
|
+
return patchCheckpointMap(nextConfig, saved ? saved.metadata : undefined);
|
|
536
|
+
}
|
|
484
537
|
if (values == null && asNode === "__copy__") {
|
|
485
538
|
const nextConfig = await checkpointer.put(saved?.parentConfig ?? checkpointConfig, createCheckpoint(checkpoint, undefined, step), {
|
|
486
539
|
source: "fork",
|
|
@@ -530,10 +583,6 @@ export class Pregel extends Runnable {
|
|
|
530
583
|
if (this.nodes[asNode] === undefined) {
|
|
531
584
|
throw new InvalidUpdateError(`Node "${asNode.toString()}" does not exist`);
|
|
532
585
|
}
|
|
533
|
-
// update channels
|
|
534
|
-
const channels = emptyChannels(this.channels, checkpoint);
|
|
535
|
-
// Pass `skipManaged: true` as managed values are not used/relevant in update state calls.
|
|
536
|
-
const { managed } = await this.prepareSpecs(config, { skipManaged: true });
|
|
537
586
|
// run all writers of the chosen node
|
|
538
587
|
const writers = this.nodes[asNode].getWriters();
|
|
539
588
|
if (!writers.length) {
|
|
@@ -549,6 +598,7 @@ export class Pregel extends Runnable {
|
|
|
549
598
|
writes: [],
|
|
550
599
|
triggers: [INTERRUPT],
|
|
551
600
|
id: uuid5(INTERRUPT, checkpoint.id),
|
|
601
|
+
writers: [],
|
|
552
602
|
};
|
|
553
603
|
// execute task
|
|
554
604
|
await task.proc.invoke(task.input, patchConfig({
|
|
@@ -564,8 +614,15 @@ export class Pregel extends Runnable {
|
|
|
564
614
|
},
|
|
565
615
|
}));
|
|
566
616
|
// save task writes
|
|
567
|
-
|
|
568
|
-
|
|
617
|
+
// channel writes are saved to current checkpoint
|
|
618
|
+
// push writes are saved to next checkpoint
|
|
619
|
+
const [channelWrites, pushWrites] = [
|
|
620
|
+
task.writes.filter((w) => w[0] !== PUSH),
|
|
621
|
+
task.writes.filter((w) => w[0] === PUSH),
|
|
622
|
+
];
|
|
623
|
+
// save task writes
|
|
624
|
+
if (saved !== undefined && channelWrites.length > 0) {
|
|
625
|
+
await checkpointer.putWrites(checkpointConfig, channelWrites, task.id);
|
|
569
626
|
}
|
|
570
627
|
// apply to checkpoint
|
|
571
628
|
// TODO: Why does keyof StrRecord allow number and symbol?
|
|
@@ -577,6 +634,9 @@ export class Pregel extends Runnable {
|
|
|
577
634
|
writes: { [asNode]: values },
|
|
578
635
|
parents: saved?.metadata?.parents ?? {},
|
|
579
636
|
}, newVersions);
|
|
637
|
+
if (pushWrites.length > 0) {
|
|
638
|
+
await checkpointer.putWrites(nextConfig, pushWrites, task.id);
|
|
639
|
+
}
|
|
580
640
|
return patchCheckpointMap(nextConfig, saved ? saved.metadata : undefined);
|
|
581
641
|
}
|
|
582
642
|
_defaults(config) {
|
package/dist/pregel/io.cjs
CHANGED
|
@@ -46,7 +46,34 @@ function readChannels(channels, select, skipEmpty = true
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
exports.readChannels = readChannels;
|
|
49
|
+
/**
|
|
50
|
+
* Map input chunk to a sequence of pending writes in the form (channel, value).
|
|
51
|
+
*/
|
|
49
52
|
function* mapCommand(cmd) {
|
|
53
|
+
if (cmd.graph === constants_js_1.Command.PARENT) {
|
|
54
|
+
throw new errors_js_1.InvalidUpdateError("There is no parent graph.");
|
|
55
|
+
}
|
|
56
|
+
if (cmd.goto) {
|
|
57
|
+
let sends;
|
|
58
|
+
if (Array.isArray(cmd.goto)) {
|
|
59
|
+
sends = cmd.goto;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
sends = [cmd.goto];
|
|
63
|
+
}
|
|
64
|
+
for (const send of sends) {
|
|
65
|
+
if ((0, constants_js_1._isSend)(send)) {
|
|
66
|
+
yield [constants_js_1.NULL_TASK_ID, constants_js_1.TASKS, send];
|
|
67
|
+
}
|
|
68
|
+
else if (typeof send === "string") {
|
|
69
|
+
yield [constants_js_1.NULL_TASK_ID, `branch:__start__:${constants_js_1.SELF}:${send}`, "__start__"];
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
throw new Error(`In Command.send, expected Send or string, got ${typeof send}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// TODO: handle goto str for state graph
|
|
76
|
+
}
|
|
50
77
|
if (cmd.resume) {
|
|
51
78
|
if (typeof cmd.resume === "object" &&
|
|
52
79
|
!!cmd.resume &&
|
|
@@ -60,6 +87,14 @@ function* mapCommand(cmd) {
|
|
|
60
87
|
yield [constants_js_1.NULL_TASK_ID, constants_js_1.RESUME, cmd.resume];
|
|
61
88
|
}
|
|
62
89
|
}
|
|
90
|
+
if (cmd.update) {
|
|
91
|
+
if (typeof cmd.update !== "object" || !cmd.update) {
|
|
92
|
+
throw new Error("Expected cmd.update to be a dict mapping channel names to update values");
|
|
93
|
+
}
|
|
94
|
+
for (const [k, v] of Object.entries(cmd.update)) {
|
|
95
|
+
yield [constants_js_1.NULL_TASK_ID, k, v];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
63
98
|
}
|
|
64
99
|
exports.mapCommand = mapCommand;
|
|
65
100
|
/**
|
package/dist/pregel/io.d.ts
CHANGED
|
@@ -4,6 +4,9 @@ import type { PregelExecutableTask } from "./types.js";
|
|
|
4
4
|
import { Command } from "../constants.js";
|
|
5
5
|
export declare function readChannel<C extends PropertyKey>(channels: Record<C, BaseChannel>, chan: C, catchErrors?: boolean, returnException?: boolean): unknown | null;
|
|
6
6
|
export declare function readChannels<C extends PropertyKey>(channels: Record<C, BaseChannel>, select: C | Array<C>, skipEmpty?: boolean): Record<string, any> | any;
|
|
7
|
+
/**
|
|
8
|
+
* Map input chunk to a sequence of pending writes in the form (channel, value).
|
|
9
|
+
*/
|
|
7
10
|
export declare function mapCommand(cmd: Command): Generator<[string, string, unknown]>;
|
|
8
11
|
/**
|
|
9
12
|
* Map input chunk to a sequence of pending writes in the form [channel, value].
|
package/dist/pregel/io.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { validate } from "uuid";
|
|
2
|
-
import { NULL_TASK_ID, RESUME, TAG_HIDDEN } from "../constants.js";
|
|
3
|
-
import { EmptyChannelError } from "../errors.js";
|
|
2
|
+
import { _isSend, Command, NULL_TASK_ID, RESUME, SELF, TAG_HIDDEN, TASKS, } from "../constants.js";
|
|
3
|
+
import { EmptyChannelError, InvalidUpdateError } from "../errors.js";
|
|
4
4
|
export function readChannel(channels, chan, catchErrors = true, returnException = false) {
|
|
5
5
|
try {
|
|
6
6
|
return channels[chan].get();
|
|
@@ -41,7 +41,34 @@ export function readChannels(channels, select, skipEmpty = true
|
|
|
41
41
|
return readChannel(channels, select);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Map input chunk to a sequence of pending writes in the form (channel, value).
|
|
46
|
+
*/
|
|
44
47
|
export function* mapCommand(cmd) {
|
|
48
|
+
if (cmd.graph === Command.PARENT) {
|
|
49
|
+
throw new InvalidUpdateError("There is no parent graph.");
|
|
50
|
+
}
|
|
51
|
+
if (cmd.goto) {
|
|
52
|
+
let sends;
|
|
53
|
+
if (Array.isArray(cmd.goto)) {
|
|
54
|
+
sends = cmd.goto;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
sends = [cmd.goto];
|
|
58
|
+
}
|
|
59
|
+
for (const send of sends) {
|
|
60
|
+
if (_isSend(send)) {
|
|
61
|
+
yield [NULL_TASK_ID, TASKS, send];
|
|
62
|
+
}
|
|
63
|
+
else if (typeof send === "string") {
|
|
64
|
+
yield [NULL_TASK_ID, `branch:__start__:${SELF}:${send}`, "__start__"];
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
throw new Error(`In Command.send, expected Send or string, got ${typeof send}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// TODO: handle goto str for state graph
|
|
71
|
+
}
|
|
45
72
|
if (cmd.resume) {
|
|
46
73
|
if (typeof cmd.resume === "object" &&
|
|
47
74
|
!!cmd.resume &&
|
|
@@ -55,6 +82,14 @@ export function* mapCommand(cmd) {
|
|
|
55
82
|
yield [NULL_TASK_ID, RESUME, cmd.resume];
|
|
56
83
|
}
|
|
57
84
|
}
|
|
85
|
+
if (cmd.update) {
|
|
86
|
+
if (typeof cmd.update !== "object" || !cmd.update) {
|
|
87
|
+
throw new Error("Expected cmd.update to be a dict mapping channel names to update values");
|
|
88
|
+
}
|
|
89
|
+
for (const [k, v] of Object.entries(cmd.update)) {
|
|
90
|
+
yield [NULL_TASK_ID, k, v];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
58
93
|
}
|
|
59
94
|
/**
|
|
60
95
|
* Map input chunk to a sequence of pending writes in the form [channel, value].
|
package/dist/pregel/loop.cjs
CHANGED
|
@@ -576,22 +576,9 @@ class PregelLoop {
|
|
|
576
576
|
async _first(inputKeys) {
|
|
577
577
|
const isResuming = Object.keys(this.checkpoint.channel_versions).length !== 0 &&
|
|
578
578
|
(this.config.configurable?.[constants_js_1.CONFIG_KEY_RESUMING] !== undefined ||
|
|
579
|
-
this.input === null
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
if (this.checkpoint.channel_versions[channelName] !== undefined) {
|
|
583
|
-
const version = this.checkpoint.channel_versions[channelName];
|
|
584
|
-
this.checkpoint.versions_seen[constants_js_1.INTERRUPT] = {
|
|
585
|
-
...this.checkpoint.versions_seen[constants_js_1.INTERRUPT],
|
|
586
|
-
[channelName]: version,
|
|
587
|
-
};
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
// produce values output
|
|
591
|
-
const valuesOutput = await (0, utils_js_1.gatherIterator)((0, utils_js_1.prefixGenerator)((0, io_js_1.mapOutputValues)(this.outputKeys, true, this.channels), "values"));
|
|
592
|
-
this._emit(valuesOutput);
|
|
593
|
-
}
|
|
594
|
-
else if ((0, constants_js_1._isCommand)(this.input)) {
|
|
579
|
+
this.input === null ||
|
|
580
|
+
(0, constants_js_1._isCommand)(this.input));
|
|
581
|
+
if ((0, constants_js_1._isCommand)(this.input)) {
|
|
595
582
|
const writes = {};
|
|
596
583
|
// group writes by task id
|
|
597
584
|
for (const [tid, key, value] of (0, io_js_1.mapCommand)(this.input)) {
|
|
@@ -608,6 +595,20 @@ class PregelLoop {
|
|
|
608
595
|
this.putWrites(tid, ws);
|
|
609
596
|
}
|
|
610
597
|
}
|
|
598
|
+
if (isResuming) {
|
|
599
|
+
for (const channelName of Object.keys(this.channels)) {
|
|
600
|
+
if (this.checkpoint.channel_versions[channelName] !== undefined) {
|
|
601
|
+
const version = this.checkpoint.channel_versions[channelName];
|
|
602
|
+
this.checkpoint.versions_seen[constants_js_1.INTERRUPT] = {
|
|
603
|
+
...this.checkpoint.versions_seen[constants_js_1.INTERRUPT],
|
|
604
|
+
[channelName]: version,
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
// produce values output
|
|
609
|
+
const valuesOutput = await (0, utils_js_1.gatherIterator)((0, utils_js_1.prefixGenerator)((0, io_js_1.mapOutputValues)(this.outputKeys, true, this.channels), "values"));
|
|
610
|
+
this._emit(valuesOutput);
|
|
611
|
+
}
|
|
611
612
|
else {
|
|
612
613
|
// map inputs to channel updates
|
|
613
614
|
const inputWrites = await (0, utils_js_1.gatherIterator)((0, io_js_1.mapInput)(inputKeys, this.input));
|
package/dist/pregel/loop.js
CHANGED
|
@@ -572,22 +572,9 @@ export class PregelLoop {
|
|
|
572
572
|
async _first(inputKeys) {
|
|
573
573
|
const isResuming = Object.keys(this.checkpoint.channel_versions).length !== 0 &&
|
|
574
574
|
(this.config.configurable?.[CONFIG_KEY_RESUMING] !== undefined ||
|
|
575
|
-
this.input === null
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
if (this.checkpoint.channel_versions[channelName] !== undefined) {
|
|
579
|
-
const version = this.checkpoint.channel_versions[channelName];
|
|
580
|
-
this.checkpoint.versions_seen[INTERRUPT] = {
|
|
581
|
-
...this.checkpoint.versions_seen[INTERRUPT],
|
|
582
|
-
[channelName]: version,
|
|
583
|
-
};
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
// produce values output
|
|
587
|
-
const valuesOutput = await gatherIterator(prefixGenerator(mapOutputValues(this.outputKeys, true, this.channels), "values"));
|
|
588
|
-
this._emit(valuesOutput);
|
|
589
|
-
}
|
|
590
|
-
else if (_isCommand(this.input)) {
|
|
575
|
+
this.input === null ||
|
|
576
|
+
_isCommand(this.input));
|
|
577
|
+
if (_isCommand(this.input)) {
|
|
591
578
|
const writes = {};
|
|
592
579
|
// group writes by task id
|
|
593
580
|
for (const [tid, key, value] of mapCommand(this.input)) {
|
|
@@ -604,6 +591,20 @@ export class PregelLoop {
|
|
|
604
591
|
this.putWrites(tid, ws);
|
|
605
592
|
}
|
|
606
593
|
}
|
|
594
|
+
if (isResuming) {
|
|
595
|
+
for (const channelName of Object.keys(this.channels)) {
|
|
596
|
+
if (this.checkpoint.channel_versions[channelName] !== undefined) {
|
|
597
|
+
const version = this.checkpoint.channel_versions[channelName];
|
|
598
|
+
this.checkpoint.versions_seen[INTERRUPT] = {
|
|
599
|
+
...this.checkpoint.versions_seen[INTERRUPT],
|
|
600
|
+
[channelName]: version,
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
// produce values output
|
|
605
|
+
const valuesOutput = await gatherIterator(prefixGenerator(mapOutputValues(this.outputKeys, true, this.channels), "values"));
|
|
606
|
+
this._emit(valuesOutput);
|
|
607
|
+
}
|
|
607
608
|
else {
|
|
608
609
|
// map inputs to channel updates
|
|
609
610
|
const inputWrites = await gatherIterator(mapInput(inputKeys, this.input));
|