@langchain/langgraph 1.4.21 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/channels/base.cjs +113 -8
- package/dist/channels/base.cjs.map +1 -1
- package/dist/channels/base.d.cts +26 -4
- package/dist/channels/base.d.cts.map +1 -1
- package/dist/channels/base.d.ts +26 -4
- package/dist/channels/base.d.ts.map +1 -1
- package/dist/channels/base.js +110 -10
- package/dist/channels/base.js.map +1 -1
- package/dist/channels/index.cjs +2 -0
- package/dist/channels/index.d.cts +2 -2
- package/dist/channels/index.d.ts +2 -2
- package/dist/channels/index.js +2 -2
- package/dist/constants.cjs +14 -0
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts.map +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +13 -1
- package/dist/constants.js.map +1 -1
- package/dist/graph/index.cjs +1 -1
- package/dist/graph/index.js +1 -1
- package/dist/graph/messages_reducer.cjs +7 -0
- package/dist/graph/messages_reducer.cjs.map +1 -1
- package/dist/graph/messages_reducer.js +7 -1
- package/dist/graph/messages_reducer.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/pregel/algo.cjs +46 -45
- package/dist/pregel/algo.cjs.map +1 -1
- package/dist/pregel/algo.d.cts.map +1 -1
- package/dist/pregel/algo.d.ts.map +1 -1
- package/dist/pregel/algo.js +47 -47
- package/dist/pregel/algo.js.map +1 -1
- package/dist/pregel/checkpoint.cjs +70 -0
- package/dist/pregel/checkpoint.cjs.map +1 -0
- package/dist/pregel/checkpoint.js +67 -0
- package/dist/pregel/checkpoint.js.map +1 -0
- package/dist/pregel/index.cjs +87 -34
- package/dist/pregel/index.cjs.map +1 -1
- package/dist/pregel/index.d.cts.map +1 -1
- package/dist/pregel/index.d.ts.map +1 -1
- package/dist/pregel/index.js +91 -38
- package/dist/pregel/index.js.map +1 -1
- package/dist/pregel/loop.cjs +325 -109
- package/dist/pregel/loop.cjs.map +1 -1
- package/dist/pregel/loop.js +330 -115
- package/dist/pregel/loop.js.map +1 -1
- package/dist/state/prebuilt/messages.cjs +1 -1
- package/dist/state/prebuilt/messages.js +1 -1
- package/dist/web.cjs +1 -1
- package/dist/web.js +1 -1
- package/package.json +5 -5
|
@@ -139,8 +139,15 @@ function messagesDeltaReducer(state, writes) {
|
|
|
139
139
|
}
|
|
140
140
|
return result.filter((m) => m !== null);
|
|
141
141
|
}
|
|
142
|
+
const MESSAGES_DELTA_REDUCER = Symbol.for("langgraph.messages_delta_reducer");
|
|
143
|
+
Object.defineProperty(messagesDeltaReducer, MESSAGES_DELTA_REDUCER, { value: true });
|
|
144
|
+
/** @internal */
|
|
145
|
+
function isMessagesDeltaReducer(reducer) {
|
|
146
|
+
return typeof reducer === "function" && reducer[MESSAGES_DELTA_REDUCER] === true;
|
|
147
|
+
}
|
|
142
148
|
//#endregion
|
|
143
149
|
exports.REMOVE_ALL_MESSAGES = REMOVE_ALL_MESSAGES;
|
|
150
|
+
exports.isMessagesDeltaReducer = isMessagesDeltaReducer;
|
|
144
151
|
exports.messagesDeltaReducer = messagesDeltaReducer;
|
|
145
152
|
exports.messagesStateReducer = messagesStateReducer;
|
|
146
153
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages_reducer.cjs","names":["coerceMessageLikeToMessage","RemoveMessage","BaseMessage"],"sources":["../../src/graph/messages_reducer.ts"],"sourcesContent":["import {\n BaseMessage,\n BaseMessageLike,\n coerceMessageLikeToMessage,\n RemoveMessage,\n} from \"@langchain/core/messages\";\nimport { v4 } from \"@langchain/core/utils/uuid\";\n\n/**\n * Special value that signifies the intent to remove all previous messages in the state reducer.\n * Used as the unique identifier for a `RemoveMessage` instance which, when encountered,\n * causes all prior messages to be discarded, leaving only those following this marker.\n */\nexport const REMOVE_ALL_MESSAGES = \"__remove_all__\";\n\n/**\n * Type that represents an acceptable input for the messages state reducer.\n *\n * - Can be a single `BaseMessage` or `BaseMessageLike`.\n * - Can be an array of `BaseMessage` or `BaseMessageLike`.\n */\nexport type Messages =\n | Array<BaseMessage | BaseMessageLike>\n | BaseMessage\n | BaseMessageLike;\n\n/**\n * Reducer function for combining two sets of messages in LangGraph's state system.\n *\n * This reducer handles several tasks:\n * 1. Normalizes both `left` and `right` message inputs to arrays.\n * 2. Coerces any message-like objects into real `BaseMessage` instances.\n * 3. Ensures all messages have unique, stable IDs by generating missing ones.\n * 4. If a `RemoveMessage` instance is encountered in `right` with the ID `REMOVE_ALL_MESSAGES`,\n * all previous messages are discarded and only the subsequent messages in `right` are returned.\n * 5. Otherwise, merges `left` and `right` messages together following these rules:\n * - If a message in `right` shares an ID with a message in `left`:\n * - If it is a `RemoveMessage`, that message (by ID) is marked for removal.\n * - If it is a normal message, it replaces the message with the same ID from `left`.\n * - If a message in `right` **does not exist** in `left`:\n * - If it is a `RemoveMessage`, this is considered an error (cannot remove non-existent ID).\n * - Otherwise, the message is appended.\n * - Messages flagged for removal are omitted from the final output.\n *\n * @param left - The existing array (or single message) of messages from current state.\n * @param right - The new array (or single message) of messages to be applied.\n * @returns A new array of `BaseMessage` objects representing the updated state.\n *\n * @throws Error if a `RemoveMessage` is used to delete a message with an ID that does not exist in the merged list.\n *\n * @example\n * ```ts\n * const msg1 = new AIMessage(\"hello\");\n * const msg2 = new HumanMessage(\"hi\");\n * const removal = new RemoveMessage({ id: msg1.id });\n * const newState = messagesStateReducer([msg1], [msg2, removal]);\n * // newState will only contain msg2 (msg1 is removed)\n * ```\n */\nexport function messagesStateReducer(\n left: Messages,\n right: Messages\n): BaseMessage[] {\n // Ensure both left and right are arrays\n const leftArray = Array.isArray(left) ? left : [left];\n const rightArray = Array.isArray(right) ? right : [right];\n\n // Convert all input to BaseMessage instances\n const leftMessages = (leftArray as BaseMessageLike[]).map(\n coerceMessageLikeToMessage\n );\n const rightMessages = (rightArray as BaseMessageLike[]).map(\n coerceMessageLikeToMessage\n );\n\n // Assign missing IDs to any message in the left array\n for (const m of leftMessages) {\n if (m.id === null || m.id === undefined) {\n m.id = v4();\n m.lc_kwargs.id = m.id;\n }\n }\n\n // Assign missing IDs and check for \"remove all\" marker in right array\n let removeAllIdx: number | undefined;\n for (let i = 0; i < rightMessages.length; i += 1) {\n const m = rightMessages[i];\n if (m.id === null || m.id === undefined) {\n m.id = v4();\n m.lc_kwargs.id = m.id;\n }\n\n // If RemoveMessage with special REMOVE_ALL_MESSAGES id is found\n if (RemoveMessage.isInstance(m) && m.id === REMOVE_ALL_MESSAGES) {\n removeAllIdx = i;\n }\n }\n\n // If remove-all is present, all previous messages are wiped; return only subsequent ones\n if (removeAllIdx != null) return rightMessages.slice(removeAllIdx + 1);\n\n // Begin normal merging logic\n const merged = [...leftMessages];\n const mergedById = new Map(merged.map((m, i) => [m.id, i]));\n const idsToRemove = new Set();\n\n for (const m of rightMessages) {\n const existingIdx = mergedById.get(m.id);\n if (existingIdx !== undefined) {\n // Case: updating or removing an existing message by id\n if (RemoveMessage.isInstance(m)) {\n idsToRemove.add(m.id);\n } else {\n idsToRemove.delete(m.id);\n merged[existingIdx] = m;\n }\n } else {\n // Case: inserting a completely new message\n if (RemoveMessage.isInstance(m)) {\n throw new Error(\n `Attempting to delete a message with an ID that doesn't exist ('${m.id}')`\n );\n }\n mergedById.set(m.id, merged.length);\n merged.push(m);\n }\n }\n\n // Remove any messages whose IDs are marked for removal\n return merged.filter((m) => !idsToRemove.has(m.id));\n}\n\n/**\n * **Experimental.** Batch reducer for use with `DeltaChannel`.\n *\n * Processes all writes in one pass — dedup by ID and `RemoveMessage`\n * tombstoning — without calling {@link messagesStateReducer}.\n *\n * This reducer is batching-invariant, as required by `DeltaChannel`:\n * `reducer(reducer(state, xs), ys) === reducer(state, xs.concat(ys))`.\n *\n * A `RemoveMessage` carrying the {@link REMOVE_ALL_MESSAGES} sentinel id\n * clears all messages accumulated so far (prior state plus earlier writes in\n * the same batch) and keeps only the messages that follow it, mirroring\n * {@link messagesStateReducer}. Clearing happens in the same single linear\n * pass, so the batching-invariant still holds.\n *\n * Raw object / string inputs are coerced to typed `BaseMessage` objects so\n * that HTTP-driven graphs work without a separate coercion step. This is not\n * full {@link messagesStateReducer} parity — unknown-id `RemoveMessage`\n * errors and missing-id UUID assignment are not handled here.\n *\n * @param state - The current accumulated list of messages.\n * @param writes - Batch of writes, each a single message-like or an array.\n * @returns The new accumulated list of messages.\n *\n * @example\n * ```typescript\n * import { DeltaChannel, messagesDeltaReducer } from \"@langchain/langgraph\";\n *\n * const channel = new DeltaChannel(messagesDeltaReducer);\n * ```\n */\nexport function messagesDeltaReducer(\n state: BaseMessage[],\n writes: Messages[]\n): BaseMessage[] {\n // Each write is either an array of message-likes or a single message-like.\n // Only arrays flatten; everything else is one message.\n const flat: BaseMessageLike[] = [];\n for (const w of writes) {\n if (Array.isArray(w)) {\n flat.push(...(w as BaseMessageLike[]));\n } else {\n flat.push(w as BaseMessageLike);\n }\n }\n\n // Steady state: the reducer's own output is already typed, so skip coercion\n // on state when the first element is a BaseMessage. Only raw input (initial\n // objects, deserialized blobs) hits the slow path.\n const stateMsgs: BaseMessage[] =\n state.length > 0 && BaseMessage.isInstance(state[0])\n ? state\n : (state as BaseMessageLike[]).map(coerceMessageLikeToMessage);\n const msgs: BaseMessage[] = flat.map(coerceMessageLikeToMessage);\n\n const index = new Map<string, number>();\n for (let i = 0; i < stateMsgs.length; i += 1) {\n const mid = stateMsgs[i].id;\n if (mid != null) index.set(mid, i);\n }\n\n const result: (BaseMessage | null)[] = [...stateMsgs];\n for (const msg of msgs) {\n const mid = msg.id;\n if (RemoveMessage.isInstance(msg) && mid === REMOVE_ALL_MESSAGES) {\n // Discard everything accumulated so far (prior state and earlier writes\n // in this batch); only messages following the sentinel are kept. Doing\n // this inline keeps the reducer batching-invariant.\n result.length = 0;\n index.clear();\n } else if (mid == null) {\n result.push(msg);\n } else if (RemoveMessage.isInstance(msg)) {\n if (index.has(mid)) {\n result[index.get(mid)!] = null;\n index.delete(mid);\n }\n } else if (index.has(mid)) {\n result[index.get(mid)!] = msg;\n } else {\n index.set(mid, result.length);\n result.push(msg);\n }\n }\n return result.filter((m): m is BaseMessage => m !== null);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"messages_reducer.cjs","names":["coerceMessageLikeToMessage","RemoveMessage","BaseMessage"],"sources":["../../src/graph/messages_reducer.ts"],"sourcesContent":["import {\n BaseMessage,\n BaseMessageLike,\n coerceMessageLikeToMessage,\n RemoveMessage,\n} from \"@langchain/core/messages\";\nimport { v4 } from \"@langchain/core/utils/uuid\";\n\n/**\n * Special value that signifies the intent to remove all previous messages in the state reducer.\n * Used as the unique identifier for a `RemoveMessage` instance which, when encountered,\n * causes all prior messages to be discarded, leaving only those following this marker.\n */\nexport const REMOVE_ALL_MESSAGES = \"__remove_all__\";\n\n/**\n * Type that represents an acceptable input for the messages state reducer.\n *\n * - Can be a single `BaseMessage` or `BaseMessageLike`.\n * - Can be an array of `BaseMessage` or `BaseMessageLike`.\n */\nexport type Messages =\n | Array<BaseMessage | BaseMessageLike>\n | BaseMessage\n | BaseMessageLike;\n\n/**\n * Reducer function for combining two sets of messages in LangGraph's state system.\n *\n * This reducer handles several tasks:\n * 1. Normalizes both `left` and `right` message inputs to arrays.\n * 2. Coerces any message-like objects into real `BaseMessage` instances.\n * 3. Ensures all messages have unique, stable IDs by generating missing ones.\n * 4. If a `RemoveMessage` instance is encountered in `right` with the ID `REMOVE_ALL_MESSAGES`,\n * all previous messages are discarded and only the subsequent messages in `right` are returned.\n * 5. Otherwise, merges `left` and `right` messages together following these rules:\n * - If a message in `right` shares an ID with a message in `left`:\n * - If it is a `RemoveMessage`, that message (by ID) is marked for removal.\n * - If it is a normal message, it replaces the message with the same ID from `left`.\n * - If a message in `right` **does not exist** in `left`:\n * - If it is a `RemoveMessage`, this is considered an error (cannot remove non-existent ID).\n * - Otherwise, the message is appended.\n * - Messages flagged for removal are omitted from the final output.\n *\n * @param left - The existing array (or single message) of messages from current state.\n * @param right - The new array (or single message) of messages to be applied.\n * @returns A new array of `BaseMessage` objects representing the updated state.\n *\n * @throws Error if a `RemoveMessage` is used to delete a message with an ID that does not exist in the merged list.\n *\n * @example\n * ```ts\n * const msg1 = new AIMessage(\"hello\");\n * const msg2 = new HumanMessage(\"hi\");\n * const removal = new RemoveMessage({ id: msg1.id });\n * const newState = messagesStateReducer([msg1], [msg2, removal]);\n * // newState will only contain msg2 (msg1 is removed)\n * ```\n */\nexport function messagesStateReducer(\n left: Messages,\n right: Messages\n): BaseMessage[] {\n // Ensure both left and right are arrays\n const leftArray = Array.isArray(left) ? left : [left];\n const rightArray = Array.isArray(right) ? right : [right];\n\n // Convert all input to BaseMessage instances\n const leftMessages = (leftArray as BaseMessageLike[]).map(\n coerceMessageLikeToMessage\n );\n const rightMessages = (rightArray as BaseMessageLike[]).map(\n coerceMessageLikeToMessage\n );\n\n // Assign missing IDs to any message in the left array\n for (const m of leftMessages) {\n if (m.id === null || m.id === undefined) {\n m.id = v4();\n m.lc_kwargs.id = m.id;\n }\n }\n\n // Assign missing IDs and check for \"remove all\" marker in right array\n let removeAllIdx: number | undefined;\n for (let i = 0; i < rightMessages.length; i += 1) {\n const m = rightMessages[i];\n if (m.id === null || m.id === undefined) {\n m.id = v4();\n m.lc_kwargs.id = m.id;\n }\n\n // If RemoveMessage with special REMOVE_ALL_MESSAGES id is found\n if (RemoveMessage.isInstance(m) && m.id === REMOVE_ALL_MESSAGES) {\n removeAllIdx = i;\n }\n }\n\n // If remove-all is present, all previous messages are wiped; return only subsequent ones\n if (removeAllIdx != null) return rightMessages.slice(removeAllIdx + 1);\n\n // Begin normal merging logic\n const merged = [...leftMessages];\n const mergedById = new Map(merged.map((m, i) => [m.id, i]));\n const idsToRemove = new Set();\n\n for (const m of rightMessages) {\n const existingIdx = mergedById.get(m.id);\n if (existingIdx !== undefined) {\n // Case: updating or removing an existing message by id\n if (RemoveMessage.isInstance(m)) {\n idsToRemove.add(m.id);\n } else {\n idsToRemove.delete(m.id);\n merged[existingIdx] = m;\n }\n } else {\n // Case: inserting a completely new message\n if (RemoveMessage.isInstance(m)) {\n throw new Error(\n `Attempting to delete a message with an ID that doesn't exist ('${m.id}')`\n );\n }\n mergedById.set(m.id, merged.length);\n merged.push(m);\n }\n }\n\n // Remove any messages whose IDs are marked for removal\n return merged.filter((m) => !idsToRemove.has(m.id));\n}\n\n/**\n * **Experimental.** Batch reducer for use with `DeltaChannel`.\n *\n * Processes all writes in one pass — dedup by ID and `RemoveMessage`\n * tombstoning — without calling {@link messagesStateReducer}.\n *\n * This reducer is batching-invariant, as required by `DeltaChannel`:\n * `reducer(reducer(state, xs), ys) === reducer(state, xs.concat(ys))`.\n *\n * A `RemoveMessage` carrying the {@link REMOVE_ALL_MESSAGES} sentinel id\n * clears all messages accumulated so far (prior state plus earlier writes in\n * the same batch) and keeps only the messages that follow it, mirroring\n * {@link messagesStateReducer}. Clearing happens in the same single linear\n * pass, so the batching-invariant still holds.\n *\n * Raw object / string inputs are coerced to typed `BaseMessage` objects so\n * that HTTP-driven graphs work without a separate coercion step. This is not\n * full {@link messagesStateReducer} parity — unknown-id `RemoveMessage`\n * errors and missing-id UUID assignment are not handled here.\n *\n * @param state - The current accumulated list of messages.\n * @param writes - Batch of writes, each a single message-like or an array.\n * @returns The new accumulated list of messages.\n *\n * @example\n * ```typescript\n * import { DeltaChannel, messagesDeltaReducer } from \"@langchain/langgraph\";\n *\n * const channel = new DeltaChannel(messagesDeltaReducer);\n * ```\n */\nexport function messagesDeltaReducer(\n state: BaseMessage[],\n writes: Messages[]\n): BaseMessage[] {\n // Each write is either an array of message-likes or a single message-like.\n // Only arrays flatten; everything else is one message.\n const flat: BaseMessageLike[] = [];\n for (const w of writes) {\n if (Array.isArray(w)) {\n flat.push(...(w as BaseMessageLike[]));\n } else {\n flat.push(w as BaseMessageLike);\n }\n }\n\n // Steady state: the reducer's own output is already typed, so skip coercion\n // on state when the first element is a BaseMessage. Only raw input (initial\n // objects, deserialized blobs) hits the slow path.\n const stateMsgs: BaseMessage[] =\n state.length > 0 && BaseMessage.isInstance(state[0])\n ? state\n : (state as BaseMessageLike[]).map(coerceMessageLikeToMessage);\n const msgs: BaseMessage[] = flat.map(coerceMessageLikeToMessage);\n\n const index = new Map<string, number>();\n for (let i = 0; i < stateMsgs.length; i += 1) {\n const mid = stateMsgs[i].id;\n if (mid != null) index.set(mid, i);\n }\n\n const result: (BaseMessage | null)[] = [...stateMsgs];\n for (const msg of msgs) {\n const mid = msg.id;\n if (RemoveMessage.isInstance(msg) && mid === REMOVE_ALL_MESSAGES) {\n // Discard everything accumulated so far (prior state and earlier writes\n // in this batch); only messages following the sentinel are kept. Doing\n // this inline keeps the reducer batching-invariant.\n result.length = 0;\n index.clear();\n } else if (mid == null) {\n result.push(msg);\n } else if (RemoveMessage.isInstance(msg)) {\n if (index.has(mid)) {\n result[index.get(mid)!] = null;\n index.delete(mid);\n }\n } else if (index.has(mid)) {\n result[index.get(mid)!] = msg;\n } else {\n index.set(mid, result.length);\n result.push(msg);\n }\n }\n return result.filter((m): m is BaseMessage => m !== null);\n}\n\n// A brand rather than reference equality, so the reducer is still recognized\n// from another copy of this package (its ESM and CJS builds both loaded, or a\n// copy nested under another package).\nconst MESSAGES_DELTA_REDUCER = Symbol.for(\"langgraph.messages_delta_reducer\");\nObject.defineProperty(messagesDeltaReducer, MESSAGES_DELTA_REDUCER, {\n value: true,\n});\n\n/** @internal */\nexport function isMessagesDeltaReducer(reducer: unknown): boolean {\n return (\n typeof reducer === \"function\" &&\n (reducer as unknown as Record<symbol, unknown>)[MESSAGES_DELTA_REDUCER] ===\n true\n );\n}\n"],"mappings":"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"}
|
|
@@ -139,7 +139,13 @@ function messagesDeltaReducer(state, writes) {
|
|
|
139
139
|
}
|
|
140
140
|
return result.filter((m) => m !== null);
|
|
141
141
|
}
|
|
142
|
+
const MESSAGES_DELTA_REDUCER = Symbol.for("langgraph.messages_delta_reducer");
|
|
143
|
+
Object.defineProperty(messagesDeltaReducer, MESSAGES_DELTA_REDUCER, { value: true });
|
|
144
|
+
/** @internal */
|
|
145
|
+
function isMessagesDeltaReducer(reducer) {
|
|
146
|
+
return typeof reducer === "function" && reducer[MESSAGES_DELTA_REDUCER] === true;
|
|
147
|
+
}
|
|
142
148
|
//#endregion
|
|
143
|
-
export { REMOVE_ALL_MESSAGES, messagesDeltaReducer, messagesStateReducer };
|
|
149
|
+
export { REMOVE_ALL_MESSAGES, isMessagesDeltaReducer, messagesDeltaReducer, messagesStateReducer };
|
|
144
150
|
|
|
145
151
|
//# sourceMappingURL=messages_reducer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages_reducer.js","names":[],"sources":["../../src/graph/messages_reducer.ts"],"sourcesContent":["import {\n BaseMessage,\n BaseMessageLike,\n coerceMessageLikeToMessage,\n RemoveMessage,\n} from \"@langchain/core/messages\";\nimport { v4 } from \"@langchain/core/utils/uuid\";\n\n/**\n * Special value that signifies the intent to remove all previous messages in the state reducer.\n * Used as the unique identifier for a `RemoveMessage` instance which, when encountered,\n * causes all prior messages to be discarded, leaving only those following this marker.\n */\nexport const REMOVE_ALL_MESSAGES = \"__remove_all__\";\n\n/**\n * Type that represents an acceptable input for the messages state reducer.\n *\n * - Can be a single `BaseMessage` or `BaseMessageLike`.\n * - Can be an array of `BaseMessage` or `BaseMessageLike`.\n */\nexport type Messages =\n | Array<BaseMessage | BaseMessageLike>\n | BaseMessage\n | BaseMessageLike;\n\n/**\n * Reducer function for combining two sets of messages in LangGraph's state system.\n *\n * This reducer handles several tasks:\n * 1. Normalizes both `left` and `right` message inputs to arrays.\n * 2. Coerces any message-like objects into real `BaseMessage` instances.\n * 3. Ensures all messages have unique, stable IDs by generating missing ones.\n * 4. If a `RemoveMessage` instance is encountered in `right` with the ID `REMOVE_ALL_MESSAGES`,\n * all previous messages are discarded and only the subsequent messages in `right` are returned.\n * 5. Otherwise, merges `left` and `right` messages together following these rules:\n * - If a message in `right` shares an ID with a message in `left`:\n * - If it is a `RemoveMessage`, that message (by ID) is marked for removal.\n * - If it is a normal message, it replaces the message with the same ID from `left`.\n * - If a message in `right` **does not exist** in `left`:\n * - If it is a `RemoveMessage`, this is considered an error (cannot remove non-existent ID).\n * - Otherwise, the message is appended.\n * - Messages flagged for removal are omitted from the final output.\n *\n * @param left - The existing array (or single message) of messages from current state.\n * @param right - The new array (or single message) of messages to be applied.\n * @returns A new array of `BaseMessage` objects representing the updated state.\n *\n * @throws Error if a `RemoveMessage` is used to delete a message with an ID that does not exist in the merged list.\n *\n * @example\n * ```ts\n * const msg1 = new AIMessage(\"hello\");\n * const msg2 = new HumanMessage(\"hi\");\n * const removal = new RemoveMessage({ id: msg1.id });\n * const newState = messagesStateReducer([msg1], [msg2, removal]);\n * // newState will only contain msg2 (msg1 is removed)\n * ```\n */\nexport function messagesStateReducer(\n left: Messages,\n right: Messages\n): BaseMessage[] {\n // Ensure both left and right are arrays\n const leftArray = Array.isArray(left) ? left : [left];\n const rightArray = Array.isArray(right) ? right : [right];\n\n // Convert all input to BaseMessage instances\n const leftMessages = (leftArray as BaseMessageLike[]).map(\n coerceMessageLikeToMessage\n );\n const rightMessages = (rightArray as BaseMessageLike[]).map(\n coerceMessageLikeToMessage\n );\n\n // Assign missing IDs to any message in the left array\n for (const m of leftMessages) {\n if (m.id === null || m.id === undefined) {\n m.id = v4();\n m.lc_kwargs.id = m.id;\n }\n }\n\n // Assign missing IDs and check for \"remove all\" marker in right array\n let removeAllIdx: number | undefined;\n for (let i = 0; i < rightMessages.length; i += 1) {\n const m = rightMessages[i];\n if (m.id === null || m.id === undefined) {\n m.id = v4();\n m.lc_kwargs.id = m.id;\n }\n\n // If RemoveMessage with special REMOVE_ALL_MESSAGES id is found\n if (RemoveMessage.isInstance(m) && m.id === REMOVE_ALL_MESSAGES) {\n removeAllIdx = i;\n }\n }\n\n // If remove-all is present, all previous messages are wiped; return only subsequent ones\n if (removeAllIdx != null) return rightMessages.slice(removeAllIdx + 1);\n\n // Begin normal merging logic\n const merged = [...leftMessages];\n const mergedById = new Map(merged.map((m, i) => [m.id, i]));\n const idsToRemove = new Set();\n\n for (const m of rightMessages) {\n const existingIdx = mergedById.get(m.id);\n if (existingIdx !== undefined) {\n // Case: updating or removing an existing message by id\n if (RemoveMessage.isInstance(m)) {\n idsToRemove.add(m.id);\n } else {\n idsToRemove.delete(m.id);\n merged[existingIdx] = m;\n }\n } else {\n // Case: inserting a completely new message\n if (RemoveMessage.isInstance(m)) {\n throw new Error(\n `Attempting to delete a message with an ID that doesn't exist ('${m.id}')`\n );\n }\n mergedById.set(m.id, merged.length);\n merged.push(m);\n }\n }\n\n // Remove any messages whose IDs are marked for removal\n return merged.filter((m) => !idsToRemove.has(m.id));\n}\n\n/**\n * **Experimental.** Batch reducer for use with `DeltaChannel`.\n *\n * Processes all writes in one pass — dedup by ID and `RemoveMessage`\n * tombstoning — without calling {@link messagesStateReducer}.\n *\n * This reducer is batching-invariant, as required by `DeltaChannel`:\n * `reducer(reducer(state, xs), ys) === reducer(state, xs.concat(ys))`.\n *\n * A `RemoveMessage` carrying the {@link REMOVE_ALL_MESSAGES} sentinel id\n * clears all messages accumulated so far (prior state plus earlier writes in\n * the same batch) and keeps only the messages that follow it, mirroring\n * {@link messagesStateReducer}. Clearing happens in the same single linear\n * pass, so the batching-invariant still holds.\n *\n * Raw object / string inputs are coerced to typed `BaseMessage` objects so\n * that HTTP-driven graphs work without a separate coercion step. This is not\n * full {@link messagesStateReducer} parity — unknown-id `RemoveMessage`\n * errors and missing-id UUID assignment are not handled here.\n *\n * @param state - The current accumulated list of messages.\n * @param writes - Batch of writes, each a single message-like or an array.\n * @returns The new accumulated list of messages.\n *\n * @example\n * ```typescript\n * import { DeltaChannel, messagesDeltaReducer } from \"@langchain/langgraph\";\n *\n * const channel = new DeltaChannel(messagesDeltaReducer);\n * ```\n */\nexport function messagesDeltaReducer(\n state: BaseMessage[],\n writes: Messages[]\n): BaseMessage[] {\n // Each write is either an array of message-likes or a single message-like.\n // Only arrays flatten; everything else is one message.\n const flat: BaseMessageLike[] = [];\n for (const w of writes) {\n if (Array.isArray(w)) {\n flat.push(...(w as BaseMessageLike[]));\n } else {\n flat.push(w as BaseMessageLike);\n }\n }\n\n // Steady state: the reducer's own output is already typed, so skip coercion\n // on state when the first element is a BaseMessage. Only raw input (initial\n // objects, deserialized blobs) hits the slow path.\n const stateMsgs: BaseMessage[] =\n state.length > 0 && BaseMessage.isInstance(state[0])\n ? state\n : (state as BaseMessageLike[]).map(coerceMessageLikeToMessage);\n const msgs: BaseMessage[] = flat.map(coerceMessageLikeToMessage);\n\n const index = new Map<string, number>();\n for (let i = 0; i < stateMsgs.length; i += 1) {\n const mid = stateMsgs[i].id;\n if (mid != null) index.set(mid, i);\n }\n\n const result: (BaseMessage | null)[] = [...stateMsgs];\n for (const msg of msgs) {\n const mid = msg.id;\n if (RemoveMessage.isInstance(msg) && mid === REMOVE_ALL_MESSAGES) {\n // Discard everything accumulated so far (prior state and earlier writes\n // in this batch); only messages following the sentinel are kept. Doing\n // this inline keeps the reducer batching-invariant.\n result.length = 0;\n index.clear();\n } else if (mid == null) {\n result.push(msg);\n } else if (RemoveMessage.isInstance(msg)) {\n if (index.has(mid)) {\n result[index.get(mid)!] = null;\n index.delete(mid);\n }\n } else if (index.has(mid)) {\n result[index.get(mid)!] = msg;\n } else {\n index.set(mid, result.length);\n result.push(msg);\n }\n }\n return result.filter((m): m is BaseMessage => m !== null);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"messages_reducer.js","names":[],"sources":["../../src/graph/messages_reducer.ts"],"sourcesContent":["import {\n BaseMessage,\n BaseMessageLike,\n coerceMessageLikeToMessage,\n RemoveMessage,\n} from \"@langchain/core/messages\";\nimport { v4 } from \"@langchain/core/utils/uuid\";\n\n/**\n * Special value that signifies the intent to remove all previous messages in the state reducer.\n * Used as the unique identifier for a `RemoveMessage` instance which, when encountered,\n * causes all prior messages to be discarded, leaving only those following this marker.\n */\nexport const REMOVE_ALL_MESSAGES = \"__remove_all__\";\n\n/**\n * Type that represents an acceptable input for the messages state reducer.\n *\n * - Can be a single `BaseMessage` or `BaseMessageLike`.\n * - Can be an array of `BaseMessage` or `BaseMessageLike`.\n */\nexport type Messages =\n | Array<BaseMessage | BaseMessageLike>\n | BaseMessage\n | BaseMessageLike;\n\n/**\n * Reducer function for combining two sets of messages in LangGraph's state system.\n *\n * This reducer handles several tasks:\n * 1. Normalizes both `left` and `right` message inputs to arrays.\n * 2. Coerces any message-like objects into real `BaseMessage` instances.\n * 3. Ensures all messages have unique, stable IDs by generating missing ones.\n * 4. If a `RemoveMessage` instance is encountered in `right` with the ID `REMOVE_ALL_MESSAGES`,\n * all previous messages are discarded and only the subsequent messages in `right` are returned.\n * 5. Otherwise, merges `left` and `right` messages together following these rules:\n * - If a message in `right` shares an ID with a message in `left`:\n * - If it is a `RemoveMessage`, that message (by ID) is marked for removal.\n * - If it is a normal message, it replaces the message with the same ID from `left`.\n * - If a message in `right` **does not exist** in `left`:\n * - If it is a `RemoveMessage`, this is considered an error (cannot remove non-existent ID).\n * - Otherwise, the message is appended.\n * - Messages flagged for removal are omitted from the final output.\n *\n * @param left - The existing array (or single message) of messages from current state.\n * @param right - The new array (or single message) of messages to be applied.\n * @returns A new array of `BaseMessage` objects representing the updated state.\n *\n * @throws Error if a `RemoveMessage` is used to delete a message with an ID that does not exist in the merged list.\n *\n * @example\n * ```ts\n * const msg1 = new AIMessage(\"hello\");\n * const msg2 = new HumanMessage(\"hi\");\n * const removal = new RemoveMessage({ id: msg1.id });\n * const newState = messagesStateReducer([msg1], [msg2, removal]);\n * // newState will only contain msg2 (msg1 is removed)\n * ```\n */\nexport function messagesStateReducer(\n left: Messages,\n right: Messages\n): BaseMessage[] {\n // Ensure both left and right are arrays\n const leftArray = Array.isArray(left) ? left : [left];\n const rightArray = Array.isArray(right) ? right : [right];\n\n // Convert all input to BaseMessage instances\n const leftMessages = (leftArray as BaseMessageLike[]).map(\n coerceMessageLikeToMessage\n );\n const rightMessages = (rightArray as BaseMessageLike[]).map(\n coerceMessageLikeToMessage\n );\n\n // Assign missing IDs to any message in the left array\n for (const m of leftMessages) {\n if (m.id === null || m.id === undefined) {\n m.id = v4();\n m.lc_kwargs.id = m.id;\n }\n }\n\n // Assign missing IDs and check for \"remove all\" marker in right array\n let removeAllIdx: number | undefined;\n for (let i = 0; i < rightMessages.length; i += 1) {\n const m = rightMessages[i];\n if (m.id === null || m.id === undefined) {\n m.id = v4();\n m.lc_kwargs.id = m.id;\n }\n\n // If RemoveMessage with special REMOVE_ALL_MESSAGES id is found\n if (RemoveMessage.isInstance(m) && m.id === REMOVE_ALL_MESSAGES) {\n removeAllIdx = i;\n }\n }\n\n // If remove-all is present, all previous messages are wiped; return only subsequent ones\n if (removeAllIdx != null) return rightMessages.slice(removeAllIdx + 1);\n\n // Begin normal merging logic\n const merged = [...leftMessages];\n const mergedById = new Map(merged.map((m, i) => [m.id, i]));\n const idsToRemove = new Set();\n\n for (const m of rightMessages) {\n const existingIdx = mergedById.get(m.id);\n if (existingIdx !== undefined) {\n // Case: updating or removing an existing message by id\n if (RemoveMessage.isInstance(m)) {\n idsToRemove.add(m.id);\n } else {\n idsToRemove.delete(m.id);\n merged[existingIdx] = m;\n }\n } else {\n // Case: inserting a completely new message\n if (RemoveMessage.isInstance(m)) {\n throw new Error(\n `Attempting to delete a message with an ID that doesn't exist ('${m.id}')`\n );\n }\n mergedById.set(m.id, merged.length);\n merged.push(m);\n }\n }\n\n // Remove any messages whose IDs are marked for removal\n return merged.filter((m) => !idsToRemove.has(m.id));\n}\n\n/**\n * **Experimental.** Batch reducer for use with `DeltaChannel`.\n *\n * Processes all writes in one pass — dedup by ID and `RemoveMessage`\n * tombstoning — without calling {@link messagesStateReducer}.\n *\n * This reducer is batching-invariant, as required by `DeltaChannel`:\n * `reducer(reducer(state, xs), ys) === reducer(state, xs.concat(ys))`.\n *\n * A `RemoveMessage` carrying the {@link REMOVE_ALL_MESSAGES} sentinel id\n * clears all messages accumulated so far (prior state plus earlier writes in\n * the same batch) and keeps only the messages that follow it, mirroring\n * {@link messagesStateReducer}. Clearing happens in the same single linear\n * pass, so the batching-invariant still holds.\n *\n * Raw object / string inputs are coerced to typed `BaseMessage` objects so\n * that HTTP-driven graphs work without a separate coercion step. This is not\n * full {@link messagesStateReducer} parity — unknown-id `RemoveMessage`\n * errors and missing-id UUID assignment are not handled here.\n *\n * @param state - The current accumulated list of messages.\n * @param writes - Batch of writes, each a single message-like or an array.\n * @returns The new accumulated list of messages.\n *\n * @example\n * ```typescript\n * import { DeltaChannel, messagesDeltaReducer } from \"@langchain/langgraph\";\n *\n * const channel = new DeltaChannel(messagesDeltaReducer);\n * ```\n */\nexport function messagesDeltaReducer(\n state: BaseMessage[],\n writes: Messages[]\n): BaseMessage[] {\n // Each write is either an array of message-likes or a single message-like.\n // Only arrays flatten; everything else is one message.\n const flat: BaseMessageLike[] = [];\n for (const w of writes) {\n if (Array.isArray(w)) {\n flat.push(...(w as BaseMessageLike[]));\n } else {\n flat.push(w as BaseMessageLike);\n }\n }\n\n // Steady state: the reducer's own output is already typed, so skip coercion\n // on state when the first element is a BaseMessage. Only raw input (initial\n // objects, deserialized blobs) hits the slow path.\n const stateMsgs: BaseMessage[] =\n state.length > 0 && BaseMessage.isInstance(state[0])\n ? state\n : (state as BaseMessageLike[]).map(coerceMessageLikeToMessage);\n const msgs: BaseMessage[] = flat.map(coerceMessageLikeToMessage);\n\n const index = new Map<string, number>();\n for (let i = 0; i < stateMsgs.length; i += 1) {\n const mid = stateMsgs[i].id;\n if (mid != null) index.set(mid, i);\n }\n\n const result: (BaseMessage | null)[] = [...stateMsgs];\n for (const msg of msgs) {\n const mid = msg.id;\n if (RemoveMessage.isInstance(msg) && mid === REMOVE_ALL_MESSAGES) {\n // Discard everything accumulated so far (prior state and earlier writes\n // in this batch); only messages following the sentinel are kept. Doing\n // this inline keeps the reducer batching-invariant.\n result.length = 0;\n index.clear();\n } else if (mid == null) {\n result.push(msg);\n } else if (RemoveMessage.isInstance(msg)) {\n if (index.has(mid)) {\n result[index.get(mid)!] = null;\n index.delete(mid);\n }\n } else if (index.has(mid)) {\n result[index.get(mid)!] = msg;\n } else {\n index.set(mid, result.length);\n result.push(msg);\n }\n }\n return result.filter((m): m is BaseMessage => m !== null);\n}\n\n// A brand rather than reference equality, so the reducer is still recognized\n// from another copy of this package (its ESM and CJS builds both loaded, or a\n// copy nested under another package).\nconst MESSAGES_DELTA_REDUCER = Symbol.for(\"langgraph.messages_delta_reducer\");\nObject.defineProperty(messagesDeltaReducer, MESSAGES_DELTA_REDUCER, {\n value: true,\n});\n\n/** @internal */\nexport function isMessagesDeltaReducer(reducer: unknown): boolean {\n return (\n typeof reducer === \"function\" &&\n (reducer as unknown as Record<symbol, unknown>)[MESSAGES_DELTA_REDUCER] ===\n true\n );\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -8,6 +8,7 @@ const require_annotation = require("./graph/annotation.cjs");
|
|
|
8
8
|
const require_config = require("./pregel/utils/config.cjs");
|
|
9
9
|
const require_callbacks = require("./callbacks.cjs");
|
|
10
10
|
const require_runtime = require("./pregel/runtime.cjs");
|
|
11
|
+
const require_messages_reducer = require("./graph/messages_reducer.cjs");
|
|
11
12
|
const require_convert = require("./stream/convert.cjs");
|
|
12
13
|
const require_stream_channel = require("./stream/stream-channel.cjs");
|
|
13
14
|
const require_lifecycle = require("./stream/transformers/lifecycle.cjs");
|
|
@@ -26,7 +27,6 @@ const require_reduced = require("./state/values/reduced.cjs");
|
|
|
26
27
|
const require_untracked = require("./state/values/untracked.cjs");
|
|
27
28
|
const require_delta$1 = require("./state/values/delta.cjs");
|
|
28
29
|
const require_schema = require("./state/schema.cjs");
|
|
29
|
-
const require_messages_reducer = require("./graph/messages_reducer.cjs");
|
|
30
30
|
const require_messages$1 = require("./state/prebuilt/messages.cjs");
|
|
31
31
|
const require_state = require("./graph/state.cjs");
|
|
32
32
|
const require_message = require("./graph/message.cjs");
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { Annotation } from "./graph/annotation.js";
|
|
|
7
7
|
import { getConfig, getCurrentTaskInput, getStore, getWriter } from "./pregel/utils/config.js";
|
|
8
8
|
import { GraphCallbackHandler } from "./callbacks.js";
|
|
9
9
|
import { RunControl } from "./pregel/runtime.js";
|
|
10
|
+
import { REMOVE_ALL_MESSAGES, messagesDeltaReducer, messagesStateReducer } from "./graph/messages_reducer.js";
|
|
10
11
|
import { STREAM_EVENTS_V3_MODES, convertToProtocolEvent, isCheckpointEnvelope } from "./stream/convert.js";
|
|
11
12
|
import { StreamChannel } from "./stream/stream-channel.js";
|
|
12
13
|
import { createLifecycleTransformer, filterLifecycleEntries } from "./stream/transformers/lifecycle.js";
|
|
@@ -26,7 +27,6 @@ import { ReducedValue } from "./state/values/reduced.js";
|
|
|
26
27
|
import { UntrackedValue } from "./state/values/untracked.js";
|
|
27
28
|
import { DeltaValue } from "./state/values/delta.js";
|
|
28
29
|
import { StateSchema } from "./state/schema.js";
|
|
29
|
-
import { REMOVE_ALL_MESSAGES, messagesDeltaReducer, messagesStateReducer } from "./graph/messages_reducer.js";
|
|
30
30
|
import { MessagesDeltaValue, MessagesValue } from "./state/prebuilt/messages.js";
|
|
31
31
|
import { CompiledStateGraph, StateGraph } from "./graph/state.js";
|
|
32
32
|
import { MessageGraph, pushMessage } from "./graph/message.js";
|
package/dist/pregel/algo.cjs
CHANGED
|
@@ -12,6 +12,28 @@ let _langchain_core_runnables = require("@langchain/core/runnables");
|
|
|
12
12
|
const increment = (current) => {
|
|
13
13
|
return current !== void 0 ? current + 1 : 1;
|
|
14
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* Serialize a task path to the string stored with its writes, matching
|
|
17
|
+
* Python's `task_path_str` byte for byte:
|
|
18
|
+
*
|
|
19
|
+
* - arrays (nested paths included) become `"~"` + their serialized elements
|
|
20
|
+
* joined by `", "` — so the empty path is `"~"`;
|
|
21
|
+
* - booleans format as ints (Python's `isinstance(x, int)` is true for
|
|
22
|
+
* `bool` there): `true` → `"0000000001"`, `false` → `"0000000000"`;
|
|
23
|
+
* - ints are zero-padded to 10 digits, sign-aware: `-1` → `"-000000001"`;
|
|
24
|
+
* - anything else (node names) is used as-is.
|
|
25
|
+
*
|
|
26
|
+
* This is the `taskPath` passed to `BaseCheckpointSaver.putWrites` and the
|
|
27
|
+
* first component of `writesSortKey`, so the stored string and its sort
|
|
28
|
+
* order are identical across the JS and Python runtimes.
|
|
29
|
+
*/
|
|
30
|
+
function taskPathStr(path) {
|
|
31
|
+
if (Array.isArray(path)) return `~${path.map(taskPathStr).join(", ")}`;
|
|
32
|
+
if (typeof path === "boolean") return path ? "0000000001" : "0000000000";
|
|
33
|
+
if (typeof path === "number") return path < 0 ? `-${String(-path).padStart(9, "0")}` : String(path).padStart(10, "0");
|
|
34
|
+
if (path === void 0 || path === null) return "~";
|
|
35
|
+
return String(path);
|
|
36
|
+
}
|
|
15
37
|
function triggersNextStep(updatedChannels, triggerToNodes) {
|
|
16
38
|
if (triggerToNodes == null) return false;
|
|
17
39
|
for (const chan of updatedChannels) if (triggerToNodes[chan]) return true;
|
|
@@ -87,17 +109,9 @@ const IGNORE = new Set([
|
|
|
87
109
|
]);
|
|
88
110
|
const RESERVED_SET = new Set(require_constants.RESERVED);
|
|
89
111
|
function _applyWrites(checkpoint, channels, tasks, getNextVersion, triggerToNodes) {
|
|
90
|
-
const
|
|
91
|
-
for (const task of tasks)
|
|
92
|
-
tasks.sort((a, b) =>
|
|
93
|
-
const aPath = pathCache.get(a);
|
|
94
|
-
const bPath = pathCache.get(b);
|
|
95
|
-
for (let i = 0; i < Math.min(aPath.length, bPath.length); i += 1) {
|
|
96
|
-
if (aPath[i] < bPath[i]) return -1;
|
|
97
|
-
if (aPath[i] > bPath[i]) return 1;
|
|
98
|
-
}
|
|
99
|
-
return aPath.length - bPath.length;
|
|
100
|
-
});
|
|
112
|
+
const sortKeys = /* @__PURE__ */ new Map();
|
|
113
|
+
for (const task of tasks) sortKeys.set(task, (0, _langchain_langgraph_checkpoint.writesSortKey)(taskPathStr(task.path)));
|
|
114
|
+
tasks.sort((a, b) => (0, _langchain_langgraph_checkpoint.compareWritesSortKeys)(sortKeys.get(a), sortKeys.get(b)));
|
|
101
115
|
const onlyChannels = require_base.getOnlyChannels(channels);
|
|
102
116
|
let bumpStep = false;
|
|
103
117
|
const channelsToConsume = /* @__PURE__ */ new Set();
|
|
@@ -110,36 +124,20 @@ function _applyWrites(checkpoint, channels, tasks, getNextVersion, triggerToNode
|
|
|
110
124
|
}
|
|
111
125
|
}
|
|
112
126
|
let maxVersion = maxChannelMapVersion(checkpoint.channel_versions);
|
|
113
|
-
let
|
|
127
|
+
let consumedVersion;
|
|
114
128
|
for (const chan of channelsToConsume) if (chan in onlyChannels && onlyChannels[chan].consume()) {
|
|
115
129
|
if (getNextVersion !== void 0) {
|
|
116
|
-
|
|
117
|
-
|
|
130
|
+
consumedVersion ??= getNextVersion(maxVersion);
|
|
131
|
+
checkpoint.channel_versions[chan] = consumedVersion;
|
|
118
132
|
}
|
|
119
133
|
}
|
|
120
134
|
const pendingWritesByChannel = {};
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
for (const [chan, val] of task.writes) if (IGNORE.has(chan)) {} else if (chan in onlyChannels) {
|
|
125
|
-
pendingWritesByChannel[chan] ??= [];
|
|
126
|
-
pendingWritesByChannel[chan].push(val);
|
|
127
|
-
pendingWriteTaskIdsByChannel[chan] ??= [];
|
|
128
|
-
pendingWriteTaskIdsByChannel[chan].push(taskId);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
for (const [chan, vals] of Object.entries(pendingWritesByChannel)) {
|
|
132
|
-
if (vals.length < 2) continue;
|
|
133
|
-
if (onlyChannels[chan]?.lc_graph_name !== "DeltaChannel") continue;
|
|
134
|
-
const taskIds = pendingWriteTaskIdsByChannel[chan];
|
|
135
|
-
const paired = vals.map((val, i) => ({
|
|
136
|
-
val,
|
|
137
|
-
taskId: taskIds[i]
|
|
138
|
-
}));
|
|
139
|
-
paired.sort((a, b) => a.taskId < b.taskId ? -1 : a.taskId > b.taskId ? 1 : 0);
|
|
140
|
-
pendingWritesByChannel[chan] = paired.map((p) => p.val);
|
|
135
|
+
for (const task of tasks) for (const [chan, val] of task.writes) if (IGNORE.has(chan)) {} else if (chan in onlyChannels) {
|
|
136
|
+
pendingWritesByChannel[chan] ??= [];
|
|
137
|
+
pendingWritesByChannel[chan].push(val);
|
|
141
138
|
}
|
|
142
|
-
if (maxVersion != null &&
|
|
139
|
+
if (maxVersion != null && consumedVersion !== void 0) maxVersion = consumedVersion;
|
|
140
|
+
const nextVersion = getNextVersion?.(maxVersion);
|
|
143
141
|
const updatedChannels = /* @__PURE__ */ new Set();
|
|
144
142
|
for (const [chan, vals] of Object.entries(pendingWritesByChannel)) if (chan in onlyChannels) {
|
|
145
143
|
const channel = onlyChannels[chan];
|
|
@@ -154,7 +152,7 @@ function _applyWrites(checkpoint, channels, tasks, getNextVersion, triggerToNode
|
|
|
154
152
|
} else throw e;
|
|
155
153
|
}
|
|
156
154
|
if (updated && getNextVersion !== void 0) {
|
|
157
|
-
checkpoint.channel_versions[chan] =
|
|
155
|
+
checkpoint.channel_versions[chan] = nextVersion;
|
|
158
156
|
if (channel.isAvailable()) updatedChannels.add(chan);
|
|
159
157
|
}
|
|
160
158
|
}
|
|
@@ -163,7 +161,7 @@ function _applyWrites(checkpoint, channels, tasks, getNextVersion, triggerToNode
|
|
|
163
161
|
const channel = onlyChannels[chan];
|
|
164
162
|
if (channel.isAvailable() && !updatedChannels.has(chan)) {
|
|
165
163
|
if (channel.update([]) && getNextVersion !== void 0) {
|
|
166
|
-
checkpoint.channel_versions[chan] =
|
|
164
|
+
checkpoint.channel_versions[chan] = nextVersion;
|
|
167
165
|
if (channel.isAvailable()) updatedChannels.add(chan);
|
|
168
166
|
}
|
|
169
167
|
}
|
|
@@ -172,7 +170,7 @@ function _applyWrites(checkpoint, channels, tasks, getNextVersion, triggerToNode
|
|
|
172
170
|
if (!Object.prototype.hasOwnProperty.call(onlyChannels, chan)) continue;
|
|
173
171
|
const channel = onlyChannels[chan];
|
|
174
172
|
if (channel.finish() && getNextVersion !== void 0) {
|
|
175
|
-
checkpoint.channel_versions[chan] =
|
|
173
|
+
checkpoint.channel_versions[chan] = nextVersion;
|
|
176
174
|
if (channel.isAvailable()) updatedChannels.add(chan);
|
|
177
175
|
}
|
|
178
176
|
}
|
|
@@ -373,11 +371,12 @@ function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processes, chan
|
|
|
373
371
|
index.toString()
|
|
374
372
|
]), checkpoint.id);
|
|
375
373
|
const taskCheckpointNamespace = `${checkpointNamespace}:${taskId}`;
|
|
374
|
+
const translatedTaskPath = [...taskPath.slice(0, 3), false];
|
|
376
375
|
let metadata = {
|
|
377
376
|
langgraph_step: step,
|
|
378
377
|
langgraph_node: packet.node,
|
|
379
378
|
langgraph_triggers: triggers,
|
|
380
|
-
langgraph_path:
|
|
379
|
+
langgraph_path: translatedTaskPath,
|
|
381
380
|
langgraph_checkpoint_ns: taskCheckpointNamespace,
|
|
382
381
|
checkpoint_ns: taskCheckpointNamespace
|
|
383
382
|
};
|
|
@@ -419,7 +418,7 @@ function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processes, chan
|
|
|
419
418
|
name: packet.node,
|
|
420
419
|
writes,
|
|
421
420
|
triggers,
|
|
422
|
-
path:
|
|
421
|
+
path: translatedTaskPath
|
|
423
422
|
}, select_, fresh_),
|
|
424
423
|
[require_constants.CONFIG_KEY_CHECKPOINTER]: checkpointer ?? configurable["__pregel_checkpointer"],
|
|
425
424
|
[require_constants.CONFIG_KEY_CHECKPOINT_MAP]: {
|
|
@@ -453,7 +452,7 @@ function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processes, chan
|
|
|
453
452
|
ttl: proc.cachePolicy.ttl
|
|
454
453
|
} : void 0,
|
|
455
454
|
id: taskId,
|
|
456
|
-
path:
|
|
455
|
+
path: translatedTaskPath,
|
|
457
456
|
writers: proc.getWriters(),
|
|
458
457
|
timeout: packet.timeout ?? proc.timeout
|
|
459
458
|
};
|
|
@@ -462,7 +461,7 @@ function _prepareSingleTask(taskPath, checkpoint, pendingWrites, processes, chan
|
|
|
462
461
|
id: taskId,
|
|
463
462
|
name: packet.node,
|
|
464
463
|
interrupts: [],
|
|
465
|
-
path:
|
|
464
|
+
path: translatedTaskPath
|
|
466
465
|
};
|
|
467
466
|
} else if (taskPath[0] === "__pregel_pull") {
|
|
468
467
|
const name = taskPath[1].toString();
|
|
@@ -624,10 +623,11 @@ function _prepareNodeErrorHandlerTask(failedTask, handlerNodeName, error, checkp
|
|
|
624
623
|
failedTask.id
|
|
625
624
|
]), checkpoint.id);
|
|
626
625
|
const taskCheckpointNamespace = `${checkpointNamespace}:${taskId}`;
|
|
626
|
+
const failedPath = failedTask.path?.slice(0, 3) ?? [];
|
|
627
627
|
const taskPath = [
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
628
|
+
failedPath[0] ?? "__pregel_push",
|
|
629
|
+
...failedPath.slice(1),
|
|
630
|
+
"node_error_handler",
|
|
631
631
|
false
|
|
632
632
|
];
|
|
633
633
|
let metadata = {
|
|
@@ -793,5 +793,6 @@ exports._prepareSingleTask = _prepareSingleTask;
|
|
|
793
793
|
exports.increment = increment;
|
|
794
794
|
exports.sanitizeUntrackedValuesInSend = sanitizeUntrackedValuesInSend;
|
|
795
795
|
exports.shouldInterrupt = shouldInterrupt;
|
|
796
|
+
exports.taskPathStr = taskPathStr;
|
|
796
797
|
|
|
797
798
|
//# sourceMappingURL=algo.cjs.map
|