@langchain/langgraph 0.0.11 → 0.0.13
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/channels/any_value.cjs +57 -0
- package/dist/channels/any_value.d.ts +16 -0
- package/dist/channels/any_value.js +53 -0
- package/dist/channels/base.cjs +19 -28
- package/dist/channels/base.d.ts +13 -19
- package/dist/channels/base.js +17 -24
- package/dist/channels/binop.cjs +4 -3
- package/dist/channels/binop.d.ts +1 -1
- package/dist/channels/binop.js +3 -2
- package/dist/channels/dynamic_barrier_value.cjs +88 -0
- package/dist/channels/dynamic_barrier_value.d.ts +26 -0
- package/dist/channels/dynamic_barrier_value.js +84 -0
- package/dist/channels/ephemeral_value.cjs +64 -0
- package/dist/channels/ephemeral_value.d.ts +14 -0
- package/dist/channels/ephemeral_value.js +60 -0
- package/dist/channels/index.cjs +1 -3
- package/dist/channels/index.d.ts +1 -1
- package/dist/channels/index.js +1 -1
- package/dist/channels/last_value.cjs +11 -5
- package/dist/channels/last_value.d.ts +5 -1
- package/dist/channels/last_value.js +9 -3
- package/dist/channels/named_barrier_value.cjs +71 -0
- package/dist/channels/named_barrier_value.d.ts +18 -0
- package/dist/channels/named_barrier_value.js +66 -0
- package/dist/channels/topic.cjs +5 -3
- package/dist/channels/topic.d.ts +3 -3
- package/dist/channels/topic.js +5 -3
- package/dist/checkpoint/base.cjs +30 -12
- package/dist/checkpoint/base.d.ts +39 -22
- package/dist/checkpoint/base.js +28 -11
- package/dist/checkpoint/id.cjs +40 -0
- package/dist/checkpoint/id.d.ts +2 -0
- package/dist/checkpoint/id.js +35 -0
- package/dist/checkpoint/index.cjs +2 -2
- package/dist/checkpoint/index.d.ts +2 -2
- package/dist/checkpoint/index.js +2 -2
- package/dist/checkpoint/memory.cjs +63 -49
- package/dist/checkpoint/memory.d.ts +7 -10
- package/dist/checkpoint/memory.js +62 -47
- package/dist/checkpoint/sqlite.cjs +170 -0
- package/dist/checkpoint/sqlite.d.ts +14 -0
- package/dist/checkpoint/sqlite.js +163 -0
- package/dist/constants.cjs +3 -1
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +2 -0
- package/dist/errors.cjs +31 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.js +24 -0
- package/dist/graph/graph.cjs +234 -96
- package/dist/graph/graph.d.ts +52 -23
- package/dist/graph/graph.js +233 -97
- package/dist/graph/index.cjs +2 -2
- package/dist/graph/index.d.ts +2 -2
- package/dist/graph/index.js +2 -2
- package/dist/graph/message.cjs +4 -3
- package/dist/graph/message.d.ts +4 -1
- package/dist/graph/message.js +4 -3
- package/dist/graph/state.cjs +237 -102
- package/dist/graph/state.d.ts +41 -18
- package/dist/graph/state.js +238 -104
- package/dist/index.cjs +6 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/prebuilt/agent_executor.cjs +22 -36
- package/dist/prebuilt/agent_executor.d.ts +7 -10
- package/dist/prebuilt/agent_executor.js +23 -37
- package/dist/prebuilt/chat_agent_executor.cjs +13 -13
- package/dist/prebuilt/chat_agent_executor.d.ts +3 -1
- package/dist/prebuilt/chat_agent_executor.js +15 -15
- package/dist/prebuilt/index.cjs +4 -1
- package/dist/prebuilt/index.d.ts +1 -0
- package/dist/prebuilt/index.js +1 -0
- package/dist/prebuilt/tool_node.cjs +59 -0
- package/dist/prebuilt/tool_node.d.ts +17 -0
- package/dist/prebuilt/tool_node.js +54 -0
- package/dist/pregel/debug.cjs +6 -8
- package/dist/pregel/debug.d.ts +2 -2
- package/dist/pregel/debug.js +5 -7
- package/dist/pregel/index.cjs +406 -236
- package/dist/pregel/index.d.ts +77 -41
- package/dist/pregel/index.js +408 -241
- package/dist/pregel/io.cjs +117 -30
- package/dist/pregel/io.d.ts +11 -3
- package/dist/pregel/io.js +111 -28
- package/dist/pregel/read.cjs +126 -46
- package/dist/pregel/read.d.ts +27 -18
- package/dist/pregel/read.js +125 -45
- package/dist/pregel/types.cjs +2 -0
- package/dist/pregel/types.d.ts +32 -0
- package/dist/pregel/types.js +1 -0
- package/dist/pregel/validate.cjs +58 -51
- package/dist/pregel/validate.d.ts +14 -13
- package/dist/pregel/validate.js +56 -50
- package/dist/pregel/write.cjs +46 -30
- package/dist/pregel/write.d.ts +18 -8
- package/dist/pregel/write.js +45 -29
- package/dist/serde/base.cjs +2 -0
- package/dist/serde/base.d.ts +4 -0
- package/dist/serde/base.js +1 -0
- package/dist/setup/async_local_storage.cjs +2 -2
- package/dist/setup/async_local_storage.js +1 -1
- package/dist/tests/channels.test.d.ts +1 -0
- package/dist/tests/channels.test.js +151 -0
- package/dist/tests/chatbot.int.test.d.ts +1 -0
- package/dist/tests/chatbot.int.test.js +61 -0
- package/dist/tests/checkpoints.test.d.ts +1 -0
- package/dist/tests/checkpoints.test.js +190 -0
- package/dist/tests/graph.test.d.ts +1 -0
- package/dist/tests/graph.test.js +15 -0
- package/dist/tests/prebuilt.int.test.d.ts +1 -0
- package/dist/tests/prebuilt.int.test.js +101 -0
- package/dist/tests/prebuilt.test.d.ts +1 -0
- package/dist/tests/prebuilt.test.js +195 -0
- package/dist/tests/pregel.io.test.d.ts +1 -0
- package/dist/tests/pregel.io.test.js +332 -0
- package/dist/tests/pregel.read.test.d.ts +1 -0
- package/dist/tests/pregel.read.test.js +109 -0
- package/dist/tests/pregel.test.d.ts +1 -0
- package/dist/tests/pregel.test.js +1879 -0
- package/dist/tests/pregel.validate.test.d.ts +1 -0
- package/dist/tests/pregel.validate.test.js +198 -0
- package/dist/tests/pregel.write.test.d.ts +1 -0
- package/dist/tests/pregel.write.test.js +44 -0
- package/dist/tests/tracing.int.test.d.ts +1 -0
- package/dist/tests/tracing.int.test.js +449 -0
- package/dist/tests/utils.d.ts +22 -0
- package/dist/tests/utils.js +76 -0
- package/dist/utils.cjs +74 -0
- package/dist/utils.d.ts +18 -0
- package/dist/utils.js +70 -0
- package/package.json +12 -8
- package/dist/pregel/reserved.cjs +0 -6
- package/dist/pregel/reserved.d.ts +0 -3
- package/dist/pregel/reserved.js +0 -3
package/dist/pregel/read.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import { RunnableBinding,
|
|
1
|
+
import { RunnableBinding, RunnablePassthrough, RunnableSequence, _coerceToRunnable, } from "@langchain/core/runnables";
|
|
2
2
|
import { CONFIG_KEY_READ } from "../constants.js";
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import { ChannelWrite } from "./write.js";
|
|
4
|
+
import { RunnableCallable } from "../utils.js";
|
|
5
|
+
export class ChannelRead extends RunnableCallable {
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
|
+
constructor(channel,
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
mapper, fresh = false) {
|
|
5
10
|
super({
|
|
6
|
-
|
|
7
|
-
func: (input, options) => {
|
|
8
|
-
if ("config" in options) {
|
|
9
|
-
return this._read(input, options.config);
|
|
10
|
-
}
|
|
11
|
-
return this._read(input, options ?? {});
|
|
12
|
-
},
|
|
11
|
+
func: (_, config) => ChannelRead.doRead(config, this.channel, this.fresh, this.mapper),
|
|
13
12
|
});
|
|
14
13
|
Object.defineProperty(this, "lc_graph_name", {
|
|
15
14
|
enumerable: true,
|
|
@@ -23,41 +22,44 @@ export class ChannelRead extends RunnableLambda {
|
|
|
23
22
|
writable: true,
|
|
24
23
|
value: void 0
|
|
25
24
|
});
|
|
25
|
+
Object.defineProperty(this, "fresh", {
|
|
26
|
+
enumerable: true,
|
|
27
|
+
configurable: true,
|
|
28
|
+
writable: true,
|
|
29
|
+
value: false
|
|
30
|
+
});
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
32
|
+
Object.defineProperty(this, "mapper", {
|
|
33
|
+
enumerable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
writable: true,
|
|
36
|
+
value: void 0
|
|
37
|
+
});
|
|
38
|
+
this.fresh = fresh;
|
|
39
|
+
this.mapper = mapper;
|
|
26
40
|
this.channel = channel;
|
|
27
|
-
this.name =
|
|
41
|
+
this.name = Array.isArray(channel)
|
|
42
|
+
? `ChannelRead<${channel.join(",")}>`
|
|
43
|
+
: `ChannelRead<${channel}>`;
|
|
28
44
|
}
|
|
29
|
-
|
|
30
|
-
return [
|
|
31
|
-
{
|
|
32
|
-
id: CONFIG_KEY_READ,
|
|
33
|
-
name: CONFIG_KEY_READ,
|
|
34
|
-
description: null,
|
|
35
|
-
default: null,
|
|
36
|
-
// TODO FIX THIS
|
|
37
|
-
annotation: "Callable[[BaseChannel], Any]",
|
|
38
|
-
isShared: true,
|
|
39
|
-
dependencies: null,
|
|
40
|
-
},
|
|
41
|
-
];
|
|
42
|
-
}
|
|
43
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
|
-
_read(_, config) {
|
|
45
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
+
static doRead(config, channel, fresh, mapper) {
|
|
46
46
|
const read = config.configurable?.[CONFIG_KEY_READ];
|
|
47
47
|
if (!read) {
|
|
48
48
|
throw new Error(`Runnable ${this} is not configured with a read function. Make sure to call in the context of a Pregel process`);
|
|
49
49
|
}
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
if (mapper) {
|
|
51
|
+
return mapper(read(channel, fresh));
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
return read(channel, fresh);
|
|
53
55
|
}
|
|
54
|
-
return read(this.channel);
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
|
-
const defaultRunnableBound =
|
|
58
|
-
|
|
58
|
+
const defaultRunnableBound =
|
|
59
|
+
/* #__PURE__ */ new RunnablePassthrough();
|
|
60
|
+
export class PregelNode extends RunnableBinding {
|
|
59
61
|
constructor(fields) {
|
|
60
|
-
const { channels, triggers,
|
|
62
|
+
const { channels, triggers, mapper, writers, bound, kwargs } = fields;
|
|
61
63
|
const mergedTags = [
|
|
62
64
|
...(fields.config?.tags ? fields.config.tags : []),
|
|
63
65
|
...(fields.tags ? fields.tags : []),
|
|
@@ -75,7 +77,7 @@ export class ChannelInvoke extends RunnableBinding {
|
|
|
75
77
|
enumerable: true,
|
|
76
78
|
configurable: true,
|
|
77
79
|
writable: true,
|
|
78
|
-
value: "
|
|
80
|
+
value: "PregelNode"
|
|
79
81
|
});
|
|
80
82
|
Object.defineProperty(this, "channels", {
|
|
81
83
|
enumerable: true,
|
|
@@ -90,48 +92,126 @@ export class ChannelInvoke extends RunnableBinding {
|
|
|
90
92
|
value: []
|
|
91
93
|
});
|
|
92
94
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
93
|
-
Object.defineProperty(this, "
|
|
95
|
+
Object.defineProperty(this, "mapper", {
|
|
94
96
|
enumerable: true,
|
|
95
97
|
configurable: true,
|
|
96
98
|
writable: true,
|
|
97
99
|
value: void 0
|
|
98
100
|
});
|
|
101
|
+
Object.defineProperty(this, "writers", {
|
|
102
|
+
enumerable: true,
|
|
103
|
+
configurable: true,
|
|
104
|
+
writable: true,
|
|
105
|
+
value: []
|
|
106
|
+
});
|
|
107
|
+
Object.defineProperty(this, "bound", {
|
|
108
|
+
enumerable: true,
|
|
109
|
+
configurable: true,
|
|
110
|
+
writable: true,
|
|
111
|
+
value: defaultRunnableBound
|
|
112
|
+
});
|
|
113
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
114
|
+
Object.defineProperty(this, "kwargs", {
|
|
115
|
+
enumerable: true,
|
|
116
|
+
configurable: true,
|
|
117
|
+
writable: true,
|
|
118
|
+
value: {}
|
|
119
|
+
});
|
|
99
120
|
this.channels = channels;
|
|
100
121
|
this.triggers = triggers;
|
|
101
|
-
this.
|
|
122
|
+
this.mapper = mapper;
|
|
123
|
+
this.writers = writers ?? this.writers;
|
|
124
|
+
this.bound = bound ?? this.bound;
|
|
125
|
+
this.kwargs = kwargs ?? this.kwargs;
|
|
126
|
+
}
|
|
127
|
+
getWriters() {
|
|
128
|
+
const newWriters = [...this.writers];
|
|
129
|
+
while (newWriters.length > 1 &&
|
|
130
|
+
// eslint-disable-next-line no-instanceof/no-instanceof
|
|
131
|
+
newWriters[newWriters.length - 1] instanceof ChannelWrite &&
|
|
132
|
+
// eslint-disable-next-line no-instanceof/no-instanceof
|
|
133
|
+
newWriters[newWriters.length - 2] instanceof ChannelWrite) {
|
|
134
|
+
// we can combine writes if they are consecutive
|
|
135
|
+
newWriters[newWriters.length - 2].writes.push(...newWriters[newWriters.length - 1].writes);
|
|
136
|
+
newWriters.pop();
|
|
137
|
+
}
|
|
138
|
+
return newWriters;
|
|
139
|
+
}
|
|
140
|
+
getNode() {
|
|
141
|
+
const writers = this.getWriters();
|
|
142
|
+
if (this.bound === defaultRunnableBound && writers.length === 0) {
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
else if (this.bound === defaultRunnableBound && writers.length === 1) {
|
|
146
|
+
return writers[0];
|
|
147
|
+
}
|
|
148
|
+
else if (this.bound === defaultRunnableBound) {
|
|
149
|
+
return new RunnableSequence({
|
|
150
|
+
first: writers[0],
|
|
151
|
+
middle: writers.slice(1, writers.length - 1),
|
|
152
|
+
last: writers[writers.length - 1],
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
else if (writers.length > 0) {
|
|
156
|
+
return new RunnableSequence({
|
|
157
|
+
first: this.bound,
|
|
158
|
+
middle: writers.slice(0, writers.length - 1),
|
|
159
|
+
last: writers[writers.length - 1],
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
return this.bound;
|
|
164
|
+
}
|
|
102
165
|
}
|
|
103
166
|
join(channels) {
|
|
167
|
+
if (!Array.isArray(channels)) {
|
|
168
|
+
throw new Error("channels must be a list");
|
|
169
|
+
}
|
|
104
170
|
if (typeof this.channels !== "object") {
|
|
105
171
|
throw new Error("all channels must be named when using .join()");
|
|
106
172
|
}
|
|
107
|
-
return new
|
|
173
|
+
return new PregelNode({
|
|
108
174
|
channels: {
|
|
109
175
|
...this.channels,
|
|
110
176
|
...Object.fromEntries(channels.map((chan) => [chan, chan])),
|
|
111
177
|
},
|
|
112
178
|
triggers: this.triggers,
|
|
113
|
-
|
|
179
|
+
mapper: this.mapper,
|
|
180
|
+
writers: this.writers,
|
|
114
181
|
bound: this.bound,
|
|
115
182
|
kwargs: this.kwargs,
|
|
116
183
|
config: this.config,
|
|
117
184
|
});
|
|
118
185
|
}
|
|
119
186
|
pipe(coerceable) {
|
|
120
|
-
if (
|
|
121
|
-
return new
|
|
187
|
+
if (ChannelWrite.isWriter(coerceable)) {
|
|
188
|
+
return new PregelNode({
|
|
189
|
+
channels: this.channels,
|
|
190
|
+
triggers: this.triggers,
|
|
191
|
+
mapper: this.mapper,
|
|
192
|
+
writers: [...this.writers, coerceable],
|
|
193
|
+
bound: this.bound,
|
|
194
|
+
config: this.config,
|
|
195
|
+
kwargs: this.kwargs,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
else if (this.bound === defaultRunnableBound) {
|
|
199
|
+
return new PregelNode({
|
|
122
200
|
channels: this.channels,
|
|
123
201
|
triggers: this.triggers,
|
|
124
|
-
|
|
202
|
+
mapper: this.mapper,
|
|
203
|
+
writers: this.writers,
|
|
125
204
|
bound: _coerceToRunnable(coerceable),
|
|
126
205
|
config: this.config,
|
|
127
206
|
kwargs: this.kwargs,
|
|
128
207
|
});
|
|
129
208
|
}
|
|
130
209
|
else {
|
|
131
|
-
return new
|
|
210
|
+
return new PregelNode({
|
|
132
211
|
channels: this.channels,
|
|
133
212
|
triggers: this.triggers,
|
|
134
|
-
|
|
213
|
+
mapper: this.mapper,
|
|
214
|
+
writers: this.writers,
|
|
135
215
|
bound: this.bound.pipe(coerceable),
|
|
136
216
|
config: this.config,
|
|
137
217
|
kwargs: this.kwargs,
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Runnable, RunnableConfig } from "@langchain/core/runnables";
|
|
2
|
+
export interface PregelTaskDescription {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly input: unknown;
|
|
5
|
+
}
|
|
6
|
+
export interface PregelExecutableTask<N extends PropertyKey, C extends PropertyKey> {
|
|
7
|
+
readonly name: N;
|
|
8
|
+
readonly input: unknown;
|
|
9
|
+
readonly proc: Runnable;
|
|
10
|
+
readonly writes: Array<[C, unknown]>;
|
|
11
|
+
readonly config: RunnableConfig | undefined;
|
|
12
|
+
}
|
|
13
|
+
export interface StateSnapshot {
|
|
14
|
+
/**
|
|
15
|
+
* Current values of channels
|
|
16
|
+
*/
|
|
17
|
+
readonly values: Record<string, any> | any;
|
|
18
|
+
/**
|
|
19
|
+
* Nodes to execute in the next step, if any
|
|
20
|
+
*/
|
|
21
|
+
readonly next: Array<string>;
|
|
22
|
+
/**
|
|
23
|
+
* Config used to fetch this snapshot
|
|
24
|
+
*/
|
|
25
|
+
readonly config: RunnableConfig;
|
|
26
|
+
/**
|
|
27
|
+
* Config used to fetch the parent snapshot, if any
|
|
28
|
+
* @default undefined
|
|
29
|
+
*/
|
|
30
|
+
readonly parentConfig?: RunnableConfig | undefined;
|
|
31
|
+
}
|
|
32
|
+
export type All = "*";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/pregel/validate.cjs
CHANGED
|
@@ -1,86 +1,93 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.validateKeys = exports.validateGraph = void 0;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
exports.validateKeys = exports.validateGraph = exports.GraphValidationError = void 0;
|
|
4
|
+
const constants_js_1 = require("../constants.cjs");
|
|
5
|
+
const read_js_1 = require("./read.cjs");
|
|
6
|
+
class GraphValidationError extends Error {
|
|
7
|
+
constructor(message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "GraphValidationError";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.GraphValidationError = GraphValidationError;
|
|
13
|
+
function validateGraph({ nodes, channels, inputChannels, outputChannels, streamChannels, interruptAfterNodes, interruptBeforeNodes, }) {
|
|
14
|
+
if (!channels) {
|
|
15
|
+
throw new GraphValidationError("Channels not provided");
|
|
16
|
+
}
|
|
8
17
|
const subscribedChannels = new Set();
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
18
|
+
const allOutputChannels = new Set();
|
|
19
|
+
for (const [name, node] of Object.entries(nodes)) {
|
|
20
|
+
if (name === constants_js_1.INTERRUPT) {
|
|
21
|
+
throw new GraphValidationError(`"Node name ${constants_js_1.INTERRUPT} is reserved"`);
|
|
22
|
+
}
|
|
23
|
+
if (node.constructor === read_js_1.PregelNode) {
|
|
24
|
+
node.triggers.forEach((trigger) => subscribedChannels.add(trigger));
|
|
17
25
|
}
|
|
18
26
|
else {
|
|
19
|
-
|
|
20
|
-
throw new Error(`Invalid node type: ${JSON.stringify(node, null, 2)}, expected Channel.subscribeTo()`);
|
|
27
|
+
throw new GraphValidationError(`Invalid node type ${typeof node}, expected PregelNode`);
|
|
21
28
|
}
|
|
22
29
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
// side effect: update channels
|
|
31
|
+
for (const chan of subscribedChannels) {
|
|
32
|
+
if (!(chan in channels)) {
|
|
33
|
+
throw new GraphValidationError(`Subcribed channel '${String(chan)}' not in channels`);
|
|
27
34
|
}
|
|
28
35
|
}
|
|
29
|
-
if (
|
|
30
|
-
if (!(
|
|
31
|
-
|
|
32
|
-
newChannels[input] = new last_value_js_1.LastValue();
|
|
33
|
-
}
|
|
34
|
-
if (!subscribedChannels.has(input)) {
|
|
35
|
-
throw new Error(`Input channel ${input} is not subscribed to by any node.`);
|
|
36
|
+
if (!Array.isArray(inputChannels)) {
|
|
37
|
+
if (!subscribedChannels.has(inputChannels)) {
|
|
38
|
+
throw new GraphValidationError(`Input channel ${String(inputChannels)} is not subscribed to by any node`);
|
|
36
39
|
}
|
|
37
40
|
}
|
|
38
41
|
else {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
42
|
-
newChannels[chan] = new last_value_js_1.LastValue();
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
if (input.every((chan) => !subscribedChannels.has(chan))) {
|
|
46
|
-
throw new Error(`None of the input channels ${input} are subscribed to by any node`);
|
|
42
|
+
if (inputChannels.every((channel) => !subscribedChannels.has(channel))) {
|
|
43
|
+
throw new GraphValidationError(`None of the input channels ${inputChannels} are subscribed to by any node`);
|
|
47
44
|
}
|
|
48
45
|
}
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
52
|
-
newChannels[output] = new last_value_js_1.LastValue();
|
|
53
|
-
}
|
|
46
|
+
if (!Array.isArray(outputChannels)) {
|
|
47
|
+
allOutputChannels.add(outputChannels);
|
|
54
48
|
}
|
|
55
49
|
else {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
50
|
+
outputChannels.forEach((chan) => allOutputChannels.add(chan));
|
|
51
|
+
}
|
|
52
|
+
if (streamChannels && !Array.isArray(streamChannels)) {
|
|
53
|
+
allOutputChannels.add(streamChannels);
|
|
54
|
+
}
|
|
55
|
+
else if (Array.isArray(streamChannels)) {
|
|
56
|
+
streamChannels.forEach((chan) => allOutputChannels.add(chan));
|
|
57
|
+
}
|
|
58
|
+
for (const chan of allOutputChannels) {
|
|
59
|
+
if (!(chan in channels)) {
|
|
60
|
+
throw new GraphValidationError(`Output channel '${String(chan)}' not in channels`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// validate interrupt before/after
|
|
64
|
+
if (interruptAfterNodes && interruptAfterNodes !== "*") {
|
|
65
|
+
for (const node of interruptAfterNodes) {
|
|
66
|
+
if (!(node in nodes)) {
|
|
67
|
+
throw new GraphValidationError(`Node ${String(node)} not in nodes`);
|
|
60
68
|
}
|
|
61
69
|
}
|
|
62
70
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
if (interruptBeforeNodes && interruptBeforeNodes !== "*") {
|
|
72
|
+
for (const node of interruptBeforeNodes) {
|
|
73
|
+
if (!(node in nodes)) {
|
|
74
|
+
throw new GraphValidationError(`Node ${String(node)} not in nodes`);
|
|
75
|
+
}
|
|
67
76
|
}
|
|
68
77
|
}
|
|
69
|
-
validateKeys(hidden, newChannels);
|
|
70
|
-
validateKeys(interrupt, newChannels);
|
|
71
78
|
}
|
|
72
79
|
exports.validateGraph = validateGraph;
|
|
73
80
|
function validateKeys(keys, channels) {
|
|
74
81
|
if (Array.isArray(keys)) {
|
|
75
82
|
for (const key of keys) {
|
|
76
83
|
if (!(key in channels)) {
|
|
77
|
-
throw new Error(`Key ${key} not found in channels`);
|
|
84
|
+
throw new Error(`Key ${String(key)} not found in channels`);
|
|
78
85
|
}
|
|
79
86
|
}
|
|
80
87
|
}
|
|
81
88
|
else {
|
|
82
89
|
if (!(keys in channels)) {
|
|
83
|
-
throw new Error(`Key ${keys} not found in channels`);
|
|
90
|
+
throw new Error(`Key ${String(keys)} not found in channels`);
|
|
84
91
|
}
|
|
85
92
|
}
|
|
86
93
|
}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { BaseChannel } from "../channels/index.js";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
2
|
+
import { PregelNode } from "./read.js";
|
|
3
|
+
import { All } from "./types.js";
|
|
4
|
+
export declare class GraphValidationError extends Error {
|
|
5
|
+
constructor(message?: string);
|
|
6
|
+
}
|
|
7
|
+
export declare function validateGraph<Nn extends Record<string, PregelNode>, Cc extends Record<string, BaseChannel>>({ nodes, channels, inputChannels, outputChannels, streamChannels, interruptAfterNodes, interruptBeforeNodes, }: {
|
|
8
|
+
nodes: Nn;
|
|
9
|
+
channels: Cc;
|
|
10
|
+
inputChannels: keyof Cc | Array<keyof Cc>;
|
|
11
|
+
outputChannels: keyof Cc | Array<keyof Cc>;
|
|
12
|
+
streamChannels?: keyof Cc | Array<keyof Cc>;
|
|
13
|
+
interruptAfterNodes?: Array<keyof Nn> | All;
|
|
14
|
+
interruptBeforeNodes?: Array<keyof Nn> | All;
|
|
15
15
|
}): void;
|
|
16
|
+
export declare function validateKeys<Cc extends Record<string, BaseChannel>>(keys: keyof Cc | Array<keyof Cc>, channels: Cc): void;
|
package/dist/pregel/validate.js
CHANGED
|
@@ -1,82 +1,88 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
export
|
|
4
|
-
|
|
1
|
+
import { INTERRUPT } from "../constants.js";
|
|
2
|
+
import { PregelNode } from "./read.js";
|
|
3
|
+
export class GraphValidationError extends Error {
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "GraphValidationError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export function validateGraph({ nodes, channels, inputChannels, outputChannels, streamChannels, interruptAfterNodes, interruptBeforeNodes, }) {
|
|
10
|
+
if (!channels) {
|
|
11
|
+
throw new GraphValidationError("Channels not provided");
|
|
12
|
+
}
|
|
5
13
|
const subscribedChannels = new Set();
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
+
const allOutputChannels = new Set();
|
|
15
|
+
for (const [name, node] of Object.entries(nodes)) {
|
|
16
|
+
if (name === INTERRUPT) {
|
|
17
|
+
throw new GraphValidationError(`"Node name ${INTERRUPT} is reserved"`);
|
|
18
|
+
}
|
|
19
|
+
if (node.constructor === PregelNode) {
|
|
20
|
+
node.triggers.forEach((trigger) => subscribedChannels.add(trigger));
|
|
14
21
|
}
|
|
15
22
|
else {
|
|
16
|
-
|
|
17
|
-
throw new Error(`Invalid node type: ${JSON.stringify(node, null, 2)}, expected Channel.subscribeTo()`);
|
|
23
|
+
throw new GraphValidationError(`Invalid node type ${typeof node}, expected PregelNode`);
|
|
18
24
|
}
|
|
19
25
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
// side effect: update channels
|
|
27
|
+
for (const chan of subscribedChannels) {
|
|
28
|
+
if (!(chan in channels)) {
|
|
29
|
+
throw new GraphValidationError(`Subcribed channel '${String(chan)}' not in channels`);
|
|
24
30
|
}
|
|
25
31
|
}
|
|
26
|
-
if (
|
|
27
|
-
if (!(
|
|
28
|
-
|
|
29
|
-
newChannels[input] = new LastValue();
|
|
30
|
-
}
|
|
31
|
-
if (!subscribedChannels.has(input)) {
|
|
32
|
-
throw new Error(`Input channel ${input} is not subscribed to by any node.`);
|
|
32
|
+
if (!Array.isArray(inputChannels)) {
|
|
33
|
+
if (!subscribedChannels.has(inputChannels)) {
|
|
34
|
+
throw new GraphValidationError(`Input channel ${String(inputChannels)} is not subscribed to by any node`);
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
else {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
39
|
-
newChannels[chan] = new LastValue();
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
if (input.every((chan) => !subscribedChannels.has(chan))) {
|
|
43
|
-
throw new Error(`None of the input channels ${input} are subscribed to by any node`);
|
|
38
|
+
if (inputChannels.every((channel) => !subscribedChannels.has(channel))) {
|
|
39
|
+
throw new GraphValidationError(`None of the input channels ${inputChannels} are subscribed to by any node`);
|
|
44
40
|
}
|
|
45
41
|
}
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
49
|
-
newChannels[output] = new LastValue();
|
|
50
|
-
}
|
|
42
|
+
if (!Array.isArray(outputChannels)) {
|
|
43
|
+
allOutputChannels.add(outputChannels);
|
|
51
44
|
}
|
|
52
45
|
else {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
46
|
+
outputChannels.forEach((chan) => allOutputChannels.add(chan));
|
|
47
|
+
}
|
|
48
|
+
if (streamChannels && !Array.isArray(streamChannels)) {
|
|
49
|
+
allOutputChannels.add(streamChannels);
|
|
50
|
+
}
|
|
51
|
+
else if (Array.isArray(streamChannels)) {
|
|
52
|
+
streamChannels.forEach((chan) => allOutputChannels.add(chan));
|
|
53
|
+
}
|
|
54
|
+
for (const chan of allOutputChannels) {
|
|
55
|
+
if (!(chan in channels)) {
|
|
56
|
+
throw new GraphValidationError(`Output channel '${String(chan)}' not in channels`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// validate interrupt before/after
|
|
60
|
+
if (interruptAfterNodes && interruptAfterNodes !== "*") {
|
|
61
|
+
for (const node of interruptAfterNodes) {
|
|
62
|
+
if (!(node in nodes)) {
|
|
63
|
+
throw new GraphValidationError(`Node ${String(node)} not in nodes`);
|
|
57
64
|
}
|
|
58
65
|
}
|
|
59
66
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
if (interruptBeforeNodes && interruptBeforeNodes !== "*") {
|
|
68
|
+
for (const node of interruptBeforeNodes) {
|
|
69
|
+
if (!(node in nodes)) {
|
|
70
|
+
throw new GraphValidationError(`Node ${String(node)} not in nodes`);
|
|
71
|
+
}
|
|
64
72
|
}
|
|
65
73
|
}
|
|
66
|
-
validateKeys(hidden, newChannels);
|
|
67
|
-
validateKeys(interrupt, newChannels);
|
|
68
74
|
}
|
|
69
75
|
export function validateKeys(keys, channels) {
|
|
70
76
|
if (Array.isArray(keys)) {
|
|
71
77
|
for (const key of keys) {
|
|
72
78
|
if (!(key in channels)) {
|
|
73
|
-
throw new Error(`Key ${key} not found in channels`);
|
|
79
|
+
throw new Error(`Key ${String(key)} not found in channels`);
|
|
74
80
|
}
|
|
75
81
|
}
|
|
76
82
|
}
|
|
77
83
|
else {
|
|
78
84
|
if (!(keys in channels)) {
|
|
79
|
-
throw new Error(`Key ${keys} not found in channels`);
|
|
85
|
+
throw new Error(`Key ${String(keys)} not found in channels`);
|
|
80
86
|
}
|
|
81
87
|
}
|
|
82
88
|
}
|