@langchain/langgraph-sdk 0.0.91 → 0.0.92

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.0.92
4
+
5
+ ### Patch Changes
6
+
7
+ - 603daa6: Make history fetching configurable in useStream via `fetchStateHistory`
8
+
3
9
  ## 0.0.91
4
10
 
5
11
  ### Patch Changes
@@ -87,6 +87,17 @@ function findLastIndex(array, predicate) {
87
87
  }
88
88
  function getBranchSequence(history) {
89
89
  const childrenMap = {};
90
+ // Short circuit if there's only a singular one state
91
+ // TODO: I think we can make this more generalizable for all `fetchStateHistory` values.
92
+ if (history.length <= 1) {
93
+ return {
94
+ rootSequence: {
95
+ type: "sequence",
96
+ items: history.map((value) => ({ type: "node", value, path: [] })),
97
+ },
98
+ paths: [],
99
+ };
100
+ }
90
101
  // First pass - collect nodes for each checkpoint
91
102
  history.forEach((state) => {
92
103
  const checkpointId = state.parent_checkpoint?.checkpoint_id ?? "$";
@@ -171,10 +182,16 @@ function getBranchView(sequence, paths, branch) {
171
182
  }
172
183
  return { history, branchByCheckpoint };
173
184
  }
174
- function fetchHistory(client, threadId) {
175
- return client.threads.getHistory(threadId, { limit: 1000 });
185
+ function fetchHistory(client, threadId, options) {
186
+ if (options?.limit === false) {
187
+ return client.threads
188
+ .getState(threadId)
189
+ .then((state) => [state]);
190
+ }
191
+ const limit = typeof options?.limit === "number" ? options.limit : 1000;
192
+ return client.threads.getHistory(threadId, { limit });
176
193
  }
177
- function useThreadHistory(threadId, client, clearCallbackRef, submittingRef) {
194
+ function useThreadHistory(threadId, client, limit, clearCallbackRef, submittingRef) {
178
195
  const [history, setHistory] = (0, react_1.useState)([]);
179
196
  const clientHash = (0, client_js_1.getClientConfigHash)(client);
180
197
  const clientRef = (0, react_1.useRef)(client);
@@ -182,7 +199,9 @@ function useThreadHistory(threadId, client, clearCallbackRef, submittingRef) {
182
199
  const fetcher = (0, react_1.useCallback)((threadId) => {
183
200
  if (threadId != null) {
184
201
  const client = clientRef.current;
185
- return fetchHistory(client, threadId).then((history) => {
202
+ return fetchHistory(client, threadId, {
203
+ limit,
204
+ }).then((history) => {
186
205
  setHistory(history);
187
206
  return history;
188
207
  });
@@ -190,12 +209,12 @@ function useThreadHistory(threadId, client, clearCallbackRef, submittingRef) {
190
209
  setHistory([]);
191
210
  clearCallbackRef.current?.();
192
211
  return Promise.resolve([]);
193
- }, [clearCallbackRef]);
212
+ }, [clearCallbackRef, limit]);
194
213
  (0, react_1.useEffect)(() => {
195
214
  if (submittingRef.current)
196
215
  return;
197
216
  void fetcher(threadId);
198
- }, [fetcher, clientHash, submittingRef, threadId]);
217
+ }, [fetcher, clientHash, limit, submittingRef, threadId]);
199
218
  return {
200
219
  data: history,
201
220
  mutate: (mutateId) => fetcher(mutateId ?? threadId),
@@ -241,8 +260,9 @@ function useStreamValuesState() {
241
260
  return [values?.[0] ?? null, setStreamValues, mutate];
242
261
  }
243
262
  function useStream(options) {
244
- // eslint-disable-next-line prefer-const
245
- let { assistantId, messagesKey, onCreated, onError, onFinish } = options;
263
+ let { messagesKey } = options;
264
+ const { assistantId, fetchStateHistory } = options;
265
+ const { onCreated, onError, onFinish } = options;
246
266
  const reconnectOnMountRef = (0, react_1.useRef)(options.reconnectOnMount);
247
267
  const runMetadataStorage = (0, react_1.useMemo)(() => {
248
268
  if (typeof window === "undefined")
@@ -310,10 +330,10 @@ function useStream(options) {
310
330
  setStreamError(undefined);
311
331
  setStreamValues(null);
312
332
  };
313
- // TODO: this should be done on the server to avoid pagination
314
- // TODO: should we permit adapter? SWR / React Query?
315
- // TODO: make this only when branching is expected
316
- const history = useThreadHistory(threadId, client, clearCallbackRef, submittingRef);
333
+ const historyLimit = typeof fetchStateHistory === "object" && fetchStateHistory != null
334
+ ? fetchStateHistory.limit ?? true
335
+ : fetchStateHistory ?? true;
336
+ const history = useThreadHistory(threadId, client, historyLimit, clearCallbackRef, submittingRef);
317
337
  const getMessages = (0, react_1.useMemo)(() => {
318
338
  return (value) => Array.isArray(value[messagesKey])
319
339
  ? value[messagesKey]
@@ -606,7 +626,12 @@ function useStream(options) {
606
626
  branch,
607
627
  setBranch,
608
628
  history: flatHistory,
609
- experimental_branchTree: rootSequence,
629
+ get experimental_branchTree() {
630
+ if (historyLimit === false) {
631
+ throw new Error("`experimental_branchTree` is not available when `fetchStateHistory` is set to `false`");
632
+ }
633
+ return rootSequence;
634
+ },
610
635
  get interrupt() {
611
636
  // Don't show the interrupt if the stream is loading
612
637
  if (isLoading)
@@ -167,6 +167,15 @@ export interface UseStreamOptions<StateType extends Record<string, unknown> = Re
167
167
  * cached UI display without server fetches.
168
168
  */
169
169
  initialValues?: StateType | null;
170
+ /**
171
+ * Whether to fetch the history of the thread.
172
+ * If true, the history will be fetched from the server. Defaults to 1000 entries.
173
+ * If false, only the last state will be fetched from the server.
174
+ * @default true
175
+ */
176
+ fetchStateHistory?: boolean | {
177
+ limit: number;
178
+ };
170
179
  }
171
180
  interface RunMetadataStorage {
172
181
  getItem(key: `lg:stream:${string}`): string | null;
@@ -83,6 +83,17 @@ function findLastIndex(array, predicate) {
83
83
  }
84
84
  function getBranchSequence(history) {
85
85
  const childrenMap = {};
86
+ // Short circuit if there's only a singular one state
87
+ // TODO: I think we can make this more generalizable for all `fetchStateHistory` values.
88
+ if (history.length <= 1) {
89
+ return {
90
+ rootSequence: {
91
+ type: "sequence",
92
+ items: history.map((value) => ({ type: "node", value, path: [] })),
93
+ },
94
+ paths: [],
95
+ };
96
+ }
86
97
  // First pass - collect nodes for each checkpoint
87
98
  history.forEach((state) => {
88
99
  const checkpointId = state.parent_checkpoint?.checkpoint_id ?? "$";
@@ -167,10 +178,16 @@ function getBranchView(sequence, paths, branch) {
167
178
  }
168
179
  return { history, branchByCheckpoint };
169
180
  }
170
- function fetchHistory(client, threadId) {
171
- return client.threads.getHistory(threadId, { limit: 1000 });
181
+ function fetchHistory(client, threadId, options) {
182
+ if (options?.limit === false) {
183
+ return client.threads
184
+ .getState(threadId)
185
+ .then((state) => [state]);
186
+ }
187
+ const limit = typeof options?.limit === "number" ? options.limit : 1000;
188
+ return client.threads.getHistory(threadId, { limit });
172
189
  }
173
- function useThreadHistory(threadId, client, clearCallbackRef, submittingRef) {
190
+ function useThreadHistory(threadId, client, limit, clearCallbackRef, submittingRef) {
174
191
  const [history, setHistory] = useState([]);
175
192
  const clientHash = getClientConfigHash(client);
176
193
  const clientRef = useRef(client);
@@ -178,7 +195,9 @@ function useThreadHistory(threadId, client, clearCallbackRef, submittingRef) {
178
195
  const fetcher = useCallback((threadId) => {
179
196
  if (threadId != null) {
180
197
  const client = clientRef.current;
181
- return fetchHistory(client, threadId).then((history) => {
198
+ return fetchHistory(client, threadId, {
199
+ limit,
200
+ }).then((history) => {
182
201
  setHistory(history);
183
202
  return history;
184
203
  });
@@ -186,12 +205,12 @@ function useThreadHistory(threadId, client, clearCallbackRef, submittingRef) {
186
205
  setHistory([]);
187
206
  clearCallbackRef.current?.();
188
207
  return Promise.resolve([]);
189
- }, [clearCallbackRef]);
208
+ }, [clearCallbackRef, limit]);
190
209
  useEffect(() => {
191
210
  if (submittingRef.current)
192
211
  return;
193
212
  void fetcher(threadId);
194
- }, [fetcher, clientHash, submittingRef, threadId]);
213
+ }, [fetcher, clientHash, limit, submittingRef, threadId]);
195
214
  return {
196
215
  data: history,
197
216
  mutate: (mutateId) => fetcher(mutateId ?? threadId),
@@ -237,8 +256,9 @@ function useStreamValuesState() {
237
256
  return [values?.[0] ?? null, setStreamValues, mutate];
238
257
  }
239
258
  export function useStream(options) {
240
- // eslint-disable-next-line prefer-const
241
- let { assistantId, messagesKey, onCreated, onError, onFinish } = options;
259
+ let { messagesKey } = options;
260
+ const { assistantId, fetchStateHistory } = options;
261
+ const { onCreated, onError, onFinish } = options;
242
262
  const reconnectOnMountRef = useRef(options.reconnectOnMount);
243
263
  const runMetadataStorage = useMemo(() => {
244
264
  if (typeof window === "undefined")
@@ -306,10 +326,10 @@ export function useStream(options) {
306
326
  setStreamError(undefined);
307
327
  setStreamValues(null);
308
328
  };
309
- // TODO: this should be done on the server to avoid pagination
310
- // TODO: should we permit adapter? SWR / React Query?
311
- // TODO: make this only when branching is expected
312
- const history = useThreadHistory(threadId, client, clearCallbackRef, submittingRef);
329
+ const historyLimit = typeof fetchStateHistory === "object" && fetchStateHistory != null
330
+ ? fetchStateHistory.limit ?? true
331
+ : fetchStateHistory ?? true;
332
+ const history = useThreadHistory(threadId, client, historyLimit, clearCallbackRef, submittingRef);
313
333
  const getMessages = useMemo(() => {
314
334
  return (value) => Array.isArray(value[messagesKey])
315
335
  ? value[messagesKey]
@@ -602,7 +622,12 @@ export function useStream(options) {
602
622
  branch,
603
623
  setBranch,
604
624
  history: flatHistory,
605
- experimental_branchTree: rootSequence,
625
+ get experimental_branchTree() {
626
+ if (historyLimit === false) {
627
+ throw new Error("`experimental_branchTree` is not available when `fetchStateHistory` is set to `false`");
628
+ }
629
+ return rootSequence;
630
+ },
606
631
  get interrupt() {
607
632
  // Don't show the interrupt if the stream is loading
608
633
  if (isLoading)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/langgraph-sdk",
3
- "version": "0.0.91",
3
+ "version": "0.0.92",
4
4
  "description": "Client library for interacting with the LangGraph API",
5
5
  "type": "module",
6
6
  "scripts": {