@langchain/langgraph 0.2.3 → 0.2.4
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/channels/base.cjs +16 -4
- package/dist/channels/base.d.ts +3 -0
- package/dist/channels/base.js +14 -3
- package/dist/constants.cjs +4 -1
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +3 -0
- package/dist/graph/annotation.cjs +5 -1
- package/dist/graph/annotation.d.ts +5 -4
- package/dist/graph/annotation.js +5 -1
- package/dist/graph/state.cjs +7 -6
- package/dist/graph/state.d.ts +5 -2
- package/dist/graph/state.js +6 -5
- package/dist/managed/base.cjs +163 -0
- package/dist/managed/base.d.ts +30 -0
- package/dist/managed/base.js +155 -0
- package/dist/managed/index.cjs +19 -0
- package/dist/managed/index.d.ts +3 -0
- package/dist/managed/index.js +3 -0
- package/dist/managed/is_last_step.cjs +11 -0
- package/dist/managed/is_last_step.d.ts +4 -0
- package/dist/managed/is_last_step.js +7 -0
- package/dist/managed/shared_value.cjs +109 -0
- package/dist/managed/shared_value.d.ts +23 -0
- package/dist/managed/shared_value.js +105 -0
- package/dist/pregel/algo.cjs +111 -50
- package/dist/pregel/algo.d.ts +7 -6
- package/dist/pregel/algo.js +112 -51
- package/dist/pregel/index.cjs +65 -6
- package/dist/pregel/index.d.ts +9 -2
- package/dist/pregel/index.js +67 -8
- package/dist/pregel/loop.cjs +40 -3
- package/dist/pregel/loop.d.ts +10 -0
- package/dist/pregel/loop.js +40 -3
- package/dist/pregel/types.d.ts +8 -2
- package/dist/pregel/validate.d.ts +3 -2
- package/dist/store/base.cjs +12 -0
- package/dist/store/base.d.ts +7 -0
- package/dist/store/base.js +8 -0
- package/dist/store/batch.cjs +126 -0
- package/dist/store/batch.d.ts +55 -0
- package/dist/store/batch.js +122 -0
- package/dist/store/index.cjs +19 -0
- package/dist/store/index.d.ts +3 -0
- package/dist/store/index.js +3 -0
- package/dist/store/memory.cjs +43 -0
- package/dist/store/memory.d.ts +6 -0
- package/dist/store/memory.js +39 -0
- package/dist/utils.cjs +26 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +24 -0
- package/dist/web.cjs +2 -0
- package/dist/web.d.ts +2 -0
- package/dist/web.js +2 -0
- package/package.json +1 -1
package/dist/pregel/index.cjs
CHANGED
|
@@ -16,6 +16,8 @@ const algo_js_1 = require("./algo.cjs");
|
|
|
16
16
|
const utils_js_1 = require("./utils.cjs");
|
|
17
17
|
const loop_js_1 = require("./loop.cjs");
|
|
18
18
|
const retry_js_1 = require("./retry.cjs");
|
|
19
|
+
const base_js_2 = require("../managed/base.cjs");
|
|
20
|
+
const utils_js_2 = require("../utils.cjs");
|
|
19
21
|
function isString(value) {
|
|
20
22
|
return typeof value === "string";
|
|
21
23
|
}
|
|
@@ -165,6 +167,12 @@ class Pregel extends runnables_1.Runnable {
|
|
|
165
167
|
writable: true,
|
|
166
168
|
value: void 0
|
|
167
169
|
});
|
|
170
|
+
Object.defineProperty(this, "store", {
|
|
171
|
+
enumerable: true,
|
|
172
|
+
configurable: true,
|
|
173
|
+
writable: true,
|
|
174
|
+
value: void 0
|
|
175
|
+
});
|
|
168
176
|
let { streamMode } = fields;
|
|
169
177
|
if (streamMode != null && !Array.isArray(streamMode)) {
|
|
170
178
|
streamMode = [streamMode];
|
|
@@ -182,6 +190,7 @@ class Pregel extends runnables_1.Runnable {
|
|
|
182
190
|
this.debug = fields.debug ?? this.debug;
|
|
183
191
|
this.checkpointer = fields.checkpointer;
|
|
184
192
|
this.retryPolicy = fields.retryPolicy;
|
|
193
|
+
this.store = fields.store;
|
|
185
194
|
if (this.autoValidate) {
|
|
186
195
|
this.validate();
|
|
187
196
|
}
|
|
@@ -227,7 +236,8 @@ class Pregel extends runnables_1.Runnable {
|
|
|
227
236
|
const saved = await this.checkpointer.getTuple(config);
|
|
228
237
|
const checkpoint = saved ? saved.checkpoint : (0, langgraph_checkpoint_1.emptyCheckpoint)();
|
|
229
238
|
const channels = (0, base_js_1.emptyChannels)(this.channels, checkpoint);
|
|
230
|
-
const
|
|
239
|
+
const { managed } = await this.prepareSpecs(config);
|
|
240
|
+
const nextTasks = (0, algo_js_1._prepareNextTasks)(checkpoint, this.nodes, channels, managed, saved !== undefined ? saved.config : config, false, { step: saved ? (saved.metadata?.step ?? -1) + 1 : -1 });
|
|
231
241
|
return {
|
|
232
242
|
values: (0, io_js_1.readChannels)(channels, this.streamChannelsAsIs),
|
|
233
243
|
next: nextTasks.map((task) => task.name),
|
|
@@ -245,9 +255,10 @@ class Pregel extends runnables_1.Runnable {
|
|
|
245
255
|
if (!this.checkpointer) {
|
|
246
256
|
throw new errors_js_1.GraphValueError("No checkpointer set");
|
|
247
257
|
}
|
|
258
|
+
const { managed } = await this.prepareSpecs(config);
|
|
248
259
|
for await (const saved of this.checkpointer.list(config, options)) {
|
|
249
260
|
const channels = (0, base_js_1.emptyChannels)(this.channels, saved.checkpoint);
|
|
250
|
-
const nextTasks = (0, algo_js_1._prepareNextTasks)(saved.checkpoint, this.nodes, channels, saved.config, false, { step: -1 });
|
|
261
|
+
const nextTasks = (0, algo_js_1._prepareNextTasks)(saved.checkpoint, this.nodes, channels, managed, saved.config, false, { step: -1 });
|
|
251
262
|
yield {
|
|
252
263
|
values: (0, io_js_1.readChannels)(channels, this.streamChannelsAsIs),
|
|
253
264
|
next: nextTasks.map((task) => task.name),
|
|
@@ -336,6 +347,7 @@ class Pregel extends runnables_1.Runnable {
|
|
|
336
347
|
}
|
|
337
348
|
// update channels
|
|
338
349
|
const channels = (0, base_js_1.emptyChannels)(this.channels, checkpoint);
|
|
350
|
+
const { managed } = await this.prepareSpecs(config);
|
|
339
351
|
// run all writers of the chosen node
|
|
340
352
|
const writers = this.nodes[asNode].getWriters();
|
|
341
353
|
if (!writers.length) {
|
|
@@ -356,9 +368,9 @@ class Pregel extends runnables_1.Runnable {
|
|
|
356
368
|
runName: config.runName ?? `${this.getName()}UpdateState`,
|
|
357
369
|
configurable: {
|
|
358
370
|
[constants_js_1.CONFIG_KEY_SEND]: (items) => task.writes.push(...items),
|
|
359
|
-
[constants_js_1.CONFIG_KEY_READ]: algo_js_1._localRead
|
|
371
|
+
[constants_js_1.CONFIG_KEY_READ]: (select_, fresh_ = false) => (0, algo_js_1._localRead)(step, checkpoint, channels, managed,
|
|
360
372
|
// TODO: Why does keyof StrRecord allow number and symbol?
|
|
361
|
-
task),
|
|
373
|
+
task, select_, fresh_),
|
|
362
374
|
},
|
|
363
375
|
}));
|
|
364
376
|
if (saved !== undefined) {
|
|
@@ -442,6 +454,43 @@ class Pregel extends runnables_1.Runnable {
|
|
|
442
454
|
async stream(input, options) {
|
|
443
455
|
return super.stream(input, options);
|
|
444
456
|
}
|
|
457
|
+
async prepareSpecs(config) {
|
|
458
|
+
const configForManaged = (0, utils_js_2.patchConfigurable)(config, {
|
|
459
|
+
[constants_js_1.CONFIG_KEY_STORE]: this.store,
|
|
460
|
+
});
|
|
461
|
+
const channelSpecs = {};
|
|
462
|
+
const managedSpecs = {};
|
|
463
|
+
for (const [name, spec] of Object.entries(this.channels)) {
|
|
464
|
+
if ((0, base_js_1.isBaseChannel)(spec)) {
|
|
465
|
+
channelSpecs[name] = spec;
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
managedSpecs[name] = spec;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
const managed = new base_js_2.ManagedValueMapping(await Object.entries(managedSpecs).reduce(async (accPromise, [key, value]) => {
|
|
472
|
+
const acc = await accPromise;
|
|
473
|
+
let initializedValue;
|
|
474
|
+
if ((0, base_js_2.isConfiguredManagedValue)(value)) {
|
|
475
|
+
if ("key" in value.params &&
|
|
476
|
+
value.params.key === base_js_2.ChannelKeyPlaceholder) {
|
|
477
|
+
value.params.key = key;
|
|
478
|
+
}
|
|
479
|
+
initializedValue = await value.cls.initialize(configForManaged, value.params);
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
initializedValue = await value.initialize(configForManaged);
|
|
483
|
+
}
|
|
484
|
+
if (initializedValue !== undefined) {
|
|
485
|
+
acc.push([key, initializedValue]);
|
|
486
|
+
}
|
|
487
|
+
return acc;
|
|
488
|
+
}, Promise.resolve([])));
|
|
489
|
+
return {
|
|
490
|
+
channelSpecs,
|
|
491
|
+
managed,
|
|
492
|
+
};
|
|
493
|
+
}
|
|
445
494
|
async *_streamIterator(input, options) {
|
|
446
495
|
const inputConfig = (0, runnables_1.ensureConfig)(options);
|
|
447
496
|
if (inputConfig.recursionLimit === undefined ||
|
|
@@ -457,6 +506,7 @@ class Pregel extends runnables_1.Runnable {
|
|
|
457
506
|
delete inputConfig.runId;
|
|
458
507
|
// assign defaults
|
|
459
508
|
const [debug, streamMode, , outputKeys, config, interruptBefore, interruptAfter, checkpointer,] = this._defaults(inputConfig);
|
|
509
|
+
const { channelSpecs, managed } = await this.prepareSpecs(config);
|
|
460
510
|
let loop;
|
|
461
511
|
try {
|
|
462
512
|
loop = await loop_js_1.PregelLoop.initialize({
|
|
@@ -464,9 +514,11 @@ class Pregel extends runnables_1.Runnable {
|
|
|
464
514
|
config,
|
|
465
515
|
checkpointer,
|
|
466
516
|
nodes: this.nodes,
|
|
467
|
-
channelSpecs
|
|
517
|
+
channelSpecs,
|
|
518
|
+
managed,
|
|
468
519
|
outputKeys,
|
|
469
520
|
streamKeys: this.streamChannelsAsIs,
|
|
521
|
+
store: this.store,
|
|
470
522
|
});
|
|
471
523
|
while (await loop.tick({
|
|
472
524
|
inputKeys: this.inputChannels,
|
|
@@ -568,7 +620,14 @@ class Pregel extends runnables_1.Runnable {
|
|
|
568
620
|
throw e;
|
|
569
621
|
}
|
|
570
622
|
finally {
|
|
571
|
-
|
|
623
|
+
// Call `.stop()` again incase it was not called in the loop, e.g due to an error.
|
|
624
|
+
if (loop) {
|
|
625
|
+
loop.store?.stop();
|
|
626
|
+
}
|
|
627
|
+
await Promise.all([
|
|
628
|
+
loop?.checkpointerPromises ?? [],
|
|
629
|
+
...Array.from(managed.values()).map((mv) => mv.promises()),
|
|
630
|
+
]);
|
|
572
631
|
}
|
|
573
632
|
}
|
|
574
633
|
/**
|
package/dist/pregel/index.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ import { ChannelWrite } from "./write.js";
|
|
|
7
7
|
import { PregelInterface, PregelParams, StateSnapshot, StreamMode } from "./types.js";
|
|
8
8
|
import { StrRecord } from "./algo.js";
|
|
9
9
|
import { RetryPolicy } from "./utils.js";
|
|
10
|
+
import { BaseStore } from "../store/base.js";
|
|
11
|
+
import { ManagedValueMapping, type ManagedValueSpec } from "../managed/base.js";
|
|
10
12
|
type WriteValue = Runnable | RunnableFunc<unknown, unknown> | unknown;
|
|
11
13
|
export declare class Channel {
|
|
12
14
|
static subscribeTo(channels: string, options?: {
|
|
@@ -21,7 +23,7 @@ export declare class Channel {
|
|
|
21
23
|
/**
|
|
22
24
|
* Config for executing the graph.
|
|
23
25
|
*/
|
|
24
|
-
export interface PregelOptions<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel>> extends RunnableConfig {
|
|
26
|
+
export interface PregelOptions<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel | ManagedValueSpec>> extends RunnableConfig {
|
|
25
27
|
/** The stream mode for the graph run. Default is ["values"]. */
|
|
26
28
|
streamMode?: StreamMode | StreamMode[];
|
|
27
29
|
inputKeys?: keyof Cc | Array<keyof Cc>;
|
|
@@ -36,7 +38,7 @@ export interface PregelOptions<Nn extends StrRecord<string, PregelNode>, Cc exte
|
|
|
36
38
|
}
|
|
37
39
|
export type PregelInputType = any;
|
|
38
40
|
export type PregelOutputType = any;
|
|
39
|
-
export declare class Pregel<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel>> extends Runnable<PregelInputType, PregelOutputType, PregelOptions<Nn, Cc>> implements PregelInterface<Nn, Cc> {
|
|
41
|
+
export declare class Pregel<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel | ManagedValueSpec>> extends Runnable<PregelInputType, PregelOutputType, PregelOptions<Nn, Cc>> implements PregelInterface<Nn, Cc> {
|
|
40
42
|
static lc_name(): string;
|
|
41
43
|
lc_namespace: string[];
|
|
42
44
|
nodes: Nn;
|
|
@@ -52,6 +54,7 @@ export declare class Pregel<Nn extends StrRecord<string, PregelNode>, Cc extends
|
|
|
52
54
|
debug: boolean;
|
|
53
55
|
checkpointer?: BaseCheckpointSaver;
|
|
54
56
|
retryPolicy?: RetryPolicy;
|
|
57
|
+
store?: BaseStore;
|
|
55
58
|
constructor(fields: PregelParams<Nn, Cc>);
|
|
56
59
|
validate(): this;
|
|
57
60
|
get streamChannelsList(): Array<keyof Cc>;
|
|
@@ -101,6 +104,10 @@ export declare class Pregel<Nn extends StrRecord<string, PregelNode>, Cc extends
|
|
|
101
104
|
* @param options.debug Whether to print debug information during execution.
|
|
102
105
|
*/
|
|
103
106
|
stream(input: PregelInputType, options?: Partial<PregelOptions<Nn, Cc>>): Promise<IterableReadableStream<PregelOutputType>>;
|
|
107
|
+
protected prepareSpecs(config: RunnableConfig): Promise<{
|
|
108
|
+
channelSpecs: Record<string, BaseChannel<unknown, unknown, unknown>>;
|
|
109
|
+
managed: ManagedValueMapping;
|
|
110
|
+
}>;
|
|
104
111
|
_streamIterator(input: PregelInputType, options?: Partial<PregelOptions<Nn, Cc>>): AsyncGenerator<PregelOutputType>;
|
|
105
112
|
/**
|
|
106
113
|
* Run the graph with a single input and config.
|
package/dist/pregel/index.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
/* eslint-disable no-param-reassign */
|
|
2
2
|
import { Runnable, RunnableSequence, _coerceToRunnable, ensureConfig, getCallbackManagerForConfig, patchConfig, } from "@langchain/core/runnables";
|
|
3
3
|
import { compareChannelVersions, copyCheckpoint, emptyCheckpoint, uuid5, } from "@langchain/langgraph-checkpoint";
|
|
4
|
-
import { createCheckpoint, emptyChannels, } from "../channels/base.js";
|
|
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, } from "../constants.js";
|
|
10
|
+
import { CONFIG_KEY_CHECKPOINTER, CONFIG_KEY_READ, CONFIG_KEY_SEND, CONFIG_KEY_STORE, ERROR, INTERRUPT, } from "../constants.js";
|
|
11
11
|
import { GraphRecursionError, GraphValueError, InvalidUpdateError, isGraphInterrupt, } from "../errors.js";
|
|
12
12
|
import { _prepareNextTasks, _localRead, _applyWrites, } from "./algo.js";
|
|
13
13
|
import { _coerceToDict, getNewChannelVersions } from "./utils.js";
|
|
14
14
|
import { PregelLoop } from "./loop.js";
|
|
15
15
|
import { executeTasksWithRetry } from "./retry.js";
|
|
16
|
+
import { ChannelKeyPlaceholder, isConfiguredManagedValue, ManagedValueMapping, } from "../managed/base.js";
|
|
17
|
+
import { patchConfigurable } from "../utils.js";
|
|
16
18
|
function isString(value) {
|
|
17
19
|
return typeof value === "string";
|
|
18
20
|
}
|
|
@@ -161,6 +163,12 @@ export class Pregel extends Runnable {
|
|
|
161
163
|
writable: true,
|
|
162
164
|
value: void 0
|
|
163
165
|
});
|
|
166
|
+
Object.defineProperty(this, "store", {
|
|
167
|
+
enumerable: true,
|
|
168
|
+
configurable: true,
|
|
169
|
+
writable: true,
|
|
170
|
+
value: void 0
|
|
171
|
+
});
|
|
164
172
|
let { streamMode } = fields;
|
|
165
173
|
if (streamMode != null && !Array.isArray(streamMode)) {
|
|
166
174
|
streamMode = [streamMode];
|
|
@@ -178,6 +186,7 @@ export class Pregel extends Runnable {
|
|
|
178
186
|
this.debug = fields.debug ?? this.debug;
|
|
179
187
|
this.checkpointer = fields.checkpointer;
|
|
180
188
|
this.retryPolicy = fields.retryPolicy;
|
|
189
|
+
this.store = fields.store;
|
|
181
190
|
if (this.autoValidate) {
|
|
182
191
|
this.validate();
|
|
183
192
|
}
|
|
@@ -223,7 +232,8 @@ export class Pregel extends Runnable {
|
|
|
223
232
|
const saved = await this.checkpointer.getTuple(config);
|
|
224
233
|
const checkpoint = saved ? saved.checkpoint : emptyCheckpoint();
|
|
225
234
|
const channels = emptyChannels(this.channels, checkpoint);
|
|
226
|
-
const
|
|
235
|
+
const { managed } = await this.prepareSpecs(config);
|
|
236
|
+
const nextTasks = _prepareNextTasks(checkpoint, this.nodes, channels, managed, saved !== undefined ? saved.config : config, false, { step: saved ? (saved.metadata?.step ?? -1) + 1 : -1 });
|
|
227
237
|
return {
|
|
228
238
|
values: readChannels(channels, this.streamChannelsAsIs),
|
|
229
239
|
next: nextTasks.map((task) => task.name),
|
|
@@ -241,9 +251,10 @@ export class Pregel extends Runnable {
|
|
|
241
251
|
if (!this.checkpointer) {
|
|
242
252
|
throw new GraphValueError("No checkpointer set");
|
|
243
253
|
}
|
|
254
|
+
const { managed } = await this.prepareSpecs(config);
|
|
244
255
|
for await (const saved of this.checkpointer.list(config, options)) {
|
|
245
256
|
const channels = emptyChannels(this.channels, saved.checkpoint);
|
|
246
|
-
const nextTasks = _prepareNextTasks(saved.checkpoint, this.nodes, channels, saved.config, false, { step: -1 });
|
|
257
|
+
const nextTasks = _prepareNextTasks(saved.checkpoint, this.nodes, channels, managed, saved.config, false, { step: -1 });
|
|
247
258
|
yield {
|
|
248
259
|
values: readChannels(channels, this.streamChannelsAsIs),
|
|
249
260
|
next: nextTasks.map((task) => task.name),
|
|
@@ -332,6 +343,7 @@ export class Pregel extends Runnable {
|
|
|
332
343
|
}
|
|
333
344
|
// update channels
|
|
334
345
|
const channels = emptyChannels(this.channels, checkpoint);
|
|
346
|
+
const { managed } = await this.prepareSpecs(config);
|
|
335
347
|
// run all writers of the chosen node
|
|
336
348
|
const writers = this.nodes[asNode].getWriters();
|
|
337
349
|
if (!writers.length) {
|
|
@@ -352,9 +364,9 @@ export class Pregel extends Runnable {
|
|
|
352
364
|
runName: config.runName ?? `${this.getName()}UpdateState`,
|
|
353
365
|
configurable: {
|
|
354
366
|
[CONFIG_KEY_SEND]: (items) => task.writes.push(...items),
|
|
355
|
-
[CONFIG_KEY_READ]: _localRead
|
|
367
|
+
[CONFIG_KEY_READ]: (select_, fresh_ = false) => _localRead(step, checkpoint, channels, managed,
|
|
356
368
|
// TODO: Why does keyof StrRecord allow number and symbol?
|
|
357
|
-
task),
|
|
369
|
+
task, select_, fresh_),
|
|
358
370
|
},
|
|
359
371
|
}));
|
|
360
372
|
if (saved !== undefined) {
|
|
@@ -438,6 +450,43 @@ export class Pregel extends Runnable {
|
|
|
438
450
|
async stream(input, options) {
|
|
439
451
|
return super.stream(input, options);
|
|
440
452
|
}
|
|
453
|
+
async prepareSpecs(config) {
|
|
454
|
+
const configForManaged = patchConfigurable(config, {
|
|
455
|
+
[CONFIG_KEY_STORE]: this.store,
|
|
456
|
+
});
|
|
457
|
+
const channelSpecs = {};
|
|
458
|
+
const managedSpecs = {};
|
|
459
|
+
for (const [name, spec] of Object.entries(this.channels)) {
|
|
460
|
+
if (isBaseChannel(spec)) {
|
|
461
|
+
channelSpecs[name] = spec;
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
managedSpecs[name] = spec;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
const managed = new ManagedValueMapping(await Object.entries(managedSpecs).reduce(async (accPromise, [key, value]) => {
|
|
468
|
+
const acc = await accPromise;
|
|
469
|
+
let initializedValue;
|
|
470
|
+
if (isConfiguredManagedValue(value)) {
|
|
471
|
+
if ("key" in value.params &&
|
|
472
|
+
value.params.key === ChannelKeyPlaceholder) {
|
|
473
|
+
value.params.key = key;
|
|
474
|
+
}
|
|
475
|
+
initializedValue = await value.cls.initialize(configForManaged, value.params);
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
initializedValue = await value.initialize(configForManaged);
|
|
479
|
+
}
|
|
480
|
+
if (initializedValue !== undefined) {
|
|
481
|
+
acc.push([key, initializedValue]);
|
|
482
|
+
}
|
|
483
|
+
return acc;
|
|
484
|
+
}, Promise.resolve([])));
|
|
485
|
+
return {
|
|
486
|
+
channelSpecs,
|
|
487
|
+
managed,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
441
490
|
async *_streamIterator(input, options) {
|
|
442
491
|
const inputConfig = ensureConfig(options);
|
|
443
492
|
if (inputConfig.recursionLimit === undefined ||
|
|
@@ -453,6 +502,7 @@ export class Pregel extends Runnable {
|
|
|
453
502
|
delete inputConfig.runId;
|
|
454
503
|
// assign defaults
|
|
455
504
|
const [debug, streamMode, , outputKeys, config, interruptBefore, interruptAfter, checkpointer,] = this._defaults(inputConfig);
|
|
505
|
+
const { channelSpecs, managed } = await this.prepareSpecs(config);
|
|
456
506
|
let loop;
|
|
457
507
|
try {
|
|
458
508
|
loop = await PregelLoop.initialize({
|
|
@@ -460,9 +510,11 @@ export class Pregel extends Runnable {
|
|
|
460
510
|
config,
|
|
461
511
|
checkpointer,
|
|
462
512
|
nodes: this.nodes,
|
|
463
|
-
channelSpecs
|
|
513
|
+
channelSpecs,
|
|
514
|
+
managed,
|
|
464
515
|
outputKeys,
|
|
465
516
|
streamKeys: this.streamChannelsAsIs,
|
|
517
|
+
store: this.store,
|
|
466
518
|
});
|
|
467
519
|
while (await loop.tick({
|
|
468
520
|
inputKeys: this.inputChannels,
|
|
@@ -564,7 +616,14 @@ export class Pregel extends Runnable {
|
|
|
564
616
|
throw e;
|
|
565
617
|
}
|
|
566
618
|
finally {
|
|
567
|
-
|
|
619
|
+
// Call `.stop()` again incase it was not called in the loop, e.g due to an error.
|
|
620
|
+
if (loop) {
|
|
621
|
+
loop.store?.stop();
|
|
622
|
+
}
|
|
623
|
+
await Promise.all([
|
|
624
|
+
loop?.checkpointerPromises ?? [],
|
|
625
|
+
...Array.from(managed.values()).map((mv) => mv.promises()),
|
|
626
|
+
]);
|
|
568
627
|
}
|
|
569
628
|
}
|
|
570
629
|
/**
|
package/dist/pregel/loop.cjs
CHANGED
|
@@ -14,6 +14,7 @@ const io_js_1 = require("./io.cjs");
|
|
|
14
14
|
const errors_js_1 = require("../errors.cjs");
|
|
15
15
|
const utils_js_2 = require("./utils.cjs");
|
|
16
16
|
const debug_js_1 = require("./debug.cjs");
|
|
17
|
+
const batch_js_1 = require("../store/batch.cjs");
|
|
17
18
|
const INPUT_DONE = Symbol.for("INPUT_DONE");
|
|
18
19
|
const INPUT_RESUMING = Symbol.for("INPUT_RESUMING");
|
|
19
20
|
const DEFAULT_LOOP_LIMIT = 25;
|
|
@@ -50,6 +51,12 @@ class PregelLoop {
|
|
|
50
51
|
writable: true,
|
|
51
52
|
value: void 0
|
|
52
53
|
});
|
|
54
|
+
Object.defineProperty(this, "managed", {
|
|
55
|
+
enumerable: true,
|
|
56
|
+
configurable: true,
|
|
57
|
+
writable: true,
|
|
58
|
+
value: void 0
|
|
59
|
+
});
|
|
53
60
|
Object.defineProperty(this, "checkpoint", {
|
|
54
61
|
enumerable: true,
|
|
55
62
|
configurable: true,
|
|
@@ -154,6 +161,12 @@ class PregelLoop {
|
|
|
154
161
|
writable: true,
|
|
155
162
|
value: Promise.resolve()
|
|
156
163
|
});
|
|
164
|
+
Object.defineProperty(this, "store", {
|
|
165
|
+
enumerable: true,
|
|
166
|
+
configurable: true,
|
|
167
|
+
writable: true,
|
|
168
|
+
value: void 0
|
|
169
|
+
});
|
|
157
170
|
this.input = params.input;
|
|
158
171
|
this.config = params.config;
|
|
159
172
|
this.checkpointer = params.checkpointer;
|
|
@@ -170,6 +183,7 @@ class PregelLoop {
|
|
|
170
183
|
this.checkpointMetadata = params.checkpointMetadata;
|
|
171
184
|
this.checkpointPreviousVersions = params.checkpointPreviousVersions;
|
|
172
185
|
this.channels = params.channels;
|
|
186
|
+
this.managed = params.managed;
|
|
173
187
|
this.checkpointPendingWrites = params.checkpointPendingWrites;
|
|
174
188
|
this.step = params.step;
|
|
175
189
|
this.stop = params.stop;
|
|
@@ -178,6 +192,7 @@ class PregelLoop {
|
|
|
178
192
|
this.streamKeys = params.streamKeys;
|
|
179
193
|
this.nodes = params.nodes;
|
|
180
194
|
this.skipDoneTasks = this.config.configurable?.checkpoint_id === undefined;
|
|
195
|
+
this.store = params.store;
|
|
181
196
|
}
|
|
182
197
|
static async initialize(params) {
|
|
183
198
|
const saved = (await params.checkpointer?.getTuple(params.config)) ?? {
|
|
@@ -205,6 +220,13 @@ class PregelLoop {
|
|
|
205
220
|
const step = (checkpointMetadata.step ?? 0) + 1;
|
|
206
221
|
const stop = step + (params.config.recursionLimit ?? DEFAULT_LOOP_LIMIT) + 1;
|
|
207
222
|
const checkpointPreviousVersions = { ...checkpoint.channel_versions };
|
|
223
|
+
const store = params.store
|
|
224
|
+
? new batch_js_1.AsyncBatchedStore(params.store)
|
|
225
|
+
: undefined;
|
|
226
|
+
if (store) {
|
|
227
|
+
// Start the store. This is a batch store, so it will run continuously
|
|
228
|
+
store.start();
|
|
229
|
+
}
|
|
208
230
|
return new PregelLoop({
|
|
209
231
|
input: params.input,
|
|
210
232
|
config: params.config,
|
|
@@ -213,6 +235,7 @@ class PregelLoop {
|
|
|
213
235
|
checkpointMetadata,
|
|
214
236
|
checkpointConfig,
|
|
215
237
|
channels,
|
|
238
|
+
managed: params.managed,
|
|
216
239
|
step,
|
|
217
240
|
stop,
|
|
218
241
|
checkpointPreviousVersions,
|
|
@@ -220,6 +243,7 @@ class PregelLoop {
|
|
|
220
243
|
outputKeys: params.outputKeys ?? [],
|
|
221
244
|
streamKeys: params.streamKeys ?? [],
|
|
222
245
|
nodes: params.nodes,
|
|
246
|
+
store,
|
|
223
247
|
});
|
|
224
248
|
}
|
|
225
249
|
_checkpointerPutAfterPrevious(input) {
|
|
@@ -228,6 +252,13 @@ class PregelLoop {
|
|
|
228
252
|
});
|
|
229
253
|
this.checkpointerPromises.push(this._checkpointerChainedPromise);
|
|
230
254
|
}
|
|
255
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
256
|
+
async updateManagedValues(key, values) {
|
|
257
|
+
const mv = this.managed.get(key);
|
|
258
|
+
if (mv && "update" in mv && typeof mv.update === "function") {
|
|
259
|
+
await mv.update(values);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
231
262
|
/**
|
|
232
263
|
* Put writes for a task, to be read by the next tick.
|
|
233
264
|
* @param taskId
|
|
@@ -261,6 +292,9 @@ class PregelLoop {
|
|
|
261
292
|
* @param params
|
|
262
293
|
*/
|
|
263
294
|
async tick(params) {
|
|
295
|
+
if (this.store && !this.store.isRunning) {
|
|
296
|
+
this.store?.start();
|
|
297
|
+
}
|
|
264
298
|
const { inputKeys = [], interruptAfter = [], interruptBefore = [], manager, } = params;
|
|
265
299
|
if (this.status !== "pending") {
|
|
266
300
|
throw new Error(`Cannot tick when status is no longer "pending". Current status: "${this.status}"`);
|
|
@@ -271,7 +305,10 @@ class PregelLoop {
|
|
|
271
305
|
else if (this.tasks.every((task) => task.writes.length > 0)) {
|
|
272
306
|
const writes = this.tasks.flatMap((t) => t.writes);
|
|
273
307
|
// All tasks have finished
|
|
274
|
-
(0, algo_js_1._applyWrites)(this.checkpoint, this.channels, this.tasks, this.checkpointerGetNextVersion);
|
|
308
|
+
const myWrites = (0, algo_js_1._applyWrites)(this.checkpoint, this.channels, this.tasks, this.checkpointerGetNextVersion);
|
|
309
|
+
for (const [key, values] of Object.entries(myWrites)) {
|
|
310
|
+
await this.updateManagedValues(key, values);
|
|
311
|
+
}
|
|
275
312
|
// produce values output
|
|
276
313
|
const valuesOutput = await (0, utils_js_1.gatherIterator)((0, utils_js_1.prefixGenerator)((0, io_js_1.mapOutputValues)(this.outputKeys, writes, this.channels), "values"));
|
|
277
314
|
this.stream.push(...valuesOutput);
|
|
@@ -299,7 +336,7 @@ class PregelLoop {
|
|
|
299
336
|
this.status = "out_of_steps";
|
|
300
337
|
return false;
|
|
301
338
|
}
|
|
302
|
-
const nextTasks = (0, algo_js_1._prepareNextTasks)(this.checkpoint, this.nodes, this.channels, this.config, true, {
|
|
339
|
+
const nextTasks = (0, algo_js_1._prepareNextTasks)(this.checkpoint, this.nodes, this.channels, this.managed, this.config, true, {
|
|
303
340
|
step: this.step,
|
|
304
341
|
checkpointer: this.checkpointer,
|
|
305
342
|
isResuming: this.input === INPUT_RESUMING,
|
|
@@ -377,7 +414,7 @@ class PregelLoop {
|
|
|
377
414
|
if (inputWrites.length === 0) {
|
|
378
415
|
throw new errors_js_1.EmptyInputError(`Received no input writes for ${JSON.stringify(inputKeys, null, 2)}`);
|
|
379
416
|
}
|
|
380
|
-
const discardTasks = (0, algo_js_1._prepareNextTasks)(this.checkpoint, this.nodes, this.channels, this.config, true, { step: this.step });
|
|
417
|
+
const discardTasks = (0, algo_js_1._prepareNextTasks)(this.checkpoint, this.nodes, this.channels, this.managed, this.config, true, { step: this.step });
|
|
381
418
|
(0, algo_js_1._applyWrites)(this.checkpoint, this.channels, discardTasks.concat([
|
|
382
419
|
{
|
|
383
420
|
name: constants_js_1.INPUT,
|
package/dist/pregel/loop.d.ts
CHANGED
|
@@ -5,6 +5,9 @@ import { BaseCheckpointSaver, Checkpoint, PendingWrite, CheckpointPendingWrite,
|
|
|
5
5
|
import { BaseChannel } from "../channels/base.js";
|
|
6
6
|
import { PregelExecutableTask, StreamMode } from "./types.js";
|
|
7
7
|
import { PregelNode } from "./read.js";
|
|
8
|
+
import { BaseStore } from "../store/base.js";
|
|
9
|
+
import { AsyncBatchedStore } from "../store/batch.js";
|
|
10
|
+
import { ManagedValueMapping } from "../managed/base.js";
|
|
8
11
|
export type PregelLoopInitializeParams = {
|
|
9
12
|
input?: any;
|
|
10
13
|
config: RunnableConfig;
|
|
@@ -13,6 +16,8 @@ export type PregelLoopInitializeParams = {
|
|
|
13
16
|
streamKeys: string | string[];
|
|
14
17
|
nodes: Record<string, PregelNode>;
|
|
15
18
|
channelSpecs: Record<string, BaseChannel>;
|
|
19
|
+
managed: ManagedValueMapping;
|
|
20
|
+
store?: BaseStore;
|
|
16
21
|
};
|
|
17
22
|
type PregelLoopParams = {
|
|
18
23
|
input?: any;
|
|
@@ -24,11 +29,13 @@ type PregelLoopParams = {
|
|
|
24
29
|
checkpointPendingWrites: CheckpointPendingWrite[];
|
|
25
30
|
checkpointConfig: RunnableConfig;
|
|
26
31
|
channels: Record<string, BaseChannel>;
|
|
32
|
+
managed: ManagedValueMapping;
|
|
27
33
|
step: number;
|
|
28
34
|
stop: number;
|
|
29
35
|
outputKeys: string | string[];
|
|
30
36
|
streamKeys: string | string[];
|
|
31
37
|
nodes: Record<string, PregelNode>;
|
|
38
|
+
store?: AsyncBatchedStore;
|
|
32
39
|
};
|
|
33
40
|
export declare class PregelLoop {
|
|
34
41
|
protected input?: any;
|
|
@@ -36,6 +43,7 @@ export declare class PregelLoop {
|
|
|
36
43
|
protected checkpointer?: BaseCheckpointSaver;
|
|
37
44
|
protected checkpointerGetNextVersion: (current: number | undefined, channel: BaseChannel) => number;
|
|
38
45
|
channels: Record<string, BaseChannel>;
|
|
46
|
+
managed: ManagedValueMapping;
|
|
39
47
|
protected checkpoint: Checkpoint;
|
|
40
48
|
protected checkpointConfig: RunnableConfig;
|
|
41
49
|
checkpointMetadata: CheckpointMetadata;
|
|
@@ -53,6 +61,7 @@ export declare class PregelLoop {
|
|
|
53
61
|
checkpointerPromises: Promise<unknown>[];
|
|
54
62
|
protected isNested: boolean;
|
|
55
63
|
protected _checkpointerChainedPromise: Promise<unknown>;
|
|
64
|
+
store?: AsyncBatchedStore;
|
|
56
65
|
constructor(params: PregelLoopParams);
|
|
57
66
|
static initialize(params: PregelLoopInitializeParams): Promise<PregelLoop>;
|
|
58
67
|
protected _checkpointerPutAfterPrevious(input: {
|
|
@@ -61,6 +70,7 @@ export declare class PregelLoop {
|
|
|
61
70
|
metadata: CheckpointMetadata;
|
|
62
71
|
newVersions: Record<string, string | number>;
|
|
63
72
|
}): void;
|
|
73
|
+
protected updateManagedValues(key: string, values: any[]): Promise<void>;
|
|
64
74
|
/**
|
|
65
75
|
* Put writes for a task, to be read by the next tick.
|
|
66
76
|
* @param taskId
|
package/dist/pregel/loop.js
CHANGED
|
@@ -8,6 +8,7 @@ import { mapInput, mapOutputUpdates, mapOutputValues } from "./io.js";
|
|
|
8
8
|
import { EmptyInputError, GraphInterrupt } from "../errors.js";
|
|
9
9
|
import { getNewChannelVersions } from "./utils.js";
|
|
10
10
|
import { mapDebugTasks, mapDebugCheckpoint, mapDebugTaskResults, } from "./debug.js";
|
|
11
|
+
import { AsyncBatchedStore } from "../store/batch.js";
|
|
11
12
|
const INPUT_DONE = Symbol.for("INPUT_DONE");
|
|
12
13
|
const INPUT_RESUMING = Symbol.for("INPUT_RESUMING");
|
|
13
14
|
const DEFAULT_LOOP_LIMIT = 25;
|
|
@@ -44,6 +45,12 @@ export class PregelLoop {
|
|
|
44
45
|
writable: true,
|
|
45
46
|
value: void 0
|
|
46
47
|
});
|
|
48
|
+
Object.defineProperty(this, "managed", {
|
|
49
|
+
enumerable: true,
|
|
50
|
+
configurable: true,
|
|
51
|
+
writable: true,
|
|
52
|
+
value: void 0
|
|
53
|
+
});
|
|
47
54
|
Object.defineProperty(this, "checkpoint", {
|
|
48
55
|
enumerable: true,
|
|
49
56
|
configurable: true,
|
|
@@ -148,6 +155,12 @@ export class PregelLoop {
|
|
|
148
155
|
writable: true,
|
|
149
156
|
value: Promise.resolve()
|
|
150
157
|
});
|
|
158
|
+
Object.defineProperty(this, "store", {
|
|
159
|
+
enumerable: true,
|
|
160
|
+
configurable: true,
|
|
161
|
+
writable: true,
|
|
162
|
+
value: void 0
|
|
163
|
+
});
|
|
151
164
|
this.input = params.input;
|
|
152
165
|
this.config = params.config;
|
|
153
166
|
this.checkpointer = params.checkpointer;
|
|
@@ -164,6 +177,7 @@ export class PregelLoop {
|
|
|
164
177
|
this.checkpointMetadata = params.checkpointMetadata;
|
|
165
178
|
this.checkpointPreviousVersions = params.checkpointPreviousVersions;
|
|
166
179
|
this.channels = params.channels;
|
|
180
|
+
this.managed = params.managed;
|
|
167
181
|
this.checkpointPendingWrites = params.checkpointPendingWrites;
|
|
168
182
|
this.step = params.step;
|
|
169
183
|
this.stop = params.stop;
|
|
@@ -172,6 +186,7 @@ export class PregelLoop {
|
|
|
172
186
|
this.streamKeys = params.streamKeys;
|
|
173
187
|
this.nodes = params.nodes;
|
|
174
188
|
this.skipDoneTasks = this.config.configurable?.checkpoint_id === undefined;
|
|
189
|
+
this.store = params.store;
|
|
175
190
|
}
|
|
176
191
|
static async initialize(params) {
|
|
177
192
|
const saved = (await params.checkpointer?.getTuple(params.config)) ?? {
|
|
@@ -199,6 +214,13 @@ export class PregelLoop {
|
|
|
199
214
|
const step = (checkpointMetadata.step ?? 0) + 1;
|
|
200
215
|
const stop = step + (params.config.recursionLimit ?? DEFAULT_LOOP_LIMIT) + 1;
|
|
201
216
|
const checkpointPreviousVersions = { ...checkpoint.channel_versions };
|
|
217
|
+
const store = params.store
|
|
218
|
+
? new AsyncBatchedStore(params.store)
|
|
219
|
+
: undefined;
|
|
220
|
+
if (store) {
|
|
221
|
+
// Start the store. This is a batch store, so it will run continuously
|
|
222
|
+
store.start();
|
|
223
|
+
}
|
|
202
224
|
return new PregelLoop({
|
|
203
225
|
input: params.input,
|
|
204
226
|
config: params.config,
|
|
@@ -207,6 +229,7 @@ export class PregelLoop {
|
|
|
207
229
|
checkpointMetadata,
|
|
208
230
|
checkpointConfig,
|
|
209
231
|
channels,
|
|
232
|
+
managed: params.managed,
|
|
210
233
|
step,
|
|
211
234
|
stop,
|
|
212
235
|
checkpointPreviousVersions,
|
|
@@ -214,6 +237,7 @@ export class PregelLoop {
|
|
|
214
237
|
outputKeys: params.outputKeys ?? [],
|
|
215
238
|
streamKeys: params.streamKeys ?? [],
|
|
216
239
|
nodes: params.nodes,
|
|
240
|
+
store,
|
|
217
241
|
});
|
|
218
242
|
}
|
|
219
243
|
_checkpointerPutAfterPrevious(input) {
|
|
@@ -222,6 +246,13 @@ export class PregelLoop {
|
|
|
222
246
|
});
|
|
223
247
|
this.checkpointerPromises.push(this._checkpointerChainedPromise);
|
|
224
248
|
}
|
|
249
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
250
|
+
async updateManagedValues(key, values) {
|
|
251
|
+
const mv = this.managed.get(key);
|
|
252
|
+
if (mv && "update" in mv && typeof mv.update === "function") {
|
|
253
|
+
await mv.update(values);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
225
256
|
/**
|
|
226
257
|
* Put writes for a task, to be read by the next tick.
|
|
227
258
|
* @param taskId
|
|
@@ -255,6 +286,9 @@ export class PregelLoop {
|
|
|
255
286
|
* @param params
|
|
256
287
|
*/
|
|
257
288
|
async tick(params) {
|
|
289
|
+
if (this.store && !this.store.isRunning) {
|
|
290
|
+
this.store?.start();
|
|
291
|
+
}
|
|
258
292
|
const { inputKeys = [], interruptAfter = [], interruptBefore = [], manager, } = params;
|
|
259
293
|
if (this.status !== "pending") {
|
|
260
294
|
throw new Error(`Cannot tick when status is no longer "pending". Current status: "${this.status}"`);
|
|
@@ -265,7 +299,10 @@ export class PregelLoop {
|
|
|
265
299
|
else if (this.tasks.every((task) => task.writes.length > 0)) {
|
|
266
300
|
const writes = this.tasks.flatMap((t) => t.writes);
|
|
267
301
|
// All tasks have finished
|
|
268
|
-
_applyWrites(this.checkpoint, this.channels, this.tasks, this.checkpointerGetNextVersion);
|
|
302
|
+
const myWrites = _applyWrites(this.checkpoint, this.channels, this.tasks, this.checkpointerGetNextVersion);
|
|
303
|
+
for (const [key, values] of Object.entries(myWrites)) {
|
|
304
|
+
await this.updateManagedValues(key, values);
|
|
305
|
+
}
|
|
269
306
|
// produce values output
|
|
270
307
|
const valuesOutput = await gatherIterator(prefixGenerator(mapOutputValues(this.outputKeys, writes, this.channels), "values"));
|
|
271
308
|
this.stream.push(...valuesOutput);
|
|
@@ -293,7 +330,7 @@ export class PregelLoop {
|
|
|
293
330
|
this.status = "out_of_steps";
|
|
294
331
|
return false;
|
|
295
332
|
}
|
|
296
|
-
const nextTasks = _prepareNextTasks(this.checkpoint, this.nodes, this.channels, this.config, true, {
|
|
333
|
+
const nextTasks = _prepareNextTasks(this.checkpoint, this.nodes, this.channels, this.managed, this.config, true, {
|
|
297
334
|
step: this.step,
|
|
298
335
|
checkpointer: this.checkpointer,
|
|
299
336
|
isResuming: this.input === INPUT_RESUMING,
|
|
@@ -371,7 +408,7 @@ export class PregelLoop {
|
|
|
371
408
|
if (inputWrites.length === 0) {
|
|
372
409
|
throw new EmptyInputError(`Received no input writes for ${JSON.stringify(inputKeys, null, 2)}`);
|
|
373
410
|
}
|
|
374
|
-
const discardTasks = _prepareNextTasks(this.checkpoint, this.nodes, this.channels, this.config, true, { step: this.step });
|
|
411
|
+
const discardTasks = _prepareNextTasks(this.checkpoint, this.nodes, this.channels, this.managed, this.config, true, { step: this.step });
|
|
375
412
|
_applyWrites(this.checkpoint, this.channels, discardTasks.concat([
|
|
376
413
|
{
|
|
377
414
|
name: INPUT,
|