@langchain/langgraph 0.0.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.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +588 -0
  3. package/dist/channels/base.cjs +58 -0
  4. package/dist/channels/base.d.ts +46 -0
  5. package/dist/channels/base.js +50 -0
  6. package/dist/channels/binop.cjs +70 -0
  7. package/dist/channels/binop.d.ts +16 -0
  8. package/dist/channels/binop.js +66 -0
  9. package/dist/channels/index.cjs +9 -0
  10. package/dist/channels/index.d.ts +1 -0
  11. package/dist/channels/index.js +1 -0
  12. package/dist/channels/last_value.cjs +53 -0
  13. package/dist/channels/last_value.d.ts +12 -0
  14. package/dist/channels/last_value.js +49 -0
  15. package/dist/channels/topic.cjs +90 -0
  16. package/dist/channels/topic.d.ts +19 -0
  17. package/dist/channels/topic.js +86 -0
  18. package/dist/checkpoint/base.cjs +32 -0
  19. package/dist/checkpoint/base.d.ts +47 -0
  20. package/dist/checkpoint/base.js +27 -0
  21. package/dist/checkpoint/index.cjs +8 -0
  22. package/dist/checkpoint/index.d.ts +2 -0
  23. package/dist/checkpoint/index.js +2 -0
  24. package/dist/checkpoint/memory.cjs +35 -0
  25. package/dist/checkpoint/memory.d.ts +8 -0
  26. package/dist/checkpoint/memory.js +31 -0
  27. package/dist/constants.cjs +5 -0
  28. package/dist/constants.d.ts +2 -0
  29. package/dist/constants.js +2 -0
  30. package/dist/graph/graph.cjs +175 -0
  31. package/dist/graph/graph.d.ts +30 -0
  32. package/dist/graph/graph.js +171 -0
  33. package/dist/graph/index.cjs +9 -0
  34. package/dist/graph/index.d.ts +2 -0
  35. package/dist/graph/index.js +2 -0
  36. package/dist/graph/state.cjs +108 -0
  37. package/dist/graph/state.d.ts +17 -0
  38. package/dist/graph/state.js +104 -0
  39. package/dist/index.cjs +8 -0
  40. package/dist/index.d.ts +1 -0
  41. package/dist/index.js +1 -0
  42. package/dist/prebuilt/agent_executor.cjs +96 -0
  43. package/dist/prebuilt/agent_executor.d.ts +12 -0
  44. package/dist/prebuilt/agent_executor.js +92 -0
  45. package/dist/prebuilt/chat_agent_executor.cjs +130 -0
  46. package/dist/prebuilt/chat_agent_executor.d.ts +6 -0
  47. package/dist/prebuilt/chat_agent_executor.js +126 -0
  48. package/dist/prebuilt/index.cjs +9 -0
  49. package/dist/prebuilt/index.d.ts +3 -0
  50. package/dist/prebuilt/index.js +3 -0
  51. package/dist/prebuilt/tool_executor.cjs +63 -0
  52. package/dist/prebuilt/tool_executor.d.ts +27 -0
  53. package/dist/prebuilt/tool_executor.js +59 -0
  54. package/dist/pregel/debug.cjs +46 -0
  55. package/dist/pregel/debug.d.ts +4 -0
  56. package/dist/pregel/debug.js +41 -0
  57. package/dist/pregel/index.cjs +475 -0
  58. package/dist/pregel/index.d.ts +75 -0
  59. package/dist/pregel/index.js +469 -0
  60. package/dist/pregel/io.cjs +57 -0
  61. package/dist/pregel/io.d.ts +9 -0
  62. package/dist/pregel/io.js +52 -0
  63. package/dist/pregel/read.cjs +217 -0
  64. package/dist/pregel/read.d.ts +43 -0
  65. package/dist/pregel/read.js +211 -0
  66. package/dist/pregel/reserved.cjs +7 -0
  67. package/dist/pregel/reserved.d.ts +3 -0
  68. package/dist/pregel/reserved.js +4 -0
  69. package/dist/pregel/validate.cjs +90 -0
  70. package/dist/pregel/validate.d.ts +15 -0
  71. package/dist/pregel/validate.js +85 -0
  72. package/dist/pregel/write.cjs +54 -0
  73. package/dist/pregel/write.d.ts +13 -0
  74. package/dist/pregel/write.js +50 -0
  75. package/index.cjs +1 -0
  76. package/index.d.ts +1 -0
  77. package/index.js +1 -0
  78. package/package.json +100 -0
  79. package/prebuilt.cjs +1 -0
  80. package/prebuilt.d.ts +1 -0
  81. package/prebuilt.js +1 -0
  82. package/pregel.cjs +1 -0
  83. package/pregel.d.ts +1 -0
  84. package/pregel.js +1 -0
@@ -0,0 +1,75 @@
1
+ import { Runnable, RunnableConfig } from "@langchain/core/runnables";
2
+ import { CallbackManagerForChainRun } from "@langchain/core/callbacks/manager";
3
+ import { IterableReadableStream } from "@langchain/core/utils/stream";
4
+ import { BaseChannel } from "../channels/base.js";
5
+ import { BaseCheckpointSaver } from "../checkpoint/base.js";
6
+ import { ChannelBatch, ChannelInvoke } from "./read.js";
7
+ import { ChannelWrite } from "./write.js";
8
+ export declare class GraphRecursionError extends Error {
9
+ constructor(message?: string);
10
+ }
11
+ export declare class Channel {
12
+ static subscribeTo(channels: string, options?: {
13
+ key?: string;
14
+ when?: (arg: any) => boolean;
15
+ tags?: string[];
16
+ }): ChannelInvoke;
17
+ static subscribeTo(channels: string[], options?: {
18
+ key?: string;
19
+ when?: (arg: any) => boolean;
20
+ tags?: string[];
21
+ }): ChannelInvoke;
22
+ static subscribeToEach(inbox: string, key?: string): ChannelBatch;
23
+ static writeTo(...args: any[]): ChannelWrite;
24
+ }
25
+ export interface PregelInterface {
26
+ /**
27
+ * @default {}
28
+ */
29
+ channels?: Record<string, BaseChannel>;
30
+ /**
31
+ * @default "output"
32
+ */
33
+ output?: string | Array<string>;
34
+ /**
35
+ * @default "input"
36
+ */
37
+ input?: string | Array<string>;
38
+ /**
39
+ * @default []
40
+ */
41
+ hidden?: Array<string>;
42
+ /**
43
+ * @default false
44
+ */
45
+ debug?: boolean;
46
+ /**
47
+ * @default []
48
+ */
49
+ interrupt?: string[];
50
+ nodes: Record<string, ChannelInvoke | ChannelBatch>;
51
+ checkpointer?: BaseCheckpointSaver;
52
+ stepTimeout?: number;
53
+ }
54
+ export interface PregelOptions extends RunnableConfig {
55
+ outputKeys?: string | string[];
56
+ }
57
+ export type PregelInputType = any;
58
+ export type PregelOutputType = any;
59
+ export declare class Pregel extends Runnable<PregelInputType, PregelOutputType, PregelOptions> implements PregelInterface {
60
+ lc_namespace: string[];
61
+ channels: Record<string, BaseChannel>;
62
+ output: string | Array<string>;
63
+ input: string | Array<string>;
64
+ hidden: Array<string>;
65
+ debug: boolean;
66
+ nodes: Record<string, ChannelInvoke | ChannelBatch>;
67
+ checkpointer?: BaseCheckpointSaver;
68
+ stepTimeout?: number;
69
+ interrupt: string[];
70
+ constructor(fields: PregelInterface);
71
+ _transform(input: AsyncGenerator<PregelInputType>, runManager?: CallbackManagerForChainRun, config?: RunnableConfig & Partial<Record<string, unknown>>): AsyncGenerator<PregelOutputType>;
72
+ invoke(input: PregelInputType, config?: PregelOptions): Promise<PregelOutputType>;
73
+ stream(input: PregelInputType, config?: PregelOptions): Promise<IterableReadableStream<PregelOutputType>>;
74
+ transform(generator: AsyncGenerator<PregelInputType>, config?: PregelOptions): AsyncGenerator<PregelOutputType>;
75
+ }
@@ -0,0 +1,469 @@
1
+ /* eslint-disable no-param-reassign */
2
+ import { Runnable, _coerceToRunnable, patchConfig, } from "@langchain/core/runnables";
3
+ import { IterableReadableStream } from "@langchain/core/utils/stream";
4
+ import { EmptyChannelError, createCheckpoint, emptyChannels, } from "../channels/base.js";
5
+ import { CheckpointAt, emptyCheckpoint, } from "../checkpoint/base.js";
6
+ import { ChannelBatch, ChannelInvoke } from "./read.js";
7
+ import { validateGraph } from "./validate.js";
8
+ import { ReservedChannels } from "./reserved.js";
9
+ import { mapInput, mapOutput } from "./io.js";
10
+ import { ChannelWrite } from "./write.js";
11
+ import { CONFIG_KEY_READ, CONFIG_KEY_SEND } from "../constants.js";
12
+ const DEFAULT_RECURSION_LIMIT = 25;
13
+ export class GraphRecursionError extends Error {
14
+ constructor(message) {
15
+ super(message);
16
+ this.name = "GraphRecursionError";
17
+ }
18
+ }
19
+ function _coerceWriteValue(value) {
20
+ if (!Runnable.isRunnable(value) && typeof value !== "function") {
21
+ return _coerceToRunnable(() => value);
22
+ }
23
+ return _coerceToRunnable(value);
24
+ }
25
+ function isString(value) {
26
+ return typeof value === "string";
27
+ }
28
+ export class Channel {
29
+ static subscribeTo(channels, options) {
30
+ const { key, when, tags } = options ?? {};
31
+ if (Array.isArray(channels) && key !== undefined) {
32
+ throw new Error("Can't specify a key when subscribing to multiple channels");
33
+ }
34
+ let channelMappingOrString;
35
+ if (isString(channels)) {
36
+ if (key) {
37
+ channelMappingOrString = { [key]: channels };
38
+ }
39
+ else {
40
+ channelMappingOrString = channels;
41
+ }
42
+ }
43
+ else {
44
+ channelMappingOrString = Object.fromEntries(channels.map((chan) => [chan, chan]));
45
+ }
46
+ const triggers = Array.isArray(channels) ? channels : [channels];
47
+ return new ChannelInvoke({
48
+ channels: channelMappingOrString,
49
+ triggers,
50
+ when,
51
+ tags,
52
+ });
53
+ }
54
+ static subscribeToEach(inbox, key) {
55
+ return new ChannelBatch({
56
+ channel: inbox,
57
+ key,
58
+ });
59
+ }
60
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
61
+ static writeTo(...args) {
62
+ // const channelPairs: Array<[string, WriteValue<RunInput, RunOutput>]> =
63
+ // channels.map((c) => [c, undefined]);
64
+ // return new ChannelWrite<RunInput, RunOutput>(channelPairs);
65
+ const channelPairs = [];
66
+ if (args.length === 1 && typeof args[0] === "object") {
67
+ // Handle the case where named arguments are passed as an object
68
+ const additionalArgs = args[0];
69
+ Object.entries(additionalArgs).forEach(([key, value]) => {
70
+ channelPairs.push([key, _coerceWriteValue(value)]);
71
+ });
72
+ }
73
+ else {
74
+ args.forEach((channel) => {
75
+ if (typeof channel === "string") {
76
+ channelPairs.push([channel, undefined]);
77
+ }
78
+ else if (typeof channel === "object") {
79
+ Object.entries(channel).forEach(([key, value]) => {
80
+ channelPairs.push([key, _coerceWriteValue(value)]);
81
+ });
82
+ }
83
+ });
84
+ }
85
+ return new ChannelWrite(channelPairs);
86
+ }
87
+ }
88
+ export class Pregel extends Runnable {
89
+ constructor(fields) {
90
+ super();
91
+ // Because Pregel extends `Runnable`.
92
+ Object.defineProperty(this, "lc_namespace", {
93
+ enumerable: true,
94
+ configurable: true,
95
+ writable: true,
96
+ value: ["langgraph", "pregel"]
97
+ });
98
+ Object.defineProperty(this, "channels", {
99
+ enumerable: true,
100
+ configurable: true,
101
+ writable: true,
102
+ value: {}
103
+ });
104
+ Object.defineProperty(this, "output", {
105
+ enumerable: true,
106
+ configurable: true,
107
+ writable: true,
108
+ value: "output"
109
+ });
110
+ Object.defineProperty(this, "input", {
111
+ enumerable: true,
112
+ configurable: true,
113
+ writable: true,
114
+ value: "input"
115
+ });
116
+ Object.defineProperty(this, "hidden", {
117
+ enumerable: true,
118
+ configurable: true,
119
+ writable: true,
120
+ value: []
121
+ });
122
+ Object.defineProperty(this, "debug", {
123
+ enumerable: true,
124
+ configurable: true,
125
+ writable: true,
126
+ value: false
127
+ });
128
+ Object.defineProperty(this, "nodes", {
129
+ enumerable: true,
130
+ configurable: true,
131
+ writable: true,
132
+ value: void 0
133
+ });
134
+ Object.defineProperty(this, "checkpointer", {
135
+ enumerable: true,
136
+ configurable: true,
137
+ writable: true,
138
+ value: void 0
139
+ });
140
+ Object.defineProperty(this, "stepTimeout", {
141
+ enumerable: true,
142
+ configurable: true,
143
+ writable: true,
144
+ value: void 0
145
+ });
146
+ Object.defineProperty(this, "interrupt", {
147
+ enumerable: true,
148
+ configurable: true,
149
+ writable: true,
150
+ value: []
151
+ });
152
+ this.channels = fields.channels ?? this.channels;
153
+ this.output = fields.output ?? this.output;
154
+ this.input = fields.input ?? this.input;
155
+ this.hidden = fields.hidden ?? this.hidden;
156
+ this.debug = fields.debug ?? this.debug;
157
+ this.nodes = fields.nodes;
158
+ this.checkpointer = fields.checkpointer;
159
+ this.stepTimeout = fields.stepTimeout;
160
+ this.interrupt = fields.interrupt ?? this.interrupt;
161
+ // Bind the method to the instance
162
+ this._transform = this._transform.bind(this);
163
+ validateGraph({
164
+ nodes: this.nodes,
165
+ channels: this.channels,
166
+ output: this.output,
167
+ input: this.input,
168
+ hidden: this.hidden,
169
+ interrupt: this.interrupt,
170
+ });
171
+ }
172
+ async *_transform(input, runManager, config = {}) {
173
+ // assign defaults
174
+ let outputKeys = [];
175
+ if (Array.isArray(config.outputKeys) ||
176
+ typeof config.outputKeys === "string") {
177
+ outputKeys = config.outputKeys;
178
+ }
179
+ else {
180
+ for (const chan in this.channels) {
181
+ if (!this.hidden.includes(chan)) {
182
+ outputKeys.push(chan);
183
+ }
184
+ }
185
+ }
186
+ // copy nodes to ignore mutations during execution
187
+ const processes = { ...this.nodes };
188
+ // get checkpoint, or create an empty one
189
+ let checkpoint;
190
+ if (this.checkpointer) {
191
+ checkpoint = this.checkpointer.get(config);
192
+ }
193
+ checkpoint = checkpoint ?? emptyCheckpoint();
194
+ // create channels from checkpoint
195
+ const channels = emptyChannels(this.channels, checkpoint);
196
+ // map inputs to channel updates
197
+ const thisInput = this.input;
198
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
199
+ const inputPendingWrites = [];
200
+ for await (const c of input) {
201
+ for (const value of mapInput(thisInput, c)) {
202
+ inputPendingWrites.push(value);
203
+ }
204
+ }
205
+ _applyWrites(checkpoint, channels, inputPendingWrites, config, 0);
206
+ const read = (chan) => _readChannel(channels, chan);
207
+ // Similarly to Bulk Synchronous Parallel / Pregel model
208
+ // computation proceeds in steps, while there are channel updates
209
+ // channel updates from step N are only visible in step N+1
210
+ // channels are guaranteed to be immutable for the duration of the step,
211
+ // with channel updates applied only at the transition between steps
212
+ const recursionLimit = config.recursionLimit ?? DEFAULT_RECURSION_LIMIT;
213
+ for (let step = 0; step < recursionLimit + 1; step += 1) {
214
+ const nextTasks = _prepareNextTasks(checkpoint, processes, channels);
215
+ // if no more tasks, we're done
216
+ if (nextTasks.length === 0) {
217
+ break;
218
+ }
219
+ else if (step === config.recursionLimit) {
220
+ throw new GraphRecursionError(`Recursion limit of ${config.recursionLimit} reached without hitting a stop condition. You can increase the limit by setting the "recursionLimit" config key.`);
221
+ }
222
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
223
+ const pendingWrites = [];
224
+ const tasksWithConfig = nextTasks.map(([proc, input, name]) => [
225
+ proc,
226
+ input,
227
+ patchConfig(config, {
228
+ callbacks: runManager?.getChild(`graph:step:${step}`),
229
+ runName: name,
230
+ configurable: {
231
+ ...config.configurable,
232
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
233
+ [CONFIG_KEY_SEND]: (items) => pendingWrites.push(...items),
234
+ [CONFIG_KEY_READ]: read,
235
+ },
236
+ }),
237
+ ]);
238
+ // execute tasks, and wait for one to fail or all to finish.
239
+ // each task is independent from all other concurrent tasks
240
+ const tasks = tasksWithConfig.map(([proc, input, updatedConfig]) => () => proc.invoke(input, updatedConfig));
241
+ await executeTasks(tasks, this.stepTimeout);
242
+ // apply writes to channels
243
+ _applyWrites(checkpoint, channels, pendingWrites, config, step + 1);
244
+ // yield current value and checkpoint view
245
+ const stepOutput = mapOutput(outputKeys, pendingWrites, channels);
246
+ if (stepOutput) {
247
+ yield stepOutput;
248
+ if (typeof outputKeys !== "string") {
249
+ _applyWritesFromView(checkpoint, channels, stepOutput);
250
+ }
251
+ }
252
+ // save end of step checkpoint
253
+ if (this.checkpointer &&
254
+ this.checkpointer.at === CheckpointAt.END_OF_STEP) {
255
+ checkpoint = await createCheckpoint(checkpoint, channels);
256
+ this.checkpointer.put(config, checkpoint);
257
+ }
258
+ // interrupt if any channel written to is in interrupt list
259
+ if (pendingWrites.some(([chan]) => this.interrupt?.some((i) => i === chan))) {
260
+ break;
261
+ }
262
+ }
263
+ // save end of run checkpoint
264
+ if (this.checkpointer && this.checkpointer.at === CheckpointAt.END_OF_RUN) {
265
+ checkpoint = await createCheckpoint(checkpoint, channels);
266
+ this.checkpointer.put(config, checkpoint);
267
+ }
268
+ }
269
+ async invoke(input, config) {
270
+ if (!config?.outputKeys) {
271
+ if (!config) {
272
+ config = {};
273
+ }
274
+ config.outputKeys = this.output;
275
+ }
276
+ let latest;
277
+ for await (const chunk of await this.stream(input, config)) {
278
+ latest = chunk;
279
+ }
280
+ if (!latest) {
281
+ return undefined;
282
+ }
283
+ return latest;
284
+ }
285
+ async stream(input, config) {
286
+ const inputIterator = (async function* () {
287
+ yield input;
288
+ })();
289
+ return IterableReadableStream.fromAsyncGenerator(this.transform(inputIterator, config));
290
+ }
291
+ async *transform(generator, config) {
292
+ for await (const chunk of this._transformStreamWithConfig(generator, this._transform, config)) {
293
+ yield chunk;
294
+ }
295
+ }
296
+ }
297
+ function timeout(ms) {
298
+ return new Promise((reject) => {
299
+ setTimeout(reject, ms);
300
+ });
301
+ }
302
+ async function executeTasks(tasks, stepTimeout) {
303
+ // Wrap each task in a Promise that respects the step timeout
304
+ const wrappedTasks = tasks.map((task) => stepTimeout
305
+ ? Promise.race([
306
+ task(),
307
+ stepTimeout ? timeout(stepTimeout) : Promise.resolve(),
308
+ ])
309
+ : task());
310
+ // Wait for all tasks to settle
311
+ const results = await Promise.allSettled(wrappedTasks);
312
+ // Process the results
313
+ for (const result of results) {
314
+ if (result.status === "rejected") {
315
+ // If any task failed, cancel all pending tasks and throw the error
316
+ throw result.reason;
317
+ }
318
+ }
319
+ }
320
+ function _readChannel(channels, chan) {
321
+ try {
322
+ return channels[chan].get();
323
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
324
+ }
325
+ catch (e) {
326
+ if (e.name === EmptyChannelError.name) {
327
+ return null;
328
+ }
329
+ throw e;
330
+ }
331
+ }
332
+ function _applyWrites(checkpoint, channels,
333
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
334
+ pendingWrites, config, forStep) {
335
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
336
+ const pendingWritesByChannel = {};
337
+ // Group writes by channel
338
+ for (const [chan, val] of pendingWrites) {
339
+ for (const c in ReservedChannels) {
340
+ if (chan === c) {
341
+ throw new Error(`Can't write to reserved channel ${chan}`);
342
+ }
343
+ }
344
+ if (chan in pendingWritesByChannel) {
345
+ pendingWritesByChannel[chan].push(val);
346
+ }
347
+ else {
348
+ pendingWritesByChannel[chan] = [val];
349
+ }
350
+ }
351
+ // Update reserved channels
352
+ pendingWritesByChannel[ReservedChannels.isLastStep] = [
353
+ forStep + 1 === config.recursionLimit,
354
+ ];
355
+ const updatedChannels = new Set();
356
+ // Apply writes to channels
357
+ for (const chan in pendingWritesByChannel) {
358
+ if (chan in pendingWritesByChannel) {
359
+ const vals = pendingWritesByChannel[chan];
360
+ if (chan in channels) {
361
+ channels[chan].update(vals);
362
+ if (checkpoint.channelVersions[chan] === undefined) {
363
+ checkpoint.channelVersions[chan] = 1;
364
+ }
365
+ else {
366
+ checkpoint.channelVersions[chan] += 1;
367
+ }
368
+ updatedChannels.add(chan);
369
+ }
370
+ else {
371
+ console.warn(`Skipping write for channel ${chan} which has no readers`);
372
+ }
373
+ }
374
+ }
375
+ // Channels that weren't updated in this step are notified of a new step
376
+ for (const chan in channels) {
377
+ if (!updatedChannels.has(chan)) {
378
+ channels[chan].update([]);
379
+ }
380
+ }
381
+ }
382
+ function _applyWritesFromView(checkpoint, channels, values) {
383
+ for (const [chan, val] of Object.entries(values)) {
384
+ if (val === _readChannel(channels, chan)) {
385
+ continue;
386
+ }
387
+ if (channels[chan].lc_graph_name === "LastValue") {
388
+ throw new Error(`Can't modify channel ${chan} with LastValue`);
389
+ }
390
+ checkpoint.channelVersions[chan] += 1;
391
+ channels[chan].update([values[chan]]);
392
+ }
393
+ }
394
+ function _prepareNextTasks(checkpoint, processes, channels) {
395
+ const tasks = [];
396
+ // Check if any processes should be run in next step
397
+ // If so, prepare the values to be passed to them
398
+ for (const name in processes) {
399
+ if (Object.prototype.hasOwnProperty.call(processes, name)) {
400
+ const proc = processes[name];
401
+ let seen = checkpoint.versionsSeen[name];
402
+ if (!seen) {
403
+ checkpoint.versionsSeen[name] = {};
404
+ seen = checkpoint.versionsSeen[name];
405
+ }
406
+ if ("triggers" in proc) {
407
+ // If any of the channels read by this process were updated
408
+ if (proc.triggers.some((chan) => checkpoint.channelVersions[chan] > (seen[chan] ?? 0))) {
409
+ // If all channels subscribed by this process have been initialized
410
+ try {
411
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
412
+ let val = {};
413
+ if (typeof proc.channels === "string") {
414
+ val[proc.channels] = _readChannel(channels, proc.channels);
415
+ }
416
+ else {
417
+ for (const [k, chan] of Object.entries(proc.channels)) {
418
+ val[k] = _readChannel(channels, chan);
419
+ }
420
+ }
421
+ // Processes that subscribe to a single keyless channel get
422
+ // the value directly, instead of a dict
423
+ if (typeof proc.channels === "string") {
424
+ val = val[proc.channels];
425
+ }
426
+ else if (Object.keys(proc.channels).length === 1 &&
427
+ proc.channels[Object.keys(proc.channels)[0]] === undefined) {
428
+ val = val[Object.keys(proc.channels)[0]];
429
+ }
430
+ // Update seen versions
431
+ proc.triggers.forEach((chan) => {
432
+ const version = checkpoint.channelVersions[chan];
433
+ if (version !== undefined) {
434
+ seen[chan] = version;
435
+ }
436
+ });
437
+ // skip if condition is not met
438
+ if (proc.when === undefined || proc.when(val)) {
439
+ tasks.push([proc, val, name]);
440
+ }
441
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
442
+ }
443
+ catch (error) {
444
+ if (error.name === EmptyChannelError.name) {
445
+ continue;
446
+ }
447
+ else {
448
+ throw error;
449
+ }
450
+ }
451
+ }
452
+ }
453
+ else if ("channel" in proc) {
454
+ // If the channel read by this process was updated
455
+ if (checkpoint.channelVersions[proc.channel] > (seen[proc.channel] ?? 0)) {
456
+ // Here we don't catch EmptyChannelError because the channel
457
+ // must be initialized if the previous `if` condition is true
458
+ let val = channels[proc.channel].get();
459
+ if (proc.key !== undefined) {
460
+ val = [{ [proc.key]: val }];
461
+ }
462
+ tasks.push([proc, val, name]);
463
+ seen[proc.channel] = checkpoint.channelVersions[proc.channel];
464
+ }
465
+ }
466
+ }
467
+ }
468
+ return tasks;
469
+ }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mapOutput = exports.mapInput = void 0;
4
+ /**
5
+ * Map input chunk to a sequence of pending writes in the form [channel, value].
6
+ */
7
+ function* mapInput(inputChannels,
8
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
+ chunk
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ ) {
12
+ if (typeof inputChannels === "string") {
13
+ yield [inputChannels, chunk];
14
+ }
15
+ else {
16
+ if ((chunk && typeof chunk !== "object") || Array.isArray(chunk)) {
17
+ throw new Error(`Expected chunk to be an object, got ${typeof chunk}`);
18
+ }
19
+ for (const k in chunk) {
20
+ if (inputChannels.includes(k)) {
21
+ yield [k, chunk[k]];
22
+ }
23
+ else {
24
+ console.warn(`Input channel ${k} not found in ${inputChannels}`);
25
+ }
26
+ }
27
+ }
28
+ }
29
+ exports.mapInput = mapInput;
30
+ /**
31
+ * Map pending writes (a list of [channel, value]) to output chunk.
32
+ */
33
+ function mapOutput(outputChannels,
34
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
+ pendingWrites, channels
36
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
+ ) {
38
+ if (typeof outputChannels === "string") {
39
+ if (pendingWrites.some(([chan, _]) => chan === outputChannels)) {
40
+ return channels[outputChannels].get();
41
+ }
42
+ }
43
+ else {
44
+ const updated = pendingWrites
45
+ .filter(([chan, _]) => outputChannels.includes(chan))
46
+ .map(([chan, _]) => chan);
47
+ if (updated.length > 0) {
48
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
+ return updated.reduce((acc, chan) => {
50
+ acc[chan] = channels[chan].get();
51
+ return acc;
52
+ }, {});
53
+ }
54
+ }
55
+ return undefined;
56
+ }
57
+ exports.mapOutput = mapOutput;
@@ -0,0 +1,9 @@
1
+ import { BaseChannel } from "../channels/base.js";
2
+ /**
3
+ * Map input chunk to a sequence of pending writes in the form [channel, value].
4
+ */
5
+ export declare function mapInput(inputChannels: string | Array<string>, chunk?: any): Generator<[string, any]>;
6
+ /**
7
+ * Map pending writes (a list of [channel, value]) to output chunk.
8
+ */
9
+ export declare function mapOutput(outputChannels: string | Array<string>, pendingWrites: Array<[string, any]>, channels: Record<string, BaseChannel>): any | undefined;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Map input chunk to a sequence of pending writes in the form [channel, value].
3
+ */
4
+ export function* mapInput(inputChannels,
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ chunk
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ ) {
9
+ if (typeof inputChannels === "string") {
10
+ yield [inputChannels, chunk];
11
+ }
12
+ else {
13
+ if ((chunk && typeof chunk !== "object") || Array.isArray(chunk)) {
14
+ throw new Error(`Expected chunk to be an object, got ${typeof chunk}`);
15
+ }
16
+ for (const k in chunk) {
17
+ if (inputChannels.includes(k)) {
18
+ yield [k, chunk[k]];
19
+ }
20
+ else {
21
+ console.warn(`Input channel ${k} not found in ${inputChannels}`);
22
+ }
23
+ }
24
+ }
25
+ }
26
+ /**
27
+ * Map pending writes (a list of [channel, value]) to output chunk.
28
+ */
29
+ export function mapOutput(outputChannels,
30
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
+ pendingWrites, channels
32
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
+ ) {
34
+ if (typeof outputChannels === "string") {
35
+ if (pendingWrites.some(([chan, _]) => chan === outputChannels)) {
36
+ return channels[outputChannels].get();
37
+ }
38
+ }
39
+ else {
40
+ const updated = pendingWrites
41
+ .filter(([chan, _]) => outputChannels.includes(chan))
42
+ .map(([chan, _]) => chan);
43
+ if (updated.length > 0) {
44
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
+ return updated.reduce((acc, chan) => {
46
+ acc[chan] = channels[chan].get();
47
+ return acc;
48
+ }, {});
49
+ }
50
+ }
51
+ return undefined;
52
+ }