@langchain/react 0.3.4 → 1.0.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 (98) hide show
  1. package/README.md +48 -523
  2. package/dist/context.cjs +12 -30
  3. package/dist/context.cjs.map +1 -1
  4. package/dist/context.d.cts +22 -39
  5. package/dist/context.d.cts.map +1 -1
  6. package/dist/context.d.ts +22 -39
  7. package/dist/context.d.ts.map +1 -1
  8. package/dist/context.js +11 -29
  9. package/dist/context.js.map +1 -1
  10. package/dist/index.cjs +29 -30
  11. package/dist/index.d.cts +10 -7
  12. package/dist/index.d.cts.map +1 -1
  13. package/dist/index.d.ts +10 -7
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +10 -6
  16. package/dist/selectors.cjs +178 -0
  17. package/dist/selectors.cjs.map +1 -0
  18. package/dist/selectors.d.cts +183 -0
  19. package/dist/selectors.d.cts.map +1 -0
  20. package/dist/selectors.d.ts +183 -0
  21. package/dist/selectors.d.ts.map +1 -0
  22. package/dist/selectors.js +168 -0
  23. package/dist/selectors.js.map +1 -0
  24. package/dist/suspense-stream.cjs +34 -159
  25. package/dist/suspense-stream.cjs.map +1 -1
  26. package/dist/suspense-stream.d.cts +15 -71
  27. package/dist/suspense-stream.d.cts.map +1 -1
  28. package/dist/suspense-stream.d.ts +15 -71
  29. package/dist/suspense-stream.d.ts.map +1 -1
  30. package/dist/suspense-stream.js +35 -158
  31. package/dist/suspense-stream.js.map +1 -1
  32. package/dist/use-audio-player.cjs +679 -0
  33. package/dist/use-audio-player.cjs.map +1 -0
  34. package/dist/use-audio-player.d.cts +161 -0
  35. package/dist/use-audio-player.d.cts.map +1 -0
  36. package/dist/use-audio-player.d.ts +161 -0
  37. package/dist/use-audio-player.d.ts.map +1 -0
  38. package/dist/use-audio-player.js +679 -0
  39. package/dist/use-audio-player.js.map +1 -0
  40. package/dist/use-media-url.cjs +49 -0
  41. package/dist/use-media-url.cjs.map +1 -0
  42. package/dist/use-media-url.d.cts +28 -0
  43. package/dist/use-media-url.d.cts.map +1 -0
  44. package/dist/use-media-url.d.ts +28 -0
  45. package/dist/use-media-url.d.ts.map +1 -0
  46. package/dist/use-media-url.js +49 -0
  47. package/dist/use-media-url.js.map +1 -0
  48. package/dist/use-projection.cjs +41 -0
  49. package/dist/use-projection.cjs.map +1 -0
  50. package/dist/use-projection.d.cts +27 -0
  51. package/dist/use-projection.d.cts.map +1 -0
  52. package/dist/use-projection.d.ts +27 -0
  53. package/dist/use-projection.d.ts.map +1 -0
  54. package/dist/use-projection.js +41 -0
  55. package/dist/use-projection.js.map +1 -0
  56. package/dist/use-stream.cjs +185 -0
  57. package/dist/use-stream.cjs.map +1 -0
  58. package/dist/use-stream.d.cts +184 -0
  59. package/dist/use-stream.d.cts.map +1 -0
  60. package/dist/use-stream.d.ts +184 -0
  61. package/dist/use-stream.d.ts.map +1 -0
  62. package/dist/use-stream.js +183 -0
  63. package/dist/use-stream.js.map +1 -0
  64. package/dist/use-video-player.cjs +218 -0
  65. package/dist/use-video-player.cjs.map +1 -0
  66. package/dist/use-video-player.d.cts +65 -0
  67. package/dist/use-video-player.d.cts.map +1 -0
  68. package/dist/use-video-player.d.ts +65 -0
  69. package/dist/use-video-player.d.ts.map +1 -0
  70. package/dist/use-video-player.js +218 -0
  71. package/dist/use-video-player.js.map +1 -0
  72. package/package.json +9 -8
  73. package/dist/stream.cjs +0 -18
  74. package/dist/stream.cjs.map +0 -1
  75. package/dist/stream.custom.cjs +0 -209
  76. package/dist/stream.custom.cjs.map +0 -1
  77. package/dist/stream.custom.d.cts +0 -3
  78. package/dist/stream.custom.d.ts +0 -3
  79. package/dist/stream.custom.js +0 -209
  80. package/dist/stream.custom.js.map +0 -1
  81. package/dist/stream.d.cts +0 -174
  82. package/dist/stream.d.cts.map +0 -1
  83. package/dist/stream.d.ts +0 -174
  84. package/dist/stream.d.ts.map +0 -1
  85. package/dist/stream.js +0 -18
  86. package/dist/stream.js.map +0 -1
  87. package/dist/stream.lgp.cjs +0 -671
  88. package/dist/stream.lgp.cjs.map +0 -1
  89. package/dist/stream.lgp.js +0 -671
  90. package/dist/stream.lgp.js.map +0 -1
  91. package/dist/thread.cjs +0 -18
  92. package/dist/thread.cjs.map +0 -1
  93. package/dist/thread.js +0 -18
  94. package/dist/thread.js.map +0 -1
  95. package/dist/types.d.cts +0 -109
  96. package/dist/types.d.cts.map +0 -1
  97. package/dist/types.d.ts +0 -109
  98. package/dist/types.d.ts.map +0 -1
@@ -0,0 +1,218 @@
1
+ "use client";
2
+ import { useCallback, useEffect, useRef, useState } from "react";
3
+ //#region src/use-video-player.ts
4
+ /**
5
+ * Bind a {@link VideoMedia} handle to a caller-owned `<video>` element.
6
+ *
7
+ * ### Contract
8
+ *
9
+ * - The caller renders `<video ref={videoRef}>` and styles it however
10
+ * they like; the hook never injects DOM nor overrides layout.
11
+ * - On `message-finish`, the underlying blob URL is minted and assigned
12
+ * as `video.src`. Progressive playback of streamed container video
13
+ * (fragmented mp4 / webm via MSE) is out of scope for this version;
14
+ * `status` stays in `"buffering"` until the blob resolves.
15
+ * - Element events (`play` / `pause` / `ended` / `timeupdate` /
16
+ * `loadedmetadata` / `error`) are translated into the shared
17
+ * {@link PlayerStatus} enum.
18
+ * - On unmount, `media.revoke()` is called to free the object URL.
19
+ *
20
+ * @param videoRef - Ref to the `<video>` element the caller renders.
21
+ * @param media - Video handle from {@link useVideo}.
22
+ * @param options - Auto-play toggle.
23
+ */
24
+ function useVideoPlayer(videoRef, media, options) {
25
+ const autoPlay = options?.autoPlay ?? false;
26
+ const [status, setStatus] = useState("idle");
27
+ const [error, setError] = useState(void 0);
28
+ const [currentTime, setCurrentTime] = useState(0);
29
+ const [duration, setDuration] = useState(void 0);
30
+ const statusRef = useRef("idle");
31
+ useEffect(() => {
32
+ statusRef.current = status;
33
+ }, [status]);
34
+ const shouldPlayRef = useRef(false);
35
+ const pendingResolveRef = useRef(null);
36
+ const pendingRejectRef = useRef(null);
37
+ const resolvePending = useCallback(() => {
38
+ const resolve = pendingResolveRef.current;
39
+ pendingResolveRef.current = null;
40
+ pendingRejectRef.current = null;
41
+ resolve?.();
42
+ }, []);
43
+ const rejectPending = useCallback((err) => {
44
+ const reject = pendingRejectRef.current;
45
+ pendingResolveRef.current = null;
46
+ pendingRejectRef.current = null;
47
+ reject?.(err);
48
+ }, []);
49
+ useEffect(() => {
50
+ if (status === "finished" || status === "paused" || status === "idle") resolvePending();
51
+ else if (status === "error") rejectPending(error ?? /* @__PURE__ */ new Error("playback error"));
52
+ }, [
53
+ status,
54
+ error,
55
+ resolvePending,
56
+ rejectPending
57
+ ]);
58
+ const play = useCallback(() => {
59
+ if (media == null) return;
60
+ if (statusRef.current === "error") return;
61
+ shouldPlayRef.current = true;
62
+ const video = videoRef.current;
63
+ if (video == null) {
64
+ setStatus("buffering");
65
+ return;
66
+ }
67
+ video.play().catch((err) => {
68
+ setError(err);
69
+ setStatus("error");
70
+ });
71
+ }, [media, videoRef]);
72
+ const pause = useCallback(() => {
73
+ shouldPlayRef.current = false;
74
+ videoRef.current?.pause();
75
+ if (statusRef.current === "playing" || statusRef.current === "buffering") setStatus("paused");
76
+ }, [videoRef]);
77
+ const stop = useCallback(() => {
78
+ shouldPlayRef.current = false;
79
+ const video = videoRef.current;
80
+ if (video != null) {
81
+ video.pause();
82
+ video.currentTime = 0;
83
+ }
84
+ setCurrentTime(0);
85
+ setStatus(media == null ? "idle" : "paused");
86
+ }, [videoRef, media]);
87
+ const reset = useCallback(() => {
88
+ stop();
89
+ setError(void 0);
90
+ setDuration(void 0);
91
+ setStatus("idle");
92
+ }, [stop]);
93
+ const toggle = useCallback(() => {
94
+ if (statusRef.current === "playing") pause();
95
+ else play();
96
+ }, [play, pause]);
97
+ const playToEnd = useCallback(() => {
98
+ pendingResolveRef.current?.();
99
+ pendingResolveRef.current = null;
100
+ pendingRejectRef.current = null;
101
+ return new Promise((resolve, reject) => {
102
+ pendingResolveRef.current = resolve;
103
+ pendingRejectRef.current = reject;
104
+ play();
105
+ });
106
+ }, [play]);
107
+ const seek = useCallback((seconds) => {
108
+ const video = videoRef.current;
109
+ if (video == null) return;
110
+ video.currentTime = seconds;
111
+ setCurrentTime(seconds);
112
+ }, [videoRef]);
113
+ useEffect(() => {
114
+ if (media?.error == null) return;
115
+ setError(new Error(media.error.message));
116
+ setStatus("error");
117
+ }, [media]);
118
+ useEffect(() => {
119
+ if (media == null) {
120
+ setStatus("idle");
121
+ setError(void 0);
122
+ setCurrentTime(0);
123
+ setDuration(void 0);
124
+ return;
125
+ }
126
+ setError(void 0);
127
+ setStatus("buffering");
128
+ setCurrentTime(0);
129
+ setDuration(void 0);
130
+ let cancelled = false;
131
+ const video = videoRef.current;
132
+ media.objectURL.then((resolved) => {
133
+ if (cancelled) return;
134
+ if (video == null) return;
135
+ video.src = resolved;
136
+ if (shouldPlayRef.current || autoPlay) video.play().catch((err) => {
137
+ setError(err);
138
+ setStatus("error");
139
+ });
140
+ else setStatus("paused");
141
+ }, () => {
142
+ if (!cancelled) {
143
+ setError(/* @__PURE__ */ new Error("media failed to materialise"));
144
+ setStatus("error");
145
+ }
146
+ });
147
+ if (video == null) return () => {
148
+ cancelled = true;
149
+ try {
150
+ media.revoke();
151
+ } catch {}
152
+ };
153
+ const onPlay = () => {
154
+ if (statusRef.current !== "error") setStatus("playing");
155
+ };
156
+ const onPause = () => {
157
+ if (video.ended) return;
158
+ if (statusRef.current === "playing") setStatus("paused");
159
+ };
160
+ const onEnded = () => {
161
+ setStatus("finished");
162
+ };
163
+ const onTimeUpdate = () => {
164
+ setCurrentTime(video.currentTime);
165
+ };
166
+ const onLoadedMetadata = () => {
167
+ if (Number.isFinite(video.duration)) setDuration(video.duration);
168
+ };
169
+ const onError = () => {
170
+ setError(/* @__PURE__ */ new Error("HTMLVideoElement error"));
171
+ setStatus("error");
172
+ };
173
+ video.addEventListener("play", onPlay);
174
+ video.addEventListener("pause", onPause);
175
+ video.addEventListener("ended", onEnded);
176
+ video.addEventListener("timeupdate", onTimeUpdate);
177
+ video.addEventListener("loadedmetadata", onLoadedMetadata);
178
+ video.addEventListener("error", onError);
179
+ return () => {
180
+ cancelled = true;
181
+ video.removeEventListener("play", onPlay);
182
+ video.removeEventListener("pause", onPause);
183
+ video.removeEventListener("ended", onEnded);
184
+ video.removeEventListener("timeupdate", onTimeUpdate);
185
+ video.removeEventListener("loadedmetadata", onLoadedMetadata);
186
+ video.removeEventListener("error", onError);
187
+ try {
188
+ video.pause();
189
+ video.removeAttribute("src");
190
+ video.load();
191
+ } catch {}
192
+ try {
193
+ media.revoke();
194
+ } catch {}
195
+ };
196
+ }, [
197
+ media,
198
+ videoRef,
199
+ autoPlay
200
+ ]);
201
+ return {
202
+ status,
203
+ play,
204
+ pause,
205
+ stop,
206
+ toggle,
207
+ reset,
208
+ playToEnd,
209
+ currentTime,
210
+ duration,
211
+ seek,
212
+ error
213
+ };
214
+ }
215
+ //#endregion
216
+ export { useVideoPlayer };
217
+
218
+ //# sourceMappingURL=use-video-player.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-video-player.js","names":[],"sources":["../src/use-video-player.ts"],"sourcesContent":["/* __LC_ALLOW_ENTRYPOINT_SIDE_EFFECTS__ */\n\n\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useRef,\n useState,\n type RefObject,\n} from \"react\";\nimport type { VideoMedia } from \"@langchain/langgraph-sdk/stream\";\nimport type { PlayerStatus } from \"./use-audio-player.js\";\n\n/**\n * Options for {@link useVideoPlayer}.\n */\nexport interface UseVideoPlayerOptions {\n /**\n * Start playback as soon as the blob URL resolves. Subject to\n * browser autoplay policies — pair with `muted={true}` on the\n * `<video>` element to bypass the user-gesture requirement.\n */\n autoPlay?: boolean;\n}\n\n/**\n * Controls + live state returned by {@link useVideoPlayer}. Mirrors\n * {@link AudioPlayerHandle} on the shared subset so callers only ever\n * learn one shape.\n */\nexport interface VideoPlayerHandle {\n status: PlayerStatus;\n play(): void;\n pause(): void;\n stop(): void;\n toggle(): void;\n reset(): void;\n /**\n * Resolve on the next terminal transition (`finished` / `paused` /\n * `idle`). Reject on transitions to `\"error\"`. Triggers `play()`\n * when called.\n */\n playToEnd(): Promise<void>;\n\n currentTime: number;\n /** Total duration (seconds) once the element has parsed the blob. */\n duration?: number;\n /** Seek to an absolute timestamp in seconds. */\n seek(seconds: number): void;\n\n error: Error | undefined;\n}\n\n/**\n * Bind a {@link VideoMedia} handle to a caller-owned `<video>` element.\n *\n * ### Contract\n *\n * - The caller renders `<video ref={videoRef}>` and styles it however\n * they like; the hook never injects DOM nor overrides layout.\n * - On `message-finish`, the underlying blob URL is minted and assigned\n * as `video.src`. Progressive playback of streamed container video\n * (fragmented mp4 / webm via MSE) is out of scope for this version;\n * `status` stays in `\"buffering\"` until the blob resolves.\n * - Element events (`play` / `pause` / `ended` / `timeupdate` /\n * `loadedmetadata` / `error`) are translated into the shared\n * {@link PlayerStatus} enum.\n * - On unmount, `media.revoke()` is called to free the object URL.\n *\n * @param videoRef - Ref to the `<video>` element the caller renders.\n * @param media - Video handle from {@link useVideo}.\n * @param options - Auto-play toggle.\n */\nexport function useVideoPlayer(\n videoRef: RefObject<HTMLVideoElement | null>,\n media: VideoMedia | undefined,\n options?: UseVideoPlayerOptions\n): VideoPlayerHandle {\n const autoPlay = options?.autoPlay ?? false;\n\n const [status, setStatus] = useState<PlayerStatus>(\"idle\");\n const [error, setError] = useState<Error | undefined>(undefined);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState<number | undefined>(undefined);\n\n const statusRef = useRef<PlayerStatus>(\"idle\");\n useEffect(() => {\n statusRef.current = status;\n }, [status]);\n\n const shouldPlayRef = useRef(false);\n const pendingResolveRef = useRef<(() => void) | null>(null);\n const pendingRejectRef = useRef<((err: Error) => void) | null>(null);\n\n const resolvePending = useCallback(() => {\n const resolve = pendingResolveRef.current;\n pendingResolveRef.current = null;\n pendingRejectRef.current = null;\n resolve?.();\n }, []);\n\n const rejectPending = useCallback((err: Error) => {\n const reject = pendingRejectRef.current;\n pendingResolveRef.current = null;\n pendingRejectRef.current = null;\n reject?.(err);\n }, []);\n\n useEffect(() => {\n if (status === \"finished\" || status === \"paused\" || status === \"idle\") {\n resolvePending();\n } else if (status === \"error\") {\n rejectPending(error ?? new Error(\"playback error\"));\n }\n }, [status, error, resolvePending, rejectPending]);\n\n const play = useCallback(() => {\n if (media == null) return;\n if (statusRef.current === \"error\") return;\n shouldPlayRef.current = true;\n const video = videoRef.current;\n if (video == null) {\n setStatus(\"buffering\");\n return;\n }\n video.play().catch((err) => {\n setError(err as Error);\n setStatus(\"error\");\n });\n }, [media, videoRef]);\n\n const pause = useCallback(() => {\n shouldPlayRef.current = false;\n videoRef.current?.pause();\n if (statusRef.current === \"playing\" || statusRef.current === \"buffering\") {\n setStatus(\"paused\");\n }\n }, [videoRef]);\n\n const stop = useCallback(() => {\n shouldPlayRef.current = false;\n const video = videoRef.current;\n if (video != null) {\n video.pause();\n video.currentTime = 0;\n }\n setCurrentTime(0);\n setStatus(media == null ? \"idle\" : \"paused\");\n }, [videoRef, media]);\n\n const reset = useCallback(() => {\n stop();\n setError(undefined);\n setDuration(undefined);\n setStatus(\"idle\");\n }, [stop]);\n\n const toggle = useCallback(() => {\n if (statusRef.current === \"playing\") pause();\n else play();\n }, [play, pause]);\n\n const playToEnd = useCallback((): Promise<void> => {\n pendingResolveRef.current?.();\n pendingResolveRef.current = null;\n pendingRejectRef.current = null;\n\n return new Promise<void>((resolve, reject) => {\n pendingResolveRef.current = resolve;\n pendingRejectRef.current = reject;\n play();\n });\n }, [play]);\n\n const seek = useCallback(\n (seconds: number) => {\n const video = videoRef.current;\n if (video == null) return;\n video.currentTime = seconds;\n setCurrentTime(seconds);\n },\n [videoRef]\n );\n\n // Surface a media-level error immediately.\n useEffect(() => {\n if (media?.error == null) return;\n setError(new Error(media.error.message));\n setStatus(\"error\");\n }, [media]);\n\n // Bind the element to the blob URL once it resolves.\n useEffect(() => {\n if (media == null) {\n setStatus(\"idle\");\n setError(undefined);\n setCurrentTime(0);\n setDuration(undefined);\n return undefined;\n }\n\n setError(undefined);\n setStatus(\"buffering\");\n setCurrentTime(0);\n setDuration(undefined);\n\n let cancelled = false;\n const video = videoRef.current;\n\n media.objectURL.then(\n (resolved) => {\n if (cancelled) return;\n if (video == null) return;\n video.src = resolved;\n\n if (shouldPlayRef.current || autoPlay) {\n video.play().catch((err) => {\n setError(err as Error);\n setStatus(\"error\");\n });\n } else {\n setStatus(\"paused\");\n }\n },\n () => {\n if (!cancelled) {\n setError(new Error(\"media failed to materialise\"));\n setStatus(\"error\");\n }\n }\n );\n\n if (video == null) {\n return () => {\n cancelled = true;\n try {\n media.revoke();\n } catch {\n // best-effort\n }\n };\n }\n\n const onPlay = () => {\n if (statusRef.current !== \"error\") setStatus(\"playing\");\n };\n const onPause = () => {\n if (video.ended) return;\n if (statusRef.current === \"playing\") setStatus(\"paused\");\n };\n const onEnded = () => {\n setStatus(\"finished\");\n };\n const onTimeUpdate = () => {\n setCurrentTime(video.currentTime);\n };\n const onLoadedMetadata = () => {\n if (Number.isFinite(video.duration)) setDuration(video.duration);\n };\n const onError = () => {\n setError(new Error(\"HTMLVideoElement error\"));\n setStatus(\"error\");\n };\n\n video.addEventListener(\"play\", onPlay);\n video.addEventListener(\"pause\", onPause);\n video.addEventListener(\"ended\", onEnded);\n video.addEventListener(\"timeupdate\", onTimeUpdate);\n video.addEventListener(\"loadedmetadata\", onLoadedMetadata);\n video.addEventListener(\"error\", onError);\n\n return () => {\n cancelled = true;\n video.removeEventListener(\"play\", onPlay);\n video.removeEventListener(\"pause\", onPause);\n video.removeEventListener(\"ended\", onEnded);\n video.removeEventListener(\"timeupdate\", onTimeUpdate);\n video.removeEventListener(\"loadedmetadata\", onLoadedMetadata);\n video.removeEventListener(\"error\", onError);\n try {\n video.pause();\n video.removeAttribute(\"src\");\n video.load();\n } catch {\n // best-effort\n }\n try {\n media.revoke();\n } catch {\n // best-effort\n }\n };\n }, [media, videoRef, autoPlay]);\n\n return {\n status,\n play,\n pause,\n stop,\n toggle,\n reset,\n playToEnd,\n currentTime,\n duration,\n seek,\n error,\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/react",
3
- "version": "0.3.4",
3
+ "version": "1.0.0",
4
4
  "description": "React integration for LangGraph & LangChain",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -10,11 +10,12 @@
10
10
  "directory": "libs/sdk-react"
11
11
  },
12
12
  "dependencies": {
13
- "@langchain/langgraph-sdk": "^1.8.10"
13
+ "@langchain/langgraph-sdk": "1.9.0"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@hono/node-server": "^1.19.13",
17
- "@langchain/core": "^1.1.40",
17
+ "@hono/node-ws": "^1.3.0",
18
+ "@langchain/core": "^1.1.44",
18
19
  "@types/node": "^25.4.0",
19
20
  "@types/react": "^19.2.14",
20
21
  "@vitejs/plugin-react": "^5.1.4",
@@ -22,19 +23,19 @@
22
23
  "@vitest/browser-webdriverio": "^4.0.18",
23
24
  "deepagents": "^1.8.3",
24
25
  "hono": "^4.12.14",
25
- "langchain": "^1.3.0",
26
+ "langchain": "^1.3.5",
26
27
  "react": "^19.2.4",
27
28
  "typescript": "^5.9.3",
28
29
  "vitest": "^4.0.18",
29
30
  "vitest-browser-react": "^2.0.5",
30
31
  "webdriverio": "^9.25.0",
31
32
  "zod": "^4.3.6",
32
- "@langchain/langgraph": "^1.2.9",
33
- "@langchain/langgraph-api": "^1.1.17",
34
- "@langchain/langgraph-checkpoint": "^1.0.1"
33
+ "@langchain/langgraph": "^1.3.0",
34
+ "@langchain/langgraph-api": "^1.2.0",
35
+ "@langchain/langgraph-checkpoint": "^1.0.2"
35
36
  },
36
37
  "peerDependencies": {
37
- "@langchain/core": "^1.1.27",
38
+ "@langchain/core": "^1.1.44",
38
39
  "react": "^18 || ^19"
39
40
  },
40
41
  "main": "./dist/index.js",
package/dist/stream.cjs DELETED
@@ -1,18 +0,0 @@
1
- const require_stream_lgp = require("./stream.lgp.cjs");
2
- const require_stream_custom = require("./stream.custom.cjs");
3
- let react = require("react");
4
- //#region src/stream.tsx
5
- function isCustomOptions(options) {
6
- return "transport" in options;
7
- }
8
- function selectStreamImplementation(options) {
9
- return isCustomOptions(options) ? require_stream_custom.useStreamCustom : require_stream_lgp.useStreamLGP;
10
- }
11
- function useStream(options) {
12
- const [useSelectedStream] = (0, react.useState)(() => selectStreamImplementation(options));
13
- return useSelectedStream(options);
14
- }
15
- //#endregion
16
- exports.useStream = useStream;
17
-
18
- //# sourceMappingURL=stream.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"stream.cjs","names":["useStreamCustom","useStreamLGP"],"sources":["../src/stream.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport type { BagTemplate } from \"@langchain/langgraph-sdk\";\nimport type {\n UseStreamOptions,\n ResolveStreamInterface,\n ResolveStreamOptions,\n InferBag,\n InferStateType,\n WithClassMessages,\n} from \"@langchain/langgraph-sdk/ui\";\nimport { useStreamLGP } from \"./stream.lgp.js\";\nimport { useStreamCustom } from \"./stream.custom.js\";\nimport type { UseStreamCustomOptions } from \"./types.js\";\n\nfunction isCustomOptions<\n StateType extends Record<string, unknown> = Record<string, unknown>,\n Bag extends BagTemplate = BagTemplate,\n>(\n options:\n | UseStreamOptions<StateType, Bag>\n | UseStreamCustomOptions<StateType, Bag>\n): options is UseStreamCustomOptions<StateType, Bag> {\n return \"transport\" in options;\n}\n\ntype UseStreamImplementation = typeof useStreamLGP | typeof useStreamCustom;\n\ntype AnyUseStreamOptions =\n | UseStreamOptions<Record<string, unknown>, BagTemplate>\n | UseStreamCustomOptions<Record<string, unknown>, BagTemplate>;\n\nfunction selectStreamImplementation(\n options: AnyUseStreamOptions\n): UseStreamImplementation {\n return isCustomOptions(options) ? useStreamCustom : useStreamLGP;\n}\n\nexport type {\n WithClassMessages,\n ClassSubagentStreamInterface,\n} from \"@langchain/langgraph-sdk/ui\";\n\n/**\n * A React hook that provides seamless integration with LangGraph streaming capabilities.\n *\n * The `useStream` hook handles all the complexities of streaming, state management, and branching logic,\n * letting you focus on building great chat experiences. It provides automatic state management for\n * messages, interrupts, loading states, subagent streams, and errors.\n *\n * ## Usage with ReactAgent (recommended for createAgent users)\n *\n * When using `createAgent` from `@langchain/langgraph`, you can pass `typeof agent` as the\n * type parameter to automatically infer tool call types:\n *\n * @example\n * ```typescript\n * // In your agent file (e.g., agent.ts)\n * import { createAgent, tool } from \"langchain\";\n * import { z } from \"zod\";\n *\n * const getWeather = tool(\n * async ({ location }) => `Weather in ${location}`,\n * { name: \"get_weather\", schema: z.object({ location: z.string() }) }\n * );\n *\n * export const agent = createAgent({\n * model: \"openai:gpt-4o\",\n * tools: [getWeather],\n * });\n *\n * // In your React component\n * import { agent } from \"./agent\";\n *\n * function Chat() {\n * // Tool calls are automatically typed from the agent's tools!\n * const stream = useStream<typeof agent>({\n * assistantId: \"agent\",\n * apiUrl: \"http://localhost:2024\",\n * });\n *\n * // stream.toolCalls[0].call.name is typed as \"get_weather\"\n * // stream.toolCalls[0].call.args is typed as { location: string }\n * }\n * ```\n *\n * ## Usage with StateGraph (for custom LangGraph applications)\n *\n * When building custom graphs with `StateGraph`, embed your tool call types directly\n * in your state's messages property using `Message<MyToolCalls>`:\n *\n * @example\n * ```typescript\n * import { Message } from \"@langchain/langgraph-sdk\";\n *\n * // Define your tool call types as a discriminated union\n * type MyToolCalls =\n * | { name: \"search\"; args: { query: string }; id?: string }\n * | { name: \"calculate\"; args: { expression: string }; id?: string };\n *\n * // Embed tool call types in your state's messages\n * interface MyGraphState {\n * messages: Message<MyToolCalls>[];\n * context?: string;\n * }\n *\n * function Chat() {\n * const stream = useStream<MyGraphState>({\n * assistantId: \"my-graph\",\n * apiUrl: \"http://localhost:2024\",\n * });\n *\n * // stream.values is typed as MyGraphState\n * // stream.toolCalls[0].call.name is typed as \"search\" | \"calculate\"\n * }\n * ```\n *\n * @example\n * ```typescript\n * // With additional type configuration (interrupts, configurable)\n * interface MyGraphState {\n * messages: Message<MyToolCalls>[];\n * }\n *\n * function Chat() {\n * const stream = useStream<MyGraphState, {\n * InterruptType: { question: string };\n * ConfigurableType: { userId: string };\n * }>({\n * assistantId: \"my-graph\",\n * apiUrl: \"http://localhost:2024\",\n * });\n *\n * // stream.interrupt is typed as { question: string } | undefined\n * }\n * ```\n *\n * ## Usage with Deep Agents (subagent streaming, experimental)\n *\n * For agents that spawn subagents (nested graphs), use `filterSubagentMessages`\n * to keep the main message stream clean while tracking subagent activity separately:\n *\n * @example\n * ```typescript\n * import { useStream, SubagentStream } from \"@langchain/langgraph-sdk/react\";\n * import type { agent } from \"./agent\";\n *\n * function DeepAgentChat() {\n * const stream = useStream<typeof agent>({\n * assistantId: \"deepagent\",\n * apiUrl: \"http://localhost:2024\",\n * // Filter subagent messages from main stream\n * filterSubagentMessages: true,\n * });\n *\n * const handleSubmit = (content: string) => {\n * stream.submit(\n * { messages: [{ content, type: \"human\" }] },\n * { streamSubgraphs: true } // Enable subgraph streaming\n * );\n * };\n *\n * // Access subagent streams via stream.subagents (Map<string, SubagentStream>)\n * const subagentList = [...stream.subagents.values()];\n *\n * return (\n * <div>\n * {stream.messages.map((msg) => <Message key={msg.id} message={msg} />)}\n *\n * {subagentList.map((subagent) => (\n * <SubagentCard\n * key={subagent.id}\n * status={subagent.status} // \"pending\" | \"running\" | \"complete\" | \"error\"\n * messages={subagent.messages}\n * toolCalls={subagent.toolCalls}\n * />\n * ))}\n * </div>\n * );\n * }\n * ```\n *\n * @template T Either a ReactAgent type (with `~agentTypes`) or a state type (`Record<string, unknown>`)\n * @template Bag Type configuration bag containing:\n * - `ConfigurableType`: Type for the `config.configurable` property\n * - `InterruptType`: Type for interrupt values\n * - `CustomEventType`: Type for custom events\n * - `UpdateType`: Type for the submit function updates\n *\n * @see {@link https://docs.langchain.com/langgraph-platform/use-stream-react | LangGraph React Integration Guide}\n */\nexport function useStream<\n T = Record<string, unknown>,\n Bag extends BagTemplate = BagTemplate,\n>(\n options: ResolveStreamOptions<T, InferBag<T, Bag>>\n): WithClassMessages<ResolveStreamInterface<T, InferBag<T, Bag>>>;\n\n/**\n * A React hook that provides seamless integration with LangGraph streaming capabilities.\n *\n * The `useStream` hook handles all the complexities of streaming, state management, and branching logic,\n * letting you focus on building great chat experiences. It provides automatic state management for\n * messages, interrupts, loading states, and errors.\n *\n * @template T Either a ReactAgent type (with `~agentTypes`) or a state type (`Record<string, unknown>`)\n * @template Bag Type configuration bag containing:\n * - `ConfigurableType`: Type for the `config.configurable` property\n * - `InterruptType`: Type for interrupt values\n * - `CustomEventType`: Type for custom events\n * - `UpdateType`: Type for the submit function updates\n *\n * @see {@link https://docs.langchain.com/langgraph-platform/use-stream-react | LangGraph React Integration Guide}\n */\nexport function useStream<\n T = Record<string, unknown>,\n Bag extends BagTemplate = BagTemplate,\n>(\n options: UseStreamCustomOptions<InferStateType<T>, InferBag<T, Bag>>\n): WithClassMessages<ResolveStreamInterface<T, InferBag<T, Bag>>>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function useStream(options: any): any {\n // Keep implementation stable for the lifetime of this hook instance.\n const [useSelectedStream] = useState(() =>\n selectStreamImplementation(options)\n );\n return useSelectedStream(options);\n}\n"],"mappings":";;;;AAcA,SAAS,gBAIP,SAGmD;AACnD,QAAO,eAAe;;AASxB,SAAS,2BACP,SACyB;AACzB,QAAO,gBAAgB,QAAQ,GAAGA,sBAAAA,kBAAkBC,mBAAAA;;AA2LtD,SAAgB,UAAU,SAAmB;CAE3C,MAAM,CAAC,sBAAA,GAAA,MAAA,gBACL,2BAA2B,QAAQ,CACpC;AACD,QAAO,kBAAkB,QAAQ"}
@@ -1,209 +0,0 @@
1
- "use client";
2
- const require_thread = require("./thread.cjs");
3
- let react = require("react");
4
- let _langchain_langgraph_sdk_ui = require("@langchain/langgraph-sdk/ui");
5
- let _langchain_langgraph_sdk_utils = require("@langchain/langgraph-sdk/utils");
6
- let _langchain_langgraph_sdk = require("@langchain/langgraph-sdk");
7
- //#region src/stream.custom.tsx
8
- function createCustomTransportThreadState(values, threadId) {
9
- return {
10
- values,
11
- next: [],
12
- tasks: [],
13
- metadata: void 0,
14
- created_at: null,
15
- checkpoint: {
16
- thread_id: threadId,
17
- checkpoint_id: null,
18
- checkpoint_ns: "",
19
- checkpoint_map: null
20
- },
21
- parent_checkpoint: null
22
- };
23
- }
24
- function useStreamCustom(options) {
25
- const [messageManager] = (0, react.useState)(() => new _langchain_langgraph_sdk_ui.MessageTupleManager());
26
- const [stream] = (0, react.useState)(() => new _langchain_langgraph_sdk_ui.StreamManager(messageManager, {
27
- throttle: options.throttle ?? false,
28
- subagentToolNames: options.subagentToolNames,
29
- filterSubagentMessages: options.filterSubagentMessages,
30
- toMessage: options.toMessage ?? _langchain_langgraph_sdk_ui.toMessageClass
31
- }));
32
- (0, react.useSyncExternalStore)(stream.subscribe, stream.getSnapshot, stream.getSnapshot);
33
- const [branch, _setBranch] = (0, react.useState)("");
34
- const [threadId, onThreadId] = require_thread.useControllableThreadId(options);
35
- const threadIdRef = (0, react.useRef)(threadId);
36
- (0, react.useEffect)(() => {
37
- if (threadIdRef.current !== threadId) {
38
- threadIdRef.current = threadId;
39
- stream.clear();
40
- }
41
- }, [threadId, stream]);
42
- const switchThread = (0, react.useCallback)((newThreadId) => {
43
- if (newThreadId !== threadIdRef.current) {
44
- threadIdRef.current = newThreadId;
45
- stream.clear();
46
- }
47
- }, [stream]);
48
- const getMessages = (value) => {
49
- const messagesKey = options.messagesKey ?? "messages";
50
- return Array.isArray(value[messagesKey]) ? value[messagesKey] : [];
51
- };
52
- const setMessages = (current, messages) => {
53
- const messagesKey = options.messagesKey ?? "messages";
54
- return {
55
- ...current,
56
- [messagesKey]: messages
57
- };
58
- };
59
- const historyValues = options.initialValues ?? {};
60
- const historyMessages = getMessages(historyValues);
61
- const shouldReconstructSubagents = options.filterSubagentMessages && !stream.isLoading && historyMessages.length > 0;
62
- (0, react.useEffect)(() => {
63
- if (shouldReconstructSubagents) stream.reconstructSubagents(historyMessages, { skipIfPopulated: true });
64
- }, [shouldReconstructSubagents, historyMessages.length]);
65
- const stop = () => stream.stop(historyValues, { onStop: options.onStop });
66
- const submitDirect = async (values, submitOptions) => {
67
- if (threadId !== threadIdRef.current) {
68
- threadIdRef.current = threadId;
69
- stream.clear();
70
- }
71
- let usableThreadId = threadIdRef.current ?? submitOptions?.threadId;
72
- stream.setStreamValues(() => {
73
- if (submitOptions?.optimisticValues != null) return {
74
- ...historyValues,
75
- ...typeof submitOptions.optimisticValues === "function" ? submitOptions.optimisticValues(historyValues) : submitOptions.optimisticValues
76
- };
77
- return { ...historyValues };
78
- });
79
- await stream.start(async (signal) => {
80
- if (!usableThreadId) {
81
- usableThreadId = crypto.randomUUID();
82
- threadIdRef.current = usableThreadId;
83
- onThreadId(usableThreadId);
84
- }
85
- if (!usableThreadId) throw new Error("Failed to obtain valid thread ID.");
86
- return options.transport.stream({
87
- input: values,
88
- context: submitOptions?.context,
89
- command: submitOptions?.command,
90
- streamSubgraphs: submitOptions?.streamSubgraphs,
91
- signal,
92
- config: {
93
- ...submitOptions?.config,
94
- configurable: {
95
- thread_id: usableThreadId,
96
- ...submitOptions?.config?.configurable
97
- }
98
- }
99
- });
100
- }, {
101
- getMessages,
102
- setMessages,
103
- initialValues: {},
104
- callbacks: options,
105
- onSuccess: () => {
106
- if (!usableThreadId) return void 0;
107
- const finalValues = stream.values ?? historyValues;
108
- options.onFinish?.(createCustomTransportThreadState(finalValues, usableThreadId), void 0);
109
- },
110
- onError(error) {
111
- options.onError?.(error, void 0);
112
- submitOptions?.onError?.(error, void 0);
113
- }
114
- });
115
- };
116
- const submit = async (values, submitOptions) => {
117
- await submitDirect(values, submitOptions);
118
- };
119
- const handledToolsRef = (0, react.useRef)(/* @__PURE__ */ new Set());
120
- (0, react.useEffect)(() => {
121
- handledToolsRef.current.clear();
122
- }, [threadId]);
123
- (0, react.useEffect)(() => {
124
- (0, _langchain_langgraph_sdk.flushPendingHeadlessToolInterrupts)(stream.values, options.tools, handledToolsRef.current, {
125
- onTool: options.onTool,
126
- defer: (run) => {
127
- Promise.resolve().then(run);
128
- },
129
- resumeSubmit: (command) => submit(null, { command })
130
- });
131
- }, [
132
- options.onTool,
133
- options.tools,
134
- stream.values,
135
- submit
136
- ]);
137
- return {
138
- get values() {
139
- return stream.values ?? {};
140
- },
141
- error: stream.error,
142
- isLoading: stream.isLoading,
143
- stop,
144
- submit,
145
- switchThread,
146
- branch,
147
- setBranch: _setBranch,
148
- getMessagesMetadata(message, index) {
149
- const streamMetadata = messageManager.get(message.id)?.metadata;
150
- if (streamMetadata != null) return {
151
- messageId: message.id ?? String(index),
152
- firstSeenState: void 0,
153
- branch: void 0,
154
- branchOptions: void 0,
155
- streamMetadata
156
- };
157
- },
158
- get interrupts() {
159
- if (stream.values != null && "__interrupt__" in stream.values && Array.isArray(stream.values.__interrupt__)) return (0, _langchain_langgraph_sdk_ui.userFacingInterruptsFromValuesArray)(stream.values.__interrupt__);
160
- return [];
161
- },
162
- get interrupt() {
163
- return (0, _langchain_langgraph_sdk_ui.extractInterrupts)(stream.values);
164
- },
165
- get messages() {
166
- if (!stream.values) return [];
167
- return (0, _langchain_langgraph_sdk_ui.ensureMessageInstances)(getMessages(stream.values));
168
- },
169
- get toolCalls() {
170
- if (!stream.values) return [];
171
- return (0, _langchain_langgraph_sdk_utils.getToolCallsWithResults)(getMessages(stream.values));
172
- },
173
- getToolCalls(message) {
174
- if (!stream.values) return [];
175
- return (0, _langchain_langgraph_sdk_utils.getToolCallsWithResults)(getMessages(stream.values)).filter((tc) => tc.aiMessage.id === message.id);
176
- },
177
- get subagents() {
178
- return stream.getSubagents();
179
- },
180
- get activeSubagents() {
181
- return stream.getActiveSubagents();
182
- },
183
- getSubagent(toolCallId) {
184
- return stream.getSubagent(toolCallId);
185
- },
186
- getSubagentsByType(type) {
187
- return stream.getSubagentsByType(type);
188
- },
189
- getSubagentsByMessage(messageId) {
190
- return stream.getSubagentsByMessage(messageId);
191
- },
192
- queue: {
193
- get entries() {
194
- return [];
195
- },
196
- get size() {
197
- return 0;
198
- },
199
- async cancel() {
200
- return false;
201
- },
202
- async clear() {}
203
- }
204
- };
205
- }
206
- //#endregion
207
- exports.useStreamCustom = useStreamCustom;
208
-
209
- //# sourceMappingURL=stream.custom.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"stream.custom.cjs","names":["MessageTupleManager","StreamManager","toMessageClass","useControllableThreadId"],"sources":["../src/stream.custom.tsx"],"sourcesContent":["/* __LC_ALLOW_ENTRYPOINT_SIDE_EFFECTS__ */\n\n\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport {\n StreamManager,\n MessageTupleManager,\n extractInterrupts,\n userFacingInterruptsFromValuesArray,\n FetchStreamTransport,\n toMessageClass,\n ensureMessageInstances,\n type EventStreamEvent,\n type GetUpdateType,\n type GetCustomEventType,\n type GetInterruptType,\n type GetToolCallsType,\n type GetConfigurableType,\n type AnyStreamCustomOptions,\n type CustomSubmitOptions,\n type MessageMetadata,\n} from \"@langchain/langgraph-sdk/ui\";\nimport { getToolCallsWithResults } from \"@langchain/langgraph-sdk/utils\";\nimport type { BaseMessage } from \"@langchain/core/messages\";\nimport type {\n BagTemplate,\n Message,\n Interrupt,\n ThreadState,\n} from \"@langchain/langgraph-sdk\";\nimport { flushPendingHeadlessToolInterrupts } from \"@langchain/langgraph-sdk\";\nimport { useControllableThreadId } from \"./thread.js\";\nimport type { UseStreamCustom } from \"./types.js\";\n\nexport { FetchStreamTransport };\n\nfunction createCustomTransportThreadState<\n StateType extends Record<string, unknown>,\n>(values: StateType, threadId: string): ThreadState<StateType> {\n return {\n values,\n next: [],\n tasks: [],\n metadata: undefined,\n created_at: null,\n checkpoint: {\n thread_id: threadId,\n checkpoint_id: null,\n checkpoint_ns: \"\",\n checkpoint_map: null,\n },\n parent_checkpoint: null,\n };\n}\n\nexport function useStreamCustom<\n StateType extends Record<string, unknown> = Record<string, unknown>,\n Bag extends BagTemplate = BagTemplate,\n>(\n options: AnyStreamCustomOptions<StateType, Bag>\n): UseStreamCustom<StateType, Bag> {\n type UpdateType = GetUpdateType<Bag, StateType>;\n type CustomType = GetCustomEventType<Bag>;\n type InterruptType = GetInterruptType<Bag>;\n type ConfigurableType = GetConfigurableType<Bag>;\n type ToolCallType = GetToolCallsType<StateType>;\n\n const [messageManager] = useState(() => new MessageTupleManager());\n const [stream] = useState(\n () =>\n new StreamManager<StateType, Bag>(messageManager, {\n throttle: options.throttle ?? false,\n subagentToolNames: options.subagentToolNames,\n filterSubagentMessages: options.filterSubagentMessages,\n toMessage: options.toMessage ?? toMessageClass,\n })\n );\n\n useSyncExternalStore(\n stream.subscribe,\n stream.getSnapshot,\n stream.getSnapshot\n );\n\n const [branch, _setBranch] = useState(\"\");\n\n const [threadId, onThreadId] = useControllableThreadId(options);\n const threadIdRef = useRef<string | null>(threadId);\n\n // Cancel the stream if thread ID has changed\n useEffect(() => {\n if (threadIdRef.current !== threadId) {\n threadIdRef.current = threadId;\n stream.clear();\n }\n }, [threadId, stream]);\n\n const switchThread = useCallback(\n (newThreadId: string | null) => {\n if (newThreadId !== threadIdRef.current) {\n threadIdRef.current = newThreadId;\n stream.clear();\n }\n },\n [stream]\n );\n\n const getMessages = (value: StateType): Message[] => {\n const messagesKey = options.messagesKey ?? \"messages\";\n return Array.isArray(value[messagesKey])\n ? (value[messagesKey] as Message[])\n : [];\n };\n\n const setMessages = (current: StateType, messages: Message[]): StateType => {\n const messagesKey = options.messagesKey ?? \"messages\";\n return { ...current, [messagesKey]: messages };\n };\n\n const historyValues = options.initialValues ?? ({} as StateType);\n\n // Reconstruct subagents from initialValues when:\n // 1. Subagent filtering is enabled\n // 2. Not currently streaming\n // 3. initialValues has messages\n // This ensures subagent visualization works with cached/persisted state\n const historyMessages = getMessages(historyValues);\n const shouldReconstructSubagents =\n options.filterSubagentMessages &&\n !stream.isLoading &&\n historyMessages.length > 0;\n\n useEffect(() => {\n if (shouldReconstructSubagents) {\n // skipIfPopulated: true ensures we don't overwrite subagents from active streaming\n stream.reconstructSubagents(historyMessages, { skipIfPopulated: true });\n }\n // We intentionally only run this when shouldReconstructSubagents changes\n // to avoid unnecessary reconstructions during streaming\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [shouldReconstructSubagents, historyMessages.length]);\n\n const stop = () => stream.stop(historyValues, { onStop: options.onStop });\n\n const submitDirect = async (\n values: UpdateType | null | undefined,\n submitOptions?: CustomSubmitOptions<StateType, ConfigurableType>\n ) => {\n if (threadId !== threadIdRef.current) {\n threadIdRef.current = threadId;\n stream.clear();\n }\n\n let usableThreadId = threadIdRef.current ?? submitOptions?.threadId;\n\n stream.setStreamValues(() => {\n if (submitOptions?.optimisticValues != null) {\n return {\n ...historyValues,\n ...(typeof submitOptions.optimisticValues === \"function\"\n ? submitOptions.optimisticValues(historyValues)\n : submitOptions.optimisticValues),\n };\n }\n\n return { ...historyValues };\n });\n\n await stream.start(\n async (signal: AbortSignal) => {\n if (!usableThreadId) {\n usableThreadId = crypto.randomUUID();\n threadIdRef.current = usableThreadId;\n onThreadId(usableThreadId);\n }\n\n if (!usableThreadId) {\n throw new Error(\"Failed to obtain valid thread ID.\");\n }\n\n return options.transport.stream({\n input: values,\n context: submitOptions?.context,\n command: submitOptions?.command,\n streamSubgraphs: submitOptions?.streamSubgraphs,\n signal,\n config: {\n ...submitOptions?.config,\n configurable: {\n thread_id: usableThreadId,\n ...submitOptions?.config?.configurable,\n } as unknown as GetConfigurableType<Bag>,\n },\n }) as Promise<\n AsyncGenerator<EventStreamEvent<StateType, UpdateType, CustomType>>\n >;\n },\n {\n getMessages,\n setMessages,\n\n initialValues: {} as StateType,\n callbacks: options,\n\n onSuccess: () => {\n if (!usableThreadId) return undefined;\n\n const finalValues = stream.values ?? historyValues;\n options.onFinish?.(\n createCustomTransportThreadState(finalValues, usableThreadId),\n undefined\n );\n\n return undefined;\n },\n onError(error) {\n options.onError?.(error, undefined);\n submitOptions?.onError?.(error, undefined);\n },\n }\n );\n };\n\n const submit = async (\n values: UpdateType | null | undefined,\n submitOptions?: CustomSubmitOptions<StateType, ConfigurableType>\n ) => {\n await submitDirect(values, submitOptions);\n };\n\n const handledToolsRef = useRef<Set<string>>(new Set());\n useEffect(() => {\n handledToolsRef.current.clear();\n }, [threadId]);\n\n useEffect(() => {\n flushPendingHeadlessToolInterrupts(\n stream.values as Record<string, unknown> | null,\n options.tools,\n handledToolsRef.current,\n {\n onTool: options.onTool,\n defer: (run) => {\n void Promise.resolve().then(run);\n },\n resumeSubmit: (command) =>\n submit(null, {\n command,\n }),\n }\n );\n }, [options.onTool, options.tools, stream.values, submit]);\n\n return {\n get values() {\n return stream.values ?? ({} as StateType);\n },\n\n error: stream.error,\n isLoading: stream.isLoading,\n\n stop,\n submit,\n switchThread,\n\n branch,\n setBranch: _setBranch,\n\n getMessagesMetadata(\n message: BaseMessage,\n index?: number\n ): MessageMetadata<StateType> | undefined {\n const streamMetadata = messageManager.get(message.id)?.metadata;\n if (streamMetadata != null) {\n return {\n messageId: message.id ?? String(index),\n firstSeenState: undefined,\n branch: undefined,\n branchOptions: undefined,\n streamMetadata,\n } as MessageMetadata<StateType>;\n }\n return undefined;\n },\n\n get interrupts(): Interrupt<InterruptType>[] {\n if (\n stream.values != null &&\n \"__interrupt__\" in stream.values &&\n Array.isArray(stream.values.__interrupt__)\n ) {\n return userFacingInterruptsFromValuesArray<InterruptType>(\n stream.values.__interrupt__ as Interrupt<InterruptType>[]\n );\n }\n\n return [];\n },\n\n get interrupt(): Interrupt<InterruptType> | undefined {\n return extractInterrupts<InterruptType>(stream.values);\n },\n\n get messages(): BaseMessage[] {\n if (!stream.values) return [];\n return ensureMessageInstances(\n getMessages(stream.values)\n ) as BaseMessage[];\n },\n\n get toolCalls() {\n if (!stream.values) return [];\n const msgs = getMessages(stream.values);\n return getToolCallsWithResults<ToolCallType>(msgs);\n },\n\n getToolCalls(message) {\n if (!stream.values) return [];\n const msgs = getMessages(stream.values);\n const allToolCalls = getToolCallsWithResults<ToolCallType>(msgs);\n return allToolCalls.filter((tc) => tc.aiMessage.id === message.id);\n },\n\n get subagents() {\n return stream.getSubagents();\n },\n\n get activeSubagents() {\n return stream.getActiveSubagents();\n },\n\n getSubagent(toolCallId: string) {\n return stream.getSubagent(toolCallId);\n },\n\n getSubagentsByType(type: string) {\n return stream.getSubagentsByType(type);\n },\n\n getSubagentsByMessage(messageId: string) {\n return stream.getSubagentsByMessage(messageId);\n },\n\n queue: {\n get entries() {\n return [];\n },\n get size() {\n return 0;\n },\n async cancel() {\n return false;\n },\n async clear() {},\n },\n };\n}\n"],"mappings":"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"}
@@ -1,3 +0,0 @@
1
- import { BagTemplate } from "@langchain/langgraph-sdk";
2
- import { FetchStreamTransport } from "@langchain/langgraph-sdk/ui";
3
- export { FetchStreamTransport };
@@ -1,3 +0,0 @@
1
- import { FetchStreamTransport } from "@langchain/langgraph-sdk/ui";
2
- import { BagTemplate } from "@langchain/langgraph-sdk";
3
- export { FetchStreamTransport };