@langchain/langgraph 0.2.29 → 0.2.31
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 +4 -1
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +3 -0
- package/dist/interrupt.cjs +85 -11
- package/dist/interrupt.d.ts +39 -0
- package/dist/interrupt.js +86 -12
- package/dist/pregel/algo.cjs +13 -14
- package/dist/pregel/algo.d.ts +1 -1
- package/dist/pregel/algo.js +14 -15
- package/dist/pregel/index.cjs +6 -1
- package/dist/pregel/index.js +7 -2
- package/dist/pregel/io.cjs +6 -2
- package/dist/pregel/io.d.ts +2 -2
- package/dist/pregel/io.js +6 -2
- package/dist/pregel/loop.cjs +24 -9
- package/dist/pregel/loop.js +25 -10
- package/dist/pregel/types.d.ts +5 -0
- package/package.json +2 -2
package/dist/constants.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports._isCommand = exports.Command = exports._isSend = exports.Send = exports._isSendInterface = exports.CHECKPOINT_NAMESPACE_END = exports.CHECKPOINT_NAMESPACE_SEPARATOR = exports.RESERVED = exports.NULL_TASK_ID = exports.TASK_NAMESPACE = exports.PULL = exports.PUSH = exports.TASKS = exports.SELF = exports.TAG_NOSTREAM = exports.TAG_HIDDEN = exports.RECURSION_LIMIT_DEFAULT = exports.RUNTIME_PLACEHOLDER = exports.RESUME = exports.INTERRUPT = exports.CONFIG_KEY_CHECKPOINT_MAP = exports.CONFIG_KEY_RESUME_VALUE = exports.CONFIG_KEY_STREAM = exports.CONFIG_KEY_TASK_ID = exports.CONFIG_KEY_RESUMING = exports.CONFIG_KEY_CHECKPOINTER = exports.CONFIG_KEY_READ = exports.CONFIG_KEY_SEND = exports.ERROR = exports.INPUT = exports.MISSING = void 0;
|
|
3
|
+
exports._isCommand = exports.Command = exports._isSend = exports.Send = exports._isSendInterface = exports.CHECKPOINT_NAMESPACE_END = exports.CHECKPOINT_NAMESPACE_SEPARATOR = exports.RESERVED = exports.NULL_TASK_ID = exports.TASK_NAMESPACE = exports.PULL = exports.PUSH = exports.TASKS = exports.SELF = exports.TAG_NOSTREAM = exports.TAG_HIDDEN = exports.RECURSION_LIMIT_DEFAULT = exports.RUNTIME_PLACEHOLDER = exports.RESUME = exports.INTERRUPT = exports.CONFIG_KEY_CHECKPOINT_MAP = exports.CONFIG_KEY_CHECKPOINT_NS = exports.CONFIG_KEY_SCRATCHPAD = exports.CONFIG_KEY_WRITES = exports.CONFIG_KEY_RESUME_VALUE = exports.CONFIG_KEY_STREAM = exports.CONFIG_KEY_TASK_ID = exports.CONFIG_KEY_RESUMING = exports.CONFIG_KEY_CHECKPOINTER = exports.CONFIG_KEY_READ = exports.CONFIG_KEY_SEND = exports.ERROR = exports.INPUT = exports.MISSING = void 0;
|
|
4
4
|
exports.MISSING = Symbol.for("__missing__");
|
|
5
5
|
exports.INPUT = "__input__";
|
|
6
6
|
exports.ERROR = "__error__";
|
|
@@ -11,6 +11,9 @@ exports.CONFIG_KEY_RESUMING = "__pregel_resuming";
|
|
|
11
11
|
exports.CONFIG_KEY_TASK_ID = "__pregel_task_id";
|
|
12
12
|
exports.CONFIG_KEY_STREAM = "__pregel_stream";
|
|
13
13
|
exports.CONFIG_KEY_RESUME_VALUE = "__pregel_resume_value";
|
|
14
|
+
exports.CONFIG_KEY_WRITES = "__pregel_writes";
|
|
15
|
+
exports.CONFIG_KEY_SCRATCHPAD = "__pregel_scratchpad";
|
|
16
|
+
exports.CONFIG_KEY_CHECKPOINT_NS = "checkpoint_ns";
|
|
14
17
|
// this one is part of public API
|
|
15
18
|
exports.CONFIG_KEY_CHECKPOINT_MAP = "checkpoint_map";
|
|
16
19
|
exports.INTERRUPT = "__interrupt__";
|
package/dist/constants.d.ts
CHANGED
|
@@ -8,6 +8,9 @@ export declare const CONFIG_KEY_RESUMING = "__pregel_resuming";
|
|
|
8
8
|
export declare const CONFIG_KEY_TASK_ID = "__pregel_task_id";
|
|
9
9
|
export declare const CONFIG_KEY_STREAM = "__pregel_stream";
|
|
10
10
|
export declare const CONFIG_KEY_RESUME_VALUE = "__pregel_resume_value";
|
|
11
|
+
export declare const CONFIG_KEY_WRITES = "__pregel_writes";
|
|
12
|
+
export declare const CONFIG_KEY_SCRATCHPAD = "__pregel_scratchpad";
|
|
13
|
+
export declare const CONFIG_KEY_CHECKPOINT_NS = "checkpoint_ns";
|
|
11
14
|
export declare const CONFIG_KEY_CHECKPOINT_MAP = "checkpoint_map";
|
|
12
15
|
export declare const INTERRUPT = "__interrupt__";
|
|
13
16
|
export declare const RESUME = "__resume__";
|
package/dist/constants.js
CHANGED
|
@@ -8,6 +8,9 @@ export const CONFIG_KEY_RESUMING = "__pregel_resuming";
|
|
|
8
8
|
export const CONFIG_KEY_TASK_ID = "__pregel_task_id";
|
|
9
9
|
export const CONFIG_KEY_STREAM = "__pregel_stream";
|
|
10
10
|
export const CONFIG_KEY_RESUME_VALUE = "__pregel_resume_value";
|
|
11
|
+
export const CONFIG_KEY_WRITES = "__pregel_writes";
|
|
12
|
+
export const CONFIG_KEY_SCRATCHPAD = "__pregel_scratchpad";
|
|
13
|
+
export const CONFIG_KEY_CHECKPOINT_NS = "checkpoint_ns";
|
|
11
14
|
// this one is part of public API
|
|
12
15
|
export const CONFIG_KEY_CHECKPOINT_MAP = "checkpoint_map";
|
|
13
16
|
export const INTERRUPT = "__interrupt__";
|
package/dist/interrupt.cjs
CHANGED
|
@@ -4,24 +4,98 @@ exports.interrupt = void 0;
|
|
|
4
4
|
const singletons_1 = require("@langchain/core/singletons");
|
|
5
5
|
const errors_js_1 = require("./errors.cjs");
|
|
6
6
|
const constants_js_1 = require("./constants.cjs");
|
|
7
|
+
/**
|
|
8
|
+
* Interrupts the execution of a graph node.
|
|
9
|
+
* This function can be used to pause execution of a node, and return the value of the `resume`
|
|
10
|
+
* input when the graph is re-invoked using `Command`.
|
|
11
|
+
* Multiple interrupts can be called within a single node, and each will be handled sequentially.
|
|
12
|
+
*
|
|
13
|
+
* When an interrupt is called:
|
|
14
|
+
* 1. If there's a `resume` value available (from a previous `Command`), it returns that value.
|
|
15
|
+
* 2. Otherwise, it throws a `GraphInterrupt` with the provided value
|
|
16
|
+
* 3. The graph can be resumed by passing a `Command` with a `resume` value
|
|
17
|
+
*
|
|
18
|
+
* @param value - The value to include in the interrupt. This will be available in task.interrupts[].value
|
|
19
|
+
* @returns The `resume` value provided when the graph is re-invoked with a Command
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* // Define a node that uses multiple interrupts
|
|
24
|
+
* const nodeWithInterrupts = () => {
|
|
25
|
+
* // First interrupt - will pause execution and include {value: 1} in task values
|
|
26
|
+
* const answer1 = interrupt({ value: 1 });
|
|
27
|
+
*
|
|
28
|
+
* // Second interrupt - only called after first interrupt is resumed
|
|
29
|
+
* const answer2 = interrupt({ value: 2 });
|
|
30
|
+
*
|
|
31
|
+
* // Use the resume values
|
|
32
|
+
* return { myKey: answer1 + " " + answer2 };
|
|
33
|
+
* };
|
|
34
|
+
*
|
|
35
|
+
* // Resume the graph after first interrupt
|
|
36
|
+
* await graph.stream(new Command({ resume: "answer 1" }));
|
|
37
|
+
*
|
|
38
|
+
* // Resume the graph after second interrupt
|
|
39
|
+
* await graph.stream(new Command({ resume: "answer 2" }));
|
|
40
|
+
* // Final result: { myKey: "answer 1 answer 2" }
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @throws {Error} If called outside the context of a graph
|
|
44
|
+
* @throws {GraphInterrupt} When no resume value is available
|
|
45
|
+
*/
|
|
7
46
|
function interrupt(value) {
|
|
8
47
|
const config = singletons_1.AsyncLocalStorageProviderSingleton.getRunnableConfig();
|
|
9
48
|
if (!config) {
|
|
10
49
|
throw new Error("Called interrupt() outside the context of a graph.");
|
|
11
50
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
51
|
+
// Track interrupt index
|
|
52
|
+
const scratchpad = config.configurable?.[constants_js_1.CONFIG_KEY_SCRATCHPAD];
|
|
53
|
+
if (scratchpad.interruptCounter === undefined) {
|
|
54
|
+
scratchpad.interruptCounter = 0;
|
|
15
55
|
}
|
|
16
56
|
else {
|
|
17
|
-
|
|
18
|
-
{
|
|
19
|
-
value,
|
|
20
|
-
when: "during",
|
|
21
|
-
resumable: true,
|
|
22
|
-
ns: config.configurable?.checkpoint_ns?.split("|"),
|
|
23
|
-
},
|
|
24
|
-
]);
|
|
57
|
+
scratchpad.interruptCounter += 1;
|
|
25
58
|
}
|
|
59
|
+
const idx = scratchpad.interruptCounter;
|
|
60
|
+
// Find previous resume values
|
|
61
|
+
const taskId = config.configurable?.[constants_js_1.CONFIG_KEY_TASK_ID];
|
|
62
|
+
const writes = config.configurable?.[constants_js_1.CONFIG_KEY_WRITES] ?? [];
|
|
63
|
+
if (!scratchpad.resume) {
|
|
64
|
+
const newResume = (writes.find((w) => w[0] === taskId && w[1] === constants_js_1.RESUME)?.[2] || []);
|
|
65
|
+
scratchpad.resume = Array.isArray(newResume) ? newResume : [newResume];
|
|
66
|
+
}
|
|
67
|
+
if (scratchpad.resume) {
|
|
68
|
+
if (idx < scratchpad.resume.length) {
|
|
69
|
+
return scratchpad.resume[idx];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Find current resume value
|
|
73
|
+
if (!scratchpad.usedNullResume) {
|
|
74
|
+
scratchpad.usedNullResume = true;
|
|
75
|
+
const sortedWrites = [...writes].sort((a, b) => b[0].localeCompare(a[0]) // Sort in reverse order
|
|
76
|
+
);
|
|
77
|
+
for (const [tid, c, v] of sortedWrites) {
|
|
78
|
+
if (tid === constants_js_1.NULL_TASK_ID && c === constants_js_1.RESUME) {
|
|
79
|
+
if (scratchpad.resume.length !== idx) {
|
|
80
|
+
throw new Error(`Resume length mismatch: ${scratchpad.resume.length} !== ${idx}`);
|
|
81
|
+
}
|
|
82
|
+
scratchpad.resume.push(v);
|
|
83
|
+
const send = config.configurable?.[constants_js_1.CONFIG_KEY_SEND];
|
|
84
|
+
if (send) {
|
|
85
|
+
send([[constants_js_1.RESUME, scratchpad.resume]]);
|
|
86
|
+
}
|
|
87
|
+
return v;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// No resume value found
|
|
92
|
+
throw new errors_js_1.GraphInterrupt([
|
|
93
|
+
{
|
|
94
|
+
value,
|
|
95
|
+
when: "during",
|
|
96
|
+
resumable: true,
|
|
97
|
+
ns: config.configurable?.[constants_js_1.CONFIG_KEY_CHECKPOINT_NS]?.split(constants_js_1.CHECKPOINT_NAMESPACE_SEPARATOR),
|
|
98
|
+
},
|
|
99
|
+
]);
|
|
26
100
|
}
|
|
27
101
|
exports.interrupt = interrupt;
|
package/dist/interrupt.d.ts
CHANGED
|
@@ -1 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interrupts the execution of a graph node.
|
|
3
|
+
* This function can be used to pause execution of a node, and return the value of the `resume`
|
|
4
|
+
* input when the graph is re-invoked using `Command`.
|
|
5
|
+
* Multiple interrupts can be called within a single node, and each will be handled sequentially.
|
|
6
|
+
*
|
|
7
|
+
* When an interrupt is called:
|
|
8
|
+
* 1. If there's a `resume` value available (from a previous `Command`), it returns that value.
|
|
9
|
+
* 2. Otherwise, it throws a `GraphInterrupt` with the provided value
|
|
10
|
+
* 3. The graph can be resumed by passing a `Command` with a `resume` value
|
|
11
|
+
*
|
|
12
|
+
* @param value - The value to include in the interrupt. This will be available in task.interrupts[].value
|
|
13
|
+
* @returns The `resume` value provided when the graph is re-invoked with a Command
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // Define a node that uses multiple interrupts
|
|
18
|
+
* const nodeWithInterrupts = () => {
|
|
19
|
+
* // First interrupt - will pause execution and include {value: 1} in task values
|
|
20
|
+
* const answer1 = interrupt({ value: 1 });
|
|
21
|
+
*
|
|
22
|
+
* // Second interrupt - only called after first interrupt is resumed
|
|
23
|
+
* const answer2 = interrupt({ value: 2 });
|
|
24
|
+
*
|
|
25
|
+
* // Use the resume values
|
|
26
|
+
* return { myKey: answer1 + " " + answer2 };
|
|
27
|
+
* };
|
|
28
|
+
*
|
|
29
|
+
* // Resume the graph after first interrupt
|
|
30
|
+
* await graph.stream(new Command({ resume: "answer 1" }));
|
|
31
|
+
*
|
|
32
|
+
* // Resume the graph after second interrupt
|
|
33
|
+
* await graph.stream(new Command({ resume: "answer 2" }));
|
|
34
|
+
* // Final result: { myKey: "answer 1 answer 2" }
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @throws {Error} If called outside the context of a graph
|
|
38
|
+
* @throws {GraphInterrupt} When no resume value is available
|
|
39
|
+
*/
|
|
1
40
|
export declare function interrupt<I = unknown, R = unknown>(value: I): R;
|
package/dist/interrupt.js
CHANGED
|
@@ -1,23 +1,97 @@
|
|
|
1
1
|
import { AsyncLocalStorageProviderSingleton } from "@langchain/core/singletons";
|
|
2
2
|
import { GraphInterrupt } from "./errors.js";
|
|
3
|
-
import {
|
|
3
|
+
import { CONFIG_KEY_CHECKPOINT_NS, CONFIG_KEY_SCRATCHPAD, CONFIG_KEY_TASK_ID, CONFIG_KEY_WRITES, CONFIG_KEY_SEND, CHECKPOINT_NAMESPACE_SEPARATOR, NULL_TASK_ID, RESUME, } from "./constants.js";
|
|
4
|
+
/**
|
|
5
|
+
* Interrupts the execution of a graph node.
|
|
6
|
+
* This function can be used to pause execution of a node, and return the value of the `resume`
|
|
7
|
+
* input when the graph is re-invoked using `Command`.
|
|
8
|
+
* Multiple interrupts can be called within a single node, and each will be handled sequentially.
|
|
9
|
+
*
|
|
10
|
+
* When an interrupt is called:
|
|
11
|
+
* 1. If there's a `resume` value available (from a previous `Command`), it returns that value.
|
|
12
|
+
* 2. Otherwise, it throws a `GraphInterrupt` with the provided value
|
|
13
|
+
* 3. The graph can be resumed by passing a `Command` with a `resume` value
|
|
14
|
+
*
|
|
15
|
+
* @param value - The value to include in the interrupt. This will be available in task.interrupts[].value
|
|
16
|
+
* @returns The `resume` value provided when the graph is re-invoked with a Command
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* // Define a node that uses multiple interrupts
|
|
21
|
+
* const nodeWithInterrupts = () => {
|
|
22
|
+
* // First interrupt - will pause execution and include {value: 1} in task values
|
|
23
|
+
* const answer1 = interrupt({ value: 1 });
|
|
24
|
+
*
|
|
25
|
+
* // Second interrupt - only called after first interrupt is resumed
|
|
26
|
+
* const answer2 = interrupt({ value: 2 });
|
|
27
|
+
*
|
|
28
|
+
* // Use the resume values
|
|
29
|
+
* return { myKey: answer1 + " " + answer2 };
|
|
30
|
+
* };
|
|
31
|
+
*
|
|
32
|
+
* // Resume the graph after first interrupt
|
|
33
|
+
* await graph.stream(new Command({ resume: "answer 1" }));
|
|
34
|
+
*
|
|
35
|
+
* // Resume the graph after second interrupt
|
|
36
|
+
* await graph.stream(new Command({ resume: "answer 2" }));
|
|
37
|
+
* // Final result: { myKey: "answer 1 answer 2" }
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @throws {Error} If called outside the context of a graph
|
|
41
|
+
* @throws {GraphInterrupt} When no resume value is available
|
|
42
|
+
*/
|
|
4
43
|
export function interrupt(value) {
|
|
5
44
|
const config = AsyncLocalStorageProviderSingleton.getRunnableConfig();
|
|
6
45
|
if (!config) {
|
|
7
46
|
throw new Error("Called interrupt() outside the context of a graph.");
|
|
8
47
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
48
|
+
// Track interrupt index
|
|
49
|
+
const scratchpad = config.configurable?.[CONFIG_KEY_SCRATCHPAD];
|
|
50
|
+
if (scratchpad.interruptCounter === undefined) {
|
|
51
|
+
scratchpad.interruptCounter = 0;
|
|
12
52
|
}
|
|
13
53
|
else {
|
|
14
|
-
|
|
15
|
-
{
|
|
16
|
-
value,
|
|
17
|
-
when: "during",
|
|
18
|
-
resumable: true,
|
|
19
|
-
ns: config.configurable?.checkpoint_ns?.split("|"),
|
|
20
|
-
},
|
|
21
|
-
]);
|
|
54
|
+
scratchpad.interruptCounter += 1;
|
|
22
55
|
}
|
|
56
|
+
const idx = scratchpad.interruptCounter;
|
|
57
|
+
// Find previous resume values
|
|
58
|
+
const taskId = config.configurable?.[CONFIG_KEY_TASK_ID];
|
|
59
|
+
const writes = config.configurable?.[CONFIG_KEY_WRITES] ?? [];
|
|
60
|
+
if (!scratchpad.resume) {
|
|
61
|
+
const newResume = (writes.find((w) => w[0] === taskId && w[1] === RESUME)?.[2] || []);
|
|
62
|
+
scratchpad.resume = Array.isArray(newResume) ? newResume : [newResume];
|
|
63
|
+
}
|
|
64
|
+
if (scratchpad.resume) {
|
|
65
|
+
if (idx < scratchpad.resume.length) {
|
|
66
|
+
return scratchpad.resume[idx];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Find current resume value
|
|
70
|
+
if (!scratchpad.usedNullResume) {
|
|
71
|
+
scratchpad.usedNullResume = true;
|
|
72
|
+
const sortedWrites = [...writes].sort((a, b) => b[0].localeCompare(a[0]) // Sort in reverse order
|
|
73
|
+
);
|
|
74
|
+
for (const [tid, c, v] of sortedWrites) {
|
|
75
|
+
if (tid === NULL_TASK_ID && c === RESUME) {
|
|
76
|
+
if (scratchpad.resume.length !== idx) {
|
|
77
|
+
throw new Error(`Resume length mismatch: ${scratchpad.resume.length} !== ${idx}`);
|
|
78
|
+
}
|
|
79
|
+
scratchpad.resume.push(v);
|
|
80
|
+
const send = config.configurable?.[CONFIG_KEY_SEND];
|
|
81
|
+
if (send) {
|
|
82
|
+
send([[RESUME, scratchpad.resume]]);
|
|
83
|
+
}
|
|
84
|
+
return v;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// No resume value found
|
|
89
|
+
throw new GraphInterrupt([
|
|
90
|
+
{
|
|
91
|
+
value,
|
|
92
|
+
when: "during",
|
|
93
|
+
resumable: true,
|
|
94
|
+
ns: config.configurable?.[CONFIG_KEY_CHECKPOINT_NS]?.split(CHECKPOINT_NAMESPACE_SEPARATOR),
|
|
95
|
+
},
|
|
96
|
+
]);
|
|
23
97
|
}
|
package/dist/pregel/algo.cjs
CHANGED
|
@@ -75,7 +75,7 @@ function _localRead(step, checkpoint, channels, managed, task, select, fresh = f
|
|
|
75
75
|
exports._localRead = _localRead;
|
|
76
76
|
function _localWrite(step,
|
|
77
77
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
78
|
-
commit, processes,
|
|
78
|
+
commit, processes, managed,
|
|
79
79
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
80
80
|
writes) {
|
|
81
81
|
for (const [chan, value] of writes) {
|
|
@@ -89,9 +89,6 @@ writes) {
|
|
|
89
89
|
// replace any runtime values with placeholders
|
|
90
90
|
managed.replaceRuntimeValues(step, value.args);
|
|
91
91
|
}
|
|
92
|
-
else if (!(chan in channels) && !managed.get(chan)) {
|
|
93
|
-
console.warn(`Skipping write for channel '${chan}' which has no readers`);
|
|
94
|
-
}
|
|
95
92
|
}
|
|
96
93
|
commit(writes);
|
|
97
94
|
}
|
|
@@ -307,7 +304,6 @@ function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processes, chan
|
|
|
307
304
|
metadata = { ...metadata, ...proc.metadata };
|
|
308
305
|
}
|
|
309
306
|
const writes = [];
|
|
310
|
-
const resume = pendingWrites?.find((w) => [taskId, constants_js_1.NULL_TASK_ID].includes(w[0]) && w[1] === constants_js_1.RESUME);
|
|
311
307
|
return {
|
|
312
308
|
name: packet.node,
|
|
313
309
|
input: packet.args,
|
|
@@ -324,7 +320,7 @@ function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processes, chan
|
|
|
324
320
|
configurable: {
|
|
325
321
|
[constants_js_1.CONFIG_KEY_TASK_ID]: taskId,
|
|
326
322
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
327
|
-
[constants_js_1.CONFIG_KEY_SEND]: (writes_) => _localWrite(step, (items) => writes.push(...items), processes,
|
|
323
|
+
[constants_js_1.CONFIG_KEY_SEND]: (writes_) => _localWrite(step, (items) => writes.push(...items), processes, managed, writes_),
|
|
328
324
|
[constants_js_1.CONFIG_KEY_READ]: (select_, fresh_ = false) => _localRead(step, checkpoint, channels, managed, {
|
|
329
325
|
name: packet.node,
|
|
330
326
|
writes: writes,
|
|
@@ -336,9 +332,11 @@ function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processes, chan
|
|
|
336
332
|
...configurable[constants_js_1.CONFIG_KEY_CHECKPOINT_MAP],
|
|
337
333
|
[parentNamespace]: checkpoint.id,
|
|
338
334
|
},
|
|
339
|
-
[constants_js_1.
|
|
340
|
-
|
|
341
|
-
|
|
335
|
+
[constants_js_1.CONFIG_KEY_WRITES]: [
|
|
336
|
+
...(pendingWrites || []),
|
|
337
|
+
...(configurable[constants_js_1.CONFIG_KEY_WRITES] || []),
|
|
338
|
+
].filter((w) => w[0] === constants_js_1.NULL_TASK_ID || w[0] === taskId),
|
|
339
|
+
[constants_js_1.CONFIG_KEY_SCRATCHPAD]: {},
|
|
342
340
|
checkpoint_id: undefined,
|
|
343
341
|
checkpoint_ns: taskCheckpointNamespace,
|
|
344
342
|
},
|
|
@@ -409,7 +407,6 @@ function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processes, chan
|
|
|
409
407
|
metadata = { ...metadata, ...proc.metadata };
|
|
410
408
|
}
|
|
411
409
|
const writes = [];
|
|
412
|
-
const resume = pendingWrites?.find((w) => [taskId, constants_js_1.NULL_TASK_ID].includes(w[0]) && w[1] === constants_js_1.RESUME);
|
|
413
410
|
const taskCheckpointNamespace = `${checkpointNamespace}${constants_js_1.CHECKPOINT_NAMESPACE_END}${taskId}`;
|
|
414
411
|
return {
|
|
415
412
|
name,
|
|
@@ -429,7 +426,7 @@ function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processes, chan
|
|
|
429
426
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
430
427
|
[constants_js_1.CONFIG_KEY_SEND]: (writes_) => _localWrite(step, (items) => {
|
|
431
428
|
writes.push(...items);
|
|
432
|
-
}, processes,
|
|
429
|
+
}, processes, managed, writes_),
|
|
433
430
|
[constants_js_1.CONFIG_KEY_READ]: (select_, fresh_ = false) => _localRead(step, checkpoint, channels, managed, {
|
|
434
431
|
name,
|
|
435
432
|
writes: writes,
|
|
@@ -441,9 +438,11 @@ function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processes, chan
|
|
|
441
438
|
...configurable[constants_js_1.CONFIG_KEY_CHECKPOINT_MAP],
|
|
442
439
|
[parentNamespace]: checkpoint.id,
|
|
443
440
|
},
|
|
444
|
-
[constants_js_1.
|
|
445
|
-
|
|
446
|
-
|
|
441
|
+
[constants_js_1.CONFIG_KEY_WRITES]: [
|
|
442
|
+
...(pendingWrites || []),
|
|
443
|
+
...(configurable[constants_js_1.CONFIG_KEY_WRITES] || []),
|
|
444
|
+
].filter((w) => w[0] === constants_js_1.NULL_TASK_ID || w[0] === taskId),
|
|
445
|
+
[constants_js_1.CONFIG_KEY_SCRATCHPAD]: {},
|
|
447
446
|
checkpoint_id: undefined,
|
|
448
447
|
checkpoint_ns: taskCheckpointNamespace,
|
|
449
448
|
},
|
package/dist/pregel/algo.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export type WritesProtocol<C = string> = {
|
|
|
20
20
|
export declare const increment: (current?: number) => number;
|
|
21
21
|
export declare function shouldInterrupt<N extends PropertyKey, C extends PropertyKey>(checkpoint: Checkpoint, interruptNodes: All | N[], tasks: PregelExecutableTask<N, C>[]): boolean;
|
|
22
22
|
export declare function _localRead<Cc extends Record<string, BaseChannel>>(step: number, checkpoint: ReadonlyCheckpoint, channels: Cc, managed: ManagedValueMapping, task: WritesProtocol<keyof Cc>, select: Array<keyof Cc> | keyof Cc, fresh?: boolean): Record<string, unknown> | unknown;
|
|
23
|
-
export declare function _localWrite(step: number, commit: (writes: [string, any][]) => any, processes: Record<string, PregelNode>,
|
|
23
|
+
export declare function _localWrite(step: number, commit: (writes: [string, any][]) => any, processes: Record<string, PregelNode>, managed: ManagedValueMapping, writes: [string, any][]): void;
|
|
24
24
|
export declare function _applyWrites<Cc extends Record<string, BaseChannel>>(checkpoint: Checkpoint, channels: Cc, tasks: WritesProtocol<keyof Cc>[], getNextVersion?: (version: any, channel: BaseChannel) => any): Record<string, PendingWriteValue[]>;
|
|
25
25
|
export type NextTaskExtraFields = {
|
|
26
26
|
step: number;
|
package/dist/pregel/algo.js
CHANGED
|
@@ -3,7 +3,7 @@ import { mergeConfigs, patchConfig, } from "@langchain/core/runnables";
|
|
|
3
3
|
import { copyCheckpoint, uuid5, maxChannelVersion, } from "@langchain/langgraph-checkpoint";
|
|
4
4
|
import { createCheckpoint, emptyChannels, isBaseChannel, } from "../channels/base.js";
|
|
5
5
|
import { readChannel, readChannels } from "./io.js";
|
|
6
|
-
import { _isSend, _isSendInterface, CONFIG_KEY_CHECKPOINT_MAP, CHECKPOINT_NAMESPACE_SEPARATOR, CONFIG_KEY_CHECKPOINTER, CONFIG_KEY_READ, CONFIG_KEY_TASK_ID, CONFIG_KEY_SEND, INTERRUPT, RESERVED, TAG_HIDDEN, TASKS, CHECKPOINT_NAMESPACE_END, PUSH, PULL, RESUME,
|
|
6
|
+
import { _isSend, _isSendInterface, CONFIG_KEY_CHECKPOINT_MAP, CHECKPOINT_NAMESPACE_SEPARATOR, CONFIG_KEY_CHECKPOINTER, CONFIG_KEY_READ, CONFIG_KEY_TASK_ID, CONFIG_KEY_SEND, INTERRUPT, RESERVED, TAG_HIDDEN, TASKS, CHECKPOINT_NAMESPACE_END, PUSH, PULL, RESUME, NULL_TASK_ID, CONFIG_KEY_SCRATCHPAD, CONFIG_KEY_WRITES, } from "../constants.js";
|
|
7
7
|
import { EmptyChannelError, InvalidUpdateError } from "../errors.js";
|
|
8
8
|
import { getNullChannelVersion } from "./utils/index.js";
|
|
9
9
|
export const increment = (current) => {
|
|
@@ -69,7 +69,7 @@ export function _localRead(step, checkpoint, channels, managed, task, select, fr
|
|
|
69
69
|
}
|
|
70
70
|
export function _localWrite(step,
|
|
71
71
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
72
|
-
commit, processes,
|
|
72
|
+
commit, processes, managed,
|
|
73
73
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
74
74
|
writes) {
|
|
75
75
|
for (const [chan, value] of writes) {
|
|
@@ -83,9 +83,6 @@ writes) {
|
|
|
83
83
|
// replace any runtime values with placeholders
|
|
84
84
|
managed.replaceRuntimeValues(step, value.args);
|
|
85
85
|
}
|
|
86
|
-
else if (!(chan in channels) && !managed.get(chan)) {
|
|
87
|
-
console.warn(`Skipping write for channel '${chan}' which has no readers`);
|
|
88
|
-
}
|
|
89
86
|
}
|
|
90
87
|
commit(writes);
|
|
91
88
|
}
|
|
@@ -298,7 +295,6 @@ export function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processe
|
|
|
298
295
|
metadata = { ...metadata, ...proc.metadata };
|
|
299
296
|
}
|
|
300
297
|
const writes = [];
|
|
301
|
-
const resume = pendingWrites?.find((w) => [taskId, NULL_TASK_ID].includes(w[0]) && w[1] === RESUME);
|
|
302
298
|
return {
|
|
303
299
|
name: packet.node,
|
|
304
300
|
input: packet.args,
|
|
@@ -315,7 +311,7 @@ export function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processe
|
|
|
315
311
|
configurable: {
|
|
316
312
|
[CONFIG_KEY_TASK_ID]: taskId,
|
|
317
313
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
318
|
-
[CONFIG_KEY_SEND]: (writes_) => _localWrite(step, (items) => writes.push(...items), processes,
|
|
314
|
+
[CONFIG_KEY_SEND]: (writes_) => _localWrite(step, (items) => writes.push(...items), processes, managed, writes_),
|
|
319
315
|
[CONFIG_KEY_READ]: (select_, fresh_ = false) => _localRead(step, checkpoint, channels, managed, {
|
|
320
316
|
name: packet.node,
|
|
321
317
|
writes: writes,
|
|
@@ -327,9 +323,11 @@ export function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processe
|
|
|
327
323
|
...configurable[CONFIG_KEY_CHECKPOINT_MAP],
|
|
328
324
|
[parentNamespace]: checkpoint.id,
|
|
329
325
|
},
|
|
330
|
-
[
|
|
331
|
-
|
|
332
|
-
|
|
326
|
+
[CONFIG_KEY_WRITES]: [
|
|
327
|
+
...(pendingWrites || []),
|
|
328
|
+
...(configurable[CONFIG_KEY_WRITES] || []),
|
|
329
|
+
].filter((w) => w[0] === NULL_TASK_ID || w[0] === taskId),
|
|
330
|
+
[CONFIG_KEY_SCRATCHPAD]: {},
|
|
333
331
|
checkpoint_id: undefined,
|
|
334
332
|
checkpoint_ns: taskCheckpointNamespace,
|
|
335
333
|
},
|
|
@@ -400,7 +398,6 @@ export function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processe
|
|
|
400
398
|
metadata = { ...metadata, ...proc.metadata };
|
|
401
399
|
}
|
|
402
400
|
const writes = [];
|
|
403
|
-
const resume = pendingWrites?.find((w) => [taskId, NULL_TASK_ID].includes(w[0]) && w[1] === RESUME);
|
|
404
401
|
const taskCheckpointNamespace = `${checkpointNamespace}${CHECKPOINT_NAMESPACE_END}${taskId}`;
|
|
405
402
|
return {
|
|
406
403
|
name,
|
|
@@ -420,7 +417,7 @@ export function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processe
|
|
|
420
417
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
421
418
|
[CONFIG_KEY_SEND]: (writes_) => _localWrite(step, (items) => {
|
|
422
419
|
writes.push(...items);
|
|
423
|
-
}, processes,
|
|
420
|
+
}, processes, managed, writes_),
|
|
424
421
|
[CONFIG_KEY_READ]: (select_, fresh_ = false) => _localRead(step, checkpoint, channels, managed, {
|
|
425
422
|
name,
|
|
426
423
|
writes: writes,
|
|
@@ -432,9 +429,11 @@ export function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processe
|
|
|
432
429
|
...configurable[CONFIG_KEY_CHECKPOINT_MAP],
|
|
433
430
|
[parentNamespace]: checkpoint.id,
|
|
434
431
|
},
|
|
435
|
-
[
|
|
436
|
-
|
|
437
|
-
|
|
432
|
+
[CONFIG_KEY_WRITES]: [
|
|
433
|
+
...(pendingWrites || []),
|
|
434
|
+
...(configurable[CONFIG_KEY_WRITES] || []),
|
|
435
|
+
].filter((w) => w[0] === NULL_TASK_ID || w[0] === taskId),
|
|
436
|
+
[CONFIG_KEY_SCRATCHPAD]: {},
|
|
438
437
|
checkpoint_id: undefined,
|
|
439
438
|
checkpoint_ns: taskCheckpointNamespace,
|
|
440
439
|
},
|
package/dist/pregel/index.cjs
CHANGED
|
@@ -863,7 +863,12 @@ class Pregel extends runnables_1.Runnable {
|
|
|
863
863
|
throw error;
|
|
864
864
|
}
|
|
865
865
|
if ((0, errors_js_1.isGraphInterrupt)(error) && error.interrupts.length) {
|
|
866
|
-
|
|
866
|
+
const interrupts = error.interrupts.map((interrupt) => [constants_js_1.INTERRUPT, interrupt]);
|
|
867
|
+
const resumes = task.writes.filter((w) => w[0] === constants_js_1.RESUME);
|
|
868
|
+
if (resumes.length) {
|
|
869
|
+
interrupts.push(...resumes);
|
|
870
|
+
}
|
|
871
|
+
loop.putWrites(task.id, interrupts);
|
|
867
872
|
}
|
|
868
873
|
}
|
|
869
874
|
else {
|
package/dist/pregel/index.js
CHANGED
|
@@ -7,7 +7,7 @@ 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, NULL_TASK_ID, INPUT, PUSH, } 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, RESUME, 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";
|
|
@@ -859,7 +859,12 @@ export class Pregel extends Runnable {
|
|
|
859
859
|
throw error;
|
|
860
860
|
}
|
|
861
861
|
if (isGraphInterrupt(error) && error.interrupts.length) {
|
|
862
|
-
|
|
862
|
+
const interrupts = error.interrupts.map((interrupt) => [INTERRUPT, interrupt]);
|
|
863
|
+
const resumes = task.writes.filter((w) => w[0] === RESUME);
|
|
864
|
+
if (resumes.length) {
|
|
865
|
+
interrupts.push(...resumes);
|
|
866
|
+
}
|
|
867
|
+
loop.putWrites(task.id, interrupts);
|
|
863
868
|
}
|
|
864
869
|
}
|
|
865
870
|
else {
|
package/dist/pregel/io.cjs
CHANGED
|
@@ -49,7 +49,7 @@ exports.readChannels = readChannels;
|
|
|
49
49
|
/**
|
|
50
50
|
* Map input chunk to a sequence of pending writes in the form (channel, value).
|
|
51
51
|
*/
|
|
52
|
-
function* mapCommand(cmd) {
|
|
52
|
+
function* mapCommand(cmd, pendingWrites) {
|
|
53
53
|
if (cmd.graph === constants_js_1.Command.PARENT) {
|
|
54
54
|
throw new errors_js_1.InvalidUpdateError("There is no parent graph.");
|
|
55
55
|
}
|
|
@@ -80,7 +80,11 @@ function* mapCommand(cmd) {
|
|
|
80
80
|
Object.keys(cmd.resume).length &&
|
|
81
81
|
Object.keys(cmd.resume).every(uuid_1.validate)) {
|
|
82
82
|
for (const [tid, resume] of Object.entries(cmd.resume)) {
|
|
83
|
-
|
|
83
|
+
// Find existing resume values for this task ID
|
|
84
|
+
const existing = (pendingWrites.find(([id, type]) => id === tid && type === constants_js_1.RESUME)?.[2] ?? []);
|
|
85
|
+
// Ensure we have an array and append the resume value
|
|
86
|
+
existing.push(resume);
|
|
87
|
+
yield [tid, constants_js_1.RESUME, existing];
|
|
84
88
|
}
|
|
85
89
|
}
|
|
86
90
|
else {
|
package/dist/pregel/io.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PendingWrite } from "@langchain/langgraph-checkpoint";
|
|
1
|
+
import type { CheckpointPendingWrite, PendingWrite } from "@langchain/langgraph-checkpoint";
|
|
2
2
|
import type { BaseChannel } from "../channels/base.js";
|
|
3
3
|
import type { PregelExecutableTask } from "./types.js";
|
|
4
4
|
import { Command } from "../constants.js";
|
|
@@ -7,7 +7,7 @@ export declare function readChannels<C extends PropertyKey>(channels: Record<C,
|
|
|
7
7
|
/**
|
|
8
8
|
* Map input chunk to a sequence of pending writes in the form (channel, value).
|
|
9
9
|
*/
|
|
10
|
-
export declare function mapCommand(cmd: Command): Generator<[string, string, unknown]>;
|
|
10
|
+
export declare function mapCommand(cmd: Command, pendingWrites: CheckpointPendingWrite<string>[]): Generator<[string, string, unknown]>;
|
|
11
11
|
/**
|
|
12
12
|
* Map input chunk to a sequence of pending writes in the form [channel, value].
|
|
13
13
|
*/
|
package/dist/pregel/io.js
CHANGED
|
@@ -44,7 +44,7 @@ export function readChannels(channels, select, skipEmpty = true
|
|
|
44
44
|
/**
|
|
45
45
|
* Map input chunk to a sequence of pending writes in the form (channel, value).
|
|
46
46
|
*/
|
|
47
|
-
export function* mapCommand(cmd) {
|
|
47
|
+
export function* mapCommand(cmd, pendingWrites) {
|
|
48
48
|
if (cmd.graph === Command.PARENT) {
|
|
49
49
|
throw new InvalidUpdateError("There is no parent graph.");
|
|
50
50
|
}
|
|
@@ -75,7 +75,11 @@ export function* mapCommand(cmd) {
|
|
|
75
75
|
Object.keys(cmd.resume).length &&
|
|
76
76
|
Object.keys(cmd.resume).every(validate)) {
|
|
77
77
|
for (const [tid, resume] of Object.entries(cmd.resume)) {
|
|
78
|
-
|
|
78
|
+
// Find existing resume values for this task ID
|
|
79
|
+
const existing = (pendingWrites.find(([id, type]) => id === tid && type === RESUME)?.[2] ?? []);
|
|
80
|
+
// Ensure we have an array and append the resume value
|
|
81
|
+
existing.push(resume);
|
|
82
|
+
yield [tid, RESUME, existing];
|
|
79
83
|
}
|
|
80
84
|
}
|
|
81
85
|
else {
|
package/dist/pregel/loop.cjs
CHANGED
|
@@ -399,14 +399,29 @@ class PregelLoop {
|
|
|
399
399
|
* @param writes
|
|
400
400
|
*/
|
|
401
401
|
putWrites(taskId, writes) {
|
|
402
|
-
|
|
402
|
+
let writesCopy = writes;
|
|
403
|
+
if (writesCopy.length === 0) {
|
|
403
404
|
return;
|
|
404
405
|
}
|
|
406
|
+
// deduplicate writes to special channels, last write wins
|
|
407
|
+
if (writesCopy.every(([key]) => key in langgraph_checkpoint_1.WRITES_IDX_MAP)) {
|
|
408
|
+
writesCopy = Array.from(new Map(writesCopy.map((w) => [w[0], w])).values());
|
|
409
|
+
}
|
|
405
410
|
// save writes
|
|
406
|
-
const
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
411
|
+
for (const [c, v] of writesCopy) {
|
|
412
|
+
if (c in langgraph_checkpoint_1.WRITES_IDX_MAP) {
|
|
413
|
+
const idx = this.checkpointPendingWrites.findIndex((w) => w[0] === taskId && w[1] === c);
|
|
414
|
+
if (idx !== -1) {
|
|
415
|
+
this.checkpointPendingWrites[idx] = [taskId, c, v];
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
this.checkpointPendingWrites.push([taskId, c, v]);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
this.checkpointPendingWrites.push([taskId, c, v]);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
410
425
|
const putWritePromise = this.checkpointer?.putWrites({
|
|
411
426
|
...this.checkpointConfig,
|
|
412
427
|
configurable: {
|
|
@@ -414,12 +429,12 @@ class PregelLoop {
|
|
|
414
429
|
checkpoint_ns: this.config.configurable?.checkpoint_ns ?? "",
|
|
415
430
|
checkpoint_id: this.checkpoint.id,
|
|
416
431
|
},
|
|
417
|
-
},
|
|
432
|
+
}, writesCopy, taskId);
|
|
418
433
|
if (putWritePromise !== undefined) {
|
|
419
434
|
this.checkpointerPromises.push(putWritePromise);
|
|
420
435
|
}
|
|
421
436
|
if (this.tasks) {
|
|
422
|
-
this._outputWrites(taskId,
|
|
437
|
+
this._outputWrites(taskId, writesCopy);
|
|
423
438
|
}
|
|
424
439
|
}
|
|
425
440
|
_outputWrites(taskId, writes, cached = false) {
|
|
@@ -457,7 +472,7 @@ class PregelLoop {
|
|
|
457
472
|
if (![INPUT_DONE, INPUT_RESUMING].includes(this.input)) {
|
|
458
473
|
await this._first(inputKeys);
|
|
459
474
|
}
|
|
460
|
-
else if (Object.values(this.tasks).every((task) => task.writes.length > 0)) {
|
|
475
|
+
else if (Object.values(this.tasks).every((task) => task.writes.filter(([c]) => !(c in langgraph_checkpoint_1.WRITES_IDX_MAP)).length > 0)) {
|
|
461
476
|
const writes = Object.values(this.tasks).flatMap((t) => t.writes);
|
|
462
477
|
// All tasks have finished
|
|
463
478
|
const managedValueWrites = (0, algo_js_1._applyWrites)(this.checkpoint, this.channels, Object.values(this.tasks), this.checkpointerGetNextVersion);
|
|
@@ -581,7 +596,7 @@ class PregelLoop {
|
|
|
581
596
|
if ((0, constants_js_1._isCommand)(this.input)) {
|
|
582
597
|
const writes = {};
|
|
583
598
|
// group writes by task id
|
|
584
|
-
for (const [tid, key, value] of (0, io_js_1.mapCommand)(this.input)) {
|
|
599
|
+
for (const [tid, key, value] of (0, io_js_1.mapCommand)(this.input, this.checkpointPendingWrites)) {
|
|
585
600
|
if (writes[tid] === undefined) {
|
|
586
601
|
writes[tid] = [];
|
|
587
602
|
}
|
package/dist/pregel/loop.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IterableReadableStream } from "@langchain/core/utils/stream";
|
|
2
|
-
import { copyCheckpoint, emptyCheckpoint, AsyncBatchedStore, } from "@langchain/langgraph-checkpoint";
|
|
2
|
+
import { copyCheckpoint, emptyCheckpoint, AsyncBatchedStore, WRITES_IDX_MAP, } from "@langchain/langgraph-checkpoint";
|
|
3
3
|
import { createCheckpoint, emptyChannels, } from "../channels/base.js";
|
|
4
4
|
import { _isCommand, CHECKPOINT_NAMESPACE_SEPARATOR, CONFIG_KEY_CHECKPOINT_MAP, CONFIG_KEY_READ, CONFIG_KEY_RESUMING, CONFIG_KEY_STREAM, ERROR, INPUT, INTERRUPT, RESUME, TAG_HIDDEN, } from "../constants.js";
|
|
5
5
|
import { _applyWrites, _prepareNextTasks, increment, shouldInterrupt, } from "./algo.js";
|
|
@@ -395,14 +395,29 @@ export class PregelLoop {
|
|
|
395
395
|
* @param writes
|
|
396
396
|
*/
|
|
397
397
|
putWrites(taskId, writes) {
|
|
398
|
-
|
|
398
|
+
let writesCopy = writes;
|
|
399
|
+
if (writesCopy.length === 0) {
|
|
399
400
|
return;
|
|
400
401
|
}
|
|
402
|
+
// deduplicate writes to special channels, last write wins
|
|
403
|
+
if (writesCopy.every(([key]) => key in WRITES_IDX_MAP)) {
|
|
404
|
+
writesCopy = Array.from(new Map(writesCopy.map((w) => [w[0], w])).values());
|
|
405
|
+
}
|
|
401
406
|
// save writes
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
407
|
+
for (const [c, v] of writesCopy) {
|
|
408
|
+
if (c in WRITES_IDX_MAP) {
|
|
409
|
+
const idx = this.checkpointPendingWrites.findIndex((w) => w[0] === taskId && w[1] === c);
|
|
410
|
+
if (idx !== -1) {
|
|
411
|
+
this.checkpointPendingWrites[idx] = [taskId, c, v];
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
this.checkpointPendingWrites.push([taskId, c, v]);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
this.checkpointPendingWrites.push([taskId, c, v]);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
406
421
|
const putWritePromise = this.checkpointer?.putWrites({
|
|
407
422
|
...this.checkpointConfig,
|
|
408
423
|
configurable: {
|
|
@@ -410,12 +425,12 @@ export class PregelLoop {
|
|
|
410
425
|
checkpoint_ns: this.config.configurable?.checkpoint_ns ?? "",
|
|
411
426
|
checkpoint_id: this.checkpoint.id,
|
|
412
427
|
},
|
|
413
|
-
},
|
|
428
|
+
}, writesCopy, taskId);
|
|
414
429
|
if (putWritePromise !== undefined) {
|
|
415
430
|
this.checkpointerPromises.push(putWritePromise);
|
|
416
431
|
}
|
|
417
432
|
if (this.tasks) {
|
|
418
|
-
this._outputWrites(taskId,
|
|
433
|
+
this._outputWrites(taskId, writesCopy);
|
|
419
434
|
}
|
|
420
435
|
}
|
|
421
436
|
_outputWrites(taskId, writes, cached = false) {
|
|
@@ -453,7 +468,7 @@ export class PregelLoop {
|
|
|
453
468
|
if (![INPUT_DONE, INPUT_RESUMING].includes(this.input)) {
|
|
454
469
|
await this._first(inputKeys);
|
|
455
470
|
}
|
|
456
|
-
else if (Object.values(this.tasks).every((task) => task.writes.length > 0)) {
|
|
471
|
+
else if (Object.values(this.tasks).every((task) => task.writes.filter(([c]) => !(c in WRITES_IDX_MAP)).length > 0)) {
|
|
457
472
|
const writes = Object.values(this.tasks).flatMap((t) => t.writes);
|
|
458
473
|
// All tasks have finished
|
|
459
474
|
const managedValueWrites = _applyWrites(this.checkpoint, this.channels, Object.values(this.tasks), this.checkpointerGetNextVersion);
|
|
@@ -577,7 +592,7 @@ export class PregelLoop {
|
|
|
577
592
|
if (_isCommand(this.input)) {
|
|
578
593
|
const writes = {};
|
|
579
594
|
// group writes by task id
|
|
580
|
-
for (const [tid, key, value] of mapCommand(this.input)) {
|
|
595
|
+
for (const [tid, key, value] of mapCommand(this.input, this.checkpointPendingWrites)) {
|
|
581
596
|
if (writes[tid] === undefined) {
|
|
582
597
|
writes[tid] = [];
|
|
583
598
|
}
|
package/dist/pregel/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@langchain/langgraph",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.31",
|
|
4
4
|
"description": "LangGraph",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"author": "LangChain",
|
|
32
32
|
"license": "MIT",
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@langchain/langgraph-checkpoint": "~0.0.
|
|
34
|
+
"@langchain/langgraph-checkpoint": "~0.0.13",
|
|
35
35
|
"@langchain/langgraph-sdk": "~0.0.21",
|
|
36
36
|
"uuid": "^10.0.0",
|
|
37
37
|
"zod": "^3.23.8"
|