UNPKG

@langchain/langgraph

Version:
790 lines (789 loc) 29 kB
import { CACHE_NS_WRITES, CONFIG_KEY_CHECKPOINTER, CONFIG_KEY_CHECKPOINT_MAP, CONFIG_KEY_NODE_ERROR, CONFIG_KEY_PREVIOUS_STATE, CONFIG_KEY_READ, CONFIG_KEY_RESUME_MAP, CONFIG_KEY_SCRATCHPAD, CONFIG_KEY_SEND, CONFIG_KEY_TASK_ID, ERROR, ERROR_SOURCE_NODE, INTERRUPT, NO_WRITES, PREVIOUS, PULL, PUSH, RESERVED, RESUME, RETURN, Send, TAG_HIDDEN, TASKS, _isSend, _isSendInterface } from "../constants.js"; import { EmptyChannelError, InvalidUpdateError, NodeError } from "../errors.js"; import { createCheckpoint, emptyChannels, getOnlyChannels, isDeltaChannel } from "../channels/base.js"; import { XXH3 } from "../hash.js"; import { readChannel, readChannels } from "./io.js"; import { isCall } from "./types.js"; import { getNullChannelVersion } from "./utils/index.js"; import { getRunnableForFunc } from "./call.js"; import { copyCheckpoint, maxChannelVersion, uuid5 } from "@langchain/langgraph-checkpoint"; import { mergeConfigs, patchConfig } from "@langchain/core/runnables"; //#region src/pregel/algo.ts const increment = (current) => { return current !== void 0 ? current + 1 : 1; }; function triggersNextStep(updatedChannels, triggerToNodes) { if (triggerToNodes == null) return false; for (const chan of updatedChannels) if (triggerToNodes[chan]) return true; return false; } function maxChannelMapVersion(channelVersions) { let maxVersion; for (const chan in channelVersions) { if (!Object.prototype.hasOwnProperty.call(channelVersions, chan)) continue; if (maxVersion == null) maxVersion = channelVersions[chan]; else maxVersion = maxChannelVersion(maxVersion, channelVersions[chan]); } return maxVersion; } function shouldInterrupt(checkpoint, interruptNodes, tasks) { const nullVersion = getNullChannelVersion(checkpoint.channel_versions); const seen = checkpoint.versions_seen["__interrupt__"] ?? {}; let anyChannelUpdated = false; if ((checkpoint.channel_versions["__start__"] ?? nullVersion) > (seen["__start__"] ?? nullVersion)) anyChannelUpdated = true; else for (const chan in checkpoint.channel_versions) { if (!Object.prototype.hasOwnProperty.call(checkpoint.channel_versions, chan)) continue; if (checkpoint.channel_versions[chan] > (seen[chan] ?? nullVersion)) { anyChannelUpdated = true; break; } } const anyTriggeredNodeInInterruptNodes = tasks.some((task) => interruptNodes === "*" ? !task.config?.tags?.includes(TAG_HIDDEN) : interruptNodes.includes(task.name)); return anyChannelUpdated && anyTriggeredNodeInInterruptNodes; } function _localRead(checkpoint, channels, task, select, fresh = false) { let updated = /* @__PURE__ */ new Set(); if (!Array.isArray(select)) { for (const [c] of task.writes) if (c === select) { updated = new Set([c]); break; } updated = updated || /* @__PURE__ */ new Set(); } else updated = new Set(select.filter((c) => task.writes.some(([key, _]) => key === c))); let values; if (fresh && updated.size > 0) { const localChannels = Object.fromEntries(Object.entries(channels).filter(([k, _]) => updated.has(k))); const channelsToSnapshot = /* @__PURE__ */ new Set(); for (const k in localChannels) { if (!Object.prototype.hasOwnProperty.call(localChannels, k)) continue; const ch = localChannels[k]; if (isDeltaChannel(ch) && ch.isAvailable()) channelsToSnapshot.add(k); } const newCheckpoint = createCheckpoint(checkpoint, localChannels, -1, { channelsToSnapshot }); const newChannels = emptyChannels(localChannels, newCheckpoint); _applyWrites(copyCheckpoint(newCheckpoint), newChannels, [task], void 0, void 0); values = readChannels({ ...channels, ...newChannels }, select); } else values = readChannels(channels, select); return values; } function _localWrite(commit, processes, writes) { for (const [chan, value] of writes) if (["__pregel_push", "__pregel_tasks"].includes(chan) && value != null) { if (!_isSend(value)) throw new InvalidUpdateError(`Invalid packet type, expected SendProtocol, got ${JSON.stringify(value)}`); if (!(value.node in processes)) throw new InvalidUpdateError(`Invalid node name "${value.node}" in Send packet`); } commit(writes); } const IGNORE = new Set([ NO_WRITES, PUSH, RESUME, INTERRUPT, RETURN, ERROR, ERROR_SOURCE_NODE ]); const RESERVED_SET = new Set(RESERVED); function _applyWrites(checkpoint, channels, tasks, getNextVersion, triggerToNodes) { const pathCache = /* @__PURE__ */ new Map(); for (const task of tasks) pathCache.set(task, task.path?.slice(0, 3) || []); tasks.sort((a, b) => { const aPath = pathCache.get(a); const bPath = pathCache.get(b); for (let i = 0; i < Math.min(aPath.length, bPath.length); i += 1) { if (aPath[i] < bPath[i]) return -1; if (aPath[i] > bPath[i]) return 1; } return aPath.length - bPath.length; }); const onlyChannels = getOnlyChannels(channels); let bumpStep = false; const channelsToConsume = /* @__PURE__ */ new Set(); for (const task of tasks) { if (task.triggers.length > 0) bumpStep = true; checkpoint.versions_seen[task.name] ??= {}; for (const chan of task.triggers) { if (chan in checkpoint.channel_versions) checkpoint.versions_seen[task.name][chan] = checkpoint.channel_versions[chan]; if (!RESERVED_SET.has(chan)) channelsToConsume.add(chan); } } let maxVersion = maxChannelMapVersion(checkpoint.channel_versions); let usedNewVersion = false; for (const chan of channelsToConsume) if (chan in onlyChannels && onlyChannels[chan].consume()) { if (getNextVersion !== void 0) { checkpoint.channel_versions[chan] = getNextVersion(maxVersion); usedNewVersion = true; } } const pendingWritesByChannel = {}; const pendingWriteTaskIdsByChannel = {}; for (const task of tasks) { const taskId = task.id ?? ""; for (const [chan, val] of task.writes) if (IGNORE.has(chan)) {} else if (chan in onlyChannels) { pendingWritesByChannel[chan] ??= []; pendingWritesByChannel[chan].push(val); pendingWriteTaskIdsByChannel[chan] ??= []; pendingWriteTaskIdsByChannel[chan].push(taskId); } } for (const [chan, vals] of Object.entries(pendingWritesByChannel)) { if (vals.length < 2) continue; if (onlyChannels[chan]?.lc_graph_name !== "DeltaChannel") continue; const taskIds = pendingWriteTaskIdsByChannel[chan]; const paired = vals.map((val, i) => ({ val, taskId: taskIds[i] })); paired.sort((a, b) => a.taskId < b.taskId ? -1 : a.taskId > b.taskId ? 1 : 0); pendingWritesByChannel[chan] = paired.map((p) => p.val); } if (maxVersion != null && getNextVersion != null) maxVersion = usedNewVersion ? getNextVersion(maxVersion) : maxVersion; const updatedChannels = /* @__PURE__ */ new Set(); for (const [chan, vals] of Object.entries(pendingWritesByChannel)) if (chan in onlyChannels) { const channel = onlyChannels[chan]; let updated; try { updated = channel.update(vals); } catch (e) { if (e.name === InvalidUpdateError.unminifiable_name) { const wrappedError = new InvalidUpdateError(`Invalid update for channel "${chan}" with values ${JSON.stringify(vals)}: ${e.message}`); wrappedError.lc_error_code = e.lc_error_code; throw wrappedError; } else throw e; } if (updated && getNextVersion !== void 0) { checkpoint.channel_versions[chan] = getNextVersion(maxVersion); if (channel.isAvailable()) updatedChannels.add(chan); } } if (bumpStep) for (const chan in onlyChannels) { if (!Object.prototype.hasOwnProperty.call(onlyChannels, chan)) continue; const channel = onlyChannels[chan]; if (channel.isAvailable() && !updatedChannels.has(chan)) { if (channel.update([]) && getNextVersion !== void 0) { checkpoint.channel_versions[chan] = getNextVersion(maxVersion); if (channel.isAvailable()) updatedChannels.add(chan); } } } if (bumpStep && !triggersNextStep(updatedChannels, triggerToNodes)) for (const chan in onlyChannels) { if (!Object.prototype.hasOwnProperty.call(onlyChannels, chan)) continue; const channel = onlyChannels[chan]; if (channel.finish() && getNextVersion !== void 0) { checkpoint.channel_versions[chan] = getNextVersion(maxVersion); if (channel.isAvailable()) updatedChannels.add(chan); } } return updatedChannels; } function* candidateNodes(checkpoint, processes, extra) { if (extra.updatedChannels != null && extra.triggerToNodes != null) { const triggeredNodes = /* @__PURE__ */ new Set(); for (const channel of extra.updatedChannels) { const nodeIds = extra.triggerToNodes[channel]; for (const id of nodeIds ?? []) triggeredNodes.add(id); } yield* [...triggeredNodes].sort(); return; } if ((() => { for (const chan in checkpoint.channel_versions) if (checkpoint.channel_versions[chan] !== null) return false; return true; })()) return; for (const name in processes) { if (!Object.prototype.hasOwnProperty.call(processes, name)) continue; yield name; } } /** * Build an index over pendingWrites for O(1) lookups. * * @internal Exported for benchmarks and regression tests only. */ function _indexPendingWrites(pendingWrites) { let nullResume; const resumeByTaskId = /* @__PURE__ */ new Map(); const successfulWriteTaskIds = /* @__PURE__ */ new Set(); if (pendingWrites) for (const [tid, chan, val] of pendingWrites) { if (tid === "00000000-0000-0000-0000-000000000000" && chan === "__resume__" && nullResume === void 0) nullResume = val; if (chan === "__resume__" && tid !== "00000000-0000-0000-0000-000000000000") { let arr = resumeByTaskId.get(tid); if (!arr) { arr = []; resumeByTaskId.set(tid, arr); } arr.push(val); } if (chan !== "__error__") successfulWriteTaskIds.add(tid); } return { nullResume, resumeByTaskId, successfulWriteTaskIds }; } /** * Prepare the set of tasks that will make up the next Pregel step. * This is the union of all PUSH tasks (Sends) and PULL tasks (nodes triggered * by edges). */ function _prepareNextTasks(checkpoint, pendingWrites, processes, channels, config, forExecution, extra) { const tasks = {}; const indexedExtra = extra.pendingWritesIndex ? extra : { ...extra, pendingWritesIndex: _indexPendingWrites(pendingWrites) }; const tasksChannel = channels[TASKS]; if (tasksChannel?.isAvailable()) { const len = tasksChannel.get().length; for (let i = 0; i < len; i += 1) { const task = _prepareSingleTask([PUSH, i], checkpoint, pendingWrites, processes, channels, config, forExecution, indexedExtra); if (task !== void 0) tasks[task.id] = task; } } for (const name of candidateNodes(checkpoint, processes, indexedExtra)) { const task = _prepareSingleTask([PULL, name], checkpoint, pendingWrites, processes, channels, config, forExecution, indexedExtra); if (task !== void 0) tasks[task.id] = task; } return tasks; } /** * Prepares a single task for the next Pregel step, given a task path, which * uniquely identifies a PUSH or PULL task within the graph. */ function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processes, channels, config, forExecution, extra) { const { step, checkpointer, manager } = extra; const configurable = config.configurable ?? {}; const parentNamespace = configurable.checkpoint_ns ?? ""; if (taskPath[0] === "__pregel_push" && isCall(taskPath[taskPath.length - 1])) { const call = taskPath[taskPath.length - 1]; const proc = getRunnableForFunc(call.name, call.func); const triggers = [PUSH]; const checkpointNamespace = parentNamespace === "" ? call.name : `${parentNamespace}|${call.name}`; const id = uuid5(JSON.stringify([ checkpointNamespace, step.toString(), call.name, PUSH, taskPath[1], taskPath[2] ]), checkpoint.id); const taskCheckpointNamespace = `${checkpointNamespace}:${id}`; const outputTaskPath = [...taskPath.slice(0, 3), true]; const metadata = { langgraph_step: step, langgraph_node: call.name, langgraph_triggers: triggers, langgraph_path: outputTaskPath, langgraph_checkpoint_ns: taskCheckpointNamespace, checkpoint_ns: taskCheckpointNamespace }; if (forExecution) { const writes = []; const executionInfo = { checkpointId: checkpoint.id, checkpointNs: taskCheckpointNamespace, taskId: id, threadId: configurable.thread_id, runId: config.runId != null ? String(config.runId) : void 0, nodeAttempt: 1 }; return { name: call.name, input: call.input, proc, writes, config: { ...patchConfig(mergeConfigs(config, { metadata, store: extra.store ?? config.store }), { runName: call.name, callbacks: manager?.getChild(`graph:step:${step}`), configurable: { [CONFIG_KEY_TASK_ID]: id, [CONFIG_KEY_SEND]: (writes_) => _localWrite((items) => writes.push(...items), processes, writes_), [CONFIG_KEY_READ]: (select_, fresh_ = false) => _localRead(checkpoint, channels, { name: call.name, writes, triggers, path: outputTaskPath }, select_, fresh_), [CONFIG_KEY_CHECKPOINTER]: checkpointer ?? configurable["__pregel_checkpointer"], [CONFIG_KEY_CHECKPOINT_MAP]: { ...configurable[CONFIG_KEY_CHECKPOINT_MAP], [parentNamespace]: checkpoint.id }, [CONFIG_KEY_SCRATCHPAD]: _scratchpad({ pendingWrites: pendingWrites ?? [], taskId: id, currentTaskInput: call.input, resumeMap: config.configurable?.[CONFIG_KEY_RESUME_MAP], namespaceHash: XXH3(taskCheckpointNamespace), pendingWritesIndex: extra.pendingWritesIndex }), [CONFIG_KEY_PREVIOUS_STATE]: checkpoint.channel_values[PREVIOUS], checkpoint_id: void 0, checkpoint_ns: taskCheckpointNamespace } }), executionInfo }, triggers, retry_policy: call.retry, cache_key: call.cache ? { key: XXH3((call.cache.keyFunc ?? JSON.stringify)([call.input])), ns: [CACHE_NS_WRITES, call.name ?? "__dynamic__"], ttl: call.cache.ttl } : void 0, id, path: outputTaskPath, writers: [], timeout: call.timeout }; } else return { id, name: call.name, interrupts: [], path: outputTaskPath }; } else if (taskPath[0] === "__pregel_push") { const index = typeof taskPath[1] === "number" ? taskPath[1] : parseInt(taskPath[1], 10); if (!channels["__pregel_tasks"]?.isAvailable()) return; const sends = channels[TASKS].get(); if (index < 0 || index >= sends.length) return; const packet = _isSendInterface(sends[index]) && !_isSend(sends[index]) ? new Send(sends[index].node, sends[index].args, sends[index].timeout !== void 0 ? { timeout: sends[index].timeout } : void 0) : sends[index]; if (!_isSendInterface(packet)) { console.warn(`Ignoring invalid packet ${JSON.stringify(packet)} in pending sends.`); return; } if (!(packet.node in processes)) { console.warn(`Ignoring unknown node name ${packet.node} in pending sends.`); return; } const triggers = [PUSH]; const checkpointNamespace = parentNamespace === "" ? packet.node : `${parentNamespace}|${packet.node}`; const taskId = uuid5(JSON.stringify([ checkpointNamespace, step.toString(), packet.node, PUSH, index.toString() ]), checkpoint.id); const taskCheckpointNamespace = `${checkpointNamespace}:${taskId}`; let metadata = { langgraph_step: step, langgraph_node: packet.node, langgraph_triggers: triggers, langgraph_path: taskPath.slice(0, 3), langgraph_checkpoint_ns: taskCheckpointNamespace, checkpoint_ns: taskCheckpointNamespace }; if (forExecution) { const proc = processes[packet.node]; const node = proc.getNode(); if (node !== void 0) { if (proc.metadata !== void 0) metadata = { ...metadata, ...proc.metadata }; const writes = []; const executionInfo = { checkpointId: checkpoint.id, checkpointNs: taskCheckpointNamespace, taskId, threadId: configurable.thread_id, runId: config.runId != null ? String(config.runId) : void 0, nodeAttempt: 1 }; return { name: packet.node, input: packet.args, proc: node, subgraphs: proc.subgraphs, writes, config: { ...patchConfig(mergeConfigs(config, { metadata, tags: proc.tags, store: extra.store ?? config.store }), { runName: packet.node, callbacks: manager?.getChild(`graph:step:${step}`), configurable: { [CONFIG_KEY_TASK_ID]: taskId, [CONFIG_KEY_SEND]: (writes_) => _localWrite((items) => writes.push(...items), processes, writes_), [CONFIG_KEY_READ]: (select_, fresh_ = false) => _localRead(checkpoint, channels, { name: packet.node, writes, triggers, path: taskPath }, select_, fresh_), [CONFIG_KEY_CHECKPOINTER]: checkpointer ?? configurable["__pregel_checkpointer"], [CONFIG_KEY_CHECKPOINT_MAP]: { ...configurable[CONFIG_KEY_CHECKPOINT_MAP], [parentNamespace]: checkpoint.id }, [CONFIG_KEY_SCRATCHPAD]: _scratchpad({ pendingWrites: pendingWrites ?? [], taskId, currentTaskInput: packet.args, resumeMap: config.configurable?.[CONFIG_KEY_RESUME_MAP], namespaceHash: XXH3(taskCheckpointNamespace), pendingWritesIndex: extra.pendingWritesIndex }), [CONFIG_KEY_PREVIOUS_STATE]: checkpoint.channel_values[PREVIOUS], checkpoint_id: void 0, checkpoint_ns: taskCheckpointNamespace } }), executionInfo }, triggers, retry_policy: proc.retryPolicy, cache_key: proc.cachePolicy ? { key: XXH3((proc.cachePolicy.keyFunc ?? JSON.stringify)([packet.args])), ns: [ CACHE_NS_WRITES, proc.name ?? "__dynamic__", packet.node ], ttl: proc.cachePolicy.ttl } : void 0, id: taskId, path: taskPath, writers: proc.getWriters(), timeout: packet.timeout ?? proc.timeout }; } } else return { id: taskId, name: packet.node, interrupts: [], path: taskPath }; } else if (taskPath[0] === "__pregel_pull") { const name = taskPath[1].toString(); const proc = processes[name]; if (proc === void 0) return; if (pendingWrites?.length) { const checkpointNamespace = parentNamespace === "" ? name : `${parentNamespace}|${name}`; const taskId = uuid5(JSON.stringify([ checkpointNamespace, step.toString(), name, PULL, name ]), checkpoint.id); if (extra.pendingWritesIndex ? extra.pendingWritesIndex.successfulWriteTaskIds.has(taskId) : pendingWrites.some((w) => w[0] === taskId && w[1] !== "__error__")) return; } const nullVersion = getNullChannelVersion(checkpoint.channel_versions); if (nullVersion === void 0) return; const seen = checkpoint.versions_seen[name] ?? {}; const trigger = proc.triggers.find((chan) => { if (!channels[chan].isAvailable()) return false; return (checkpoint.channel_versions[chan] ?? nullVersion) > (seen[chan] ?? nullVersion); }); if (trigger !== void 0) { const val = _procInput(proc, channels, forExecution); if (val === void 0) return; const checkpointNamespace = parentNamespace === "" ? name : `${parentNamespace}|${name}`; const taskId = uuid5(JSON.stringify([ checkpointNamespace, step.toString(), name, PULL, [trigger] ]), checkpoint.id); const taskCheckpointNamespace = `${checkpointNamespace}:${taskId}`; let metadata = { langgraph_step: step, langgraph_node: name, langgraph_triggers: [trigger], langgraph_path: taskPath, langgraph_checkpoint_ns: taskCheckpointNamespace, checkpoint_ns: taskCheckpointNamespace }; if (forExecution) { const node = proc.getNode(); if (node !== void 0) { if (proc.metadata !== void 0) metadata = { ...metadata, ...proc.metadata }; const writes = []; const executionInfo = { checkpointId: checkpoint.id, checkpointNs: taskCheckpointNamespace, taskId, threadId: configurable.thread_id, runId: config.runId != null ? String(config.runId) : void 0, nodeAttempt: 1 }; return { name, input: val, proc: node, subgraphs: proc.subgraphs, writes, config: { ...patchConfig(mergeConfigs(config, { metadata, tags: proc.tags, store: extra.store ?? config.store }), { runName: name, callbacks: manager?.getChild(`graph:step:${step}`), configurable: { [CONFIG_KEY_TASK_ID]: taskId, [CONFIG_KEY_SEND]: (writes_) => _localWrite((items) => { writes.push(...items); }, processes, writes_), [CONFIG_KEY_READ]: (select_, fresh_ = false) => _localRead(checkpoint, channels, { name, writes, triggers: [trigger], path: taskPath }, select_, fresh_), [CONFIG_KEY_CHECKPOINTER]: checkpointer ?? configurable["__pregel_checkpointer"], [CONFIG_KEY_CHECKPOINT_MAP]: { ...configurable[CONFIG_KEY_CHECKPOINT_MAP], [parentNamespace]: checkpoint.id }, [CONFIG_KEY_SCRATCHPAD]: _scratchpad({ pendingWrites: pendingWrites ?? [], taskId, currentTaskInput: val, resumeMap: config.configurable?.[CONFIG_KEY_RESUME_MAP], namespaceHash: XXH3(taskCheckpointNamespace), pendingWritesIndex: extra.pendingWritesIndex }), [CONFIG_KEY_PREVIOUS_STATE]: checkpoint.channel_values[PREVIOUS], checkpoint_id: void 0, checkpoint_ns: taskCheckpointNamespace } }), executionInfo }, triggers: [trigger], retry_policy: proc.retryPolicy, cache_key: proc.cachePolicy ? { key: XXH3((proc.cachePolicy.keyFunc ?? JSON.stringify)([val])), ns: [ CACHE_NS_WRITES, proc.name ?? "__dynamic__", name ], ttl: proc.cachePolicy.ttl } : void 0, id: taskId, path: taskPath, writers: proc.getWriters(), timeout: proc.timeout }; } } else return { id: taskId, name, interrupts: [], path: taskPath }; } } } /** * Prepare an immediate node-level error handler task for a failed task. * * The handler runs only after the failed node's retry policy is exhausted (the * runner schedules it once a non-bubble-up error settles). It is prepared like * a PUSH task targeting the auto-generated handler node, receives the failed * node's input, and is injected with a {@link NodeError} under * {@link CONFIG_KEY_NODE_ERROR} so the handler can inspect the failure * provenance (and route via `Command({ goto })`). * * @internal */ function _prepareNodeErrorHandlerTask(failedTask, handlerNodeName, error, checkpoint, pendingWrites, processes, channels, config, extra) { const { step, checkpointer, manager } = extra; const proc = processes[handlerNodeName]; if (proc === void 0) return; const node = proc.getNode(); if (node === void 0) return; const configurable = config.configurable ?? {}; const parentNamespace = configurable.checkpoint_ns ?? ""; const triggers = [PUSH]; const checkpointNamespace = parentNamespace === "" ? handlerNodeName : `${parentNamespace}|${handlerNodeName}`; const taskId = uuid5(JSON.stringify([ checkpointNamespace, step.toString(), handlerNodeName, PUSH, "node_error_handler", failedTask.id ]), checkpoint.id); const taskCheckpointNamespace = `${checkpointNamespace}:${taskId}`; const taskPath = [ PUSH, String(failedTask.name), handlerNodeName, false ]; let metadata = { langgraph_step: step, langgraph_node: handlerNodeName, langgraph_triggers: triggers, langgraph_path: taskPath, langgraph_checkpoint_ns: taskCheckpointNamespace, checkpoint_ns: taskCheckpointNamespace }; if (proc.metadata !== void 0) metadata = { ...metadata, ...proc.metadata }; const writes = []; const executionInfo = { checkpointId: checkpoint.id, checkpointNs: taskCheckpointNamespace, taskId, threadId: configurable.thread_id, runId: config.runId != null ? String(config.runId) : void 0, nodeAttempt: 1 }; return { name: handlerNodeName, input: failedTask.input, proc: node, subgraphs: proc.subgraphs, writes, config: { ...patchConfig(mergeConfigs(config, { metadata, tags: proc.tags, store: extra.store ?? config.store }), { runName: handlerNodeName, callbacks: manager?.getChild(`graph:step:${step}`), configurable: { [CONFIG_KEY_TASK_ID]: taskId, [CONFIG_KEY_SEND]: (writes_) => _localWrite((items) => writes.push(...items), processes, writes_), [CONFIG_KEY_READ]: (select_, fresh_ = false) => _localRead(checkpoint, channels, { name: handlerNodeName, writes, triggers, path: taskPath }, select_, fresh_), [CONFIG_KEY_CHECKPOINTER]: checkpointer ?? configurable["__pregel_checkpointer"], [CONFIG_KEY_CHECKPOINT_MAP]: { ...configurable[CONFIG_KEY_CHECKPOINT_MAP], [parentNamespace]: checkpoint.id }, [CONFIG_KEY_SCRATCHPAD]: _scratchpad({ pendingWrites: pendingWrites ?? [], taskId, currentTaskInput: failedTask.input, resumeMap: config.configurable?.[CONFIG_KEY_RESUME_MAP], namespaceHash: XXH3(taskCheckpointNamespace) }), [CONFIG_KEY_PREVIOUS_STATE]: checkpoint.channel_values[PREVIOUS], [CONFIG_KEY_NODE_ERROR]: new NodeError(String(failedTask.name), error), checkpoint_id: void 0, checkpoint_ns: taskCheckpointNamespace } }), executionInfo }, triggers, retry_policy: proc.retryPolicy, cache_key: void 0, id: taskId, path: taskPath, writers: proc.getWriters() }; } /** * Function injected under CONFIG_KEY_READ in task config, to read current state. * Used by conditional edges to read a copy of the state with reflecting the writes * from that node only. * * @internal */ function _procInput(proc, channels, forExecution) { let val; if (typeof proc.channels === "object" && !Array.isArray(proc.channels)) { val = {}; for (const [k, chan] of Object.entries(proc.channels)) if (proc.triggers.includes(chan)) try { val[k] = readChannel(channels, chan, false); } catch (e) { if (e.name === EmptyChannelError.unminifiable_name) return; else throw e; } else if (chan in channels) try { val[k] = readChannel(channels, chan, false); } catch (e) { if (e.name === EmptyChannelError.unminifiable_name) continue; else throw e; } } else if (Array.isArray(proc.channels)) { let successfulRead = false; for (const chan of proc.channels) try { val = readChannel(channels, chan, false); successfulRead = true; break; } catch (e) { if (e.name === EmptyChannelError.unminifiable_name) continue; else throw e; } if (!successfulRead) return; } else throw new Error(`Invalid channels type, expected list or dict, got ${proc.channels}`); if (forExecution && proc.mapper !== void 0) val = proc.mapper(val); return val; } /** * Remove any values belonging to UntrackedValue channels from a Send packet * before checkpointing. * * Send is often called with state to be passed to the destination node, * which may contain UntrackedValues at the top level. * * @internal */ function sanitizeUntrackedValuesInSend(packet, channels) { if (typeof packet.args !== "object" || packet.args === null) return packet; const sanitizedArg = {}; for (const [key, value] of Object.entries(packet.args)) { const channel = channels[key]; if (!channel || channel.lc_graph_name !== "UntrackedValue") sanitizedArg[key] = value; } return new Send(packet.node, sanitizedArg); } function _scratchpad({ pendingWrites, taskId, currentTaskInput, resumeMap, namespaceHash, pendingWritesIndex }) { const nullResume = pendingWritesIndex ? pendingWritesIndex.nullResume : pendingWrites.find(([writeTaskId, chan]) => writeTaskId === "00000000-0000-0000-0000-000000000000" && chan === "__resume__")?.[2]; const scratchpad = { callCounter: 0, interruptCounter: -1, resume: (() => { const result = pendingWritesIndex ? (pendingWritesIndex.resumeByTaskId.get(taskId) ?? []).flat() : pendingWrites.filter(([writeTaskId, chan]) => writeTaskId === taskId && chan === "__resume__").flatMap(([_writeTaskId, _chan, resume]) => resume); if (resumeMap != null && namespaceHash in resumeMap) { const mappedResume = resumeMap[namespaceHash]; result.push(mappedResume); } return result; })(), nullResume, subgraphCounter: 0, currentTaskInput, consumeNullResume: () => { if (scratchpad.nullResume) { delete scratchpad.nullResume; pendingWrites.splice(pendingWrites.findIndex(([writeTaskId, chan]) => writeTaskId === "00000000-0000-0000-0000-000000000000" && chan === "__resume__"), 1); return nullResume; } } }; return scratchpad; } //#endregion export { _applyWrites, _localRead, _prepareNextTasks, _prepareNodeErrorHandlerTask, _prepareSingleTask, increment, sanitizeUntrackedValuesInSend, shouldInterrupt }; //# sourceMappingURL=algo.js.map