@langchain/langgraph 0.2.54 → 0.2.56
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 +38 -302
- package/dist/interrupt.cjs +5 -2
- package/dist/interrupt.d.ts +4 -0
- package/dist/interrupt.js +5 -2
- package/dist/interrupt.js.map +1 -1
- package/dist/pregel/algo.cjs +14 -23
- package/dist/pregel/algo.js +14 -23
- package/dist/pregel/algo.js.map +1 -1
- package/dist/pregel/index.cjs +276 -210
- package/dist/pregel/index.d.ts +20 -0
- package/dist/pregel/index.js +276 -210
- package/dist/pregel/index.js.map +1 -1
- package/dist/pregel/loop.cjs +13 -19
- package/dist/pregel/loop.d.ts +0 -4
- package/dist/pregel/loop.js +13 -19
- package/dist/pregel/loop.js.map +1 -1
- package/dist/pregel/runner.cjs +13 -10
- package/dist/pregel/runner.js +13 -10
- package/dist/pregel/runner.js.map +1 -1
- package/dist/pregel/types.d.ts +1 -0
- package/dist/pregel/types.js.map +1 -1
- package/package.json +2 -2
package/dist/pregel/index.js
CHANGED
|
@@ -673,89 +673,162 @@ export class Pregel extends Runnable {
|
|
|
673
673
|
}
|
|
674
674
|
}
|
|
675
675
|
/**
|
|
676
|
-
*
|
|
676
|
+
* Apply updates to the graph state in bulk.
|
|
677
677
|
* Requires a checkpointer to be configured.
|
|
678
678
|
*
|
|
679
|
-
* This method
|
|
680
|
-
*
|
|
681
|
-
*
|
|
682
|
-
* - Integrating external inputs into the graph
|
|
679
|
+
* This method is useful for recreating a thread
|
|
680
|
+
* from a list of updates, especially if a checkpoint
|
|
681
|
+
* is created as a result of multiple tasks.
|
|
683
682
|
*
|
|
684
|
-
* @param
|
|
685
|
-
* @param
|
|
686
|
-
* @param asNode - Optional node name to attribute the update to
|
|
683
|
+
* @param startConfig - Configuration for the update
|
|
684
|
+
* @param updates - The list of updates to apply to graph state
|
|
687
685
|
* @returns Updated configuration
|
|
688
686
|
* @throws {GraphValueError} If no checkpointer is configured
|
|
689
|
-
* @throws {InvalidUpdateError} If the update cannot be attributed to a node
|
|
687
|
+
* @throws {InvalidUpdateError} If the update cannot be attributed to a node or an update can be only applied in sequence.
|
|
690
688
|
*/
|
|
691
|
-
async
|
|
692
|
-
const checkpointer =
|
|
689
|
+
async bulkUpdateState(startConfig, supersteps) {
|
|
690
|
+
const checkpointer = startConfig.configurable?.[CONFIG_KEY_CHECKPOINTER] ?? this.checkpointer;
|
|
693
691
|
if (!checkpointer) {
|
|
694
692
|
throw new GraphValueError("No checkpointer set");
|
|
695
693
|
}
|
|
694
|
+
if (supersteps.length === 0) {
|
|
695
|
+
throw new Error("No supersteps provided");
|
|
696
|
+
}
|
|
697
|
+
if (supersteps.some((s) => s.updates.length === 0)) {
|
|
698
|
+
throw new Error("No updates provided");
|
|
699
|
+
}
|
|
696
700
|
// delegate to subgraph
|
|
697
|
-
const checkpointNamespace =
|
|
701
|
+
const checkpointNamespace = startConfig.configurable?.checkpoint_ns ?? "";
|
|
698
702
|
if (checkpointNamespace !== "" &&
|
|
699
|
-
|
|
703
|
+
startConfig.configurable?.[CONFIG_KEY_CHECKPOINTER] === undefined) {
|
|
700
704
|
// remove task_ids from checkpoint_ns
|
|
701
705
|
const recastNamespace = recastCheckpointNamespace(checkpointNamespace);
|
|
702
706
|
// find the subgraph with the matching name
|
|
703
707
|
// eslint-disable-next-line no-unreachable-loop
|
|
704
708
|
for await (const [, pregel] of this.getSubgraphsAsync(recastNamespace, true)) {
|
|
705
|
-
return await pregel.
|
|
709
|
+
return await pregel.bulkUpdateState(patchConfigurable(startConfig, {
|
|
706
710
|
[CONFIG_KEY_CHECKPOINTER]: checkpointer,
|
|
707
|
-
}),
|
|
711
|
+
}), supersteps);
|
|
708
712
|
}
|
|
709
713
|
throw new Error(`Subgraph "${recastNamespace}" not found`);
|
|
710
714
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
const step = saved?.metadata?.step ?? -1;
|
|
723
|
-
// merge configurable fields with previous checkpoint config
|
|
724
|
-
let checkpointConfig = patchConfigurable(config, {
|
|
725
|
-
checkpoint_ns: config.configurable?.checkpoint_ns ?? "",
|
|
726
|
-
});
|
|
727
|
-
let checkpointMetadata = config.metadata ?? {};
|
|
728
|
-
if (saved?.config.configurable) {
|
|
729
|
-
checkpointConfig = patchConfigurable(config, saved.config.configurable);
|
|
730
|
-
checkpointMetadata = {
|
|
731
|
-
...saved.metadata,
|
|
732
|
-
...checkpointMetadata,
|
|
715
|
+
const updateSuperStep = async (inputConfig, updates) => {
|
|
716
|
+
// get last checkpoint
|
|
717
|
+
const config = this.config
|
|
718
|
+
? mergeConfigs(this.config, inputConfig)
|
|
719
|
+
: inputConfig;
|
|
720
|
+
const saved = await checkpointer.getTuple(config);
|
|
721
|
+
const checkpoint = saved !== undefined
|
|
722
|
+
? copyCheckpoint(saved.checkpoint)
|
|
723
|
+
: emptyCheckpoint();
|
|
724
|
+
const checkpointPreviousVersions = {
|
|
725
|
+
...saved?.checkpoint.channel_versions,
|
|
733
726
|
};
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
727
|
+
const step = saved?.metadata?.step ?? -1;
|
|
728
|
+
// merge configurable fields with previous checkpoint config
|
|
729
|
+
let checkpointConfig = patchConfigurable(config, {
|
|
730
|
+
checkpoint_ns: config.configurable?.checkpoint_ns ?? "",
|
|
731
|
+
});
|
|
732
|
+
let checkpointMetadata = config.metadata ?? {};
|
|
733
|
+
if (saved?.config.configurable) {
|
|
734
|
+
checkpointConfig = patchConfigurable(config, saved.config.configurable);
|
|
735
|
+
checkpointMetadata = {
|
|
736
|
+
...saved.metadata,
|
|
737
|
+
...checkpointMetadata,
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
// Find last node that updated the state, if not provided
|
|
741
|
+
const { values, asNode } = updates[0];
|
|
742
|
+
if (values == null && asNode === undefined) {
|
|
743
|
+
if (updates.length > 1) {
|
|
744
|
+
throw new InvalidUpdateError(`Cannot create empty checkpoint with multiple updates`);
|
|
745
|
+
}
|
|
746
|
+
const nextConfig = await checkpointer.put(checkpointConfig, createCheckpoint(checkpoint, undefined, step), {
|
|
747
|
+
source: "update",
|
|
748
|
+
step: step + 1,
|
|
749
|
+
writes: {},
|
|
750
|
+
parents: saved?.metadata?.parents ?? {},
|
|
751
|
+
}, {});
|
|
752
|
+
return patchCheckpointMap(nextConfig, saved ? saved.metadata : undefined);
|
|
753
|
+
}
|
|
754
|
+
// update channels
|
|
755
|
+
const channels = emptyChannels(this.channels, checkpoint);
|
|
756
|
+
// Pass `skipManaged: true` as managed values are not used/relevant in update state calls.
|
|
757
|
+
const { managed } = await this.prepareSpecs(config, {
|
|
758
|
+
skipManaged: true,
|
|
759
|
+
});
|
|
760
|
+
if (values === null && asNode === "__end__") {
|
|
761
|
+
if (updates.length > 1) {
|
|
762
|
+
throw new InvalidUpdateError(`Cannot apply multiple updates when clearing state`);
|
|
763
|
+
}
|
|
764
|
+
if (saved) {
|
|
765
|
+
// tasks for this checkpoint
|
|
766
|
+
const nextTasks = _prepareNextTasks(checkpoint, saved.pendingWrites || [], this.nodes, channels, managed, saved.config, true, {
|
|
767
|
+
step: (saved.metadata?.step ?? -1) + 1,
|
|
768
|
+
checkpointer: this.checkpointer || undefined,
|
|
769
|
+
store: this.store,
|
|
770
|
+
});
|
|
771
|
+
// apply null writes
|
|
772
|
+
const nullWrites = (saved.pendingWrites || [])
|
|
773
|
+
.filter((w) => w[0] === NULL_TASK_ID)
|
|
774
|
+
.map((w) => w.slice(1));
|
|
775
|
+
if (nullWrites.length > 0) {
|
|
776
|
+
_applyWrites(saved.checkpoint, channels, [
|
|
777
|
+
{
|
|
778
|
+
name: INPUT,
|
|
779
|
+
writes: nullWrites,
|
|
780
|
+
triggers: [],
|
|
781
|
+
},
|
|
782
|
+
]);
|
|
783
|
+
}
|
|
784
|
+
// apply writes from tasks that already ran
|
|
785
|
+
for (const [taskId, k, v] of saved.pendingWrites || []) {
|
|
786
|
+
if ([ERROR, INTERRUPT, SCHEDULED].includes(k)) {
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
if (!(taskId in nextTasks)) {
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
nextTasks[taskId].writes.push([k, v]);
|
|
793
|
+
}
|
|
794
|
+
// clear all current tasks
|
|
795
|
+
_applyWrites(checkpoint, channels, Object.values(nextTasks));
|
|
796
|
+
}
|
|
797
|
+
// save checkpoint
|
|
798
|
+
const nextConfig = await checkpointer.put(checkpointConfig, createCheckpoint(checkpoint, undefined, step), {
|
|
799
|
+
...checkpointMetadata,
|
|
800
|
+
source: "update",
|
|
801
|
+
step: step + 1,
|
|
802
|
+
writes: {},
|
|
803
|
+
parents: saved?.metadata?.parents ?? {},
|
|
804
|
+
}, {});
|
|
805
|
+
return patchCheckpointMap(nextConfig, saved ? saved.metadata : undefined);
|
|
806
|
+
}
|
|
807
|
+
if (values == null && asNode === "__copy__") {
|
|
808
|
+
if (updates.length > 1) {
|
|
809
|
+
throw new InvalidUpdateError(`Cannot copy checkpoint with multiple updates`);
|
|
810
|
+
}
|
|
811
|
+
const nextConfig = await checkpointer.put(saved?.parentConfig ?? checkpointConfig, createCheckpoint(checkpoint, undefined, step), {
|
|
812
|
+
source: "fork",
|
|
813
|
+
step: step + 1,
|
|
814
|
+
writes: {},
|
|
815
|
+
parents: saved?.metadata?.parents ?? {},
|
|
816
|
+
}, {});
|
|
817
|
+
return patchCheckpointMap(nextConfig, saved ? saved.metadata : undefined);
|
|
818
|
+
}
|
|
819
|
+
// apply pending writes, if not on specific checkpoint
|
|
820
|
+
if (config.configurable?.checkpoint_id === undefined &&
|
|
821
|
+
saved?.pendingWrites !== undefined &&
|
|
822
|
+
saved.pendingWrites.length > 0) {
|
|
751
823
|
// tasks for this checkpoint
|
|
752
|
-
const nextTasks = _prepareNextTasks(checkpoint, saved.pendingWrites
|
|
753
|
-
step: (saved.metadata?.step ?? -1) + 1,
|
|
754
|
-
checkpointer: this.checkpointer || undefined,
|
|
824
|
+
const nextTasks = _prepareNextTasks(checkpoint, saved.pendingWrites, this.nodes, channels, managed, saved.config, true, {
|
|
755
825
|
store: this.store,
|
|
826
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
827
|
+
checkpointer: this.checkpointer,
|
|
828
|
+
step: (saved.metadata?.step ?? -1) + 1,
|
|
756
829
|
});
|
|
757
830
|
// apply null writes
|
|
758
|
-
const nullWrites = (saved.pendingWrites
|
|
831
|
+
const nullWrites = (saved.pendingWrites ?? [])
|
|
759
832
|
.filter((w) => w[0] === NULL_TASK_ID)
|
|
760
833
|
.map((w) => w.slice(1));
|
|
761
834
|
if (nullWrites.length > 0) {
|
|
@@ -767,172 +840,165 @@ export class Pregel extends Runnable {
|
|
|
767
840
|
},
|
|
768
841
|
]);
|
|
769
842
|
}
|
|
770
|
-
// apply writes
|
|
771
|
-
for (const [
|
|
772
|
-
if ([ERROR, INTERRUPT, SCHEDULED].includes(k)
|
|
773
|
-
|
|
774
|
-
}
|
|
775
|
-
if (!(taskId in nextTasks)) {
|
|
843
|
+
// apply writes
|
|
844
|
+
for (const [tid, k, v] of saved.pendingWrites) {
|
|
845
|
+
if ([ERROR, INTERRUPT, SCHEDULED].includes(k) ||
|
|
846
|
+
nextTasks[tid] === undefined) {
|
|
776
847
|
continue;
|
|
777
848
|
}
|
|
778
|
-
nextTasks[
|
|
849
|
+
nextTasks[tid].writes.push([k, v]);
|
|
850
|
+
}
|
|
851
|
+
const tasks = Object.values(nextTasks).filter((task) => {
|
|
852
|
+
return task.writes.length > 0;
|
|
853
|
+
});
|
|
854
|
+
if (tasks.length > 0) {
|
|
855
|
+
_applyWrites(checkpoint, channels, tasks);
|
|
779
856
|
}
|
|
780
|
-
// clear all current tasks
|
|
781
|
-
_applyWrites(checkpoint, channels, Object.values(nextTasks));
|
|
782
857
|
}
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
858
|
+
const nonNullVersion = Object.values(checkpoint.versions_seen)
|
|
859
|
+
.map((seenVersions) => {
|
|
860
|
+
return Object.values(seenVersions);
|
|
861
|
+
})
|
|
862
|
+
.flat()
|
|
863
|
+
.find((v) => !!v);
|
|
864
|
+
const validUpdates = [];
|
|
865
|
+
if (updates.length === 1) {
|
|
866
|
+
// eslint-disable-next-line prefer-const
|
|
867
|
+
let { values, asNode } = updates[0];
|
|
868
|
+
if (asNode === undefined && nonNullVersion === undefined) {
|
|
869
|
+
if (typeof this.inputChannels === "string" &&
|
|
870
|
+
this.nodes[this.inputChannels] !== undefined) {
|
|
871
|
+
asNode = this.inputChannels;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
else if (asNode === undefined) {
|
|
875
|
+
const lastSeenByNode = Object.entries(checkpoint.versions_seen)
|
|
876
|
+
.map(([n, seen]) => {
|
|
877
|
+
return Object.values(seen).map((v) => {
|
|
878
|
+
return [v, n];
|
|
879
|
+
});
|
|
880
|
+
})
|
|
881
|
+
.flat()
|
|
882
|
+
.sort(([aNumber], [bNumber]) => compareChannelVersions(aNumber, bNumber));
|
|
883
|
+
// if two nodes updated the state at the same time, it's ambiguous
|
|
884
|
+
if (lastSeenByNode) {
|
|
885
|
+
if (lastSeenByNode.length === 1) {
|
|
886
|
+
// eslint-disable-next-line prefer-destructuring
|
|
887
|
+
asNode = lastSeenByNode[0][1];
|
|
888
|
+
}
|
|
889
|
+
else if (lastSeenByNode[lastSeenByNode.length - 1][0] !==
|
|
890
|
+
lastSeenByNode[lastSeenByNode.length - 2][0]) {
|
|
891
|
+
// eslint-disable-next-line prefer-destructuring
|
|
892
|
+
asNode = lastSeenByNode[lastSeenByNode.length - 1][1];
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
if (asNode === undefined) {
|
|
897
|
+
throw new InvalidUpdateError(`Ambiguous update, specify "asNode"`);
|
|
898
|
+
}
|
|
899
|
+
validUpdates.push({ values, asNode });
|
|
825
900
|
}
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
901
|
+
else {
|
|
902
|
+
for (const { asNode, values } of updates) {
|
|
903
|
+
if (asNode == null) {
|
|
904
|
+
throw new InvalidUpdateError(`"asNode" is required when applying multiple updates`);
|
|
905
|
+
}
|
|
906
|
+
validUpdates.push({ values, asNode });
|
|
831
907
|
}
|
|
832
|
-
nextTasks[tid].writes.push([k, v]);
|
|
833
908
|
}
|
|
834
|
-
const tasks =
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
909
|
+
const tasks = [];
|
|
910
|
+
for (const { asNode, values } of validUpdates) {
|
|
911
|
+
if (this.nodes[asNode] === undefined) {
|
|
912
|
+
throw new InvalidUpdateError(`Node "${asNode.toString()}" does not exist`);
|
|
913
|
+
}
|
|
914
|
+
// run all writers of the chosen node
|
|
915
|
+
const writers = this.nodes[asNode].getWriters();
|
|
916
|
+
if (!writers.length) {
|
|
917
|
+
throw new InvalidUpdateError(`No writers found for node "${asNode.toString()}"`);
|
|
918
|
+
}
|
|
919
|
+
tasks.push({
|
|
920
|
+
name: asNode,
|
|
921
|
+
input: values,
|
|
922
|
+
proc: writers.length > 1
|
|
923
|
+
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
924
|
+
RunnableSequence.from(writers, {
|
|
925
|
+
omitSequenceTags: true,
|
|
926
|
+
})
|
|
927
|
+
: writers[0],
|
|
928
|
+
writes: [],
|
|
929
|
+
triggers: [INTERRUPT],
|
|
930
|
+
id: uuid5(INTERRUPT, checkpoint.id),
|
|
931
|
+
writers: [],
|
|
932
|
+
});
|
|
839
933
|
}
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
934
|
+
for (const task of tasks) {
|
|
935
|
+
// execute task
|
|
936
|
+
await task.proc.invoke(task.input, patchConfig({
|
|
937
|
+
...config,
|
|
938
|
+
store: config?.store ?? this.store,
|
|
939
|
+
}, {
|
|
940
|
+
runName: config.runName ?? `${this.getName()}UpdateState`,
|
|
941
|
+
configurable: {
|
|
942
|
+
[CONFIG_KEY_SEND]: (items) => task.writes.push(...items),
|
|
943
|
+
[CONFIG_KEY_READ]: (select_, fresh_ = false) => _localRead(step, checkpoint, channels, managed,
|
|
944
|
+
// TODO: Why does keyof StrRecord allow number and symbol?
|
|
945
|
+
task, select_, fresh_),
|
|
946
|
+
},
|
|
947
|
+
}));
|
|
851
948
|
}
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
});
|
|
859
|
-
})
|
|
860
|
-
.flat()
|
|
861
|
-
.sort(([aNumber], [bNumber]) => compareChannelVersions(aNumber, bNumber));
|
|
862
|
-
// if two nodes updated the state at the same time, it's ambiguous
|
|
863
|
-
if (lastSeenByNode) {
|
|
864
|
-
if (lastSeenByNode.length === 1) {
|
|
865
|
-
// eslint-disable-next-line prefer-destructuring
|
|
866
|
-
asNode = lastSeenByNode[0][1];
|
|
949
|
+
for (const task of tasks) {
|
|
950
|
+
// channel writes are saved to current checkpoint
|
|
951
|
+
const channelWrites = task.writes.filter((w) => w[0] !== PUSH);
|
|
952
|
+
// save task writes
|
|
953
|
+
if (saved !== undefined && channelWrites.length > 0) {
|
|
954
|
+
await checkpointer.putWrites(checkpointConfig, channelWrites, task.id);
|
|
867
955
|
}
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
956
|
+
}
|
|
957
|
+
// apply to checkpoint
|
|
958
|
+
// TODO: Why does keyof StrRecord allow number and symbol?
|
|
959
|
+
_applyWrites(checkpoint, channels, tasks, checkpointer.getNextVersion.bind(this.checkpointer));
|
|
960
|
+
const newVersions = getNewChannelVersions(checkpointPreviousVersions, checkpoint.channel_versions);
|
|
961
|
+
const nextConfig = await checkpointer.put(checkpointConfig, createCheckpoint(checkpoint, channels, step + 1), {
|
|
962
|
+
source: "update",
|
|
963
|
+
step: step + 1,
|
|
964
|
+
writes: Object.fromEntries(validUpdates.map((update) => [update.asNode, update.values])),
|
|
965
|
+
parents: saved?.metadata?.parents ?? {},
|
|
966
|
+
}, newVersions);
|
|
967
|
+
for (const task of tasks) {
|
|
968
|
+
// push writes are saved to next checkpoint
|
|
969
|
+
const pushWrites = task.writes.filter((w) => w[0] === PUSH);
|
|
970
|
+
if (pushWrites.length > 0) {
|
|
971
|
+
await checkpointer.putWrites(nextConfig, pushWrites, task.id);
|
|
872
972
|
}
|
|
873
973
|
}
|
|
874
|
-
|
|
875
|
-
if (asNode === undefined) {
|
|
876
|
-
throw new InvalidUpdateError(`Ambiguous update, specify "asNode"`);
|
|
877
|
-
}
|
|
878
|
-
if (this.nodes[asNode] === undefined) {
|
|
879
|
-
throw new InvalidUpdateError(`Node "${asNode.toString()}" does not exist`);
|
|
880
|
-
}
|
|
881
|
-
// run all writers of the chosen node
|
|
882
|
-
const writers = this.nodes[asNode].getWriters();
|
|
883
|
-
if (!writers.length) {
|
|
884
|
-
throw new InvalidUpdateError(`No writers found for node "${asNode.toString()}"`);
|
|
885
|
-
}
|
|
886
|
-
const task = {
|
|
887
|
-
name: asNode,
|
|
888
|
-
input: values,
|
|
889
|
-
proc: writers.length > 1
|
|
890
|
-
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
891
|
-
RunnableSequence.from(writers, { omitSequenceTags: true })
|
|
892
|
-
: writers[0],
|
|
893
|
-
writes: [],
|
|
894
|
-
triggers: [INTERRUPT],
|
|
895
|
-
id: uuid5(INTERRUPT, checkpoint.id),
|
|
896
|
-
writers: [],
|
|
974
|
+
return patchCheckpointMap(nextConfig, saved ? saved.metadata : undefined);
|
|
897
975
|
};
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
store: config?.store ?? this.store,
|
|
902
|
-
}, {
|
|
903
|
-
runName: config.runName ?? `${this.getName()}UpdateState`,
|
|
904
|
-
configurable: {
|
|
905
|
-
[CONFIG_KEY_SEND]: (items) => task.writes.push(...items),
|
|
906
|
-
[CONFIG_KEY_READ]: (select_, fresh_ = false) => _localRead(step, checkpoint, channels, managed,
|
|
907
|
-
// TODO: Why does keyof StrRecord allow number and symbol?
|
|
908
|
-
task, select_, fresh_),
|
|
909
|
-
},
|
|
910
|
-
}));
|
|
911
|
-
// save task writes
|
|
912
|
-
// channel writes are saved to current checkpoint
|
|
913
|
-
// push writes are saved to next checkpoint
|
|
914
|
-
const [channelWrites, pushWrites] = [
|
|
915
|
-
task.writes.filter((w) => w[0] !== PUSH),
|
|
916
|
-
task.writes.filter((w) => w[0] === PUSH),
|
|
917
|
-
];
|
|
918
|
-
// save task writes
|
|
919
|
-
if (saved !== undefined && channelWrites.length > 0) {
|
|
920
|
-
await checkpointer.putWrites(checkpointConfig, channelWrites, task.id);
|
|
976
|
+
let currentConfig = startConfig;
|
|
977
|
+
for (const { updates } of supersteps) {
|
|
978
|
+
currentConfig = await updateSuperStep(currentConfig, updates);
|
|
921
979
|
}
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
980
|
+
return currentConfig;
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
983
|
+
* Updates the state of the graph with new values.
|
|
984
|
+
* Requires a checkpointer to be configured.
|
|
985
|
+
*
|
|
986
|
+
* This method can be used for:
|
|
987
|
+
* - Implementing human-in-the-loop workflows
|
|
988
|
+
* - Modifying graph state during breakpoints
|
|
989
|
+
* - Integrating external inputs into the graph
|
|
990
|
+
*
|
|
991
|
+
* @param inputConfig - Configuration for the update
|
|
992
|
+
* @param values - The values to update the state with
|
|
993
|
+
* @param asNode - Optional node name to attribute the update to
|
|
994
|
+
* @returns Updated configuration
|
|
995
|
+
* @throws {GraphValueError} If no checkpointer is configured
|
|
996
|
+
* @throws {InvalidUpdateError} If the update cannot be attributed to a node
|
|
997
|
+
*/
|
|
998
|
+
async updateState(inputConfig, values, asNode) {
|
|
999
|
+
return this.bulkUpdateState(inputConfig, [
|
|
1000
|
+
{ updates: [{ values, asNode }] },
|
|
1001
|
+
]);
|
|
936
1002
|
}
|
|
937
1003
|
/**
|
|
938
1004
|
* Gets the default values for various graph configuration options.
|