@langchain/langgraph-sdk 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @langchain/langgraph-sdk
2
2
 
3
+ ## 0.1.7
4
+
5
+ ### Patch Changes
6
+
7
+ - bbc90e6: Fix thread history state being kept stale when changing `thread_id`
8
+
3
9
  ## 0.1.6
4
10
 
5
11
  ### Patch Changes
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.FetchStreamTransport = void 0;
7
7
  exports.useStreamCustom = useStreamCustom;
8
8
  const react_1 = require("react");
9
- const manager_js_1 = require("./manager.cjs");
10
- const messages_js_1 = require("./messages.cjs");
9
+ const manager_js_1 = require("../ui/manager.cjs");
10
+ const messages_js_1 = require("../ui/messages.cjs");
11
11
  const sse_js_1 = require("../utils/sse.cjs");
12
12
  const stream_js_1 = require("../utils/stream.cjs");
13
13
  const thread_js_1 = require("./thread.cjs");
@@ -1,8 +1,8 @@
1
1
  /* __LC_ALLOW_ENTRYPOINT_SIDE_EFFECTS__ */
2
2
  "use client";
3
3
  import { useEffect, useRef, useState, useSyncExternalStore } from "react";
4
- import { StreamManager } from "./manager.js";
5
- import { MessageTupleManager } from "./messages.js";
4
+ import { StreamManager } from "../ui/manager.js";
5
+ import { MessageTupleManager } from "../ui/messages.js";
6
6
  import { BytesLineDecoder, SSEDecoder } from "../utils/sse.js";
7
7
  import { IterableReadableStream } from "../utils/stream.js";
8
8
  import { useControllableThreadId } from "./thread.js";
@@ -5,12 +5,12 @@
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.useStreamLGP = useStreamLGP;
7
7
  const react_1 = require("react");
8
- const utils_js_1 = require("./utils.cjs");
9
- const errors_js_1 = require("./errors.cjs");
10
- const branching_js_1 = require("./branching.cjs");
11
- const manager_js_1 = require("./manager.cjs");
8
+ const utils_js_1 = require("../ui/utils.cjs");
9
+ const errors_js_1 = require("../ui/errors.cjs");
10
+ const branching_js_1 = require("../ui/branching.cjs");
11
+ const manager_js_1 = require("../ui/manager.cjs");
12
12
  const client_js_1 = require("../client.cjs");
13
- const messages_js_1 = require("./messages.cjs");
13
+ const messages_js_1 = require("../ui/messages.cjs");
14
14
  const thread_js_1 = require("./thread.cjs");
15
15
  function getFetchHistoryKey(client, threadId, limit) {
16
16
  return [(0, client_js_1.getClientConfigHash)(client), threadId, limit].join(":");
@@ -29,6 +29,7 @@ function fetchHistory(client, threadId, options) {
29
29
  function useThreadHistory(client, threadId, limit, options) {
30
30
  const key = getFetchHistoryKey(client, threadId, limit);
31
31
  const [state, setState] = (0, react_1.useState)(() => ({
32
+ key: undefined,
32
33
  data: undefined,
33
34
  error: undefined,
34
35
  isLoading: threadId != null,
@@ -38,19 +39,32 @@ function useThreadHistory(client, threadId, limit, options) {
38
39
  const onErrorRef = (0, react_1.useRef)(options?.onError);
39
40
  onErrorRef.current = options?.onError;
40
41
  const fetcher = (0, react_1.useCallback)((threadId, limit) => {
42
+ const client = clientRef.current;
43
+ const key = getFetchHistoryKey(client, threadId, limit);
41
44
  if (threadId != null) {
42
- const client = clientRef.current;
43
- setState((state) => ({ ...state, isLoading: true }));
45
+ setState((state) => {
46
+ if (state.key === key)
47
+ return { ...state, isLoading: true };
48
+ return { key, data: undefined, error: undefined, isLoading: true };
49
+ });
44
50
  return fetchHistory(client, threadId, { limit }).then((data) => {
45
- setState({ data, error: undefined, isLoading: false });
51
+ setState((state) => {
52
+ if (state.key !== key)
53
+ return state;
54
+ return { key, data, error: undefined, isLoading: false };
55
+ });
46
56
  return data;
47
57
  }, (error) => {
48
- setState(({ data }) => ({ data, error, isLoading: false }));
58
+ setState((state) => {
59
+ if (state.key !== key)
60
+ return state;
61
+ return { key, data: state.data, error, isLoading: false };
62
+ });
49
63
  onErrorRef.current?.(error);
50
64
  return Promise.reject(error);
51
65
  });
52
66
  }
53
- setState({ data: undefined, error: undefined, isLoading: false });
67
+ setState({ key, data: undefined, error: undefined, isLoading: false });
54
68
  return Promise.resolve([]);
55
69
  }, []);
56
70
  (0, react_1.useEffect)(() => {
@@ -1,12 +1,12 @@
1
1
  /* __LC_ALLOW_ENTRYPOINT_SIDE_EFFECTS__ */
2
2
  "use client";
3
3
  import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore, } from "react";
4
- import { findLast, unique } from "./utils.js";
5
- import { StreamError } from "./errors.js";
6
- import { getBranchContext } from "./branching.js";
7
- import { StreamManager } from "./manager.js";
4
+ import { findLast, unique } from "../ui/utils.js";
5
+ import { StreamError } from "../ui/errors.js";
6
+ import { getBranchContext } from "../ui/branching.js";
7
+ import { StreamManager } from "../ui/manager.js";
8
8
  import { Client, getClientConfigHash } from "../client.js";
9
- import { MessageTupleManager } from "./messages.js";
9
+ import { MessageTupleManager } from "../ui/messages.js";
10
10
  import { useControllableThreadId } from "./thread.js";
11
11
  function getFetchHistoryKey(client, threadId, limit) {
12
12
  return [getClientConfigHash(client), threadId, limit].join(":");
@@ -25,6 +25,7 @@ function fetchHistory(client, threadId, options) {
25
25
  function useThreadHistory(client, threadId, limit, options) {
26
26
  const key = getFetchHistoryKey(client, threadId, limit);
27
27
  const [state, setState] = useState(() => ({
28
+ key: undefined,
28
29
  data: undefined,
29
30
  error: undefined,
30
31
  isLoading: threadId != null,
@@ -34,19 +35,32 @@ function useThreadHistory(client, threadId, limit, options) {
34
35
  const onErrorRef = useRef(options?.onError);
35
36
  onErrorRef.current = options?.onError;
36
37
  const fetcher = useCallback((threadId, limit) => {
38
+ const client = clientRef.current;
39
+ const key = getFetchHistoryKey(client, threadId, limit);
37
40
  if (threadId != null) {
38
- const client = clientRef.current;
39
- setState((state) => ({ ...state, isLoading: true }));
41
+ setState((state) => {
42
+ if (state.key === key)
43
+ return { ...state, isLoading: true };
44
+ return { key, data: undefined, error: undefined, isLoading: true };
45
+ });
40
46
  return fetchHistory(client, threadId, { limit }).then((data) => {
41
- setState({ data, error: undefined, isLoading: false });
47
+ setState((state) => {
48
+ if (state.key !== key)
49
+ return state;
50
+ return { key, data, error: undefined, isLoading: false };
51
+ });
42
52
  return data;
43
53
  }, (error) => {
44
- setState(({ data }) => ({ data, error, isLoading: false }));
54
+ setState((state) => {
55
+ if (state.key !== key)
56
+ return state;
57
+ return { key, data: state.data, error, isLoading: false };
58
+ });
45
59
  onErrorRef.current?.(error);
46
60
  return Promise.reject(error);
47
61
  });
48
62
  }
49
- setState({ data: undefined, error: undefined, isLoading: false });
63
+ setState({ key, data: undefined, error: undefined, isLoading: false });
50
64
  return Promise.resolve([]);
51
65
  }, []);
52
66
  useEffect(() => {
@@ -3,7 +3,7 @@ import type { ThreadState, Interrupt, Config, Checkpoint, Metadata } from "../sc
3
3
  import type { Command, MultitaskStrategy, OnCompletionBehavior, DisconnectMode, Durability } from "../types.js";
4
4
  import type { Message } from "../types.messages.js";
5
5
  import type { UpdatesStreamEvent, CustomStreamEvent, MetadataStreamEvent, EventsStreamEvent, DebugStreamEvent, CheckpointsStreamEvent, TasksStreamEvent, StreamMode } from "../types.stream.js";
6
- import type { Sequence } from "./branching.js";
6
+ import type { Sequence } from "../ui/branching.js";
7
7
  export type MessageMetadata<StateType extends Record<string, unknown>> = {
8
8
  /**
9
9
  * The ID of the message used.
@@ -0,0 +1,370 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const branching_js_1 = require("./branching.cjs");
5
+ const history = [
6
+ {
7
+ values: {
8
+ messages: [
9
+ {
10
+ content: "Fork: Hello",
11
+ additional_kwargs: {},
12
+ response_metadata: {},
13
+ id: "33357aea-9c2d-4718-92f4-20038d5a2d29",
14
+ type: "human",
15
+ },
16
+ {
17
+ content: "Hey",
18
+ additional_kwargs: {},
19
+ response_metadata: {},
20
+ tool_call_chunks: [],
21
+ id: "run-782a2f90-2e39-4c21-9ee6-0f38585a0ae6",
22
+ tool_calls: [],
23
+ invalid_tool_calls: [],
24
+ type: "ai",
25
+ },
26
+ ],
27
+ },
28
+ next: [],
29
+ tasks: [],
30
+ metadata: {
31
+ source: "loop",
32
+ step: 2,
33
+ parents: {},
34
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
35
+ },
36
+ created_at: "2025-08-21T12:47:12.640Z",
37
+ checkpoint: {
38
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
39
+ checkpoint_id: "1f07e8cf-56a7-6000-8002-5e272b1d5285",
40
+ checkpoint_ns: "",
41
+ checkpoint_map: null,
42
+ },
43
+ parent_checkpoint: {
44
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
45
+ checkpoint_id: "1f07e8cf-53aa-6d70-8001-2096b0bbf835",
46
+ checkpoint_ns: "",
47
+ checkpoint_map: null,
48
+ },
49
+ },
50
+ {
51
+ values: {
52
+ messages: [
53
+ {
54
+ content: "Fork: Hello",
55
+ additional_kwargs: {},
56
+ response_metadata: {},
57
+ id: "33357aea-9c2d-4718-92f4-20038d5a2d29",
58
+ type: "human",
59
+ },
60
+ ],
61
+ },
62
+ next: ["agent"],
63
+ tasks: [
64
+ {
65
+ id: "680ee831-4d10-50f9-956f-e0d937321614",
66
+ name: "agent",
67
+ error: null,
68
+ interrupts: [],
69
+ path: ["__pregel_pull", "agent"],
70
+ checkpoint: null,
71
+ state: null,
72
+ result: {
73
+ messages: [
74
+ {
75
+ content: "Hey",
76
+ additional_kwargs: {},
77
+ response_metadata: {},
78
+ tool_call_chunks: [],
79
+ id: "run-782a2f90-2e39-4c21-9ee6-0f38585a0ae6",
80
+ tool_calls: [],
81
+ invalid_tool_calls: [],
82
+ type: "ai",
83
+ },
84
+ ],
85
+ },
86
+ },
87
+ ],
88
+ metadata: {
89
+ source: "loop",
90
+ step: 1,
91
+ parents: {},
92
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
93
+ },
94
+ created_at: "2025-08-21T12:47:12.327Z",
95
+ checkpoint: {
96
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
97
+ checkpoint_id: "1f07e8cf-53aa-6d70-8001-2096b0bbf835",
98
+ checkpoint_ns: "",
99
+ checkpoint_map: null,
100
+ },
101
+ parent_checkpoint: {
102
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
103
+ checkpoint_id: "1f07e8cf-53a8-6660-8000-f157c9cf7d66",
104
+ checkpoint_ns: "",
105
+ checkpoint_map: null,
106
+ },
107
+ },
108
+ {
109
+ values: { messages: [] },
110
+ next: ["__start__"],
111
+ tasks: [
112
+ {
113
+ id: "4233f4c0-5ea2-5117-9153-95e5fe59eee3",
114
+ name: "__start__",
115
+ error: null,
116
+ interrupts: [],
117
+ path: ["__pregel_pull", "__start__"],
118
+ checkpoint: null,
119
+ state: null,
120
+ result: { messages: [{ type: "human", content: "Fork: Hello" }] },
121
+ },
122
+ ],
123
+ metadata: {
124
+ source: "input",
125
+ step: 0,
126
+ parents: {},
127
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
128
+ },
129
+ created_at: "2025-08-21T12:47:12.326Z",
130
+ checkpoint: {
131
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
132
+ checkpoint_id: "1f07e8cf-53a8-6660-8000-f157c9cf7d66",
133
+ checkpoint_ns: "",
134
+ checkpoint_map: null,
135
+ },
136
+ parent_checkpoint: {
137
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
138
+ checkpoint_id: "1f07e8cf-4cea-6530-ffff-0995a53299c5",
139
+ checkpoint_ns: "",
140
+ checkpoint_map: null,
141
+ },
142
+ },
143
+ {
144
+ values: {
145
+ messages: [
146
+ {
147
+ content: "Hello",
148
+ additional_kwargs: {},
149
+ response_metadata: {},
150
+ id: "ec300e63-1494-4ef6-936c-60d02d7bdce1",
151
+ type: "human",
152
+ },
153
+ {
154
+ content: "Hey",
155
+ additional_kwargs: {},
156
+ response_metadata: {},
157
+ tool_call_chunks: [],
158
+ id: "run-593064bd-dd07-4ed3-99c0-a918625f4884",
159
+ tool_calls: [],
160
+ invalid_tool_calls: [],
161
+ type: "ai",
162
+ },
163
+ ],
164
+ },
165
+ next: [],
166
+ tasks: [],
167
+ metadata: {
168
+ source: "loop",
169
+ step: 1,
170
+ parents: {},
171
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
172
+ },
173
+ created_at: "2025-08-21T12:47:12.285Z",
174
+ checkpoint: {
175
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
176
+ checkpoint_id: "1f07e8cf-5344-64d0-8001-8a176e91d12f",
177
+ checkpoint_ns: "",
178
+ checkpoint_map: null,
179
+ },
180
+ parent_checkpoint: {
181
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
182
+ checkpoint_id: "1f07e8cf-4cf8-6f90-8000-7707fd155a8a",
183
+ checkpoint_ns: "",
184
+ checkpoint_map: null,
185
+ },
186
+ },
187
+ {
188
+ values: {
189
+ messages: [
190
+ {
191
+ content: "Hello",
192
+ additional_kwargs: {},
193
+ response_metadata: {},
194
+ id: "ec300e63-1494-4ef6-936c-60d02d7bdce1",
195
+ type: "human",
196
+ },
197
+ {
198
+ content: "Hey",
199
+ additional_kwargs: {},
200
+ response_metadata: {},
201
+ tool_call_chunks: [],
202
+ id: "run-b86ef558-eaff-4838-bc03-218f20554a9f",
203
+ tool_calls: [],
204
+ invalid_tool_calls: [],
205
+ type: "ai",
206
+ },
207
+ ],
208
+ },
209
+ next: [],
210
+ tasks: [],
211
+ metadata: {
212
+ source: "loop",
213
+ step: 1,
214
+ parents: {},
215
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
216
+ },
217
+ created_at: "2025-08-21T12:47:11.936Z",
218
+ checkpoint: {
219
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
220
+ checkpoint_id: "1f07e8cf-4ff0-6400-8001-a54aedf52a76",
221
+ checkpoint_ns: "",
222
+ checkpoint_map: null,
223
+ },
224
+ parent_checkpoint: {
225
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
226
+ checkpoint_id: "1f07e8cf-4cf8-6f90-8000-7707fd155a8a",
227
+ checkpoint_ns: "",
228
+ checkpoint_map: null,
229
+ },
230
+ },
231
+ {
232
+ values: {
233
+ messages: [
234
+ {
235
+ content: "Hello",
236
+ additional_kwargs: {},
237
+ response_metadata: {},
238
+ id: "ec300e63-1494-4ef6-936c-60d02d7bdce1",
239
+ type: "human",
240
+ },
241
+ ],
242
+ },
243
+ next: ["agent"],
244
+ tasks: [
245
+ {
246
+ id: "e3f503f1-73bc-5db1-8b4d-13f660ad1b51",
247
+ name: "agent",
248
+ error: null,
249
+ interrupts: [],
250
+ path: ["__pregel_pull", "agent"],
251
+ checkpoint: null,
252
+ state: null,
253
+ result: {
254
+ messages: [
255
+ {
256
+ content: "Hey",
257
+ additional_kwargs: {},
258
+ response_metadata: {},
259
+ tool_call_chunks: [],
260
+ id: "run-b86ef558-eaff-4838-bc03-218f20554a9f",
261
+ tool_calls: [],
262
+ invalid_tool_calls: [],
263
+ type: "ai",
264
+ },
265
+ ],
266
+ },
267
+ },
268
+ ],
269
+ metadata: {
270
+ source: "loop",
271
+ step: 0,
272
+ parents: {},
273
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
274
+ },
275
+ created_at: "2025-08-21T12:47:11.625Z",
276
+ checkpoint: {
277
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
278
+ checkpoint_id: "1f07e8cf-4cf8-6f90-8000-7707fd155a8a",
279
+ checkpoint_ns: "",
280
+ checkpoint_map: null,
281
+ },
282
+ parent_checkpoint: {
283
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
284
+ checkpoint_id: "1f07e8cf-4cea-6530-ffff-0995a53299c5",
285
+ checkpoint_ns: "",
286
+ checkpoint_map: null,
287
+ },
288
+ },
289
+ {
290
+ values: { messages: [] },
291
+ next: ["__start__"],
292
+ tasks: [
293
+ {
294
+ id: "cddede95-d276-59f1-bc08-8051d6448734",
295
+ name: "__start__",
296
+ error: null,
297
+ interrupts: [],
298
+ path: ["__pregel_pull", "__start__"],
299
+ checkpoint: null,
300
+ state: null,
301
+ result: { messages: [{ type: "human", content: "Hello" }] },
302
+ },
303
+ ],
304
+ metadata: {
305
+ source: "input",
306
+ step: -1,
307
+ parents: {},
308
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
309
+ },
310
+ created_at: "2025-08-21T12:47:11.619Z",
311
+ checkpoint: {
312
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
313
+ checkpoint_id: "1f07e8cf-4cea-6530-ffff-0995a53299c5",
314
+ checkpoint_ns: "",
315
+ checkpoint_map: null,
316
+ },
317
+ parent_checkpoint: null,
318
+ },
319
+ ];
320
+ const node = (value, paths = []) => ({
321
+ type: "node",
322
+ value: typeof value === "number" ? history.at(value) : value,
323
+ path: paths.map((v) => {
324
+ if (typeof v === "number") {
325
+ return history.at(v)?.checkpoint?.checkpoint_id;
326
+ }
327
+ return v?.checkpoint?.checkpoint_id;
328
+ }),
329
+ });
330
+ const fork = (...items) => ({ type: "fork", items });
331
+ const sequence = (...items) => ({ type: "sequence", items });
332
+ (0, vitest_1.it)("full tree", async () => {
333
+ const { rootSequence, paths } = (0, branching_js_1.getBranchSequence)(history);
334
+ vitest_1.expect
335
+ .soft(paths)
336
+ .toMatchObject(vitest_1.expect.arrayContaining([[5], [5, 4], [5, 3], [2]].map((p) => p.map((i) => history.at(i)?.checkpoint?.checkpoint_id))));
337
+ vitest_1.expect
338
+ .soft(rootSequence)
339
+ .toMatchObject(sequence(node(6), fork(sequence(node(5, [5]), fork(sequence(node(4, [5, 4])), sequence(node(3, [5, 3])))), sequence(node(2, [2]), node(1, [2]), node(0, [2])))));
340
+ });
341
+ (0, vitest_1.it)("partial tree", async () => {
342
+ (0, vitest_1.expect)((0, branching_js_1.getBranchSequence)(history.slice(0, 1))).toMatchObject({
343
+ paths: [],
344
+ rootSequence: sequence(node(0)),
345
+ });
346
+ (0, vitest_1.expect)((0, branching_js_1.getBranchSequence)(history.slice(0, 2))).toMatchObject({
347
+ paths: [],
348
+ rootSequence: sequence(node(1), node(0)),
349
+ });
350
+ (0, vitest_1.expect)((0, branching_js_1.getBranchSequence)(history.slice(0, 3))).toMatchObject({
351
+ paths: [],
352
+ rootSequence: sequence(node(2), node(1), node(0)),
353
+ });
354
+ (0, vitest_1.expect)((0, branching_js_1.getBranchSequence)(history.slice(0, 4))).toMatchObject({
355
+ paths: [],
356
+ rootSequence: sequence(node(2), node(1), node(0)),
357
+ });
358
+ (0, vitest_1.expect)((0, branching_js_1.getBranchSequence)(history.slice(0, 5))).toMatchObject({
359
+ paths: [],
360
+ rootSequence: sequence(node(2), node(1), node(0)),
361
+ });
362
+ (0, vitest_1.expect)((0, branching_js_1.getBranchSequence)(history.slice(0, 6))).toMatchObject({
363
+ paths: vitest_1.expect.arrayContaining([[5], [5, 4], [5, 3], [2]].map((p) => p.map((i) => history.at(i)?.checkpoint?.checkpoint_id))),
364
+ rootSequence: sequence(fork(sequence(node(5, [5]), fork(sequence(node(4, [5, 4])), sequence(node(3, [5, 3])))), sequence(node(2, [2]), node(1, [2]), node(0, [2])))),
365
+ });
366
+ (0, vitest_1.expect)((0, branching_js_1.getBranchSequence)(history.slice(0, 7))).toMatchObject({
367
+ paths: vitest_1.expect.arrayContaining([[5], [5, 4], [5, 3], [2]].map((p) => p.map((i) => history.at(i)?.checkpoint?.checkpoint_id))),
368
+ rootSequence: sequence(node(6), fork(sequence(node(5, [5]), fork(sequence(node(4, [5, 4])), sequence(node(3, [5, 3])))), sequence(node(2, [2]), node(1, [2]), node(0, [2])))),
369
+ });
370
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,368 @@
1
+ import { it, expect } from "vitest";
2
+ import { getBranchSequence } from "./branching.js";
3
+ const history = [
4
+ {
5
+ values: {
6
+ messages: [
7
+ {
8
+ content: "Fork: Hello",
9
+ additional_kwargs: {},
10
+ response_metadata: {},
11
+ id: "33357aea-9c2d-4718-92f4-20038d5a2d29",
12
+ type: "human",
13
+ },
14
+ {
15
+ content: "Hey",
16
+ additional_kwargs: {},
17
+ response_metadata: {},
18
+ tool_call_chunks: [],
19
+ id: "run-782a2f90-2e39-4c21-9ee6-0f38585a0ae6",
20
+ tool_calls: [],
21
+ invalid_tool_calls: [],
22
+ type: "ai",
23
+ },
24
+ ],
25
+ },
26
+ next: [],
27
+ tasks: [],
28
+ metadata: {
29
+ source: "loop",
30
+ step: 2,
31
+ parents: {},
32
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
33
+ },
34
+ created_at: "2025-08-21T12:47:12.640Z",
35
+ checkpoint: {
36
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
37
+ checkpoint_id: "1f07e8cf-56a7-6000-8002-5e272b1d5285",
38
+ checkpoint_ns: "",
39
+ checkpoint_map: null,
40
+ },
41
+ parent_checkpoint: {
42
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
43
+ checkpoint_id: "1f07e8cf-53aa-6d70-8001-2096b0bbf835",
44
+ checkpoint_ns: "",
45
+ checkpoint_map: null,
46
+ },
47
+ },
48
+ {
49
+ values: {
50
+ messages: [
51
+ {
52
+ content: "Fork: Hello",
53
+ additional_kwargs: {},
54
+ response_metadata: {},
55
+ id: "33357aea-9c2d-4718-92f4-20038d5a2d29",
56
+ type: "human",
57
+ },
58
+ ],
59
+ },
60
+ next: ["agent"],
61
+ tasks: [
62
+ {
63
+ id: "680ee831-4d10-50f9-956f-e0d937321614",
64
+ name: "agent",
65
+ error: null,
66
+ interrupts: [],
67
+ path: ["__pregel_pull", "agent"],
68
+ checkpoint: null,
69
+ state: null,
70
+ result: {
71
+ messages: [
72
+ {
73
+ content: "Hey",
74
+ additional_kwargs: {},
75
+ response_metadata: {},
76
+ tool_call_chunks: [],
77
+ id: "run-782a2f90-2e39-4c21-9ee6-0f38585a0ae6",
78
+ tool_calls: [],
79
+ invalid_tool_calls: [],
80
+ type: "ai",
81
+ },
82
+ ],
83
+ },
84
+ },
85
+ ],
86
+ metadata: {
87
+ source: "loop",
88
+ step: 1,
89
+ parents: {},
90
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
91
+ },
92
+ created_at: "2025-08-21T12:47:12.327Z",
93
+ checkpoint: {
94
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
95
+ checkpoint_id: "1f07e8cf-53aa-6d70-8001-2096b0bbf835",
96
+ checkpoint_ns: "",
97
+ checkpoint_map: null,
98
+ },
99
+ parent_checkpoint: {
100
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
101
+ checkpoint_id: "1f07e8cf-53a8-6660-8000-f157c9cf7d66",
102
+ checkpoint_ns: "",
103
+ checkpoint_map: null,
104
+ },
105
+ },
106
+ {
107
+ values: { messages: [] },
108
+ next: ["__start__"],
109
+ tasks: [
110
+ {
111
+ id: "4233f4c0-5ea2-5117-9153-95e5fe59eee3",
112
+ name: "__start__",
113
+ error: null,
114
+ interrupts: [],
115
+ path: ["__pregel_pull", "__start__"],
116
+ checkpoint: null,
117
+ state: null,
118
+ result: { messages: [{ type: "human", content: "Fork: Hello" }] },
119
+ },
120
+ ],
121
+ metadata: {
122
+ source: "input",
123
+ step: 0,
124
+ parents: {},
125
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
126
+ },
127
+ created_at: "2025-08-21T12:47:12.326Z",
128
+ checkpoint: {
129
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
130
+ checkpoint_id: "1f07e8cf-53a8-6660-8000-f157c9cf7d66",
131
+ checkpoint_ns: "",
132
+ checkpoint_map: null,
133
+ },
134
+ parent_checkpoint: {
135
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
136
+ checkpoint_id: "1f07e8cf-4cea-6530-ffff-0995a53299c5",
137
+ checkpoint_ns: "",
138
+ checkpoint_map: null,
139
+ },
140
+ },
141
+ {
142
+ values: {
143
+ messages: [
144
+ {
145
+ content: "Hello",
146
+ additional_kwargs: {},
147
+ response_metadata: {},
148
+ id: "ec300e63-1494-4ef6-936c-60d02d7bdce1",
149
+ type: "human",
150
+ },
151
+ {
152
+ content: "Hey",
153
+ additional_kwargs: {},
154
+ response_metadata: {},
155
+ tool_call_chunks: [],
156
+ id: "run-593064bd-dd07-4ed3-99c0-a918625f4884",
157
+ tool_calls: [],
158
+ invalid_tool_calls: [],
159
+ type: "ai",
160
+ },
161
+ ],
162
+ },
163
+ next: [],
164
+ tasks: [],
165
+ metadata: {
166
+ source: "loop",
167
+ step: 1,
168
+ parents: {},
169
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
170
+ },
171
+ created_at: "2025-08-21T12:47:12.285Z",
172
+ checkpoint: {
173
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
174
+ checkpoint_id: "1f07e8cf-5344-64d0-8001-8a176e91d12f",
175
+ checkpoint_ns: "",
176
+ checkpoint_map: null,
177
+ },
178
+ parent_checkpoint: {
179
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
180
+ checkpoint_id: "1f07e8cf-4cf8-6f90-8000-7707fd155a8a",
181
+ checkpoint_ns: "",
182
+ checkpoint_map: null,
183
+ },
184
+ },
185
+ {
186
+ values: {
187
+ messages: [
188
+ {
189
+ content: "Hello",
190
+ additional_kwargs: {},
191
+ response_metadata: {},
192
+ id: "ec300e63-1494-4ef6-936c-60d02d7bdce1",
193
+ type: "human",
194
+ },
195
+ {
196
+ content: "Hey",
197
+ additional_kwargs: {},
198
+ response_metadata: {},
199
+ tool_call_chunks: [],
200
+ id: "run-b86ef558-eaff-4838-bc03-218f20554a9f",
201
+ tool_calls: [],
202
+ invalid_tool_calls: [],
203
+ type: "ai",
204
+ },
205
+ ],
206
+ },
207
+ next: [],
208
+ tasks: [],
209
+ metadata: {
210
+ source: "loop",
211
+ step: 1,
212
+ parents: {},
213
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
214
+ },
215
+ created_at: "2025-08-21T12:47:11.936Z",
216
+ checkpoint: {
217
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
218
+ checkpoint_id: "1f07e8cf-4ff0-6400-8001-a54aedf52a76",
219
+ checkpoint_ns: "",
220
+ checkpoint_map: null,
221
+ },
222
+ parent_checkpoint: {
223
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
224
+ checkpoint_id: "1f07e8cf-4cf8-6f90-8000-7707fd155a8a",
225
+ checkpoint_ns: "",
226
+ checkpoint_map: null,
227
+ },
228
+ },
229
+ {
230
+ values: {
231
+ messages: [
232
+ {
233
+ content: "Hello",
234
+ additional_kwargs: {},
235
+ response_metadata: {},
236
+ id: "ec300e63-1494-4ef6-936c-60d02d7bdce1",
237
+ type: "human",
238
+ },
239
+ ],
240
+ },
241
+ next: ["agent"],
242
+ tasks: [
243
+ {
244
+ id: "e3f503f1-73bc-5db1-8b4d-13f660ad1b51",
245
+ name: "agent",
246
+ error: null,
247
+ interrupts: [],
248
+ path: ["__pregel_pull", "agent"],
249
+ checkpoint: null,
250
+ state: null,
251
+ result: {
252
+ messages: [
253
+ {
254
+ content: "Hey",
255
+ additional_kwargs: {},
256
+ response_metadata: {},
257
+ tool_call_chunks: [],
258
+ id: "run-b86ef558-eaff-4838-bc03-218f20554a9f",
259
+ tool_calls: [],
260
+ invalid_tool_calls: [],
261
+ type: "ai",
262
+ },
263
+ ],
264
+ },
265
+ },
266
+ ],
267
+ metadata: {
268
+ source: "loop",
269
+ step: 0,
270
+ parents: {},
271
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
272
+ },
273
+ created_at: "2025-08-21T12:47:11.625Z",
274
+ checkpoint: {
275
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
276
+ checkpoint_id: "1f07e8cf-4cf8-6f90-8000-7707fd155a8a",
277
+ checkpoint_ns: "",
278
+ checkpoint_map: null,
279
+ },
280
+ parent_checkpoint: {
281
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
282
+ checkpoint_id: "1f07e8cf-4cea-6530-ffff-0995a53299c5",
283
+ checkpoint_ns: "",
284
+ checkpoint_map: null,
285
+ },
286
+ },
287
+ {
288
+ values: { messages: [] },
289
+ next: ["__start__"],
290
+ tasks: [
291
+ {
292
+ id: "cddede95-d276-59f1-bc08-8051d6448734",
293
+ name: "__start__",
294
+ error: null,
295
+ interrupts: [],
296
+ path: ["__pregel_pull", "__start__"],
297
+ checkpoint: null,
298
+ state: null,
299
+ result: { messages: [{ type: "human", content: "Hello" }] },
300
+ },
301
+ ],
302
+ metadata: {
303
+ source: "input",
304
+ step: -1,
305
+ parents: {},
306
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
307
+ },
308
+ created_at: "2025-08-21T12:47:11.619Z",
309
+ checkpoint: {
310
+ thread_id: "efa80f4d-725f-4903-b19f-216309f060bc",
311
+ checkpoint_id: "1f07e8cf-4cea-6530-ffff-0995a53299c5",
312
+ checkpoint_ns: "",
313
+ checkpoint_map: null,
314
+ },
315
+ parent_checkpoint: null,
316
+ },
317
+ ];
318
+ const node = (value, paths = []) => ({
319
+ type: "node",
320
+ value: typeof value === "number" ? history.at(value) : value,
321
+ path: paths.map((v) => {
322
+ if (typeof v === "number") {
323
+ return history.at(v)?.checkpoint?.checkpoint_id;
324
+ }
325
+ return v?.checkpoint?.checkpoint_id;
326
+ }),
327
+ });
328
+ const fork = (...items) => ({ type: "fork", items });
329
+ const sequence = (...items) => ({ type: "sequence", items });
330
+ it("full tree", async () => {
331
+ const { rootSequence, paths } = getBranchSequence(history);
332
+ expect
333
+ .soft(paths)
334
+ .toMatchObject(expect.arrayContaining([[5], [5, 4], [5, 3], [2]].map((p) => p.map((i) => history.at(i)?.checkpoint?.checkpoint_id))));
335
+ expect
336
+ .soft(rootSequence)
337
+ .toMatchObject(sequence(node(6), fork(sequence(node(5, [5]), fork(sequence(node(4, [5, 4])), sequence(node(3, [5, 3])))), sequence(node(2, [2]), node(1, [2]), node(0, [2])))));
338
+ });
339
+ it("partial tree", async () => {
340
+ expect(getBranchSequence(history.slice(0, 1))).toMatchObject({
341
+ paths: [],
342
+ rootSequence: sequence(node(0)),
343
+ });
344
+ expect(getBranchSequence(history.slice(0, 2))).toMatchObject({
345
+ paths: [],
346
+ rootSequence: sequence(node(1), node(0)),
347
+ });
348
+ expect(getBranchSequence(history.slice(0, 3))).toMatchObject({
349
+ paths: [],
350
+ rootSequence: sequence(node(2), node(1), node(0)),
351
+ });
352
+ expect(getBranchSequence(history.slice(0, 4))).toMatchObject({
353
+ paths: [],
354
+ rootSequence: sequence(node(2), node(1), node(0)),
355
+ });
356
+ expect(getBranchSequence(history.slice(0, 5))).toMatchObject({
357
+ paths: [],
358
+ rootSequence: sequence(node(2), node(1), node(0)),
359
+ });
360
+ expect(getBranchSequence(history.slice(0, 6))).toMatchObject({
361
+ paths: expect.arrayContaining([[5], [5, 4], [5, 3], [2]].map((p) => p.map((i) => history.at(i)?.checkpoint?.checkpoint_id))),
362
+ rootSequence: sequence(fork(sequence(node(5, [5]), fork(sequence(node(4, [5, 4])), sequence(node(3, [5, 3])))), sequence(node(2, [2]), node(1, [2]), node(0, [2])))),
363
+ });
364
+ expect(getBranchSequence(history.slice(0, 7))).toMatchObject({
365
+ paths: expect.arrayContaining([[5], [5, 4], [5, 3], [2]].map((p) => p.map((i) => history.at(i)?.checkpoint?.checkpoint_id))),
366
+ rootSequence: sequence(node(6), fork(sequence(node(5, [5]), fork(sequence(node(4, [5, 4])), sequence(node(3, [5, 3])))), sequence(node(2, [2]), node(1, [2]), node(0, [2])))),
367
+ });
368
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/langgraph-sdk",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Client library for interacting with the LangGraph API",
5
5
  "type": "module",
6
6
  "scripts": {
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes