@langchain/langgraph 1.3.6 → 1.4.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.
Files changed (221) hide show
  1. package/dist/channels/base.cjs +78 -2
  2. package/dist/channels/base.cjs.map +1 -1
  3. package/dist/channels/base.d.cts +35 -2
  4. package/dist/channels/base.d.cts.map +1 -1
  5. package/dist/channels/base.d.ts +35 -2
  6. package/dist/channels/base.d.ts.map +1 -1
  7. package/dist/channels/base.js +77 -4
  8. package/dist/channels/base.js.map +1 -1
  9. package/dist/channels/delta.cjs +136 -0
  10. package/dist/channels/delta.cjs.map +1 -0
  11. package/dist/channels/delta.d.cts +99 -0
  12. package/dist/channels/delta.d.cts.map +1 -0
  13. package/dist/channels/delta.d.ts +99 -0
  14. package/dist/channels/delta.d.ts.map +1 -0
  15. package/dist/channels/delta.js +136 -0
  16. package/dist/channels/delta.js.map +1 -0
  17. package/dist/channels/index.cjs +5 -0
  18. package/dist/channels/index.d.cts +3 -2
  19. package/dist/channels/index.d.ts +3 -2
  20. package/dist/channels/index.js +3 -2
  21. package/dist/constants.cjs +62 -4
  22. package/dist/constants.cjs.map +1 -1
  23. package/dist/constants.d.cts +33 -2
  24. package/dist/constants.d.cts.map +1 -1
  25. package/dist/constants.d.ts +33 -2
  26. package/dist/constants.d.ts.map +1 -1
  27. package/dist/constants.js +59 -5
  28. package/dist/constants.js.map +1 -1
  29. package/dist/errors.cjs +128 -0
  30. package/dist/errors.cjs.map +1 -1
  31. package/dist/errors.d.cts +86 -1
  32. package/dist/errors.d.cts.map +1 -1
  33. package/dist/errors.d.ts +86 -1
  34. package/dist/errors.d.ts.map +1 -1
  35. package/dist/errors.js +123 -1
  36. package/dist/errors.js.map +1 -1
  37. package/dist/func/index.cjs +9 -2
  38. package/dist/func/index.cjs.map +1 -1
  39. package/dist/func/index.d.cts +14 -0
  40. package/dist/func/index.d.cts.map +1 -1
  41. package/dist/func/index.d.ts +14 -0
  42. package/dist/func/index.d.ts.map +1 -1
  43. package/dist/func/index.js +9 -2
  44. package/dist/func/index.js.map +1 -1
  45. package/dist/graph/graph.cjs +44 -7
  46. package/dist/graph/graph.cjs.map +1 -1
  47. package/dist/graph/graph.d.cts +24 -2
  48. package/dist/graph/graph.d.cts.map +1 -1
  49. package/dist/graph/graph.d.ts +24 -2
  50. package/dist/graph/graph.d.ts.map +1 -1
  51. package/dist/graph/graph.js +44 -7
  52. package/dist/graph/graph.js.map +1 -1
  53. package/dist/graph/index.d.ts +3 -3
  54. package/dist/graph/messages_reducer.cjs +55 -0
  55. package/dist/graph/messages_reducer.cjs.map +1 -1
  56. package/dist/graph/messages_reducer.d.cts +28 -1
  57. package/dist/graph/messages_reducer.d.cts.map +1 -1
  58. package/dist/graph/messages_reducer.d.ts +28 -1
  59. package/dist/graph/messages_reducer.d.ts.map +1 -1
  60. package/dist/graph/messages_reducer.js +56 -2
  61. package/dist/graph/messages_reducer.js.map +1 -1
  62. package/dist/graph/state.cjs +174 -7
  63. package/dist/graph/state.cjs.map +1 -1
  64. package/dist/graph/state.d.cts +193 -17
  65. package/dist/graph/state.d.cts.map +1 -1
  66. package/dist/graph/state.d.ts +193 -17
  67. package/dist/graph/state.d.ts.map +1 -1
  68. package/dist/graph/state.js +175 -8
  69. package/dist/graph/state.js.map +1 -1
  70. package/dist/graph/zod/schema.cjs +5 -0
  71. package/dist/graph/zod/schema.cjs.map +1 -1
  72. package/dist/graph/zod/schema.d.cts.map +1 -1
  73. package/dist/graph/zod/schema.d.ts.map +1 -1
  74. package/dist/graph/zod/schema.js +5 -0
  75. package/dist/graph/zod/schema.js.map +1 -1
  76. package/dist/index.cjs +11 -0
  77. package/dist/index.cjs.map +1 -1
  78. package/dist/index.d.cts +11 -8
  79. package/dist/index.d.ts +11 -8
  80. package/dist/index.js +5 -3
  81. package/dist/index.js.map +1 -1
  82. package/dist/prebuilt/react_agent_executor.d.cts +1 -1
  83. package/dist/pregel/algo.cjs +182 -21
  84. package/dist/pregel/algo.cjs.map +1 -1
  85. package/dist/pregel/algo.d.cts +1 -1
  86. package/dist/pregel/algo.d.cts.map +1 -1
  87. package/dist/pregel/algo.d.ts +1 -1
  88. package/dist/pregel/algo.d.ts.map +1 -1
  89. package/dist/pregel/algo.js +185 -25
  90. package/dist/pregel/algo.js.map +1 -1
  91. package/dist/pregel/call.cjs +2 -1
  92. package/dist/pregel/call.cjs.map +1 -1
  93. package/dist/pregel/call.js +2 -1
  94. package/dist/pregel/call.js.map +1 -1
  95. package/dist/pregel/index.cjs +15 -3
  96. package/dist/pregel/index.cjs.map +1 -1
  97. package/dist/pregel/index.d.cts.map +1 -1
  98. package/dist/pregel/index.d.ts.map +1 -1
  99. package/dist/pregel/index.js +17 -5
  100. package/dist/pregel/index.js.map +1 -1
  101. package/dist/pregel/loop.cjs +362 -41
  102. package/dist/pregel/loop.cjs.map +1 -1
  103. package/dist/pregel/loop.js +365 -44
  104. package/dist/pregel/loop.js.map +1 -1
  105. package/dist/pregel/messages-v2.cjs +1 -1
  106. package/dist/pregel/messages-v2.js +1 -1
  107. package/dist/pregel/messages.cjs +1 -1
  108. package/dist/pregel/messages.js +1 -1
  109. package/dist/pregel/read.cjs +15 -5
  110. package/dist/pregel/read.cjs.map +1 -1
  111. package/dist/pregel/read.d.cts +9 -0
  112. package/dist/pregel/read.d.cts.map +1 -1
  113. package/dist/pregel/read.d.ts +9 -0
  114. package/dist/pregel/read.d.ts.map +1 -1
  115. package/dist/pregel/read.js +15 -5
  116. package/dist/pregel/read.js.map +1 -1
  117. package/dist/pregel/remote-run-stream.cjs +107 -0
  118. package/dist/pregel/remote-run-stream.cjs.map +1 -0
  119. package/dist/pregel/remote-run-stream.d.cts +33 -0
  120. package/dist/pregel/remote-run-stream.d.cts.map +1 -0
  121. package/dist/pregel/remote-run-stream.d.ts +33 -0
  122. package/dist/pregel/remote-run-stream.d.ts.map +1 -0
  123. package/dist/pregel/remote-run-stream.js +107 -0
  124. package/dist/pregel/remote-run-stream.js.map +1 -0
  125. package/dist/pregel/remote.cjs +61 -1
  126. package/dist/pregel/remote.cjs.map +1 -1
  127. package/dist/pregel/remote.d.cts +17 -0
  128. package/dist/pregel/remote.d.cts.map +1 -1
  129. package/dist/pregel/remote.d.ts +17 -0
  130. package/dist/pregel/remote.d.ts.map +1 -1
  131. package/dist/pregel/remote.js +61 -1
  132. package/dist/pregel/remote.js.map +1 -1
  133. package/dist/pregel/replay.cjs +62 -0
  134. package/dist/pregel/replay.cjs.map +1 -0
  135. package/dist/pregel/replay.js +62 -0
  136. package/dist/pregel/replay.js.map +1 -0
  137. package/dist/pregel/retry.cjs +8 -6
  138. package/dist/pregel/retry.cjs.map +1 -1
  139. package/dist/pregel/retry.js +8 -6
  140. package/dist/pregel/retry.js.map +1 -1
  141. package/dist/pregel/runnable_types.d.cts +20 -0
  142. package/dist/pregel/runnable_types.d.cts.map +1 -1
  143. package/dist/pregel/runnable_types.d.ts +20 -0
  144. package/dist/pregel/runnable_types.d.ts.map +1 -1
  145. package/dist/pregel/runner.cjs +48 -7
  146. package/dist/pregel/runner.cjs.map +1 -1
  147. package/dist/pregel/runner.js +50 -9
  148. package/dist/pregel/runner.js.map +1 -1
  149. package/dist/pregel/runtime.cjs +64 -0
  150. package/dist/pregel/runtime.cjs.map +1 -0
  151. package/dist/pregel/runtime.d.cts +57 -0
  152. package/dist/pregel/runtime.d.cts.map +1 -0
  153. package/dist/pregel/runtime.d.ts +57 -0
  154. package/dist/pregel/runtime.d.ts.map +1 -0
  155. package/dist/pregel/runtime.js +64 -0
  156. package/dist/pregel/runtime.js.map +1 -0
  157. package/dist/pregel/stream.cjs +2 -0
  158. package/dist/pregel/stream.cjs.map +1 -1
  159. package/dist/pregel/stream.js +2 -0
  160. package/dist/pregel/stream.js.map +1 -1
  161. package/dist/pregel/timeout.cjs +216 -0
  162. package/dist/pregel/timeout.cjs.map +1 -0
  163. package/dist/pregel/timeout.js +216 -0
  164. package/dist/pregel/timeout.js.map +1 -0
  165. package/dist/pregel/types.cjs +3 -1
  166. package/dist/pregel/types.cjs.map +1 -1
  167. package/dist/pregel/types.d.cts +13 -0
  168. package/dist/pregel/types.d.cts.map +1 -1
  169. package/dist/pregel/types.d.ts +14 -1
  170. package/dist/pregel/types.d.ts.map +1 -1
  171. package/dist/pregel/types.js +3 -1
  172. package/dist/pregel/types.js.map +1 -1
  173. package/dist/pregel/utils/config.cjs +3 -1
  174. package/dist/pregel/utils/config.cjs.map +1 -1
  175. package/dist/pregel/utils/config.d.cts.map +1 -1
  176. package/dist/pregel/utils/config.d.ts.map +1 -1
  177. package/dist/pregel/utils/config.js +3 -1
  178. package/dist/pregel/utils/config.js.map +1 -1
  179. package/dist/pregel/utils/index.cjs +1 -0
  180. package/dist/pregel/utils/index.cjs.map +1 -1
  181. package/dist/pregel/utils/index.d.cts +6 -1
  182. package/dist/pregel/utils/index.d.cts.map +1 -1
  183. package/dist/pregel/utils/index.d.ts +6 -1
  184. package/dist/pregel/utils/index.d.ts.map +1 -1
  185. package/dist/pregel/utils/index.js +1 -0
  186. package/dist/pregel/utils/index.js.map +1 -1
  187. package/dist/pregel/utils/timeout.cjs +34 -0
  188. package/dist/pregel/utils/timeout.cjs.map +1 -0
  189. package/dist/pregel/utils/timeout.d.cts +45 -0
  190. package/dist/pregel/utils/timeout.d.cts.map +1 -0
  191. package/dist/pregel/utils/timeout.d.ts +45 -0
  192. package/dist/pregel/utils/timeout.d.ts.map +1 -0
  193. package/dist/pregel/utils/timeout.js +34 -0
  194. package/dist/pregel/utils/timeout.js.map +1 -0
  195. package/dist/stream/index.cjs +1 -0
  196. package/dist/stream/index.d.cts +2 -1
  197. package/dist/stream/index.d.ts +2 -1
  198. package/dist/stream/index.js +1 -0
  199. package/dist/stream/stream-channel.d.cts +9 -1
  200. package/dist/stream/stream-channel.d.cts.map +1 -1
  201. package/dist/stream/stream-channel.d.ts +9 -1
  202. package/dist/stream/stream-channel.d.ts.map +1 -1
  203. package/dist/stream/subscription.cjs +136 -0
  204. package/dist/stream/subscription.cjs.map +1 -0
  205. package/dist/stream/subscription.d.cts +94 -0
  206. package/dist/stream/subscription.d.cts.map +1 -0
  207. package/dist/stream/subscription.d.ts +94 -0
  208. package/dist/stream/subscription.d.ts.map +1 -0
  209. package/dist/stream/subscription.js +131 -0
  210. package/dist/stream/subscription.js.map +1 -0
  211. package/dist/stream/types.d.cts +1 -1
  212. package/dist/stream/types.d.ts +1 -1
  213. package/dist/stream.cjs +16 -0
  214. package/dist/stream.d.cts +5 -0
  215. package/dist/stream.d.ts +5 -0
  216. package/dist/stream.js +4 -0
  217. package/dist/web.cjs +11 -0
  218. package/dist/web.d.cts +11 -8
  219. package/dist/web.d.ts +11 -8
  220. package/dist/web.js +5 -3
  221. package/package.json +18 -6
@@ -1 +1 @@
1
- {"version":3,"file":"stream.js","names":[],"sources":["../../src/pregel/stream.ts"],"sourcesContent":["import { IterableReadableStream } from \"@langchain/core/utils/stream\";\nimport type { RunnableConfig } from \"@langchain/core/runnables\";\nimport { BaseCallbackHandler } from \"@langchain/core/callbacks/base\";\nimport { Serialized } from \"@langchain/core/load/serializable\";\nimport { isCheckpointEnvelope } from \"../stream/convert.js\";\nimport type { Checkpoint } from \"../stream/types.js\";\nimport type { StreamMode, StreamOutputMap } from \"./types.js\";\nimport { TAG_HIDDEN } from \"../constants.js\";\n\n/**\n * Optional chunk-level metadata carried alongside the payload. Used by\n * `streamEvents(..., { version: \"v3\" })` to emit a companion `checkpoints`\n * protocol event adjacent to the `values` event for the same superstep, so\n * clients can build branching / time-travel UIs without subscribing to a\n * full-state `checkpoints` stream.\n *\n * Companion checkpoint envelopes are emitted as separate\n * ``[namespace, \"checkpoints\", envelope]`` chunks (see\n * ``PregelLoop._emitValuesWithCheckpointMeta``).\n */\nexport interface StreamChunkMeta {\n /**\n * Lightweight checkpoint envelope for the superstep that produced the\n * paired `values` chunk. Shape matches the canonical {@link Checkpoint}\n * generated from `protocol.cddl`.\n */\n checkpoint?: Checkpoint;\n}\n\n// [namespace, streamMode, payload]\nexport type StreamChunk = [string[], StreamMode, unknown];\n\ntype StreamCheckpointsOutput<StreamValues> = StreamOutputMap<\n \"checkpoints\",\n false,\n StreamValues,\n unknown,\n string,\n unknown,\n unknown,\n undefined\n>;\n\ntype AnyStreamOutput = StreamOutputMap<\n StreamMode[],\n true,\n unknown,\n unknown,\n string,\n unknown,\n unknown,\n undefined\n>;\n\ntype ToolRunInfo = {\n ns: string[];\n toolCallId?: string;\n toolName: string;\n input: unknown;\n};\n\n/**\n * A wrapper around an IterableReadableStream that allows for aborting the stream when\n * {@link cancel} is called.\n */\nexport class IterableReadableStreamWithAbortSignal<\n T,\n> extends IterableReadableStream<T> {\n protected _abortController: AbortController;\n\n protected _innerReader: ReadableStreamDefaultReader<T>;\n\n /**\n * @param readableStream - The stream to wrap.\n * @param abortController - The abort controller to use. Optional. One will be created if not provided.\n */\n constructor(\n readableStream: ReadableStream<T>,\n abortController?: AbortController\n ) {\n const reader = readableStream.getReader();\n const ac = abortController ?? new AbortController();\n super({\n start(controller: ReadableStreamDefaultController<T>) {\n return pump();\n function pump(): Promise<T | undefined> {\n return reader.read().then(({ done, value }) => {\n // When no more data needs to be consumed, close the stream\n if (done) {\n controller.close();\n return;\n }\n // Enqueue the next data chunk into our target stream\n controller.enqueue(value);\n return pump();\n });\n }\n },\n });\n this._abortController = ac;\n this._innerReader = reader;\n }\n\n /**\n * Aborts the stream, abandoning any pending operations in progress. Calling this triggers an\n * {@link AbortSignal} that is propagated to the tasks that are producing the data for this stream.\n * @param reason - The reason for aborting the stream. Optional.\n */\n override async cancel(reason?: unknown) {\n this._abortController.abort(reason);\n this._innerReader.releaseLock();\n }\n\n /**\n * The {@link AbortSignal} for the stream. Aborted when {@link cancel} is called.\n */\n get signal() {\n return this._abortController.signal;\n }\n}\n\nexport class IterableReadableWritableStream extends IterableReadableStream<StreamChunk> {\n modes: Set<StreamMode>;\n\n private controller: ReadableStreamDefaultController;\n\n private passthroughFn?: (chunk: StreamChunk) => void;\n\n private _closed: boolean = false;\n\n get closed() {\n return this._closed;\n }\n\n constructor(params: {\n passthroughFn?: (chunk: StreamChunk) => void;\n modes: Set<StreamMode>;\n }) {\n let streamControllerPromiseResolver: (\n controller: ReadableStreamDefaultController\n ) => void;\n const streamControllerPromise: Promise<ReadableStreamDefaultController> =\n new Promise<ReadableStreamDefaultController>((resolve) => {\n streamControllerPromiseResolver = resolve;\n });\n\n super({\n start: (controller) => {\n streamControllerPromiseResolver!(controller);\n },\n });\n\n // .start() will always be called before the stream can be interacted\n // with anyway\n void streamControllerPromise.then((controller) => {\n this.controller = controller;\n });\n\n this.passthroughFn = params.passthroughFn;\n this.modes = params.modes;\n }\n\n push(chunk: StreamChunk) {\n // Prevent pushing to a closed stream to avoid race condition errors\n if (this._closed || !this.controller) {\n // Silently drop chunks when stream is closed - this is expected behavior\n // when async operations try to push after stream termination\n return;\n }\n\n // Forward chunk to passthrough function if provided\n this.passthroughFn?.(chunk);\n\n // Attempt to enqueue the chunk to the underlying stream\n this.controller.enqueue(chunk);\n }\n\n close() {\n try {\n this.controller.close();\n } catch {\n // pass\n } finally {\n this._closed = true;\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n error(e: any) {\n try {\n this.controller?.error(e);\n } finally {\n // Mark the stream as closed so any late `push()` calls from in-flight\n // parallel tasks are dropped instead of throwing on an errored controller.\n this._closed = true;\n }\n }\n}\n\n/**\n * A callback handler that implements stream_mode=tools.\n * Emits on_tool_start, on_tool_event, on_tool_end, on_tool_error events.\n */\nexport class StreamToolsHandler extends BaseCallbackHandler {\n name = \"StreamToolsHandler\";\n\n streamFn: (streamChunk: StreamChunk) => void;\n\n runs: Record<string, ToolRunInfo | undefined> = {};\n\n constructor(streamFn: (streamChunk: StreamChunk) => void) {\n super();\n this.streamFn = streamFn;\n }\n\n handleToolStart(\n _tool: Serialized,\n input: string,\n runId: string,\n _parentRunId?: string,\n tags?: string[],\n metadata?: Record<string, unknown>,\n runName?: string,\n toolCallId?: string\n ) {\n if (!metadata || (tags && tags.includes(TAG_HIDDEN))) return;\n\n const ns = (metadata.langgraph_checkpoint_ns as string)?.split(\"|\") ?? [];\n const info: ToolRunInfo = {\n ns,\n toolCallId,\n toolName: runName ?? \"unknown\",\n input,\n };\n this.runs[runId] = info;\n\n this.streamFn([\n ns,\n \"tools\",\n {\n event: \"on_tool_start\",\n toolCallId: info.toolCallId,\n name: info.toolName,\n input,\n },\n ]);\n }\n\n handleToolEvent(chunk: unknown, runId: string) {\n const info = this.runs[runId];\n if (!info) return;\n\n this.streamFn([\n info.ns,\n \"tools\",\n {\n event: \"on_tool_event\",\n toolCallId: info.toolCallId,\n name: info.toolName,\n data: chunk,\n },\n ]);\n }\n\n handleToolEnd(output: unknown, runId: string) {\n const info = this.runs[runId];\n delete this.runs[runId];\n if (!info) return;\n\n this.streamFn([\n info.ns,\n \"tools\",\n {\n event: \"on_tool_end\",\n toolCallId: info.toolCallId,\n name: info.toolName,\n output,\n },\n ]);\n }\n\n handleToolError(err: unknown, runId: string) {\n const info = this.runs[runId];\n delete this.runs[runId];\n if (!info) return;\n\n this.streamFn([\n info.ns,\n \"tools\",\n {\n event: \"on_tool_error\",\n toolCallId: info.toolCallId,\n name: info.toolName,\n error: err,\n },\n ]);\n }\n}\n\nfunction _stringifyAsDict(obj: unknown) {\n return JSON.stringify(obj, function (key: string | number, value: unknown) {\n const rawValue = this[key];\n if (\n rawValue != null &&\n typeof rawValue === \"object\" &&\n \"toDict\" in rawValue &&\n typeof rawValue.toDict === \"function\"\n ) {\n const { type, data } = rawValue.toDict();\n return { ...data, type };\n }\n\n return value;\n });\n}\n\nfunction _serializeError(error: unknown) {\n // eslint-disable-next-line no-instanceof/no-instanceof\n if (error instanceof Error) {\n return { error: error.name, message: error.message };\n }\n return { error: \"Error\", message: JSON.stringify(error) };\n}\n\nfunction _isRunnableConfig(\n config: unknown\n): config is RunnableConfig & { configurable: Record<string, unknown> } {\n if (typeof config !== \"object\" || config == null) return false;\n return (\n \"configurable\" in config &&\n typeof config.configurable === \"object\" &&\n config.configurable != null\n );\n}\n\nfunction _extractCheckpointFromConfig(\n config: RunnableConfig | null | undefined\n) {\n if (!_isRunnableConfig(config) || !config.configurable.thread_id) {\n return null;\n }\n\n return {\n thread_id: config.configurable.thread_id,\n checkpoint_ns: config.configurable.checkpoint_ns || \"\",\n checkpoint_id: config.configurable.checkpoint_id || null,\n checkpoint_map: config.configurable.checkpoint_map || null,\n };\n}\n\nfunction _serializeConfig(config: unknown) {\n if (_isRunnableConfig(config)) {\n const configurable = Object.fromEntries(\n Object.entries(config.configurable).filter(\n ([key]) => !key.startsWith(\"__\")\n )\n );\n\n const newConfig = { ...config, configurable };\n delete newConfig.callbacks;\n return newConfig;\n }\n\n return config;\n}\n\nfunction _serializeCheckpoint(payload: StreamCheckpointsOutput<unknown>) {\n const result: Record<string, unknown> = {\n ...payload,\n checkpoint: _extractCheckpointFromConfig(payload.config),\n parent_checkpoint: _extractCheckpointFromConfig(payload.parentConfig),\n\n config: _serializeConfig(payload.config),\n parent_config: _serializeConfig(payload.parentConfig),\n\n tasks: payload.tasks.map((task) => {\n if (_isRunnableConfig(task.state)) {\n const checkpoint = _extractCheckpointFromConfig(task.state);\n if (checkpoint != null) {\n const cloneTask: Record<string, unknown> = { ...task, checkpoint };\n delete cloneTask.state;\n return cloneTask;\n }\n }\n\n return task;\n }),\n };\n\n delete result.parentConfig;\n return result;\n}\n\nexport function toEventStream(stream: AsyncGenerator) {\n const encoder = new TextEncoder();\n return new ReadableStream<Uint8Array>({\n async start(controller) {\n const enqueueChunk = (sse: {\n id?: string;\n event: string;\n data: unknown;\n }) => {\n controller.enqueue(\n encoder.encode(\n `event: ${sse.event}\\ndata: ${_stringifyAsDict(sse.data)}\\n\\n`\n )\n );\n };\n\n try {\n for await (const payload of stream) {\n const [ns, mode, chunk] = payload as AnyStreamOutput;\n\n let data: unknown = chunk;\n if (mode === \"debug\") {\n const debugChunk = chunk;\n\n if (debugChunk.type === \"checkpoint\") {\n data = {\n ...debugChunk,\n payload: _serializeCheckpoint(debugChunk.payload),\n };\n }\n }\n\n if (mode === \"checkpoints\") {\n data = _serializeCheckpoint(chunk);\n }\n\n const event = ns?.length ? `${mode}|${ns.join(\"|\")}` : mode;\n enqueueChunk({ event, data });\n }\n } catch (error) {\n enqueueChunk({ event: \"error\", data: _serializeError(error) });\n }\n\n controller.close();\n },\n });\n}\n\n/** Multiplex subgraph stream chunks into the parent pregel stream. */\nexport function createDuplexStream(\n ...streams: IterableReadableWritableStream[]\n) {\n return new IterableReadableWritableStream({\n passthroughFn: (value: StreamChunk) => {\n const isEnvelope =\n value[1] === \"checkpoints\" && isCheckpointEnvelope(value[2]);\n for (const stream of streams) {\n if (stream.modes.has(value[1]) || isEnvelope) {\n stream.push(value);\n }\n }\n },\n modes: new Set(streams.flatMap((s) => Array.from(s.modes))),\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"stream.js","names":[],"sources":["../../src/pregel/stream.ts"],"sourcesContent":["import { IterableReadableStream } from \"@langchain/core/utils/stream\";\nimport type { RunnableConfig } from \"@langchain/core/runnables\";\nimport { BaseCallbackHandler } from \"@langchain/core/callbacks/base\";\nimport { Serialized } from \"@langchain/core/load/serializable\";\nimport { isCheckpointEnvelope } from \"../stream/convert.js\";\nimport type { Checkpoint } from \"../stream/types.js\";\nimport type { StreamMode, StreamOutputMap } from \"./types.js\";\nimport { TAG_HIDDEN } from \"../constants.js\";\n\n/**\n * Optional chunk-level metadata carried alongside the payload. Used by\n * `streamEvents(..., { version: \"v3\" })` to emit a companion `checkpoints`\n * protocol event adjacent to the `values` event for the same superstep, so\n * clients can build branching / time-travel UIs without subscribing to a\n * full-state `checkpoints` stream.\n *\n * Companion checkpoint envelopes are emitted as separate\n * ``[namespace, \"checkpoints\", envelope]`` chunks (see\n * ``PregelLoop._emitValuesWithCheckpointMeta``).\n */\nexport interface StreamChunkMeta {\n /**\n * Lightweight checkpoint envelope for the superstep that produced the\n * paired `values` chunk. Shape matches the canonical {@link Checkpoint}\n * generated from `protocol.cddl`.\n */\n checkpoint?: Checkpoint;\n}\n\n// [namespace, streamMode, payload]\nexport type StreamChunk = [string[], StreamMode, unknown];\n\ntype StreamCheckpointsOutput<StreamValues> = StreamOutputMap<\n \"checkpoints\",\n false,\n StreamValues,\n unknown,\n string,\n unknown,\n unknown,\n undefined\n>;\n\ntype AnyStreamOutput = StreamOutputMap<\n StreamMode[],\n true,\n unknown,\n unknown,\n string,\n unknown,\n unknown,\n undefined\n>;\n\ntype ToolRunInfo = {\n ns: string[];\n toolCallId?: string;\n toolName: string;\n input: unknown;\n};\n\n/**\n * A wrapper around an IterableReadableStream that allows for aborting the stream when\n * {@link cancel} is called.\n */\nexport class IterableReadableStreamWithAbortSignal<\n T,\n> extends IterableReadableStream<T> {\n protected _abortController: AbortController;\n\n protected _innerReader: ReadableStreamDefaultReader<T>;\n\n /**\n * @param readableStream - The stream to wrap.\n * @param abortController - The abort controller to use. Optional. One will be created if not provided.\n */\n constructor(\n readableStream: ReadableStream<T>,\n abortController?: AbortController\n ) {\n const reader = readableStream.getReader();\n const ac = abortController ?? new AbortController();\n super({\n start(controller: ReadableStreamDefaultController<T>) {\n return pump();\n function pump(): Promise<T | undefined> {\n return reader.read().then(({ done, value }) => {\n // When no more data needs to be consumed, close the stream\n if (done) {\n controller.close();\n return;\n }\n // Enqueue the next data chunk into our target stream\n controller.enqueue(value);\n return pump();\n });\n }\n },\n });\n this._abortController = ac;\n this._innerReader = reader;\n }\n\n /**\n * Aborts the stream, abandoning any pending operations in progress. Calling this triggers an\n * {@link AbortSignal} that is propagated to the tasks that are producing the data for this stream.\n * @param reason - The reason for aborting the stream. Optional.\n */\n override async cancel(reason?: unknown) {\n this._abortController.abort(reason);\n this._innerReader.releaseLock();\n }\n\n /**\n * The {@link AbortSignal} for the stream. Aborted when {@link cancel} is called.\n */\n get signal() {\n return this._abortController.signal;\n }\n}\n\nexport class IterableReadableWritableStream extends IterableReadableStream<StreamChunk> {\n modes: Set<StreamMode>;\n\n private controller: ReadableStreamDefaultController;\n\n private passthroughFn?: (chunk: StreamChunk) => void;\n\n private _closed: boolean = false;\n\n get closed() {\n return this._closed;\n }\n\n constructor(params: {\n passthroughFn?: (chunk: StreamChunk) => void;\n modes: Set<StreamMode>;\n }) {\n let streamControllerPromiseResolver: (\n controller: ReadableStreamDefaultController\n ) => void;\n const streamControllerPromise: Promise<ReadableStreamDefaultController> =\n new Promise<ReadableStreamDefaultController>((resolve) => {\n streamControllerPromiseResolver = resolve;\n });\n\n super({\n start: (controller) => {\n streamControllerPromiseResolver!(controller);\n },\n });\n\n // .start() will always be called before the stream can be interacted\n // with anyway\n void streamControllerPromise.then((controller) => {\n this.controller = controller;\n });\n\n this.passthroughFn = params.passthroughFn;\n this.modes = params.modes;\n }\n\n push(chunk: StreamChunk) {\n // Prevent pushing to a closed stream to avoid race condition errors\n if (this._closed || !this.controller) {\n // Silently drop chunks when stream is closed - this is expected behavior\n // when async operations try to push after stream termination\n return;\n }\n\n // Forward chunk to passthrough function if provided\n this.passthroughFn?.(chunk);\n\n // Attempt to enqueue the chunk to the underlying stream\n this.controller.enqueue(chunk);\n }\n\n close() {\n try {\n this.controller.close();\n } catch {\n // pass\n } finally {\n this._closed = true;\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n error(e: any) {\n try {\n this.controller?.error(e);\n } finally {\n // Mark the stream as closed so any late `push()` calls from in-flight\n // parallel tasks are dropped instead of throwing on an errored controller.\n this._closed = true;\n }\n }\n}\n\n/**\n * A callback handler that implements stream_mode=tools.\n * Emits on_tool_start, on_tool_event, on_tool_end, on_tool_error events.\n */\nexport class StreamToolsHandler extends BaseCallbackHandler {\n name = \"StreamToolsHandler\";\n\n /** Ensure tool lifecycle callbacks run before tool.invoke returns/errors. */\n awaitHandlers = true;\n\n streamFn: (streamChunk: StreamChunk) => void;\n\n runs: Record<string, ToolRunInfo | undefined> = {};\n\n constructor(streamFn: (streamChunk: StreamChunk) => void) {\n super();\n this.streamFn = streamFn;\n }\n\n handleToolStart(\n _tool: Serialized,\n input: string,\n runId: string,\n _parentRunId?: string,\n tags?: string[],\n metadata?: Record<string, unknown>,\n runName?: string,\n toolCallId?: string\n ) {\n if (!metadata || (tags && tags.includes(TAG_HIDDEN))) return;\n\n const ns = (metadata.langgraph_checkpoint_ns as string)?.split(\"|\") ?? [];\n const info: ToolRunInfo = {\n ns,\n toolCallId,\n toolName: runName ?? \"unknown\",\n input,\n };\n this.runs[runId] = info;\n\n this.streamFn([\n ns,\n \"tools\",\n {\n event: \"on_tool_start\",\n toolCallId: info.toolCallId,\n name: info.toolName,\n input,\n },\n ]);\n }\n\n handleToolEvent(chunk: unknown, runId: string) {\n const info = this.runs[runId];\n if (!info) return;\n\n this.streamFn([\n info.ns,\n \"tools\",\n {\n event: \"on_tool_event\",\n toolCallId: info.toolCallId,\n name: info.toolName,\n data: chunk,\n },\n ]);\n }\n\n handleToolEnd(output: unknown, runId: string) {\n const info = this.runs[runId];\n delete this.runs[runId];\n if (!info) return;\n\n this.streamFn([\n info.ns,\n \"tools\",\n {\n event: \"on_tool_end\",\n toolCallId: info.toolCallId,\n name: info.toolName,\n output,\n },\n ]);\n }\n\n handleToolError(err: unknown, runId: string) {\n const info = this.runs[runId];\n delete this.runs[runId];\n if (!info) return;\n\n this.streamFn([\n info.ns,\n \"tools\",\n {\n event: \"on_tool_error\",\n toolCallId: info.toolCallId,\n name: info.toolName,\n error: err,\n },\n ]);\n }\n}\n\nfunction _stringifyAsDict(obj: unknown) {\n return JSON.stringify(obj, function (key: string | number, value: unknown) {\n const rawValue = this[key];\n if (\n rawValue != null &&\n typeof rawValue === \"object\" &&\n \"toDict\" in rawValue &&\n typeof rawValue.toDict === \"function\"\n ) {\n const { type, data } = rawValue.toDict();\n return { ...data, type };\n }\n\n return value;\n });\n}\n\nfunction _serializeError(error: unknown) {\n // eslint-disable-next-line no-instanceof/no-instanceof\n if (error instanceof Error) {\n return { error: error.name, message: error.message };\n }\n return { error: \"Error\", message: JSON.stringify(error) };\n}\n\nfunction _isRunnableConfig(\n config: unknown\n): config is RunnableConfig & { configurable: Record<string, unknown> } {\n if (typeof config !== \"object\" || config == null) return false;\n return (\n \"configurable\" in config &&\n typeof config.configurable === \"object\" &&\n config.configurable != null\n );\n}\n\nfunction _extractCheckpointFromConfig(\n config: RunnableConfig | null | undefined\n) {\n if (!_isRunnableConfig(config) || !config.configurable.thread_id) {\n return null;\n }\n\n return {\n thread_id: config.configurable.thread_id,\n checkpoint_ns: config.configurable.checkpoint_ns || \"\",\n checkpoint_id: config.configurable.checkpoint_id || null,\n checkpoint_map: config.configurable.checkpoint_map || null,\n };\n}\n\nfunction _serializeConfig(config: unknown) {\n if (_isRunnableConfig(config)) {\n const configurable = Object.fromEntries(\n Object.entries(config.configurable).filter(\n ([key]) => !key.startsWith(\"__\")\n )\n );\n\n const newConfig = { ...config, configurable };\n delete newConfig.callbacks;\n return newConfig;\n }\n\n return config;\n}\n\nfunction _serializeCheckpoint(payload: StreamCheckpointsOutput<unknown>) {\n const result: Record<string, unknown> = {\n ...payload,\n checkpoint: _extractCheckpointFromConfig(payload.config),\n parent_checkpoint: _extractCheckpointFromConfig(payload.parentConfig),\n\n config: _serializeConfig(payload.config),\n parent_config: _serializeConfig(payload.parentConfig),\n\n tasks: payload.tasks.map((task) => {\n if (_isRunnableConfig(task.state)) {\n const checkpoint = _extractCheckpointFromConfig(task.state);\n if (checkpoint != null) {\n const cloneTask: Record<string, unknown> = { ...task, checkpoint };\n delete cloneTask.state;\n return cloneTask;\n }\n }\n\n return task;\n }),\n };\n\n delete result.parentConfig;\n return result;\n}\n\nexport function toEventStream(stream: AsyncGenerator) {\n const encoder = new TextEncoder();\n return new ReadableStream<Uint8Array>({\n async start(controller) {\n const enqueueChunk = (sse: {\n id?: string;\n event: string;\n data: unknown;\n }) => {\n controller.enqueue(\n encoder.encode(\n `event: ${sse.event}\\ndata: ${_stringifyAsDict(sse.data)}\\n\\n`\n )\n );\n };\n\n try {\n for await (const payload of stream) {\n const [ns, mode, chunk] = payload as AnyStreamOutput;\n\n let data: unknown = chunk;\n if (mode === \"debug\") {\n const debugChunk = chunk;\n\n if (debugChunk.type === \"checkpoint\") {\n data = {\n ...debugChunk,\n payload: _serializeCheckpoint(debugChunk.payload),\n };\n }\n }\n\n if (mode === \"checkpoints\") {\n data = _serializeCheckpoint(chunk);\n }\n\n const event = ns?.length ? `${mode}|${ns.join(\"|\")}` : mode;\n enqueueChunk({ event, data });\n }\n } catch (error) {\n enqueueChunk({ event: \"error\", data: _serializeError(error) });\n }\n\n controller.close();\n },\n });\n}\n\n/** Multiplex subgraph stream chunks into the parent pregel stream. */\nexport function createDuplexStream(\n ...streams: IterableReadableWritableStream[]\n) {\n return new IterableReadableWritableStream({\n passthroughFn: (value: StreamChunk) => {\n const isEnvelope =\n value[1] === \"checkpoints\" && isCheckpointEnvelope(value[2]);\n for (const stream of streams) {\n if (stream.modes.has(value[1]) || isEnvelope) {\n stream.push(value);\n }\n }\n },\n modes: new Set(streams.flatMap((s) => Array.from(s.modes))),\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,216 @@
1
+ const require_constants = require("../constants.cjs");
2
+ const require_errors = require("../errors.cjs");
3
+ const require_index = require("./utils/index.cjs");
4
+ let _langchain_core_callbacks_base = require("@langchain/core/callbacks/base");
5
+ //#region src/pregel/timeout.ts
6
+ /**
7
+ * Tracks the live progress state of a single timed node attempt.
8
+ *
9
+ * The scope guards observable-progress channels (writes, child-task
10
+ * scheduling, the custom stream writer, callbacks) so that:
11
+ *
12
+ * 1. `idleTimeout` is refreshed whenever the node makes progress, and
13
+ * 2. once the attempt is `close()`d after a timeout fires, late writes/calls
14
+ * from the still-running background task are dropped (Python parity:
15
+ * buffered writes from the failed attempt must not leak into the
16
+ * checkpoint).
17
+ *
18
+ * @internal
19
+ */
20
+ var TimedAttemptScope = class {
21
+ active = true;
22
+ lastProgress = Date.now();
23
+ refreshOn;
24
+ constructor(refreshOn) {
25
+ this.refreshOn = refreshOn;
26
+ }
27
+ /** Record progress now. Always honored (used by `runtime.heartbeat()`). */
28
+ touch() {
29
+ this.lastProgress = Date.now();
30
+ }
31
+ /**
32
+ * Record progress for an automatic signal (write/call/stream/callback).
33
+ * No-op when `refreshOn === "heartbeat"`, where only explicit heartbeats
34
+ * count as progress.
35
+ */
36
+ autoTouch() {
37
+ if (this.refreshOn === "auto") this.lastProgress = Date.now();
38
+ }
39
+ close() {
40
+ this.active = false;
41
+ }
42
+ };
43
+ /**
44
+ * Callback handler that refreshes a {@link TimedAttemptScope} on any LangChain
45
+ * callback event emitted under the node's run. Because it is attached via
46
+ * `config.callbacks`, it only observes events from runs descended from this
47
+ * node's attempt, not from sibling nodes.
48
+ *
49
+ * @internal
50
+ */
51
+ var IdleProgressCallbackHandler = class extends _langchain_core_callbacks_base.BaseCallbackHandler {
52
+ name = "IdleProgressCallbackHandler";
53
+ awaitHandlers = false;
54
+ #scope;
55
+ constructor(scope) {
56
+ super();
57
+ this.#scope = scope;
58
+ }
59
+ #touch = () => {
60
+ this.#scope.autoTouch();
61
+ };
62
+ handleLLMStart = this.#touch;
63
+ handleChatModelStart = this.#touch;
64
+ handleLLMNewToken = this.#touch;
65
+ handleLLMEnd = this.#touch;
66
+ handleLLMError = this.#touch;
67
+ handleChainStart = this.#touch;
68
+ handleChainEnd = this.#touch;
69
+ handleChainError = this.#touch;
70
+ handleToolStart = this.#touch;
71
+ handleToolEnd = this.#touch;
72
+ handleToolError = this.#touch;
73
+ handleText = this.#touch;
74
+ handleRetrieverStart = this.#touch;
75
+ handleRetrieverEnd = this.#touch;
76
+ handleRetrieverError = this.#touch;
77
+ handleCustomEvent = this.#touch;
78
+ };
79
+ /**
80
+ * Wrap the node attempt config so observable-progress signals refresh the idle
81
+ * clock and are dropped once the scope is closed. Also injects
82
+ * {@link LangGraphRunnableConfig.heartbeat}.
83
+ */
84
+ function wrapConfig(config, scope, policy, taskName) {
85
+ const configurable = config.configurable ?? {};
86
+ const patch = {};
87
+ const send = configurable[require_constants.CONFIG_KEY_SEND];
88
+ if (typeof send === "function") patch[require_constants.CONFIG_KEY_SEND] = (writes) => {
89
+ if (!scope.active) return void 0;
90
+ if (writes && writes.length) scope.autoTouch();
91
+ return send(writes);
92
+ };
93
+ const callFn = configurable[require_constants.CONFIG_KEY_CALL];
94
+ if (typeof callFn === "function") patch[require_constants.CONFIG_KEY_CALL] = (...args) => {
95
+ if (!scope.active) throw new Error(`Node "${taskName}" attempt was cancelled after its timeout fired`);
96
+ scope.autoTouch();
97
+ return callFn(...args);
98
+ };
99
+ const wrapped = { ...Object.keys(patch).length > 0 ? require_index.patchConfigurable(config, patch) : config };
100
+ wrapped.heartbeat = () => {
101
+ if (policy.idleTimeout !== void 0) scope.touch();
102
+ };
103
+ if (typeof wrapped.writer === "function") {
104
+ const writer = wrapped.writer;
105
+ wrapped.writer = ((chunk) => {
106
+ if (!scope.active) return void 0;
107
+ scope.autoTouch();
108
+ return writer(chunk);
109
+ });
110
+ }
111
+ if ((policy.refreshOn ?? "auto") === "auto" && policy.idleTimeout !== void 0) {
112
+ const handler = new IdleProgressCallbackHandler(scope);
113
+ const cb = wrapped.callbacks;
114
+ if (cb === void 0) wrapped.callbacks = [handler];
115
+ else if (Array.isArray(cb)) wrapped.callbacks = [...cb, handler];
116
+ else {
117
+ const copied = cb.copy();
118
+ copied.addHandler(handler, true);
119
+ wrapped.callbacks = copied;
120
+ }
121
+ }
122
+ return wrapped;
123
+ }
124
+ /**
125
+ * Run a single node attempt under a {@link TimeoutPolicy}.
126
+ *
127
+ * Races the node invocation against per-attempt run/idle watchdogs. On
128
+ * successful completion (or node error), returns/rethrows normally. When a
129
+ * watchdog fires first, the scope is closed, the task's buffered writes are
130
+ * dropped, the attempt's {@link AbortSignal} is aborted, and a
131
+ * {@link NodeTimeoutError} is thrown.
132
+ *
133
+ * @internal
134
+ */
135
+ async function runAttemptWithTimeout(task, config, policy, invoke) {
136
+ const scope = new TimedAttemptScope(policy.refreshOn ?? "auto");
137
+ const timeoutController = new AbortController();
138
+ const { signal: composedSignal, dispose } = require_index.combineAbortSignals(config.signal, timeoutController.signal);
139
+ const scopedConfig = wrapConfig({
140
+ ...config,
141
+ signal: composedSignal
142
+ }, scope, policy, String(task.name));
143
+ const start = Date.now();
144
+ const nodeOutcome = invoke(scopedConfig).then((value) => ({
145
+ type: "ok",
146
+ value
147
+ }), (error) => ({
148
+ type: "err",
149
+ error
150
+ }));
151
+ let runTimer;
152
+ let idleTimer;
153
+ const clearTimers = () => {
154
+ if (runTimer !== void 0) clearTimeout(runTimer);
155
+ if (idleTimer !== void 0) clearTimeout(idleTimer);
156
+ };
157
+ const watchdog = new Promise((resolve) => {
158
+ if (policy.runTimeout !== void 0) runTimer = setTimeout(() => resolve({
159
+ type: "timeout",
160
+ kind: "run"
161
+ }), policy.runTimeout);
162
+ if (policy.idleTimeout !== void 0) {
163
+ const idleMs = policy.idleTimeout;
164
+ const checkIdle = () => {
165
+ const remaining = scope.lastProgress + idleMs - Date.now();
166
+ if (remaining <= 0) resolve({
167
+ type: "timeout",
168
+ kind: "idle"
169
+ });
170
+ else idleTimer = setTimeout(checkIdle, remaining);
171
+ };
172
+ idleTimer = setTimeout(checkIdle, idleMs);
173
+ }
174
+ });
175
+ let outcome;
176
+ try {
177
+ outcome = await Promise.race([nodeOutcome, watchdog]);
178
+ } finally {
179
+ clearTimers();
180
+ }
181
+ if (outcome.type !== "timeout") {
182
+ const now = Date.now();
183
+ if (policy.runTimeout !== void 0 && now - start >= policy.runTimeout) outcome = {
184
+ type: "timeout",
185
+ kind: "run"
186
+ };
187
+ else if (policy.idleTimeout !== void 0 && now - scope.lastProgress >= policy.idleTimeout) outcome = {
188
+ type: "timeout",
189
+ kind: "idle"
190
+ };
191
+ }
192
+ if (outcome.type === "ok") {
193
+ dispose?.();
194
+ return outcome.value;
195
+ }
196
+ if (outcome.type === "err") {
197
+ dispose?.();
198
+ throw outcome.error;
199
+ }
200
+ const elapsed = Date.now() - start;
201
+ scope.close();
202
+ task.writes.splice(0, task.writes.length);
203
+ timeoutController.abort();
204
+ dispose?.();
205
+ throw new require_errors.NodeTimeoutError({
206
+ node: String(task.name),
207
+ elapsed,
208
+ kind: outcome.kind,
209
+ runTimeout: policy.runTimeout,
210
+ idleTimeout: policy.idleTimeout
211
+ });
212
+ }
213
+ //#endregion
214
+ exports.runAttemptWithTimeout = runAttemptWithTimeout;
215
+
216
+ //# sourceMappingURL=timeout.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeout.cjs","names":["BaseCallbackHandler","#scope","#touch","CONFIG_KEY_SEND","CONFIG_KEY_CALL","patchConfigurable","combineAbortSignals","NodeTimeoutError"],"sources":["../../src/pregel/timeout.ts"],"sourcesContent":["import { BaseCallbackHandler } from \"@langchain/core/callbacks/base\";\nimport { PendingWrite } from \"@langchain/langgraph-checkpoint\";\nimport { CONFIG_KEY_CALL, CONFIG_KEY_SEND } from \"../constants.js\";\nimport { NodeTimeoutError } from \"../errors.js\";\nimport { LangGraphRunnableConfig } from \"./runnable_types.js\";\nimport { PregelExecutableTask } from \"./types.js\";\nimport {\n combineAbortSignals,\n patchConfigurable,\n TimeoutPolicy,\n} from \"./utils/index.js\";\n\n/**\n * Tracks the live progress state of a single timed node attempt.\n *\n * The scope guards observable-progress channels (writes, child-task\n * scheduling, the custom stream writer, callbacks) so that:\n *\n * 1. `idleTimeout` is refreshed whenever the node makes progress, and\n * 2. once the attempt is `close()`d after a timeout fires, late writes/calls\n * from the still-running background task are dropped (Python parity:\n * buffered writes from the failed attempt must not leak into the\n * checkpoint).\n *\n * @internal\n */\nclass TimedAttemptScope {\n active = true;\n\n lastProgress = Date.now();\n\n private refreshOn: \"auto\" | \"heartbeat\";\n\n constructor(refreshOn: \"auto\" | \"heartbeat\") {\n this.refreshOn = refreshOn;\n }\n\n /** Record progress now. Always honored (used by `runtime.heartbeat()`). */\n touch(): void {\n this.lastProgress = Date.now();\n }\n\n /**\n * Record progress for an automatic signal (write/call/stream/callback).\n * No-op when `refreshOn === \"heartbeat\"`, where only explicit heartbeats\n * count as progress.\n */\n autoTouch(): void {\n if (this.refreshOn === \"auto\") {\n this.lastProgress = Date.now();\n }\n }\n\n close(): void {\n this.active = false;\n }\n}\n\n/**\n * Callback handler that refreshes a {@link TimedAttemptScope} on any LangChain\n * callback event emitted under the node's run. Because it is attached via\n * `config.callbacks`, it only observes events from runs descended from this\n * node's attempt, not from sibling nodes.\n *\n * @internal\n */\nclass IdleProgressCallbackHandler extends BaseCallbackHandler {\n name = \"IdleProgressCallbackHandler\";\n\n awaitHandlers = false;\n\n #scope: TimedAttemptScope;\n\n constructor(scope: TimedAttemptScope) {\n super();\n this.#scope = scope;\n }\n\n #touch = () => {\n this.#scope.autoTouch();\n };\n\n handleLLMStart = this.#touch;\n\n handleChatModelStart = this.#touch;\n\n handleLLMNewToken = this.#touch;\n\n handleLLMEnd = this.#touch;\n\n handleLLMError = this.#touch;\n\n handleChainStart = this.#touch;\n\n handleChainEnd = this.#touch;\n\n handleChainError = this.#touch;\n\n handleToolStart = this.#touch;\n\n handleToolEnd = this.#touch;\n\n handleToolError = this.#touch;\n\n handleText = this.#touch;\n\n handleRetrieverStart = this.#touch;\n\n handleRetrieverEnd = this.#touch;\n\n handleRetrieverError = this.#touch;\n\n handleCustomEvent = this.#touch;\n}\n\n/**\n * Wrap the node attempt config so observable-progress signals refresh the idle\n * clock and are dropped once the scope is closed. Also injects\n * {@link LangGraphRunnableConfig.heartbeat}.\n */\nfunction wrapConfig(\n config: LangGraphRunnableConfig,\n scope: TimedAttemptScope,\n policy: TimeoutPolicy,\n taskName: string\n): LangGraphRunnableConfig {\n const configurable = config.configurable ?? {};\n const patch: Record<string, unknown> = {};\n\n const send = configurable[CONFIG_KEY_SEND];\n if (typeof send === \"function\") {\n patch[CONFIG_KEY_SEND] = (writes: PendingWrite[]) => {\n if (!scope.active) return undefined;\n if (writes && writes.length) scope.autoTouch();\n return send(writes);\n };\n }\n\n const callFn = configurable[CONFIG_KEY_CALL];\n if (typeof callFn === \"function\") {\n patch[CONFIG_KEY_CALL] = (...args: unknown[]) => {\n if (!scope.active) {\n throw new Error(\n `Node \"${taskName}\" attempt was cancelled after its timeout fired`\n );\n }\n scope.autoTouch();\n return (callFn as (...a: unknown[]) => unknown)(...args);\n };\n }\n\n const out: LangGraphRunnableConfig =\n Object.keys(patch).length > 0 ? patchConfigurable(config, patch) : config;\n const wrapped: LangGraphRunnableConfig = { ...out };\n\n // `heartbeat` always resets the idle clock, even under `refreshOn:\n // \"heartbeat\"`. It is a no-op when no idle timeout is configured.\n wrapped.heartbeat = () => {\n if (policy.idleTimeout !== undefined) scope.touch();\n };\n\n if (typeof wrapped.writer === \"function\") {\n const writer = wrapped.writer;\n wrapped.writer = ((chunk: unknown) => {\n if (!scope.active) return undefined;\n scope.autoTouch();\n return (writer as (c: unknown) => unknown)(chunk);\n }) as typeof wrapped.writer;\n }\n\n if (\n (policy.refreshOn ?? \"auto\") === \"auto\" &&\n policy.idleTimeout !== undefined\n ) {\n const handler = new IdleProgressCallbackHandler(scope);\n const cb = wrapped.callbacks;\n if (cb === undefined) {\n wrapped.callbacks = [handler];\n } else if (Array.isArray(cb)) {\n wrapped.callbacks = [...cb, handler];\n } else {\n const copied = cb.copy();\n copied.addHandler(handler, true);\n wrapped.callbacks = copied;\n }\n }\n\n return wrapped;\n}\n\ntype AttemptOutcome<T> =\n | { type: \"ok\"; value: T }\n | { type: \"err\"; error: unknown }\n | { type: \"timeout\"; kind: \"run\" | \"idle\" };\n\n/**\n * Run a single node attempt under a {@link TimeoutPolicy}.\n *\n * Races the node invocation against per-attempt run/idle watchdogs. On\n * successful completion (or node error), returns/rethrows normally. When a\n * watchdog fires first, the scope is closed, the task's buffered writes are\n * dropped, the attempt's {@link AbortSignal} is aborted, and a\n * {@link NodeTimeoutError} is thrown.\n *\n * @internal\n */\nexport async function runAttemptWithTimeout<T>(\n task: PregelExecutableTask<string, string>,\n config: LangGraphRunnableConfig,\n policy: TimeoutPolicy,\n invoke: (scopedConfig: LangGraphRunnableConfig) => Promise<T>\n): Promise<T> {\n const refreshOn = policy.refreshOn ?? \"auto\";\n const scope = new TimedAttemptScope(refreshOn);\n\n const timeoutController = new AbortController();\n const { signal: composedSignal, dispose } = combineAbortSignals(\n config.signal,\n timeoutController.signal\n );\n\n const scopedConfig = wrapConfig(\n { ...config, signal: composedSignal },\n scope,\n policy,\n String(task.name)\n );\n\n const start = Date.now();\n const bg = invoke(scopedConfig);\n\n // Normalize the node result into an outcome. Catching the rejection here\n // (rather than via a separate `bg.catch`) keeps `bg` handled even when a\n // watchdog wins the race below, so its late settlement can never surface as\n // an unhandled rejection.\n const nodeOutcome: Promise<AttemptOutcome<T>> = bg.then(\n (value) => ({ type: \"ok\", value }),\n (error) => ({ type: \"err\", error })\n );\n\n let runTimer: ReturnType<typeof setTimeout> | undefined;\n let idleTimer: ReturnType<typeof setTimeout> | undefined;\n const clearTimers = () => {\n if (runTimer !== undefined) clearTimeout(runTimer);\n if (idleTimer !== undefined) clearTimeout(idleTimer);\n };\n\n // The watchdog never rejects: it either resolves with a timeout outcome or\n // stays pending forever (and is dropped) when the node wins the race. The\n // idle timer re-arms itself off `scope.lastProgress`, so it can't be a single\n // fixed `setTimeout`.\n const watchdog = new Promise<AttemptOutcome<T>>((resolve) => {\n if (policy.runTimeout !== undefined) {\n runTimer = setTimeout(\n () => resolve({ type: \"timeout\", kind: \"run\" }),\n policy.runTimeout\n );\n }\n\n if (policy.idleTimeout !== undefined) {\n const idleMs = policy.idleTimeout;\n const checkIdle = () => {\n const remaining = scope.lastProgress + idleMs - Date.now();\n if (remaining <= 0) {\n resolve({ type: \"timeout\", kind: \"idle\" });\n } else {\n idleTimer = setTimeout(checkIdle, remaining);\n }\n };\n idleTimer = setTimeout(checkIdle, idleMs);\n }\n });\n\n let outcome: AttemptOutcome<T>;\n try {\n outcome = await Promise.race([nodeOutcome, watchdog]);\n } finally {\n clearTimers();\n }\n\n // Watchdog timers are macrotasks and cannot fire while a synchronous\n // (CPU-bound) node blocks the event loop. Such a node — or one with a long\n // synchronous prefix — can therefore settle the race as \"ok\"/\"err\" before an\n // already-expired timer ever runs, bypassing the documented hard wall-clock\n // cap. Re-check the budget against wall-clock time here so the caps hold for\n // synchronous nodes too. (For genuinely async work the watchdog already\n // wins the race, so this only catches what timers structurally cannot.)\n if (outcome.type !== \"timeout\") {\n const now = Date.now();\n if (policy.runTimeout !== undefined && now - start >= policy.runTimeout) {\n outcome = { type: \"timeout\", kind: \"run\" };\n } else if (\n policy.idleTimeout !== undefined &&\n now - scope.lastProgress >= policy.idleTimeout\n ) {\n outcome = { type: \"timeout\", kind: \"idle\" };\n }\n }\n\n if (outcome.type === \"ok\") {\n dispose?.();\n return outcome.value;\n }\n if (outcome.type === \"err\") {\n dispose?.();\n throw outcome.error;\n }\n\n // A watchdog fired: close the scope (drop late writes/calls), discard the\n // attempt's buffered writes, abort the node, and surface a NodeTimeoutError.\n const elapsed = Date.now() - start;\n scope.close();\n task.writes.splice(0, task.writes.length);\n // Abort BEFORE disposing the combined signal so the abort actually\n // propagates to the node's signal (dispose removes the relay listeners).\n timeoutController.abort();\n dispose?.();\n\n throw new NodeTimeoutError({\n node: String(task.name),\n elapsed,\n kind: outcome.kind,\n runTimeout: policy.runTimeout,\n idleTimeout: policy.idleTimeout,\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,216 @@
1
+ import { CONFIG_KEY_CALL, CONFIG_KEY_SEND } from "../constants.js";
2
+ import { NodeTimeoutError } from "../errors.js";
3
+ import { combineAbortSignals, patchConfigurable } from "./utils/index.js";
4
+ import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
5
+ //#region src/pregel/timeout.ts
6
+ /**
7
+ * Tracks the live progress state of a single timed node attempt.
8
+ *
9
+ * The scope guards observable-progress channels (writes, child-task
10
+ * scheduling, the custom stream writer, callbacks) so that:
11
+ *
12
+ * 1. `idleTimeout` is refreshed whenever the node makes progress, and
13
+ * 2. once the attempt is `close()`d after a timeout fires, late writes/calls
14
+ * from the still-running background task are dropped (Python parity:
15
+ * buffered writes from the failed attempt must not leak into the
16
+ * checkpoint).
17
+ *
18
+ * @internal
19
+ */
20
+ var TimedAttemptScope = class {
21
+ active = true;
22
+ lastProgress = Date.now();
23
+ refreshOn;
24
+ constructor(refreshOn) {
25
+ this.refreshOn = refreshOn;
26
+ }
27
+ /** Record progress now. Always honored (used by `runtime.heartbeat()`). */
28
+ touch() {
29
+ this.lastProgress = Date.now();
30
+ }
31
+ /**
32
+ * Record progress for an automatic signal (write/call/stream/callback).
33
+ * No-op when `refreshOn === "heartbeat"`, where only explicit heartbeats
34
+ * count as progress.
35
+ */
36
+ autoTouch() {
37
+ if (this.refreshOn === "auto") this.lastProgress = Date.now();
38
+ }
39
+ close() {
40
+ this.active = false;
41
+ }
42
+ };
43
+ /**
44
+ * Callback handler that refreshes a {@link TimedAttemptScope} on any LangChain
45
+ * callback event emitted under the node's run. Because it is attached via
46
+ * `config.callbacks`, it only observes events from runs descended from this
47
+ * node's attempt, not from sibling nodes.
48
+ *
49
+ * @internal
50
+ */
51
+ var IdleProgressCallbackHandler = class extends BaseCallbackHandler {
52
+ name = "IdleProgressCallbackHandler";
53
+ awaitHandlers = false;
54
+ #scope;
55
+ constructor(scope) {
56
+ super();
57
+ this.#scope = scope;
58
+ }
59
+ #touch = () => {
60
+ this.#scope.autoTouch();
61
+ };
62
+ handleLLMStart = this.#touch;
63
+ handleChatModelStart = this.#touch;
64
+ handleLLMNewToken = this.#touch;
65
+ handleLLMEnd = this.#touch;
66
+ handleLLMError = this.#touch;
67
+ handleChainStart = this.#touch;
68
+ handleChainEnd = this.#touch;
69
+ handleChainError = this.#touch;
70
+ handleToolStart = this.#touch;
71
+ handleToolEnd = this.#touch;
72
+ handleToolError = this.#touch;
73
+ handleText = this.#touch;
74
+ handleRetrieverStart = this.#touch;
75
+ handleRetrieverEnd = this.#touch;
76
+ handleRetrieverError = this.#touch;
77
+ handleCustomEvent = this.#touch;
78
+ };
79
+ /**
80
+ * Wrap the node attempt config so observable-progress signals refresh the idle
81
+ * clock and are dropped once the scope is closed. Also injects
82
+ * {@link LangGraphRunnableConfig.heartbeat}.
83
+ */
84
+ function wrapConfig(config, scope, policy, taskName) {
85
+ const configurable = config.configurable ?? {};
86
+ const patch = {};
87
+ const send = configurable[CONFIG_KEY_SEND];
88
+ if (typeof send === "function") patch[CONFIG_KEY_SEND] = (writes) => {
89
+ if (!scope.active) return void 0;
90
+ if (writes && writes.length) scope.autoTouch();
91
+ return send(writes);
92
+ };
93
+ const callFn = configurable[CONFIG_KEY_CALL];
94
+ if (typeof callFn === "function") patch[CONFIG_KEY_CALL] = (...args) => {
95
+ if (!scope.active) throw new Error(`Node "${taskName}" attempt was cancelled after its timeout fired`);
96
+ scope.autoTouch();
97
+ return callFn(...args);
98
+ };
99
+ const wrapped = { ...Object.keys(patch).length > 0 ? patchConfigurable(config, patch) : config };
100
+ wrapped.heartbeat = () => {
101
+ if (policy.idleTimeout !== void 0) scope.touch();
102
+ };
103
+ if (typeof wrapped.writer === "function") {
104
+ const writer = wrapped.writer;
105
+ wrapped.writer = ((chunk) => {
106
+ if (!scope.active) return void 0;
107
+ scope.autoTouch();
108
+ return writer(chunk);
109
+ });
110
+ }
111
+ if ((policy.refreshOn ?? "auto") === "auto" && policy.idleTimeout !== void 0) {
112
+ const handler = new IdleProgressCallbackHandler(scope);
113
+ const cb = wrapped.callbacks;
114
+ if (cb === void 0) wrapped.callbacks = [handler];
115
+ else if (Array.isArray(cb)) wrapped.callbacks = [...cb, handler];
116
+ else {
117
+ const copied = cb.copy();
118
+ copied.addHandler(handler, true);
119
+ wrapped.callbacks = copied;
120
+ }
121
+ }
122
+ return wrapped;
123
+ }
124
+ /**
125
+ * Run a single node attempt under a {@link TimeoutPolicy}.
126
+ *
127
+ * Races the node invocation against per-attempt run/idle watchdogs. On
128
+ * successful completion (or node error), returns/rethrows normally. When a
129
+ * watchdog fires first, the scope is closed, the task's buffered writes are
130
+ * dropped, the attempt's {@link AbortSignal} is aborted, and a
131
+ * {@link NodeTimeoutError} is thrown.
132
+ *
133
+ * @internal
134
+ */
135
+ async function runAttemptWithTimeout(task, config, policy, invoke) {
136
+ const scope = new TimedAttemptScope(policy.refreshOn ?? "auto");
137
+ const timeoutController = new AbortController();
138
+ const { signal: composedSignal, dispose } = combineAbortSignals(config.signal, timeoutController.signal);
139
+ const scopedConfig = wrapConfig({
140
+ ...config,
141
+ signal: composedSignal
142
+ }, scope, policy, String(task.name));
143
+ const start = Date.now();
144
+ const nodeOutcome = invoke(scopedConfig).then((value) => ({
145
+ type: "ok",
146
+ value
147
+ }), (error) => ({
148
+ type: "err",
149
+ error
150
+ }));
151
+ let runTimer;
152
+ let idleTimer;
153
+ const clearTimers = () => {
154
+ if (runTimer !== void 0) clearTimeout(runTimer);
155
+ if (idleTimer !== void 0) clearTimeout(idleTimer);
156
+ };
157
+ const watchdog = new Promise((resolve) => {
158
+ if (policy.runTimeout !== void 0) runTimer = setTimeout(() => resolve({
159
+ type: "timeout",
160
+ kind: "run"
161
+ }), policy.runTimeout);
162
+ if (policy.idleTimeout !== void 0) {
163
+ const idleMs = policy.idleTimeout;
164
+ const checkIdle = () => {
165
+ const remaining = scope.lastProgress + idleMs - Date.now();
166
+ if (remaining <= 0) resolve({
167
+ type: "timeout",
168
+ kind: "idle"
169
+ });
170
+ else idleTimer = setTimeout(checkIdle, remaining);
171
+ };
172
+ idleTimer = setTimeout(checkIdle, idleMs);
173
+ }
174
+ });
175
+ let outcome;
176
+ try {
177
+ outcome = await Promise.race([nodeOutcome, watchdog]);
178
+ } finally {
179
+ clearTimers();
180
+ }
181
+ if (outcome.type !== "timeout") {
182
+ const now = Date.now();
183
+ if (policy.runTimeout !== void 0 && now - start >= policy.runTimeout) outcome = {
184
+ type: "timeout",
185
+ kind: "run"
186
+ };
187
+ else if (policy.idleTimeout !== void 0 && now - scope.lastProgress >= policy.idleTimeout) outcome = {
188
+ type: "timeout",
189
+ kind: "idle"
190
+ };
191
+ }
192
+ if (outcome.type === "ok") {
193
+ dispose?.();
194
+ return outcome.value;
195
+ }
196
+ if (outcome.type === "err") {
197
+ dispose?.();
198
+ throw outcome.error;
199
+ }
200
+ const elapsed = Date.now() - start;
201
+ scope.close();
202
+ task.writes.splice(0, task.writes.length);
203
+ timeoutController.abort();
204
+ dispose?.();
205
+ throw new NodeTimeoutError({
206
+ node: String(task.name),
207
+ elapsed,
208
+ kind: outcome.kind,
209
+ runTimeout: policy.runTimeout,
210
+ idleTimeout: policy.idleTimeout
211
+ });
212
+ }
213
+ //#endregion
214
+ export { runAttemptWithTimeout };
215
+
216
+ //# sourceMappingURL=timeout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeout.js","names":["#scope","#touch"],"sources":["../../src/pregel/timeout.ts"],"sourcesContent":["import { BaseCallbackHandler } from \"@langchain/core/callbacks/base\";\nimport { PendingWrite } from \"@langchain/langgraph-checkpoint\";\nimport { CONFIG_KEY_CALL, CONFIG_KEY_SEND } from \"../constants.js\";\nimport { NodeTimeoutError } from \"../errors.js\";\nimport { LangGraphRunnableConfig } from \"./runnable_types.js\";\nimport { PregelExecutableTask } from \"./types.js\";\nimport {\n combineAbortSignals,\n patchConfigurable,\n TimeoutPolicy,\n} from \"./utils/index.js\";\n\n/**\n * Tracks the live progress state of a single timed node attempt.\n *\n * The scope guards observable-progress channels (writes, child-task\n * scheduling, the custom stream writer, callbacks) so that:\n *\n * 1. `idleTimeout` is refreshed whenever the node makes progress, and\n * 2. once the attempt is `close()`d after a timeout fires, late writes/calls\n * from the still-running background task are dropped (Python parity:\n * buffered writes from the failed attempt must not leak into the\n * checkpoint).\n *\n * @internal\n */\nclass TimedAttemptScope {\n active = true;\n\n lastProgress = Date.now();\n\n private refreshOn: \"auto\" | \"heartbeat\";\n\n constructor(refreshOn: \"auto\" | \"heartbeat\") {\n this.refreshOn = refreshOn;\n }\n\n /** Record progress now. Always honored (used by `runtime.heartbeat()`). */\n touch(): void {\n this.lastProgress = Date.now();\n }\n\n /**\n * Record progress for an automatic signal (write/call/stream/callback).\n * No-op when `refreshOn === \"heartbeat\"`, where only explicit heartbeats\n * count as progress.\n */\n autoTouch(): void {\n if (this.refreshOn === \"auto\") {\n this.lastProgress = Date.now();\n }\n }\n\n close(): void {\n this.active = false;\n }\n}\n\n/**\n * Callback handler that refreshes a {@link TimedAttemptScope} on any LangChain\n * callback event emitted under the node's run. Because it is attached via\n * `config.callbacks`, it only observes events from runs descended from this\n * node's attempt, not from sibling nodes.\n *\n * @internal\n */\nclass IdleProgressCallbackHandler extends BaseCallbackHandler {\n name = \"IdleProgressCallbackHandler\";\n\n awaitHandlers = false;\n\n #scope: TimedAttemptScope;\n\n constructor(scope: TimedAttemptScope) {\n super();\n this.#scope = scope;\n }\n\n #touch = () => {\n this.#scope.autoTouch();\n };\n\n handleLLMStart = this.#touch;\n\n handleChatModelStart = this.#touch;\n\n handleLLMNewToken = this.#touch;\n\n handleLLMEnd = this.#touch;\n\n handleLLMError = this.#touch;\n\n handleChainStart = this.#touch;\n\n handleChainEnd = this.#touch;\n\n handleChainError = this.#touch;\n\n handleToolStart = this.#touch;\n\n handleToolEnd = this.#touch;\n\n handleToolError = this.#touch;\n\n handleText = this.#touch;\n\n handleRetrieverStart = this.#touch;\n\n handleRetrieverEnd = this.#touch;\n\n handleRetrieverError = this.#touch;\n\n handleCustomEvent = this.#touch;\n}\n\n/**\n * Wrap the node attempt config so observable-progress signals refresh the idle\n * clock and are dropped once the scope is closed. Also injects\n * {@link LangGraphRunnableConfig.heartbeat}.\n */\nfunction wrapConfig(\n config: LangGraphRunnableConfig,\n scope: TimedAttemptScope,\n policy: TimeoutPolicy,\n taskName: string\n): LangGraphRunnableConfig {\n const configurable = config.configurable ?? {};\n const patch: Record<string, unknown> = {};\n\n const send = configurable[CONFIG_KEY_SEND];\n if (typeof send === \"function\") {\n patch[CONFIG_KEY_SEND] = (writes: PendingWrite[]) => {\n if (!scope.active) return undefined;\n if (writes && writes.length) scope.autoTouch();\n return send(writes);\n };\n }\n\n const callFn = configurable[CONFIG_KEY_CALL];\n if (typeof callFn === \"function\") {\n patch[CONFIG_KEY_CALL] = (...args: unknown[]) => {\n if (!scope.active) {\n throw new Error(\n `Node \"${taskName}\" attempt was cancelled after its timeout fired`\n );\n }\n scope.autoTouch();\n return (callFn as (...a: unknown[]) => unknown)(...args);\n };\n }\n\n const out: LangGraphRunnableConfig =\n Object.keys(patch).length > 0 ? patchConfigurable(config, patch) : config;\n const wrapped: LangGraphRunnableConfig = { ...out };\n\n // `heartbeat` always resets the idle clock, even under `refreshOn:\n // \"heartbeat\"`. It is a no-op when no idle timeout is configured.\n wrapped.heartbeat = () => {\n if (policy.idleTimeout !== undefined) scope.touch();\n };\n\n if (typeof wrapped.writer === \"function\") {\n const writer = wrapped.writer;\n wrapped.writer = ((chunk: unknown) => {\n if (!scope.active) return undefined;\n scope.autoTouch();\n return (writer as (c: unknown) => unknown)(chunk);\n }) as typeof wrapped.writer;\n }\n\n if (\n (policy.refreshOn ?? \"auto\") === \"auto\" &&\n policy.idleTimeout !== undefined\n ) {\n const handler = new IdleProgressCallbackHandler(scope);\n const cb = wrapped.callbacks;\n if (cb === undefined) {\n wrapped.callbacks = [handler];\n } else if (Array.isArray(cb)) {\n wrapped.callbacks = [...cb, handler];\n } else {\n const copied = cb.copy();\n copied.addHandler(handler, true);\n wrapped.callbacks = copied;\n }\n }\n\n return wrapped;\n}\n\ntype AttemptOutcome<T> =\n | { type: \"ok\"; value: T }\n | { type: \"err\"; error: unknown }\n | { type: \"timeout\"; kind: \"run\" | \"idle\" };\n\n/**\n * Run a single node attempt under a {@link TimeoutPolicy}.\n *\n * Races the node invocation against per-attempt run/idle watchdogs. On\n * successful completion (or node error), returns/rethrows normally. When a\n * watchdog fires first, the scope is closed, the task's buffered writes are\n * dropped, the attempt's {@link AbortSignal} is aborted, and a\n * {@link NodeTimeoutError} is thrown.\n *\n * @internal\n */\nexport async function runAttemptWithTimeout<T>(\n task: PregelExecutableTask<string, string>,\n config: LangGraphRunnableConfig,\n policy: TimeoutPolicy,\n invoke: (scopedConfig: LangGraphRunnableConfig) => Promise<T>\n): Promise<T> {\n const refreshOn = policy.refreshOn ?? \"auto\";\n const scope = new TimedAttemptScope(refreshOn);\n\n const timeoutController = new AbortController();\n const { signal: composedSignal, dispose } = combineAbortSignals(\n config.signal,\n timeoutController.signal\n );\n\n const scopedConfig = wrapConfig(\n { ...config, signal: composedSignal },\n scope,\n policy,\n String(task.name)\n );\n\n const start = Date.now();\n const bg = invoke(scopedConfig);\n\n // Normalize the node result into an outcome. Catching the rejection here\n // (rather than via a separate `bg.catch`) keeps `bg` handled even when a\n // watchdog wins the race below, so its late settlement can never surface as\n // an unhandled rejection.\n const nodeOutcome: Promise<AttemptOutcome<T>> = bg.then(\n (value) => ({ type: \"ok\", value }),\n (error) => ({ type: \"err\", error })\n );\n\n let runTimer: ReturnType<typeof setTimeout> | undefined;\n let idleTimer: ReturnType<typeof setTimeout> | undefined;\n const clearTimers = () => {\n if (runTimer !== undefined) clearTimeout(runTimer);\n if (idleTimer !== undefined) clearTimeout(idleTimer);\n };\n\n // The watchdog never rejects: it either resolves with a timeout outcome or\n // stays pending forever (and is dropped) when the node wins the race. The\n // idle timer re-arms itself off `scope.lastProgress`, so it can't be a single\n // fixed `setTimeout`.\n const watchdog = new Promise<AttemptOutcome<T>>((resolve) => {\n if (policy.runTimeout !== undefined) {\n runTimer = setTimeout(\n () => resolve({ type: \"timeout\", kind: \"run\" }),\n policy.runTimeout\n );\n }\n\n if (policy.idleTimeout !== undefined) {\n const idleMs = policy.idleTimeout;\n const checkIdle = () => {\n const remaining = scope.lastProgress + idleMs - Date.now();\n if (remaining <= 0) {\n resolve({ type: \"timeout\", kind: \"idle\" });\n } else {\n idleTimer = setTimeout(checkIdle, remaining);\n }\n };\n idleTimer = setTimeout(checkIdle, idleMs);\n }\n });\n\n let outcome: AttemptOutcome<T>;\n try {\n outcome = await Promise.race([nodeOutcome, watchdog]);\n } finally {\n clearTimers();\n }\n\n // Watchdog timers are macrotasks and cannot fire while a synchronous\n // (CPU-bound) node blocks the event loop. Such a node — or one with a long\n // synchronous prefix — can therefore settle the race as \"ok\"/\"err\" before an\n // already-expired timer ever runs, bypassing the documented hard wall-clock\n // cap. Re-check the budget against wall-clock time here so the caps hold for\n // synchronous nodes too. (For genuinely async work the watchdog already\n // wins the race, so this only catches what timers structurally cannot.)\n if (outcome.type !== \"timeout\") {\n const now = Date.now();\n if (policy.runTimeout !== undefined && now - start >= policy.runTimeout) {\n outcome = { type: \"timeout\", kind: \"run\" };\n } else if (\n policy.idleTimeout !== undefined &&\n now - scope.lastProgress >= policy.idleTimeout\n ) {\n outcome = { type: \"timeout\", kind: \"idle\" };\n }\n }\n\n if (outcome.type === \"ok\") {\n dispose?.();\n return outcome.value;\n }\n if (outcome.type === \"err\") {\n dispose?.();\n throw outcome.error;\n }\n\n // A watchdog fired: close the scope (drop late writes/calls), discard the\n // attempt's buffered writes, abort the node, and surface a NodeTimeoutError.\n const elapsed = Date.now() - start;\n scope.close();\n task.writes.splice(0, task.writes.length);\n // Abort BEFORE disposing the combined signal so the abort actually\n // propagates to the node's signal (dispose removes the relay listeners).\n timeoutController.abort();\n dispose?.();\n\n throw new NodeTimeoutError({\n node: String(task.name),\n elapsed,\n kind: outcome.kind,\n runTimeout: policy.runTimeout,\n idleTimeout: policy.idleTimeout,\n });\n}\n"],"mappings":"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"}
@@ -5,14 +5,16 @@ var Call = class {
5
5
  input;
6
6
  retry;
7
7
  cache;
8
+ timeout;
8
9
  callbacks;
9
10
  __lg_type = "call";
10
- constructor({ func, name, input, retry, cache, callbacks }) {
11
+ constructor({ func, name, input, retry, cache, timeout, callbacks }) {
11
12
  this.func = func;
12
13
  this.name = name;
13
14
  this.input = input;
14
15
  this.retry = retry;
15
16
  this.cache = cache;
17
+ this.timeout = timeout;
16
18
  this.callbacks = callbacks;
17
19
  }
18
20
  };