@langchain/langgraph 0.0.33 → 0.0.34
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/README.md +1 -1
- package/dist/channels/base.cjs +1 -0
- package/dist/channels/base.js +1 -0
- package/dist/channels/ephemeral_value.cjs +1 -1
- package/dist/channels/ephemeral_value.js +1 -1
- package/dist/checkpoint/base.cjs +2 -0
- package/dist/checkpoint/base.d.ts +6 -0
- package/dist/checkpoint/base.js +2 -0
- package/dist/constants.cjs +80 -1
- package/dist/constants.d.ts +57 -0
- package/dist/constants.js +76 -0
- package/dist/graph/annotation.cjs +51 -0
- package/dist/graph/annotation.d.ts +41 -0
- package/dist/graph/annotation.js +45 -0
- package/dist/graph/graph.cjs +19 -4
- package/dist/graph/graph.d.ts +5 -4
- package/dist/graph/graph.js +20 -5
- package/dist/graph/index.cjs +3 -1
- package/dist/graph/index.d.ts +1 -0
- package/dist/graph/index.js +1 -0
- package/dist/graph/message.d.ts +1 -1
- package/dist/graph/state.cjs +24 -39
- package/dist/graph/state.d.ts +4 -29
- package/dist/graph/state.js +22 -36
- package/dist/pregel/index.cjs +111 -16
- package/dist/pregel/index.d.ts +7 -2
- package/dist/pregel/index.js +110 -16
- package/dist/pregel/io.cjs +31 -28
- package/dist/pregel/io.d.ts +1 -1
- package/dist/pregel/io.js +31 -28
- package/dist/pregel/read.cjs +4 -1
- package/dist/pregel/read.js +4 -1
- package/dist/pregel/write.cjs +60 -24
- package/dist/pregel/write.d.ts +11 -6
- package/dist/pregel/write.js +61 -25
- package/dist/web.cjs +4 -1
- package/dist/web.d.ts +2 -1
- package/dist/web.js +2 -1
- package/package.json +3 -2
package/dist/pregel/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { PregelNode } from "./read.js";
|
|
|
7
7
|
import { validateGraph, validateKeys } from "./validate.js";
|
|
8
8
|
import { mapInput, mapOutputUpdates, mapOutputValues, readChannel, readChannels, single, } from "./io.js";
|
|
9
9
|
import { ChannelWrite, PASSTHROUGH } from "./write.js";
|
|
10
|
-
import { CONFIG_KEY_READ, CONFIG_KEY_SEND, INTERRUPT, TAG_HIDDEN, TASKS, } from "../constants.js";
|
|
10
|
+
import { _isSend, _isSendInterface, CONFIG_KEY_READ, CONFIG_KEY_SEND, INTERRUPT, TAG_HIDDEN, TASKS, } from "../constants.js";
|
|
11
11
|
import { EmptyChannelError, GraphRecursionError, GraphValueError, InvalidUpdateError, } from "../errors.js";
|
|
12
12
|
const DEFAULT_LOOP_LIMIT = 25;
|
|
13
13
|
function isString(value) {
|
|
@@ -209,7 +209,7 @@ export class Pregel extends Runnable {
|
|
|
209
209
|
const checkpoint = saved ? saved.checkpoint : emptyCheckpoint();
|
|
210
210
|
const channels = emptyChannels(this.channels, checkpoint);
|
|
211
211
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
212
|
-
const [_, nextTasks] = _prepareNextTasks(checkpoint, this.nodes, channels, false);
|
|
212
|
+
const [_, nextTasks] = _prepareNextTasks(checkpoint, this.nodes, channels, false, { step: -1 });
|
|
213
213
|
return {
|
|
214
214
|
values: readChannels(channels, this.streamChannelsAsIs),
|
|
215
215
|
next: nextTasks.map((task) => task.name),
|
|
@@ -226,7 +226,7 @@ export class Pregel extends Runnable {
|
|
|
226
226
|
for await (const saved of this.checkpointer.list(config, limit, before)) {
|
|
227
227
|
const channels = emptyChannels(this.channels, saved.checkpoint);
|
|
228
228
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
229
|
-
const [_, nextTasks] = _prepareNextTasks(saved.checkpoint, this.nodes, channels, false);
|
|
229
|
+
const [_, nextTasks] = _prepareNextTasks(saved.checkpoint, this.nodes, channels, false, { step: -1 });
|
|
230
230
|
yield {
|
|
231
231
|
values: readChannels(channels, this.streamChannelsAsIs),
|
|
232
232
|
next: nextTasks.map((task) => task.name),
|
|
@@ -376,7 +376,7 @@ export class Pregel extends Runnable {
|
|
|
376
376
|
}
|
|
377
377
|
if (inputPendingWrites.length) {
|
|
378
378
|
// discard any unfinished tasks from previous checkpoint
|
|
379
|
-
const discarded = _prepareNextTasks(checkpoint, processes, channels, true);
|
|
379
|
+
const discarded = _prepareNextTasks(checkpoint, processes, channels, true, { step: -1 });
|
|
380
380
|
checkpoint = discarded[0]; // eslint-disable-line prefer-destructuring
|
|
381
381
|
// apply input writes
|
|
382
382
|
_applyWrites(checkpoint, channels, inputPendingWrites);
|
|
@@ -415,7 +415,7 @@ export class Pregel extends Runnable {
|
|
|
415
415
|
// with channel updates applied only at the transition between steps
|
|
416
416
|
const stop = start + (config.recursionLimit ?? DEFAULT_LOOP_LIMIT);
|
|
417
417
|
for (let step = start; step < stop + 1; step += 1) {
|
|
418
|
-
const [nextCheckpoint, nextTasks] = _prepareNextTasks(checkpoint, processes, channels, true);
|
|
418
|
+
const [nextCheckpoint, nextTasks] = _prepareNextTasks(checkpoint, processes, channels, true, { step });
|
|
419
419
|
// if no more tasks, we're done
|
|
420
420
|
if (nextTasks.length === 0 && step === start) {
|
|
421
421
|
throw new GraphValueError(`No tasks to run in graph.`);
|
|
@@ -474,7 +474,10 @@ export class Pregel extends Runnable {
|
|
|
474
474
|
yield* mapOutputValues(outputKeys, pendingWrites, channels);
|
|
475
475
|
}
|
|
476
476
|
else if (streamMode === "updates") {
|
|
477
|
-
|
|
477
|
+
// TODO: Refactor
|
|
478
|
+
for await (const task of nextTasks) {
|
|
479
|
+
yield* mapOutputUpdates(outputKeys, [task]);
|
|
480
|
+
}
|
|
478
481
|
}
|
|
479
482
|
// save end of step checkpoint
|
|
480
483
|
if (this.checkpointer) {
|
|
@@ -580,15 +583,46 @@ export function _localRead(checkpoint, channels, writes, select, fresh = false)
|
|
|
580
583
|
return readChannels(channels, select);
|
|
581
584
|
}
|
|
582
585
|
}
|
|
586
|
+
export function _localWrite(
|
|
587
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
588
|
+
commit, processes, channels,
|
|
589
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
590
|
+
writes) {
|
|
591
|
+
for (const [chan, value] of writes) {
|
|
592
|
+
if (chan === TASKS) {
|
|
593
|
+
if (!_isSend(value)) {
|
|
594
|
+
throw new InvalidUpdateError(`Invalid packet type, expected SendProtocol, got ${JSON.stringify(value)}`);
|
|
595
|
+
}
|
|
596
|
+
if (!(value.node in processes)) {
|
|
597
|
+
throw new InvalidUpdateError(`Invalid node name ${value.node} in packet`);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
else if (!(chan in channels)) {
|
|
601
|
+
console.warn(`Skipping write for channel '${chan}' which has no readers`);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
commit(writes);
|
|
605
|
+
}
|
|
583
606
|
export function _applyWrites(checkpoint, channels, pendingWrites) {
|
|
607
|
+
if (checkpoint.pending_sends) {
|
|
608
|
+
checkpoint.pending_sends = [];
|
|
609
|
+
}
|
|
584
610
|
const pendingWritesByChannel = {};
|
|
585
611
|
// Group writes by channel
|
|
586
612
|
for (const [chan, val] of pendingWrites) {
|
|
587
|
-
if (chan
|
|
588
|
-
|
|
613
|
+
if (chan === TASKS) {
|
|
614
|
+
checkpoint.pending_sends.push({
|
|
615
|
+
node: val.node,
|
|
616
|
+
args: val.args,
|
|
617
|
+
});
|
|
589
618
|
}
|
|
590
619
|
else {
|
|
591
|
-
|
|
620
|
+
if (chan in pendingWritesByChannel) {
|
|
621
|
+
pendingWritesByChannel[chan].push(val);
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
pendingWritesByChannel[chan] = [val];
|
|
625
|
+
}
|
|
592
626
|
}
|
|
593
627
|
}
|
|
594
628
|
// find the highest version of all channels
|
|
@@ -607,7 +641,7 @@ export function _applyWrites(checkpoint, channels, pendingWrites) {
|
|
|
607
641
|
}
|
|
608
642
|
catch (e) {
|
|
609
643
|
if (e.name === InvalidUpdateError.unminifiable_name) {
|
|
610
|
-
throw new InvalidUpdateError(`Invalid update for channel ${chan}. Values: ${vals}`);
|
|
644
|
+
throw new InvalidUpdateError(`Invalid update for channel ${chan}. Values: ${vals}\n\nError: ${e.message}`);
|
|
611
645
|
}
|
|
612
646
|
}
|
|
613
647
|
// side effect: update checkpoint channel versions
|
|
@@ -626,15 +660,60 @@ export function _applyWrites(checkpoint, channels, pendingWrites) {
|
|
|
626
660
|
}
|
|
627
661
|
}
|
|
628
662
|
}
|
|
629
|
-
export function _prepareNextTasks(checkpoint, processes, channels, forExecution) {
|
|
663
|
+
export function _prepareNextTasks(checkpoint, processes, channels, forExecution, extra) {
|
|
630
664
|
const newCheckpoint = copyCheckpoint(checkpoint);
|
|
631
665
|
const tasks = [];
|
|
632
666
|
const taskDescriptions = [];
|
|
667
|
+
for (const packet of checkpoint.pending_sends) {
|
|
668
|
+
if (!_isSendInterface(packet)) {
|
|
669
|
+
console.warn(`Ignoring invalid packet ${JSON.stringify(packet)} in pending sends.`);
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
if (!(packet.node in processes)) {
|
|
673
|
+
console.warn(`Ignoring unknown node name ${packet.node} in pending sends.`);
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
if (forExecution) {
|
|
677
|
+
const proc = processes[packet.node];
|
|
678
|
+
const node = proc.getNode();
|
|
679
|
+
if (node !== undefined) {
|
|
680
|
+
const triggers = [TASKS];
|
|
681
|
+
const metadata = {
|
|
682
|
+
langgraph_step: extra.step,
|
|
683
|
+
langgraph_node: packet.node,
|
|
684
|
+
langgraph_triggers: triggers,
|
|
685
|
+
langgraph_task_idx: tasks.length,
|
|
686
|
+
};
|
|
687
|
+
const writes = [];
|
|
688
|
+
tasks.push({
|
|
689
|
+
name: packet.node,
|
|
690
|
+
input: packet.args,
|
|
691
|
+
proc: node,
|
|
692
|
+
writes,
|
|
693
|
+
config: patchConfig(mergeConfigs(proc.config, processes[packet.node].config, {
|
|
694
|
+
metadata,
|
|
695
|
+
}), {
|
|
696
|
+
runName: packet.node,
|
|
697
|
+
// callbacks:
|
|
698
|
+
configurable: {
|
|
699
|
+
[CONFIG_KEY_SEND]: _localWrite.bind(undefined, (items) => writes.push(...items), processes, channels),
|
|
700
|
+
[CONFIG_KEY_READ]: _localRead.bind(undefined, checkpoint, channels, writes),
|
|
701
|
+
},
|
|
702
|
+
}),
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
else {
|
|
707
|
+
taskDescriptions.push({
|
|
708
|
+
name: packet.node,
|
|
709
|
+
input: packet.args,
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
}
|
|
633
713
|
// Check if any processes should be run in next step
|
|
634
714
|
// If so, prepare the values to be passed to them
|
|
635
715
|
for (const [name, proc] of Object.entries(processes)) {
|
|
636
|
-
|
|
637
|
-
if (proc.triggers
|
|
716
|
+
const hasUpdatedChannels = proc.triggers
|
|
638
717
|
.filter((chan) => {
|
|
639
718
|
try {
|
|
640
719
|
readChannel(channels, chan, false);
|
|
@@ -645,7 +724,9 @@ export function _prepareNextTasks(checkpoint, processes, channels, forExecution)
|
|
|
645
724
|
}
|
|
646
725
|
})
|
|
647
726
|
.some((chan) => getChannelVersion(newCheckpoint, chan) >
|
|
648
|
-
getVersionSeen(newCheckpoint, name, chan))
|
|
727
|
+
getVersionSeen(newCheckpoint, name, chan));
|
|
728
|
+
// If any of the channels read by this process were updated
|
|
729
|
+
if (hasUpdatedChannels) {
|
|
649
730
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
650
731
|
let val;
|
|
651
732
|
// If all trigger channels subscribed by this process are not empty
|
|
@@ -710,12 +791,25 @@ export function _prepareNextTasks(checkpoint, processes, channels, forExecution)
|
|
|
710
791
|
});
|
|
711
792
|
const node = proc.getNode();
|
|
712
793
|
if (node !== undefined) {
|
|
794
|
+
const metadata = {
|
|
795
|
+
langgraph_step: extra.step,
|
|
796
|
+
langgraph_node: name,
|
|
797
|
+
langgraph_triggers: proc.triggers,
|
|
798
|
+
langgraph_task_idx: tasks.length,
|
|
799
|
+
};
|
|
800
|
+
const writes = [];
|
|
713
801
|
tasks.push({
|
|
714
802
|
name,
|
|
715
803
|
input: val,
|
|
716
804
|
proc: node,
|
|
717
|
-
writes
|
|
718
|
-
config: proc.config,
|
|
805
|
+
writes,
|
|
806
|
+
config: patchConfig(mergeConfigs(proc.config, { metadata }), {
|
|
807
|
+
runName: name,
|
|
808
|
+
configurable: {
|
|
809
|
+
[CONFIG_KEY_SEND]: _localWrite.bind(undefined, (items) => writes.push(...items), processes, channels),
|
|
810
|
+
[CONFIG_KEY_READ]: _localRead.bind(undefined, checkpoint, channels, writes),
|
|
811
|
+
},
|
|
812
|
+
}),
|
|
719
813
|
});
|
|
720
814
|
}
|
|
721
815
|
}
|
package/dist/pregel/io.cjs
CHANGED
|
@@ -98,39 +98,42 @@ function* mapOutputUpdates(outputChannels, tasks
|
|
|
98
98
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
99
99
|
) {
|
|
100
100
|
const outputTasks = tasks.filter((task) => task.config === undefined || !task.config.tags?.includes(constants_js_1.TAG_HIDDEN));
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
updated[task.name] = nodes;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
if (Object.keys(updated).length > 0) {
|
|
117
|
-
yield updated;
|
|
118
|
-
}
|
|
101
|
+
if (!outputTasks.length) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
105
|
+
let updated;
|
|
106
|
+
if (!Array.isArray(outputChannels)) {
|
|
107
|
+
updated = outputTasks.flatMap((task) => task.writes
|
|
108
|
+
.filter(([chan, _]) => chan === outputChannels)
|
|
109
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
110
|
+
.map(([_, value]) => [task.name, value]));
|
|
119
111
|
}
|
|
120
112
|
else {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
113
|
+
updated = outputTasks
|
|
114
|
+
.filter((task) => task.writes.some(([chan]) => outputChannels.includes(chan)))
|
|
115
|
+
.map((task) => [
|
|
116
|
+
task.name,
|
|
117
|
+
Object.fromEntries(task.writes.filter(([chan]) => outputChannels.includes(chan))),
|
|
118
|
+
]);
|
|
119
|
+
}
|
|
120
|
+
const grouped = Object.fromEntries(outputTasks.map((t) => [t.name, []])
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
122
|
+
);
|
|
123
|
+
for (const [node, value] of updated) {
|
|
124
|
+
grouped[node].push(value);
|
|
125
|
+
}
|
|
126
|
+
for (const [node, value] of Object.entries(grouped)) {
|
|
127
|
+
if (value.length === 0) {
|
|
128
|
+
delete grouped[node];
|
|
129
129
|
}
|
|
130
|
-
if (
|
|
131
|
-
|
|
130
|
+
else if (value.length === 1) {
|
|
131
|
+
// TODO: Fix incorrect cast here
|
|
132
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
133
|
+
grouped[node] = value[0];
|
|
132
134
|
}
|
|
133
135
|
}
|
|
136
|
+
yield grouped;
|
|
134
137
|
}
|
|
135
138
|
exports.mapOutputUpdates = mapOutputUpdates;
|
|
136
139
|
function single(iter) {
|
package/dist/pregel/io.d.ts
CHANGED
|
@@ -13,5 +13,5 @@ export declare function mapOutputValues<C extends PropertyKey>(outputChannels: C
|
|
|
13
13
|
/**
|
|
14
14
|
* Map pending writes (a sequence of tuples (channel, value)) to output chunk.
|
|
15
15
|
*/
|
|
16
|
-
export declare function mapOutputUpdates<N extends PropertyKey, C extends PropertyKey>(outputChannels: C | Array<C>, tasks: readonly PregelExecutableTask<N, C>[]): Generator<Record<N, any | Record<string, any
|
|
16
|
+
export declare function mapOutputUpdates<N extends PropertyKey, C extends PropertyKey>(outputChannels: C | Array<C>, tasks: readonly PregelExecutableTask<N, C>[]): Generator<Record<N, Record<string, any> | Record<string, any>[]>>;
|
|
17
17
|
export declare function single<T>(iter: IterableIterator<T>): T | null;
|
package/dist/pregel/io.js
CHANGED
|
@@ -91,39 +91,42 @@ export function* mapOutputUpdates(outputChannels, tasks
|
|
|
91
91
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
92
92
|
) {
|
|
93
93
|
const outputTasks = tasks.filter((task) => task.config === undefined || !task.config.tags?.includes(TAG_HIDDEN));
|
|
94
|
-
if (
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
updated[task.name] = nodes;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
if (Object.keys(updated).length > 0) {
|
|
110
|
-
yield updated;
|
|
111
|
-
}
|
|
94
|
+
if (!outputTasks.length) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
98
|
+
let updated;
|
|
99
|
+
if (!Array.isArray(outputChannels)) {
|
|
100
|
+
updated = outputTasks.flatMap((task) => task.writes
|
|
101
|
+
.filter(([chan, _]) => chan === outputChannels)
|
|
102
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
103
|
+
.map(([_, value]) => [task.name, value]));
|
|
112
104
|
}
|
|
113
105
|
else {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
106
|
+
updated = outputTasks
|
|
107
|
+
.filter((task) => task.writes.some(([chan]) => outputChannels.includes(chan)))
|
|
108
|
+
.map((task) => [
|
|
109
|
+
task.name,
|
|
110
|
+
Object.fromEntries(task.writes.filter(([chan]) => outputChannels.includes(chan))),
|
|
111
|
+
]);
|
|
112
|
+
}
|
|
113
|
+
const grouped = Object.fromEntries(outputTasks.map((t) => [t.name, []])
|
|
114
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
115
|
+
);
|
|
116
|
+
for (const [node, value] of updated) {
|
|
117
|
+
grouped[node].push(value);
|
|
118
|
+
}
|
|
119
|
+
for (const [node, value] of Object.entries(grouped)) {
|
|
120
|
+
if (value.length === 0) {
|
|
121
|
+
delete grouped[node];
|
|
122
122
|
}
|
|
123
|
-
if (
|
|
124
|
-
|
|
123
|
+
else if (value.length === 1) {
|
|
124
|
+
// TODO: Fix incorrect cast here
|
|
125
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
126
|
+
grouped[node] = value[0];
|
|
125
127
|
}
|
|
126
128
|
}
|
|
129
|
+
yield grouped;
|
|
127
130
|
}
|
|
128
131
|
export function single(iter) {
|
|
129
132
|
// eslint-disable-next-line no-unreachable-loop
|
package/dist/pregel/read.cjs
CHANGED
|
@@ -136,7 +136,10 @@ class PregelNode extends runnables_1.RunnableBinding {
|
|
|
136
136
|
// eslint-disable-next-line no-instanceof/no-instanceof
|
|
137
137
|
newWriters[newWriters.length - 2] instanceof write_js_1.ChannelWrite) {
|
|
138
138
|
// we can combine writes if they are consecutive
|
|
139
|
-
|
|
139
|
+
// careful to not modify the original writers list or ChannelWrite
|
|
140
|
+
const endWriters = newWriters.slice(-2);
|
|
141
|
+
const combinedWrites = endWriters[0].writes.concat(endWriters[1].writes);
|
|
142
|
+
newWriters[newWriters.length - 2] = new write_js_1.ChannelWrite(combinedWrites, endWriters[0].config?.tags);
|
|
140
143
|
newWriters.pop();
|
|
141
144
|
}
|
|
142
145
|
return newWriters;
|
package/dist/pregel/read.js
CHANGED
|
@@ -132,7 +132,10 @@ export class PregelNode extends RunnableBinding {
|
|
|
132
132
|
// eslint-disable-next-line no-instanceof/no-instanceof
|
|
133
133
|
newWriters[newWriters.length - 2] instanceof ChannelWrite) {
|
|
134
134
|
// we can combine writes if they are consecutive
|
|
135
|
-
|
|
135
|
+
// careful to not modify the original writers list or ChannelWrite
|
|
136
|
+
const endWriters = newWriters.slice(-2);
|
|
137
|
+
const combinedWrites = endWriters[0].writes.concat(endWriters[1].writes);
|
|
138
|
+
newWriters[newWriters.length - 2] = new ChannelWrite(combinedWrites, endWriters[0].config?.tags);
|
|
136
139
|
newWriters.pop();
|
|
137
140
|
}
|
|
138
141
|
return newWriters;
|
package/dist/pregel/write.cjs
CHANGED
|
@@ -3,8 +3,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ChannelWrite = exports.PASSTHROUGH = exports.SKIP_WRITE = void 0;
|
|
4
4
|
const constants_js_1 = require("../constants.cjs");
|
|
5
5
|
const utils_js_1 = require("../utils.cjs");
|
|
6
|
-
|
|
7
|
-
exports.
|
|
6
|
+
const errors_js_1 = require("../errors.cjs");
|
|
7
|
+
exports.SKIP_WRITE = {
|
|
8
|
+
[Symbol.for("LG_SKIP_WRITE")]: true,
|
|
9
|
+
};
|
|
10
|
+
function _isSkipWrite(x) {
|
|
11
|
+
return (typeof x === "object" &&
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
+
x?.[Symbol.for("LG_SKIP_WRITE")] !== undefined);
|
|
14
|
+
}
|
|
15
|
+
exports.PASSTHROUGH = {
|
|
16
|
+
[Symbol.for("LG_PASSTHROUGH")]: true,
|
|
17
|
+
};
|
|
18
|
+
function _isPassthrough(x) {
|
|
19
|
+
return (typeof x === "object" &&
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
|
+
x?.[Symbol.for("LG_PASSTHROUGH")] !== undefined);
|
|
22
|
+
}
|
|
8
23
|
const IS_WRITER = Symbol("IS_WRITER");
|
|
9
24
|
/**
|
|
10
25
|
* Mapping of write channels to Runnables that return the value to be written,
|
|
@@ -13,7 +28,9 @@ const IS_WRITER = Symbol("IS_WRITER");
|
|
|
13
28
|
class ChannelWrite extends utils_js_1.RunnableCallable {
|
|
14
29
|
constructor(writes, tags) {
|
|
15
30
|
const name = `ChannelWrite<${writes
|
|
16
|
-
.map((
|
|
31
|
+
.map((packet) => {
|
|
32
|
+
return (0, constants_js_1._isSend)(packet) ? packet.node : packet.channel;
|
|
33
|
+
})
|
|
17
34
|
.join(",")}>`;
|
|
18
35
|
super({
|
|
19
36
|
...{ writes, name, tags },
|
|
@@ -28,34 +45,53 @@ class ChannelWrite extends utils_js_1.RunnableCallable {
|
|
|
28
45
|
this.writes = writes;
|
|
29
46
|
}
|
|
30
47
|
async _getWriteValues(input, config) {
|
|
31
|
-
|
|
32
|
-
.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
const writes = this.writes
|
|
49
|
+
.filter(constants_js_1._isSend)
|
|
50
|
+
.map((packet) => {
|
|
51
|
+
return [constants_js_1.TASKS, packet];
|
|
52
|
+
});
|
|
53
|
+
const entries = this.writes.filter((write) => {
|
|
54
|
+
return !(0, constants_js_1._isSend)(write);
|
|
55
|
+
});
|
|
56
|
+
const invalidEntry = entries.find((write) => {
|
|
57
|
+
return write.channel === constants_js_1.TASKS;
|
|
58
|
+
});
|
|
59
|
+
if (invalidEntry) {
|
|
60
|
+
throw new errors_js_1.InvalidUpdateError(`Cannot write to the reserved channel ${constants_js_1.TASKS}`);
|
|
61
|
+
}
|
|
62
|
+
const values = await Promise.all(entries.map(async (write) => {
|
|
63
|
+
let value;
|
|
64
|
+
if (_isPassthrough(write.value)) {
|
|
65
|
+
value = input;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
value = write.value;
|
|
69
|
+
}
|
|
70
|
+
const mappedValue = write.mapper
|
|
71
|
+
? await write.mapper.invoke(value, config)
|
|
72
|
+
: value;
|
|
73
|
+
return {
|
|
74
|
+
...write,
|
|
75
|
+
value: mappedValue,
|
|
76
|
+
};
|
|
77
|
+
})).then((writes) => {
|
|
78
|
+
return writes
|
|
79
|
+
.filter((write) => !write.skipNone || write.value !== null)
|
|
80
|
+
.map((write) => {
|
|
81
|
+
return [write.channel, write.value];
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
return [...writes, ...values];
|
|
51
85
|
}
|
|
52
86
|
async _write(input, config) {
|
|
53
87
|
const values = await this._getWriteValues(input, config);
|
|
54
88
|
ChannelWrite.doWrite(config, values);
|
|
55
89
|
}
|
|
90
|
+
// TODO: Support requireAtLeastOneOf
|
|
56
91
|
static doWrite(config, values) {
|
|
57
92
|
const write = config.configurable?.[constants_js_1.CONFIG_KEY_SEND];
|
|
58
|
-
|
|
93
|
+
const filtered = values.filter(([_, value]) => !_isSkipWrite(value));
|
|
94
|
+
write(filtered);
|
|
59
95
|
}
|
|
60
96
|
static isWriter(runnable) {
|
|
61
97
|
return (
|
package/dist/pregel/write.d.ts
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import { Runnable, RunnableConfig, RunnableLike } from "@langchain/core/runnables";
|
|
2
|
+
import { Send } from "../constants.js";
|
|
2
3
|
import { RunnableCallable } from "../utils.js";
|
|
3
|
-
export declare const SKIP_WRITE: {
|
|
4
|
-
|
|
4
|
+
export declare const SKIP_WRITE: {
|
|
5
|
+
[x: symbol]: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare const PASSTHROUGH: {
|
|
8
|
+
[x: symbol]: boolean;
|
|
9
|
+
};
|
|
5
10
|
/**
|
|
6
11
|
* Mapping of write channels to Runnables that return the value to be written,
|
|
7
12
|
* or None to skip writing.
|
|
8
13
|
*/
|
|
9
14
|
export declare class ChannelWrite<RunInput = any> extends RunnableCallable {
|
|
10
|
-
writes: Array<ChannelWriteEntry>;
|
|
11
|
-
constructor(writes: Array<ChannelWriteEntry>, tags?: string[]);
|
|
12
|
-
_getWriteValues(input: unknown, config: RunnableConfig): Promise<
|
|
15
|
+
writes: Array<ChannelWriteEntry | Send>;
|
|
16
|
+
constructor(writes: Array<ChannelWriteEntry | Send>, tags?: string[]);
|
|
17
|
+
_getWriteValues(input: unknown, config: RunnableConfig): Promise<[string, unknown][]>;
|
|
13
18
|
_write(input: unknown, config: RunnableConfig): Promise<void>;
|
|
14
|
-
static doWrite(config: RunnableConfig, values:
|
|
19
|
+
static doWrite(config: RunnableConfig, values: [string, unknown][]): void;
|
|
15
20
|
static isWriter(runnable: RunnableLike): boolean;
|
|
16
21
|
static registerWriter<T extends Runnable>(runnable: T): T;
|
|
17
22
|
}
|
package/dist/pregel/write.js
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
|
-
import { CONFIG_KEY_SEND } from "../constants.js";
|
|
1
|
+
import { _isSend, CONFIG_KEY_SEND, TASKS } from "../constants.js";
|
|
2
2
|
import { RunnableCallable } from "../utils.js";
|
|
3
|
-
|
|
4
|
-
export const
|
|
3
|
+
import { InvalidUpdateError } from "../errors.js";
|
|
4
|
+
export const SKIP_WRITE = {
|
|
5
|
+
[Symbol.for("LG_SKIP_WRITE")]: true,
|
|
6
|
+
};
|
|
7
|
+
function _isSkipWrite(x) {
|
|
8
|
+
return (typeof x === "object" &&
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
+
x?.[Symbol.for("LG_SKIP_WRITE")] !== undefined);
|
|
11
|
+
}
|
|
12
|
+
export const PASSTHROUGH = {
|
|
13
|
+
[Symbol.for("LG_PASSTHROUGH")]: true,
|
|
14
|
+
};
|
|
15
|
+
function _isPassthrough(x) {
|
|
16
|
+
return (typeof x === "object" &&
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
x?.[Symbol.for("LG_PASSTHROUGH")] !== undefined);
|
|
19
|
+
}
|
|
5
20
|
const IS_WRITER = Symbol("IS_WRITER");
|
|
6
21
|
/**
|
|
7
22
|
* Mapping of write channels to Runnables that return the value to be written,
|
|
@@ -10,7 +25,9 @@ const IS_WRITER = Symbol("IS_WRITER");
|
|
|
10
25
|
export class ChannelWrite extends RunnableCallable {
|
|
11
26
|
constructor(writes, tags) {
|
|
12
27
|
const name = `ChannelWrite<${writes
|
|
13
|
-
.map((
|
|
28
|
+
.map((packet) => {
|
|
29
|
+
return _isSend(packet) ? packet.node : packet.channel;
|
|
30
|
+
})
|
|
14
31
|
.join(",")}>`;
|
|
15
32
|
super({
|
|
16
33
|
...{ writes, name, tags },
|
|
@@ -25,34 +42,53 @@ export class ChannelWrite extends RunnableCallable {
|
|
|
25
42
|
this.writes = writes;
|
|
26
43
|
}
|
|
27
44
|
async _getWriteValues(input, config) {
|
|
28
|
-
|
|
29
|
-
.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
const writes = this.writes
|
|
46
|
+
.filter(_isSend)
|
|
47
|
+
.map((packet) => {
|
|
48
|
+
return [TASKS, packet];
|
|
49
|
+
});
|
|
50
|
+
const entries = this.writes.filter((write) => {
|
|
51
|
+
return !_isSend(write);
|
|
52
|
+
});
|
|
53
|
+
const invalidEntry = entries.find((write) => {
|
|
54
|
+
return write.channel === TASKS;
|
|
55
|
+
});
|
|
56
|
+
if (invalidEntry) {
|
|
57
|
+
throw new InvalidUpdateError(`Cannot write to the reserved channel ${TASKS}`);
|
|
58
|
+
}
|
|
59
|
+
const values = await Promise.all(entries.map(async (write) => {
|
|
60
|
+
let value;
|
|
61
|
+
if (_isPassthrough(write.value)) {
|
|
62
|
+
value = input;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
value = write.value;
|
|
66
|
+
}
|
|
67
|
+
const mappedValue = write.mapper
|
|
68
|
+
? await write.mapper.invoke(value, config)
|
|
69
|
+
: value;
|
|
70
|
+
return {
|
|
71
|
+
...write,
|
|
72
|
+
value: mappedValue,
|
|
73
|
+
};
|
|
74
|
+
})).then((writes) => {
|
|
75
|
+
return writes
|
|
76
|
+
.filter((write) => !write.skipNone || write.value !== null)
|
|
77
|
+
.map((write) => {
|
|
78
|
+
return [write.channel, write.value];
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
return [...writes, ...values];
|
|
48
82
|
}
|
|
49
83
|
async _write(input, config) {
|
|
50
84
|
const values = await this._getWriteValues(input, config);
|
|
51
85
|
ChannelWrite.doWrite(config, values);
|
|
52
86
|
}
|
|
87
|
+
// TODO: Support requireAtLeastOneOf
|
|
53
88
|
static doWrite(config, values) {
|
|
54
89
|
const write = config.configurable?.[CONFIG_KEY_SEND];
|
|
55
|
-
|
|
90
|
+
const filtered = values.filter(([_, value]) => !_isSkipWrite(value));
|
|
91
|
+
write(filtered);
|
|
56
92
|
}
|
|
57
93
|
static isWriter(runnable) {
|
|
58
94
|
return (
|