@langchain/langgraph 1.4.21 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/channels/base.cjs +123 -11
  2. package/dist/channels/base.cjs.map +1 -1
  3. package/dist/channels/base.d.cts +26 -4
  4. package/dist/channels/base.d.cts.map +1 -1
  5. package/dist/channels/base.d.ts +26 -4
  6. package/dist/channels/base.d.ts.map +1 -1
  7. package/dist/channels/base.js +120 -13
  8. package/dist/channels/base.js.map +1 -1
  9. package/dist/channels/binop.cjs +2 -1
  10. package/dist/channels/binop.cjs.map +1 -1
  11. package/dist/channels/binop.d.cts.map +1 -1
  12. package/dist/channels/binop.d.ts.map +1 -1
  13. package/dist/channels/binop.js +2 -1
  14. package/dist/channels/binop.js.map +1 -1
  15. package/dist/channels/index.cjs +2 -0
  16. package/dist/channels/index.d.cts +2 -2
  17. package/dist/channels/index.d.ts +2 -2
  18. package/dist/channels/index.js +2 -2
  19. package/dist/constants.cjs +14 -0
  20. package/dist/constants.cjs.map +1 -1
  21. package/dist/constants.d.cts.map +1 -1
  22. package/dist/constants.d.ts.map +1 -1
  23. package/dist/constants.js +13 -1
  24. package/dist/constants.js.map +1 -1
  25. package/dist/errors.cjs +22 -0
  26. package/dist/errors.cjs.map +1 -1
  27. package/dist/errors.d.cts +10 -1
  28. package/dist/errors.d.cts.map +1 -1
  29. package/dist/errors.d.ts +10 -1
  30. package/dist/errors.d.ts.map +1 -1
  31. package/dist/errors.js +21 -1
  32. package/dist/errors.js.map +1 -1
  33. package/dist/graph/index.cjs +1 -1
  34. package/dist/graph/index.js +1 -1
  35. package/dist/graph/messages_reducer.cjs +30 -6
  36. package/dist/graph/messages_reducer.cjs.map +1 -1
  37. package/dist/graph/messages_reducer.d.cts +4 -2
  38. package/dist/graph/messages_reducer.d.cts.map +1 -1
  39. package/dist/graph/messages_reducer.d.ts +4 -2
  40. package/dist/graph/messages_reducer.d.ts.map +1 -1
  41. package/dist/graph/messages_reducer.js +31 -8
  42. package/dist/graph/messages_reducer.js.map +1 -1
  43. package/dist/index.cjs +3 -1
  44. package/dist/index.d.cts +2 -2
  45. package/dist/index.d.ts +2 -2
  46. package/dist/index.js +3 -3
  47. package/dist/interrupt.cjs +9 -1
  48. package/dist/interrupt.cjs.map +1 -1
  49. package/dist/interrupt.d.cts.map +1 -1
  50. package/dist/interrupt.d.ts.map +1 -1
  51. package/dist/interrupt.js +10 -2
  52. package/dist/interrupt.js.map +1 -1
  53. package/dist/pregel/algo.cjs +46 -45
  54. package/dist/pregel/algo.cjs.map +1 -1
  55. package/dist/pregel/algo.d.cts.map +1 -1
  56. package/dist/pregel/algo.d.ts.map +1 -1
  57. package/dist/pregel/algo.js +47 -47
  58. package/dist/pregel/algo.js.map +1 -1
  59. package/dist/pregel/checkpoint.cjs +70 -0
  60. package/dist/pregel/checkpoint.cjs.map +1 -0
  61. package/dist/pregel/checkpoint.js +67 -0
  62. package/dist/pregel/checkpoint.js.map +1 -0
  63. package/dist/pregel/debug.cjs +1 -0
  64. package/dist/pregel/debug.cjs.map +1 -1
  65. package/dist/pregel/debug.js +1 -0
  66. package/dist/pregel/debug.js.map +1 -1
  67. package/dist/pregel/index.cjs +87 -34
  68. package/dist/pregel/index.cjs.map +1 -1
  69. package/dist/pregel/index.d.cts.map +1 -1
  70. package/dist/pregel/index.d.ts.map +1 -1
  71. package/dist/pregel/index.js +91 -38
  72. package/dist/pregel/index.js.map +1 -1
  73. package/dist/pregel/loop.cjs +336 -113
  74. package/dist/pregel/loop.cjs.map +1 -1
  75. package/dist/pregel/loop.js +341 -119
  76. package/dist/pregel/loop.js.map +1 -1
  77. package/dist/pregel/runner.cjs +1 -1
  78. package/dist/pregel/runner.cjs.map +1 -1
  79. package/dist/pregel/runner.js +2 -2
  80. package/dist/pregel/runner.js.map +1 -1
  81. package/dist/state/prebuilt/messages.cjs +1 -1
  82. package/dist/state/prebuilt/messages.js +1 -1
  83. package/dist/web.cjs +3 -1
  84. package/dist/web.d.cts +2 -2
  85. package/dist/web.d.ts +2 -2
  86. package/dist/web.js +3 -3
  87. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -1,12 +1,13 @@
1
1
  import { initializeAsyncLocalStorageSingleton } from "./node.js";
2
2
  import { COMMAND_SYMBOL, Command, CommandInstance, END, INTERRUPT, Overwrite, START, Send, isCommand, isInterrupted } from "./constants.js";
3
- import { BaseLangGraphError, EmptyChannelError, EmptyInputError, GraphBubbleUp, GraphDrained, GraphInterrupt, GraphRecursionError, GraphValueError, InvalidUpdateError, MultipleSubgraphsError, NodeError, NodeInterrupt, NodeTimeoutError, ParentCommand, RemoteException, StateGraphInputError, UnreachableNodeError, getSubgraphsSeenSet, isGraphBubbleUp, isGraphDrained, isGraphInterrupt, isNodeError, isNodeTimeoutError, isParentCommand } from "./errors.js";
3
+ import { BaseLangGraphError, EmptyChannelError, EmptyInputError, GraphBubbleUp, GraphDrained, GraphInterrupt, GraphRecursionError, GraphValueError, InvalidUpdateError, MultipleSubgraphsError, NodeError, NodeInterrupt, NodeTimeoutError, ParentCommand, RemoteException, StateGraphInputError, UnreachableNodeError, _markInvalidResume, getSubgraphsSeenSet, isGraphBubbleUp, isGraphDrained, isGraphInterrupt, isInvalidResume, isNodeError, isNodeTimeoutError, isParentCommand } from "./errors.js";
4
4
  import { BaseChannel } from "./channels/base.js";
5
5
  import { BinaryOperatorAggregate } from "./channels/binop.js";
6
6
  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";
@@ -37,6 +37,6 @@ import { AsyncBatchedStore, BaseCheckpointSaver, BaseStore, InMemoryStore, Memor
37
37
  //#region src/index.ts
38
38
  initializeAsyncLocalStorageSingleton();
39
39
  //#endregion
40
- export { Annotation, AsyncBatchedStore, BaseChannel, BaseCheckpointSaver, BaseLangGraphError, BaseStore, BinaryOperatorAggregate, COMMAND_SYMBOL, ChatModelStreamImpl, Command, CommandInstance, CompiledStateGraph, DeltaChannel, DeltaValue, END, EmptyChannelError, EmptyInputError, StreamChannel as EventLog, Graph, GraphBubbleUp, GraphCallbackHandler, GraphDrained, GraphInterrupt, GraphRecursionError, GraphRunStream, GraphValueError, INTERRUPT, InMemoryStore, InvalidUpdateError, MemorySaver, MessageGraph, MessagesAnnotation, MessagesDeltaValue, MessagesValue, MessagesZodMeta, MessagesZodState, MultipleSubgraphsError, NodeError, NodeInterrupt, NodeTimeoutError, Overwrite, ParentCommand, REMOVE_ALL_MESSAGES, ReducedValue, RemoteException, RunControl, START, STREAM_EVENTS_V3_MODES, Send, StateGraph, StateGraphInputError, StateSchema, StreamChannel, SubgraphRunStream, UnreachableNodeError, UntrackedValue, UntrackedValueChannel, messagesStateReducer as addMessages, convertToProtocolEvent, copyCheckpoint, createGraphRunStream, createLifecycleTransformer, createMessagesTransformer, createSubgraphDiscoveryTransformer, createValuesTransformer, emptyCheckpoint, entrypoint, filterLifecycleEntries, filterSubgraphHandles, getConfig, getCurrentTaskInput, getJsonSchemaFromSchema, getPreviousState, getSchemaDefaultGetter, getStore, getSubgraphsSeenSet, getWriter, interrupt, isCheckpointEnvelope, isCommand, isGraphBubbleUp, isGraphDrained, isGraphInterrupt, isInterrupted, isNativeTransformer, isNodeError, isNodeTimeoutError, isParentCommand, isSerializableSchema, isStandardSchema, messagesDeltaReducer, messagesStateReducer, pushMessage, task, writer };
40
+ export { Annotation, AsyncBatchedStore, BaseChannel, BaseCheckpointSaver, BaseLangGraphError, BaseStore, BinaryOperatorAggregate, COMMAND_SYMBOL, ChatModelStreamImpl, Command, CommandInstance, CompiledStateGraph, DeltaChannel, DeltaValue, END, EmptyChannelError, EmptyInputError, StreamChannel as EventLog, Graph, GraphBubbleUp, GraphCallbackHandler, GraphDrained, GraphInterrupt, GraphRecursionError, GraphRunStream, GraphValueError, INTERRUPT, InMemoryStore, InvalidUpdateError, MemorySaver, MessageGraph, MessagesAnnotation, MessagesDeltaValue, MessagesValue, MessagesZodMeta, MessagesZodState, MultipleSubgraphsError, NodeError, NodeInterrupt, NodeTimeoutError, Overwrite, ParentCommand, REMOVE_ALL_MESSAGES, ReducedValue, RemoteException, RunControl, START, STREAM_EVENTS_V3_MODES, Send, StateGraph, StateGraphInputError, StateSchema, StreamChannel, SubgraphRunStream, UnreachableNodeError, UntrackedValue, UntrackedValueChannel, _markInvalidResume, messagesStateReducer as addMessages, convertToProtocolEvent, copyCheckpoint, createGraphRunStream, createLifecycleTransformer, createMessagesTransformer, createSubgraphDiscoveryTransformer, createValuesTransformer, emptyCheckpoint, entrypoint, filterLifecycleEntries, filterSubgraphHandles, getConfig, getCurrentTaskInput, getJsonSchemaFromSchema, getPreviousState, getSchemaDefaultGetter, getStore, getSubgraphsSeenSet, getWriter, interrupt, isCheckpointEnvelope, isCommand, isGraphBubbleUp, isGraphDrained, isGraphInterrupt, isInterrupted, isInvalidResume, isNativeTransformer, isNodeError, isNodeTimeoutError, isParentCommand, isSerializableSchema, isStandardSchema, messagesDeltaReducer, messagesStateReducer, pushMessage, task, writer };
41
41
 
42
42
  //# sourceMappingURL=index.js.map
@@ -59,7 +59,15 @@ function interrupt(value, options) {
59
59
  if (!conf) throw new Error("No configurable found in config");
60
60
  if (!conf["__pregel_checkpointer"]) throw new require_errors.GraphValueError("No checkpointer set", { lc_error_code: "MISSING_CHECKPOINTER" });
61
61
  const schema = options?.responseSchema;
62
- const parseResume = (resume) => schema !== void 0 && (0, _langchain_core_utils_types.isInteropZodSchema)(schema) ? (0, _langchain_core_utils_types.interopParse)(schema, resume) : resume;
62
+ const parseResume = (resume) => {
63
+ if (schema === void 0 || !(0, _langchain_core_utils_types.isInteropZodSchema)(schema)) return resume;
64
+ try {
65
+ return (0, _langchain_core_utils_types.interopParse)(schema, resume);
66
+ } catch (error) {
67
+ require_errors._markInvalidResume(error);
68
+ throw error;
69
+ }
70
+ };
63
71
  const scratchpad = conf[require_constants.CONFIG_KEY_SCRATCHPAD];
64
72
  scratchpad.interruptCounter += 1;
65
73
  const idx = scratchpad.interruptCounter;
@@ -1 +1 @@
1
- {"version":3,"file":"interrupt.cjs","names":["AsyncLocalStorageProviderSingleton","GraphValueError","CONFIG_KEY_SCRATCHPAD","CONFIG_KEY_SEND","RESUME","CONFIG_KEY_CHECKPOINT_NS","XXH3","GraphInterrupt"],"sources":["../src/interrupt.ts"],"sourcesContent":["import { AsyncLocalStorageProviderSingleton } from \"@langchain/core/singletons\";\nimport { RunnableConfig } from \"@langchain/core/runnables\";\nimport {\n type JSONSchema,\n toJsonSchema,\n} from \"@langchain/core/utils/json_schema\";\nimport {\n type InteropZodType,\n interopParse,\n isInteropZodSchema,\n} from \"@langchain/core/utils/types\";\nimport {\n BaseCheckpointSaver,\n type PendingWrite,\n} from \"@langchain/langgraph-checkpoint\";\nimport { GraphInterrupt, GraphValueError } from \"./errors.js\";\nimport {\n CONFIG_KEY_CHECKPOINT_NS,\n CONFIG_KEY_SCRATCHPAD,\n CONFIG_KEY_SEND,\n CONFIG_KEY_CHECKPOINTER,\n CHECKPOINT_NAMESPACE_SEPARATOR,\n RESUME,\n type Interrupt,\n} from \"./constants.js\";\nimport { PregelScratchpad } from \"./pregel/types.js\";\nimport { XXH3 } from \"./hash.js\";\n\nexport interface InterruptOptions {\n /**\n * Schema for the value expected when the graph is resumed, surfaced to\n * clients on `Interrupt.response_schema` (as JSON Schema) so they can render\n * a typed input form. A Zod schema also parses the resume value, and the\n * parsed value is what `interrupt` returns; a JSON Schema object is passed\n * through to clients without validating the resume value.\n */\n responseSchema?: InteropZodType | JSONSchema;\n}\n\n/**\n * Interrupts the execution of a graph node.\n * This function can be used to pause execution of a node, and return the value of the `resume`\n * input when the graph is re-invoked using `Command`.\n * Multiple interrupts can be called within a single node, and each will be handled sequentially.\n *\n * When an interrupt is called:\n * 1. If there's a `resume` value available (from a previous `Command`), it returns that value.\n * 2. Otherwise, it throws a `GraphInterrupt` with the provided value\n * 3. The graph can be resumed by passing a `Command` with a `resume` value\n *\n * Because the `interrupt` function propagates by throwing a special `GraphInterrupt` error,\n * you should avoid using `try/catch` blocks around the `interrupt` function,\n * or if you do, ensure that the `GraphInterrupt` error is thrown again within your `catch` block.\n *\n * @param value - The value to include in the interrupt. This will be available in task.interrupts[].value\n * @param options - Optional settings. `responseSchema` describes the expected resume value and is\n * available in task.interrupts[].response_schema as JSON Schema; a Zod schema also validates the resume value.\n * @returns The `resume` value provided when the graph is re-invoked with a Command, parsed by\n * `responseSchema` when it is a Zod schema\n *\n * @example\n * ```typescript\n * // Define a node that uses multiple interrupts\n * const nodeWithInterrupts = () => {\n * // First interrupt - will pause execution and include {value: 1} in task values\n * const answer1 = interrupt({ value: 1 });\n *\n * // Second interrupt - only called after first interrupt is resumed\n * const answer2 = interrupt({ value: 2 });\n *\n * // Use the resume values\n * return { myKey: answer1 + \" \" + answer2 };\n * };\n *\n * // Resume the graph after first interrupt\n * await graph.stream(new Command({ resume: \"answer 1\" }));\n *\n * // Resume the graph after second interrupt\n * await graph.stream(new Command({ resume: \"answer 2\" }));\n * // Final result: { myKey: \"answer 1 answer 2\" }\n * ```\n *\n * @throws {Error} If called outside the context of a graph\n * @throws {GraphInterrupt} When no resume value is available\n * @throws {ZodError} When the resume value does not match a Zod `responseSchema`\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function interrupt<I = unknown, R = any>(\n value: I,\n options?: InterruptOptions\n): R {\n const config: RunnableConfig | undefined =\n AsyncLocalStorageProviderSingleton.getRunnableConfig();\n if (!config) {\n throw new Error(\"Called interrupt() outside the context of a graph.\");\n }\n\n const conf = config.configurable;\n if (!conf) {\n throw new Error(\"No configurable found in config\");\n }\n\n const checkpointer: BaseCheckpointSaver = conf[CONFIG_KEY_CHECKPOINTER];\n if (!checkpointer) {\n throw new GraphValueError(\"No checkpointer set\", {\n lc_error_code: \"MISSING_CHECKPOINTER\",\n });\n }\n\n const schema = options?.responseSchema;\n const parseResume = (resume: unknown): R =>\n (schema !== undefined && isInteropZodSchema(schema)\n ? interopParse(schema, resume)\n : resume) as R;\n\n // Track interrupt index\n const scratchpad: PregelScratchpad = conf[CONFIG_KEY_SCRATCHPAD];\n scratchpad.interruptCounter += 1;\n const idx = scratchpad.interruptCounter;\n\n // Find previous resume values\n if (scratchpad.resume.length > 0 && idx < scratchpad.resume.length) {\n const parsed = parseResume(scratchpad.resume[idx]);\n // Persist only through the interrupt being consumed. Values past `idx` belong\n // to later interrupts and may be unvalidated mapped resumes; a failing one must\n // not become durable, or its retry can never replace it.\n conf[CONFIG_KEY_SEND]?.([\n [RESUME, scratchpad.resume.slice(0, idx + 1)] as PendingWrite,\n ]);\n return parsed;\n }\n\n // Find current resume value\n if (scratchpad.nullResume !== undefined) {\n if (scratchpad.resume.length !== idx) {\n throw new Error(\n `Resume length mismatch: ${scratchpad.resume.length} !== ${idx}`\n );\n }\n const v = scratchpad.consumeNullResume();\n const parsed = parseResume(v);\n scratchpad.resume.push(v);\n conf[CONFIG_KEY_SEND]?.([[RESUME, scratchpad.resume] as PendingWrite]);\n return parsed;\n }\n\n // No resume value found\n const ns: string[] | undefined = conf[CONFIG_KEY_CHECKPOINT_NS]?.split(\n CHECKPOINT_NAMESPACE_SEPARATOR\n );\n\n const id = ns ? XXH3(ns.join(CHECKPOINT_NAMESPACE_SEPARATOR)) : undefined;\n const pending: Interrupt<I> = { id, value };\n if (schema !== undefined) {\n pending.response_schema = toJsonSchema(schema);\n }\n throw new GraphInterrupt([pending]);\n}\n\ntype FilterAny<X> =\n (<T>() => T extends X ? 1 : 2) extends <\n T,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n >() => T extends any ? 1 : 2\n ? never\n : X;\n\nexport type InferInterruptInputType<T> = T extends typeof interrupt<\n infer I,\n unknown\n>\n ? I\n : // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends { [key: string]: typeof interrupt<any, any> }\n ? { [K in keyof T]: InferInterruptInputType<T[K]> }[keyof T]\n : unknown;\n\nexport type InferInterruptResumeType<\n T,\n TInner = false,\n> = T extends typeof interrupt<never, infer R>\n ? TInner extends true\n ? FilterAny<R>\n : R\n : // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends { [key: string]: typeof interrupt<any, any> }\n ? { [K in keyof T]: InferInterruptResumeType<T[K], true> }[keyof T]\n : unknown;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuFA,SAAgB,UACd,OACA,SACG;CACH,MAAM,SACJA,2BAAAA,mCAAmC,mBAAmB;AACxD,KAAI,CAAC,OACH,OAAM,IAAI,MAAM,qDAAqD;CAGvE,MAAM,OAAO,OAAO;AACpB,KAAI,CAAC,KACH,OAAM,IAAI,MAAM,kCAAkC;AAIpD,KAAI,CADsC,KAAA,yBAExC,OAAM,IAAIC,eAAAA,gBAAgB,uBAAuB,EAC/C,eAAe,wBAChB,CAAC;CAGJ,MAAM,SAAS,SAAS;CACxB,MAAM,eAAe,WAClB,WAAW,KAAA,MAAA,GAAA,4BAAA,oBAAgC,OAAO,IAAA,GAAA,4BAAA,cAClC,QAAQ,OAAO,GAC5B;CAGN,MAAM,aAA+B,KAAKC,kBAAAA;AAC1C,YAAW,oBAAoB;CAC/B,MAAM,MAAM,WAAW;AAGvB,KAAI,WAAW,OAAO,SAAS,KAAK,MAAM,WAAW,OAAO,QAAQ;EAClE,MAAM,SAAS,YAAY,WAAW,OAAO,KAAK;AAIlD,OAAKC,kBAAAA,mBAAmB,CACtB,CAACC,kBAAAA,QAAQ,WAAW,OAAO,MAAM,GAAG,MAAM,EAAE,CAAC,CAC9C,CAAC;AACF,SAAO;;AAIT,KAAI,WAAW,eAAe,KAAA,GAAW;AACvC,MAAI,WAAW,OAAO,WAAW,IAC/B,OAAM,IAAI,MACR,2BAA2B,WAAW,OAAO,OAAO,OAAO,MAC5D;EAEH,MAAM,IAAI,WAAW,mBAAmB;EACxC,MAAM,SAAS,YAAY,EAAE;AAC7B,aAAW,OAAO,KAAK,EAAE;AACzB,OAAKD,kBAAAA,mBAAmB,CAAC,CAACC,kBAAAA,QAAQ,WAAW,OAAO,CAAiB,CAAC;AACtE,SAAO;;CAIT,MAAM,KAA2B,KAAKC,kBAAAA,2BAA2B,MAAA,IAEhE;CAGD,MAAM,UAAwB;EAAE,IADrB,KAAKC,aAAAA,KAAK,GAAG,KAAA,IAAoC,CAAC,GAAG,KAAA;EAC5B;EAAO;AAC3C,KAAI,WAAW,KAAA,EACb,SAAQ,mBAAA,GAAA,kCAAA,cAA+B,OAAO;AAEhD,OAAM,IAAIC,eAAAA,eAAe,CAAC,QAAQ,CAAC"}
1
+ {"version":3,"file":"interrupt.cjs","names":["AsyncLocalStorageProviderSingleton","GraphValueError","CONFIG_KEY_SCRATCHPAD","CONFIG_KEY_SEND","RESUME","CONFIG_KEY_CHECKPOINT_NS","XXH3","GraphInterrupt"],"sources":["../src/interrupt.ts"],"sourcesContent":["import { AsyncLocalStorageProviderSingleton } from \"@langchain/core/singletons\";\nimport { RunnableConfig } from \"@langchain/core/runnables\";\nimport {\n type JSONSchema,\n toJsonSchema,\n} from \"@langchain/core/utils/json_schema\";\nimport {\n type InteropZodType,\n interopParse,\n isInteropZodSchema,\n} from \"@langchain/core/utils/types\";\nimport {\n BaseCheckpointSaver,\n type PendingWrite,\n} from \"@langchain/langgraph-checkpoint\";\nimport {\n GraphInterrupt,\n GraphValueError,\n _markInvalidResume,\n} from \"./errors.js\";\nimport {\n CONFIG_KEY_CHECKPOINT_NS,\n CONFIG_KEY_SCRATCHPAD,\n CONFIG_KEY_SEND,\n CONFIG_KEY_CHECKPOINTER,\n CHECKPOINT_NAMESPACE_SEPARATOR,\n RESUME,\n type Interrupt,\n} from \"./constants.js\";\nimport { PregelScratchpad } from \"./pregel/types.js\";\nimport { XXH3 } from \"./hash.js\";\n\nexport interface InterruptOptions {\n /**\n * Schema for the value expected when the graph is resumed, surfaced to\n * clients on `Interrupt.response_schema` (as JSON Schema) so they can render\n * a typed input form. A Zod schema also parses the resume value, and the\n * parsed value is what `interrupt` returns; a JSON Schema object is passed\n * through to clients without validating the resume value.\n */\n responseSchema?: InteropZodType | JSONSchema;\n}\n\n/**\n * Interrupts the execution of a graph node.\n * This function can be used to pause execution of a node, and return the value of the `resume`\n * input when the graph is re-invoked using `Command`.\n * Multiple interrupts can be called within a single node, and each will be handled sequentially.\n *\n * When an interrupt is called:\n * 1. If there's a `resume` value available (from a previous `Command`), it returns that value.\n * 2. Otherwise, it throws a `GraphInterrupt` with the provided value\n * 3. The graph can be resumed by passing a `Command` with a `resume` value\n *\n * Because the `interrupt` function propagates by throwing a special `GraphInterrupt` error,\n * you should avoid using `try/catch` blocks around the `interrupt` function,\n * or if you do, ensure that the `GraphInterrupt` error is thrown again within your `catch` block.\n *\n * @param value - The value to include in the interrupt. This will be available in task.interrupts[].value\n * @param options - Optional settings. `responseSchema` describes the expected resume value and is\n * available in task.interrupts[].response_schema as JSON Schema; a Zod schema also validates the resume value.\n * @returns The `resume` value provided when the graph is re-invoked with a Command, parsed by\n * `responseSchema` when it is a Zod schema\n *\n * @example\n * ```typescript\n * // Define a node that uses multiple interrupts\n * const nodeWithInterrupts = () => {\n * // First interrupt - will pause execution and include {value: 1} in task values\n * const answer1 = interrupt({ value: 1 });\n *\n * // Second interrupt - only called after first interrupt is resumed\n * const answer2 = interrupt({ value: 2 });\n *\n * // Use the resume values\n * return { myKey: answer1 + \" \" + answer2 };\n * };\n *\n * // Resume the graph after first interrupt\n * await graph.stream(new Command({ resume: \"answer 1\" }));\n *\n * // Resume the graph after second interrupt\n * await graph.stream(new Command({ resume: \"answer 2\" }));\n * // Final result: { myKey: \"answer 1 answer 2\" }\n * ```\n *\n * @throws {Error} If called outside the context of a graph\n * @throws {GraphInterrupt} When no resume value is available\n * @throws {ZodError} When the resume value does not match a Zod `responseSchema`\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function interrupt<I = unknown, R = any>(\n value: I,\n options?: InterruptOptions\n): R {\n const config: RunnableConfig | undefined =\n AsyncLocalStorageProviderSingleton.getRunnableConfig();\n if (!config) {\n throw new Error(\"Called interrupt() outside the context of a graph.\");\n }\n\n const conf = config.configurable;\n if (!conf) {\n throw new Error(\"No configurable found in config\");\n }\n\n const checkpointer: BaseCheckpointSaver = conf[CONFIG_KEY_CHECKPOINTER];\n if (!checkpointer) {\n throw new GraphValueError(\"No checkpointer set\", {\n lc_error_code: \"MISSING_CHECKPOINTER\",\n });\n }\n\n const schema = options?.responseSchema;\n const parseResume = (resume: unknown): R => {\n if (schema === undefined || !isInteropZodSchema(schema)) return resume as R;\n try {\n return interopParse(schema, resume) as R;\n } catch (error) {\n _markInvalidResume(error);\n throw error;\n }\n };\n\n // Track interrupt index\n const scratchpad: PregelScratchpad = conf[CONFIG_KEY_SCRATCHPAD];\n scratchpad.interruptCounter += 1;\n const idx = scratchpad.interruptCounter;\n\n // Find previous resume values\n if (scratchpad.resume.length > 0 && idx < scratchpad.resume.length) {\n const parsed = parseResume(scratchpad.resume[idx]);\n // Persist only through the interrupt being consumed. Values past `idx` belong\n // to later interrupts and may be unvalidated mapped resumes; a failing one must\n // not become durable, or its retry can never replace it.\n conf[CONFIG_KEY_SEND]?.([\n [RESUME, scratchpad.resume.slice(0, idx + 1)] as PendingWrite,\n ]);\n return parsed;\n }\n\n // Find current resume value\n if (scratchpad.nullResume !== undefined) {\n if (scratchpad.resume.length !== idx) {\n throw new Error(\n `Resume length mismatch: ${scratchpad.resume.length} !== ${idx}`\n );\n }\n const v = scratchpad.consumeNullResume();\n const parsed = parseResume(v);\n scratchpad.resume.push(v);\n conf[CONFIG_KEY_SEND]?.([[RESUME, scratchpad.resume] as PendingWrite]);\n return parsed;\n }\n\n // No resume value found\n const ns: string[] | undefined = conf[CONFIG_KEY_CHECKPOINT_NS]?.split(\n CHECKPOINT_NAMESPACE_SEPARATOR\n );\n\n const id = ns ? XXH3(ns.join(CHECKPOINT_NAMESPACE_SEPARATOR)) : undefined;\n const pending: Interrupt<I> = { id, value };\n if (schema !== undefined) {\n pending.response_schema = toJsonSchema(schema);\n }\n throw new GraphInterrupt([pending]);\n}\n\ntype FilterAny<X> =\n (<T>() => T extends X ? 1 : 2) extends <\n T,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n >() => T extends any ? 1 : 2\n ? never\n : X;\n\nexport type InferInterruptInputType<T> = T extends typeof interrupt<\n infer I,\n unknown\n>\n ? I\n : // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends { [key: string]: typeof interrupt<any, any> }\n ? { [K in keyof T]: InferInterruptInputType<T[K]> }[keyof T]\n : unknown;\n\nexport type InferInterruptResumeType<\n T,\n TInner = false,\n> = T extends typeof interrupt<never, infer R>\n ? TInner extends true\n ? FilterAny<R>\n : R\n : // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends { [key: string]: typeof interrupt<any, any> }\n ? { [K in keyof T]: InferInterruptResumeType<T[K], true> }[keyof T]\n : unknown;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2FA,SAAgB,UACd,OACA,SACG;CACH,MAAM,SACJA,2BAAAA,mCAAmC,mBAAmB;AACxD,KAAI,CAAC,OACH,OAAM,IAAI,MAAM,qDAAqD;CAGvE,MAAM,OAAO,OAAO;AACpB,KAAI,CAAC,KACH,OAAM,IAAI,MAAM,kCAAkC;AAIpD,KAAI,CADsC,KAAA,yBAExC,OAAM,IAAIC,eAAAA,gBAAgB,uBAAuB,EAC/C,eAAe,wBAChB,CAAC;CAGJ,MAAM,SAAS,SAAS;CACxB,MAAM,eAAe,WAAuB;AAC1C,MAAI,WAAW,KAAA,KAAa,EAAA,GAAA,4BAAA,oBAAoB,OAAO,CAAE,QAAO;AAChE,MAAI;AACF,WAAA,GAAA,4BAAA,cAAoB,QAAQ,OAAO;WAC5B,OAAO;AACd,kBAAA,mBAAmB,MAAM;AACzB,SAAM;;;CAKV,MAAM,aAA+B,KAAKC,kBAAAA;AAC1C,YAAW,oBAAoB;CAC/B,MAAM,MAAM,WAAW;AAGvB,KAAI,WAAW,OAAO,SAAS,KAAK,MAAM,WAAW,OAAO,QAAQ;EAClE,MAAM,SAAS,YAAY,WAAW,OAAO,KAAK;AAIlD,OAAKC,kBAAAA,mBAAmB,CACtB,CAACC,kBAAAA,QAAQ,WAAW,OAAO,MAAM,GAAG,MAAM,EAAE,CAAC,CAC9C,CAAC;AACF,SAAO;;AAIT,KAAI,WAAW,eAAe,KAAA,GAAW;AACvC,MAAI,WAAW,OAAO,WAAW,IAC/B,OAAM,IAAI,MACR,2BAA2B,WAAW,OAAO,OAAO,OAAO,MAC5D;EAEH,MAAM,IAAI,WAAW,mBAAmB;EACxC,MAAM,SAAS,YAAY,EAAE;AAC7B,aAAW,OAAO,KAAK,EAAE;AACzB,OAAKD,kBAAAA,mBAAmB,CAAC,CAACC,kBAAAA,QAAQ,WAAW,OAAO,CAAiB,CAAC;AACtE,SAAO;;CAIT,MAAM,KAA2B,KAAKC,kBAAAA,2BAA2B,MAAA,IAEhE;CAGD,MAAM,UAAwB;EAAE,IADrB,KAAKC,aAAAA,KAAK,GAAG,KAAA,IAAoC,CAAC,GAAG,KAAA;EAC5B;EAAO;AAC3C,KAAI,WAAW,KAAA,EACb,SAAQ,mBAAA,GAAA,kCAAA,cAA+B,OAAO;AAEhD,OAAM,IAAIC,eAAAA,eAAe,CAAC,QAAQ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"interrupt.d.cts","names":[],"sources":["../src/interrupt.ts"],"mappings":";;;;UA4BiB,gBAAA;;AAAjB;;;;;;EAQE,cAAA,GAAiB,cAAA,GAAiB,UAAA;AAAA;;AAmDpC;;;;;;;;;;;;;;;;;AAsEC;;;;;;;;;;;;;;;;;;AAUD;;;;;;;;;;;iBAhFgB,SAAA,sBAAA,CACd,KAAA,EAAO,CAAA,EACP,OAAA,GAAU,gBAAA,GACT,CAAA;AAAA,KAqEE,SAAA,gBACO,CAAA,SAAU,CAAA,6BAGb,CAAA,iCAEH,CAAA;AAAA,KAEM,uBAAA,MAA6B,CAAA,gBAAiB,SAAA,qBAItD,CAAA,GAEA,CAAA;EAAA,CAAa,GAAA,kBAAqB,SAAA;AAAA,kBAClB,CAAA,GAAI,uBAAA,CAAwB,CAAA,CAAE,CAAA,WAAY,CAAA;AAAA,KAGlD,wBAAA,sBAGR,CAAA,gBAAiB,SAAA,mBACjB,MAAA,gBACE,SAAA,CAAU,CAAA,IACV,CAAA,GAEF,CAAA;EAAA,CAAa,GAAA,kBAAqB,SAAA;AAAA,kBAClB,CAAA,GAAI,wBAAA,CAAyB,CAAA,CAAE,CAAA,iBAAkB,CAAA"}
1
+ {"version":3,"file":"interrupt.d.cts","names":[],"sources":["../src/interrupt.ts"],"mappings":";;;;UAgCiB,gBAAA;;AAAjB;;;;;;EAQE,cAAA,GAAiB,cAAA,GAAiB,UAAA;AAAA;;AAmDpC;;;;;;;;;;;;;;;;;AA2EC;;;;;;;;;;;;;;;;;;AAUD;;;;;;;;;;;iBArFgB,SAAA,sBAAA,CACd,KAAA,EAAO,CAAA,EACP,OAAA,GAAU,gBAAA,GACT,CAAA;AAAA,KA0EE,SAAA,gBACO,CAAA,SAAU,CAAA,6BAGb,CAAA,iCAEH,CAAA;AAAA,KAEM,uBAAA,MAA6B,CAAA,gBAAiB,SAAA,qBAItD,CAAA,GAEA,CAAA;EAAA,CAAa,GAAA,kBAAqB,SAAA;AAAA,kBAClB,CAAA,GAAI,uBAAA,CAAwB,CAAA,CAAE,CAAA,WAAY,CAAA;AAAA,KAGlD,wBAAA,sBAGR,CAAA,gBAAiB,SAAA,mBACjB,MAAA,gBACE,SAAA,CAAU,CAAA,IACV,CAAA,GAEF,CAAA;EAAA,CAAa,GAAA,kBAAqB,SAAA;AAAA,kBAClB,CAAA,GAAI,wBAAA,CAAyB,CAAA,CAAE,CAAA,iBAAkB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"interrupt.d.ts","names":[],"sources":["../src/interrupt.ts"],"mappings":";;;;UA4BiB,gBAAA;;AAAjB;;;;;;EAQE,cAAA,GAAiB,cAAA,GAAiB,UAAA;AAAA;;AAmDpC;;;;;;;;;;;;;;;;;AAsEC;;;;;;;;;;;;;;;;;;AAUD;;;;;;;;;;;iBAhFgB,SAAA,sBAAA,CACd,KAAA,EAAO,CAAA,EACP,OAAA,GAAU,gBAAA,GACT,CAAA;AAAA,KAqEE,SAAA,gBACO,CAAA,SAAU,CAAA,6BAGb,CAAA,iCAEH,CAAA;AAAA,KAEM,uBAAA,MAA6B,CAAA,gBAAiB,SAAA,qBAItD,CAAA,GAEA,CAAA;EAAA,CAAa,GAAA,kBAAqB,SAAA;AAAA,kBAClB,CAAA,GAAI,uBAAA,CAAwB,CAAA,CAAE,CAAA,WAAY,CAAA;AAAA,KAGlD,wBAAA,sBAGR,CAAA,gBAAiB,SAAA,mBACjB,MAAA,gBACE,SAAA,CAAU,CAAA,IACV,CAAA,GAEF,CAAA;EAAA,CAAa,GAAA,kBAAqB,SAAA;AAAA,kBAClB,CAAA,GAAI,wBAAA,CAAyB,CAAA,CAAE,CAAA,iBAAkB,CAAA"}
1
+ {"version":3,"file":"interrupt.d.ts","names":[],"sources":["../src/interrupt.ts"],"mappings":";;;;UAgCiB,gBAAA;;AAAjB;;;;;;EAQE,cAAA,GAAiB,cAAA,GAAiB,UAAA;AAAA;;AAmDpC;;;;;;;;;;;;;;;;;AA2EC;;;;;;;;;;;;;;;;;;AAUD;;;;;;;;;;;iBArFgB,SAAA,sBAAA,CACd,KAAA,EAAO,CAAA,EACP,OAAA,GAAU,gBAAA,GACT,CAAA;AAAA,KA0EE,SAAA,gBACO,CAAA,SAAU,CAAA,6BAGb,CAAA,iCAEH,CAAA;AAAA,KAEM,uBAAA,MAA6B,CAAA,gBAAiB,SAAA,qBAItD,CAAA,GAEA,CAAA;EAAA,CAAa,GAAA,kBAAqB,SAAA;AAAA,kBAClB,CAAA,GAAI,uBAAA,CAAwB,CAAA,CAAE,CAAA,WAAY,CAAA;AAAA,KAGlD,wBAAA,sBAGR,CAAA,gBAAiB,SAAA,mBACjB,MAAA,gBACE,SAAA,CAAU,CAAA,IACV,CAAA,GAEF,CAAA;EAAA,CAAa,GAAA,kBAAqB,SAAA;AAAA,kBAClB,CAAA,GAAI,wBAAA,CAAyB,CAAA,CAAE,CAAA,iBAAkB,CAAA"}
package/dist/interrupt.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { CONFIG_KEY_CHECKPOINT_NS, CONFIG_KEY_SCRATCHPAD, CONFIG_KEY_SEND, RESUME } from "./constants.js";
2
- import { GraphInterrupt, GraphValueError } from "./errors.js";
2
+ import { GraphInterrupt, GraphValueError, _markInvalidResume } from "./errors.js";
3
3
  import { XXH3 } from "./hash.js";
4
4
  import { AsyncLocalStorageProviderSingleton } from "@langchain/core/singletons";
5
5
  import { toJsonSchema } from "@langchain/core/utils/json_schema";
@@ -59,7 +59,15 @@ function interrupt(value, options) {
59
59
  if (!conf) throw new Error("No configurable found in config");
60
60
  if (!conf["__pregel_checkpointer"]) throw new GraphValueError("No checkpointer set", { lc_error_code: "MISSING_CHECKPOINTER" });
61
61
  const schema = options?.responseSchema;
62
- const parseResume = (resume) => schema !== void 0 && isInteropZodSchema(schema) ? interopParse(schema, resume) : resume;
62
+ const parseResume = (resume) => {
63
+ if (schema === void 0 || !isInteropZodSchema(schema)) return resume;
64
+ try {
65
+ return interopParse(schema, resume);
66
+ } catch (error) {
67
+ _markInvalidResume(error);
68
+ throw error;
69
+ }
70
+ };
63
71
  const scratchpad = conf[CONFIG_KEY_SCRATCHPAD];
64
72
  scratchpad.interruptCounter += 1;
65
73
  const idx = scratchpad.interruptCounter;
@@ -1 +1 @@
1
- {"version":3,"file":"interrupt.js","names":[],"sources":["../src/interrupt.ts"],"sourcesContent":["import { AsyncLocalStorageProviderSingleton } from \"@langchain/core/singletons\";\nimport { RunnableConfig } from \"@langchain/core/runnables\";\nimport {\n type JSONSchema,\n toJsonSchema,\n} from \"@langchain/core/utils/json_schema\";\nimport {\n type InteropZodType,\n interopParse,\n isInteropZodSchema,\n} from \"@langchain/core/utils/types\";\nimport {\n BaseCheckpointSaver,\n type PendingWrite,\n} from \"@langchain/langgraph-checkpoint\";\nimport { GraphInterrupt, GraphValueError } from \"./errors.js\";\nimport {\n CONFIG_KEY_CHECKPOINT_NS,\n CONFIG_KEY_SCRATCHPAD,\n CONFIG_KEY_SEND,\n CONFIG_KEY_CHECKPOINTER,\n CHECKPOINT_NAMESPACE_SEPARATOR,\n RESUME,\n type Interrupt,\n} from \"./constants.js\";\nimport { PregelScratchpad } from \"./pregel/types.js\";\nimport { XXH3 } from \"./hash.js\";\n\nexport interface InterruptOptions {\n /**\n * Schema for the value expected when the graph is resumed, surfaced to\n * clients on `Interrupt.response_schema` (as JSON Schema) so they can render\n * a typed input form. A Zod schema also parses the resume value, and the\n * parsed value is what `interrupt` returns; a JSON Schema object is passed\n * through to clients without validating the resume value.\n */\n responseSchema?: InteropZodType | JSONSchema;\n}\n\n/**\n * Interrupts the execution of a graph node.\n * This function can be used to pause execution of a node, and return the value of the `resume`\n * input when the graph is re-invoked using `Command`.\n * Multiple interrupts can be called within a single node, and each will be handled sequentially.\n *\n * When an interrupt is called:\n * 1. If there's a `resume` value available (from a previous `Command`), it returns that value.\n * 2. Otherwise, it throws a `GraphInterrupt` with the provided value\n * 3. The graph can be resumed by passing a `Command` with a `resume` value\n *\n * Because the `interrupt` function propagates by throwing a special `GraphInterrupt` error,\n * you should avoid using `try/catch` blocks around the `interrupt` function,\n * or if you do, ensure that the `GraphInterrupt` error is thrown again within your `catch` block.\n *\n * @param value - The value to include in the interrupt. This will be available in task.interrupts[].value\n * @param options - Optional settings. `responseSchema` describes the expected resume value and is\n * available in task.interrupts[].response_schema as JSON Schema; a Zod schema also validates the resume value.\n * @returns The `resume` value provided when the graph is re-invoked with a Command, parsed by\n * `responseSchema` when it is a Zod schema\n *\n * @example\n * ```typescript\n * // Define a node that uses multiple interrupts\n * const nodeWithInterrupts = () => {\n * // First interrupt - will pause execution and include {value: 1} in task values\n * const answer1 = interrupt({ value: 1 });\n *\n * // Second interrupt - only called after first interrupt is resumed\n * const answer2 = interrupt({ value: 2 });\n *\n * // Use the resume values\n * return { myKey: answer1 + \" \" + answer2 };\n * };\n *\n * // Resume the graph after first interrupt\n * await graph.stream(new Command({ resume: \"answer 1\" }));\n *\n * // Resume the graph after second interrupt\n * await graph.stream(new Command({ resume: \"answer 2\" }));\n * // Final result: { myKey: \"answer 1 answer 2\" }\n * ```\n *\n * @throws {Error} If called outside the context of a graph\n * @throws {GraphInterrupt} When no resume value is available\n * @throws {ZodError} When the resume value does not match a Zod `responseSchema`\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function interrupt<I = unknown, R = any>(\n value: I,\n options?: InterruptOptions\n): R {\n const config: RunnableConfig | undefined =\n AsyncLocalStorageProviderSingleton.getRunnableConfig();\n if (!config) {\n throw new Error(\"Called interrupt() outside the context of a graph.\");\n }\n\n const conf = config.configurable;\n if (!conf) {\n throw new Error(\"No configurable found in config\");\n }\n\n const checkpointer: BaseCheckpointSaver = conf[CONFIG_KEY_CHECKPOINTER];\n if (!checkpointer) {\n throw new GraphValueError(\"No checkpointer set\", {\n lc_error_code: \"MISSING_CHECKPOINTER\",\n });\n }\n\n const schema = options?.responseSchema;\n const parseResume = (resume: unknown): R =>\n (schema !== undefined && isInteropZodSchema(schema)\n ? interopParse(schema, resume)\n : resume) as R;\n\n // Track interrupt index\n const scratchpad: PregelScratchpad = conf[CONFIG_KEY_SCRATCHPAD];\n scratchpad.interruptCounter += 1;\n const idx = scratchpad.interruptCounter;\n\n // Find previous resume values\n if (scratchpad.resume.length > 0 && idx < scratchpad.resume.length) {\n const parsed = parseResume(scratchpad.resume[idx]);\n // Persist only through the interrupt being consumed. Values past `idx` belong\n // to later interrupts and may be unvalidated mapped resumes; a failing one must\n // not become durable, or its retry can never replace it.\n conf[CONFIG_KEY_SEND]?.([\n [RESUME, scratchpad.resume.slice(0, idx + 1)] as PendingWrite,\n ]);\n return parsed;\n }\n\n // Find current resume value\n if (scratchpad.nullResume !== undefined) {\n if (scratchpad.resume.length !== idx) {\n throw new Error(\n `Resume length mismatch: ${scratchpad.resume.length} !== ${idx}`\n );\n }\n const v = scratchpad.consumeNullResume();\n const parsed = parseResume(v);\n scratchpad.resume.push(v);\n conf[CONFIG_KEY_SEND]?.([[RESUME, scratchpad.resume] as PendingWrite]);\n return parsed;\n }\n\n // No resume value found\n const ns: string[] | undefined = conf[CONFIG_KEY_CHECKPOINT_NS]?.split(\n CHECKPOINT_NAMESPACE_SEPARATOR\n );\n\n const id = ns ? XXH3(ns.join(CHECKPOINT_NAMESPACE_SEPARATOR)) : undefined;\n const pending: Interrupt<I> = { id, value };\n if (schema !== undefined) {\n pending.response_schema = toJsonSchema(schema);\n }\n throw new GraphInterrupt([pending]);\n}\n\ntype FilterAny<X> =\n (<T>() => T extends X ? 1 : 2) extends <\n T,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n >() => T extends any ? 1 : 2\n ? never\n : X;\n\nexport type InferInterruptInputType<T> = T extends typeof interrupt<\n infer I,\n unknown\n>\n ? I\n : // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends { [key: string]: typeof interrupt<any, any> }\n ? { [K in keyof T]: InferInterruptInputType<T[K]> }[keyof T]\n : unknown;\n\nexport type InferInterruptResumeType<\n T,\n TInner = false,\n> = T extends typeof interrupt<never, infer R>\n ? TInner extends true\n ? FilterAny<R>\n : R\n : // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends { [key: string]: typeof interrupt<any, any> }\n ? { [K in keyof T]: InferInterruptResumeType<T[K], true> }[keyof T]\n : unknown;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuFA,SAAgB,UACd,OACA,SACG;CACH,MAAM,SACJ,mCAAmC,mBAAmB;AACxD,KAAI,CAAC,OACH,OAAM,IAAI,MAAM,qDAAqD;CAGvE,MAAM,OAAO,OAAO;AACpB,KAAI,CAAC,KACH,OAAM,IAAI,MAAM,kCAAkC;AAIpD,KAAI,CADsC,KAAA,yBAExC,OAAM,IAAI,gBAAgB,uBAAuB,EAC/C,eAAe,wBAChB,CAAC;CAGJ,MAAM,SAAS,SAAS;CACxB,MAAM,eAAe,WAClB,WAAW,KAAA,KAAa,mBAAmB,OAAO,GAC/C,aAAa,QAAQ,OAAO,GAC5B;CAGN,MAAM,aAA+B,KAAK;AAC1C,YAAW,oBAAoB;CAC/B,MAAM,MAAM,WAAW;AAGvB,KAAI,WAAW,OAAO,SAAS,KAAK,MAAM,WAAW,OAAO,QAAQ;EAClE,MAAM,SAAS,YAAY,WAAW,OAAO,KAAK;AAIlD,OAAK,mBAAmB,CACtB,CAAC,QAAQ,WAAW,OAAO,MAAM,GAAG,MAAM,EAAE,CAAC,CAC9C,CAAC;AACF,SAAO;;AAIT,KAAI,WAAW,eAAe,KAAA,GAAW;AACvC,MAAI,WAAW,OAAO,WAAW,IAC/B,OAAM,IAAI,MACR,2BAA2B,WAAW,OAAO,OAAO,OAAO,MAC5D;EAEH,MAAM,IAAI,WAAW,mBAAmB;EACxC,MAAM,SAAS,YAAY,EAAE;AAC7B,aAAW,OAAO,KAAK,EAAE;AACzB,OAAK,mBAAmB,CAAC,CAAC,QAAQ,WAAW,OAAO,CAAiB,CAAC;AACtE,SAAO;;CAIT,MAAM,KAA2B,KAAK,2BAA2B,MAAA,IAEhE;CAGD,MAAM,UAAwB;EAAE,IADrB,KAAK,KAAK,GAAG,KAAA,IAAoC,CAAC,GAAG,KAAA;EAC5B;EAAO;AAC3C,KAAI,WAAW,KAAA,EACb,SAAQ,kBAAkB,aAAa,OAAO;AAEhD,OAAM,IAAI,eAAe,CAAC,QAAQ,CAAC"}
1
+ {"version":3,"file":"interrupt.js","names":[],"sources":["../src/interrupt.ts"],"sourcesContent":["import { AsyncLocalStorageProviderSingleton } from \"@langchain/core/singletons\";\nimport { RunnableConfig } from \"@langchain/core/runnables\";\nimport {\n type JSONSchema,\n toJsonSchema,\n} from \"@langchain/core/utils/json_schema\";\nimport {\n type InteropZodType,\n interopParse,\n isInteropZodSchema,\n} from \"@langchain/core/utils/types\";\nimport {\n BaseCheckpointSaver,\n type PendingWrite,\n} from \"@langchain/langgraph-checkpoint\";\nimport {\n GraphInterrupt,\n GraphValueError,\n _markInvalidResume,\n} from \"./errors.js\";\nimport {\n CONFIG_KEY_CHECKPOINT_NS,\n CONFIG_KEY_SCRATCHPAD,\n CONFIG_KEY_SEND,\n CONFIG_KEY_CHECKPOINTER,\n CHECKPOINT_NAMESPACE_SEPARATOR,\n RESUME,\n type Interrupt,\n} from \"./constants.js\";\nimport { PregelScratchpad } from \"./pregel/types.js\";\nimport { XXH3 } from \"./hash.js\";\n\nexport interface InterruptOptions {\n /**\n * Schema for the value expected when the graph is resumed, surfaced to\n * clients on `Interrupt.response_schema` (as JSON Schema) so they can render\n * a typed input form. A Zod schema also parses the resume value, and the\n * parsed value is what `interrupt` returns; a JSON Schema object is passed\n * through to clients without validating the resume value.\n */\n responseSchema?: InteropZodType | JSONSchema;\n}\n\n/**\n * Interrupts the execution of a graph node.\n * This function can be used to pause execution of a node, and return the value of the `resume`\n * input when the graph is re-invoked using `Command`.\n * Multiple interrupts can be called within a single node, and each will be handled sequentially.\n *\n * When an interrupt is called:\n * 1. If there's a `resume` value available (from a previous `Command`), it returns that value.\n * 2. Otherwise, it throws a `GraphInterrupt` with the provided value\n * 3. The graph can be resumed by passing a `Command` with a `resume` value\n *\n * Because the `interrupt` function propagates by throwing a special `GraphInterrupt` error,\n * you should avoid using `try/catch` blocks around the `interrupt` function,\n * or if you do, ensure that the `GraphInterrupt` error is thrown again within your `catch` block.\n *\n * @param value - The value to include in the interrupt. This will be available in task.interrupts[].value\n * @param options - Optional settings. `responseSchema` describes the expected resume value and is\n * available in task.interrupts[].response_schema as JSON Schema; a Zod schema also validates the resume value.\n * @returns The `resume` value provided when the graph is re-invoked with a Command, parsed by\n * `responseSchema` when it is a Zod schema\n *\n * @example\n * ```typescript\n * // Define a node that uses multiple interrupts\n * const nodeWithInterrupts = () => {\n * // First interrupt - will pause execution and include {value: 1} in task values\n * const answer1 = interrupt({ value: 1 });\n *\n * // Second interrupt - only called after first interrupt is resumed\n * const answer2 = interrupt({ value: 2 });\n *\n * // Use the resume values\n * return { myKey: answer1 + \" \" + answer2 };\n * };\n *\n * // Resume the graph after first interrupt\n * await graph.stream(new Command({ resume: \"answer 1\" }));\n *\n * // Resume the graph after second interrupt\n * await graph.stream(new Command({ resume: \"answer 2\" }));\n * // Final result: { myKey: \"answer 1 answer 2\" }\n * ```\n *\n * @throws {Error} If called outside the context of a graph\n * @throws {GraphInterrupt} When no resume value is available\n * @throws {ZodError} When the resume value does not match a Zod `responseSchema`\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function interrupt<I = unknown, R = any>(\n value: I,\n options?: InterruptOptions\n): R {\n const config: RunnableConfig | undefined =\n AsyncLocalStorageProviderSingleton.getRunnableConfig();\n if (!config) {\n throw new Error(\"Called interrupt() outside the context of a graph.\");\n }\n\n const conf = config.configurable;\n if (!conf) {\n throw new Error(\"No configurable found in config\");\n }\n\n const checkpointer: BaseCheckpointSaver = conf[CONFIG_KEY_CHECKPOINTER];\n if (!checkpointer) {\n throw new GraphValueError(\"No checkpointer set\", {\n lc_error_code: \"MISSING_CHECKPOINTER\",\n });\n }\n\n const schema = options?.responseSchema;\n const parseResume = (resume: unknown): R => {\n if (schema === undefined || !isInteropZodSchema(schema)) return resume as R;\n try {\n return interopParse(schema, resume) as R;\n } catch (error) {\n _markInvalidResume(error);\n throw error;\n }\n };\n\n // Track interrupt index\n const scratchpad: PregelScratchpad = conf[CONFIG_KEY_SCRATCHPAD];\n scratchpad.interruptCounter += 1;\n const idx = scratchpad.interruptCounter;\n\n // Find previous resume values\n if (scratchpad.resume.length > 0 && idx < scratchpad.resume.length) {\n const parsed = parseResume(scratchpad.resume[idx]);\n // Persist only through the interrupt being consumed. Values past `idx` belong\n // to later interrupts and may be unvalidated mapped resumes; a failing one must\n // not become durable, or its retry can never replace it.\n conf[CONFIG_KEY_SEND]?.([\n [RESUME, scratchpad.resume.slice(0, idx + 1)] as PendingWrite,\n ]);\n return parsed;\n }\n\n // Find current resume value\n if (scratchpad.nullResume !== undefined) {\n if (scratchpad.resume.length !== idx) {\n throw new Error(\n `Resume length mismatch: ${scratchpad.resume.length} !== ${idx}`\n );\n }\n const v = scratchpad.consumeNullResume();\n const parsed = parseResume(v);\n scratchpad.resume.push(v);\n conf[CONFIG_KEY_SEND]?.([[RESUME, scratchpad.resume] as PendingWrite]);\n return parsed;\n }\n\n // No resume value found\n const ns: string[] | undefined = conf[CONFIG_KEY_CHECKPOINT_NS]?.split(\n CHECKPOINT_NAMESPACE_SEPARATOR\n );\n\n const id = ns ? XXH3(ns.join(CHECKPOINT_NAMESPACE_SEPARATOR)) : undefined;\n const pending: Interrupt<I> = { id, value };\n if (schema !== undefined) {\n pending.response_schema = toJsonSchema(schema);\n }\n throw new GraphInterrupt([pending]);\n}\n\ntype FilterAny<X> =\n (<T>() => T extends X ? 1 : 2) extends <\n T,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n >() => T extends any ? 1 : 2\n ? never\n : X;\n\nexport type InferInterruptInputType<T> = T extends typeof interrupt<\n infer I,\n unknown\n>\n ? I\n : // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends { [key: string]: typeof interrupt<any, any> }\n ? { [K in keyof T]: InferInterruptInputType<T[K]> }[keyof T]\n : unknown;\n\nexport type InferInterruptResumeType<\n T,\n TInner = false,\n> = T extends typeof interrupt<never, infer R>\n ? TInner extends true\n ? FilterAny<R>\n : R\n : // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends { [key: string]: typeof interrupt<any, any> }\n ? { [K in keyof T]: InferInterruptResumeType<T[K], true> }[keyof T]\n : unknown;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2FA,SAAgB,UACd,OACA,SACG;CACH,MAAM,SACJ,mCAAmC,mBAAmB;AACxD,KAAI,CAAC,OACH,OAAM,IAAI,MAAM,qDAAqD;CAGvE,MAAM,OAAO,OAAO;AACpB,KAAI,CAAC,KACH,OAAM,IAAI,MAAM,kCAAkC;AAIpD,KAAI,CADsC,KAAA,yBAExC,OAAM,IAAI,gBAAgB,uBAAuB,EAC/C,eAAe,wBAChB,CAAC;CAGJ,MAAM,SAAS,SAAS;CACxB,MAAM,eAAe,WAAuB;AAC1C,MAAI,WAAW,KAAA,KAAa,CAAC,mBAAmB,OAAO,CAAE,QAAO;AAChE,MAAI;AACF,UAAO,aAAa,QAAQ,OAAO;WAC5B,OAAO;AACd,sBAAmB,MAAM;AACzB,SAAM;;;CAKV,MAAM,aAA+B,KAAK;AAC1C,YAAW,oBAAoB;CAC/B,MAAM,MAAM,WAAW;AAGvB,KAAI,WAAW,OAAO,SAAS,KAAK,MAAM,WAAW,OAAO,QAAQ;EAClE,MAAM,SAAS,YAAY,WAAW,OAAO,KAAK;AAIlD,OAAK,mBAAmB,CACtB,CAAC,QAAQ,WAAW,OAAO,MAAM,GAAG,MAAM,EAAE,CAAC,CAC9C,CAAC;AACF,SAAO;;AAIT,KAAI,WAAW,eAAe,KAAA,GAAW;AACvC,MAAI,WAAW,OAAO,WAAW,IAC/B,OAAM,IAAI,MACR,2BAA2B,WAAW,OAAO,OAAO,OAAO,MAC5D;EAEH,MAAM,IAAI,WAAW,mBAAmB;EACxC,MAAM,SAAS,YAAY,EAAE;AAC7B,aAAW,OAAO,KAAK,EAAE;AACzB,OAAK,mBAAmB,CAAC,CAAC,QAAQ,WAAW,OAAO,CAAiB,CAAC;AACtE,SAAO;;CAIT,MAAM,KAA2B,KAAK,2BAA2B,MAAA,IAEhE;CAGD,MAAM,UAAwB;EAAE,IADrB,KAAK,KAAK,GAAG,KAAA,IAAoC,CAAC,GAAG,KAAA;EAC5B;EAAO;AAC3C,KAAI,WAAW,KAAA,EACb,SAAQ,kBAAkB,aAAa,OAAO;AAEhD,OAAM,IAAI,eAAe,CAAC,QAAQ,CAAC"}
@@ -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 pathCache = /* @__PURE__ */ new Map();
91
- for (const task of tasks) pathCache.set(task, task.path?.slice(0, 3) || []);
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 usedNewVersion = false;
127
+ let consumedVersion;
114
128
  for (const chan of channelsToConsume) if (chan in onlyChannels && onlyChannels[chan].consume()) {
115
129
  if (getNextVersion !== void 0) {
116
- checkpoint.channel_versions[chan] = getNextVersion(maxVersion);
117
- usedNewVersion = true;
130
+ consumedVersion ??= getNextVersion(maxVersion);
131
+ checkpoint.channel_versions[chan] = consumedVersion;
118
132
  }
119
133
  }
120
134
  const pendingWritesByChannel = {};
121
- const pendingWriteTaskIdsByChannel = {};
122
- for (const task of tasks) {
123
- const taskId = task.id ?? "";
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 && getNextVersion != null) maxVersion = usedNewVersion ? getNextVersion(maxVersion) : maxVersion;
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] = getNextVersion(maxVersion);
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] = getNextVersion(maxVersion);
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] = getNextVersion(maxVersion);
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: taskPath.slice(0, 3),
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: taskPath
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: taskPath,
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: taskPath
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
- require_constants.PUSH,
629
- String(failedTask.name),
630
- handlerNodeName,
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