@langchain/langgraph 0.2.8 → 0.2.9
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 +12 -2
- package/dist/constants.d.ts +7 -1
- package/dist/constants.js +11 -1
- package/dist/errors.cjs +5 -4
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +5 -4
- package/dist/graph/graph.cjs +7 -2
- package/dist/graph/graph.js +8 -3
- package/dist/graph/message.cjs +2 -0
- package/dist/graph/message.js +2 -0
- package/dist/graph/state.cjs +8 -3
- package/dist/graph/state.d.ts +1 -1
- package/dist/graph/state.js +9 -4
- package/dist/managed/shared_value.cjs +1 -1
- package/dist/managed/shared_value.js +1 -1
- package/dist/pregel/algo.cjs +119 -54
- package/dist/pregel/algo.d.ts +5 -2
- package/dist/pregel/algo.js +117 -53
- package/dist/pregel/debug.cjs +15 -18
- package/dist/pregel/debug.d.ts +3 -2
- package/dist/pregel/debug.js +16 -19
- package/dist/pregel/index.cjs +295 -110
- package/dist/pregel/index.d.ts +16 -4
- package/dist/pregel/index.js +292 -110
- package/dist/pregel/io.cjs +15 -8
- package/dist/pregel/io.d.ts +2 -2
- package/dist/pregel/io.js +15 -8
- package/dist/pregel/loop.cjs +256 -111
- package/dist/pregel/loop.d.ts +21 -5
- package/dist/pregel/loop.js +256 -109
- package/dist/pregel/read.cjs +9 -2
- package/dist/pregel/read.d.ts +2 -1
- package/dist/pregel/read.js +9 -2
- package/dist/pregel/retry.d.ts +1 -1
- package/dist/pregel/types.d.ts +5 -1
- package/dist/pregel/utils/config.cjs +72 -0
- package/dist/pregel/utils/config.d.ts +2 -0
- package/dist/pregel/utils/config.js +68 -0
- package/dist/pregel/{utils.cjs → utils/index.cjs} +33 -10
- package/dist/pregel/{utils.d.ts → utils/index.d.ts} +4 -7
- package/dist/pregel/{utils.js → utils/index.js} +30 -8
- package/dist/utils.cjs +5 -3
- package/dist/utils.js +5 -3
- package/dist/web.d.ts +2 -1
- package/package.json +1 -1
package/dist/pregel/index.cjs
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.Pregel = exports.Channel = void 0;
|
|
4
7
|
/* eslint-disable no-param-reassign */
|
|
5
8
|
const runnables_1 = require("@langchain/core/runnables");
|
|
6
9
|
const langgraph_checkpoint_1 = require("@langchain/langgraph-checkpoint");
|
|
10
|
+
const double_ended_queue_1 = __importDefault(require("double-ended-queue"));
|
|
7
11
|
const base_js_1 = require("../channels/base.cjs");
|
|
8
12
|
const read_js_1 = require("./read.cjs");
|
|
9
13
|
const validate_js_1 = require("./validate.cjs");
|
|
@@ -13,11 +17,12 @@ const write_js_1 = require("./write.cjs");
|
|
|
13
17
|
const constants_js_1 = require("../constants.cjs");
|
|
14
18
|
const errors_js_1 = require("../errors.cjs");
|
|
15
19
|
const algo_js_1 = require("./algo.cjs");
|
|
16
|
-
const
|
|
20
|
+
const index_js_1 = require("./utils/index.cjs");
|
|
17
21
|
const loop_js_1 = require("./loop.cjs");
|
|
18
22
|
const retry_js_1 = require("./retry.cjs");
|
|
19
23
|
const base_js_2 = require("../managed/base.cjs");
|
|
20
|
-
const
|
|
24
|
+
const utils_js_1 = require("../utils.cjs");
|
|
25
|
+
const config_js_1 = require("./utils/config.cjs");
|
|
21
26
|
function isString(value) {
|
|
22
27
|
return typeof value === "string";
|
|
23
28
|
}
|
|
@@ -76,6 +81,16 @@ class Channel {
|
|
|
76
81
|
}
|
|
77
82
|
}
|
|
78
83
|
exports.Channel = Channel;
|
|
84
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
85
|
+
function isPregel(x) {
|
|
86
|
+
return ("inputChannels" in x &&
|
|
87
|
+
x.inputChannels !== undefined &&
|
|
88
|
+
"outputChannels" &&
|
|
89
|
+
x.outputChannels !== undefined);
|
|
90
|
+
}
|
|
91
|
+
function isRunnableSequence(x) {
|
|
92
|
+
return "steps" in x && Array.isArray(x.steps);
|
|
93
|
+
}
|
|
79
94
|
class Pregel extends runnables_1.Runnable {
|
|
80
95
|
static lc_name() {
|
|
81
96
|
return "LangGraph";
|
|
@@ -167,6 +182,12 @@ class Pregel extends runnables_1.Runnable {
|
|
|
167
182
|
writable: true,
|
|
168
183
|
value: void 0
|
|
169
184
|
});
|
|
185
|
+
Object.defineProperty(this, "config", {
|
|
186
|
+
enumerable: true,
|
|
187
|
+
configurable: true,
|
|
188
|
+
writable: true,
|
|
189
|
+
value: void 0
|
|
190
|
+
});
|
|
170
191
|
Object.defineProperty(this, "store", {
|
|
171
192
|
enumerable: true,
|
|
172
193
|
configurable: true,
|
|
@@ -190,11 +211,18 @@ class Pregel extends runnables_1.Runnable {
|
|
|
190
211
|
this.debug = fields.debug ?? this.debug;
|
|
191
212
|
this.checkpointer = fields.checkpointer;
|
|
192
213
|
this.retryPolicy = fields.retryPolicy;
|
|
214
|
+
this.config = fields.config;
|
|
193
215
|
this.store = fields.store;
|
|
194
216
|
if (this.autoValidate) {
|
|
195
217
|
this.validate();
|
|
196
218
|
}
|
|
197
219
|
}
|
|
220
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
221
|
+
// @ts-ignore Remove ignore when we remove support for 0.2 versions of core
|
|
222
|
+
withConfig(config) {
|
|
223
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
224
|
+
return new this.constructor({ ...this, config });
|
|
225
|
+
}
|
|
198
226
|
validate() {
|
|
199
227
|
(0, validate_js_1.validateGraph)({
|
|
200
228
|
nodes: this.nodes,
|
|
@@ -226,50 +254,181 @@ class Pregel extends runnables_1.Runnable {
|
|
|
226
254
|
return Object.keys(this.channels);
|
|
227
255
|
}
|
|
228
256
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
257
|
+
*getSubgraphs(namespace, recurse
|
|
258
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
259
|
+
) {
|
|
260
|
+
for (const [name, node] of Object.entries(this.nodes)) {
|
|
261
|
+
// filter by prefix
|
|
262
|
+
if (namespace !== undefined) {
|
|
263
|
+
if (!namespace.startsWith(name)) {
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
// find the subgraph if any
|
|
268
|
+
let graph;
|
|
269
|
+
const candidates = [node.bound];
|
|
270
|
+
for (const candidate of candidates) {
|
|
271
|
+
if (isPregel(candidate)) {
|
|
272
|
+
graph = candidate;
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
else if (isRunnableSequence(candidate)) {
|
|
276
|
+
candidates.push(...candidate.steps);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// if found, yield recursively
|
|
280
|
+
if (graph !== undefined) {
|
|
281
|
+
if (name === namespace) {
|
|
282
|
+
yield [name, graph];
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (namespace === undefined) {
|
|
286
|
+
yield [name, graph];
|
|
287
|
+
}
|
|
288
|
+
if (recurse) {
|
|
289
|
+
let newNamespace = namespace;
|
|
290
|
+
if (namespace !== undefined) {
|
|
291
|
+
newNamespace = namespace.slice(name.length + 1);
|
|
292
|
+
}
|
|
293
|
+
for (const [subgraphName, subgraph] of graph.getSubgraphs(newNamespace, recurse)) {
|
|
294
|
+
yield [
|
|
295
|
+
`${name}${constants_js_1.CHECKPOINT_NAMESPACE_SEPARATOR}${subgraphName}`,
|
|
296
|
+
subgraph,
|
|
297
|
+
];
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
async _prepareStateSnapshot({ config, saved, subgraphCheckpointer, }) {
|
|
304
|
+
if (saved === undefined) {
|
|
305
|
+
return {
|
|
306
|
+
values: {},
|
|
307
|
+
next: [],
|
|
308
|
+
config,
|
|
309
|
+
tasks: [],
|
|
310
|
+
};
|
|
235
311
|
}
|
|
236
|
-
const saved = await this.checkpointer.getTuple(config);
|
|
237
|
-
const checkpoint = saved ? saved.checkpoint : (0, langgraph_checkpoint_1.emptyCheckpoint)();
|
|
238
|
-
const channels = (0, base_js_1.emptyChannels)(this.channels, checkpoint);
|
|
239
312
|
// Pass `skipManaged: true` as managed values should not be returned in get state calls.
|
|
240
313
|
const { managed } = await this.prepareSpecs(config, { skipManaged: true });
|
|
241
|
-
const
|
|
314
|
+
const channels = (0, base_js_1.emptyChannels)(this.channels, saved.checkpoint);
|
|
315
|
+
const nextTasks = Object.values((0, algo_js_1._prepareNextTasks)(saved.checkpoint, this.nodes, channels, managed, saved.config, false, { step: (saved.metadata?.step ?? -1) + 1 }));
|
|
316
|
+
const subgraphs = await (0, utils_js_1.gatherIterator)(this.getSubgraphs());
|
|
317
|
+
const parentNamespace = saved.config.configurable?.checkpoint_ns ?? "";
|
|
318
|
+
const taskStates = {};
|
|
319
|
+
for (const task of nextTasks) {
|
|
320
|
+
const matchingSubgraph = subgraphs.find(([name]) => name === task.name);
|
|
321
|
+
if (!matchingSubgraph) {
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
// assemble checkpoint_ns for this task
|
|
325
|
+
let taskNs = `${task.name}${constants_js_1.CHECKPOINT_NAMESPACE_END}${task.id}`;
|
|
326
|
+
if (parentNamespace) {
|
|
327
|
+
taskNs = `${parentNamespace}${constants_js_1.CHECKPOINT_NAMESPACE_SEPARATOR}${taskNs}`;
|
|
328
|
+
}
|
|
329
|
+
if (subgraphCheckpointer === undefined) {
|
|
330
|
+
// set config as signal that subgraph checkpoints exist
|
|
331
|
+
const config = {
|
|
332
|
+
configurable: {
|
|
333
|
+
thread_id: saved.config.configurable?.thread_id,
|
|
334
|
+
checkpoint_ns: taskNs,
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
taskStates[task.id] = config;
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
// get the state of the subgraph
|
|
341
|
+
const config = {
|
|
342
|
+
configurable: {
|
|
343
|
+
[constants_js_1.CONFIG_KEY_CHECKPOINTER]: subgraphCheckpointer,
|
|
344
|
+
thread_id: saved.config.configurable?.thread_id,
|
|
345
|
+
checkpoint_ns: taskNs,
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
taskStates[task.id] = await matchingSubgraph[1].getState(config, {
|
|
349
|
+
subgraphs: true,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
// assemble the state snapshot
|
|
242
354
|
return {
|
|
243
355
|
values: (0, io_js_1.readChannels)(channels, this.streamChannelsAsIs),
|
|
244
356
|
next: nextTasks.map((task) => task.name),
|
|
245
|
-
tasks: (0, debug_js_1.tasksWithWrites)(nextTasks, saved?.pendingWrites ?? []),
|
|
246
|
-
metadata: saved
|
|
247
|
-
config:
|
|
248
|
-
createdAt: saved
|
|
249
|
-
parentConfig: saved
|
|
357
|
+
tasks: (0, debug_js_1.tasksWithWrites)(nextTasks, saved?.pendingWrites ?? [], taskStates),
|
|
358
|
+
metadata: saved.metadata,
|
|
359
|
+
config: (0, index_js_1.patchCheckpointMap)(saved.config, saved.metadata),
|
|
360
|
+
createdAt: saved.checkpoint.ts,
|
|
361
|
+
parentConfig: saved.parentConfig,
|
|
250
362
|
};
|
|
251
363
|
}
|
|
364
|
+
/**
|
|
365
|
+
* Get the current state of the graph.
|
|
366
|
+
*/
|
|
367
|
+
async getState(config, options) {
|
|
368
|
+
const checkpointer = config.configurable?.[constants_js_1.CONFIG_KEY_CHECKPOINTER] ?? this.checkpointer;
|
|
369
|
+
if (!checkpointer) {
|
|
370
|
+
throw new errors_js_1.GraphValueError("No checkpointer set");
|
|
371
|
+
}
|
|
372
|
+
const checkpointNamespace = config.configurable?.checkpoint_ns ?? "";
|
|
373
|
+
if (checkpointNamespace !== "" &&
|
|
374
|
+
config.configurable?.[constants_js_1.CONFIG_KEY_CHECKPOINTER] === undefined) {
|
|
375
|
+
// remove task_ids from checkpoint_ns
|
|
376
|
+
const recastCheckpointNamespace = checkpointNamespace
|
|
377
|
+
.split(constants_js_1.CHECKPOINT_NAMESPACE_SEPARATOR)
|
|
378
|
+
.map((part) => part.split(constants_js_1.CHECKPOINT_NAMESPACE_END)[0])
|
|
379
|
+
.join(constants_js_1.CHECKPOINT_NAMESPACE_SEPARATOR);
|
|
380
|
+
for (const [name, subgraph] of this.getSubgraphs(recastCheckpointNamespace, true)) {
|
|
381
|
+
if (name === recastCheckpointNamespace) {
|
|
382
|
+
return await subgraph.getState((0, utils_js_1.patchConfigurable)(config, {
|
|
383
|
+
[constants_js_1.CONFIG_KEY_CHECKPOINTER]: checkpointer,
|
|
384
|
+
}), { subgraphs: options?.subgraphs });
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
throw new Error(`Subgraph with namespace "${recastCheckpointNamespace}" not found.`);
|
|
388
|
+
}
|
|
389
|
+
const mergedConfig = (0, runnables_1.mergeConfigs)(this.config, config);
|
|
390
|
+
const saved = await checkpointer.getTuple(config);
|
|
391
|
+
const snapshot = await this._prepareStateSnapshot({
|
|
392
|
+
config: mergedConfig,
|
|
393
|
+
saved,
|
|
394
|
+
subgraphCheckpointer: options?.subgraphs ? checkpointer : undefined,
|
|
395
|
+
});
|
|
396
|
+
return snapshot;
|
|
397
|
+
}
|
|
252
398
|
/**
|
|
253
399
|
* Get the history of the state of the graph.
|
|
254
400
|
*/
|
|
255
401
|
async *getStateHistory(config, options) {
|
|
256
|
-
|
|
257
|
-
|
|
402
|
+
const checkpointer = config.configurable?.[constants_js_1.CONFIG_KEY_CHECKPOINTER] ?? this.checkpointer;
|
|
403
|
+
if (!checkpointer) {
|
|
404
|
+
throw new Error("No checkpointer set");
|
|
405
|
+
}
|
|
406
|
+
const checkpointNamespace = config.configurable?.checkpoint_ns ?? "";
|
|
407
|
+
if (checkpointNamespace !== "" &&
|
|
408
|
+
config.configurable?.[constants_js_1.CONFIG_KEY_CHECKPOINTER] === undefined) {
|
|
409
|
+
const recastCheckpointNamespace = checkpointNamespace
|
|
410
|
+
.split(constants_js_1.CHECKPOINT_NAMESPACE_SEPARATOR)
|
|
411
|
+
.map((part) => part.split(constants_js_1.CHECKPOINT_NAMESPACE_END)[0])
|
|
412
|
+
.join(constants_js_1.CHECKPOINT_NAMESPACE_SEPARATOR);
|
|
413
|
+
// find the subgraph with the matching name
|
|
414
|
+
for (const [name, pregel] of this.getSubgraphs(recastCheckpointNamespace, true)) {
|
|
415
|
+
if (name === recastCheckpointNamespace) {
|
|
416
|
+
yield* pregel.getStateHistory((0, utils_js_1.patchConfigurable)(config, {
|
|
417
|
+
[constants_js_1.CONFIG_KEY_CHECKPOINTER]: checkpointer,
|
|
418
|
+
}), options);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
throw new Error(`Subgraph with namespace "${recastCheckpointNamespace}" not found.`);
|
|
258
423
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
tasks: (0, debug_js_1.tasksWithWrites)(nextTasks, saved.pendingWrites ?? []),
|
|
268
|
-
metadata: saved.metadata,
|
|
269
|
-
config: saved.config,
|
|
270
|
-
createdAt: saved.checkpoint.ts,
|
|
271
|
-
parentConfig: saved.parentConfig,
|
|
272
|
-
};
|
|
424
|
+
const mergedConfig = (0, runnables_1.mergeConfigs)(this.config, config, {
|
|
425
|
+
configurable: { checkpoint_ns: checkpointNamespace },
|
|
426
|
+
});
|
|
427
|
+
for await (const checkpointTuple of checkpointer.list(mergedConfig, options)) {
|
|
428
|
+
yield this._prepareStateSnapshot({
|
|
429
|
+
config: checkpointTuple.config,
|
|
430
|
+
saved: checkpointTuple,
|
|
431
|
+
});
|
|
273
432
|
}
|
|
274
433
|
}
|
|
275
434
|
/**
|
|
@@ -277,34 +436,59 @@ class Pregel extends runnables_1.Runnable {
|
|
|
277
436
|
* node `as_node`. If `as_node` is not provided, it will be set to the last node
|
|
278
437
|
* that updated the state, if not ambiguous.
|
|
279
438
|
*/
|
|
280
|
-
async updateState(
|
|
281
|
-
|
|
439
|
+
async updateState(inputConfig, values, asNode) {
|
|
440
|
+
const checkpointer = inputConfig.configurable?.[constants_js_1.CONFIG_KEY_CHECKPOINTER] ?? this.checkpointer;
|
|
441
|
+
if (!checkpointer) {
|
|
282
442
|
throw new errors_js_1.GraphValueError("No checkpointer set");
|
|
283
443
|
}
|
|
284
|
-
//
|
|
285
|
-
const
|
|
286
|
-
|
|
444
|
+
// delegate to subgraph
|
|
445
|
+
const checkpointNamespace = inputConfig.configurable?.checkpoint_ns ?? "";
|
|
446
|
+
if (checkpointNamespace !== "" &&
|
|
447
|
+
inputConfig.configurable?.[constants_js_1.CONFIG_KEY_CHECKPOINTER] === undefined) {
|
|
448
|
+
// remove task_ids from checkpoint_ns
|
|
449
|
+
const recastCheckpointNamespace = checkpointNamespace
|
|
450
|
+
.split(constants_js_1.CHECKPOINT_NAMESPACE_SEPARATOR)
|
|
451
|
+
.map((part) => {
|
|
452
|
+
return part.split(constants_js_1.CHECKPOINT_NAMESPACE_END)[0];
|
|
453
|
+
})
|
|
454
|
+
.join(constants_js_1.CHECKPOINT_NAMESPACE_SEPARATOR);
|
|
455
|
+
// find the subgraph with the matching name
|
|
456
|
+
// eslint-disable-next-line no-unreachable-loop
|
|
457
|
+
for (const [, pregel] of this.getSubgraphs(recastCheckpointNamespace, true)) {
|
|
458
|
+
return await pregel.updateState((0, utils_js_1.patchConfigurable)(inputConfig, {
|
|
459
|
+
[constants_js_1.CONFIG_KEY_CHECKPOINTER]: checkpointer,
|
|
460
|
+
}), values, asNode);
|
|
461
|
+
}
|
|
462
|
+
throw new Error(`Subgraph "${recastCheckpointNamespace}" not found`);
|
|
463
|
+
}
|
|
464
|
+
// get last checkpoint
|
|
465
|
+
const config = this.config
|
|
466
|
+
? (0, runnables_1.mergeConfigs)(this.config, inputConfig)
|
|
467
|
+
: inputConfig;
|
|
468
|
+
const saved = await checkpointer.getTuple(config);
|
|
469
|
+
const checkpoint = saved !== undefined
|
|
287
470
|
? (0, langgraph_checkpoint_1.copyCheckpoint)(saved.checkpoint)
|
|
288
471
|
: (0, langgraph_checkpoint_1.emptyCheckpoint)();
|
|
289
|
-
const checkpointPreviousVersions =
|
|
472
|
+
const checkpointPreviousVersions = {
|
|
473
|
+
...saved?.checkpoint.channel_versions,
|
|
474
|
+
};
|
|
290
475
|
const step = saved?.metadata?.step ?? -1;
|
|
291
476
|
// merge configurable fields with previous checkpoint config
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
...saved?.config.configurable,
|
|
299
|
-
},
|
|
300
|
-
};
|
|
477
|
+
let checkpointConfig = (0, utils_js_1.patchConfigurable)(config, {
|
|
478
|
+
checkpoint_ns: config.configurable?.checkpoint_ns ?? "",
|
|
479
|
+
});
|
|
480
|
+
if (saved) {
|
|
481
|
+
checkpointConfig = (0, utils_js_1.patchConfigurable)(config, saved.config.configurable);
|
|
482
|
+
}
|
|
301
483
|
// Find last node that updated the state, if not provided
|
|
302
484
|
if (values == null && asNode === undefined) {
|
|
303
|
-
|
|
485
|
+
const nextConfig = await checkpointer.put(checkpointConfig, (0, base_js_1.createCheckpoint)(checkpoint, undefined, step), {
|
|
304
486
|
source: "update",
|
|
305
487
|
step,
|
|
306
488
|
writes: {},
|
|
489
|
+
parents: saved?.metadata?.parents ?? {},
|
|
307
490
|
}, {});
|
|
491
|
+
return (0, index_js_1.patchCheckpointMap)(nextConfig, saved ? saved.metadata : undefined);
|
|
308
492
|
}
|
|
309
493
|
const nonNullVersion = Object.values(checkpoint.versions_seen)
|
|
310
494
|
.map((seenVersions) => {
|
|
@@ -319,7 +503,6 @@ class Pregel extends runnables_1.Runnable {
|
|
|
319
503
|
}
|
|
320
504
|
}
|
|
321
505
|
else if (asNode === undefined) {
|
|
322
|
-
// TODO: Double check
|
|
323
506
|
const lastSeenByNode = Object.entries(checkpoint.versions_seen)
|
|
324
507
|
.map(([n, seen]) => {
|
|
325
508
|
return Object.values(seen).map((v) => {
|
|
@@ -376,18 +559,21 @@ class Pregel extends runnables_1.Runnable {
|
|
|
376
559
|
task, select_, fresh_),
|
|
377
560
|
},
|
|
378
561
|
}));
|
|
562
|
+
// save task writes
|
|
379
563
|
if (saved !== undefined) {
|
|
380
|
-
await
|
|
564
|
+
await checkpointer.putWrites(checkpointConfig, task.writes, task.id);
|
|
381
565
|
}
|
|
382
566
|
// apply to checkpoint
|
|
383
567
|
// TODO: Why does keyof StrRecord allow number and symbol?
|
|
384
|
-
(0, algo_js_1._applyWrites)(checkpoint, channels, [task],
|
|
385
|
-
const newVersions = (0,
|
|
386
|
-
|
|
568
|
+
(0, algo_js_1._applyWrites)(checkpoint, channels, [task], checkpointer.getNextVersion.bind(this.checkpointer));
|
|
569
|
+
const newVersions = (0, index_js_1.getNewChannelVersions)(checkpointPreviousVersions, checkpoint.channel_versions);
|
|
570
|
+
const nextConfig = await checkpointer.put(checkpointConfig, (0, base_js_1.createCheckpoint)(checkpoint, channels, step + 1), {
|
|
387
571
|
source: "update",
|
|
388
572
|
step: step + 1,
|
|
389
573
|
writes: { [asNode]: values },
|
|
574
|
+
parents: saved?.metadata?.parents ?? {},
|
|
390
575
|
}, newVersions);
|
|
576
|
+
return (0, index_js_1.patchCheckpointMap)(nextConfig, saved ? saved.metadata : undefined);
|
|
391
577
|
}
|
|
392
578
|
_defaults(config) {
|
|
393
579
|
const { debug, streamMode, inputKeys, outputKeys, interruptAfter, interruptBefore, ...rest } = config;
|
|
@@ -415,14 +601,13 @@ class Pregel extends runnables_1.Runnable {
|
|
|
415
601
|
else {
|
|
416
602
|
defaultStreamMode = this.streamMode;
|
|
417
603
|
}
|
|
418
|
-
|
|
419
|
-
if (config.configurable !== undefined
|
|
420
|
-
config.configurable[constants_js_1.CONFIG_KEY_READ] !== undefined) {
|
|
604
|
+
// if being called as a node in another graph, always use values mode
|
|
605
|
+
if (config.configurable?.[constants_js_1.CONFIG_KEY_TASK_ID] !== undefined) {
|
|
421
606
|
defaultStreamMode = ["values"];
|
|
422
607
|
}
|
|
608
|
+
let defaultCheckpointer;
|
|
423
609
|
if (config !== undefined &&
|
|
424
|
-
config.configurable?.[constants_js_1.CONFIG_KEY_CHECKPOINTER] !== undefined
|
|
425
|
-
(defaultInterruptAfter.length > 0 || defaultInterruptBefore.length > 0)) {
|
|
610
|
+
config.configurable?.[constants_js_1.CONFIG_KEY_CHECKPOINTER] !== undefined) {
|
|
426
611
|
defaultCheckpointer = config.configurable[constants_js_1.CONFIG_KEY_CHECKPOINTER];
|
|
427
612
|
}
|
|
428
613
|
else {
|
|
@@ -458,7 +643,7 @@ class Pregel extends runnables_1.Runnable {
|
|
|
458
643
|
return super.stream(input, options);
|
|
459
644
|
}
|
|
460
645
|
async prepareSpecs(config, options) {
|
|
461
|
-
const configForManaged = (0,
|
|
646
|
+
const configForManaged = (0, utils_js_1.patchConfigurable)(config, {
|
|
462
647
|
[constants_js_1.CONFIG_KEY_STORE]: this.store,
|
|
463
648
|
});
|
|
464
649
|
const channelSpecs = {};
|
|
@@ -501,7 +686,8 @@ class Pregel extends runnables_1.Runnable {
|
|
|
501
686
|
};
|
|
502
687
|
}
|
|
503
688
|
async *_streamIterator(input, options) {
|
|
504
|
-
const
|
|
689
|
+
const streamSubgraphs = options?.subgraphs;
|
|
690
|
+
const inputConfig = (0, config_js_1.ensureLangGraphConfig)(this.config, options);
|
|
505
691
|
if (inputConfig.recursionLimit === undefined ||
|
|
506
692
|
inputConfig.recursionLimit < 1) {
|
|
507
693
|
throw new Error(`Passed "recursionLimit" must be at least 1.`);
|
|
@@ -511,12 +697,36 @@ class Pregel extends runnables_1.Runnable {
|
|
|
511
697
|
throw new Error(`Checkpointer requires one or more of the following "configurable" keys: "thread_id", "checkpoint_ns", "checkpoint_id"`);
|
|
512
698
|
}
|
|
513
699
|
const callbackManager = await (0, runnables_1.getCallbackManagerForConfig)(inputConfig);
|
|
514
|
-
const runManager = await callbackManager?.handleChainStart(this.toJSON(), (0,
|
|
700
|
+
const runManager = await callbackManager?.handleChainStart(this.toJSON(), (0, index_js_1._coerceToDict)(input, "input"), inputConfig.runId, undefined, undefined, undefined, inputConfig?.runName ?? this.getName());
|
|
515
701
|
delete inputConfig.runId;
|
|
516
702
|
// assign defaults
|
|
517
703
|
const [debug, streamMode, , outputKeys, config, interruptBefore, interruptAfter, checkpointer,] = this._defaults(inputConfig);
|
|
518
704
|
const { channelSpecs, managed } = await this.prepareSpecs(config);
|
|
519
705
|
let loop;
|
|
706
|
+
const stream = new double_ended_queue_1.default();
|
|
707
|
+
function* emitCurrentLoopOutputs() {
|
|
708
|
+
while (loop !== undefined && stream.length > 0) {
|
|
709
|
+
const nextItem = stream.shift();
|
|
710
|
+
if (nextItem === undefined) {
|
|
711
|
+
throw new Error("Data structure error.");
|
|
712
|
+
}
|
|
713
|
+
const [namespace, mode, payload] = nextItem;
|
|
714
|
+
if (streamMode.includes(mode)) {
|
|
715
|
+
if (streamSubgraphs && streamMode.length > 1) {
|
|
716
|
+
yield [namespace, mode, payload];
|
|
717
|
+
}
|
|
718
|
+
else if (streamMode.length > 1) {
|
|
719
|
+
yield [mode, payload];
|
|
720
|
+
}
|
|
721
|
+
else if (streamSubgraphs) {
|
|
722
|
+
yield [namespace, payload];
|
|
723
|
+
}
|
|
724
|
+
else {
|
|
725
|
+
yield payload;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
520
730
|
try {
|
|
521
731
|
loop = await loop_js_1.PregelLoop.initialize({
|
|
522
732
|
input,
|
|
@@ -528,7 +738,14 @@ class Pregel extends runnables_1.Runnable {
|
|
|
528
738
|
outputKeys,
|
|
529
739
|
streamKeys: this.streamChannelsAsIs,
|
|
530
740
|
store: this.store,
|
|
741
|
+
stream: new loop_js_1.StreamProtocol((chunk) => stream.push(chunk), new Set(streamMode)),
|
|
531
742
|
});
|
|
743
|
+
if (options?.subgraphs) {
|
|
744
|
+
loop.config.configurable = {
|
|
745
|
+
...loop.config.configurable,
|
|
746
|
+
[constants_js_1.CONFIG_KEY_STREAM]: loop.stream,
|
|
747
|
+
};
|
|
748
|
+
}
|
|
532
749
|
while (await loop.tick({
|
|
533
750
|
inputKeys: this.inputChannels,
|
|
534
751
|
interruptAfter,
|
|
@@ -538,27 +755,14 @@ class Pregel extends runnables_1.Runnable {
|
|
|
538
755
|
if (debug) {
|
|
539
756
|
(0, debug_js_1.printStepCheckpoint)(loop.checkpointMetadata.step, loop.channels, this.streamChannelsList);
|
|
540
757
|
}
|
|
541
|
-
|
|
542
|
-
const nextItem = loop.stream.shift();
|
|
543
|
-
if (nextItem === undefined) {
|
|
544
|
-
throw new Error("Data structure error.");
|
|
545
|
-
}
|
|
546
|
-
if (streamMode.includes(nextItem[0])) {
|
|
547
|
-
if (streamMode.length === 1) {
|
|
548
|
-
yield nextItem[1];
|
|
549
|
-
}
|
|
550
|
-
else {
|
|
551
|
-
yield nextItem;
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
}
|
|
758
|
+
yield* emitCurrentLoopOutputs();
|
|
555
759
|
if (debug) {
|
|
556
|
-
(0, debug_js_1.printStepTasks)(loop.step, loop.tasks);
|
|
760
|
+
(0, debug_js_1.printStepTasks)(loop.step, Object.values(loop.tasks));
|
|
557
761
|
}
|
|
558
762
|
// execute tasks, and wait for one to fail or all to finish.
|
|
559
763
|
// each task is independent from all other concurrent tasks
|
|
560
764
|
// yield updates/debug output as each task finishes
|
|
561
|
-
const taskStream = (0, retry_js_1.executeTasksWithRetry)(loop.tasks.filter((task) => task.writes.length === 0), {
|
|
765
|
+
const taskStream = (0, retry_js_1.executeTasksWithRetry)(Object.values(loop.tasks).filter((task) => task.writes.length === 0), {
|
|
562
766
|
stepTimeout: this.stepTimeout,
|
|
563
767
|
signal: config.signal,
|
|
564
768
|
retryPolicy: this.retryPolicy,
|
|
@@ -567,7 +771,12 @@ class Pregel extends runnables_1.Runnable {
|
|
|
567
771
|
for await (const { task, error } of taskStream) {
|
|
568
772
|
if (error !== undefined) {
|
|
569
773
|
if ((0, errors_js_1.isGraphInterrupt)(error)) {
|
|
570
|
-
loop.
|
|
774
|
+
if (loop.isNested) {
|
|
775
|
+
throw error;
|
|
776
|
+
}
|
|
777
|
+
if (error.interrupts.length) {
|
|
778
|
+
loop.putWrites(task.id, error.interrupts.map((interrupt) => [constants_js_1.INTERRUPT, interrupt]));
|
|
779
|
+
}
|
|
571
780
|
}
|
|
572
781
|
else {
|
|
573
782
|
loop.putWrites(task.id, [
|
|
@@ -578,42 +787,18 @@ class Pregel extends runnables_1.Runnable {
|
|
|
578
787
|
else {
|
|
579
788
|
loop.putWrites(task.id, task.writes);
|
|
580
789
|
}
|
|
581
|
-
|
|
582
|
-
const nextItem = loop.stream.shift();
|
|
583
|
-
if (nextItem === undefined) {
|
|
584
|
-
throw new Error("Data structure error.");
|
|
585
|
-
}
|
|
586
|
-
if (streamMode.includes(nextItem[0])) {
|
|
587
|
-
if (streamMode.length === 1) {
|
|
588
|
-
yield nextItem[1];
|
|
589
|
-
}
|
|
590
|
-
else {
|
|
591
|
-
yield nextItem;
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
}
|
|
790
|
+
yield* emitCurrentLoopOutputs();
|
|
595
791
|
if (error !== undefined && !(0, errors_js_1.isGraphInterrupt)(error)) {
|
|
596
792
|
throw error;
|
|
597
793
|
}
|
|
598
794
|
}
|
|
599
795
|
if (debug) {
|
|
600
|
-
(0, debug_js_1.printStepWrites)(loop.step, loop.tasks
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
while (loop.stream.length > 0) {
|
|
604
|
-
const nextItem = loop.stream.shift();
|
|
605
|
-
if (nextItem === undefined) {
|
|
606
|
-
throw new Error("Data structure error.");
|
|
607
|
-
}
|
|
608
|
-
if (streamMode.includes(nextItem[0])) {
|
|
609
|
-
if (streamMode.length === 1) {
|
|
610
|
-
yield nextItem[1];
|
|
611
|
-
}
|
|
612
|
-
else {
|
|
613
|
-
yield nextItem;
|
|
614
|
-
}
|
|
796
|
+
(0, debug_js_1.printStepWrites)(loop.step, Object.values(loop.tasks)
|
|
797
|
+
.map((task) => task.writes)
|
|
798
|
+
.flat(), this.streamChannelsList);
|
|
615
799
|
}
|
|
616
800
|
}
|
|
801
|
+
yield* emitCurrentLoopOutputs();
|
|
617
802
|
if (loop.status === "out_of_steps") {
|
|
618
803
|
throw new errors_js_1.GraphRecursionError([
|
|
619
804
|
`Recursion limit of ${config.recursionLimit} reached`,
|
|
@@ -622,7 +807,7 @@ class Pregel extends runnables_1.Runnable {
|
|
|
622
807
|
].join(" "));
|
|
623
808
|
}
|
|
624
809
|
await Promise.all(loop?.checkpointerPromises ?? []);
|
|
625
|
-
await runManager?.handleChainEnd(
|
|
810
|
+
await runManager?.handleChainEnd(loop.output);
|
|
626
811
|
}
|
|
627
812
|
catch (e) {
|
|
628
813
|
await runManager?.handleChainError(e);
|
|
@@ -657,7 +842,7 @@ class Pregel extends runnables_1.Runnable {
|
|
|
657
842
|
async invoke(input, options) {
|
|
658
843
|
const streamMode = options?.streamMode ?? "values";
|
|
659
844
|
const config = {
|
|
660
|
-
...
|
|
845
|
+
...options,
|
|
661
846
|
outputKeys: options?.outputKeys ?? this.outputChannels,
|
|
662
847
|
streamMode,
|
|
663
848
|
};
|
package/dist/pregel/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Runnable, RunnableConfig, RunnableFunc } from "@langchain/core/runnables";
|
|
2
2
|
import { IterableReadableStream } from "@langchain/core/utils/stream";
|
|
3
|
-
import { All, BaseCheckpointSaver, CheckpointListOptions } from "@langchain/langgraph-checkpoint";
|
|
3
|
+
import { All, BaseCheckpointSaver, CheckpointListOptions, CheckpointTuple } from "@langchain/langgraph-checkpoint";
|
|
4
4
|
import { BaseChannel } from "../channels/base.js";
|
|
5
5
|
import { PregelNode } from "./read.js";
|
|
6
6
|
import { ChannelWrite } from "./write.js";
|
|
7
7
|
import { PregelInterface, PregelParams, StateSnapshot, StreamMode } from "./types.js";
|
|
8
8
|
import { StrRecord } from "./algo.js";
|
|
9
|
-
import { RetryPolicy } from "./utils.js";
|
|
9
|
+
import { RetryPolicy } from "./utils/index.js";
|
|
10
10
|
import { BaseStore } from "../store/base.js";
|
|
11
11
|
import { ManagedValueMapping, type ManagedValueSpec } from "../managed/base.js";
|
|
12
12
|
type WriteValue = Runnable | RunnableFunc<unknown, unknown> | unknown;
|
|
@@ -35,6 +35,8 @@ export interface PregelOptions<Nn extends StrRecord<string, PregelNode>, Cc exte
|
|
|
35
35
|
interruptAfter?: All | Array<keyof Nn>;
|
|
36
36
|
/** Enable debug mode for the graph run. */
|
|
37
37
|
debug?: boolean;
|
|
38
|
+
/** Whether to stream subgraphs. */
|
|
39
|
+
subgraphs?: boolean;
|
|
38
40
|
}
|
|
39
41
|
export type PregelInputType = any;
|
|
40
42
|
export type PregelOutputType = any;
|
|
@@ -54,15 +56,25 @@ export declare class Pregel<Nn extends StrRecord<string, PregelNode>, Cc extends
|
|
|
54
56
|
debug: boolean;
|
|
55
57
|
checkpointer?: BaseCheckpointSaver;
|
|
56
58
|
retryPolicy?: RetryPolicy;
|
|
59
|
+
config?: RunnableConfig;
|
|
57
60
|
store?: BaseStore;
|
|
58
61
|
constructor(fields: PregelParams<Nn, Cc>);
|
|
62
|
+
withConfig(config: RunnableConfig): typeof this;
|
|
59
63
|
validate(): this;
|
|
60
64
|
get streamChannelsList(): Array<keyof Cc>;
|
|
61
65
|
get streamChannelsAsIs(): keyof Cc | Array<keyof Cc>;
|
|
66
|
+
getSubgraphs(namespace?: string, recurse?: boolean): Generator<[string, Pregel<any, any>]>;
|
|
67
|
+
protected _prepareStateSnapshot({ config, saved, subgraphCheckpointer, }: {
|
|
68
|
+
config: RunnableConfig;
|
|
69
|
+
saved?: CheckpointTuple;
|
|
70
|
+
subgraphCheckpointer?: BaseCheckpointSaver;
|
|
71
|
+
}): Promise<StateSnapshot>;
|
|
62
72
|
/**
|
|
63
73
|
* Get the current state of the graph.
|
|
64
74
|
*/
|
|
65
|
-
getState(config: RunnableConfig
|
|
75
|
+
getState(config: RunnableConfig, options?: {
|
|
76
|
+
subgraphs?: boolean;
|
|
77
|
+
}): Promise<StateSnapshot>;
|
|
66
78
|
/**
|
|
67
79
|
* Get the history of the state of the graph.
|
|
68
80
|
*/
|
|
@@ -72,7 +84,7 @@ export declare class Pregel<Nn extends StrRecord<string, PregelNode>, Cc extends
|
|
|
72
84
|
* node `as_node`. If `as_node` is not provided, it will be set to the last node
|
|
73
85
|
* that updated the state, if not ambiguous.
|
|
74
86
|
*/
|
|
75
|
-
updateState(
|
|
87
|
+
updateState(inputConfig: RunnableConfig, values: Record<string, unknown> | unknown, asNode?: keyof Nn | string): Promise<RunnableConfig>;
|
|
76
88
|
_defaults(config: PregelOptions<Nn, Cc>): [
|
|
77
89
|
boolean,
|
|
78
90
|
StreamMode[],
|