@langchain/langgraph 1.4.21 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/channels/base.cjs +123 -11
- package/dist/channels/base.cjs.map +1 -1
- package/dist/channels/base.d.cts +26 -4
- package/dist/channels/base.d.cts.map +1 -1
- package/dist/channels/base.d.ts +26 -4
- package/dist/channels/base.d.ts.map +1 -1
- package/dist/channels/base.js +120 -13
- package/dist/channels/base.js.map +1 -1
- package/dist/channels/binop.cjs +2 -1
- package/dist/channels/binop.cjs.map +1 -1
- package/dist/channels/binop.d.cts.map +1 -1
- package/dist/channels/binop.d.ts.map +1 -1
- package/dist/channels/binop.js +2 -1
- package/dist/channels/binop.js.map +1 -1
- package/dist/channels/index.cjs +2 -0
- package/dist/channels/index.d.cts +2 -2
- package/dist/channels/index.d.ts +2 -2
- package/dist/channels/index.js +2 -2
- package/dist/constants.cjs +14 -0
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts.map +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +13 -1
- package/dist/constants.js.map +1 -1
- package/dist/errors.cjs +22 -0
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +10 -1
- package/dist/errors.d.cts.map +1 -1
- package/dist/errors.d.ts +10 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +21 -1
- package/dist/errors.js.map +1 -1
- package/dist/graph/index.cjs +1 -1
- package/dist/graph/index.js +1 -1
- package/dist/graph/messages_reducer.cjs +30 -6
- package/dist/graph/messages_reducer.cjs.map +1 -1
- package/dist/graph/messages_reducer.d.cts +4 -2
- package/dist/graph/messages_reducer.d.cts.map +1 -1
- package/dist/graph/messages_reducer.d.ts +4 -2
- package/dist/graph/messages_reducer.d.ts.map +1 -1
- package/dist/graph/messages_reducer.js +31 -8
- package/dist/graph/messages_reducer.js.map +1 -1
- package/dist/index.cjs +3 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/interrupt.cjs +9 -1
- package/dist/interrupt.cjs.map +1 -1
- package/dist/interrupt.d.cts.map +1 -1
- package/dist/interrupt.d.ts.map +1 -1
- package/dist/interrupt.js +10 -2
- package/dist/interrupt.js.map +1 -1
- package/dist/pregel/algo.cjs +46 -45
- package/dist/pregel/algo.cjs.map +1 -1
- package/dist/pregel/algo.d.cts.map +1 -1
- package/dist/pregel/algo.d.ts.map +1 -1
- package/dist/pregel/algo.js +47 -47
- package/dist/pregel/algo.js.map +1 -1
- package/dist/pregel/checkpoint.cjs +70 -0
- package/dist/pregel/checkpoint.cjs.map +1 -0
- package/dist/pregel/checkpoint.js +67 -0
- package/dist/pregel/checkpoint.js.map +1 -0
- package/dist/pregel/debug.cjs +1 -0
- package/dist/pregel/debug.cjs.map +1 -1
- package/dist/pregel/debug.js +1 -0
- package/dist/pregel/debug.js.map +1 -1
- package/dist/pregel/index.cjs +87 -34
- package/dist/pregel/index.cjs.map +1 -1
- package/dist/pregel/index.d.cts.map +1 -1
- package/dist/pregel/index.d.ts.map +1 -1
- package/dist/pregel/index.js +91 -38
- package/dist/pregel/index.js.map +1 -1
- package/dist/pregel/loop.cjs +336 -113
- package/dist/pregel/loop.cjs.map +1 -1
- package/dist/pregel/loop.js +341 -119
- package/dist/pregel/loop.js.map +1 -1
- package/dist/pregel/runner.cjs +1 -1
- package/dist/pregel/runner.cjs.map +1 -1
- package/dist/pregel/runner.js +2 -2
- package/dist/pregel/runner.js.map +1 -1
- package/dist/state/prebuilt/messages.cjs +1 -1
- package/dist/state/prebuilt/messages.js +1 -1
- package/dist/web.cjs +3 -1
- package/dist/web.d.cts +2 -2
- package/dist/web.d.ts +2 -2
- package/dist/web.js +3 -3
- package/package.json +5 -5
package/dist/channels/base.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getDeltaMaxSuperstepsSinceSnapshot } from "../constants.js";
|
|
1
|
+
import { EXIT_DELTA_NULL_TASK_ID, SNAPSHOT_BUMPS, getDeltaMaxSuperstepsSinceSnapshot } from "../constants.js";
|
|
2
2
|
import { EmptyChannelError } from "../errors.js";
|
|
3
3
|
import { DeltaSnapshot, uuid6 } from "@langchain/langgraph-checkpoint";
|
|
4
4
|
//#region src/channels/base.ts
|
|
@@ -11,6 +11,9 @@ const STRUCTURED_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-
|
|
|
11
11
|
function isDeltaChannel(channel) {
|
|
12
12
|
return channel != null && channel.lc_graph_name === "DeltaChannel";
|
|
13
13
|
}
|
|
14
|
+
function deltaChannelWrites(channels, writes) {
|
|
15
|
+
return writes.filter(([ch]) => isDeltaChannel(channels[String(ch)]));
|
|
16
|
+
}
|
|
14
17
|
function isBaseChannel(obj) {
|
|
15
18
|
return obj != null && obj.lg_is_channel === true;
|
|
16
19
|
}
|
|
@@ -93,12 +96,46 @@ function emptyChannels(channels, checkpoint) {
|
|
|
93
96
|
*
|
|
94
97
|
* Embeds the superstep in the first UUID group so `ORDER BY task_id, idx`
|
|
95
98
|
* preserves chronological order while remaining a valid RFC UUID (required by
|
|
96
|
-
* Postgres `checkpoint_writes.task_id uuid` columns).
|
|
99
|
+
* Postgres `checkpoint_writes.task_id uuid` columns). Never `NULL_TASK_ID`:
|
|
100
|
+
* readers apply writes under it as the anchor checkpoint's own pending writes.
|
|
97
101
|
*/
|
|
98
102
|
function exitDeltaTaskId(step, taskId) {
|
|
99
103
|
if (!STRUCTURED_UUID.test(taskId)) throw new TypeError(`Invalid task id for exit delta: ${taskId}`);
|
|
100
104
|
const parts = taskId.toLowerCase().split("-");
|
|
101
|
-
|
|
105
|
+
const synthetic = `${String(step).padStart(8, "0")}-${parts[1]}-${parts[2]}-${parts[3]}-${parts[4]}`;
|
|
106
|
+
return synthetic === "00000000-0000-0000-0000-000000000000" ? EXIT_DELTA_NULL_TASK_ID : synthetic;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Synthetic task id for exit-mode writes of a superstep after the anchor's
|
|
110
|
+
* own.
|
|
111
|
+
*
|
|
112
|
+
* Sorts after every real task id, in step order, so replay keeps them after
|
|
113
|
+
* the anchor's own superstep whether a saver orders writes by task path
|
|
114
|
+
* (`writesSortKey`) or by task id. Mirrors Python's
|
|
115
|
+
* `exit_delta_late_task_id` exactly, including the two hexadecimal step
|
|
116
|
+
* groups (step is split across them).
|
|
117
|
+
*/
|
|
118
|
+
function exitDeltaLateTaskId(step, taskId) {
|
|
119
|
+
if (!STRUCTURED_UUID.test(taskId)) throw new TypeError(`Invalid task id for exit delta: ${taskId}`);
|
|
120
|
+
const parts = taskId.toLowerCase().split("-");
|
|
121
|
+
return `ffffffff-${(step >> 16).toString(16).padStart(4, "0")}-${(step & 65535).toString(16).padStart(4, "0")}-${parts[3]}-${parts[4]}`;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* DeltaChannels a branch starting from this checkpoint must snapshot.
|
|
125
|
+
*
|
|
126
|
+
* A checkpoint's pending writes belong to the child that consumed them, and
|
|
127
|
+
* nothing records which child that was. A new branch snapshots every delta
|
|
128
|
+
* channel they touch, so its ancestor walk never replays them (port of
|
|
129
|
+
* Python #8548's `delta_channels_with_pending_writes`).
|
|
130
|
+
*/
|
|
131
|
+
function deltaChannelsWithPendingWrites(channels, pendingWrites) {
|
|
132
|
+
const result = /* @__PURE__ */ new Set();
|
|
133
|
+
if (pendingWrites === void 0) return result;
|
|
134
|
+
for (const [, ch] of pendingWrites) {
|
|
135
|
+
const channel = channels[ch];
|
|
136
|
+
if (channel != null && isDeltaChannel(channel)) result.add(ch);
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
102
139
|
}
|
|
103
140
|
/**
|
|
104
141
|
* Return the set of {@link DeltaChannel} names that should snapshot now.
|
|
@@ -113,19 +150,23 @@ function exitDeltaTaskId(step, taskId) {
|
|
|
113
150
|
*/
|
|
114
151
|
function deltaChannelsToSnapshot(channels, countersSinceDeltaSnapshot, channelVersions) {
|
|
115
152
|
const result = /* @__PURE__ */ new Set();
|
|
116
|
-
const maxSupersteps = getDeltaMaxSuperstepsSinceSnapshot();
|
|
117
153
|
for (const name in channels) {
|
|
118
154
|
if (!Object.prototype.hasOwnProperty.call(channels, name)) continue;
|
|
119
155
|
const ch = channels[name];
|
|
120
156
|
if (!isDeltaChannel(ch) || !ch.isAvailable() || channelVersions !== void 0 && channelVersions[name] === void 0) continue;
|
|
121
|
-
|
|
122
|
-
if (updates >= ch.snapshotFrequency || supersteps >= maxSupersteps) result.add(name);
|
|
157
|
+
if (reachedSnapshotBound(ch, countersSinceDeltaSnapshot[name])) result.add(name);
|
|
123
158
|
}
|
|
124
159
|
return result;
|
|
125
160
|
}
|
|
161
|
+
/** Whether a delta channel's counters reached the bound where it snapshots. */
|
|
162
|
+
function reachedSnapshotBound(channel, counters) {
|
|
163
|
+
const [updates, supersteps] = counters ?? [0, 0];
|
|
164
|
+
return updates >= channel.snapshotFrequency || supersteps >= getDeltaMaxSuperstepsSinceSnapshot();
|
|
165
|
+
}
|
|
126
166
|
function createCheckpoint(checkpoint, channels, step, options) {
|
|
127
167
|
const channelsToSnapshot = options?.channelsToSnapshot ?? /* @__PURE__ */ new Set();
|
|
128
|
-
const {
|
|
168
|
+
const { storedVersions, getNextVersion } = options ?? {};
|
|
169
|
+
const bumped = {};
|
|
129
170
|
let values;
|
|
130
171
|
let channelVersions = checkpoint.channel_versions;
|
|
131
172
|
if (channels === void 0) values = checkpoint.channel_values;
|
|
@@ -136,7 +177,11 @@ function createCheckpoint(checkpoint, channels, step, options) {
|
|
|
136
177
|
if (!Object.prototype.hasOwnProperty.call(channels, k)) continue;
|
|
137
178
|
const channel = channels[k];
|
|
138
179
|
if (channelsToSnapshot.has(k)) {
|
|
139
|
-
if (getNextVersion !== void 0 &&
|
|
180
|
+
if (getNextVersion !== void 0 && storedVersions !== void 0 && channelVersions[k] === storedVersions[k]) {
|
|
181
|
+
const old = channelVersions[k];
|
|
182
|
+
channelVersions[k] = getNextVersion(old);
|
|
183
|
+
bumped[k] = [old, channelVersions[k]];
|
|
184
|
+
}
|
|
140
185
|
values[k] = new DeltaSnapshot(channel.get());
|
|
141
186
|
continue;
|
|
142
187
|
}
|
|
@@ -154,10 +199,65 @@ function createCheckpoint(checkpoint, channels, step, options) {
|
|
|
154
199
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
155
200
|
channel_values: values,
|
|
156
201
|
channel_versions: channelVersions,
|
|
157
|
-
versions_seen: checkpoint.versions_seen
|
|
202
|
+
versions_seen: markBumpsSeen(checkpoint.versions_seen, bumped, options?.triggerToNodes)
|
|
158
203
|
};
|
|
159
204
|
}
|
|
160
205
|
/**
|
|
206
|
+
* Advance whoever had seen a bumped channel's old version to the new one.
|
|
207
|
+
*
|
|
208
|
+
* A bump that only stores a snapshot is not a write. Left unseen, it would
|
|
209
|
+
* re-fire `interruptBefore` and rerun the channel's subscribers. A channel
|
|
210
|
+
* bumped from no version was never written, so it also goes to the
|
|
211
|
+
* subscribers that never ran: they have no entry, and would start on the bump.
|
|
212
|
+
* For each entry it advances, {@link SNAPSHOT_BUMPS} keeps the new version and
|
|
213
|
+
* the one the node really read, so {@link versionsSeenWithoutBumps} can put
|
|
214
|
+
* the read back.
|
|
215
|
+
*/
|
|
216
|
+
function markBumpsSeen(versionsSeen, bumped, triggerToNodes = {}) {
|
|
217
|
+
if (Object.keys(bumped).length === 0) return versionsSeen;
|
|
218
|
+
const out = { ...versionsSeen };
|
|
219
|
+
for (const [k, [old]] of Object.entries(bumped)) {
|
|
220
|
+
if (old !== void 0) continue;
|
|
221
|
+
for (const node of triggerToNodes[k] ?? []) out[node] ??= {};
|
|
222
|
+
}
|
|
223
|
+
const marks = { ...versionsSeen[SNAPSHOT_BUMPS] };
|
|
224
|
+
for (const node in out) {
|
|
225
|
+
if (!Object.prototype.hasOwnProperty.call(out, node) || node === "__snapshot_bumps__") continue;
|
|
226
|
+
for (const [k, [old, next]] of Object.entries(bumped)) {
|
|
227
|
+
if (out[node][k] !== old) continue;
|
|
228
|
+
const [advanced, read] = bumpKeys(node, k);
|
|
229
|
+
if (old !== void 0 && marks[advanced] !== old) marks[read] = old;
|
|
230
|
+
marks[advanced] = next;
|
|
231
|
+
out[node] = {
|
|
232
|
+
...out[node],
|
|
233
|
+
[k]: next
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (Object.keys(marks).length > 0) out[SNAPSHOT_BUMPS] = marks;
|
|
238
|
+
return out;
|
|
239
|
+
}
|
|
240
|
+
const bumpKeys = (node, channel) => [`${node}|${channel}`, `${node}:${channel}`];
|
|
241
|
+
/**
|
|
242
|
+
* `versions_seen` as the nodes read it: an entry a snapshot bump advanced goes
|
|
243
|
+
* back to the version the node really read, or away if it never read one.
|
|
244
|
+
*/
|
|
245
|
+
function versionsSeenWithoutBumps(versionsSeen) {
|
|
246
|
+
const marks = versionsSeen[SNAPSHOT_BUMPS];
|
|
247
|
+
if (marks === void 0 || Object.keys(marks).length === 0) return versionsSeen;
|
|
248
|
+
const out = {};
|
|
249
|
+
for (const node in versionsSeen) {
|
|
250
|
+
if (!Object.prototype.hasOwnProperty.call(versionsSeen, node) || node === "__snapshot_bumps__") continue;
|
|
251
|
+
out[node] = {};
|
|
252
|
+
for (const [k, v] of Object.entries(versionsSeen[node])) {
|
|
253
|
+
const [advanced, read] = bumpKeys(node, k);
|
|
254
|
+
if (marks[advanced] !== v) out[node][k] = v;
|
|
255
|
+
else if (read in marks) out[node][k] = marks[read];
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return out;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
161
261
|
* Checkpoint metadata key, `true` when every DeltaChannel write on the
|
|
162
262
|
* checkpoint's branch carries a version. A delta channel with no version there
|
|
163
263
|
* was never written, so it needs no walk. Branches started before this key
|
|
@@ -169,19 +269,25 @@ function isDeltaWritesVersioned(metadata) {
|
|
|
169
269
|
return metadata?.[DELTA_WRITES_VERSIONED] === true;
|
|
170
270
|
}
|
|
171
271
|
/**
|
|
172
|
-
*
|
|
272
|
+
* Channels whose value at `checkpoint` the ancestor walk rebuilds.
|
|
173
273
|
*
|
|
174
274
|
* A {@link DeltaSnapshot} or a migrated plain value in `channel_values`
|
|
175
275
|
* resolves directly, so only a channel with nothing stored there needs the
|
|
176
276
|
* walk. With `versionedOnly`, a channel with no version was never written, so
|
|
177
277
|
* it is empty without one; a walk for it would find no snapshot to stop at and
|
|
178
278
|
* read every ancestor, every time the thread is loaded.
|
|
279
|
+
*
|
|
280
|
+
* A `BinaryOperatorAggregate` stores its value at every version, so one with a
|
|
281
|
+
* version and nothing stored was written as a DeltaChannel before its
|
|
282
|
+
* annotation changed, and is rebuilt the same way.
|
|
179
283
|
*/
|
|
180
284
|
function deltaChannelsToReplay(specs, checkpoint, versionedOnly) {
|
|
181
285
|
const keys = [];
|
|
182
286
|
for (const k in specs) {
|
|
183
287
|
if (!Object.prototype.hasOwnProperty.call(specs, k)) continue;
|
|
184
|
-
if (
|
|
288
|
+
if (Object.prototype.hasOwnProperty.call(checkpoint.channel_values, k)) continue;
|
|
289
|
+
const versioned = checkpoint.channel_versions[k] !== void 0;
|
|
290
|
+
if (isDeltaChannel(specs[k]) && (versioned || !versionedOnly) || specs[k].lc_graph_name === "BinaryOperatorAggregate" && versioned) keys.push(k);
|
|
185
291
|
}
|
|
186
292
|
return keys;
|
|
187
293
|
}
|
|
@@ -215,12 +321,13 @@ async function channelsFromCheckpoint(specs, checkpoint, options) {
|
|
|
215
321
|
const history = histories[k];
|
|
216
322
|
if (history === void 0) continue;
|
|
217
323
|
const replayCh = filteredSpecs[k].fromCheckpoint(history.seed);
|
|
218
|
-
replayCh.replayWrites(history.writes);
|
|
324
|
+
if (isDeltaChannel(replayCh)) replayCh.replayWrites(history.writes);
|
|
325
|
+
else for (const [, , value] of history.writes) replayCh.update([value]);
|
|
219
326
|
channels[k] = replayCh;
|
|
220
327
|
}
|
|
221
328
|
return channels;
|
|
222
329
|
}
|
|
223
330
|
//#endregion
|
|
224
|
-
export { BaseChannel, DELTA_WRITES_VERSIONED, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, emptyChannels, exitDeltaTaskId, getOnlyChannels, isBaseChannel, isDeltaChannel, isDeltaWritesVersioned };
|
|
331
|
+
export { BaseChannel, DELTA_WRITES_VERSIONED, channelsFromCheckpoint, createCheckpoint, deltaChannelWrites, deltaChannelsToSnapshot, deltaChannelsWithPendingWrites, emptyChannels, exitDeltaLateTaskId, exitDeltaTaskId, getOnlyChannels, isBaseChannel, isDeltaChannel, isDeltaWritesVersioned, reachedSnapshotBound, versionsSeenWithoutBumps };
|
|
225
332
|
|
|
226
333
|
//# sourceMappingURL=base.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","names":[],"sources":["../../src/channels/base.ts"],"sourcesContent":["import {\n ReadonlyCheckpoint,\n uuid6,\n Checkpoint,\n DeltaSnapshot,\n type BaseCheckpointSaver,\n type CheckpointMetadata,\n type DeltaChannelHistory,\n} from \"@langchain/langgraph-checkpoint\";\nimport type { RunnableConfig } from \"@langchain/core/runnables\";\nimport { EmptyChannelError } from \"../errors.js\";\nimport { getDeltaMaxSuperstepsSinceSnapshot } from \"../constants.js\";\n\n/** Matches Postgres `uuid` / Python `uuid.UUID` (128-bit, 8-4-4-4-12 hex). */\nconst STRUCTURED_UUID =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Structural check for a {@link DeltaChannel} without importing it (avoids an\n * import cycle: `delta.ts` imports `base.ts`).\n */\nexport function isDeltaChannel(channel: BaseChannel): boolean {\n return channel != null && channel.lc_graph_name === \"DeltaChannel\";\n}\n\nexport function isBaseChannel(obj: unknown): obj is BaseChannel {\n return obj != null && (obj as BaseChannel).lg_is_channel === true;\n}\n\n/** @internal */\nexport abstract class BaseChannel<\n ValueType = unknown,\n UpdateType = unknown,\n CheckpointType = unknown,\n> {\n ValueType: ValueType;\n\n UpdateType: UpdateType;\n\n /**\n * The name of the channel.\n */\n abstract lc_graph_name: string;\n\n /** @ignore */\n lg_is_channel = true;\n\n /**\n * Return a new identical channel, optionally initialized from a checkpoint.\n * Can be thought of as a \"restoration\" from a checkpoint which is a \"snapshot\" of the channel's state.\n *\n * @param {CheckpointType | undefined} checkpoint\n * @param {CheckpointType | undefined} initialValue\n * @returns {this}\n */\n abstract fromCheckpoint(checkpoint?: CheckpointType): this;\n\n /**\n * Update the channel's value with the given sequence of updates.\n * The order of the updates in the sequence is arbitrary.\n * This method is called by Pregel for all channels at the end of each step.\n * If there are no updates, it is called with an empty sequence.\n *\n * Raises InvalidUpdateError if the sequence of updates is invalid.\n * Returns True if the channel was updated, False otherwise.\n *\n * @throws {InvalidUpdateError} if the sequence of updates is invalid.\n * @param {Array<UpdateType>} values\n * @returns {void}\n */\n abstract update(values: UpdateType[]): boolean;\n\n /**\n * Return the current value of the channel.\n *\n * @throws {EmptyChannelError} if the channel is empty (never updated yet).\n * @returns {ValueType}\n */\n abstract get(): ValueType;\n\n /**\n * Return a string representation of the channel's current state.\n *\n * @throws {EmptyChannelError} if the channel is empty (never updated yet), or doesn't support checkpoints.\n * @returns {CheckpointType | undefined}\n */\n abstract checkpoint(): CheckpointType | undefined;\n\n /**\n * Mark the current value of the channel as consumed. By default, no-op.\n * A channel can use this method to modify its state, preventing the value\n * from being consumed again.\n *\n * Returns True if the channel was updated, False otherwise.\n */\n consume(): boolean {\n return false;\n }\n\n /**\n * Notify the channel that the Pregel run is finishing. By default, no-op.\n * A channel can use this method to modify its state, preventing finish.\n *\n * Returns True if the channel was updated, False otherwise.\n */\n finish(): boolean {\n return false;\n }\n\n /**\n * Return True if the channel is available (not empty), False otherwise.\n * Subclasses should override this method to provide a more efficient\n * implementation than calling get() and catching EmptyChannelError.\n */\n isAvailable(): boolean {\n try {\n this.get();\n return true;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n if (error.name === EmptyChannelError.unminifiable_name) {\n return false;\n }\n throw error;\n }\n }\n\n /**\n * Compare this channel with another channel for equality.\n * Used to determine if two channels with the same key are semantically equivalent.\n * Subclasses should override this method to provide a meaningful comparison.\n *\n * @param {BaseChannel} other - The other channel to compare with.\n * @returns {boolean} True if the channels are equal, false otherwise.\n */\n equals(other: BaseChannel): boolean {\n return this === other;\n }\n}\n\nconst IS_ONLY_BASE_CHANNEL = Symbol.for(\"LG_IS_ONLY_BASE_CHANNEL\");\nexport function getOnlyChannels(\n channels: Record<string, BaseChannel>\n): Record<string, BaseChannel> {\n // @ts-expect-error - we know it's a record of base channels\n if (channels[IS_ONLY_BASE_CHANNEL] === true) return channels;\n\n const newChannels = {} as Record<string, BaseChannel>;\n for (const k in channels) {\n if (!Object.prototype.hasOwnProperty.call(channels, k)) continue;\n const value = channels[k];\n if (isBaseChannel(value)) newChannels[k] = value;\n }\n\n Object.assign(newChannels, { [IS_ONLY_BASE_CHANNEL]: true });\n return newChannels;\n}\n\nexport function emptyChannels<Cc extends Record<string, BaseChannel>>(\n channels: Cc,\n checkpoint: ReadonlyCheckpoint\n): Cc {\n const filteredChannels = getOnlyChannels(channels) as Cc;\n\n const newChannels = {} as Cc;\n for (const k in filteredChannels) {\n if (!Object.prototype.hasOwnProperty.call(filteredChannels, k)) continue;\n const channelValue = checkpoint.channel_values[k];\n newChannels[k] = filteredChannels[k].fromCheckpoint(channelValue);\n }\n Object.assign(newChannels, { [IS_ONLY_BASE_CHANNEL]: true });\n return newChannels;\n}\n\n/**\n * Minimal structural view of a {@link DeltaChannel}, used by helpers in this\n * module that must not import the concrete class (import-cycle avoidance).\n */\ninterface DeltaChannelLike extends BaseChannel {\n snapshotFrequency: number;\n replayWrites(writes: DeltaChannelHistory[\"writes\"]): void;\n}\n\n/**\n * Synthetic task id for exit-mode DeltaChannel writes.\n *\n * Embeds the superstep in the first UUID group so `ORDER BY task_id, idx`\n * preserves chronological order while remaining a valid RFC UUID (required by\n * Postgres `checkpoint_writes.task_id uuid` columns).\n */\nexport function exitDeltaTaskId(step: number, taskId: string): string {\n if (!STRUCTURED_UUID.test(taskId)) {\n throw new TypeError(`Invalid task id for exit delta: ${taskId}`);\n }\n const parts = taskId.toLowerCase().split(\"-\");\n const stepPart = String(step).padStart(8, \"0\");\n return `${stepPart}-${parts[1]}-${parts[2]}-${parts[3]}-${parts[4]}`;\n}\n\n/**\n * Return the set of {@link DeltaChannel} names that should snapshot now.\n *\n * A channel snapshots when EITHER its accumulated update count reaches\n * `snapshotFrequency` OR the total supersteps since its last snapshot reaches\n * `DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT`. Given `channelVersions`, a channel\n * without a version is skipped: it was never written on this branch, so it\n * has nothing to snapshot. Pass `undefined` on a branch without\n * {@link DELTA_WRITES_VERSIONED}, where such a channel can still hold writes\n * and this snapshot is what bounds its walk. Pure predicate, no mutation.\n */\nexport function deltaChannelsToSnapshot(\n channels: Record<string, BaseChannel>,\n countersSinceDeltaSnapshot: Record<string, [number, number]>,\n channelVersions: Record<string, number | string> | undefined\n): Set<string> {\n const result = new Set<string>();\n const maxSupersteps = getDeltaMaxSuperstepsSinceSnapshot();\n for (const name in channels) {\n if (!Object.prototype.hasOwnProperty.call(channels, name)) continue;\n const ch = channels[name];\n if (\n !isDeltaChannel(ch) ||\n !ch.isAvailable() ||\n (channelVersions !== undefined && channelVersions[name] === undefined)\n ) {\n continue;\n }\n const [updates, supersteps] = countersSinceDeltaSnapshot[name] ?? [0, 0];\n if (\n updates >= (ch as DeltaChannelLike).snapshotFrequency ||\n supersteps >= maxSupersteps\n ) {\n result.add(name);\n }\n }\n return result;\n}\n\nexport function createCheckpoint<ValueType>(\n checkpoint: ReadonlyCheckpoint,\n channels: Record<string, BaseChannel<ValueType>> | undefined,\n step: number,\n options?: {\n id?: string;\n channelsToSnapshot?: Set<string>;\n updatedChannels?: Set<string>;\n getNextVersion?: (current: number | string | undefined) => number | string;\n }\n): Checkpoint {\n const channelsToSnapshot = options?.channelsToSnapshot ?? new Set<string>();\n const { updatedChannels, getNextVersion } = options ?? {};\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let values: Record<string, any>;\n let channelVersions: Record<string, number | string> =\n checkpoint.channel_versions;\n if (channels === undefined) {\n values = checkpoint.channel_values;\n } else {\n values = {};\n channelVersions = { ...checkpoint.channel_versions };\n for (const k in channels) {\n if (!Object.prototype.hasOwnProperty.call(channels, k)) continue;\n const channel = channels[k];\n if (channelsToSnapshot.has(k)) {\n // Snapshot a DeltaChannel: store the materialized value directly. In\n // exit/deferred modes the channel may have reached its snapshot\n // threshold over several supersteps without the LAST superstep\n // writing to it, so its version wouldn't be bumped by applyWrites —\n // bump it here so the saver includes the snapshot blob.\n if (\n getNextVersion !== undefined &&\n (updatedChannels === undefined || !updatedChannels.has(k))\n ) {\n channelVersions[k] = getNextVersion(channelVersions[k]);\n }\n values[k] = new DeltaSnapshot(channel.get());\n continue;\n }\n if (isDeltaChannel(channel)) {\n // Omitted from channel_values; reconstructed from ancestor writes.\n continue;\n }\n try {\n values[k] = channel.checkpoint();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n if (error.name === EmptyChannelError.unminifiable_name) {\n // no-op\n } else {\n throw error; // Rethrow unexpected errors\n }\n }\n }\n }\n\n return {\n v: 4,\n id: options?.id ?? uuid6(step),\n ts: new Date().toISOString(),\n channel_values: values,\n channel_versions: channelVersions,\n versions_seen: checkpoint.versions_seen,\n };\n}\n\n/**\n * Checkpoint metadata key, `true` when every DeltaChannel write on the\n * checkpoint's branch carries a version. A delta channel with no version there\n * was never written, so it needs no walk. Branches started before this key\n * existed never get it: an older `updateState` stored a delta channel's first\n * write without a version, and only the walk finds such a write.\n */\nexport const DELTA_WRITES_VERSIONED = \"delta_writes_versioned\";\n\nexport function isDeltaWritesVersioned(\n metadata: CheckpointMetadata | undefined\n): boolean {\n return (\n (metadata as Record<string, unknown> | undefined)?.[\n DELTA_WRITES_VERSIONED\n ] === true\n );\n}\n\n/**\n * Delta channels whose value at `checkpoint` the ancestor walk rebuilds.\n *\n * A {@link DeltaSnapshot} or a migrated plain value in `channel_values`\n * resolves directly, so only a channel with nothing stored there needs the\n * walk. With `versionedOnly`, a channel with no version was never written, so\n * it is empty without one; a walk for it would find no snapshot to stop at and\n * read every ancestor, every time the thread is loaded.\n */\nfunction deltaChannelsToReplay(\n specs: Record<string, BaseChannel>,\n checkpoint: ReadonlyCheckpoint,\n versionedOnly: boolean\n): string[] {\n const keys: string[] = [];\n for (const k in specs) {\n if (!Object.prototype.hasOwnProperty.call(specs, k)) continue;\n if (\n isDeltaChannel(specs[k]) &&\n (!versionedOnly || checkpoint.channel_versions[k] !== undefined) &&\n !Object.prototype.hasOwnProperty.call(checkpoint.channel_values, k)\n ) {\n keys.push(k);\n }\n }\n return keys;\n}\n\n/**\n * Hydrate channels from a checkpoint, reconstructing any {@link DeltaChannel}\n * whose value is absent from `channel_values` by replaying ancestor writes.\n *\n * For most channels (and for delta channels with a {@link DeltaSnapshot} or a\n * migrated plain value in `channel_values`), {@link emptyChannels} is\n * sufficient and no saver access is required. When a delta channel is absent\n * from `channel_values`, an ancestor walk via `saver.getDeltaChannelHistory`\n * finds the nearest seed and accumulates the writes between it and the\n * target. All delta channels needing replay are batched into a single saver\n * call. Pass `deltaWritesVersioned` for a checkpoint whose metadata carries\n * {@link DELTA_WRITES_VERSIONED}, so delta channels without a version are\n * left empty instead of walked.\n */\nexport async function channelsFromCheckpoint<\n Cc extends Record<string, BaseChannel>,\n>(\n specs: Cc,\n checkpoint: ReadonlyCheckpoint,\n options?: {\n saver?: BaseCheckpointSaver;\n config?: RunnableConfig;\n deltaWritesVersioned?: boolean;\n }\n): Promise<Cc> {\n const channels = emptyChannels(specs, checkpoint);\n const { saver, config } = options ?? {};\n\n const filteredSpecs = getOnlyChannels(specs);\n const deltaKeys = deltaChannelsToReplay(\n filteredSpecs,\n checkpoint,\n options?.deltaWritesVersioned ?? false\n );\n\n const written = deltaKeys.filter(\n (k) => checkpoint.channel_versions[k] !== undefined\n );\n if ((saver === undefined || config === undefined) && written.length > 0) {\n throw new Error(\n `DeltaChannel ${written.join(\", \")} has history to replay but no checkpointer or config was passed to read it`\n );\n }\n if (deltaKeys.length === 0 || saver === undefined || config === undefined) {\n return channels;\n }\n\n const histories = await saver.getDeltaChannelHistory({\n config,\n channels: deltaKeys,\n });\n for (const k of deltaKeys) {\n const history = histories[k];\n if (history === undefined) continue;\n const replayCh = filteredSpecs[k].fromCheckpoint(\n history.seed\n ) as unknown as DeltaChannelLike;\n replayCh.replayWrites(history.writes);\n (channels as Record<string, BaseChannel>)[k] = replayCh;\n }\n return channels;\n}\n"],"mappings":";;;;;AAcA,MAAM,kBACJ;;;;;AAMF,SAAgB,eAAe,SAA+B;AAC5D,QAAO,WAAW,QAAQ,QAAQ,kBAAkB;;AAGtD,SAAgB,cAAc,KAAkC;AAC9D,QAAO,OAAO,QAAS,IAAoB,kBAAkB;;;AAI/D,IAAsB,cAAtB,MAIE;CACA;CAEA;;CAQA,gBAAgB;;;;;;;;CAkDhB,UAAmB;AACjB,SAAO;;;;;;;;CAST,SAAkB;AAChB,SAAO;;;;;;;CAQT,cAAuB;AACrB,MAAI;AACF,QAAK,KAAK;AACV,UAAO;WAEA,OAAY;AACnB,OAAI,MAAM,SAAS,kBAAkB,kBACnC,QAAO;AAET,SAAM;;;;;;;;;;;CAYV,OAAO,OAA6B;AAClC,SAAO,SAAS;;;AAIpB,MAAM,uBAAuB,OAAO,IAAI,0BAA0B;AAClE,SAAgB,gBACd,UAC6B;AAE7B,KAAI,SAAS,0BAA0B,KAAM,QAAO;CAEpD,MAAM,cAAc,EAAE;AACtB,MAAK,MAAM,KAAK,UAAU;AACxB,MAAI,CAAC,OAAO,UAAU,eAAe,KAAK,UAAU,EAAE,CAAE;EACxD,MAAM,QAAQ,SAAS;AACvB,MAAI,cAAc,MAAM,CAAE,aAAY,KAAK;;AAG7C,QAAO,OAAO,aAAa,GAAG,uBAAuB,MAAM,CAAC;AAC5D,QAAO;;AAGT,SAAgB,cACd,UACA,YACI;CACJ,MAAM,mBAAmB,gBAAgB,SAAS;CAElD,MAAM,cAAc,EAAE;AACtB,MAAK,MAAM,KAAK,kBAAkB;AAChC,MAAI,CAAC,OAAO,UAAU,eAAe,KAAK,kBAAkB,EAAE,CAAE;EAChE,MAAM,eAAe,WAAW,eAAe;AAC/C,cAAY,KAAK,iBAAiB,GAAG,eAAe,aAAa;;AAEnE,QAAO,OAAO,aAAa,GAAG,uBAAuB,MAAM,CAAC;AAC5D,QAAO;;;;;;;;;AAmBT,SAAgB,gBAAgB,MAAc,QAAwB;AACpE,KAAI,CAAC,gBAAgB,KAAK,OAAO,CAC/B,OAAM,IAAI,UAAU,mCAAmC,SAAS;CAElE,MAAM,QAAQ,OAAO,aAAa,CAAC,MAAM,IAAI;AAE7C,QAAO,GADU,OAAO,KAAK,CAAC,SAAS,GAAG,IAAI,CAC3B,GAAG,MAAM,GAAG,GAAG,MAAM,GAAG,GAAG,MAAM,GAAG,GAAG,MAAM;;;;;;;;;;;;;AAclE,SAAgB,wBACd,UACA,4BACA,iBACa;CACb,MAAM,yBAAS,IAAI,KAAa;CAChC,MAAM,gBAAgB,oCAAoC;AAC1D,MAAK,MAAM,QAAQ,UAAU;AAC3B,MAAI,CAAC,OAAO,UAAU,eAAe,KAAK,UAAU,KAAK,CAAE;EAC3D,MAAM,KAAK,SAAS;AACpB,MACE,CAAC,eAAe,GAAG,IACnB,CAAC,GAAG,aAAa,IAChB,oBAAoB,KAAA,KAAa,gBAAgB,UAAU,KAAA,EAE5D;EAEF,MAAM,CAAC,SAAS,cAAc,2BAA2B,SAAS,CAAC,GAAG,EAAE;AACxE,MACE,WAAY,GAAwB,qBACpC,cAAc,cAEd,QAAO,IAAI,KAAK;;AAGpB,QAAO;;AAGT,SAAgB,iBACd,YACA,UACA,MACA,SAMY;CACZ,MAAM,qBAAqB,SAAS,sCAAsB,IAAI,KAAa;CAC3E,MAAM,EAAE,iBAAiB,mBAAmB,WAAW,EAAE;CAEzD,IAAI;CACJ,IAAI,kBACF,WAAW;AACb,KAAI,aAAa,KAAA,EACf,UAAS,WAAW;MACf;AACL,WAAS,EAAE;AACX,oBAAkB,EAAE,GAAG,WAAW,kBAAkB;AACpD,OAAK,MAAM,KAAK,UAAU;AACxB,OAAI,CAAC,OAAO,UAAU,eAAe,KAAK,UAAU,EAAE,CAAE;GACxD,MAAM,UAAU,SAAS;AACzB,OAAI,mBAAmB,IAAI,EAAE,EAAE;AAM7B,QACE,mBAAmB,KAAA,MAClB,oBAAoB,KAAA,KAAa,CAAC,gBAAgB,IAAI,EAAE,EAEzD,iBAAgB,KAAK,eAAe,gBAAgB,GAAG;AAEzD,WAAO,KAAK,IAAI,cAAc,QAAQ,KAAK,CAAC;AAC5C;;AAEF,OAAI,eAAe,QAAQ,CAEzB;AAEF,OAAI;AACF,WAAO,KAAK,QAAQ,YAAY;YAEzB,OAAY;AACnB,QAAI,MAAM,SAAS,kBAAkB,mBAAmB,OAGtD,OAAM;;;;AAMd,QAAO;EACL,GAAG;EACH,IAAI,SAAS,MAAM,MAAM,KAAK;EAC9B,qBAAI,IAAI,MAAM,EAAC,aAAa;EAC5B,gBAAgB;EAChB,kBAAkB;EAClB,eAAe,WAAW;EAC3B;;;;;;;;;AAUH,MAAa,yBAAyB;AAEtC,SAAgB,uBACd,UACS;AACT,QACG,WACC,4BACI;;;;;;;;;;;AAaV,SAAS,sBACP,OACA,YACA,eACU;CACV,MAAM,OAAiB,EAAE;AACzB,MAAK,MAAM,KAAK,OAAO;AACrB,MAAI,CAAC,OAAO,UAAU,eAAe,KAAK,OAAO,EAAE,CAAE;AACrD,MACE,eAAe,MAAM,GAAG,KACvB,CAAC,iBAAiB,WAAW,iBAAiB,OAAO,KAAA,MACtD,CAAC,OAAO,UAAU,eAAe,KAAK,WAAW,gBAAgB,EAAE,CAEnE,MAAK,KAAK,EAAE;;AAGhB,QAAO;;;;;;;;;;;;;;;;AAiBT,eAAsB,uBAGpB,OACA,YACA,SAKa;CACb,MAAM,WAAW,cAAc,OAAO,WAAW;CACjD,MAAM,EAAE,OAAO,WAAW,WAAW,EAAE;CAEvC,MAAM,gBAAgB,gBAAgB,MAAM;CAC5C,MAAM,YAAY,sBAChB,eACA,YACA,SAAS,wBAAwB,MAClC;CAED,MAAM,UAAU,UAAU,QACvB,MAAM,WAAW,iBAAiB,OAAO,KAAA,EAC3C;AACD,MAAK,UAAU,KAAA,KAAa,WAAW,KAAA,MAAc,QAAQ,SAAS,EACpE,OAAM,IAAI,MACR,gBAAgB,QAAQ,KAAK,KAAK,CAAC,4EACpC;AAEH,KAAI,UAAU,WAAW,KAAK,UAAU,KAAA,KAAa,WAAW,KAAA,EAC9D,QAAO;CAGT,MAAM,YAAY,MAAM,MAAM,uBAAuB;EACnD;EACA,UAAU;EACX,CAAC;AACF,MAAK,MAAM,KAAK,WAAW;EACzB,MAAM,UAAU,UAAU;AAC1B,MAAI,YAAY,KAAA,EAAW;EAC3B,MAAM,WAAW,cAAc,GAAG,eAChC,QAAQ,KACT;AACD,WAAS,aAAa,QAAQ,OAAO;AACpC,WAAyC,KAAK;;AAEjD,QAAO"}
|
|
1
|
+
{"version":3,"file":"base.js","names":[],"sources":["../../src/channels/base.ts"],"sourcesContent":["import {\n ReadonlyCheckpoint,\n uuid6,\n Checkpoint,\n DeltaSnapshot,\n type BaseCheckpointSaver,\n type CheckpointMetadata,\n type DeltaChannelHistory,\n} from \"@langchain/langgraph-checkpoint\";\nimport type { RunnableConfig } from \"@langchain/core/runnables\";\nimport { EmptyChannelError } from \"../errors.js\";\nimport {\n CHECKPOINT_NAMESPACE_END,\n CHECKPOINT_NAMESPACE_SEPARATOR,\n EXIT_DELTA_NULL_TASK_ID,\n getDeltaMaxSuperstepsSinceSnapshot,\n NULL_TASK_ID,\n SNAPSHOT_BUMPS,\n} from \"../constants.js\";\n\n/** Matches Postgres `uuid` / Python `uuid.UUID` (128-bit, 8-4-4-4-12 hex). */\nconst STRUCTURED_UUID =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Structural check for a {@link DeltaChannel} without importing it (avoids an\n * import cycle: `delta.ts` imports `base.ts`).\n */\nexport function isDeltaChannel(channel: BaseChannel): boolean {\n return channel != null && channel.lc_graph_name === \"DeltaChannel\";\n}\n\nexport function deltaChannelWrites<W extends readonly [PropertyKey, unknown]>(\n channels: Record<string, BaseChannel>,\n writes: ReadonlyArray<W>\n): W[] {\n return writes.filter(([ch]) => isDeltaChannel(channels[String(ch)]));\n}\n\nexport function isBaseChannel(obj: unknown): obj is BaseChannel {\n return obj != null && (obj as BaseChannel).lg_is_channel === true;\n}\n\n/** @internal */\nexport abstract class BaseChannel<\n ValueType = unknown,\n UpdateType = unknown,\n CheckpointType = unknown,\n> {\n ValueType: ValueType;\n\n UpdateType: UpdateType;\n\n /**\n * The name of the channel.\n */\n abstract lc_graph_name: string;\n\n /** @ignore */\n lg_is_channel = true;\n\n /**\n * Return a new identical channel, optionally initialized from a checkpoint.\n * Can be thought of as a \"restoration\" from a checkpoint which is a \"snapshot\" of the channel's state.\n *\n * @param {CheckpointType | undefined} checkpoint\n * @param {CheckpointType | undefined} initialValue\n * @returns {this}\n */\n abstract fromCheckpoint(checkpoint?: CheckpointType): this;\n\n /**\n * Update the channel's value with the given sequence of updates.\n * The order of the updates in the sequence is arbitrary.\n * This method is called by Pregel for all channels at the end of each step.\n * If there are no updates, it is called with an empty sequence.\n *\n * Raises InvalidUpdateError if the sequence of updates is invalid.\n * Returns True if the channel was updated, False otherwise.\n *\n * @throws {InvalidUpdateError} if the sequence of updates is invalid.\n * @param {Array<UpdateType>} values\n * @returns {void}\n */\n abstract update(values: UpdateType[]): boolean;\n\n /**\n * Return the current value of the channel.\n *\n * @throws {EmptyChannelError} if the channel is empty (never updated yet).\n * @returns {ValueType}\n */\n abstract get(): ValueType;\n\n /**\n * Return a string representation of the channel's current state.\n *\n * @throws {EmptyChannelError} if the channel is empty (never updated yet), or doesn't support checkpoints.\n * @returns {CheckpointType | undefined}\n */\n abstract checkpoint(): CheckpointType | undefined;\n\n /**\n * Mark the current value of the channel as consumed. By default, no-op.\n * A channel can use this method to modify its state, preventing the value\n * from being consumed again.\n *\n * Returns True if the channel was updated, False otherwise.\n */\n consume(): boolean {\n return false;\n }\n\n /**\n * Notify the channel that the Pregel run is finishing. By default, no-op.\n * A channel can use this method to modify its state, preventing finish.\n *\n * Returns True if the channel was updated, False otherwise.\n */\n finish(): boolean {\n return false;\n }\n\n /**\n * Return True if the channel is available (not empty), False otherwise.\n * Subclasses should override this method to provide a more efficient\n * implementation than calling get() and catching EmptyChannelError.\n */\n isAvailable(): boolean {\n try {\n this.get();\n return true;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n if (error.name === EmptyChannelError.unminifiable_name) {\n return false;\n }\n throw error;\n }\n }\n\n /**\n * Compare this channel with another channel for equality.\n * Used to determine if two channels with the same key are semantically equivalent.\n * Subclasses should override this method to provide a meaningful comparison.\n *\n * @param {BaseChannel} other - The other channel to compare with.\n * @returns {boolean} True if the channels are equal, false otherwise.\n */\n equals(other: BaseChannel): boolean {\n return this === other;\n }\n}\n\nconst IS_ONLY_BASE_CHANNEL = Symbol.for(\"LG_IS_ONLY_BASE_CHANNEL\");\nexport function getOnlyChannels(\n channels: Record<string, BaseChannel>\n): Record<string, BaseChannel> {\n // @ts-expect-error - we know it's a record of base channels\n if (channels[IS_ONLY_BASE_CHANNEL] === true) return channels;\n\n const newChannels = {} as Record<string, BaseChannel>;\n for (const k in channels) {\n if (!Object.prototype.hasOwnProperty.call(channels, k)) continue;\n const value = channels[k];\n if (isBaseChannel(value)) newChannels[k] = value;\n }\n\n Object.assign(newChannels, { [IS_ONLY_BASE_CHANNEL]: true });\n return newChannels;\n}\n\nexport function emptyChannels<Cc extends Record<string, BaseChannel>>(\n channels: Cc,\n checkpoint: ReadonlyCheckpoint\n): Cc {\n const filteredChannels = getOnlyChannels(channels) as Cc;\n\n const newChannels = {} as Cc;\n for (const k in filteredChannels) {\n if (!Object.prototype.hasOwnProperty.call(filteredChannels, k)) continue;\n const channelValue = checkpoint.channel_values[k];\n newChannels[k] = filteredChannels[k].fromCheckpoint(channelValue);\n }\n Object.assign(newChannels, { [IS_ONLY_BASE_CHANNEL]: true });\n return newChannels;\n}\n\n/**\n * Minimal structural view of a {@link DeltaChannel}, used by helpers in this\n * module that must not import the concrete class (import-cycle avoidance).\n */\ninterface DeltaChannelLike extends BaseChannel {\n snapshotFrequency: number;\n replayWrites(writes: DeltaChannelHistory[\"writes\"]): void;\n}\n\n/**\n * Synthetic task id for exit-mode DeltaChannel writes.\n *\n * Embeds the superstep in the first UUID group so `ORDER BY task_id, idx`\n * preserves chronological order while remaining a valid RFC UUID (required by\n * Postgres `checkpoint_writes.task_id uuid` columns). Never `NULL_TASK_ID`:\n * readers apply writes under it as the anchor checkpoint's own pending writes.\n */\nexport function exitDeltaTaskId(step: number, taskId: string): string {\n if (!STRUCTURED_UUID.test(taskId)) {\n throw new TypeError(`Invalid task id for exit delta: ${taskId}`);\n }\n const parts = taskId.toLowerCase().split(\"-\");\n const stepPart = String(step).padStart(8, \"0\");\n const synthetic = `${stepPart}-${parts[1]}-${parts[2]}-${parts[3]}-${parts[4]}`;\n return synthetic === NULL_TASK_ID ? EXIT_DELTA_NULL_TASK_ID : synthetic;\n}\n\n/**\n * Synthetic task id for exit-mode writes of a superstep after the anchor's\n * own.\n *\n * Sorts after every real task id, in step order, so replay keeps them after\n * the anchor's own superstep whether a saver orders writes by task path\n * (`writesSortKey`) or by task id. Mirrors Python's\n * `exit_delta_late_task_id` exactly, including the two hexadecimal step\n * groups (step is split across them).\n */\nexport function exitDeltaLateTaskId(step: number, taskId: string): string {\n if (!STRUCTURED_UUID.test(taskId)) {\n throw new TypeError(`Invalid task id for exit delta: ${taskId}`);\n }\n const parts = taskId.toLowerCase().split(\"-\");\n const stepHi = (step >> 16).toString(16).padStart(4, \"0\");\n const stepLo = (step & 0xffff).toString(16).padStart(4, \"0\");\n return `ffffffff-${stepHi}-${stepLo}-${parts[3]}-${parts[4]}`;\n}\n\n/**\n * DeltaChannels a branch starting from this checkpoint must snapshot.\n *\n * A checkpoint's pending writes belong to the child that consumed them, and\n * nothing records which child that was. A new branch snapshots every delta\n * channel they touch, so its ancestor walk never replays them (port of\n * Python #8548's `delta_channels_with_pending_writes`).\n */\nexport function deltaChannelsWithPendingWrites(\n channels: Record<string, BaseChannel>,\n pendingWrites: ReadonlyArray<[string, string, unknown]> | undefined\n): Set<string> {\n const result = new Set<string>();\n if (pendingWrites === undefined) return result;\n for (const [, ch] of pendingWrites) {\n const channel = channels[ch];\n if (channel != null && isDeltaChannel(channel)) result.add(ch);\n }\n return result;\n}\n\n/**\n * Return the set of {@link DeltaChannel} names that should snapshot now.\n *\n * A channel snapshots when EITHER its accumulated update count reaches\n * `snapshotFrequency` OR the total supersteps since its last snapshot reaches\n * `DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT`. Given `channelVersions`, a channel\n * without a version is skipped: it was never written on this branch, so it\n * has nothing to snapshot. Pass `undefined` on a branch without\n * {@link DELTA_WRITES_VERSIONED}, where such a channel can still hold writes\n * and this snapshot is what bounds its walk. Pure predicate, no mutation.\n */\nexport function deltaChannelsToSnapshot(\n channels: Record<string, BaseChannel>,\n countersSinceDeltaSnapshot: Record<string, [number, number]>,\n channelVersions: Record<string, number | string> | undefined\n): Set<string> {\n const result = new Set<string>();\n for (const name in channels) {\n if (!Object.prototype.hasOwnProperty.call(channels, name)) continue;\n const ch = channels[name];\n if (\n !isDeltaChannel(ch) ||\n !ch.isAvailable() ||\n (channelVersions !== undefined && channelVersions[name] === undefined)\n ) {\n continue;\n }\n if (reachedSnapshotBound(ch, countersSinceDeltaSnapshot[name])) {\n result.add(name);\n }\n }\n return result;\n}\n\n/** Whether a delta channel's counters reached the bound where it snapshots. */\nexport function reachedSnapshotBound(\n channel: BaseChannel,\n counters: [number, number] | undefined\n): boolean {\n const [updates, supersteps] = counters ?? [0, 0];\n return (\n updates >= (channel as DeltaChannelLike).snapshotFrequency ||\n supersteps >= getDeltaMaxSuperstepsSinceSnapshot()\n );\n}\n\nexport function createCheckpoint<ValueType>(\n checkpoint: ReadonlyCheckpoint,\n channels: Record<string, BaseChannel<ValueType>> | undefined,\n step: number,\n options?: {\n id?: string;\n channelsToSnapshot?: Set<string>;\n /** The channel versions of the last checkpoint the saver stored. */\n storedVersions?: Record<string, number | string>;\n getNextVersion?: (current: number | string | undefined) => number | string;\n triggerToNodes?: Record<string, string[]>;\n }\n): Checkpoint {\n const channelsToSnapshot = options?.channelsToSnapshot ?? new Set<string>();\n const { storedVersions, getNextVersion } = options ?? {};\n const bumped: Record<string, [number | string | undefined, number | string]> =\n {};\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let values: Record<string, any>;\n let channelVersions: Record<string, number | string> =\n checkpoint.channel_versions;\n if (channels === undefined) {\n values = checkpoint.channel_values;\n } else {\n values = {};\n channelVersions = { ...checkpoint.channel_versions };\n for (const k in channels) {\n if (!Object.prototype.hasOwnProperty.call(channels, k)) continue;\n const channel = channels[k];\n if (channelsToSnapshot.has(k)) {\n // `put` only stores a blob for a channel whose version moved, so\n // snapshotting a channel that didn't move since the last stored\n // checkpoint needs a bump: exit mode reaching the cadence on a\n // superstep that skipped the channel, and a fork's first checkpoint.\n if (\n getNextVersion !== undefined &&\n storedVersions !== undefined &&\n channelVersions[k] === storedVersions[k]\n ) {\n const old = channelVersions[k];\n channelVersions[k] = getNextVersion(old);\n bumped[k] = [old, channelVersions[k]];\n }\n values[k] = new DeltaSnapshot(channel.get());\n continue;\n }\n if (isDeltaChannel(channel)) {\n // Omitted from channel_values; reconstructed from ancestor writes.\n continue;\n }\n try {\n values[k] = channel.checkpoint();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n if (error.name === EmptyChannelError.unminifiable_name) {\n // no-op\n } else {\n throw error; // Rethrow unexpected errors\n }\n }\n }\n }\n\n return {\n v: 4,\n id: options?.id ?? uuid6(step),\n ts: new Date().toISOString(),\n channel_values: values,\n channel_versions: channelVersions,\n versions_seen: markBumpsSeen(\n checkpoint.versions_seen,\n bumped,\n options?.triggerToNodes\n ),\n };\n}\n\n/**\n * Advance whoever had seen a bumped channel's old version to the new one.\n *\n * A bump that only stores a snapshot is not a write. Left unseen, it would\n * re-fire `interruptBefore` and rerun the channel's subscribers. A channel\n * bumped from no version was never written, so it also goes to the\n * subscribers that never ran: they have no entry, and would start on the bump.\n * For each entry it advances, {@link SNAPSHOT_BUMPS} keeps the new version and\n * the one the node really read, so {@link versionsSeenWithoutBumps} can put\n * the read back.\n */\nfunction markBumpsSeen(\n versionsSeen: ReadonlyCheckpoint[\"versions_seen\"],\n bumped: Record<string, [number | string | undefined, number | string]>,\n triggerToNodes: Record<string, string[]> = {}\n): Checkpoint[\"versions_seen\"] {\n if (Object.keys(bumped).length === 0) return versionsSeen;\n const out = { ...versionsSeen };\n for (const [k, [old]] of Object.entries(bumped)) {\n if (old !== undefined) continue;\n for (const node of triggerToNodes[k] ?? []) out[node] ??= {};\n }\n const marks = { ...versionsSeen[SNAPSHOT_BUMPS] };\n for (const node in out) {\n if (\n !Object.prototype.hasOwnProperty.call(out, node) ||\n node === SNAPSHOT_BUMPS\n ) {\n continue;\n }\n for (const [k, [old, next]] of Object.entries(bumped)) {\n if (out[node][k] !== old) continue;\n const [advanced, read] = bumpKeys(node, k);\n // If an earlier bump set `old`, the real read is already recorded.\n if (old !== undefined && marks[advanced] !== old) marks[read] = old;\n marks[advanced] = next;\n out[node] = { ...out[node], [k]: next };\n }\n }\n if (Object.keys(marks).length > 0) out[SNAPSHOT_BUMPS] = marks;\n return out;\n}\n\n// Node names can't contain either separator, so the keys can't collide.\nconst bumpKeys = (node: string, channel: string): [string, string] => [\n `${node}${CHECKPOINT_NAMESPACE_SEPARATOR}${channel}`,\n `${node}${CHECKPOINT_NAMESPACE_END}${channel}`,\n];\n\n/**\n * `versions_seen` as the nodes read it: an entry a snapshot bump advanced goes\n * back to the version the node really read, or away if it never read one.\n */\nexport function versionsSeenWithoutBumps(\n versionsSeen: ReadonlyCheckpoint[\"versions_seen\"]\n): ReadonlyCheckpoint[\"versions_seen\"] {\n const marks = versionsSeen[SNAPSHOT_BUMPS];\n if (marks === undefined || Object.keys(marks).length === 0) {\n return versionsSeen;\n }\n const out: Checkpoint[\"versions_seen\"] = {};\n for (const node in versionsSeen) {\n if (\n !Object.prototype.hasOwnProperty.call(versionsSeen, node) ||\n node === SNAPSHOT_BUMPS\n ) {\n continue;\n }\n out[node] = {};\n for (const [k, v] of Object.entries(versionsSeen[node])) {\n const [advanced, read] = bumpKeys(node, k);\n if (marks[advanced] !== v) out[node][k] = v;\n else if (read in marks) out[node][k] = marks[read];\n }\n }\n return out;\n}\n\n/**\n * Checkpoint metadata key, `true` when every DeltaChannel write on the\n * checkpoint's branch carries a version. A delta channel with no version there\n * was never written, so it needs no walk. Branches started before this key\n * existed never get it: an older `updateState` stored a delta channel's first\n * write without a version, and only the walk finds such a write.\n */\nexport const DELTA_WRITES_VERSIONED = \"delta_writes_versioned\";\n\nexport function isDeltaWritesVersioned(\n metadata: CheckpointMetadata | undefined\n): boolean {\n return (\n (metadata as Record<string, unknown> | undefined)?.[\n DELTA_WRITES_VERSIONED\n ] === true\n );\n}\n\n/**\n * Channels whose value at `checkpoint` the ancestor walk rebuilds.\n *\n * A {@link DeltaSnapshot} or a migrated plain value in `channel_values`\n * resolves directly, so only a channel with nothing stored there needs the\n * walk. With `versionedOnly`, a channel with no version was never written, so\n * it is empty without one; a walk for it would find no snapshot to stop at and\n * read every ancestor, every time the thread is loaded.\n *\n * A `BinaryOperatorAggregate` stores its value at every version, so one with a\n * version and nothing stored was written as a DeltaChannel before its\n * annotation changed, and is rebuilt the same way.\n */\nfunction deltaChannelsToReplay(\n specs: Record<string, BaseChannel>,\n checkpoint: ReadonlyCheckpoint,\n versionedOnly: boolean\n): string[] {\n const keys: string[] = [];\n for (const k in specs) {\n if (!Object.prototype.hasOwnProperty.call(specs, k)) continue;\n if (Object.prototype.hasOwnProperty.call(checkpoint.channel_values, k)) {\n continue;\n }\n const versioned = checkpoint.channel_versions[k] !== undefined;\n if (\n (isDeltaChannel(specs[k]) && (versioned || !versionedOnly)) ||\n (specs[k].lc_graph_name === \"BinaryOperatorAggregate\" && versioned)\n ) {\n keys.push(k);\n }\n }\n return keys;\n}\n\n/**\n * Hydrate channels from a checkpoint, reconstructing any {@link DeltaChannel}\n * whose value is absent from `channel_values` by replaying ancestor writes.\n *\n * For most channels (and for delta channels with a {@link DeltaSnapshot} or a\n * migrated plain value in `channel_values`), {@link emptyChannels} is\n * sufficient and no saver access is required. When a delta channel is absent\n * from `channel_values`, an ancestor walk via `saver.getDeltaChannelHistory`\n * finds the nearest seed and accumulates the writes between it and the\n * target. All delta channels needing replay are batched into a single saver\n * call. Pass `deltaWritesVersioned` for a checkpoint whose metadata carries\n * {@link DELTA_WRITES_VERSIONED}, so delta channels without a version are\n * left empty instead of walked.\n */\nexport async function channelsFromCheckpoint<\n Cc extends Record<string, BaseChannel>,\n>(\n specs: Cc,\n checkpoint: ReadonlyCheckpoint,\n options?: {\n saver?: BaseCheckpointSaver;\n config?: RunnableConfig;\n deltaWritesVersioned?: boolean;\n }\n): Promise<Cc> {\n const channels = emptyChannels(specs, checkpoint);\n const { saver, config } = options ?? {};\n\n const filteredSpecs = getOnlyChannels(specs);\n const deltaKeys = deltaChannelsToReplay(\n filteredSpecs,\n checkpoint,\n options?.deltaWritesVersioned ?? false\n );\n\n const written = deltaKeys.filter(\n (k) => checkpoint.channel_versions[k] !== undefined\n );\n if ((saver === undefined || config === undefined) && written.length > 0) {\n throw new Error(\n `DeltaChannel ${written.join(\", \")} has history to replay but no checkpointer or config was passed to read it`\n );\n }\n if (deltaKeys.length === 0 || saver === undefined || config === undefined) {\n return channels;\n }\n\n const histories = await saver.getDeltaChannelHistory({\n config,\n channels: deltaKeys,\n });\n for (const k of deltaKeys) {\n const history = histories[k];\n if (history === undefined) continue;\n const replayCh = filteredSpecs[k].fromCheckpoint(history.seed);\n if (isDeltaChannel(replayCh)) {\n (replayCh as DeltaChannelLike).replayWrites(history.writes);\n } else {\n // One update per write: the writes span many supersteps, and a single\n // call would reject a second Overwrite and drop the writes after one.\n for (const [, , value] of history.writes) replayCh.update([value]);\n }\n (channels as Record<string, BaseChannel>)[k] = replayCh;\n }\n return channels;\n}\n"],"mappings":"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"}
|
package/dist/channels/binop.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const require_constants = require("../constants.cjs");
|
|
2
2
|
const require_errors = require("../errors.cjs");
|
|
3
3
|
const require_base = require("./base.cjs");
|
|
4
|
+
let _langchain_langgraph_checkpoint = require("@langchain/langgraph-checkpoint");
|
|
4
5
|
//#region src/channels/binop.ts
|
|
5
6
|
const isBinaryOperatorAggregate = (value) => {
|
|
6
7
|
return value != null && value.lc_graph_name === "BinaryOperatorAggregate";
|
|
@@ -21,7 +22,7 @@ var BinaryOperatorAggregate = class BinaryOperatorAggregate extends require_base
|
|
|
21
22
|
}
|
|
22
23
|
fromCheckpoint(checkpoint) {
|
|
23
24
|
const empty = new BinaryOperatorAggregate(this.operator, this.initialValueFactory);
|
|
24
|
-
if (typeof checkpoint !== "undefined") empty.value = checkpoint;
|
|
25
|
+
if (typeof checkpoint !== "undefined") empty.value = (0, _langchain_langgraph_checkpoint.isDeltaSnapshot)(checkpoint) ? checkpoint.value : checkpoint;
|
|
25
26
|
return empty;
|
|
26
27
|
}
|
|
27
28
|
update(values) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"binop.cjs","names":["BaseChannel","_getOverwriteValue","_isOverwriteValue","InvalidUpdateError","EmptyChannelError"],"sources":["../../src/channels/binop.ts"],"sourcesContent":["import {\n _getOverwriteValue,\n _isOverwriteValue,\n type OverwriteValue,\n} from \"../constants.js\";\nimport { EmptyChannelError, InvalidUpdateError } from \"../errors.js\";\nimport { BaseChannel } from \"./base.js\";\n\ntype OverwriteOrValue<ValueType, UpdateType> =\n | OverwriteValue<ValueType>\n | UpdateType;\n\nexport type BinaryOperator<ValueType, UpdateType> = (\n a: ValueType,\n b: UpdateType\n) => ValueType;\n\nconst isBinaryOperatorAggregate = (\n value: BaseChannel\n): value is BinaryOperatorAggregate<unknown, unknown> => {\n return value != null && value.lc_graph_name === \"BinaryOperatorAggregate\";\n};\n\n/**\n * Stores the result of applying a binary operator to the current value and each new value.\n */\nexport class BinaryOperatorAggregate<\n ValueType,\n UpdateType = ValueType,\n> extends BaseChannel<\n ValueType,\n OverwriteOrValue<ValueType, UpdateType>,\n ValueType\n> {\n lc_graph_name = \"BinaryOperatorAggregate\";\n\n value: ValueType | undefined;\n\n operator: BinaryOperator<ValueType, UpdateType>;\n\n initialValueFactory?: () => ValueType;\n\n constructor(\n operator: BinaryOperator<ValueType, UpdateType>,\n initialValueFactory?: () => ValueType\n ) {\n super();\n\n this.operator = operator;\n this.initialValueFactory = initialValueFactory;\n this.value = initialValueFactory?.();\n }\n\n public fromCheckpoint(checkpoint?: ValueType) {\n const empty = new BinaryOperatorAggregate(\n this.operator,\n this.initialValueFactory\n );\n if (typeof checkpoint !== \"undefined\") {\n empty.value = checkpoint;\n }\n return empty as this;\n }\n\n public update(values: OverwriteOrValue<ValueType, UpdateType>[]): boolean {\n let newValues = values;\n if (!newValues.length) return false;\n\n if (this.value === undefined) {\n const first = newValues[0];\n const [isOverwrite, overwriteVal] = _getOverwriteValue<ValueType>(first);\n if (isOverwrite) {\n this.value = overwriteVal;\n } else {\n this.value = first as ValueType;\n }\n newValues = newValues.slice(1);\n }\n\n let seenOverwrite = false;\n for (const incoming of newValues) {\n if (_isOverwriteValue<ValueType>(incoming)) {\n if (seenOverwrite) {\n throw new InvalidUpdateError(\n \"Can receive only one Overwrite value per step.\"\n );\n }\n const [, val] = _getOverwriteValue<ValueType>(incoming);\n this.value = val;\n seenOverwrite = true;\n continue;\n } else if (!seenOverwrite && this.value !== undefined) {\n this.value = this.operator(this.value, incoming);\n }\n }\n return true;\n }\n\n public get(): ValueType {\n if (this.value === undefined) {\n throw new EmptyChannelError();\n }\n return this.value;\n }\n\n public checkpoint(): ValueType {\n if (this.value === undefined) {\n throw new EmptyChannelError();\n }\n return this.value;\n }\n\n isAvailable(): boolean {\n return this.value !== undefined;\n }\n\n /**\n * Compare this channel with another channel for equality.\n * Two BinaryOperatorAggregate channels are equal if they have the same operator function.\n * This follows the Python implementation which compares operator references.\n */\n equals(other: BaseChannel): boolean {\n if (this === other) return true;\n if (!isBinaryOperatorAggregate(other)) return false;\n return this.operator === other.operator;\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"binop.cjs","names":["BaseChannel","_getOverwriteValue","_isOverwriteValue","InvalidUpdateError","EmptyChannelError"],"sources":["../../src/channels/binop.ts"],"sourcesContent":["import { isDeltaSnapshot } from \"@langchain/langgraph-checkpoint\";\nimport {\n _getOverwriteValue,\n _isOverwriteValue,\n type OverwriteValue,\n} from \"../constants.js\";\nimport { EmptyChannelError, InvalidUpdateError } from \"../errors.js\";\nimport { BaseChannel } from \"./base.js\";\n\ntype OverwriteOrValue<ValueType, UpdateType> =\n | OverwriteValue<ValueType>\n | UpdateType;\n\nexport type BinaryOperator<ValueType, UpdateType> = (\n a: ValueType,\n b: UpdateType\n) => ValueType;\n\nconst isBinaryOperatorAggregate = (\n value: BaseChannel\n): value is BinaryOperatorAggregate<unknown, unknown> => {\n return value != null && value.lc_graph_name === \"BinaryOperatorAggregate\";\n};\n\n/**\n * Stores the result of applying a binary operator to the current value and each new value.\n */\nexport class BinaryOperatorAggregate<\n ValueType,\n UpdateType = ValueType,\n> extends BaseChannel<\n ValueType,\n OverwriteOrValue<ValueType, UpdateType>,\n ValueType\n> {\n lc_graph_name = \"BinaryOperatorAggregate\";\n\n value: ValueType | undefined;\n\n operator: BinaryOperator<ValueType, UpdateType>;\n\n initialValueFactory?: () => ValueType;\n\n constructor(\n operator: BinaryOperator<ValueType, UpdateType>,\n initialValueFactory?: () => ValueType\n ) {\n super();\n\n this.operator = operator;\n this.initialValueFactory = initialValueFactory;\n this.value = initialValueFactory?.();\n }\n\n public fromCheckpoint(checkpoint?: ValueType) {\n const empty = new BinaryOperatorAggregate(\n this.operator,\n this.initialValueFactory\n );\n if (typeof checkpoint !== \"undefined\") {\n // A thread written while this field was a DeltaChannel stores snapshots.\n empty.value = isDeltaSnapshot(checkpoint)\n ? (checkpoint.value as ValueType)\n : checkpoint;\n }\n return empty as this;\n }\n\n public update(values: OverwriteOrValue<ValueType, UpdateType>[]): boolean {\n let newValues = values;\n if (!newValues.length) return false;\n\n if (this.value === undefined) {\n const first = newValues[0];\n const [isOverwrite, overwriteVal] = _getOverwriteValue<ValueType>(first);\n if (isOverwrite) {\n this.value = overwriteVal;\n } else {\n this.value = first as ValueType;\n }\n newValues = newValues.slice(1);\n }\n\n let seenOverwrite = false;\n for (const incoming of newValues) {\n if (_isOverwriteValue<ValueType>(incoming)) {\n if (seenOverwrite) {\n throw new InvalidUpdateError(\n \"Can receive only one Overwrite value per step.\"\n );\n }\n const [, val] = _getOverwriteValue<ValueType>(incoming);\n this.value = val;\n seenOverwrite = true;\n continue;\n } else if (!seenOverwrite && this.value !== undefined) {\n this.value = this.operator(this.value, incoming);\n }\n }\n return true;\n }\n\n public get(): ValueType {\n if (this.value === undefined) {\n throw new EmptyChannelError();\n }\n return this.value;\n }\n\n public checkpoint(): ValueType {\n if (this.value === undefined) {\n throw new EmptyChannelError();\n }\n return this.value;\n }\n\n isAvailable(): boolean {\n return this.value !== undefined;\n }\n\n /**\n * Compare this channel with another channel for equality.\n * Two BinaryOperatorAggregate channels are equal if they have the same operator function.\n * This follows the Python implementation which compares operator references.\n */\n equals(other: BaseChannel): boolean {\n if (this === other) return true;\n if (!isBinaryOperatorAggregate(other)) return false;\n return this.operator === other.operator;\n }\n}\n"],"mappings":";;;;;AAkBA,MAAM,6BACJ,UACuD;AACvD,QAAO,SAAS,QAAQ,MAAM,kBAAkB;;;;;AAMlD,IAAa,0BAAb,MAAa,gCAGHA,aAAAA,YAIR;CACA,gBAAgB;CAEhB;CAEA;CAEA;CAEA,YACE,UACA,qBACA;AACA,SAAO;AAEP,OAAK,WAAW;AAChB,OAAK,sBAAsB;AAC3B,OAAK,QAAQ,uBAAuB;;CAGtC,eAAsB,YAAwB;EAC5C,MAAM,QAAQ,IAAI,wBAChB,KAAK,UACL,KAAK,oBACN;AACD,MAAI,OAAO,eAAe,YAExB,OAAM,SAAA,GAAA,gCAAA,iBAAwB,WAAW,GACpC,WAAW,QACZ;AAEN,SAAO;;CAGT,OAAc,QAA4D;EACxE,IAAI,YAAY;AAChB,MAAI,CAAC,UAAU,OAAQ,QAAO;AAE9B,MAAI,KAAK,UAAU,KAAA,GAAW;GAC5B,MAAM,QAAQ,UAAU;GACxB,MAAM,CAAC,aAAa,gBAAgBC,kBAAAA,mBAA8B,MAAM;AACxE,OAAI,YACF,MAAK,QAAQ;OAEb,MAAK,QAAQ;AAEf,eAAY,UAAU,MAAM,EAAE;;EAGhC,IAAI,gBAAgB;AACpB,OAAK,MAAM,YAAY,UACrB,KAAIC,kBAAAA,kBAA6B,SAAS,EAAE;AAC1C,OAAI,cACF,OAAM,IAAIC,eAAAA,mBACR,iDACD;GAEH,MAAM,GAAG,OAAOF,kBAAAA,mBAA8B,SAAS;AACvD,QAAK,QAAQ;AACb,mBAAgB;AAChB;aACS,CAAC,iBAAiB,KAAK,UAAU,KAAA,EAC1C,MAAK,QAAQ,KAAK,SAAS,KAAK,OAAO,SAAS;AAGpD,SAAO;;CAGT,MAAwB;AACtB,MAAI,KAAK,UAAU,KAAA,EACjB,OAAM,IAAIG,eAAAA,mBAAmB;AAE/B,SAAO,KAAK;;CAGd,aAA+B;AAC7B,MAAI,KAAK,UAAU,KAAA,EACjB,OAAM,IAAIA,eAAAA,mBAAmB;AAE/B,SAAO,KAAK;;CAGd,cAAuB;AACrB,SAAO,KAAK,UAAU,KAAA;;;;;;;CAQxB,OAAO,OAA6B;AAClC,MAAI,SAAS,MAAO,QAAO;AAC3B,MAAI,CAAC,0BAA0B,MAAM,CAAE,QAAO;AAC9C,SAAO,KAAK,aAAa,MAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"binop.d.cts","names":[],"sources":["../../src/channels/binop.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"binop.d.cts","names":[],"sources":["../../src/channels/binop.ts"],"mappings":";;;;KASK,gBAAA,0BACD,cAAA,CAAe,SAAA,IACf,UAAA;AAAA,KAEQ,cAAA,2BACV,CAAA,EAAG,SAAA,EACH,CAAA,EAAG,UAAA,KACA,SAAA;AATmC;;;AAAA,cAoB3B,uBAAA,yBAEE,SAAA,UACL,WAAA,CACR,SAAA,EACA,gBAAA,CAAiB,SAAA,EAAW,UAAA,GAC5B,SAAA;EAEA,aAAA;EAEA,KAAA,EAAO,SAAA;EAEP,QAAA,EAAU,cAAA,CAAe,SAAA,EAAW,UAAA;EAEpC,mBAAA,SAA4B,SAAA;EAE5B,WAAA,CACE,QAAA,EAAU,cAAA,CAAe,SAAA,EAAW,UAAA,GACpC,mBAAA,SAA4B,SAAA;EASvB,cAAA,CAAe,UAAA,GAAa,SAAA;EAc5B,MAAA,CAAO,MAAA,EAAQ,gBAAA,CAAiB,SAAA,EAAW,UAAA;EAkC3C,GAAA,CAAA,GAAO,SAAA;EAOP,UAAA,CAAA,GAAc,SAAA;EAOrB,WAAA,CAAA;EAzGY;AAEd;;;;EAgHE,MAAA,CAAO,KAAA,EAAO,WAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"binop.d.ts","names":[],"sources":["../../src/channels/binop.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"binop.d.ts","names":[],"sources":["../../src/channels/binop.ts"],"mappings":";;;;KASK,gBAAA,0BACD,cAAA,CAAe,SAAA,IACf,UAAA;AAAA,KAEQ,cAAA,2BACV,CAAA,EAAG,SAAA,EACH,CAAA,EAAG,UAAA,KACA,SAAA;AATmC;;;AAAA,cAoB3B,uBAAA,yBAEE,SAAA,UACL,WAAA,CACR,SAAA,EACA,gBAAA,CAAiB,SAAA,EAAW,UAAA,GAC5B,SAAA;EAEA,aAAA;EAEA,KAAA,EAAO,SAAA;EAEP,QAAA,EAAU,cAAA,CAAe,SAAA,EAAW,UAAA;EAEpC,mBAAA,SAA4B,SAAA;EAE5B,WAAA,CACE,QAAA,EAAU,cAAA,CAAe,SAAA,EAAW,UAAA,GACpC,mBAAA,SAA4B,SAAA;EASvB,cAAA,CAAe,UAAA,GAAa,SAAA;EAc5B,MAAA,CAAO,MAAA,EAAQ,gBAAA,CAAiB,SAAA,EAAW,UAAA;EAkC3C,GAAA,CAAA,GAAO,SAAA;EAOP,UAAA,CAAA,GAAc,SAAA;EAOrB,WAAA,CAAA;EAzGY;AAEd;;;;EAgHE,MAAA,CAAO,KAAA,EAAO,WAAA;AAAA"}
|
package/dist/channels/binop.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { _getOverwriteValue, _isOverwriteValue } from "../constants.js";
|
|
2
2
|
import { EmptyChannelError, InvalidUpdateError } from "../errors.js";
|
|
3
3
|
import { BaseChannel } from "./base.js";
|
|
4
|
+
import { isDeltaSnapshot } from "@langchain/langgraph-checkpoint";
|
|
4
5
|
//#region src/channels/binop.ts
|
|
5
6
|
const isBinaryOperatorAggregate = (value) => {
|
|
6
7
|
return value != null && value.lc_graph_name === "BinaryOperatorAggregate";
|
|
@@ -21,7 +22,7 @@ var BinaryOperatorAggregate = class BinaryOperatorAggregate extends BaseChannel
|
|
|
21
22
|
}
|
|
22
23
|
fromCheckpoint(checkpoint) {
|
|
23
24
|
const empty = new BinaryOperatorAggregate(this.operator, this.initialValueFactory);
|
|
24
|
-
if (typeof checkpoint !== "undefined") empty.value = checkpoint;
|
|
25
|
+
if (typeof checkpoint !== "undefined") empty.value = isDeltaSnapshot(checkpoint) ? checkpoint.value : checkpoint;
|
|
25
26
|
return empty;
|
|
26
27
|
}
|
|
27
28
|
update(values) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"binop.js","names":[],"sources":["../../src/channels/binop.ts"],"sourcesContent":["import {\n _getOverwriteValue,\n _isOverwriteValue,\n type OverwriteValue,\n} from \"../constants.js\";\nimport { EmptyChannelError, InvalidUpdateError } from \"../errors.js\";\nimport { BaseChannel } from \"./base.js\";\n\ntype OverwriteOrValue<ValueType, UpdateType> =\n | OverwriteValue<ValueType>\n | UpdateType;\n\nexport type BinaryOperator<ValueType, UpdateType> = (\n a: ValueType,\n b: UpdateType\n) => ValueType;\n\nconst isBinaryOperatorAggregate = (\n value: BaseChannel\n): value is BinaryOperatorAggregate<unknown, unknown> => {\n return value != null && value.lc_graph_name === \"BinaryOperatorAggregate\";\n};\n\n/**\n * Stores the result of applying a binary operator to the current value and each new value.\n */\nexport class BinaryOperatorAggregate<\n ValueType,\n UpdateType = ValueType,\n> extends BaseChannel<\n ValueType,\n OverwriteOrValue<ValueType, UpdateType>,\n ValueType\n> {\n lc_graph_name = \"BinaryOperatorAggregate\";\n\n value: ValueType | undefined;\n\n operator: BinaryOperator<ValueType, UpdateType>;\n\n initialValueFactory?: () => ValueType;\n\n constructor(\n operator: BinaryOperator<ValueType, UpdateType>,\n initialValueFactory?: () => ValueType\n ) {\n super();\n\n this.operator = operator;\n this.initialValueFactory = initialValueFactory;\n this.value = initialValueFactory?.();\n }\n\n public fromCheckpoint(checkpoint?: ValueType) {\n const empty = new BinaryOperatorAggregate(\n this.operator,\n this.initialValueFactory\n );\n if (typeof checkpoint !== \"undefined\") {\n empty.value = checkpoint;\n }\n return empty as this;\n }\n\n public update(values: OverwriteOrValue<ValueType, UpdateType>[]): boolean {\n let newValues = values;\n if (!newValues.length) return false;\n\n if (this.value === undefined) {\n const first = newValues[0];\n const [isOverwrite, overwriteVal] = _getOverwriteValue<ValueType>(first);\n if (isOverwrite) {\n this.value = overwriteVal;\n } else {\n this.value = first as ValueType;\n }\n newValues = newValues.slice(1);\n }\n\n let seenOverwrite = false;\n for (const incoming of newValues) {\n if (_isOverwriteValue<ValueType>(incoming)) {\n if (seenOverwrite) {\n throw new InvalidUpdateError(\n \"Can receive only one Overwrite value per step.\"\n );\n }\n const [, val] = _getOverwriteValue<ValueType>(incoming);\n this.value = val;\n seenOverwrite = true;\n continue;\n } else if (!seenOverwrite && this.value !== undefined) {\n this.value = this.operator(this.value, incoming);\n }\n }\n return true;\n }\n\n public get(): ValueType {\n if (this.value === undefined) {\n throw new EmptyChannelError();\n }\n return this.value;\n }\n\n public checkpoint(): ValueType {\n if (this.value === undefined) {\n throw new EmptyChannelError();\n }\n return this.value;\n }\n\n isAvailable(): boolean {\n return this.value !== undefined;\n }\n\n /**\n * Compare this channel with another channel for equality.\n * Two BinaryOperatorAggregate channels are equal if they have the same operator function.\n * This follows the Python implementation which compares operator references.\n */\n equals(other: BaseChannel): boolean {\n if (this === other) return true;\n if (!isBinaryOperatorAggregate(other)) return false;\n return this.operator === other.operator;\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"binop.js","names":[],"sources":["../../src/channels/binop.ts"],"sourcesContent":["import { isDeltaSnapshot } from \"@langchain/langgraph-checkpoint\";\nimport {\n _getOverwriteValue,\n _isOverwriteValue,\n type OverwriteValue,\n} from \"../constants.js\";\nimport { EmptyChannelError, InvalidUpdateError } from \"../errors.js\";\nimport { BaseChannel } from \"./base.js\";\n\ntype OverwriteOrValue<ValueType, UpdateType> =\n | OverwriteValue<ValueType>\n | UpdateType;\n\nexport type BinaryOperator<ValueType, UpdateType> = (\n a: ValueType,\n b: UpdateType\n) => ValueType;\n\nconst isBinaryOperatorAggregate = (\n value: BaseChannel\n): value is BinaryOperatorAggregate<unknown, unknown> => {\n return value != null && value.lc_graph_name === \"BinaryOperatorAggregate\";\n};\n\n/**\n * Stores the result of applying a binary operator to the current value and each new value.\n */\nexport class BinaryOperatorAggregate<\n ValueType,\n UpdateType = ValueType,\n> extends BaseChannel<\n ValueType,\n OverwriteOrValue<ValueType, UpdateType>,\n ValueType\n> {\n lc_graph_name = \"BinaryOperatorAggregate\";\n\n value: ValueType | undefined;\n\n operator: BinaryOperator<ValueType, UpdateType>;\n\n initialValueFactory?: () => ValueType;\n\n constructor(\n operator: BinaryOperator<ValueType, UpdateType>,\n initialValueFactory?: () => ValueType\n ) {\n super();\n\n this.operator = operator;\n this.initialValueFactory = initialValueFactory;\n this.value = initialValueFactory?.();\n }\n\n public fromCheckpoint(checkpoint?: ValueType) {\n const empty = new BinaryOperatorAggregate(\n this.operator,\n this.initialValueFactory\n );\n if (typeof checkpoint !== \"undefined\") {\n // A thread written while this field was a DeltaChannel stores snapshots.\n empty.value = isDeltaSnapshot(checkpoint)\n ? (checkpoint.value as ValueType)\n : checkpoint;\n }\n return empty as this;\n }\n\n public update(values: OverwriteOrValue<ValueType, UpdateType>[]): boolean {\n let newValues = values;\n if (!newValues.length) return false;\n\n if (this.value === undefined) {\n const first = newValues[0];\n const [isOverwrite, overwriteVal] = _getOverwriteValue<ValueType>(first);\n if (isOverwrite) {\n this.value = overwriteVal;\n } else {\n this.value = first as ValueType;\n }\n newValues = newValues.slice(1);\n }\n\n let seenOverwrite = false;\n for (const incoming of newValues) {\n if (_isOverwriteValue<ValueType>(incoming)) {\n if (seenOverwrite) {\n throw new InvalidUpdateError(\n \"Can receive only one Overwrite value per step.\"\n );\n }\n const [, val] = _getOverwriteValue<ValueType>(incoming);\n this.value = val;\n seenOverwrite = true;\n continue;\n } else if (!seenOverwrite && this.value !== undefined) {\n this.value = this.operator(this.value, incoming);\n }\n }\n return true;\n }\n\n public get(): ValueType {\n if (this.value === undefined) {\n throw new EmptyChannelError();\n }\n return this.value;\n }\n\n public checkpoint(): ValueType {\n if (this.value === undefined) {\n throw new EmptyChannelError();\n }\n return this.value;\n }\n\n isAvailable(): boolean {\n return this.value !== undefined;\n }\n\n /**\n * Compare this channel with another channel for equality.\n * Two BinaryOperatorAggregate channels are equal if they have the same operator function.\n * This follows the Python implementation which compares operator references.\n */\n equals(other: BaseChannel): boolean {\n if (this === other) return true;\n if (!isBinaryOperatorAggregate(other)) return false;\n return this.operator === other.operator;\n }\n}\n"],"mappings":";;;;;AAkBA,MAAM,6BACJ,UACuD;AACvD,QAAO,SAAS,QAAQ,MAAM,kBAAkB;;;;;AAMlD,IAAa,0BAAb,MAAa,gCAGH,YAIR;CACA,gBAAgB;CAEhB;CAEA;CAEA;CAEA,YACE,UACA,qBACA;AACA,SAAO;AAEP,OAAK,WAAW;AAChB,OAAK,sBAAsB;AAC3B,OAAK,QAAQ,uBAAuB;;CAGtC,eAAsB,YAAwB;EAC5C,MAAM,QAAQ,IAAI,wBAChB,KAAK,UACL,KAAK,oBACN;AACD,MAAI,OAAO,eAAe,YAExB,OAAM,QAAQ,gBAAgB,WAAW,GACpC,WAAW,QACZ;AAEN,SAAO;;CAGT,OAAc,QAA4D;EACxE,IAAI,YAAY;AAChB,MAAI,CAAC,UAAU,OAAQ,QAAO;AAE9B,MAAI,KAAK,UAAU,KAAA,GAAW;GAC5B,MAAM,QAAQ,UAAU;GACxB,MAAM,CAAC,aAAa,gBAAgB,mBAA8B,MAAM;AACxE,OAAI,YACF,MAAK,QAAQ;OAEb,MAAK,QAAQ;AAEf,eAAY,UAAU,MAAM,EAAE;;EAGhC,IAAI,gBAAgB;AACpB,OAAK,MAAM,YAAY,UACrB,KAAI,kBAA6B,SAAS,EAAE;AAC1C,OAAI,cACF,OAAM,IAAI,mBACR,iDACD;GAEH,MAAM,GAAG,OAAO,mBAA8B,SAAS;AACvD,QAAK,QAAQ;AACb,mBAAgB;AAChB;aACS,CAAC,iBAAiB,KAAK,UAAU,KAAA,EAC1C,MAAK,QAAQ,KAAK,SAAS,KAAK,OAAO,SAAS;AAGpD,SAAO;;CAGT,MAAwB;AACtB,MAAI,KAAK,UAAU,KAAA,EACjB,OAAM,IAAI,mBAAmB;AAE/B,SAAO,KAAK;;CAGd,aAA+B;AAC7B,MAAI,KAAK,UAAU,KAAA,EACjB,OAAM,IAAI,mBAAmB;AAE/B,SAAO,KAAK;;CAGd,cAAuB;AACrB,SAAO,KAAK,UAAU,KAAA;;;;;;;CAQxB,OAAO,OAA6B;AAClC,MAAI,SAAS,MAAO,QAAO;AAC3B,MAAI,CAAC,0BAA0B,MAAM,CAAE,QAAO;AAC9C,SAAO,KAAK,aAAa,MAAM"}
|
package/dist/channels/index.cjs
CHANGED
|
@@ -24,6 +24,8 @@ exports.UntrackedValueChannel = require_untracked_value.UntrackedValueChannel;
|
|
|
24
24
|
exports.channelsFromCheckpoint = require_base.channelsFromCheckpoint;
|
|
25
25
|
exports.createCheckpoint = require_base.createCheckpoint;
|
|
26
26
|
exports.deltaChannelsToSnapshot = require_base.deltaChannelsToSnapshot;
|
|
27
|
+
exports.deltaChannelsWithPendingWrites = require_base.deltaChannelsWithPendingWrites;
|
|
27
28
|
exports.empty = require_base.emptyChannels;
|
|
29
|
+
exports.exitDeltaLateTaskId = require_base.exitDeltaLateTaskId;
|
|
28
30
|
exports.exitDeltaTaskId = require_base.exitDeltaTaskId;
|
|
29
31
|
exports.isDeltaChannel = require_base.isDeltaChannel;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseChannel, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, emptyChannels, exitDeltaTaskId, isDeltaChannel } from "./base.cjs";
|
|
1
|
+
import { BaseChannel, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, deltaChannelsWithPendingWrites, emptyChannels, exitDeltaLateTaskId, exitDeltaTaskId, isDeltaChannel } from "./base.cjs";
|
|
2
2
|
import { DeltaChannel, DeltaReducer } from "./delta.cjs";
|
|
3
3
|
import { BinaryOperator, BinaryOperatorAggregate } from "./binop.cjs";
|
|
4
4
|
import { AnyValue } from "./any_value.cjs";
|
|
@@ -8,4 +8,4 @@ import { EphemeralValue } from "./ephemeral_value.cjs";
|
|
|
8
8
|
import { NamedBarrierValue, NamedBarrierValueAfterFinish } from "./named_barrier_value.cjs";
|
|
9
9
|
import { Topic } from "./topic.cjs";
|
|
10
10
|
import { UntrackedValueChannel } from "./untracked_value.cjs";
|
|
11
|
-
export { AnyValue, BaseChannel, type BinaryOperator, BinaryOperatorAggregate, DeltaChannel, type DeltaReducer, DynamicBarrierValue, EphemeralValue, LastValue, LastValueAfterFinish, NamedBarrierValue, NamedBarrierValueAfterFinish, Topic, UntrackedValueChannel, type WaitForNames, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, emptyChannels as empty, exitDeltaTaskId, isDeltaChannel };
|
|
11
|
+
export { AnyValue, BaseChannel, type BinaryOperator, BinaryOperatorAggregate, DeltaChannel, type DeltaReducer, DynamicBarrierValue, EphemeralValue, LastValue, LastValueAfterFinish, NamedBarrierValue, NamedBarrierValueAfterFinish, Topic, UntrackedValueChannel, type WaitForNames, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, deltaChannelsWithPendingWrites, emptyChannels as empty, exitDeltaLateTaskId, exitDeltaTaskId, isDeltaChannel };
|
package/dist/channels/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseChannel, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, emptyChannels, exitDeltaTaskId, isDeltaChannel } from "./base.js";
|
|
1
|
+
import { BaseChannel, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, deltaChannelsWithPendingWrites, emptyChannels, exitDeltaLateTaskId, exitDeltaTaskId, isDeltaChannel } from "./base.js";
|
|
2
2
|
import { DeltaChannel, DeltaReducer } from "./delta.js";
|
|
3
3
|
import { BinaryOperator, BinaryOperatorAggregate } from "./binop.js";
|
|
4
4
|
import { AnyValue } from "./any_value.js";
|
|
@@ -8,4 +8,4 @@ import { EphemeralValue } from "./ephemeral_value.js";
|
|
|
8
8
|
import { NamedBarrierValue, NamedBarrierValueAfterFinish } from "./named_barrier_value.js";
|
|
9
9
|
import { Topic } from "./topic.js";
|
|
10
10
|
import { UntrackedValueChannel } from "./untracked_value.js";
|
|
11
|
-
export { AnyValue, BaseChannel, type BinaryOperator, BinaryOperatorAggregate, DeltaChannel, type DeltaReducer, DynamicBarrierValue, EphemeralValue, LastValue, LastValueAfterFinish, NamedBarrierValue, NamedBarrierValueAfterFinish, Topic, UntrackedValueChannel, type WaitForNames, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, emptyChannels as empty, exitDeltaTaskId, isDeltaChannel };
|
|
11
|
+
export { AnyValue, BaseChannel, type BinaryOperator, BinaryOperatorAggregate, DeltaChannel, type DeltaReducer, DynamicBarrierValue, EphemeralValue, LastValue, LastValueAfterFinish, NamedBarrierValue, NamedBarrierValueAfterFinish, Topic, UntrackedValueChannel, type WaitForNames, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, deltaChannelsWithPendingWrites, emptyChannels as empty, exitDeltaLateTaskId, exitDeltaTaskId, isDeltaChannel };
|
package/dist/channels/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseChannel, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, emptyChannels, exitDeltaTaskId, isDeltaChannel } from "./base.js";
|
|
1
|
+
import { BaseChannel, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, deltaChannelsWithPendingWrites, emptyChannels, exitDeltaLateTaskId, exitDeltaTaskId, isDeltaChannel } from "./base.js";
|
|
2
2
|
import { BinaryOperatorAggregate } from "./binop.js";
|
|
3
3
|
import { LastValue, LastValueAfterFinish } from "./last_value.js";
|
|
4
4
|
import { Topic } from "./topic.js";
|
|
@@ -8,4 +8,4 @@ import { DeltaChannel } from "./delta.js";
|
|
|
8
8
|
import { AnyValue } from "./any_value.js";
|
|
9
9
|
import { DynamicBarrierValue } from "./dynamic_barrier_value.js";
|
|
10
10
|
import { UntrackedValueChannel } from "./untracked_value.js";
|
|
11
|
-
export { AnyValue, BaseChannel, BinaryOperatorAggregate, DeltaChannel, DynamicBarrierValue, EphemeralValue, LastValue, LastValueAfterFinish, NamedBarrierValue, NamedBarrierValueAfterFinish, Topic, UntrackedValueChannel, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, emptyChannels as empty, exitDeltaTaskId, isDeltaChannel };
|
|
11
|
+
export { AnyValue, BaseChannel, BinaryOperatorAggregate, DeltaChannel, DynamicBarrierValue, EphemeralValue, LastValue, LastValueAfterFinish, NamedBarrierValue, NamedBarrierValueAfterFinish, Topic, UntrackedValueChannel, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, deltaChannelsWithPendingWrites, emptyChannels as empty, exitDeltaLateTaskId, exitDeltaTaskId, isDeltaChannel };
|
package/dist/constants.cjs
CHANGED
|
@@ -69,6 +69,11 @@ const INTERRUPT = "__interrupt__";
|
|
|
69
69
|
const RESUME = "__resume__";
|
|
70
70
|
/** Special channel reserved for cases when a task exits without any writes */
|
|
71
71
|
const NO_WRITES = "__no_writes__";
|
|
72
|
+
/**
|
|
73
|
+
* `versions_seen` key recording which entries a snapshot-only version bump
|
|
74
|
+
* advanced, and the versions those nodes really read.
|
|
75
|
+
*/
|
|
76
|
+
const SNAPSHOT_BUMPS = "__snapshot_bumps__";
|
|
72
77
|
/** Special channel reserved for graph return */
|
|
73
78
|
const RETURN = "__return__";
|
|
74
79
|
/** Special channel reserved for graph previous state */
|
|
@@ -80,6 +85,12 @@ const TASKS = "__pregel_tasks";
|
|
|
80
85
|
const PUSH = "__pregel_push";
|
|
81
86
|
const PULL = "__pregel_pull";
|
|
82
87
|
const NULL_TASK_ID = "00000000-0000-0000-0000-000000000000";
|
|
88
|
+
/**
|
|
89
|
+
* Stands in for `NULL_TASK_ID` as an exit-mode delta write's synthetic id (see
|
|
90
|
+
* `exitDeltaTaskId`). No task's synthetic id can equal it: those keep the task
|
|
91
|
+
* id's third group, and task ids are uuid5, so that group starts with 5.
|
|
92
|
+
*/
|
|
93
|
+
const EXIT_DELTA_NULL_TASK_ID = "00000000-0000-0000-0000-000000000001";
|
|
83
94
|
const RESERVED = [
|
|
84
95
|
TAG_HIDDEN,
|
|
85
96
|
INPUT,
|
|
@@ -88,6 +99,7 @@ const RESERVED = [
|
|
|
88
99
|
ERROR,
|
|
89
100
|
ERROR_SOURCE_NODE,
|
|
90
101
|
NO_WRITES,
|
|
102
|
+
SNAPSHOT_BUMPS,
|
|
91
103
|
CONFIG_KEY_SEND,
|
|
92
104
|
CONFIG_KEY_READ,
|
|
93
105
|
CONFIG_KEY_CHECKPOINTER,
|
|
@@ -527,6 +539,7 @@ exports.CommandInstance = CommandInstance;
|
|
|
527
539
|
exports.END = END;
|
|
528
540
|
exports.ERROR = ERROR;
|
|
529
541
|
exports.ERROR_SOURCE_NODE = ERROR_SOURCE_NODE;
|
|
542
|
+
exports.EXIT_DELTA_NULL_TASK_ID = EXIT_DELTA_NULL_TASK_ID;
|
|
530
543
|
exports.INPUT = INPUT;
|
|
531
544
|
exports.INTERRUPT = INTERRUPT;
|
|
532
545
|
exports.NO_WRITES = NO_WRITES;
|
|
@@ -540,6 +553,7 @@ exports.RESERVED = RESERVED;
|
|
|
540
553
|
exports.RESUME = RESUME;
|
|
541
554
|
exports.RETURN = RETURN;
|
|
542
555
|
exports.SELF = SELF;
|
|
556
|
+
exports.SNAPSHOT_BUMPS = SNAPSHOT_BUMPS;
|
|
543
557
|
exports.START = START;
|
|
544
558
|
exports.Send = Send;
|
|
545
559
|
exports.TAG_HIDDEN = TAG_HIDDEN;
|