@langchain/langgraph 0.2.38 → 0.2.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/pregel/index.cjs +102 -62
- package/dist/pregel/index.d.ts +10 -0
- package/dist/pregel/index.js +102 -62
- package/dist/pregel/io.cjs +4 -2
- package/dist/pregel/io.js +5 -3
- package/dist/pregel/loop.cjs +107 -107
- package/dist/pregel/loop.d.ts +1 -0
- package/dist/pregel/loop.js +107 -107
- package/package.json +1 -1
package/dist/pregel/index.cjs
CHANGED
|
@@ -824,6 +824,92 @@ class Pregel extends runnables_1.Runnable {
|
|
|
824
824
|
managed,
|
|
825
825
|
};
|
|
826
826
|
}
|
|
827
|
+
async _runLoop(params) {
|
|
828
|
+
const { loop, interruptAfter, interruptBefore, runManager, debug, config } = params;
|
|
829
|
+
let tickError;
|
|
830
|
+
try {
|
|
831
|
+
while (await loop.tick({
|
|
832
|
+
inputKeys: this.inputChannels,
|
|
833
|
+
interruptAfter,
|
|
834
|
+
interruptBefore,
|
|
835
|
+
manager: runManager,
|
|
836
|
+
})) {
|
|
837
|
+
if (debug) {
|
|
838
|
+
(0, debug_js_1.printStepCheckpoint)(loop.checkpointMetadata.step, loop.channels, this.streamChannelsList);
|
|
839
|
+
}
|
|
840
|
+
if (debug) {
|
|
841
|
+
(0, debug_js_1.printStepTasks)(loop.step, Object.values(loop.tasks));
|
|
842
|
+
}
|
|
843
|
+
// execute tasks, and wait for one to fail or all to finish.
|
|
844
|
+
// each task is independent from all other concurrent tasks
|
|
845
|
+
// yield updates/debug output as each task finishes
|
|
846
|
+
const taskStream = (0, retry_js_1.executeTasksWithRetry)(Object.values(loop.tasks).filter((task) => task.writes.length === 0), {
|
|
847
|
+
stepTimeout: this.stepTimeout,
|
|
848
|
+
signal: config.signal,
|
|
849
|
+
retryPolicy: this.retryPolicy,
|
|
850
|
+
});
|
|
851
|
+
let graphInterrupt;
|
|
852
|
+
for await (const { task, error } of taskStream) {
|
|
853
|
+
if (error !== undefined) {
|
|
854
|
+
if ((0, errors_js_1.isGraphBubbleUp)(error)) {
|
|
855
|
+
if (loop.isNested) {
|
|
856
|
+
throw error;
|
|
857
|
+
}
|
|
858
|
+
if ((0, errors_js_1.isGraphInterrupt)(error)) {
|
|
859
|
+
graphInterrupt = error;
|
|
860
|
+
if (error.interrupts.length) {
|
|
861
|
+
const interrupts = error.interrupts.map((interrupt) => [constants_js_1.INTERRUPT, interrupt]);
|
|
862
|
+
const resumes = task.writes.filter((w) => w[0] === constants_js_1.RESUME);
|
|
863
|
+
if (resumes.length) {
|
|
864
|
+
interrupts.push(...resumes);
|
|
865
|
+
}
|
|
866
|
+
loop.putWrites(task.id, interrupts);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
else {
|
|
871
|
+
loop.putWrites(task.id, [
|
|
872
|
+
[constants_js_1.ERROR, { message: error.message, name: error.name }],
|
|
873
|
+
]);
|
|
874
|
+
throw error;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
else {
|
|
878
|
+
loop.putWrites(task.id, task.writes);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
if (debug) {
|
|
882
|
+
(0, debug_js_1.printStepWrites)(loop.step, Object.values(loop.tasks)
|
|
883
|
+
.map((task) => task.writes)
|
|
884
|
+
.flat(), this.streamChannelsList);
|
|
885
|
+
}
|
|
886
|
+
if (graphInterrupt !== undefined) {
|
|
887
|
+
throw graphInterrupt;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
if (loop.status === "out_of_steps") {
|
|
891
|
+
throw new errors_js_1.GraphRecursionError([
|
|
892
|
+
`Recursion limit of ${config.recursionLimit} reached`,
|
|
893
|
+
"without hitting a stop condition. You can increase the",
|
|
894
|
+
`limit by setting the "recursionLimit" config key.`,
|
|
895
|
+
].join(" "), {
|
|
896
|
+
lc_error_code: "GRAPH_RECURSION_LIMIT",
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
catch (e) {
|
|
901
|
+
tickError = e;
|
|
902
|
+
const suppress = await loop.finishAndHandleError(tickError);
|
|
903
|
+
if (!suppress) {
|
|
904
|
+
throw e;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
finally {
|
|
908
|
+
if (tickError === undefined) {
|
|
909
|
+
await loop.finishAndHandleError();
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
}
|
|
827
913
|
async *_streamIterator(input, options) {
|
|
828
914
|
const streamSubgraphs = options?.subgraphs;
|
|
829
915
|
const inputConfig = (0, config_js_1.ensureLangGraphConfig)(this.config, options);
|
|
@@ -867,7 +953,15 @@ class Pregel extends runnables_1.Runnable {
|
|
|
867
953
|
const { channelSpecs, managed } = await this.prepareSpecs(config);
|
|
868
954
|
let loop;
|
|
869
955
|
let loopError;
|
|
870
|
-
|
|
956
|
+
/**
|
|
957
|
+
* The PregelLoop will yield events from concurrent tasks as soon as they are
|
|
958
|
+
* generated. Each task can push multiple events onto the stream in any order.
|
|
959
|
+
*
|
|
960
|
+
* We use a separate background method and stream here in order to yield events
|
|
961
|
+
* from the loop to the main stream and therefore back to the user as soon as
|
|
962
|
+
* they are available.
|
|
963
|
+
*/
|
|
964
|
+
const createAndRunLoop = async () => {
|
|
871
965
|
try {
|
|
872
966
|
loop = await loop_js_1.PregelLoop.initialize({
|
|
873
967
|
input,
|
|
@@ -887,68 +981,14 @@ class Pregel extends runnables_1.Runnable {
|
|
|
887
981
|
[constants_js_1.CONFIG_KEY_STREAM]: loop.stream,
|
|
888
982
|
};
|
|
889
983
|
}
|
|
890
|
-
|
|
891
|
-
|
|
984
|
+
await this._runLoop({
|
|
985
|
+
loop,
|
|
892
986
|
interruptAfter,
|
|
893
987
|
interruptBefore,
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
}
|
|
899
|
-
if (debug) {
|
|
900
|
-
(0, debug_js_1.printStepTasks)(loop.step, Object.values(loop.tasks));
|
|
901
|
-
}
|
|
902
|
-
// execute tasks, and wait for one to fail or all to finish.
|
|
903
|
-
// each task is independent from all other concurrent tasks
|
|
904
|
-
// yield updates/debug output as each task finishes
|
|
905
|
-
const taskStream = (0, retry_js_1.executeTasksWithRetry)(Object.values(loop.tasks).filter((task) => task.writes.length === 0), {
|
|
906
|
-
stepTimeout: this.stepTimeout,
|
|
907
|
-
signal: config.signal,
|
|
908
|
-
retryPolicy: this.retryPolicy,
|
|
909
|
-
});
|
|
910
|
-
// Timeouts will be thrown
|
|
911
|
-
for await (const { task, error } of taskStream) {
|
|
912
|
-
if (error !== undefined) {
|
|
913
|
-
if ((0, errors_js_1.isGraphBubbleUp)(error)) {
|
|
914
|
-
if (loop.isNested) {
|
|
915
|
-
throw error;
|
|
916
|
-
}
|
|
917
|
-
if ((0, errors_js_1.isGraphInterrupt)(error) && error.interrupts.length) {
|
|
918
|
-
const interrupts = error.interrupts.map((interrupt) => [constants_js_1.INTERRUPT, interrupt]);
|
|
919
|
-
const resumes = task.writes.filter((w) => w[0] === constants_js_1.RESUME);
|
|
920
|
-
if (resumes.length) {
|
|
921
|
-
interrupts.push(...resumes);
|
|
922
|
-
}
|
|
923
|
-
loop.putWrites(task.id, interrupts);
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
else {
|
|
927
|
-
loop.putWrites(task.id, [
|
|
928
|
-
[constants_js_1.ERROR, { message: error.message, name: error.name }],
|
|
929
|
-
]);
|
|
930
|
-
throw error;
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
else {
|
|
934
|
-
loop.putWrites(task.id, task.writes);
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
if (debug) {
|
|
938
|
-
(0, debug_js_1.printStepWrites)(loop.step, Object.values(loop.tasks)
|
|
939
|
-
.map((task) => task.writes)
|
|
940
|
-
.flat(), this.streamChannelsList);
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
if (loop.status === "out_of_steps") {
|
|
944
|
-
throw new errors_js_1.GraphRecursionError([
|
|
945
|
-
`Recursion limit of ${config.recursionLimit} reached`,
|
|
946
|
-
"without hitting a stop condition. You can increase the",
|
|
947
|
-
`limit by setting the "recursionLimit" config key.`,
|
|
948
|
-
].join(" "), {
|
|
949
|
-
lc_error_code: "GRAPH_RECURSION_LIMIT",
|
|
950
|
-
});
|
|
951
|
-
}
|
|
988
|
+
runManager,
|
|
989
|
+
debug,
|
|
990
|
+
config,
|
|
991
|
+
});
|
|
952
992
|
}
|
|
953
993
|
catch (e) {
|
|
954
994
|
loopError = e;
|
|
@@ -982,7 +1022,7 @@ class Pregel extends runnables_1.Runnable {
|
|
|
982
1022
|
}
|
|
983
1023
|
}
|
|
984
1024
|
};
|
|
985
|
-
const runLoopPromise =
|
|
1025
|
+
const runLoopPromise = createAndRunLoop();
|
|
986
1026
|
try {
|
|
987
1027
|
for await (const chunk of stream) {
|
|
988
1028
|
if (chunk === undefined) {
|
package/dist/pregel/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Runnable, RunnableConfig, RunnableFunc } from "@langchain/core/runnables";
|
|
2
2
|
import { IterableReadableStream } from "@langchain/core/utils/stream";
|
|
3
|
+
import type { CallbackManagerForChainRun } from "@langchain/core/callbacks/manager";
|
|
3
4
|
import { All, BaseCheckpointSaver, BaseStore, CheckpointListOptions, CheckpointTuple } from "@langchain/langgraph-checkpoint";
|
|
4
5
|
import { BaseChannel } from "../channels/base.js";
|
|
5
6
|
import { PregelNode } from "./read.js";
|
|
@@ -8,6 +9,7 @@ import { Command } from "../constants.js";
|
|
|
8
9
|
import { PregelInterface, PregelParams, StateSnapshot, StreamMode, PregelInputType, PregelOutputType, PregelOptions } from "./types.js";
|
|
9
10
|
import { StrRecord } from "./algo.js";
|
|
10
11
|
import { RetryPolicy } from "./utils/index.js";
|
|
12
|
+
import { PregelLoop } from "./loop.js";
|
|
11
13
|
import { ManagedValueMapping, type ManagedValueSpec } from "../managed/base.js";
|
|
12
14
|
import { LangGraphRunnableConfig } from "./runnable_types.js";
|
|
13
15
|
type WriteValue = Runnable | RunnableFunc<unknown, unknown> | unknown;
|
|
@@ -110,6 +112,14 @@ export declare class Pregel<Nn extends StrRecord<string, PregelNode>, Cc extends
|
|
|
110
112
|
channelSpecs: Record<string, BaseChannel<unknown, unknown, unknown>>;
|
|
111
113
|
managed: ManagedValueMapping;
|
|
112
114
|
}>;
|
|
115
|
+
_runLoop(params: {
|
|
116
|
+
loop: PregelLoop;
|
|
117
|
+
interruptAfter: string[] | "*";
|
|
118
|
+
interruptBefore: string[] | "*";
|
|
119
|
+
runManager?: CallbackManagerForChainRun;
|
|
120
|
+
debug: boolean;
|
|
121
|
+
config: LangGraphRunnableConfig;
|
|
122
|
+
}): Promise<void>;
|
|
113
123
|
_streamIterator(input: PregelInputType | Command, options?: Partial<PregelOptions<Nn, Cc>>): AsyncGenerator<PregelOutputType>;
|
|
114
124
|
/**
|
|
115
125
|
* Run the graph with a single input and config.
|
package/dist/pregel/index.js
CHANGED
|
@@ -820,6 +820,92 @@ export class Pregel extends Runnable {
|
|
|
820
820
|
managed,
|
|
821
821
|
};
|
|
822
822
|
}
|
|
823
|
+
async _runLoop(params) {
|
|
824
|
+
const { loop, interruptAfter, interruptBefore, runManager, debug, config } = params;
|
|
825
|
+
let tickError;
|
|
826
|
+
try {
|
|
827
|
+
while (await loop.tick({
|
|
828
|
+
inputKeys: this.inputChannels,
|
|
829
|
+
interruptAfter,
|
|
830
|
+
interruptBefore,
|
|
831
|
+
manager: runManager,
|
|
832
|
+
})) {
|
|
833
|
+
if (debug) {
|
|
834
|
+
printStepCheckpoint(loop.checkpointMetadata.step, loop.channels, this.streamChannelsList);
|
|
835
|
+
}
|
|
836
|
+
if (debug) {
|
|
837
|
+
printStepTasks(loop.step, Object.values(loop.tasks));
|
|
838
|
+
}
|
|
839
|
+
// execute tasks, and wait for one to fail or all to finish.
|
|
840
|
+
// each task is independent from all other concurrent tasks
|
|
841
|
+
// yield updates/debug output as each task finishes
|
|
842
|
+
const taskStream = executeTasksWithRetry(Object.values(loop.tasks).filter((task) => task.writes.length === 0), {
|
|
843
|
+
stepTimeout: this.stepTimeout,
|
|
844
|
+
signal: config.signal,
|
|
845
|
+
retryPolicy: this.retryPolicy,
|
|
846
|
+
});
|
|
847
|
+
let graphInterrupt;
|
|
848
|
+
for await (const { task, error } of taskStream) {
|
|
849
|
+
if (error !== undefined) {
|
|
850
|
+
if (isGraphBubbleUp(error)) {
|
|
851
|
+
if (loop.isNested) {
|
|
852
|
+
throw error;
|
|
853
|
+
}
|
|
854
|
+
if (isGraphInterrupt(error)) {
|
|
855
|
+
graphInterrupt = error;
|
|
856
|
+
if (error.interrupts.length) {
|
|
857
|
+
const interrupts = error.interrupts.map((interrupt) => [INTERRUPT, interrupt]);
|
|
858
|
+
const resumes = task.writes.filter((w) => w[0] === RESUME);
|
|
859
|
+
if (resumes.length) {
|
|
860
|
+
interrupts.push(...resumes);
|
|
861
|
+
}
|
|
862
|
+
loop.putWrites(task.id, interrupts);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
else {
|
|
867
|
+
loop.putWrites(task.id, [
|
|
868
|
+
[ERROR, { message: error.message, name: error.name }],
|
|
869
|
+
]);
|
|
870
|
+
throw error;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
else {
|
|
874
|
+
loop.putWrites(task.id, task.writes);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
if (debug) {
|
|
878
|
+
printStepWrites(loop.step, Object.values(loop.tasks)
|
|
879
|
+
.map((task) => task.writes)
|
|
880
|
+
.flat(), this.streamChannelsList);
|
|
881
|
+
}
|
|
882
|
+
if (graphInterrupt !== undefined) {
|
|
883
|
+
throw graphInterrupt;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
if (loop.status === "out_of_steps") {
|
|
887
|
+
throw new GraphRecursionError([
|
|
888
|
+
`Recursion limit of ${config.recursionLimit} reached`,
|
|
889
|
+
"without hitting a stop condition. You can increase the",
|
|
890
|
+
`limit by setting the "recursionLimit" config key.`,
|
|
891
|
+
].join(" "), {
|
|
892
|
+
lc_error_code: "GRAPH_RECURSION_LIMIT",
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
catch (e) {
|
|
897
|
+
tickError = e;
|
|
898
|
+
const suppress = await loop.finishAndHandleError(tickError);
|
|
899
|
+
if (!suppress) {
|
|
900
|
+
throw e;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
finally {
|
|
904
|
+
if (tickError === undefined) {
|
|
905
|
+
await loop.finishAndHandleError();
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
}
|
|
823
909
|
async *_streamIterator(input, options) {
|
|
824
910
|
const streamSubgraphs = options?.subgraphs;
|
|
825
911
|
const inputConfig = ensureLangGraphConfig(this.config, options);
|
|
@@ -863,7 +949,15 @@ export class Pregel extends Runnable {
|
|
|
863
949
|
const { channelSpecs, managed } = await this.prepareSpecs(config);
|
|
864
950
|
let loop;
|
|
865
951
|
let loopError;
|
|
866
|
-
|
|
952
|
+
/**
|
|
953
|
+
* The PregelLoop will yield events from concurrent tasks as soon as they are
|
|
954
|
+
* generated. Each task can push multiple events onto the stream in any order.
|
|
955
|
+
*
|
|
956
|
+
* We use a separate background method and stream here in order to yield events
|
|
957
|
+
* from the loop to the main stream and therefore back to the user as soon as
|
|
958
|
+
* they are available.
|
|
959
|
+
*/
|
|
960
|
+
const createAndRunLoop = async () => {
|
|
867
961
|
try {
|
|
868
962
|
loop = await PregelLoop.initialize({
|
|
869
963
|
input,
|
|
@@ -883,68 +977,14 @@ export class Pregel extends Runnable {
|
|
|
883
977
|
[CONFIG_KEY_STREAM]: loop.stream,
|
|
884
978
|
};
|
|
885
979
|
}
|
|
886
|
-
|
|
887
|
-
|
|
980
|
+
await this._runLoop({
|
|
981
|
+
loop,
|
|
888
982
|
interruptAfter,
|
|
889
983
|
interruptBefore,
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
}
|
|
895
|
-
if (debug) {
|
|
896
|
-
printStepTasks(loop.step, Object.values(loop.tasks));
|
|
897
|
-
}
|
|
898
|
-
// execute tasks, and wait for one to fail or all to finish.
|
|
899
|
-
// each task is independent from all other concurrent tasks
|
|
900
|
-
// yield updates/debug output as each task finishes
|
|
901
|
-
const taskStream = executeTasksWithRetry(Object.values(loop.tasks).filter((task) => task.writes.length === 0), {
|
|
902
|
-
stepTimeout: this.stepTimeout,
|
|
903
|
-
signal: config.signal,
|
|
904
|
-
retryPolicy: this.retryPolicy,
|
|
905
|
-
});
|
|
906
|
-
// Timeouts will be thrown
|
|
907
|
-
for await (const { task, error } of taskStream) {
|
|
908
|
-
if (error !== undefined) {
|
|
909
|
-
if (isGraphBubbleUp(error)) {
|
|
910
|
-
if (loop.isNested) {
|
|
911
|
-
throw error;
|
|
912
|
-
}
|
|
913
|
-
if (isGraphInterrupt(error) && error.interrupts.length) {
|
|
914
|
-
const interrupts = error.interrupts.map((interrupt) => [INTERRUPT, interrupt]);
|
|
915
|
-
const resumes = task.writes.filter((w) => w[0] === RESUME);
|
|
916
|
-
if (resumes.length) {
|
|
917
|
-
interrupts.push(...resumes);
|
|
918
|
-
}
|
|
919
|
-
loop.putWrites(task.id, interrupts);
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
else {
|
|
923
|
-
loop.putWrites(task.id, [
|
|
924
|
-
[ERROR, { message: error.message, name: error.name }],
|
|
925
|
-
]);
|
|
926
|
-
throw error;
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
else {
|
|
930
|
-
loop.putWrites(task.id, task.writes);
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
if (debug) {
|
|
934
|
-
printStepWrites(loop.step, Object.values(loop.tasks)
|
|
935
|
-
.map((task) => task.writes)
|
|
936
|
-
.flat(), this.streamChannelsList);
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
if (loop.status === "out_of_steps") {
|
|
940
|
-
throw new GraphRecursionError([
|
|
941
|
-
`Recursion limit of ${config.recursionLimit} reached`,
|
|
942
|
-
"without hitting a stop condition. You can increase the",
|
|
943
|
-
`limit by setting the "recursionLimit" config key.`,
|
|
944
|
-
].join(" "), {
|
|
945
|
-
lc_error_code: "GRAPH_RECURSION_LIMIT",
|
|
946
|
-
});
|
|
947
|
-
}
|
|
984
|
+
runManager,
|
|
985
|
+
debug,
|
|
986
|
+
config,
|
|
987
|
+
});
|
|
948
988
|
}
|
|
949
989
|
catch (e) {
|
|
950
990
|
loopError = e;
|
|
@@ -978,7 +1018,7 @@ export class Pregel extends Runnable {
|
|
|
978
1018
|
}
|
|
979
1019
|
}
|
|
980
1020
|
};
|
|
981
|
-
const runLoopPromise =
|
|
1021
|
+
const runLoopPromise = createAndRunLoop();
|
|
982
1022
|
try {
|
|
983
1023
|
for await (const chunk of stream) {
|
|
984
1024
|
if (chunk === undefined) {
|
package/dist/pregel/io.cjs
CHANGED
|
@@ -154,8 +154,10 @@ exports.mapOutputValues = mapOutputValues;
|
|
|
154
154
|
function* mapOutputUpdates(outputChannels, tasks, cached
|
|
155
155
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
156
156
|
) {
|
|
157
|
-
const outputTasks = tasks.filter(([task]) => {
|
|
158
|
-
return task.config === undefined || !task.config.tags?.includes(constants_js_1.TAG_HIDDEN)
|
|
157
|
+
const outputTasks = tasks.filter(([task, ww]) => {
|
|
158
|
+
return ((task.config === undefined || !task.config.tags?.includes(constants_js_1.TAG_HIDDEN)) &&
|
|
159
|
+
ww[0][0] !== constants_js_1.ERROR &&
|
|
160
|
+
ww[0][0] !== constants_js_1.INTERRUPT);
|
|
159
161
|
});
|
|
160
162
|
if (!outputTasks.length) {
|
|
161
163
|
return;
|
package/dist/pregel/io.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { validate } from "uuid";
|
|
2
|
-
import { _isSend, Command, NULL_TASK_ID, RESUME, SELF, TAG_HIDDEN, TASKS, } from "../constants.js";
|
|
2
|
+
import { _isSend, Command, ERROR, INTERRUPT, NULL_TASK_ID, RESUME, SELF, TAG_HIDDEN, TASKS, } from "../constants.js";
|
|
3
3
|
import { EmptyChannelError, InvalidUpdateError } from "../errors.js";
|
|
4
4
|
export function readChannel(channels, chan, catchErrors = true, returnException = false) {
|
|
5
5
|
try {
|
|
@@ -146,8 +146,10 @@ export function* mapOutputValues(outputChannels, pendingWrites, channels
|
|
|
146
146
|
export function* mapOutputUpdates(outputChannels, tasks, cached
|
|
147
147
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
148
148
|
) {
|
|
149
|
-
const outputTasks = tasks.filter(([task]) => {
|
|
150
|
-
return task.config === undefined || !task.config.tags?.includes(TAG_HIDDEN)
|
|
149
|
+
const outputTasks = tasks.filter(([task, ww]) => {
|
|
150
|
+
return ((task.config === undefined || !task.config.tags?.includes(TAG_HIDDEN)) &&
|
|
151
|
+
ww[0][0] !== ERROR &&
|
|
152
|
+
ww[0][0] !== INTERRUPT);
|
|
151
153
|
});
|
|
152
154
|
if (!outputTasks.length) {
|
|
153
155
|
return;
|
package/dist/pregel/loop.cjs
CHANGED
|
@@ -455,125 +455,125 @@ class PregelLoop {
|
|
|
455
455
|
* @param params
|
|
456
456
|
*/
|
|
457
457
|
async tick(params) {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
for (const [key, values] of Object.entries(managedValueWrites)) {
|
|
475
|
-
await this.updateManagedValues(key, values);
|
|
476
|
-
}
|
|
477
|
-
// produce values output
|
|
478
|
-
const valuesOutput = await (0, utils_js_1.gatherIterator)((0, utils_js_1.prefixGenerator)((0, io_js_1.mapOutputValues)(this.outputKeys, writes, this.channels), "values"));
|
|
479
|
-
this._emit(valuesOutput);
|
|
480
|
-
// clear pending writes
|
|
481
|
-
this.checkpointPendingWrites = [];
|
|
482
|
-
await this._putCheckpoint({
|
|
483
|
-
source: "loop",
|
|
484
|
-
writes: (0, io_js_1.mapOutputUpdates)(this.outputKeys, Object.values(this.tasks).map((task) => [task, task.writes])).next().value ?? null,
|
|
485
|
-
});
|
|
486
|
-
// after execution, check if we should interrupt
|
|
487
|
-
if ((0, algo_js_1.shouldInterrupt)(this.checkpoint, interruptAfter, Object.values(this.tasks))) {
|
|
488
|
-
this.status = "interrupt_after";
|
|
489
|
-
if (this.isNested) {
|
|
490
|
-
throw new errors_js_1.GraphInterrupt();
|
|
491
|
-
}
|
|
492
|
-
else {
|
|
493
|
-
return false;
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
else {
|
|
498
|
-
return false;
|
|
499
|
-
}
|
|
500
|
-
if (this.step > this.stop) {
|
|
501
|
-
this.status = "out_of_steps";
|
|
502
|
-
return false;
|
|
458
|
+
if (this.store && !this.store.isRunning) {
|
|
459
|
+
this.store?.start();
|
|
460
|
+
}
|
|
461
|
+
const { inputKeys = [], interruptAfter = [], interruptBefore = [], manager, } = params;
|
|
462
|
+
if (this.status !== "pending") {
|
|
463
|
+
throw new Error(`Cannot tick when status is no longer "pending". Current status: "${this.status}"`);
|
|
464
|
+
}
|
|
465
|
+
if (![INPUT_DONE, INPUT_RESUMING].includes(this.input)) {
|
|
466
|
+
await this._first(inputKeys);
|
|
467
|
+
}
|
|
468
|
+
else if (Object.values(this.tasks).every((task) => task.writes.filter(([c]) => !(c in langgraph_checkpoint_1.WRITES_IDX_MAP)).length > 0)) {
|
|
469
|
+
const writes = Object.values(this.tasks).flatMap((t) => t.writes);
|
|
470
|
+
// All tasks have finished
|
|
471
|
+
const managedValueWrites = (0, algo_js_1._applyWrites)(this.checkpoint, this.channels, Object.values(this.tasks), this.checkpointerGetNextVersion);
|
|
472
|
+
for (const [key, values] of Object.entries(managedValueWrites)) {
|
|
473
|
+
await this.updateManagedValues(key, values);
|
|
503
474
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
475
|
+
// produce values output
|
|
476
|
+
const valuesOutput = await (0, utils_js_1.gatherIterator)((0, utils_js_1.prefixGenerator)((0, io_js_1.mapOutputValues)(this.outputKeys, writes, this.channels), "values"));
|
|
477
|
+
this._emit(valuesOutput);
|
|
478
|
+
// clear pending writes
|
|
479
|
+
this.checkpointPendingWrites = [];
|
|
480
|
+
await this._putCheckpoint({
|
|
481
|
+
source: "loop",
|
|
482
|
+
writes: (0, io_js_1.mapOutputUpdates)(this.outputKeys, Object.values(this.tasks).map((task) => [task, task.writes])).next().value ?? null,
|
|
510
483
|
});
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
this.checkpointConfig, this.channels, this.streamKeys, this.checkpointMetadata, Object.values(this.tasks), this.checkpointPendingWrites, this.prevCheckpointConfig), "debug")));
|
|
516
|
-
}
|
|
517
|
-
if (Object.values(this.tasks).length === 0) {
|
|
518
|
-
this.status = "done";
|
|
519
|
-
return false;
|
|
484
|
+
// after execution, check if we should interrupt
|
|
485
|
+
if ((0, algo_js_1.shouldInterrupt)(this.checkpoint, interruptAfter, Object.values(this.tasks))) {
|
|
486
|
+
this.status = "interrupt_after";
|
|
487
|
+
throw new errors_js_1.GraphInterrupt();
|
|
520
488
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
489
|
+
}
|
|
490
|
+
else {
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
if (this.step > this.stop) {
|
|
494
|
+
this.status = "out_of_steps";
|
|
495
|
+
return false;
|
|
496
|
+
}
|
|
497
|
+
const nextTasks = (0, algo_js_1._prepareNextTasks)(this.checkpoint, this.checkpointPendingWrites, this.nodes, this.channels, this.managed, this.config, true, {
|
|
498
|
+
step: this.step,
|
|
499
|
+
checkpointer: this.checkpointer,
|
|
500
|
+
isResuming: this.input === INPUT_RESUMING,
|
|
501
|
+
manager,
|
|
502
|
+
store: this.store,
|
|
503
|
+
});
|
|
504
|
+
this.tasks = nextTasks;
|
|
505
|
+
// Produce debug output
|
|
506
|
+
if (this.checkpointer) {
|
|
507
|
+
this._emit(await (0, utils_js_1.gatherIterator)((0, utils_js_1.prefixGenerator)((0, debug_js_1.mapDebugCheckpoint)(this.step - 1, // printing checkpoint for previous step
|
|
508
|
+
this.checkpointConfig, this.channels, this.streamKeys, this.checkpointMetadata, Object.values(this.tasks), this.checkpointPendingWrites, this.prevCheckpointConfig), "debug")));
|
|
509
|
+
}
|
|
510
|
+
if (Object.values(this.tasks).length === 0) {
|
|
511
|
+
this.status = "done";
|
|
512
|
+
return false;
|
|
513
|
+
}
|
|
514
|
+
// if there are pending writes from a previous loop, apply them
|
|
515
|
+
if (this.skipDoneTasks && this.checkpointPendingWrites.length > 0) {
|
|
516
|
+
for (const [tid, k, v] of this.checkpointPendingWrites) {
|
|
517
|
+
if (k === constants_js_1.ERROR || k === constants_js_1.INTERRUPT || k === constants_js_1.RESUME) {
|
|
518
|
+
continue;
|
|
531
519
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
}
|
|
520
|
+
const task = Object.values(this.tasks).find((t) => t.id === tid);
|
|
521
|
+
if (task) {
|
|
522
|
+
task.writes.push([k, v]);
|
|
536
523
|
}
|
|
537
524
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
inputKeys,
|
|
542
|
-
interruptAfter,
|
|
543
|
-
interruptBefore,
|
|
544
|
-
manager,
|
|
545
|
-
});
|
|
546
|
-
}
|
|
547
|
-
// Before execution, check if we should interrupt
|
|
548
|
-
if ((0, algo_js_1.shouldInterrupt)(this.checkpoint, interruptBefore, Object.values(this.tasks))) {
|
|
549
|
-
this.status = "interrupt_before";
|
|
550
|
-
if (this.isNested) {
|
|
551
|
-
throw new errors_js_1.GraphInterrupt();
|
|
552
|
-
}
|
|
553
|
-
else {
|
|
554
|
-
return false;
|
|
525
|
+
for (const task of Object.values(this.tasks)) {
|
|
526
|
+
if (task.writes.length > 0) {
|
|
527
|
+
this._outputWrites(task.id, task.writes, true);
|
|
555
528
|
}
|
|
556
529
|
}
|
|
557
|
-
// Produce debug output
|
|
558
|
-
const debugOutput = await (0, utils_js_1.gatherIterator)((0, utils_js_1.prefixGenerator)((0, debug_js_1.mapDebugTasks)(this.step, Object.values(this.tasks)), "debug"));
|
|
559
|
-
this._emit(debugOutput);
|
|
560
|
-
return true;
|
|
561
530
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
}
|
|
570
|
-
|
|
531
|
+
// if all tasks have finished, re-tick
|
|
532
|
+
if (Object.values(this.tasks).every((task) => task.writes.length > 0)) {
|
|
533
|
+
return this.tick({
|
|
534
|
+
inputKeys,
|
|
535
|
+
interruptAfter,
|
|
536
|
+
interruptBefore,
|
|
537
|
+
manager,
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
// Before execution, check if we should interrupt
|
|
541
|
+
if ((0, algo_js_1.shouldInterrupt)(this.checkpoint, interruptBefore, Object.values(this.tasks))) {
|
|
542
|
+
this.status = "interrupt_before";
|
|
543
|
+
throw new errors_js_1.GraphInterrupt();
|
|
571
544
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
545
|
+
// Produce debug output
|
|
546
|
+
const debugOutput = await (0, utils_js_1.gatherIterator)((0, utils_js_1.prefixGenerator)((0, debug_js_1.mapDebugTasks)(this.step, Object.values(this.tasks)), "debug"));
|
|
547
|
+
this._emit(debugOutput);
|
|
548
|
+
return true;
|
|
549
|
+
}
|
|
550
|
+
async finishAndHandleError(error) {
|
|
551
|
+
const suppress = this._suppressInterrupt(error);
|
|
552
|
+
if (suppress || error === undefined) {
|
|
553
|
+
this.output = (0, io_js_1.readChannels)(this.channels, this.outputKeys);
|
|
554
|
+
}
|
|
555
|
+
if (suppress) {
|
|
556
|
+
// emit one last "values" event, with pending writes applied
|
|
557
|
+
if (this.tasks !== undefined &&
|
|
558
|
+
this.checkpointPendingWrites.length > 0 &&
|
|
559
|
+
Object.values(this.tasks).some((task) => task.writes.length > 0)) {
|
|
560
|
+
const managedValueWrites = (0, algo_js_1._applyWrites)(this.checkpoint, this.channels, Object.values(this.tasks), this.checkpointerGetNextVersion);
|
|
561
|
+
for (const [key, values] of Object.entries(managedValueWrites)) {
|
|
562
|
+
await this.updateManagedValues(key, values);
|
|
563
|
+
}
|
|
564
|
+
this._emit((0, utils_js_1.gatherIteratorSync)((0, utils_js_1.prefixGenerator)((0, io_js_1.mapOutputValues)(this.outputKeys, Object.values(this.tasks).flatMap((t) => t.writes), this.channels), "values")));
|
|
575
565
|
}
|
|
566
|
+
// Emit INTERRUPT event
|
|
567
|
+
this._emit([
|
|
568
|
+
[
|
|
569
|
+
"updates",
|
|
570
|
+
{
|
|
571
|
+
[constants_js_1.INTERRUPT]: error.interrupts,
|
|
572
|
+
},
|
|
573
|
+
],
|
|
574
|
+
]);
|
|
576
575
|
}
|
|
576
|
+
return suppress;
|
|
577
577
|
}
|
|
578
578
|
_suppressInterrupt(e) {
|
|
579
579
|
return (0, errors_js_1.isGraphInterrupt)(e) && !this.isNested;
|
package/dist/pregel/loop.d.ts
CHANGED
|
@@ -112,6 +112,7 @@ export declare class PregelLoop {
|
|
|
112
112
|
interruptBefore: string[] | All;
|
|
113
113
|
manager?: CallbackManagerForChainRun;
|
|
114
114
|
}): Promise<boolean>;
|
|
115
|
+
finishAndHandleError(error?: Error): Promise<boolean>;
|
|
115
116
|
protected _suppressInterrupt(e?: Error): boolean;
|
|
116
117
|
/**
|
|
117
118
|
* Resuming from previous checkpoint requires
|
package/dist/pregel/loop.js
CHANGED
|
@@ -451,125 +451,125 @@ export class PregelLoop {
|
|
|
451
451
|
* @param params
|
|
452
452
|
*/
|
|
453
453
|
async tick(params) {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
for (const [key, values] of Object.entries(managedValueWrites)) {
|
|
471
|
-
await this.updateManagedValues(key, values);
|
|
472
|
-
}
|
|
473
|
-
// produce values output
|
|
474
|
-
const valuesOutput = await gatherIterator(prefixGenerator(mapOutputValues(this.outputKeys, writes, this.channels), "values"));
|
|
475
|
-
this._emit(valuesOutput);
|
|
476
|
-
// clear pending writes
|
|
477
|
-
this.checkpointPendingWrites = [];
|
|
478
|
-
await this._putCheckpoint({
|
|
479
|
-
source: "loop",
|
|
480
|
-
writes: mapOutputUpdates(this.outputKeys, Object.values(this.tasks).map((task) => [task, task.writes])).next().value ?? null,
|
|
481
|
-
});
|
|
482
|
-
// after execution, check if we should interrupt
|
|
483
|
-
if (shouldInterrupt(this.checkpoint, interruptAfter, Object.values(this.tasks))) {
|
|
484
|
-
this.status = "interrupt_after";
|
|
485
|
-
if (this.isNested) {
|
|
486
|
-
throw new GraphInterrupt();
|
|
487
|
-
}
|
|
488
|
-
else {
|
|
489
|
-
return false;
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
else {
|
|
494
|
-
return false;
|
|
495
|
-
}
|
|
496
|
-
if (this.step > this.stop) {
|
|
497
|
-
this.status = "out_of_steps";
|
|
498
|
-
return false;
|
|
454
|
+
if (this.store && !this.store.isRunning) {
|
|
455
|
+
this.store?.start();
|
|
456
|
+
}
|
|
457
|
+
const { inputKeys = [], interruptAfter = [], interruptBefore = [], manager, } = params;
|
|
458
|
+
if (this.status !== "pending") {
|
|
459
|
+
throw new Error(`Cannot tick when status is no longer "pending". Current status: "${this.status}"`);
|
|
460
|
+
}
|
|
461
|
+
if (![INPUT_DONE, INPUT_RESUMING].includes(this.input)) {
|
|
462
|
+
await this._first(inputKeys);
|
|
463
|
+
}
|
|
464
|
+
else if (Object.values(this.tasks).every((task) => task.writes.filter(([c]) => !(c in WRITES_IDX_MAP)).length > 0)) {
|
|
465
|
+
const writes = Object.values(this.tasks).flatMap((t) => t.writes);
|
|
466
|
+
// All tasks have finished
|
|
467
|
+
const managedValueWrites = _applyWrites(this.checkpoint, this.channels, Object.values(this.tasks), this.checkpointerGetNextVersion);
|
|
468
|
+
for (const [key, values] of Object.entries(managedValueWrites)) {
|
|
469
|
+
await this.updateManagedValues(key, values);
|
|
499
470
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
471
|
+
// produce values output
|
|
472
|
+
const valuesOutput = await gatherIterator(prefixGenerator(mapOutputValues(this.outputKeys, writes, this.channels), "values"));
|
|
473
|
+
this._emit(valuesOutput);
|
|
474
|
+
// clear pending writes
|
|
475
|
+
this.checkpointPendingWrites = [];
|
|
476
|
+
await this._putCheckpoint({
|
|
477
|
+
source: "loop",
|
|
478
|
+
writes: mapOutputUpdates(this.outputKeys, Object.values(this.tasks).map((task) => [task, task.writes])).next().value ?? null,
|
|
506
479
|
});
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
this.checkpointConfig, this.channels, this.streamKeys, this.checkpointMetadata, Object.values(this.tasks), this.checkpointPendingWrites, this.prevCheckpointConfig), "debug")));
|
|
512
|
-
}
|
|
513
|
-
if (Object.values(this.tasks).length === 0) {
|
|
514
|
-
this.status = "done";
|
|
515
|
-
return false;
|
|
480
|
+
// after execution, check if we should interrupt
|
|
481
|
+
if (shouldInterrupt(this.checkpoint, interruptAfter, Object.values(this.tasks))) {
|
|
482
|
+
this.status = "interrupt_after";
|
|
483
|
+
throw new GraphInterrupt();
|
|
516
484
|
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
if (this.step > this.stop) {
|
|
490
|
+
this.status = "out_of_steps";
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
const nextTasks = _prepareNextTasks(this.checkpoint, this.checkpointPendingWrites, this.nodes, this.channels, this.managed, this.config, true, {
|
|
494
|
+
step: this.step,
|
|
495
|
+
checkpointer: this.checkpointer,
|
|
496
|
+
isResuming: this.input === INPUT_RESUMING,
|
|
497
|
+
manager,
|
|
498
|
+
store: this.store,
|
|
499
|
+
});
|
|
500
|
+
this.tasks = nextTasks;
|
|
501
|
+
// Produce debug output
|
|
502
|
+
if (this.checkpointer) {
|
|
503
|
+
this._emit(await gatherIterator(prefixGenerator(mapDebugCheckpoint(this.step - 1, // printing checkpoint for previous step
|
|
504
|
+
this.checkpointConfig, this.channels, this.streamKeys, this.checkpointMetadata, Object.values(this.tasks), this.checkpointPendingWrites, this.prevCheckpointConfig), "debug")));
|
|
505
|
+
}
|
|
506
|
+
if (Object.values(this.tasks).length === 0) {
|
|
507
|
+
this.status = "done";
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
// if there are pending writes from a previous loop, apply them
|
|
511
|
+
if (this.skipDoneTasks && this.checkpointPendingWrites.length > 0) {
|
|
512
|
+
for (const [tid, k, v] of this.checkpointPendingWrites) {
|
|
513
|
+
if (k === ERROR || k === INTERRUPT || k === RESUME) {
|
|
514
|
+
continue;
|
|
527
515
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
}
|
|
516
|
+
const task = Object.values(this.tasks).find((t) => t.id === tid);
|
|
517
|
+
if (task) {
|
|
518
|
+
task.writes.push([k, v]);
|
|
532
519
|
}
|
|
533
520
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
inputKeys,
|
|
538
|
-
interruptAfter,
|
|
539
|
-
interruptBefore,
|
|
540
|
-
manager,
|
|
541
|
-
});
|
|
542
|
-
}
|
|
543
|
-
// Before execution, check if we should interrupt
|
|
544
|
-
if (shouldInterrupt(this.checkpoint, interruptBefore, Object.values(this.tasks))) {
|
|
545
|
-
this.status = "interrupt_before";
|
|
546
|
-
if (this.isNested) {
|
|
547
|
-
throw new GraphInterrupt();
|
|
548
|
-
}
|
|
549
|
-
else {
|
|
550
|
-
return false;
|
|
521
|
+
for (const task of Object.values(this.tasks)) {
|
|
522
|
+
if (task.writes.length > 0) {
|
|
523
|
+
this._outputWrites(task.id, task.writes, true);
|
|
551
524
|
}
|
|
552
525
|
}
|
|
553
|
-
// Produce debug output
|
|
554
|
-
const debugOutput = await gatherIterator(prefixGenerator(mapDebugTasks(this.step, Object.values(this.tasks)), "debug"));
|
|
555
|
-
this._emit(debugOutput);
|
|
556
|
-
return true;
|
|
557
526
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
}
|
|
566
|
-
|
|
527
|
+
// if all tasks have finished, re-tick
|
|
528
|
+
if (Object.values(this.tasks).every((task) => task.writes.length > 0)) {
|
|
529
|
+
return this.tick({
|
|
530
|
+
inputKeys,
|
|
531
|
+
interruptAfter,
|
|
532
|
+
interruptBefore,
|
|
533
|
+
manager,
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
// Before execution, check if we should interrupt
|
|
537
|
+
if (shouldInterrupt(this.checkpoint, interruptBefore, Object.values(this.tasks))) {
|
|
538
|
+
this.status = "interrupt_before";
|
|
539
|
+
throw new GraphInterrupt();
|
|
567
540
|
}
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
541
|
+
// Produce debug output
|
|
542
|
+
const debugOutput = await gatherIterator(prefixGenerator(mapDebugTasks(this.step, Object.values(this.tasks)), "debug"));
|
|
543
|
+
this._emit(debugOutput);
|
|
544
|
+
return true;
|
|
545
|
+
}
|
|
546
|
+
async finishAndHandleError(error) {
|
|
547
|
+
const suppress = this._suppressInterrupt(error);
|
|
548
|
+
if (suppress || error === undefined) {
|
|
549
|
+
this.output = readChannels(this.channels, this.outputKeys);
|
|
550
|
+
}
|
|
551
|
+
if (suppress) {
|
|
552
|
+
// emit one last "values" event, with pending writes applied
|
|
553
|
+
if (this.tasks !== undefined &&
|
|
554
|
+
this.checkpointPendingWrites.length > 0 &&
|
|
555
|
+
Object.values(this.tasks).some((task) => task.writes.length > 0)) {
|
|
556
|
+
const managedValueWrites = _applyWrites(this.checkpoint, this.channels, Object.values(this.tasks), this.checkpointerGetNextVersion);
|
|
557
|
+
for (const [key, values] of Object.entries(managedValueWrites)) {
|
|
558
|
+
await this.updateManagedValues(key, values);
|
|
559
|
+
}
|
|
560
|
+
this._emit(gatherIteratorSync(prefixGenerator(mapOutputValues(this.outputKeys, Object.values(this.tasks).flatMap((t) => t.writes), this.channels), "values")));
|
|
571
561
|
}
|
|
562
|
+
// Emit INTERRUPT event
|
|
563
|
+
this._emit([
|
|
564
|
+
[
|
|
565
|
+
"updates",
|
|
566
|
+
{
|
|
567
|
+
[INTERRUPT]: error.interrupts,
|
|
568
|
+
},
|
|
569
|
+
],
|
|
570
|
+
]);
|
|
572
571
|
}
|
|
572
|
+
return suppress;
|
|
573
573
|
}
|
|
574
574
|
_suppressInterrupt(e) {
|
|
575
575
|
return isGraphInterrupt(e) && !this.isNested;
|