@langchain/langgraph-checkpoint 1.1.2 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/base.cjs +10 -3
- package/dist/base.cjs.map +1 -1
- package/dist/base.d.cts +5 -0
- package/dist/base.d.cts.map +1 -1
- package/dist/base.d.ts +5 -0
- package/dist/base.d.ts.map +1 -1
- package/dist/base.js +10 -3
- package/dist/base.js.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/serde/jsonplus.cjs +58 -29
- package/dist/serde/jsonplus.cjs.map +1 -1
- package/dist/serde/jsonplus.js +58 -29
- package/dist/serde/jsonplus.js.map +1 -1
- package/dist/serde/types.cjs.map +1 -1
- package/dist/serde/types.d.cts +8 -1
- package/dist/serde/types.d.cts.map +1 -1
- package/dist/serde/types.d.ts +8 -1
- package/dist/serde/types.d.ts.map +1 -1
- package/dist/serde/types.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -30,6 +30,17 @@ You must pass these when invoking the graph as part of the configurable part of
|
|
|
30
30
|
|
|
31
31
|
When a graph node fails mid-execution at a given superstep, LangGraph stores pending checkpoint writes from any other nodes that completed successfully at that superstep, so that whenever we resume graph execution from that superstep we don't re-run the successful nodes.
|
|
32
32
|
|
|
33
|
+
### When are checkpoints persisted
|
|
34
|
+
|
|
35
|
+
By default (`durability: "async"`) checkpoint writes are dispatched in the background while the graph keeps executing, which keeps runs fast. Regardless of the `durability` mode, LangGraph **awaits all outstanding checkpointer writes before `invoke()` / `stream()` resolves**, `invoke()` fully drains the underlying stream, and the stream awaits every pending checkpointer promise before it completes.
|
|
36
|
+
|
|
37
|
+
In practice this means:
|
|
38
|
+
|
|
39
|
+
- If you `await graph.invoke(...)` (or fully consume `for await (... of graph.stream(...))`), persistence is guaranteed to be complete by the time the call returns. You do **not** need to keep the process/runtime alive for any trailing background writes.
|
|
40
|
+
- On serverless/edge runtimes (e.g. Cloudflare Workers) you therefore do not need `ctx.waitUntil()` to flush checkpoints, just make sure you `await` the run before returning a response. The only way to orphan writes is to start a stream and never consume it (e.g. piping `graph.stream()` into a detached, un-awaited task).
|
|
41
|
+
|
|
42
|
+
> Note: this guarantees the *write was issued and awaited*, not that your database driver works in a given runtime. See the runtime-compatibility notes in the [Postgres](../checkpoint-postgres/README.md#edge--serverless-runtimes-cloudflare-workers-etc) and [Redis](../checkpoint-redis/README.md#edge--serverless-runtimes-cloudflare-workers-etc) checkpointer READMEs.
|
|
43
|
+
|
|
33
44
|
## Interface
|
|
34
45
|
|
|
35
46
|
Each checkpointer should conform to `BaseCheckpointSaver` interface and must implement the following methods:
|
package/dist/base.cjs
CHANGED
|
@@ -25,9 +25,9 @@ function copyCheckpoint(checkpoint) {
|
|
|
25
25
|
v: checkpoint.v,
|
|
26
26
|
id: checkpoint.id,
|
|
27
27
|
ts: checkpoint.ts,
|
|
28
|
-
channel_values: { ...checkpoint.channel_values },
|
|
29
|
-
channel_versions: { ...checkpoint.channel_versions },
|
|
30
|
-
versions_seen: deepCopy(checkpoint.versions_seen)
|
|
28
|
+
channel_values: { ...checkpoint.channel_values ?? {} },
|
|
29
|
+
channel_versions: { ...checkpoint.channel_versions ?? {} },
|
|
30
|
+
versions_seen: deepCopy(checkpoint.versions_seen ?? {})
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
var BaseCheckpointSaver = class {
|
|
@@ -35,6 +35,13 @@ var BaseCheckpointSaver = class {
|
|
|
35
35
|
constructor(serde) {
|
|
36
36
|
this.serde = serde || this.serde;
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Prevent `JSON.stringify` from traversing backend clients (e.g. pg Pool
|
|
40
|
+
* timers) when a checkpointer is present in runnable `configurable`.
|
|
41
|
+
*/
|
|
42
|
+
toJSON() {
|
|
43
|
+
return `[${this.constructor.name}]`;
|
|
44
|
+
}
|
|
38
45
|
async get(config) {
|
|
39
46
|
const value = await this.getTuple(config);
|
|
40
47
|
return value ? value.checkpoint : void 0;
|
package/dist/base.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.cjs","names":["uuid6","JsonPlusSerializer","ERROR","SCHEDULED","INTERRUPT","RESUME"],"sources":["../src/base.ts"],"sourcesContent":["import type { RunnableConfig } from \"@langchain/core/runnables\";\nimport { SerializerProtocol } from \"./serde/base.js\";\nimport { uuid6 } from \"./id.js\";\nimport type {\n PendingWrite,\n CheckpointPendingWrite,\n CheckpointMetadata,\n DeltaChannelHistory,\n} from \"./types.js\";\nimport { ERROR, INTERRUPT, RESUME, SCHEDULED } from \"./serde/types.js\";\nimport { JsonPlusSerializer } from \"./serde/jsonplus.js\";\n\n/** @inline */\ntype ChannelVersion = number | string;\n\nexport type ChannelVersions = Record<string, ChannelVersion>;\n\nexport interface Checkpoint<\n N extends string = string,\n C extends string = string,\n> {\n /**\n * The version of the checkpoint format. Currently 4\n */\n v: number;\n /**\n * Checkpoint ID {uuid6}\n */\n id: string;\n /**\n * Timestamp {new Date().toISOString()}\n */\n ts: string;\n /**\n * @default {}\n */\n channel_values: Record<C, unknown>;\n /**\n * @default {}\n */\n channel_versions: Record<C, ChannelVersion>;\n /**\n * @default {}\n */\n versions_seen: Record<N, Record<C, ChannelVersion>>;\n}\n\nexport interface ReadonlyCheckpoint extends Readonly<Checkpoint> {\n readonly channel_values: Readonly<Record<string, unknown>>;\n readonly channel_versions: Readonly<Record<string, ChannelVersion>>;\n readonly versions_seen: Readonly<\n Record<string, Readonly<Record<string, ChannelVersion>>>\n >;\n}\n\nexport function deepCopy<T>(obj: T): T {\n if (typeof obj !== \"object\" || obj === null) {\n return obj;\n }\n\n const newObj = Array.isArray(obj) ? [] : {};\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n (newObj as Record<PropertyKey, unknown>)[key] = deepCopy(\n (obj as Record<string, unknown>)[key]\n );\n }\n }\n\n return newObj as T;\n}\n\n/** @hidden */\nexport function emptyCheckpoint(): Checkpoint {\n return {\n v: 4,\n id: uuid6(0),\n ts: new Date().toISOString(),\n channel_values: {},\n channel_versions: {},\n versions_seen: {},\n };\n}\n\n/** @hidden */\nexport function copyCheckpoint(checkpoint: ReadonlyCheckpoint): Checkpoint {\n return {\n v: checkpoint.v,\n id: checkpoint.id,\n ts: checkpoint.ts,\n channel_values: { ...checkpoint.channel_values },\n channel_versions: { ...checkpoint.channel_versions },\n versions_seen: deepCopy(checkpoint.versions_seen),\n };\n}\n\nexport interface CheckpointTuple {\n config: RunnableConfig;\n checkpoint: Checkpoint;\n metadata?: CheckpointMetadata;\n parentConfig?: RunnableConfig;\n pendingWrites?: CheckpointPendingWrite[];\n}\n\nexport type CheckpointListOptions = {\n limit?: number;\n before?: RunnableConfig;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n filter?: Record<string, any>;\n};\n\nexport abstract class BaseCheckpointSaver<V extends string | number = number> {\n serde: SerializerProtocol = new JsonPlusSerializer();\n\n constructor(serde?: SerializerProtocol) {\n this.serde = serde || this.serde;\n }\n\n async get(config: RunnableConfig): Promise<Checkpoint | undefined> {\n const value = await this.getTuple(config);\n return value ? value.checkpoint : undefined;\n }\n\n abstract getTuple(\n config: RunnableConfig\n ): Promise<CheckpointTuple | undefined>;\n\n abstract list(\n config: RunnableConfig,\n options?: CheckpointListOptions\n ): AsyncGenerator<CheckpointTuple>;\n\n abstract put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata,\n newVersions: ChannelVersions\n ): Promise<RunnableConfig>;\n\n /**\n * Store intermediate writes linked to a checkpoint.\n */\n abstract putWrites(\n config: RunnableConfig,\n writes: PendingWrite[],\n taskId: string\n ): Promise<void>;\n\n /**\n * Delete all checkpoints and writes associated with a specific thread ID.\n * @param threadId The thread ID whose checkpoints should be deleted.\n */\n abstract deleteThread(threadId: string): Promise<void>;\n\n /**\n * Walk the parent chain returning per-channel writes + seed, used to\n * reconstruct `DeltaChannel` state from `checkpoint_writes`.\n *\n * For each requested channel, walks ancestors of the checkpoint identified\n * by `config` (following `parentConfig`) and accumulates the pending writes\n * for that channel. The walk terminates per-channel at the nearest ancestor\n * whose `channel_values[ch]` is populated; that value is returned as `seed`.\n * If the walk reaches the root without finding a stored value, `seed` is\n * omitted from that channel's entry — the consumer treats the absence as\n * \"start empty\".\n *\n * Walks the parent chain (not `list({ before })`): for forked threads, only\n * on-path ancestors contribute.\n *\n * The default implementation walks `getTuple` + `parentConfig` once for all\n * channels — each ancestor visited once, not once per channel. Savers with\n * direct storage access (e.g. `MemorySaver`) override for performance; the\n * return contract is fixed here.\n *\n * @remarks Beta. The signature, return shape, and interaction with\n * `DeltaSnapshot` blobs may change. Override at your own risk; the default\n * implementation will continue to work against the public\n * `BaseCheckpointSaver` contract.\n *\n * @param options.config Configuration identifying the target checkpoint.\n * @param options.channels Channel names to walk for. Empty → empty mapping.\n * @returns Per-channel {@link DeltaChannelHistory} for every requested name.\n */\n async getDeltaChannelHistory(options: {\n config: RunnableConfig;\n channels: string[];\n }): Promise<Record<string, DeltaChannelHistory>> {\n const { config, channels } = options;\n if (channels.length === 0) return {};\n\n const collectedByCh: Record<string, CheckpointPendingWrite[]> = {};\n const seedByCh: Record<string, unknown> = {};\n const remaining = new Set(channels);\n for (const ch of channels) collectedByCh[ch] = [];\n\n const targetTuple = await this.getTuple(config);\n let cursorConfig: RunnableConfig | undefined = targetTuple?.parentConfig;\n\n while (cursorConfig != null && remaining.size > 0) {\n const tup: CheckpointTuple | undefined =\n await this.getTuple(cursorConfig);\n if (tup === undefined) break;\n if (tup.pendingWrites && tup.pendingWrites.length > 0) {\n // DeltaChannel reconstruction must replay concurrent same-superstep\n // writes in the canonical (task_id, idx) order that live execution uses\n // (see `_applyWrites`), or the reconstructed value can diverge from the\n // live one. Group per channel and stable-sort by task id: a stable sort\n // keeps each task's writes in their stored `idx` order, making the\n // result independent of how a saver returns `pendingWrites`.\n const perChannel: Record<string, CheckpointPendingWrite[]> = {};\n for (const write of tup.pendingWrites) {\n const ch = write[1];\n if (remaining.has(ch)) (perChannel[ch] ??= []).push(write);\n }\n for (const ch of Object.keys(perChannel)) {\n const block = perChannel[ch];\n block.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));\n // Pushed reversed so the final `.reverse()` below yields oldest→newest\n // checkpoints with each checkpoint's writes ascending by (task_id, idx).\n for (let i = block.length - 1; i >= 0; i -= 1) {\n collectedByCh[ch].push(block[i]);\n }\n }\n }\n for (const ch of Array.from(remaining)) {\n if (\n Object.prototype.hasOwnProperty.call(\n tup.checkpoint.channel_values,\n ch\n )\n ) {\n seedByCh[ch] = tup.checkpoint.channel_values[ch];\n remaining.delete(ch);\n }\n }\n cursorConfig = tup.parentConfig;\n }\n\n const result: Record<string, DeltaChannelHistory> = {};\n for (const ch of channels) {\n const entry: DeltaChannelHistory = {\n writes: collectedByCh[ch].slice().reverse(),\n };\n if (Object.prototype.hasOwnProperty.call(seedByCh, ch)) {\n entry.seed = seedByCh[ch];\n }\n result[ch] = entry;\n }\n return result;\n }\n\n /**\n * Generate the next version ID for a channel.\n *\n * Default is to use integer versions, incrementing by 1. If you override, you can use str/int/float versions,\n * as long as they are monotonically increasing.\n */\n getNextVersion(current: V | undefined): V {\n if (typeof current === \"string\") {\n throw new Error(\"Please override this method to use string versions.\");\n }\n return (\n current !== undefined && typeof current === \"number\" ? current + 1 : 1\n ) as V;\n }\n}\n\nexport function compareChannelVersions(\n a: ChannelVersion,\n b: ChannelVersion\n): number {\n if (typeof a === \"number\" && typeof b === \"number\") {\n return Math.sign(a - b);\n }\n\n return String(a).localeCompare(String(b));\n}\n\nexport function maxChannelVersion(\n ...versions: ChannelVersion[]\n): ChannelVersion {\n return versions.reduce((max, version, idx) => {\n if (idx === 0) return version;\n return compareChannelVersions(max, version) >= 0 ? max : version;\n });\n}\n\n/**\n * Mapping from error type to error index.\n * Regular writes just map to their index in the list of writes being saved.\n * Special writes (e.g. errors) map to negative indices, to avoid those writes from\n * conflicting with regular writes.\n * Each Checkpointer implementation should use this mapping in put_writes.\n */\nexport const WRITES_IDX_MAP: Record<string, number> = {\n [ERROR]: -1,\n [SCHEDULED]: -2,\n [INTERRUPT]: -3,\n [RESUME]: -4,\n};\n\n/**\n * Metadata keys that are LangGraph's internal framework bookkeeping and\n * should not be surfaced as user-meaningful metadata.\n *\n * Consumed by stream handlers (e.g. the `tasks` debug stream) to drop\n * framework keys — which are redundant with a task's own fields and\n * namespace — while keeping keys like `lc_agent_name`, `ls_integration`,\n * and user-supplied metadata.\n */\nexport const EXCLUDED_METADATA_KEYS: ReadonlySet<string> = new Set([\n \"thread_id\",\n \"checkpoint_id\",\n \"checkpoint_ns\",\n \"checkpoint_map\",\n \"langgraph_step\",\n \"langgraph_node\",\n \"langgraph_triggers\",\n \"langgraph_path\",\n \"langgraph_checkpoint_ns\",\n]);\n\nexport function getCheckpointId(config: RunnableConfig): string {\n return (\n config.configurable?.checkpoint_id || config.configurable?.thread_ts || \"\"\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"base.cjs","names":["uuid6","JsonPlusSerializer","ERROR","SCHEDULED","INTERRUPT","RESUME"],"sources":["../src/base.ts"],"sourcesContent":["import type { RunnableConfig } from \"@langchain/core/runnables\";\nimport { SerializerProtocol } from \"./serde/base.js\";\nimport { uuid6 } from \"./id.js\";\nimport type {\n PendingWrite,\n CheckpointPendingWrite,\n CheckpointMetadata,\n DeltaChannelHistory,\n} from \"./types.js\";\nimport { ERROR, INTERRUPT, RESUME, SCHEDULED } from \"./serde/types.js\";\nimport { JsonPlusSerializer } from \"./serde/jsonplus.js\";\n\n/** @inline */\ntype ChannelVersion = number | string;\n\nexport type ChannelVersions = Record<string, ChannelVersion>;\n\nexport interface Checkpoint<\n N extends string = string,\n C extends string = string,\n> {\n /**\n * The version of the checkpoint format. Currently 4\n */\n v: number;\n /**\n * Checkpoint ID {uuid6}\n */\n id: string;\n /**\n * Timestamp {new Date().toISOString()}\n */\n ts: string;\n /**\n * @default {}\n */\n channel_values: Record<C, unknown>;\n /**\n * @default {}\n */\n channel_versions: Record<C, ChannelVersion>;\n /**\n * @default {}\n */\n versions_seen: Record<N, Record<C, ChannelVersion>>;\n}\n\nexport interface ReadonlyCheckpoint extends Readonly<Checkpoint> {\n readonly channel_values: Readonly<Record<string, unknown>>;\n readonly channel_versions: Readonly<Record<string, ChannelVersion>>;\n readonly versions_seen: Readonly<\n Record<string, Readonly<Record<string, ChannelVersion>>>\n >;\n}\n\nexport function deepCopy<T>(obj: T): T {\n if (typeof obj !== \"object\" || obj === null) {\n return obj;\n }\n\n const newObj = Array.isArray(obj) ? [] : {};\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n (newObj as Record<PropertyKey, unknown>)[key] = deepCopy(\n (obj as Record<string, unknown>)[key]\n );\n }\n }\n\n return newObj as T;\n}\n\n/** @hidden */\nexport function emptyCheckpoint(): Checkpoint {\n return {\n v: 4,\n id: uuid6(0),\n ts: new Date().toISOString(),\n channel_values: {},\n channel_versions: {},\n versions_seen: {},\n };\n}\n\n/** @hidden */\nexport function copyCheckpoint(checkpoint: ReadonlyCheckpoint): Checkpoint {\n return {\n v: checkpoint.v,\n id: checkpoint.id,\n ts: checkpoint.ts,\n channel_values: { ...(checkpoint.channel_values ?? {}) },\n channel_versions: { ...(checkpoint.channel_versions ?? {}) },\n versions_seen: deepCopy(checkpoint.versions_seen ?? {}),\n };\n}\n\nexport interface CheckpointTuple {\n config: RunnableConfig;\n checkpoint: Checkpoint;\n metadata?: CheckpointMetadata;\n parentConfig?: RunnableConfig;\n pendingWrites?: CheckpointPendingWrite[];\n}\n\nexport type CheckpointListOptions = {\n limit?: number;\n before?: RunnableConfig;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n filter?: Record<string, any>;\n};\n\nexport abstract class BaseCheckpointSaver<V extends string | number = number> {\n serde: SerializerProtocol = new JsonPlusSerializer();\n\n constructor(serde?: SerializerProtocol) {\n this.serde = serde || this.serde;\n }\n\n /**\n * Prevent `JSON.stringify` from traversing backend clients (e.g. pg Pool\n * timers) when a checkpointer is present in runnable `configurable`.\n */\n toJSON(): string {\n return `[${this.constructor.name}]`;\n }\n\n async get(config: RunnableConfig): Promise<Checkpoint | undefined> {\n const value = await this.getTuple(config);\n return value ? value.checkpoint : undefined;\n }\n\n abstract getTuple(\n config: RunnableConfig\n ): Promise<CheckpointTuple | undefined>;\n\n abstract list(\n config: RunnableConfig,\n options?: CheckpointListOptions\n ): AsyncGenerator<CheckpointTuple>;\n\n abstract put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata,\n newVersions: ChannelVersions\n ): Promise<RunnableConfig>;\n\n /**\n * Store intermediate writes linked to a checkpoint.\n */\n abstract putWrites(\n config: RunnableConfig,\n writes: PendingWrite[],\n taskId: string\n ): Promise<void>;\n\n /**\n * Delete all checkpoints and writes associated with a specific thread ID.\n * @param threadId The thread ID whose checkpoints should be deleted.\n */\n abstract deleteThread(threadId: string): Promise<void>;\n\n /**\n * Walk the parent chain returning per-channel writes + seed, used to\n * reconstruct `DeltaChannel` state from `checkpoint_writes`.\n *\n * For each requested channel, walks ancestors of the checkpoint identified\n * by `config` (following `parentConfig`) and accumulates the pending writes\n * for that channel. The walk terminates per-channel at the nearest ancestor\n * whose `channel_values[ch]` is populated; that value is returned as `seed`.\n * If the walk reaches the root without finding a stored value, `seed` is\n * omitted from that channel's entry — the consumer treats the absence as\n * \"start empty\".\n *\n * Walks the parent chain (not `list({ before })`): for forked threads, only\n * on-path ancestors contribute.\n *\n * The default implementation walks `getTuple` + `parentConfig` once for all\n * channels — each ancestor visited once, not once per channel. Savers with\n * direct storage access (e.g. `MemorySaver`) override for performance; the\n * return contract is fixed here.\n *\n * @remarks Beta. The signature, return shape, and interaction with\n * `DeltaSnapshot` blobs may change. Override at your own risk; the default\n * implementation will continue to work against the public\n * `BaseCheckpointSaver` contract.\n *\n * @param options.config Configuration identifying the target checkpoint.\n * @param options.channels Channel names to walk for. Empty → empty mapping.\n * @returns Per-channel {@link DeltaChannelHistory} for every requested name.\n */\n async getDeltaChannelHistory(options: {\n config: RunnableConfig;\n channels: string[];\n }): Promise<Record<string, DeltaChannelHistory>> {\n const { config, channels } = options;\n if (channels.length === 0) return {};\n\n const collectedByCh: Record<string, CheckpointPendingWrite[]> = {};\n const seedByCh: Record<string, unknown> = {};\n const remaining = new Set(channels);\n for (const ch of channels) collectedByCh[ch] = [];\n\n const targetTuple = await this.getTuple(config);\n let cursorConfig: RunnableConfig | undefined = targetTuple?.parentConfig;\n\n while (cursorConfig != null && remaining.size > 0) {\n const tup: CheckpointTuple | undefined =\n await this.getTuple(cursorConfig);\n if (tup === undefined) break;\n if (tup.pendingWrites && tup.pendingWrites.length > 0) {\n // DeltaChannel reconstruction must replay concurrent same-superstep\n // writes in the canonical (task_id, idx) order that live execution uses\n // (see `_applyWrites`), or the reconstructed value can diverge from the\n // live one. Group per channel and stable-sort by task id: a stable sort\n // keeps each task's writes in their stored `idx` order, making the\n // result independent of how a saver returns `pendingWrites`.\n const perChannel: Record<string, CheckpointPendingWrite[]> = {};\n for (const write of tup.pendingWrites) {\n const ch = write[1];\n if (remaining.has(ch)) (perChannel[ch] ??= []).push(write);\n }\n for (const ch of Object.keys(perChannel)) {\n const block = perChannel[ch];\n block.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));\n // Pushed reversed so the final `.reverse()` below yields oldest→newest\n // checkpoints with each checkpoint's writes ascending by (task_id, idx).\n for (let i = block.length - 1; i >= 0; i -= 1) {\n collectedByCh[ch].push(block[i]);\n }\n }\n }\n for (const ch of Array.from(remaining)) {\n if (\n Object.prototype.hasOwnProperty.call(\n tup.checkpoint.channel_values,\n ch\n )\n ) {\n seedByCh[ch] = tup.checkpoint.channel_values[ch];\n remaining.delete(ch);\n }\n }\n cursorConfig = tup.parentConfig;\n }\n\n const result: Record<string, DeltaChannelHistory> = {};\n for (const ch of channels) {\n const entry: DeltaChannelHistory = {\n writes: collectedByCh[ch].slice().reverse(),\n };\n if (Object.prototype.hasOwnProperty.call(seedByCh, ch)) {\n entry.seed = seedByCh[ch];\n }\n result[ch] = entry;\n }\n return result;\n }\n\n /**\n * Generate the next version ID for a channel.\n *\n * Default is to use integer versions, incrementing by 1. If you override, you can use str/int/float versions,\n * as long as they are monotonically increasing.\n */\n getNextVersion(current: V | undefined): V {\n if (typeof current === \"string\") {\n throw new Error(\"Please override this method to use string versions.\");\n }\n return (\n current !== undefined && typeof current === \"number\" ? current + 1 : 1\n ) as V;\n }\n}\n\nexport function compareChannelVersions(\n a: ChannelVersion,\n b: ChannelVersion\n): number {\n if (typeof a === \"number\" && typeof b === \"number\") {\n return Math.sign(a - b);\n }\n\n return String(a).localeCompare(String(b));\n}\n\nexport function maxChannelVersion(\n ...versions: ChannelVersion[]\n): ChannelVersion {\n return versions.reduce((max, version, idx) => {\n if (idx === 0) return version;\n return compareChannelVersions(max, version) >= 0 ? max : version;\n });\n}\n\n/**\n * Mapping from error type to error index.\n * Regular writes just map to their index in the list of writes being saved.\n * Special writes (e.g. errors) map to negative indices, to avoid those writes from\n * conflicting with regular writes.\n * Each Checkpointer implementation should use this mapping in put_writes.\n */\nexport const WRITES_IDX_MAP: Record<string, number> = {\n [ERROR]: -1,\n [SCHEDULED]: -2,\n [INTERRUPT]: -3,\n [RESUME]: -4,\n};\n\n/**\n * Metadata keys that are LangGraph's internal framework bookkeeping and\n * should not be surfaced as user-meaningful metadata.\n *\n * Consumed by stream handlers (e.g. the `tasks` debug stream) to drop\n * framework keys — which are redundant with a task's own fields and\n * namespace — while keeping keys like `lc_agent_name`, `ls_integration`,\n * and user-supplied metadata.\n */\nexport const EXCLUDED_METADATA_KEYS: ReadonlySet<string> = new Set([\n \"thread_id\",\n \"checkpoint_id\",\n \"checkpoint_ns\",\n \"checkpoint_map\",\n \"langgraph_step\",\n \"langgraph_node\",\n \"langgraph_triggers\",\n \"langgraph_path\",\n \"langgraph_checkpoint_ns\",\n]);\n\nexport function getCheckpointId(config: RunnableConfig): string {\n return (\n config.configurable?.checkpoint_id || config.configurable?.thread_ts || \"\"\n );\n}\n"],"mappings":"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"}
|
package/dist/base.d.cts
CHANGED
|
@@ -57,6 +57,11 @@ type CheckpointListOptions = {
|
|
|
57
57
|
declare abstract class BaseCheckpointSaver<V extends string | number = number> {
|
|
58
58
|
serde: SerializerProtocol;
|
|
59
59
|
constructor(serde?: SerializerProtocol);
|
|
60
|
+
/**
|
|
61
|
+
* Prevent `JSON.stringify` from traversing backend clients (e.g. pg Pool
|
|
62
|
+
* timers) when a checkpointer is present in runnable `configurable`.
|
|
63
|
+
*/
|
|
64
|
+
toJSON(): string;
|
|
60
65
|
get(config: RunnableConfig): Promise<Checkpoint | undefined>;
|
|
61
66
|
abstract getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined>;
|
|
62
67
|
abstract list(config: RunnableConfig, options?: CheckpointListOptions): AsyncGenerator<CheckpointTuple>;
|
package/dist/base.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.cts","names":[],"sources":["../src/base.ts"],"mappings":";;;;;;KAaK,cAAA;AAAA,KAEO,eAAA,GAAkB,MAAA,SAAe,cAAA;AAAA,UAE5B,UAAA;;;;EAOf,CAAA;EATyB;;;EAazB,EAAA;EAXe;;;EAef,EAAA;EAIgB;;;EAAhB,cAAA,EAAgB,MAAA,CAAO,CAAA;EAQD;;;EAJtB,gBAAA,EAAkB,MAAA,CAAO,CAAA,EAAG,cAAA;EAIb;;;EAAf,aAAA,EAAe,MAAA,CAAO,CAAA,EAAG,MAAA,CAAO,CAAA,EAAG,cAAA;AAAA;AAAA,UAGpB,kBAAA,SAA2B,QAAA,CAAS,UAAA;EAAA,SAC1C,cAAA,EAAgB,QAAA,CAAS,MAAA;EAAA,SACzB,gBAAA,EAAkB,QAAA,CAAS,MAAA,SAAe,cAAA;EAAA,SAC1C,aAAA,EAAe,QAAA,CACtB,MAAA,SAAe,QAAA,CAAS,MAAA,SAAe,cAAA;AAAA;AAAA,iBAI3B,QAAA,GAAA,CAAY,GAAA,EAAK,CAAA,GAAI,CAAA;;iBAmBrB,eAAA,CAAA,GAAmB,UAAA;;iBAYnB,cAAA,CAAe,UAAA,EAAY,kBAAA,GAAqB,UAAA;AAAA,UAW/C,eAAA;EACf,MAAA,EAAQ,cAAA;EACR,UAAA,EAAY,UAAA;EACZ,QAAA,GAAW,kBAAA;EACX,YAAA,GAAe,cAAA;EACf,aAAA,GAAgB,sBAAA;AAAA;AAAA,KAGN,qBAAA;EACV,KAAA;EACA,MAAA,GAAS,cAAA;EAET,MAAA,GAAS,MAAA;AAAA;AAAA,uBAGW,mBAAA;EACpB,KAAA,EAAO,kBAAA;EAEP,WAAA,CAAY,KAAA,GAAQ,kBAAA;
|
|
1
|
+
{"version":3,"file":"base.d.cts","names":[],"sources":["../src/base.ts"],"mappings":";;;;;;KAaK,cAAA;AAAA,KAEO,eAAA,GAAkB,MAAA,SAAe,cAAA;AAAA,UAE5B,UAAA;;;;EAOf,CAAA;EATyB;;;EAazB,EAAA;EAXe;;;EAef,EAAA;EAIgB;;;EAAhB,cAAA,EAAgB,MAAA,CAAO,CAAA;EAQD;;;EAJtB,gBAAA,EAAkB,MAAA,CAAO,CAAA,EAAG,cAAA;EAIb;;;EAAf,aAAA,EAAe,MAAA,CAAO,CAAA,EAAG,MAAA,CAAO,CAAA,EAAG,cAAA;AAAA;AAAA,UAGpB,kBAAA,SAA2B,QAAA,CAAS,UAAA;EAAA,SAC1C,cAAA,EAAgB,QAAA,CAAS,MAAA;EAAA,SACzB,gBAAA,EAAkB,QAAA,CAAS,MAAA,SAAe,cAAA;EAAA,SAC1C,aAAA,EAAe,QAAA,CACtB,MAAA,SAAe,QAAA,CAAS,MAAA,SAAe,cAAA;AAAA;AAAA,iBAI3B,QAAA,GAAA,CAAY,GAAA,EAAK,CAAA,GAAI,CAAA;;iBAmBrB,eAAA,CAAA,GAAmB,UAAA;;iBAYnB,cAAA,CAAe,UAAA,EAAY,kBAAA,GAAqB,UAAA;AAAA,UAW/C,eAAA;EACf,MAAA,EAAQ,cAAA;EACR,UAAA,EAAY,UAAA;EACZ,QAAA,GAAW,kBAAA;EACX,YAAA,GAAe,cAAA;EACf,aAAA,GAAgB,sBAAA;AAAA;AAAA,KAGN,qBAAA;EACV,KAAA;EACA,MAAA,GAAS,cAAA;EAET,MAAA,GAAS,MAAA;AAAA;AAAA,uBAGW,mBAAA;EACpB,KAAA,EAAO,kBAAA;EAEP,WAAA,CAAY,KAAA,GAAQ,kBAAA;EAlEgB;;;;EA0EpC,MAAA,CAAA;EAIM,GAAA,CAAI,MAAA,EAAQ,cAAA,GAAiB,OAAA,CAAQ,UAAA;EAAA,SAKlC,QAAA,CACP,MAAA,EAAQ,cAAA,GACP,OAAA,CAAQ,eAAA;EAAA,SAEF,IAAA,CACP,MAAA,EAAQ,cAAA,EACR,OAAA,GAAU,qBAAA,GACT,cAAA,CAAe,eAAA;EAAA,SAET,GAAA,CACP,MAAA,EAAQ,cAAA,EACR,UAAA,EAAY,UAAA,EACZ,QAAA,EAAU,kBAAA,EACV,WAAA,EAAa,eAAA,GACZ,OAAA,CAAQ,cAAA;EAnGuC;;;EAAA,SAwGzC,SAAA,CACP,MAAA,EAAQ,cAAA,EACR,MAAA,EAAQ,YAAA,IACR,MAAA,WACC,OAAA;EA3GsB;;;;EAAA,SAiHhB,YAAA,CAAa,QAAA,WAAmB,OAAA;EAhHU;;;;;;;;;AAMrD;;;;;;;;;;AAmBA;;;;;AAYA;;;;;EA0GQ,sBAAA,CAAuB,OAAA;IAC3B,MAAA,EAAQ,cAAA;IACR,QAAA;EAAA,IACE,OAAA,CAAQ,MAAA,SAAe,mBAAA;EAlGZ;;;;;;EAyKf,cAAA,CAAe,OAAA,EAAS,CAAA,eAAgB,CAAA;AAAA;AAAA,iBAU1B,sBAAA,CACd,CAAA,EAAG,cAAA,EACH,CAAA,EAAG,cAAA;AAAA,iBASW,iBAAA,CAAA,GACX,QAAA,EAAU,cAAA,KACZ,cAAA;;;;;;;;cAcU,cAAA,EAAgB,MAAA;;;;;AAtM7B;;;;;cAsNa,sBAAA,EAAwB,WAAA;AAAA,iBAYrB,eAAA,CAAgB,MAAA,EAAQ,cAAA"}
|
package/dist/base.d.ts
CHANGED
|
@@ -57,6 +57,11 @@ type CheckpointListOptions = {
|
|
|
57
57
|
declare abstract class BaseCheckpointSaver<V extends string | number = number> {
|
|
58
58
|
serde: SerializerProtocol;
|
|
59
59
|
constructor(serde?: SerializerProtocol);
|
|
60
|
+
/**
|
|
61
|
+
* Prevent `JSON.stringify` from traversing backend clients (e.g. pg Pool
|
|
62
|
+
* timers) when a checkpointer is present in runnable `configurable`.
|
|
63
|
+
*/
|
|
64
|
+
toJSON(): string;
|
|
60
65
|
get(config: RunnableConfig): Promise<Checkpoint | undefined>;
|
|
61
66
|
abstract getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined>;
|
|
62
67
|
abstract list(config: RunnableConfig, options?: CheckpointListOptions): AsyncGenerator<CheckpointTuple>;
|
package/dist/base.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","names":[],"sources":["../src/base.ts"],"mappings":";;;;;;KAaK,cAAA;AAAA,KAEO,eAAA,GAAkB,MAAA,SAAe,cAAA;AAAA,UAE5B,UAAA;;;;EAOf,CAAA;EATyB;;;EAazB,EAAA;EAXe;;;EAef,EAAA;EAIgB;;;EAAhB,cAAA,EAAgB,MAAA,CAAO,CAAA;EAQD;;;EAJtB,gBAAA,EAAkB,MAAA,CAAO,CAAA,EAAG,cAAA;EAIb;;;EAAf,aAAA,EAAe,MAAA,CAAO,CAAA,EAAG,MAAA,CAAO,CAAA,EAAG,cAAA;AAAA;AAAA,UAGpB,kBAAA,SAA2B,QAAA,CAAS,UAAA;EAAA,SAC1C,cAAA,EAAgB,QAAA,CAAS,MAAA;EAAA,SACzB,gBAAA,EAAkB,QAAA,CAAS,MAAA,SAAe,cAAA;EAAA,SAC1C,aAAA,EAAe,QAAA,CACtB,MAAA,SAAe,QAAA,CAAS,MAAA,SAAe,cAAA;AAAA;AAAA,iBAI3B,QAAA,GAAA,CAAY,GAAA,EAAK,CAAA,GAAI,CAAA;;iBAmBrB,eAAA,CAAA,GAAmB,UAAA;;iBAYnB,cAAA,CAAe,UAAA,EAAY,kBAAA,GAAqB,UAAA;AAAA,UAW/C,eAAA;EACf,MAAA,EAAQ,cAAA;EACR,UAAA,EAAY,UAAA;EACZ,QAAA,GAAW,kBAAA;EACX,YAAA,GAAe,cAAA;EACf,aAAA,GAAgB,sBAAA;AAAA;AAAA,KAGN,qBAAA;EACV,KAAA;EACA,MAAA,GAAS,cAAA;EAET,MAAA,GAAS,MAAA;AAAA;AAAA,uBAGW,mBAAA;EACpB,KAAA,EAAO,kBAAA;EAEP,WAAA,CAAY,KAAA,GAAQ,kBAAA;
|
|
1
|
+
{"version":3,"file":"base.d.ts","names":[],"sources":["../src/base.ts"],"mappings":";;;;;;KAaK,cAAA;AAAA,KAEO,eAAA,GAAkB,MAAA,SAAe,cAAA;AAAA,UAE5B,UAAA;;;;EAOf,CAAA;EATyB;;;EAazB,EAAA;EAXe;;;EAef,EAAA;EAIgB;;;EAAhB,cAAA,EAAgB,MAAA,CAAO,CAAA;EAQD;;;EAJtB,gBAAA,EAAkB,MAAA,CAAO,CAAA,EAAG,cAAA;EAIb;;;EAAf,aAAA,EAAe,MAAA,CAAO,CAAA,EAAG,MAAA,CAAO,CAAA,EAAG,cAAA;AAAA;AAAA,UAGpB,kBAAA,SAA2B,QAAA,CAAS,UAAA;EAAA,SAC1C,cAAA,EAAgB,QAAA,CAAS,MAAA;EAAA,SACzB,gBAAA,EAAkB,QAAA,CAAS,MAAA,SAAe,cAAA;EAAA,SAC1C,aAAA,EAAe,QAAA,CACtB,MAAA,SAAe,QAAA,CAAS,MAAA,SAAe,cAAA;AAAA;AAAA,iBAI3B,QAAA,GAAA,CAAY,GAAA,EAAK,CAAA,GAAI,CAAA;;iBAmBrB,eAAA,CAAA,GAAmB,UAAA;;iBAYnB,cAAA,CAAe,UAAA,EAAY,kBAAA,GAAqB,UAAA;AAAA,UAW/C,eAAA;EACf,MAAA,EAAQ,cAAA;EACR,UAAA,EAAY,UAAA;EACZ,QAAA,GAAW,kBAAA;EACX,YAAA,GAAe,cAAA;EACf,aAAA,GAAgB,sBAAA;AAAA;AAAA,KAGN,qBAAA;EACV,KAAA;EACA,MAAA,GAAS,cAAA;EAET,MAAA,GAAS,MAAA;AAAA;AAAA,uBAGW,mBAAA;EACpB,KAAA,EAAO,kBAAA;EAEP,WAAA,CAAY,KAAA,GAAQ,kBAAA;EAlEgB;;;;EA0EpC,MAAA,CAAA;EAIM,GAAA,CAAI,MAAA,EAAQ,cAAA,GAAiB,OAAA,CAAQ,UAAA;EAAA,SAKlC,QAAA,CACP,MAAA,EAAQ,cAAA,GACP,OAAA,CAAQ,eAAA;EAAA,SAEF,IAAA,CACP,MAAA,EAAQ,cAAA,EACR,OAAA,GAAU,qBAAA,GACT,cAAA,CAAe,eAAA;EAAA,SAET,GAAA,CACP,MAAA,EAAQ,cAAA,EACR,UAAA,EAAY,UAAA,EACZ,QAAA,EAAU,kBAAA,EACV,WAAA,EAAa,eAAA,GACZ,OAAA,CAAQ,cAAA;EAnGuC;;;EAAA,SAwGzC,SAAA,CACP,MAAA,EAAQ,cAAA,EACR,MAAA,EAAQ,YAAA,IACR,MAAA,WACC,OAAA;EA3GsB;;;;EAAA,SAiHhB,YAAA,CAAa,QAAA,WAAmB,OAAA;EAhHU;;;;;;;;;AAMrD;;;;;;;;;;AAmBA;;;;;AAYA;;;;;EA0GQ,sBAAA,CAAuB,OAAA;IAC3B,MAAA,EAAQ,cAAA;IACR,QAAA;EAAA,IACE,OAAA,CAAQ,MAAA,SAAe,mBAAA;EAlGZ;;;;;;EAyKf,cAAA,CAAe,OAAA,EAAS,CAAA,eAAgB,CAAA;AAAA;AAAA,iBAU1B,sBAAA,CACd,CAAA,EAAG,cAAA,EACH,CAAA,EAAG,cAAA;AAAA,iBASW,iBAAA,CAAA,GACX,QAAA,EAAU,cAAA,KACZ,cAAA;;;;;;;;cAcU,cAAA,EAAgB,MAAA;;;;;AAtM7B;;;;;cAsNa,sBAAA,EAAwB,WAAA;AAAA,iBAYrB,eAAA,CAAgB,MAAA,EAAQ,cAAA"}
|
package/dist/base.js
CHANGED
|
@@ -25,9 +25,9 @@ function copyCheckpoint(checkpoint) {
|
|
|
25
25
|
v: checkpoint.v,
|
|
26
26
|
id: checkpoint.id,
|
|
27
27
|
ts: checkpoint.ts,
|
|
28
|
-
channel_values: { ...checkpoint.channel_values },
|
|
29
|
-
channel_versions: { ...checkpoint.channel_versions },
|
|
30
|
-
versions_seen: deepCopy(checkpoint.versions_seen)
|
|
28
|
+
channel_values: { ...checkpoint.channel_values ?? {} },
|
|
29
|
+
channel_versions: { ...checkpoint.channel_versions ?? {} },
|
|
30
|
+
versions_seen: deepCopy(checkpoint.versions_seen ?? {})
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
var BaseCheckpointSaver = class {
|
|
@@ -35,6 +35,13 @@ var BaseCheckpointSaver = class {
|
|
|
35
35
|
constructor(serde) {
|
|
36
36
|
this.serde = serde || this.serde;
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Prevent `JSON.stringify` from traversing backend clients (e.g. pg Pool
|
|
40
|
+
* timers) when a checkpointer is present in runnable `configurable`.
|
|
41
|
+
*/
|
|
42
|
+
toJSON() {
|
|
43
|
+
return `[${this.constructor.name}]`;
|
|
44
|
+
}
|
|
38
45
|
async get(config) {
|
|
39
46
|
const value = await this.getTuple(config);
|
|
40
47
|
return value ? value.checkpoint : void 0;
|
package/dist/base.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","names":[],"sources":["../src/base.ts"],"sourcesContent":["import type { RunnableConfig } from \"@langchain/core/runnables\";\nimport { SerializerProtocol } from \"./serde/base.js\";\nimport { uuid6 } from \"./id.js\";\nimport type {\n PendingWrite,\n CheckpointPendingWrite,\n CheckpointMetadata,\n DeltaChannelHistory,\n} from \"./types.js\";\nimport { ERROR, INTERRUPT, RESUME, SCHEDULED } from \"./serde/types.js\";\nimport { JsonPlusSerializer } from \"./serde/jsonplus.js\";\n\n/** @inline */\ntype ChannelVersion = number | string;\n\nexport type ChannelVersions = Record<string, ChannelVersion>;\n\nexport interface Checkpoint<\n N extends string = string,\n C extends string = string,\n> {\n /**\n * The version of the checkpoint format. Currently 4\n */\n v: number;\n /**\n * Checkpoint ID {uuid6}\n */\n id: string;\n /**\n * Timestamp {new Date().toISOString()}\n */\n ts: string;\n /**\n * @default {}\n */\n channel_values: Record<C, unknown>;\n /**\n * @default {}\n */\n channel_versions: Record<C, ChannelVersion>;\n /**\n * @default {}\n */\n versions_seen: Record<N, Record<C, ChannelVersion>>;\n}\n\nexport interface ReadonlyCheckpoint extends Readonly<Checkpoint> {\n readonly channel_values: Readonly<Record<string, unknown>>;\n readonly channel_versions: Readonly<Record<string, ChannelVersion>>;\n readonly versions_seen: Readonly<\n Record<string, Readonly<Record<string, ChannelVersion>>>\n >;\n}\n\nexport function deepCopy<T>(obj: T): T {\n if (typeof obj !== \"object\" || obj === null) {\n return obj;\n }\n\n const newObj = Array.isArray(obj) ? [] : {};\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n (newObj as Record<PropertyKey, unknown>)[key] = deepCopy(\n (obj as Record<string, unknown>)[key]\n );\n }\n }\n\n return newObj as T;\n}\n\n/** @hidden */\nexport function emptyCheckpoint(): Checkpoint {\n return {\n v: 4,\n id: uuid6(0),\n ts: new Date().toISOString(),\n channel_values: {},\n channel_versions: {},\n versions_seen: {},\n };\n}\n\n/** @hidden */\nexport function copyCheckpoint(checkpoint: ReadonlyCheckpoint): Checkpoint {\n return {\n v: checkpoint.v,\n id: checkpoint.id,\n ts: checkpoint.ts,\n channel_values: { ...checkpoint.channel_values },\n channel_versions: { ...checkpoint.channel_versions },\n versions_seen: deepCopy(checkpoint.versions_seen),\n };\n}\n\nexport interface CheckpointTuple {\n config: RunnableConfig;\n checkpoint: Checkpoint;\n metadata?: CheckpointMetadata;\n parentConfig?: RunnableConfig;\n pendingWrites?: CheckpointPendingWrite[];\n}\n\nexport type CheckpointListOptions = {\n limit?: number;\n before?: RunnableConfig;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n filter?: Record<string, any>;\n};\n\nexport abstract class BaseCheckpointSaver<V extends string | number = number> {\n serde: SerializerProtocol = new JsonPlusSerializer();\n\n constructor(serde?: SerializerProtocol) {\n this.serde = serde || this.serde;\n }\n\n async get(config: RunnableConfig): Promise<Checkpoint | undefined> {\n const value = await this.getTuple(config);\n return value ? value.checkpoint : undefined;\n }\n\n abstract getTuple(\n config: RunnableConfig\n ): Promise<CheckpointTuple | undefined>;\n\n abstract list(\n config: RunnableConfig,\n options?: CheckpointListOptions\n ): AsyncGenerator<CheckpointTuple>;\n\n abstract put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata,\n newVersions: ChannelVersions\n ): Promise<RunnableConfig>;\n\n /**\n * Store intermediate writes linked to a checkpoint.\n */\n abstract putWrites(\n config: RunnableConfig,\n writes: PendingWrite[],\n taskId: string\n ): Promise<void>;\n\n /**\n * Delete all checkpoints and writes associated with a specific thread ID.\n * @param threadId The thread ID whose checkpoints should be deleted.\n */\n abstract deleteThread(threadId: string): Promise<void>;\n\n /**\n * Walk the parent chain returning per-channel writes + seed, used to\n * reconstruct `DeltaChannel` state from `checkpoint_writes`.\n *\n * For each requested channel, walks ancestors of the checkpoint identified\n * by `config` (following `parentConfig`) and accumulates the pending writes\n * for that channel. The walk terminates per-channel at the nearest ancestor\n * whose `channel_values[ch]` is populated; that value is returned as `seed`.\n * If the walk reaches the root without finding a stored value, `seed` is\n * omitted from that channel's entry — the consumer treats the absence as\n * \"start empty\".\n *\n * Walks the parent chain (not `list({ before })`): for forked threads, only\n * on-path ancestors contribute.\n *\n * The default implementation walks `getTuple` + `parentConfig` once for all\n * channels — each ancestor visited once, not once per channel. Savers with\n * direct storage access (e.g. `MemorySaver`) override for performance; the\n * return contract is fixed here.\n *\n * @remarks Beta. The signature, return shape, and interaction with\n * `DeltaSnapshot` blobs may change. Override at your own risk; the default\n * implementation will continue to work against the public\n * `BaseCheckpointSaver` contract.\n *\n * @param options.config Configuration identifying the target checkpoint.\n * @param options.channels Channel names to walk for. Empty → empty mapping.\n * @returns Per-channel {@link DeltaChannelHistory} for every requested name.\n */\n async getDeltaChannelHistory(options: {\n config: RunnableConfig;\n channels: string[];\n }): Promise<Record<string, DeltaChannelHistory>> {\n const { config, channels } = options;\n if (channels.length === 0) return {};\n\n const collectedByCh: Record<string, CheckpointPendingWrite[]> = {};\n const seedByCh: Record<string, unknown> = {};\n const remaining = new Set(channels);\n for (const ch of channels) collectedByCh[ch] = [];\n\n const targetTuple = await this.getTuple(config);\n let cursorConfig: RunnableConfig | undefined = targetTuple?.parentConfig;\n\n while (cursorConfig != null && remaining.size > 0) {\n const tup: CheckpointTuple | undefined =\n await this.getTuple(cursorConfig);\n if (tup === undefined) break;\n if (tup.pendingWrites && tup.pendingWrites.length > 0) {\n // DeltaChannel reconstruction must replay concurrent same-superstep\n // writes in the canonical (task_id, idx) order that live execution uses\n // (see `_applyWrites`), or the reconstructed value can diverge from the\n // live one. Group per channel and stable-sort by task id: a stable sort\n // keeps each task's writes in their stored `idx` order, making the\n // result independent of how a saver returns `pendingWrites`.\n const perChannel: Record<string, CheckpointPendingWrite[]> = {};\n for (const write of tup.pendingWrites) {\n const ch = write[1];\n if (remaining.has(ch)) (perChannel[ch] ??= []).push(write);\n }\n for (const ch of Object.keys(perChannel)) {\n const block = perChannel[ch];\n block.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));\n // Pushed reversed so the final `.reverse()` below yields oldest→newest\n // checkpoints with each checkpoint's writes ascending by (task_id, idx).\n for (let i = block.length - 1; i >= 0; i -= 1) {\n collectedByCh[ch].push(block[i]);\n }\n }\n }\n for (const ch of Array.from(remaining)) {\n if (\n Object.prototype.hasOwnProperty.call(\n tup.checkpoint.channel_values,\n ch\n )\n ) {\n seedByCh[ch] = tup.checkpoint.channel_values[ch];\n remaining.delete(ch);\n }\n }\n cursorConfig = tup.parentConfig;\n }\n\n const result: Record<string, DeltaChannelHistory> = {};\n for (const ch of channels) {\n const entry: DeltaChannelHistory = {\n writes: collectedByCh[ch].slice().reverse(),\n };\n if (Object.prototype.hasOwnProperty.call(seedByCh, ch)) {\n entry.seed = seedByCh[ch];\n }\n result[ch] = entry;\n }\n return result;\n }\n\n /**\n * Generate the next version ID for a channel.\n *\n * Default is to use integer versions, incrementing by 1. If you override, you can use str/int/float versions,\n * as long as they are monotonically increasing.\n */\n getNextVersion(current: V | undefined): V {\n if (typeof current === \"string\") {\n throw new Error(\"Please override this method to use string versions.\");\n }\n return (\n current !== undefined && typeof current === \"number\" ? current + 1 : 1\n ) as V;\n }\n}\n\nexport function compareChannelVersions(\n a: ChannelVersion,\n b: ChannelVersion\n): number {\n if (typeof a === \"number\" && typeof b === \"number\") {\n return Math.sign(a - b);\n }\n\n return String(a).localeCompare(String(b));\n}\n\nexport function maxChannelVersion(\n ...versions: ChannelVersion[]\n): ChannelVersion {\n return versions.reduce((max, version, idx) => {\n if (idx === 0) return version;\n return compareChannelVersions(max, version) >= 0 ? max : version;\n });\n}\n\n/**\n * Mapping from error type to error index.\n * Regular writes just map to their index in the list of writes being saved.\n * Special writes (e.g. errors) map to negative indices, to avoid those writes from\n * conflicting with regular writes.\n * Each Checkpointer implementation should use this mapping in put_writes.\n */\nexport const WRITES_IDX_MAP: Record<string, number> = {\n [ERROR]: -1,\n [SCHEDULED]: -2,\n [INTERRUPT]: -3,\n [RESUME]: -4,\n};\n\n/**\n * Metadata keys that are LangGraph's internal framework bookkeeping and\n * should not be surfaced as user-meaningful metadata.\n *\n * Consumed by stream handlers (e.g. the `tasks` debug stream) to drop\n * framework keys — which are redundant with a task's own fields and\n * namespace — while keeping keys like `lc_agent_name`, `ls_integration`,\n * and user-supplied metadata.\n */\nexport const EXCLUDED_METADATA_KEYS: ReadonlySet<string> = new Set([\n \"thread_id\",\n \"checkpoint_id\",\n \"checkpoint_ns\",\n \"checkpoint_map\",\n \"langgraph_step\",\n \"langgraph_node\",\n \"langgraph_triggers\",\n \"langgraph_path\",\n \"langgraph_checkpoint_ns\",\n]);\n\nexport function getCheckpointId(config: RunnableConfig): string {\n return (\n config.configurable?.checkpoint_id || config.configurable?.thread_ts || \"\"\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"base.js","names":[],"sources":["../src/base.ts"],"sourcesContent":["import type { RunnableConfig } from \"@langchain/core/runnables\";\nimport { SerializerProtocol } from \"./serde/base.js\";\nimport { uuid6 } from \"./id.js\";\nimport type {\n PendingWrite,\n CheckpointPendingWrite,\n CheckpointMetadata,\n DeltaChannelHistory,\n} from \"./types.js\";\nimport { ERROR, INTERRUPT, RESUME, SCHEDULED } from \"./serde/types.js\";\nimport { JsonPlusSerializer } from \"./serde/jsonplus.js\";\n\n/** @inline */\ntype ChannelVersion = number | string;\n\nexport type ChannelVersions = Record<string, ChannelVersion>;\n\nexport interface Checkpoint<\n N extends string = string,\n C extends string = string,\n> {\n /**\n * The version of the checkpoint format. Currently 4\n */\n v: number;\n /**\n * Checkpoint ID {uuid6}\n */\n id: string;\n /**\n * Timestamp {new Date().toISOString()}\n */\n ts: string;\n /**\n * @default {}\n */\n channel_values: Record<C, unknown>;\n /**\n * @default {}\n */\n channel_versions: Record<C, ChannelVersion>;\n /**\n * @default {}\n */\n versions_seen: Record<N, Record<C, ChannelVersion>>;\n}\n\nexport interface ReadonlyCheckpoint extends Readonly<Checkpoint> {\n readonly channel_values: Readonly<Record<string, unknown>>;\n readonly channel_versions: Readonly<Record<string, ChannelVersion>>;\n readonly versions_seen: Readonly<\n Record<string, Readonly<Record<string, ChannelVersion>>>\n >;\n}\n\nexport function deepCopy<T>(obj: T): T {\n if (typeof obj !== \"object\" || obj === null) {\n return obj;\n }\n\n const newObj = Array.isArray(obj) ? [] : {};\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n (newObj as Record<PropertyKey, unknown>)[key] = deepCopy(\n (obj as Record<string, unknown>)[key]\n );\n }\n }\n\n return newObj as T;\n}\n\n/** @hidden */\nexport function emptyCheckpoint(): Checkpoint {\n return {\n v: 4,\n id: uuid6(0),\n ts: new Date().toISOString(),\n channel_values: {},\n channel_versions: {},\n versions_seen: {},\n };\n}\n\n/** @hidden */\nexport function copyCheckpoint(checkpoint: ReadonlyCheckpoint): Checkpoint {\n return {\n v: checkpoint.v,\n id: checkpoint.id,\n ts: checkpoint.ts,\n channel_values: { ...(checkpoint.channel_values ?? {}) },\n channel_versions: { ...(checkpoint.channel_versions ?? {}) },\n versions_seen: deepCopy(checkpoint.versions_seen ?? {}),\n };\n}\n\nexport interface CheckpointTuple {\n config: RunnableConfig;\n checkpoint: Checkpoint;\n metadata?: CheckpointMetadata;\n parentConfig?: RunnableConfig;\n pendingWrites?: CheckpointPendingWrite[];\n}\n\nexport type CheckpointListOptions = {\n limit?: number;\n before?: RunnableConfig;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n filter?: Record<string, any>;\n};\n\nexport abstract class BaseCheckpointSaver<V extends string | number = number> {\n serde: SerializerProtocol = new JsonPlusSerializer();\n\n constructor(serde?: SerializerProtocol) {\n this.serde = serde || this.serde;\n }\n\n /**\n * Prevent `JSON.stringify` from traversing backend clients (e.g. pg Pool\n * timers) when a checkpointer is present in runnable `configurable`.\n */\n toJSON(): string {\n return `[${this.constructor.name}]`;\n }\n\n async get(config: RunnableConfig): Promise<Checkpoint | undefined> {\n const value = await this.getTuple(config);\n return value ? value.checkpoint : undefined;\n }\n\n abstract getTuple(\n config: RunnableConfig\n ): Promise<CheckpointTuple | undefined>;\n\n abstract list(\n config: RunnableConfig,\n options?: CheckpointListOptions\n ): AsyncGenerator<CheckpointTuple>;\n\n abstract put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata,\n newVersions: ChannelVersions\n ): Promise<RunnableConfig>;\n\n /**\n * Store intermediate writes linked to a checkpoint.\n */\n abstract putWrites(\n config: RunnableConfig,\n writes: PendingWrite[],\n taskId: string\n ): Promise<void>;\n\n /**\n * Delete all checkpoints and writes associated with a specific thread ID.\n * @param threadId The thread ID whose checkpoints should be deleted.\n */\n abstract deleteThread(threadId: string): Promise<void>;\n\n /**\n * Walk the parent chain returning per-channel writes + seed, used to\n * reconstruct `DeltaChannel` state from `checkpoint_writes`.\n *\n * For each requested channel, walks ancestors of the checkpoint identified\n * by `config` (following `parentConfig`) and accumulates the pending writes\n * for that channel. The walk terminates per-channel at the nearest ancestor\n * whose `channel_values[ch]` is populated; that value is returned as `seed`.\n * If the walk reaches the root without finding a stored value, `seed` is\n * omitted from that channel's entry — the consumer treats the absence as\n * \"start empty\".\n *\n * Walks the parent chain (not `list({ before })`): for forked threads, only\n * on-path ancestors contribute.\n *\n * The default implementation walks `getTuple` + `parentConfig` once for all\n * channels — each ancestor visited once, not once per channel. Savers with\n * direct storage access (e.g. `MemorySaver`) override for performance; the\n * return contract is fixed here.\n *\n * @remarks Beta. The signature, return shape, and interaction with\n * `DeltaSnapshot` blobs may change. Override at your own risk; the default\n * implementation will continue to work against the public\n * `BaseCheckpointSaver` contract.\n *\n * @param options.config Configuration identifying the target checkpoint.\n * @param options.channels Channel names to walk for. Empty → empty mapping.\n * @returns Per-channel {@link DeltaChannelHistory} for every requested name.\n */\n async getDeltaChannelHistory(options: {\n config: RunnableConfig;\n channels: string[];\n }): Promise<Record<string, DeltaChannelHistory>> {\n const { config, channels } = options;\n if (channels.length === 0) return {};\n\n const collectedByCh: Record<string, CheckpointPendingWrite[]> = {};\n const seedByCh: Record<string, unknown> = {};\n const remaining = new Set(channels);\n for (const ch of channels) collectedByCh[ch] = [];\n\n const targetTuple = await this.getTuple(config);\n let cursorConfig: RunnableConfig | undefined = targetTuple?.parentConfig;\n\n while (cursorConfig != null && remaining.size > 0) {\n const tup: CheckpointTuple | undefined =\n await this.getTuple(cursorConfig);\n if (tup === undefined) break;\n if (tup.pendingWrites && tup.pendingWrites.length > 0) {\n // DeltaChannel reconstruction must replay concurrent same-superstep\n // writes in the canonical (task_id, idx) order that live execution uses\n // (see `_applyWrites`), or the reconstructed value can diverge from the\n // live one. Group per channel and stable-sort by task id: a stable sort\n // keeps each task's writes in their stored `idx` order, making the\n // result independent of how a saver returns `pendingWrites`.\n const perChannel: Record<string, CheckpointPendingWrite[]> = {};\n for (const write of tup.pendingWrites) {\n const ch = write[1];\n if (remaining.has(ch)) (perChannel[ch] ??= []).push(write);\n }\n for (const ch of Object.keys(perChannel)) {\n const block = perChannel[ch];\n block.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));\n // Pushed reversed so the final `.reverse()` below yields oldest→newest\n // checkpoints with each checkpoint's writes ascending by (task_id, idx).\n for (let i = block.length - 1; i >= 0; i -= 1) {\n collectedByCh[ch].push(block[i]);\n }\n }\n }\n for (const ch of Array.from(remaining)) {\n if (\n Object.prototype.hasOwnProperty.call(\n tup.checkpoint.channel_values,\n ch\n )\n ) {\n seedByCh[ch] = tup.checkpoint.channel_values[ch];\n remaining.delete(ch);\n }\n }\n cursorConfig = tup.parentConfig;\n }\n\n const result: Record<string, DeltaChannelHistory> = {};\n for (const ch of channels) {\n const entry: DeltaChannelHistory = {\n writes: collectedByCh[ch].slice().reverse(),\n };\n if (Object.prototype.hasOwnProperty.call(seedByCh, ch)) {\n entry.seed = seedByCh[ch];\n }\n result[ch] = entry;\n }\n return result;\n }\n\n /**\n * Generate the next version ID for a channel.\n *\n * Default is to use integer versions, incrementing by 1. If you override, you can use str/int/float versions,\n * as long as they are monotonically increasing.\n */\n getNextVersion(current: V | undefined): V {\n if (typeof current === \"string\") {\n throw new Error(\"Please override this method to use string versions.\");\n }\n return (\n current !== undefined && typeof current === \"number\" ? current + 1 : 1\n ) as V;\n }\n}\n\nexport function compareChannelVersions(\n a: ChannelVersion,\n b: ChannelVersion\n): number {\n if (typeof a === \"number\" && typeof b === \"number\") {\n return Math.sign(a - b);\n }\n\n return String(a).localeCompare(String(b));\n}\n\nexport function maxChannelVersion(\n ...versions: ChannelVersion[]\n): ChannelVersion {\n return versions.reduce((max, version, idx) => {\n if (idx === 0) return version;\n return compareChannelVersions(max, version) >= 0 ? max : version;\n });\n}\n\n/**\n * Mapping from error type to error index.\n * Regular writes just map to their index in the list of writes being saved.\n * Special writes (e.g. errors) map to negative indices, to avoid those writes from\n * conflicting with regular writes.\n * Each Checkpointer implementation should use this mapping in put_writes.\n */\nexport const WRITES_IDX_MAP: Record<string, number> = {\n [ERROR]: -1,\n [SCHEDULED]: -2,\n [INTERRUPT]: -3,\n [RESUME]: -4,\n};\n\n/**\n * Metadata keys that are LangGraph's internal framework bookkeeping and\n * should not be surfaced as user-meaningful metadata.\n *\n * Consumed by stream handlers (e.g. the `tasks` debug stream) to drop\n * framework keys — which are redundant with a task's own fields and\n * namespace — while keeping keys like `lc_agent_name`, `ls_integration`,\n * and user-supplied metadata.\n */\nexport const EXCLUDED_METADATA_KEYS: ReadonlySet<string> = new Set([\n \"thread_id\",\n \"checkpoint_id\",\n \"checkpoint_ns\",\n \"checkpoint_map\",\n \"langgraph_step\",\n \"langgraph_node\",\n \"langgraph_triggers\",\n \"langgraph_path\",\n \"langgraph_checkpoint_ns\",\n]);\n\nexport function getCheckpointId(config: RunnableConfig): string {\n return (\n config.configurable?.checkpoint_id || config.configurable?.thread_ts || \"\"\n );\n}\n"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -3,10 +3,10 @@ import { All, CheckpointMetadata, CheckpointPendingWrite, DeltaChannelHistory, P
|
|
|
3
3
|
import { BaseCheckpointSaver, ChannelVersions, Checkpoint, CheckpointListOptions, CheckpointTuple, EXCLUDED_METADATA_KEYS, ReadonlyCheckpoint, WRITES_IDX_MAP, compareChannelVersions, copyCheckpoint, deepCopy, emptyCheckpoint, getCheckpointId, maxChannelVersion } from "./base.cjs";
|
|
4
4
|
import { MemorySaver } from "./memory.cjs";
|
|
5
5
|
import { uuid5, uuid6 } from "./id.cjs";
|
|
6
|
-
import { ChannelProtocol, DeltaSnapshot, ERROR, INTERRUPT, RESUME, SCHEDULED, SendProtocol, TASKS, isDeltaSnapshot } from "./serde/types.cjs";
|
|
6
|
+
import { ChannelProtocol, ConstructorRecord, DeltaSnapshot, ERROR, INTERRUPT, RESUME, SCHEDULED, SendProtocol, TASKS, isDeltaSnapshot } from "./serde/types.cjs";
|
|
7
7
|
import { BaseStore, GetOperation, IndexConfig, InvalidNamespaceError, Item, ListNamespacesOperation, MatchCondition, NameSpacePath, NamespaceMatchType, Operation, OperationResults, PutOperation, SearchItem, SearchOperation, getTextAtPath, tokenizePath } from "./store/base.cjs";
|
|
8
8
|
import { AsyncBatchedStore } from "./store/batch.cjs";
|
|
9
9
|
import { InMemoryStore, MemoryStore } from "./store/memory.cjs";
|
|
10
10
|
import { BaseCache, CacheFullKey, CacheNamespace } from "./cache/base.cjs";
|
|
11
11
|
import { InMemoryCache } from "./cache/memory.cjs";
|
|
12
|
-
export { All, AsyncBatchedStore, BaseCache, BaseCheckpointSaver, BaseStore, CacheFullKey, CacheNamespace, ChannelProtocol, ChannelVersions, Checkpoint, CheckpointListOptions, CheckpointMetadata, CheckpointPendingWrite, CheckpointTuple, DeltaChannelHistory, DeltaSnapshot, ERROR, EXCLUDED_METADATA_KEYS, GetOperation, INTERRUPT, InMemoryCache, InMemoryStore, IndexConfig, InvalidNamespaceError, Item, ListNamespacesOperation, MatchCondition, MemorySaver, MemoryStore, NameSpacePath, NamespaceMatchType, Operation, OperationResults, PendingWrite, PendingWriteValue, PutOperation, RESUME, ReadonlyCheckpoint, SCHEDULED, SearchItem, SearchOperation, SendProtocol, SerializerProtocol, TASKS, WRITES_IDX_MAP, compareChannelVersions, copyCheckpoint, deepCopy, emptyCheckpoint, getCheckpointId, getTextAtPath, isDeltaSnapshot, maxChannelVersion, tokenizePath, uuid5, uuid6 };
|
|
12
|
+
export { All, AsyncBatchedStore, BaseCache, BaseCheckpointSaver, BaseStore, CacheFullKey, CacheNamespace, ChannelProtocol, ChannelVersions, Checkpoint, CheckpointListOptions, CheckpointMetadata, CheckpointPendingWrite, CheckpointTuple, ConstructorRecord, DeltaChannelHistory, DeltaSnapshot, ERROR, EXCLUDED_METADATA_KEYS, GetOperation, INTERRUPT, InMemoryCache, InMemoryStore, IndexConfig, InvalidNamespaceError, Item, ListNamespacesOperation, MatchCondition, MemorySaver, MemoryStore, NameSpacePath, NamespaceMatchType, Operation, OperationResults, PendingWrite, PendingWriteValue, PutOperation, RESUME, ReadonlyCheckpoint, SCHEDULED, SearchItem, SearchOperation, SendProtocol, SerializerProtocol, TASKS, WRITES_IDX_MAP, compareChannelVersions, copyCheckpoint, deepCopy, emptyCheckpoint, getCheckpointId, getTextAtPath, isDeltaSnapshot, maxChannelVersion, tokenizePath, uuid5, uuid6 };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,10 +3,10 @@ import { All, CheckpointMetadata, CheckpointPendingWrite, DeltaChannelHistory, P
|
|
|
3
3
|
import { BaseCheckpointSaver, ChannelVersions, Checkpoint, CheckpointListOptions, CheckpointTuple, EXCLUDED_METADATA_KEYS, ReadonlyCheckpoint, WRITES_IDX_MAP, compareChannelVersions, copyCheckpoint, deepCopy, emptyCheckpoint, getCheckpointId, maxChannelVersion } from "./base.js";
|
|
4
4
|
import { MemorySaver } from "./memory.js";
|
|
5
5
|
import { uuid5, uuid6 } from "./id.js";
|
|
6
|
-
import { ChannelProtocol, DeltaSnapshot, ERROR, INTERRUPT, RESUME, SCHEDULED, SendProtocol, TASKS, isDeltaSnapshot } from "./serde/types.js";
|
|
6
|
+
import { ChannelProtocol, ConstructorRecord, DeltaSnapshot, ERROR, INTERRUPT, RESUME, SCHEDULED, SendProtocol, TASKS, isDeltaSnapshot } from "./serde/types.js";
|
|
7
7
|
import { BaseStore, GetOperation, IndexConfig, InvalidNamespaceError, Item, ListNamespacesOperation, MatchCondition, NameSpacePath, NamespaceMatchType, Operation, OperationResults, PutOperation, SearchItem, SearchOperation, getTextAtPath, tokenizePath } from "./store/base.js";
|
|
8
8
|
import { AsyncBatchedStore } from "./store/batch.js";
|
|
9
9
|
import { InMemoryStore, MemoryStore } from "./store/memory.js";
|
|
10
10
|
import { BaseCache, CacheFullKey, CacheNamespace } from "./cache/base.js";
|
|
11
11
|
import { InMemoryCache } from "./cache/memory.js";
|
|
12
|
-
export { All, AsyncBatchedStore, BaseCache, BaseCheckpointSaver, BaseStore, CacheFullKey, CacheNamespace, ChannelProtocol, ChannelVersions, Checkpoint, CheckpointListOptions, CheckpointMetadata, CheckpointPendingWrite, CheckpointTuple, DeltaChannelHistory, DeltaSnapshot, ERROR, EXCLUDED_METADATA_KEYS, GetOperation, INTERRUPT, InMemoryCache, InMemoryStore, IndexConfig, InvalidNamespaceError, Item, ListNamespacesOperation, MatchCondition, MemorySaver, MemoryStore, NameSpacePath, NamespaceMatchType, Operation, OperationResults, PendingWrite, PendingWriteValue, PutOperation, RESUME, ReadonlyCheckpoint, SCHEDULED, SearchItem, SearchOperation, SendProtocol, SerializerProtocol, TASKS, WRITES_IDX_MAP, compareChannelVersions, copyCheckpoint, deepCopy, emptyCheckpoint, getCheckpointId, getTextAtPath, isDeltaSnapshot, maxChannelVersion, tokenizePath, uuid5, uuid6 };
|
|
12
|
+
export { All, AsyncBatchedStore, BaseCache, BaseCheckpointSaver, BaseStore, CacheFullKey, CacheNamespace, ChannelProtocol, ChannelVersions, Checkpoint, CheckpointListOptions, CheckpointMetadata, CheckpointPendingWrite, CheckpointTuple, ConstructorRecord, DeltaChannelHistory, DeltaSnapshot, ERROR, EXCLUDED_METADATA_KEYS, GetOperation, INTERRUPT, InMemoryCache, InMemoryStore, IndexConfig, InvalidNamespaceError, Item, ListNamespacesOperation, MatchCondition, MemorySaver, MemoryStore, NameSpacePath, NamespaceMatchType, Operation, OperationResults, PendingWrite, PendingWriteValue, PutOperation, RESUME, ReadonlyCheckpoint, SCHEDULED, SearchItem, SearchOperation, SendProtocol, SerializerProtocol, TASKS, WRITES_IDX_MAP, compareChannelVersions, copyCheckpoint, deepCopy, emptyCheckpoint, getCheckpointId, getTextAtPath, isDeltaSnapshot, maxChannelVersion, tokenizePath, uuid5, uuid6 };
|
package/dist/serde/jsonplus.cjs
CHANGED
|
@@ -5,6 +5,60 @@ let _langchain_core_load = require("@langchain/core/load");
|
|
|
5
5
|
function isLangChainSerializedObject(value) {
|
|
6
6
|
return value !== null && value.lc === 1 && value.type === "constructor" && Array.isArray(value.id);
|
|
7
7
|
}
|
|
8
|
+
function isUndefinedRecord(value) {
|
|
9
|
+
return value.lc === 2 && value.type === "undefined";
|
|
10
|
+
}
|
|
11
|
+
function isDeltaSnapshotRecord(value) {
|
|
12
|
+
return value.lc === 2 && value.type === "delta_snapshot" && Object.prototype.hasOwnProperty.call(value, "value");
|
|
13
|
+
}
|
|
14
|
+
function isConstructorRecord(value) {
|
|
15
|
+
return value.lc === 2 && value.type === "constructor";
|
|
16
|
+
}
|
|
17
|
+
function hasConstructorId(record, name) {
|
|
18
|
+
return Array.isArray(record.id) && record.id.length === 1 && record.id[0] === name;
|
|
19
|
+
}
|
|
20
|
+
function hasNoMethod(record) {
|
|
21
|
+
return record.method === void 0 || record.method === null;
|
|
22
|
+
}
|
|
23
|
+
function hasSingleArrayArg(record) {
|
|
24
|
+
return Array.isArray(record.args) && record.args.length === 1 && Array.isArray(record.args[0]);
|
|
25
|
+
}
|
|
26
|
+
function isByteArray(value) {
|
|
27
|
+
return value.every((item) => typeof item === "number" && Number.isInteger(item) && item >= 0 && item <= 255);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Reconstruct only the closed set of lc:2 values written by `_default`.
|
|
31
|
+
*
|
|
32
|
+
* A constructor record is serialized data, not an instruction to resolve a
|
|
33
|
+
* property or invoke a method. Invalid and unsupported records are kept inert
|
|
34
|
+
* by the caller so old or attacker-controlled checkpoint data cannot execute.
|
|
35
|
+
*/
|
|
36
|
+
function reviveConstructorRecord(record) {
|
|
37
|
+
if (hasConstructorId(record, "Set") && hasNoMethod(record)) {
|
|
38
|
+
if (!hasSingleArrayArg(record)) return void 0;
|
|
39
|
+
return new Set(record.args[0]);
|
|
40
|
+
}
|
|
41
|
+
if (hasConstructorId(record, "Map") && hasNoMethod(record)) {
|
|
42
|
+
if (!hasSingleArrayArg(record) || !record.args[0].every((entry) => Array.isArray(entry) && entry.length === 2)) return;
|
|
43
|
+
return new Map(record.args[0]);
|
|
44
|
+
}
|
|
45
|
+
if (hasConstructorId(record, "RegExp") && hasNoMethod(record)) {
|
|
46
|
+
if (!Array.isArray(record.args) || record.args.length !== 2 || typeof record.args[0] !== "string" || typeof record.args[1] !== "string") return;
|
|
47
|
+
try {
|
|
48
|
+
return new RegExp(record.args[0], record.args[1]);
|
|
49
|
+
} catch {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (hasConstructorId(record, "Error") && hasNoMethod(record)) {
|
|
54
|
+
if (!Array.isArray(record.args) || record.args.length !== 1 || typeof record.args[0] !== "string") return;
|
|
55
|
+
return new Error(record.args[0]);
|
|
56
|
+
}
|
|
57
|
+
if (hasConstructorId(record, "Uint8Array") && (hasNoMethod(record) || record.method === "from")) {
|
|
58
|
+
if (!hasSingleArrayArg(record) || !isByteArray(record.args[0])) return;
|
|
59
|
+
return new Uint8Array(record.args[0]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
8
62
|
/**
|
|
9
63
|
* The replacer in stringify does not allow delegation to built-in LangChain
|
|
10
64
|
* serialization methods, and instead immediately calls `.toJSON()` and
|
|
@@ -18,34 +72,9 @@ async function _reviver(value) {
|
|
|
18
72
|
else {
|
|
19
73
|
const revivedObj = {};
|
|
20
74
|
for (const [k, v] of Object.entries(value)) revivedObj[k] = await _reviver(v);
|
|
21
|
-
if (revivedObj
|
|
22
|
-
else if (revivedObj
|
|
23
|
-
else if (revivedObj
|
|
24
|
-
const constructorName = revivedObj.id[revivedObj.id.length - 1];
|
|
25
|
-
let constructor;
|
|
26
|
-
switch (constructorName) {
|
|
27
|
-
case "Set":
|
|
28
|
-
constructor = Set;
|
|
29
|
-
break;
|
|
30
|
-
case "Map":
|
|
31
|
-
constructor = Map;
|
|
32
|
-
break;
|
|
33
|
-
case "RegExp":
|
|
34
|
-
constructor = RegExp;
|
|
35
|
-
break;
|
|
36
|
-
case "Error":
|
|
37
|
-
constructor = Error;
|
|
38
|
-
break;
|
|
39
|
-
case "Uint8Array":
|
|
40
|
-
constructor = Uint8Array;
|
|
41
|
-
break;
|
|
42
|
-
default: return revivedObj;
|
|
43
|
-
}
|
|
44
|
-
if (revivedObj.method) return constructor[revivedObj.method](...revivedObj.args || []);
|
|
45
|
-
else return new constructor(...revivedObj.args || []);
|
|
46
|
-
} catch {
|
|
47
|
-
return revivedObj;
|
|
48
|
-
}
|
|
75
|
+
if (isUndefinedRecord(revivedObj)) return;
|
|
76
|
+
else if (isDeltaSnapshotRecord(revivedObj)) return new require_types.DeltaSnapshot(revivedObj.value);
|
|
77
|
+
else if (isConstructorRecord(revivedObj)) return reviveConstructorRecord(revivedObj) ?? revivedObj;
|
|
49
78
|
else if (isLangChainSerializedObject(revivedObj)) return (0, _langchain_core_load.load)(JSON.stringify(revivedObj));
|
|
50
79
|
return revivedObj;
|
|
51
80
|
}
|
|
@@ -73,7 +102,7 @@ function _default(obj) {
|
|
|
73
102
|
};
|
|
74
103
|
else if (obj instanceof Set || obj instanceof Map) return _encodeConstructorArgs(obj.constructor, void 0, [Array.from(obj)]);
|
|
75
104
|
else if (obj instanceof RegExp) return _encodeConstructorArgs(RegExp, void 0, [obj.source, obj.flags]);
|
|
76
|
-
else if (obj instanceof Error) return _encodeConstructorArgs(
|
|
105
|
+
else if (obj instanceof Error) return _encodeConstructorArgs(Error, void 0, [obj.message]);
|
|
77
106
|
else if (obj?.lg_name === "Send") return {
|
|
78
107
|
node: obj.node,
|
|
79
108
|
args: obj.args,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsonplus.cjs","names":["DeltaSnapshot","stringify"],"sources":["../../src/serde/jsonplus.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable no-instanceof/no-instanceof */\nimport { load } from \"@langchain/core/load\";\nimport { SerializerProtocol } from \"./base.js\";\nimport { stringify } from \"./utils/fast-safe-stringify/index.js\";\nimport { DeltaSnapshot } from \"./types.js\";\n\nfunction isLangChainSerializedObject(value: Record<string, unknown>) {\n return (\n value !== null &&\n value.lc === 1 &&\n value.type === \"constructor\" &&\n Array.isArray(value.id)\n );\n}\n\n/**\n * The replacer in stringify does not allow delegation to built-in LangChain\n * serialization methods, and instead immediately calls `.toJSON()` and\n * continues to stringify subfields.\n *\n * We therefore must start from the most nested elements in the input and\n * deserialize upwards rather than top-down.\n */\nasync function _reviver(value: any): Promise<any> {\n if (value && typeof value === \"object\") {\n if (Array.isArray(value)) {\n const revivedArray = await Promise.all(\n value.map((item) => _reviver(item))\n );\n return revivedArray;\n } else {\n const revivedObj: any = {};\n for (const [k, v] of Object.entries(value)) {\n revivedObj[k] = await _reviver(v);\n }\n\n if (revivedObj.lc === 2 && revivedObj.type === \"undefined\") {\n return undefined;\n } else if (revivedObj.lc === 2 && revivedObj.type === \"delta_snapshot\") {\n // Wrapped value is already revived (bottom-up traversal).\n return new DeltaSnapshot(revivedObj.value);\n } else if (\n revivedObj.lc === 2 &&\n revivedObj.type === \"constructor\" &&\n Array.isArray(revivedObj.id)\n ) {\n try {\n const constructorName = revivedObj.id[revivedObj.id.length - 1];\n let constructor: any;\n\n switch (constructorName) {\n case \"Set\":\n constructor = Set;\n break;\n case \"Map\":\n constructor = Map;\n break;\n case \"RegExp\":\n constructor = RegExp;\n break;\n case \"Error\":\n constructor = Error;\n break;\n case \"Uint8Array\":\n constructor = Uint8Array;\n break;\n default:\n return revivedObj;\n }\n if (revivedObj.method) {\n return (constructor as any)[revivedObj.method](\n ...(revivedObj.args || [])\n );\n } else {\n return new (constructor as any)(...(revivedObj.args || []));\n }\n } catch {\n return revivedObj;\n }\n } else if (isLangChainSerializedObject(revivedObj)) {\n return load(JSON.stringify(revivedObj));\n }\n\n return revivedObj;\n }\n }\n return value;\n}\n\nfunction _encodeConstructorArgs(\n // oxlint-disable-next-line @typescript-eslint/no-unsafe-function-type\n constructor: Function,\n method?: string,\n args?: any[],\n kwargs?: Record<string, any>\n): object {\n return {\n lc: 2,\n type: \"constructor\",\n id: [constructor.name],\n method: method ?? null,\n args: args ?? [],\n kwargs: kwargs ?? {},\n };\n}\n\nfunction _default(obj: any): any {\n if (obj === undefined) {\n return {\n lc: 2,\n type: \"undefined\",\n };\n } else if (obj instanceof DeltaSnapshot) {\n return {\n lc: 2,\n type: \"delta_snapshot\",\n // `value` continues to be walked by `stringify`, so nested\n // serializable types (messages, Maps, etc.) are encoded normally.\n value: obj.value,\n };\n } else if (obj instanceof Set || obj instanceof Map) {\n return _encodeConstructorArgs(obj.constructor, undefined, [\n Array.from(obj),\n ]);\n } else if (obj instanceof RegExp) {\n return _encodeConstructorArgs(RegExp, undefined, [obj.source, obj.flags]);\n } else if (obj instanceof Error) {\n return _encodeConstructorArgs(obj.constructor, undefined, [obj.message]);\n // TODO: Remove special case\n } else if (obj?.lg_name === \"Send\") {\n return {\n node: obj.node,\n args: obj.args,\n // preserve an optional per-task timeout policy across (de)serialization\n ...(obj.timeout !== undefined ? { timeout: obj.timeout } : {}),\n };\n } else if (obj instanceof Uint8Array) {\n return _encodeConstructorArgs(Uint8Array, \"from\", [Array.from(obj)]);\n } else {\n return obj;\n }\n}\n\nexport class JsonPlusSerializer implements SerializerProtocol {\n protected _dumps(obj: any): Uint8Array {\n const encoder = new TextEncoder();\n return encoder.encode(\n stringify(obj, (_: string, value: any) => {\n return _default(value);\n })\n );\n }\n\n async dumpsTyped(obj: any): Promise<[string, Uint8Array]> {\n if (obj instanceof Uint8Array) {\n return [\"bytes\", obj];\n } else {\n return [\"json\", this._dumps(obj)];\n }\n }\n\n protected async _loads(data: string): Promise<any> {\n const parsed = JSON.parse(data);\n return _reviver(parsed);\n }\n\n async loadsTyped(type: string, data: Uint8Array | string): Promise<any> {\n if (type === \"bytes\") {\n return typeof data === \"string\" ? new TextEncoder().encode(data) : data;\n } else if (type === \"json\") {\n return this._loads(\n typeof data === \"string\" ? data : new TextDecoder().decode(data)\n );\n } else {\n throw new Error(`Unknown serialization type: ${type}`);\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"jsonplus.cjs","names":["DeltaSnapshot","stringify"],"sources":["../../src/serde/jsonplus.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable no-instanceof/no-instanceof */\nimport { load } from \"@langchain/core/load\";\nimport { SerializerProtocol } from \"./base.js\";\nimport { stringify } from \"./utils/fast-safe-stringify/index.js\";\nimport { type ConstructorRecord, DeltaSnapshot } from \"./types.js\";\n\nfunction isLangChainSerializedObject(value: Record<string, unknown>) {\n return (\n value !== null &&\n value.lc === 1 &&\n value.type === \"constructor\" &&\n Array.isArray(value.id)\n );\n}\n\nfunction isUndefinedRecord(value: Record<string, unknown>): boolean {\n return value.lc === 2 && value.type === \"undefined\";\n}\n\nfunction isDeltaSnapshotRecord(value: Record<string, unknown>): boolean {\n return (\n value.lc === 2 &&\n value.type === \"delta_snapshot\" &&\n Object.prototype.hasOwnProperty.call(value, \"value\")\n );\n}\n\nfunction isConstructorRecord(\n value: Record<string, unknown>\n): value is ConstructorRecord {\n return value.lc === 2 && value.type === \"constructor\";\n}\n\nfunction hasConstructorId(record: ConstructorRecord, name: string): boolean {\n return (\n Array.isArray(record.id) && record.id.length === 1 && record.id[0] === name\n );\n}\n\nfunction hasNoMethod(record: ConstructorRecord): boolean {\n return record.method === undefined || record.method === null;\n}\n\nfunction hasSingleArrayArg(\n record: ConstructorRecord\n): record is ConstructorRecord & { args: unknown[][] } {\n return (\n Array.isArray(record.args) &&\n record.args.length === 1 &&\n Array.isArray(record.args[0])\n );\n}\n\nfunction isByteArray(value: unknown[]): value is number[] {\n return value.every(\n (item) =>\n typeof item === \"number\" &&\n Number.isInteger(item) &&\n item >= 0 &&\n item <= 255\n );\n}\n\n/**\n * Reconstruct only the closed set of lc:2 values written by `_default`.\n *\n * A constructor record is serialized data, not an instruction to resolve a\n * property or invoke a method. Invalid and unsupported records are kept inert\n * by the caller so old or attacker-controlled checkpoint data cannot execute.\n */\nfunction reviveConstructorRecord(\n record: ConstructorRecord\n): unknown | undefined {\n if (hasConstructorId(record, \"Set\") && hasNoMethod(record)) {\n if (!hasSingleArrayArg(record)) return undefined;\n return new Set(record.args[0]);\n }\n\n if (hasConstructorId(record, \"Map\") && hasNoMethod(record)) {\n if (\n !hasSingleArrayArg(record) ||\n !record.args[0].every(\n (entry) => Array.isArray(entry) && entry.length === 2\n )\n ) {\n return undefined;\n }\n return new Map(record.args[0] as [unknown, unknown][]);\n }\n\n if (hasConstructorId(record, \"RegExp\") && hasNoMethod(record)) {\n if (\n !Array.isArray(record.args) ||\n record.args.length !== 2 ||\n typeof record.args[0] !== \"string\" ||\n typeof record.args[1] !== \"string\"\n ) {\n return undefined;\n }\n try {\n return new RegExp(record.args[0], record.args[1]);\n } catch {\n // Invalid patterns are malformed persisted data and stay inert.\n return undefined;\n }\n }\n\n if (hasConstructorId(record, \"Error\") && hasNoMethod(record)) {\n if (\n !Array.isArray(record.args) ||\n record.args.length !== 1 ||\n typeof record.args[0] !== \"string\"\n ) {\n return undefined;\n }\n return new Error(record.args[0]);\n }\n\n if (\n hasConstructorId(record, \"Uint8Array\") &&\n (hasNoMethod(record) || record.method === \"from\")\n ) {\n if (!hasSingleArrayArg(record) || !isByteArray(record.args[0])) {\n return undefined;\n }\n // `from` is a legacy format tag. Do not forward its serialized method or\n // any extra arguments: the validated bytes are the entire persisted form.\n return new Uint8Array(record.args[0]);\n }\n\n return undefined;\n}\n\n/**\n * The replacer in stringify does not allow delegation to built-in LangChain\n * serialization methods, and instead immediately calls `.toJSON()` and\n * continues to stringify subfields.\n *\n * We therefore must start from the most nested elements in the input and\n * deserialize upwards rather than top-down.\n */\nasync function _reviver(value: any): Promise<any> {\n if (value && typeof value === \"object\") {\n if (Array.isArray(value)) {\n const revivedArray = await Promise.all(\n value.map((item) => _reviver(item))\n );\n return revivedArray;\n } else {\n const revivedObj: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n revivedObj[k] = await _reviver(v);\n }\n\n if (isUndefinedRecord(revivedObj)) {\n return undefined;\n } else if (isDeltaSnapshotRecord(revivedObj)) {\n // Wrapped value is already revived (bottom-up traversal).\n return new DeltaSnapshot(revivedObj.value);\n } else if (isConstructorRecord(revivedObj)) {\n return reviveConstructorRecord(revivedObj) ?? revivedObj;\n } else if (isLangChainSerializedObject(revivedObj)) {\n return load(JSON.stringify(revivedObj));\n }\n\n return revivedObj;\n }\n }\n return value;\n}\n\nfunction _encodeConstructorArgs(\n // oxlint-disable-next-line @typescript-eslint/no-unsafe-function-type\n constructor: Function,\n method?: string,\n args?: any[],\n kwargs?: Record<string, any>\n): object {\n return {\n lc: 2,\n type: \"constructor\",\n id: [constructor.name],\n method: method ?? null,\n args: args ?? [],\n kwargs: kwargs ?? {},\n };\n}\n\nfunction _default(obj: any): any {\n if (obj === undefined) {\n return {\n lc: 2,\n type: \"undefined\",\n };\n } else if (obj instanceof DeltaSnapshot) {\n return {\n lc: 2,\n type: \"delta_snapshot\",\n // `value` continues to be walked by `stringify`, so nested\n // serializable types (messages, Maps, etc.) are encoded normally.\n value: obj.value,\n };\n } else if (obj instanceof Set || obj instanceof Map) {\n return _encodeConstructorArgs(obj.constructor, undefined, [\n Array.from(obj),\n ]);\n } else if (obj instanceof RegExp) {\n return _encodeConstructorArgs(RegExp, undefined, [obj.source, obj.flags]);\n } else if (obj instanceof Error) {\n return _encodeConstructorArgs(Error, undefined, [obj.message]);\n // TODO: Remove special case\n } else if (obj?.lg_name === \"Send\") {\n return {\n node: obj.node,\n args: obj.args,\n // preserve an optional per-task timeout policy across (de)serialization\n ...(obj.timeout !== undefined ? { timeout: obj.timeout } : {}),\n };\n } else if (obj instanceof Uint8Array) {\n return _encodeConstructorArgs(Uint8Array, \"from\", [Array.from(obj)]);\n } else {\n return obj;\n }\n}\n\nexport class JsonPlusSerializer implements SerializerProtocol {\n protected _dumps(obj: any): Uint8Array {\n const encoder = new TextEncoder();\n return encoder.encode(\n stringify(obj, (_: string, value: any) => {\n return _default(value);\n })\n );\n }\n\n async dumpsTyped(obj: any): Promise<[string, Uint8Array]> {\n if (obj instanceof Uint8Array) {\n return [\"bytes\", obj];\n } else {\n return [\"json\", this._dumps(obj)];\n }\n }\n\n protected async _loads(data: string): Promise<any> {\n const parsed = JSON.parse(data);\n return _reviver(parsed);\n }\n\n async loadsTyped(type: string, data: Uint8Array | string): Promise<any> {\n if (type === \"bytes\") {\n return typeof data === \"string\" ? new TextEncoder().encode(data) : data;\n } else if (type === \"json\") {\n return this._loads(\n typeof data === \"string\" ? data : new TextDecoder().decode(data)\n );\n } else {\n throw new Error(`Unknown serialization type: ${type}`);\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/serde/jsonplus.js
CHANGED
|
@@ -5,6 +5,60 @@ import { load } from "@langchain/core/load";
|
|
|
5
5
|
function isLangChainSerializedObject(value) {
|
|
6
6
|
return value !== null && value.lc === 1 && value.type === "constructor" && Array.isArray(value.id);
|
|
7
7
|
}
|
|
8
|
+
function isUndefinedRecord(value) {
|
|
9
|
+
return value.lc === 2 && value.type === "undefined";
|
|
10
|
+
}
|
|
11
|
+
function isDeltaSnapshotRecord(value) {
|
|
12
|
+
return value.lc === 2 && value.type === "delta_snapshot" && Object.prototype.hasOwnProperty.call(value, "value");
|
|
13
|
+
}
|
|
14
|
+
function isConstructorRecord(value) {
|
|
15
|
+
return value.lc === 2 && value.type === "constructor";
|
|
16
|
+
}
|
|
17
|
+
function hasConstructorId(record, name) {
|
|
18
|
+
return Array.isArray(record.id) && record.id.length === 1 && record.id[0] === name;
|
|
19
|
+
}
|
|
20
|
+
function hasNoMethod(record) {
|
|
21
|
+
return record.method === void 0 || record.method === null;
|
|
22
|
+
}
|
|
23
|
+
function hasSingleArrayArg(record) {
|
|
24
|
+
return Array.isArray(record.args) && record.args.length === 1 && Array.isArray(record.args[0]);
|
|
25
|
+
}
|
|
26
|
+
function isByteArray(value) {
|
|
27
|
+
return value.every((item) => typeof item === "number" && Number.isInteger(item) && item >= 0 && item <= 255);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Reconstruct only the closed set of lc:2 values written by `_default`.
|
|
31
|
+
*
|
|
32
|
+
* A constructor record is serialized data, not an instruction to resolve a
|
|
33
|
+
* property or invoke a method. Invalid and unsupported records are kept inert
|
|
34
|
+
* by the caller so old or attacker-controlled checkpoint data cannot execute.
|
|
35
|
+
*/
|
|
36
|
+
function reviveConstructorRecord(record) {
|
|
37
|
+
if (hasConstructorId(record, "Set") && hasNoMethod(record)) {
|
|
38
|
+
if (!hasSingleArrayArg(record)) return void 0;
|
|
39
|
+
return new Set(record.args[0]);
|
|
40
|
+
}
|
|
41
|
+
if (hasConstructorId(record, "Map") && hasNoMethod(record)) {
|
|
42
|
+
if (!hasSingleArrayArg(record) || !record.args[0].every((entry) => Array.isArray(entry) && entry.length === 2)) return;
|
|
43
|
+
return new Map(record.args[0]);
|
|
44
|
+
}
|
|
45
|
+
if (hasConstructorId(record, "RegExp") && hasNoMethod(record)) {
|
|
46
|
+
if (!Array.isArray(record.args) || record.args.length !== 2 || typeof record.args[0] !== "string" || typeof record.args[1] !== "string") return;
|
|
47
|
+
try {
|
|
48
|
+
return new RegExp(record.args[0], record.args[1]);
|
|
49
|
+
} catch {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (hasConstructorId(record, "Error") && hasNoMethod(record)) {
|
|
54
|
+
if (!Array.isArray(record.args) || record.args.length !== 1 || typeof record.args[0] !== "string") return;
|
|
55
|
+
return new Error(record.args[0]);
|
|
56
|
+
}
|
|
57
|
+
if (hasConstructorId(record, "Uint8Array") && (hasNoMethod(record) || record.method === "from")) {
|
|
58
|
+
if (!hasSingleArrayArg(record) || !isByteArray(record.args[0])) return;
|
|
59
|
+
return new Uint8Array(record.args[0]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
8
62
|
/**
|
|
9
63
|
* The replacer in stringify does not allow delegation to built-in LangChain
|
|
10
64
|
* serialization methods, and instead immediately calls `.toJSON()` and
|
|
@@ -18,34 +72,9 @@ async function _reviver(value) {
|
|
|
18
72
|
else {
|
|
19
73
|
const revivedObj = {};
|
|
20
74
|
for (const [k, v] of Object.entries(value)) revivedObj[k] = await _reviver(v);
|
|
21
|
-
if (revivedObj
|
|
22
|
-
else if (revivedObj
|
|
23
|
-
else if (revivedObj
|
|
24
|
-
const constructorName = revivedObj.id[revivedObj.id.length - 1];
|
|
25
|
-
let constructor;
|
|
26
|
-
switch (constructorName) {
|
|
27
|
-
case "Set":
|
|
28
|
-
constructor = Set;
|
|
29
|
-
break;
|
|
30
|
-
case "Map":
|
|
31
|
-
constructor = Map;
|
|
32
|
-
break;
|
|
33
|
-
case "RegExp":
|
|
34
|
-
constructor = RegExp;
|
|
35
|
-
break;
|
|
36
|
-
case "Error":
|
|
37
|
-
constructor = Error;
|
|
38
|
-
break;
|
|
39
|
-
case "Uint8Array":
|
|
40
|
-
constructor = Uint8Array;
|
|
41
|
-
break;
|
|
42
|
-
default: return revivedObj;
|
|
43
|
-
}
|
|
44
|
-
if (revivedObj.method) return constructor[revivedObj.method](...revivedObj.args || []);
|
|
45
|
-
else return new constructor(...revivedObj.args || []);
|
|
46
|
-
} catch {
|
|
47
|
-
return revivedObj;
|
|
48
|
-
}
|
|
75
|
+
if (isUndefinedRecord(revivedObj)) return;
|
|
76
|
+
else if (isDeltaSnapshotRecord(revivedObj)) return new DeltaSnapshot(revivedObj.value);
|
|
77
|
+
else if (isConstructorRecord(revivedObj)) return reviveConstructorRecord(revivedObj) ?? revivedObj;
|
|
49
78
|
else if (isLangChainSerializedObject(revivedObj)) return load(JSON.stringify(revivedObj));
|
|
50
79
|
return revivedObj;
|
|
51
80
|
}
|
|
@@ -73,7 +102,7 @@ function _default(obj) {
|
|
|
73
102
|
};
|
|
74
103
|
else if (obj instanceof Set || obj instanceof Map) return _encodeConstructorArgs(obj.constructor, void 0, [Array.from(obj)]);
|
|
75
104
|
else if (obj instanceof RegExp) return _encodeConstructorArgs(RegExp, void 0, [obj.source, obj.flags]);
|
|
76
|
-
else if (obj instanceof Error) return _encodeConstructorArgs(
|
|
105
|
+
else if (obj instanceof Error) return _encodeConstructorArgs(Error, void 0, [obj.message]);
|
|
77
106
|
else if (obj?.lg_name === "Send") return {
|
|
78
107
|
node: obj.node,
|
|
79
108
|
args: obj.args,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsonplus.js","names":[],"sources":["../../src/serde/jsonplus.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable no-instanceof/no-instanceof */\nimport { load } from \"@langchain/core/load\";\nimport { SerializerProtocol } from \"./base.js\";\nimport { stringify } from \"./utils/fast-safe-stringify/index.js\";\nimport { DeltaSnapshot } from \"./types.js\";\n\nfunction isLangChainSerializedObject(value: Record<string, unknown>) {\n return (\n value !== null &&\n value.lc === 1 &&\n value.type === \"constructor\" &&\n Array.isArray(value.id)\n );\n}\n\n/**\n * The replacer in stringify does not allow delegation to built-in LangChain\n * serialization methods, and instead immediately calls `.toJSON()` and\n * continues to stringify subfields.\n *\n * We therefore must start from the most nested elements in the input and\n * deserialize upwards rather than top-down.\n */\nasync function _reviver(value: any): Promise<any> {\n if (value && typeof value === \"object\") {\n if (Array.isArray(value)) {\n const revivedArray = await Promise.all(\n value.map((item) => _reviver(item))\n );\n return revivedArray;\n } else {\n const revivedObj: any = {};\n for (const [k, v] of Object.entries(value)) {\n revivedObj[k] = await _reviver(v);\n }\n\n if (revivedObj.lc === 2 && revivedObj.type === \"undefined\") {\n return undefined;\n } else if (revivedObj.lc === 2 && revivedObj.type === \"delta_snapshot\") {\n // Wrapped value is already revived (bottom-up traversal).\n return new DeltaSnapshot(revivedObj.value);\n } else if (\n revivedObj.lc === 2 &&\n revivedObj.type === \"constructor\" &&\n Array.isArray(revivedObj.id)\n ) {\n try {\n const constructorName = revivedObj.id[revivedObj.id.length - 1];\n let constructor: any;\n\n switch (constructorName) {\n case \"Set\":\n constructor = Set;\n break;\n case \"Map\":\n constructor = Map;\n break;\n case \"RegExp\":\n constructor = RegExp;\n break;\n case \"Error\":\n constructor = Error;\n break;\n case \"Uint8Array\":\n constructor = Uint8Array;\n break;\n default:\n return revivedObj;\n }\n if (revivedObj.method) {\n return (constructor as any)[revivedObj.method](\n ...(revivedObj.args || [])\n );\n } else {\n return new (constructor as any)(...(revivedObj.args || []));\n }\n } catch {\n return revivedObj;\n }\n } else if (isLangChainSerializedObject(revivedObj)) {\n return load(JSON.stringify(revivedObj));\n }\n\n return revivedObj;\n }\n }\n return value;\n}\n\nfunction _encodeConstructorArgs(\n // oxlint-disable-next-line @typescript-eslint/no-unsafe-function-type\n constructor: Function,\n method?: string,\n args?: any[],\n kwargs?: Record<string, any>\n): object {\n return {\n lc: 2,\n type: \"constructor\",\n id: [constructor.name],\n method: method ?? null,\n args: args ?? [],\n kwargs: kwargs ?? {},\n };\n}\n\nfunction _default(obj: any): any {\n if (obj === undefined) {\n return {\n lc: 2,\n type: \"undefined\",\n };\n } else if (obj instanceof DeltaSnapshot) {\n return {\n lc: 2,\n type: \"delta_snapshot\",\n // `value` continues to be walked by `stringify`, so nested\n // serializable types (messages, Maps, etc.) are encoded normally.\n value: obj.value,\n };\n } else if (obj instanceof Set || obj instanceof Map) {\n return _encodeConstructorArgs(obj.constructor, undefined, [\n Array.from(obj),\n ]);\n } else if (obj instanceof RegExp) {\n return _encodeConstructorArgs(RegExp, undefined, [obj.source, obj.flags]);\n } else if (obj instanceof Error) {\n return _encodeConstructorArgs(obj.constructor, undefined, [obj.message]);\n // TODO: Remove special case\n } else if (obj?.lg_name === \"Send\") {\n return {\n node: obj.node,\n args: obj.args,\n // preserve an optional per-task timeout policy across (de)serialization\n ...(obj.timeout !== undefined ? { timeout: obj.timeout } : {}),\n };\n } else if (obj instanceof Uint8Array) {\n return _encodeConstructorArgs(Uint8Array, \"from\", [Array.from(obj)]);\n } else {\n return obj;\n }\n}\n\nexport class JsonPlusSerializer implements SerializerProtocol {\n protected _dumps(obj: any): Uint8Array {\n const encoder = new TextEncoder();\n return encoder.encode(\n stringify(obj, (_: string, value: any) => {\n return _default(value);\n })\n );\n }\n\n async dumpsTyped(obj: any): Promise<[string, Uint8Array]> {\n if (obj instanceof Uint8Array) {\n return [\"bytes\", obj];\n } else {\n return [\"json\", this._dumps(obj)];\n }\n }\n\n protected async _loads(data: string): Promise<any> {\n const parsed = JSON.parse(data);\n return _reviver(parsed);\n }\n\n async loadsTyped(type: string, data: Uint8Array | string): Promise<any> {\n if (type === \"bytes\") {\n return typeof data === \"string\" ? new TextEncoder().encode(data) : data;\n } else if (type === \"json\") {\n return this._loads(\n typeof data === \"string\" ? data : new TextDecoder().decode(data)\n );\n } else {\n throw new Error(`Unknown serialization type: ${type}`);\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"jsonplus.js","names":[],"sources":["../../src/serde/jsonplus.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable no-instanceof/no-instanceof */\nimport { load } from \"@langchain/core/load\";\nimport { SerializerProtocol } from \"./base.js\";\nimport { stringify } from \"./utils/fast-safe-stringify/index.js\";\nimport { type ConstructorRecord, DeltaSnapshot } from \"./types.js\";\n\nfunction isLangChainSerializedObject(value: Record<string, unknown>) {\n return (\n value !== null &&\n value.lc === 1 &&\n value.type === \"constructor\" &&\n Array.isArray(value.id)\n );\n}\n\nfunction isUndefinedRecord(value: Record<string, unknown>): boolean {\n return value.lc === 2 && value.type === \"undefined\";\n}\n\nfunction isDeltaSnapshotRecord(value: Record<string, unknown>): boolean {\n return (\n value.lc === 2 &&\n value.type === \"delta_snapshot\" &&\n Object.prototype.hasOwnProperty.call(value, \"value\")\n );\n}\n\nfunction isConstructorRecord(\n value: Record<string, unknown>\n): value is ConstructorRecord {\n return value.lc === 2 && value.type === \"constructor\";\n}\n\nfunction hasConstructorId(record: ConstructorRecord, name: string): boolean {\n return (\n Array.isArray(record.id) && record.id.length === 1 && record.id[0] === name\n );\n}\n\nfunction hasNoMethod(record: ConstructorRecord): boolean {\n return record.method === undefined || record.method === null;\n}\n\nfunction hasSingleArrayArg(\n record: ConstructorRecord\n): record is ConstructorRecord & { args: unknown[][] } {\n return (\n Array.isArray(record.args) &&\n record.args.length === 1 &&\n Array.isArray(record.args[0])\n );\n}\n\nfunction isByteArray(value: unknown[]): value is number[] {\n return value.every(\n (item) =>\n typeof item === \"number\" &&\n Number.isInteger(item) &&\n item >= 0 &&\n item <= 255\n );\n}\n\n/**\n * Reconstruct only the closed set of lc:2 values written by `_default`.\n *\n * A constructor record is serialized data, not an instruction to resolve a\n * property or invoke a method. Invalid and unsupported records are kept inert\n * by the caller so old or attacker-controlled checkpoint data cannot execute.\n */\nfunction reviveConstructorRecord(\n record: ConstructorRecord\n): unknown | undefined {\n if (hasConstructorId(record, \"Set\") && hasNoMethod(record)) {\n if (!hasSingleArrayArg(record)) return undefined;\n return new Set(record.args[0]);\n }\n\n if (hasConstructorId(record, \"Map\") && hasNoMethod(record)) {\n if (\n !hasSingleArrayArg(record) ||\n !record.args[0].every(\n (entry) => Array.isArray(entry) && entry.length === 2\n )\n ) {\n return undefined;\n }\n return new Map(record.args[0] as [unknown, unknown][]);\n }\n\n if (hasConstructorId(record, \"RegExp\") && hasNoMethod(record)) {\n if (\n !Array.isArray(record.args) ||\n record.args.length !== 2 ||\n typeof record.args[0] !== \"string\" ||\n typeof record.args[1] !== \"string\"\n ) {\n return undefined;\n }\n try {\n return new RegExp(record.args[0], record.args[1]);\n } catch {\n // Invalid patterns are malformed persisted data and stay inert.\n return undefined;\n }\n }\n\n if (hasConstructorId(record, \"Error\") && hasNoMethod(record)) {\n if (\n !Array.isArray(record.args) ||\n record.args.length !== 1 ||\n typeof record.args[0] !== \"string\"\n ) {\n return undefined;\n }\n return new Error(record.args[0]);\n }\n\n if (\n hasConstructorId(record, \"Uint8Array\") &&\n (hasNoMethod(record) || record.method === \"from\")\n ) {\n if (!hasSingleArrayArg(record) || !isByteArray(record.args[0])) {\n return undefined;\n }\n // `from` is a legacy format tag. Do not forward its serialized method or\n // any extra arguments: the validated bytes are the entire persisted form.\n return new Uint8Array(record.args[0]);\n }\n\n return undefined;\n}\n\n/**\n * The replacer in stringify does not allow delegation to built-in LangChain\n * serialization methods, and instead immediately calls `.toJSON()` and\n * continues to stringify subfields.\n *\n * We therefore must start from the most nested elements in the input and\n * deserialize upwards rather than top-down.\n */\nasync function _reviver(value: any): Promise<any> {\n if (value && typeof value === \"object\") {\n if (Array.isArray(value)) {\n const revivedArray = await Promise.all(\n value.map((item) => _reviver(item))\n );\n return revivedArray;\n } else {\n const revivedObj: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n revivedObj[k] = await _reviver(v);\n }\n\n if (isUndefinedRecord(revivedObj)) {\n return undefined;\n } else if (isDeltaSnapshotRecord(revivedObj)) {\n // Wrapped value is already revived (bottom-up traversal).\n return new DeltaSnapshot(revivedObj.value);\n } else if (isConstructorRecord(revivedObj)) {\n return reviveConstructorRecord(revivedObj) ?? revivedObj;\n } else if (isLangChainSerializedObject(revivedObj)) {\n return load(JSON.stringify(revivedObj));\n }\n\n return revivedObj;\n }\n }\n return value;\n}\n\nfunction _encodeConstructorArgs(\n // oxlint-disable-next-line @typescript-eslint/no-unsafe-function-type\n constructor: Function,\n method?: string,\n args?: any[],\n kwargs?: Record<string, any>\n): object {\n return {\n lc: 2,\n type: \"constructor\",\n id: [constructor.name],\n method: method ?? null,\n args: args ?? [],\n kwargs: kwargs ?? {},\n };\n}\n\nfunction _default(obj: any): any {\n if (obj === undefined) {\n return {\n lc: 2,\n type: \"undefined\",\n };\n } else if (obj instanceof DeltaSnapshot) {\n return {\n lc: 2,\n type: \"delta_snapshot\",\n // `value` continues to be walked by `stringify`, so nested\n // serializable types (messages, Maps, etc.) are encoded normally.\n value: obj.value,\n };\n } else if (obj instanceof Set || obj instanceof Map) {\n return _encodeConstructorArgs(obj.constructor, undefined, [\n Array.from(obj),\n ]);\n } else if (obj instanceof RegExp) {\n return _encodeConstructorArgs(RegExp, undefined, [obj.source, obj.flags]);\n } else if (obj instanceof Error) {\n return _encodeConstructorArgs(Error, undefined, [obj.message]);\n // TODO: Remove special case\n } else if (obj?.lg_name === \"Send\") {\n return {\n node: obj.node,\n args: obj.args,\n // preserve an optional per-task timeout policy across (de)serialization\n ...(obj.timeout !== undefined ? { timeout: obj.timeout } : {}),\n };\n } else if (obj instanceof Uint8Array) {\n return _encodeConstructorArgs(Uint8Array, \"from\", [Array.from(obj)]);\n } else {\n return obj;\n }\n}\n\nexport class JsonPlusSerializer implements SerializerProtocol {\n protected _dumps(obj: any): Uint8Array {\n const encoder = new TextEncoder();\n return encoder.encode(\n stringify(obj, (_: string, value: any) => {\n return _default(value);\n })\n );\n }\n\n async dumpsTyped(obj: any): Promise<[string, Uint8Array]> {\n if (obj instanceof Uint8Array) {\n return [\"bytes\", obj];\n } else {\n return [\"json\", this._dumps(obj)];\n }\n }\n\n protected async _loads(data: string): Promise<any> {\n const parsed = JSON.parse(data);\n return _reviver(parsed);\n }\n\n async loadsTyped(type: string, data: Uint8Array | string): Promise<any> {\n if (type === \"bytes\") {\n return typeof data === \"string\" ? new TextEncoder().encode(data) : data;\n } else if (type === \"json\") {\n return this._loads(\n typeof data === \"string\" ? data : new TextDecoder().decode(data)\n );\n } else {\n throw new Error(`Unknown serialization type: ${type}`);\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/serde/types.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.cjs","names":[],"sources":["../../src/serde/types.ts"],"sourcesContent":["export const TASKS = \"__pregel_tasks\";\nexport const ERROR = \"__error__\";\nexport const SCHEDULED = \"__scheduled__\";\nexport const INTERRUPT = \"__interrupt__\";\nexport const RESUME = \"__resume__\";\n\n/**\n * Snapshot blob for a `DeltaChannel` with finite snapshot frequency.\n *\n * Stored directly in a checkpoint's `channel_values` in place of the full\n * accumulated value. The ancestor walk in\n * {@link BaseCheckpointSaver.getDeltaChannelHistory} terminates when it\n * encounters a populated `channel_values` entry for a channel; a\n * `DeltaSnapshot` value is the materialized state at that ancestor, so the\n * channel reconstructs directly from `.value` without replaying earlier\n * writes.\n *\n * @remarks Beta. The on-disk representation may change in future releases.\n */\nexport class DeltaSnapshot<Value = unknown> {\n /** Marker used for structural detection across module/realm boundaries. */\n lg_name = \"DeltaSnapshot\" as const;\n\n value: Value;\n\n constructor(value: Value) {\n this.value = value;\n }\n}\n\n/**\n * Structural type guard for {@link DeltaSnapshot}. Uses the `lg_name` marker\n * so it survives serialization round-trips and cross-package duplication.\n */\nexport function isDeltaSnapshot<Value = unknown>(\n value: unknown\n): value is DeltaSnapshot<Value> {\n return (\n value != null &&\n typeof value === \"object\" &&\n (value as { lg_name?: string }).lg_name === \"DeltaSnapshot\"\n );\n}\n\n// Mirrors BaseChannel in \"@langchain/langgraph\"\nexport interface ChannelProtocol<\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 lc_graph_name: string;\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 * @returns {this}\n */\n 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 *\n * @throws {InvalidUpdateError} if the sequence of updates is invalid.\n * @param {Array<UpdateType>} values\n * @returns {void}\n */\n update(values: UpdateType[]): void;\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 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 checkpoint(): CheckpointType | undefined;\n}\n\n// Mirrors SendInterface in \"@langchain/langgraph\"\nexport interface SendProtocol {\n node: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n args: any;\n // Optional per-task timeout policy. Structural to avoid a dependency on the\n // langgraph package; mirrors `TimeoutPolicy` in \"@langchain/langgraph\".\n timeout?: {\n runTimeout?: number;\n idleTimeout?: number;\n refreshOn?: \"auto\" | \"heartbeat\";\n };\n}\n"],"mappings":";
|
|
1
|
+
{"version":3,"file":"types.cjs","names":[],"sources":["../../src/serde/types.ts"],"sourcesContent":["export type ConstructorRecord = {\n lc: 2;\n type: \"constructor\";\n id: unknown;\n method?: unknown;\n args?: unknown;\n};\n\nexport const TASKS = \"__pregel_tasks\";\nexport const ERROR = \"__error__\";\nexport const SCHEDULED = \"__scheduled__\";\nexport const INTERRUPT = \"__interrupt__\";\nexport const RESUME = \"__resume__\";\n\n/**\n * Snapshot blob for a `DeltaChannel` with finite snapshot frequency.\n *\n * Stored directly in a checkpoint's `channel_values` in place of the full\n * accumulated value. The ancestor walk in\n * {@link BaseCheckpointSaver.getDeltaChannelHistory} terminates when it\n * encounters a populated `channel_values` entry for a channel; a\n * `DeltaSnapshot` value is the materialized state at that ancestor, so the\n * channel reconstructs directly from `.value` without replaying earlier\n * writes.\n *\n * @remarks Beta. The on-disk representation may change in future releases.\n */\nexport class DeltaSnapshot<Value = unknown> {\n /** Marker used for structural detection across module/realm boundaries. */\n lg_name = \"DeltaSnapshot\" as const;\n\n value: Value;\n\n constructor(value: Value) {\n this.value = value;\n }\n}\n\n/**\n * Structural type guard for {@link DeltaSnapshot}. Uses the `lg_name` marker\n * so it survives serialization round-trips and cross-package duplication.\n */\nexport function isDeltaSnapshot<Value = unknown>(\n value: unknown\n): value is DeltaSnapshot<Value> {\n return (\n value != null &&\n typeof value === \"object\" &&\n (value as { lg_name?: string }).lg_name === \"DeltaSnapshot\"\n );\n}\n\n// Mirrors BaseChannel in \"@langchain/langgraph\"\nexport interface ChannelProtocol<\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 lc_graph_name: string;\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 * @returns {this}\n */\n 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 *\n * @throws {InvalidUpdateError} if the sequence of updates is invalid.\n * @param {Array<UpdateType>} values\n * @returns {void}\n */\n update(values: UpdateType[]): void;\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 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 checkpoint(): CheckpointType | undefined;\n}\n\n// Mirrors SendInterface in \"@langchain/langgraph\"\nexport interface SendProtocol {\n node: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n args: any;\n // Optional per-task timeout policy. Structural to avoid a dependency on the\n // langgraph package; mirrors `TimeoutPolicy` in \"@langchain/langgraph\".\n timeout?: {\n runTimeout?: number;\n idleTimeout?: number;\n refreshOn?: \"auto\" | \"heartbeat\";\n };\n}\n"],"mappings":";AAQA,MAAa,QAAQ;AACrB,MAAa,QAAQ;AACrB,MAAa,YAAY;AACzB,MAAa,YAAY;AACzB,MAAa,SAAS;;;;;;;;;;;;;;AAetB,IAAa,gBAAb,MAA4C;;CAE1C,UAAU;CAEV;CAEA,YAAY,OAAc;AACxB,OAAK,QAAQ;;;;;;;AAQjB,SAAgB,gBACd,OAC+B;AAC/B,QACE,SAAS,QACT,OAAO,UAAU,YAChB,MAA+B,YAAY"}
|
package/dist/serde/types.d.cts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
//#region src/serde/types.d.ts
|
|
2
|
+
type ConstructorRecord = {
|
|
3
|
+
lc: 2;
|
|
4
|
+
type: "constructor";
|
|
5
|
+
id: unknown;
|
|
6
|
+
method?: unknown;
|
|
7
|
+
args?: unknown;
|
|
8
|
+
};
|
|
2
9
|
declare const TASKS = "__pregel_tasks";
|
|
3
10
|
declare const ERROR = "__error__";
|
|
4
11
|
declare const SCHEDULED = "__scheduled__";
|
|
@@ -77,5 +84,5 @@ interface SendProtocol {
|
|
|
77
84
|
};
|
|
78
85
|
}
|
|
79
86
|
//#endregion
|
|
80
|
-
export { ChannelProtocol, DeltaSnapshot, ERROR, INTERRUPT, RESUME, SCHEDULED, SendProtocol, TASKS, isDeltaSnapshot };
|
|
87
|
+
export { ChannelProtocol, ConstructorRecord, DeltaSnapshot, ERROR, INTERRUPT, RESUME, SCHEDULED, SendProtocol, TASKS, isDeltaSnapshot };
|
|
81
88
|
//# sourceMappingURL=types.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.cts","names":[],"sources":["../../src/serde/types.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"types.d.cts","names":[],"sources":["../../src/serde/types.ts"],"mappings":";KAAY,iBAAA;EACV,EAAA;EACA,IAAA;EACA,EAAA;EACA,MAAA;EACA,IAAA;AAAA;AAAA,cAGW,KAAA;AAAA,cACA,KAAA;AAAA,cACA,SAAA;AAAA,cACA,SAAA;AAAA,cACA,MAAA;AAJb;;;;;AACA;;;;;AACA;;;AAFA,cAmBa,aAAA;EAjBS;EAmBpB,OAAA;EAEA,KAAA,EAAO,KAAA;EAEP,WAAA,CAAY,KAAA,EAAO,KAAA;AAAA;;AArBrB;;;iBA8BgB,eAAA,iBAAA,CACd,KAAA,YACC,KAAA,IAAS,aAAA,CAAc,KAAA;AAAA,UAST,eAAA;EAKf,SAAA,EAAW,SAAA;EAEX,UAAA,EAAY,UAAA;EAjCY;;;EAsCxB,aAAA;EAlCA;;;;;;;EA2CA,cAAA,CAAe,UAAA,GAAa,cAAA;EAhCC;;;;;;;;EA0C7B,MAAA,CAAO,MAAA,EAAQ,UAAA;EAxCc;AAS/B;;;;;EAuCE,GAAA,IAAO,SAAA;EARQ;;;;;;EAgBf,UAAA,IAAc,cAAA;AAAA;AAAA,UAIC,YAAA;EACf,IAAA;EAEA,IAAA;EAGA,OAAA;IACE,UAAA;IACA,WAAA;IACA,SAAA;EAAA;AAAA"}
|
package/dist/serde/types.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
//#region src/serde/types.d.ts
|
|
2
|
+
type ConstructorRecord = {
|
|
3
|
+
lc: 2;
|
|
4
|
+
type: "constructor";
|
|
5
|
+
id: unknown;
|
|
6
|
+
method?: unknown;
|
|
7
|
+
args?: unknown;
|
|
8
|
+
};
|
|
2
9
|
declare const TASKS = "__pregel_tasks";
|
|
3
10
|
declare const ERROR = "__error__";
|
|
4
11
|
declare const SCHEDULED = "__scheduled__";
|
|
@@ -77,5 +84,5 @@ interface SendProtocol {
|
|
|
77
84
|
};
|
|
78
85
|
}
|
|
79
86
|
//#endregion
|
|
80
|
-
export { ChannelProtocol, DeltaSnapshot, ERROR, INTERRUPT, RESUME, SCHEDULED, SendProtocol, TASKS, isDeltaSnapshot };
|
|
87
|
+
export { ChannelProtocol, ConstructorRecord, DeltaSnapshot, ERROR, INTERRUPT, RESUME, SCHEDULED, SendProtocol, TASKS, isDeltaSnapshot };
|
|
81
88
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/serde/types.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/serde/types.ts"],"mappings":";KAAY,iBAAA;EACV,EAAA;EACA,IAAA;EACA,EAAA;EACA,MAAA;EACA,IAAA;AAAA;AAAA,cAGW,KAAA;AAAA,cACA,KAAA;AAAA,cACA,SAAA;AAAA,cACA,SAAA;AAAA,cACA,MAAA;AAJb;;;;;AACA;;;;;AACA;;;AAFA,cAmBa,aAAA;EAjBS;EAmBpB,OAAA;EAEA,KAAA,EAAO,KAAA;EAEP,WAAA,CAAY,KAAA,EAAO,KAAA;AAAA;;AArBrB;;;iBA8BgB,eAAA,iBAAA,CACd,KAAA,YACC,KAAA,IAAS,aAAA,CAAc,KAAA;AAAA,UAST,eAAA;EAKf,SAAA,EAAW,SAAA;EAEX,UAAA,EAAY,UAAA;EAjCY;;;EAsCxB,aAAA;EAlCA;;;;;;;EA2CA,cAAA,CAAe,UAAA,GAAa,cAAA;EAhCC;;;;;;;;EA0C7B,MAAA,CAAO,MAAA,EAAQ,UAAA;EAxCc;AAS/B;;;;;EAuCE,GAAA,IAAO,SAAA;EARQ;;;;;;EAgBf,UAAA,IAAc,cAAA;AAAA;AAAA,UAIC,YAAA;EACf,IAAA;EAEA,IAAA;EAGA,OAAA;IACE,UAAA;IACA,WAAA;IACA,SAAA;EAAA;AAAA"}
|
package/dist/serde/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","names":[],"sources":["../../src/serde/types.ts"],"sourcesContent":["export const TASKS = \"__pregel_tasks\";\nexport const ERROR = \"__error__\";\nexport const SCHEDULED = \"__scheduled__\";\nexport const INTERRUPT = \"__interrupt__\";\nexport const RESUME = \"__resume__\";\n\n/**\n * Snapshot blob for a `DeltaChannel` with finite snapshot frequency.\n *\n * Stored directly in a checkpoint's `channel_values` in place of the full\n * accumulated value. The ancestor walk in\n * {@link BaseCheckpointSaver.getDeltaChannelHistory} terminates when it\n * encounters a populated `channel_values` entry for a channel; a\n * `DeltaSnapshot` value is the materialized state at that ancestor, so the\n * channel reconstructs directly from `.value` without replaying earlier\n * writes.\n *\n * @remarks Beta. The on-disk representation may change in future releases.\n */\nexport class DeltaSnapshot<Value = unknown> {\n /** Marker used for structural detection across module/realm boundaries. */\n lg_name = \"DeltaSnapshot\" as const;\n\n value: Value;\n\n constructor(value: Value) {\n this.value = value;\n }\n}\n\n/**\n * Structural type guard for {@link DeltaSnapshot}. Uses the `lg_name` marker\n * so it survives serialization round-trips and cross-package duplication.\n */\nexport function isDeltaSnapshot<Value = unknown>(\n value: unknown\n): value is DeltaSnapshot<Value> {\n return (\n value != null &&\n typeof value === \"object\" &&\n (value as { lg_name?: string }).lg_name === \"DeltaSnapshot\"\n );\n}\n\n// Mirrors BaseChannel in \"@langchain/langgraph\"\nexport interface ChannelProtocol<\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 lc_graph_name: string;\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 * @returns {this}\n */\n 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 *\n * @throws {InvalidUpdateError} if the sequence of updates is invalid.\n * @param {Array<UpdateType>} values\n * @returns {void}\n */\n update(values: UpdateType[]): void;\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 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 checkpoint(): CheckpointType | undefined;\n}\n\n// Mirrors SendInterface in \"@langchain/langgraph\"\nexport interface SendProtocol {\n node: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n args: any;\n // Optional per-task timeout policy. Structural to avoid a dependency on the\n // langgraph package; mirrors `TimeoutPolicy` in \"@langchain/langgraph\".\n timeout?: {\n runTimeout?: number;\n idleTimeout?: number;\n refreshOn?: \"auto\" | \"heartbeat\";\n };\n}\n"],"mappings":";
|
|
1
|
+
{"version":3,"file":"types.js","names":[],"sources":["../../src/serde/types.ts"],"sourcesContent":["export type ConstructorRecord = {\n lc: 2;\n type: \"constructor\";\n id: unknown;\n method?: unknown;\n args?: unknown;\n};\n\nexport const TASKS = \"__pregel_tasks\";\nexport const ERROR = \"__error__\";\nexport const SCHEDULED = \"__scheduled__\";\nexport const INTERRUPT = \"__interrupt__\";\nexport const RESUME = \"__resume__\";\n\n/**\n * Snapshot blob for a `DeltaChannel` with finite snapshot frequency.\n *\n * Stored directly in a checkpoint's `channel_values` in place of the full\n * accumulated value. The ancestor walk in\n * {@link BaseCheckpointSaver.getDeltaChannelHistory} terminates when it\n * encounters a populated `channel_values` entry for a channel; a\n * `DeltaSnapshot` value is the materialized state at that ancestor, so the\n * channel reconstructs directly from `.value` without replaying earlier\n * writes.\n *\n * @remarks Beta. The on-disk representation may change in future releases.\n */\nexport class DeltaSnapshot<Value = unknown> {\n /** Marker used for structural detection across module/realm boundaries. */\n lg_name = \"DeltaSnapshot\" as const;\n\n value: Value;\n\n constructor(value: Value) {\n this.value = value;\n }\n}\n\n/**\n * Structural type guard for {@link DeltaSnapshot}. Uses the `lg_name` marker\n * so it survives serialization round-trips and cross-package duplication.\n */\nexport function isDeltaSnapshot<Value = unknown>(\n value: unknown\n): value is DeltaSnapshot<Value> {\n return (\n value != null &&\n typeof value === \"object\" &&\n (value as { lg_name?: string }).lg_name === \"DeltaSnapshot\"\n );\n}\n\n// Mirrors BaseChannel in \"@langchain/langgraph\"\nexport interface ChannelProtocol<\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 lc_graph_name: string;\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 * @returns {this}\n */\n 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 *\n * @throws {InvalidUpdateError} if the sequence of updates is invalid.\n * @param {Array<UpdateType>} values\n * @returns {void}\n */\n update(values: UpdateType[]): void;\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 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 checkpoint(): CheckpointType | undefined;\n}\n\n// Mirrors SendInterface in \"@langchain/langgraph\"\nexport interface SendProtocol {\n node: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n args: any;\n // Optional per-task timeout policy. Structural to avoid a dependency on the\n // langgraph package; mirrors `TimeoutPolicy` in \"@langchain/langgraph\".\n timeout?: {\n runTimeout?: number;\n idleTimeout?: number;\n refreshOn?: \"auto\" | \"heartbeat\";\n };\n}\n"],"mappings":";AAQA,MAAa,QAAQ;AACrB,MAAa,QAAQ;AACrB,MAAa,YAAY;AACzB,MAAa,YAAY;AACzB,MAAa,SAAS;;;;;;;;;;;;;;AAetB,IAAa,gBAAb,MAA4C;;CAE1C,UAAU;CAEV;CAEA,YAAY,OAAc;AACxB,OAAK,QAAQ;;;;;;;AAQjB,SAAgB,gBACd,OAC+B;AAC/B,QACE,SAAS,QACT,OAAO,UAAU,YAChB,MAA+B,YAAY"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@langchain/langgraph-checkpoint",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"description": "Library with base interfaces for LangGraph checkpoint savers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
"@tsconfig/recommended": "^1.0.3",
|
|
24
24
|
"dotenv": "^16.3.1",
|
|
25
25
|
"dpdm": "^3.12.0",
|
|
26
|
-
"rollup": "^4.
|
|
26
|
+
"rollup": "^4.62.3",
|
|
27
27
|
"tsx": "^4.19.3",
|
|
28
28
|
"typescript": "^4.9.5 || ^5.4.5",
|
|
29
|
-
"vitest": "^4.1.
|
|
29
|
+
"vitest": "^4.1.10"
|
|
30
30
|
},
|
|
31
31
|
"publishConfig": {
|
|
32
32
|
"access": "public",
|