@ai-sdk/langchain 2.0.87 → 2.0.88

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,17 @@
1
1
  # @ai-sdk/langchain
2
2
 
3
+ ## 2.0.88
4
+
5
+ ### Patch Changes
6
+
7
+ - 2b29f7a: Add `onFinish`, `onError`, and `onAbort` callbacks to `StreamCallbacks` for `toUIMessageStream`.
8
+
9
+ - `onFinish(state)`: Called on successful completion with final LangGraph state (or `undefined` for other stream types)
10
+ - `onError(error)`: Called when stream encounters an error
11
+ - `onAbort()`: Called when stream is aborted
12
+
13
+ Also adds `parseLangGraphEvent` helper for parsing LangGraph event tuples.
14
+
3
15
  ## 2.0.87
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -56,6 +56,30 @@ return createUIMessageStreamResponse({
56
56
  });
57
57
  ```
58
58
 
59
+ ### Streaming with Callbacks
60
+
61
+ Use callbacks to access the final LangGraph state, handle errors, or detect aborts:
62
+
63
+ ```ts
64
+ const langchainStream = await graph.stream(
65
+ { messages: langchainMessages },
66
+ { streamMode: ['values', 'messages'] },
67
+ );
68
+
69
+ return createUIMessageStreamResponse({
70
+ stream: toUIMessageStream<MyGraphState>(langchainStream, {
71
+ onFinish: async finalState => {
72
+ if (finalState) {
73
+ await saveConversation(finalState.messages);
74
+ await sendAnalytics(finalState);
75
+ }
76
+ },
77
+ onError: error => console.error('Stream failed:', error),
78
+ onAbort: () => console.log('Client disconnected'),
79
+ }),
80
+ });
81
+ ```
82
+
59
83
  ### Streaming with `streamEvents`
60
84
 
61
85
  You can also use `toUIMessageStream` with `streamEvents()` for more granular event handling:
@@ -195,13 +219,21 @@ Converts AI SDK `ModelMessage` objects to LangChain `BaseMessage` objects.
195
219
 
196
220
  **Returns:** `BaseMessage[]`
197
221
 
198
- ### `toUIMessageStream(stream)`
222
+ ### `toUIMessageStream(stream, callbacks?)`
199
223
 
200
224
  Converts a LangChain/LangGraph stream to an AI SDK `UIMessageStream`.
201
225
 
202
226
  **Parameters:**
203
227
 
204
228
  - `stream`: `AsyncIterable | ReadableStream` - A stream from LangChain `model.stream()`, LangGraph `graph.stream()`, or `streamEvents()`
229
+ - `callbacks?`: `StreamCallbacks<TState>` - Optional lifecycle callbacks:
230
+ - `onStart()` - Called when stream initializes
231
+ - `onToken(token)` - Called for each token
232
+ - `onText(text)` - Called for each text chunk
233
+ - `onFinal(text)` - Called with aggregated text (on success, error, or abort)
234
+ - `onFinish(state)` - Called on success with LangGraph state (or `undefined` for other streams)
235
+ - `onError(error)` - Called when stream errors
236
+ - `onAbort()` - Called when stream is aborted
205
237
 
206
238
  **Returns:** `ReadableStream<UIMessageChunk>`
207
239
 
package/dist/index.d.mts CHANGED
@@ -3,17 +3,26 @@ import { UIMessage, UIMessageChunk, ModelMessage, ChatTransport, ChatRequestOpti
3
3
  import { RemoteGraph, RemoteGraphParams } from '@langchain/langgraph/remote';
4
4
 
5
5
  /**
6
- * Configuration options and helper callback methods for stream lifecycle events.
6
+ * Callback options for stream lifecycle events.
7
7
  */
8
- interface StreamCallbacks {
9
- /** `onStart`: Called once when the stream is initialized. */
8
+ interface StreamCallbacks<TState = unknown> {
9
+ /** Called once when the stream is initialized. */
10
10
  onStart?: () => Promise<void> | void;
11
- /** `onFinal`: Called once when the stream is closed with the final completion message. */
12
- onFinal?: (completion: string) => Promise<void> | void;
13
- /** `onToken`: Called for each tokenized message. */
11
+ /** Called for each tokenized message. */
14
12
  onToken?: (token: string) => Promise<void> | void;
15
- /** `onText`: Called for each text chunk. */
13
+ /** Called for each text chunk. */
16
14
  onText?: (text: string) => Promise<void> | void;
15
+ /** Called with aggregated text when stream ends (success, error, or abort). */
16
+ onFinal?: (completion: string) => Promise<void> | void;
17
+ /**
18
+ * Called on successful completion. Receives final graph state for LangGraph
19
+ * streams (from last "values" event), undefined for other stream types.
20
+ */
21
+ onFinish?: (finalState: TState | undefined) => Promise<void> | void;
22
+ /** Called when the stream encounters an error. */
23
+ onError?: (error: Error) => Promise<void> | void;
24
+ /** Called when the stream is aborted. */
25
+ onAbort?: () => Promise<void> | void;
17
26
  }
18
27
 
19
28
  /**
@@ -81,9 +90,26 @@ declare function convertModelMessages(modelMessages: ModelMessage[]): BaseMessag
81
90
  * return createUIMessageStreamResponse({
82
91
  * stream: toUIMessageStream(streamEvents),
83
92
  * });
93
+ *
94
+ * // With callbacks for LangGraph state
95
+ * const graphStream = await graph.stream(
96
+ * { messages },
97
+ * { streamMode: ['values', 'messages'] }
98
+ * );
99
+ * return createUIMessageStreamResponse({
100
+ * stream: toUIMessageStream<MyStateType>(graphStream, {
101
+ * onFinish: async (finalState) => {
102
+ * if (finalState) {
103
+ * await saveToDatabase(finalState);
104
+ * }
105
+ * },
106
+ * onError: (error) => console.error('Stream failed:', error),
107
+ * onAbort: () => console.log('Stream aborted'),
108
+ * }),
109
+ * });
84
110
  * ```
85
111
  */
86
- declare function toUIMessageStream(stream: AsyncIterable<AIMessageChunk> | ReadableStream, callbacks?: StreamCallbacks): ReadableStream<UIMessageChunk>;
112
+ declare function toUIMessageStream<TState = unknown>(stream: AsyncIterable<AIMessageChunk> | ReadableStream, callbacks?: StreamCallbacks<TState>): ReadableStream<UIMessageChunk>;
87
113
 
88
114
  /**
89
115
  * Options for configuring a LangSmith deployment transport.
package/dist/index.d.ts CHANGED
@@ -3,17 +3,26 @@ import { UIMessage, UIMessageChunk, ModelMessage, ChatTransport, ChatRequestOpti
3
3
  import { RemoteGraph, RemoteGraphParams } from '@langchain/langgraph/remote';
4
4
 
5
5
  /**
6
- * Configuration options and helper callback methods for stream lifecycle events.
6
+ * Callback options for stream lifecycle events.
7
7
  */
8
- interface StreamCallbacks {
9
- /** `onStart`: Called once when the stream is initialized. */
8
+ interface StreamCallbacks<TState = unknown> {
9
+ /** Called once when the stream is initialized. */
10
10
  onStart?: () => Promise<void> | void;
11
- /** `onFinal`: Called once when the stream is closed with the final completion message. */
12
- onFinal?: (completion: string) => Promise<void> | void;
13
- /** `onToken`: Called for each tokenized message. */
11
+ /** Called for each tokenized message. */
14
12
  onToken?: (token: string) => Promise<void> | void;
15
- /** `onText`: Called for each text chunk. */
13
+ /** Called for each text chunk. */
16
14
  onText?: (text: string) => Promise<void> | void;
15
+ /** Called with aggregated text when stream ends (success, error, or abort). */
16
+ onFinal?: (completion: string) => Promise<void> | void;
17
+ /**
18
+ * Called on successful completion. Receives final graph state for LangGraph
19
+ * streams (from last "values" event), undefined for other stream types.
20
+ */
21
+ onFinish?: (finalState: TState | undefined) => Promise<void> | void;
22
+ /** Called when the stream encounters an error. */
23
+ onError?: (error: Error) => Promise<void> | void;
24
+ /** Called when the stream is aborted. */
25
+ onAbort?: () => Promise<void> | void;
17
26
  }
18
27
 
19
28
  /**
@@ -81,9 +90,26 @@ declare function convertModelMessages(modelMessages: ModelMessage[]): BaseMessag
81
90
  * return createUIMessageStreamResponse({
82
91
  * stream: toUIMessageStream(streamEvents),
83
92
  * });
93
+ *
94
+ * // With callbacks for LangGraph state
95
+ * const graphStream = await graph.stream(
96
+ * { messages },
97
+ * { streamMode: ['values', 'messages'] }
98
+ * );
99
+ * return createUIMessageStreamResponse({
100
+ * stream: toUIMessageStream<MyStateType>(graphStream, {
101
+ * onFinish: async (finalState) => {
102
+ * if (finalState) {
103
+ * await saveToDatabase(finalState);
104
+ * }
105
+ * },
106
+ * onError: (error) => console.error('Stream failed:', error),
107
+ * onAbort: () => console.log('Stream aborted'),
108
+ * }),
109
+ * });
84
110
  * ```
85
111
  */
86
- declare function toUIMessageStream(stream: AsyncIterable<AIMessageChunk> | ReadableStream, callbacks?: StreamCallbacks): ReadableStream<UIMessageChunk>;
112
+ declare function toUIMessageStream<TState = unknown>(stream: AsyncIterable<AIMessageChunk> | ReadableStream, callbacks?: StreamCallbacks<TState>): ReadableStream<UIMessageChunk>;
87
113
 
88
114
  /**
89
115
  * Options for configuring a LangSmith deployment transport.
package/dist/index.js CHANGED
@@ -33,6 +33,9 @@ var import_ai = require("ai");
33
33
 
34
34
  // src/utils.ts
35
35
  var import_messages = require("@langchain/core/messages");
36
+ function parseLangGraphEvent(event) {
37
+ return event.length === 3 ? [event[1], event[2]] : [event[0], event[1]];
38
+ }
36
39
  function convertToolResultPart(block) {
37
40
  const content = (() => {
38
41
  if (block.output.type === "text" || block.output.type === "error-text") {
@@ -462,7 +465,7 @@ function processLangGraphEvent(event, state, controller) {
462
465
  toolCallInfoByIndex,
463
466
  emittedToolCallsByKey
464
467
  } = state;
465
- const [type, data] = event.length === 3 ? event.slice(1) : event;
468
+ const [type, data] = parseLangGraphEvent(event);
466
469
  switch (type) {
467
470
  case "custom": {
468
471
  let customTypeName = "custom";
@@ -922,8 +925,15 @@ function processStreamEventsEvent(event, state, controller) {
922
925
  }
923
926
  }
924
927
  }
928
+ function isAbortError(error) {
929
+ if (error instanceof Error) {
930
+ return error.name === "AbortError" || error instanceof DOMException && error.name === "AbortError";
931
+ }
932
+ return false;
933
+ }
925
934
  function toUIMessageStream(stream, callbacks) {
926
935
  const textChunks = [];
936
+ let lastValuesData;
927
937
  const modelState = {
928
938
  started: false,
929
939
  messageId: "langchain-msg-1",
@@ -979,7 +989,7 @@ function toUIMessageStream(stream, callbacks) {
979
989
  };
980
990
  return new ReadableStream({
981
991
  async start(controller) {
982
- var _a, _b, _c, _d;
992
+ var _a, _b, _c, _d, _e, _f, _g, _h;
983
993
  await ((_a = callbacks == null ? void 0 : callbacks.onStart) == null ? void 0 : _a.call(callbacks));
984
994
  const wrappedController = createCallbackController(controller);
985
995
  controller.enqueue({ type: "start" });
@@ -1009,8 +1019,13 @@ function toUIMessageStream(stream, callbacks) {
1009
1019
  wrappedController
1010
1020
  );
1011
1021
  } else {
1022
+ const eventArray = value;
1023
+ const [type, data] = parseLangGraphEvent(eventArray);
1024
+ if (type === "values") {
1025
+ lastValuesData = data;
1026
+ }
1012
1027
  processLangGraphEvent(
1013
- value,
1028
+ eventArray,
1014
1029
  langGraphState,
1015
1030
  wrappedController
1016
1031
  );
@@ -1037,10 +1052,18 @@ function toUIMessageStream(stream, callbacks) {
1037
1052
  controller.enqueue({ type: "finish" });
1038
1053
  }
1039
1054
  await ((_d = callbacks == null ? void 0 : callbacks.onFinal) == null ? void 0 : _d.call(callbacks, textChunks.join("")));
1055
+ await ((_e = callbacks == null ? void 0 : callbacks.onFinish) == null ? void 0 : _e.call(callbacks, lastValuesData));
1040
1056
  } catch (error) {
1057
+ const errorObj = error instanceof Error ? error : new Error(String(error));
1058
+ await ((_f = callbacks == null ? void 0 : callbacks.onFinal) == null ? void 0 : _f.call(callbacks, textChunks.join("")));
1059
+ if (isAbortError(error)) {
1060
+ await ((_g = callbacks == null ? void 0 : callbacks.onAbort) == null ? void 0 : _g.call(callbacks));
1061
+ } else {
1062
+ await ((_h = callbacks == null ? void 0 : callbacks.onError) == null ? void 0 : _h.call(callbacks, errorObj));
1063
+ }
1041
1064
  controller.enqueue({
1042
1065
  type: "error",
1043
- errorText: error instanceof Error ? error.message : "Unknown error"
1066
+ errorText: errorObj.message
1044
1067
  });
1045
1068
  } finally {
1046
1069
  controller.close();