@langchain/langgraph-sdk 0.0.43 → 0.0.45

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.
@@ -15,6 +15,14 @@ class StreamError extends Error {
15
15
  return typeof error === "object" && error != null && "message" in error;
16
16
  }
17
17
  }
18
+ function tryConvertToChunk(message) {
19
+ try {
20
+ return (0, messages_1.convertToChunk)(message);
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
18
26
  class MessageTupleManager {
19
27
  constructor() {
20
28
  Object.defineProperty(this, "chunks", {
@@ -26,12 +34,29 @@ class MessageTupleManager {
26
34
  this.chunks = {};
27
35
  }
28
36
  add(serialized) {
29
- const chunk = (0, messages_1.convertToChunk)((0, messages_1.coerceMessageLikeToMessage)(serialized));
30
- const id = chunk.id;
31
- if (!id)
37
+ // TODO: this is sometimes sent from the API
38
+ // figure out how to prevent this or move this to LC.js
39
+ if (serialized.type.endsWith("MessageChunk")) {
40
+ serialized.type = serialized.type
41
+ .slice(0, -"MessageChunk".length)
42
+ .toLowerCase();
43
+ }
44
+ const message = (0, messages_1.coerceMessageLikeToMessage)(serialized);
45
+ const chunk = tryConvertToChunk(message);
46
+ const id = (chunk ?? message).id;
47
+ if (!id) {
48
+ console.warn("No message ID found for chunk, ignoring in state", serialized);
32
49
  return null;
50
+ }
33
51
  this.chunks[id] ??= {};
34
- this.chunks[id].chunk = this.chunks[id]?.chunk?.concat(chunk) ?? chunk;
52
+ if (chunk) {
53
+ const prev = this.chunks[id].chunk;
54
+ this.chunks[id].chunk =
55
+ ((0, messages_1.isBaseMessageChunk)(prev) ? prev : null)?.concat(chunk) ?? chunk;
56
+ }
57
+ else {
58
+ this.chunks[id].chunk = message;
59
+ }
35
60
  return id;
36
61
  }
37
62
  clear() {
@@ -195,7 +220,7 @@ function useStream(options) {
195
220
  const messageManagerRef = (0, react_1.useRef)(new MessageTupleManager());
196
221
  const submittingRef = (0, react_1.useRef)(false);
197
222
  const abortRef = (0, react_1.useRef)(null);
198
- const trackStreamModeRef = (0, react_1.useRef)(["values", "messages-tuple"]);
223
+ const trackStreamModeRef = (0, react_1.useRef)(["values"]);
199
224
  const trackStreamMode = (0, react_1.useCallback)((mode) => {
200
225
  if (!trackStreamModeRef.current.includes(mode))
201
226
  trackStreamModeRef.current.push(mode);
@@ -376,6 +401,7 @@ function useStream(options) {
376
401
  catch (error) {
377
402
  if (!(error instanceof Error &&
378
403
  (error.name === "AbortError" || error.name === "TimeoutError"))) {
404
+ console.error(error);
379
405
  setStreamError(error);
380
406
  onError?.(error);
381
407
  }
@@ -388,7 +414,7 @@ function useStream(options) {
388
414
  abortRef.current = null;
389
415
  }
390
416
  };
391
- const error = isLoading ? streamError : historyError;
417
+ const error = streamError ?? historyError;
392
418
  const values = streamValues ?? historyValues;
393
419
  return {
394
420
  get values() {
@@ -403,6 +429,16 @@ function useStream(options) {
403
429
  setBranch,
404
430
  history: flatHistory,
405
431
  experimental_branchTree: rootSequence,
432
+ get interrupt() {
433
+ // Don't show the interrupt if the stream is loading
434
+ if (isLoading)
435
+ return undefined;
436
+ const interrupts = threadHead?.tasks?.at(-1)?.interrupts;
437
+ if (interrupts == null || interrupts.length === 0)
438
+ return undefined;
439
+ // Return only the current interrupt
440
+ return interrupts.at(-1);
441
+ },
406
442
  get messages() {
407
443
  trackStreamMode("messages-tuple");
408
444
  return getMessages(values);
@@ -1,7 +1,7 @@
1
1
  import { type ClientConfig } from "../client.js";
2
2
  import type { Command, DisconnectMode, MultitaskStrategy, OnCompletionBehavior } from "../types.js";
3
3
  import type { Message } from "../types.messages.js";
4
- import type { Checkpoint, Config, Metadata, ThreadState } from "../schema.js";
4
+ import type { Checkpoint, Config, Interrupt, Metadata, ThreadState } from "../schema.js";
5
5
  import type { CustomStreamEvent, MetadataStreamEvent, StreamMode, UpdatesStreamEvent } from "../types.stream.js";
6
6
  interface Node<StateType = any> {
7
7
  type: "node";
@@ -35,7 +35,25 @@ export type MessageMetadata<StateType extends Record<string, unknown>> = {
35
35
  */
36
36
  branchOptions: string[] | undefined;
37
37
  };
38
- interface UseStreamOptions<StateType extends Record<string, unknown> = Record<string, unknown>, UpdateType extends Record<string, unknown> = Partial<StateType>, CustomType = unknown> {
38
+ type BagTemplate = {
39
+ ConfigurableType?: Record<string, unknown>;
40
+ InterruptType?: unknown;
41
+ CustomEventType?: unknown;
42
+ UpdateType?: unknown;
43
+ };
44
+ type GetUpdateType<Bag extends BagTemplate, StateType extends Record<string, unknown>> = Bag extends {
45
+ UpdateType: unknown;
46
+ } ? Bag["UpdateType"] : Partial<StateType>;
47
+ type GetConfigurableType<Bag extends BagTemplate> = Bag extends {
48
+ ConfigurableType: Record<string, unknown>;
49
+ } ? Bag["ConfigurableType"] : Record<string, unknown>;
50
+ type GetInterruptType<Bag extends BagTemplate> = Bag extends {
51
+ InterruptType: unknown;
52
+ } ? Bag["InterruptType"] : unknown;
53
+ type GetCustomEventType<Bag extends BagTemplate> = Bag extends {
54
+ CustomEventType: unknown;
55
+ } ? Bag["CustomEventType"] : unknown;
56
+ interface UseStreamOptions<StateType extends Record<string, unknown> = Record<string, unknown>, Bag extends BagTemplate = BagTemplate> {
39
57
  /**
40
58
  * The ID of the assistant to use.
41
59
  */
@@ -66,11 +84,11 @@ interface UseStreamOptions<StateType extends Record<string, unknown> = Record<st
66
84
  /**
67
85
  * Callback that is called when an update event is received.
68
86
  */
69
- onUpdateEvent?: (data: UpdatesStreamEvent<UpdateType>["data"]) => void;
87
+ onUpdateEvent?: (data: UpdatesStreamEvent<GetUpdateType<Bag, StateType>>["data"]) => void;
70
88
  /**
71
89
  * Callback that is called when a custom event is received.
72
90
  */
73
- onCustomEvent?: (data: CustomStreamEvent<CustomType>["data"]) => void;
91
+ onCustomEvent?: (data: CustomStreamEvent<GetCustomEventType<Bag>>["data"]) => void;
74
92
  /**
75
93
  * Callback that is called when a metadata event is received.
76
94
  */
@@ -84,7 +102,7 @@ interface UseStreamOptions<StateType extends Record<string, unknown> = Record<st
84
102
  */
85
103
  onThreadId?: (threadId: string) => void;
86
104
  }
87
- interface UseStream<StateType extends Record<string, unknown> = Record<string, unknown>, UpdateType extends Record<string, unknown> = Partial<StateType>> {
105
+ interface UseStream<StateType extends Record<string, unknown> = Record<string, unknown>, Bag extends BagTemplate = BagTemplate> {
88
106
  /**
89
107
  * The current values of the thread.
90
108
  */
@@ -104,7 +122,7 @@ interface UseStream<StateType extends Record<string, unknown> = Record<string, u
104
122
  /**
105
123
  * Create and stream a run to the thread.
106
124
  */
107
- submit: (values: UpdateType, options?: SubmitOptions<StateType>) => void;
125
+ submit: (values: GetUpdateType<Bag, StateType> | null | undefined, options?: SubmitOptions<StateType, GetConfigurableType<Bag>>) => void;
108
126
  /**
109
127
  * The current branch of the thread.
110
128
  */
@@ -122,6 +140,10 @@ interface UseStream<StateType extends Record<string, unknown> = Record<string, u
122
140
  * @experimental
123
141
  */
124
142
  experimental_branchTree: Sequence<StateType>;
143
+ /**
144
+ * Get the interrupt value for the stream if interrupted.
145
+ */
146
+ interrupt: Interrupt<GetInterruptType<Bag>> | undefined;
125
147
  /**
126
148
  * Messages inferred from the thread.
127
149
  * Will automatically update with incoming message chunks.
@@ -137,8 +159,11 @@ interface UseStream<StateType extends Record<string, unknown> = Record<string, u
137
159
  */
138
160
  getMessagesMetadata: (message: Message, index?: number) => MessageMetadata<StateType> | undefined;
139
161
  }
140
- interface SubmitOptions<StateType extends Record<string, unknown> = Record<string, unknown>> {
141
- config?: Config;
162
+ type ConfigWithConfigurable<ConfigurableType extends Record<string, unknown>> = Config & {
163
+ configurable?: ConfigurableType;
164
+ };
165
+ interface SubmitOptions<StateType extends Record<string, unknown> = Record<string, unknown>, ConfigurableType extends Record<string, unknown> = Record<string, unknown>> {
166
+ config?: ConfigWithConfigurable<ConfigurableType>;
142
167
  checkpoint?: Omit<Checkpoint, "thread_id"> | null;
143
168
  command?: Command;
144
169
  interruptBefore?: "*" | string[];
@@ -151,5 +176,10 @@ interface SubmitOptions<StateType extends Record<string, unknown> = Record<strin
151
176
  streamMode?: Array<StreamMode>;
152
177
  optimisticValues?: Partial<StateType> | ((prev: StateType) => Partial<StateType>);
153
178
  }
154
- export declare function useStream<StateType extends Record<string, unknown> = Record<string, unknown>, UpdateType extends Record<string, unknown> = Partial<StateType>, CustomType = unknown>(options: UseStreamOptions<StateType, UpdateType, CustomType>): UseStream<StateType, UpdateType>;
179
+ export declare function useStream<StateType extends Record<string, unknown> = Record<string, unknown>, Bag extends {
180
+ ConfigurableType?: Record<string, unknown>;
181
+ InterruptType?: unknown;
182
+ CustomEventType?: unknown;
183
+ UpdateType?: unknown;
184
+ } = BagTemplate>(options: UseStreamOptions<StateType, Bag>): UseStream<StateType, Bag>;
155
185
  export {};
@@ -2,7 +2,7 @@
2
2
  "use client";
3
3
  import { Client } from "../client.js";
4
4
  import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
5
- import { coerceMessageLikeToMessage, convertToChunk, } from "@langchain/core/messages";
5
+ import { coerceMessageLikeToMessage, convertToChunk, isBaseMessageChunk, } from "@langchain/core/messages";
6
6
  class StreamError extends Error {
7
7
  constructor(data) {
8
8
  super(data.message);
@@ -12,6 +12,14 @@ class StreamError extends Error {
12
12
  return typeof error === "object" && error != null && "message" in error;
13
13
  }
14
14
  }
15
+ function tryConvertToChunk(message) {
16
+ try {
17
+ return convertToChunk(message);
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ }
15
23
  class MessageTupleManager {
16
24
  constructor() {
17
25
  Object.defineProperty(this, "chunks", {
@@ -23,12 +31,29 @@ class MessageTupleManager {
23
31
  this.chunks = {};
24
32
  }
25
33
  add(serialized) {
26
- const chunk = convertToChunk(coerceMessageLikeToMessage(serialized));
27
- const id = chunk.id;
28
- if (!id)
34
+ // TODO: this is sometimes sent from the API
35
+ // figure out how to prevent this or move this to LC.js
36
+ if (serialized.type.endsWith("MessageChunk")) {
37
+ serialized.type = serialized.type
38
+ .slice(0, -"MessageChunk".length)
39
+ .toLowerCase();
40
+ }
41
+ const message = coerceMessageLikeToMessage(serialized);
42
+ const chunk = tryConvertToChunk(message);
43
+ const id = (chunk ?? message).id;
44
+ if (!id) {
45
+ console.warn("No message ID found for chunk, ignoring in state", serialized);
29
46
  return null;
47
+ }
30
48
  this.chunks[id] ??= {};
31
- this.chunks[id].chunk = this.chunks[id]?.chunk?.concat(chunk) ?? chunk;
49
+ if (chunk) {
50
+ const prev = this.chunks[id].chunk;
51
+ this.chunks[id].chunk =
52
+ (isBaseMessageChunk(prev) ? prev : null)?.concat(chunk) ?? chunk;
53
+ }
54
+ else {
55
+ this.chunks[id].chunk = message;
56
+ }
32
57
  return id;
33
58
  }
34
59
  clear() {
@@ -192,7 +217,7 @@ export function useStream(options) {
192
217
  const messageManagerRef = useRef(new MessageTupleManager());
193
218
  const submittingRef = useRef(false);
194
219
  const abortRef = useRef(null);
195
- const trackStreamModeRef = useRef(["values", "messages-tuple"]);
220
+ const trackStreamModeRef = useRef(["values"]);
196
221
  const trackStreamMode = useCallback((mode) => {
197
222
  if (!trackStreamModeRef.current.includes(mode))
198
223
  trackStreamModeRef.current.push(mode);
@@ -373,6 +398,7 @@ export function useStream(options) {
373
398
  catch (error) {
374
399
  if (!(error instanceof Error &&
375
400
  (error.name === "AbortError" || error.name === "TimeoutError"))) {
401
+ console.error(error);
376
402
  setStreamError(error);
377
403
  onError?.(error);
378
404
  }
@@ -385,7 +411,7 @@ export function useStream(options) {
385
411
  abortRef.current = null;
386
412
  }
387
413
  };
388
- const error = isLoading ? streamError : historyError;
414
+ const error = streamError ?? historyError;
389
415
  const values = streamValues ?? historyValues;
390
416
  return {
391
417
  get values() {
@@ -400,6 +426,16 @@ export function useStream(options) {
400
426
  setBranch,
401
427
  history: flatHistory,
402
428
  experimental_branchTree: rootSequence,
429
+ get interrupt() {
430
+ // Don't show the interrupt if the stream is loading
431
+ if (isLoading)
432
+ return undefined;
433
+ const interrupts = threadHead?.tasks?.at(-1)?.interrupts;
434
+ if (interrupts == null || interrupts.length === 0)
435
+ return undefined;
436
+ // Return only the current interrupt
437
+ return interrupts.at(-1);
438
+ },
403
439
  get messages() {
404
440
  trackStreamMode("messages-tuple");
405
441
  return getMessages(values);
package/dist/schema.d.ts CHANGED
@@ -103,8 +103,8 @@ export interface AssistantGraph {
103
103
  /**
104
104
  * An interrupt thrown inside a thread.
105
105
  */
106
- export interface Interrupt {
107
- value: unknown;
106
+ export interface Interrupt<TValue = unknown> {
107
+ value: TValue;
108
108
  when: "during";
109
109
  resumable: boolean;
110
110
  ns?: string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/langgraph-sdk",
3
- "version": "0.0.43",
3
+ "version": "0.0.45",
4
4
  "description": "Client library for interacting with the LangGraph API",
5
5
  "type": "module",
6
6
  "packageManager": "yarn@1.22.19",