@langchain/langgraph 0.0.34 → 0.1.0-rc.1
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/LICENSE +1 -1
- package/README.md +11 -15
- package/dist/channels/any_value.cjs +3 -1
- package/dist/channels/any_value.d.ts +1 -1
- package/dist/channels/any_value.js +3 -1
- package/dist/channels/base.cjs +28 -15
- package/dist/channels/base.d.ts +14 -4
- package/dist/channels/base.js +26 -13
- package/dist/channels/binop.cjs +2 -1
- package/dist/channels/binop.d.ts +1 -1
- package/dist/channels/binop.js +2 -1
- package/dist/channels/dynamic_barrier_value.cjs +30 -18
- package/dist/channels/dynamic_barrier_value.d.ts +2 -1
- package/dist/channels/dynamic_barrier_value.js +30 -18
- package/dist/channels/ephemeral_value.cjs +3 -1
- package/dist/channels/ephemeral_value.d.ts +1 -1
- package/dist/channels/ephemeral_value.js +3 -1
- package/dist/channels/last_value.cjs +3 -2
- package/dist/channels/last_value.d.ts +1 -1
- package/dist/channels/last_value.js +3 -2
- package/dist/channels/named_barrier_value.cjs +14 -6
- package/dist/channels/named_barrier_value.d.ts +2 -1
- package/dist/channels/named_barrier_value.js +15 -7
- package/dist/channels/topic.cjs +10 -11
- package/dist/channels/topic.d.ts +1 -1
- package/dist/channels/topic.js +10 -11
- package/dist/checkpoint/sqlite.cjs +14 -170
- package/dist/checkpoint/sqlite.d.ts +1 -14
- package/dist/checkpoint/sqlite.js +1 -166
- package/dist/constants.cjs +17 -1
- package/dist/constants.d.ts +7 -0
- package/dist/constants.js +16 -0
- package/dist/errors.cjs +21 -1
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +18 -0
- package/dist/graph/graph.cjs +6 -3
- package/dist/graph/graph.d.ts +7 -3
- package/dist/graph/graph.js +7 -4
- package/dist/graph/index.d.ts +1 -1
- package/dist/graph/state.cjs +9 -8
- package/dist/graph/state.d.ts +1 -1
- package/dist/graph/state.js +9 -8
- package/dist/prebuilt/react_agent_executor.d.ts +1 -1
- package/dist/pregel/algo.cjs +391 -0
- package/dist/pregel/algo.d.ts +35 -0
- package/dist/pregel/algo.js +381 -0
- package/dist/pregel/debug.cjs +155 -9
- package/dist/pregel/debug.d.ts +40 -2
- package/dist/pregel/debug.js +147 -7
- package/dist/pregel/index.cjs +286 -512
- package/dist/pregel/index.d.ts +66 -65
- package/dist/pregel/index.js +285 -506
- package/dist/pregel/io.cjs +2 -2
- package/dist/pregel/io.d.ts +5 -4
- package/dist/pregel/io.js +2 -2
- package/dist/pregel/loop.cjs +432 -0
- package/dist/pregel/loop.d.ts +83 -0
- package/dist/pregel/loop.js +425 -0
- package/dist/pregel/types.d.ts +56 -4
- package/dist/pregel/utils.cjs +48 -0
- package/dist/pregel/utils.d.ts +10 -0
- package/dist/pregel/utils.js +41 -0
- package/dist/pregel/write.cjs +3 -1
- package/dist/pregel/write.js +3 -1
- package/dist/utils.cjs +21 -1
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +18 -0
- package/dist/web.cjs +6 -7
- package/dist/web.d.ts +2 -4
- package/dist/web.js +1 -2
- package/package.json +6 -12
- package/dist/checkpoint/base.cjs +0 -66
- package/dist/checkpoint/base.d.ts +0 -73
- package/dist/checkpoint/base.js +0 -57
- package/dist/checkpoint/id.cjs +0 -8
- package/dist/checkpoint/id.d.ts +0 -1
- package/dist/checkpoint/id.js +0 -4
- package/dist/checkpoint/index.cjs +0 -9
- package/dist/checkpoint/index.d.ts +0 -2
- package/dist/checkpoint/index.js +0 -2
- package/dist/checkpoint/memory.cjs +0 -82
- package/dist/checkpoint/memory.d.ts +0 -10
- package/dist/checkpoint/memory.js +0 -78
- package/dist/serde/base.cjs +0 -8
- package/dist/serde/base.d.ts +0 -12
- package/dist/serde/base.js +0 -5
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/* eslint-disable no-param-reassign */
|
|
2
|
+
import { mergeConfigs, patchConfig, } from "@langchain/core/runnables";
|
|
3
|
+
import { copyCheckpoint, uuid5, } from "@langchain/langgraph-checkpoint";
|
|
4
|
+
import { createCheckpoint, emptyChannels, } from "../channels/base.js";
|
|
5
|
+
import { readChannel, readChannels } from "./io.js";
|
|
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
|
+
import { EmptyChannelError, InvalidUpdateError } from "../errors.js";
|
|
8
|
+
import { _getIdMetadata, getNullChannelVersion } from "./utils.js";
|
|
9
|
+
export const increment = (current) => {
|
|
10
|
+
return current !== undefined ? current + 1 : 1;
|
|
11
|
+
};
|
|
12
|
+
export async function* executeTasks(tasks, stepTimeout, signal
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
) {
|
|
15
|
+
if (stepTimeout && signal) {
|
|
16
|
+
if ("any" in AbortSignal) {
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
signal = AbortSignal.any([
|
|
19
|
+
signal,
|
|
20
|
+
AbortSignal.timeout(stepTimeout),
|
|
21
|
+
]);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else if (stepTimeout) {
|
|
25
|
+
signal = AbortSignal.timeout(stepTimeout);
|
|
26
|
+
}
|
|
27
|
+
// Abort if signal is aborted
|
|
28
|
+
signal?.throwIfAborted();
|
|
29
|
+
// Start all tasks
|
|
30
|
+
const executingTasks = Object.fromEntries(Object.entries(tasks).map(([taskId, task]) => {
|
|
31
|
+
return [taskId, task()];
|
|
32
|
+
}));
|
|
33
|
+
let listener;
|
|
34
|
+
const signalPromise = new Promise((_resolve, reject) => {
|
|
35
|
+
listener = () => reject(new Error("Abort"));
|
|
36
|
+
signal?.addEventListener("abort", listener);
|
|
37
|
+
}).finally(() => signal?.removeEventListener("abort", listener));
|
|
38
|
+
while (Object.keys(executingTasks).length > 0) {
|
|
39
|
+
const { task, error } = await Promise.race([
|
|
40
|
+
...Object.values(executingTasks),
|
|
41
|
+
signalPromise,
|
|
42
|
+
]);
|
|
43
|
+
if (error !== undefined) {
|
|
44
|
+
// TODO: don't stop others if exception is interrupt
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
yield task;
|
|
48
|
+
delete executingTasks[task.id];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export function shouldInterrupt(checkpoint, interruptNodes, tasks) {
|
|
52
|
+
const versionValues = Object.values(checkpoint.channel_versions);
|
|
53
|
+
const versionType = versionValues.length > 0 ? typeof versionValues[0] : undefined;
|
|
54
|
+
let nullVersion;
|
|
55
|
+
if (versionType === "number") {
|
|
56
|
+
nullVersion = 0;
|
|
57
|
+
}
|
|
58
|
+
else if (versionType === "string") {
|
|
59
|
+
nullVersion = "";
|
|
60
|
+
}
|
|
61
|
+
const seen = checkpoint.versions_seen[INTERRUPT] ?? {};
|
|
62
|
+
const anyChannelUpdated = Object.entries(checkpoint.channel_versions).some(([chan, version]) => {
|
|
63
|
+
return version > (seen[chan] ?? nullVersion);
|
|
64
|
+
});
|
|
65
|
+
const anyTriggeredNodeInInterruptNodes = tasks.some((task) => interruptNodes === "*"
|
|
66
|
+
? !task.config?.tags?.includes(TAG_HIDDEN)
|
|
67
|
+
: interruptNodes.includes(task.name));
|
|
68
|
+
return anyChannelUpdated && anyTriggeredNodeInInterruptNodes;
|
|
69
|
+
}
|
|
70
|
+
export function _localRead(checkpoint, channels, task, select, fresh = false) {
|
|
71
|
+
if (fresh) {
|
|
72
|
+
const newCheckpoint = createCheckpoint(checkpoint, channels, -1);
|
|
73
|
+
// create a new copy of channels
|
|
74
|
+
const newChannels = emptyChannels(channels, newCheckpoint);
|
|
75
|
+
// Note: _applyWrites contains side effects
|
|
76
|
+
_applyWrites(copyCheckpoint(newCheckpoint), newChannels, [task]);
|
|
77
|
+
return readChannels(newChannels, select);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
return readChannels(channels, select);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
export function _localWrite(
|
|
84
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
85
|
+
commit, processes, channels,
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
87
|
+
writes) {
|
|
88
|
+
for (const [chan, value] of writes) {
|
|
89
|
+
if (chan === TASKS) {
|
|
90
|
+
if (!_isSend(value)) {
|
|
91
|
+
throw new InvalidUpdateError(`Invalid packet type, expected SendProtocol, got ${JSON.stringify(value)}`);
|
|
92
|
+
}
|
|
93
|
+
if (!(value.node in processes)) {
|
|
94
|
+
throw new InvalidUpdateError(`Invalid node name ${value.node} in packet`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else if (!(chan in channels)) {
|
|
98
|
+
console.warn(`Skipping write for channel '${chan}' which has no readers`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
commit(writes);
|
|
102
|
+
}
|
|
103
|
+
export function _applyWrites(checkpoint, channels, tasks,
|
|
104
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
105
|
+
getNextVersion) {
|
|
106
|
+
// Update seen versions
|
|
107
|
+
for (const task of tasks) {
|
|
108
|
+
if (checkpoint.versions_seen[task.name] === undefined) {
|
|
109
|
+
checkpoint.versions_seen[task.name] = {};
|
|
110
|
+
}
|
|
111
|
+
for (const chan of task.triggers) {
|
|
112
|
+
if (chan in checkpoint.channel_versions) {
|
|
113
|
+
checkpoint.versions_seen[task.name][chan] =
|
|
114
|
+
checkpoint.channel_versions[chan];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Find the highest version of all channels
|
|
119
|
+
let maxVersion;
|
|
120
|
+
if (Object.keys(checkpoint.channel_versions).length > 0) {
|
|
121
|
+
maxVersion = Math.max(...Object.values(checkpoint.channel_versions));
|
|
122
|
+
}
|
|
123
|
+
// Consume all channels that were read
|
|
124
|
+
const channelsToConsume = new Set(tasks
|
|
125
|
+
.flatMap((task) => task.triggers)
|
|
126
|
+
.filter((chan) => !RESERVED.includes(chan)));
|
|
127
|
+
for (const chan of channelsToConsume) {
|
|
128
|
+
if (channels[chan].consume()) {
|
|
129
|
+
if (getNextVersion !== undefined) {
|
|
130
|
+
checkpoint.channel_versions[chan] = getNextVersion(maxVersion, channels[chan]);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Clear pending sends
|
|
135
|
+
if (checkpoint.pending_sends) {
|
|
136
|
+
checkpoint.pending_sends = [];
|
|
137
|
+
}
|
|
138
|
+
// Group writes by channel
|
|
139
|
+
const pendingWriteValuesByChannel = {};
|
|
140
|
+
for (const task of tasks) {
|
|
141
|
+
for (const [chan, val] of task.writes) {
|
|
142
|
+
if (chan === TASKS) {
|
|
143
|
+
checkpoint.pending_sends.push({
|
|
144
|
+
node: val.node,
|
|
145
|
+
args: val.args,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
if (chan in pendingWriteValuesByChannel) {
|
|
150
|
+
pendingWriteValuesByChannel[chan].push(val);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
pendingWriteValuesByChannel[chan] = [val];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// find the highest version of all channels
|
|
159
|
+
maxVersion = undefined;
|
|
160
|
+
if (Object.keys(checkpoint.channel_versions).length > 0) {
|
|
161
|
+
maxVersion = Math.max(...Object.values(checkpoint.channel_versions));
|
|
162
|
+
}
|
|
163
|
+
const updatedChannels = new Set();
|
|
164
|
+
// Apply writes to channels
|
|
165
|
+
for (const [chan, vals] of Object.entries(pendingWriteValuesByChannel)) {
|
|
166
|
+
if (chan in channels) {
|
|
167
|
+
let updated;
|
|
168
|
+
try {
|
|
169
|
+
updated = channels[chan].update(vals);
|
|
170
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
171
|
+
}
|
|
172
|
+
catch (e) {
|
|
173
|
+
if (e.name === InvalidUpdateError.unminifiable_name) {
|
|
174
|
+
throw new InvalidUpdateError(`Invalid update for channel ${chan} with values ${JSON.stringify(vals)}`);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
throw e;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (updated && getNextVersion !== undefined) {
|
|
181
|
+
checkpoint.channel_versions[chan] = getNextVersion(maxVersion, channels[chan]);
|
|
182
|
+
}
|
|
183
|
+
updatedChannels.add(chan);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// Channels that weren't updated in this step are notified of a new step
|
|
187
|
+
for (const chan of Object.keys(channels)) {
|
|
188
|
+
if (!updatedChannels.has(chan)) {
|
|
189
|
+
const updated = channels[chan].update([]);
|
|
190
|
+
if (updated && getNextVersion !== undefined) {
|
|
191
|
+
checkpoint.channel_versions[chan] = getNextVersion(maxVersion, channels[chan]);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
export function _prepareNextTasks(checkpoint, processes, channels, config, forExecution, extra) {
|
|
197
|
+
const parentNamespace = config.configurable?.checkpoint_ns ?? "";
|
|
198
|
+
const tasks = [];
|
|
199
|
+
const taskDescriptions = [];
|
|
200
|
+
const { step, isResuming = false, checkpointer, manager } = extra;
|
|
201
|
+
for (const packet of checkpoint.pending_sends) {
|
|
202
|
+
if (!_isSendInterface(packet)) {
|
|
203
|
+
console.warn(`Ignoring invalid packet ${JSON.stringify(packet)} in pending sends.`);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
if (!(packet.node in processes)) {
|
|
207
|
+
console.warn(`Ignoring unknown node name ${packet.node} in pending sends.`);
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
const triggers = [TASKS];
|
|
211
|
+
const metadata = _getIdMetadata({
|
|
212
|
+
langgraph_step: step,
|
|
213
|
+
langgraph_node: packet.node,
|
|
214
|
+
langgraph_triggers: triggers,
|
|
215
|
+
langgraph_task_idx: forExecution ? tasks.length : taskDescriptions.length,
|
|
216
|
+
});
|
|
217
|
+
const checkpointNamespace = parentNamespace === ""
|
|
218
|
+
? packet.node
|
|
219
|
+
: `${parentNamespace}${CHECKPOINT_NAMESPACE_SEPARATOR}${packet.node}`;
|
|
220
|
+
const taskId = uuid5(JSON.stringify([checkpointNamespace, metadata]), checkpoint.id);
|
|
221
|
+
if (forExecution) {
|
|
222
|
+
const proc = processes[packet.node];
|
|
223
|
+
const node = proc.getNode();
|
|
224
|
+
if (node !== undefined) {
|
|
225
|
+
const writes = [];
|
|
226
|
+
tasks.push({
|
|
227
|
+
name: packet.node,
|
|
228
|
+
input: packet.args,
|
|
229
|
+
proc: node,
|
|
230
|
+
writes,
|
|
231
|
+
triggers,
|
|
232
|
+
config: patchConfig(mergeConfigs(config, processes[packet.node].config, {
|
|
233
|
+
metadata,
|
|
234
|
+
}), {
|
|
235
|
+
runName: packet.node,
|
|
236
|
+
callbacks: manager?.getChild(`graph:step:${step}`),
|
|
237
|
+
configurable: {
|
|
238
|
+
[CONFIG_KEY_SEND]: _localWrite.bind(undefined, (items) => writes.push(...items), processes, channels),
|
|
239
|
+
[CONFIG_KEY_READ]: _localRead.bind(undefined, checkpoint, channels, {
|
|
240
|
+
name: packet.node,
|
|
241
|
+
writes: writes,
|
|
242
|
+
triggers,
|
|
243
|
+
}),
|
|
244
|
+
},
|
|
245
|
+
}),
|
|
246
|
+
id: taskId,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
taskDescriptions.push({ id: taskId, name: packet.node });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Check if any processes should be run in next step
|
|
255
|
+
// If so, prepare the values to be passed to them
|
|
256
|
+
const nullVersion = getNullChannelVersion(checkpoint.channel_versions);
|
|
257
|
+
if (nullVersion === undefined) {
|
|
258
|
+
return forExecution ? tasks : taskDescriptions;
|
|
259
|
+
}
|
|
260
|
+
for (const [name, proc] of Object.entries(processes)) {
|
|
261
|
+
const seen = checkpoint.versions_seen[name] ?? {};
|
|
262
|
+
const triggers = proc.triggers
|
|
263
|
+
.filter((chan) => {
|
|
264
|
+
const result = readChannel(channels, chan, false, true);
|
|
265
|
+
const isEmptyChannelError =
|
|
266
|
+
// eslint-disable-next-line no-instanceof/no-instanceof
|
|
267
|
+
result instanceof Error &&
|
|
268
|
+
result.name === EmptyChannelError.unminifiable_name;
|
|
269
|
+
return (!isEmptyChannelError &&
|
|
270
|
+
(checkpoint.channel_versions[chan] ?? nullVersion) >
|
|
271
|
+
(seen[chan] ?? nullVersion));
|
|
272
|
+
})
|
|
273
|
+
.sort();
|
|
274
|
+
// If any of the channels read by this process were updated
|
|
275
|
+
if (triggers.length > 0) {
|
|
276
|
+
const val = _procInput(proc, channels, forExecution);
|
|
277
|
+
if (val === undefined) {
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
const metadata = _getIdMetadata({
|
|
281
|
+
langgraph_step: step,
|
|
282
|
+
langgraph_node: name,
|
|
283
|
+
langgraph_triggers: triggers,
|
|
284
|
+
langgraph_task_idx: forExecution
|
|
285
|
+
? tasks.length
|
|
286
|
+
: taskDescriptions.length,
|
|
287
|
+
});
|
|
288
|
+
const checkpointNamespace = parentNamespace === ""
|
|
289
|
+
? name
|
|
290
|
+
: `${parentNamespace}${CHECKPOINT_NAMESPACE_SEPARATOR}${name}`;
|
|
291
|
+
const taskId = uuid5(JSON.stringify([checkpointNamespace, metadata]), checkpoint.id);
|
|
292
|
+
if (forExecution) {
|
|
293
|
+
const node = proc.getNode();
|
|
294
|
+
if (node !== undefined) {
|
|
295
|
+
const writes = [];
|
|
296
|
+
tasks.push({
|
|
297
|
+
name,
|
|
298
|
+
input: val,
|
|
299
|
+
proc: node,
|
|
300
|
+
writes,
|
|
301
|
+
triggers,
|
|
302
|
+
config: patchConfig(mergeConfigs(config, proc.config, { metadata }), {
|
|
303
|
+
runName: name,
|
|
304
|
+
callbacks: manager?.getChild(`graph:step:${step}`),
|
|
305
|
+
configurable: {
|
|
306
|
+
[CONFIG_KEY_SEND]: _localWrite.bind(undefined, (items) => writes.push(...items), processes, channels),
|
|
307
|
+
[CONFIG_KEY_READ]: _localRead.bind(undefined, checkpoint, channels, {
|
|
308
|
+
name,
|
|
309
|
+
writes: writes,
|
|
310
|
+
triggers,
|
|
311
|
+
}),
|
|
312
|
+
[CONFIG_KEY_CHECKPOINTER]: checkpointer,
|
|
313
|
+
[CONFIG_KEY_RESUMING]: isResuming,
|
|
314
|
+
checkpoint_id: checkpoint.id,
|
|
315
|
+
checkpoint_ns: checkpointNamespace,
|
|
316
|
+
},
|
|
317
|
+
}),
|
|
318
|
+
id: taskId,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
taskDescriptions.push({ id: taskId, name });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return forExecution ? tasks : taskDescriptions;
|
|
328
|
+
}
|
|
329
|
+
function _procInput(proc, channels, forExecution) {
|
|
330
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
331
|
+
let val;
|
|
332
|
+
// If all trigger channels subscribed by this process are not empty
|
|
333
|
+
// then invoke the process with the values of all non-empty channels
|
|
334
|
+
if (Array.isArray(proc.channels)) {
|
|
335
|
+
let successfulRead = false;
|
|
336
|
+
for (const chan of proc.channels) {
|
|
337
|
+
try {
|
|
338
|
+
val = readChannel(channels, chan, false);
|
|
339
|
+
successfulRead = true;
|
|
340
|
+
break;
|
|
341
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
342
|
+
}
|
|
343
|
+
catch (e) {
|
|
344
|
+
if (e.name === EmptyChannelError.unminifiable_name) {
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
throw e;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (!successfulRead) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
else if (typeof proc.channels === "object") {
|
|
357
|
+
val = {};
|
|
358
|
+
try {
|
|
359
|
+
for (const [k, chan] of Object.entries(proc.channels)) {
|
|
360
|
+
val[k] = readChannel(channels, chan, !proc.triggers.includes(chan));
|
|
361
|
+
}
|
|
362
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
363
|
+
}
|
|
364
|
+
catch (e) {
|
|
365
|
+
if (e.name === EmptyChannelError.unminifiable_name) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
throw e;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
throw new Error(`Invalid channels type, expected list or dict, got ${proc.channels}`);
|
|
375
|
+
}
|
|
376
|
+
// If the process has a mapper, apply it to the value
|
|
377
|
+
if (forExecution && proc.mapper !== undefined) {
|
|
378
|
+
val = proc.mapper(val);
|
|
379
|
+
}
|
|
380
|
+
return val;
|
|
381
|
+
}
|
package/dist/pregel/debug.cjs
CHANGED
|
@@ -1,26 +1,35 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.printStepWrites = exports.printStepTasks = exports.printStepCheckpoint = exports.tasksWithWrites = exports.mapDebugCheckpoint = exports.mapDebugTaskResults = exports.mapDebugTasks = exports.printCheckpoint = void 0;
|
|
4
|
+
const langgraph_checkpoint_1 = require("@langchain/langgraph-checkpoint");
|
|
5
|
+
const constants_js_1 = require("../constants.cjs");
|
|
4
6
|
const errors_js_1 = require("../errors.cjs");
|
|
7
|
+
const io_js_1 = require("./io.cjs");
|
|
8
|
+
const utils_js_1 = require("./utils.cjs");
|
|
5
9
|
const COLORS_MAP = {
|
|
6
10
|
blue: {
|
|
7
11
|
start: "\x1b[34m",
|
|
8
12
|
end: "\x1b[0m",
|
|
9
13
|
},
|
|
14
|
+
green: {
|
|
15
|
+
start: "\x1b[32m",
|
|
16
|
+
end: "\x1b[0m",
|
|
17
|
+
},
|
|
18
|
+
yellow: {
|
|
19
|
+
start: "\x1b[33;1m",
|
|
20
|
+
end: "\x1b[0m",
|
|
21
|
+
},
|
|
10
22
|
};
|
|
11
23
|
/**
|
|
12
24
|
* Wrap some text in a color for printing to the console.
|
|
13
25
|
*/
|
|
14
26
|
const wrap = (color, text) => `${color.start}${text}${color.end}`;
|
|
15
|
-
function printStepStart(step, nextTasks) {
|
|
16
|
-
const nTasks = nextTasks.length;
|
|
17
|
-
console.log(`${wrap(COLORS_MAP.blue, "[langgraph/step]")}`, `Starting step ${step} with ${nTasks} task${nTasks === 1 ? "" : "s"}. Next tasks:\n`, `\n${nextTasks
|
|
18
|
-
.map((task) => `${String(task.name)}(${JSON.stringify(task.input, null, 2)})`)
|
|
19
|
-
.join("\n")}`);
|
|
20
|
-
}
|
|
21
|
-
exports.printStepStart = printStepStart;
|
|
22
27
|
function printCheckpoint(step, channels) {
|
|
23
|
-
console.log(
|
|
28
|
+
console.log([
|
|
29
|
+
`${wrap(COLORS_MAP.blue, "[langgraph/checkpoint]")}`,
|
|
30
|
+
`Finishing step ${step}. Channel values:\n`,
|
|
31
|
+
`\n${JSON.stringify(Object.fromEntries(_readChannels(channels)), null, 2)}`,
|
|
32
|
+
].join(""));
|
|
24
33
|
}
|
|
25
34
|
exports.printCheckpoint = printCheckpoint;
|
|
26
35
|
function* _readChannels(channels
|
|
@@ -42,3 +51,140 @@ function* _readChannels(channels
|
|
|
42
51
|
}
|
|
43
52
|
}
|
|
44
53
|
}
|
|
54
|
+
function* mapDebugTasks(step, tasks) {
|
|
55
|
+
const ts = new Date().toISOString();
|
|
56
|
+
for (const { name, input, config, triggers } of tasks) {
|
|
57
|
+
if (config?.tags?.includes(constants_js_1.TAG_HIDDEN))
|
|
58
|
+
continue;
|
|
59
|
+
const metadata = { ...config?.metadata };
|
|
60
|
+
const idMetadata = (0, utils_js_1._getIdMetadata)({
|
|
61
|
+
langgraph_step: metadata.langgraph_step,
|
|
62
|
+
langgraph_node: metadata.langgraph_node,
|
|
63
|
+
langgraph_triggers: metadata.langgraph_triggers,
|
|
64
|
+
langgraph_task_idx: metadata.langgraph_task_idx,
|
|
65
|
+
});
|
|
66
|
+
yield {
|
|
67
|
+
type: "task",
|
|
68
|
+
timestamp: ts,
|
|
69
|
+
step,
|
|
70
|
+
payload: {
|
|
71
|
+
id: (0, langgraph_checkpoint_1.uuid5)(JSON.stringify([name, step, idMetadata]), constants_js_1.TASK_NAMESPACE),
|
|
72
|
+
name,
|
|
73
|
+
input,
|
|
74
|
+
triggers,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
exports.mapDebugTasks = mapDebugTasks;
|
|
80
|
+
function* mapDebugTaskResults(step, tasks, streamChannelsList) {
|
|
81
|
+
const ts = new Date().toISOString();
|
|
82
|
+
for (const { name, writes, config } of tasks) {
|
|
83
|
+
if (config?.tags?.includes(constants_js_1.TAG_HIDDEN))
|
|
84
|
+
continue;
|
|
85
|
+
const metadata = { ...config?.metadata };
|
|
86
|
+
const idMetadata = (0, utils_js_1._getIdMetadata)(metadata);
|
|
87
|
+
yield {
|
|
88
|
+
type: "task_result",
|
|
89
|
+
timestamp: ts,
|
|
90
|
+
step,
|
|
91
|
+
payload: {
|
|
92
|
+
id: (0, langgraph_checkpoint_1.uuid5)(JSON.stringify([name, step, idMetadata]), constants_js_1.TASK_NAMESPACE),
|
|
93
|
+
name,
|
|
94
|
+
result: writes.filter(([channel]) => streamChannelsList.includes(channel)),
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
exports.mapDebugTaskResults = mapDebugTaskResults;
|
|
100
|
+
function* mapDebugCheckpoint(step, config, channels, streamChannels, metadata, tasks, pendingWrites) {
|
|
101
|
+
function formatConfig(config) {
|
|
102
|
+
// make sure the config is consistent with Python
|
|
103
|
+
const pyConfig = {};
|
|
104
|
+
if (config.callbacks != null)
|
|
105
|
+
pyConfig.callbacks = config.callbacks;
|
|
106
|
+
if (config.configurable != null)
|
|
107
|
+
pyConfig.configurable = config.configurable;
|
|
108
|
+
if (config.maxConcurrency != null)
|
|
109
|
+
pyConfig.max_concurrency = config.maxConcurrency;
|
|
110
|
+
if (config.metadata != null)
|
|
111
|
+
pyConfig.metadata = config.metadata;
|
|
112
|
+
if (config.recursionLimit != null)
|
|
113
|
+
pyConfig.recursion_limit = config.recursionLimit;
|
|
114
|
+
if (config.runId != null)
|
|
115
|
+
pyConfig.run_id = config.runId;
|
|
116
|
+
if (config.runName != null)
|
|
117
|
+
pyConfig.run_name = config.runName;
|
|
118
|
+
if (config.tags != null)
|
|
119
|
+
pyConfig.tags = config.tags;
|
|
120
|
+
return pyConfig;
|
|
121
|
+
}
|
|
122
|
+
function getCurrentUTC() {
|
|
123
|
+
const now = new Date();
|
|
124
|
+
return new Date(now.getTime() - now.getTimezoneOffset() * 60 * 1000);
|
|
125
|
+
}
|
|
126
|
+
const ts = getCurrentUTC().toISOString();
|
|
127
|
+
yield {
|
|
128
|
+
type: "checkpoint",
|
|
129
|
+
timestamp: ts,
|
|
130
|
+
step,
|
|
131
|
+
payload: {
|
|
132
|
+
config: formatConfig(config),
|
|
133
|
+
values: (0, io_js_1.readChannels)(channels, streamChannels),
|
|
134
|
+
metadata,
|
|
135
|
+
next: tasks.map((task) => task.name),
|
|
136
|
+
tasks: tasksWithWrites(tasks, pendingWrites),
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
exports.mapDebugCheckpoint = mapDebugCheckpoint;
|
|
141
|
+
function tasksWithWrites(tasks, pendingWrites) {
|
|
142
|
+
return tasks.map((task) => {
|
|
143
|
+
const error = pendingWrites.find(([id, n]) => id === task.id && n === constants_js_1.ERROR)?.[2];
|
|
144
|
+
if (error)
|
|
145
|
+
return { id: task.id, name: task.name, error };
|
|
146
|
+
return { id: task.id, name: task.name };
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
exports.tasksWithWrites = tasksWithWrites;
|
|
150
|
+
function printStepCheckpoint(step, channels, whitelist) {
|
|
151
|
+
console.log([
|
|
152
|
+
`${wrap(COLORS_MAP.blue, `[${step}:checkpoint]`)}`,
|
|
153
|
+
`\x1b[1m State at the end of step ${step}:\x1b[0m\n`,
|
|
154
|
+
JSON.stringify((0, io_js_1.readChannels)(channels, whitelist), null, 2),
|
|
155
|
+
].join(""));
|
|
156
|
+
}
|
|
157
|
+
exports.printStepCheckpoint = printStepCheckpoint;
|
|
158
|
+
function printStepTasks(step, nextTasks) {
|
|
159
|
+
const nTasks = nextTasks.length;
|
|
160
|
+
console.log([
|
|
161
|
+
`${wrap(COLORS_MAP.blue, `[${step}:tasks]`)}`,
|
|
162
|
+
`\x1b[1m Starting step ${step} with ${nTasks} task${nTasks === 1 ? "" : "s"}:\x1b[0m\n`,
|
|
163
|
+
nextTasks
|
|
164
|
+
.map((task) => `- ${wrap(COLORS_MAP.green, String(task.name))} -> ${JSON.stringify(task.input, null, 2)}`)
|
|
165
|
+
.join("\n"),
|
|
166
|
+
].join(""));
|
|
167
|
+
}
|
|
168
|
+
exports.printStepTasks = printStepTasks;
|
|
169
|
+
function printStepWrites(step, writes, whitelist) {
|
|
170
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
171
|
+
const byChannel = {};
|
|
172
|
+
for (const [channel, value] of writes) {
|
|
173
|
+
if (whitelist.includes(channel)) {
|
|
174
|
+
if (!byChannel[channel]) {
|
|
175
|
+
byChannel[channel] = [];
|
|
176
|
+
}
|
|
177
|
+
byChannel[channel].push(value);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
console.log([
|
|
181
|
+
`${wrap(COLORS_MAP.blue, `[${step}:writes]`)}`,
|
|
182
|
+
`\x1b[1m Finished step ${step} with writes to ${Object.keys(byChannel).length} channel${Object.keys(byChannel).length !== 1 ? "s" : ""}:\x1b[0m\n`,
|
|
183
|
+
Object.entries(byChannel)
|
|
184
|
+
.map(([name, vals]) => `- ${wrap(COLORS_MAP.yellow, name)} -> ${vals
|
|
185
|
+
.map((v) => JSON.stringify(v))
|
|
186
|
+
.join(", ")}`)
|
|
187
|
+
.join("\n"),
|
|
188
|
+
].join(""));
|
|
189
|
+
}
|
|
190
|
+
exports.printStepWrites = printStepWrites;
|
package/dist/pregel/debug.d.ts
CHANGED
|
@@ -1,4 +1,42 @@
|
|
|
1
|
+
import { RunnableConfig } from "@langchain/core/runnables";
|
|
2
|
+
import { CheckpointMetadata, CheckpointPendingWrite, PendingWrite } from "@langchain/langgraph-checkpoint";
|
|
1
3
|
import { BaseChannel } from "../channels/base.js";
|
|
2
|
-
import { PregelExecutableTask } from "./types.js";
|
|
3
|
-
export declare function printStepStart<N extends PropertyKey, C extends PropertyKey>(step: number, nextTasks: readonly PregelExecutableTask<N, C>[]): void;
|
|
4
|
+
import { PregelExecutableTask, PregelTaskDescription } from "./types.js";
|
|
4
5
|
export declare function printCheckpoint<Value>(step: number, channels: Record<string, BaseChannel<Value>>): void;
|
|
6
|
+
export declare function mapDebugTasks<N extends PropertyKey, C extends PropertyKey>(step: number, tasks: readonly PregelExecutableTask<N, C>[]): Generator<{
|
|
7
|
+
type: string;
|
|
8
|
+
timestamp: string;
|
|
9
|
+
step: number;
|
|
10
|
+
payload: {
|
|
11
|
+
id: string;
|
|
12
|
+
name: N;
|
|
13
|
+
input: unknown;
|
|
14
|
+
triggers: string[];
|
|
15
|
+
};
|
|
16
|
+
}, void, unknown>;
|
|
17
|
+
export declare function mapDebugTaskResults<N extends PropertyKey, C extends PropertyKey>(step: number, tasks: readonly PregelExecutableTask<N, C>[], streamChannelsList: Array<PropertyKey>): Generator<{
|
|
18
|
+
type: string;
|
|
19
|
+
timestamp: string;
|
|
20
|
+
step: number;
|
|
21
|
+
payload: {
|
|
22
|
+
id: string;
|
|
23
|
+
name: N;
|
|
24
|
+
result: PendingWrite<C>[];
|
|
25
|
+
};
|
|
26
|
+
}, void, unknown>;
|
|
27
|
+
export declare function mapDebugCheckpoint<N extends PropertyKey, C extends PropertyKey>(step: number, config: RunnableConfig, channels: Record<string, BaseChannel>, streamChannels: string | string[], metadata: CheckpointMetadata, tasks: readonly PregelExecutableTask<N, C>[], pendingWrites: CheckpointPendingWrite[]): Generator<{
|
|
28
|
+
type: string;
|
|
29
|
+
timestamp: string;
|
|
30
|
+
step: number;
|
|
31
|
+
payload: {
|
|
32
|
+
config: Partial<Record<"tags" | "configurable" | "timeout" | "signal" | "metadata" | "callbacks" | "recursion_limit" | "max_concurrency" | "run_name" | "run_id", unknown>>;
|
|
33
|
+
values: any;
|
|
34
|
+
metadata: CheckpointMetadata;
|
|
35
|
+
next: N[];
|
|
36
|
+
tasks: PregelTaskDescription[];
|
|
37
|
+
};
|
|
38
|
+
}, void, unknown>;
|
|
39
|
+
export declare function tasksWithWrites<N extends PropertyKey, C extends PropertyKey>(tasks: PregelTaskDescription[] | readonly PregelExecutableTask<N, C>[], pendingWrites: CheckpointPendingWrite[]): PregelTaskDescription[];
|
|
40
|
+
export declare function printStepCheckpoint(step: number, channels: Record<string, BaseChannel<unknown>>, whitelist: string[]): void;
|
|
41
|
+
export declare function printStepTasks<N extends PropertyKey, C extends PropertyKey>(step: number, nextTasks: readonly PregelExecutableTask<N, C>[]): void;
|
|
42
|
+
export declare function printStepWrites(step: number, writes: PendingWrite[], whitelist: string[]): void;
|