@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/algo.cjs
CHANGED
|
@@ -33,23 +33,49 @@ function shouldInterrupt(checkpoint, interruptNodes, tasks) {
|
|
|
33
33
|
return anyChannelUpdated && anyTriggeredNodeInInterruptNodes;
|
|
34
34
|
}
|
|
35
35
|
exports.shouldInterrupt = shouldInterrupt;
|
|
36
|
-
function _localRead(checkpoint, channels, task, select, fresh = false) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
36
|
+
function _localRead(step, checkpoint, channels, managed, task, select, fresh = false) {
|
|
37
|
+
let managedKeys = [];
|
|
38
|
+
let updated = new Set();
|
|
39
|
+
if (!Array.isArray(select)) {
|
|
40
|
+
for (const [c] of task.writes) {
|
|
41
|
+
if (c === select) {
|
|
42
|
+
updated = new Set([c]);
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
updated = updated || new Set();
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
managedKeys = select.filter((k) => managed.get(k));
|
|
50
|
+
select = select.filter((k) => !managed.get(k));
|
|
51
|
+
updated = new Set(select.filter((c) => task.writes.some(([key, _]) => key === c)));
|
|
52
|
+
}
|
|
53
|
+
let values;
|
|
54
|
+
if (fresh && updated.size > 0) {
|
|
55
|
+
const localChannels = Object.fromEntries(Object.entries(channels).filter(([k, _]) => updated.has(k)));
|
|
56
|
+
const newCheckpoint = (0, base_js_1.createCheckpoint)(checkpoint, localChannels, -1);
|
|
57
|
+
const newChannels = (0, base_js_1.emptyChannels)(localChannels, newCheckpoint);
|
|
42
58
|
_applyWrites((0, langgraph_checkpoint_1.copyCheckpoint)(newCheckpoint), newChannels, [task]);
|
|
43
|
-
|
|
59
|
+
values = (0, io_js_1.readChannels)({ ...channels, ...newChannels }, select);
|
|
44
60
|
}
|
|
45
61
|
else {
|
|
46
|
-
|
|
62
|
+
values = (0, io_js_1.readChannels)(channels, select);
|
|
47
63
|
}
|
|
64
|
+
if (managedKeys.length > 0) {
|
|
65
|
+
for (const k of managedKeys) {
|
|
66
|
+
const managedValue = managed.get(k);
|
|
67
|
+
if (managedValue) {
|
|
68
|
+
const resultOfManagedCall = managedValue.call(step);
|
|
69
|
+
values[k] = resultOfManagedCall;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return values;
|
|
48
74
|
}
|
|
49
75
|
exports._localRead = _localRead;
|
|
50
|
-
function _localWrite(
|
|
76
|
+
function _localWrite(step,
|
|
51
77
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
52
|
-
commit, processes, channels,
|
|
78
|
+
commit, processes, channels, managed,
|
|
53
79
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
54
80
|
writes) {
|
|
55
81
|
for (const [chan, value] of writes) {
|
|
@@ -60,8 +86,10 @@ writes) {
|
|
|
60
86
|
if (!(value.node in processes)) {
|
|
61
87
|
throw new errors_js_1.InvalidUpdateError(`Invalid node name ${value.node} in packet`);
|
|
62
88
|
}
|
|
89
|
+
// replace any runtime values with placeholders
|
|
90
|
+
managed.replaceRuntimeValues(step, value.args);
|
|
63
91
|
}
|
|
64
|
-
else if (!(chan in channels)) {
|
|
92
|
+
else if (!(chan in channels) && !managed.get(chan)) {
|
|
65
93
|
console.warn(`Skipping write for channel '${chan}' which has no readers`);
|
|
66
94
|
}
|
|
67
95
|
}
|
|
@@ -71,6 +99,8 @@ exports._localWrite = _localWrite;
|
|
|
71
99
|
function _applyWrites(checkpoint, channels, tasks,
|
|
72
100
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
73
101
|
getNextVersion) {
|
|
102
|
+
// Filter out non instances of BaseChannel
|
|
103
|
+
const onlyChannels = Object.fromEntries(Object.entries(channels).filter(([_, value]) => (0, base_js_1.isBaseChannel)(value)));
|
|
74
104
|
// Update seen versions
|
|
75
105
|
for (const task of tasks) {
|
|
76
106
|
if (checkpoint.versions_seen[task.name] === undefined) {
|
|
@@ -93,9 +123,9 @@ getNextVersion) {
|
|
|
93
123
|
.flatMap((task) => task.triggers)
|
|
94
124
|
.filter((chan) => !constants_js_1.RESERVED.includes(chan)));
|
|
95
125
|
for (const chan of channelsToConsume) {
|
|
96
|
-
if (
|
|
126
|
+
if (chan in onlyChannels && onlyChannels[chan].consume()) {
|
|
97
127
|
if (getNextVersion !== undefined) {
|
|
98
|
-
checkpoint.channel_versions[chan] = getNextVersion(maxVersion,
|
|
128
|
+
checkpoint.channel_versions[chan] = getNextVersion(maxVersion, onlyChannels[chan]);
|
|
99
129
|
}
|
|
100
130
|
}
|
|
101
131
|
}
|
|
@@ -105,6 +135,7 @@ getNextVersion) {
|
|
|
105
135
|
}
|
|
106
136
|
// Group writes by channel
|
|
107
137
|
const pendingWriteValuesByChannel = {};
|
|
138
|
+
const pendingWritesByManaged = {};
|
|
108
139
|
for (const task of tasks) {
|
|
109
140
|
for (const [chan, val] of task.writes) {
|
|
110
141
|
if (chan === constants_js_1.TASKS) {
|
|
@@ -113,7 +144,7 @@ getNextVersion) {
|
|
|
113
144
|
args: val.args,
|
|
114
145
|
});
|
|
115
146
|
}
|
|
116
|
-
else {
|
|
147
|
+
else if (chan in onlyChannels) {
|
|
117
148
|
if (chan in pendingWriteValuesByChannel) {
|
|
118
149
|
pendingWriteValuesByChannel[chan].push(val);
|
|
119
150
|
}
|
|
@@ -121,6 +152,14 @@ getNextVersion) {
|
|
|
121
152
|
pendingWriteValuesByChannel[chan] = [val];
|
|
122
153
|
}
|
|
123
154
|
}
|
|
155
|
+
else {
|
|
156
|
+
if (chan in pendingWritesByManaged) {
|
|
157
|
+
pendingWritesByManaged[chan].push(val);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
pendingWritesByManaged[chan] = [val];
|
|
161
|
+
}
|
|
162
|
+
}
|
|
124
163
|
}
|
|
125
164
|
}
|
|
126
165
|
// find the highest version of all channels
|
|
@@ -131,10 +170,10 @@ getNextVersion) {
|
|
|
131
170
|
const updatedChannels = new Set();
|
|
132
171
|
// Apply writes to channels
|
|
133
172
|
for (const [chan, vals] of Object.entries(pendingWriteValuesByChannel)) {
|
|
134
|
-
if (chan in
|
|
173
|
+
if (chan in onlyChannels) {
|
|
135
174
|
let updated;
|
|
136
175
|
try {
|
|
137
|
-
updated =
|
|
176
|
+
updated = onlyChannels[chan].update(vals);
|
|
138
177
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
139
178
|
}
|
|
140
179
|
catch (e) {
|
|
@@ -146,23 +185,25 @@ getNextVersion) {
|
|
|
146
185
|
}
|
|
147
186
|
}
|
|
148
187
|
if (updated && getNextVersion !== undefined) {
|
|
149
|
-
checkpoint.channel_versions[chan] = getNextVersion(maxVersion,
|
|
188
|
+
checkpoint.channel_versions[chan] = getNextVersion(maxVersion, onlyChannels[chan]);
|
|
150
189
|
}
|
|
151
190
|
updatedChannels.add(chan);
|
|
152
191
|
}
|
|
153
192
|
}
|
|
154
193
|
// Channels that weren't updated in this step are notified of a new step
|
|
155
|
-
for (const chan of Object.keys(
|
|
194
|
+
for (const chan of Object.keys(onlyChannels)) {
|
|
156
195
|
if (!updatedChannels.has(chan)) {
|
|
157
|
-
const updated =
|
|
196
|
+
const updated = onlyChannels[chan].update([]);
|
|
158
197
|
if (updated && getNextVersion !== undefined) {
|
|
159
|
-
checkpoint.channel_versions[chan] = getNextVersion(maxVersion,
|
|
198
|
+
checkpoint.channel_versions[chan] = getNextVersion(maxVersion, onlyChannels[chan]);
|
|
160
199
|
}
|
|
161
200
|
}
|
|
162
201
|
}
|
|
202
|
+
// Return managed values writes to be applied externally
|
|
203
|
+
return pendingWritesByManaged;
|
|
163
204
|
}
|
|
164
205
|
exports._applyWrites = _applyWrites;
|
|
165
|
-
function _prepareNextTasks(checkpoint, processes, channels, config, forExecution, extra) {
|
|
206
|
+
function _prepareNextTasks(checkpoint, processes, channels, managed, config, forExecution, extra) {
|
|
166
207
|
const parentNamespace = config.configurable?.checkpoint_ns ?? "";
|
|
167
208
|
const tasks = [];
|
|
168
209
|
const taskDescriptions = [];
|
|
@@ -192,6 +233,7 @@ function _prepareNextTasks(checkpoint, processes, channels, config, forExecution
|
|
|
192
233
|
const node = proc.getNode();
|
|
193
234
|
if (node !== undefined) {
|
|
194
235
|
const writes = [];
|
|
236
|
+
managed.replaceRuntimePlaceholders(step, packet.args);
|
|
195
237
|
tasks.push({
|
|
196
238
|
name: packet.node,
|
|
197
239
|
input: packet.args,
|
|
@@ -204,12 +246,13 @@ function _prepareNextTasks(checkpoint, processes, channels, config, forExecution
|
|
|
204
246
|
runName: packet.node,
|
|
205
247
|
callbacks: manager?.getChild(`graph:step:${step}`),
|
|
206
248
|
configurable: {
|
|
207
|
-
|
|
208
|
-
[constants_js_1.
|
|
249
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
250
|
+
[constants_js_1.CONFIG_KEY_SEND]: (writes_) => _localWrite(step, (items) => writes.push(...items), processes, channels, managed, writes_),
|
|
251
|
+
[constants_js_1.CONFIG_KEY_READ]: (select_, fresh_ = false) => _localRead(step, checkpoint, channels, managed, {
|
|
209
252
|
name: packet.node,
|
|
210
253
|
writes: writes,
|
|
211
254
|
triggers,
|
|
212
|
-
}),
|
|
255
|
+
}, select_, fresh_),
|
|
213
256
|
},
|
|
214
257
|
}),
|
|
215
258
|
id: taskId,
|
|
@@ -243,7 +286,7 @@ function _prepareNextTasks(checkpoint, processes, channels, config, forExecution
|
|
|
243
286
|
.sort();
|
|
244
287
|
// If any of the channels read by this process were updated
|
|
245
288
|
if (triggers.length > 0) {
|
|
246
|
-
const val = _procInput(proc, channels, forExecution);
|
|
289
|
+
const val = _procInput(step, proc, managed, channels, forExecution);
|
|
247
290
|
if (val === undefined) {
|
|
248
291
|
continue;
|
|
249
292
|
}
|
|
@@ -273,12 +316,13 @@ function _prepareNextTasks(checkpoint, processes, channels, config, forExecution
|
|
|
273
316
|
runName: name,
|
|
274
317
|
callbacks: manager?.getChild(`graph:step:${step}`),
|
|
275
318
|
configurable: {
|
|
276
|
-
|
|
277
|
-
[constants_js_1.
|
|
319
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
320
|
+
[constants_js_1.CONFIG_KEY_SEND]: (writes_) => _localWrite(step, (items) => writes.push(...items), processes, channels, managed, writes_),
|
|
321
|
+
[constants_js_1.CONFIG_KEY_READ]: (select_, fresh_ = false) => _localRead(step, checkpoint, channels, managed, {
|
|
278
322
|
name,
|
|
279
323
|
writes: writes,
|
|
280
324
|
triggers,
|
|
281
|
-
}),
|
|
325
|
+
}, select_, fresh_),
|
|
282
326
|
[constants_js_1.CONFIG_KEY_CHECKPOINTER]: checkpointer,
|
|
283
327
|
[constants_js_1.CONFIG_KEY_RESUMING]: isResuming,
|
|
284
328
|
checkpoint_id: checkpoint.id,
|
|
@@ -298,12 +342,46 @@ function _prepareNextTasks(checkpoint, processes, channels, config, forExecution
|
|
|
298
342
|
return forExecution ? tasks : taskDescriptions;
|
|
299
343
|
}
|
|
300
344
|
exports._prepareNextTasks = _prepareNextTasks;
|
|
301
|
-
function _procInput(proc, channels, forExecution) {
|
|
345
|
+
function _procInput(step, proc, managed, channels, forExecution) {
|
|
302
346
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
303
347
|
let val;
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
348
|
+
if (typeof proc.channels === "object" && !Array.isArray(proc.channels)) {
|
|
349
|
+
val = {};
|
|
350
|
+
for (const [k, chan] of Object.entries(proc.channels)) {
|
|
351
|
+
if (proc.triggers.includes(chan)) {
|
|
352
|
+
try {
|
|
353
|
+
val[k] = (0, io_js_1.readChannel)(channels, chan, false);
|
|
354
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
355
|
+
}
|
|
356
|
+
catch (e) {
|
|
357
|
+
if (e.name === errors_js_1.EmptyChannelError.unminifiable_name) {
|
|
358
|
+
return undefined;
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
throw e;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
else if (chan in channels) {
|
|
366
|
+
try {
|
|
367
|
+
val[k] = (0, io_js_1.readChannel)(channels, chan, true);
|
|
368
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
369
|
+
}
|
|
370
|
+
catch (e) {
|
|
371
|
+
if (e.name === errors_js_1.EmptyChannelError.unminifiable_name) {
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
throw e;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
val[k] = managed.get(k)?.call(step);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
else if (Array.isArray(proc.channels)) {
|
|
307
385
|
let successfulRead = false;
|
|
308
386
|
for (const chan of proc.channels) {
|
|
309
387
|
try {
|
|
@@ -322,24 +400,7 @@ function _procInput(proc, channels, forExecution) {
|
|
|
322
400
|
}
|
|
323
401
|
}
|
|
324
402
|
if (!successfulRead) {
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
else if (typeof proc.channels === "object") {
|
|
329
|
-
val = {};
|
|
330
|
-
for (const [k, chan] of Object.entries(proc.channels)) {
|
|
331
|
-
try {
|
|
332
|
-
val[k] = (0, io_js_1.readChannel)(channels, chan, !proc.triggers.includes(chan));
|
|
333
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
334
|
-
}
|
|
335
|
-
catch (e) {
|
|
336
|
-
if (e.name === errors_js_1.EmptyChannelError.unminifiable_name) {
|
|
337
|
-
continue;
|
|
338
|
-
}
|
|
339
|
-
else {
|
|
340
|
-
throw e;
|
|
341
|
-
}
|
|
342
|
-
}
|
|
403
|
+
return undefined;
|
|
343
404
|
}
|
|
344
405
|
}
|
|
345
406
|
else {
|
package/dist/pregel/algo.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { RunnableConfig } from "@langchain/core/runnables";
|
|
2
2
|
import { CallbackManagerForChainRun } from "@langchain/core/callbacks/manager";
|
|
3
|
-
import { All, BaseCheckpointSaver, Checkpoint, ReadonlyCheckpoint, type PendingWrite } from "@langchain/langgraph-checkpoint";
|
|
3
|
+
import { All, BaseCheckpointSaver, Checkpoint, ReadonlyCheckpoint, type PendingWrite, type PendingWriteValue } from "@langchain/langgraph-checkpoint";
|
|
4
4
|
import { BaseChannel } from "../channels/base.js";
|
|
5
5
|
import { PregelNode } from "./read.js";
|
|
6
6
|
import { PregelExecutableTask, PregelTaskDescription } from "./types.js";
|
|
7
|
+
import { ManagedValueMapping } from "../managed/base.js";
|
|
7
8
|
/**
|
|
8
9
|
* Construct a type with a set of properties K of type T
|
|
9
10
|
*/
|
|
@@ -17,14 +18,14 @@ export type WritesProtocol<C = string> = {
|
|
|
17
18
|
};
|
|
18
19
|
export declare const increment: (current?: number) => number;
|
|
19
20
|
export declare function shouldInterrupt<N extends PropertyKey, C extends PropertyKey>(checkpoint: Checkpoint, interruptNodes: All | N[], tasks: PregelExecutableTask<N, C>[]): boolean;
|
|
20
|
-
export declare function _localRead<Cc extends
|
|
21
|
-
export declare function _localWrite(commit: (writes: [string, any][]) =>
|
|
22
|
-
export declare function _applyWrites<Cc extends Record<string, BaseChannel>>(checkpoint: Checkpoint, channels: Cc, tasks: WritesProtocol<keyof Cc>[], getNextVersion?: (version: any, channel: BaseChannel) => any):
|
|
21
|
+
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;
|
|
22
|
+
export declare function _localWrite(step: number, commit: (writes: [string, any][]) => any, processes: Record<string, PregelNode>, channels: Record<string, BaseChannel>, managed: ManagedValueMapping, writes: [string, any][]): void;
|
|
23
|
+
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[]>;
|
|
23
24
|
export type NextTaskExtraFields = {
|
|
24
25
|
step: number;
|
|
25
26
|
isResuming?: boolean;
|
|
26
27
|
checkpointer?: BaseCheckpointSaver;
|
|
27
28
|
manager?: CallbackManagerForChainRun;
|
|
28
29
|
};
|
|
29
|
-
export declare function _prepareNextTasks<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel>>(checkpoint: ReadonlyCheckpoint, processes: Nn, channels: Cc, config: RunnableConfig, forExecution: false, extra: NextTaskExtraFields): PregelTaskDescription[];
|
|
30
|
-
export declare function _prepareNextTasks<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel>>(checkpoint: ReadonlyCheckpoint, processes: Nn, channels: Cc, config: RunnableConfig, forExecution: true, extra: NextTaskExtraFields): PregelExecutableTask<keyof Nn, keyof Cc>[];
|
|
30
|
+
export declare function _prepareNextTasks<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel>>(checkpoint: ReadonlyCheckpoint, processes: Nn, channels: Cc, managed: ManagedValueMapping, config: RunnableConfig, forExecution: false, extra: NextTaskExtraFields): PregelTaskDescription[];
|
|
31
|
+
export declare function _prepareNextTasks<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel>>(checkpoint: ReadonlyCheckpoint, processes: Nn, channels: Cc, managed: ManagedValueMapping, config: RunnableConfig, forExecution: true, extra: NextTaskExtraFields): PregelExecutableTask<keyof Nn, keyof Cc>[];
|
package/dist/pregel/algo.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable no-param-reassign */
|
|
2
2
|
import { mergeConfigs, patchConfig, } from "@langchain/core/runnables";
|
|
3
3
|
import { copyCheckpoint, uuid5, maxChannelVersion, } from "@langchain/langgraph-checkpoint";
|
|
4
|
-
import { createCheckpoint, emptyChannels, } from "../channels/base.js";
|
|
4
|
+
import { createCheckpoint, emptyChannels, isBaseChannel, } from "../channels/base.js";
|
|
5
5
|
import { readChannel, readChannels } from "./io.js";
|
|
6
6
|
import { _isSend, _isSendInterface, CHECKPOINT_NAMESPACE_SEPARATOR, CONFIG_KEY_CHECKPOINTER, CONFIG_KEY_READ, CONFIG_KEY_RESUMING, CONFIG_KEY_SEND, INTERRUPT, RESERVED, TAG_HIDDEN, TASKS, } from "../constants.js";
|
|
7
7
|
import { EmptyChannelError, InvalidUpdateError } from "../errors.js";
|
|
@@ -28,22 +28,48 @@ export function shouldInterrupt(checkpoint, interruptNodes, tasks) {
|
|
|
28
28
|
: interruptNodes.includes(task.name));
|
|
29
29
|
return anyChannelUpdated && anyTriggeredNodeInInterruptNodes;
|
|
30
30
|
}
|
|
31
|
-
export function _localRead(checkpoint, channels, task, select, fresh = false) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
31
|
+
export function _localRead(step, checkpoint, channels, managed, task, select, fresh = false) {
|
|
32
|
+
let managedKeys = [];
|
|
33
|
+
let updated = new Set();
|
|
34
|
+
if (!Array.isArray(select)) {
|
|
35
|
+
for (const [c] of task.writes) {
|
|
36
|
+
if (c === select) {
|
|
37
|
+
updated = new Set([c]);
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
updated = updated || new Set();
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
managedKeys = select.filter((k) => managed.get(k));
|
|
45
|
+
select = select.filter((k) => !managed.get(k));
|
|
46
|
+
updated = new Set(select.filter((c) => task.writes.some(([key, _]) => key === c)));
|
|
47
|
+
}
|
|
48
|
+
let values;
|
|
49
|
+
if (fresh && updated.size > 0) {
|
|
50
|
+
const localChannels = Object.fromEntries(Object.entries(channels).filter(([k, _]) => updated.has(k)));
|
|
51
|
+
const newCheckpoint = createCheckpoint(checkpoint, localChannels, -1);
|
|
52
|
+
const newChannels = emptyChannels(localChannels, newCheckpoint);
|
|
37
53
|
_applyWrites(copyCheckpoint(newCheckpoint), newChannels, [task]);
|
|
38
|
-
|
|
54
|
+
values = readChannels({ ...channels, ...newChannels }, select);
|
|
39
55
|
}
|
|
40
56
|
else {
|
|
41
|
-
|
|
57
|
+
values = readChannels(channels, select);
|
|
42
58
|
}
|
|
59
|
+
if (managedKeys.length > 0) {
|
|
60
|
+
for (const k of managedKeys) {
|
|
61
|
+
const managedValue = managed.get(k);
|
|
62
|
+
if (managedValue) {
|
|
63
|
+
const resultOfManagedCall = managedValue.call(step);
|
|
64
|
+
values[k] = resultOfManagedCall;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return values;
|
|
43
69
|
}
|
|
44
|
-
export function _localWrite(
|
|
70
|
+
export function _localWrite(step,
|
|
45
71
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
46
|
-
commit, processes, channels,
|
|
72
|
+
commit, processes, channels, managed,
|
|
47
73
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
48
74
|
writes) {
|
|
49
75
|
for (const [chan, value] of writes) {
|
|
@@ -54,8 +80,10 @@ writes) {
|
|
|
54
80
|
if (!(value.node in processes)) {
|
|
55
81
|
throw new InvalidUpdateError(`Invalid node name ${value.node} in packet`);
|
|
56
82
|
}
|
|
83
|
+
// replace any runtime values with placeholders
|
|
84
|
+
managed.replaceRuntimeValues(step, value.args);
|
|
57
85
|
}
|
|
58
|
-
else if (!(chan in channels)) {
|
|
86
|
+
else if (!(chan in channels) && !managed.get(chan)) {
|
|
59
87
|
console.warn(`Skipping write for channel '${chan}' which has no readers`);
|
|
60
88
|
}
|
|
61
89
|
}
|
|
@@ -64,6 +92,8 @@ writes) {
|
|
|
64
92
|
export function _applyWrites(checkpoint, channels, tasks,
|
|
65
93
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
66
94
|
getNextVersion) {
|
|
95
|
+
// Filter out non instances of BaseChannel
|
|
96
|
+
const onlyChannels = Object.fromEntries(Object.entries(channels).filter(([_, value]) => isBaseChannel(value)));
|
|
67
97
|
// Update seen versions
|
|
68
98
|
for (const task of tasks) {
|
|
69
99
|
if (checkpoint.versions_seen[task.name] === undefined) {
|
|
@@ -86,9 +116,9 @@ getNextVersion) {
|
|
|
86
116
|
.flatMap((task) => task.triggers)
|
|
87
117
|
.filter((chan) => !RESERVED.includes(chan)));
|
|
88
118
|
for (const chan of channelsToConsume) {
|
|
89
|
-
if (
|
|
119
|
+
if (chan in onlyChannels && onlyChannels[chan].consume()) {
|
|
90
120
|
if (getNextVersion !== undefined) {
|
|
91
|
-
checkpoint.channel_versions[chan] = getNextVersion(maxVersion,
|
|
121
|
+
checkpoint.channel_versions[chan] = getNextVersion(maxVersion, onlyChannels[chan]);
|
|
92
122
|
}
|
|
93
123
|
}
|
|
94
124
|
}
|
|
@@ -98,6 +128,7 @@ getNextVersion) {
|
|
|
98
128
|
}
|
|
99
129
|
// Group writes by channel
|
|
100
130
|
const pendingWriteValuesByChannel = {};
|
|
131
|
+
const pendingWritesByManaged = {};
|
|
101
132
|
for (const task of tasks) {
|
|
102
133
|
for (const [chan, val] of task.writes) {
|
|
103
134
|
if (chan === TASKS) {
|
|
@@ -106,7 +137,7 @@ getNextVersion) {
|
|
|
106
137
|
args: val.args,
|
|
107
138
|
});
|
|
108
139
|
}
|
|
109
|
-
else {
|
|
140
|
+
else if (chan in onlyChannels) {
|
|
110
141
|
if (chan in pendingWriteValuesByChannel) {
|
|
111
142
|
pendingWriteValuesByChannel[chan].push(val);
|
|
112
143
|
}
|
|
@@ -114,6 +145,14 @@ getNextVersion) {
|
|
|
114
145
|
pendingWriteValuesByChannel[chan] = [val];
|
|
115
146
|
}
|
|
116
147
|
}
|
|
148
|
+
else {
|
|
149
|
+
if (chan in pendingWritesByManaged) {
|
|
150
|
+
pendingWritesByManaged[chan].push(val);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
pendingWritesByManaged[chan] = [val];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
117
156
|
}
|
|
118
157
|
}
|
|
119
158
|
// find the highest version of all channels
|
|
@@ -124,10 +163,10 @@ getNextVersion) {
|
|
|
124
163
|
const updatedChannels = new Set();
|
|
125
164
|
// Apply writes to channels
|
|
126
165
|
for (const [chan, vals] of Object.entries(pendingWriteValuesByChannel)) {
|
|
127
|
-
if (chan in
|
|
166
|
+
if (chan in onlyChannels) {
|
|
128
167
|
let updated;
|
|
129
168
|
try {
|
|
130
|
-
updated =
|
|
169
|
+
updated = onlyChannels[chan].update(vals);
|
|
131
170
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
132
171
|
}
|
|
133
172
|
catch (e) {
|
|
@@ -139,22 +178,24 @@ getNextVersion) {
|
|
|
139
178
|
}
|
|
140
179
|
}
|
|
141
180
|
if (updated && getNextVersion !== undefined) {
|
|
142
|
-
checkpoint.channel_versions[chan] = getNextVersion(maxVersion,
|
|
181
|
+
checkpoint.channel_versions[chan] = getNextVersion(maxVersion, onlyChannels[chan]);
|
|
143
182
|
}
|
|
144
183
|
updatedChannels.add(chan);
|
|
145
184
|
}
|
|
146
185
|
}
|
|
147
186
|
// Channels that weren't updated in this step are notified of a new step
|
|
148
|
-
for (const chan of Object.keys(
|
|
187
|
+
for (const chan of Object.keys(onlyChannels)) {
|
|
149
188
|
if (!updatedChannels.has(chan)) {
|
|
150
|
-
const updated =
|
|
189
|
+
const updated = onlyChannels[chan].update([]);
|
|
151
190
|
if (updated && getNextVersion !== undefined) {
|
|
152
|
-
checkpoint.channel_versions[chan] = getNextVersion(maxVersion,
|
|
191
|
+
checkpoint.channel_versions[chan] = getNextVersion(maxVersion, onlyChannels[chan]);
|
|
153
192
|
}
|
|
154
193
|
}
|
|
155
194
|
}
|
|
195
|
+
// Return managed values writes to be applied externally
|
|
196
|
+
return pendingWritesByManaged;
|
|
156
197
|
}
|
|
157
|
-
export function _prepareNextTasks(checkpoint, processes, channels, config, forExecution, extra) {
|
|
198
|
+
export function _prepareNextTasks(checkpoint, processes, channels, managed, config, forExecution, extra) {
|
|
158
199
|
const parentNamespace = config.configurable?.checkpoint_ns ?? "";
|
|
159
200
|
const tasks = [];
|
|
160
201
|
const taskDescriptions = [];
|
|
@@ -184,6 +225,7 @@ export function _prepareNextTasks(checkpoint, processes, channels, config, forEx
|
|
|
184
225
|
const node = proc.getNode();
|
|
185
226
|
if (node !== undefined) {
|
|
186
227
|
const writes = [];
|
|
228
|
+
managed.replaceRuntimePlaceholders(step, packet.args);
|
|
187
229
|
tasks.push({
|
|
188
230
|
name: packet.node,
|
|
189
231
|
input: packet.args,
|
|
@@ -196,12 +238,13 @@ export function _prepareNextTasks(checkpoint, processes, channels, config, forEx
|
|
|
196
238
|
runName: packet.node,
|
|
197
239
|
callbacks: manager?.getChild(`graph:step:${step}`),
|
|
198
240
|
configurable: {
|
|
199
|
-
|
|
200
|
-
[
|
|
241
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
242
|
+
[CONFIG_KEY_SEND]: (writes_) => _localWrite(step, (items) => writes.push(...items), processes, channels, managed, writes_),
|
|
243
|
+
[CONFIG_KEY_READ]: (select_, fresh_ = false) => _localRead(step, checkpoint, channels, managed, {
|
|
201
244
|
name: packet.node,
|
|
202
245
|
writes: writes,
|
|
203
246
|
triggers,
|
|
204
|
-
}),
|
|
247
|
+
}, select_, fresh_),
|
|
205
248
|
},
|
|
206
249
|
}),
|
|
207
250
|
id: taskId,
|
|
@@ -235,7 +278,7 @@ export function _prepareNextTasks(checkpoint, processes, channels, config, forEx
|
|
|
235
278
|
.sort();
|
|
236
279
|
// If any of the channels read by this process were updated
|
|
237
280
|
if (triggers.length > 0) {
|
|
238
|
-
const val = _procInput(proc, channels, forExecution);
|
|
281
|
+
const val = _procInput(step, proc, managed, channels, forExecution);
|
|
239
282
|
if (val === undefined) {
|
|
240
283
|
continue;
|
|
241
284
|
}
|
|
@@ -265,12 +308,13 @@ export function _prepareNextTasks(checkpoint, processes, channels, config, forEx
|
|
|
265
308
|
runName: name,
|
|
266
309
|
callbacks: manager?.getChild(`graph:step:${step}`),
|
|
267
310
|
configurable: {
|
|
268
|
-
|
|
269
|
-
[
|
|
311
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
312
|
+
[CONFIG_KEY_SEND]: (writes_) => _localWrite(step, (items) => writes.push(...items), processes, channels, managed, writes_),
|
|
313
|
+
[CONFIG_KEY_READ]: (select_, fresh_ = false) => _localRead(step, checkpoint, channels, managed, {
|
|
270
314
|
name,
|
|
271
315
|
writes: writes,
|
|
272
316
|
triggers,
|
|
273
|
-
}),
|
|
317
|
+
}, select_, fresh_),
|
|
274
318
|
[CONFIG_KEY_CHECKPOINTER]: checkpointer,
|
|
275
319
|
[CONFIG_KEY_RESUMING]: isResuming,
|
|
276
320
|
checkpoint_id: checkpoint.id,
|
|
@@ -289,12 +333,46 @@ export function _prepareNextTasks(checkpoint, processes, channels, config, forEx
|
|
|
289
333
|
}
|
|
290
334
|
return forExecution ? tasks : taskDescriptions;
|
|
291
335
|
}
|
|
292
|
-
function _procInput(proc, channels, forExecution) {
|
|
336
|
+
function _procInput(step, proc, managed, channels, forExecution) {
|
|
293
337
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
294
338
|
let val;
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
339
|
+
if (typeof proc.channels === "object" && !Array.isArray(proc.channels)) {
|
|
340
|
+
val = {};
|
|
341
|
+
for (const [k, chan] of Object.entries(proc.channels)) {
|
|
342
|
+
if (proc.triggers.includes(chan)) {
|
|
343
|
+
try {
|
|
344
|
+
val[k] = readChannel(channels, chan, false);
|
|
345
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
346
|
+
}
|
|
347
|
+
catch (e) {
|
|
348
|
+
if (e.name === EmptyChannelError.unminifiable_name) {
|
|
349
|
+
return undefined;
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
throw e;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
else if (chan in channels) {
|
|
357
|
+
try {
|
|
358
|
+
val[k] = readChannel(channels, chan, true);
|
|
359
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
360
|
+
}
|
|
361
|
+
catch (e) {
|
|
362
|
+
if (e.name === EmptyChannelError.unminifiable_name) {
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
throw e;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
val[k] = managed.get(k)?.call(step);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
else if (Array.isArray(proc.channels)) {
|
|
298
376
|
let successfulRead = false;
|
|
299
377
|
for (const chan of proc.channels) {
|
|
300
378
|
try {
|
|
@@ -313,24 +391,7 @@ function _procInput(proc, channels, forExecution) {
|
|
|
313
391
|
}
|
|
314
392
|
}
|
|
315
393
|
if (!successfulRead) {
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
else if (typeof proc.channels === "object") {
|
|
320
|
-
val = {};
|
|
321
|
-
for (const [k, chan] of Object.entries(proc.channels)) {
|
|
322
|
-
try {
|
|
323
|
-
val[k] = readChannel(channels, chan, !proc.triggers.includes(chan));
|
|
324
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
325
|
-
}
|
|
326
|
-
catch (e) {
|
|
327
|
-
if (e.name === EmptyChannelError.unminifiable_name) {
|
|
328
|
-
continue;
|
|
329
|
-
}
|
|
330
|
-
else {
|
|
331
|
-
throw e;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
394
|
+
return undefined;
|
|
334
395
|
}
|
|
335
396
|
}
|
|
336
397
|
else {
|